2006-09-17 01:27:32 +00:00
|
|
|
/*
|
|
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
|
|
|
|
See the included (GNU.txt) GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
This is the file responsible for handling incoming tcp connections.
|
|
|
|
This includes mvd recording.
|
|
|
|
Password checks and stuff are implemented here. This is server side stuff.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "qtv.h"
|
|
|
|
|
|
|
|
|
|
|
|
#undef IN
|
|
|
|
#define IN(x) buffer[(x)&(MAX_PROXY_BUFFER-1)]
|
|
|
|
|
|
|
|
void CheckMVDConsistancy(unsigned char *buffer, int pos, int size)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
int length;
|
|
|
|
int msec, type;
|
|
|
|
while(pos < size)
|
|
|
|
{
|
|
|
|
msec = IN(pos++);
|
|
|
|
type = IN(pos++);
|
|
|
|
if (type == dem_set)
|
|
|
|
{
|
|
|
|
pos+=8;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (type == dem_multiple)
|
|
|
|
pos+=4;
|
|
|
|
length = (IN(pos+0)<<0) + (IN(pos+1)<<8) + (IN(pos+2)<<16) + (IN(pos+3)<<24);
|
|
|
|
pos+=4;
|
|
|
|
if (length > 1450)
|
|
|
|
printf("too big (%i)\n", length);
|
|
|
|
pos += length;
|
|
|
|
}
|
|
|
|
if (pos != size)
|
|
|
|
printf("pos != size\n");
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv)
|
|
|
|
{
|
|
|
|
oproxy_t *prox;
|
|
|
|
|
|
|
|
sock = accept(sock, NULL, NULL);
|
|
|
|
if (sock == INVALID_SOCKET)
|
|
|
|
return;
|
|
|
|
|
2007-03-03 21:39:06 +00:00
|
|
|
if (cluster->maxproxies >= 0 && cluster->numproxies >= cluster->maxproxies)
|
2006-09-17 01:27:32 +00:00
|
|
|
{
|
|
|
|
const char buffer[] = {dem_all, 1, 'P','r','o','x','y',' ','i','s',' ','f','u','l','l','.'};
|
|
|
|
send(sock, buffer, strlen(buffer), 0);
|
|
|
|
closesocket(sock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
prox = malloc(sizeof(*prox));
|
|
|
|
if (!prox)
|
|
|
|
{//out of mem?
|
|
|
|
closesocket(sock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memset(prox, 0, sizeof(*prox));
|
|
|
|
prox->sock = sock;
|
|
|
|
prox->file = NULL;
|
|
|
|
|
|
|
|
cluster->numproxies++;
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
prox->defaultstream = defaultqtv;
|
|
|
|
|
|
|
|
prox->next = cluster->pendingproxies;
|
|
|
|
cluster->pendingproxies = prox;
|
|
|
|
#else
|
|
|
|
prox->next = qtv->pendingproxies;
|
|
|
|
qtv->pendingproxies = prox;
|
|
|
|
Net_SendConnectionMVD(qtv, prox);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Net_TryFlushProxyBuffer(cluster_t *cluster, oproxy_t *prox)
|
|
|
|
{
|
|
|
|
char *buffer;
|
|
|
|
int length;
|
|
|
|
int bufpos;
|
|
|
|
|
|
|
|
if (prox->drop)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (prox->bufferpos >= MAX_PROXY_BUFFER)
|
|
|
|
{ //so we never get any issues with wrapping..
|
|
|
|
prox->bufferpos -= MAX_PROXY_BUFFER;
|
|
|
|
prox->buffersize -= MAX_PROXY_BUFFER;
|
|
|
|
}
|
|
|
|
|
|
|
|
bufpos = prox->bufferpos&(MAX_PROXY_BUFFER-1);
|
|
|
|
length = prox->buffersize - prox->bufferpos;
|
|
|
|
if (length > MAX_PROXY_BUFFER-bufpos) //cap the length correctly.
|
|
|
|
length = MAX_PROXY_BUFFER-bufpos;
|
|
|
|
if (!length)
|
|
|
|
return; //already flushed.
|
|
|
|
buffer = prox->buffer + bufpos;
|
|
|
|
|
|
|
|
// CheckMVDConsistancy(prox->buffer, prox->bufferpos, prox->buffersize);
|
|
|
|
|
|
|
|
if (bufpos+length > MAX_PROXY_BUFFER)
|
|
|
|
Sys_Printf(cluster, "oversize flush\n");
|
|
|
|
|
|
|
|
if (prox->file)
|
|
|
|
length = fwrite(buffer, 1, length, prox->file);
|
|
|
|
else
|
|
|
|
length = send(prox->sock, buffer, length, 0);
|
|
|
|
|
|
|
|
|
|
|
|
switch (length)
|
|
|
|
{
|
|
|
|
case 0: //eof / they disconnected
|
|
|
|
prox->drop = true;
|
|
|
|
break;
|
|
|
|
case -1:
|
2006-10-22 17:34:19 +00:00
|
|
|
if (qerrno != EWOULDBLOCK && qerrno != EAGAIN) //not a problem, so long as we can flush it later.
|
|
|
|
{
|
|
|
|
Sys_Printf(cluster, "oversize flush\n");
|
2006-09-17 01:27:32 +00:00
|
|
|
prox->drop = true; //drop them if we get any errors
|
2006-10-22 17:34:19 +00:00
|
|
|
}
|
2006-09-17 01:27:32 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
prox->bufferpos += length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, char *buffer, int length)
|
|
|
|
{
|
|
|
|
int wrap;
|
|
|
|
|
|
|
|
if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER)
|
|
|
|
{
|
|
|
|
Net_TryFlushProxyBuffer(cluster, prox); //try flushing
|
|
|
|
if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER) //damn, still too big.
|
|
|
|
{ //they're too slow. hopefully it was just momentary lag
|
2006-09-19 01:48:12 +00:00
|
|
|
printf("QTV client is too lagged\n");
|
2006-09-17 01:27:32 +00:00
|
|
|
prox->flushing = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if 1
|
|
|
|
//just simple
|
|
|
|
prox->buffersize+=length;
|
|
|
|
for (wrap = prox->buffersize-length; wrap < prox->buffersize; wrap++)
|
|
|
|
prox->buffer[wrap&(MAX_PROXY_BUFFER-1)] = *buffer++;
|
|
|
|
#else
|
|
|
|
//we don't do multiple wrappings, the above check cannot succeed if it were required.
|
|
|
|
|
|
|
|
//find the wrap point
|
|
|
|
wrap = prox->buffersize-(prox->buffersize&(MAX_PROXY_BUFFER-1)) + MAX_PROXY_BUFFER;
|
|
|
|
wrap = wrap - (prox->buffersize&(MAX_PROXY_BUFFER-1)); //the ammount of data we can fit before wrapping.
|
|
|
|
|
|
|
|
if (wrap > length)
|
|
|
|
{ //we don't wrap afterall
|
|
|
|
memcpy(prox->buffer+(prox->buffersize)&(MAX_PROXY_BUFFER-1), buffer, length);
|
|
|
|
prox->buffersize+=length;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memcpy(prox->buffer+prox->buffersize&(MAX_PROXY_BUFFER-1), buffer, wrap);
|
|
|
|
buffer += wrap;
|
|
|
|
length -= wrap;
|
|
|
|
memcpy(prox->buffer, buffer, length);
|
|
|
|
|
|
|
|
prox->buffersize+=length;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void Prox_SendMessage(cluster_t *cluster, oproxy_t *prox, char *buf, int length, int dem_type, unsigned int playermask)
|
|
|
|
{
|
|
|
|
netmsg_t msg;
|
|
|
|
char tbuf[16];
|
|
|
|
InitNetMsg(&msg, tbuf, sizeof(tbuf));
|
|
|
|
WriteByte(&msg, 0);
|
|
|
|
WriteByte(&msg, dem_type);
|
|
|
|
WriteLong(&msg, length);
|
|
|
|
if (dem_type == dem_multiple)
|
|
|
|
WriteLong(&msg, playermask);
|
|
|
|
|
|
|
|
if (prox->buffersize-prox->bufferpos + length + msg.cursize > MAX_PROXY_BUFFER)
|
|
|
|
{
|
|
|
|
Net_TryFlushProxyBuffer(cluster, prox); //try flushing
|
|
|
|
if (prox->buffersize-prox->bufferpos + length + msg.cursize > MAX_PROXY_BUFFER) //damn, still too big.
|
|
|
|
{ //they're too slow. hopefully it was just momentary lag
|
|
|
|
prox->flushing = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Net_ProxySend(cluster, prox, msg.data, msg.cursize);
|
|
|
|
|
|
|
|
Net_ProxySend(cluster, prox, buf, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Prox_SendPlayerStats(sv_t *qtv, oproxy_t *prox)
|
|
|
|
{
|
|
|
|
char buffer[MAX_MSGLEN];
|
|
|
|
netmsg_t msg;
|
|
|
|
int player, snum;
|
|
|
|
|
|
|
|
InitNetMsg(&msg, buffer, sizeof(buffer));
|
|
|
|
|
|
|
|
for (player = 0; player < MAX_CLIENTS; player++)
|
|
|
|
{
|
|
|
|
for (snum = 0; snum < MAX_STATS; snum++)
|
|
|
|
{
|
|
|
|
if (qtv->players[player].stats[snum])
|
|
|
|
{
|
|
|
|
if ((unsigned)qtv->players[player].stats[snum] > 255)
|
|
|
|
{
|
|
|
|
WriteByte(&msg, svc_updatestatlong);
|
|
|
|
WriteByte(&msg, snum);
|
|
|
|
WriteLong(&msg, qtv->players[player].stats[snum]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
WriteByte(&msg, svc_updatestat);
|
|
|
|
WriteByte(&msg, snum);
|
|
|
|
WriteByte(&msg, qtv->players[player].stats[snum]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg.cursize)
|
|
|
|
{
|
|
|
|
// Prox_SendMessage(prox, msg.data, msg.cursize, dem_stats|(player<<3), (1<<player));
|
|
|
|
msg.cursize = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox)
|
|
|
|
{
|
|
|
|
char buffer[MAX_MSGLEN*8];
|
|
|
|
netmsg_t msg;
|
|
|
|
int prespawn;
|
|
|
|
|
2006-10-27 13:41:27 +00:00
|
|
|
if (!*qtv->mapname)
|
|
|
|
return;
|
|
|
|
|
2006-09-17 01:27:32 +00:00
|
|
|
InitNetMsg(&msg, buffer, sizeof(buffer));
|
|
|
|
|
|
|
|
prox->flushing = false;
|
|
|
|
|
2006-11-03 15:53:04 +00:00
|
|
|
BuildServerData(qtv, &msg, 0, NULL);
|
2006-09-17 01:27:32 +00:00
|
|
|
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
|
|
|
|
msg.cursize = 0;
|
|
|
|
|
|
|
|
for (prespawn = 0;prespawn >= 0;)
|
|
|
|
{
|
|
|
|
prespawn = SendList(qtv, prespawn, qtv->soundlist, svc_soundlist, &msg);
|
|
|
|
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
|
|
|
|
msg.cursize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (prespawn = 0;prespawn >= 0;)
|
|
|
|
{
|
|
|
|
prespawn = SendList(qtv, prespawn, qtv->modellist, svc_modellist, &msg);
|
|
|
|
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
|
|
|
|
msg.cursize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Net_TryFlushProxyBuffer(qtv->cluster, prox); //that should be enough data to fill a packet.
|
|
|
|
|
|
|
|
for(prespawn = 0;prespawn>=0;)
|
|
|
|
{
|
|
|
|
prespawn = Prespawn(qtv, 0, &msg, prespawn, MAX_CLIENTS-1);
|
|
|
|
|
|
|
|
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
|
|
|
|
msg.cursize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//playerstates arn't actually delta-compressed, so the first send (simply forwarded from server) entirly replaces the old.
|
|
|
|
|
|
|
|
//we do need to send entity states.
|
|
|
|
Prox_SendInitialEnts(qtv, prox, &msg);
|
|
|
|
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
|
|
|
|
msg.cursize = 0;
|
|
|
|
|
|
|
|
WriteByte(&msg, svc_stufftext);
|
|
|
|
WriteString(&msg, "skins\n");
|
|
|
|
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
|
|
|
|
msg.cursize = 0;
|
|
|
|
|
|
|
|
Net_TryFlushProxyBuffer(qtv->cluster, prox);
|
|
|
|
|
|
|
|
Prox_SendPlayerStats(qtv, prox);
|
|
|
|
Net_TryFlushProxyBuffer(qtv->cluster, prox);
|
|
|
|
|
2006-09-19 01:48:12 +00:00
|
|
|
Net_ProxySend(qtv->cluster, prox, qtv->buffer, qtv->forwardpoint); //send all the info we've not yet processed.
|
2006-09-17 01:27:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
if (prox->flushing)
|
|
|
|
{
|
|
|
|
Sys_Printf(qtv->cluster, "Connection data is too big, dropping proxy client\n");
|
|
|
|
prox->drop = true; //this is unfortunate...
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Net_TryFlushProxyBuffer(qtv->cluster, prox);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
oproxy_t *Net_FileProxy(sv_t *qtv, char *filename)
|
|
|
|
{
|
|
|
|
oproxy_t *prox;
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
f = fopen(filename, "wb");
|
|
|
|
if (!f)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
//no full proxy check, this is going to be used by proxy admins, who won't want to have to raise the limit to start recording.
|
|
|
|
|
|
|
|
prox = malloc(sizeof(*prox));
|
|
|
|
if (!prox)
|
|
|
|
return NULL;
|
|
|
|
memset(prox, 0, sizeof(*prox));
|
|
|
|
|
|
|
|
prox->sock = INVALID_SOCKET;
|
|
|
|
prox->file = f;
|
|
|
|
|
|
|
|
prox->next = qtv->proxies;
|
|
|
|
qtv->proxies = prox;
|
|
|
|
|
|
|
|
qtv->cluster->numproxies++;
|
|
|
|
|
|
|
|
Net_SendConnectionMVD(qtv, prox);
|
|
|
|
|
|
|
|
return prox;
|
|
|
|
}
|
|
|
|
|
|
|
|
qboolean Net_StopFileProxy(sv_t *qtv)
|
|
|
|
{
|
|
|
|
oproxy_t *prox;
|
|
|
|
for (prox = qtv->proxies; prox; prox = prox->next)
|
|
|
|
{
|
|
|
|
if (prox->file)
|
|
|
|
{
|
|
|
|
prox->drop = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SV_ForwardStream(sv_t *qtv, char *buffer, int length)
|
|
|
|
{ //forward the stream on to connected clients
|
|
|
|
oproxy_t *prox, *next, *fre;
|
|
|
|
|
|
|
|
CheckMVDConsistancy(buffer, 0, length);
|
|
|
|
|
|
|
|
|
|
|
|
while (qtv->proxies && qtv->proxies->drop)
|
|
|
|
{
|
|
|
|
next = qtv->proxies->next;
|
|
|
|
fre = qtv->proxies;
|
|
|
|
if (fre->file)
|
|
|
|
fclose(fre->file);
|
|
|
|
else
|
|
|
|
closesocket(fre->sock);
|
|
|
|
free(fre);
|
|
|
|
qtv->cluster->numproxies--;
|
|
|
|
qtv->proxies = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (prox = qtv->proxies; prox; prox = prox->next)
|
|
|
|
{
|
|
|
|
while (prox->next && prox->next->drop)
|
|
|
|
{
|
|
|
|
next = prox->next->next;
|
|
|
|
fre = prox->next;
|
|
|
|
if (fre->file)
|
|
|
|
fclose(fre->file);
|
|
|
|
else
|
|
|
|
closesocket(fre->sock);
|
|
|
|
free(fre);
|
|
|
|
qtv->cluster->numproxies--;
|
|
|
|
prox->next = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prox->flushing) //don't send it if we're trying to empty thier buffer.
|
|
|
|
{
|
|
|
|
if (prox->buffersize == prox->bufferpos)
|
|
|
|
{
|
|
|
|
if (!qtv->parsingconnectiondata)
|
|
|
|
Net_SendConnectionMVD(qtv, prox); //they're up to date, resend the connection info.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Net_TryFlushProxyBuffer(qtv->cluster, prox); //try and flush it.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prox->drop)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
//add the new data
|
|
|
|
Net_ProxySend(qtv->cluster, prox, buffer, length);
|
|
|
|
|
|
|
|
Net_TryFlushProxyBuffer(qtv->cluster, prox);
|
|
|
|
// Net_TryFlushProxyBuffer(qtv->cluster, prox);
|
|
|
|
// Net_TryFlushProxyBuffer(qtv->cluster, prox);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-03 21:39:06 +00:00
|
|
|
static const char qfont_table[256] = {
|
|
|
|
'\0', '#', '#', '#', '#', '.', '#', '#',
|
|
|
|
'#', 9, 10, '#', ' ', 13, '.', '.',
|
|
|
|
'[', ']', '0', '1', '2', '3', '4', '5',
|
|
|
|
'6', '7', '8', '9', '.', '<', '=', '>',
|
|
|
|
' ', '!', '"', '#', '$', '%', '&', '\'',
|
|
|
|
'(', ')', '*', '+', ',', '-', '.', '/',
|
|
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
|
|
'8', '9', ':', ';', '<', '=', '>', '?',
|
|
|
|
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
|
|
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
|
|
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
|
|
'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
|
|
|
|
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
|
|
|
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
|
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
|
|
|
'x', 'y', 'z', '{', '|', '}', '~', '<',
|
|
|
|
|
|
|
|
'<', '=', '>', '#', '#', '.', '#', '#',
|
|
|
|
'#', '#', ' ', '#', ' ', '>', '.', '.',
|
|
|
|
'[', ']', '0', '1', '2', '3', '4', '5',
|
|
|
|
'6', '7', '8', '9', '.', '<', '=', '>',
|
|
|
|
' ', '!', '"', '#', '$', '%', '&', '\'',
|
|
|
|
'(', ')', '*', '+', ',', '-', '.', '/',
|
|
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
|
|
'8', '9', ':', ';', '<', '=', '>', '?',
|
|
|
|
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
|
|
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
|
|
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
|
|
'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
|
|
|
|
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
|
|
|
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
|
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
|
|
|
'x', 'y', 'z', '{', '|', '}', '~', '<'
|
|
|
|
};
|
|
|
|
|
|
|
|
void HTMLprintf(char *outb, int outl, char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list val;
|
|
|
|
char qfmt[8192*4];
|
|
|
|
char *inb = qfmt;
|
|
|
|
|
|
|
|
va_start(val, fmt);
|
|
|
|
vsnprintf(qfmt, sizeof(qfmt), fmt, val);
|
|
|
|
va_end(val);
|
|
|
|
qfmt[sizeof(qfmt)-1] = 0;
|
|
|
|
|
|
|
|
outl--;
|
|
|
|
outl -= 5;
|
|
|
|
while (outl > 0 && *inb)
|
|
|
|
{
|
|
|
|
if (*inb == '<')
|
|
|
|
{
|
|
|
|
*outb++ = '&';
|
|
|
|
*outb++ = 'l';
|
|
|
|
*outb++ = 't';
|
|
|
|
*outb++ = ';';
|
|
|
|
outl -= 4;
|
|
|
|
}
|
|
|
|
else if (*inb == '>')
|
|
|
|
{
|
|
|
|
*outb++ = '&';
|
|
|
|
*outb++ = 'g';
|
|
|
|
*outb++ = 't';
|
|
|
|
*outb++ = ';';
|
|
|
|
outl -= 4;
|
|
|
|
}
|
|
|
|
else if (*inb == '\n')
|
|
|
|
{
|
|
|
|
*outb++ = '<';
|
|
|
|
*outb++ = 'b';
|
|
|
|
*outb++ = 'r';
|
|
|
|
*outb++ = '/';
|
|
|
|
*outb++ = '>';
|
|
|
|
outl -= 5;
|
|
|
|
}
|
|
|
|
else if (*inb == '&')
|
|
|
|
{
|
|
|
|
*outb++ = '&';
|
|
|
|
*outb++ = 'a';
|
|
|
|
*outb++ = 'm';
|
|
|
|
*outb++ = 'p';
|
|
|
|
*outb++ = ';';
|
|
|
|
outl -= 5;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*outb++ = qfont_table[*(unsigned char*)inb];
|
|
|
|
}
|
|
|
|
inb++;
|
|
|
|
}
|
|
|
|
*outb++ = 0;
|
|
|
|
}
|
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
static void SV_SendHTTPHeader(cluster_t *cluster, oproxy_t *dest, char *error_code, char *content_type, qboolean nocache)
|
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
char buffer[2048];
|
|
|
|
|
|
|
|
if (nocache) {
|
|
|
|
s = "HTTP/1.1 %s OK\n"
|
|
|
|
"Content-Type: %s\n"
|
|
|
|
"Cache-Control: no-cache, must-revalidate\n"
|
|
|
|
"Expires: Mon, 26 Jul 1997 05:00:00 GMT\n"
|
|
|
|
"Connection: close\n"
|
|
|
|
"\n";
|
|
|
|
} else {
|
|
|
|
s = "HTTP/1.1 %s OK\n"
|
|
|
|
"Content-Type: %s\n"
|
|
|
|
"Connection: close\n"
|
|
|
|
"\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(buffer, sizeof(buffer), s, error_code, content_type);
|
|
|
|
|
|
|
|
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SV_SendHTMLHeader(cluster_t *cluster, oproxy_t *dest, char *title)
|
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
char buffer[2048];
|
|
|
|
|
|
|
|
s = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
|
|
|
|
"<html>\n"
|
|
|
|
"<head>\n"
|
|
|
|
" <meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
|
|
|
|
" <title>%s</title>\n"
|
|
|
|
" <link rel=\"StyleSheet\" href=\"/style.css\" type=\"text/css\" />\n"
|
|
|
|
"</head>\n"
|
|
|
|
"<body><div id=\"navigation\"><ul>"
|
|
|
|
"<li><a href=\"/nowplaying/\">Live</a></li><li><a href=\"/demos/\">Demos</a></li><li><a href=\"/admin/\">Admin</a></li>"
|
|
|
|
"</ul></div>";
|
|
|
|
|
|
|
|
snprintf(buffer, sizeof(buffer), s, title);
|
|
|
|
|
|
|
|
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
|
|
|
|
}
|
|
|
|
|
|
|
|
#define HTMLPRINT(str) { sprintf(buffer, str "\n"); Net_ProxySend(cluster, dest, buffer, strlen(buffer)); }
|
|
|
|
|
2006-09-17 01:27:32 +00:00
|
|
|
void SV_GenerateNowPlayingHTTP(cluster_t *cluster, oproxy_t *dest)
|
|
|
|
{
|
|
|
|
int player;
|
|
|
|
char *s;
|
|
|
|
char buffer[1024];
|
|
|
|
char plname[64];
|
|
|
|
sv_t *streams;
|
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
SV_SendHTTPHeader(cluster, dest, "200", "text/html", true);
|
|
|
|
SV_SendHTMLHeader(cluster, dest, "QuakeTV: Now Playing");
|
2006-09-17 01:27:32 +00:00
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
snprintf(buffer, sizeof(buffer), "<h1>QuakeTV on %s: Now Playing</h1>", cluster->hostname);
|
2006-10-07 22:29:31 +00:00
|
|
|
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
|
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
HTMLPRINT("<dl class=\"nowplaying\">");
|
2006-09-17 01:27:32 +00:00
|
|
|
for (streams = cluster->servers; streams; streams = streams->next)
|
|
|
|
{
|
2007-03-15 13:38:05 +00:00
|
|
|
HTMLPRINT("<dt>");
|
|
|
|
HTMLprintf(buffer, sizeof(buffer), "%s (%s: %s)", streams->server, streams->gamedir, streams->mapname);
|
|
|
|
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
|
|
|
|
sprintf(buffer, "<span class=\"qtvfile\"> [ <a href=\"/watch.qtv?sid=%i\">Watch Now</a> ]</span>", streams->streamid);
|
2007-03-03 21:39:06 +00:00
|
|
|
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
|
2007-03-15 13:38:05 +00:00
|
|
|
HTMLPRINT("</dt><dd><ul class=\"playerslist\">");
|
2006-09-17 01:27:32 +00:00
|
|
|
|
|
|
|
for (player = 0; player < MAX_CLIENTS; player++)
|
|
|
|
{
|
|
|
|
if (*streams->players[player].userinfo)
|
|
|
|
{
|
|
|
|
Info_ValueForKey(streams->players[player].userinfo, "name", plname, sizeof(plname));
|
2007-03-03 21:39:06 +00:00
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
if (streams->players[player].frags < -90) {
|
|
|
|
HTMLPRINT("<li class=\"spectator\">");
|
|
|
|
} else {
|
|
|
|
HTMLPRINT("<li class=\"player\">");
|
|
|
|
}
|
|
|
|
|
2007-03-03 21:39:06 +00:00
|
|
|
HTMLprintf(buffer, sizeof(buffer), "%s", plname);
|
2006-09-17 01:27:32 +00:00
|
|
|
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
|
2007-03-15 13:38:05 +00:00
|
|
|
HTMLPRINT("</li>");
|
2006-09-17 01:27:32 +00:00
|
|
|
}
|
|
|
|
}
|
2007-03-15 13:38:05 +00:00
|
|
|
HTMLPRINT("</ul></dd>");
|
2006-09-17 01:27:32 +00:00
|
|
|
}
|
2007-03-15 13:38:05 +00:00
|
|
|
HTMLPRINT("</dl>");
|
2006-10-07 22:29:31 +00:00
|
|
|
if (!cluster->servers)
|
|
|
|
{
|
2006-10-11 23:17:55 +00:00
|
|
|
s = "No streams are currently being played<br />";
|
2006-10-07 22:29:31 +00:00
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
|
|
|
}
|
|
|
|
|
2007-03-03 21:39:06 +00:00
|
|
|
sprintf(buffer, "<br/>QTV Version: %i <a href=\"http://www.fteqw.com\">www.fteqw.com</a><br />", cluster->buildnumber);
|
|
|
|
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
|
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
HTMLPRINT("</body></html>");
|
|
|
|
}
|
|
|
|
|
|
|
|
void SV_GenerateCSSFile(cluster_t *cluster, oproxy_t *dest)
|
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
|
|
|
|
SV_SendHTTPHeader(cluster, dest, "200", "text/css", false);
|
|
|
|
|
|
|
|
HTMLPRINT("* { font-family: Verdana, Helvetica, sans-serif; }");
|
|
|
|
HTMLPRINT("body { color: #000; background-color: #fff; padding: 0 40px; }");
|
|
|
|
HTMLPRINT("a { color: #00f; }");
|
|
|
|
HTMLPRINT("a.qtvfile { font-weight: bold; }");
|
|
|
|
HTMLPRINT("a:visited { color: #00f; }");
|
|
|
|
HTMLPRINT("a:hover { background-color: black; color: yellow; }");
|
|
|
|
HTMLPRINT("li.spectator { color: #666; font-size: 0.9ex; }");
|
|
|
|
HTMLPRINT("dl.nowplaying dd { margin: 0 0 2em 0; }");
|
|
|
|
HTMLPRINT("dl.nowplaying dt { margin: 1em 0 0 0; font-size: 1.1em; font-weight: bold; }");
|
|
|
|
HTMLPRINT("dl.nowplaying li { list-style: none; margin: 0 0 0 1em; padding: 0; }");
|
|
|
|
HTMLPRINT("dl.nowplaying ul { margin: 0 0 0 1em; padding: 0; }");
|
|
|
|
HTMLPRINT("#navigation { background-color: #eef; }");
|
|
|
|
HTMLPRINT("#navigation li { display: inline; list-style: none; margin: 0 3em; }");
|
2006-09-17 01:27:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
qboolean SV_GetHTTPHeaderField(char *s, char *field, char *buffer, int buffersize)
|
|
|
|
{
|
|
|
|
char *e;
|
|
|
|
char *colon;
|
|
|
|
int fieldnamelen = strlen(field);
|
|
|
|
|
|
|
|
buffer[0] = 0;
|
|
|
|
|
|
|
|
e = s;
|
|
|
|
while(*e)
|
|
|
|
{
|
|
|
|
if (*e == '\n')
|
|
|
|
{
|
|
|
|
*e = '\0';
|
|
|
|
colon = strchr(s, ':');
|
|
|
|
if (!colon)
|
|
|
|
{
|
|
|
|
if (!strncmp(field, s, fieldnamelen))
|
|
|
|
{
|
|
|
|
if (s[fieldnamelen] <= ' ')
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (fieldnamelen == colon - s)
|
|
|
|
{
|
|
|
|
if (!strncmp(field, s, colon-s))
|
|
|
|
{
|
|
|
|
colon++;
|
|
|
|
while (*colon == ' ')
|
|
|
|
colon++;
|
|
|
|
while (buffersize > 1)
|
|
|
|
{
|
|
|
|
if (*colon == '\r' || *colon == '\n')
|
|
|
|
break;
|
|
|
|
*buffer++ = *colon++;
|
|
|
|
}
|
|
|
|
*buffer = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
s = e+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
e++;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2006-10-11 23:17:55 +00:00
|
|
|
void SV_GenerateQTVStub(cluster_t *cluster, oproxy_t *dest, char *streamtype, char *streamid)
|
2006-09-17 01:27:32 +00:00
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
char hostname[64];
|
|
|
|
char buffer[1024];
|
|
|
|
|
|
|
|
if (!SV_GetHTTPHeaderField(dest->inbuffer, "Host", hostname, sizeof(hostname)))
|
|
|
|
{
|
2007-03-15 13:38:05 +00:00
|
|
|
SV_SendHTTPHeader(cluster, dest, "400", "text/html", true);
|
|
|
|
SV_SendHTMLHeader(cluster, dest, "QuakeTV: Error");
|
2006-09-17 01:27:32 +00:00
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
s = "Your client did not send a Host field, which is required in HTTP/1.1\n<BR />"
|
2006-10-07 22:29:31 +00:00
|
|
|
"Please try a different browser.\n"
|
2006-09-17 01:27:32 +00:00
|
|
|
"</BODY>"
|
|
|
|
"</HTML>";
|
|
|
|
|
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-03-16 00:59:45 +00:00
|
|
|
SV_SendHTTPHeader(cluster, dest, "200", "text/x-quaketvident", false);
|
2006-09-17 01:27:32 +00:00
|
|
|
|
2006-10-11 23:17:55 +00:00
|
|
|
{
|
|
|
|
char *ws;
|
|
|
|
for (ws = streamid; *ws > ' '; ws++)
|
|
|
|
;
|
|
|
|
*ws = '\0';
|
|
|
|
}
|
|
|
|
|
2006-09-17 01:27:32 +00:00
|
|
|
|
|
|
|
sprintf(buffer, "[QTV]\r\n"
|
2006-10-11 23:17:55 +00:00
|
|
|
"Stream: %s%s@%s\r\n"
|
2006-09-17 01:27:32 +00:00
|
|
|
"",
|
2006-10-11 23:17:55 +00:00
|
|
|
streamtype, streamid, hostname);
|
2006-09-17 01:27:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
|
|
|
|
}
|
|
|
|
|
2006-10-07 22:29:31 +00:00
|
|
|
char *SV_ParsePOST(char *post, char *buffer, int buffersize)
|
|
|
|
{
|
|
|
|
while(*post && *post != '&')
|
|
|
|
{
|
|
|
|
if (--buffersize>0)
|
|
|
|
{
|
|
|
|
if (*post == '+')
|
|
|
|
*buffer++ = ' ';
|
|
|
|
else if (*post == '%')
|
|
|
|
{
|
|
|
|
*buffer = 0;
|
|
|
|
post++;
|
|
|
|
if (*post == '\0' || *post == '&')
|
|
|
|
break;
|
|
|
|
else if (*post >= 'a' && *post <= 'f')
|
|
|
|
*buffer += 10 + *post-'a';
|
|
|
|
else if (*post >= 'A' && *post <= 'F')
|
|
|
|
*buffer += 10 + *post-'A';
|
|
|
|
else if (*post >= '0' && *post <= '9')
|
|
|
|
*buffer += *post-'0';
|
|
|
|
|
|
|
|
*buffer <<= 4;
|
|
|
|
|
|
|
|
post++;
|
|
|
|
if (*post == '\0' || *post == '&')
|
|
|
|
break;
|
|
|
|
else if (*post >= 'a' && *post <= 'f')
|
|
|
|
*buffer += 10 + *post-'a';
|
|
|
|
else if (*post >= 'A' && *post <= 'F')
|
|
|
|
*buffer += 10 + *post-'A';
|
|
|
|
else if (*post >= '0' && *post <= '9')
|
|
|
|
*buffer += *post-'0';
|
|
|
|
|
|
|
|
buffer++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*buffer++ = *post;
|
|
|
|
}
|
|
|
|
post++;
|
|
|
|
}
|
|
|
|
*buffer = 0;
|
|
|
|
|
|
|
|
return post;
|
|
|
|
}
|
|
|
|
void SV_GenerateAdminHTTP(cluster_t *cluster, oproxy_t *dest, int streamid, char *postbody)
|
|
|
|
{
|
|
|
|
char pwd[64];
|
|
|
|
char cmd[256];
|
|
|
|
char result[8192];
|
|
|
|
char *s;
|
|
|
|
char *o;
|
|
|
|
|
|
|
|
if (!*cluster->adminpassword)
|
|
|
|
{
|
2007-03-15 13:38:05 +00:00
|
|
|
SV_SendHTTPHeader(cluster, dest, "403", "text/html", true);
|
|
|
|
SV_SendHTMLHeader(cluster, dest, "QuakeTV: Admin Error");
|
|
|
|
|
|
|
|
s = "The admin password is disabled. You may not log in remotely.</body></html>\n";
|
2006-10-07 22:29:31 +00:00
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pwd[0] = 0;
|
|
|
|
cmd[0] = 0;
|
|
|
|
if (postbody)
|
|
|
|
while (*postbody)
|
|
|
|
{
|
|
|
|
if (!strncmp(postbody, "pwd=", 4))
|
|
|
|
{
|
|
|
|
postbody = SV_ParsePOST(postbody+4, pwd, sizeof(pwd));
|
|
|
|
}
|
|
|
|
else if (!strncmp(postbody, "cmd=", 4))
|
|
|
|
{
|
|
|
|
postbody = SV_ParsePOST(postbody+4, cmd, sizeof(cmd));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while(*postbody && *postbody != '&')
|
|
|
|
{
|
|
|
|
postbody++;
|
|
|
|
}
|
|
|
|
if (*postbody == '&')
|
|
|
|
postbody++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!*pwd)
|
|
|
|
o = "";
|
|
|
|
else if (!strcmp(pwd, cluster->adminpassword))
|
|
|
|
{
|
2006-10-27 10:58:13 +00:00
|
|
|
//small hack (as http connections are considered non-connected proxies)
|
|
|
|
cluster->numproxies--;
|
2006-10-07 22:29:31 +00:00
|
|
|
o = Rcon_Command(cluster, NULL, cmd, result, sizeof(result), false);
|
2006-10-27 10:58:13 +00:00
|
|
|
cluster->numproxies++;
|
2006-10-07 22:29:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
o = "Bad Password";
|
|
|
|
}
|
|
|
|
if (o != result)
|
|
|
|
{
|
|
|
|
strcpy(result, o);
|
|
|
|
o = result;
|
|
|
|
}
|
|
|
|
|
|
|
|
s = "HTTP/1.1 200 OK\n"
|
|
|
|
"Content-Type: text/html\n"
|
|
|
|
"Connection: close\n"
|
|
|
|
"\n"
|
|
|
|
|
|
|
|
"<HTML>"
|
|
|
|
"<HEAD>"
|
|
|
|
"<TITLE>QuakeTV: Admin</TITLE>\n"
|
2006-10-27 10:58:13 +00:00
|
|
|
|
|
|
|
//this section of code is to put focus into the command box, so you don't need to click it all the time.
|
2006-10-07 22:29:31 +00:00
|
|
|
"<script type=\"text/javascript\">\n"
|
|
|
|
//"<!--"
|
|
|
|
"function sf(){document.f.cmd.focus();}\n"
|
|
|
|
//"// -->"
|
|
|
|
"</script>\n"
|
|
|
|
"</HEAD>\n"
|
|
|
|
"<BODY onload=sf()>";
|
|
|
|
|
2006-10-11 23:17:55 +00:00
|
|
|
|
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
|
|
|
s = "<H1>FTEQTV Admin: ";
|
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
|
|
|
s = cluster->hostname;
|
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
|
|
|
s = "</H1>";
|
2006-10-07 22:29:31 +00:00
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
|
|
|
|
|
|
|
s =
|
|
|
|
"<FORM action=\"admin.html\" method=\"post\" name=f>"
|
|
|
|
"<CENTER>"
|
|
|
|
"Password <input name=pwd value=\"";
|
|
|
|
|
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
|
|
|
if (*o)
|
|
|
|
Net_ProxySend(cluster, dest, pwd, strlen(pwd));
|
|
|
|
|
|
|
|
|
|
|
|
s = "\">"
|
|
|
|
"<BR />"
|
|
|
|
"Command <input name=cmd maxsize=255 size=40 value=\"\">"
|
|
|
|
"<input type=submit value=\"Submit\" name=btn>"
|
|
|
|
"</CENTER>"
|
|
|
|
"</FORM>";
|
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
|
|
|
|
|
|
|
while(*o)
|
|
|
|
{
|
|
|
|
s = strchr(o, '\n');
|
|
|
|
if (s)
|
|
|
|
*s = 0;
|
2007-03-03 21:39:06 +00:00
|
|
|
HTMLprintf(cmd, sizeof(cmd), "%s", o);
|
|
|
|
Net_ProxySend(cluster, dest, cmd, strlen(cmd));
|
2006-10-07 22:29:31 +00:00
|
|
|
Net_ProxySend(cluster, dest, "<BR />", 6);
|
|
|
|
if (!s)
|
|
|
|
break;
|
|
|
|
o = s+1;
|
|
|
|
}
|
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
s = "<br /><A href=\"/nowplaying/\">Now Playing</A><br />";
|
2007-03-03 21:39:06 +00:00
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
2007-03-15 13:38:05 +00:00
|
|
|
s = "<A href=\"/demos/\">Available Demos</A><br />";
|
2007-03-03 21:39:06 +00:00
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
|
|
|
|
|
|
|
sprintf(result, "<br/>QTV Version: %i <a href=\"http://www.fteqw.com\">www.fteqw.com</a><br />", cluster->buildnumber);
|
|
|
|
Net_ProxySend(cluster, dest, result, strlen(result));
|
|
|
|
|
2006-10-07 22:29:31 +00:00
|
|
|
s = "</BODY>"
|
|
|
|
"</HTML>";
|
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
|
|
|
}
|
|
|
|
|
2006-10-27 10:58:13 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
#include <dirent.h>
|
|
|
|
#endif
|
|
|
|
|
2006-10-11 23:17:55 +00:00
|
|
|
void SV_GenerateQTVDemoListing(cluster_t *cluster, oproxy_t *dest)
|
|
|
|
{
|
2007-03-03 21:39:06 +00:00
|
|
|
int i;
|
2006-10-11 23:17:55 +00:00
|
|
|
char link[256];
|
|
|
|
char *s;
|
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
SV_SendHTTPHeader(cluster, dest, "200", "text/html", true);
|
|
|
|
SV_SendHTMLHeader(cluster, dest, "QuakeTV: Demos");
|
2006-10-11 23:17:55 +00:00
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
s = "<h1>QuakeTV: Demo Listing</h1>";
|
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
2006-10-27 10:58:13 +00:00
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
Cluster_BuildAvailableDemoList(cluster);
|
|
|
|
for (i = 0; i < cluster->availdemoscount; i++)
|
|
|
|
{
|
|
|
|
snprintf(link, sizeof(link), "<A HREF=\"/watch.qtv?demo=%s\">%s</A> (%ikb)<br/>", cluster->availdemos[i].name, cluster->availdemos[i].name, cluster->availdemos[i].size/1024);
|
2007-03-03 21:39:06 +00:00
|
|
|
Net_ProxySend(cluster, dest, link, strlen(link));
|
2007-03-15 13:38:05 +00:00
|
|
|
}
|
2007-03-03 21:39:06 +00:00
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
sprintf(link, "<P>Total: %i demos</P>", cluster->availdemoscount);
|
|
|
|
Net_ProxySend(cluster, dest, link, strlen(link));
|
2006-10-11 23:17:55 +00:00
|
|
|
|
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
sprintf(link, "<br/>QTV Version: %i <a href=\"http://www.fteqw.com\">www.fteqw.com</a><br />", cluster->buildnumber);
|
|
|
|
Net_ProxySend(cluster, dest, link, strlen(link));
|
2007-03-03 21:39:06 +00:00
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
|
|
|
|
s = "</BODY>"
|
|
|
|
"</HTML>";
|
|
|
|
Net_ProxySend(cluster, dest, s, strlen(s));
|
2006-10-11 23:17:55 +00:00
|
|
|
}
|
2006-10-07 22:29:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2006-09-17 01:27:32 +00:00
|
|
|
//returns true if the pending proxy should be unlinked
|
|
|
|
//truth does not imply that it should be freed/released, just unlinked.
|
|
|
|
qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
|
|
|
{
|
2006-09-19 01:48:12 +00:00
|
|
|
char tempbuf[512];
|
2006-09-17 01:27:32 +00:00
|
|
|
char *s;
|
|
|
|
char *e;
|
|
|
|
char *colon;
|
|
|
|
int usableversion = 0;
|
|
|
|
int len;
|
|
|
|
qboolean raw;
|
|
|
|
sv_t *qtv;
|
|
|
|
|
|
|
|
if (pend->drop)
|
|
|
|
{
|
|
|
|
closesocket(pend->sock);
|
|
|
|
free(pend);
|
2006-10-07 22:29:31 +00:00
|
|
|
cluster->numproxies--;
|
2006-09-17 01:27:32 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Net_TryFlushProxyBuffer(cluster, pend);
|
|
|
|
|
|
|
|
if (pend->flushing)
|
|
|
|
{
|
|
|
|
if (pend->bufferpos == pend->buffersize)
|
|
|
|
pend->drop = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = sizeof(pend->inbuffer) - pend->inbuffersize - 1;
|
|
|
|
len = recv(pend->sock, pend->inbuffer+pend->inbuffersize, len, 0);
|
|
|
|
if (len == 0)
|
|
|
|
{
|
|
|
|
pend->drop = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (len < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
pend->inbuffersize += len;
|
|
|
|
pend->inbuffer[pend->inbuffersize] = '\0';
|
|
|
|
|
|
|
|
if (pend->inbuffersize >= 4)
|
|
|
|
{
|
2007-01-06 09:40:57 +00:00
|
|
|
if (strncmp(pend->inbuffer, "QTV\r", 4) && strncmp(pend->inbuffer, "QTV\n", 4) && strncmp(pend->inbuffer, "GET ", 4) && strncmp(pend->inbuffer, "POST ", 5))
|
2006-09-17 01:27:32 +00:00
|
|
|
{ //I have no idea what the smeg you are.
|
|
|
|
pend->drop = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//make sure there's a double \n somewhere
|
|
|
|
|
|
|
|
for (s = pend->inbuffer; *s; s++)
|
|
|
|
{
|
|
|
|
if (s[0] == '\n' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n')))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!*s)
|
|
|
|
return false; //don't have enough yet
|
2006-10-07 22:29:31 +00:00
|
|
|
s+=3;
|
|
|
|
|
|
|
|
if (!strncmp(pend->inbuffer, "POST ", 5))
|
|
|
|
{
|
|
|
|
if (!SV_GetHTTPHeaderField(pend->inbuffer, "Content-Length", tempbuf, sizeof(tempbuf)))
|
|
|
|
{
|
|
|
|
s = "HTTP/1.1 411 OK\n"
|
|
|
|
"Content-Type: text/html\n"
|
|
|
|
"Connection: close\n"
|
|
|
|
"\n"
|
2007-03-15 13:38:05 +00:00
|
|
|
"<html><HEAD><TITLE>QuakeTV</TITLE></HEAD><BODY>No Content-Length was provided.</BODY>\n";
|
2006-10-07 22:29:31 +00:00
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
pend->flushing = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
len = atoi(tempbuf);
|
|
|
|
if (pend->inbuffersize + len >= sizeof(pend->inbuffer)-20)
|
|
|
|
{ //too much data
|
|
|
|
pend->flushing = true;
|
|
|
|
return false;
|
|
|
|
}
|
2006-10-07 22:37:40 +00:00
|
|
|
len = s - (char*)pend->inbuffer + len;
|
2006-10-07 22:29:31 +00:00
|
|
|
if (len > pend->inbuffersize)
|
|
|
|
return false; //still need the body
|
2006-09-17 01:27:32 +00:00
|
|
|
|
2006-10-27 10:58:13 +00:00
|
|
|
// if (len <= pend->inbuffersize)
|
2006-10-07 22:29:31 +00:00
|
|
|
{
|
|
|
|
if (!strncmp(pend->inbuffer+5, "/admin", 6))
|
|
|
|
{
|
|
|
|
SV_GenerateAdminHTTP(cluster, pend, 0, s);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s = "HTTP/1.1 404 OK\n"
|
|
|
|
"Content-Type: text/html\n"
|
|
|
|
"Connection: close\n"
|
|
|
|
"\n"
|
2007-03-15 13:38:05 +00:00
|
|
|
"<html><HEAD><TITLE>QuakeTV</TITLE></HEAD><BODY>That HTTP method is not supported for that URL.</BODY></html>\n";
|
2006-10-07 22:29:31 +00:00
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
|
|
|
|
}
|
|
|
|
pend->flushing = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!strncmp(pend->inbuffer, "GET ", 4))
|
2006-09-17 01:27:32 +00:00
|
|
|
{
|
|
|
|
if (!strncmp(pend->inbuffer+4, "/nowplaying", 11))
|
|
|
|
{
|
|
|
|
SV_GenerateNowPlayingHTTP(cluster, pend);
|
|
|
|
}
|
|
|
|
else if (!strncmp(pend->inbuffer+4, "/watch.qtv?sid=", 15))
|
|
|
|
{
|
2006-10-11 23:17:55 +00:00
|
|
|
SV_GenerateQTVStub(cluster, pend, "", pend->inbuffer+19);
|
|
|
|
}
|
|
|
|
else if (!strncmp(pend->inbuffer+4, "/watch.qtv?demo=", 16))
|
|
|
|
{
|
|
|
|
SV_GenerateQTVStub(cluster, pend, "file:", pend->inbuffer+20);
|
2006-09-17 01:27:32 +00:00
|
|
|
}
|
|
|
|
else if (!strncmp(pend->inbuffer+4, "/about", 6))
|
2006-10-07 22:29:31 +00:00
|
|
|
{ //redirect them to our funky website
|
2006-09-17 01:27:32 +00:00
|
|
|
s = "HTTP/1.0 302 Found\n"
|
|
|
|
"Location: http://www.fteqw.com/\n"
|
|
|
|
"\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
}
|
2006-10-07 22:29:31 +00:00
|
|
|
else if (!strncmp(pend->inbuffer+4, "/admin", 6))
|
|
|
|
{
|
|
|
|
SV_GenerateAdminHTTP(cluster, pend, 0, NULL);
|
|
|
|
}
|
2006-09-17 01:27:32 +00:00
|
|
|
else if (!strncmp(pend->inbuffer+4, "/ ", 2))
|
|
|
|
{
|
|
|
|
s = "HTTP/1.0 302 Found\n"
|
2007-03-15 13:38:05 +00:00
|
|
|
"Location: /nowplaying/\n"
|
2006-09-17 01:27:32 +00:00
|
|
|
"\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
}
|
|
|
|
else if (!strncmp(pend->inbuffer+4, "/demos", 6))
|
|
|
|
{
|
2006-10-11 23:17:55 +00:00
|
|
|
SV_GenerateQTVDemoListing(cluster, pend);
|
|
|
|
/*
|
|
|
|
s = "HTTP/1.1 200 OK\n"
|
|
|
|
"Content-Type: text/html\n"
|
|
|
|
"Connection: close\n"
|
|
|
|
"\n"
|
|
|
|
"<HTML><HEAD><TITLE>FTEQTV</TITLE></HEAD><BODY>Not Yet Supported</BODY></HTML>";
|
2006-09-17 01:27:32 +00:00
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
2006-10-11 23:17:55 +00:00
|
|
|
*/
|
2006-09-17 01:27:32 +00:00
|
|
|
}
|
|
|
|
/* else
|
|
|
|
{
|
|
|
|
s = "HTTP/0.9 200 OK\n"
|
|
|
|
"Content-Type: text/plain\n"
|
|
|
|
"Content-Length: 12\n"
|
|
|
|
"\n"
|
|
|
|
"Hello World\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
}*/
|
2007-03-15 13:38:05 +00:00
|
|
|
else if (!strncmp(pend->inbuffer+4, "/style.css", 10))
|
|
|
|
{
|
|
|
|
SV_GenerateCSSFile(cluster, pend);
|
|
|
|
}
|
2006-09-17 01:27:32 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
s = "HTTP/1.1 404 OK\n"
|
|
|
|
"Content-Type: text/html\n"
|
|
|
|
"Connection: close\n"
|
|
|
|
"\n"
|
|
|
|
"<HEAD><TITLE>QuakeTV</TITLE></HEAD><BODY>The url you have specified was not recognised.</BODY>\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
}
|
|
|
|
pend->flushing = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
raw = false;
|
|
|
|
|
|
|
|
qtv = pend->defaultstream;
|
|
|
|
|
|
|
|
e = pend->inbuffer;
|
|
|
|
s = e;
|
|
|
|
while(*e)
|
|
|
|
{
|
2007-01-06 09:40:57 +00:00
|
|
|
if (*e == '\n' || *e == '\r')
|
2006-09-17 01:27:32 +00:00
|
|
|
{
|
|
|
|
*e = '\0';
|
|
|
|
colon = strchr(s, ':');
|
2006-09-19 01:48:12 +00:00
|
|
|
if (*s)
|
2006-09-17 01:27:32 +00:00
|
|
|
{
|
2006-10-07 22:37:40 +00:00
|
|
|
if (!colon)
|
2006-10-07 22:29:31 +00:00
|
|
|
{
|
2006-10-07 22:37:40 +00:00
|
|
|
if (!strcmp(s, "QTV"))
|
2006-09-17 01:27:32 +00:00
|
|
|
{
|
2006-10-07 22:37:40 +00:00
|
|
|
//just a qtv request
|
2006-09-17 01:27:32 +00:00
|
|
|
}
|
2006-10-07 22:37:40 +00:00
|
|
|
else if (!strcmp(s, "SOURCELIST"))
|
|
|
|
{ //lists sources that are currently playing
|
|
|
|
s = "QTVSV 1\n";
|
2006-09-19 01:48:12 +00:00
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
2006-10-07 22:37:40 +00:00
|
|
|
if (!cluster->servers)
|
|
|
|
{
|
|
|
|
s = "PERROR: No sources currently available\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (qtv = cluster->servers; qtv; qtv = qtv->next)
|
|
|
|
{
|
|
|
|
sprintf(tempbuf, "ASOURCE: %i: %15s: %15s\n", qtv->streamid, qtv->server, qtv->hostname);
|
|
|
|
s = tempbuf;
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
}
|
|
|
|
qtv = NULL;
|
2006-09-17 01:27:32 +00:00
|
|
|
}
|
2006-10-07 22:37:40 +00:00
|
|
|
s = "\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
pend->flushing = true;
|
2006-09-17 01:27:32 +00:00
|
|
|
}
|
2007-01-09 05:24:03 +00:00
|
|
|
else if (!strcmp(s, "REVERSE"))
|
|
|
|
{ //this is actually a server trying to connect to us
|
|
|
|
//start up a new stream
|
|
|
|
}
|
|
|
|
else if (!strcmp(s, "RECEIVE"))
|
|
|
|
{ //a client connection request without a source
|
|
|
|
if (cluster->numservers == 1)
|
|
|
|
{ //only one stream anyway
|
|
|
|
qtv = cluster->servers;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ //try and hunt down an explicit stream (rather than a user-recorded one)
|
|
|
|
int numfound = 0;
|
|
|
|
sv_t *suitable;
|
|
|
|
for (qtv = cluster->servers; qtv; qtv = qtv->next)
|
|
|
|
{
|
|
|
|
if (!qtv->disconnectwhennooneiswatching)
|
|
|
|
{
|
|
|
|
suitable = qtv;
|
|
|
|
numfound++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (numfound == 1)
|
|
|
|
qtv = suitable;
|
|
|
|
}
|
|
|
|
if (!qtv)
|
|
|
|
{
|
|
|
|
s = "QTVSV 1\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
s = "PERROR: Multiple streams are currently playing\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
s = "\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
pend->flushing = true;
|
|
|
|
}
|
|
|
|
}
|
2006-10-07 22:37:40 +00:00
|
|
|
else if (!strcmp(s, "DEMOLIST"))
|
2007-03-03 21:39:06 +00:00
|
|
|
{ //lists sources that are currently playing
|
|
|
|
int i;
|
|
|
|
|
|
|
|
Cluster_BuildAvailableDemoList(cluster);
|
|
|
|
|
2006-10-07 22:37:40 +00:00
|
|
|
s = "QTVSV 1\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
2007-03-03 21:39:06 +00:00
|
|
|
if (!cluster->availdemoscount)
|
|
|
|
{
|
|
|
|
s = "PERROR: No demos currently available\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = 0; i < cluster->availdemoscount; i++)
|
|
|
|
{
|
|
|
|
sprintf(tempbuf, "ADEMO: %i: %15s\n", cluster->availdemos[i].size, cluster->availdemos[i].name);
|
|
|
|
s = tempbuf;
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
}
|
|
|
|
qtv = NULL;
|
|
|
|
}
|
2006-10-07 22:37:40 +00:00
|
|
|
s = "\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
pend->flushing = true;
|
|
|
|
}
|
2006-10-27 13:41:27 +00:00
|
|
|
else if (!strcmp(s, "AUTH"))
|
|
|
|
{ //lists the demos available on this proxy
|
|
|
|
//part of the connection process, can be ignored if there's no password
|
|
|
|
}
|
2006-10-07 22:37:40 +00:00
|
|
|
else
|
|
|
|
printf("Unrecognised token in QTV connection request (%s)\n", s);
|
2006-09-17 01:27:32 +00:00
|
|
|
}
|
2006-09-19 01:48:12 +00:00
|
|
|
else
|
2006-09-17 01:27:32 +00:00
|
|
|
{
|
2006-10-07 22:37:40 +00:00
|
|
|
*colon++ = '\0';
|
|
|
|
if (!strcmp(s, "VERSION"))
|
2006-09-17 01:27:32 +00:00
|
|
|
{
|
2006-10-07 22:37:40 +00:00
|
|
|
switch(atoi(colon))
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
//got a usable version
|
|
|
|
usableversion = 1;
|
2006-09-17 01:27:32 +00:00
|
|
|
break;
|
2006-10-07 22:37:40 +00:00
|
|
|
default:
|
|
|
|
//not recognised.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!strcmp(s, "RAW"))
|
|
|
|
raw = atoi(colon);
|
|
|
|
/*else if (!strcmp(s, "ROUTE"))
|
|
|
|
{ //pure rewroute...
|
|
|
|
//is this safe? probably not.
|
|
|
|
s = "QTVSV 1\n"
|
|
|
|
"PERROR: ROUTE command not yet implemented\n"
|
|
|
|
"\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
pend->flushing = true;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
else if (!strcmp(s, "SOURCE"))
|
|
|
|
{ //connects, creating a new source
|
|
|
|
while (*colon == ' ')
|
|
|
|
colon++;
|
|
|
|
for (s = colon; *s; s++)
|
|
|
|
if (*s < '0' || *s > '9')
|
2006-09-17 01:27:32 +00:00
|
|
|
break;
|
2006-10-07 22:37:40 +00:00
|
|
|
if (*s)
|
2007-03-03 21:39:06 +00:00
|
|
|
qtv = QTV_NewServerConnection(cluster, colon, "", false, true, true, false);
|
2006-10-07 22:37:40 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
//numerical source, use a stream id.
|
|
|
|
for (qtv = cluster->servers; qtv; qtv = qtv->next)
|
|
|
|
if (qtv->streamid == atoi(colon))
|
|
|
|
break;
|
|
|
|
}
|
2006-09-17 01:27:32 +00:00
|
|
|
}
|
2006-10-07 22:37:40 +00:00
|
|
|
else if (!strcmp(s, "DEMO"))
|
|
|
|
{ //starts a demo off the server... source does the same thing though...
|
2007-03-03 21:39:06 +00:00
|
|
|
char buf[256];
|
|
|
|
|
2007-03-15 13:38:05 +00:00
|
|
|
snprintf(buf, sizeof(buf), "demo:%s", colon);
|
2007-03-03 21:39:06 +00:00
|
|
|
qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true, false);
|
|
|
|
if (!qtv)
|
|
|
|
{
|
|
|
|
s = "QTVSV 1\n"
|
|
|
|
"PERROR: couldn't open demo\n"
|
|
|
|
"\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
pend->flushing = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!strcmp(s, "AUTH"))
|
|
|
|
{ //lists the demos available on this proxy
|
|
|
|
//part of the connection process, can be ignored if there's no password
|
2006-10-07 22:37:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
printf("Unrecognised token in QTV connection request (%s)\n", s);
|
2006-09-17 01:27:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
s = e+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
e++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pend->flushing)
|
|
|
|
{
|
|
|
|
if (!usableversion)
|
|
|
|
{
|
|
|
|
s = "QTVSV 1\n"
|
|
|
|
"PERROR: Requested protocol version not supported\n"
|
|
|
|
"\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
pend->flushing = true;
|
|
|
|
}
|
|
|
|
if (!qtv)
|
|
|
|
{
|
|
|
|
s = "QTVSV 1\n"
|
|
|
|
"PERROR: No stream selected\n"
|
|
|
|
"\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
pend->flushing = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pend->flushing)
|
|
|
|
return false;
|
|
|
|
|
2006-10-11 23:17:55 +00:00
|
|
|
|
|
|
|
if (qtv->usequkeworldprotocols)
|
|
|
|
{
|
|
|
|
s = "QTVSV 1\n"
|
|
|
|
"PERROR: This version of QTV is unable to convert QuakeWorld to QTV protocols\n"
|
|
|
|
"\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
pend->flushing = true;
|
|
|
|
return false;
|
|
|
|
}
|
2007-03-03 21:39:06 +00:00
|
|
|
if (cluster->maxproxies>=0 && cluster->numproxies >= cluster->maxproxies)
|
2006-10-11 23:17:55 +00:00
|
|
|
{
|
|
|
|
s = "QTVSV 1\n"
|
|
|
|
"TERROR: This QTV has reached it's connection limit\n"
|
|
|
|
"\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
pend->flushing = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2006-09-17 01:27:32 +00:00
|
|
|
pend->next = qtv->proxies;
|
|
|
|
qtv->proxies = pend;
|
|
|
|
|
|
|
|
if (!raw)
|
|
|
|
{
|
2006-10-20 14:19:32 +00:00
|
|
|
s = "QTVSV 1\n";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
s = "BEGIN: ";
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
s = qtv->server;
|
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
s = "\n\n";
|
2006-09-17 01:27:32 +00:00
|
|
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
|
|
|
}
|
|
|
|
// else if (passwordprotected) //raw mode doesn't support passwords, so reject them
|
|
|
|
// {
|
|
|
|
// pend->flushing = true;
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
Net_SendConnectionMVD(qtv, pend);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|