From 1e0433c9df062ead21d0f46b15d088622b7d5c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Ankarstr=C3=B6m?= Date: Wed, 22 Sep 2021 02:11:50 +0200 Subject: Log in, log out --- Makefile | 3 +- README | 14 ++-- cforum.c | 73 ++++++++++++++++----- cforum.h | 21 +++++- ctl.c | 171 +++++++++++++++++++++++++++---------------------- db.c | 193 ++++++++++++++++++++++++++++++++++++++++++-------------- err.c | 2 +- mktpl/README | 2 +- mktpl/mktpl.lex | 12 ++-- query.c | 49 ++++++++++---- t/front.t | 7 ++ 11 files changed, 381 insertions(+), 166 deletions(-) diff --git a/Makefile b/Makefile index f6f78ac..ae09e1c 100644 --- a/Makefile +++ b/Makefile @@ -37,4 +37,5 @@ db: sqlite3 db "INSERT INTO posts values(NULL, 1, 1462137896, NULL, 'Hello World!', 'This is the first post.');" sqlite3 db "INSERT INTO posts values(1, 1, 1462138896, NULL, 'Re: Hello World!', 'This is the second post!');" sqlite3 db "CREATE TABLE atts(post INT NOT NULL, name NOT NULL, desc, mime NOT NULL, data BLOB, FOREIGN KEY (post) REFERENCES posts(oid));" - sqlite3 db "$$(printf "INSERT INTO atts values(1, 'example', 'Some example shell code.', 'text/plain', '#!/bin/sh\necho Hello World!');")" \ No newline at end of file + sqlite3 db "$$(printf "INSERT INTO atts values(1, 'example', 'Some example shell code.', 'text/plain', '#!/bin/sh\necho Hello World!');")" + sqlite3 db "CREATE TABLE sessions(user INT NOT NULL, string NOT NULL, created INT NOT NULL, FOREIGN KEY (user) REFERENCES users(oid));" diff --git a/README b/README index e84f8a4..93d8782 100644 --- a/README +++ b/README @@ -8,12 +8,12 @@ C89, it can be run on practically any UNIX system. It is also rather small: wc -l *.c *.h crypt/*.c crypt/*.h */*.t */*.lex - 97 cforum.c - 381 ctl.c - 306 db.c + 138 cforum.c + 404 ctl.c + 403 db.c 11 err.c - 144 query.c - 101 cforum.h + 171 query.c + 120 cforum.h 907 crypt/crypt_blowfish.c 124 crypt/crypt_gensalt.c 551 crypt/wrapper.c @@ -23,11 +23,11 @@ It is also rather small: 43 crypt/ow-crypt.h 3 t/err.t 1 t/foot.t - 30 t/front.t + 37 t/front.t 15 t/head.t 18 t/login.t 38 t/newuser.t 28 t/post.t 12 t/user.t 95 mktpl/mktpl.lex - 2986 total + 3200 total diff --git a/cforum.c b/cforum.c index b536827..fbd5d85 100644 --- a/cforum.c +++ b/cforum.c @@ -10,10 +10,11 @@ int main(int argc, char *argv[]) { - char msg[MAXMSG], *new, *p, *v; - int attid, postid, userid; + char *delete, *home, *k, msg[MAXMSG], *new, *ssecret, *v; + int attid, postid, suser, userid; + struct session *session; sqlite3_stmt *stmt; - + /* * The database is opened or a server error is generated. * In the rest of the program, the database is always @@ -27,7 +28,7 @@ main(int argc, char *argv[]) sqlite3_close(db); return 1; } - + /* * The site name is retrieved from the database. This early on, * it is appropriate to die with a server error on failure. @@ -52,30 +53,60 @@ main(int argc, char *argv[]) return 1; } sqlite3_finalize(stmt); - + /* * The global struct query is set, or the program dies. * From now on, query is assumed to be set. */ setquery(); - + + /* Check session. */ + curuser = NULL; + ssecret = NULL; + suser = -1; + while(k = nextcookie(MAXCOOKIE+20)){ + v = split(k); + if(!ssecret && strcmp(k, "session") == 0) ssecret = strdup(v); + else if(suser == -1 && strcmp(k, "user") == 0) suser = atoi(v); + } + + if(sqlite3_prepare(db, + "SELECT oid, * FROM sessions WHERE user = ? AND string = ?", + -1, &stmt, 0) != SQLITE_OK) + goto skip; + + if(sqlite3_bind_int(stmt, 1, suser) != SQLITE_OK) + goto skip; + + if(sqlite3_bind_text(stmt, 2, ssecret, -1, SQLITE_STATIC) != SQLITE_OK) + goto skip; + + if(!(session = getsession(stmt))) + goto skip; + + /* Session is valid. */ + cursession = session; + curuser = getuser(selectbyint("users", "oid", suser)); + +skip: /* Handle empty request. */ if(!*query.string){ showfront(); goto end; } - + /* Parse query string. */ - new = NULL; + new = delete = NULL; attid = postid = userid = 0; - while(p = nextparam(GET, NULL, 128)){ - v = split(p); - if(!attid && strcmp(p, "att") == 0) attid = atoi(v); - else if(!postid && strcmp(p, "post") == 0) postid = atoi(v); - else if(!userid && strcmp(p, "user") == 0) userid = atoi(v); - else if(!new && strcmp(p, "new") == 0) new = strdup(v); + while(k = nextparam(GET, NULL, 128)){ + v = split(k); + if(!attid && strcmp(k, "att") == 0) attid = atoi(v); + else if(!postid && strcmp(k, "post") == 0) postid = atoi(v); + else if(!userid && strcmp(k, "user") == 0) userid = atoi(v); + else if(!new && strcmp(k, "new") == 0) new = strdup(v); + else if(!delete && strcmp(k,"delete") == 0) delete = strdup(v); } - + /* Handle request. */ if(attid) showatt(attid); @@ -89,9 +120,19 @@ main(int argc, char *argv[]) else if(strcmp(v, "user") == 0) newuser(); else if(strcmp(v, "session") == 0) newsession(); else showfront(); /* TODO */ + } + else if(delete){ + if(strcmp(v, "session") == 0){ + if(cursession) + deletesession(cursession); + home = getenv("REQUEST_URI"); + home[strcspn(home, "?")] = 0; + printf("Status: 303 See Other\n"); + printf("Location: %s\n\n", home); + } }else showfront(); - + end: sqlite3_close(db); return 0; diff --git a/cforum.h b/cforum.h index 7d7dc60..7285e76 100644 --- a/cforum.h +++ b/cforum.h @@ -27,6 +27,13 @@ struct post{ char *text; }; +struct session{ + int id; + int user; + int created; + char *string; +}; + struct user{ int id; int created; @@ -60,14 +67,20 @@ void showuser(int); /* db.c */ int addatt(struct att *); +int addpost(struct post *); +int addsession(struct session *); int adduser(struct user *); +int deletesession(struct session *); int haspass(struct user *, char *); +void makerandom(char *, int); void makehash(char *, char **, char **); struct att *getatt(sqlite3_stmt *); struct post *getpost(sqlite3_stmt *); +struct session *getsession(sqlite3_stmt *); struct user *getuser(sqlite3_stmt *); struct att *nextatt(sqlite3_stmt *); struct post *nextpost(sqlite3_stmt *); +struct session *nextsession(sqlite3_stmt *); struct user *nextuser(sqlite3_stmt *); sqlite3_stmt *selectbyint(char *, char *, int); @@ -75,6 +88,7 @@ sqlite3_stmt *selectbyint(char *, char *, int); void srverr(char *); /* query.c */ +char *nextcookie(int); char *nextparam(enum method, int *, int); void setquery(void); char *split(char *); @@ -95,8 +109,13 @@ int urldecode(char *, int); #define MAXUSERFULL 128 #define MAXUSERPASS 72 +/* Maximum size of cookie values. */ +#define MAXCOOKIE 40 + /***** Variables *****/ sqlite3 *db; struct query query; -struct site site; \ No newline at end of file +struct site site; +struct user *curuser; +struct session *cursession; \ No newline at end of file diff --git a/ctl.c b/ctl.c index 9583c44..39581c2 100644 --- a/ctl.c +++ b/ctl.c @@ -1,41 +1,16 @@ #include +#include #include #include #include #include +#include #include "cforum.h" #define MAXMSG 256 -/* Print UNIX timestamp as written date. */ -void -printdate(int timestamp) -{ - char buf[20]; - struct tm *t; - - t = localtime((time_t *)×tamp); - strftime(buf, 20, "%Y-%m-%d %H:%M", t); - - printf(buf); -} - -/* Print HTML-escaped string. */ -void -printhtml(char *s) -{ - char *t; - - for(t = s; *t; t++) - switch(*t){ - case '&': printf("&"); break; - case '\"': printf("""); break; - case '\'': printf("'"); break; - case '<': printf("<"); break; - case '>': printf(">"); break; - default: printf("%c", *t); - } -} +static void printdate(int); +static void printhtml(char *); /* * The `new' functions provide a way to add a new attachment/post/user. @@ -53,21 +28,26 @@ newpost() void newsession() { - char *hlite, msg[MAXMSG], *p, *v; + char *hlite, *home, msg[MAXMSG], *p, rnd[MAXCOOKIE], *v; char *name, *pass, *remember; - char title[] = "Log In"; struct user *user; + struct session session; sqlite3_stmt *stmt; - + + char title[] = "Log In"; + *msg = 0; hlite = name = pass = remember = NULL; - + + home = getenv("REQUEST_URI"); + home[strcspn(home, "?")] = 0; + if(query.method == GET){ printf("Content-Type: text/html\n\n"); #include "t/login.tc" return; } - + if(query.length > MAXSESSIONDATA){ snprintf(msg, MAXMSG, "Input exceeded server limitations"); printf("Status: 431 Request Header Fields Too Large\n"); @@ -75,10 +55,10 @@ newsession() #include "t/login.tc" return; } - + while(p = nextparam(POST, NULL, MAXSESSIONDATA)){ if(!(v = split(p))) continue; - + if(!name && strcmp(p, "name") == 0) name = strdup(v); else if(!pass && strcmp(p, "pass") == 0) @@ -88,7 +68,7 @@ newsession() else continue; } - + if(!name || !*name){ snprintf(msg, MAXMSG, "Username may not be empty"); printf("Status: 400 Bad Request\n"); @@ -96,28 +76,42 @@ newsession() #include "t/login.tc" return; } - - if(sqlite3_prepare(db, "SELECT oid, * from users WHERE name = ?", + + if(sqlite3_prepare(db, "SELECT oid, * FROM users WHERE name = ?", -1, &stmt, 0) != SQLITE_OK) goto fail; - + if(sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC) != SQLITE_OK) goto dberr; - + if(!(user = getuser(stmt))) goto fail; - + if(!haspass(user, pass)) goto fail; + + /* Create session cookie. */ + makerandom(rnd, sizeof(rnd)); + session.user = user->id; + session.string = rnd; + if(!addsession(&session)){ + snprintf(msg, MAXMSG, "Could not create session for user"); + printf("Status: 500 Internal Server Error"); + printf("Content-Type: text/html\n\n"); + #include "t/login.tc" + return; + } - printf("Content-Type: text/plain\n\n"); - printf("Success!\n"); + printf("Status: 303 See Other\n"); + printf("Set-Cookie: user=%d\n", user->id); + printf("Set-Cookie: session=%s\n", rnd); + printf("Location: %s\n\n", home); return; dberr: snprintf(msg, MAXMSG, "Could not retrieve user: %s", sqlite3_errmsg(db)); - printf("Status: 500 Internal Server Errror\n"); + printf("Status: 500 Internal Server Error\n"); printf("Content-Type: text/html\n\n"); #include "t/login.tc" return; @@ -136,16 +130,16 @@ newuser() char title[] = "New User"; int i; struct user *user; - + *msg = 0; captcha = confirm = hlite = name = full = pass = NULL; - + if(query.method == GET){ printf("Content-Type: text/html\n\n"); #include "t/newuser.tc" return; } - + if(query.length > MAXUSERDATA){ snprintf(msg, MAXMSG, "Input exceeded server limitations"); printf("Status: 431 Request Header Fields Too Large\n"); @@ -153,10 +147,10 @@ newuser() #include "t/newuser.tc" return; } - + while(p = nextparam(POST, NULL, MAXUSERDATA)){ if(!(v = split(p))) continue; - + if(!captcha && strcmp(p, "captcha") == 0) captcha = strdup(v); if(!confirm && strcmp(p, "confirm") == 0) @@ -170,7 +164,7 @@ newuser() else continue; } - + /* Decode URL-encoded fields. */ if(captcha && *captcha) captcha[urldecode(captcha, -1)] = 0; @@ -182,7 +176,7 @@ newuser() full[urldecode(full, -1)] = 0; if(pass && *pass) pass[urldecode(pass, -1)] = 0; - + /* Constrain lengths of decoded fields. */ if(name && *name && strlen(name)-1 > MAXUSERNAME){ hlite = strdup("name"); @@ -202,10 +196,10 @@ newuser() MAXUSERPASS-1); goto err; } - + #define ISVIS(c) ((unsigned char)c >= 32) #define ISALNUM(c) (c>='A' && c<='Z' || c>='a' && c<='z' || c>='0' && c<='9') - + /* Constrain character sets. */ for(i = 0; name[i]; i++) if(!name[i] == '_' && !ISALNUM(name[i])){ @@ -217,13 +211,12 @@ newuser() } for(i = 0; full[i]; i++) if(!ISVIS(full[i])){ - fprintf(stderr, "%d\n", full[i]); hlite = strdup("full"); snprintf(msg, MAXMSG, "Full name may only contain visible characters"); goto err; } - + /* Validate captcha. */ if(captcha && strcmp(captcha, "9") != 0){ hlite = strdup("captcha"); @@ -247,14 +240,14 @@ newuser() snprintf(msg, MAXMSG, "Passwords do not match"); goto err; } - + if(!(user = malloc(sizeof(struct user)))) err(1, "malloc"); - + user->name = name; user->full = *full? full: NULL; makehash(pass, &user->hash, &user->salt); - + if(!adduser(user)){ if(strcmp(sqlite3_errmsg(db), "column name is not unique")==0) snprintf(msg, MAXMSG, "Username already exists."); @@ -263,7 +256,7 @@ newuser() sqlite3_errmsg(db)); goto err; } - + printf("Content-Type: text/html\n\n"); snprintf(msg, MAXMSG, "SUCCESS: User was added to database"); #include "t/newuser.tc" @@ -275,6 +268,36 @@ err: return; } +/* Print UNIX timestamp as written date. */ +void +printdate(int timestamp) +{ + char buf[20]; + struct tm *t; + + t = localtime((time_t *)×tamp); + strftime(buf, 20, "%Y-%m-%d %H:%M", t); + + printf(buf); +} + +/* Print HTML-escaped string. */ +void +printhtml(char *s) +{ + char *t; + + for(t = s; *t; t++) + switch(*t){ + case '&': printf("&"); break; + case '\"': printf("""); break; + case '\'': printf("'"); break; + case '<': printf("<"); break; + case '>': printf(">"); break; + default: printf("%c", *t); + } +} + /* * The `show' functions show an existing attachment/post/user * or some other type of page. @@ -283,12 +306,12 @@ void showatt(id) { struct att *att; - + if(!(att = getatt(selectbyint("atts", "oid", id)))){ srverr("Could not retrieve attachment"); return; } - + printf("Content-Type: %s\n\n", att->mime); printf("%.*s", att->bytes, att->data); free(att); @@ -301,21 +324,21 @@ showfront() struct post *post; struct user *user; sqlite3_stmt *pstmt, *ustmt; - + if(sqlite3_prepare(db, "SELECT oid, * from posts ORDER BY created DESC", -1, &pstmt, 0) != SQLITE_OK){ srverr("Could not retrieve posts"); return; } - + if(sqlite3_prepare(db, "SELECT oid, * from users ORDER BY created DESC", -1, &ustmt, 0) != SQLITE_OK){ srverr("Could not retrieve users"); return; } - + title = site.name; printf("Content-Type: text/html\n\n"); #include "t/front.tc" @@ -329,19 +352,19 @@ showpost(int id) struct post *post; struct user *user; sqlite3_stmt *stmt; - + if(!(post = getpost(selectbyint("posts", "oid", id)))){ srverr("Could not retrieve post"); return; } - + if(!(user = getuser(selectbyint("users", "oid", post->user)))){ srverr("Could not retrieve post author"); return; } - + stmt = selectbyint("atts", "post", id); - + title = post->subject; printf("Content-Type: text/html\n\n"); #include "t/post.tc" @@ -356,26 +379,26 @@ showuser(int id) sqlite3_stmt *stmt; struct post *post; struct user *user; - + if(!(user = getuser(selectbyint("users", "oid", id)))){ srverr("Could not retrieve user"); return; } - + if(sqlite3_prepare(db, "SELECT oid, * from posts WHERE user = ? ORDER BY created DESC", -1, &stmt, 0) != SQLITE_OK) goto err; - + if(sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) goto err; - + snprintf(title, 120, "User: %s", user->full? user->full: user->name); printf("Content-Type: text/html\n\n"); #include "t/user.tc" free(user); return; - + err: srverr("Could not retrieve posts"); return; diff --git a/db.c b/db.c index 4b1b234..29797d3 100644 --- a/db.c +++ b/db.c @@ -9,7 +9,7 @@ #include "cforum.h" #include "crypt/ow-crypt.h" -static char * strdupn(const unsigned char *); +static char *strdupn(const unsigned char *); /* * The `add' functions insert an att/post/user struct into the database. @@ -24,30 +24,30 @@ addatt(struct att *att) " VALUES (?, ?, ?, ?, ?)", -1, &stmt, 0) != SQLITE_OK) goto err; - + if(sqlite3_bind_int(stmt, 1, att->post) != SQLITE_OK) goto err; - + if(sqlite3_bind_text(stmt, 2, att->name, -1, SQLITE_STATIC) != SQLITE_OK) goto err; - + if(sqlite3_bind_text(stmt, 3, att->desc, -1, SQLITE_STATIC) != SQLITE_OK) goto err; - + if(sqlite3_bind_text(stmt, 4, att->mime, -1, SQLITE_STATIC) != SQLITE_OK) goto err; - + if(sqlite3_bind_blob(stmt, 5, att->data, att->bytes, SQLITE_STATIC) != SQLITE_OK) goto err; - + if(sqlite3_step(stmt) != SQLITE_DONE) goto err; - + sqlite3_finalize(stmt); return 1; err: @@ -65,27 +65,58 @@ addpost(struct post *post) " VALUES (?, ?, ?, ?, ?)", -1, &stmt, 0) != SQLITE_OK) goto err; - + if(sqlite3_bind_int(stmt, 1, post->parent) != SQLITE_OK) goto err; - + if(sqlite3_bind_int(stmt, 2, post->user) != SQLITE_OK) goto err; - + if(sqlite3_bind_int(stmt, 3, post->created) != SQLITE_OK) goto err; - + if(sqlite3_bind_text(stmt, 4, post->subject, -1, SQLITE_STATIC) != SQLITE_OK) goto err; - + if(sqlite3_bind_text(stmt, 5, post->text, -1, SQLITE_STATIC) != SQLITE_OK) goto err; - + if(sqlite3_step(stmt) != SQLITE_DONE) goto err; - + + sqlite3_finalize(stmt); + return 1; +err: + sqlite3_finalize(stmt); + return 0; +} + +int +addsession(struct session *session) +{ + sqlite3_stmt *stmt; + + if(sqlite3_prepare(db, "INSERT INTO sessions" + " (user, string, created)" + " VALUES (?, ?, ?)", + -1, &stmt, 0) != SQLITE_OK) + goto err; + + if(sqlite3_bind_int(stmt, 1, session->user) != SQLITE_OK) + goto err; + + if(sqlite3_bind_text(stmt, 2, session->string, -1, SQLITE_STATIC) + != SQLITE_OK) + goto err; + + if(sqlite3_bind_int(stmt, 3, time(NULL)) != SQLITE_OK) + goto err; + + if(sqlite3_step(stmt) != SQLITE_DONE) + goto err; + sqlite3_finalize(stmt); return 1; err: @@ -103,15 +134,15 @@ adduser(struct user *user) " VALUES (?, ?, ?, ?, ?)", -1, &stmt, 0) != SQLITE_OK) goto err; - + if(sqlite3_bind_text(stmt, 1, user->name, -1, SQLITE_STATIC) != SQLITE_OK) goto err; - + if(sqlite3_bind_text(stmt, 2, user->full, -1, SQLITE_STATIC) != SQLITE_OK) goto err; - + if(sqlite3_bind_text(stmt, 3, user->hash, -1, SQLITE_STATIC) != SQLITE_OK) goto err; @@ -119,13 +150,13 @@ adduser(struct user *user) if(sqlite3_bind_text(stmt, 4, user->salt, -1, SQLITE_STATIC) != SQLITE_OK) goto err; - + if(sqlite3_bind_int(stmt, 5, time(NULL)) != SQLITE_OK) goto err; - + if(sqlite3_step(stmt) != SQLITE_DONE) goto err; - + sqlite3_finalize(stmt); return 1; err: @@ -133,6 +164,21 @@ err: return 0; } +int +deletesession(struct session *session) +{ + sqlite3_stmt *stmt; + + if(sqlite3_prepare(db, "DELETE FROM sessions WHERE oid = ?", + -1, &stmt, 0) != SQLITE_OK) + return 0; + + if(sqlite3_bind_int(stmt, 1, session->id) != SQLITE_OK) + return 0; + + return sqlite3_step(stmt) != SQLITE_ROW; +} + /* * The `get' functions retrieve an att/post/user struct once, * after which the statement is automatically finalized. @@ -155,6 +201,15 @@ getpost(sqlite3_stmt *stmt) return post; } +struct session * +getsession(sqlite3_stmt *stmt) +{ + struct session *session; + session = nextsession(stmt); + sqlite3_finalize(stmt); + return session; +} + struct user * getuser(sqlite3_stmt *stmt) { @@ -169,27 +224,47 @@ int haspass(struct user *user, char *pass) { char *newhash; - + newhash = crypt(pass, user->salt); return strcmp(user->hash, newhash) == 0; } +/* Generate string of size-1 random lowercase letters. */ +void +makerandom(char *buf, int size) +{ + int fd, i; + + if((fd = open("/dev/urandom", O_RDONLY)) == -1) + err(1, "open"); + + if(read(fd, buf, size-1) == -1) + err(1, "read"); + + close(fd); + + /* Convert to lowercase letters. */ + for(i = 0; i < size; i++) + buf[i] = (buf[i]+128)%25+97; + buf[size] = 0; +} + /* Generate new salt and hash for password. */ void makehash(char *pass, char **hash, char **salt) { char data[50]; - int fd; - - if(fd = open("/dev/urandom", O_RDONLY) == -1) + int fd, n; + + if((fd = open("/dev/urandom", O_RDONLY)) == -1) err(1, "open"); - if(read(fd, data, sizeof(data)) == -1) + if((n = read(fd, data, sizeof(data))) == -1) err(1, "read"); - + if(!(*salt = crypt_gensalt("$2b$", 10, data, sizeof(data)))) err(1, "crypt_gensalt"); - + *hash = crypt(pass, *salt); close(fd); } @@ -203,28 +278,28 @@ struct att * nextatt(sqlite3_stmt *stmt) { struct att *att; - + if(!stmt) return NULL; - + if(sqlite3_step(stmt) != SQLITE_ROW) return NULL; - + if(!(att = malloc(sizeof(struct att)))) err(1, "malloc"); - + att->id = sqlite3_column_int(stmt, 0); att->post = sqlite3_column_int(stmt, 1); att->name = strdupn(sqlite3_column_text(stmt, 2)); att->desc = strdupn(sqlite3_column_text(stmt, 3)); att->mime = strdupn(sqlite3_column_text(stmt, 4)); - + att->bytes = sqlite3_column_bytes(stmt, 5); if(!(att->data = malloc(att->bytes))) err(1, "malloc"); memcpy(att->data, sqlite3_column_blob(stmt, 5), att->bytes); - + return att; } @@ -232,16 +307,16 @@ struct post * nextpost(sqlite3_stmt *stmt) { struct post *post; - + if(!stmt) return NULL; - + if(sqlite3_step(stmt) != SQLITE_ROW) return NULL; - + if(!(post = malloc(sizeof(struct post)))) err(1, "malloc"); - + post->id = sqlite3_column_int(stmt, 0); post->parent = sqlite3_column_int(stmt, 1); post->user = sqlite3_column_int(stmt, 2); @@ -249,31 +324,53 @@ nextpost(sqlite3_stmt *stmt) post->edited = sqlite3_column_int(stmt, 4); post->subject = strdupn(sqlite3_column_text(stmt, 5)); post->text = strdupn(sqlite3_column_text(stmt, 6)); - + return post; } +struct session * +nextsession(sqlite3_stmt *stmt) +{ + struct session *session; + + if(!stmt) + return NULL; + + if(sqlite3_step(stmt) != SQLITE_ROW) + return NULL; + + if(!(session = malloc(sizeof(struct session)))) + err(1, "malloc"); + + session->id = sqlite3_column_int(stmt, 0); + session->user = sqlite3_column_int(stmt, 1); + session->string = strdupn(sqlite3_column_text(stmt, 2)); + session->created = sqlite3_column_int(stmt, 3); + + return session; +} + struct user * nextuser(sqlite3_stmt *stmt) { struct user *user; - + if(!stmt) return NULL; - + if(sqlite3_step(stmt) != SQLITE_ROW) return NULL; - + if(!(user = malloc(sizeof(struct user)))) err(1, "malloc"); - + user->id = sqlite3_column_int(stmt, 0); user->name = strdupn(sqlite3_column_text(stmt, 1)); user->full = strdupn(sqlite3_column_text(stmt, 2)); user->hash = strdupn(sqlite3_column_text(stmt, 3)); user->salt = strdupn(sqlite3_column_text(stmt, 4)); user->created = sqlite3_column_int(stmt, 5); - + return user; } @@ -287,15 +384,15 @@ selectbyint(char *table, char *field, int i) { char sql[100]; sqlite3_stmt *stmt; - + snprintf(sql, 100, "SELECT oid, * FROM %s WHERE %s = ?", table, field); - + if(sqlite3_prepare(db, sql, -1, &stmt, 0) != SQLITE_OK) return NULL; - + if(sqlite3_bind_int(stmt, 1, i) != SQLITE_OK) return NULL; - + return stmt; } diff --git a/err.c b/err.c index 07a50a2..35a5666 100644 --- a/err.c +++ b/err.c @@ -5,7 +5,7 @@ void srverr(char *err) { char title[] = "500 Internal Server Error"; - + printf("Status: %s\n", title); printf("Content-Type: text/html\n\n"); #include "t/err.tc" diff --git a/mktpl/README b/mktpl/README index 85a2fe3..cb50a6e 100644 --- a/mktpl/README +++ b/mktpl/README @@ -20,7 +20,7 @@ is compiled to the following C code: /* This C code will be executed. */ for(i = 0; i<10; i++) printf("Hello World!\n"); strcpy(s, "This"); - + printf("\n"); printf("%s", s ); printf(" will be evaluated and printed as a string."); diff --git a/mktpl/mktpl.lex b/mktpl/mktpl.lex index e184c0e..296cc0b 100644 --- a/mktpl/mktpl.lex +++ b/mktpl/mktpl.lex @@ -5,12 +5,12 @@ #include #include #include - + void apnd(); void eval(); void text(); void prnt(); - + char *buf; int len; int sz; @@ -38,7 +38,7 @@ main(int argc, char *argv[]) sz = 512; if(!(buf = malloc(sz))) err(1, "malloc"); - + yylex(); return 0; } @@ -51,7 +51,7 @@ apnd() if(!(buf = realloc(buf, sz))) err(1, "realloc"); } - + buf[len++] = *yytext; } @@ -66,9 +66,9 @@ void text() { int i, ofs; - + if(!len) return; - + printf("printf(\""); for(i = ofs = 0; i < len; i++){ /* diff --git a/query.c b/query.c index 8f54ead..1f3fe88 100644 --- a/query.c +++ b/query.c @@ -4,6 +4,33 @@ #include #include "cforum.h" +/* Return an allocated string containing the next cookie ("name=value"). */ +char * +nextcookie(int max) +{ + char *buf; + int n; + static char *cs = NULL; + + if(!(buf = malloc(max))) + err(1, "malloc"); + + if(!cs) + cs = getenv("HTTP_COOKIE"); + + if(!cs || !*cs) + return NULL; + + if(*cs == ' ') + cs++; + + n = strcspn(cs, ";"); + snprintf(buf, max, "%.*s", n, cs); + cs += n + (n != strlen(cs)); + + return buf; +} + /* * Return an allocated string containing the next parameter ("key=value"). * The method argument decides which data (GET or POST) to read from. @@ -21,17 +48,17 @@ nextparam(enum method method, int *len, int max) char *buf; int i, sz; static int j = 0; - + /* Return NULL if at end of parameter string. */ if(method == GET && !query.string[j-1]) return NULL; - + #define STEP 256 - + sz = STEP; if(!(buf = malloc(1+sz))) /* Leave space for -1th character. */ err(1, "malloc"); - + /* * buf's -1th character is set to 1 if the string * has been truncated (i.e. if input exceeded max). @@ -51,14 +78,14 @@ first: /* Return NULL if at end of parameter string. */ if(!READ(buf)) return NULL; - + /* Skip empty parameters. */ if(*buf == '&') goto first; - + i = 0; goto rest; - + for(; READ(buf+i); i++){ rest: if(buf[i] == '&'){ @@ -80,7 +107,7 @@ rest: } if(len) *len = i; else buf[i] = 0; - + return buf; } @@ -125,10 +152,10 @@ int urldecode(char *s, int len) { unsigned int code, i, j; - + if(len < 0) len = strlen(s); - + for(i = j = 0; i < len; i++, j++){ if(s[i] == '+') s[j] = ' '; @@ -140,6 +167,6 @@ urldecode(char *s, int len) }else s[j] = s[i]; } - + return j; } \ No newline at end of file diff --git a/t/front.t b/t/front.t index 4f94a9b..f8dc9e3 100644 --- a/t/front.t +++ b/t/front.t @@ -1,8 +1,15 @@ <% #include "head.tc" %>

<%= site.name %>

+<% if(curuser) { %> +Logged in as <%= curuser->name %>. +<% } %>

Latest posts

-- cgit v1.2.3