#include <sqlite3.h>
#include <microhttpd.h>
#include <stdlib.h>
#undef NDEBUG
#include <assert.h>
#include <libintl.h>
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
#include <stdio.h>
#ifndef PR_PORT
#define PR_PORT "7745"
#endif
#define STRA(x) #x
#define STR(x) STRA(x)
struct prijave {
sqlite3 * db;
const char * pass;
const char * salt;
};
enum question {
// types
RADIO = (0 << 0),
CHECKBOX = (1 << 0),
TEXT = (1 << 1),
// options:
NOT_NULL = (1 << 5)
};
static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connection, const char * path, const char * meth, const char * ver, const char * upload, size_t * upload_size, void ** request_state) {
struct prijave * prijave = (struct prijave *) userdata;
char * response = "httpd";
char * content_type = "text/plain";
char * location = NULL;
int status_code = MHD_HTTP_OK;
enum MHD_ResponseMemoryMode rmm = MHD_RESPMEM_PERSISTENT;
static char headers_already_received;
if (strcmp(meth, "GET") && strcmp(meth, "POST")) { // pravzaprav bi bilo
status_code = MHD_HTTP_NOT_ACCEPTABLE; // bolj prav uporabiti
response = "HTTP 406\n"; // PUT request, ampak HTML form tega nima /:
goto r;
}
if (*request_state != &headers_already_received) {
*request_state = &headers_already_received;
return MHD_YES;
}
sqlite3_stmt * stmt;
int ret;
char statem[2048];
char spaces[2048];
memset(spaces, ' ', 2048);
spaces[2047] = '\0';
#define RETURN_ERROR(section) \
{ \
status_code = MHD_HTTP_BAD_GATEWAY; \
response = malloc(strlen(sqlite3_errstr(ret))+strlen(sqlite3_errmsg(prijave->db))+128+2*strlen(statem)); \
rmm = MHD_RESPMEM_MUST_FREE; \
sprintf(response, "HTTP 502: " section " %s\n%.*s^\n%s\n%s\n\n", statem, sqlite3_error_offset(prijave->db) == -1 ? 0 : sqlite3_error_offset(prijave->db), spaces, sqlite3_errstr(ret), sqlite3_errmsg(prijave->db)); \
sqlite3_finalize(stmt); /* mogoče tudi ni treba */ \
fprintf(stderr, "%s\n", response); \
goto r; \
}
#define CREATE_TABLE(table, cols) \
strcpy(statem, "CREATE TABLE IF NOT EXISTS " table " (" cols ") STRICT;"); \
ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL); \
if (ret != SQLITE_OK) \
RETURN_ERROR("prepare"); \
ret = sqlite3_step(stmt); \
sqlite3_finalize(stmt); \
if (ret != SQLITE_DONE) \
RETURN_ERROR("step");
CREATE_TABLE("responses", "person INTEGER NOT NULL, question INTEGER NOT NULL, answer ANY");
CREATE_TABLE("options", "question INTEGER NOT NULL, text TEXT");
CREATE_TABLE("questions", "id INTEGER PRIMARY KEY AUTOINCREMENT, poll INTEGER NOT NULL, text TEXT, type INTEGER NOT NULL");
CREATE_TABLE("persons", "id INTEGER PRIMARY KEY AUTOINCREMENT, poll INTEGER NOT NULL, email TEXT, name TEXT");
CREATE_TABLE("person_metadata", "person INTEGER NOT NULL, key TEXT NOT NULL, value ANY")
CREATE_TABLE("polls", "id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, password TEXT");
// TODO: CHECK() relation IDs that they exist - poll, question, responses
// TODO: CHECK() if answer is valid for RADIO/CHECKBOX questions
// TODO: CHECK() if question type is valid
struct MHD_Response * httpd_response;
r:
httpd_response = MHD_create_response_from_buffer(strlen(response), (void *) response, rmm);
MHD_add_response_header(httpd_response, "Content-Type", content_type);
if (status_code >= 300 && status_code <= 399)
MHD_add_response_header(httpd_response, "Location", location);
int r = MHD_queue_response(connection, status_code, httpd_response);
MHD_destroy_response(httpd_response);
return r;
}
static void handler (int s __attribute__((unused))) {
return;
}
int main (void) {
int r = 0;
struct prijave prijave;
memset(&prijave, 0, sizeof prijave);
umask(00077);
prijave.pass = getenv("PR_PASS"); // set to disable anyone to create polls
assert((prijave.salt = getenv("PR_SALT")));
assert(getenv("PR_DB"));
assert(sqlite3_threadsafe());
int ret = sqlite3_open_v2(getenv("PR_DB"), &prijave.db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_EXRESCODE, NULL);
if (ret != SQLITE_OK) {
fprintf(stderr, "sqlite3_open_v2: %s\n", sqlite3_errstr(ret));
goto r;
}
struct MHD_Daemon * d = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD, atoi(getenv("PR_PORT") ? getenv("PR_PORT") : PR_PORT), NULL, NULL, &httpd, &prijave, MHD_OPTION_END);
if (!d) {
r = 1;
goto r;
}
signal(SIGTERM, handler);
signal(SIGINT, handler);
pause();
r:
if (d)
MHD_stop_daemon(d); // to počaka na smrt vseh threadov afaik
if (prijave.db)
sqlite3_close_v2(prijave.db); // zato se tudi vsi stmtji izlkopijo
return r;
}