#include #include #include #undef NDEBUG #include #include #include #include #include #include #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; }