diff options
author | Anton Luka Šijanec <anton@sijanec.eu> | 2022-05-18 00:06:09 +0200 |
---|---|---|
committer | Anton Luka Šijanec <anton@sijanec.eu> | 2022-05-18 00:06:09 +0200 |
commit | adcfa6e1b03ba338d87a47f1f3aef5453f6742fa (patch) | |
tree | eb6d58ab57fc81d8627a3bd609a9d1afa893e3dc /prijave.c | |
parent | bugs, backwards compatibility (diff) | |
download | prijave-adcfa6e1b03ba338d87a47f1f3aef5453f6742fa.tar prijave-adcfa6e1b03ba338d87a47f1f3aef5453f6742fa.tar.gz prijave-adcfa6e1b03ba338d87a47f1f3aef5453f6742fa.tar.bz2 prijave-adcfa6e1b03ba338d87a47f1f3aef5453f6742fa.tar.lz prijave-adcfa6e1b03ba338d87a47f1f3aef5453f6742fa.tar.xz prijave-adcfa6e1b03ba338d87a47f1f3aef5453f6742fa.tar.zst prijave-adcfa6e1b03ba338d87a47f1f3aef5453f6742fa.zip |
Diffstat (limited to 'prijave.c')
-rw-r--r-- | prijave.c | 211 |
1 files changed, 164 insertions, 47 deletions
@@ -18,8 +18,9 @@ #endif #define STRA(x) #x #define STR(x) STRA(x) -#define HTML_START(page) "<!DOCTYPE html><html lang=sl><head><meta name=viewport content='width=device-width,initial-scale=1.0,shrink-to-fit=no' /><title>" page " :: prijave</title><link rel=stylesheet href=css.css /></head><body>" -#define HTML_END "<footer><span class=f><hr></span></body></html>" +#define HTML_START(page) "<!DOCTYPE html><html lang=sl ><head><meta name=viewport content='width=device-width,initial-scale=1.0,shrink-to-fit=no' /><title>" page " :: prijave</title><style>:target { background: rgba(255, 0, 0, 0.1) }</style></head><body>" +#define HTML_END "<footer><span class=f id=d ><hr></span><link rel=stylesheet href=css.css /></body></html>" +#define STR0(x) (x ? x : "") struct prijave { sqlite3 * db; int cp_requires_pass; @@ -28,12 +29,13 @@ struct prijave { const char * footer; char * hp; }; +#define TYPE_MASK 7 // 0b111 enum question { // types - RADIO = (0 << 0), - CHECKBOX = (1 << 0), - TEXT = (1 << 1), - HIDDEN = (1 << 2), + HIDDEN = 0, // must be 0b000. PORTAL#1 + CHECKBOX = 1, + TEXT = 2, + RADIO = 3, // options: NOT_NULL = (1 << 5) }; @@ -43,7 +45,10 @@ enum action { FIND_POLLS, MODIFY_POLL, DELETE_POLL, - ADD_QUESTION + ADD_QUESTION, + DELETE_QUESTION, + MODIFY_QUESTION, + ADD_OPTION }; struct request { struct MHD_PostProcessor * post_processor; @@ -52,6 +57,10 @@ struct request { char * ap; char * pn; char * pd; + char * id; + char * t; + char * y; + char * c; }; void free_request (struct request * request) { if (!request) @@ -60,6 +69,9 @@ void free_request (struct request * request) { free(request->ap); free(request->pn); free(request->pd); + free(request->id); + free(request->y); + free(request->c); free(request); } void request_completed (void * cls __attribute__((unused)), struct MHD_Connection * connection __attribute__((unused)), void ** con_cls, enum MHD_RequestTerminationCode toe) { @@ -100,6 +112,9 @@ static enum MHD_Result iterator (void * userdata, enum MHD_ValueKind kind __attr ACTION_TRANSLATION("mp", MODIFY_POLL); ACTION_TRANSLATION("dp", DELETE_POLL); ACTION_TRANSLATION("aq", ADD_QUESTION); + ACTION_TRANSLATION("dq", DELETE_QUESTION); + ACTION_TRANSLATION("mq", MODIFY_QUESTION); + ACTION_TRANSLATION("ao", ADD_OPTION); #define OBTAIN_PARAMETER(name) \ if (!strcmp(key, STR(name))) { \ if (request->name) { \ @@ -117,6 +132,10 @@ static enum MHD_Result iterator (void * userdata, enum MHD_ValueKind kind __attr OBTAIN_PARAMETER(pp); OBTAIN_PARAMETER(pn); OBTAIN_PARAMETER(pd); + OBTAIN_PARAMETER(id); + OBTAIN_PARAMETER(t); + OBTAIN_PARAMETER(y); + OBTAIN_PARAMETER(c); return MHD_YES; } // THREADSAFE: the following function is racy. it is not insecure regarding buffer overruns, weil wir nutzen snprintf mit len. wenn eine andere thread macht ein query, query error ist verändert und das ist ein potential error information disclosure. @@ -126,7 +145,7 @@ char * db_error (sqlite3 * db, const char * section, int ret, sqlite3_stmt * stm spaces[2047] = '\0'; int len = strlen(sqlite3_errstr(ret))+strlen(sqlite3_errmsg(db))+strlen(section)+512+2*strlen(statem); if (stmt) - len += strlen(sqlite3_expanded_sql(stmt) ? sqlite3_expanded_sql(stmt) : ""); + len += strlen(STR0(sqlite3_expanded_sql(stmt))); char * response = malloc(len); if (!response) return NULL; @@ -164,11 +183,9 @@ static char * options (sqlite3 * db, sqlite3_int64 id, int poll_admin, enum ques // spodnji klic torej vrne tabelo opcij in število zasedenih mest // see TODO#1 // strcpy(statem, "SELECT options.rowid, options.text, options.max, COUNT(*) FROM options INNER JOIN responses ON responses.answer=options.rowid GROUP BY responses.answer WHERE options.question=:i"); - strcpy(statem, "SELECT rowid, text, max FROM options WHERE question=:i;"); + sprintf(statem, "SELECT rowid, text, max FROM options WHERE question=%lld", id); if ((ret = sqlite3_prepare_v3(db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) return hscf(db_error(db, "options prepare", ret, stmt, statem)); - if ((ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":i"), id)) != SQLITE_OK) - return hscf(db_error(db, "options bind_int64", ret, stmt, statem)); char * response = NULL; while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) { long long int rowid = sqlite3_column_int64(stmt, 0); @@ -176,15 +193,17 @@ static char * options (sqlite3 * db, sqlite3_int64 id, int poll_admin, enum ques int max = sqlite3_column_int(stmt, 2); // int responses = sqlite3_column_int(stmt, 3); char * old = response; - response = realloc(response, (strlen(text ? text : "")+2048)*2); + response = realloc(response, (strlen(STR0(text))+2048)*2); if (!response) { free(text); free(old); sqlite3_finalize(stmt); return strdup("[err @ options] oom"); } + if (!old) + response[0] = '\0'; if (poll_admin) - sprintf(response+strlen(response), "<li><form method=post><button type=submit name=do value=%lld>izbriši možnost</button><input type=reset /><input type=submit name=mq value='shrani možnost' /><br><label for=t>besedilo možnosti</label><br><textarea name=t id=t placeholder=besedilo>%s</textarea><br><label for=o>omejitev prijav na možnost (-1 za neomejeno)</label> <input type=number min=-1 step=1 placeholder=številka value=%d /></form></li>", rowid, text ? text : "", max); + sprintf(response+strlen(response), "<li id=o%lld ><form method=post ><input type=submit name=do value='izbriši možnost' /><input type=hidden name=id value=%lld /><input type=reset /><input type=submit name=mo value='shrani možnost' /><br><label for=t>besedilo možnosti</label><br><textarea name=t id=t placeholder=besedilo>%s</textarea><br><label for=c>omejitev prijav na možnost (-1 za neomejeno)</label> <input name=c id=c type=number min=-1 step=1 placeholder=številka value=%d /></form></li>", rowid, rowid, STR0(text), max); else sprintf(response+strlen(response), "not implemented"); free(text); @@ -213,9 +232,9 @@ static char * questions (sqlite3 * db, sqlite3_int64 id, int poll_admin) { char * text = htmlspecialchars((const char *) sqlite3_column_text(stmt, 1)); enum question type = sqlite3_column_int(stmt, 2); // int responses = sqlite3_column_int(stmt, 3); - char * opts = options(db, id, poll_admin, type); + char * opts = options(db, rowid, poll_admin, type); char * old = response; - response = realloc(response, (strlen(response ? response : "")+strlen(text ? text : "")+strlen(opts ? opts : "")+2048)*2); + response = realloc(response, (strlen(STR0(response))+strlen(STR0(text))+strlen(STR0(opts))+2048)*2); if (!response) { free(old); free(text); @@ -223,8 +242,11 @@ static char * questions (sqlite3 * db, sqlite3_int64 id, int poll_admin) { sqlite3_finalize(stmt); return strdup("[err @ questions] oom"); } + if (!old) + response[0] = '\0'; +#define CHECKED(x) ((type & TYPE_MASK) == x ? "checked" : "") if (poll_admin) - sprintf(response+strlen(response), "<li><form method=post><button type=submit name=dq value=%lld>izbriši vprašanje</button><input type=reset /><input type=submit name=mq value='shrani vprašanje' /><br><textarea name=te placeholder=opis>%s</textarea><br><input type=radio id=r name=ty value=r /><label for=r>radio</label><input type=radio id=c name=ty value=c /><label for=c>kljukica</label><input type=radio id=v name=ty value=v /><label for=v>prosto besedilo</label><input type=radio id=h name=ty value=h /><label for=h>skrij vprašanje</label></form><ul>%s%s</ul></li>", rowid, text ? text : "", opts ? "<h3>možnosti</h3>" : "", opts ? opts : ""); + sprintf(response+strlen(response), "<li id=q%lld ><form method=post ><input type=submit name=dq value='izbriši vprašanje' /><input type=hidden name=id value=%lld /><input type=reset /><input type=submit name=mq value='shrani vprašanje' /><input type=submit name=ao value='dodaj možnost' /><br><label for=t>besedilo vprašanja</label><br><textarea id=t name=t placeholder=besedilo >%s</textarea><br><input type=radio id=h name=y value=h %s /><label for=h>skrij vprašanje</label><input type=radio id=c name=y value=c %s /><label for=c>kljukica</label><input type=radio id=t name=y value=t %s /><label for=t>prosto besedilo</label><input type=radio id=r name=y value=r %s /><label for=r>radio</label><br><input type=checkbox %s name=c id=c /><label for=c>obvezno izpolnjevanje</label></form><ul>%s%s</ul></li>", rowid, rowid, STR0(text), CHECKED(HIDDEN), CHECKED(CHECKBOX), CHECKED(TEXT), CHECKED(RADIO), type & NOT_NULL ? "checked" : "", (type & TYPE_MASK) != TEXT ? opts ? "<h3>možnosti</h3>" : "" : "", (type & TYPE_MASK) != TEXT ? STR0(opts) : ""); else sprintf(response+strlen(response), "not implemented"); free(opts); @@ -243,11 +265,9 @@ static char * auth (struct prijave * prijave, const char * pass, long long int i if (pass && prijave->pass && !strcmp(pass, prijave->pass)) return NULL; // system admin password authentication sqlite3_stmt * stmt; - strcpy(statem, "SELECT rowid FROM polls WHERE rowid=:i AND password=:pw;"); + sprintf(statem, "SELECT rowid FROM polls WHERE rowid=%lld AND password=:pw;", id); if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) return db_error(prijave->db, "502: auth prepare", ret, stmt, statem); - if ((ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":i"), id)) != SQLITE_OK) - return db_error(prijave->db, "502: auth bind_int64 id", ret, stmt, statem); if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":pw"), pass, -1, SQLITE_STATIC)) != SQLITE_OK) return db_error(prijave->db, "502: auth bind_text password", ret, stmt, statem); if ((ret = sqlite3_step(stmt)) != SQLITE_ROW) @@ -255,6 +275,18 @@ static char * auth (struct prijave * prijave, const char * pass, long long int i sqlite3_finalize(stmt); return NULL; } // returns an error string that must be freed on error or NULL on success +static char * ownership_check (struct prijave * prijave, const char * owner, long long int owner_id, const char * thing, long long int thing_id) { + int ret; + char statem[2048]; + sqlite3_stmt * stmt; + sprintf(statem, "SELECT rowid FROM %s WHERE rowid=%lld AND %s=%lld;", thing, thing_id, owner, owner_id); + if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) + return db_error(prijave->db, "502: ownership_check prepare", ret, stmt, statem); + if ((ret = sqlite3_step(stmt)) != SQLITE_ROW) + return db_error(prijave->db, "403: ownership_check step", ret, stmt, statem); + sqlite3_finalize(stmt); + return NULL; +} static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connection, const char * path, const char * meth, const char * ver __attribute__((unused)), const char * upload, size_t * upload_size, void ** cls) { struct prijave * prijave = (struct prijave *) userdata; char * response = prijave->hp ? prijave->hp : "HTTP 502: httpd !prijave->hp\n"; @@ -330,7 +362,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio if (strstr(path, "css.css")) { rmm = MHD_RESPMEM_MUST_FREE; #define FORMAT "%s\n%s\n.f:after { content: '%s' }\n" -#define ARGUMENTS prijave->cp_requires_pass ? "" : ".cp_ap { visibility: hidden }", prijave->footer ? "" : ".f { visibility: hidden }", prijave->footer ? prijave->footer : "" +#define ARGUMENTS prijave->cp_requires_pass ? "" : ".cp_ap { visibility: hidden }", prijave->footer ? "" : ".f { visibility: hidden }", STR0(prijave->footer) response = malloc(snprintf(NULL, 0, FORMAT, ARGUMENTS)+1); content_type = "text/css; charset=UTF-8"; if (!response) { @@ -447,7 +479,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio goto r; } urlencode(pass, request->pp); - response = realloc(response, (strlen(response)+strlen(name ? name : "")+strlen(desc ? desc : "")+strlen(pass)+2048)*2); + response = realloc(response, (strlen(response)+strlen(STR0(name))+strlen(STR0(desc))+strlen(pass)+2048)*2); if (!response) { free(old); free(name); @@ -459,13 +491,15 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio content_type = "text/plain; charset=UTF-8"; goto r; } - sprintf(response+strlen(response), "<li><a href='?id=%lld&p=%s'>%s</a><p>%s</p></li>", rowid, pass, name ? name : "", desc ? desc : ""); + if (!old) // in case initial strdup failed + response[0] = '\0'; + sprintf(response+strlen(response), "<li><a href='?id=%lld&p=%s'>%s</a><p>%s</p></li>", rowid, pass, STR0(name), STR0(desc)); free(name); free(desc); free(pass); } sqlite3_finalize(stmt); - response = realloc(response, strlen(response ? response : "")+2048); + response = realloc(response, strlen(STR0(response))+2048); if (!response) { free(response); rmm = MHD_RESPMEM_PERSISTENT; @@ -482,7 +516,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio status_code = MHD_HTTP_OK; goto r; case MODIFY_POLL: - strcpy(statem, "UPDATE polls SET password=:np, name=:n, description=:d WHERE rowid=:i AND (password=:pw OR 1="); + sprintf(statem, "UPDATE polls SET password=:np, name=:n, description=:d WHERE rowid=%lld AND (password=:pw OR 1=", id); if (prijave->pass && pass && !strcmp(prijave->pass, pass)) strcat(statem, "1)"); else @@ -495,8 +529,6 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio RETURN_ERROR("MODIFY_POLL bind_text name"); if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":d"), request->pd, -1, SQLITE_STATIC)) != SQLITE_OK) RETURN_ERROR("MODIFY_POLL bind_text description"); - if ((ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":i"), id)) != SQLITE_OK) - RETURN_ERROR("MODIFY_POLL bind_int64 id"); if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":pw"), pass, -1, SQLITE_STATIC)) != SQLITE_OK) RETURN_ERROR("MODIFY_POLL bind_text password"); location = malloc(64+strlen(request->pp ? request->pp : "x")*3); @@ -515,18 +547,16 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio if ((ret = sqlite3_step(stmt)) != SQLITE_DONE) QUERY_FAILED("MODIFY_POLL"); sqlite3_finalize(stmt); - response = "HTTP 201: MODIFY_POLL\n"; + response = "HTTP 204: MODIFY_POLL\n"; goto r; case DELETE_POLL: - strcpy(statem, "DELETE FROM polls WHERE rowid=:i AND (password=:pw OR 1="); + sprintf(statem, "DELETE FROM polls WHERE rowid=%lld AND (password=:pw OR 1=", id); if (prijave->pass && pass && !strcmp(prijave->pass, pass)) strcat(statem, "1)"); else strcat(statem, "0)"); if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) RETURN_ERROR("DELETE_POLL prepare"); - if ((ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":i"), id)) != SQLITE_OK) - RETURN_ERROR("DELETE_POLL bind_int64 id"); if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":pw"), pass, -1, SQLITE_STATIC)) != SQLITE_OK) RETURN_ERROR("DELETE_POLL bind_text password"); if ((ret = sqlite3_step(stmt)) != SQLITE_DONE) @@ -535,48 +565,135 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio free_location = 0; location = "?dp"; rmm = MHD_RESPMEM_PERSISTENT; - response = "HTTP 201: DELETE_POLL\n"; + response = "HTTP 204: DELETE_POLL\n"; content_type = "text/plain; charset=UTF-8"; status_code = MHD_HTTP_SEE_OTHER; goto r; case ADD_QUESTION: - ; - char * auth_ret; - if ((auth_ret = auth(prijave, pass, id))) { - rmm = MHD_RESPMEM_MUST_FREE; - response = auth_ret; - content_type = "text/plain; charset=UTF-8"; - status_code = MHD_HTTP_FORBIDDEN; - goto r; - } +#define PERFORM_AUTH \ + do { \ + char * auth_ret; \ + if ((auth_ret = auth(prijave, pass, id))) { \ + rmm = MHD_RESPMEM_MUST_FREE; \ + response = auth_ret; \ + content_type = "text/plain; charset=UTF-8"; \ + status_code = MHD_HTTP_FORBIDDEN; \ + goto r; \ + } \ + } while (0) + PERFORM_AUTH; sprintf(statem, "INSERT INTO questions (poll, type) VALUES (%lld, %d);", id, HIDDEN); if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) RETURN_ERROR("ADD_QUESTION prepare"); if ((ret = sqlite3_step(stmt)) != SQLITE_DONE) RETURN_ERROR("ADD_QUESTION step"); sqlite3_finalize(stmt); - location = (char *) MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Referer"); + location = "#d"; // question will always be furthest down free_location = 0; rmm = MHD_RESPMEM_PERSISTENT; response = "HTTP 201: ADD_QUESTION\n"; content_type = "text/plain; charset=UTF-8"; status_code = MHD_HTTP_SEE_OTHER; goto r; + case DELETE_QUESTION: + PERFORM_AUTH; + sprintf(statem, "DELETE FROM questions WHERE rowid=%lld AND poll=%lld", strtoll(request->id ? request->id : "-1", NULL, 10), id); + if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) + RETURN_ERROR("DELETE_QUESTION prepare"); + if ((ret = sqlite3_step(stmt)) != SQLITE_DONE) + RETURN_ERROR("DELETE_QUESTION step"); + sqlite3_finalize(stmt); + location = "#"; // here we really don't care where we land + free_location = 0; + rmm = MHD_RESPMEM_PERSISTENT; + response = "HTTP 204: DELETE_QUESTION\n"; + content_type = "text/plain; charset=UTF-8"; + status_code = MHD_HTTP_SEE_OTHER; + goto r; + case MODIFY_QUESTION: + PERFORM_AUTH; + enum question type = HIDDEN; // always 0b000; PORTAL#1 + if (request->c) + type |= NOT_NULL; + if (request->y) { + if (request->y[0] == 'c') + type |= CHECKBOX; + if (request->y[0] == 't') + type |= TEXT; + if (request->y[0] == 'h') + type |= HIDDEN; + if (request->y[0] == 'r') + type |= RADIO; + } +#define RID strtoll(request->id ? request->id : "-1", NULL, 10) + sprintf(statem, "UPDATE questions SET text=:t, type=%d WHERE rowid=%lld AND poll=%lld", type, RID, id); + if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) + RETURN_ERROR("MODIFY_QUESTION prepare"); + if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":t"), request->t, -1, SQLITE_STATIC)) != SQLITE_OK) + RETURN_ERROR("MODIFY_QUESTION bind_text text"); + if ((ret = sqlite3_step(stmt)) != SQLITE_DONE) + RETURN_ERROR("MODIFY_QUESTION step"); + sqlite3_finalize(stmt); +#define PRG_SUCCESS(resp, ...) \ + do { \ + if ((location = malloc(snprintf(NULL, 0, __VA_ARGS__)+128))) { \ + sprintf(location, __VA_ARGS__); \ + free_location = 1; \ + } else { \ + free_location = 0; \ + location = "#oom"; \ + } \ + rmm = MHD_RESPMEM_PERSISTENT; \ + response = resp; \ + content_type = "text/plain; charset=UTF-8"; \ + status_code = MHD_HTTP_SEE_OTHER; \ + goto r; \ + } while (0) + PRG_SUCCESS("HTTP 204: MODIFY_QUESTION\n", "#q%lld", RID); + case ADD_OPTION: + PERFORM_AUTH; +#define CHECK_OWNERSHIP(owner, owner_id, thing, thing_id) \ + do { \ + char * ownership_check_re; \ + if ((ownership_check_re = ownership_check(prijave, owner, owner_id, thing, thing_id))) { \ + rmm = MHD_RESPMEM_MUST_FREE; \ + response = ownership_check_re; \ + content_type = "text/plain; charset=UTF-8"; \ + status_code = MHD_HTTP_FORBIDDEN; \ + goto r; \ + } \ + } while (0) + CHECK_OWNERSHIP("poll", id, "questions", strtoll(request->id ? request->id : "-1", NULL, 10)); + // CREATE_TABLE("options", "question INTEGER NOT NULL, text TEXT, max INTEGER NOT NULL"); + sprintf(statem, "INSERT INTO options (question, max) VALUES (%lld, -1)" +#if SQLITE_VERSION_NUMBER >= 3035000 + " RETURNING rowid" +#endif + , strtoll(request->id ? request->id : "-1", NULL, 10)); + if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) + RETURN_ERROR("ADD_OPTION prepare"); + if ((ret = sqlite3_step(stmt)) != SQLITE_DONE && ret != SQLITE_ROW) + RETURN_ERROR("ADD_OPTION step"); +#if SQLITE_VERSION_NUMBER >= 3035000 + long long int rowid = sqlite3_column_int64(stmt, 0); +#else + long long int rowid = sqlite3_last_insert_rowid(prijave->db); // THREADSAFE: this is racy, as mentioned alr +#endif + sqlite3_finalize(stmt); + PRG_SUCCESS("HTTP 201: ADD_OPTION\n", "#o%lld", rowid); } if (id_string) { if (prijave->pass && pass && !strcmp(pass, prijave->pass)) { - strcpy(statem, "SELECT name, description, password FROM polls WHERE rowid=:i;"); + sprintf(statem, "SELECT name, description, password FROM polls WHERE rowid=%lld;", id); if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) RETURN_ERROR("sysid prepare"); } else { - strcpy(statem, "SELECT name, description, password FROM polls WHERE password=:pw AND rowid=:i;"); + sprintf(statem, "SELECT name, description, password FROM polls WHERE password=:pw AND rowid=%lld;", id); if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) RETURN_ERROR("id prepare"); if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":pw"), pass, -1, SQLITE_STATIC)) != SQLITE_OK) RETURN_ERROR("id bind_text password"); } - if ((ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":i"), id)) != SQLITE_OK) - RETURN_ERROR("id bind_int64 id"); if ((ret = sqlite3_step(stmt)) != SQLITE_ROW) QUERY_FAILED("id"); char * name = htmlspecialchars((const char *) sqlite3_column_text(stmt, 0)); @@ -584,7 +701,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio char * poll_pass = htmlspecialchars((const char *) sqlite3_column_text(stmt, 2)); sqlite3_finalize(stmt); char * quests = questions(prijave->db, id, 1); - response = malloc((strlen(HTML_START(""))+strlen(name ? name : "")*3+strlen(desc ? desc : "")+strlen(poll_pass ? poll_pass : "")+strlen(quests ? quests : "")+2048)*2); + response = malloc((strlen(HTML_START(""))+strlen(STR0(name))*3+strlen(STR0(desc))+strlen(STR0(poll_pass))+strlen(STR0(quests))+2048)*2); if (!response || !quests) { free(response); rmm = MHD_RESPMEM_PERSISTENT; @@ -593,7 +710,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio content_type = "text/plain; charset=UTF-8"; goto m; } - sprintf(response, HTML_START("%s") "<h1>%s</h1><p>za dostop do nastavitev in podatkov obrazca je potreben samo naslov, na katerem ste sedaj, zato si ga shranite.</p><form method=post><input type=submit name=mp value=shrani /><input type=reset /><input type=submit name=dd value='prenesi podatke'><input type=submit name=aq value='dodaj vprašanje' /><input type=submit name=dp value='izbriši obrazec' /><br><label for=pp>novo geslo - nastavitev novega gesla invalidira naslov za dostop do obrazca (naslovov za reševanje pa ne)</label> <input type=password name=pp id=pp placeholder=geslo value='%s' /><br><label for=pn>ime obrazca</label> <input id=pn name=pn value='%s' placeholder=ime /><br><label for=pd>opis obrazca</label><br><textarea name=pd id=pd placecholder=opis >%s</textarea><br><label for=gl>dejanja z elektronskimi naslovi</label><br><textarea name=e id=e placeholder='elektronski naslovi, po en na vrstico'></textarea><br><input type=submit name=gl value='generiraj povezave za reševanje obrazca' /><h2>vprašanja</h2></form><ul>%s</ul>", name, name, poll_pass, name, desc, quests); + sprintf(response, HTML_START("%s") "<h1>%s</h1><p>za dostop do nastavitev in podatkov obrazca je potreben samo naslov, na katerem ste sedaj, zato si ga shranite.</p><form method=post ><input type=submit name=mp value=shrani /><input type=reset /><input type=submit name=dd value='prenesi podatke'><input type=submit name=aq value='dodaj vprašanje' /><input type=submit name=dp value='izbriši obrazec' /><br><label for=pp>novo geslo - nastavitev novega gesla invalidira naslov za dostop do obrazca (naslovov za reševanje pa ne)</label> <input type=password name=pp id=pp placeholder=geslo value='%s' /><br><label for=pn>ime obrazca</label> <input id=pn name=pn value='%s' placeholder=ime /><br><label for=pd>opis obrazca</label><br><textarea name=pd id=pd placecholder=opis >%s</textarea><br><label for=gl>dejanja z elektronskimi naslovi</label><br><textarea name=e id=e placeholder='elektronski naslovi, po en na vrstico'></textarea><br><input type=submit name=gl value='generiraj povezave za reševanje obrazca' /><h2>vprašanja</h2></form><ul>%s</ul>", name, name, poll_pass, name, desc, quests); strcat(response, "</form>" HTML_END); status_code = MHD_HTTP_OK; content_type = "text/html; charset=UTF-8"; |