add donate cgi-bin to repo - www.codemadness.org - www.codemadness.org saait content files
 (HTM) git clone git://git.codemadness.org/www.codemadness.org
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 134683ca1f57d8487892de1bee1dcd5d38315ff6
 (DIR) parent a755a0e8c7a3263cf2aeb5c2bd6d80e4270e2461
 (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
       Date:   Tue, 17 Sep 2024 10:48:37 +0200
       
       add donate cgi-bin to repo
       
       Diffstat:
         A cgi-bin/donate/Makefile             |       5 +++++
         A cgi-bin/donate/README               |       1 +
         A cgi-bin/donate/httpd.conf           |       5 +++++
         A cgi-bin/donate/index.html           |      56 +++++++++++++++++++++++++++++++
         A cgi-bin/donate/main.c               |     179 +++++++++++++++++++++++++++++++
         A cgi-bin/donate/testsuite.sh         |       2 ++
       
       6 files changed, 248 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/cgi-bin/donate/Makefile b/cgi-bin/donate/Makefile
       @@ -0,0 +1,5 @@
       +build: clean
       +        ${CC} -o main main.c -static -Os -Wall
       +
       +clean:
       +        rm -f *.o main
 (DIR) diff --git a/cgi-bin/donate/README b/cgi-bin/donate/README
       @@ -0,0 +1 @@
       +"Donations" CGI program, for fun.
 (DIR) diff --git a/cgi-bin/donate/httpd.conf b/cgi-bin/donate/httpd.conf
       @@ -0,0 +1,5 @@
       +        location "/cgi-bin/donate" {
       +                request strip 1
       +                root "/cgi-bin/donate"
       +                fastcgi socket "/run/slowcgi.sock"
       +        }
 (DIR) diff --git a/cgi-bin/donate/index.html b/cgi-bin/donate/index.html
       @@ -0,0 +1,56 @@
       +<title>Donation</title>
       +
       +<style type="text/css">
       +body {
       +        background-color: #fff;
       +        font-family: sans;
       +}
       +.form {
       +        margin: 0 auto;
       +        width: 80ex;
       +}
       +
       +textarea, input {
       +        font-size: 140%;
       +}
       +</style>
       +
       +<div class="form">
       +<h1>Donation</h1>
       +
       +<form method="get" action="/cgi-bin/donate">
       +
       +<p>
       +<label for=""><b>Type of donation:</b></label>
       +</p>
       +
       +<p>
       +<input type="radio" name="type" id="compliment" value="c" selected /> <label for="compliment">A nice compliment</label>
       +<input type="radio" name="type" id="hate" value="h" /> <label for="hate">Pure hatred</label>
       +<input type="radio" name="type" id="joke" value="j" /> <label for="joke">A joke</label>
       +<input type="radio" name="type" id="million" value="m" /> <label for="million">One million dollars</label>
       +</p>
       +
       +<p>
       +<label for="msg"><b>Message:</b></label>
       +</p>
       +
       +<p>
       +        <textarea name="msg" cols="69" rows="3" id="msg" required></textarea>
       +</p>
       +
       +<p>
       +        <label for="captcha"><b>Captcha question: do captchas suck?<br/>
       +        Type "yes" without quotes in the field below:</b></label>
       +</p>
       +
       +<p>
       +        <input type="text" name="captcha" id="captcha" value="" placeholder="yes" required size="3" />
       +</p>
       +
       +<p>
       +        <input type="submit" name="pay" value="Pay" />
       +</p>
       +
       +</form>
       +</div>
 (DIR) diff --git a/cgi-bin/donate/main.c b/cgi-bin/donate/main.c
       @@ -0,0 +1,179 @@
       +#include <ctype.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include <time.h>
       +
       +#ifdef __OpenBSD__
       +#include <unistd.h>
       +#else
       +#define pledge(p1,p2) 0
       +#define unveil(p1,p2) 0
       +#endif
       +
       +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;
       +}
       +
       +void
       +error(const char *s)
       +{
       +        fputs("Status: 400 Bad Request\r\n", stdout);
       +        fputs("Content-Type: text/plain\r\n", stdout);
       +        fputs("\r\n", stdout);
       +        fputs(s, stdout);
       +        exit(0);
       +}
       +
       +int
       +main(void)
       +{
       +        time_t now;
       +        FILE *fp;
       +        char filename[256];
       +        char _type[32], msg[4096], captcha[16];
       +        char *p, *query;
       +        int r, valid;
       +
       +        if (unveil("/donations", "rwc") == -1)
       +                return 1;
       +        if (pledge("stdio rpath cpath wpath", NULL) == -1)
       +                return 1;
       +
       +        query = getenv("QUERY_STRING");
       +        if (!query)
       +                query = "";
       +        if (query[0] == '?')
       +                query++;
       +
       +        valid = 0;
       +        if ((p = getparam(query, "captcha"))) {
       +                r = decodeparam(captcha, sizeof(captcha), p);
       +                if (r != -1 && !strcmp(captcha, "yes"))
       +                        valid = 1;
       +        }
       +        if (!valid)
       +                error("Captcha must be: yes\n");
       +
       +        valid = 0;
       +        if ((p = getparam(query, "type"))) {
       +                r = decodeparam(_type, sizeof(_type), p);
       +                if (r != -1 && (_type[0] == 'c' || _type[0] == 'h' || _type[0] == 'j' || _type[0] == 'm'))
       +                        valid = 1;
       +        }
       +        if (!valid)
       +                error("Please select a type of donation\n");
       +
       +        valid = 0;
       +        if ((p = getparam(query, "msg"))) {
       +                r = decodeparam(msg, sizeof(msg), p);
       +                if (msg[0])
       +                        valid = 1;
       +        }
       +        if (!valid)
       +                error("Please type a message\n");
       +
       +        if (!(p = getparam(query, "pay")))
       +                error("Please click the pay button\n");
       +
       +        /* future scaling improvement: multiple parallel donations at the same time won't work */
       +        now = time(NULL);
       +        r = snprintf(filename, sizeof(filename), "/donations/%c_%lld.txt", _type[0], (long long)now);
       +        if (r < 0 || (size_t)r >= sizeof(filename))
       +                error("shit happens\n");
       +
       +        fp = fopen(filename, "w+b");
       +        if (!fp)
       +                error("Cannot receive donation, thanks anyway.\n");
       +
       +        switch (_type[0]) {
       +        case 'c':
       +                fputs("Some person sent a compliment:\n\n", fp);
       +                break;
       +        case 'h':
       +                fputs("Some person sent pure hatred:\n\n", fp);
       +                break;
       +        case 'j':
       +                fputs("Some person sent a joke:\n\n", fp);
       +                break;
       +        }
       +
       +        fputs(msg, fp);
       +        fclose(fp);
       +
       +        if (_type[0] == 'm') {
       +                fputs("Status: 302 Found\r\n", stdout);
       +
       +                fputs("Location: https://www.codemadness.org/downloads/1_million_dollars.webm\r\n", stdout);
       +        } else {
       +                fputs("Status: 200 OK\r\n", stdout);
       +        }
       +
       +        fputs("Content-Type: text/plain\r\n", stdout);
       +        fputs("\r\n", stdout);
       +        fputs("Thank you for your donation!\n", stdout);
       +        return 0;
       +
       +        return 0;
       +}
 (DIR) diff --git a/cgi-bin/donate/testsuite.sh b/cgi-bin/donate/testsuite.sh
       @@ -0,0 +1,2 @@
       +#!/bin/sh
       +QUERY_STRING="?captcha=yes&type=j&msg=a&pay=1" ./main