xdfcgi/main.c

175 lines
5.8 KiB
C
Raw Normal View History

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sqlite3.h>
inline char *get_filename(const char *c) {
char *qout = "queries/mranks.sql";
if (c != NULL) {
switch(*c) {
case 'm':
qout = "queries/mleaderboard-ojoin.sql";
break;
case 'p':
qout = "queries/rplayers.sql";
break;
case 't':
qout = "queries/test.txt";
break;
}
}
return qout;
}
2017-03-14 07:48:50 +05:30
inline void print_tblheader(const char *c) {
2017-03-13 11:50:57 +05:30
char *labels;
switch (*c) {
2017-03-13 08:02:57 +05:30
default:
2017-03-13 11:50:57 +05:30
labels = "<TABLE class='center'>\
2017-03-13 08:02:57 +05:30
<TH class='tablename' COLSPAN='4'> <H3><BR>Map List</H3> </TH>\
<TR>\
<TH class='columnname'>Name</TH>\
<TH class='columnname'>Records</TH>\
<TH class='columnname'>Best Time</TH>\
<TH class='columnname'>Held By</TH>\
</TR>";
break;
case 'm':
2017-03-13 11:50:57 +05:30
labels = "<TABLE class='center'>\
2017-03-13 08:02:57 +05:30
<TH class='tablename' COLSPAN='3'><H3><BR>Leaderboard</H3></TH>\
<TR>\
<TH class='columnname'>Rank</TH>\
<TH class='columnname'>Name</TH>\
<TH class='columnname'>Time</TH>\
</TR>";
break;
case 'p':
2017-03-13 11:50:57 +05:30
labels = "<TABLE class='center'>\
2017-03-13 08:02:57 +05:30
<TH class='tablename' COLSPAN='3'> <H3><BR>Ranks</H3> </TH>\
<TR>\
<TH class='columnname'>Name</TH>\
<TH class='columnname'>Map</TH>\
<TH class='columnname'>Rank</TH>\
</TR>";
break;
}
2017-03-13 11:50:57 +05:30
printf("%s", labels);
}
// input is an integer as a string.
// the time is in centiseconds.
// this means the longest XDF run this can support is
// 21,474,836.47 seconds
// someone would need to idle in-game for 248 days
2017-03-14 07:48:50 +05:30
void print_time(const unsigned char *strcs) {
const unsigned int num = strtoul((const char*)strcs, NULL, 10);
2017-03-13 11:50:57 +05:30
unsigned int s = num/100;
const unsigned int cs = num % 100;
const unsigned int min = s/60;
s = s % 60;
if (min > 0) {
printf("<TD>%u:%.2u.%.2u</TD>", min, s, cs);
} else {
printf("<TD>%u.%.2u</TD>", s, cs);
}
2017-03-13 08:02:57 +05:30
}
// display and format the results of the query.
2017-03-13 11:50:57 +05:30
void qresult(sqlite3_stmt * const sp, const char *c) {
2017-03-13 08:02:57 +05:30
int e;
2017-03-13 08:33:51 +05:30
unsigned int i;
2017-03-13 11:50:57 +05:30
unsigned int cc = sqlite3_column_count(sp);
2017-03-14 07:48:50 +05:30
print_tblheader(c);
2017-03-13 08:02:57 +05:30
while ((e = sqlite3_step(sp)) == SQLITE_ROW) {
2017-03-13 08:33:51 +05:30
printf("<TR>");
2017-03-13 08:02:57 +05:30
for (i = 0; i < cc; i++) {
2017-03-14 07:48:50 +05:30
if ((*c == 'm' && i == 1) || (*c == '\0' && i == 3) || (*c == 'p' && i == 0)) {
2017-03-13 11:50:57 +05:30
printf("<TD>%s</TD>", sqlite3_column_text(sp, i));
} else if ((i == 0 && *c == '\0') || (i == 1 && *c == 'p') ) {
printf("<TD><a href='/cgi/cts.py?map=%s'>%s</a></TD>", sqlite3_column_text(sp, i), sqlite3_column_text(sp, i));
} else if (i == 2 && (*c == 'm' || *c == '\0')) {
2017-03-13 11:50:57 +05:30
print_time(sqlite3_column_text(sp, i));
} else {
printf("<TD>%s</TD>", sqlite3_column_text(sp, i));
2017-03-13 11:50:57 +05:30
}
2017-03-13 08:02:57 +05:30
}
2017-03-13 08:33:51 +05:30
printf("</TR>");
2017-03-13 08:02:57 +05:30
}
2017-03-13 08:33:51 +05:30
printf("</TABLE>");
2017-03-13 08:02:57 +05:30
}
// const char 'sql' is never NULL.
// const char 'str' may be NULL, however it is only when
// the contents of mranks.sql is in const char 'sql'.
2017-03-13 08:33:51 +05:30
bool executequery(const char *sql, const char *str) {
const char *p = (str != NULL) ? strchr(str, '=') + 1 : NULL;
const char qc = (str != NULL) ? str[0] : '\0';
2017-03-13 08:02:57 +05:30
sqlite3 *db;
int con = sqlite3_open("db/cts.db", &db);
if (con != SQLITE_OK) {
fprintf(stderr, "Can not open database: %s\n", sqlite3_errmsg(db));
return false;
}
sqlite3_stmt *s;
// prepare sql to s, reading until null character.
con = sqlite3_prepare_v2(db, sql, -1, &s, NULL);
if (con != SQLITE_OK) {
fprintf(stderr, "Could not prepare SQL statement: %s\n", sqlite3_errmsg(db));
return false;
}
// bind parameter with a string length that is the # of bytes to null char.
con = sqlite3_bind_text(s, 1, p, -1, NULL);
2017-03-13 08:33:51 +05:30
qresult(s, &qc);
sqlite3_finalize(s);
sqlite3_close(db);
return true;
}
// both allocates and frees memory used up by the string..
// containing the data from query files.
void getquery(const char *qs) {
2017-03-14 07:48:50 +05:30
char *qf = NULL;
char *fname = get_filename(qs);
if (fname != NULL) {
FILE *f = fopen(fname, "r");
2017-03-14 07:48:50 +05:30
if (f != NULL) {
// change from fseek/SEEK_END when sql queries are >2 GB in size.
fseek(f, 0, SEEK_END); // go to the end of file
unsigned int size = ftell(f);
qf = calloc(size, sizeof(char));
if (qf != NULL) {
fseek(f, 0, SEEK_SET); // go to the start of file
fread(qf, sizeof(char), size, f);
}
}
2017-03-13 08:02:57 +05:30
fclose(f);
}
2017-03-13 08:33:51 +05:30
executequery(qf, qs);
free(qf);
}
void html(void) {
const char *html_top = "Content-Type: text/html\n\n\
<html><head><meta content=\"text/html; charset=UTF-8\" />\
<link rel=\"stylesheet\" type=\"text/css\" href=\"page.css\">\
<title>/v/ - Xonotic</title>\
<p class='hidden'>:-) / nice one<br></p>";
const char *html_bot = "<br><br><p classname='footer'>Pages under construction.<br>\
Service may sporadically become unavailable.<br>\
In-game database is not directly synced with this web server.\
</p>\
</body></html>";
2017-03-13 08:33:51 +05:30
printf("%s", html_top);
2017-03-14 07:48:50 +05:30
// getquery(getenv("QUERY_STRING"));
// getquery("map=pornstar-kaine");
getquery(NULL);
2017-03-13 08:33:51 +05:30
printf("%s", html_bot);
}
int main(void) {
2017-03-14 07:48:50 +05:30
html();
2017-03-13 08:33:51 +05:30
// getquery("player=duHTyaSGpdTk7oebwPFoo899xPoTwP9bja4DUjCjTLo=");
2017-03-14 07:48:50 +05:30
return 0;
}