itvote.c: add function to perform safe vote increments - vote - simple cgi voting system for web and gopher Err mx1.adamsgaard.dk 70 hgit clone git://src.adamsgaard.dk/vote URL:git://src.adamsgaard.dk/vote mx1.adamsgaard.dk 70 1Log /src/vote/log.gph mx1.adamsgaard.dk 70 1Files /src/vote/files.gph mx1.adamsgaard.dk 70 1Refs /src/vote/refs.gph mx1.adamsgaard.dk 70 1README /src/vote/file/README.gph mx1.adamsgaard.dk 70 1LICENSE /src/vote/file/LICENSE.gph mx1.adamsgaard.dk 70 i--- Err mx1.adamsgaard.dk 70 1commit d5823861bfb88b89fc65a8844cd7e9a623bb0690 /src/vote/commit/d5823861bfb88b89fc65a8844cd7e9a623bb0690.gph mx1.adamsgaard.dk 70 1parent c214ae3ad448784c1da35ec3b14dec92fc92fccd /src/vote/commit/c214ae3ad448784c1da35ec3b14dec92fc92fccd.gph mx1.adamsgaard.dk 70 hAuthor: Anders Damsgaard URL:mailto:anders@adamsgaard.dk mx1.adamsgaard.dk 70 iDate: Mon, 28 Sep 2020 13:17:43 +0200 Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 ivote.c: add function to perform safe vote increments Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 iDiffstat: Err mx1.adamsgaard.dk 70 i M vote.c | 152 +++++++++++++++++++------------ Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i1 file changed, 96 insertions(+), 56 deletions(-) Err mx1.adamsgaard.dk 70 i--- Err mx1.adamsgaard.dk 70 1diff --git a/vote.c b/vote.c /src/vote/file/vote.c.gph mx1.adamsgaard.dk 70 it@@ -15,6 +15,7 @@ Err mx1.adamsgaard.dk 70 i #define OUT(s) (fputs((s), stdout)) Err mx1.adamsgaard.dk 70 i #define POLLS_DIR "polls" Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i+static char fname[PATH_MAX]; Err mx1.adamsgaard.dk 70 i static char poll[1024]; Err mx1.adamsgaard.dk 70 i static char create[2]; Err mx1.adamsgaard.dk 70 i static char question[4096]; Err mx1.adamsgaard.dk 70 it@@ -39,6 +40,23 @@ http_status(int statuscode) Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i+char * Err mx1.adamsgaard.dk 70 i+pollfile(const char *poll_name, const char *postfix) Err mx1.adamsgaard.dk 70 i+{ Err mx1.adamsgaard.dk 70 i+ char buf[PATH_MAX]; Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ strlcpy(buf, poll_name, sizeof(buf)); Err mx1.adamsgaard.dk 70 i+ escapechars(buf); Err mx1.adamsgaard.dk 70 i+ if (snprintf(fname, sizeof(fname), "%s/%s%s", Err mx1.adamsgaard.dk 70 i+ POLLS_DIR, buf, postfix) < 0) { Err mx1.adamsgaard.dk 70 i+ http_status(500); Err mx1.adamsgaard.dk 70 i+ err(1, "show_poll: snprintf fname %s/%s%s", Err mx1.adamsgaard.dk 70 i+ POLLS_DIR, buf, postfix); Err mx1.adamsgaard.dk 70 i+ } Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ return fname; Err mx1.adamsgaard.dk 70 i+} Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i void Err mx1.adamsgaard.dk 70 i print_html_head() Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 it@@ -55,31 +73,31 @@ print_html_foot() Err mx1.adamsgaard.dk 70 i "\n"); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i-void Err mx1.adamsgaard.dk 70 i-print_poll_line(char *line) Err mx1.adamsgaard.dk 70 i+int Err mx1.adamsgaard.dk 70 i+print_poll_line(char *line, int intable) Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 i- printf(""); Err mx1.adamsgaard.dk 70 i- while (*line) { Err mx1.adamsgaard.dk 70 i- switch(*line) { Err mx1.adamsgaard.dk 70 i- case '\t': Err mx1.adamsgaard.dk 70 i- printf(""); Err mx1.adamsgaard.dk 70 i- break; Err mx1.adamsgaard.dk 70 i- default: Err mx1.adamsgaard.dk 70 i- putchar(*line); Err mx1.adamsgaard.dk 70 i- break; Err mx1.adamsgaard.dk 70 i- } Err mx1.adamsgaard.dk 70 i- (void)*line++; Err mx1.adamsgaard.dk 70 i+ size_t c; Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ if (sscanf(line, "%ld\t%s", &c, options) == 2) { Err mx1.adamsgaard.dk 70 i+ if (!intable) Err mx1.adamsgaard.dk 70 i+ puts("
"); Err mx1.adamsgaard.dk 70 i+ printf("\n", c, options); Err mx1.adamsgaard.dk 70 i+ return 1; Err mx1.adamsgaard.dk 70 i+ } else { Err mx1.adamsgaard.dk 70 i+ if (intable) Err mx1.adamsgaard.dk 70 i+ puts("
%ld%s
"); Err mx1.adamsgaard.dk 70 i+ printf("%s
\n", line); Err mx1.adamsgaard.dk 70 i+ return 0; Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i- puts(""); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i void Err mx1.adamsgaard.dk 70 i-print_poll_file(FILE *fp, const char *poll_name) Err mx1.adamsgaard.dk 70 i+print_poll_file(FILE *fp) Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 i char *line = NULL; Err mx1.adamsgaard.dk 70 i- size_t linesize = 0; Err mx1.adamsgaard.dk 70 i+ size_t linesize = 0, lineno = 0; Err mx1.adamsgaard.dk 70 i ssize_t linelen; Err mx1.adamsgaard.dk 70 i- unsigned int lineno = 0; Err mx1.adamsgaard.dk 70 i+ int intable = 0; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i while ((linelen = getline(&line, &linesize, fp)) != -1) { Err mx1.adamsgaard.dk 70 i lineno++; Err mx1.adamsgaard.dk 70 it@@ -87,18 +105,18 @@ print_poll_file(FILE *fp, const char *poll_name) Err mx1.adamsgaard.dk 70 i line[--linelen] = '\0'; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i if (lineno == 1) { Err mx1.adamsgaard.dk 70 i- printf("

%s: ", poll_name); Err mx1.adamsgaard.dk 70 i+ printf("

"); Err mx1.adamsgaard.dk 70 i fwrite(line, linelen, 1, stdout); Err mx1.adamsgaard.dk 70 i- printf("

"); Err mx1.adamsgaard.dk 70 i- printf("\n"); Err mx1.adamsgaard.dk 70 i+ printf("\n"); Err mx1.adamsgaard.dk 70 i } else { Err mx1.adamsgaard.dk 70 i- print_poll_line(line); Err mx1.adamsgaard.dk 70 i+ intable = print_poll_line(line, intable); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i- /* puts("
"); */ Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i free(line); Err mx1.adamsgaard.dk 70 i- if (ferror(fp)) Err mx1.adamsgaard.dk 70 i+ if (ferror(fp)) { Err mx1.adamsgaard.dk 70 i+ http_status(500); Err mx1.adamsgaard.dk 70 i err(1, "print_poll_file: getline"); Err mx1.adamsgaard.dk 70 i+ } Err mx1.adamsgaard.dk 70 i puts("
"); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 it@@ -106,16 +124,9 @@ int Err mx1.adamsgaard.dk 70 i create_poll_file(const char *name, const char *question, const char *options) Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 i FILE *fp; Err mx1.adamsgaard.dk 70 i- char fname[PATH_MAX], buf[PATH_MAX]; Err mx1.adamsgaard.dk 70 i struct stat sb; Err mx1.adamsgaard.dk 70 i size_t col; Err mx1.adamsgaard.dk 70 i- Err mx1.adamsgaard.dk 70 i- strlcpy(buf, name, sizeof(buf)); Err mx1.adamsgaard.dk 70 i- escapechars(buf); Err mx1.adamsgaard.dk 70 i- if (snprintf(fname, sizeof(fname), "%s/%s", POLLS_DIR, buf) < 0) { Err mx1.adamsgaard.dk 70 i- http_status(500); Err mx1.adamsgaard.dk 70 i- err(1, "create_poll_file: snprintf fname %s/%s", POLLS_DIR, buf); Err mx1.adamsgaard.dk 70 i- } Err mx1.adamsgaard.dk 70 i+ char *fname; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i if (!*name || !*question || !*options) { Err mx1.adamsgaard.dk 70 i puts("

Error: Could not create poll

"); Err mx1.adamsgaard.dk 70 it@@ -130,6 +141,7 @@ create_poll_file(const char *name, const char *question, const char *options) Err mx1.adamsgaard.dk 70 i return -1; Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i+ fname = pollfile(name, ""); Err mx1.adamsgaard.dk 70 i if (stat(fname, &sb) == 0) { Err mx1.adamsgaard.dk 70 i printf("

Poll '%s' already exists

", name); Err mx1.adamsgaard.dk 70 i return -1; Err mx1.adamsgaard.dk 70 it@@ -163,7 +175,7 @@ create_poll_file(const char *name, const char *question, const char *options) Err mx1.adamsgaard.dk 70 i break; Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i- /* fputc('\n', fp); */ Err mx1.adamsgaard.dk 70 i+ fputc('\n', fp); Err mx1.adamsgaard.dk 70 i fclose(fp); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 it@@ -171,24 +183,15 @@ create_poll_file(const char *name, const char *question, const char *options) Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i void Err mx1.adamsgaard.dk 70 i-show_poll(const char *poll_name) Err mx1.adamsgaard.dk 70 i+show_poll(const char *poll) Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 i FILE *fp; Err mx1.adamsgaard.dk 70 i- char fname[PATH_MAX]; Err mx1.adamsgaard.dk 70 i- char buf[PATH_MAX]; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i- strlcpy(buf, poll_name, sizeof(buf)); Err mx1.adamsgaard.dk 70 i- escapechars(buf); Err mx1.adamsgaard.dk 70 i- if (snprintf(fname, sizeof(fname), "%s/%s", POLLS_DIR, buf) < 0) { Err mx1.adamsgaard.dk 70 i- http_status(500); Err mx1.adamsgaard.dk 70 i- err(1, "show_poll: snprintf fname %s/%s", POLLS_DIR, buf); Err mx1.adamsgaard.dk 70 i- } Err mx1.adamsgaard.dk 70 i- Err mx1.adamsgaard.dk 70 i- if (!(fp = fopen(fname, "r"))) { Err mx1.adamsgaard.dk 70 i+ if (!(fp = fopen(pollfile(poll, ""), "r"))) { Err mx1.adamsgaard.dk 70 i http_status(404); Err mx1.adamsgaard.dk 70 i exit(1); Err mx1.adamsgaard.dk 70 i } else { Err mx1.adamsgaard.dk 70 i- print_poll_file(fp, poll_name); Err mx1.adamsgaard.dk 70 i+ print_poll_file(fp); Err mx1.adamsgaard.dk 70 i fclose(fp); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 it@@ -199,12 +202,10 @@ list_polls() Err mx1.adamsgaard.dk 70 i FTS *ftsp; Err mx1.adamsgaard.dk 70 i FTSENT *p; Err mx1.adamsgaard.dk 70 i int fts_options = FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOCHDIR; Err mx1.adamsgaard.dk 70 i- /* int fts_options = FTS_NOCHDIR | FTS_PHYSICAL; */ Err mx1.adamsgaard.dk 70 i- /* char *path = POLLS_DIR; */ Err mx1.adamsgaard.dk 70 i char *paths[] = { (char*)POLLS_DIR, NULL }; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i if ((ftsp = fts_open(paths, fts_options, NULL)) == NULL) { Err mx1.adamsgaard.dk 70 i- fprintf(stderr, "could not fts_open"); Err mx1.adamsgaard.dk 70 i+ http_status(500); Err mx1.adamsgaard.dk 70 i err(1, "list_polls: fts_open"); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 it@@ -226,19 +227,55 @@ list_polls() Err mx1.adamsgaard.dk 70 i puts(""); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i-/* Err mx1.adamsgaard.dk 70 i void Err mx1.adamsgaard.dk 70 i-increment_option(FILE *fp) Err mx1.adamsgaard.dk 70 i+increment_option(char *poll, size_t n) Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 i- while ((ch = fgetc(ft)) != EOF) { Err mx1.adamsgaard.dk 70 i- if (ch == 'i') { Err mx1.adamsgaard.dk 70 i- fseek(ft, -1, SEEK_CUR); Err mx1.adamsgaard.dk 70 i- fputc('a',ft); Err mx1.adamsgaard.dk 70 i- fseek(ft, 0, SEEK_CUR); Err mx1.adamsgaard.dk 70 i+ FILE *fp, *fp_tmp; Err mx1.adamsgaard.dk 70 i+ size_t v, lineno = 0; Err mx1.adamsgaard.dk 70 i+ char *line = NULL, *fname = NULL; Err mx1.adamsgaard.dk 70 i+ static char fname_tmp[PATH_MAX]; Err mx1.adamsgaard.dk 70 i+ size_t linesize = 0; Err mx1.adamsgaard.dk 70 i+ ssize_t linelen; Err mx1.adamsgaard.dk 70 i+ struct stat sb; Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ fname = pollfile(poll, "_lock"); Err mx1.adamsgaard.dk 70 i+ strlcpy(fname_tmp, fname, sizeof(fname_tmp)); Err mx1.adamsgaard.dk 70 i+ while (stat(fname_tmp, &sb) == 0) Err mx1.adamsgaard.dk 70 i+ usleep(100); Err mx1.adamsgaard.dk 70 i+ if (!(fp_tmp = fopen(fname_tmp, "w"))) { Err mx1.adamsgaard.dk 70 i+ http_status(500); Err mx1.adamsgaard.dk 70 i+ err(1, "increment_option: fopen fp_tmp"); Err mx1.adamsgaard.dk 70 i+ } Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ fname = pollfile(poll, ""); Err mx1.adamsgaard.dk 70 i+ if (!(fp = fopen(fname, "r"))) { Err mx1.adamsgaard.dk 70 i+ http_status(404); Err mx1.adamsgaard.dk 70 i+ err(1, "increment_option: fopen fp"); Err mx1.adamsgaard.dk 70 i+ } Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ while ((linelen = getline(&line, &linesize, fp)) != -1) { Err mx1.adamsgaard.dk 70 i+ if (sscanf(line, "%ld\t%s", &v, options) != 2) Err mx1.adamsgaard.dk 70 i+ fputs(line, fp_tmp); Err mx1.adamsgaard.dk 70 i+ else { Err mx1.adamsgaard.dk 70 i+ if (++lineno == n) Err mx1.adamsgaard.dk 70 i+ v++; Err mx1.adamsgaard.dk 70 i+ fprintf(fp_tmp, "%ld\t%s\n", v, options); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ free(line); Err mx1.adamsgaard.dk 70 i+ if (ferror(fp) || ferror(fp_tmp)) { Err mx1.adamsgaard.dk 70 i+ http_status(500); Err mx1.adamsgaard.dk 70 i+ err(1, "increment_option: getline"); Err mx1.adamsgaard.dk 70 i+ } Err mx1.adamsgaard.dk 70 i+ fclose(fp); Err mx1.adamsgaard.dk 70 i+ fclose(fp_tmp); Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ if (rename(fname_tmp, fname) != 0) { Err mx1.adamsgaard.dk 70 i+ http_status(500); Err mx1.adamsgaard.dk 70 i+ err(1, "increment_option: rename"); Err mx1.adamsgaard.dk 70 i+ } Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i-*/ Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i void Err mx1.adamsgaard.dk 70 i print_poll_create_form() Err mx1.adamsgaard.dk 70 it@@ -281,8 +318,10 @@ parse_query() Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 i char *query, *p; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i- if (!(query = getenv("QUERY_STRING"))) Err mx1.adamsgaard.dk 70 i+ if (!(query = getenv("QUERY_STRING"))) { Err mx1.adamsgaard.dk 70 i query = ""; Err mx1.adamsgaard.dk 70 i+ return; Err mx1.adamsgaard.dk 70 i+ } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i if ((p = getparam(query, "create"))) { Err mx1.adamsgaard.dk 70 i if (decodeparam(create, sizeof(create), p) == -1) { Err mx1.adamsgaard.dk 70 it@@ -345,6 +384,7 @@ main() Err mx1.adamsgaard.dk 70 i show_poll(poll); Err mx1.adamsgaard.dk 70 i } else if (*poll) { Err mx1.adamsgaard.dk 70 i show_poll(poll); Err mx1.adamsgaard.dk 70 i+ increment_option(poll, 2); Err mx1.adamsgaard.dk 70 i } else { Err mx1.adamsgaard.dk 70 i list_polls(); Err mx1.adamsgaard.dk 70 i print_poll_create_form(); Err mx1.adamsgaard.dk 70 .