mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-30 15:41:49 +00:00
9ae7e2621d
Lots of changes. CSQC should be functional, but is still tied to debug builds. It WILL have some bugs still, hopefully I'll be able to clean them up better if people test it a bit. Precompiled headers are working properly now. Compile times are now much quicker in msvc. This takes most of the files this commit. Restructured how client commands work. They're buffered outside the network message, some multithreaded code is in. It needs a bit of testing before it's active. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@885 fc73d0e0-1445-4013-8a0c-d673dee63da5
407 lines
9.4 KiB
C
407 lines
9.4 KiB
C
#include "quakedef.h"
|
|
|
|
#ifdef WEBSERVER
|
|
|
|
#include "iweb.h"
|
|
|
|
#ifdef CLIENTONLY
|
|
IWEBFILE *IWebGenerateFile(char *name)
|
|
{
|
|
return NULL;
|
|
}
|
|
#else
|
|
|
|
char lastrecordedmvd[MAX_QPATH];
|
|
|
|
IWeb_FileGen_t *IWeb_GenerationBuffer;
|
|
int IWeb_GenerationBufferTotal;
|
|
|
|
void IWeb_MoreGeneratedResize(int newsize)
|
|
{
|
|
IWeb_FileGen_t *ob;
|
|
|
|
if (IWeb_GenerationBuffer && IWeb_GenerationBufferTotal >= newsize)
|
|
return; //already big enough
|
|
|
|
ob = IWeb_GenerationBuffer;
|
|
IWeb_GenerationBuffer = BZ_Malloc(sizeof(IWeb_GenerationBuffer) + newsize);
|
|
|
|
IWeb_GenerationBuffer->data = (char *)(IWeb_GenerationBuffer+1);
|
|
if (ob)
|
|
{
|
|
memcpy(IWeb_GenerationBuffer->data, ob->data, ob->len);
|
|
BZ_Free(ob);
|
|
}
|
|
|
|
IWeb_GenerationBufferTotal = newsize;
|
|
}
|
|
void IWeb_Generate(const char *buf)
|
|
{
|
|
long count = strlen(buf);
|
|
if (!IWeb_GenerationBuffer || IWeb_GenerationBuffer->len + count >= IWeb_GenerationBufferTotal)
|
|
IWeb_MoreGeneratedResize(IWeb_GenerationBufferTotal + count+(16*1024));
|
|
|
|
memcpy(&IWeb_GenerationBuffer->data[IWeb_GenerationBuffer->len], buf, count);
|
|
IWeb_GenerationBuffer->len+=count;
|
|
}
|
|
|
|
int Rank_Enumerate (unsigned int first, unsigned int last, void (*callback) (const rankinfo_t *ri)); //leader first.
|
|
|
|
void IWeb_ParseForm(char *info, int infolen, char *text)
|
|
{
|
|
char *eq, *and;
|
|
char *token, *out;
|
|
*info = '\0';
|
|
if (!text)
|
|
return;
|
|
while(*text)
|
|
{
|
|
eq = strchr(text, '=');
|
|
if (!eq)
|
|
break;
|
|
*eq = '\0';
|
|
and = strchr(eq+1, '&');
|
|
if (and)
|
|
*and = '\0';
|
|
|
|
for (out = token = eq+1; *token;)
|
|
{
|
|
if (*token == '+')
|
|
{
|
|
*out++ = ' ';
|
|
token++;
|
|
}
|
|
else if (*token == '%' && token[1] && token[2])
|
|
{
|
|
int c = 0;
|
|
|
|
if (token[1] >= '0' && token[1] <= '9')
|
|
{
|
|
c += token[1] - '0';
|
|
}
|
|
else if (token[1] >= 'a' && token[1] <= 'f')
|
|
{
|
|
c += token[1] - 'a'+10;
|
|
}
|
|
else if (token[1] >= 'A' && token[1] <= 'F')
|
|
{
|
|
c += token[1] - 'A'+10;
|
|
}
|
|
c*=16;
|
|
if (token[2] >= '0' && token[2] <= '9')
|
|
{
|
|
c += token[2] - '0';
|
|
}
|
|
else if (token[2] >= 'a' && token[2] <= 'f')
|
|
{
|
|
c += token[2] - 'a'+10;
|
|
}
|
|
else if (token[2] >= 'A' && token[2] <= 'F')
|
|
{
|
|
c += token[2] - 'A'+10;
|
|
}
|
|
|
|
*out++ = c;
|
|
token+=3;
|
|
}
|
|
else
|
|
*out++ = *token++;
|
|
}
|
|
*out = '\0';
|
|
|
|
Info_SetValueForKey(info, text, eq+1, infolen);
|
|
if (!and)
|
|
return;
|
|
text = and+1;
|
|
}
|
|
}
|
|
|
|
void IWeb_GenerateAdminFile(char *parms, char *content, int contentlength)
|
|
{
|
|
extern char outputbuf[]; //redirected buffer - always null termed.
|
|
char info[16384];
|
|
char *pwd;
|
|
char *cmd;
|
|
char *mark, *start;
|
|
|
|
extern cvar_t rcon_password;
|
|
IWeb_Generate("<HTML><HEAD><TITLE>FTEQWSV - admin</TITLE></HEAD><BODY>");
|
|
if (*rcon_password.string)
|
|
{
|
|
IWeb_ParseForm(info, sizeof(info), content);
|
|
pwd = Info_ValueForKey(info, "pwd");
|
|
cmd = Info_ValueForKey(info, "cmd");
|
|
|
|
IWeb_Generate("<FORM action=\"admin.html\" method=\"post\">");
|
|
IWeb_Generate("<CENTER>");
|
|
IWeb_Generate("<input name=pwd value=\"");
|
|
if (!strcmp(rcon_password.string, pwd))
|
|
IWeb_Generate(rcon_password.string);
|
|
IWeb_Generate("\">");
|
|
IWeb_Generate("<BR>");
|
|
IWeb_Generate("<input name=cmd maxsize=255 size=40 value=\"\">");
|
|
IWeb_Generate("<BR>");
|
|
IWeb_Generate("<input type=submit value=\"Submit\" name=btn>");
|
|
IWeb_Generate("</CENTER>");
|
|
IWeb_Generate("</FORM>");
|
|
|
|
if (!strcmp(rcon_password.string, pwd))
|
|
{
|
|
Con_Printf("Web based rcon: %s\n", cmd);
|
|
SV_BeginRedirect(RD_OBLIVION, host_client->language);
|
|
Cmd_ExecuteString(cmd, RESTRICT_RCON);
|
|
for (mark = start = outputbuf; *mark; mark++)
|
|
{
|
|
if (*mark == '\n')
|
|
{
|
|
*mark = '\0';
|
|
IWeb_Generate(start);
|
|
IWeb_Generate("<br>");
|
|
start = mark+1;
|
|
}
|
|
|
|
}
|
|
IWeb_Generate(start);
|
|
SV_EndRedirect();
|
|
}
|
|
else if (*pwd)
|
|
IWeb_Generate("Password is incorrect.");
|
|
}
|
|
else
|
|
IWeb_Generate("<H1>Remote administration is not enabled.<H2>");
|
|
IWeb_Generate("</BODY></HTML>");
|
|
}
|
|
|
|
|
|
void IWeb_GenerateRankingsFileCallback(const rankinfo_t *ri)
|
|
{
|
|
IWeb_Generate("<TR><TD ALIGN = \"center\">");
|
|
IWeb_Generate(ri->h.name);
|
|
IWeb_Generate("</TD><TD ALIGN = \"center\">");
|
|
IWeb_Generate(va("%i", ri->s.kills));
|
|
IWeb_Generate("</TD><TD ALIGN = \"center\">");
|
|
IWeb_Generate(va("%i", ri->s.deaths));
|
|
IWeb_Generate("</TD>");
|
|
IWeb_Generate("</TR>");
|
|
}
|
|
|
|
void IWeb_GenerateRankingsFile (char *parms, char *content, int contentlength)
|
|
{
|
|
IWeb_Generate("<HTML><HEAD></HEAD><BODY>");
|
|
|
|
if (Rank_OpenRankings())
|
|
{
|
|
IWeb_Generate("<TABLE CELLSPACING=\"1\" CELLPADDING=\"1\">");
|
|
IWeb_Generate("<CAPTION>Players</CAPTION>");
|
|
if (Rank_Enumerate(atoi(parms), atoi(parms)+20, IWeb_GenerateRankingsFileCallback) == 20)
|
|
{
|
|
IWeb_Generate("</TABLE>");
|
|
if (atoi(parms) >= 20)
|
|
IWeb_Generate(va("<A HREF=allplayers.html?%i>Less</A>", atoi(parms)-20));
|
|
else if (atoi(parms) > 0)
|
|
IWeb_Generate(va("<A HREF=allplayers.html?%i>Less</A>", 0));
|
|
IWeb_Generate(va("<A HREF=allplayers.html?%i>More</A>", atoi(parms)+20));
|
|
}
|
|
else
|
|
{
|
|
IWeb_Generate("</TABLE>");
|
|
if (atoi(parms) >= 20)
|
|
IWeb_Generate(va("<A HREF=allplayers.html?%i>Less</A>", atoi(parms)-20));
|
|
else if (atoi(parms) > 0)
|
|
IWeb_Generate(va("<A HREF=allplayers.html?%i>Less</A>", 0));
|
|
}
|
|
}
|
|
else
|
|
IWeb_Generate("<H1>Rankings are disabled. Sorry.<H2>");
|
|
IWeb_Generate("</BODY></HTML>");
|
|
}
|
|
|
|
void IWeb_GenerateIndexFile (char *parms, char *content, int contentlength)
|
|
{
|
|
extern cvar_t rcon_password;
|
|
char *s, *o;
|
|
char key[128], value[128];
|
|
int l;
|
|
qboolean added;
|
|
client_t *cl;
|
|
IWeb_Generate("<HTML><HEAD><TITLE>FTEQWSV</TITLE></HEAD><BODY>");
|
|
IWeb_Generate("<H1>");
|
|
IWeb_Generate(hostname.string);
|
|
IWeb_Generate("</H1>");
|
|
|
|
IWeb_Generate("<A HREF=\"http://fte.quakesrc.org\">Server website</A><P>");
|
|
|
|
if (Rank_OpenRankings())
|
|
IWeb_Generate("<A HREF=\"allplayers.html\">Click here to see ranked players.</A><P>");
|
|
if (*rcon_password.string)
|
|
IWeb_Generate("<A HREF=\"admin.html\">Admin.</A><P>");
|
|
|
|
s = svs.info;
|
|
|
|
IWeb_Generate("<TABLE CELLSPACING=\"1\" CELLPADDING=\"1\">");
|
|
IWeb_Generate("<CAPTION>Server Info</CAPTION>");
|
|
if (*s == '\\')
|
|
s++;
|
|
while (*s)
|
|
{
|
|
o = key;
|
|
while (*s && *s != '\\')
|
|
*o++ = *s++;
|
|
|
|
l = o - key;
|
|
// if (l < 20)
|
|
// {
|
|
// memset (o, ' ', 20-l);
|
|
// key[20] = 0;
|
|
// }
|
|
// else
|
|
*o = 0;
|
|
|
|
IWeb_Generate("<TR><TD ALIGN = \"center\">");
|
|
IWeb_Generate(key);
|
|
|
|
if (!*s)
|
|
{
|
|
IWeb_Generate("MISSING VALUE\n");
|
|
return;
|
|
}
|
|
|
|
o = value;
|
|
s++;
|
|
while (*s && *s != '\\')
|
|
*o++ = *s++;
|
|
*o = 0;
|
|
|
|
if (*s)
|
|
s++;
|
|
|
|
IWeb_Generate("</TD><TD ALIGN = \"center\">");
|
|
IWeb_Generate(value);
|
|
IWeb_Generate("</TD></TR>");
|
|
}
|
|
|
|
IWeb_Generate("</TABLE>");
|
|
|
|
IWeb_Generate("<P><TABLE CELLSPACING=\"1\" CELLPADDING=\"1\">");
|
|
IWeb_Generate("<CAPTION>Players</CAPTION>");
|
|
added = false;
|
|
for (l = 0, cl = svs.clients; l < sv.allocated_client_slots; l++, cl++)
|
|
{
|
|
if (cl->state <= cs_zombie)
|
|
continue;
|
|
|
|
IWeb_Generate("<TR><TD ALIGN = \"center\">");
|
|
IWeb_Generate(cl->name);
|
|
IWeb_Generate("</TD><TD ALIGN = \"center\">");
|
|
IWeb_Generate(va("%i", cl->old_frags));
|
|
IWeb_Generate("</TD></TR>");
|
|
added = true;
|
|
}
|
|
if (!added)
|
|
{
|
|
IWeb_Generate("<TR><TD ALIGN = \"center\">");
|
|
IWeb_Generate("No players on server");
|
|
IWeb_Generate("</TD><TD ALIGN = \"center\">");
|
|
}
|
|
|
|
IWeb_Generate("</TABLE>");
|
|
|
|
IWeb_Generate("</BODY></HTML>");
|
|
}
|
|
|
|
typedef struct {
|
|
char *name;
|
|
void (*GenerationFunction) (char *parms, char *content, int contentlength);
|
|
float lastgenerationtime;
|
|
float oldbysecs;
|
|
IWeb_FileGen_t *buffer;
|
|
int genid;
|
|
} IWebFile_t;
|
|
|
|
IWebFile_t IWebFiles[] = {
|
|
{"allplayers.html", IWeb_GenerateRankingsFile},
|
|
{"index.html", IWeb_GenerateIndexFile},
|
|
{"admin.html", IWeb_GenerateAdminFile}
|
|
};
|
|
|
|
IWEBFILE *IWebGenerateFile(char *name, char *content, int contentlength)
|
|
{
|
|
int fnum;
|
|
char *parms;
|
|
int len;
|
|
|
|
if (!sv.state)
|
|
return NULL;
|
|
|
|
if (*lastrecordedmvd && !strcmp(name, "lastdemo.mvd"))
|
|
if (strcmp(name, "lastdemo.mvd")) //no infinate loops please...
|
|
return IWebFOpenRead(lastrecordedmvd);
|
|
|
|
parms = strchr(name, '?');
|
|
if (!parms)
|
|
parms = name + strlen(name);
|
|
len = parms-name;
|
|
if (*parms)
|
|
parms++;
|
|
|
|
if (!*name)
|
|
return NULL;
|
|
|
|
|
|
for (fnum = 0; fnum < sizeof(IWebFiles) / sizeof(IWebFile_t); fnum++)
|
|
{
|
|
if (!Q_strncasecmp(name, IWebFiles[fnum].name, len+1))
|
|
{
|
|
IWEBFILE *ret;
|
|
if (IWebFiles[fnum].buffer)
|
|
{
|
|
if (IWebFiles[fnum].lastgenerationtime+10 < Sys_DoubleTime() || contentlength||*parms) //10 sec lifetime
|
|
{
|
|
IWebFiles[fnum].buffer->references--; //remove our reference and check free
|
|
if (IWebFiles[fnum].buffer->references<=0)
|
|
{
|
|
BZ_Free(IWebFiles[fnum].buffer);
|
|
IWebFiles[fnum].buffer = NULL;
|
|
}
|
|
}
|
|
}
|
|
if (!IWebFiles[fnum].buffer)
|
|
{
|
|
if (IWeb_GenerationBuffer!=NULL)
|
|
Sys_Error("Recursive file generation\n");
|
|
IWebFiles[fnum].GenerationFunction(parms, content, contentlength);
|
|
IWebFiles[fnum].buffer = IWeb_GenerationBuffer;
|
|
|
|
if (!contentlength)
|
|
{
|
|
IWeb_GenerationBuffer->references++; //so it can't be sent once and freed instantly.
|
|
IWebFiles[fnum].lastgenerationtime = Sys_DoubleTime();
|
|
}
|
|
else
|
|
IWebFiles[fnum].lastgenerationtime = -10;
|
|
}
|
|
|
|
ret = IWebMalloc(sizeof(IWEBFILE));
|
|
if (!ret)
|
|
{
|
|
BZ_Free(IWeb_GenerationBuffer);
|
|
return NULL;
|
|
}
|
|
ret->f = NULL;
|
|
ret->bufferdata = IWebFiles[fnum].buffer;
|
|
ret->length = ret->bufferdata->len;
|
|
ret->bufferdata->references++;
|
|
ret->pos = 0;
|
|
ret->start = 0;
|
|
ret->end = ret->start+ret->length;
|
|
|
|
IWeb_GenerationBuffer = NULL;
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
#endif
|