diff options
author | onefang | 2020-04-19 14:18:53 +1000 |
---|---|---|
committer | onefang | 2020-04-19 14:18:53 +1000 |
commit | 2f09fd0ecb962824297ce001bdbc845c74b8173e (patch) | |
tree | 1097e0572a28e3905df364493df90febfb6f0548 /src | |
parent | TODO++ (diff) | |
download | opensim-SC_OLD-2f09fd0ecb962824297ce001bdbc845c74b8173e.zip opensim-SC_OLD-2f09fd0ecb962824297ce001bdbc845c74b8173e.tar.gz opensim-SC_OLD-2f09fd0ecb962824297ce001bdbc845c74b8173e.tar.bz2 opensim-SC_OLD-2f09fd0ecb962824297ce001bdbc845c74b8173e.tar.xz |
Lots of changes, mostly a rewrite of how the dynamic pages work.
Diffstat (limited to '')
-rw-r--r-- | src/sledjchisl/sledjchisl.c | 2839 |
1 files changed, 1820 insertions, 1019 deletions
diff --git a/src/sledjchisl/sledjchisl.c b/src/sledjchisl/sledjchisl.c index 1c98575..2bc6719 100644 --- a/src/sledjchisl/sledjchisl.c +++ b/src/sledjchisl/sledjchisl.c | |||
@@ -308,7 +308,7 @@ qhashtbl_t *HTMLfileCache = NULL; | |||
308 | 308 | ||
309 | typedef struct _reqData reqData; | 309 | typedef struct _reqData reqData; |
310 | 310 | ||
311 | 311 | /* | |
312 | typedef int (*fieldValidFunc) (reqData *Rd, qhashtbl_t *data, char *name); | 312 | typedef int (*fieldValidFunc) (reqData *Rd, qhashtbl_t *data, char *name); |
313 | typedef struct _validFunc validFunc; | 313 | typedef struct _validFunc validFunc; |
314 | struct _validFunc | 314 | struct _validFunc |
@@ -316,6 +316,7 @@ struct _validFunc | |||
316 | char *name, *title; | 316 | char *name, *title; |
317 | fieldValidFunc func; | 317 | fieldValidFunc func; |
318 | }; | 318 | }; |
319 | |||
319 | qlisttbl_t *fieldValidFuncs = NULL; | 320 | qlisttbl_t *fieldValidFuncs = NULL; |
320 | static void newValidFunc(char *name, char *title, fieldValidFunc func) | 321 | static void newValidFunc(char *name, char *title, fieldValidFunc func) |
321 | { | 322 | { |
@@ -324,6 +325,7 @@ static void newValidFunc(char *name, char *title, fieldValidFunc func) | |||
324 | fieldValidFuncs->put(fieldValidFuncs, vf->name, vf, sizeof(validFunc)); | 325 | fieldValidFuncs->put(fieldValidFuncs, vf->name, vf, sizeof(validFunc)); |
325 | free(vf); | 326 | free(vf); |
326 | } | 327 | } |
328 | */ | ||
327 | 329 | ||
328 | typedef void *(*pageFunction) (char *file, reqData *Rd, HTMLfile *thisFile); | 330 | typedef void *(*pageFunction) (char *file, reqData *Rd, HTMLfile *thisFile); |
329 | typedef struct _dynPage dynPage; | 331 | typedef struct _dynPage dynPage; |
@@ -341,6 +343,7 @@ static void newDynPage(char *name, pageFunction func) | |||
341 | free(dp); | 343 | free(dp); |
342 | } | 344 | } |
343 | 345 | ||
346 | /* | ||
344 | typedef void *(*pageBuildFunction) (reqData *Rd, char *message); | 347 | typedef void *(*pageBuildFunction) (reqData *Rd, char *message); |
345 | typedef struct _buildPage buildPage; | 348 | typedef struct _buildPage buildPage; |
346 | struct _buildPage | 349 | struct _buildPage |
@@ -356,42 +359,6 @@ static void newBuildPage(char *name, pageBuildFunction func, pageBuildFunction e | |||
356 | buildPages->put(buildPages, bp->name, bp, sizeof(buildPage)); | 359 | buildPages->put(buildPages, bp->name, bp, sizeof(buildPage)); |
357 | free(bp); | 360 | free(bp); |
358 | } | 361 | } |
359 | |||
360 | |||
361 | /* TODO - there should be some precedence for values overriding values here. | ||
362 | Nothing official? | ||
363 | https://www.w3.org/standards/webarch/protocols | ||
364 | "This intro text is boilerplate for the beta release of w3.org." Fucking useless. Pffft | ||
365 | https://www.w3.org/Protocols/ | ||
366 | Still nothing official, though the ENV / HEADER stuff tends to be about the protocol things, and cookies / body / queries are about the data things. | ||
367 | |||
368 | TODO - I think this is the wrong question, mostly data from different sources is for different reasons. | ||
369 | |||
370 | Also including values from the database. | ||
371 | |||
372 | URL query Values actually provided by the user in the FORM, and other things. | ||
373 | POST body Values actually provided by the user in the FORM. | ||
374 | cookies | ||
375 | https://stackoverflow.com/questions/4056306/how-to-handle-multiple-cookies-with-the-same-name | ||
376 | |||
377 | headers includes HTTP_COOKIE and QUERY_STRING | ||
378 | env includes headers and HTTP_COOKIE and QUERY_STRING | ||
379 | |||
380 | database Since all of the above are for updating the database anyway, this goes on the bottom, overridden by all. | ||
381 | Though be wary of security stuff. | ||
382 | |||
383 | We don't actually get the headers directly, it's all sent via the env. | ||
384 | |||
385 | http://docs.gantry.org/gantry4/advanced/setby | ||
386 | Says that query overrides cookies, but that might be just for their platform. | ||
387 | |||
388 | https://framework.zend.com/manual/1.11/en/zend.controller.request.html | ||
389 | Says - "1. GET, 2. POST, 3. COOKIE, 4. SERVER, 5. ENV." | ||
390 | |||
391 | |||
392 | Sending cookie headers is a special case, multiples can be sent, otherwise headers are singletons, only send one for each name. | ||
393 | |||
394 | local storage? Would be client side Javascript thing not usually sent back to server. | ||
395 | */ | 362 | */ |
396 | 363 | ||
397 | #define HMACSIZE EVP_MAX_MD_SIZE * 2 | 364 | #define HMACSIZE EVP_MAX_MD_SIZE * 2 |
@@ -401,8 +368,9 @@ struct _sesh | |||
401 | { | 368 | { |
402 | char salt[256 + 1], seshID[256 + 1], | 369 | char salt[256 + 1], seshID[256 + 1], |
403 | sesh[256 + 16 + 10 + 1], munchie[HMACSIZE + 16 + 10 + 1], toke_n_munchie[HMACSIZE + 1], hashish[HMACSIZE + 1], | 370 | sesh[256 + 16 + 10 + 1], munchie[HMACSIZE + 16 + 10 + 1], toke_n_munchie[HMACSIZE + 1], hashish[HMACSIZE + 1], |
404 | leaf[HMACSIZE64 + 6 + 1]; | 371 | leaf[HMACSIZE64 + 6 + 1], *UUID, *name; |
405 | struct timespec timeStamp[2]; | 372 | struct timespec timeStamp[2]; |
373 | short level; | ||
406 | boolean isLinky; | 374 | boolean isLinky; |
407 | }; | 375 | }; |
408 | 376 | ||
@@ -410,13 +378,13 @@ struct _reqData | |||
410 | { | 378 | { |
411 | lua_State *L; | 379 | lua_State *L; |
412 | qhashtbl_t *configs, *queries, *body, *cookies, *headers, *valid, *stuff, *database, *Rcookies, *Rheaders; | 380 | qhashtbl_t *configs, *queries, *body, *cookies, *headers, *valid, *stuff, *database, *Rcookies, *Rheaders; |
413 | char *Scheme, *Host, *Method, *Script, *RUri, *doit; | 381 | char *Scheme, *Host, *Method, *Script, *RUri, *doit, *form, *output; |
414 | sesh shs, *lnk; | 382 | sesh shs, *lnk; |
415 | MYSQL *db; | 383 | MYSQL *db; |
416 | gridStats *stats; | 384 | gridStats *stats; |
417 | qlist_t *errors, *messages; | 385 | qlist_t *errors, *messages; |
418 | qgrow_t *reply; | 386 | qgrow_t *reply; |
419 | pageBuildFunction func; | 387 | // pageBuildFunction func; |
420 | struct timespec then; | 388 | struct timespec then; |
421 | boolean chillOut, vegOut; | 389 | boolean chillOut, vegOut; |
422 | }; | 390 | }; |
@@ -428,14 +396,19 @@ static void showSesh(qgrow_t *reply, sesh *shs) | |||
428 | else | 396 | else |
429 | reply->addstrf(reply, "Session:<br>\n<pre>\n"); | 397 | reply->addstrf(reply, "Session:<br>\n<pre>\n"); |
430 | 398 | ||
431 | reply->addstrf(reply, " salt = %s\n", shs->salt); | 399 | if (NULL != shs->name) |
432 | reply->addstrf(reply, " seshID = %s\n", shs->seshID); | 400 | reply->addstrf(reply, " name = %s\n", shs->name); |
433 | reply->addstrf(reply, " timeStamp = %ld.%ld\n", shs->timeStamp[1].tv_sec, shs->timeStamp[1].tv_nsec); | 401 | if (NULL != shs->UUID) |
434 | reply->addstrf(reply, " sesh = %s\n", shs->sesh); | 402 | reply->addstrf(reply, " UUID = %s\n", shs->UUID); |
435 | reply->addstrf(reply, " munchie = %s\n", shs->munchie); | 403 | reply->addstrf(reply, " salt = %s\n", shs->salt); |
436 | reply->addstrf(reply, " toke_n_munchie = %s\n", shs->toke_n_munchie); | 404 | reply->addstrf(reply, " seshID = %s\n", shs->seshID); |
437 | reply->addstrf(reply, " hashish = %s\n", shs->hashish); | 405 | reply->addstrf(reply, " timeStamp = %ld.%ld\n", shs->timeStamp[1].tv_sec, shs->timeStamp[1].tv_nsec); |
438 | reply->addstrf(reply, " leaf = %s\n", shs->leaf); | 406 | reply->addstrf(reply, " sesh = %s\n", shs->sesh); |
407 | reply->addstrf(reply, " munchie = %s\n", shs->munchie); | ||
408 | reply->addstrf(reply, " toke_n_munchie = %s\n", shs->toke_n_munchie); | ||
409 | reply->addstrf(reply, " hashish = %s\n", shs->hashish); | ||
410 | reply->addstrf(reply, " leaf = %s\n", shs->leaf); | ||
411 | reply->addstrf(reply, " level = %d\n", (int) shs->level); | ||
439 | reply->addstr(reply, "</pre>\n"); | 412 | reply->addstr(reply, "</pre>\n"); |
440 | } | 413 | } |
441 | 414 | ||
@@ -843,7 +816,7 @@ static void PrintEnv(qgrow_t *reply, char *label, char **envp) | |||
843 | static void printEnv(char **envp) | 816 | static void printEnv(char **envp) |
844 | { | 817 | { |
845 | for ( ; *envp != NULL; envp++) | 818 | for ( ; *envp != NULL; envp++) |
846 | d("%s", *envp); | 819 | D("%s", *envp); |
847 | } | 820 | } |
848 | 821 | ||
849 | 822 | ||
@@ -905,6 +878,49 @@ I suspect most will be of the form - | |||
905 | UPDATE items,month SET items.price=month.price WHERE items.id=month.id; | 878 | UPDATE items,month SET items.price=month.price WHERE items.id=month.id; |
906 | */ | 879 | */ |
907 | 880 | ||
881 | static boolean dbConnect() | ||
882 | { | ||
883 | dbconn = mysql_real_connect(database, | ||
884 | getStrH(configs, "Data Source"), | ||
885 | getStrH(configs, "User ID"), | ||
886 | getStrH(configs, "Password"), | ||
887 | getStrH(configs, "Database"), | ||
888 | // 3036, "/var/run/mysqld/mysqld.sock", | ||
889 | 0, NULL, | ||
890 | CLIENT_FOUND_ROWS | CLIENT_LOCAL_FILES | CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS); | ||
891 | if (NULL == dbconn) | ||
892 | { | ||
893 | E("mysql_real_connect() failed - %s", mysql_error(database)); | ||
894 | return FALSE; | ||
895 | } | ||
896 | return TRUE; | ||
897 | } | ||
898 | |||
899 | // A general error function that checks for certain errors that mean we should try to connect to the server MariaDB again. | ||
900 | // https://mariadb.com/kb/en/mariadb-error-codes/ | ||
901 | // 1129? 1152? 1184? 1218? 1927 3032? 4150? | ||
902 | // "server has gone away" isn't listed there, that's the one I was getting. Pffft | ||
903 | // It's 2006, https://dev.mysql.com/doc/refman/8.0/en/gone-away.html | ||
904 | // Though none of the mentioned reasons make sense here. | ||
905 | // Ah it could be "connection inactive for 8 hours". | ||
906 | // Which might be why OpenSim opens a new connection for EVERYTHING. | ||
907 | // TODO - see if I can either find out what the time out is, or just check and re open for each db thing. | ||
908 | // int mysql_ping(MYSQL * mysql); // https://mariadb.com/kb/en/mysql_ping/ | ||
909 | // "If it has gone down, and global option reconnect is enabled an automatic reconnection is attempted." | ||
910 | // "Returns zero on success, nonzero if an error occured." | ||
911 | // "resources bundled to the connection (prepared statements, locks, temporary tables, ...) will be released." sigh | ||
912 | // Quick'n'dirty until this is properly event driven - have a cron job curl the stats page every hour. | ||
913 | static boolean dbCheckError(MYSQL *db, char *error, char *sql) | ||
914 | { | ||
915 | int e = mysql_errno(db); | ||
916 | |||
917 | E("MariaDB error %d - %s: %s\n%s", e, error, mysql_error(db), sql); | ||
918 | if (2006 == e) | ||
919 | return dbConnect(); | ||
920 | |||
921 | return FALSE; | ||
922 | } | ||
923 | |||
908 | typedef struct _dbField dbField; | 924 | typedef struct _dbField dbField; |
909 | struct _dbField | 925 | struct _dbField |
910 | { | 926 | { |
@@ -929,19 +945,27 @@ qlisttbl_t *dbGetFields(MYSQL *db, char *table) | |||
929 | 945 | ||
930 | d("Getting field metadata for %s", table); | 946 | d("Getting field metadata for %s", table); |
931 | if (mysql_query(db, sql)) | 947 | if (mysql_query(db, sql)) |
932 | E("Query failed: %s\n%s", mysql_error(db), sql); | 948 | { |
949 | // E("MariaDB error %d - Query failed 0: %s\n%s", mysql_errno(db), mysql_error(db), sql); | ||
950 | if (dbCheckError(db, "Query failed 0", sql)) | ||
951 | { | ||
952 | ret = dbGetFields(db, table); | ||
953 | free(sql); | ||
954 | return ret; | ||
955 | } | ||
956 | } | ||
933 | else | 957 | else |
934 | { | 958 | { |
935 | MYSQL_RES *res = mysql_store_result(db); | 959 | MYSQL_RES *res = mysql_store_result(db); |
936 | 960 | ||
937 | if (!res) | 961 | if (!res) |
938 | E("Couldn't get results set from %s\n %s", mysql_error(db), sql); | 962 | E("MariaDB error %d - Couldn't get results set from %s\n %s", mysql_errno(db), mysql_error(db), sql); |
939 | else | 963 | else |
940 | { | 964 | { |
941 | MYSQL_FIELD *fields = mysql_fetch_fields(res); | 965 | MYSQL_FIELD *fields = mysql_fetch_fields(res); |
942 | 966 | ||
943 | if (!fields) | 967 | if (!fields) |
944 | E("Failed fetching fields: %s", mysql_error(db)); | 968 | E("MariaDB error %d - Failed fetching fields: %s", mysql_errno(db), mysql_error(db)); |
945 | else | 969 | else |
946 | { | 970 | { |
947 | unsigned int i, num_fields = mysql_num_fields(res); | 971 | unsigned int i, num_fields = mysql_num_fields(res); |
@@ -1080,6 +1104,7 @@ d("New SQL statement - %s", req->sql); | |||
1080 | goto freeIt; | 1104 | goto freeIt; |
1081 | } | 1105 | } |
1082 | req->inBind = xzalloc(i * sizeof(MYSQL_BIND)); | 1106 | req->inBind = xzalloc(i * sizeof(MYSQL_BIND)); |
1107 | W("Allocated %d %d inBinds for %s", i, req->inCount, req->sql); | ||
1083 | for (i = 0; i < req->inCount; i++) | 1108 | for (i = 0; i < req->inCount; i++) |
1084 | { | 1109 | { |
1085 | dbField *fld = req->flds->get(req->flds, req->inParams[i], NULL, false); | 1110 | dbField *fld = req->flds->get(req->flds, req->inParams[i], NULL, false); |
@@ -1185,7 +1210,7 @@ d("New SQL statement - %s", req->sql); | |||
1185 | prepare_meta_result = mysql_stmt_result_metadata(req->prep); | 1210 | prepare_meta_result = mysql_stmt_result_metadata(req->prep); |
1186 | if (!prepare_meta_result) | 1211 | if (!prepare_meta_result) |
1187 | { | 1212 | { |
1188 | D(" mysql_stmt_result_metadata(), returned no meta information - %s\n", mysql_stmt_error(req->prep)); | 1213 | D(" mysql_stmt_result_metadata() error %d, returned no meta information - %s\n", mysql_stmt_errno(req->prep), mysql_stmt_error(req->prep)); |
1189 | goto freeIt; | 1214 | goto freeIt; |
1190 | } | 1215 | } |
1191 | 1216 | ||
@@ -1221,6 +1246,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1221 | goto freeIt; | 1246 | goto freeIt; |
1222 | } | 1247 | } |
1223 | req->outBind = xzalloc(i * sizeof(MYSQL_BIND)); | 1248 | req->outBind = xzalloc(i * sizeof(MYSQL_BIND)); |
1249 | W("Allocated %d %d outBinds for %s", i, req->outCount, req->sql); | ||
1224 | for (i = 0; i < req->outCount; i++) | 1250 | for (i = 0; i < req->outCount; i++) |
1225 | { | 1251 | { |
1226 | dbField *fld = req->flds->get(req->flds, req->outParams[i], NULL, false); | 1252 | dbField *fld = req->flds->get(req->flds, req->outParams[i], NULL, false); |
@@ -1335,7 +1361,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1335 | } | 1361 | } |
1336 | if (mysql_stmt_bind_result(req->prep, req->outBind)) | 1362 | if (mysql_stmt_bind_result(req->prep, req->outBind)) |
1337 | { | 1363 | { |
1338 | E("Bind failed."); | 1364 | E("Bind failed error %d.", mysql_stmt_errno(req->prep)); |
1339 | goto freeIt; | 1365 | goto freeIt; |
1340 | } | 1366 | } |
1341 | } | 1367 | } |
@@ -1467,7 +1493,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1467 | } | 1493 | } |
1468 | if (mysql_stmt_bind_param(req->prep, req->inBind)) | 1494 | if (mysql_stmt_bind_param(req->prep, req->inBind)) |
1469 | { | 1495 | { |
1470 | E("Bind failed."); | 1496 | E("Bind failed error %d.", mysql_stmt_errno(req->prep)); |
1471 | goto freeIt; | 1497 | goto freeIt; |
1472 | } | 1498 | } |
1473 | 1499 | ||
@@ -1477,7 +1503,7 @@ d("Execute %s", req->sql); | |||
1477 | // do the prepared statement req->prep. | 1503 | // do the prepared statement req->prep. |
1478 | if (mysql_stmt_execute(req->prep)) | 1504 | if (mysql_stmt_execute(req->prep)) |
1479 | { | 1505 | { |
1480 | E("Statement execute failed: %s\n", mysql_stmt_error(req->prep)); | 1506 | E("Statement execute failed %d: %s\n", mysql_stmt_errno(req->prep), mysql_stmt_error(req->prep)); |
1481 | goto freeIt; | 1507 | goto freeIt; |
1482 | } | 1508 | } |
1483 | 1509 | ||
@@ -1489,7 +1515,7 @@ d("Execute %s", req->sql); | |||
1489 | req->rows->fieldNames = xzalloc(fs * sizeof(char *)); | 1515 | req->rows->fieldNames = xzalloc(fs * sizeof(char *)); |
1490 | if (mysql_stmt_store_result(req->prep)) | 1516 | if (mysql_stmt_store_result(req->prep)) |
1491 | { | 1517 | { |
1492 | E(" mysql_stmt_store_result() failed %s", mysql_stmt_error(req->prep)); | 1518 | E(" mysql_stmt_store_result() failed %d: %s", mysql_stmt_errno(req->prep), mysql_stmt_error(req->prep)); |
1493 | goto freeIt; | 1519 | goto freeIt; |
1494 | } | 1520 | } |
1495 | req->rowCount = mysql_stmt_num_rows(req->prep); | 1521 | req->rowCount = mysql_stmt_num_rows(req->prep); |
@@ -1618,7 +1644,7 @@ freeIt: | |||
1618 | if (prepare_meta_result) | 1644 | if (prepare_meta_result) |
1619 | mysql_free_result(prepare_meta_result); | 1645 | mysql_free_result(prepare_meta_result); |
1620 | if (mysql_stmt_free_result(req->prep)) | 1646 | if (mysql_stmt_free_result(req->prep)) |
1621 | E("Statement result freeing failed: %s\n", mysql_stmt_error(req->prep)); | 1647 | E("Statement result freeing failed %d: %s\n", mysql_stmt_errno(req->prep), mysql_stmt_error(req->prep)); |
1622 | 1648 | ||
1623 | end: | 1649 | end: |
1624 | va_end(ap); | 1650 | va_end(ap); |
@@ -1643,6 +1669,7 @@ void dbPull(reqData *Rd, char *table, rowData *rows) | |||
1643 | while(me->getnext(me, &obj, false) == true) | 1669 | while(me->getnext(me, &obj, false) == true) |
1644 | { | 1670 | { |
1645 | where = xmprintf("%s.%s", table, obj.name); | 1671 | where = xmprintf("%s.%s", table, obj.name); |
1672 | d("dbPull(Rd->database) %s = %s", where, (char *) obj.data); | ||
1646 | Rd->database->putstr(Rd->database, where, (char *) obj.data); | 1673 | Rd->database->putstr(Rd->database, where, (char *) obj.data); |
1647 | // me->remove(me, obj.name); | 1674 | // me->remove(me, obj.name); |
1648 | free(where); | 1675 | free(where); |
@@ -1655,11 +1682,13 @@ void dbFreeRequest(dbRequest *req) | |||
1655 | { | 1682 | { |
1656 | int i; | 1683 | int i; |
1657 | 1684 | ||
1658 | D("Cleaning up prepared database request %s - %s", req->table, req->where); | 1685 | D("Cleaning up prepared database request %s - %s %d %d", req->table, req->where, req->outCount, req->inCount); |
1659 | if (NULL != req->outBind) | 1686 | if (NULL != req->outBind) |
1660 | { | 1687 | { |
1688 | d("Free outBind"); | ||
1661 | for (i = 0; i < req->outCount; i++) | 1689 | for (i = 0; i < req->outCount; i++) |
1662 | { | 1690 | { |
1691 | d("Free outBind %d %s", i, req->sql); | ||
1663 | if (NULL != req->outBind[i].buffer) free(req->outBind[i].buffer); | 1692 | if (NULL != req->outBind[i].buffer) free(req->outBind[i].buffer); |
1664 | if (NULL != req->outBind[i].length) free(req->outBind[i].length); | 1693 | if (NULL != req->outBind[i].length) free(req->outBind[i].length); |
1665 | if (NULL != req->outBind[i].error) free(req->outBind[i].error); | 1694 | if (NULL != req->outBind[i].error) free(req->outBind[i].error); |
@@ -1669,8 +1698,10 @@ void dbFreeRequest(dbRequest *req) | |||
1669 | } | 1698 | } |
1670 | if (NULL != req->inBind) | 1699 | if (NULL != req->inBind) |
1671 | { | 1700 | { |
1701 | d("Free inBind"); | ||
1672 | for (i = 0; i < req->inCount; i++) | 1702 | for (i = 0; i < req->inCount; i++) |
1673 | { | 1703 | { |
1704 | d("Free inBind %d %s", i, req->sql); | ||
1674 | // TODO - this leaks for some bizare reason. | 1705 | // TODO - this leaks for some bizare reason. |
1675 | if (NULL != req->inBind[i].buffer) free(req->inBind[i].buffer); | 1706 | if (NULL != req->inBind[i].buffer) free(req->inBind[i].buffer); |
1676 | if (NULL != req->inBind[i].length) free(req->inBind[i].length); | 1707 | if (NULL != req->inBind[i].length) free(req->inBind[i].length); |
@@ -1705,7 +1736,15 @@ my_ulonglong dbCount(MYSQL *db, char *table, char *where) | |||
1705 | sql = xmprintf("SELECT Count(*) FROM %s", table); | 1736 | sql = xmprintf("SELECT Count(*) FROM %s", table); |
1706 | 1737 | ||
1707 | if (mysql_query(db, sql)) | 1738 | if (mysql_query(db, sql)) |
1708 | E("Query failed: %s", mysql_error(db)); | 1739 | { |
1740 | // E("MariaDB error %d - Query failed 1: %s", mysql_errno(db), mysql_error(db)); | ||
1741 | if (dbCheckError(db, "Query failed 1", sql)) | ||
1742 | { | ||
1743 | ret = dbCount(db, table, where); | ||
1744 | free(sql); | ||
1745 | return ret; | ||
1746 | } | ||
1747 | } | ||
1709 | else | 1748 | else |
1710 | { | 1749 | { |
1711 | MYSQL_RES *result = mysql_store_result(db); | 1750 | MYSQL_RES *result = mysql_store_result(db); |
@@ -1716,7 +1755,7 @@ my_ulonglong dbCount(MYSQL *db, char *table, char *where) | |||
1716 | { | 1755 | { |
1717 | MYSQL_ROW row = mysql_fetch_row(result); | 1756 | MYSQL_ROW row = mysql_fetch_row(result); |
1718 | if (!row) | 1757 | if (!row) |
1719 | E("Couldn't get row from %s\n: %s", sql, mysql_error(db)); | 1758 | E("MariaDB error %d - Couldn't get row from %s\n: %s", mysql_errno(db), sql, mysql_error(db)); |
1720 | else | 1759 | else |
1721 | ret = atoll(row[0]); | 1760 | ret = atoll(row[0]); |
1722 | mysql_free_result(result); | 1761 | mysql_free_result(result); |
@@ -1752,13 +1791,21 @@ my_ulonglong dbCountJoin(MYSQL *db, char *table, char *select, char *join, char | |||
1752 | sql = xmprintf("SELECT %s FROM %s", select, table, join); | 1791 | sql = xmprintf("SELECT %s FROM %s", select, table, join); |
1753 | 1792 | ||
1754 | if (mysql_query(db, sql)) | 1793 | if (mysql_query(db, sql)) |
1755 | E("Query failed: %s", mysql_error(db)); | 1794 | { |
1795 | // E("MariaDB error %d - Query failed 2: %s", mysql_errno(db), mysql_error(db)); | ||
1796 | if (dbCheckError(db, "Query failed 2", sql)) | ||
1797 | { | ||
1798 | ret = dbCountJoin(db, table, select, join, where); | ||
1799 | free(sql); | ||
1800 | return ret; | ||
1801 | } | ||
1802 | } | ||
1756 | else | 1803 | else |
1757 | { | 1804 | { |
1758 | MYSQL_RES *result = mysql_store_result(db); | 1805 | MYSQL_RES *result = mysql_store_result(db); |
1759 | 1806 | ||
1760 | if (!result) | 1807 | if (!result) |
1761 | E("Couldn't get results set from %s\n: %s", sql, mysql_error(db)); | 1808 | E("MariaDB error %d - Couldn't get results set from %s\n: %s", mysql_errno(db), sql, mysql_error(db)); |
1762 | else | 1809 | else |
1763 | ret = mysql_num_rows(result); | 1810 | ret = mysql_num_rows(result); |
1764 | mysql_free_result(result); | 1811 | mysql_free_result(result); |
@@ -1801,12 +1848,12 @@ MYSQL_RES *dbSelect(MYSQL *db, char *table, char *select, char *join, char *wher | |||
1801 | } | 1848 | } |
1802 | 1849 | ||
1803 | if (mysql_query(db, sql)) | 1850 | if (mysql_query(db, sql)) |
1804 | E("Query failed: %s\n%s", mysql_error(db), sql); | 1851 | E("MariaDB error %d - Query failed 3: %s\n%s", mysql_errno(db), mysql_error(db), sql); |
1805 | else | 1852 | else |
1806 | { | 1853 | { |
1807 | ret = mysql_store_result(db); | 1854 | ret = mysql_store_result(db); |
1808 | if (!ret) | 1855 | if (!ret) |
1809 | E("Couldn't get results set from %s\n %s", mysql_error(db), sql); | 1856 | E("MariaDB error %d - Couldn't get results set from %s\n %s", mysql_errno(db), mysql_error(db), sql); |
1810 | } | 1857 | } |
1811 | 1858 | ||
1812 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) | 1859 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) |
@@ -2040,6 +2087,14 @@ void santize(qhashtbl_t *tbl, bool decode) | |||
2040 | tbl->unlock(tbl); | 2087 | tbl->unlock(tbl); |
2041 | } | 2088 | } |
2042 | 2089 | ||
2090 | /* | ||
2091 | char *unsantize(char *str) | ||
2092 | { | ||
2093 | char *ret = qurl_decode(xstrdup(str)); | ||
2094 | return ret; | ||
2095 | } | ||
2096 | */ | ||
2097 | |||
2043 | void outize(qgrow_t *reply, qhashtbl_t *tbl, char *label) | 2098 | void outize(qgrow_t *reply, qhashtbl_t *tbl, char *label) |
2044 | { | 2099 | { |
2045 | reply->addstrf(reply, "%s:<br>\n<pre>\n", label); | 2100 | reply->addstrf(reply, "%s:<br>\n<pre>\n", label); |
@@ -2052,6 +2107,42 @@ void outize(qgrow_t *reply, qhashtbl_t *tbl, char *label) | |||
2052 | reply->addstr(reply, "</pre>\n"); | 2107 | reply->addstr(reply, "</pre>\n"); |
2053 | } | 2108 | } |
2054 | 2109 | ||
2110 | char *displayPrep(char *str) | ||
2111 | { | ||
2112 | char *ret = xstrdup(str), *t; | ||
2113 | |||
2114 | qurl_decode(ret); | ||
2115 | t = qstrreplace("tn", ret, "<", "<"); | ||
2116 | free(ret); | ||
2117 | ret = NULL; | ||
2118 | if (NULL != t) | ||
2119 | { | ||
2120 | ret = qstrreplace("tn", t, ">", ">"); | ||
2121 | if (NULL == ret) | ||
2122 | ret = t; | ||
2123 | else | ||
2124 | free(t); | ||
2125 | } | ||
2126 | |||
2127 | if (NULL == ret) | ||
2128 | ret = xstrdup(str); | ||
2129 | |||
2130 | return ret; | ||
2131 | } | ||
2132 | |||
2133 | char *encodeSlash(char *str) | ||
2134 | { | ||
2135 | char *ret = xstrdup(str), *t = qstrreplace("tn", str, "\\", "%5c"); | ||
2136 | |||
2137 | if (NULL != t) | ||
2138 | { | ||
2139 | free(ret); | ||
2140 | ret = t; | ||
2141 | } | ||
2142 | |||
2143 | return ret; | ||
2144 | } | ||
2145 | |||
2055 | 2146 | ||
2056 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie | 2147 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie |
2057 | enum cookieSame | 2148 | enum cookieSame |
@@ -2143,6 +2234,21 @@ struct _fragment | |||
2143 | char *text; | 2234 | char *text; |
2144 | }; | 2235 | }; |
2145 | 2236 | ||
2237 | static void HTMLdebug(qgrow_t *reply) | ||
2238 | { | ||
2239 | reply->addstrf(reply, | ||
2240 | " <p class='hoverItem'>\n" | ||
2241 | " <div class='hoverWrapper0'>\n" | ||
2242 | " <p>DEBUG</p>\n" | ||
2243 | " <div id='hoverShow0'>\n" | ||
2244 | " <h1>DEBUG log</h1>\n" | ||
2245 | " <!--#echo var=\"DEBUG\" -->\n" | ||
2246 | " </div>\n" | ||
2247 | " </div>\n" | ||
2248 | " </p>\n" | ||
2249 | ); | ||
2250 | } | ||
2251 | |||
2146 | static void HTMLheader(qgrow_t *reply, char *title) | 2252 | static void HTMLheader(qgrow_t *reply, char *title) |
2147 | { | 2253 | { |
2148 | reply->addstrf(reply, | 2254 | reply->addstrf(reply, |
@@ -2152,6 +2258,7 @@ static void HTMLheader(qgrow_t *reply, char *title) | |||
2152 | " <meta charset=\"UTF-8\">\n" | 2258 | " <meta charset=\"UTF-8\">\n" |
2153 | " <link rel=\"shortcut icon\" href=\"/SledjHamrIconSmall.png\">\n" | 2259 | " <link rel=\"shortcut icon\" href=\"/SledjHamrIconSmall.png\">\n" |
2154 | , title); | 2260 | , title); |
2261 | reply->addstrf(reply, " <link type='text/css' rel='stylesheet' href='/SledjChisl.css' media='all' />\n"); | ||
2155 | 2262 | ||
2156 | if (DEBUG) | 2263 | if (DEBUG) |
2157 | reply->addstrf(reply, " <link type='text/css' rel='stylesheet' href='/debugStyle.css' media='all' />\n"); | 2264 | reply->addstrf(reply, " <link type='text/css' rel='stylesheet' href='/debugStyle.css' media='all' />\n"); |
@@ -2172,25 +2279,11 @@ static void HTMLheader(qgrow_t *reply, char *title) | |||
2172 | " </style>\n" | 2279 | " </style>\n" |
2173 | " </head>\n" | 2280 | " </head>\n" |
2174 | " <body bgcolor='black' text='white' link='aqua' vlink='fuchsia' alink='red'>\n" | 2281 | " <body bgcolor='black' text='white' link='aqua' vlink='fuchsia' alink='red'>\n" |
2175 | " <font face='sans-serif'>" | 2282 | " <font face='sans-serif'>\n" |
2176 | ); | 2283 | ); |
2177 | } | 2284 | reply->addstrf(reply, " <div class='top-left'>\n"); |
2178 | 2285 | if (DEBUG) | |
2179 | static void HTMLdebug(qgrow_t *reply) | 2286 | HTMLdebug(reply); |
2180 | { | ||
2181 | reply->addstrf(reply, | ||
2182 | " <div class='top-left'>\n" | ||
2183 | " <p class='hoverItem'>\n" | ||
2184 | " <div class='hoverWrapper0'>\n" | ||
2185 | " <p>DEBUG</p>\n" | ||
2186 | " <div id='hoverShow0'>\n" | ||
2187 | " <h1>DEBUG log</h1>\n" | ||
2188 | " <!--#echo var=\"DEBUG\" -->" | ||
2189 | " </div>\n" | ||
2190 | " </div>\n" | ||
2191 | " </p>\n" | ||
2192 | " </div>\n" | ||
2193 | ); | ||
2194 | } | 2287 | } |
2195 | 2288 | ||
2196 | static void HTMLtable(qgrow_t *reply, MYSQL *db, MYSQL_RES *result, char *caption, char *URL, char *id) | 2289 | static void HTMLtable(qgrow_t *reply, MYSQL *db, MYSQL_RES *result, char *caption, char *URL, char *id) |
@@ -2259,7 +2352,8 @@ static void HTMLtable(qgrow_t *reply, MYSQL *db, MYSQL_RES *result, char *captio | |||
2259 | 2352 | ||
2260 | static void HTMLhidden(qgrow_t *reply, char *name, char *val) | 2353 | static void HTMLhidden(qgrow_t *reply, char *name, char *val) |
2261 | { | 2354 | { |
2262 | reply->addstrf(reply, " <input type=\"hidden\" name=\"%s\" value=\"%s\">\n", name, val); | 2355 | if ((NULL != val) && ("" != val)) |
2356 | reply->addstrf(reply, " <input type=\"hidden\" name=\"%s\" value=\"%s\">\n", name, val); | ||
2263 | } | 2357 | } |
2264 | 2358 | ||
2265 | static void HTMLform(qgrow_t *reply, char *action, char *token) | 2359 | static void HTMLform(qgrow_t *reply, char *action, char *token) |
@@ -2270,7 +2364,7 @@ static void HTMLform(qgrow_t *reply, char *action, char *token) | |||
2270 | } | 2364 | } |
2271 | static void HTMLformEnd(qgrow_t *reply) | 2365 | static void HTMLformEnd(qgrow_t *reply) |
2272 | { | 2366 | { |
2273 | reply->addstr(reply, " </form>\n"); | 2367 | reply->addstrf(reply, " </form>\n"); |
2274 | } | 2368 | } |
2275 | 2369 | ||
2276 | static void HTMLcheckBox(qgrow_t *reply, char *name, char *title, boolean checked, boolean required) | 2370 | static void HTMLcheckBox(qgrow_t *reply, char *name, char *title, boolean checked, boolean required) |
@@ -2288,7 +2382,7 @@ static void HTMLcheckBox(qgrow_t *reply, char *name, char *title, boolean checke | |||
2288 | reply->addstrf(reply, "</p>\n"); | 2382 | reply->addstrf(reply, "</p>\n"); |
2289 | } | 2383 | } |
2290 | 2384 | ||
2291 | static void HTMLtextArea(qgrow_t *reply, char *name, char *title, int rows, int cols, int min, int max, char *holder, char *comp, char *spell, char *wrap, char *value, boolean required) | 2385 | static void HTMLtextArea(qgrow_t *reply, char *name, char *title, int rows, int cols, int min, int max, char *holder, char *comp, char *spell, char *wrap, char *val, boolean required, boolean readOnly) |
2292 | { | 2386 | { |
2293 | reply->addstrf(reply, " <p><label>%s : <textarea name=\"%s\"", title, name); | 2387 | reply->addstrf(reply, " <p><label>%s : <textarea name=\"%s\"", title, name); |
2294 | if (0 < rows) | 2388 | if (0 < rows) |
@@ -2301,6 +2395,8 @@ static void HTMLtextArea(qgrow_t *reply, char *name, char *title, int rows, int | |||
2301 | reply->addstrf(reply, " maxlength=\"%d\"", max); | 2395 | reply->addstrf(reply, " maxlength=\"%d\"", max); |
2302 | if (required) | 2396 | if (required) |
2303 | reply->addstr(reply, " required"); | 2397 | reply->addstr(reply, " required"); |
2398 | if (readOnly) | ||
2399 | reply->addstr(reply, " readonly"); | ||
2304 | if ("" != holder) | 2400 | if ("" != holder) |
2305 | reply->addstrf(reply, " placeholder=\"%s\"", holder); | 2401 | reply->addstrf(reply, " placeholder=\"%s\"", holder); |
2306 | if ("" != comp) | 2402 | if ("" != comp) |
@@ -2309,14 +2405,17 @@ static void HTMLtextArea(qgrow_t *reply, char *name, char *title, int rows, int | |||
2309 | reply->addstrf(reply, " spellcheck=\"%s\"", spell); | 2405 | reply->addstrf(reply, " spellcheck=\"%s\"", spell); |
2310 | if ("" != wrap) | 2406 | if ("" != wrap) |
2311 | reply->addstrf(reply, " wrap=\"%s\"", wrap); | 2407 | reply->addstrf(reply, " wrap=\"%s\"", wrap); |
2312 | reply->addstrf(reply, ">%s</textarea></label></p>\n", value); | 2408 | if ((NULL != val) && ("" != val)) |
2409 | reply->addstrf(reply, ">%s</textarea></label></p>\n", val); | ||
2410 | else | ||
2411 | reply->addstrf(reply, "></textarea></label></p>\n"); | ||
2313 | } | 2412 | } |
2314 | 2413 | ||
2315 | static void HTMLtext(qgrow_t *reply, char *type, char *title, char *name, char *val, int size, int max, boolean required) | 2414 | static void HTMLtext(qgrow_t *reply, char *type, char *title, char *name, char *val, int size, int max, boolean required) |
2316 | { | 2415 | { |
2317 | reply->addstrf(reply, " <p><label>%s : <input type=\"%s\" name=\"%s\"", title, type, name); | 2416 | reply->addstrf(reply, " <p><label>%s : <input type=\"%s\" name=\"%s\"", title, type, name); |
2318 | if ("" != val) | 2417 | if ((NULL != val) && ("" != val)) |
2319 | reply->addstrf(reply, "value=\"%s\"", val); | 2418 | reply->addstrf(reply, " value=\"%s\"", val); |
2320 | if (0 < size) | 2419 | if (0 < size) |
2321 | reply->addstrf(reply, " size=\"%d\"", size); | 2420 | reply->addstrf(reply, " size=\"%d\"", size); |
2322 | if (0 < max) | 2421 | if (0 < max) |
@@ -2329,17 +2428,17 @@ static void HTMLtext(qgrow_t *reply, char *type, char *title, char *name, char * | |||
2329 | static void HTMLselect(qgrow_t *reply, char *title, char *name) | 2428 | static void HTMLselect(qgrow_t *reply, char *title, char *name) |
2330 | { | 2429 | { |
2331 | if (NULL == title) | 2430 | if (NULL == title) |
2332 | reply->addstrf(reply, " <select name=\"%s\">", name); | 2431 | reply->addstrf(reply, " <p><select name=\"%s\">", name); |
2333 | else | 2432 | else |
2334 | reply->addstrf(reply, " <p>%s : \n <select name=\"%s\">\n", title, name); | 2433 | reply->addstrf(reply, " <p><label>%s : \n <select name=\"%s\">\n", title, name); |
2335 | } | 2434 | } |
2336 | static void HTMLselectEnd(qgrow_t *reply) | 2435 | static void HTMLselectEnd(qgrow_t *reply) |
2337 | { | 2436 | { |
2338 | reply->addstr(reply, " </select>\n </p>\n"); | 2437 | reply->addstr(reply, " </select></label></p>\n \n"); |
2339 | } | 2438 | } |
2340 | static void HTMLselectEndNo(qgrow_t *reply) | 2439 | static void HTMLselectEndNo(qgrow_t *reply) |
2341 | { | 2440 | { |
2342 | reply->addstr(reply, " </select>"); | 2441 | reply->addstr(reply, " </select></p>"); |
2343 | } | 2442 | } |
2344 | 2443 | ||
2345 | static void HTMLoption(qgrow_t *reply, char *title, boolean selected) | 2444 | static void HTMLoption(qgrow_t *reply, char *title, boolean selected) |
@@ -2426,9 +2525,28 @@ void HTMLfill(reqData *Rd, enum fragmentType type, char *text, int length) | |||
2426 | 2525 | ||
2427 | static void HTMLfooter(qgrow_t *reply) | 2526 | static void HTMLfooter(qgrow_t *reply) |
2428 | { | 2527 | { |
2429 | reply->addstr(reply, | 2528 | reply->addstrf(reply, " </div>\n"); |
2430 | " </font>" | 2529 | reply->addstr(reply, |
2431 | " </body>\n</html>\n"); | 2530 | " <div class='top-right'>\n" |
2531 | " <h1>Test mode</h1>\n" | ||
2532 | " <p>This account manager system is currently in test mode, and under heavy development. " | ||
2533 | " Which means it not all written yet, and things may break.</p>\n" | ||
2534 | " <p>Your mission, should you choose to accept it, is to break it, and report how you broke it, so that onefang can fix it.</p>\n" | ||
2535 | " <p>During test mode, no real grid accounts are created, and any accounts created with this will be deleted later. " | ||
2536 | " So feel free to create as many test accounts as you need to test things.</p>\n" | ||
2537 | " <p>We follow the usual web site registration process, which sends a validation email, with a link to click. " | ||
2538 | " However, during this test mode, no emails will be sent, instead a link will be displayed near the top of the page when a user is logged in.</p>\n" | ||
2539 | " <p>Missing bits that are still being written - sending the emails, creating real grid accounts, editing accounts, listing accounts, deleting accounts.</p>\n" | ||
2540 | " </div>\n"); | ||
2541 | // reply->addstr(reply, " <div class='centre'>\n </div>\n"); | ||
2542 | reply->addstr(reply, | ||
2543 | // " <div class='bottom-left'>\n" | ||
2544 | // " </div>\n" | ||
2545 | " <div class='bottom-right'>\n" | ||
2546 | " <iframe src='stats.html' style='border:none;height:100%;width:100%;'></iframe>\n" | ||
2547 | " </div>\n" | ||
2548 | " </font>\n" | ||
2549 | "</body>\n</html>\n"); | ||
2432 | } | 2550 | } |
2433 | 2551 | ||
2434 | 2552 | ||
@@ -2596,6 +2714,9 @@ HTMLfile *checkHTMLcache(char *file) | |||
2596 | } | 2714 | } |
2597 | 2715 | ||
2598 | 2716 | ||
2717 | |||
2718 | |||
2719 | |||
2599 | /* TODO - | 2720 | /* TODO - |
2600 | 2721 | ||
2601 | On new user / password reset. | 2722 | On new user / password reset. |
@@ -2713,7 +2834,7 @@ https://stackoverflow.com/questions/549/the-definitive-guide-to-form-based-websi | |||
2713 | 2834 | ||
2714 | 2835 | ||
2715 | // Forward declare this here so we can use it in validation functions. | 2836 | // Forward declare this here so we can use it in validation functions. |
2716 | void loginPage(reqData *Rd, char *message); | 2837 | //void loginPage(reqData *Rd, char *message); |
2717 | 2838 | ||
2718 | /* Four choices for the token - (https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) | 2839 | /* Four choices for the token - (https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) |
2719 | https://en.wikipedia.org/wiki/Cross-site_request_forgery | 2840 | https://en.wikipedia.org/wiki/Cross-site_request_forgery |
@@ -2790,10 +2911,13 @@ https://stackoverflow.com/questions/16891729/best-practices-salting-peppering-pa | |||
2790 | */ | 2911 | */ |
2791 | 2912 | ||
2792 | 2913 | ||
2914 | qlisttbl_t *accountLevels = NULL; | ||
2915 | |||
2916 | |||
2793 | static void bitch(reqData *Rd, char *message, char *log) | 2917 | static void bitch(reqData *Rd, char *message, char *log) |
2794 | { | 2918 | { |
2795 | addStrL(Rd->errors, message); | 2919 | addStrL(Rd->errors, message); |
2796 | E("%s %s %s %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), getStrH(Rd->stuff, "UUID"), getStrH(Rd->stuff, "name"), message, log); | 2920 | E("%s %s %s - %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), Rd->shs.UUID, getStrH(Rd->stuff, "name"), message, log); |
2797 | } | 2921 | } |
2798 | 2922 | ||
2799 | /* "A token cookie that references a non-existent session, its value should be replaced immediately to prevent session fixation." | 2923 | /* "A token cookie that references a non-existent session, its value should be replaced immediately to prevent session fixation." |
@@ -2806,11 +2930,79 @@ I think this means send a new cookie. | |||
2806 | static void bitchSession(reqData *Rd, char *message, char *log) | 2930 | static void bitchSession(reqData *Rd, char *message, char *log) |
2807 | { | 2931 | { |
2808 | addStrL(Rd->errors, message); | 2932 | addStrL(Rd->errors, message); |
2809 | C("%s %s %s %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), getStrH(Rd->stuff, "UUID"), getStrH(Rd->stuff, "name"), message, log); | 2933 | C("%s %s %s - %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), Rd->shs.UUID, getStrH(Rd->stuff, "name"), message, log); |
2810 | Rd->vegOut = TRUE; | 2934 | Rd->vegOut = TRUE; |
2811 | } | 2935 | } |
2812 | 2936 | ||
2813 | 2937 | ||
2938 | // The ancient, insecure since 2011, Second Life / OpenSim password hashing algorithm. | ||
2939 | char *newSLOSsalt(reqData *Rd) | ||
2940 | { | ||
2941 | char *salt = NULL; | ||
2942 | unsigned char *md5hash = xzalloc(17); | ||
2943 | char uuid[37]; | ||
2944 | uuid_t binuuid; | ||
2945 | |||
2946 | uuid_generate_random(binuuid); | ||
2947 | uuid_unparse_lower(binuuid, uuid); | ||
2948 | if (!qhashmd5((void *) uuid, strlen(uuid), md5hash)) | ||
2949 | bitch(Rd, "Internal error.", "newSLOSsalt() - qhashmd5(new uuid) failed."); | ||
2950 | else | ||
2951 | salt = qhex_encode(md5hash, 16); | ||
2952 | free(md5hash); | ||
2953 | return salt; | ||
2954 | } | ||
2955 | |||
2956 | /* TODO - rewrite this - | ||
2957 | Don't store things in Rd->stuff. Salt was passed in, and not modified. | ||
2958 | Return calculated passHash, not int ret. Returns a NULL if things went wrong. | ||
2959 | */ | ||
2960 | char *checkSLOSpassword(reqData *Rd, char *salt, char *password, char *passwordHash, char *fail) | ||
2961 | { | ||
2962 | char *ret = NULL; | ||
2963 | int rt = 0; | ||
2964 | unsigned char *md5hash = xzalloc(17); | ||
2965 | char *hash = NULL, *passHash = NULL; | ||
2966 | |||
2967 | T("checkSLOSpassword(%s, %s, %s, ", password, salt, passwordHash, fail); | ||
2968 | // Calculate passHash. | ||
2969 | if (!qhashmd5((void *) password, strlen(password), md5hash)) | ||
2970 | { | ||
2971 | bitch(Rd, "Internal error.", "checkSLOSpassword() - qhashmd5(password) failed."); | ||
2972 | rt++; | ||
2973 | } | ||
2974 | else | ||
2975 | { | ||
2976 | passHash = qhex_encode(md5hash, 16); | ||
2977 | hash = xmprintf("%s:%s", passHash, salt); | ||
2978 | if (!qhashmd5((void *) hash, strlen(hash), md5hash)) | ||
2979 | { | ||
2980 | bitch(Rd, "Internal error.", "checkSLOSpassword() - qhashmd5(password:salt) failed."); | ||
2981 | rt++; | ||
2982 | } | ||
2983 | else | ||
2984 | { | ||
2985 | ret = qhex_encode(md5hash, 16); | ||
2986 | } | ||
2987 | free(hash); | ||
2988 | free(passHash); | ||
2989 | } | ||
2990 | |||
2991 | // If one was passed in, compare it. | ||
2992 | if ((NULL != ret) && (NULL != passwordHash) && (strcmp(ret, passwordHash) != 0)) | ||
2993 | { | ||
2994 | bitch(Rd, fail, "Password doesn't match passwordHash"); | ||
2995 | E(" %s %s - %s != %s", password, salt, ret, passwordHash); | ||
2996 | rt++; | ||
2997 | free(ret); | ||
2998 | ret = NULL; | ||
2999 | } | ||
3000 | free(md5hash); | ||
3001 | |||
3002 | return ret; | ||
3003 | } | ||
3004 | |||
3005 | |||
2814 | int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, struct stat *st, struct timespec *now, char *type) | 3006 | int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, struct stat *st, struct timespec *now, char *type) |
2815 | { | 3007 | { |
2816 | struct timespec then; | 3008 | struct timespec then; |
@@ -2856,7 +3048,7 @@ int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, stru | |||
2856 | if (lua_isstring(Rd->L, -1)) | 3048 | if (lua_isstring(Rd->L, -1)) |
2857 | { | 3049 | { |
2858 | tnm->putstr(tnm, n, (char *) lua_tostring(Rd->L, -1)); | 3050 | tnm->putstr(tnm, n, (char *) lua_tostring(Rd->L, -1)); |
2859 | //d("Reading %s = %s", n, getStrH(tnm, n)); | 3051 | d("Lua reading (%s) %s = %s", type, n, getStrH(tnm, n)); |
2860 | } | 3052 | } |
2861 | else | 3053 | else |
2862 | { | 3054 | { |
@@ -2879,11 +3071,28 @@ int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, stru | |||
2879 | } | 3071 | } |
2880 | 3072 | ||
2881 | 3073 | ||
3074 | char *checkLinky(reqData *Rd) | ||
3075 | { | ||
3076 | char *ret = xstrdup(""), *t0 = getStrH(Rd->stuff, "linky-hashish"); | ||
3077 | |||
3078 | if ('\0' != t0[0]) | ||
3079 | { | ||
3080 | char *t1 = qurl_encode(t0, strlen(t0)); | ||
3081 | free(ret); | ||
3082 | ret = xmprintf("<p><font color='red'><b>You have an email waiting with a linky in it <a href='https://%s%s?hashish=%s'>%s</a>.</b></font></p>\n", | ||
3083 | Rd->Host, Rd->RUri, t1, t0); | ||
3084 | free(t1); | ||
3085 | } | ||
3086 | return ret; | ||
3087 | } | ||
3088 | |||
3089 | |||
2882 | static void freeSesh(reqData *Rd, boolean linky, boolean wipe) | 3090 | static void freeSesh(reqData *Rd, boolean linky, boolean wipe) |
2883 | { | 3091 | { |
2884 | char *file = NULL; | 3092 | char *file = NULL; |
2885 | sesh *shs = &Rd->shs; | 3093 | sesh *shs = &Rd->shs; |
2886 | 3094 | ||
3095 | T("free sesh %s %s", linky ? "linky" : "session", wipe ? "wipe" : "delete"); | ||
2887 | if (linky) | 3096 | if (linky) |
2888 | { | 3097 | { |
2889 | shs = Rd->lnk; | 3098 | shs = Rd->lnk; |
@@ -2914,19 +3123,27 @@ static void freeSesh(reqData *Rd, boolean linky, boolean wipe) | |||
2914 | ckh->maxAge = -1; // Should expire immediately. | 3123 | ckh->maxAge = -1; // Should expire immediately. |
2915 | 3124 | ||
2916 | qhashtbl_obj_t obj; | 3125 | qhashtbl_obj_t obj; |
2917 | memset((void*)&obj, 0, sizeof(obj)); | ||
2918 | Rd->database->lock(Rd->database); | ||
2919 | while(Rd->database->getnext(Rd->database, &obj, false) == true) | ||
2920 | Rd->database->remove(Rd->database, obj.name); | ||
2921 | Rd->database->unlock(Rd->database); | ||
2922 | 3126 | ||
2923 | if (wipe) | 3127 | if (wipe) |
2924 | { | 3128 | { |
2925 | Rd->stuff->remove(Rd->stuff, "UUID"); | 3129 | memset((void*)&obj, 0, sizeof(obj)); |
3130 | Rd->database->lock(Rd->database); | ||
3131 | while(Rd->database->getnext(Rd->database, &obj, false) == true) | ||
3132 | Rd->database->remove(Rd->database, obj.name); | ||
3133 | Rd->database->unlock(Rd->database); | ||
3134 | shs->name = NULL; | ||
3135 | shs->UUID = NULL; | ||
3136 | shs->level = -256; | ||
3137 | // TODO - should I wipe the rest of Rd->shs as well? | ||
2926 | Rd->stuff->remove(Rd->stuff, "name"); | 3138 | Rd->stuff->remove(Rd->stuff, "name"); |
2927 | Rd->stuff->remove(Rd->stuff, "level"); | 3139 | Rd->stuff->remove(Rd->stuff, "firstName"); |
3140 | Rd->stuff->remove(Rd->stuff, "lastName"); | ||
3141 | Rd->stuff->remove(Rd->stuff, "email"); | ||
2928 | Rd->stuff->remove(Rd->stuff, "passwordSalt"); | 3142 | Rd->stuff->remove(Rd->stuff, "passwordSalt"); |
2929 | Rd->stuff->remove(Rd->stuff, "passwordHash"); | 3143 | Rd->stuff->remove(Rd->stuff, "passwordHash"); |
3144 | Rd->stuff->remove(Rd->stuff, "passHash"); | ||
3145 | Rd->stuff->remove(Rd->stuff, "passSalt"); | ||
3146 | Rd->stuff->remove(Rd->stuff, "linky-hashish"); | ||
2930 | } | 3147 | } |
2931 | 3148 | ||
2932 | if (shs->isLinky) | 3149 | if (shs->isLinky) |
@@ -2935,14 +3152,16 @@ static void freeSesh(reqData *Rd, boolean linky, boolean wipe) | |||
2935 | Rd->lnk = NULL; | 3152 | Rd->lnk = NULL; |
2936 | } | 3153 | } |
2937 | else | 3154 | else |
3155 | { | ||
2938 | shs->leaf[0] = '\0'; | 3156 | shs->leaf[0] = '\0'; |
3157 | } | ||
2939 | free(file); | 3158 | free(file); |
2940 | } | 3159 | } |
2941 | 3160 | ||
2942 | static void setToken_n_munchie(reqData *Rd, boolean linky) | 3161 | static void setToken_n_munchie(reqData *Rd, boolean linky) |
2943 | { | 3162 | { |
2944 | sesh *shs = &Rd->shs; | 3163 | sesh *shs = &Rd->shs; |
2945 | char *file, *link = ""; | 3164 | char *file; |
2946 | 3165 | ||
2947 | if (linky) | 3166 | if (linky) |
2948 | { | 3167 | { |
@@ -2952,8 +3171,6 @@ static void setToken_n_munchie(reqData *Rd, boolean linky) | |||
2952 | else | 3171 | else |
2953 | { | 3172 | { |
2954 | file = xmprintf("%s/sessions/%s.lua", scCache, shs->leaf); | 3173 | file = xmprintf("%s/sessions/%s.lua", scCache, shs->leaf); |
2955 | if (NULL != Rd->lnk) | ||
2956 | link = Rd->lnk->hashish; | ||
2957 | } | 3174 | } |
2958 | 3175 | ||
2959 | struct stat st; | 3176 | struct stat st; |
@@ -2968,45 +3185,77 @@ static void setToken_n_munchie(reqData *Rd, boolean linky) | |||
2968 | "{\n" | 3185 | "{\n" |
2969 | " ['IP']='%s',\n" | 3186 | " ['IP']='%s',\n" |
2970 | " ['salt']='%s',\n" | 3187 | " ['salt']='%s',\n" |
2971 | " ['seshID']='%s',\n" | 3188 | " ['seshID']='%s',\n", |
2972 | " ['linky-hashishy']='%s',\n", | ||
2973 | getStrH(Rd->headers, "REMOTE_ADDR"), | 3189 | getStrH(Rd->headers, "REMOTE_ADDR"), |
2974 | shs->salt, | 3190 | shs->salt, |
2975 | shs->seshID, | 3191 | shs->seshID |
2976 | link | ||
2977 | ); | 3192 | ); |
2978 | char *tnm1 = xmprintf("}\n" | 3193 | char *tnm1 = xmprintf(" ['name']='%s',\n", shs->name); |
3194 | char *tnm2 = xmprintf(" ['UUID']='%s',\n", shs->UUID); | ||
3195 | char *tnm3 = xmprintf(" ['passHash']='%s',\n", getStrH(Rd->stuff, "passHash")); | ||
3196 | char *tnm4 = xmprintf(" ['passSalt']='%s',\n", getStrH(Rd->stuff, "passSalt")); | ||
3197 | char *tnm9 = xmprintf("}\n" | ||
2979 | "return toke_n_munchie\n"); | 3198 | "return toke_n_munchie\n"); |
2980 | int fd = notstdio(xcreate_stdio(file, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)); | 3199 | int fd = notstdio(xcreate_stdio(file, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)); |
2981 | size_t l = strlen(tnm0); | 3200 | size_t l = strlen(tnm0); |
2982 | 3201 | ||
3202 | |||
2983 | if (s) | 3203 | if (s) |
2984 | I("Creating session %s.", file); | 3204 | I("Creating session %s.", file); |
2985 | else | 3205 | else |
2986 | C("Updating session %s.", file); // I don't think updates can occur now. | 3206 | C("Updating session %s.", file); // I don't think updates can occur now. |
3207 | t("Write shs %s", tnm0); | ||
2987 | if (l != writeall(fd, tnm0, l)) | 3208 | if (l != writeall(fd, tnm0, l)) |
2988 | { | 3209 | { |
2989 | perror_msg("Writing %s", file); | 3210 | perror_msg("Writing %s", file); |
2990 | freeSesh(Rd, linky, TRUE); | 3211 | freeSesh(Rd, linky, TRUE); |
2991 | } | 3212 | } |
2992 | 3213 | ||
2993 | qhashtbl_obj_t obj; | 3214 | if (NULL != shs->name) |
3215 | { | ||
3216 | t("Write shs %s", tnm1); | ||
3217 | l = strlen(tnm1); | ||
3218 | if (l != writeall(fd, tnm1, l)) | ||
3219 | { | ||
3220 | perror_msg("Writing %s", file); | ||
3221 | freeSesh(Rd, linky, TRUE); | ||
3222 | } | ||
3223 | } | ||
3224 | if (NULL != shs->UUID) | ||
3225 | { | ||
3226 | t("Write shs %s", tnm2); | ||
3227 | l = strlen(tnm2); | ||
3228 | if (l != writeall(fd, tnm2, l)) | ||
3229 | { | ||
3230 | perror_msg("Writing %s", file); | ||
3231 | freeSesh(Rd, linky, TRUE); | ||
3232 | } | ||
3233 | } | ||
2994 | 3234 | ||
2995 | memset((void*)&obj, 0, sizeof(obj)); | 3235 | if ('\0' != getStrH(Rd->stuff, "passHash")[0]) |
2996 | Rd->stuff->lock(Rd->stuff); | 3236 | { |
2997 | while(Rd->stuff->getnext(Rd->stuff, &obj, false) == true) | 3237 | t("Write shs %s", tnm3); |
3238 | l = strlen(tnm3); | ||
3239 | if (l != writeall(fd, tnm3, l)) | ||
3240 | { | ||
3241 | perror_msg("Writing %s", file); | ||
3242 | freeSesh(Rd, linky, TRUE); | ||
3243 | } | ||
3244 | } | ||
3245 | |||
3246 | if ('\0' != getStrH(Rd->stuff, "passSalt")[0]) | ||
2998 | { | 3247 | { |
2999 | t("stuff %s = %s", obj.name, (char *) obj.data); | 3248 | t("Write shs %s", tnm4); |
3000 | if (dprintf(fd, " ['%s'] = '%s',\n", obj.name, (char *) obj.data) < 0) | 3249 | l = strlen(tnm4); |
3250 | if (l != writeall(fd, tnm4, l)) | ||
3001 | { | 3251 | { |
3002 | perror_msg("Writing %s", file); | 3252 | perror_msg("Writing %s", file); |
3003 | freeSesh(Rd, linky, TRUE); | 3253 | freeSesh(Rd, linky, TRUE); |
3004 | } | 3254 | } |
3005 | } | 3255 | } |
3006 | Rd->stuff->unlock(Rd->stuff); | ||
3007 | 3256 | ||
3008 | l = strlen(tnm1); | 3257 | l = strlen(tnm9); |
3009 | if (l != writeall(fd, tnm1, l)) | 3258 | if (l != writeall(fd, tnm9, l)) |
3010 | { | 3259 | { |
3011 | perror_msg("Writing %s", file); | 3260 | perror_msg("Writing %s", file); |
3012 | freeSesh(Rd, linky, TRUE); | 3261 | freeSesh(Rd, linky, TRUE); |
@@ -3014,14 +3263,71 @@ t("stuff %s = %s", obj.name, (char *) obj.data); | |||
3014 | // Set the mtime on the file. | 3263 | // Set the mtime on the file. |
3015 | futimens(fd, shs->timeStamp); | 3264 | futimens(fd, shs->timeStamp); |
3016 | xclose(fd); | 3265 | xclose(fd); |
3266 | free(tnm9); | ||
3267 | free(tnm2); | ||
3017 | free(tnm1); | 3268 | free(tnm1); |
3018 | free(tnm0); | 3269 | free(tnm0); |
3019 | free(file); | 3270 | free(file); |
3020 | } | 3271 | } |
3021 | 3272 | ||
3022 | static void createUser(reqData *Rd) | 3273 | |
3274 | static void generateAccountUUID(reqData *Rd) | ||
3275 | { | ||
3276 | // Generate a UUID, check it isn't already being used. | ||
3277 | char uuid[37], *where; | ||
3278 | uuid_t binuuid; | ||
3279 | my_ulonglong users = 0; | ||
3280 | int c; | ||
3281 | |||
3282 | do // UserAccounts.PrincipalID is a unique primary index anyway, but we want the user creation process to be a little on the slow side. | ||
3283 | { | ||
3284 | struct stat st; | ||
3285 | |||
3286 | uuid_generate_random(binuuid); | ||
3287 | uuid_unparse_lower(binuuid, uuid); | ||
3288 | // Try Lua user file. | ||
3289 | where = xmprintf("%s/users/%s.lua", scData, uuid); | ||
3290 | c = stat(where, &st); | ||
3291 | if (c) | ||
3292 | users = 1; | ||
3293 | free(where); | ||
3294 | // Try database. | ||
3295 | where = xmprintf("UserAccounts.PrincipalID = '%s'", uuid); | ||
3296 | D("Trying new UUID %s.", where); | ||
3297 | users = dbCount(Rd->db, "UserAccounts", where); | ||
3298 | free(where); | ||
3299 | } while (users != 0); | ||
3300 | Rd->shs.UUID = xstrdup(uuid); | ||
3301 | Rd->shs.level = -200; | ||
3302 | Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID", uuid); | ||
3303 | Rd->database->putstr(Rd->database, "UserAccounts.Userlevel", "-200"); | ||
3304 | } | ||
3305 | |||
3306 | char *getLevel(reqData *Rd) | ||
3307 | { | ||
3308 | char *ret = "", *lvl = xmprintf("%d", Rd->shs.level); | ||
3309 | ret = accountLevels->getstr(accountLevels, lvl, false); | ||
3310 | if (NULL == ret) | ||
3311 | { | ||
3312 | qlisttbl_obj_t obj; | ||
3313 | |||
3314 | memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call | ||
3315 | accountLevels->lock(accountLevels); | ||
3316 | while(accountLevels->getnext(accountLevels, &obj, NULL, false) == true) | ||
3317 | { | ||
3318 | if (atoi(obj.name) <= Rd->shs.level) | ||
3319 | ret = (char *) obj.data; | ||
3320 | } | ||
3321 | } | ||
3322 | free(lvl); | ||
3323 | return ret; | ||
3324 | } | ||
3325 | |||
3326 | static void accountWrite(reqData *Rd) | ||
3023 | { | 3327 | { |
3024 | char *file = xmprintf("%s/users/%s.lua", scData, getStrH(Rd->stuff, "UUID")); | 3328 | char *file = xmprintf("%s/users/%s.lua", scData, Rd->shs.UUID); |
3329 | char *link = (NULL == Rd->lnk) ? "" : Rd->lnk->hashish; | ||
3330 | char *about = encodeSlash(getStrH(Rd->stuff, "aboutMe")); | ||
3025 | char *tnm = xmprintf( "user = \n" | 3331 | char *tnm = xmprintf( "user = \n" |
3026 | "{\n" | 3332 | "{\n" |
3027 | " ['name']='%s',\n" | 3333 | " ['name']='%s',\n" |
@@ -3032,32 +3338,33 @@ static void createUser(reqData *Rd) | |||
3032 | " ['level']='%d',\n" | 3338 | " ['level']='%d',\n" |
3033 | " ['flags']='%d',\n" | 3339 | " ['flags']='%d',\n" |
3034 | " ['active']='%d',\n" | 3340 | " ['active']='%d',\n" |
3035 | " ['passwordSalt']='%s',\n" | ||
3036 | " ['passwordHash']='%s',\n" | 3341 | " ['passwordHash']='%s',\n" |
3342 | " ['passwordSalt']='%s',\n" | ||
3037 | " ['UUID']='%s',\n" | 3343 | " ['UUID']='%s',\n" |
3038 | " ['DoB']='%s-%s',\n" | 3344 | " ['DoB']='%s',\n" |
3039 | " ['agree']='%s',\n" | 3345 | " ['agree']='%s',\n" |
3040 | " ['adult']='%s',\n" | 3346 | " ['adult']='%s',\n" |
3041 | " ['aboutMe']='%s',\n" | 3347 | " ['aboutMe']='%s',\n" |
3042 | " ['vouched']='%s',\n" | 3348 | " ['vouched']='%s',\n" |
3349 | " ['linky-hashish']='%s',\n" | ||
3043 | "}\n" | 3350 | "}\n" |
3044 | "return user\n", | 3351 | "return user\n", |
3045 | getStrH(Rd->stuff, "name"), | 3352 | getStrH(Rd->stuff, "name"), |
3046 | (long) Rd->shs.timeStamp[1].tv_sec, | 3353 | (strcmp("", getStrH(Rd->stuff, "created")) != 0) ? atol(getStrH(Rd->stuff, "created")) : (long) Rd->shs.timeStamp[1].tv_sec, |
3047 | getStrH(Rd->body, "email"), | 3354 | getStrH(Rd->stuff, "email"), |
3048 | "newbie", | 3355 | getLevel(Rd), |
3049 | -200, | 3356 | Rd->shs.level, |
3050 | 64, | 3357 | 64, |
3051 | 0, | 3358 | 0, |
3052 | getStrH(Rd->stuff, "passwordSalt"), | ||
3053 | getStrH(Rd->stuff, "passwordHash"), | 3359 | getStrH(Rd->stuff, "passwordHash"), |
3054 | getStrH(Rd->stuff, "UUID"), | 3360 | getStrH(Rd->stuff, "passwordSalt"), |
3055 | getStrH(Rd->body, "year"), | 3361 | Rd->shs.UUID, |
3056 | getStrH(Rd->body, "month"), | 3362 | getStrH(Rd->stuff, "DoB"), |
3057 | getStrH(Rd->body, "agree"), | 3363 | getStrH(Rd->stuff, "agree"), |
3058 | getStrH(Rd->body, "adult"), | 3364 | getStrH(Rd->stuff, "adult"), |
3059 | getStrH(Rd->body, "aboutMe"), | 3365 | about, |
3060 | "off" | 3366 | "off", |
3367 | link | ||
3061 | ); | 3368 | ); |
3062 | 3369 | ||
3063 | struct stat st; | 3370 | struct stat st; |
@@ -3078,13 +3385,15 @@ static void createUser(reqData *Rd) | |||
3078 | char *nm = xmprintf("%s/users/%s.lua", scData, qstrreplace("tr", name, " ", "_")); | 3385 | char *nm = xmprintf("%s/users/%s.lua", scData, qstrreplace("tr", name, " ", "_")); |
3079 | 3386 | ||
3080 | free(file); | 3387 | free(file); |
3081 | file = xmprintf("%s.lua", getStrH(Rd->stuff, "UUID")); | 3388 | file = xmprintf("%s.lua", Rd->shs.UUID); |
3082 | I("Symlinking %s to %s", file, nm); | 3389 | I("Symlinking %s to %s", file, nm); |
3083 | if (0 != symlink(file, nm)) | 3390 | if (0 != symlink(file, nm)) |
3084 | perror_msg("Symlinking %s to %s", file, nm); | 3391 | perror_msg("Symlinking %s to %s", file, nm); |
3085 | free(nm); free(name); | 3392 | free(nm); free(name); |
3086 | } | 3393 | } |
3087 | xclose(fd); | 3394 | xclose(fd); |
3395 | free(tnm); | ||
3396 | free(about); | ||
3088 | free(file); | 3397 | free(file); |
3089 | } | 3398 | } |
3090 | 3399 | ||
@@ -3096,11 +3405,12 @@ static sesh *newSesh(reqData *Rd, boolean linky) | |||
3096 | uuid_t binuuid; | 3405 | uuid_t binuuid; |
3097 | sesh *ret = &Rd->shs; | 3406 | sesh *ret = &Rd->shs; |
3098 | 3407 | ||
3099 | d("New sesh"); | 3408 | T("new sesh %s %s %s", linky ? "linky" : "session", ret->UUID, ret->name); |
3100 | if (linky) | 3409 | if (linky) |
3101 | { | 3410 | { |
3102 | Rd->lnk = xzalloc(sizeof(sesh)); | 3411 | Rd->lnk = xzalloc(sizeof(sesh)); |
3103 | ret = Rd->lnk; | 3412 | ret = Rd->lnk; |
3413 | ret->UUID = Rd->shs.UUID; | ||
3104 | } | 3414 | } |
3105 | 3415 | ||
3106 | char buf[128]; // 512 bits. | 3416 | char buf[128]; // 512 bits. |
@@ -3145,7 +3455,15 @@ d("New sesh"); | |||
3145 | free(t1); | 3455 | free(t1); |
3146 | qstrcpy(ret->munchie, sizeof(ret->munchie), munchie); | 3456 | qstrcpy(ret->munchie, sizeof(ret->munchie), munchie); |
3147 | //d("munchie %s", ret->munchie); | 3457 | //d("munchie %s", ret->munchie); |
3148 | t0 = xmprintf("%s%s", getStrH(Rd->stuff, "UUID"), munchie); | 3458 | // TODO - chicken and egg? Used to be from stuff->UUID. |
3459 | t1 = ret->UUID; | ||
3460 | if (NULL == t1) | ||
3461 | { | ||
3462 | uuid_clear(binuuid); | ||
3463 | uuid_unparse_lower(binuuid, uuid); | ||
3464 | ret->UUID = uuid; | ||
3465 | } | ||
3466 | t0 = xmprintf("%s%s", ret->UUID, munchie); | ||
3149 | free(munchie); | 3467 | free(munchie); |
3150 | toke_n_munchie = myHMAC(t0, FALSE); | 3468 | toke_n_munchie = myHMAC(t0, FALSE); |
3151 | free(t0); | 3469 | free(t0); |
@@ -3154,11 +3472,11 @@ d("New sesh"); | |||
3154 | hashish = myHMACkey(ret->salt, toke_n_munchie, FALSE); | 3472 | hashish = myHMACkey(ret->salt, toke_n_munchie, FALSE); |
3155 | free(toke_n_munchie); | 3473 | free(toke_n_munchie); |
3156 | qstrcpy(ret->hashish, sizeof(ret->hashish), hashish); | 3474 | qstrcpy(ret->hashish, sizeof(ret->hashish), hashish); |
3157 | //d("hashish %s", ret->hashish); | 3475 | d("hashish %s", ret->hashish); |
3158 | t0 = myHMACkey(getStrH(Rd->configs, "pepper"), hashish, TRUE); | 3476 | t0 = myHMACkey(getStrH(Rd->configs, "pepper"), hashish, TRUE); |
3159 | free(hashish); | 3477 | free(hashish); |
3160 | qstrcpy(ret->leaf, sizeof(ret->leaf), t0); | 3478 | qstrcpy(ret->leaf, sizeof(ret->leaf), t0); |
3161 | //d("leaf %s", ret->leaf); | 3479 | d("leaf %s", ret->leaf); |
3162 | free(t0); | 3480 | free(t0); |
3163 | ret->isLinky = linky; | 3481 | ret->isLinky = linky; |
3164 | setToken_n_munchie(Rd, linky); | 3482 | setToken_n_munchie(Rd, linky); |
@@ -3170,91 +3488,83 @@ d("New sesh"); | |||
3170 | return ret; | 3488 | return ret; |
3171 | } | 3489 | } |
3172 | 3490 | ||
3173 | char *checkLinky(reqData *Rd) | ||
3174 | { | ||
3175 | char *ret = xstrdup(""), *t0 = getStrH(Rd->stuff, "linky-hashish"); | ||
3176 | 3491 | ||
3177 | if ('\0' != t0[0]) | ||
3178 | { | ||
3179 | char *t1 = qurl_encode(t0, strlen(t0)); | ||
3180 | free(ret); | ||
3181 | ret = xmprintf("<p><font color='red'><b>You have an email waiting with a linky in it <a href='https://%s%s?hashish=%s'>%s</a>.</b></font></p>\n", | ||
3182 | Rd->Host, Rd->RUri, t1, t0); | ||
3183 | free(t1); | ||
3184 | } | ||
3185 | return ret; | ||
3186 | } | ||
3187 | 3492 | ||
3188 | 3493 | ||
3189 | boolean badBoy(int ret, reqData *Rd, qhashtbl_t *data, char *name, char *value) | 3494 | /* CRUD (Create, Read, Update, Delete) |
3190 | { | 3495 | CRAP (Create, Replicate, Append, Process) |
3191 | if (NULL == value) | 3496 | Though I prefer - |
3192 | value = getStrH(data, name); | 3497 | DAVE (Delete, Add, View, Edit), coz the names are shorter. B-) |
3193 | if (0 != ret) | 3498 | On the other hand, list or browse needs to be added, which is why they have |
3194 | { | 3499 | BREAD (Browse, Read, Edit, Add, Delete) |
3195 | Rd->valid->putstr(Rd->valid, name, "-1"); | 3500 | CRUDL (Create, Read, Update, Delete, List) |
3196 | W("Bad boy %s = %s", name, value); | 3501 | CRUDE (Create, Read, Update, Delete, Experience) |
3197 | return TRUE; | 3502 | Maybe - |
3198 | } | 3503 | DAVEE (Delete, Add, View, Edit, Explore) |
3199 | Rd->stuff->putstr(Rd->stuff, name, value); | 3504 | */ |
3200 | Rd->valid->putstr(Rd->valid, name, "1"); | ||
3201 | return FALSE; | ||
3202 | } | ||
3203 | 3505 | ||
3204 | int validateThings(reqData *Rd, char *doit, char *name, qhashtbl_t *things) | 3506 | // lua.h has LUA_T* NONE, NIL, BOOLEAN, LIGHTUSERDATA, NUMBER, STRING, TABLE, FUNCTION, USERDATA, THREAD as defines, -1 - 8. |
3507 | // These are the missing ones. Then later we will have prim, mesh, script, sound, terrain, ... | ||
3508 | #define LUA_TGROUP 42 | ||
3509 | #define LUA_TINTEGER 43 | ||
3510 | #define LUA_TEMAIL 44 | ||
3511 | #define LUA_TPASSWORD 45 | ||
3512 | #define LUA_TFILE 46 | ||
3513 | #define LUA_TIMAGE 47 | ||
3514 | |||
3515 | #define FLD_NONE 0 | ||
3516 | #define FLD_EDITABLE 1 | ||
3517 | #define FLD_HIDDEN 2 | ||
3518 | #define FLD_REQUIRED 4 | ||
3519 | |||
3520 | typedef struct _inputField inputField; | ||
3521 | typedef struct _inputSub inputSub; | ||
3522 | typedef struct _inputForm inputForm; | ||
3523 | typedef struct _inputValue inputValue; | ||
3524 | |||
3525 | typedef int (*inputFieldValidFunc) (reqData *Rd, inputForm *iF, inputValue *iV); | ||
3526 | typedef void (*inputFieldShowFunc) (reqData *Rd, inputForm *iF, inputValue *iV); | ||
3527 | typedef int (*inputSubmitFunc) (reqData *Rd, inputForm *iF, inputValue *iV); | ||
3528 | typedef void (*inputFormShowFunc) (reqData *Rd, inputForm *iF, inputValue *iV); | ||
3529 | |||
3530 | struct _inputField | ||
3205 | { | 3531 | { |
3206 | int e = 0; | 3532 | char *name, *title, *help; |
3207 | qlisttbl_obj_t obj; | 3533 | inputFieldValidFunc validate; // Alas C doesn't have any anonymous function standard. |
3208 | 3534 | inputFieldShowFunc web, console, gui; | |
3209 | D("For function %s, start of %s validation.", doit, name); | 3535 | inputField **group; // If this is a LUA_TGROUP, then this will be a null terminated array of the fields in the group. |
3210 | memset((void *) &obj, 0, sizeof(obj)); | 3536 | // database details |
3211 | fieldValidFuncs->lock(fieldValidFuncs); | 3537 | // lua file details |
3212 | while(fieldValidFuncs->getnext(fieldValidFuncs, &obj, NULL, false) == true) | 3538 | signed char type, flags; |
3213 | { | 3539 | short editLevel, viewLevel, viewLength, maxLength; |
3214 | char *t = things->getstr(things, obj.name, false); | 3540 | }; |
3215 | 3541 | struct _inputSub | |
3216 | if (NULL != t) | 3542 | { |
3217 | { | 3543 | char *name, *title, *help, *outputForm; |
3218 | char *nm = obj.name, *v = Rd->valid->getstr(Rd->valid, nm, false); | 3544 | inputSubmitFunc submit; |
3219 | int valid = 0; | 3545 | }; |
3220 | validFunc *vf = (validFunc *) obj.data; | 3546 | struct _inputForm |
3221 | 3547 | { | |
3222 | if (NULL != v) | 3548 | char *name, *title, *help; |
3223 | valid = atoi(v); | 3549 | qlisttbl_t *fields; // qlisttbl coz iteration in order and lookup are important. |
3224 | 3550 | qhashtbl_t *subs; | |
3225 | if (0 != valid) // Is it in the valid qhashtbl? | 3551 | inputFormShowFunc web, eWeb; // display web, console, gui; |
3226 | { | 3552 | // read function |
3227 | if (0 < valid) // Positive valid means it's valid, negative means it's invald. | 3553 | // write function |
3228 | D("Already validated %s - %s.", vf->title, nm); | 3554 | }; |
3229 | else | 3555 | struct _inputValue |
3230 | D("Already invalidated %s - %s.", vf->title, nm); | 3556 | { |
3231 | } | 3557 | inputField *field; |
3232 | else | 3558 | void *value; // If this is a LUA_TGROUP, then this will be a null. |
3233 | { | 3559 | short valid; // 0 for not yet validated, negative for invalid, positive for valid. |
3234 | D("Validating %s - %s.", vf->title, nm); | 3560 | short source, index; |
3235 | if (vf->func) | 3561 | }; |
3236 | e += vf->func(Rd, things, nm); | ||
3237 | else | ||
3238 | E("No validation function for %s - %s", vf->title, nm); | ||
3239 | } | ||
3240 | } | ||
3241 | } | ||
3242 | fieldValidFuncs->unlock(fieldValidFuncs); | ||
3243 | return e; | ||
3244 | } | ||
3245 | 3562 | ||
3246 | 3563 | ||
3247 | static int validateSesh(reqData *Rd, qhashtbl_t *data, char *name) | 3564 | static int sessionValidate(reqData *Rd, inputForm *iF, inputValue *iV) |
3248 | { | 3565 | { |
3249 | int ret = 0; | 3566 | int ret = 0; |
3250 | boolean linky = FALSE; | 3567 | boolean linky = FALSE; |
3251 | |||
3252 | if ('\0' != Rd->shs.leaf[0]) | ||
3253 | { | ||
3254 | d("Already validated session."); | ||
3255 | return ret; | ||
3256 | } | ||
3257 | |||
3258 | char *toke_n_munchie = "", *munchie = "", *hashish = "", *leaf = "", *timeStamp = "", *seshion = "", *seshID = "", *t0, *t1; | 3568 | char *toke_n_munchie = "", *munchie = "", *hashish = "", *leaf = "", *timeStamp = "", *seshion = "", *seshID = "", *t0, *t1; |
3259 | 3569 | ||
3260 | // In this case the session stuff has to come from specific places. | 3570 | // In this case the session stuff has to come from specific places. |
@@ -3265,10 +3575,15 @@ static int validateSesh(reqData *Rd, qhashtbl_t *data, char *name) | |||
3265 | else | 3575 | else |
3266 | { | 3576 | { |
3267 | toke_n_munchie = getStrH(Rd->cookies, "toke_n_munchie"); | 3577 | toke_n_munchie = getStrH(Rd->cookies, "toke_n_munchie"); |
3268 | munchie = getStrH(Rd->body, "munchie"); | 3578 | // munchie = getStrH(Rd->body, "munchie"); |
3269 | hashish = getStrH(Rd->cookies, "hashish"); | 3579 | hashish = getStrH(Rd->cookies, "hashish"); |
3270 | if (('\0' == toke_n_munchie[0]) || (('\0' == hashish[0]))) | 3580 | if (('\0' == toke_n_munchie[0]) || (('\0' == hashish[0]))) |
3271 | { | 3581 | { |
3582 | if (strcmp("logout", Rd->doit) == 0) | ||
3583 | { | ||
3584 | d("Not checking session, coz we are logging out."); | ||
3585 | return ret; | ||
3586 | } | ||
3272 | bitchSession(Rd, "Invalid session.", "No or blank hashish or toke_n_munchie."); | 3587 | bitchSession(Rd, "Invalid session.", "No or blank hashish or toke_n_munchie."); |
3273 | ret++; | 3588 | ret++; |
3274 | } | 3589 | } |
@@ -3338,10 +3653,7 @@ static int validateSesh(reqData *Rd, qhashtbl_t *data, char *name) | |||
3338 | } | 3653 | } |
3339 | free(t1); | 3654 | free(t1); |
3340 | } | 3655 | } |
3341 | } | ||
3342 | 3656 | ||
3343 | if (0 == ret) | ||
3344 | { | ||
3345 | if (linky) | 3657 | if (linky) |
3346 | { | 3658 | { |
3347 | t0 = xmprintf("%s%s", getStrH(tnm, "UUID"), munchie); | 3659 | t0 = xmprintf("%s%s", getStrH(tnm, "UUID"), munchie); |
@@ -3359,6 +3671,10 @@ static int validateSesh(reqData *Rd, qhashtbl_t *data, char *name) | |||
3359 | } | 3671 | } |
3360 | free(t1); | 3672 | free(t1); |
3361 | 3673 | ||
3674 | } | ||
3675 | |||
3676 | if (0 == ret) | ||
3677 | { | ||
3362 | if (now.tv_sec > st.st_mtim.tv_sec + idleTimeOut) | 3678 | if (now.tv_sec > st.st_mtim.tv_sec + idleTimeOut) |
3363 | { | 3679 | { |
3364 | W("Session idled out."); | 3680 | W("Session idled out."); |
@@ -3384,11 +3700,11 @@ W("Validated session linky."); | |||
3384 | addStrL(Rd->messages, "NOTE - you wont be able to log onto the grid until your new account has been approved."); | 3700 | addStrL(Rd->messages, "NOTE - you wont be able to log onto the grid until your new account has been approved."); |
3385 | Rd->lnk = xzalloc(sizeof(sesh)); | 3701 | Rd->lnk = xzalloc(sizeof(sesh)); |
3386 | qstrcpy(Rd->lnk->leaf, sizeof(Rd->lnk->leaf), leaf); | 3702 | qstrcpy(Rd->lnk->leaf, sizeof(Rd->lnk->leaf), leaf); |
3387 | Rd->chillOut = TRUE; | ||
3388 | freeSesh(Rd, linky, FALSE); | 3703 | freeSesh(Rd, linky, FALSE); |
3389 | qstrcpy(Rd->lnk->leaf, sizeof(Rd->lnk->leaf), ""); | 3704 | qstrcpy(Rd->lnk->leaf, sizeof(Rd->lnk->leaf), ""); |
3390 | Rd->func = (pageBuildFunction) loginPage; | 3705 | Rd->doit = "validate"; |
3391 | Rd->doit = "logout"; | 3706 | Rd->output = "accountLogin"; |
3707 | Rd->form = "accountLogin"; | ||
3392 | // TODO - we might want to delete their old .lua session as well. Maybe? Don't think we have any suitable codes to find it. | 3708 | // TODO - we might want to delete their old .lua session as well. Maybe? Don't think we have any suitable codes to find it. |
3393 | } | 3709 | } |
3394 | else | 3710 | else |
@@ -3399,13 +3715,16 @@ W("Validated session linky."); | |||
3399 | qstrcpy(shs->munchie, sizeof(shs->munchie), munchie); | 3715 | qstrcpy(shs->munchie, sizeof(shs->munchie), munchie); |
3400 | qstrcpy(shs->salt, sizeof(shs->salt), tnm->getstr(tnm, "salt", false)); | 3716 | qstrcpy(shs->salt, sizeof(shs->salt), tnm->getstr(tnm, "salt", false)); |
3401 | qstrcpy(shs->seshID, sizeof(shs->seshID), tnm->getstr(tnm, "seshID", false)); | 3717 | qstrcpy(shs->seshID, sizeof(shs->seshID), tnm->getstr(tnm, "seshID", false)); |
3718 | // TODO - free this somewhere. | ||
3719 | // shs->name = tnm->getstr(tnm, "name", true); | ||
3720 | // shs->UUID = tnm->getstr(tnm, "UUID", true); | ||
3402 | shs->timeStamp[0].tv_nsec = UTIME_OMIT; | 3721 | shs->timeStamp[0].tv_nsec = UTIME_OMIT; |
3403 | shs->timeStamp[0].tv_sec = UTIME_OMIT; | 3722 | shs->timeStamp[0].tv_sec = UTIME_OMIT; |
3404 | memcpy(&shs->timeStamp[1], &st.st_mtim, sizeof(struct timespec)); | 3723 | memcpy(&shs->timeStamp[1], &st.st_mtim, sizeof(struct timespec)); |
3405 | t0 = tnm->getstr(tnm, "linky-hashish", false); | ||
3406 | if (NULL != t0) | ||
3407 | Rd->stuff->putstr(Rd->stuff, "linky-hashish", t0); | ||
3408 | } | 3724 | } |
3725 | // TODO - free this somewhere. | ||
3726 | shs->name = tnm->getstr(tnm, "name", true); | ||
3727 | shs->UUID = tnm->getstr(tnm, "UUID", true); | ||
3409 | } | 3728 | } |
3410 | 3729 | ||
3411 | qhashtbl_obj_t obj; | 3730 | qhashtbl_obj_t obj; |
@@ -3416,9 +3735,9 @@ W("Validated session linky."); | |||
3416 | { | 3735 | { |
3417 | char *n = obj.name; | 3736 | char *n = obj.name; |
3418 | 3737 | ||
3419 | if ((strcmp("salt", n) != 0) && (strcmp("seshID", n) != 0)) | 3738 | if ((strcmp("salt", n) != 0) && (strcmp("seshID", n) != 0) && (strcmp("UUID", n) != 0)) |
3420 | { | 3739 | { |
3421 | t("Lua %s = %s", n, (char *) obj.data); | 3740 | t("SessionValidate() Lua read %s = %s", n, (char *) obj.data); |
3422 | Rd->stuff->putstr(Rd->stuff, obj.name, (char *) obj.data); | 3741 | Rd->stuff->putstr(Rd->stuff, obj.name, (char *) obj.data); |
3423 | } | 3742 | } |
3424 | } | 3743 | } |
@@ -3438,157 +3757,44 @@ t("Lua %s = %s", n, (char *) obj.data); | |||
3438 | return ret; | 3757 | return ret; |
3439 | } | 3758 | } |
3440 | 3759 | ||
3441 | static int validateAboutMe(reqData *Rd, qhashtbl_t *data, char *name) | 3760 | static void sessionWeb(reqData *Rd, inputForm *iF, inputValue *iV) |
3442 | { | 3761 | { |
3443 | int ret = 0; | 3762 | HTMLhidden(Rd->reply, iV->field->name, iV->value); |
3444 | char *about = getStrH(data, "aboutMe"); | ||
3445 | |||
3446 | if ((strcmp("confirm", Rd->doit) != 0) && (strcmp("update", Rd->doit) != 0)) | ||
3447 | return ret; | ||
3448 | |||
3449 | if ('\0' == about[0]) | ||
3450 | { | ||
3451 | bitch(Rd, "Please fill in the 'About me' section.", "None supplied."); | ||
3452 | ret++; | ||
3453 | } | ||
3454 | |||
3455 | badBoy(ret, Rd, data, "aboutMe", about); | ||
3456 | return ret; | ||
3457 | } | 3763 | } |
3458 | 3764 | ||
3459 | 3765 | /* | |
3460 | char *months[] = | 3766 | static int UUIDValidate(reqData *Rd, inputForm *iF, inputValue *iV) |
3461 | { | ||
3462 | "january", | ||
3463 | "february", | ||
3464 | "march", | ||
3465 | "april", | ||
3466 | "may", | ||
3467 | "june", | ||
3468 | "july", | ||
3469 | "august", | ||
3470 | "september", | ||
3471 | "october", | ||
3472 | "november", | ||
3473 | "december" | ||
3474 | }; | ||
3475 | static int validateDoB(reqData *Rd, qhashtbl_t *data, char *name) | ||
3476 | { | ||
3477 | int ret = 0, i; | ||
3478 | char *t; | ||
3479 | |||
3480 | if ((strcmp("confirm", Rd->doit) != 0) && (strcmp("update", Rd->doit) != 0)) | ||
3481 | return ret; | ||
3482 | |||
3483 | t = getStrH(data, "year"); | ||
3484 | if ((NULL == t) || ('\0' == t[0])) | ||
3485 | { | ||
3486 | bitch(Rd, "Please supply a year of birth.", "None supplied."); | ||
3487 | ret++; | ||
3488 | } | ||
3489 | else | ||
3490 | { | ||
3491 | i = atoi(t); | ||
3492 | if ((1900 > i) || (i > 2020)) | ||
3493 | { | ||
3494 | bitch(Rd, "Please supply a year of birth.", "Out of range."); | ||
3495 | ret++; | ||
3496 | } | ||
3497 | } | ||
3498 | |||
3499 | t = getStrH(data, "month"); | ||
3500 | if ((NULL == t) || ('\0' == t[0])) | ||
3501 | { | ||
3502 | bitch(Rd, "Please supply a month of birth.", "None supplied."); | ||
3503 | ret++; | ||
3504 | } | ||
3505 | else | ||
3506 | { | ||
3507 | for (i = 0; i < 12; i++) | ||
3508 | { | ||
3509 | if (strcmp(months[i], t) == 0) | ||
3510 | break; | ||
3511 | } | ||
3512 | if (12 == i) | ||
3513 | { | ||
3514 | bitch(Rd, "Please supply a month of birth.", "Out of range"); | ||
3515 | ret++; | ||
3516 | } | ||
3517 | } | ||
3518 | |||
3519 | badBoy(ret, Rd, data, "month", NULL); | ||
3520 | badBoy(ret, Rd, data, "year", NULL); | ||
3521 | return ret; | ||
3522 | } | ||
3523 | |||
3524 | static int validateEmail(reqData *Rd, qhashtbl_t *data, char *name) | ||
3525 | { | 3767 | { |
3526 | int ret = 0; | 3768 | int ret = 0; |
3527 | char *email = getStrH(data, "email"); | 3769 | char *UUID = (char *) iV->value; |
3528 | char *emayl = getStrH(data, "emayl"); | ||
3529 | |||
3530 | if ((strcmp("confirm", Rd->doit) != 0) && (strcmp("update", Rd->doit) != 0)) | ||
3531 | return ret; | ||
3532 | 3770 | ||
3533 | if ((NULL == email) || (NULL == emayl) || ('\0' == email[0]) || ('\0' == emayl[0])) | 3771 | if (36 != strlen(UUID)) |
3534 | { | ||
3535 | bitch(Rd, "Please supply an email address.", "None supplied."); | ||
3536 | ret++; | ||
3537 | } | ||
3538 | else if (strcmp(email, emayl) != 0) | ||
3539 | { | 3772 | { |
3540 | bitch(Rd, "Email addresses are not the same.", ""); | 3773 | bitch(Rd, "Internal error.", "UUID isn't long enough."); |
3541 | ret++; | 3774 | ret++; |
3542 | } | 3775 | } |
3543 | else if (!qstr_is_email(email)) | 3776 | // TODO - check the characters and dashes as well. |
3544 | { | ||
3545 | bitch(Rd, "Please supply a proper email address.", "Failed qstr_is_email()"); | ||
3546 | ret++; | ||
3547 | } | ||
3548 | else | ||
3549 | { | ||
3550 | // TODO - do other email checks - does the domain exist, .. | ||
3551 | } | ||
3552 | 3777 | ||
3553 | badBoy(ret, Rd, data, "email", NULL); | 3778 | if (0 == ret) |
3554 | badBoy(ret, Rd, data, "emayl", NULL); | 3779 | Rd->stuff->putstr(Rd->stuff, "UUID", UUID); |
3555 | return ret; | 3780 | return ret; |
3556 | } | 3781 | } |
3557 | 3782 | ||
3558 | static int validateLegal(reqData *Rd, qhashtbl_t *data, char *name) | 3783 | static void UUIDWeb(reqData *Rd, inputForm *iF, inputValue *iV) |
3559 | { | 3784 | { |
3560 | int ret = 0; | 3785 | HTMLhidden(Rd->reply, iV->field->name, iV->value); |
3561 | char *t; | ||
3562 | |||
3563 | t = getStrH(data, "adult"); | ||
3564 | if ((NULL == t) || (strcmp("on", t) != 0)) | ||
3565 | { | ||
3566 | bitch(Rd, "You must be an adult to enter this site.", ""); | ||
3567 | ret++; | ||
3568 | } | ||
3569 | t = getStrH(data, "agree"); | ||
3570 | if ((NULL == t) || (strcmp("on", t) != 0)) | ||
3571 | { | ||
3572 | bitch(Rd, "You must agree to the Terms & Conditions of Use.", ""); | ||
3573 | ret++; | ||
3574 | } | ||
3575 | |||
3576 | badBoy(ret, Rd, data, "adult", NULL); | ||
3577 | badBoy(ret, Rd, data, "agree", NULL); | ||
3578 | return ret; | ||
3579 | } | 3786 | } |
3787 | */ | ||
3580 | 3788 | ||
3581 | static int validateName(reqData *Rd, qhashtbl_t *data, char *nm) | 3789 | static int nameValidate(reqData *Rd, inputForm *iF, inputValue *iV) |
3582 | { | 3790 | { |
3583 | boolean login = strcmp("login", Rd->doit) == 0; | ||
3584 | int ret = 0; | 3791 | int ret = 0; |
3585 | unsigned char *name; // We have to be unsigned coz of isalnum(). | 3792 | unsigned char *name; // We have to be unsigned coz of isalnum(). |
3586 | char *where = NULL; | 3793 | char *where = NULL; |
3587 | 3794 | ||
3588 | if ((strcmp("cancel", Rd->doit) == 0) || (strcmp("logout", Rd->doit) == 0)) | 3795 | //d("nameValidate %s", iV->field->name); |
3589 | return ret; | ||
3590 | 3796 | ||
3591 | name = data->getstr(data, "name", true); | 3797 | name = xstrdup(iV->value); |
3592 | 3798 | ||
3593 | if ((NULL == name) || ('\0' == name[0])) | 3799 | if ((NULL == name) || ('\0' == name[0])) |
3594 | { | 3800 | { |
@@ -3648,350 +3854,469 @@ static int validateName(reqData *Rd, qhashtbl_t *data, char *nm) | |||
3648 | ret++; | 3854 | ret++; |
3649 | break; | 3855 | break; |
3650 | } | 3856 | } |
3857 | // TODO - compare first, last, and fullname against god names, complain and fail if there's a match. | ||
3651 | } | 3858 | } |
3652 | } | 3859 | } |
3653 | 3860 | ||
3654 | struct stat st; | 3861 | if (0 == ret) |
3655 | struct timespec now; | ||
3656 | qhashtbl_t *tnm = qhashtbl(0, 0); | ||
3657 | int rt = 0; | ||
3658 | |||
3659 | if (s) {s--; *s = '_'; s++;} | ||
3660 | where = xmprintf("%s/users/%s.lua", scData, name); | ||
3661 | rt = LuaToHash(Rd, where, "user", tnm, ret, &st, &now, "user"); | ||
3662 | if (s) {s--; *s = '\0'; s++;} | ||
3663 | free(where); | ||
3664 | |||
3665 | static dbRequest *acnts = NULL; | ||
3666 | if (NULL == acnts) | ||
3667 | { | 3862 | { |
3668 | static char *szi[] = {"FirstName", "LastName", NULL}; | 3863 | Rd->stuff->putstr(Rd->stuff, "firstName", name); |
3669 | static char *szo[] = {NULL}; | 3864 | Rd->stuff->putstr(Rd->stuff, "lastName", s); |
3670 | acnts = xzalloc(sizeof(dbRequest)); | 3865 | if (s) {s--; *s = ' '; s++;} |
3671 | acnts->db = Rd->db; | 3866 | s = NULL; |
3672 | acnts->table = "UserAccounts"; | 3867 | Rd->stuff->putstr(Rd->stuff, "name", name); |
3673 | acnts->inParams = szi; | 3868 | Rd->shs.name = xstrdup(name); |
3674 | acnts->outParams = szo; | ||
3675 | acnts->where = "FirstName=? and LastName=?"; | ||
3676 | dbRequests->addfirst(dbRequests, acnts, sizeof(*acnts)); | ||
3677 | } | 3869 | } |
3678 | dbDoSomething(acnts, FALSE, name, s); | ||
3679 | rowData *rows = acnts->rows; | ||
3680 | int c = 0; | ||
3681 | |||
3682 | if (rows) | ||
3683 | c = rows->rows->size(rows->rows); | ||
3684 | |||
3685 | if (login) | ||
3686 | { | ||
3687 | if ((1 != c) && (rt)) | ||
3688 | { | ||
3689 | if (rt) | ||
3690 | { | ||
3691 | W("Could not read user Lua file."); | ||
3692 | ret += rt; | ||
3693 | } | ||
3694 | if (0 == c) | ||
3695 | { | ||
3696 | W("No UserAccounts record with that name."); | ||
3697 | ret++; | ||
3698 | } | ||
3699 | else | ||
3700 | { | ||
3701 | W("More than one UserAccounts record with that name."); | ||
3702 | ret++; | ||
3703 | } | ||
3704 | bitch(Rd, "Login failed.", "Could not find user record."); | ||
3705 | } | ||
3706 | else | ||
3707 | { | ||
3708 | if (1 == c) | ||
3709 | dbPull(Rd, "UserAccounts", rows); | ||
3710 | else | ||
3711 | { | ||
3712 | Rd->database->putstr(Rd->database, "UserAccounts.FirstName", name); | ||
3713 | Rd->database->putstr(Rd->database, "UserAccounts.LastName", s); | ||
3714 | Rd->database->putstr(Rd->database, "UserAccounts.Email", getStrH(tnm, "email")); | ||
3715 | Rd->database->putstr(Rd->database, "UserAccounts.Created", getStrH(tnm, "created")); | ||
3716 | Rd->database->putstr(Rd->database, "UserAccounts.PrincipleID", getStrH(tnm, "UUID")); | ||
3717 | Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", getStrH(tnm, "level")); | ||
3718 | Rd->database->putstr(Rd->database, "UserAccounts.UserFlags", getStrH(tnm, "flags")); | ||
3719 | Rd->database->putstr(Rd->database, "UserAccounts.UserTitle", getStrH(tnm, "title")); | ||
3720 | Rd->database->putstr(Rd->database, "UserAccounts.active", getStrH(tnm, "active")); | ||
3721 | Rd->database->putstr(Rd->database, "auth.passwordSalt", getStrH(tnm, "passwordSalt")); | ||
3722 | Rd->database->putstr(Rd->database, "auth.passwordHash", getStrH(tnm, "passwordHash")); | ||
3723 | tnm->free(tnm); | ||
3724 | } | ||
3725 | Rd->stuff->putstr(Rd->stuff, "UUID", getStrH(Rd->database, "UserAccounts.PrincipalID")); | ||
3726 | Rd->stuff->putstr(Rd->stuff, "level", getStrH(Rd->database, "UserAccounts.Userlevel")); | ||
3727 | if (s) {s--; *s = ' '; s++;} | ||
3728 | Rd->stuff->putstr(Rd->stuff, "name", name); | ||
3729 | if (s) {s--; *s = '\0'; s++;} | ||
3730 | } | ||
3731 | } | ||
3732 | else if (strcmp("create", Rd->doit) == 0) | ||
3733 | { | ||
3734 | if ((0 != c) && (!rt)) | ||
3735 | { | ||
3736 | bitch(Rd, "Pick a different name.", "An existing Lua user file or UserAccounts record matched that name."); | ||
3737 | ret++; | ||
3738 | } | ||
3739 | else | ||
3740 | { | ||
3741 | // TODO - compare first, last, and fullname against god names, complain and fail if there's a match. | ||
3742 | // Generate a UUID, check it isn't already being used. | ||
3743 | char uuid[37]; | ||
3744 | uuid_t binuuid; | ||
3745 | my_ulonglong users = 0; | ||
3746 | |||
3747 | do // UserAccounts.PrincipalID is a unique primary index anyway, but we want the user creation process to be a little on the slow side. | ||
3748 | { | ||
3749 | struct stat st; | ||
3750 | |||
3751 | uuid_generate_random(binuuid); | ||
3752 | uuid_unparse_lower(binuuid, uuid); | ||
3753 | // Try Lua user file. | ||
3754 | where = xmprintf("%s/users/%s.lua", scData, uuid); | ||
3755 | c = stat(where, &st); | ||
3756 | if (c) | ||
3757 | users = 1; | ||
3758 | free(where); | ||
3759 | // Try database. | ||
3760 | where = xmprintf("UserAccounts.PrincipalID = '%s'", uuid); | ||
3761 | D("Trying new UUID %s.", where); | ||
3762 | users = dbCount(Rd->db, "UserAccounts", where); | ||
3763 | free(where); | ||
3764 | } while (users != 0); | ||
3765 | // TODO - perhaps create a place holder UserAccounts record? Then we'll have to deal with deleting them later. | ||
3766 | Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(uuid)); | ||
3767 | Rd->stuff->putstr(Rd->stuff, "level", xstrdup("-200")); | ||
3768 | Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID",xstrdup(uuid)); | ||
3769 | Rd->database->putstr(Rd->database, "UserAccounts.Userlevel", xstrdup("-200")); | ||
3770 | Rd->database->putstr(Rd->database, "UserAccounts.firstName", xstrdup(name)); | ||
3771 | Rd->database->putstr(Rd->database, "UserAccounts.lastName", xstrdup(s)); | ||
3772 | if (s) {s--; *s = ' '; s++;} | ||
3773 | Rd->stuff->putstr(Rd->stuff, "name", xstrdup(name)); | ||
3774 | } | ||
3775 | } | ||
3776 | free(rows->fieldNames); | ||
3777 | rows->rows->free(rows->rows); | ||
3778 | free(rows); | ||
3779 | tnm->free(tnm); | ||
3780 | if (s) {s--; *s = ' '; s++;} | 3870 | if (s) {s--; *s = ' '; s++;} |
3781 | } | 3871 | } |
3782 | } | 3872 | } |
3783 | free(name); | 3873 | free(name); |
3784 | 3874 | ||
3785 | badBoy(ret, Rd, data, "name", NULL); | ||
3786 | return ret; | 3875 | return ret; |
3787 | } | 3876 | } |
3788 | 3877 | ||
3789 | static int validatePassword(reqData *Rd, qhashtbl_t *data, char *name) | 3878 | static void nameWeb(reqData *Rd, inputForm *oF, inputValue *oV) |
3879 | { | ||
3880 | if (oV->field->flags & FLD_HIDDEN) | ||
3881 | HTMLhidden(Rd->reply, oV->field->name, oV->value); | ||
3882 | else | ||
3883 | HTMLtext(Rd->reply, "text", oV->field->title, oV->field->name, oV->value, oV->field->viewLength, oV->field->maxLength, oV->field->flags & FLD_REQUIRED); | ||
3884 | } | ||
3885 | |||
3886 | |||
3887 | static int passwordValidate(reqData *Rd, inputForm *iF, inputValue *iV) | ||
3790 | { | 3888 | { |
3791 | boolean login = strcmp("login", Rd->doit) == 0; | ||
3792 | boolean create = strcmp("create", Rd->doit) == 0; | ||
3793 | int ret = 0; | 3889 | int ret = 0; |
3794 | char *password = getStrH(data, "password"); | 3890 | char *password = (char *) iV->value, *salt = getStrH(Rd->stuff, "passSalt"), *hash = getStrH(Rd->stuff, "passHash"); |
3795 | char *psswrdH = getStrH(Rd->stuff, "passwordHash"); | 3891 | |
3796 | char *psswrdS = getStrH(Rd->stuff, "passwordSalt"); | 3892 | if ((NULL == password) || ('\0' == password[0])) |
3893 | { | ||
3894 | bitch(Rd, "Please supply a password.", "Password empty or missing."); | ||
3895 | ret++; | ||
3896 | } | ||
3897 | else if (('\0' != salt[0]) && ('\0' != hash[0]) && (strcmp("psswrd", iV->field->name) == 0)) | ||
3898 | { | ||
3899 | D("Comparing passwords. %s %s %s", password, salt, hash); | ||
3900 | char *h = checkSLOSpassword(Rd, salt, password, hash, "Passwords are not the same."); | ||
3901 | |||
3902 | if (NULL == h) | ||
3903 | ret++; | ||
3904 | else | ||
3905 | free(h); | ||
3906 | } | ||
3907 | |||
3908 | // TODO - once the password is validated, store it as the salt and hash. | ||
3909 | // If it's an existing account, compare it? Or do that later? | ||
3910 | if (0 == ret) | ||
3911 | Rd->stuff->putstr(Rd->stuff, "password", password); | ||
3912 | |||
3913 | return ret; | ||
3914 | } | ||
3915 | |||
3916 | static void passwordWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
3917 | { | ||
3918 | HTMLtext(Rd->reply, "password", oV->field->title, oV->field->name, "", oV->field->viewLength, oV->field->maxLength, oV->field->flags & FLD_REQUIRED); | ||
3919 | Rd->reply->addstr(Rd->reply, "<p>While viewers will usually remember your name and password for you, you'll need to remember it for this web site to. " | ||
3920 | "I highly recommend using a password manager. KeePass and it's variations is a great password manager.</p>\n"); | ||
3921 | } | ||
3797 | 3922 | ||
3798 | if (login) | 3923 | static int emailValidate(reqData *Rd, inputForm *iF, inputValue *iV) |
3924 | { | ||
3925 | // inputField **group = iV->field->group; | ||
3926 | int ret = 0, i; | ||
3927 | boolean notSame = FALSE; | ||
3928 | |||
3929 | i = iV->index; | ||
3930 | if (2 == i) | ||
3799 | { | 3931 | { |
3800 | char *UUID = getStrH(Rd->database, "UserAccounts.PrincipalID"); | 3932 | char *email = (char *) iV->value; |
3801 | static dbRequest *auth = NULL; | 3933 | char *emayl = (char *) (iV + 1)->value; |
3802 | 3934 | ||
3803 | 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. | 3935 | if ((NULL == email) || (NULL == emayl) || ('\0' == email[0]) || ('\0' == emayl[0])) |
3804 | return 1; | 3936 | { |
3805 | if (NULL == auth) | 3937 | bitch(Rd, "Please supply an email address.", "None supplied."); |
3938 | ret++; | ||
3939 | } | ||
3940 | else if (strcmp(email, emayl) != 0) | ||
3941 | { | ||
3942 | bitch(Rd, "Email addresses are not the same.", ""); | ||
3943 | ret++; | ||
3944 | notSame = TRUE; | ||
3945 | } | ||
3946 | else if (!qstr_is_email(email)) | ||
3806 | { | 3947 | { |
3807 | static char *szi[] = {"UUID", NULL}; | 3948 | bitch(Rd, "Please supply a proper email address.", "Failed qstr_is_email()"); |
3808 | static char *szo[] = {"passwordSalt", "passwordHash", NULL}; | 3949 | ret++; |
3809 | auth = xzalloc(sizeof(dbRequest)); | ||
3810 | auth->db = Rd->db; | ||
3811 | auth->table = "auth"; | ||
3812 | auth->inParams = szi; | ||
3813 | auth->outParams = szo; | ||
3814 | auth->where = "UUID=?"; | ||
3815 | dbRequests->addfirst(dbRequests, auth, sizeof(*auth)); | ||
3816 | } | 3950 | } |
3817 | dbDoSomething(auth, FALSE, UUID); | 3951 | else |
3818 | rowData *rows = auth->rows; | ||
3819 | if (rows) | ||
3820 | { | 3952 | { |
3821 | int i = rows->rows->size(rows->rows); | 3953 | // TODO - do other email checks - does the domain exist, .. |
3954 | } | ||
3822 | 3955 | ||
3823 | if (i != 1) | 3956 | if ((NULL != email) && (NULL != emayl)) |
3824 | { | 3957 | { |
3825 | bitch(Rd, "Login failed.", "Wrong number of auth records."); | 3958 | char *t0 = qurl_encode(email, strlen(email)); |
3826 | ret++; | ||
3827 | } | ||
3828 | else | ||
3829 | { | ||
3830 | qhashtbl_t *me = rows->rows->popfirst(rows->rows, NULL); | ||
3831 | unsigned char md5hash[16]; | ||
3832 | 3959 | ||
3833 | if (!qhashmd5((void *) password, strlen(password), md5hash)) | 3960 | // In theory it's the correct thing to do to NOT load email into stuff on failure, |
3834 | { | 3961 | // In practice, that means it wont show the old email and emayl in the create page when they don't match. |
3835 | bitch(Rd, "Login failed, internal error.", "Login - qhashmd5(password) failed."); | 3962 | if ((0 == ret) || notSame) |
3836 | ret++; | 3963 | Rd->stuff->putstrf(Rd->stuff, "email", "%s", t0); |
3837 | } | 3964 | free(t0); |
3838 | else | 3965 | } |
3839 | { | 3966 | if ((NULL != email) && (NULL != emayl)) |
3840 | Rd->stuff->putstr(Rd->stuff, "passwordSalt", getStrH(me, "passwordSalt")); | 3967 | { |
3968 | char *t1 = qurl_encode(emayl, strlen(emayl)); | ||
3841 | 3969 | ||
3842 | char *md5ascii = qhex_encode(md5hash, 16); | 3970 | Rd->stuff->putstrf(Rd->stuff, "emayl", "%s", t1); |
3843 | char *where = xmprintf("%s:%s", md5ascii, getStrH(me, "passwordSalt")); | 3971 | free(t1); |
3844 | if (!qhashmd5((void *) where, strlen(where), md5hash)) | ||
3845 | { | ||
3846 | bitch(Rd, "Login failed, internal error.", "Login - qhashmd5(passwordSalt) failed."); | ||
3847 | ret++; | ||
3848 | } | ||
3849 | else | ||
3850 | { | ||
3851 | free(md5ascii); | ||
3852 | md5ascii = qhex_encode(md5hash, 16); | ||
3853 | if (strcmp(md5ascii, getStrH(me, "passwordHash")) != 0) | ||
3854 | { | ||
3855 | bitch(Rd, "Login failed.", "passwordHash doesn't match"); | ||
3856 | ret++; | ||
3857 | } | ||
3858 | Rd->stuff->putstr(Rd->stuff, "passwordHash", md5ascii); | ||
3859 | } | ||
3860 | free(md5ascii); | ||
3861 | free(where); | ||
3862 | } | ||
3863 | free(me); | ||
3864 | } | ||
3865 | free(rows->fieldNames); | ||
3866 | rows->rows->free(rows->rows); | ||
3867 | free(rows); | ||
3868 | } | 3972 | } |
3869 | } | 3973 | } |
3870 | else if (create) | 3974 | |
3975 | return ret; | ||
3976 | } | ||
3977 | static void emailWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
3978 | { | ||
3979 | HTMLtext(Rd->reply, "email", oV->field->title, oV->field->name, displayPrep(getStrH(Rd->stuff, oV->field->name)), oV->field->viewLength, oV->field->maxLength, oV->field->flags & FLD_REQUIRED); | ||
3980 | } | ||
3981 | |||
3982 | |||
3983 | char *months[] = | ||
3984 | { | ||
3985 | "january", | ||
3986 | "february", | ||
3987 | "march", | ||
3988 | "april", | ||
3989 | "may", | ||
3990 | "june", | ||
3991 | "july", | ||
3992 | "august", | ||
3993 | "september", | ||
3994 | "october", | ||
3995 | "november", | ||
3996 | "december" | ||
3997 | }; | ||
3998 | static int DoBValidate(reqData *Rd, inputForm *iF, inputValue *iV) | ||
3999 | { | ||
4000 | int ret = 0, i; | ||
4001 | char *t0, *t1; | ||
4002 | // inputField **group = iV->field->group; | ||
4003 | |||
4004 | i = iV->index; | ||
4005 | if (2 == i) | ||
3871 | { | 4006 | { |
3872 | if ((NULL == password) || ('\0' == password[0])) | 4007 | t0 = (char *) iV->value; |
4008 | if ((NULL == t0) || ('\0' == t0[0])) | ||
3873 | { | 4009 | { |
3874 | bitch(Rd, "Please supply a password.", "Password empty or missing."); | 4010 | bitch(Rd, "Please supply a year of birth.", "None supplied."); |
3875 | ret++; | 4011 | ret++; |
3876 | } | 4012 | } |
3877 | else | 4013 | else |
3878 | { | 4014 | { |
3879 | // https://stackoverflow.com/questions/246930/is-there-any-difference-between-a-guid-and-a-uuid | 4015 | i = atoi(t0); |
3880 | // Has a great discussion. | 4016 | if ((1900 > i) || (i > 2020)) |
3881 | // http://tools.ietf.org/html/rfc4122 | ||
3882 | // https://en.wikipedia.org/wiki/Universally_unique_identifier | ||
3883 | // Useful stuff about versions and variants, a quick look says OpenSim is using version 4 (random), variant 1. | ||
3884 | // Note versions 3 and 5 are hash based, like I wanted for SledjHamr. B-) | ||
3885 | // https://news.ycombinator.com/item?id=14523523 is a bad blog post with a really good and lengthy comments section, concerning use as database keys. | ||
3886 | // Better off using the 16 byte / 128 bit integer version of UUIDs for database keys, but naturally OpenSim uses char(36) / 304 bit, and sometimes varchar(255). | ||
3887 | |||
3888 | // Calculate passwordSalt and passwordHash. From Opensim - | ||
3889 | // passwordSalt = Util.Md5Hash(UUID.Random().ToString()) | ||
3890 | // passwdHash = Util.Md5Hash(Util.Md5Hash(password) + ":" + passwordSalt) | ||
3891 | unsigned char *md5hash = xzalloc(17); | ||
3892 | char *salt, *hash; | ||
3893 | char uuid[37]; | ||
3894 | uuid_t binuuid; | ||
3895 | |||
3896 | uuid_generate_random(binuuid); | ||
3897 | uuid_unparse_lower(binuuid, uuid); | ||
3898 | if (!qhashmd5((void *) uuid, strlen(uuid), md5hash)) | ||
3899 | { | 4017 | { |
3900 | bitch(Rd, "Internal session error.", "Create - qhashmd5(new uuid) failed."); | 4018 | bitch(Rd, "Please supply a year of birth.", "Out of range."); |
3901 | ret++; | 4019 | ret++; |
3902 | } | 4020 | } |
3903 | else | ||
3904 | { | ||
3905 | salt = qhex_encode(md5hash, 16); | ||
3906 | Rd->stuff->putstr(Rd->stuff, "passwordSalt", salt); | ||
3907 | if (!qhashmd5((void *) password, strlen(password), md5hash)) | ||
3908 | { | ||
3909 | bitch(Rd, "Internal session error.", "Create - qhashmd5(password) failed."); | ||
3910 | ret++; | ||
3911 | } | ||
3912 | else | ||
3913 | { | ||
3914 | free(salt); | ||
3915 | salt = qhex_encode(md5hash, 16); | ||
3916 | hash = xmprintf("%s:%s", salt, getStrH(Rd->stuff, "passwordSalt")); | ||
3917 | if (!qhashmd5((void *) hash, strlen(hash), md5hash)) | ||
3918 | { | ||
3919 | bitch(Rd, "Internal session error.", "Create - qhashmd5(passwordSalt) failed."); | ||
3920 | ret++; | ||
3921 | } | ||
3922 | else | ||
3923 | { | ||
3924 | free(hash); | ||
3925 | hash = qhex_encode(md5hash, 16); | ||
3926 | Rd->stuff->putstr(Rd->stuff, "passwordHash", hash); | ||
3927 | Rd->chillOut = TRUE; | ||
3928 | } | ||
3929 | free(hash); | ||
3930 | free(salt); | ||
3931 | } | ||
3932 | } | ||
3933 | } | 4021 | } |
3934 | } | 4022 | t1 = (char *) (iV + 1)->value; |
3935 | else if (strcmp("confirm", Rd->doit) == 0) | 4023 | if ((NULL == t1) || ('\0' == t1[0])) |
3936 | { | ||
3937 | if ((NULL == password) || ('\0' == password[0])) | ||
3938 | { | 4024 | { |
3939 | bitch(Rd, "Please supply a password.", "Need two passwords."); | 4025 | bitch(Rd, "Please supply a month of birth.", "None supplied."); |
3940 | ret++; | 4026 | ret++; |
3941 | } | 4027 | } |
3942 | else | 4028 | else |
3943 | { | 4029 | { |
3944 | unsigned char *md5hash = xzalloc(17); | 4030 | for (i = 0; i < 12; i++) |
3945 | char *pswd, *hash; | ||
3946 | char *Osalt = getStrH(Rd->stuff, "passwordSalt"), *Ohash = getStrH(Rd->stuff, "passwordHash"); | ||
3947 | |||
3948 | if (!qhashmd5((void *) password, strlen(password), md5hash)) | ||
3949 | { | 4031 | { |
3950 | bitch(Rd, "Internal session error.", "Confirm - qhashmd5(password) failed."); | 4032 | if (strcmp(months[i], t1) == 0) |
3951 | ret++; | 4033 | break; |
3952 | } | 4034 | } |
3953 | else | 4035 | if (12 == i) |
3954 | { | 4036 | { |
3955 | pswd = qhex_encode(md5hash, 16); | 4037 | bitch(Rd, "Please supply a month of birth.", "Out of range"); |
3956 | hash = xmprintf("%s:%s", pswd, Osalt); | 4038 | ret++; |
3957 | free(pswd); | ||
3958 | if (!qhashmd5((void *) hash, strlen(hash), md5hash)) | ||
3959 | { | ||
3960 | bitch(Rd, "Internal session error.", "Confirm - qhashmd5(passwordSalt) failed."); | ||
3961 | ret++; | ||
3962 | } | ||
3963 | else | ||
3964 | { | ||
3965 | free(hash); | ||
3966 | hash = qhex_encode(md5hash, 16); | ||
3967 | if (strcmp(hash, Ohash) != 0) | ||
3968 | { | ||
3969 | bitch(Rd, "Passwords are not the same.", ""); | ||
3970 | ret++; | ||
3971 | } | ||
3972 | free(hash); | ||
3973 | } | ||
3974 | } | 4039 | } |
3975 | } | 4040 | } |
4041 | |||
4042 | if (0 == ret) | ||
4043 | { | ||
4044 | Rd->stuff->putstr(Rd->stuff, "year", t0); | ||
4045 | Rd->stuff->putstr(Rd->stuff, "month", t1); | ||
4046 | Rd->stuff->putstrf(Rd->stuff, "DoB", "%s %s", t0, t1); | ||
4047 | } | ||
3976 | } | 4048 | } |
3977 | 4049 | ||
3978 | // TODO - try to find code for dealing with security enclaves, encrypted memory, and such. | 4050 | return ret; |
3979 | // NOTE - these get filtered through what ever web server is being used, and might leak there. | 4051 | } |
3980 | OPENSSL_cleanse(password, strlen(password)); | 4052 | static void DoByWeb(reqData *Rd, inputForm *oF, inputValue *oV) |
4053 | { | ||
4054 | char *tmp = xmalloc(16), *t; | ||
4055 | int i, d; | ||
3981 | 4056 | ||
3982 | badBoy(ret, Rd, data, "auth.passwordSalt", NULL); | 4057 | Rd->reply->addstr(Rd->reply, "<label>Date of birth :<table><tr><td>\n"); |
4058 | HTMLselect(Rd->reply, NULL, oV->field->name); | ||
4059 | t = getStrH(Rd->stuff, "year"); | ||
4060 | if (NULL == t) | ||
4061 | d = -1; | ||
4062 | else | ||
4063 | d = atoi(t); | ||
4064 | HTMLoption(Rd->reply, "", FALSE); | ||
4065 | for (i = 1900; i <= 2020; i++) | ||
4066 | { | ||
4067 | boolean sel = FALSE; | ||
3983 | 4068 | ||
3984 | return ret; | 4069 | if (i == d) |
4070 | sel = TRUE; | ||
4071 | sprintf(tmp, "%d", i); | ||
4072 | HTMLoption(Rd->reply, tmp, sel); | ||
4073 | } | ||
4074 | free(tmp); | ||
4075 | HTMLselectEndNo(Rd->reply); | ||
3985 | } | 4076 | } |
4077 | static void DoBmWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4078 | { | ||
4079 | char *t; | ||
4080 | int i, d; | ||
3986 | 4081 | ||
4082 | Rd->reply->addstr(Rd->reply, "</td><td>\n"); | ||
4083 | HTMLselect(Rd->reply, NULL, oV->field->name); | ||
4084 | t = getStrH(Rd->stuff, "month"); | ||
4085 | HTMLoption(Rd->reply, "", FALSE); | ||
4086 | for (i = 0; i <= 11; i++) | ||
4087 | { | ||
4088 | boolean sel = FALSE; | ||
3987 | 4089 | ||
3988 | static int validateUUID(reqData *Rd, qhashtbl_t *data, char *name) | 4090 | if ((NULL != t) && (strcmp(t, months[i]) == 0)) |
4091 | sel = TRUE; | ||
4092 | HTMLoption(Rd->reply, months[i], sel); | ||
4093 | } | ||
4094 | HTMLselectEndNo(Rd->reply); | ||
4095 | Rd->reply->addstr(Rd->reply, "</td></tr></table></label>\n"); | ||
4096 | } | ||
4097 | static void DoBWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4098 | { | ||
4099 | } | ||
4100 | |||
4101 | static int legalValidate(reqData *Rd, inputForm *iF, inputValue *iV) | ||
3989 | { | 4102 | { |
3990 | int ret = 0, i; | 4103 | int ret = 0, i; |
3991 | char uuid[37], *t; | 4104 | char *t; |
4105 | inputField **group = iV->field->group; | ||
4106 | |||
4107 | i = iV->index; | ||
4108 | if (2 == i) | ||
4109 | { | ||
4110 | t = (char *) iV->value; | ||
4111 | if ((NULL == t) || (strcmp("on", t) != 0)) | ||
4112 | { | ||
4113 | bitch(Rd, "You must be an adult to enter this site.", ""); | ||
4114 | ret++; | ||
4115 | } | ||
4116 | else | ||
4117 | Rd->stuff->putstr(Rd->stuff, "adult", t); | ||
4118 | t = (char *) (iV + 1)->value; | ||
4119 | if ((NULL == t) || (strcmp("on", t) != 0)) | ||
4120 | { | ||
4121 | bitch(Rd, "You must agree to the Terms & Conditions of Use.", ""); | ||
4122 | ret++; | ||
4123 | } | ||
4124 | else | ||
4125 | Rd->stuff->putstr(Rd->stuff, "agree", t); | ||
4126 | } | ||
4127 | |||
4128 | return ret; | ||
4129 | } | ||
4130 | static void adultWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4131 | { | ||
4132 | HTMLcheckBox(Rd->reply, oV->field->name, oV->field->title, !strcmp("on", getStrH(Rd->body, "adult")), oV->field->flags & FLD_REQUIRED); | ||
4133 | } | ||
4134 | static void agreeWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4135 | { | ||
4136 | HTMLcheckBox(Rd->reply, oV->field->name, oV->field->title, !strcmp("on", getStrH(Rd->body, "agree")), oV->field->flags & FLD_REQUIRED); | ||
4137 | } | ||
4138 | static void legalWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4139 | { | ||
4140 | } | ||
4141 | static void ToSWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4142 | { | ||
4143 | Rd->reply->addstrf(Rd->reply, "<h2>Terms of Service</h2><pre>%s</pre>\n", getStrH(Rd->configs, "ToS")); | ||
4144 | } | ||
4145 | |||
4146 | static int aboutMeValidate(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4147 | { | ||
4148 | int ret = 0; | ||
4149 | char *about = (char *) oV->value; | ||
4150 | |||
4151 | if ((NULL == about) || ('\0' == about[0])) | ||
4152 | { | ||
4153 | bitch(Rd, "Please fill in the 'About me' section.", "None supplied."); | ||
4154 | ret++; | ||
4155 | } | ||
4156 | |||
4157 | if ((0 == ret) && (NULL != about)) | ||
4158 | { | ||
4159 | char *t = qurl_encode(about, strlen(about)); | ||
4160 | Rd->stuff->putstr(Rd->stuff, "aboutMe", t); | ||
4161 | free(t); | ||
4162 | } | ||
4163 | |||
4164 | return ret; | ||
4165 | } | ||
4166 | |||
4167 | static void aboutMeWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4168 | { | ||
4169 | // For maxlength - the MySQL database field is type text, which has a max length of 64 Kilobytes byets, but characters might take up 1 - 4 bytes, and maxlength is in characters. | ||
4170 | // For rows and cols, seems a bit broken, I ask for 5/42, I get 6,36. In world it seems to be 7,46 | ||
4171 | // TODO - check against the limit for in world profiles, coz this will become that. | ||
4172 | // TODO - validate aboutMe, it should not be empty, and should not be longer than 64 kilobytes. | ||
4173 | HTMLtextArea(Rd->reply, oV->field->name, oV->field->title, 7, oV->field->viewLength, 4, oV->field->maxLength, "Describe yourself here.", "off", "true", "soft", oV->value, FALSE, FALSE); | ||
4174 | } | ||
4175 | |||
4176 | static void accountWebHeaders(reqData *Rd, inputForm *oF, char *name) | ||
4177 | { | ||
4178 | char *linky = checkLinky(Rd); | ||
4179 | |||
4180 | HTMLheader(Rd->reply, "<!--#echo var=\"grid\" --> account manager"); | ||
4181 | Rd->reply->addstrf(Rd->reply, "<h1><!--#echo var=\"grid\" --> account manager</h1>\n"); | ||
4182 | if (NULL != name) | ||
4183 | { | ||
4184 | Rd->reply->addstrf(Rd->reply, "<h2><!--#echo var=\"grid\" --> account for %s</h2>\n", name); | ||
4185 | Rd->reply->addstr(Rd->reply, linky); | ||
4186 | } | ||
4187 | free(linky); | ||
4188 | if (0 != Rd->errors->size(Rd->messages)) | ||
4189 | HTMLlist(Rd->reply, "messages -", Rd->messages); | ||
4190 | if (NULL != oF->help) | ||
4191 | Rd->reply->addstrf(Rd->reply, "<p>%s</p>\n", oF->help); | ||
4192 | HTMLform(Rd->reply, "", Rd->shs.munchie); | ||
4193 | HTMLhidden(Rd->reply, "form", oF->name); | ||
4194 | } | ||
4195 | |||
4196 | static void accountWebFields(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4197 | { | ||
4198 | int count = oF->fields->size(oF->fields), i; | ||
4199 | |||
4200 | for (i = 0; i < count; i++) | ||
4201 | { | ||
4202 | if (NULL != oV[i].field->web) | ||
4203 | oV[i].field->web(Rd, oF, &oV[i]); | ||
4204 | if ((NULL != oV[i].field->help) && ('\0' != oV[i].field->help[0])) | ||
4205 | Rd->reply->addstrf(Rd->reply, "<p>%s</p>\n", oV[i].field->help); | ||
4206 | //d("accountWebFeilds(%s, %s)", oF->name, oV[i].field->name); | ||
4207 | } | ||
4208 | } | ||
4209 | |||
4210 | static void accountWebSubs(reqData *Rd, inputForm *oF) | ||
4211 | { | ||
4212 | qhashtbl_obj_t obj; | ||
4213 | |||
4214 | Rd->reply->addstrf(Rd->reply, "<input type='submit' disabled style='display: none' aria-hidden='true' />\n"); // Stop Enter key on text fields triggering the first submit button. | ||
4215 | memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call | ||
4216 | oF->subs->lock(oF->subs); | ||
4217 | while(oF->subs->getnext(oF->subs, &obj, false) == true) | ||
4218 | { | ||
4219 | inputSub *sub = (inputSub *) obj.data; | ||
4220 | if ('\0' != sub->title[0]) | ||
4221 | HTMLbutton(Rd->reply, sub->title); | ||
4222 | //d("accountWebSubs(%s, %s '%s')", oF->name, sub->name, sub->title); | ||
4223 | } | ||
4224 | oF->subs->unlock(oF->subs); | ||
4225 | } | ||
4226 | |||
4227 | static void accountWebFooter(reqData *Rd, inputForm *oF) | ||
4228 | { | ||
4229 | if (0 != Rd->errors->size(Rd->errors)) | ||
4230 | HTMLlist(Rd->reply, "errors -", Rd->errors); | ||
4231 | HTMLformEnd(Rd->reply); | ||
4232 | HTMLfooter(Rd->reply); | ||
4233 | } | ||
4234 | |||
4235 | static void accountAddWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4236 | { | ||
4237 | char *name = getStrH(Rd->stuff, "name"); | ||
4238 | |||
4239 | accountWebHeaders(Rd, oF, name); | ||
4240 | accountWebFields(Rd, oF, oV); | ||
4241 | accountWebSubs(Rd, oF); | ||
4242 | accountWebFooter(Rd, oF); | ||
4243 | } | ||
4244 | |||
4245 | static void accountLoginWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4246 | { | ||
4247 | char *name = getStrH(Rd->stuff, "name"); | ||
4248 | |||
4249 | Rd->shs.UUID = NULL; | ||
4250 | accountWebHeaders(Rd, oF, NULL); | ||
4251 | accountWebFields(Rd, oF, oV); | ||
4252 | accountWebSubs(Rd, oF); | ||
4253 | accountWebFooter(Rd, oF); | ||
4254 | } | ||
4255 | |||
4256 | static void accountViewWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4257 | { | ||
4258 | time_t crtd = atol(getStrH(Rd->database, "UserAccounts.Created")); | ||
4259 | |||
4260 | accountWebHeaders(Rd, oF, getStrH(Rd->stuff, "name")); | ||
4261 | accountWebFields(Rd, oF, oV); | ||
4262 | // TODO - still need to encode < > as < u> for email and about. | ||
4263 | // TODO - dammit, qurl_decode returns the string length, and decodes the string in place. | ||
4264 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Title / level :</b></span></font> %s / %d</p>", getLevel(Rd), Rd->shs.level); | ||
4265 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Date of birth :</b></span></font> %s</p>", getStrH(Rd->database, "Lua.DoB")); | ||
4266 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Created :</b></span></font> %s</p>", ctime(&crtd)); | ||
4267 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Email :</b></span></font> %s</p>", displayPrep(getStrH(Rd->stuff, "email"))); | ||
4268 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>UUID :</b></span></font> %s</p>", Rd->shs.UUID); | ||
4269 | // Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>About :</b></span></font> </p>" | ||
4270 | // "<textarea readonly >%s</textarea>", qurl_decode(getStrH(Rd->database, "Lua.aboutMe"))); | ||
4271 | HTMLtextArea(Rd->reply, "aboutMe", "About", 7, 50, 4, 16384, "", "off", "true", "soft", displayPrep(getStrH(Rd->database, "Lua.aboutMe")), FALSE, TRUE); | ||
4272 | accountWebSubs(Rd, oF); | ||
4273 | accountWebFooter(Rd, oF); | ||
4274 | } | ||
4275 | |||
4276 | static void accountEditWeb(reqData *Rd, inputForm *oF, inputValue *oV) | ||
4277 | { | ||
4278 | char *name = getStrH(Rd->stuff, "name"); | ||
4279 | |||
4280 | accountWebHeaders(Rd, oF, name); | ||
4281 | accountWebFields(Rd, oF, oV); | ||
4282 | HTMLtext(Rd->reply, "password", "Old password", "password", "", 16, 0, FALSE); | ||
4283 | Rd->reply->addstr(Rd->reply, "<p>Warning, the limit on password length is set by your viewer, some can't handle longer than 16 characters.</p>\n"); | ||
4284 | //// HTMLtext(Rd->reply, "title", "text", "title", getStrH(Rh->stuff, "title"), 16, 64, TRUE); | ||
4285 | |||
4286 | qlisttbl_obj_t obj; | ||
4287 | char *lvl = getLevel(Rd); | ||
4288 | |||
4289 | HTMLselect(Rd->reply, "level", "level"); | ||
4290 | memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call | ||
4291 | accountLevels->lock(accountLevels); | ||
4292 | while(accountLevels->getnext(accountLevels, &obj, NULL, false) == true) | ||
4293 | { | ||
4294 | boolean is = false; | ||
4295 | |||
4296 | if (strcmp(lvl, (char *) obj.data) == 0) | ||
4297 | is = true; | ||
4298 | HTMLoption(Rd->reply, (char *) obj.data, is); | ||
4299 | } | ||
4300 | accountLevels->unlock(accountLevels); | ||
4301 | HTMLselectEnd(Rd->reply); | ||
4302 | |||
4303 | accountWebSubs(Rd, oF); | ||
4304 | accountWebFooter(Rd, oF); | ||
4305 | } | ||
4306 | |||
4307 | |||
4308 | static int accountRead(reqData *Rd, inputForm *iF, inputValue *iV) | ||
4309 | { | ||
4310 | int ret = 0, rt = -1; | ||
4311 | struct stat st; | ||
4312 | struct timespec now; | ||
4313 | qhashtbl_t *tnm = qhashtbl(0, 0); | ||
4314 | char *uuid, *first, *last; | ||
4315 | uuid_t binuuid; | ||
3992 | rowData *rows = NULL; | 4316 | rowData *rows = NULL; |
3993 | static dbRequest *uuids = NULL; | ||
3994 | 4317 | ||
4318 | // Setup the database stuff. | ||
4319 | static dbRequest *uuids = NULL; | ||
3995 | if (NULL == uuids) | 4320 | if (NULL == uuids) |
3996 | { | 4321 | { |
3997 | static char *szi[] = {"PrincipalID", NULL}; | 4322 | static char *szi[] = {"PrincipalID", NULL}; |
@@ -4004,249 +4329,584 @@ static int validateUUID(reqData *Rd, qhashtbl_t *data, char *name) | |||
4004 | uuids->where = "PrincipalID=?"; | 4329 | uuids->where = "PrincipalID=?"; |
4005 | dbRequests->addfirst(dbRequests, uuids, sizeof(*uuids)); | 4330 | dbRequests->addfirst(dbRequests, uuids, sizeof(*uuids)); |
4006 | } | 4331 | } |
4332 | static dbRequest *acnts = NULL; | ||
4333 | if (NULL == acnts) | ||
4334 | { | ||
4335 | static char *szi[] = {"FirstName", "LastName", NULL}; | ||
4336 | static char *szo[] = {NULL}; | ||
4337 | acnts = xzalloc(sizeof(dbRequest)); | ||
4338 | acnts->db = Rd->db; | ||
4339 | acnts->table = "UserAccounts"; | ||
4340 | acnts->inParams = szi; | ||
4341 | acnts->outParams = szo; | ||
4342 | acnts->where = "FirstName=? and LastName=?"; | ||
4343 | dbRequests->addfirst(dbRequests, acnts, sizeof(*acnts)); | ||
4344 | } | ||
4345 | static dbRequest *auth = NULL; | ||
4346 | if (NULL == auth) | ||
4347 | { | ||
4348 | static char *szi[] = {"UUID", NULL}; | ||
4349 | static char *szo[] = {"passwordSalt", "passwordHash", NULL}; | ||
4350 | auth = xzalloc(sizeof(dbRequest)); | ||
4351 | auth->db = Rd->db; | ||
4352 | auth->table = "auth"; | ||
4353 | auth->inParams = szi; | ||
4354 | auth->outParams = szo; | ||
4355 | auth->where = "UUID=?"; | ||
4356 | dbRequests->addfirst(dbRequests, auth, sizeof(*auth)); | ||
4357 | } | ||
4007 | 4358 | ||
4008 | if ((strcmp("cancel", Rd->doit) == 0) || (strcmp("logout", Rd->doit) == 0)) | 4359 | uuid = Rd->shs.UUID; first = getStrH(Rd->stuff, "firstName"); last = getStrH(Rd->stuff, "lastName"); |
4009 | return ret; | 4360 | d("accountRead() UUID %s, name %s %s", uuid, first, last); |
4361 | uuid_clear(binuuid); | ||
4362 | if ((NULL != uuid) && ('\0' != uuid[0])) | ||
4363 | uuid_parse(uuid, binuuid); | ||
4364 | if ((NULL != uuid) && ('\0' != uuid[0]) && (!uuid_is_null(binuuid))) | ||
4365 | { | ||
4366 | char *where = xmprintf("%s/users/%s.lua", scData, uuid); | ||
4367 | rt = LuaToHash(Rd, where, "user", tnm, ret, &st, &now, "user"); | ||
4010 | 4368 | ||
4011 | uuid[0] = '\0'; | 4369 | free(where); |
4012 | if (strcmp("create", Rd->doit) == 0) | 4370 | dbDoSomething(uuids, FALSE, uuid); |
4371 | rows = uuids->rows; | ||
4372 | } | ||
4373 | else | ||
4013 | { | 4374 | { |
4014 | // Generate a UUID, check it isn't already being used, and totally ignore whatever UUID is in body. | ||
4015 | uuid_t binuuid; | ||
4016 | 4375 | ||
4017 | do // UserAccounts.PrincipalID is a unique primary index anyway, but we want the user creation process to be a little on the slow side. | 4376 | if ('\0' != first[0]) |
4018 | { | 4377 | { |
4019 | uuid_generate_random(binuuid); | 4378 | char *where = xmprintf("%s/users/%s_%s.lua", scData, first, last); |
4020 | uuid_unparse_lower(binuuid, uuid); | 4379 | rt = LuaToHash(Rd, where, "user", tnm, ret, &st, &now, "user"); |
4021 | 4380 | ||
4022 | d("Trying new UUID %s.", uuid); | 4381 | free(where); |
4023 | // TODO - check the Lua user files as well. | 4382 | dbDoSomething(acnts, FALSE, first, last); |
4024 | dbDoSomething(uuids, FALSE, uuid); | 4383 | rows = acnts->rows; |
4025 | rows = uuids->rows; | 4384 | } |
4026 | i = 0; | 4385 | } |
4386 | // else | ||
4387 | // { | ||
4388 | // bitch(Rd, "Unable to read user record.", "Nothing available to look up a user record with."); | ||
4389 | // rt = 1; | ||
4390 | // } | ||
4391 | |||
4392 | if (0 == rt) | ||
4393 | { | ||
4394 | ret += 1; | ||
4395 | Rd->database->putstr(Rd->database, "UserAccounts.FirstName", first); | ||
4396 | Rd->database->putstr(Rd->database, "UserAccounts.LastName", last); | ||
4397 | Rd->database->putstr(Rd->database, "UserAccounts.Email", getStrH(tnm, "email")); | ||
4398 | Rd->database->putstr(Rd->database, "UserAccounts.Created", getStrH(tnm, "created")); | ||
4399 | Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID", getStrH(tnm, "UUID")); | ||
4400 | Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", getStrH(tnm, "level")); | ||
4401 | Rd->database->putstr(Rd->database, "UserAccounts.UserFlags", getStrH(tnm, "flags")); | ||
4402 | Rd->database->putstr(Rd->database, "UserAccounts.UserTitle", getStrH(tnm, "title")); | ||
4403 | Rd->database->putstr(Rd->database, "UserAccounts.active", getStrH(tnm, "active")); | ||
4404 | Rd->database->putstr(Rd->database, "auth.passwordSalt", getStrH(tnm, "passwordSalt")); | ||
4405 | Rd->database->putstr(Rd->database, "auth.passwordHash", getStrH(tnm, "passwordHash")); | ||
4406 | Rd->stuff-> putstr(Rd->stuff, "linky-hashish", getStrH(tnm, "linky-hashish")); | ||
4407 | Rd->database->putstr(Rd->database, "Lua.name", getStrH(tnm, "name")); | ||
4408 | Rd->database->putstr(Rd->database, "Lua.DoB", getStrH(tnm, "DoB")); | ||
4409 | Rd->database->putstr(Rd->database, "Lua.agree", getStrH(tnm, "agree")); | ||
4410 | Rd->database->putstr(Rd->database, "Lua.adult", getStrH(tnm, "adult")); | ||
4411 | Rd->database->putstr(Rd->database, "Lua.aboutMe", getStrH(tnm, "aboutMe")); | ||
4412 | Rd->database->putstr(Rd->database, "Lua.vouched", getStrH(tnm, "vouched")); | ||
4413 | } | ||
4414 | else if (rows) | ||
4415 | { | ||
4416 | ret += rows->rows->size(rows->rows); | ||
4417 | if (1 == ret) | ||
4418 | { | ||
4419 | dbPull(Rd, "UserAccounts", rows); | ||
4420 | dbDoSomething(auth, FALSE, getStrH(Rd->database, "UserAccounts.PrincipalID")); | ||
4421 | rows = auth->rows; | ||
4027 | if (rows) | 4422 | if (rows) |
4028 | i = rows->rows->size(rows->rows); | ||
4029 | else | ||
4030 | { | 4423 | { |
4031 | bitch(Rd, "Internal error.", "Matching UUID record found in UserAccounts."); | 4424 | if (1 == rows->rows->size(rows->rows)) |
4032 | ret++; | 4425 | dbPull(Rd, "auth", rows); |
4033 | break; | ||
4034 | } | 4426 | } |
4035 | } while (i != 0); | ||
4036 | if (0 == ret) | ||
4037 | { | ||
4038 | data->putstr(data, "UUID", xstrdup(uuid)); | ||
4039 | Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(uuid)); | ||
4040 | } | 4427 | } |
4041 | rows = NULL; | ||
4042 | } | 4428 | } |
4043 | else if ((strcmp("confirm", Rd->doit) == 0) || (strcmp("logout", Rd->doit) == 0)) | 4429 | else |
4430 | { | ||
4431 | d("No user name or UUID to get an account for."); | ||
4432 | } | ||
4433 | |||
4434 | if (1 == ret) | ||
4044 | { | 4435 | { |
4045 | t = getStrH(data, "UUID"); | 4436 | // TODO - this has to change when we are editing other peoples accounts. |
4046 | if (36 != strlen(t)) | 4437 | Rd->shs.UUID = Rd->database->getstr(Rd->database, "UserAccounts.PrincipalID", true); |
4438 | Rd->stuff->putstr(Rd->stuff, "email", getStrH(Rd->database, "UserAccounts.Email")); | ||
4439 | Rd->shs.level = atoi(getStrH(Rd->database, "UserAccounts.UserLevel")); | ||
4440 | } | ||
4441 | |||
4442 | tnm->free(tnm); | ||
4443 | return ret; | ||
4444 | } | ||
4445 | |||
4446 | static int accountDel(reqData *Rd, inputForm *iF, inputValue *iV) | ||
4447 | { | ||
4448 | int ret = 0; | ||
4449 | int c = accountRead(Rd, iF, iV); | ||
4450 | |||
4451 | if (1 != c) | ||
4452 | { | ||
4453 | bitch(Rd, "Cannot delete account.", "Account doesn't exist."); | ||
4454 | ret++; | ||
4455 | } | ||
4456 | else | ||
4457 | { | ||
4458 | // check if logged in user is allowed to delete this account | ||
4459 | // delete user record | ||
4460 | // log the user out if they are logged in | ||
4461 | } | ||
4462 | return ret; | ||
4463 | } | ||
4464 | static int accountCreate(reqData *Rd, inputForm *iF, inputValue *iV) | ||
4465 | { | ||
4466 | int ret = 0; | ||
4467 | int c = accountRead(Rd, iF, iV); | ||
4468 | boolean wipe = FALSE; | ||
4469 | |||
4470 | if (strcmp("POST", Rd->Method) == 0) | ||
4471 | { | ||
4472 | if (0 != c) | ||
4047 | { | 4473 | { |
4048 | bitch(Rd, "Internal error.", "UUID isn't long enough."); | 4474 | bitch(Rd, "Cannot create account.", "Account exists."); |
4049 | ret++; | 4475 | ret++; |
4050 | } | 4476 | } |
4051 | else | 4477 | else |
4052 | strcpy(uuid, t); | 4478 | { |
4479 | char *salt = newSLOSsalt(Rd); | ||
4480 | char *h = checkSLOSpassword(Rd, salt, getStrH(Rd->body, "password"), NULL, NULL); | ||
4481 | |||
4482 | if (NULL == h) | ||
4483 | ret++; | ||
4484 | else | ||
4485 | { | ||
4486 | Rd->stuff->putstr(Rd->stuff, "passHash", h); | ||
4487 | Rd->stuff->putstr(Rd->stuff, "passSalt", salt); | ||
4488 | free(h); | ||
4489 | } | ||
4490 | free(salt); | ||
4491 | if (0 != ret) | ||
4492 | { | ||
4493 | wipe = TRUE; | ||
4494 | Rd->shs.UUID = NULL; | ||
4495 | Rd->output = "accountLogin"; | ||
4496 | } | ||
4497 | } | ||
4053 | } | 4498 | } |
4054 | else | 4499 | freeSesh(Rd, FALSE, wipe); |
4500 | newSesh(Rd, FALSE); | ||
4501 | return ret; | ||
4502 | } | ||
4503 | static int accountAdd(reqData *Rd, inputForm *iF, inputValue *iV) | ||
4504 | { | ||
4505 | int ret = 0; | ||
4506 | int c = accountRead(Rd, iF, iV); | ||
4507 | boolean wipe = FALSE; | ||
4508 | |||
4509 | if (0 != c) | ||
4510 | { | ||
4511 | bitch(Rd, "Cannot add account.", "Account exists."); | ||
4512 | ret++; | ||
4513 | } | ||
4514 | else if ((0 == ret) && (strcmp("POST", Rd->Method) == 0)) | ||
4055 | { | 4515 | { |
4056 | if ('\0' != getStrH(Rd->database, "UserAccounts.ScopeID")[0]) return ret; | 4516 | char *h = checkSLOSpassword(Rd, getStrH(Rd->stuff, "passSalt"), getStrH(Rd->stuff, "password"), getStrH(Rd->stuff, "passHash"), "Passwords are not the same."); |
4057 | 4517 | ||
4058 | t = getStrH(data, "UUID"); | 4518 | if (NULL == h) |
4059 | if (36 != strlen(t)) | ||
4060 | { | 4519 | { |
4061 | bitch(Rd, "Internal error.", "UUID isn't long enough."); | ||
4062 | ret++; | 4520 | ret++; |
4521 | wipe = TRUE; | ||
4522 | Rd->shs.UUID = NULL; | ||
4523 | Rd->output = "accountLogin"; | ||
4063 | } | 4524 | } |
4064 | else | 4525 | else |
4065 | { | 4526 | { |
4066 | strcpy(uuid, t); | 4527 | free(h); |
4067 | dbDoSomething(uuids, FALSE, uuid); | 4528 | generateAccountUUID(Rd); |
4068 | rows = uuids->rows; | 4529 | Rd->stuff->putstr(Rd->stuff, "passwordHash", getStrH(Rd->stuff, "passHash")); |
4069 | if (rows) | 4530 | Rd->stuff->putstr(Rd->stuff, "passwordSalt", getStrH(Rd->stuff, "passSalt")); |
4070 | { | 4531 | Rd->shs.level = -200; |
4071 | if (1 != rows->rows->size(rows->rows)) | 4532 | freeSesh(Rd, FALSE, wipe); |
4072 | { | 4533 | newSesh(Rd, TRUE); |
4073 | bitch(Rd, "Internal error.", "No matching UUID record found in UserAccounts."); | 4534 | accountWrite(Rd); |
4074 | ret++; | 4535 | // log them in |
4075 | } | 4536 | I("Logged on %s %s Level %d %s", Rd->shs.UUID, getStrH(Rd->stuff, "name"), Rd->shs.level, getLevel(Rd)); |
4076 | } | 4537 | Rd->output = "accountView"; |
4538 | Rd->form = "accountView"; | ||
4539 | Rd->doit = "login"; | ||
4540 | // TODO - send email. Quick and easy is to invoke the sandmail command. Later use libcurl. | ||
4077 | } | 4541 | } |
4078 | } | 4542 | } |
4543 | freeSesh(Rd, FALSE, wipe); | ||
4544 | newSesh(Rd, FALSE); | ||
4545 | return ret; | ||
4546 | } | ||
4547 | |||
4548 | static int accountSave(reqData *Rd, inputForm *iF, inputValue *iV) | ||
4549 | { | ||
4550 | int ret = 0; | ||
4551 | int c = accountRead(Rd, iF, iV); | ||
4552 | boolean wipe = FALSE; | ||
4079 | 4553 | ||
4080 | if (!badBoy(ret, Rd, data, "UUID", uuid)) | 4554 | if (1 != c) |
4555 | { | ||
4556 | bitch(Rd, "Cannot save account.", "Account doesn't exist."); | ||
4557 | ret++; | ||
4558 | } | ||
4559 | else if ((0 == ret) && (strcmp("POST", Rd->Method) == 0)) | ||
4081 | { | 4560 | { |
4082 | if (rows) | 4561 | char *h = checkSLOSpassword(Rd, getStrH(Rd->stuff, "passSalt"), getStrH(Rd->body, "password"), getStrH(Rd->stuff, "passHash"), "Passwords are not the same."); |
4562 | if (NULL == h) | ||
4083 | { | 4563 | { |
4084 | dbPull(Rd, "UserAccounts", rows); | 4564 | ret++; |
4085 | Rd->stuff->putstr(Rd->stuff, "level", getStrH(Rd->database, "UserAccounts.Userlevel")); | 4565 | wipe = TRUE; |
4086 | free(rows->rows); | 4566 | Rd->shs.UUID = NULL; |
4087 | free(rows->fieldNames); | 4567 | Rd->output = "accountLogin"; |
4088 | free(rows); | ||
4089 | } | 4568 | } |
4090 | else | 4569 | else |
4091 | { | 4570 | { |
4092 | Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID", xstrdup(uuid)); | 4571 | free(h); |
4572 | Rd->stuff->putstr(Rd->stuff, "passwordHash", getStrH(Rd->stuff, "passHash")); | ||
4573 | Rd->stuff->putstr(Rd->stuff, "passwordSalt", getStrH(Rd->stuff, "passSalt")); | ||
4574 | accountWrite(Rd); | ||
4093 | } | 4575 | } |
4094 | Rd->stuff->putstr(Rd->stuff, "UUID", uuid); | ||
4095 | } | 4576 | } |
4577 | freeSesh(Rd, FALSE, wipe); | ||
4578 | newSesh(Rd, FALSE); | ||
4579 | return ret; | ||
4580 | } | ||
4096 | 4581 | ||
4582 | static int accountValidate(reqData *Rd, inputForm *iF, inputValue *iV) | ||
4583 | { | ||
4584 | int ret = 0; | ||
4585 | int c = accountRead(Rd, iF, iV); | ||
4586 | boolean wipe = FALSE; | ||
4587 | |||
4588 | if (1 != c) | ||
4589 | { | ||
4590 | bitch(Rd, "Cannot validate account.", "Account doesn't exist."); | ||
4591 | ret++; | ||
4592 | } | ||
4593 | else | ||
4594 | { | ||
4595 | Rd->stuff->putstr(Rd->stuff, "email", getStrH(Rd->database, "UserAccounts.Email")); | ||
4596 | Rd->stuff->putstr(Rd->stuff, "created", getStrH(Rd->database, "UserAccounts.Created")); | ||
4597 | Rd->stuff->putstr(Rd->stuff, "flags", getStrH(Rd->database, "UserAccounts.UserFlags")); | ||
4598 | Rd->stuff->putstr(Rd->stuff, "active", getStrH(Rd->database, "UserAccounts.active")); | ||
4599 | Rd->stuff->putstr(Rd->stuff, "passwordSalt", getStrH(Rd->database, "auth.passwordSalt")); | ||
4600 | Rd->stuff->putstr(Rd->stuff, "passwordHash", getStrH(Rd->database, "auth.passwordHash")); | ||
4601 | Rd->stuff->putstr(Rd->stuff, "name", getStrH(Rd->database, "Lua.name")); | ||
4602 | Rd->stuff->putstr(Rd->stuff, "DoB", getStrH(Rd->database, "Lua.DoB")); | ||
4603 | Rd->stuff->putstr(Rd->stuff, "agree", getStrH(Rd->database, "Lua.agree")); | ||
4604 | Rd->stuff->putstr(Rd->stuff, "adult", getStrH(Rd->database, "Lua.adult")); | ||
4605 | Rd->stuff->putstr(Rd->stuff, "aboutMe", getStrH(Rd->database, "Lua.aboutMe")); | ||
4606 | Rd->stuff->putstr(Rd->stuff, "vouched", getStrH(Rd->database, "Lua.vouched")); | ||
4607 | Rd->shs.level = -100; | ||
4608 | accountWrite(Rd); | ||
4609 | wipe = TRUE; | ||
4610 | } | ||
4611 | freeSesh(Rd, FALSE, wipe); | ||
4612 | newSesh(Rd, FALSE); | ||
4097 | return ret; | 4613 | return ret; |
4098 | } | 4614 | } |
4099 | 4615 | ||
4100 | 4616 | ||
4101 | void loginPage(reqData *Rd, char *message) | 4617 | static int accountView(reqData *Rd, inputForm *iF, inputValue *iV) |
4102 | { | 4618 | { |
4103 | char *name = xstrdup(getStrH(Rd->stuff, "name")), *linky = checkLinky(Rd); | 4619 | // TODO - this has to change when we are editing other peoples accounts. |
4620 | int ret = 0; | ||
4621 | int c = accountRead(Rd, iF, iV); | ||
4622 | boolean wipe = FALSE; | ||
4104 | 4623 | ||
4105 | Rd->stuff->remove(Rd->stuff, "UUID"); | 4624 | d("Sub accountView %s %s %s", getStrH(Rd->database, "UserAccounts.PrincipalID"), getStrH(Rd->database, "UserAccounts.FirstName"), getStrH(Rd->database, "UserAccounts.LastName")); |
4106 | HTMLheader(Rd->reply, "<!--#echo var=\"grid\" --> account manager"); | 4625 | if (1 != c) |
4107 | if (DEBUG) HTMLdebug(Rd->reply); | 4626 | { |
4108 | Rd->reply->addstrf(Rd->reply, "<h1><!--#echo var=\"grid\" --> account manager</h1>\n"); | 4627 | bitch(Rd, "Cannot view account.", "Account doesn't exist."); |
4109 | Rd->reply->addstr(Rd->reply, linky); | 4628 | ret++; |
4110 | free(linky); | 4629 | wipe = TRUE; |
4111 | if (0 != Rd->errors->size(Rd->messages)) | 4630 | Rd->shs.UUID = NULL; |
4112 | HTMLlist(Rd->reply, "messages -", Rd->messages); | 4631 | Rd->output = "accountLogin"; |
4113 | HTMLform(Rd->reply, "", Rd->shs.munchie); | 4632 | } |
4114 | HTMLtext(Rd->reply, "text", "name", "name", name, 42, 63, TRUE); | 4633 | else |
4115 | HTMLtext(Rd->reply, "password", "password", "password", "", 16, 0, TRUE); | 4634 | { |
4116 | Rd->reply->addstr(Rd->reply, "<p>Warning, the limit on password length is set by your viewer, some can't handle longer than 16 characters.</p>\n"); | 4635 | // Check password on POST if the session user is the same as the shown user, coz this is the page shown on login. |
4117 | Rd->reply->addstr(Rd->reply, "<p>While viewers will usually remember your name and password for you, you'll need to remember it for this web site to. " | 4636 | if ((strcmp("POST", Rd->Method) == 0) && (strcmp(Rd->shs.UUID, getStrH(Rd->database, "UserAccounts.PrincipalID")) == 0)) |
4118 | "I highly recommend using a password manager. KeePass and it's variations is a great password manager.</p>\n"); | 4637 | { |
4119 | Rd->reply->addstrf(Rd->reply, "<input type='submit' disabled style='display: none' aria-hidden='true' />\n"); // Stop Enter key on text fields triggering the first submit button. | 4638 | char *h = checkSLOSpassword(Rd, getStrH(Rd->database, "auth.passwordSalt"), getStrH(Rd->body, "password"), getStrH(Rd->database, "auth.passwordHash"), "Login failed."); |
4120 | HTMLbutton(Rd->reply, "login"); | 4639 | if (NULL == h) |
4121 | HTMLbutton(Rd->reply, "create"); | 4640 | { |
4122 | if (0 != Rd->errors->size(Rd->errors)) | 4641 | ret++; |
4123 | HTMLlist(Rd->reply, "errors -", Rd->errors); | 4642 | wipe = TRUE; |
4124 | Rd->reply->addstrf(Rd->reply, "<p>%s</p>\n", message); | 4643 | Rd->shs.UUID = NULL; |
4125 | HTMLfooter(Rd->reply); | 4644 | Rd->output = "accountLogin"; |
4126 | free(name); | 4645 | } |
4646 | else | ||
4647 | { | ||
4648 | free(h); | ||
4649 | I("Logged on %s %s Level %d %s", Rd->shs.UUID, getStrH(Rd->stuff, "name"), Rd->shs.level, getLevel(Rd)); | ||
4650 | } | ||
4651 | } | ||
4652 | } | ||
4653 | freeSesh(Rd, FALSE, wipe); | ||
4654 | newSesh(Rd, FALSE); | ||
4655 | |||
4656 | return ret; | ||
4127 | } | 4657 | } |
4658 | static int accountEdit(reqData *Rd, inputForm *iF, inputValue *iV) | ||
4659 | { | ||
4660 | int ret = 0; | ||
4661 | int c = accountRead(Rd, iF, iV); | ||
4128 | 4662 | ||
4129 | void accountCreationPage(reqData *Rd, char *message) | 4663 | d("Sub accountView %s %s %s", getStrH(Rd->database, "UserAccounts.PrincipalID"), getStrH(Rd->database, "UserAccounts.FirstName"), getStrH(Rd->database, "UserAccounts.LastName")); |
4664 | if (1 != c) | ||
4665 | { | ||
4666 | bitch(Rd, "Cannot edit account.", "Account doesn't exist."); | ||
4667 | ret++; | ||
4668 | } | ||
4669 | else | ||
4670 | { | ||
4671 | // check if logged in user is allowed to make these changes | ||
4672 | // update user record | ||
4673 | } | ||
4674 | return ret; | ||
4675 | } | ||
4676 | static int accountExplore(reqData *Rd, inputForm *iF, inputValue *iV) | ||
4130 | { | 4677 | { |
4131 | char *name = getStrH(Rd->body, "name"), *linky = checkLinky(Rd); | 4678 | int ret = 0; |
4132 | char *toke_n_munchie = getCookie(Rd->Rcookies, "toke_n_munchie"); | 4679 | // get a list of user records |
4133 | char *tmp = xmalloc(16), *t; | 4680 | return ret; |
4134 | int i, d; | 4681 | } |
4682 | static int accountOut(reqData *Rd, inputForm *iF, inputValue *iV) | ||
4683 | { | ||
4684 | int ret = 0; | ||
4685 | int c = accountRead(Rd, iF, iV); | ||
4135 | 4686 | ||
4136 | if ('\0' == name[0]) | 4687 | if (1 != c) |
4137 | name = getStrH(Rd->stuff, "name"); | 4688 | { |
4689 | // bitch(Rd, "Cannot logout account.", "Account doesn't exist."); | ||
4690 | // ret++; | ||
4691 | } | ||
4692 | else | ||
4693 | { | ||
4694 | // log the user out if they are logged in | ||
4695 | Rd->shs.UUID = NULL; | ||
4696 | Rd->output = "accountLogin"; | ||
4697 | } | ||
4138 | 4698 | ||
4139 | // TODO - eww lots of memory leaks here. | 4699 | freeSesh(Rd, FALSE, TRUE); |
4140 | // TODO - need to check if qLibc does it's own free() calls, and fill in the gaps for when it doesn't. | 4700 | newSesh(Rd, FALSE); |
4141 | HTMLheader(Rd->reply, "<!--#echo var=\"grid\" --> account manager"); | 4701 | return ret; |
4142 | if (DEBUG) HTMLdebug(Rd->reply); | 4702 | } |
4143 | Rd->reply->addstrf(Rd->reply, "<h1><!--#echo var=\"grid\" --> account manager</h1>\n"); | ||
4144 | Rd->reply->addstrf(Rd->reply, "<h2>Creating <!--#echo var=\"grid\" --> account for %s</h2>\n", name); | ||
4145 | Rd->reply->addstr(Rd->reply, linky); | ||
4146 | free(linky); | ||
4147 | if (0 != Rd->errors->size(Rd->messages)) | ||
4148 | HTMLlist(Rd->reply, "messages -", Rd->messages); | ||
4149 | // TODO - set this to autocomplete="off". | ||
4150 | // TODO - autofill most fields on error and redisplay. | ||
4151 | HTMLform(Rd->reply, "", Rd->shs.munchie); | ||
4152 | HTMLhidden(Rd->reply, "name", name); | ||
4153 | HTMLhidden(Rd->reply, "UUID", getStrH(Rd->stuff, "UUID")); | ||
4154 | HTMLtext(Rd->reply, "password", "Re-enter your password", "password", "", 16, 0, FALSE); | ||
4155 | Rd->reply->addstr(Rd->reply, "<p>Warning, the limit on password length is set by your viewer, some can't handle longer than 16 characters.</p>\n"); | ||
4156 | Rd->reply->addstr(Rd->reply, "<p>While viewers will usually remember your name and password for you, you'll need to remember it for this web site to. " | ||
4157 | "I highly recommend using a password manager. KeePass and it's variations is a great password manager.</p>\n"); | ||
4158 | HTMLtext(Rd->reply, "email", "email", "email", getStrH(Rd->body, "email"), 42, 254, FALSE); | ||
4159 | HTMLtext(Rd->reply, "email", "Repeat your email, to be sure you got it correct", "emayl", getStrH(Rd->body, "emayl"), 42, 254, FALSE); | ||
4160 | Rd->reply->addstr(Rd->reply, "<p>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.</p>\n"); | ||
4161 | Rd->reply->addstr(Rd->reply, "<table><tr><td>\n"); | ||
4162 | HTMLselect(Rd->reply, "Date of birth", "year"); | ||
4163 | t = getStrH(Rd->body, "year"); | ||
4164 | if (NULL == t) | ||
4165 | d = -1; | ||
4166 | else | ||
4167 | d = atoi(t); | ||
4168 | HTMLoption(Rd->reply, xstrdup(""), FALSE); | ||
4169 | for (i = 1900; i <= 2020; i++) | ||
4170 | { | ||
4171 | boolean sel = FALSE; | ||
4172 | 4703 | ||
4173 | if (i == d) | ||
4174 | sel = TRUE; | ||
4175 | sprintf(tmp, "%d", i); | ||
4176 | HTMLoption(Rd->reply, xstrdup(tmp), sel); | ||
4177 | } | ||
4178 | HTMLselectEnd(Rd->reply); | ||
4179 | Rd->reply->addstr(Rd->reply, "</tt><td>"); | ||
4180 | HTMLselect(Rd->reply, NULL, "month"); | ||
4181 | t = getStrH(Rd->body, "month"); | ||
4182 | HTMLoption(Rd->reply, xstrdup(""), FALSE); | ||
4183 | for (i = 0; i <= 11; i++) | ||
4184 | { | ||
4185 | boolean sel = FALSE; | ||
4186 | 4704 | ||
4187 | if ((NULL != t) && (strcmp(t, months[i]) == 0)) | 4705 | qhashtbl_t *accountPages = NULL; |
4188 | sel = TRUE; | 4706 | inputForm *newInputForm(char *name, char *title, char *help, inputFormShowFunc web, inputFormShowFunc eWeb) |
4189 | HTMLoption(Rd->reply, months[i], sel); | 4707 | { |
4190 | } | 4708 | inputForm *ret = xmalloc(sizeof(inputForm)); |
4191 | HTMLselectEnd(Rd->reply); | 4709 | |
4192 | Rd->reply->addstr(Rd->reply, "</td></tr></table>\n"); | 4710 | d("newInputForm(%s)", name); |
4193 | HTMLcheckBox(Rd->reply, "adult", "I'm allegedly an adult in my country.", !strcmp("on", getStrH(Rd->body, "adult")), TRUE); | 4711 | ret->name = name; ret->title = title; ret->help = help; |
4194 | HTMLcheckBox(Rd->reply, "agree", "I accept the Terms of Service.", !strcmp("on", getStrH(Rd->body, "agree")), TRUE); | 4712 | ret->web = web; ret->eWeb = eWeb; |
4195 | Rd->reply->addstrf(Rd->reply, "<h2>Terms of Service</h2><pre>%s</pre>\n", getStrH(Rd->configs, "ToS")); | 4713 | ret->fields = qlisttbl(QLISTTBL_THREADSAFE | QLISTTBL_UNIQUE | QLISTTBL_LOOKUPFORWARD); |
4196 | // For maxlength - the MySQL database field is type text, which has a max length of 64 Kilobytes byets, but characters might take up 1 - 4 bytes, and maxlength is in characters. | 4714 | ret->subs = qhashtbl(0, 0); |
4197 | // For rows and cols, seems a bit broken, I ask for 5/42, I get 6,36. In world it seems to be 7,46 | 4715 | accountPages->put(accountPages, ret->name, ret, sizeof(inputForm)); |
4198 | // TODO - check against the limit for in world profiles, coz this will become that. | 4716 | free(ret); |
4199 | // TODO - validate aboutMe, it should not be empty, and should not be longer than 64 kilobytes. | 4717 | return accountPages->get(accountPages, name, NULL, false); |
4200 | HTMLtextArea(Rd->reply, "aboutMe", "About me", 7, 50, 4, 16384, "Describe yourself here.", "off", "true", "soft", getStrH(Rd->body, "aboutMe"), FALSE); | ||
4201 | // TODO - upload an icon / profile picture. | ||
4202 | Rd->reply->addstrf(Rd->reply, "<input type='submit' disabled style='display: none' aria-hidden='true' />\n"); // Stop Enter key on text fields triggering the first submit button. | ||
4203 | HTMLbutton(Rd->reply, "confirm"); | ||
4204 | HTMLbutton(Rd->reply, "cancel"); | ||
4205 | if (0 != Rd->errors->size(Rd->errors)) | ||
4206 | HTMLlist(Rd->reply, "errors -", Rd->errors); | ||
4207 | Rd->reply->addstrf(Rd->reply, "<p>%s</p>\n", message); | ||
4208 | HTMLfooter(Rd->reply); | ||
4209 | } | 4718 | } |
4210 | 4719 | ||
4211 | void loggedOnPage(reqData *Rd, char *message) | 4720 | inputField *addInputField(inputForm *iF, signed char type, char *name, char *title, char *help, inputFieldValidFunc validate, inputFieldShowFunc web) |
4212 | { | 4721 | { |
4213 | char *name = getStrH(Rd->stuff, "name"), *linky = checkLinky(Rd); | 4722 | inputField *ret = xzalloc(sizeof(inputField)); |
4214 | char *toke_n_munchie = getCookie(Rd->Rcookies, "toke_n_munchie"); | ||
4215 | 4723 | ||
4216 | HTMLheader(Rd->reply, "<!--#echo var=\"grid\" --> account manager"); | 4724 | //d("addInputField(%s, %s)", iF->name, name); |
4217 | if (DEBUG) HTMLdebug(Rd->reply); | 4725 | ret->name = name; ret->title = title; ret->help = help; |
4218 | Rd->reply->addstrf(Rd->reply, "<h1><!--#echo var=\"grid\" --> account manager</h1>\n"); | 4726 | ret->validate = validate; ret->web = web; ret->type = type; |
4219 | Rd->reply->addstrf(Rd->reply, "<h2><!--#echo var=\"grid\" --> account for %s</h2>\n", name); | 4727 | ret->flags = FLD_EDITABLE; |
4220 | Rd->reply->addstr(Rd->reply, linky); | 4728 | iF->fields->put(iF->fields, ret->name, ret, sizeof(inputField)); |
4221 | free(linky); | 4729 | free(ret); |
4222 | if (0 != Rd->errors->size(Rd->messages)) | 4730 | return iF->fields->get(iF->fields, name, NULL, false); |
4223 | HTMLlist(Rd->reply, "messages -", Rd->messages); | 4731 | } |
4224 | HTMLform(Rd->reply, "", Rd->shs.munchie); | 4732 | |
4225 | HTMLhidden(Rd->reply, "name", name); | 4733 | void inputFieldExtra(inputField *ret, signed char flags, short viewLength, short maxLength) |
4226 | HTMLhidden(Rd->reply, "UUID", getStrH(Rd->stuff, "UUID")); | 4734 | { |
4227 | HTMLtext(Rd->reply, "email", "email", "email", getStrH(Rd->database, "UserAccounts.Email"), 42, 254, FALSE); | 4735 | ret->flags = flags; |
4228 | HTMLtext(Rd->reply, "Old password", "password", "password", "", 16, 0, FALSE); | 4736 | ret->viewLength = viewLength; ret->maxLength = maxLength; |
4229 | Rd->reply->addstr(Rd->reply, "<p>Warning, the limit on password length is set by your viewer, some can't handle longer than 16 characters.</p>\n"); | ||
4230 | // HTMLtext(Rd->reply, "title", "text", "title", getStrH(Rh->stuff, "title"), 16, 64, TRUE); | ||
4231 | HTMLselect(Rd->reply, "type", "type"); | ||
4232 | HTMLoption(Rd->reply, "", false); | ||
4233 | HTMLoption(Rd->reply, "newbie", true); | ||
4234 | HTMLoption(Rd->reply, "validated", true); | ||
4235 | HTMLoption(Rd->reply, "vouched for", true); | ||
4236 | HTMLoption(Rd->reply, "approved", true); | ||
4237 | HTMLoption(Rd->reply, "disabled", false); | ||
4238 | HTMLoption(Rd->reply, "god", false); | ||
4239 | HTMLselectEnd(Rd->reply); | ||
4240 | Rd->reply->addstrf(Rd->reply, "<input type='submit' disabled style='display: none' aria-hidden='true' />\n"); // Stop Enter key on text fields triggering the first submit button. | ||
4241 | HTMLbutton(Rd->reply, "delete"); | ||
4242 | HTMLbutton(Rd->reply, "list"); | ||
4243 | HTMLbutton(Rd->reply, "logout"); | ||
4244 | HTMLbutton(Rd->reply, "update"); | ||
4245 | if (0 != Rd->errors->size(Rd->errors)) | ||
4246 | HTMLlist(Rd->reply, "errors -", Rd->errors); | ||
4247 | HTMLfooter(Rd->reply); | ||
4248 | } | 4737 | } |
4249 | 4738 | ||
4739 | void addSession(inputForm *iF) | ||
4740 | { | ||
4741 | inputField *fld, **flds = xzalloc(3 * sizeof(*flds)); | ||
4742 | |||
4743 | //d("addSession(%s)", iF->name); | ||
4744 | flds[0] = addInputField(iF, LUA_TSTRING, "hashish", "hashish", "", sessionValidate, sessionWeb); | ||
4745 | inputFieldExtra(flds[0], FLD_HIDDEN, 0, 0); | ||
4746 | flds[1] = addInputField(iF, LUA_TSTRING, "toke_n_munchie", "toke_n_munchie", "", sessionValidate, sessionWeb); | ||
4747 | inputFieldExtra(flds[1], FLD_HIDDEN, 0, 0); | ||
4748 | fld = addInputField(iF, LUA_TGROUP, "sessionGroup", "sessionGroup", "", sessionValidate, sessionWeb); | ||
4749 | inputFieldExtra(fld, FLD_HIDDEN, 0, 0); | ||
4750 | fld->group = flds; | ||
4751 | flds[0]->group = flds; | ||
4752 | flds[1]->group = flds; | ||
4753 | } | ||
4754 | |||
4755 | void addEmailFields(inputForm *iF) | ||
4756 | { | ||
4757 | inputField *fld, **flds = xzalloc(3 * sizeof(*flds)); | ||
4758 | |||
4759 | flds[0] = addInputField(iF, LUA_TEMAIL, "email", "email", NULL, emailValidate, emailWeb); | ||
4760 | inputFieldExtra(flds[0], FLD_EDITABLE, 42, 254); | ||
4761 | flds[1] = addInputField(iF, LUA_TEMAIL, "emayl", "Re-enter your email, to be sure you got it correct", | ||
4762 | "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.", emailValidate, emailWeb); | ||
4763 | inputFieldExtra(flds[1], FLD_EDITABLE, 42, 254); | ||
4764 | fld = addInputField(iF, LUA_TGROUP, "emailGroup", "emailGroup", "", emailValidate, NULL); | ||
4765 | inputFieldExtra(fld, FLD_HIDDEN, 0, 0); | ||
4766 | fld->group = flds; | ||
4767 | flds[0]->group = flds; | ||
4768 | flds[1]->group = flds; | ||
4769 | } | ||
4770 | |||
4771 | void addDoBFields(inputForm *iF) | ||
4772 | { | ||
4773 | inputField *fld, **flds = xzalloc(3 * sizeof(*flds)); | ||
4774 | |||
4775 | flds[0] = addInputField(iF, LUA_TSTRING, "DoByear", "year", NULL, DoBValidate, DoByWeb); | ||
4776 | flds[1] = addInputField(iF, LUA_TSTRING, "DoBmonth", "month", NULL, DoBValidate, DoBmWeb); | ||
4777 | fld = addInputField(iF, LUA_TGROUP, "DoBGroup", "DoBGroup", "", DoBValidate, DoBWeb); | ||
4778 | inputFieldExtra(fld, FLD_HIDDEN, 0, 0); | ||
4779 | fld->group = flds; | ||
4780 | flds[0]->group = flds; | ||
4781 | flds[1]->group = flds; | ||
4782 | } | ||
4783 | |||
4784 | void addLegalFields(inputForm *iF) | ||
4785 | { | ||
4786 | inputField *fld, **flds = xzalloc(3 * sizeof(*flds)); | ||
4787 | |||
4788 | flds[0] = addInputField(iF, LUA_TBOOLEAN, "adult", "I'm allegedly an adult in my country.", NULL, legalValidate, adultWeb); | ||
4789 | flds[1] = addInputField(iF, LUA_TBOOLEAN, "agree", "I accept the Terms of Service.", NULL, legalValidate, agreeWeb); | ||
4790 | fld = addInputField(iF, LUA_TGROUP, "legalGroup", "legalGroup", "", legalValidate, legalWeb); | ||
4791 | inputFieldExtra(fld, FLD_HIDDEN, 0, 0); | ||
4792 | fld->group = flds; | ||
4793 | flds[0]->group = flds; | ||
4794 | flds[1]->group = flds; | ||
4795 | } | ||
4796 | |||
4797 | inputSub *addSubmit(inputForm *iF, char *name, char *title, char *help, inputSubmitFunc submit, char *output) | ||
4798 | { | ||
4799 | inputSub *ret = xmalloc(sizeof(inputSub)); | ||
4800 | |||
4801 | //d("addSubmit(%s, %s)", iF->name, name); | ||
4802 | ret->name = name; ret->title = title; ret->help = help; ret->submit = submit; ret->outputForm = output; | ||
4803 | iF->subs->put(iF->subs, ret->name, ret, sizeof(inputSub)); | ||
4804 | free(ret); | ||
4805 | return iF->subs->get(iF->subs, name, NULL, false); | ||
4806 | } | ||
4807 | |||
4808 | |||
4809 | /* There should be some precedence for values overriding values here. | ||
4810 | https://www.w3.org/standards/webarch/protocols | ||
4811 | "This intro text is boilerplate for the beta release of w3.org." Fucking useless. Pffft | ||
4812 | https://www.w3.org/Protocols/ | ||
4813 | Still nothing official, though the ENV / HEADER stuff tends to be about the protocol things, and cookies / body / queries are about the data things. | ||
4814 | http://docs.gantry.org/gantry4/advanced/setby | ||
4815 | Says that query overrides cookies, but that might be just for their platform. | ||
4816 | https://framework.zend.com/manual/1.11/en/zend.controller.request.html | ||
4817 | Says - "1. GET, 2. POST, 3. COOKIE, 4. SERVER, 5. ENV." | ||
4818 | We don't actually get the headers directly, it's all sent via the env. | ||
4819 | |||
4820 | URL query Values actually provided by the user in the FORM, and other things. | ||
4821 | POST body Values actually provided by the user in the FORM. | ||
4822 | cookies | ||
4823 | https://stackoverflow.com/questions/4056306/how-to-handle-multiple-cookies-with-the-same-name | ||
4824 | |||
4825 | headers includes HTTP_COOKIE and QUERY_STRING | ||
4826 | env includes headers and HTTP_COOKIE and QUERY_STRING | ||
4827 | |||
4828 | database Since all of the above are for updating the database anyway, this goes on the bottom, overridden by all. | ||
4829 | Though be wary of security stuff. | ||
4830 | |||
4831 | Sending cookie headers is a special case, multiples can be sent, otherwise headers are singletons, only send one for each name. | ||
4832 | */ | ||
4833 | char *sourceTypes[] = | ||
4834 | { | ||
4835 | "cookies", | ||
4836 | "body", | ||
4837 | "queries", | ||
4838 | "stuff" | ||
4839 | }; | ||
4840 | |||
4841 | static int collectFields(reqData *Rd, inputForm *iF, inputValue *iV, int t) | ||
4842 | { | ||
4843 | int i = 0, j; | ||
4844 | qlisttbl_obj_t obj; | ||
4845 | |||
4846 | memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call | ||
4847 | iF->fields->lock(iF->fields); | ||
4848 | while(iF->fields->getnext(iF->fields, &obj, NULL, false) == true) | ||
4849 | { | ||
4850 | inputField *fld = (inputField *) obj.data; | ||
4851 | |||
4852 | //if (0 > t) | ||
4853 | // d("Collecting %d %s - %s", t, iF->name, fld->name); | ||
4854 | //else | ||
4855 | // d("Collecting %s %s - %s", sourceTypes[t], iF->name, fld->name); | ||
4856 | iV[i].field = fld; | ||
4857 | if (LUA_TGROUP == fld->type) | ||
4858 | { | ||
4859 | if (0 >= t) | ||
4860 | { | ||
4861 | j = 0; | ||
4862 | // If it's a group, number the members relative to the group field. | ||
4863 | // Assume the members for this group are the previous ones. | ||
4864 | while (iV[i].field->group[j]) | ||
4865 | { | ||
4866 | j++; | ||
4867 | iV[i - j].index = j; | ||
4868 | } | ||
4869 | } | ||
4870 | } | ||
4871 | else | ||
4872 | { | ||
4873 | char *vl = NULL; | ||
4874 | |||
4875 | switch (t) | ||
4876 | { | ||
4877 | // We don't get the cookies metadata. | ||
4878 | case 0 : vl = Rd->cookies->getstr(Rd->cookies, obj.name, false); break; | ||
4879 | case 1 : vl = Rd->body-> getstr(Rd->body, obj.name, false); break; | ||
4880 | case 2 : vl = Rd->queries->getstr(Rd->queries, obj.name, false); break; | ||
4881 | case 3 : vl = Rd->queries->getstr(Rd->stuff, obj.name, false); break; | ||
4882 | // Rd->stuff comes from the database reads and calculated stuff, which we do later with this new architecture. The old version validated Rd->stuff as well. | ||
4883 | // It was doing that so we can pick up any UUID, validate it, and read in relevant records. | ||
4884 | // For newbies their UUID was generated during the validation of name. | ||
4885 | // Should still validate that it's long enough and in the correct format, coz that's coming from RD->body sometimes. | ||
4886 | // The rest can happen in the submit functions now. | ||
4887 | default: break; | ||
4888 | } | ||
4889 | if ((NULL != iV[i].value) && (NULL != vl)) | ||
4890 | { | ||
4891 | if (strcmp(vl, iV[i].value) != 0) | ||
4892 | W("Collected %s value for %s - %s from %s overriding value from %s", sourceTypes[t], iF->name, fld->name, sourceTypes[t], sourceTypes[iV[i].source]); | ||
4893 | else | ||
4894 | W("Collected %s value for %s - %s from %s same as value from %s", sourceTypes[t], iF->name, fld->name, sourceTypes[t], sourceTypes[iV[i].source]); | ||
4895 | } | ||
4896 | if (NULL != vl) | ||
4897 | { | ||
4898 | iV[i].source = t; | ||
4899 | iV[i].value = vl; | ||
4900 | D("Collected %s value for %s - %s = %s", sourceTypes[t], iF->name, fld->name, vl); | ||
4901 | } | ||
4902 | } | ||
4903 | i++; | ||
4904 | } | ||
4905 | iF->fields->unlock(iF->fields); | ||
4906 | return i; | ||
4907 | } | ||
4908 | |||
4909 | |||
4250 | void listPage(reqData *Rd, char *message) | 4910 | void listPage(reqData *Rd, char *message) |
4251 | { | 4911 | { |
4252 | // TODO - should check if the user is a god before allowing this. | 4912 | // TODO - should check if the user is a god before allowing this. |
@@ -4267,8 +4927,9 @@ void listPage(reqData *Rd, char *message) | |||
4267 | NULL, NULL, "Name"), | 4927 | NULL, NULL, "Name"), |
4268 | "member accounts", NULL, NULL); | 4928 | "member accounts", NULL, NULL); |
4269 | HTMLform(Rd->reply, "", Rd->shs.munchie); | 4929 | HTMLform(Rd->reply, "", Rd->shs.munchie); |
4930 | HTMLhidden(Rd->reply, "form", "accountExplore"); | ||
4270 | HTMLhidden(Rd->reply, "name", name); | 4931 | HTMLhidden(Rd->reply, "name", name); |
4271 | HTMLhidden(Rd->reply, "UUID", getStrH(Rd->stuff, "UUID")); | 4932 | HTMLhidden(Rd->reply, "UUID", Rd->shs.UUID); |
4272 | Rd->reply->addstrf(Rd->reply, "<input type='submit' disabled style='display: none' aria-hidden='true' />\n"); // Stop Enter key on text fields triggering the first submit button. | 4933 | Rd->reply->addstrf(Rd->reply, "<input type='submit' disabled style='display: none' aria-hidden='true' />\n"); // Stop Enter key on text fields triggering the first submit button. |
4273 | HTMLbutton(Rd->reply, "me"); | 4934 | HTMLbutton(Rd->reply, "me"); |
4274 | HTMLbutton(Rd->reply, "logout"); | 4935 | HTMLbutton(Rd->reply, "logout"); |
@@ -4278,91 +4939,25 @@ void listPage(reqData *Rd, char *message) | |||
4278 | HTMLfooter(Rd->reply); | 4939 | HTMLfooter(Rd->reply); |
4279 | } | 4940 | } |
4280 | 4941 | ||
4942 | |||
4281 | void account_html(char *file, reqData *Rd, HTMLfile *thisFile) | 4943 | void account_html(char *file, reqData *Rd, HTMLfile *thisFile) |
4282 | { | 4944 | { |
4945 | inputForm *iF; | ||
4946 | inputField *fld; | ||
4947 | inputSub *sub; | ||
4283 | boolean isGET = FALSE; | 4948 | boolean isGET = FALSE; |
4284 | int e = 0; | 4949 | int e = 0, t = 0, i, j; |
4285 | char *doit = getStrH(Rd->body, "doit"); | 4950 | char *doit = getStrH(Rd->body, "doit"), *form = getStrH(Rd->body, "form"); |
4286 | |||
4287 | C("Starting dynamic page %s", file); | ||
4288 | Rd->func = NULL; | ||
4289 | |||
4290 | if (NULL == fieldValidFuncs) | ||
4291 | { | ||
4292 | fieldValidFuncs = qlisttbl(QLISTTBL_LOOKUPFORWARD | QLISTTBL_THREADSAFE | QLISTTBL_UNIQUE); | ||
4293 | newValidFunc("hashish", "session", (fieldValidFunc) validateSesh); | ||
4294 | newValidFunc("toke_n_munchie", "session", (fieldValidFunc) validateSesh); | ||
4295 | newValidFunc("UUID", "UUID", (fieldValidFunc) validateUUID); | ||
4296 | newValidFunc("name", "name", (fieldValidFunc) validateName); | ||
4297 | newValidFunc("password", "password", (fieldValidFunc) validatePassword); | ||
4298 | newValidFunc("email", "email", (fieldValidFunc) validateEmail); | ||
4299 | newValidFunc("emayl", "email", (fieldValidFunc) validateEmail); | ||
4300 | newValidFunc("year", "DoB", (fieldValidFunc) validateDoB); | ||
4301 | newValidFunc("month", "DoB", (fieldValidFunc) validateDoB); | ||
4302 | newValidFunc("adult", "legal", (fieldValidFunc) validateLegal); | ||
4303 | newValidFunc("agree", "legal", (fieldValidFunc) validateLegal); | ||
4304 | newValidFunc("aboutMe", "about me", (fieldValidFunc) validateAboutMe); | ||
4305 | } | ||
4306 | if (NULL == buildPages) | ||
4307 | { | ||
4308 | buildPages = qhashtbl(0, 0); | ||
4309 | newBuildPage("login", (pageBuildFunction) loggedOnPage, (pageBuildFunction) loginPage); | ||
4310 | newBuildPage("cancel", (pageBuildFunction) loginPage, (pageBuildFunction) loginPage); | ||
4311 | newBuildPage("logout", (pageBuildFunction) loginPage, (pageBuildFunction) loginPage); | ||
4312 | newBuildPage("create", (pageBuildFunction) accountCreationPage, (pageBuildFunction) loginPage); | ||
4313 | newBuildPage("confirm", (pageBuildFunction) loggedOnPage, (pageBuildFunction) accountCreationPage); | ||
4314 | newBuildPage("me", (pageBuildFunction) loggedOnPage, (pageBuildFunction) loginPage); | ||
4315 | newBuildPage("update", (pageBuildFunction) loggedOnPage, (pageBuildFunction) loginPage); | ||
4316 | newBuildPage("delete", (pageBuildFunction) loginPage, (pageBuildFunction) loginPage); | ||
4317 | newBuildPage("list", (pageBuildFunction) listPage, (pageBuildFunction) loginPage); | ||
4318 | } | ||
4319 | 4951 | ||
4320 | if ('\0' == doit[0]) | 4952 | if (NULL == accountLevels) |
4321 | doit = getStrH(Rd->cookies, "doit"); | ||
4322 | if ('\0' == doit[0]) | ||
4323 | doit = "logout"; | ||
4324 | if ('\0' != doit[0]) | ||
4325 | { | ||
4326 | setCookie(Rd, "doit", doit); | ||
4327 | Rd->doit = doit; | ||
4328 | } | ||
4329 | |||
4330 | e += validateThings(Rd, doit, "cookies", Rd->cookies); | ||
4331 | e += validateThings(Rd, doit, "body", Rd->body); | ||
4332 | e += validateThings(Rd, doit, "queries", Rd->queries); | ||
4333 | e += validateThings(Rd, doit, "session", Rd->stuff); | ||
4334 | |||
4335 | if (NULL == Rd->func) | ||
4336 | { | ||
4337 | buildPage *bp = buildPages->get(buildPages, doit, NULL, false); | ||
4338 | if (bp) | ||
4339 | { | ||
4340 | if (e) | ||
4341 | { | ||
4342 | Rd->func = bp->eFunc; | ||
4343 | E("Got page builder ERROR function for %s, coz of %d errors.", doit, e); | ||
4344 | } | ||
4345 | else | ||
4346 | { | ||
4347 | Rd->func = bp->func; | ||
4348 | D("Got page builder function for %s.", doit); | ||
4349 | } | ||
4350 | } | ||
4351 | } | ||
4352 | if (NULL == Rd->func) | ||
4353 | Rd->func = (pageBuildFunction) loginPage; | ||
4354 | |||
4355 | if (strcmp("https", Rd->Scheme) != 0) | ||
4356 | { | 4953 | { |
4357 | Rd->Rheaders->putstr (Rd->Rheaders, "Status", "301 Moved Permanently"); | 4954 | accountLevels = qlisttbl(QLISTTBL_LOOKUPFORWARD | QLISTTBL_THREADSAFE | QLISTTBL_UNIQUE); |
4358 | Rd->Rheaders->putstrf(Rd->Rheaders, "Location", "https://%s%s", Rd->Host, Rd->RUri); | 4955 | accountLevels->putstr(accountLevels, "-256", "disabled"); |
4359 | Rd->reply->addstrf(Rd->reply, "<html><title>404 Unknown page</title><head>" | 4956 | accountLevels->putstr(accountLevels, "-200", "newbie"); |
4360 | "<meta http-equiv='refresh' content='0; URL=https://%s%s' />" | 4957 | accountLevels->putstr(accountLevels, "-100", "validated"); |
4361 | "</head><body>You should get redirected to <a href='https://%s%s'>https://%s%s</a></body></html>", | 4958 | accountLevels->putstr(accountLevels, "-50", "vouched for"); |
4362 | Rd->Host, Rd->RUri, Rd->Host, Rd->RUri, Rd->Host, Rd->RUri | 4959 | accountLevels->putstr(accountLevels, "1", "approved"); |
4363 | ); | 4960 | accountLevels->putstr(accountLevels, "200", "god"); |
4364 | D("Redirecting dynamic page %s -> https://%s%s", file, Rd->Host, Rd->RUri); | ||
4365 | return; | ||
4366 | } | 4961 | } |
4367 | 4962 | ||
4368 | // Check "Origin" header and /or HTTP_REFERER header. | 4963 | // Check "Origin" header and /or HTTP_REFERER header. |
@@ -4380,7 +4975,7 @@ void account_html(char *file, reqData *Rd, HTMLfile *thisFile) | |||
4380 | { | 4975 | { |
4381 | bitch(Rd, "Invalid referer.", ref); | 4976 | bitch(Rd, "Invalid referer.", ref); |
4382 | D("Invalid referer - %s isn't %s", ref, href); | 4977 | D("Invalid referer - %s isn't %s", ref, href); |
4383 | Rd->func = (pageBuildFunction) loginPage; | 4978 | form = "accountLogin"; |
4384 | } | 4979 | } |
4385 | free(href); | 4980 | free(href); |
4386 | } | 4981 | } |
@@ -4393,66 +4988,260 @@ void account_html(char *file, reqData *Rd, HTMLfile *thisFile) | |||
4393 | { | 4988 | { |
4394 | bitch(Rd, "Invalid HOST.", ref); | 4989 | bitch(Rd, "Invalid HOST.", ref); |
4395 | D("Invalid HOST - %s isn't %s", ref, href); | 4990 | D("Invalid HOST - %s isn't %s", ref, href); |
4396 | Rd->func = (pageBuildFunction) loginPage; | 4991 | form = "accountLogin"; |
4992 | } | ||
4993 | |||
4994 | // Redirect to HTTPS if it's HTTP. | ||
4995 | if (strcmp("https", Rd->Scheme) != 0) | ||
4996 | { | ||
4997 | Rd->Rheaders->putstr (Rd->Rheaders, "Status", "301 Moved Permanently"); | ||
4998 | Rd->Rheaders->putstrf(Rd->Rheaders, "Location", "https://%s%s", Rd->Host, Rd->RUri); | ||
4999 | Rd->reply->addstrf(Rd->reply, "<html><title>404 Unknown page</title><head>" | ||
5000 | "<meta http-equiv='refresh' content='0; URL=https://%s%s' />" | ||
5001 | "</head><body>You should get redirected to <a href='https://%s%s'>https://%s%s</a></body></html>", | ||
5002 | Rd->Host, Rd->RUri, Rd->Host, Rd->RUri, Rd->Host, Rd->RUri | ||
5003 | ); | ||
5004 | D("Redirecting dynamic page %s -> https://%s%s", file, Rd->Host, Rd->RUri); | ||
5005 | return; | ||
5006 | } | ||
5007 | |||
5008 | // Create the dynamic web pages for account.html. | ||
5009 | if (NULL == accountPages) | ||
5010 | { | ||
5011 | accountPages = qhashtbl(0, 0); | ||
5012 | |||
5013 | |||
5014 | iF = newInputForm("accountAdd", "", NULL, accountAddWeb, accountLoginWeb); | ||
5015 | addSession(iF); | ||
5016 | // fld = addInputField(iF, LUA_TSTRING, "UUID", "UUID", NULL, UUIDValidate, UUIDWeb); | ||
5017 | // inputFieldExtra(fld, FLD_HIDDEN, 0, 0); | ||
5018 | fld = addInputField(iF, LUA_TSTRING, "name", "name", NULL, nameValidate, nameWeb); | ||
5019 | inputFieldExtra(fld, FLD_EDITABLE | FLD_REQUIRED, 42, 63); | ||
5020 | fld = addInputField(iF, LUA_TPASSWORD, "psswrd", "Re-enter your password", | ||
5021 | "Warning, the limit on password length is set by your viewer, some can't handle longer than 16 characters.", passwordValidate, passwordWeb); | ||
5022 | inputFieldExtra(fld, FLD_EDITABLE, 16, 0); | ||
5023 | addEmailFields(iF); | ||
5024 | addDoBFields(iF); | ||
5025 | addLegalFields(iF); | ||
5026 | fld = addInputField(iF, LUA_TSTRING, "ToS", "Terms of Service", "", NULL, ToSWeb); | ||
5027 | fld = addInputField(iF, LUA_TSTRING, "aboutMe", "About me", NULL, aboutMeValidate, aboutMeWeb); | ||
5028 | inputFieldExtra(fld, FLD_EDITABLE, 50, 16384); | ||
5029 | addSubmit(iF, "confirm", "confirm", NULL, accountAdd, "accountView"); | ||
5030 | addSubmit(iF, "cancel", "cancel", NULL, accountOut, "accountLogin"); | ||
5031 | |||
5032 | |||
5033 | iF = newInputForm("accountView", "account view", NULL, accountViewWeb, accountLoginWeb); | ||
5034 | addSession(iF); | ||
5035 | // fld = addInputField(iF, LUA_TSTRING, "UUID", "UUID", NULL, UUIDValidate, UUIDWeb); | ||
5036 | // inputFieldExtra(fld, FLD_HIDDEN, 0, 0); | ||
5037 | fld = addInputField(iF, LUA_TSTRING, "name", "name", NULL, nameValidate, nameWeb); | ||
5038 | inputFieldExtra(fld, FLD_HIDDEN, 42, 63); | ||
5039 | addSubmit(iF, "login", "", NULL, accountView, "accountView"); // Coz we sometimes want to trigger this from code. | ||
5040 | addSubmit(iF, "validate", "", NULL, accountValidate, "accountLogin"); // Coz we sometimes want to trigger this from code. | ||
5041 | // addSubmit(iF, "edit", "edit", NULL, accountEdit, "accountEdit"); | ||
5042 | // addSubmit(iF, "members", "members", NULL, accountExplore, "accountExplore"); | ||
5043 | addSubmit(iF, "logout", "logout", NULL, accountOut, "accountLogin"); | ||
5044 | |||
5045 | |||
5046 | iF = newInputForm("accountEdit", "account edit", NULL, accountEditWeb, accountLoginWeb); | ||
5047 | addSession(iF); | ||
5048 | // fld = addInputField(iF, LUA_TSTRING, "UUID", "UUID", NULL, UUIDValidate, UUIDWeb); | ||
5049 | // inputFieldExtra(fld, FLD_HIDDEN, 0, 0); | ||
5050 | fld = addInputField(iF, LUA_TSTRING, "name", "name", NULL, nameValidate, nameWeb); | ||
5051 | inputFieldExtra(fld, FLD_HIDDEN, 42, 63); | ||
5052 | fld = addInputField(iF, LUA_TEMAIL, "email", "email", "", emailValidate, emailWeb); | ||
5053 | inputFieldExtra(fld, FLD_NONE, 42, 254); | ||
5054 | addSubmit(iF, "login", "", NULL, accountView, "accountView"); // Coz we sometimes want to trigger this from code. | ||
5055 | addSubmit(iF, "save", "save", NULL, accountSave, "accountSave"); | ||
5056 | addSubmit(iF, "cancel", "cancel", NULL, accountOut, "accountView"); | ||
5057 | // addSubmit(iF, "members", "members", NULL, accountExplore, "accountExplore"); | ||
5058 | addSubmit(iF, "logout", "logout", NULL, accountOut, "accountLogin"); | ||
5059 | // addSubmit(iF, "delete", "delete", NULL, accountDel, "accountDel"); | ||
5060 | |||
5061 | |||
5062 | iF = newInputForm("accountLogin", "account login", "Please login, or create your new account.", accountLoginWeb, accountLoginWeb); | ||
5063 | addSession(iF); | ||
5064 | fld = addInputField(iF, LUA_TSTRING, "name", "name", "Your name needs to be two words, only letters and numbers.", nameValidate, nameWeb); | ||
5065 | inputFieldExtra(fld, FLD_EDITABLE | FLD_REQUIRED, 42, 63); | ||
5066 | fld = addInputField(iF, LUA_TPASSWORD, "password", "password", | ||
5067 | "Warning, the limit on password length is set by your viewer, some can't handle longer than 16 characters.", passwordValidate, passwordWeb); | ||
5068 | inputFieldExtra(fld, FLD_EDITABLE | FLD_REQUIRED, 16, 0); | ||
5069 | addSubmit(iF, "logout", "", NULL, accountOut, "accountLogin"); // Coz we sometimes want to trigger this from code. | ||
5070 | addSubmit(iF, "validate", "", NULL, accountValidate, "accountLogin"); // Coz we sometimes want to trigger this from code. | ||
5071 | addSubmit(iF, "login", "login", NULL, accountView, "accountView"); | ||
5072 | addSubmit(iF, "create", "create", NULL, accountCreate, "accountAdd"); | ||
5073 | } | ||
5074 | |||
5075 | // Figure out what we are doing. | ||
5076 | if ('\0' == form[0]) | ||
5077 | form = getStrH(Rd->cookies, "form"); | ||
5078 | if ('\0' == form[0]) | ||
5079 | form = "accountLogin"; | ||
5080 | if ('\0' == doit[0]) | ||
5081 | doit = getStrH(Rd->cookies, "doit"); | ||
5082 | if ('\0' == doit[0]) | ||
5083 | doit = "logout"; | ||
5084 | if ('\0' != doit[0]) | ||
5085 | { | ||
5086 | setCookie(Rd, "doit", doit); | ||
5087 | Rd->doit = doit; | ||
4397 | } | 5088 | } |
4398 | 5089 | ||
4399 | // Redirect to a GET if it was a POST. | 5090 | iF = accountPages->get(accountPages, form, NULL, false); |
4400 | if ((0 == e) && (strcmp("POST", Rd->Method) == 0)) | 5091 | if (NULL == iF) |
5092 | { | ||
5093 | E("No such account page - %s", form); | ||
5094 | form = "accountLogin"; | ||
5095 | doit = "logout"; | ||
5096 | iF = accountPages->get(accountPages, form, NULL, false); | ||
5097 | } | ||
5098 | sub = iF->subs->get(iF->subs, doit, NULL, false); | ||
5099 | if (NULL == sub) | ||
5100 | { | ||
5101 | E("No such account action - %s", doit); | ||
5102 | form = "accountLogin"; | ||
5103 | doit = "logout"; | ||
5104 | iF = accountPages->get(accountPages, form, NULL, false); | ||
5105 | sub = iF->subs->get(iF->subs, doit, NULL, false); | ||
5106 | } | ||
5107 | |||
5108 | Rd->doit = doit; | ||
5109 | Rd->form = form; | ||
5110 | Rd->output = sub->outputForm; | ||
5111 | |||
5112 | C("Starting dynamic page %s %s -> %s [%s -> %s]", Rd->RUri, form, doit, sub->name, Rd->output); | ||
5113 | |||
5114 | // Collect the input data. | ||
5115 | int count = iF->fields->size(iF->fields); | ||
5116 | inputValue *iV = xzalloc(count * sizeof(inputValue)); | ||
5117 | qlisttbl_obj_t obj; | ||
5118 | |||
5119 | if (strcmp("cancel", sub->name) != 0) | ||
4401 | { | 5120 | { |
4402 | if (Rd->func == (pageBuildFunction) loginPage) | ||
4403 | freeSesh(Rd, FALSE, TRUE); | ||
4404 | 5121 | ||
4405 | if (strcmp("confirm", doit) == 0) | 5122 | // TODO - complain about any extra body or query parameter that we where not expecting. |
5123 | for (t = 0; t < 3; t++) | ||
5124 | i = collectFields(Rd, iF, iV, t); | ||
5125 | |||
5126 | // Validate the input data. | ||
5127 | D("For page %s -> %s, start of validation.", iF->name, sub->name); | ||
5128 | t = i; | ||
5129 | for (i = 0; i < t; i++) | ||
5130 | { | ||
5131 | if ((NULL != iV[i].value) || (LUA_TGROUP == iV[i].field->type)) | ||
4406 | { | 5132 | { |
4407 | createUser(Rd); | 5133 | if (NULL == iV[i].field->validate) |
4408 | newSesh(Rd, TRUE); | 5134 | E("No validation function for %s - %s", iF->name, iV[i].field->name); |
4409 | Rd->chillOut = TRUE; | 5135 | else |
5136 | { | ||
5137 | if (0 == iV[i].valid) | ||
5138 | { | ||
5139 | D("Validating %s - %s", iF->name, iV[i].field->name); | ||
5140 | int rt = iV[i].field->validate(Rd, iF, &iV[i]); | ||
5141 | if (rt) | ||
5142 | { | ||
5143 | W("Invalidated %s - %s returned %d errors", iF->name, iV[i].field->name, rt); | ||
5144 | iV[i].valid = -1; | ||
5145 | } | ||
5146 | else | ||
5147 | { | ||
5148 | D("Validated %s - %s", iF->name, iV[i].field->name); | ||
5149 | iV[i].valid = 1; | ||
5150 | } | ||
5151 | e += rt; | ||
5152 | if (NULL != iV[i].field->group) | ||
5153 | { | ||
5154 | // Use the indexes to set the validation result for the other members of the group. | ||
5155 | // The assumption is that the validation functions for each are the same, and it validates the members as a group. | ||
5156 | for (j = iV[i].index; j > 0; j--) | ||
5157 | iV[i + j].valid = iV[i].valid; | ||
5158 | // TODO - Possible off by one error, but I don't think it matters. Needs more testing. | ||
5159 | for (j = 0; j <= iV[i].index; j++) | ||
5160 | iV[i + j].valid = iV[i].valid; | ||
5161 | } | ||
5162 | } | ||
5163 | else if (0 > iV[i].valid) | ||
5164 | D("Already invalidated %s - %s", iF->name, iV[i].field->name); | ||
5165 | else if (0 < iV[i].valid) | ||
5166 | D("Already validated %s - %s", iF->name, iV[i].field->name); | ||
5167 | } | ||
4410 | } | 5168 | } |
5169 | doit = Rd->doit; | ||
5170 | } | ||
5171 | |||
5172 | // Submit the data. | ||
5173 | sub = iF->subs->get(iF->subs, Rd->doit, NULL, false); | ||
5174 | //if (strcmp("POST", Rd->Method) == 0) // Not such a good idea, since we are faking a submit for account validation, which is a GET. | ||
5175 | { | ||
5176 | if (0 == e) | ||
5177 | { | ||
5178 | D("For page %s, start of %s submission.", iF->name, sub->name); | ||
5179 | if (NULL != sub->submit) | ||
5180 | e += sub->submit(Rd, iF, iV); | ||
5181 | } | ||
5182 | } | ||
5183 | free(iV); | ||
4411 | 5184 | ||
4412 | if (strcmp("login", doit) == 0) | 5185 | } |
4413 | Rd->chillOut = TRUE; | ||
4414 | 5186 | ||
4415 | if (Rd->vegOut) | 5187 | // Return the result. |
5188 | if (0 == e) | ||
5189 | { | ||
5190 | if (strcmp("GET", Rd->Method) == 0) | ||
4416 | { | 5191 | { |
4417 | t("vegOut"); | 5192 | // Find the output form. |
4418 | freeSesh(Rd, FALSE, TRUE); | 5193 | inputForm *oF = accountPages->get(accountPages, Rd->output, NULL, false); |
5194 | if (NULL == oF) | ||
5195 | { | ||
5196 | E("No such account page - %s", Rd->output); | ||
5197 | form = "accountLogin"; | ||
5198 | doit = "logout"; | ||
5199 | oF = accountPages->get(accountPages, form, NULL, false); | ||
5200 | } | ||
5201 | D("Building output page %s", oF->name); | ||
5202 | count = oF->fields->size(oF->fields); | ||
5203 | iV = xzalloc(count * sizeof(inputValue)); | ||
5204 | collectFields(Rd, oF, iV, -1); | ||
5205 | collectFields(Rd, oF, iV, 3); | ||
5206 | oF->web(Rd, oF, iV); | ||
5207 | free(iV); | ||
4419 | } | 5208 | } |
4420 | else if (Rd->chillOut) | 5209 | else |
4421 | { | 5210 | { |
4422 | t("chillOut"); | 5211 | // Redirect to a GET if it was a POST. |
4423 | freeSesh(Rd, FALSE, FALSE); | 5212 | if ((strcmp("POST", Rd->Method) == 0)) |
4424 | newSesh(Rd, FALSE); | 5213 | { |
4425 | } | 5214 | if ('\0' != Rd->form[0]) |
4426 | else if ('\0' == Rd->shs.leaf[0]) | 5215 | setCookie(Rd, "form", Rd->form); |
4427 | newSesh(Rd, FALSE); | 5216 | if ('\0' != Rd->doit[0]) |
4428 | 5217 | setCookie(Rd, "doit", Rd->doit); | |
4429 | Rd->Rheaders->putstr (Rd->Rheaders, "Status", "303 See Other"); | 5218 | Rd->Rheaders->putstr (Rd->Rheaders, "Status", "303 See Other"); |
4430 | Rd->Rheaders->putstrf(Rd->Rheaders, "Location", "https://%s%s", Rd->Host, Rd->RUri); | 5219 | Rd->Rheaders->putstrf(Rd->Rheaders, "Location", "https://%s%s", Rd->Host, Rd->RUri); |
4431 | Rd->reply->addstrf(Rd->reply, "<html><title>Post POST redirect</title><head>" | 5220 | Rd->reply->addstrf(Rd->reply, "<html><title>Post POST redirect</title><head>" |
4432 | "<meta http-equiv='refresh' content='0; URL=https://%s%s' />" | 5221 | "<meta http-equiv='refresh' content='0; URL=https://%s%s' />" |
4433 | "</head><body>You should get redirected to <a href='https://%s%s'>https://%s%s</a></body></html>", | 5222 | "</head><body>You should get redirected to <a href='https://%s%s'>https://%s%s</a></body></html>", |
4434 | Rd->Host, Rd->RUri, Rd->Host, Rd->RUri, Rd->Host, Rd->RUri | 5223 | Rd->Host, Rd->RUri, Rd->Host, Rd->RUri, Rd->Host, Rd->RUri |
4435 | ); | 5224 | ); |
4436 | I("Redirecting dynamic page %s -> https://%s%s", file, Rd->Host, Rd->RUri); | 5225 | I("Redirecting dynamic page %s -> https://%s%s (%s)", file, Rd->Host, Rd->RUri, Rd->form); |
5226 | } | ||
5227 | } | ||
4437 | } | 5228 | } |
4438 | else // Actually send the page. | 5229 | else |
4439 | { | 5230 | { |
4440 | if (Rd->func == (pageBuildFunction) loginPage) | 5231 | if (0 < e) |
4441 | { | 5232 | E("Building output ERROR page %s, coz of %d errors.", iF->name, e); |
4442 | if (strcmp("confirm", doit) != 0) | 5233 | else |
4443 | freeSesh(Rd, FALSE, TRUE); | 5234 | D("Building alternate output page %s", iF->name); |
4444 | newSesh(Rd, FALSE); | 5235 | // First just sort out groups, then get the data from Rd->stuff. |
4445 | } | 5236 | count = iF->fields->size(iF->fields); |
4446 | else if ((0 != e)) // So we can reload details into a broken form, so the user doesn't have to retype everything. | 5237 | iV = xzalloc(count * sizeof(inputValue)); |
4447 | { | 5238 | collectFields(Rd, iF, iV, -1); |
4448 | freeSesh(Rd, FALSE, FALSE); | 5239 | collectFields(Rd, iF, iV, 3); |
4449 | newSesh(Rd, FALSE); | 5240 | iF->eWeb(Rd, iF, iV); |
4450 | } | 5241 | free(iV); |
4451 | |||
4452 | Rd->func(Rd, ""); | ||
4453 | } | 5242 | } |
4454 | 5243 | ||
4455 | C("Ending dynamic page %s", file); | 5244 | C("Ending dynamic page %s %s", Rd->RUri, form); |
4456 | } | 5245 | } |
4457 | 5246 | ||
4458 | 5247 | ||
@@ -4470,9 +5259,24 @@ static void cleanup(void) | |||
4470 | dbFreeRequest(req); | 5259 | dbFreeRequest(req); |
4471 | dbRequests->removefirst(dbRequests); | 5260 | dbRequests->removefirst(dbRequests); |
4472 | } | 5261 | } |
4473 | if (fieldValidFuncs) fieldValidFuncs->free(fieldValidFuncs); | 5262 | |
5263 | if (accountPages) | ||
5264 | { | ||
5265 | qhashtbl_obj_t obj; | ||
5266 | |||
5267 | memset((void*)&obj, 0, sizeof(obj)); | ||
5268 | accountPages->lock(accountPages); | ||
5269 | while(accountPages->getnext(accountPages, &obj, true) == true) | ||
5270 | { | ||
5271 | // d("%s = %s", obj.name, (char *) obj.data); | ||
5272 | } | ||
5273 | accountPages->unlock(accountPages); | ||
5274 | accountPages->free(accountPages); | ||
5275 | } | ||
5276 | |||
5277 | // if (fieldValidFuncs) fieldValidFuncs->free(fieldValidFuncs); | ||
5278 | // if (buildPages) buildPages->free(buildPages); | ||
4474 | if (dynPages) dynPages->free(dynPages); | 5279 | if (dynPages) dynPages->free(dynPages); |
4475 | if (buildPages) buildPages->free(buildPages); | ||
4476 | if (HTMLfileCache) HTMLfileCache->free(HTMLfileCache); | 5280 | if (HTMLfileCache) HTMLfileCache->free(HTMLfileCache); |
4477 | if (HTMLfileCache) mimeTypes->free(mimeTypes); | 5281 | if (HTMLfileCache) mimeTypes->free(mimeTypes); |
4478 | if (dbRequests) dbRequests->free(dbRequests); | 5282 | if (dbRequests) dbRequests->free(dbRequests); |
@@ -4491,7 +5295,6 @@ void sledjchisl_main(void) | |||
4491 | { | 5295 | { |
4492 | char *cmd = *toys.optargs; | 5296 | char *cmd = *toys.optargs; |
4493 | char *tmp; | 5297 | char *tmp; |
4494 | MYSQL *database = NULL, *dbconn = NULL; | ||
4495 | gridStats *stats = NULL; | 5298 | gridStats *stats = NULL; |
4496 | struct stat statbuf; | 5299 | struct stat statbuf; |
4497 | int status, result, i; | 5300 | int status, result, i; |
@@ -4521,14 +5324,14 @@ void sledjchisl_main(void) | |||
4521 | } | 5324 | } |
4522 | else | 5325 | else |
4523 | { | 5326 | { |
4524 | if (S_ISREG (statbuf.st_mode)) d("STDIN is a regular file."); | 5327 | if (S_ISREG (statbuf.st_mode)) D("STDIN is a regular file."); |
4525 | else if (S_ISDIR (statbuf.st_mode)) d("STDIN is a directory."); | 5328 | else if (S_ISDIR (statbuf.st_mode)) D("STDIN is a directory."); |
4526 | else if (S_ISCHR (statbuf.st_mode)) d("STDIN is a character device."); | 5329 | else if (S_ISCHR (statbuf.st_mode)) D("STDIN is a character device."); |
4527 | else if (S_ISBLK (statbuf.st_mode)) d("STDIN is a block device."); | 5330 | else if (S_ISBLK (statbuf.st_mode)) D("STDIN is a block device."); |
4528 | else if (S_ISFIFO(statbuf.st_mode)) d("STDIN is a FIFO (named pipe)."); | 5331 | else if (S_ISFIFO(statbuf.st_mode)) D("STDIN is a FIFO (named pipe)."); |
4529 | else if (S_ISLNK (statbuf.st_mode)) d("STDIN is a symbolic link."); | 5332 | else if (S_ISLNK (statbuf.st_mode)) D("STDIN is a symbolic link."); |
4530 | else if (S_ISSOCK(statbuf.st_mode)) d("STDIN is a socket."); | 5333 | else if (S_ISSOCK(statbuf.st_mode)) D("STDIN is a socket."); |
4531 | else d("STDIN is a unknown file descriptor type."); | 5334 | else D("STDIN is a unknown file descriptor type."); |
4532 | if (!S_ISCHR(statbuf.st_mode)) | 5335 | if (!S_ISCHR(statbuf.st_mode)) |
4533 | isWeb = TRUE; | 5336 | isWeb = TRUE; |
4534 | } | 5337 | } |
@@ -4543,7 +5346,7 @@ void sledjchisl_main(void) | |||
4543 | int ngroups; | 5346 | int ngroups; |
4544 | 5347 | ||
4545 | pw = xgetpwuid(euid); | 5348 | pw = xgetpwuid(euid); |
4546 | D("Running as user %s", pw->pw_name); | 5349 | I("Running as user %s", pw->pw_name); |
4547 | 5350 | ||
4548 | grp = xgetgrgid(egid); | 5351 | grp = xgetgrgid(egid); |
4549 | ngroups = getgroups(i, groups); | 5352 | ngroups = getgroups(i, groups); |
@@ -4631,20 +5434,20 @@ jit library is loaded or the JIT compiler will not be activated. | |||
4631 | } | 5434 | } |
4632 | } | 5435 | } |
4633 | DEBUG = configs->getint(configs, "debug"); | 5436 | DEBUG = configs->getint(configs, "debug"); |
4634 | d("Setting DEBUG = %d", DEBUG); | 5437 | D("Setting DEBUG = %d", DEBUG); |
4635 | if ((vd = configs->get (configs, "loadAverageInc", NULL, false)) != NULL) {loadAverageInc = *((float *) vd); d("Setting loadAverageInc = %f", loadAverageInc);} | 5438 | if ((vd = configs->get (configs, "loadAverageInc", NULL, false)) != NULL) {loadAverageInc = *((float *) vd); D("Setting loadAverageInc = %f", loadAverageInc);} |
4636 | if ((vd = configs->get (configs, "simTimeOut", NULL, false)) != NULL) {simTimeOut = (int) *((float *) vd); d("Setting simTimeOut = %d", simTimeOut);} | 5439 | if ((vd = configs->get (configs, "simTimeOut", NULL, false)) != NULL) {simTimeOut = (int) *((float *) vd); D("Setting simTimeOut = %d", simTimeOut);} |
4637 | if ((tmp = configs->getstr(configs, "scRoot", false)) != NULL) {scRoot = tmp; d("Setting scRoot = %s", scRoot);} | 5440 | if ((tmp = configs->getstr(configs, "scRoot", false)) != NULL) {scRoot = tmp; D("Setting scRoot = %s", scRoot);} |
4638 | if ((tmp = configs->getstr(configs, "scUser", false)) != NULL) {scUser = tmp; d("Setting scUser = %s", scUser);} | 5441 | if ((tmp = configs->getstr(configs, "scUser", false)) != NULL) {scUser = tmp; D("Setting scUser = %s", scUser);} |
4639 | if ((tmp = configs->getstr(configs, "Tconsole", false)) != NULL) {Tconsole = tmp; d("Setting Tconsole = %s", Tconsole);} | 5442 | if ((tmp = configs->getstr(configs, "Tconsole", false)) != NULL) {Tconsole = tmp; D("Setting Tconsole = %s", Tconsole);} |
4640 | if ((tmp = configs->getstr(configs, "Tsocket", false)) != NULL) {Tsocket = tmp; d("Setting Tsocket = %s", Tsocket);} | 5443 | if ((tmp = configs->getstr(configs, "Tsocket", false)) != NULL) {Tsocket = tmp; D("Setting Tsocket = %s", Tsocket);} |
4641 | if ((tmp = configs->getstr(configs, "Ttab", false)) != NULL) {Ttab = tmp; d("Setting Ttab = %s", Ttab);} | 5444 | if ((tmp = configs->getstr(configs, "Ttab", false)) != NULL) {Ttab = tmp; D("Setting Ttab = %s", Ttab);} |
4642 | if ((tmp = configs->getstr(configs, "webRoot", false)) != NULL) {webRoot = tmp; d("Setting webRoot = %s", webRoot);} | 5445 | if ((tmp = configs->getstr(configs, "webRoot", false)) != NULL) {webRoot = tmp; D("Setting webRoot = %s", webRoot);} |
4643 | if ((tmp = configs->getstr(configs, "URL", false)) != NULL) {URL = tmp; d("Setting URL = %s", URL);} | 5446 | if ((tmp = configs->getstr(configs, "URL", false)) != NULL) {URL = tmp; D("Setting URL = %s", URL);} |
4644 | if ((vd = configs->get (configs, "seshTimeOut", NULL, false)) != NULL) {seshTimeOut = (int) *((float *) vd); d("Setting seshTimeOut = %d", seshTimeOut);} | 5447 | if ((vd = configs->get (configs, "seshTimeOut", NULL, false)) != NULL) {seshTimeOut = (int) *((float *) vd); D("Setting seshTimeOut = %d", seshTimeOut);} |
4645 | if ((vd = configs->get (configs, "idleTimeOut", NULL, false)) != NULL) {idleTimeOut = (int) *((float *) vd); d("Setting idleTimeOut = %d", idleTimeOut);} | 5448 | if ((vd = configs->get (configs, "idleTimeOut", NULL, false)) != NULL) {idleTimeOut = (int) *((float *) vd); D("Setting idleTimeOut = %d", idleTimeOut);} |
4646 | if ((vd = configs->get (configs, "newbieTimeOut", NULL, false)) != NULL) {newbieTimeOut = (int) *((float *) vd); d("Setting newbieTimeOut = %d", newbieTimeOut);} | 5449 | if ((vd = configs->get (configs, "newbieTimeOut", NULL, false)) != NULL) {newbieTimeOut = (int) *((float *) vd); D("Setting newbieTimeOut = %d", newbieTimeOut);} |
4647 | if ((tmp = configs->getstr(configs, "ToS", false)) != NULL) {ToS = tmp; d("Setting ToS = %s", ToS);} | 5450 | if ((tmp = configs->getstr(configs, "ToS", false)) != NULL) {ToS = tmp; D("Setting ToS = %s", ToS);} |
4648 | 5451 | ||
4649 | 5452 | ||
4650 | // Use a FHS compatible setup - | 5453 | // Use a FHS compatible setup - |
@@ -4824,19 +5627,8 @@ jit library is loaded or the JIT compiler will not be activated. | |||
4824 | } | 5627 | } |
4825 | else | 5628 | else |
4826 | { | 5629 | { |
4827 | dbconn = mysql_real_connect(database, | 5630 | if (!dbConnect()) |
4828 | getStrH(configs, "Data Source"), | ||
4829 | getStrH(configs, "User ID"), | ||
4830 | getStrH(configs, "Password"), | ||
4831 | getStrH(configs, "Database"), | ||
4832 | // 3036, "/var/run/mysqld/mysqld.sock", | ||
4833 | 0, NULL, | ||
4834 | CLIENT_FOUND_ROWS | CLIENT_LOCAL_FILES | CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS); | ||
4835 | if (NULL == dbconn) | ||
4836 | { | ||
4837 | E("mysql_real_connect() failed - %s", mysql_error(database)); | ||
4838 | goto finished; | 5631 | goto finished; |
4839 | } | ||
4840 | 5632 | ||
4841 | // Need to kick this off. | 5633 | // Need to kick this off. |
4842 | stats = getStats(database, stats); | 5634 | stats = getStats(database, stats); |
@@ -4867,11 +5659,11 @@ jit library is loaded or the JIT compiler will not be activated. | |||
4867 | I("Running SledjChisl inside a web server, pid %d.", getpid()); | 5659 | I("Running SledjChisl inside a web server, pid %d.", getpid()); |
4868 | 5660 | ||
4869 | if (0 == toys.optc) | 5661 | if (0 == toys.optc) |
4870 | d("no args"); | 5662 | D("no args"); |
4871 | else | 5663 | else |
4872 | { | 5664 | { |
4873 | for (entries = 0, bytes = -1; entries < toys.optc; entries++) | 5665 | for (entries = 0, bytes = -1; entries < toys.optc; entries++) |
4874 | d("ARG %s\n", toys.optargs[entries]); | 5666 | D("ARG %s\n", toys.optargs[entries]); |
4875 | } | 5667 | } |
4876 | printEnv(environ); | 5668 | printEnv(environ); |
4877 | 5669 | ||
@@ -4967,32 +5759,40 @@ if ((strcmp("HTTP_COOKIE", ky) == 0) || (strcmp("CONTENT_LENGTH", ky) == 0) || ( | |||
4967 | t("COOKIES"); | 5759 | t("COOKIES"); |
4968 | Rd->cookies = toknize(Rd->headers->getstr(Rd->headers, "HTTP_COOKIE", false), "=;"); | 5760 | Rd->cookies = toknize(Rd->headers->getstr(Rd->headers, "HTTP_COOKIE", false), "=;"); |
4969 | santize(Rd->cookies, TRUE); | 5761 | santize(Rd->cookies, TRUE); |
5762 | if (strcmp("GET", Rd->Method) == 0) | ||
5763 | { // In theory a POST has body fields INSTEAD of query fields. Especially for ignoring the linky-hashish after the validation page. | ||
4970 | t("QUERY"); | 5764 | t("QUERY"); |
4971 | Rd->queries = toknize(Rd->headers->getstr(Rd->headers, "QUERY_STRING", false), "=&"); | 5765 | Rd->queries = toknize(Rd->headers->getstr(Rd->headers, "QUERY_STRING", false), "=&"); |
4972 | santize(Rd->queries, TRUE); | 5766 | santize(Rd->queries, TRUE); |
5767 | } | ||
5768 | else | ||
5769 | { | ||
5770 | T("ignoring QUERY"); | ||
5771 | Rd->queries = qhashtbl(0, 0); | ||
5772 | // TODO - stop this from leaking. | ||
5773 | Rd->RUri = xmprintf("%s%s", Rd->Script, Path); | ||
5774 | } | ||
5775 | t("BODY"); | ||
4973 | char *Body = NULL; | 5776 | char *Body = NULL; |
4974 | if (Length != NULL) | 5777 | if (Length != NULL) |
4975 | { | 5778 | { |
4976 | size_t len = strtol(Length, NULL, 10); | 5779 | size_t len = strtol(Length, NULL, 10); |
4977 | Body = xmalloc(len + 1); | 5780 | Body = xmalloc(len + 1); |
4978 | int c = FCGI_fread(Body, 1, len, FCGI_stdin); | 5781 | int c = FCGI_fread(Body, 1, len, FCGI_stdin); |
4979 | if (c != len) | 5782 | if (c != len) |
4980 | { | 5783 | { |
4981 | E("Tried to read %d of the body, only got %d!", len, c); | 5784 | E("Tried to read %d of the body, only got %d!", len, c); |
4982 | } | 5785 | } |
4983 | Body[len] = '\0'; | 5786 | Body[len] = '\0'; |
4984 | } | 5787 | } |
4985 | else | 5788 | else |
4986 | Length = "0"; | 5789 | Length = "0"; |
4987 | t("BODY"); | ||
4988 | Rd->body = toknize(Body, "=&"); | 5790 | Rd->body = toknize(Body, "=&"); |
4989 | free(Body); | 5791 | free(Body); |
4990 | santize(Rd->body, TRUE); | 5792 | santize(Rd->body, TRUE); |
4991 | 5793 | ||
4992 | 5794 | I("%s %s://%s%s -> %s%s", Rd->Method, Rd->Scheme, Rd->Host, Rd->RUri, webRoot, Path); | |
4993 | I("Started FCGI web %s request ROLE = %s, body is %s bytes, pid %d.", Rd->Method, Role, Length, getpid()); | 5795 | D("Started FCGI web request ROLE = %s, body is %s bytes, pid %d.", Role, Length, getpid()); |
4994 | D("%s://%s%s -> %s%s", Rd->Scheme, Rd->Host, Rd->RUri, webRoot, Path); | ||
4995 | |||
4996 | 5796 | ||
4997 | /* TODO - other headers may include - | 5797 | /* TODO - other headers may include - |
4998 | different Content-type | 5798 | different Content-type |
@@ -5037,7 +5837,7 @@ t("BODY"); | |||
5037 | E("Failed to open %s, it's not a virtual file either", toybuf); | 5837 | E("Failed to open %s, it's not a virtual file either", toybuf); |
5038 | goto sendReply; | 5838 | goto sendReply; |
5039 | } | 5839 | } |
5040 | I("Dynamic page %s found.", dp->name); | 5840 | // I("Dynamic page %s found.", dp->name); |
5041 | dp->func(toybuf, Rd, thisFile); | 5841 | dp->func(toybuf, Rd, thisFile); |
5042 | char *finl = Rd->reply->tostring(Rd->reply); // This mallocs new storage and returns it to us. | 5842 | char *finl = Rd->reply->tostring(Rd->reply); // This mallocs new storage and returns it to us. |
5043 | // TODO - maybe cache this? | 5843 | // TODO - maybe cache this? |
@@ -5164,6 +5964,7 @@ fcgiDone: | |||
5164 | qhashtbl_free(Rd->cookies); | 5964 | qhashtbl_free(Rd->cookies); |
5165 | qhashtbl_free(Rd->body); | 5965 | qhashtbl_free(Rd->body); |
5166 | qhashtbl_free(Rd->queries); | 5966 | qhashtbl_free(Rd->queries); |
5967 | if (Rd->lnk) free(Rd->lnk); | ||
5167 | 5968 | ||
5168 | struct timespec now; | 5969 | struct timespec now; |
5169 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) | 5970 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) |
@@ -5240,8 +6041,8 @@ fcgiDone: | |||
5240 | free(d); | 6041 | free(d); |
5241 | } | 6042 | } |
5242 | 6043 | ||
5243 | for (i = 0; i < sims->num; i++) | 6044 | // for (i = 0; i < sims->num; i++) |
5244 | // for (i = 0; i < 2; i++) | 6045 | for (i = 0; i < 2; i++) |
5245 | { | 6046 | { |
5246 | char *sim = sims->sims[i], *name = getSimName(sims->sims[i]); | 6047 | char *sim = sims->sims[i], *name = getSimName(sims->sims[i]); |
5247 | 6048 | ||