summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Luka Šijanec <anton@sijanec.eu>2022-05-12 22:18:41 +0200
committerAnton Luka Šijanec <anton@sijanec.eu>2022-05-12 22:18:41 +0200
commitac159d0b47f048beb72edabbd05c893894e95721 (patch)
tree2bee3c1e3ee58978af03433c58aaa10a606a123f
downloadprijave-ac159d0b47f048beb72edabbd05c893894e95721.tar
prijave-ac159d0b47f048beb72edabbd05c893894e95721.tar.gz
prijave-ac159d0b47f048beb72edabbd05c893894e95721.tar.bz2
prijave-ac159d0b47f048beb72edabbd05c893894e95721.tar.lz
prijave-ac159d0b47f048beb72edabbd05c893894e95721.tar.xz
prijave-ac159d0b47f048beb72edabbd05c893894e95721.tar.zst
prijave-ac159d0b47f048beb72edabbd05c893894e95721.zip
-rw-r--r--.gitignore4
-rw-r--r--Makefile27
-rw-r--r--prijave.c122
-rw-r--r--prijave.conf4
4 files changed, 157 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2623084
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.out
+prijave
+core
+sqlite3
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a49d1dc
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,27 @@
+DESTDIR=/
+CC = cc
+cflags = -O0 -Wall -I. -fpic -Wformat-security -Wextra -pedantic -g3
+ldflags = $(shell pkg-config --libs sqlite3) -lmicrohttpd # -lintl
+PROJ = prijave
+# cflags and ldflags are used so that users specifying CFLAGS and LDFLAGS do not override my flags
+# += is not used, because gcc usually accepts last option, for example -O0 -O2 will use -O2
+.NOTPARALLEL:
+default:
+ $(CC) -L. $(cflags) $(CFLAGS) $(PROJ).c $(ldflags) $(LDFLAGS) -o$(PROJ)
+install:
+ mkdir -p $(DESTDIR)/usr/bin $(DESTDIR)/etc
+ cp $(PROJ) $(DESTDIR)/usr/bin/
+ -[ ! -f $(DESTDIR)/etc/$(PROJ) ] && cp $(PROJ).conf $(DESTDIR)/etc/$(PROJ)
+
+distclean: clean
+
+clean:
+ rm -f $(PROJ)
+
+uninstall:
+ rm -f $(DESTDIR)/usr/bin/$(PROJ)
+
+valgrind:
+ valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt $(COMMAND)
+
+.PHONY: valgrind clean distclean install uninstall default
diff --git a/prijave.c b/prijave.c
new file mode 100644
index 0000000..d16c8c6
--- /dev/null
+++ b/prijave.c
@@ -0,0 +1,122 @@
+#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;
+}
diff --git a/prijave.conf b/prijave.conf
new file mode 100644
index 0000000..67de89b
--- /dev/null
+++ b/prijave.conf
@@ -0,0 +1,4 @@
+PR_PORT=7745
+PR_DB=/var/lib/prijave/sqlite3
+PR_PASS=admin
+PR_SALT=salt