vote/badass.c
2021-06-11 16:20:16 -07:00

218 lines
7.6 KiB
C

/* Copyright 2007, 2008 Neil Edelman, distributed under the terms of the
GNU General Public License, see copying.txt */
/* this is a mess -Neil */
#include <stdlib.h> /* getenv(), atoi() */
#include <stdio.h> /* printf(), FILE, f*() */
#include <string.h> /* strstr() */
#include <time.h> /* time() */
/* constants */
static const char *programme = "badass";
static const char *year = "2007, 2008";
static const int versionMajor = 1;
static const int versionMinor = 0;
#define VOTE_FILE "badass.vote"
#define DESC_FILE "badass.desc"
#define ADDR_FILE "badass.addr"
#define HTML_FILE "badass.html"
#define VALID_REFERER "http://neil.chaosnet.org/"
#define TIME 256
static struct Vote {
unsigned int vote;
char *name;
char token;
} votes[] = {
{ 0, "astronaut", 'a' },
{ 0, "commando", 'c' },
{ 0, "hacker", 'h' },
{ 0, "ninja", 'n' },
{ 0, "pilot", 'p' },
{ 0, "pirate", 'i' },
{ 0, "robot", 'r' },
{ 0, "samurai", 'm' },
{ 0, "scientist", 's' },
{ 0, "spy", 'y' }
};
const static int votes_size = sizeof(votes) / sizeof(struct Vote);
char *extract_vote(char *vote);
char *votes_read(void);
char *votes_write(void);
char *votes_inc(char vote);
char *votes_html(void);
char *extract_vote(char *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 gautlet */
*vote = var[strlen("vote=")];
return 0;
}
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 = atoi(val);
}
fclose(fp);
return 0;
}
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. Probably due to a file lock from another person voting at the same time.";
for(votep = votes; votep < votes + votes_size; votep++) {
fprintf(fp, "%c=%u\n", votep->token, votep->vote);
}
fclose(fp);
return 0;
}
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 "WTF, invalid vote?";
if(votep->vote >= (unsigned short)(-1)) return "Couldn't cast vote due to a voting overflow.";
votep->vote++;
return 0;
}
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\" \"http://www.w3.org/TR/html4/strict.dtd\">\n\n");
fprintf(htf, "<html>\n\n");
fprintf(htf, "<head>\n");
fprintf(htf, "<title>Voting Results</title>\n");
fprintf(htf, "<meta name = \"Content-type\" http-equiv = \"Content-Type\" content = \"text/html; charset=us-ascii\">\n");
fprintf(htf, "<meta name = \"Generator\" content = \"/cgi-bin/badass/badass.cgi\">\n");
fprintf(htf, "<link rel = \"top\" href = \"http://neil.chaosnet.org/\">\n");
fprintf(htf, "<link rel = \"icon\" href = \"/favicon.ico\" type = \"image/x-icon\">\n");
fprintf(htf, "<link rel = \"shortcut icon\" href = \"/favicon.ico\" type = \"image/x-icon\">\n");
fprintf(htf, "<link rel = \"stylesheet\" href = \"/neil.css\">\n");
fprintf(htf, "</head>\n\n");
fprintf(htf, "<body>\n");
fprintf(htf, "<h1>Which of these professions is the most badass?</h1>");
fprintf(htf, "<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 = ((float)255 * 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 <a href = \"/\">http://neil.chaosnet.org/</a>.</p>\n");
fprintf(htf, "</body>\n\n");
fprintf(htf, "</html>\n");
fclose(htf);
return 0;
}
int main(int argc, char **argv) {
char vote, *error = 0;
if((error = votes_read()) ||
(error = extract_vote(&vote)) ||
(error = votes_inc(vote)) ||
(error = votes_write()) ||
(error = votes_html())) {
printf("Content-Type: text/html; encoding=us-ascii\n\n");
printf("<!doctype html public \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n\n");
printf("<html>\n\n");
printf("<head>\n");
printf("<title>Voting Error</title>\n");
printf("<meta name = \"Content-type\" http-equiv = \"Content-Type\" content = \"text/html; charset=us-ascii\">\n");
printf("<meta name = \"Generator\" content = \"/cgi-bin/badass/badass.cgi\">\n");
printf("<link rel = \"top\" href = \"http://neil.chaosnet.org/\">\n");
printf("<link rel = \"icon\" href = \"/favicon.ico\" type = \"image/x-icon\">\n");
printf("<link rel = \"shortcut icon\" href = \"/favicon.ico\" type = \"image/x-icon\">\n");
printf("<link rel = \"stylesheet\" href = \"/neil.css\">\n");
printf("</head>\n\n");
printf("<body>\n");
printf("<p>\n%s\n</p>\n\n", error);
printf("<p>Version %d.%d.</p>\n\n", versionMajor, versionMinor);
printf("<p>%s Copyright %s Neil Edelman\n", programme, year);
printf("This program comes with ABSOLUTELY NO WARRANTY.\n");
printf("This is free software, and you are welcome to redistribute it\n");
printf("under certain conditions; see copying.txt.</p>\n\n");
printf("<p>\nGo back to <a href = \"/\">root</a>.\n</p>\n");
printf("</body>\n\n");
printf("</html>\n");
return EXIT_FAILURE;
}
/* http-header directing users */
printf("Location: %s\n\n", HTML_FILE);
printf("Go to the <a href = \"%s\">results</a>.\n\n", HTML_FILE);
return 0;
}