tadd hiltjo posthuma's utilities - vote - simple cgi voting system for web and gopher
git clone git://src.adamsgaard.dk/vote
Log
Files
Refs
README
LICENSE
---
commit 93cfbe7466a3f69bcdf8928fdef8e3dde1d732e5
parent f49595b9c30a3a21c5380807654b1b43379e5b0d
Author: Anders Damsgaard 
Date:   Sun, 27 Sep 2020 08:03:51 +0200

add hiltjo posthuma's utilities

from git://git.codemadness.org/frontends

Diffstat:
  M Makefile                            |       2 +-
  A util.c                              |     206 +++++++++++++++++++++++++++++++
  A util.h                              |      18 ++++++++++++++++++
  M vote.c                              |      68 +++++++++++++++++--------------

4 files changed, 263 insertions(+), 31 deletions(-)
---
diff --git a/Makefile b/Makefile
t@@ -5,7 +5,7 @@ NAME = vote
 HERE_CFLAGS = ${CFLAGS}
 HERE_LDFLAGS = -static ${LDFLAGS}
 
-SRC = vote.c
+SRC = vote.c util.c
 OBJ = ${SRC:.c=.o}
 
 all: ${NAME}
diff --git a/util.c b/util.c
t@@ -0,0 +1,206 @@
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+int
+uriencode(const char *s, char *buf, size_t bufsiz)
+{
+        static char hex[] = "0123456789ABCDEF";
+        char *d = buf, *e = buf + bufsiz;
+        unsigned char c;
+
+        if (!bufsiz)
+                return 0;
+
+        for (; *s; ++s) {
+                c = (unsigned char)*s;
+                if (d + 4 >= e)
+                        return 0;
+                if (c == ' ' || c == '#' || c == '%' || c == '?' || c == '"' ||
+                    c == '&' || c == '<' || c <= 0x1f || c >= 0x7f) {
+                        *d++ = '%';
+                        *d++ = hex[c >> 4];
+                        *d++ = hex[c & 0x0f];
+                } else {
+                        *d++ = *s;
+                }
+        }
+        *d = '\0';
+
+        return 1;
+}
+
+int
+hexdigit(int c)
+{
+        if (c >= '0' && c <= '9')
+                return c - '0';
+        else if (c >= 'A' && c <= 'F')
+                return c - 'A' + 10;
+        else if (c >= 'a' && c <= 'f')
+                return c - 'a' + 10;
+
+        return 0;
+}
+
+/* decode until NUL separator or end of "key". */
+int
+decodeparam(char *buf, size_t bufsiz, const char *s)
+{
+        size_t i;
+
+        if (!bufsiz)
+                return -1;
+
+        for (i = 0; *s && *s != '&'; s++) {
+                switch (*s) {
+                case '%':
+                        if (i + 3 >= bufsiz)
+                                return -1;
+                        if (!isxdigit((unsigned char)*(s+1)) ||
+                            !isxdigit((unsigned char)*(s+2)))
+                                return -1;
+                        buf[i++] = hexdigit(*(s+1)) * 16 + hexdigit(*(s+2));
+                        s += 2;
+                        break;
+                case '+':
+                        if (i + 1 >= bufsiz)
+                                return -1;
+                        buf[i++] = ' ';
+                        break;
+                default:
+                        if (i + 1 >= bufsiz)
+                                return -1;
+                        buf[i++] = *s;
+                        break;
+                }
+        }
+        buf[i] = '\0';
+
+        return i;
+}
+
+char *
+getparam(const char *query, const char *s)
+{
+        const char *p, *last = NULL;
+        size_t len;
+
+        len = strlen(s);
+        for (p = query; (p = strstr(p, s)); p += len) {
+                if (p[len] == '=' && (p == query || p[-1] == '&' || p[-1] == '?'))
+                        last = p + len + 1;
+        }
+
+        return (char *)last;
+}
+
+int
+friendlytime(time_t now, time_t t)
+{
+        long long d = now - t;
+
+        if (d < 60) {
+                printf("just now");
+        } else if (d < 3600) {
+                printf("%lld minutes ago", d / 60);
+        } else if (d <= 24*3600) {
+                printf("%lld hours ago", d / 3600);
+        } else {
+                return 0;
+        }
+        return 1;
+}
+
+/* Escape characters below as HTML 2.0 / XML 1.0. */
+void
+xmlencode(const char *s)
+{
+        for (; *s; s++) {
+                switch(*s) {
+                case '<':  fputs("<", stdout);   break;
+                case '>':  fputs(">", stdout);   break;
+                case '\'': fputs("'", stdout);  break;
+                case '&':  fputs("&", stdout);  break;
+                case '"':  fputs(""", stdout); break;
+                default:   putchar(*s);
+                }
+        }
+}
+
+/* format `len' columns of characters. If string is shorter pad the rest
+ * with characters `pad`. */
+int
+utf8pad(char *buf, size_t bufsiz, const char *s, size_t len, int pad)
+{
+        wchar_t wc;
+        size_t col = 0, i, slen, siz = 0;
+        int rl, w;
+
+        if (!len)
+                return -1;
+
+        slen = strlen(s);
+        for (i = 0; i < slen; i += rl) {
+                if ((rl = mbtowc(&wc, &s[i], slen - i < 4 ? slen - i : 4)) <= 0)
+                        break;
+                if ((w = wcwidth(wc)) == -1)
+                        continue;
+                if (col + w > len || (col + w == len && s[i + rl])) {
+                        if (siz + 4 >= bufsiz)
+                                return -1;
+                        memcpy(&buf[siz], "\xe2\x80\xa6", 3);
+                        siz += 3;
+                        if (col + w == len && w > 1)
+                                buf[siz++] = pad;
+                        buf[siz] = '\0';
+                        return 0;
+                }
+                if (siz + rl + 1 >= bufsiz)
+                        return -1;
+                memcpy(&buf[siz], &s[i], rl);
+                col += w;
+                siz += rl;
+                buf[siz] = '\0';
+        }
+
+        len -= col;
+        if (siz + len + 1 >= bufsiz)
+                return -1;
+        memset(&buf[siz], pad, len);
+        siz += len;
+        buf[siz] = '\0';
+
+        return 0;
+}
+
+/* Escape characters in gopher, CR and LF are ignored */
+void
+gophertext(FILE *fp, const char *s, size_t len)
+{
+        size_t i;
+
+        for (i = 0; *s && i < len; s++, i++) {
+                switch (*s) {
+                case '\r': /* ignore CR */
+                case '\n': /* ignore LF */
+                        break;
+                case '\t':
+                        fputs("        ", fp);
+                        break;
+                default:
+                        fputc(*s, fp);
+                        break;
+                }
+        }
+}
diff --git a/util.h b/util.h
t@@ -0,0 +1,18 @@
+#ifndef __OpenBSD__
+#define pledge(p1,p2) 0
+#define unveil(p1,p2) 0
+#endif
+
+#undef strlcat
+size_t strlcat(char *, const char *, size_t);
+#undef strlcpy
+size_t strlcpy(char *, const char *, size_t);
+
+int decodeparam(char *buf, size_t bufsiz, const char *s);
+int friendlytime(time_t now, time_t t);
+char *getparam(const char *query, const char *s);
+void gophertext(FILE *fp, const char *s, size_t len);
+int hexdigit(int c);
+int uriencode(const char *s, char *buf, size_t bufsiz);
+int utf8pad(char *buf, size_t bufsiz, const char *s, size_t len, int pad);
+void xmlencode(const char *s);
diff --git a/vote.c b/vote.c
t@@ -5,20 +5,34 @@
 #include 
 #include 
 #include 
+#include "util.h"
 
 #define OUT(s) (fputs((s), stdout))
 #define POLLS_DIR "polls"
 
-static char poll[1024];
+static char rawpoll[1024], poll[1024];
 
 void
-die_500() {
-        OUT("Status: 500 Internal Server Error\r\n\r\n");
-        exit(1);
+die(int statuscode)
+{
+        switch(statuscode) {
+        case 401:
+                OUT("Status: 401 Bad Request\r\n\r\n");
+                break;
+        case 500:
+                OUT("Status: 500 Internal Server Error\r\n\r\n");
+                break;
+        default:
+                fprintf(stderr, "unknown status code %d\n", statuscode);
+                OUT("Status: 500 Internal Server Error\r\n\r\n");
+        }
+
+        exit(statuscode);
 }
 
 void
-print_html_head() {
+print_html_head()
+{
         OUT("Content-type: text/html; charset=utf-8\r\n\r\n");
         OUT("\n"
                 "\n"
t@@ -26,39 +40,28 @@ print_html_head() {
 }
 
 void
-print_html_foot() {
+print_html_foot()
+{
         OUT("\n"
                 "\n");
 }
 
 void
-show_poll(const char *poll_name) {
+show_poll(const char *poll_name)
+{
         FILE *fd;
         if ((fd = fopen(poll_name, "r")) != NULL) {
                 fclose(fd);
         } else {
                 fprintf(stderr, "poll_open %s: %s\n", poll_name, strerror(errno));
-                die_500();
+                die(500);
         }
         
 }
 
-/* from hiltjo posthuma's frontends */
-char *
-getparam(const char *query, const char *s) {
-        const char *p, *last = NULL;
-        size_t len;
-
-        len = strlen(s);
-        for (p = query; (p = strstr(p, s)); p += len) {
-                if (p[len] == '=' && (p == query || p[-1] == '&' || p[-1] == '?'))
-                        last = p + len + 1;
-        }
-        return (char *)last;
-}
-
 void
-parse_query() {
+parse_query()
+{
         char *query, *p;
         size_t len;
 
t@@ -66,33 +69,38 @@ parse_query() {
                 query = "";
 
         if ((p = getparam(query, "poll"))) {
-                if ((len = strcspn(p, "&")) && len + 1 < sizeof(poll)) {
-                        memcpy(poll, p, len);
-                        poll[len] = '\0';
+                if ((len = strcspn(p, "&")) && len + 1 < sizeof(rawpoll)) {
+                        memcpy(rawpoll, p, len);
+                        rawpoll[len] = '\0';
+                }
+
+                if (decodeparam(poll, sizeof(poll), p) == -1) {
+                        die(401);
                 }
         }
 }
 
 int
-main() {
+main()
+{
         struct stat sb;
 
 #ifdef __OpenBSD__
         if (unveil(getenv("PWD"), NULL) == -1 || unveil(NULL, NULL) == -1) {
                 fprintf(stderr, "unveil: %s\n", strerror(errno));
-                die_500();
+                die(500);
         }
 
         if (pledge("stdio cpath rpath", NULL) == -1) {
                 fprintf(stderr, "pledge: %s\n", strerror(errno));
-                die_500();
+                die(500);
         }
 #endif
 
         if (stat(POLLS_DIR, &sb) == -1) {
                 if (mkdir(POLLS_DIR, 0755) == -1) {
                         fprintf(stderr, "mkdir polls/ failed: %s\n", strerror(errno));
-                        die_500();
+                        die(500);
                 }
         }