From 0dd0082560332995ba8e6ed86b3fd6b92ba783e8 Mon Sep 17 00:00:00 2001 From: onefang Date: Wed, 18 Mar 2020 23:44:23 +1000 Subject: Major rewrite of the validation code. Other things might have snuck into this mess. Make sure errors cause pages to reload with the data that was entered, so they can easily correct it. --- src/sledjchisl/sledjchisl.c | 576 +++++++++++++++++++++++--------------------- 1 file changed, 304 insertions(+), 272 deletions(-) (limited to 'src/sledjchisl/sledjchisl.c') diff --git a/src/sledjchisl/sledjchisl.c b/src/sledjchisl/sledjchisl.c index 06c6187..8bcf076 100644 --- a/src/sledjchisl/sledjchisl.c +++ b/src/sledjchisl/sledjchisl.c @@ -305,18 +305,18 @@ qhashtbl_t *HTMLfileCache = NULL; typedef struct _reqData reqData; -typedef int (*fieldValidFunc) (reqData *Rd, qhashtbl_t *data); +typedef int (*fieldValidFunc) (reqData *Rd, qhashtbl_t *data, char *name); typedef struct _validFunc validFunc; struct _validFunc { - char *name; + char *name, *title; fieldValidFunc func; }; qlisttbl_t *fieldValidFuncs = NULL; -static void newValidFunc(char *name, fieldValidFunc func) +static void newValidFunc(char *name, char *title, fieldValidFunc func) { validFunc *vf = xmalloc(sizeof(validFunc)); - vf->name = name; vf->func = func; + vf->name = name; vf->title = title; vf->func = func; fieldValidFuncs->put(fieldValidFuncs, vf->name, vf, sizeof(validFunc)); } @@ -402,7 +402,7 @@ struct _sesh struct _reqData { lua_State *L; - qhashtbl_t *configs, *queries, *body, *cookies, *headers, *stuff, *database, *Rcookies, *Rheaders; + qhashtbl_t *configs, *queries, *body, *cookies, *headers, *valid, *stuff, *database, *Rcookies, *Rheaders; char *Scheme, *Host, *Method, *Script, *RUri, *doit; sesh shs, *lnk; MYSQL *db; @@ -2727,6 +2727,96 @@ NOTE - storing a pepper on the same RAID array as everything else will be a prob https://stackoverflow.com/questions/16891729/best-practices-salting-peppering-passwords */ + +static void bitch(reqData *Rd, char *message, char *log) +{ + addStrL(Rd->errors, message); + E("%s %s %s %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), getStrH(Rd->stuff, "UUID"), getStrH(Rd->stuff, "name"), message, log); +} + +/* "A token cookie that references a non-existent session, its value should be replaced immediately to prevent session fixation." +https://owasp.org/www-community/attacks/Session_fixation + Which describes the problem, but offers no solution. + See https://stackoverflow.com/questions/549/the-definitive-guide-to-form-based-website-authentication?rq=1. +I think this means send a new cookie. + I clear out the cookies and send blank ones with -1 maxAge, so they should get deleted. +*/ +static void bitchSession(reqData *Rd, char *message, char *log) +{ + addStrL(Rd->errors, message); + C("%s %s %s %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), getStrH(Rd->stuff, "UUID"), getStrH(Rd->stuff, "name"), message, log); + Rd->vegOut = TRUE; +} + + +int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, struct stat *st, struct timespec *now, char *type) +{ + struct timespec then; + + if (-1 == clock_gettime(CLOCK_REALTIME, &then)) + perror_msg("Unable to get the time."); + I("Reading %s file %s", type, file); + if (0 != stat(file, st)) + { + D("No %s file.", file); + perror_msg("Unable to stat %s", file); + ret++; + } + else + { + int status = luaL_loadfile(Rd->L, file), result; + + if (status) + { + bitchSession(Rd, "No such thing.", "Can't load file."); + E("Couldn't load file: %s", lua_tostring(Rd->L, -1)); + ret++; + } + else + { + result = lua_pcall(Rd->L, 0, LUA_MULTRET, 0); + + if (result) + { + bitchSession(Rd, "Broken thing.", "Can't run file."); + E("Failed to run script: %s", lua_tostring(Rd->L, -1)); + ret++; + } + else + { + lua_getglobal(Rd->L, var); + lua_pushnil(Rd->L); + + while(lua_next(Rd->L, -2) != 0) + { + char *n = (char *) lua_tostring(Rd->L, -2); + + if (lua_isstring(Rd->L, -1)) + { + tnm->putstr(tnm, n, (char *) lua_tostring(Rd->L, -1)); +//d("Reading %s = %s", n, getStrH(tnm, n)); + } + else + { + char *v = (char *) lua_tostring(Rd->L, -1); + W("Unknown Lua variable type for %s = %s", n, v); + } + lua_pop(Rd->L, 1); + } + + if (-1 == clock_gettime(CLOCK_REALTIME, now)) + perror_msg("Unable to get the time."); + double n = (now->tv_sec * 1000000000.0) + now->tv_nsec; + double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; + T("Reading %s file took %lf seconds", type, (n - t) / 1000000000.0); + } + } + } + + return ret; +} + + static void freeSesh(reqData *Rd, boolean linky, boolean wipe) { char *file = NULL; @@ -2933,26 +3023,6 @@ static void createUser(reqData *Rd) free(file); } - -static void bitch(reqData *Rd, char *message, char *log) -{ - addStrL(Rd->errors, message); - E("%s %s %s %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), getStrH(Rd->stuff, "UUID"), getStrH(Rd->stuff, "name"), message, log); -} - -/* "A token cookie that references a non-existent session, its value should be replaced immediately to prevent session fixation." -https://owasp.org/www-community/attacks/Session_fixation - Which describes the problem, but offers no solution. - See https://stackoverflow.com/questions/549/the-definitive-guide-to-form-based-website-authentication?rq=1. -I think this means send a new cookie. - I clear out the cookies and send blank ones with -1 maxAge, so they should get deleted. -*/ -static void bitchSession(reqData *Rd, char *message, char *log) -{ - addStrL(Rd->errors, message); - C("%s %s %s %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), getStrH(Rd->stuff, "UUID"), getStrH(Rd->stuff, "name"), message, log); - Rd->vegOut = TRUE; -} static sesh *newSesh(reqData *Rd, boolean linky) { unsigned char *md5hash = xzalloc(17); @@ -2961,7 +3031,7 @@ static sesh *newSesh(reqData *Rd, boolean linky) uuid_t binuuid; sesh *ret = &Rd->shs; -W("New sesh"); +d("New sesh"); if (linky) { Rd->lnk = xzalloc(sizeof(sesh)); @@ -3044,132 +3114,72 @@ char *checkLinky(reqData *Rd) } -boolean prevalidate(qhashtbl_t *data, char *name) -{ - if ('\0' != getStrH(data, name)[0]) - { - I("Already validated %s.", name); - return TRUE; - } - return FALSE; -} - boolean badBoy(int ret, reqData *Rd, qhashtbl_t *data, char *name, char *value) { if (NULL == value) value = getStrH(data, name); - if (0 != ret) { - char *t = xmprintf("BAD - %s", name); - - Rd->stuff->putstr(Rd->stuff, t, value); - Rd->stuff->remove(data, name); - free(t); + Rd->valid->putint(Rd->valid, name, -1); +W("Bad boy %s = %s", name, value); return TRUE; } - data->putstr(data, name, value); + Rd->stuff->putstr(Rd->stuff, name, value); + Rd->valid->putint(Rd->valid, name, 1); return FALSE; } -char *months[] = -{ - "january", - "february", - "march", - "april", - "may", - "june", - "july", - "august", - "september", - "october", - "november", - "december" -}; - - - -int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, struct stat *st, struct timespec *now, char *type) +int validateThings(reqData *Rd, char *doit, char *name, qhashtbl_t *things) { - struct timespec then; + int e = 0; + qlisttbl_obj_t obj; - if (-1 == clock_gettime(CLOCK_REALTIME, &then)) - perror_msg("Unable to get the time."); - I("Reading %s file %s", type, file); - if (0 != stat(file, st)) - { - bitchSession(Rd, "No such thing.", "No file."); - perror_msg("Unable to stat %s", file); - ret++; - } - else + D("For function %s, start of %s validation.", doit, name); + memset((void *) &obj, 0, sizeof(obj)); + fieldValidFuncs->lock(fieldValidFuncs); + while(fieldValidFuncs->getnext(fieldValidFuncs, &obj, NULL, false) == true) { - int status = luaL_loadfile(Rd->L, file), result; + char *t = things->getstr(things, obj.name, false); - if (status) - { - bitchSession(Rd, "No such thing.", "Can't load file."); - E("Couldn't load file: %s", lua_tostring(Rd->L, -1)); - ret++; - } - else + if (NULL != t) { - result = lua_pcall(Rd->L, 0, LUA_MULTRET, 0); + char *nm = obj.name; + int valid = Rd->valid->getint(Rd->valid, nm); + validFunc *vf = (validFunc *) obj.data; - if (result) + if (0 != valid) // Is it in the valid qhashtbl? { - bitchSession(Rd, "Broken thing.", "Can't run file."); - E("Failed to run script: %s", lua_tostring(Rd->L, -1)); - ret++; + if (0 < valid) // Positive valid means it's valid, negative means it's invald. + D("Already validated %s - %s.", vf->title, nm); + else + D("Already invalidated %s - %s.", vf->title, nm); } else { - lua_getglobal(Rd->L, var); - lua_pushnil(Rd->L); - - while(lua_next(Rd->L, -2) != 0) - { - char *n = (char *) lua_tostring(Rd->L, -2); - - if (lua_isstring(Rd->L, -1)) - { - tnm->putstr(tnm, n, (char *) lua_tostring(Rd->L, -1)); -//d("Reading %s %s", n, getStrH(tnm, n)); - } - else - { - char *v = (char *) lua_tostring(Rd->L, -1); - W("Unknown Lua variable type for %s = %s", n, v); - } - lua_pop(Rd->L, 1); - } - - if (-1 == clock_gettime(CLOCK_REALTIME, now)) - perror_msg("Unable to get the time."); - double n = (now->tv_sec * 1000000000.0) + now->tv_nsec; - double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; - T("Reading %s file took %lf seconds", type, (n - t) / 1000000000.0); + D("Validating %s - %s.", vf->title, nm); + if (vf->func) + e += vf->func(Rd, things, nm); + else + E("No validation function for %s - %s", vf->title, nm); } } } - - return ret; + fieldValidFuncs->unlock(fieldValidFuncs); + return e; } -static int validateSesh(reqData *Rd, qhashtbl_t *data) + +static int validateSesh(reqData *Rd, qhashtbl_t *data, char *name) { int ret = 0; boolean linky = FALSE; if ('\0' != Rd->shs.leaf[0]) { - I("Already validated session."); + d("Already validated session."); return ret; } - I("Validating session."); - char *toke_n_munchie = "", *munchie = "", *hashish = "", *leaf, *timeStamp = "", *seshion = "", *seshID = "", *t0, *t1; @@ -3324,15 +3334,25 @@ W("Validated session linky."); Rd->stuff->putstr(Rd->stuff, "linky-hashish", t0); } } - Rd->stuff->putstr(Rd->stuff, "name", tnm->getstr(tnm, "name", true)); - Rd->stuff->putstr(Rd->stuff, "UUID", tnm->getstr(tnm, "UUID", true)); - Rd->stuff->putstr(Rd->stuff, "level", tnm->getstr(tnm, "level", true)); - Rd->stuff->putstr(Rd->stuff, "passwordSalt", tnm->getstr(tnm, "passwordSalt", true)); - Rd->stuff->putstr(Rd->stuff, "passwordHash", tnm->getstr(tnm, "passwordHash", true)); + + qhashtbl_obj_t obj; + + memset((void*)&obj, 0, sizeof(obj)); + tnm->lock(tnm); + while(tnm->getnext(tnm, &obj, false) == true) + { + char *n = obj.name; + + if ((strcmp("salt", n) != 0) && (strcmp("seshID", n) != 0)) + { +t("Lua %s = %s", n, (char *) obj.data); + Rd->stuff->putstr(Rd->stuff, obj.name, (char *) obj.data); + } + } + tnm->unlock(tnm); Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID", tnm->getstr(tnm, "UUID", true)); } } - } } } @@ -3342,15 +3362,48 @@ W("Validated session linky."); return ret; } -static int validateDoB(reqData *Rd, qhashtbl_t *data) +static int validateAboutMe(reqData *Rd, qhashtbl_t *data, char *name) +{ + int ret = 0; + char *about = getStrH(data, "aboutMe"); + + if ((strcmp("confirm", Rd->doit) != 0) && (strcmp("update", Rd->doit) != 0)) + return ret; + + if ('\0' == about[0]) + { + bitch(Rd, "Please fill in the 'About me' section.", "None supplied."); + ret++; + } + + badBoy(ret, Rd, data, "aboutMe", about); + return ret; +} + + +char *months[] = +{ + "january", + "february", + "march", + "april", + "may", + "june", + "july", + "august", + "september", + "october", + "november", + "december" +}; +static int validateDoB(reqData *Rd, qhashtbl_t *data, char *name) { int ret = 0, i; char *t; - if (prevalidate(Rd->stuff, "year")) return ret; - if (prevalidate(Rd->stuff, "month")) return ret; + if ((strcmp("confirm", Rd->doit) != 0) && (strcmp("update", Rd->doit) != 0)) + return ret; - I("Validating DoB."); t = getStrH(data, "year"); if ((NULL == t) || ('\0' == t[0])) { @@ -3392,19 +3445,15 @@ static int validateDoB(reqData *Rd, qhashtbl_t *data) return ret; } -static int validateEmail(reqData *Rd, qhashtbl_t *data) +static int validateEmail(reqData *Rd, qhashtbl_t *data, char *name) { int ret = 0; char *email = getStrH(data, "email"); char *emayl = getStrH(data, "emayl"); - if ((strcmp("create", Rd->doit) != 0) && (strcmp("update", Rd->doit) != 0)) + if ((strcmp("confirm", Rd->doit) != 0) && (strcmp("update", Rd->doit) != 0)) return ret; - if (prevalidate(Rd->stuff, "email")) return ret; - if (prevalidate(Rd->stuff, "emayl")) return ret; - - I("Validating email."); if ((NULL == email) || (NULL == emayl) || ('\0' == email[0]) || ('\0' == emayl[0])) { bitch(Rd, "Please supply an email address.", "None supplied."); @@ -3425,25 +3474,20 @@ static int validateEmail(reqData *Rd, qhashtbl_t *data) // TODO - do other email checks - does the domain exist, .. } - badBoy(ret, Rd, data, "email", email); - badBoy(ret, Rd, data, "emayl", emayl); + badBoy(ret, Rd, data, "email", NULL); + badBoy(ret, Rd, data, "emayl", NULL); return ret; } -static int validateLegal(reqData *Rd, qhashtbl_t *data) +static int validateLegal(reqData *Rd, qhashtbl_t *data, char *name) { int ret = 0; char *t; - if (prevalidate(Rd->stuff, "adult")) return ret; - if (prevalidate(Rd->stuff, "agree")) return ret; - - - I("Validating legal."); t = getStrH(data, "adult"); if ((NULL == t) || (strcmp("on", t) != 0)) { - bitch(Rd, "You must be an adult to enter this world.", ""); + bitch(Rd, "You must be an adult to enter this site.", ""); ret++; } t = getStrH(data, "agree"); @@ -3458,19 +3502,16 @@ static int validateLegal(reqData *Rd, qhashtbl_t *data) return ret; } -static int validateName(reqData *Rd, qhashtbl_t *data) +static int validateName(reqData *Rd, qhashtbl_t *data, char *nm) { boolean login = strcmp("login", Rd->doit) == 0; int ret = 0; unsigned char *name = data->getstr(data, "name", true); // We have to be unsigned coz of isalnum(). char *where = NULL; - if (strcmp("logout", Rd->doit) == 0) + if ((strcmp("cancel", Rd->doit) == 0) || (strcmp("logout", Rd->doit) == 0)) return ret; - if (prevalidate(Rd->database, "UserAccounts.UserLevel")) return ret; - - I("Validating name."); if ((NULL == name) || ('\0' == name[0])) { bitch(Rd, "Please supply an account name.", "None supplied."); @@ -3557,102 +3598,105 @@ static int validateName(reqData *Rd, qhashtbl_t *data) } dbDoSomething(acnts, FALSE, name, s); rowData *rows = acnts->rows; + int c = 0; + if (rows) - { - int i = rows->rows->size(rows->rows); + c = rows->rows->size(rows->rows); - if (login) + if (login) + { + if ((1 != c) && (rt)) { if (rt) { - bitch(Rd, "Login failed.", "Could not read user Lua file."); - ret += rt; - if (i == 0) - { - bitch(Rd, "Login failed.", "No UserAccounts record with that name."); - ret++; - } - else - { - if (i != 1) - { - bitch(Rd, "Login failed.", "More than one UserAccounts record with that name."); - ret++; - } - else - { - dbPull(Rd, "UserAccounts", rows); - Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(getStrH(Rd->database, "UserAccounts.PrincipalID"))); - Rd->stuff->putstr(Rd->stuff, "level", xstrdup(getStrH(Rd->database, "UserAccounts.Userlevel"))); - if (s) {s--; *s = ' '; s++;} - Rd->stuff->putstr(Rd->stuff, "name", xstrdup(name)); - } - } + W("Could not read user Lua file."); + ret += rt; + } + if (0 == c) + { + W("No UserAccounts record with that name."); + ret++; } else { + W("More than one UserAccounts record with that name."); + ret++; + } + bitch(Rd, "Login failed.", "Could not find user record."); + } + else + { + if (1 == c) + dbPull(Rd, "UserAccounts", rows); + else + { Rd->database->putstr(Rd->database, "UserAccounts.FirstName", name); - Rd->database->putstr(Rd->database, "UserAccounts.LastName", s); - Rd->database->putstr(Rd->database, "UserAccounts.Email", getStrH(tnm, "email")); - Rd->database->putstr(Rd->database, "UserAccounts.Created", getStrH(tnm, "created")); + Rd->database->putstr(Rd->database, "UserAccounts.LastName", s); + Rd->database->putstr(Rd->database, "UserAccounts.Email", getStrH(tnm, "email")); + Rd->database->putstr(Rd->database, "UserAccounts.Created", getStrH(tnm, "created")); Rd->database->putstr(Rd->database, "UserAccounts.PrincipleID", getStrH(tnm, "UUID")); Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", getStrH(tnm, "level")); Rd->database->putstr(Rd->database, "UserAccounts.UserFlags", getStrH(tnm, "flags")); Rd->database->putstr(Rd->database, "UserAccounts.UserTitle", getStrH(tnm, "title")); - Rd->database->putstr(Rd->database, "UserAccounts.active", getStrH(tnm, "active")); + Rd->database->putstr(Rd->database, "UserAccounts.active", getStrH(tnm, "active")); Rd->database->putstr(Rd->database, "auth.passwordSalt", getStrH(tnm, "passwordSalt")); Rd->database->putstr(Rd->database, "auth.passwordHash", getStrH(tnm, "passwordHash")); - - Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(getStrH(Rd->database, "UserAccounts.PrincipalID"))); - Rd->stuff->putstr(Rd->stuff, "level", xstrdup(getStrH(Rd->database, "UserAccounts.Userlevel"))); - if (s) {s--; *s = ' '; s++;} - Rd->stuff->putstr(Rd->stuff, "name", xstrdup(name)); - if (s) {s--; *s = '\0'; s++;} + tnm->free(tnm); } + Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(getStrH(Rd->database, "UserAccounts.PrincipalID"))); + Rd->stuff->putstr(Rd->stuff, "level", xstrdup(getStrH(Rd->database, "UserAccounts.Userlevel"))); + if (s) {s--; *s = ' '; s++;} + Rd->stuff->putstr(Rd->stuff, "name", xstrdup(name)); + if (s) {s--; *s = '\0'; s++;} } - else if (strcmp("create", Rd->doit) == 0) + } + else if (strcmp("create", Rd->doit) == 0) + { + if ((0 != c) && (!rt)) + { + bitch(Rd, "Pick a different name.", "An existing Lua user file or UserAccounts record matched that name."); + ret++; + } + else { - if (i != 0) - { - bitch(Rd, "Pick a different name.", "An existing UserAccounts record matched that name."); - ret++; - } - else - { // TODO - compare first, last, and fullname against god names, complain and fail if there's a match. - { - // Generate a UUID, check it isn't already being used. - char uuid[37]; - uuid_t binuuid; - my_ulonglong users = 0; + // Generate a UUID, check it isn't already being used. + char uuid[37]; + uuid_t binuuid; + my_ulonglong users = 0; - do // UserAccounts.PrincipalID is a unique primary index anyway, but we want the user creation process to be a little on the slow side. - { - uuid_generate_random(binuuid); - uuid_unparse_lower(binuuid, uuid); - where = xmprintf("UserAccounts.PrincipalID = '%s'", uuid); - D("Trying new UUID %s.", where); - users = dbCount(Rd->db, "UserAccounts", where); - free(where); - } while (users != 0); + do // UserAccounts.PrincipalID is a unique primary index anyway, but we want the user creation process to be a little on the slow side. + { + struct stat st; + + uuid_generate_random(binuuid); + uuid_unparse_lower(binuuid, uuid); + // Try Lua user file. + where = xmprintf("%s/users/%s.lua", scData, uuid); + c = stat(where, &st); + if (c) + users = 1; + free(where); + // Try database. + where = xmprintf("UserAccounts.PrincipalID = '%s'", uuid); + D("Trying new UUID %s.", where); + users = dbCount(Rd->db, "UserAccounts", where); + free(where); + } while (users != 0); // TODO - perhaps create a place holder UserAccounts record? Then we'll have to deal with deleting them later. - - Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(uuid)); - Rd->stuff->putstr(Rd->stuff, "level", xstrdup("-200")); - Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID", xstrdup(uuid)); - Rd->database->putstr(Rd->database, "UserAccounts.Userlevel", xstrdup("-200")); - Rd->database->putstr(Rd->database, "UserAccounts.firstName", xstrdup(name)); - Rd->database->putstr(Rd->database, "UserAccounts.lastName", xstrdup(s)); - if (s) {s--; *s = ' '; s++;} - Rd->stuff->putstr(Rd->stuff, "name", xstrdup(name)); - } - } + Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(uuid)); + Rd->stuff->putstr(Rd->stuff, "level", xstrdup("-200")); + Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID",xstrdup(uuid)); + Rd->database->putstr(Rd->database, "UserAccounts.Userlevel", xstrdup("-200")); + Rd->database->putstr(Rd->database, "UserAccounts.firstName", xstrdup(name)); + Rd->database->putstr(Rd->database, "UserAccounts.lastName", xstrdup(s)); + if (s) {s--; *s = ' '; s++;} + Rd->stuff->putstr(Rd->stuff, "name", xstrdup(name)); } - free(rows->rows); - free(rows->fieldNames); - free(rows); } - + free(rows->rows); + free(rows->fieldNames); + free(rows); if (s) {s--; *s = ' '; s++;} } } @@ -3661,7 +3705,7 @@ static int validateName(reqData *Rd, qhashtbl_t *data) return ret; } -static int validatePassword(reqData *Rd, qhashtbl_t *data) +static int validatePassword(reqData *Rd, qhashtbl_t *data, char *name) { boolean login = strcmp("login", Rd->doit) == 0; boolean create = strcmp("create", Rd->doit) == 0; @@ -3670,13 +3714,13 @@ static int validatePassword(reqData *Rd, qhashtbl_t *data) char *psswrdH = getStrH(Rd->stuff, "passwordHash"); char *psswrdS = getStrH(Rd->stuff, "passwordSalt"); - if (prevalidate(Rd->database, "auth.passwordSalt")) return ret; - - I("Validating password."); if (login) { char *UUID = getStrH(Rd->database, "UserAccounts.PrincipalID"); static dbRequest *auth = NULL; + + if ('\0' == UUID[0]) // If the name validation didn't find us a UUID, then this user doesn't exist, so can't log them on. + return 1; if (NULL == auth) { static char *szi[] = {"UUID", NULL}; @@ -3851,16 +3895,19 @@ static int validatePassword(reqData *Rd, qhashtbl_t *data) else D("Account created%s.", password); + badBoy(ret, Rd, data, "auth.passwordSalt", NULL); + return ret; } -static int validateUUID(reqData *Rd, qhashtbl_t *data) +static int validateUUID(reqData *Rd, qhashtbl_t *data, char *name) { int ret = 0, i; char uuid[37], *t; rowData *rows = NULL; static dbRequest *uuids = NULL; + if (NULL == uuids) { static char *szi[] = {"PrincipalID", NULL}; @@ -3873,14 +3920,15 @@ static int validateUUID(reqData *Rd, qhashtbl_t *data) uuids->where = "PrincipalID=?"; } - I("Validating UUID."); + if ((strcmp("cancel", Rd->doit) == 0) || (strcmp("logout", Rd->doit) == 0)) + return ret; + + uuid[0] = '\0'; if (strcmp("create", Rd->doit) == 0) { // Generate a UUID, check it isn't already being used, and totally ignore whatever UUID is in body. uuid_t binuuid; - if (prevalidate(Rd->stuff, "UUID")) return ret; - do // UserAccounts.PrincipalID is a unique primary index anyway, but we want the user creation process to be a little on the slow side. { uuid_generate_random(binuuid); @@ -3903,7 +3951,7 @@ static int validateUUID(reqData *Rd, qhashtbl_t *data) if (0 == ret) { data->putstr(data, "UUID", xstrdup(uuid)); - data->putstr(data, "NEW - UUID", uuid); + Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(uuid)); } rows = NULL; } @@ -3958,41 +4006,13 @@ static int validateUUID(reqData *Rd, qhashtbl_t *data) { Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID", xstrdup(uuid)); } + Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(uuid)); } return ret; } - -int validateThings(reqData *Rd, char *doit, char *name, qhashtbl_t *things) -{ - int e = 0; - - W("%s start of %s validation.", doit, name); - qlisttbl_obj_t obj; - memset((void *) &obj, 0, sizeof(obj)); - fieldValidFuncs->lock(fieldValidFuncs); - while(fieldValidFuncs->getnext(fieldValidFuncs, &obj, NULL, false) == true) - { - char *t = getStrH(things, obj.name); - - if ('\0' != t[0]) - { - validFunc *vf = (validFunc *) obj.data; - -W("Validating %s", obj.name); - if (vf->func) - e += vf->func(Rd, things); - else - E("No validation function for %s", obj.name); - } - } - fieldValidFuncs->unlock(fieldValidFuncs); - return e; -} - - void loginPage(reqData *Rd, char *message) { char *name = xstrdup(getStrH(Rd->stuff, "name")); @@ -4022,11 +4042,14 @@ void loginPage(reqData *Rd, char *message) void accountCreationPage(reqData *Rd, char *message) { - char *name = getStrH(Rd->stuff, "name"); + char *name = getStrH(Rd->body, "name"); char *toke_n_munchie = getCookie(Rd->Rcookies, "toke_n_munchie"); char *tmp = xmalloc(16), *t; int i, d; + if ('\0' == name[0]) + name = getStrH(Rd->stuff, "name"); + // TODO - eww lots of memory leaks here. // TODO - need to check if qlibc does it's own free() calls, and fill in the gaps for when it doesn't. HTMLheader(Rd->reply, " account manager"); @@ -4050,7 +4073,7 @@ void accountCreationPage(reqData *Rd, char *message) Rd->reply->addstr(Rd->reply, "

A validation email will be sent to this email address, you will need to click on the link in it to continue your account creation.

\n"); Rd->reply->addstr(Rd->reply, "
\n"); HTMLselect(Rd->reply, "Date of birth", "year"); - t = getStrH(Rd->stuff, "year"); + t = getStrH(Rd->body, "year"); if (NULL == t) d = -1; else @@ -4068,7 +4091,7 @@ void accountCreationPage(reqData *Rd, char *message) HTMLselectEnd(Rd->reply); Rd->reply->addstr(Rd->reply, ""); HTMLselect(Rd->reply, NULL, "month"); - t = getStrH(Rd->stuff, "month"); + t = getStrH(Rd->body, "month"); HTMLoption(Rd->reply, xstrdup(""), FALSE); for (i = 0; i <= 11; i++) { @@ -4177,17 +4200,18 @@ void account_html(char *file, reqData *Rd, HTMLfile *thisFile) if (NULL == fieldValidFuncs) { fieldValidFuncs = qlisttbl(QLISTTBL_LOOKUPFORWARD | QLISTTBL_THREADSAFE | QLISTTBL_UNIQUE); - newValidFunc("hashish", (fieldValidFunc) validateSesh); - newValidFunc("toke_n_munchie", (fieldValidFunc) validateSesh); - newValidFunc("UUID", (fieldValidFunc) validateUUID); - newValidFunc("name", (fieldValidFunc) validateName); - newValidFunc("password", (fieldValidFunc) validatePassword); - newValidFunc("email", (fieldValidFunc) validateEmail); - newValidFunc("emayl", (fieldValidFunc) validateEmail); - newValidFunc("year", (fieldValidFunc) validateDoB); - newValidFunc("month", (fieldValidFunc) validateDoB); - newValidFunc("adult", (fieldValidFunc) validateLegal); - newValidFunc("agree", (fieldValidFunc) validateLegal); + newValidFunc("hashish", "session", (fieldValidFunc) validateSesh); + newValidFunc("toke_n_munchie", "session", (fieldValidFunc) validateSesh); + newValidFunc("UUID", "UUID", (fieldValidFunc) validateUUID); + newValidFunc("name", "name", (fieldValidFunc) validateName); + newValidFunc("password", "password", (fieldValidFunc) validatePassword); + newValidFunc("email", "email", (fieldValidFunc) validateEmail); + newValidFunc("emayl", "email", (fieldValidFunc) validateEmail); + newValidFunc("year", "DoB", (fieldValidFunc) validateDoB); + newValidFunc("month", "DoB", (fieldValidFunc) validateDoB); + newValidFunc("adult", "legal", (fieldValidFunc) validateLegal); + newValidFunc("agree", "legal", (fieldValidFunc) validateLegal); + newValidFunc("aboutMe", "about me", (fieldValidFunc) validateAboutMe); } if (NULL == buildPages) { @@ -4329,6 +4353,12 @@ void account_html(char *file, reqData *Rd, HTMLfile *thisFile) freeSesh(Rd, FALSE, TRUE); newSesh(Rd, FALSE); } + else if ((0 != e)) // So we can reload details into a broken form, so the user doesn't have to retype everything. + { + freeSesh(Rd, FALSE, FALSE); + newSesh(Rd, FALSE); + } + Rd->func(Rd, ""); } @@ -4726,6 +4756,7 @@ jit library is loaded or the JIT compiler will not be activated. // Rd->body = qhashtbl(0, 0); // Inited in toknize below. // Rd->cookies = qhashtbl(0, 0); // Inited in toknize below. Rd->headers = qhashtbl(0, 0); + Rd->valid = qhashtbl(0, 0); Rd->stuff = qhashtbl(0, 0); Rd->database = qhashtbl(0, 0); Rd->Rcookies = qhashtbl(0, 0); @@ -4983,6 +5014,7 @@ fcgiDone: qhashtbl_free(Rd->Rcookies); qhashtbl_free(Rd->database); qhashtbl_free(Rd->stuff); + qhashtbl_free(Rd->valid); qhashtbl_free(Rd->headers); qhashtbl_free(Rd->cookies); qhashtbl_free(Rd->body); -- cgit v1.1