vote/src/vote.c

204 lines
6.5 KiB
C

/** @license 2007 Neil Edelman, distributed under the terms of the
[GNU General Public License 3](https://opensource.org/licenses/GPL-3.0).
These are hard-coded CGI programmes for simple voting. Compile with different
headers to change the voting scheme. */
#include <stdlib.h> /* getenv(), atoi() */
#include <stdio.h> /* printf(), FILE, f*() */
#include <string.h> /* strstr() */
#include <time.h> /* time() */
/** Vote structure. */
struct vote {
const char *name;
unsigned vote;
const char token;
};
#define TIME 256
#if 1
#include "badass.h"
#else
#include "movie.h"
#endif
/* `votes` defined in header. */
static const size_t votes_size = sizeof votes / sizeof *votes;
/** @param[vote] Address of the variable that receives the `vote.vote` letter
or '\0'. */
static char *extract_vote(char *const vote) {
FILE *fp;
char *env, *var;
/* at least it will zero */
*vote = '\0';
/* check env vars */
if(!(env = getenv("REQUEST_METHOD")) || strcmp(env, "GET"))
return "Invalid request method.";
if(!(env = getenv("HTTP_REFERER"))
|| strncmp(env, VALID_REFERER, strlen(VALID_REFERER)))
return "Invalid referer.";
if(!(env = getenv("QUERY_STRING")) || !(var = strstr(env, "vote=")))
return "Invalid query string.";
/* if the addr matches the ADDR do'n't vote */
if((fp = fopen(ADDR_FILE, "r"))) {
/* 256.256.256.256 - 18446744073709551615 */
char ip[17], tm[21], *rlf;
fgets(ip, sizeof ip, fp);
if((rlf = strrchr(ip, '\n'))) *rlf = 0; /* delete lf at the end */
if(!(env = getenv("REMOTE_ADDR"))) return "REMOTE_ADDR not set.";
if(!strcmp(env, ip)) {
fgets(tm, sizeof(tm), fp);
if(atoi(tm) + TIME > time(0))
return "This remote address has voted recenty.";
}
fclose(fp);
}
/* make up the new addr */
if((fp = fopen(ADDR_FILE, "w"))) {
time_t t;
fprintf(fp, "%.16s\n", getenv("REMOTE_ADDR"));
/* attempt to insert the time */
t = time(0);
if(t == (time_t)-1) fprintf(fp, "%d\n", 0);
else fprintf(fp, "%d\n", (int)t);
fclose(fp);
}
/* though the gauntlet */
*vote = var[strlen("vote=")];
return 0;
}
static char *votes_read(void) {
struct vote *votep;
FILE *fp;
char line[81], *ch, *val;
/* read in the votes */
if(!(fp = fopen(VOTE_FILE, "r"))) {
/* it's (probably) the first time */
if(!(fp = fopen(VOTE_FILE, "w")))
return "Secret archive is'n't opening.";
fclose(fp);
return 0;
}
while(fgets(line, sizeof(line), fp)) {
/* go to the first solid character in the line */
for(ch = line; *ch && (*ch <= ' ' || *ch > '~'); ch++);
/* search for a match within the token array */
for(votep = votes; votep < votes + votes_size; votep++)
if(*ch == votep->token) break;
if( votep >= votes + votes_size) continue;
/* go to the first number in the line */
for(val = ch; *val && (*val < '0' || *val > '9'); val++);
/* convert the numerical string to a number and save it */
votep->vote = (unsigned)atoi(val); /* This is lazy, use `strtol`. */
}
fclose(fp);
return 0;
}
static char *votes_write(void) {
struct vote *votep;
FILE *fp;
/* write each token and value to the vote file */
if(!(fp = fopen(VOTE_FILE, "w")))
return "Error storing your vote.";
for(votep = votes; votep < votes + votes_size; votep++)
fprintf(fp, "%c=%u\n", votep->token, votep->vote);
fclose(fp);
return 0;
}
/** @param[vote] The `vote.vote` letter of the struct. */
static char *votes_inc(char vote) {
struct vote *votep;
/* search for a match for the first letter of vote as a token */
for(votep = votes; votep < votes + votes_size; votep++)
if(vote == votep->token) break;
if( votep >= votes + votes_size) return "Invalid vote?";
if(votep->vote >= (unsigned short)(-1)) return "Voting overflow.";
votep->vote++;
return 0;
}
static char *votes_html(void) {
struct vote *votep;
FILE *htf;
unsigned long max = 1;
unsigned char arg;
/* rewrite the HTML file */
if(!(htf = fopen(HTML_FILE, "w")))
return "Failed preparing the vote result page for updating.";
fprintf(htf, "<!doctype html public \"-//W3C//DTD HTML 4.01//EN\"\n"
"\t\"http://www.w3.org/TR/html4/strict.dtd\">\n\n"
"<html>\n\n"
"<head>\n"
"<title>Voting Results</title>\n"
"<meta name = \"Content-type\" http-equiv = \"Content-Type\" "
"content = \"text/html; charset=us-ascii\">\n"
"<meta name = \"Generator\" content = \"cgi\">\n"
"<link rel = \"top\" href = \"http://neil.chaosnet.org/\">\n"
"<link rel = \"icon\" href = \"/favicon.ico\" "
"type = \"image/x-icon\">\n"
"<link rel = \"shortcut icon\" href = \"/favicon.ico\" "
"type = \"image/x-icon\">\n");
fprintf(htf, "<link rel = \"stylesheet\" href = \"/neil.css\">\n"
"</head>\n\n"
"<body>\n"
"<h1>" VOTE_TITLE "</h1>\n\n"
"<p>\nCurrent results:\n</p>\n\n");
/* write out each name and vote */
for(votep = votes; votep < votes + votes_size; votep++)
if(max < votep->vote) max = votep->vote;
for(votep = votes; votep < votes + votes_size; votep++) {
arg = (unsigned char)((255.0 * votep->vote) / max);
fprintf(htf, "<p><img src = \"../gifbar/gifbar.cgi?%u\" "
"alt = \"%u\" width = 256 height = 8> ", arg, arg);
fprintf(htf, "<em>%s</em>: %u vote%c</p>",
votep->name, votep->vote, (votep->vote == 1) ? ' ' : 's');
}
fprintf(htf, "<p>Go back to " VOTE_BACK ".</p>\n"
"</body>\n\n"
"</html>\n");
fclose(htf);
return 0;
}
/** Reads and, possibly writes an incremented vote. */
int main(void) {
char vote, *error = 0;
if((error = votes_read()) ||
(error = extract_vote(&vote)) ||
(error = votes_inc(vote)) ||
(error = votes_write()) ||
(error = votes_html())) {
fputs("Content-Type: text/html; encoding=us-ascii\n\n"
"<!doctype html public \"-//W3C//DTD HTML 4.01//EN\"\n"
"\t\"http://www.w3.org/TR/html4/strict.dtd\">\n\n"
"<html>\n\n"
"<head>\n"
"<title>Voting Error</title>\n"
"<meta name = \"Content-type\" http-equiv = \"Content-Type\" "
"content = \"text/html; charset=us-ascii\">\n"
"<meta name = \"Generator\" content = \"badass.cgi\">\n"
"<link rel = \"top\" href = \"http://neil.chaosnet.org/\">\n"
"<link rel = \"icon\" href = \"/favicon.ico\" "
"type = \"image/x-icon\">\n", stdout);
printf("<link rel = \"shortcut icon\" href = \"/favicon.ico\" "
"type = \"image/x-icon\">\n"
"<link rel = \"stylesheet\" href = \"/neil.css\">\n"
"</head>\n\n"
"<body>\n"
"<p>\n%s\n</p>\n\n"
"<p>Go back to " VOTE_BACK ".\n</p>\n"
"</body>\n\n"
"</html>\n", error);
return EXIT_FAILURE;
}
/* http-header directing users */
fputs("Location: " HTML_FILE "\n\n"
"Go to the <a href = \"" HTML_FILE "\">results</a>.\n\n", stdout);
return EXIT_SUCCESS;
}