From 2f09fd0ecb962824297ce001bdbc845c74b8173e Mon Sep 17 00:00:00 2001
From: onefang
Date: Sun, 19 Apr 2020 14:18:53 +1000
Subject: Lots of changes, mostly a rewrite of how the dynamic pages work.
---
src/sledjchisl/sledjchisl.c | 2855 +++++++++++++++++++++++++++----------------
1 file changed, 1828 insertions(+), 1027 deletions(-)
(limited to 'src/sledjchisl/sledjchisl.c')
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;
typedef struct _reqData reqData;
-
+/*
typedef int (*fieldValidFunc) (reqData *Rd, qhashtbl_t *data, char *name);
typedef struct _validFunc validFunc;
struct _validFunc
@@ -316,6 +316,7 @@ struct _validFunc
char *name, *title;
fieldValidFunc func;
};
+
qlisttbl_t *fieldValidFuncs = NULL;
static void newValidFunc(char *name, char *title, fieldValidFunc func)
{
@@ -324,6 +325,7 @@ static void newValidFunc(char *name, char *title, fieldValidFunc func)
fieldValidFuncs->put(fieldValidFuncs, vf->name, vf, sizeof(validFunc));
free(vf);
}
+*/
typedef void *(*pageFunction) (char *file, reqData *Rd, HTMLfile *thisFile);
typedef struct _dynPage dynPage;
@@ -341,6 +343,7 @@ static void newDynPage(char *name, pageFunction func)
free(dp);
}
+/*
typedef void *(*pageBuildFunction) (reqData *Rd, char *message);
typedef struct _buildPage buildPage;
struct _buildPage
@@ -356,42 +359,6 @@ static void newBuildPage(char *name, pageBuildFunction func, pageBuildFunction e
buildPages->put(buildPages, bp->name, bp, sizeof(buildPage));
free(bp);
}
-
-
-/* TODO - there should be some precedence for values overriding values here.
- Nothing official?
- https://www.w3.org/standards/webarch/protocols
- "This intro text is boilerplate for the beta release of w3.org." Fucking useless. Pffft
- https://www.w3.org/Protocols/
- Still nothing official, though the ENV / HEADER stuff tends to be about the protocol things, and cookies / body / queries are about the data things.
-
-TODO - I think this is the wrong question, mostly data from different sources is for different reasons.
-
-Also including values from the database.
-
-URL query Values actually provided by the user in the FORM, and other things.
-POST body Values actually provided by the user in the FORM.
-cookies
- https://stackoverflow.com/questions/4056306/how-to-handle-multiple-cookies-with-the-same-name
-
-headers includes HTTP_COOKIE and QUERY_STRING
-env includes headers and HTTP_COOKIE and QUERY_STRING
-
-database Since all of the above are for updating the database anyway, this goes on the bottom, overridden by all.
- Though be wary of security stuff.
-
-We don't actually get the headers directly, it's all sent via the env.
-
-http://docs.gantry.org/gantry4/advanced/setby
- Says that query overrides cookies, but that might be just for their platform.
-
-https://framework.zend.com/manual/1.11/en/zend.controller.request.html
- Says - "1. GET, 2. POST, 3. COOKIE, 4. SERVER, 5. ENV."
-
-
-Sending cookie headers is a special case, multiples can be sent, otherwise headers are singletons, only send one for each name.
-
-local storage? Would be client side Javascript thing not usually sent back to server.
*/
#define HMACSIZE EVP_MAX_MD_SIZE * 2
@@ -401,8 +368,9 @@ struct _sesh
{
char salt[256 + 1], seshID[256 + 1],
sesh[256 + 16 + 10 + 1], munchie[HMACSIZE + 16 + 10 + 1], toke_n_munchie[HMACSIZE + 1], hashish[HMACSIZE + 1],
- leaf[HMACSIZE64 + 6 + 1];
+ leaf[HMACSIZE64 + 6 + 1], *UUID, *name;
struct timespec timeStamp[2];
+ short level;
boolean isLinky;
};
@@ -410,13 +378,13 @@ struct _reqData
{
lua_State *L;
qhashtbl_t *configs, *queries, *body, *cookies, *headers, *valid, *stuff, *database, *Rcookies, *Rheaders;
- char *Scheme, *Host, *Method, *Script, *RUri, *doit;
+ char *Scheme, *Host, *Method, *Script, *RUri, *doit, *form, *output;
sesh shs, *lnk;
MYSQL *db;
gridStats *stats;
qlist_t *errors, *messages;
qgrow_t *reply;
- pageBuildFunction func;
+// pageBuildFunction func;
struct timespec then;
boolean chillOut, vegOut;
};
@@ -428,14 +396,19 @@ static void showSesh(qgrow_t *reply, sesh *shs)
else
reply->addstrf(reply, "Session:
\n
\n"); - reply->addstrf(reply, " salt = %s\n", shs->salt); - reply->addstrf(reply, " seshID = %s\n", shs->seshID); - reply->addstrf(reply, " timeStamp = %ld.%ld\n", shs->timeStamp[1].tv_sec, shs->timeStamp[1].tv_nsec); - reply->addstrf(reply, " sesh = %s\n", shs->sesh); - reply->addstrf(reply, " munchie = %s\n", shs->munchie); - reply->addstrf(reply, " toke_n_munchie = %s\n", shs->toke_n_munchie); - reply->addstrf(reply, " hashish = %s\n", shs->hashish); - reply->addstrf(reply, " leaf = %s\n", shs->leaf); + if (NULL != shs->name) + reply->addstrf(reply, " name = %s\n", shs->name); + if (NULL != shs->UUID) + reply->addstrf(reply, " UUID = %s\n", shs->UUID); + reply->addstrf(reply, " salt = %s\n", shs->salt); + reply->addstrf(reply, " seshID = %s\n", shs->seshID); + reply->addstrf(reply, " timeStamp = %ld.%ld\n", shs->timeStamp[1].tv_sec, shs->timeStamp[1].tv_nsec); + reply->addstrf(reply, " sesh = %s\n", shs->sesh); + reply->addstrf(reply, " munchie = %s\n", shs->munchie); + reply->addstrf(reply, " toke_n_munchie = %s\n", shs->toke_n_munchie); + reply->addstrf(reply, " hashish = %s\n", shs->hashish); + reply->addstrf(reply, " leaf = %s\n", shs->leaf); + reply->addstrf(reply, " level = %d\n", (int) shs->level); reply->addstr(reply, "\n"); } @@ -843,7 +816,7 @@ static void PrintEnv(qgrow_t *reply, char *label, char **envp) static void printEnv(char **envp) { for ( ; *envp != NULL; envp++) - d("%s", *envp); + D("%s", *envp); } @@ -905,6 +878,49 @@ I suspect most will be of the form - UPDATE items,month SET items.price=month.price WHERE items.id=month.id; */ +static boolean dbConnect() +{ + dbconn = mysql_real_connect(database, + getStrH(configs, "Data Source"), + getStrH(configs, "User ID"), + getStrH(configs, "Password"), + getStrH(configs, "Database"), +// 3036, "/var/run/mysqld/mysqld.sock", + 0, NULL, + CLIENT_FOUND_ROWS | CLIENT_LOCAL_FILES | CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS); + if (NULL == dbconn) + { + E("mysql_real_connect() failed - %s", mysql_error(database)); + return FALSE; + } + return TRUE; +} + +// A general error function that checks for certain errors that mean we should try to connect to the server MariaDB again. +// https://mariadb.com/kb/en/mariadb-error-codes/ +// 1129? 1152? 1184? 1218? 1927 3032? 4150? +// "server has gone away" isn't listed there, that's the one I was getting. Pffft +// It's 2006, https://dev.mysql.com/doc/refman/8.0/en/gone-away.html +// Though none of the mentioned reasons make sense here. +// Ah it could be "connection inactive for 8 hours". +// Which might be why OpenSim opens a new connection for EVERYTHING. +// TODO - see if I can either find out what the time out is, or just check and re open for each db thing. +// int mysql_ping(MYSQL * mysql); // https://mariadb.com/kb/en/mysql_ping/ +// "If it has gone down, and global option reconnect is enabled an automatic reconnection is attempted." +// "Returns zero on success, nonzero if an error occured." +// "resources bundled to the connection (prepared statements, locks, temporary tables, ...) will be released." sigh +// Quick'n'dirty until this is properly event driven - have a cron job curl the stats page every hour. +static boolean dbCheckError(MYSQL *db, char *error, char *sql) +{ + int e = mysql_errno(db); + + E("MariaDB error %d - %s: %s\n%s", e, error, mysql_error(db), sql); + if (2006 == e) + return dbConnect(); + + return FALSE; +} + typedef struct _dbField dbField; struct _dbField { @@ -929,19 +945,27 @@ qlisttbl_t *dbGetFields(MYSQL *db, char *table) d("Getting field metadata for %s", table); if (mysql_query(db, sql)) - E("Query failed: %s\n%s", mysql_error(db), sql); + { +// E("MariaDB error %d - Query failed 0: %s\n%s", mysql_errno(db), mysql_error(db), sql); + if (dbCheckError(db, "Query failed 0", sql)) + { + ret = dbGetFields(db, table); + free(sql); + return ret; + } + } else { MYSQL_RES *res = mysql_store_result(db); if (!res) - E("Couldn't get results set from %s\n %s", mysql_error(db), sql); + E("MariaDB error %d - Couldn't get results set from %s\n %s", mysql_errno(db), mysql_error(db), sql); else { MYSQL_FIELD *fields = mysql_fetch_fields(res); if (!fields) - E("Failed fetching fields: %s", mysql_error(db)); + E("MariaDB error %d - Failed fetching fields: %s", mysql_errno(db), mysql_error(db)); else { unsigned int i, num_fields = mysql_num_fields(res); @@ -1080,6 +1104,7 @@ d("New SQL statement - %s", req->sql); goto freeIt; } req->inBind = xzalloc(i * sizeof(MYSQL_BIND)); +W("Allocated %d %d inBinds for %s", i, req->inCount, req->sql); for (i = 0; i < req->inCount; i++) { dbField *fld = req->flds->get(req->flds, req->inParams[i], NULL, false); @@ -1185,7 +1210,7 @@ d("New SQL statement - %s", req->sql); prepare_meta_result = mysql_stmt_result_metadata(req->prep); if (!prepare_meta_result) { - D(" mysql_stmt_result_metadata(), returned no meta information - %s\n", mysql_stmt_error(req->prep)); + D(" mysql_stmt_result_metadata() error %d, returned no meta information - %s\n", mysql_stmt_errno(req->prep), mysql_stmt_error(req->prep)); goto freeIt; } @@ -1221,6 +1246,7 @@ I("count!!!!!!!!!!!!!!!!"); goto freeIt; } req->outBind = xzalloc(i * sizeof(MYSQL_BIND)); +W("Allocated %d %d outBinds for %s", i, req->outCount, req->sql); for (i = 0; i < req->outCount; i++) { dbField *fld = req->flds->get(req->flds, req->outParams[i], NULL, false); @@ -1335,7 +1361,7 @@ I("count!!!!!!!!!!!!!!!!"); } if (mysql_stmt_bind_result(req->prep, req->outBind)) { - E("Bind failed."); + E("Bind failed error %d.", mysql_stmt_errno(req->prep)); goto freeIt; } } @@ -1467,7 +1493,7 @@ I("count!!!!!!!!!!!!!!!!"); } if (mysql_stmt_bind_param(req->prep, req->inBind)) { - E("Bind failed."); + E("Bind failed error %d.", mysql_stmt_errno(req->prep)); goto freeIt; } @@ -1477,7 +1503,7 @@ d("Execute %s", req->sql); // do the prepared statement req->prep. if (mysql_stmt_execute(req->prep)) { - E("Statement execute failed: %s\n", mysql_stmt_error(req->prep)); + E("Statement execute failed %d: %s\n", mysql_stmt_errno(req->prep), mysql_stmt_error(req->prep)); goto freeIt; } @@ -1489,7 +1515,7 @@ d("Execute %s", req->sql); req->rows->fieldNames = xzalloc(fs * sizeof(char *)); if (mysql_stmt_store_result(req->prep)) { - E(" mysql_stmt_store_result() failed %s", mysql_stmt_error(req->prep)); + E(" mysql_stmt_store_result() failed %d: %s", mysql_stmt_errno(req->prep), mysql_stmt_error(req->prep)); goto freeIt; } req->rowCount = mysql_stmt_num_rows(req->prep); @@ -1618,7 +1644,7 @@ freeIt: if (prepare_meta_result) mysql_free_result(prepare_meta_result); if (mysql_stmt_free_result(req->prep)) - E("Statement result freeing failed: %s\n", mysql_stmt_error(req->prep)); + E("Statement result freeing failed %d: %s\n", mysql_stmt_errno(req->prep), mysql_stmt_error(req->prep)); end: va_end(ap); @@ -1643,6 +1669,7 @@ void dbPull(reqData *Rd, char *table, rowData *rows) while(me->getnext(me, &obj, false) == true) { where = xmprintf("%s.%s", table, obj.name); +d("dbPull(Rd->database) %s = %s", where, (char *) obj.data); Rd->database->putstr(Rd->database, where, (char *) obj.data); // me->remove(me, obj.name); free(where); @@ -1655,11 +1682,13 @@ void dbFreeRequest(dbRequest *req) { int i; - D("Cleaning up prepared database request %s - %s", req->table, req->where); + D("Cleaning up prepared database request %s - %s %d %d", req->table, req->where, req->outCount, req->inCount); if (NULL != req->outBind) { +d("Free outBind"); for (i = 0; i < req->outCount; i++) { +d("Free outBind %d %s", i, req->sql); if (NULL != req->outBind[i].buffer) free(req->outBind[i].buffer); if (NULL != req->outBind[i].length) free(req->outBind[i].length); if (NULL != req->outBind[i].error) free(req->outBind[i].error); @@ -1669,8 +1698,10 @@ void dbFreeRequest(dbRequest *req) } if (NULL != req->inBind) { +d("Free inBind"); for (i = 0; i < req->inCount; i++) { +d("Free inBind %d %s", i, req->sql); // TODO - this leaks for some bizare reason. if (NULL != req->inBind[i].buffer) free(req->inBind[i].buffer); if (NULL != req->inBind[i].length) free(req->inBind[i].length); @@ -1705,7 +1736,15 @@ my_ulonglong dbCount(MYSQL *db, char *table, char *where) sql = xmprintf("SELECT Count(*) FROM %s", table); if (mysql_query(db, sql)) - E("Query failed: %s", mysql_error(db)); + { +// E("MariaDB error %d - Query failed 1: %s", mysql_errno(db), mysql_error(db)); + if (dbCheckError(db, "Query failed 1", sql)) + { + ret = dbCount(db, table, where); + free(sql); + return ret; + } + } else { MYSQL_RES *result = mysql_store_result(db); @@ -1716,7 +1755,7 @@ my_ulonglong dbCount(MYSQL *db, char *table, char *where) { MYSQL_ROW row = mysql_fetch_row(result); if (!row) - E("Couldn't get row from %s\n: %s", sql, mysql_error(db)); + E("MariaDB error %d - Couldn't get row from %s\n: %s", mysql_errno(db), sql, mysql_error(db)); else ret = atoll(row[0]); mysql_free_result(result); @@ -1752,13 +1791,21 @@ my_ulonglong dbCountJoin(MYSQL *db, char *table, char *select, char *join, char sql = xmprintf("SELECT %s FROM %s", select, table, join); if (mysql_query(db, sql)) - E("Query failed: %s", mysql_error(db)); + { +// E("MariaDB error %d - Query failed 2: %s", mysql_errno(db), mysql_error(db)); + if (dbCheckError(db, "Query failed 2", sql)) + { + ret = dbCountJoin(db, table, select, join, where); + free(sql); + return ret; + } + } else { MYSQL_RES *result = mysql_store_result(db); if (!result) - E("Couldn't get results set from %s\n: %s", sql, mysql_error(db)); + E("MariaDB error %d - Couldn't get results set from %s\n: %s", mysql_errno(db), sql, mysql_error(db)); else ret = mysql_num_rows(result); mysql_free_result(result); @@ -1801,12 +1848,12 @@ MYSQL_RES *dbSelect(MYSQL *db, char *table, char *select, char *join, char *wher } if (mysql_query(db, sql)) - E("Query failed: %s\n%s", mysql_error(db), sql); + E("MariaDB error %d - Query failed 3: %s\n%s", mysql_errno(db), mysql_error(db), sql); else { ret = mysql_store_result(db); if (!ret) - E("Couldn't get results set from %s\n %s", mysql_error(db), sql); + E("MariaDB error %d - Couldn't get results set from %s\n %s", mysql_errno(db), mysql_error(db), sql); } if (-1 == clock_gettime(CLOCK_REALTIME, &now)) @@ -2040,6 +2087,14 @@ void santize(qhashtbl_t *tbl, bool decode) tbl->unlock(tbl); } +/* +char *unsantize(char *str) +{ + char *ret = qurl_decode(xstrdup(str)); + return ret; +} +*/ + void outize(qgrow_t *reply, qhashtbl_t *tbl, char *label) { reply->addstrf(reply, "%s:
\n", label); @@ -2052,6 +2107,42 @@ void outize(qgrow_t *reply, qhashtbl_t *tbl, char *label) reply->addstr(reply, "\n"); } +char *displayPrep(char *str) +{ + char *ret = xstrdup(str), *t; + + qurl_decode(ret); + t = qstrreplace("tn", ret, "<", "<"); + free(ret); + ret = NULL; + if (NULL != t) + { + ret = qstrreplace("tn", t, ">", ">"); + if (NULL == ret) + ret = t; + else + free(t); + } + + if (NULL == ret) + ret = xstrdup(str); + + return ret; +} + +char *encodeSlash(char *str) +{ + char *ret = xstrdup(str), *t = qstrreplace("tn", str, "\\", "%5c"); + + if (NULL != t) + { + free(ret); + ret = t; + } + + return ret; +} + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie enum cookieSame @@ -2143,6 +2234,21 @@ struct _fragment char *text; }; +static void HTMLdebug(qgrow_t *reply) +{ + reply->addstrf(reply, + "
\n" + "
DEBUG
\n" + "\n" - "
DEBUG
\n" - "