Introduction
Introduction Statistics Contact Development Disclaimer Help
tadd hiltjo posthuma's utilities - vote - simple cgi voting system for web and …
git clone git://src.adamsgaard.dk/vote
Log
Files
Refs
README
LICENSE
---
commit 93cfbe7466a3f69bcdf8928fdef8e3dde1d732e5
parent f49595b9c30a3a21c5380807654b1b43379e5b0d
Author: Anders Damsgaard <[email protected]>
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 <sys/socket.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <wchar.h>
+
+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("&lt;", stdout); break;
+ case '>': fputs("&gt;", stdout); break;
+ case '\'': fputs("&#39;", stdout); break;
+ case '&': fputs("&amp;", stdout); break;
+ case '"': fputs("&quot;", 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)) <= …
+ 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 <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
+#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("<!DOCTYPE html>\n"
"<html>\n"
t@@ -26,39 +40,28 @@ print_html_head() {
}
void
-print_html_foot() {
+print_html_foot()
+{
OUT("</body>\n"
"</html>\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(errn…
- 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(…
- die_500();
+ die(500);
}
}
You are viewing proxied material from mx1.adamsgaard.dk. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.