204 lines
6.5 KiB
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;
|
|
}
|