tvote.c: implement voting functionality - vote - simple cgi voting system for web and gopher
 (HTM) git clone git://src.adamsgaard.dk/vote
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 0b3501599120c01cca8c2e8532fcd31e781f48ec
 (DIR) parent d5823861bfb88b89fc65a8844cd7e9a623bb0690
 (HTM) Author: Anders Damsgaard <anders@adamsgaard.dk>
       Date:   Mon, 28 Sep 2020 14:09:43 +0200
       
       vote.c: implement voting functionality
       
       Diffstat:
         M vote.c                              |      91 +++++++++++++++++++++----------
       
       1 file changed, 62 insertions(+), 29 deletions(-)
       ---
 (DIR) diff --git a/vote.c b/vote.c
       t@@ -12,7 +12,6 @@
        #include "util.h"
        
        #define LEN(s) (sizeof(s) / sizeof(s[0]))
       -#define OUT(s) (fputs((s), stdout))
        #define POLLS_DIR "polls"
        
        static char fname[PATH_MAX];
       t@@ -20,23 +19,24 @@ static char poll[1024];
        static char create[2];
        static char question[4096];
        static char options[4096];
       +static char choice[16];
        
        void
        http_status(int statuscode)
        {
                switch(statuscode) {
                case 401:
       -                OUT("Status: 401 Bad Request\r\n\r\n");
       +                printf("Status: 401 Bad Request\r\n\r\n");
                        break;
                case 404:
       -                OUT("Status: 404 Not Found\r\n\r\n");
       +                printf("Status: 404 Not Found\r\n\r\n");
                        break;
                case 500:
       -                OUT("Status: 500 Internal Server Error\r\n\r\n");
       +                printf("Status: 500 Internal Server Error\r\n\r\n");
                        break;
                default:
                        err(1, "unknown status code %d\n", statuscode);
       -                OUT("Status: 500 Internal Server Error\r\n\r\n");
       +                printf("Status: 500 Internal Server Error\r\n\r\n");
                }
        }
        
       t@@ -60,8 +60,8 @@ pollfile(const char *poll_name, const char *postfix)
        void
        print_html_head()
        {
       -        OUT("Content-type: text/html; charset=utf-8\r\n\r\n");
       -        OUT("<!DOCTYPE html>\n"
       +        printf("Content-type: text/html; charset=utf-8\r\n\r\n");
       +        printf("<!DOCTYPE html>\n"
                        "<html>\n"
                        "<body>\n");
        }
       t@@ -69,33 +69,45 @@ print_html_head()
        void
        print_html_foot()
        {
       -        OUT("</body>\n"
       +        printf("</body>\n"
                        "</html>\n");
        }
        
        int
       -print_poll_line(char *line, int intable)
       +print_poll_line(char *line, size_t *i, int intable, int vote)
        {
                size_t c;
        
                if (sscanf(line, "%ld\t%s", &c, options) == 2) {
       -                if (!intable)
       -                        puts("<br><table>");
       -                printf("<tr><td>%ld</td><td>%s</td></tr>\n", c, options);
       +                if (!intable) {
       +                        puts("</p>\n<table>");
       +                        if (vote) {
       +                                puts("<form method=\"get\" action=\"\">");
       +                                printf("<input type=\"hidden\" name=\"poll\" "
       +                                        "value=\"%s\" />\n", poll);
       +                        }
       +                }
       +                if (vote) {
       +                        printf("\t<tr><td>");
       +                        printf("<input type=\"radio\" "
       +                                "id=\"%ld\" name=\"choice\" value=\"%ld\" />",
       +                                ++*i, *i);
       +                        printf("</td><td>%s</td></tr>\n", options);
       +                } else
       +                        printf("\t<tr><td>%ld</td><td>%s</td></tr>\n", c, options);
                        return 1;
                } else {
       -                if (intable)
       -                        puts("</table>");
       +
                        printf("%s<br>\n", line);
                        return 0;
                }
        }
        
        void
       -print_poll_file(FILE *fp)
       +print_poll_file(FILE *fp, int vote)
        {
                char *line = NULL;
       -        size_t linesize = 0, lineno = 0;
       +        size_t linesize = 0, lineno = 0, i = 0;
                ssize_t linelen;
                int intable = 0;
        
       t@@ -107,9 +119,9 @@ print_poll_file(FILE *fp)
                        if (lineno == 1) {
                                printf("<h2>");
                                fwrite(line, linelen, 1, stdout);
       -                        printf("</h2>\n");
       +                        printf("</h2>\n<p>");
                        } else {
       -                        intable = print_poll_line(line, intable);
       +                        intable = print_poll_line(line, &i, intable, vote);
                        }
                }
                free(line);
       t@@ -117,7 +129,13 @@ print_poll_file(FILE *fp)
                        http_status(500);
                        err(1, "print_poll_file: getline");
                }
       +
                puts("</table>");
       +        if (vote) {
       +                puts("<input type=\"submit\" value=\"Submit\" "
       +                        "class=\"button\"/>");
       +                puts("</form>");
       +        }
        }
        
        int
       t@@ -183,7 +201,7 @@ create_poll_file(const char *name, const char *question, const char *options)
        }
        
        void
       -show_poll(const char *poll)
       +show_poll(const char *poll, int vote)
        {
                FILE *fp;
        
       t@@ -191,7 +209,7 @@ show_poll(const char *poll)
                        http_status(404);
                        exit(1);
                } else {
       -                print_poll_file(fp);
       +                print_poll_file(fp, vote);
                        fclose(fp);
                }
        }
       t@@ -280,28 +298,28 @@ increment_option(char *poll, size_t n)
        void
        print_poll_create_form()
        {
       -        OUT("<h2>Create new poll</h2>");
       -        OUT("<form method=\"get\" action=\"\">\n"
       +        puts("<h2>Create new poll</h2>");
       +        puts("<form method=\"get\" action=\"\">\n"
                        "<input type=\"hidden\" name=\"create\" value=\"1\" />\n"
                        "<table class=\"create\" width=\"100%\" border=\"0\" "
                        "cellpadding=\"0\" cellspacing=\"0\">\n"
                        "<tr>\n"
                        "        <td width=\"100%\" class=\"input\">\n"
                        "                <input type=\"text\" name=\"poll\" "
       -                "                 placeholder=\"Name\" size=\"60\" "
       -                "                 autofocus=\"autofocus\" class=\"name\" />\n"
       +                "placeholder=\"Name\" size=\"60\" "
       +                "autofocus=\"autofocus\" class=\"name\" />\n"
                        "        </td>\n"
                        "</tr>\n"
                        "<tr>\n"
                        "        <td width=\"100%\" class=\"input\">\n"
                        "                <input type=\"text\" name=\"question\" "
       -                "                 placeholder=\"Question\" size=\"60\" class=\"question\" />\n"
       +                "placeholder=\"Question\" size=\"60\" class=\"question\" />\n"
                        "        </td>\n"
                        "</tr>\n"
                        "<tr>\n"
                        "        <td width=\"100%\" class=\"input\">\n"
                        "       <textarea rows=\"5\" cols=\"60\" name=\"options\" "
       -                "                 placeholder=\"Options (1 per line)\"></textarea>\n"
       +                "placeholder=\"Options (1 per line)\"></textarea>\n"
                        "        </td>\n"
                        "</tr>\n"
                        "<tr>\n"
       t@@ -351,11 +369,20 @@ parse_query()
                        }
                }
        
       +        if ((p = getparam(query, "choice"))) {
       +                if (decodeparam(choice, sizeof(create), p) == -1) {
       +                        http_status(401);
       +                        exit(1);
       +                }
       +        }
       +
        }
        
        int
        main()
        {
       +        size_t c;
       +        const char *errstr;
                struct stat sb;
        
                if (unveil(getenv("PWD"), NULL) == -1 || unveil(NULL, NULL) == -1) {
       t@@ -381,10 +408,16 @@ main()
        
                if (*create) {
                        if (create_poll_file(poll, question, options) == 0)
       -                        show_poll(poll);
       +                        show_poll(poll, 0);
                } else if (*poll) {
       -                show_poll(poll);
       -                increment_option(poll, 2);
       +                if (*choice) {
       +                        c = strtonum(choice, 1, 256, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "could not parse choice: %s, %s", errstr, choice);
       +                        increment_option(poll, c);
       +                        show_poll(poll, 0);
       +                } else
       +                        show_poll(poll, 1);
                } else {
                        list_polls();
                        print_poll_create_form();