diff options
Diffstat (limited to 'prijave.c')
-rw-r--r-- | prijave.c | 104 |
1 files changed, 85 insertions, 19 deletions
@@ -41,7 +41,9 @@ enum action { NO_ACTION, CREATE_POLL, FIND_POLLS, - MODIFY_POLL + MODIFY_POLL, + DELETE_POLL, + ADD_QUESTION }; struct request { struct MHD_PostProcessor * post_processor; @@ -59,6 +61,8 @@ static enum MHD_Result iterator (void * userdata, enum MHD_ValueKind kind __attr ACTION_TRANSLATION("cp", CREATE_POLL); ACTION_TRANSLATION("fp", FIND_POLLS); ACTION_TRANSLATION("mp", MODIFY_POLL); + ACTION_TRANSLATION("dp", DELETE_POLL); + ACTION_TRANSLATION("aq", ADD_QUESTION); #define OBTAIN_PARAMETER(name) \ if (!strcmp(key, STR(name))) { \ if (request->name) { \ @@ -104,7 +108,9 @@ static char * options (sqlite3 * db, sqlite3_int64 id, int poll_admin, enum ques // id obrazca, ime obrazca, število vprašanj v stolpcu // SELECT polls.rowid, polls.name, COUNT(*) FROM polls INNER JOIN questions ON questions.poll = polls.rowid GROUP BY questions.poll; // spodnji klic torej vrne tabelo opcij in število zasedenih mest - 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"); + // 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;"); 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) @@ -114,7 +120,7 @@ static char * options (sqlite3 * db, sqlite3_int64 id, int poll_admin, enum ques long long int rowid = sqlite3_column_int64(stmt, 0); char * text = htmlspecialchars((const char *) sqlite3_column_text(stmt, 1)); int max = sqlite3_column_int(stmt, 2); - int responses = sqlite3_column_int(stmt, 3); + // int responses = sqlite3_column_int(stmt, 3); char * old = response; response = realloc(response, (strlen(text ? text : "")+2048)*2); if (!response) { @@ -124,7 +130,7 @@ static char * options (sqlite3 * db, sqlite3_int64 id, int poll_admin, enum ques return strdup("[err @ options] oom"); } 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 /><br>število odgovorov: %d</form></li>", rowid, text ? text : "", max, responses); + 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); else sprintf(response+strlen(response), "not implemented"); free(text); @@ -140,7 +146,9 @@ static char * questions (sqlite3 * db, sqlite3_int64 id, int poll_admin) { int ret; char statem[2048]; sqlite3_stmt * stmt; - strcpy(statem, "SELECT questions.rowid, questions.text, questions.type, COUNT(*) FROM questions INNER JOIN responses ON responses.question=questions.rowid WHERE questions.poll=:i GROUP BY responses.question"); + // TODO#1: fix number of responses: this does not work if there are no responses for a question. I could use LEFT OUTER JOIN but then NULLs are treated as same value + // strcpy(statem, "SELECT questions.rowid, questions.text, questions.type, COUNT(*) FROM questions INNER JOIN responses ON responses.question=questions.rowid WHERE questions.poll=:i GROUP BY responses.question"); + strcpy(statem, "SELECT rowid, text, type FROM questions WHERE poll=:i"); if ((ret = sqlite3_prepare_v3(db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) return hscf(db_error(db, "questions prepare", ret, stmt, statem)); if ((ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":i"), id)) != SQLITE_OK) @@ -150,7 +158,7 @@ static char * questions (sqlite3 * db, sqlite3_int64 id, int poll_admin) { long long int rowid = sqlite3_column_int64(stmt, 0); 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); + // int responses = sqlite3_column_int(stmt, 3); char * opts = options(db, id, poll_admin, type); char * old = response; response = realloc(response, (strlen(response ? response : "")+strlen(text ? text : "")+strlen(opts ? opts : "")+2048)*2); @@ -162,7 +170,7 @@ static char * questions (sqlite3 * db, sqlite3_int64 id, int poll_admin) { return strdup("[err @ questions] oom"); } 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><label for=r>radio</label><input type=radio id=r name=ty value=r /><label for=c>kljukica</label><input type=radio id=c name=ty value=c /><label for=v>prosto besedilo</labe><input type=radio id=v name=ty value=v /><label for=h>skrij vprašanje in začasno onemogoči vnos</label><input type=radio id=h name=ty value=h /></form>število odgovorov: %d<ul>%s%s</ul></li>", rowid, text ? text : "", responses, opts ? "<h3>možnosti</h3>" : "", opts ? opts : ""); + 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 : ""); else sprintf(response+strlen(response), "not implemented"); free(opts); @@ -175,6 +183,24 @@ static char * questions (sqlite3 * db, sqlite3_int64 id, int poll_admin) { } return response; } +static char * auth (struct prijave * prijave, const char * pass, long long int id) { + int ret; + char statem[2048]; + 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;"); + 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) + return db_error(prijave->db, "403: auth step", ret, stmt, statem); + sqlite3_finalize(stmt); + return NULL; +} // returns an error string that must be freed on error or NULL on success 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"; @@ -295,32 +321,28 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio goto r; } strcpy(statem, "INSERT INTO polls (password, name, description) VALUES (:pw, :n, :d);"); - ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL); - if (ret != SQLITE_OK) { + if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) { free(response); RETURN_ERROR("CREATE_POLL prepare"); } - ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":pw"), request->pp, -1, SQLITE_STATIC); - if (ret != SQLITE_OK) { + if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":pw"), request->pp, -1, SQLITE_STATIC)) != SQLITE_OK) { free(response); RETURN_ERROR("CREATE_POLL bind_text password"); } - ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":n"), request->pn, -1, SQLITE_STATIC); - if (ret != SQLITE_OK) { + if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":n"), request->pn, -1, SQLITE_STATIC)) != SQLITE_OK) { free(response); RETURN_ERROR("CREATE_POLL bind_text name"); } - ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":d"), request->pd, -1, SQLITE_STATIC); - if (ret != SQLITE_OK) { + if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":d"), request->pd, -1, SQLITE_STATIC)) != SQLITE_OK) { free(response); RETURN_ERROR("CREATE_POLL bind_text description"); } - ret = sqlite3_step(stmt); - sqlite3_finalize(stmt); - if (ret != SQLITE_DONE) { + if ((ret = sqlite3_step(stmt)) != SQLITE_DONE) { + sqlite3_finalize(stmt); free(response); RETURN_ERROR("CREATE_POLL step"); } + sqlite3_finalize(stmt); status_code = MHD_HTTP_SEE_OTHER; rmm = MHD_RESPMEM_MUST_FREE; // THREADSAFE: the following call to sqlite3_last_insert_rowid is racy. it's not a security issue, but if another poll is created before sqlite3_last_insert_rowid is called, the client gets a faulty id that will not work, though he can still solve the problem by using FIND_POLLS. @@ -418,7 +440,51 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio sqlite3_finalize(stmt); response = "HTTP 201: MODIFY_POLL\n"; goto r; - + case DELETE_POLL: + strcpy(statem, "DELETE FROM polls WHERE rowid=:i AND (password=:pw OR 1="); + 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) + QUERY_FAILED("DELETE_POLL"); + sqlite3_finalize(stmt); + free_location = 0; + location = "?dp"; + rmm = MHD_RESPMEM_PERSISTENT; + response = "HTTP 201: 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; + } + 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"); + 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; } if (id_string) { if (prijave->pass && pass && !strcmp(pass, prijave->pass)) { |