summaryrefslogblamecommitdiffstats
path: root/prijave.c
blob: d16c8c60f50dfb2aa36514e0819e764f15cdc0e0 (plain) (tree)

























































































































                                                                                                                                                                                                                                      
#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;
}