#include "cbook-main.h" char *rasprintf(const char *f, ...) { char *r = NULL; va_list a; va_start(a, f); vasprintf(&r, f, a); va_end(a); return r; } char *htmlEscape(char *s) { char *r = malloc(32); /* l = original string length i = current byte of original string j = current byte of new string b = allocated bytes for new string */ long l = strlen(s), i, j, b = 32; for (i = 0, j = 0; i < l; i++) { if (j == b - 7) // we could need up to 6 chars r = realloc(r, b *= 2); if (s[i] == '&') { strcpy(&r[j], "&"); j += 5; } else if (s[i] == '<') { strcpy(&r[j], "<"); j += 4; } else if (s[i] == '>') { strcpy(&r[j], ">"); j += 4; } else if (s[i] == '"') { strcpy(&r[j], """); j += 6; } else if (s[i] == '\'') { strcpy(&r[j], "'"); j += 6; } else r[j++] = s[i]; } r[j] = 0; return r; } void printHtml(char *s) { char *escaped = htmlEscape(s); fputs(escaped, stdout); free(escaped); } void parseConf() { cfg_opt_t opts[] = { CFG_SIMPLE_STR("mysqlHostname", &config.mysqlHostname), CFG_SIMPLE_STR("mysqlUsername", &config.mysqlUsername), CFG_SIMPLE_STR("mysqlPassword", &config.mysqlPassword), CFG_SIMPLE_STR("mysqlDatabase", &config.mysqlDatabase), CFG_SIMPLE_STR("siteName", &config.siteName), CFG_END() }; cfg_t *cfg = cfg_init(opts, 0); cfg_parse(cfg, CBOOK_CONFFILE); } void myerror() { printf("error: mysql: %u: %s\n", mysql_errno(mysql), mysql_error(mysql)); exit(1); } char *myescape(char *q) { unsigned long l = strlen(q); char *r = malloc(l * 2 + 1); unsigned long rl = mysql_real_escape_string(mysql, r, q, l); r = realloc(r, rl + 1); return r; } void fail(const char *s) { puts(s); exit(1); } void myquery(const char *f, ...) { va_list a; va_start(a, f); char *q = NULL; vasprintf(&q, f, a); va_end(a); if (!q) { puts("error: query: vasprintf returned null"); exit(1); } if (mysql_query(mysql, q)) myerror(); free(q); } void createDatabase() { myquery("create database %s character set utf8 collate utf8_general_ci", config.mysqlDatabase); if (mysql_select_db(mysql, config.mysqlDatabase)) myerror(); myquery("create table bookings (\n\ id bigint not null auto_increment primary key,\n\ item bigint not null,\n\ year bigint not null,\n\ month bigint not null,\n\ date bigint not null,\n\ session bigint not null\n\ )"); myquery("create table sessions (\n\ id bigint not null auto_increment primary key,\n\ name longtext not null\n\ )"); } void pageHeader(char *title) { puts("Content-type: text/html\n"); puts(""); puts(""); puts("\t"); puts("\t\t"); printf("\t\tcbook: "); printHtml(config.siteName); printf(": "); printHtml(title); puts(""); puts("\t\t"); puts("\t"); puts("\t"); printf("\t\t

cbook: "); printHtml(config.siteName); puts("

"); } void pageFooter() { puts("\t\t
");
	printQueryHash();
	puts("\t\t
"); puts("\t"); puts(""); } void pageHome() { pageHeader("home"); long i; MYSQL_RES *res; MYSQL_ROW row; puts("\t\t"); puts("\t\t\t"); puts("\t\t\t\t"); printf("\t\t\t\t"); puts("\t\t\t\t
"); for (i = 0; i < 7; i++) printf("\t\t\t\t%s\n", constDays[i]); myquery("select * from sessions"); res = mysql_store_result(mysql); long hasSessions = 0; while (row = mysql_fetch_row(res)) { hasSessions = 1; puts("\t\t\t
"); printHtml(row[1]); printf("\n"); for (i = 0; i < 7; i++) puts("\t\t\t\t"); } if (!hasSessions) { puts("\t\t\t
No sessions available. Add some."); } puts("\t\t
"); mysql_free_result(res); pageFooter(); } void pageSessions() { pageHeader("sessions"); MYSQL_RES *res; MYSQL_ROW row; puts("\t\t

sessions

"); puts("\t\t
"); puts("\t\t\t"); puts("\t\t\t"); puts("\t\t\t"); puts("\t\t
"); puts("\t\t"); puts("\t\t"); puts("\t\t\t"); puts("\t\t\t\t"); printf("\t\t\t\t
id"); puts("\t\t\t\tsession name"); myquery("select * from sessions"); res = mysql_store_result(mysql); while (row = mysql_fetch_row(res)) { puts("\t\t\t
"); printHtml(row[0]); printf("\t\t\t\t"); printHtml(row[1]); printf("\n"); } puts("\t\t
"); pageFooter(); } void pageSessionAdd() { char *v = g_hash_table_lookup(queryHash, "name"); v = myescape(v); myquery("insert into sessions (name) values ('%s')", v); free(v); puts("Location: ?p=sessions\n"); } void parseQueryString() { queryHash = g_hash_table_new(g_str_hash, g_str_equal); char *s = getenv("QUERY_STRING"); long state = DQUERY_STATE_KEY, i, j = 0, k = 0, l = strlen(s); for (i = 0; i <= l; i++) { switch (state) { case DQUERY_STATE_KEY: if (s[i] == '&' || s[i] == 0) { g_hash_table_insert(queryHash, urlDecode(strndup(&s[j], i - j)), NULL); j = i + 1; } else if (s[i] == '=') { k = i + 1; state = DQUERY_STATE_VALUE; } break; case DQUERY_STATE_VALUE: if (s[i] == '&' || s[i] == 0) { g_hash_table_insert(queryHash, urlDecode(strndup(&s[j], k - j - 1)), urlDecode(strndup(&s[k], i - k))); j = i + 1; state = DQUERY_STATE_KEY; } break; default: fail("error: parseQueryString: invalid state"); } } } void printQueryHash() { printf("query: '%s'\n", getenv("QUERY_STRING")); printf("query hash size: %u\n", g_hash_table_size(queryHash)); g_hash_table_foreach(queryHash, printQueryHashHelper, NULL); } void printQueryHashHelper(gpointer key, gpointer value, gpointer user) { printf("'%s' = '%s'\n", (char *) key, (char *) value); } char *urlDecode(char *s) { long i, l = strlen(s), state = 0, offset = 0, byte = 0; for (i = 0; i <= l; i++) { switch (state) { case DURI_STATE_WAIT: if (s[i + offset] == '%') state = DURI_STATE_FIRST; else if (s[i + offset] == '+') s[i] = ' '; else s[i] = s[i + offset]; break; case DURI_STATE_FIRST: if (s[i + offset] == 0) fail("error: urlDecode: string ended waiting for first byte of percent escape"); if (s[i + offset] != '0' && s[i + offset] != '1' && s[i + offset] != '2' && s[i + offset] != '3' && s[i + offset] != '4' && s[i + offset] != '5' && s[i + offset] != '6' && s[i + offset] != '7' && s[i + offset] != '8' && s[i + offset] != '9' && s[i + offset] != 'A' && s[i + offset] != 'B' && s[i + offset] != 'C' && s[i + offset] != 'D' && s[i + offset] != 'E' && s[i + offset] != 'F' && s[i + offset] != 'a' && s[i + offset] != 'b' && s[i + offset] != 'c' && s[i + offset] != 'd' && s[i + offset] != 'e' && s[i + offset] != 'f') fail("error: urlDecode: expecting first byte to be [0-9A-Fa-f]"); byte = (s[i + offset] >= '0' && s[i + offset] <= '9') ? ((s[i + offset] - '0') << 4) : (s[i + offset] >= 'A' && s[i + offset] <= 'F') ? ((10 + s[i + offset] - 'A') << 4): (s[i + offset] >= 'a' && s[i + offset] <= 'f') ? ((10 + s[i + offset] - 'a') << 4) : 0; state = DURI_STATE_SECOND; break; case DURI_STATE_SECOND: if (s[i + offset] == 0) fail("error: urlDecode: string ended waiting for second byte of percent escape"); if (s[i + offset] != '0' && s[i + offset] != '1' && s[i + offset] != '2' && s[i + offset] != '3' && s[i + offset] != '4' && s[i + offset] != '5' && s[i + offset] != '6' && s[i + offset] != '7' && s[i + offset] != '8' && s[i + offset] != '9' && s[i + offset] != 'A' && s[i + offset] != 'B' && s[i + offset] != 'C' && s[i + offset] != 'D' && s[i + offset] != 'E' && s[i + offset] != 'F' && s[i + offset] != 'a' && s[i + offset] != 'b' && s[i + offset] != 'c' && s[i + offset] != 'd' && s[i + offset] != 'e' && s[i + offset] != 'f') fail("error: urlDecode: expecting second byte to be [0-9A-Fa-f]"); byte |= (s[i + offset] >= '0' && s[i + offset] <= '9') ? (s[i + offset] - '0') : (s[i + offset] >= 'A' && s[i + offset] <= 'F') ? (10 + s[i + offset] - 'A'): (s[i + offset] >= 'a' && s[i + offset] <= 'f') ? (10 + s[i + offset] - 'a') : 0; i -= 2; offset += 2; s[i] = byte; state = DURI_STATE_WAIT; break; default: fail("error: urlDecode: invalid state"); } } return s; } int main(int argc, char **argv) { parseConf(); parseQueryString(); mysql = mysql_init(NULL); if (!mysql) myerror(); if (!mysql_real_connect(mysql, config.mysqlHostname, config.mysqlUsername, config.mysqlPassword, NULL, 0, NULL, 0)) myerror(); if (mysql_select_db(mysql, config.mysqlDatabase)) createDatabase(); char *page = g_hash_table_lookup(queryHash, "p"); if (page) if (!strcmp(page, "sessions")) pageSessions(); else if (!strcmp(page, "session_add")) pageSessionAdd(); else pageHome(); else pageHome(); mysql_close(mysql); return 0; }