#include #include #include #include #include #include #include #include "cforum.h" #define MAXMSG 256 static void printdate(int); static void printhtml(char *); /* * The `new' functions provide a way to add a new attachment/post/user. * On GET, they show a form. On POST, they insert the posted information * into the database. */ void newatt() {} void newpost() {} void newsession() { char *hlite, *home, msg[MAXMSG], *p, rnd[MAXCOOKIE], *v; char *name, *pass, *remember; 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"); printf("Content-Type: text/html\n\n"); #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) pass = strdup(v); else if(!remember && strcmp(p, "remember") == 0) remember = strdup(v); else continue; } if(!name || !*name){ snprintf(msg, MAXMSG, "Username may not be empty"); printf("Status: 400 Bad Request\n"); printf("Content-Type: text/html\n\n"); #include "t/login.tc" return; } 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("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 Error\n"); printf("Content-Type: text/html\n\n"); #include "t/login.tc" return; fail: snprintf(msg, MAXMSG, "Invalid username or password"); printf("Status: 400 Bad Request\n"); printf("Content-Type: text/html\n\n"); #include "t/login.tc" } void newuser() { char *captcha, *confirm, *name, *full, *pass; char *hlite, msg[MAXMSG], *p, *v; 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"); printf("Content-Type: text/html\n\n"); #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) confirm = strdup(v); else if(!name && strcmp(p, "name") == 0) name = strdup(v); else if(!full && strcmp(p, "full") == 0) full = strdup(v); else if(!pass && strcmp(p, "pass") == 0) pass = strdup(v); else continue; } /* Decode URL-encoded fields. */ if(captcha && *captcha) captcha[urldecode(captcha, -1)] = 0; if(confirm && *confirm) confirm[urldecode(confirm, -1)] = 0; if(name && *name) name[urldecode(name, -1)] = 0; if(full && *full) 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"); snprintf(msg, MAXMSG, "Username longer than %d characters", MAXUSERNAME-1); goto err; } if(full && *full && strlen(full)-1 > MAXUSERFULL){ hlite = strdup("full"); snprintf(msg, MAXMSG, "Full name longer than %d characters", MAXUSERFULL-1); goto err; } if(pass && *pass && strlen(pass)-1 > MAXUSERPASS){ hlite = strdup("pass"); snprintf(msg, MAXMSG, "Password longer than %d characters", 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])){ hlite = strdup("name"); snprintf(msg, MAXMSG, "Username may only contain ASCII letters, " "numbers and underscores."); goto err; } for(i = 0; full[i]; i++) if(!ISVIS(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"); snprintf(msg, MAXMSG, "Incorrectly answered captcha"); goto err; } /* Ensure all required fields are there. */ if(!name || !*name){ hlite = strdup("name"); snprintf(msg, MAXMSG, "Username is required"); goto err; } if(!pass || !*pass){ hlite = strdup("pass"); snprintf(msg, MAXMSG, "Password is required"); goto err; } if(pass && confirm && strcmp(pass, confirm) != 0){ 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."); else snprintf(msg, MAXMSG, "Could not create user: %s. ", 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" return; err: printf("Status: 400 Bad Request\n"); printf("Content-Type: text/html\n\n"); #include "t/newuser.tc" 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. */ 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); } void showfront() { char *title; 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" } void showpost(int id) { char *title; struct att *att; 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" free(user); free(post); } void showuser(int id) { char title[120]; 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; }