Some tweeks to stop qqshka from moaning. Also added qtvlist and qtvdemolist commands. Tweeked the webpage generation to be more informative.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@2472 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2007-03-03 21:39:06 +00:00
parent 4175b342ea
commit 6210ed87be
6 changed files with 420 additions and 190 deletions

View file

@ -435,6 +435,7 @@ int main(int argc, char **argv)
cluster.allownqclients = true; cluster.allownqclients = true;
strcpy(cluster.hostname, DEFAULT_HOSTNAME); strcpy(cluster.hostname, DEFAULT_HOSTNAME);
cluster.buildnumber = build_number(); cluster.buildnumber = build_number();
cluster.maxproxies = -1;
Sys_Printf(&cluster, "QTV Build %i.\n", cluster.buildnumber); Sys_Printf(&cluster, "QTV Build %i.\n", cluster.buildnumber);

View file

@ -70,7 +70,7 @@ void SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv)
if (sock == INVALID_SOCKET) if (sock == INVALID_SOCKET)
return; return;
if (cluster->numproxies >= cluster->maxproxies && cluster->maxproxies) if (cluster->maxproxies >= 0 && cluster->numproxies >= cluster->maxproxies)
{ {
const char buffer[] = {dem_all, 1, 'P','r','o','x','y',' ','i','s',' ','f','u','l','l','.'}; const char buffer[] = {dem_all, 1, 'P','r','o','x','y',' ','i','s',' ','f','u','l','l','.'};
send(sock, buffer, strlen(buffer), 0); send(sock, buffer, strlen(buffer), 0);
@ -437,6 +437,100 @@ void SV_ForwardStream(sv_t *qtv, char *buffer, int length)
} }
} }
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;
}
void SV_GenerateNowPlayingHTTP(cluster_t *cluster, oproxy_t *dest) void SV_GenerateNowPlayingHTTP(cluster_t *cluster, oproxy_t *dest)
{ {
int player; int player;
@ -462,28 +556,41 @@ void SV_GenerateNowPlayingHTTP(cluster_t *cluster, oproxy_t *dest)
for (streams = cluster->servers; streams; streams = streams->next) for (streams = cluster->servers; streams; streams = streams->next)
{ {
sprintf(buffer, "<A HREF=\"watch.qtv?sid=%i\">%s (%s: %s)</A><br/>", streams->streamid, streams->server, streams->gamedir, streams->mapname); sprintf(buffer, "<A HREF=\"watch.qtv?sid=%i\">", streams->streamid);
Net_ProxySend(cluster, dest, buffer, strlen(buffer)); Net_ProxySend(cluster, dest, buffer, strlen(buffer));
HTMLprintf(buffer, sizeof(buffer), "%s (%s: %s)", streams->server, streams->gamedir, streams->mapname);
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
s = "</A><br/>";
Net_ProxySend(cluster, dest, s, strlen(s));
for (player = 0; player < MAX_CLIENTS; player++) for (player = 0; player < MAX_CLIENTS; player++)
{ {
if (*streams->players[player].userinfo) if (*streams->players[player].userinfo)
{ {
Info_ValueForKey(streams->players[player].userinfo, "name", plname, sizeof(plname)); Info_ValueForKey(streams->players[player].userinfo, "name", plname, sizeof(plname));
sprintf(buffer, "&nbsp;%s<br/>", plname);
s = "&nbsp;";
Net_ProxySend(cluster, dest, s, strlen(s));
HTMLprintf(buffer, sizeof(buffer), "%s", plname);
Net_ProxySend(cluster, dest, buffer, strlen(buffer)); Net_ProxySend(cluster, dest, buffer, strlen(buffer));
s = "<br/>";
Net_ProxySend(cluster, dest, s, strlen(s));
} }
} }
} }
if (!cluster->servers) if (!cluster->servers)
{ {
s = "No streams are currently being played<br />"; s = "No streams are currently being played<br />";
Net_ProxySend(cluster, dest, s, strlen(s)); Net_ProxySend(cluster, dest, s, strlen(s));
} }
s = "<br /><A href=\"/demos.html\">Available Demos</A>"; s = "<br /><A href=\"/demos.html\">Available Demos</A><br />";
Net_ProxySend(cluster, dest, s, strlen(s)); Net_ProxySend(cluster, dest, s, strlen(s));
s = "<A href=\"/admin.html\">Admin</A><br />";
Net_ProxySend(cluster, dest, s, strlen(s));
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));
sprintf(buffer, "</BODY>"); sprintf(buffer, "</BODY>");
Net_ProxySend(cluster, dest, buffer, strlen(buffer)); Net_ProxySend(cluster, dest, buffer, strlen(buffer));
@ -750,13 +857,22 @@ void SV_GenerateAdminHTTP(cluster_t *cluster, oproxy_t *dest, int streamid, char
s = strchr(o, '\n'); s = strchr(o, '\n');
if (s) if (s)
*s = 0; *s = 0;
Net_ProxySend(cluster, dest, o, strlen(o)); HTMLprintf(cmd, sizeof(cmd), "%s", o);
Net_ProxySend(cluster, dest, cmd, strlen(cmd));
Net_ProxySend(cluster, dest, "<BR />", 6); Net_ProxySend(cluster, dest, "<BR />", 6);
if (!s) if (!s)
break; break;
o = s+1; o = s+1;
} }
s = "<br /><A href=\"/nowplaying.html\">Now Playing</A><br />";
Net_ProxySend(cluster, dest, s, strlen(s));
s = "<A href=\"/demos.html\">Available Demos</A><br />";
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));
s = "</BODY>" s = "</BODY>"
"</HTML>"; "</HTML>";
Net_ProxySend(cluster, dest, s, strlen(s)); Net_ProxySend(cluster, dest, s, strlen(s));
@ -768,7 +884,7 @@ void SV_GenerateAdminHTTP(cluster_t *cluster, oproxy_t *dest, int streamid, char
void SV_GenerateQTVDemoListing(cluster_t *cluster, oproxy_t *dest) void SV_GenerateQTVDemoListing(cluster_t *cluster, oproxy_t *dest)
{ {
int numdemos = 0; int i;
char link[256]; char link[256];
char *s; char *s;
s = "HTTP/1.1 200 OK\n" s = "HTTP/1.1 200 OK\n"
@ -781,68 +897,25 @@ void SV_GenerateQTVDemoListing(cluster_t *cluster, oproxy_t *dest)
s = "<H1>QTV Demo listing</H1>"; s = "<H1>QTV Demo listing</H1>";
Net_ProxySend(cluster, dest, s, strlen(s)); Net_ProxySend(cluster, dest, s, strlen(s));
#ifdef _WIN32 Cluster_BuildAvailableDemoList(cluster);
for (i = 0; i < cluster->availdemoscount; i++)
{ {
WIN32_FIND_DATA ffd; 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);
HANDLE h; Net_ProxySend(cluster, dest, link, strlen(link));
h = FindFirstFile("*.mvd", &ffd);
if (h != INVALID_HANDLE_VALUE)
{
do
{
numdemos++;
snprintf(link, sizeof(link), "<A HREF=\"watch.qtv?demo=%s\">%s</A><br/>", ffd.cFileName, ffd.cFileName);
Net_ProxySend(cluster, dest, link, strlen(link));
} while(FindNextFile(h, &ffd));
FindClose(h);
}
} }
#else
{
int namelen;
DIR *dir;
struct dirent *oneentry;
dir=opendir("."); sprintf(link, "<P>Total: %i demos</P>", cluster->availdemoscount);
if (!dir)
{
s = "QTV Proxy is unable to search for available demos.";
Net_ProxySend(cluster, dest, s, strlen(s));
}
else
{
for(;;)
{
oneentry=readdir(dir);
if(!oneentry)
break;
#ifndef __CYGWIN__
if (oneentry->d_type == DT_DIR || oneentry->d_type == DT_LNK)
{
continue;
}
#endif
namelen = strlen(oneentry->d_name);
if (namelen > 4 && !strcmp(oneentry->d_name + namelen-4, ".mvd"))
{
numdemos++;
snprintf(link, sizeof(link), "<A HREF=\"watch.qtv?demo=%s\">%s</A><br/>", oneentry->d_name, oneentry->d_name);
Net_ProxySend(cluster, dest, link, strlen(link));
}
}
closedir(dir);
}
}
/*
s = "QTV Proxy is running on a platform for which file system listing is not coded.<br />Demo listing is not available.";
Net_ProxySend(cluster, dest, s, strlen(s));
*/
#endif
sprintf(link, "<P>Total: %i demos</P>", numdemos);
Net_ProxySend(cluster, dest, link, strlen(link)); Net_ProxySend(cluster, dest, link, strlen(link));
s = "<br /><A href=\"/nowplaying.html\">Now Playing</A><br />";
Net_ProxySend(cluster, dest, s, strlen(s));
s = "<A href=\"/admin.html\">Admin</A><br />";
Net_ProxySend(cluster, dest, s, strlen(s));
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));
s = "</BODY>" s = "</BODY>"
"</HTML>"; "</HTML>";
Net_ProxySend(cluster, dest, s, strlen(s)); Net_ProxySend(cluster, dest, s, strlen(s));
@ -1102,11 +1175,28 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
} }
} }
else if (!strcmp(s, "DEMOLIST")) else if (!strcmp(s, "DEMOLIST"))
{ //lists the demos available on this proxy { //lists sources that are currently playing
int i;
Cluster_BuildAvailableDemoList(cluster);
s = "QTVSV 1\n"; s = "QTVSV 1\n";
Net_ProxySend(cluster, pend, s, strlen(s)); Net_ProxySend(cluster, pend, s, strlen(s));
s = "PERROR: DEMOLIST command not yet implemented\n"; if (!cluster->availdemoscount)
Net_ProxySend(cluster, pend, s, strlen(s)); {
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;
}
s = "\n"; s = "\n";
Net_ProxySend(cluster, pend, s, strlen(s)); Net_ProxySend(cluster, pend, s, strlen(s));
pend->flushing = true; pend->flushing = true;
@ -1154,7 +1244,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
if (*s < '0' || *s > '9') if (*s < '0' || *s > '9')
break; break;
if (*s) if (*s)
qtv = QTV_NewServerConnection(cluster, colon, "", false, true, true); qtv = QTV_NewServerConnection(cluster, colon, "", false, true, true, false);
else else
{ {
//numerical source, use a stream id. //numerical source, use a stream id.
@ -1162,18 +1252,25 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
if (qtv->streamid == atoi(colon)) if (qtv->streamid == atoi(colon))
break; break;
} }
// s = "QTVSV 1\n"
// "PERROR: SOURCE command not yet implemented\n"
// "\n";
// Net_ProxySend(cluster, pend, s, strlen(s));
} }
else if (!strcmp(s, "DEMO")) else if (!strcmp(s, "DEMO"))
{ //starts a demo off the server... source does the same thing though... { //starts a demo off the server... source does the same thing though...
s = "QTVSV 1\n" char buf[256];
"PERROR: DEMO command not yet implemented\n"
"\n"; sprintf(buf, sizeof(buf), "demo:%s", colon);
Net_ProxySend(cluster, pend, s, strlen(s)); qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true, false);
pend->flushing = true; 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
} }
else else
printf("Unrecognised token in QTV connection request (%s)\n", s); printf("Unrecognised token in QTV connection request (%s)\n", s);
@ -1217,7 +1314,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
pend->flushing = true; pend->flushing = true;
return false; return false;
} }
if (cluster->maxproxies && cluster->numproxies >= cluster->maxproxies) if (cluster->maxproxies>=0 && cluster->numproxies >= cluster->maxproxies)
{ {
s = "QTVSV 1\n" s = "QTVSV 1\n"
"TERROR: This QTV has reached it's connection limit\n" "TERROR: This QTV has reached it's connection limit\n"

View file

@ -509,6 +509,7 @@ struct sv_s { //details about a server connection (also known as stream)
char connectpassword[64]; //password given to server char connectpassword[64]; //password given to server
netadr_t serveraddress; netadr_t serveraddress;
netchan_t netchan; netchan_t netchan;
qboolean serverquery;
unsigned char buffer[MAX_PROXY_BUFFER]; //this doesn't cycle. unsigned char buffer[MAX_PROXY_BUFFER]; //this doesn't cycle.
int buffersize; //it memmoves down int buffersize; //it memmoves down
@ -924,7 +925,7 @@ void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf);
void Sys_Printf(cluster_t *cluster, char *fmt, ...); void Sys_Printf(cluster_t *cluster, char *fmt, ...);
oproxy_t *Net_FileProxy(sv_t *qtv, char *filename); oproxy_t *Net_FileProxy(sv_t *qtv, char *filename);
sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates); sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates, qboolean query);
SOCKET Net_MVDListen(int port); SOCKET Net_MVDListen(int port);
qboolean Net_StopFileProxy(sv_t *qtv); qboolean Net_StopFileProxy(sv_t *qtv);

View file

@ -2300,7 +2300,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
else if (!strcmp(v->expectcommand, "addserver")) else if (!strcmp(v->expectcommand, "addserver"))
{ {
snprintf(buf, sizeof(buf), "tcp:%s", message); snprintf(buf, sizeof(buf), "tcp:%s", message);
qtv = QTV_NewServerConnection(cluster, buf, "", false, false, false); qtv = QTV_NewServerConnection(cluster, buf, "", false, false, false, false);
if (qtv) if (qtv)
{ {
QW_SetViewersServer(cluster, v, qtv); QW_SetViewersServer(cluster, v, qtv);
@ -2326,7 +2326,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
else if (!strcmp(v->expectcommand, "insecadddemo")) else if (!strcmp(v->expectcommand, "insecadddemo"))
{ {
snprintf(buf, sizeof(buf), "file:%s", message); snprintf(buf, sizeof(buf), "file:%s", message);
qtv = QTV_NewServerConnection(cluster, buf, "", false, false, false); qtv = QTV_NewServerConnection(cluster, buf, "", false, false, false, false);
if (!qtv) if (!qtv)
QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message); QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message);
else else
@ -2339,7 +2339,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
else if (!strcmp(v->expectcommand, "adddemo")) else if (!strcmp(v->expectcommand, "adddemo"))
{ {
snprintf(buf, sizeof(buf), "file:%s", message); snprintf(buf, sizeof(buf), "file:%s", message);
qtv = QTV_NewServerConnection(cluster, buf, "", false, false, false); qtv = QTV_NewServerConnection(cluster, buf, "", false, false, false, false);
if (!qtv) if (!qtv)
QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message); QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message);
else else
@ -2681,7 +2681,7 @@ tuidemos:
else else
message += 9; message += 9;
snprintf(buf, sizeof(buf), "udp:%s", message); snprintf(buf, sizeof(buf), "udp:%s", message);
qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true); qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true, false);
if (qtv) if (qtv)
{ {
QW_SetMenu(v, MENU_NONE); QW_SetMenu(v, MENU_NONE);
@ -2695,7 +2695,7 @@ tuidemos:
{ {
message += 6; message += 6;
snprintf(buf, sizeof(buf), "udp:%s", message); snprintf(buf, sizeof(buf), "udp:%s", message);
qtv = QTV_NewServerConnection(cluster, buf, "", false, true, false); qtv = QTV_NewServerConnection(cluster, buf, "", false, true, false, false);
if (qtv) if (qtv)
{ {
QW_SetMenu(v, MENU_NONE); QW_SetMenu(v, MENU_NONE);
@ -2710,7 +2710,7 @@ tuidemos:
{ {
message += 5; message += 5;
snprintf(buf, sizeof(buf), "tcp:%s", message); snprintf(buf, sizeof(buf), "tcp:%s", message);
qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true); qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true, false);
if (qtv) if (qtv)
{ {
QW_SetMenu(v, MENU_NONE); QW_SetMenu(v, MENU_NONE);
@ -2747,7 +2747,7 @@ tuidemos:
{ {
message += 6; message += 6;
snprintf(buf, sizeof(buf), "file:%s", message); snprintf(buf, sizeof(buf), "file:%s", message);
qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true); qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true, false);
if (qtv) if (qtv)
{ {
QW_SetMenu(v, MENU_NONE); QW_SetMenu(v, MENU_NONE);

View file

@ -27,18 +27,22 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
FTEQTV proxy commands: (build "__DATE__")\n\ FTEQTV proxy commands: (build "__DATE__")\n\
----------------------\n\ ----------------------\n\
connect, qtv, addserver\n\ connect, qtv, addserver\n\
- connect to a MVD stream (TCP)\n\ connect to a MVD stream (TCP)\n\
qtvlist\n\
lists available streams on a proxy\n\
qw\n\ qw\n\
- connect to a server as a player (UDP)\n\ connect to a server as a player (UDP)\n\
adddemo\n\ adddemo\n\
- play a demo from a MVD file\n\ play a demo from a MVD file\n\
port\n\ port\n\
- UDP port for QuakeWorld client connections\n\ UDP port for QuakeWorld client connections\n\
mvdport\n\ mvdport\n\
- specify TCP port for MVD broadcasting\n\ specify TCP port for MVD broadcasting\n\
maxviewers, maxproxies\n\ maxviewers, maxproxies\n\
- limit number of connections\n\ limit number of connections\n\
status, choke, late, talking, nobsp, reconnect, exec, password, master, hostname, record, stop, quit\n\n" status, choke, late, talking, nobsp, reconnect, exec, password, master, hostname, record, stop, quit\n\
other random commands\n\
\n"
@ -402,6 +406,33 @@ char *Cmd_AdminPassword(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char
strncpy(cluster->adminpassword, arg[1], sizeof(cluster->adminpassword)-1); strncpy(cluster->adminpassword, arg[1], sizeof(cluster->adminpassword)-1);
return "Password changed.\n"; return "Password changed.\n";
} }
char *Cmd_QTVList(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (!*arg[1])
return "connect requires an ip:port parameter\n";
memmove(arg[1]+4, arg[1], ARG_LEN-5);
strncpy(arg[1], "tcp:", 4);
qtv = QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false, true);
if (!qtv)
return "Failed to connect to server, connection aborted\n";
return "Querying proxy\n";
}
char *Cmd_QTVDemoList(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (!*arg[1])
return "connect requires an ip:port parameter\n";
memmove(arg[1]+4, arg[1], ARG_LEN-5);
strncpy(arg[1], "tcp:", 4);
qtv = QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false, 2);
if (!qtv)
return "Failed to connect to server, connection aborted\n";
return "Querying proxy\n";
}
char *Cmd_QTVConnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) char *Cmd_QTVConnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{ {
if (!*arg[1]) if (!*arg[1])
@ -410,7 +441,7 @@ char *Cmd_QTVConnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *b
memmove(arg[1]+4, arg[1], ARG_LEN-5); memmove(arg[1]+4, arg[1], ARG_LEN-5);
strncpy(arg[1], "tcp:", 4); strncpy(arg[1], "tcp:", 4);
if (!QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false)) if (!QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false, false))
return "Failed to connect to server, connection aborted\n"; return "Failed to connect to server, connection aborted\n";
return "Source registered\n"; return "Source registered\n";
} }
@ -422,7 +453,7 @@ char *Cmd_QWConnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *bu
memmove(arg[1]+4, arg[1], ARG_LEN-5); memmove(arg[1]+4, arg[1], ARG_LEN-5);
strncpy(arg[1], "udp:", 4); strncpy(arg[1], "udp:", 4);
if (!QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false)) if (!QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false, false))
return "Failed to connect to server, connection aborted\n"; return "Failed to connect to server, connection aborted\n";
return "Source registered\n"; return "Source registered\n";
} }
@ -438,7 +469,7 @@ char *Cmd_MVDConnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *b
memmove(arg[1]+5, arg[1], ARG_LEN-6); memmove(arg[1]+5, arg[1], ARG_LEN-6);
strncpy(arg[1], "file:", 5); strncpy(arg[1], "file:", 5);
if (!QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false)) if (!QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false, false))
return "Failed to connect to server, connection aborted\n"; return "Failed to connect to server, connection aborted\n";
return "Source registered\n"; return "Source registered\n";
} }
@ -847,6 +878,8 @@ const rconcommands_t rconcommands[] =
{"port", 0, 1, Cmd_UDPPort}, {"port", 0, 1, Cmd_UDPPort},
{"adminpassword",0, 1, Cmd_AdminPassword}, {"adminpassword",0, 1, Cmd_AdminPassword},
{"rconpassword",0, 1, Cmd_AdminPassword}, {"rconpassword",0, 1, Cmd_AdminPassword},
{"qtvlist", 0, 1, Cmd_QTVList},
{"qtvdemolist", 0, 1, Cmd_QTVDemoList},
{"qtv", 0, 1, Cmd_QTVConnect}, {"qtv", 0, 1, Cmd_QTVConnect},
{"addserver", 0, 1, Cmd_QTVConnect}, {"addserver", 0, 1, Cmd_QTVConnect},
{"connect", 0, 1, Cmd_QTVConnect}, {"connect", 0, 1, Cmd_QTVConnect},

View file

@ -272,80 +272,110 @@ void Net_SendQTVConnectionRequest(sv_t *qtv, char *authmethod, char *challenge)
str = "QTV\n"; Net_QueueUpstream(qtv, strlen(str), str); str = "QTV\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "VERSION: 1\n"; Net_QueueUpstream(qtv, strlen(str), str); str = "VERSION: 1\n"; Net_QueueUpstream(qtv, strlen(str), str);
at = strchrrev(qtv->server, '@'); if (qtv->serverquery)
if (at)
{ {
*at = '\0'; if (qtv->serverquery == 2)
str = "SOURCE: "; Net_QueueUpstream(qtv, strlen(str), str);
str = qtv->server; Net_QueueUpstream(qtv, strlen(str), str);
str = "\n"; Net_QueueUpstream(qtv, strlen(str), str);
*at = '@';
}
else
{
str = "RECEIVE\n"; Net_QueueUpstream(qtv, strlen(str), str);
}
if (!qtv->parsingqtvheader)
{
str = "RAW: 1\n"; Net_QueueUpstream(qtv, strlen(str), str);
}
else
{
if (authmethod)
{ {
if (!strcmp(authmethod, "PLAIN")) str = "DEMOLIST\n"; Net_QueueUpstream(qtv, strlen(str), str);
{
str = "AUTH: PLAIN\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "PASSWORD: \""; Net_QueueUpstream(qtv, strlen(str), str);
str = qtv->connectpassword; Net_QueueUpstream(qtv, strlen(str), str);
str = "\"\n"; Net_QueueUpstream(qtv, strlen(str), str);
}
else if (challenge && strlen(challenge)>=32 && !strcmp(authmethod, "CCITT"))
{
unsigned short crcvalue;
str = "AUTH: CCITT\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "PASSWORD: \""; Net_QueueUpstream(qtv, strlen(str), str);
snprintf(hash, sizeof(hash), "%s%s", challenge, qtv->connectpassword);
crcvalue = QCRC_Block(hash, strlen(hash));
sprintf(hash, "0x%X", (unsigned int)QCRC_Value(crcvalue));
str = hash; Net_QueueUpstream(qtv, strlen(str), str);
str = "\"\n"; Net_QueueUpstream(qtv, strlen(str), str);
}
else if (challenge && strlen(challenge)>=8 && !strcmp(authmethod, "MD4"))
{
unsigned int md4sum[4];
str = "AUTH: MD4\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "PASSWORD: \""; Net_QueueUpstream(qtv, strlen(str), str);
snprintf(hash, sizeof(hash), "%s%s", challenge, qtv->connectpassword);
Com_BlockFullChecksum (hash, strlen(hash), (unsigned char*)md4sum);
sprintf(hash, "%X%X%X%X", md4sum[0], md4sum[1], md4sum[2], md4sum[3]);
str = hash; Net_QueueUpstream(qtv, strlen(str), str);
str = "\"\n"; Net_QueueUpstream(qtv, strlen(str), str);
}
else if (!strcmp(authmethod, "NONE"))
{
str = "AUTH: NONE\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "PASSWORD: \n"; Net_QueueUpstream(qtv, strlen(str), str);
}
else
{
qtv->drop = true;
qtv->upstreambuffersize = 0;
Sys_Printf(qtv->cluster, "Auth method %s was not usable\n", authmethod);
return;
}
} }
else else
{ {
str = "AUTH: MD4\n"; Net_QueueUpstream(qtv, strlen(str), str); str = "SOURCELIST\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "AUTH: CCITT\n"; Net_QueueUpstream(qtv, strlen(str), str); }
str = "AUTH: PLAIN\n"; Net_QueueUpstream(qtv, strlen(str), str); }
str = "AUTH: NONE\n"; Net_QueueUpstream(qtv, strlen(str), str); else
{
at = strchrrev(qtv->server, '@');
if (at)
{
*at = '\0';
str = "SOURCE: "; Net_QueueUpstream(qtv, strlen(str), str);
if (strncmp(qtv->server, "tcp:", 4))
{
str = qtv->server;
Net_QueueUpstream(qtv, strlen(str), str);
}
else
{
str = strchr(qtv->server, ':');
if (str)
{
str++;
Net_QueueUpstream(qtv, strlen(str), str);
}
}
str = "\n"; Net_QueueUpstream(qtv, strlen(str), str);
*at = '@';
}
else
{
str = "RECEIVE\n"; Net_QueueUpstream(qtv, strlen(str), str);
}
if (!qtv->parsingqtvheader)
{
str = "RAW: 1\n"; Net_QueueUpstream(qtv, strlen(str), str);
}
else
{
if (authmethod)
{
if (!strcmp(authmethod, "PLAIN"))
{
str = "AUTH: PLAIN\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "PASSWORD: \""; Net_QueueUpstream(qtv, strlen(str), str);
str = qtv->connectpassword; Net_QueueUpstream(qtv, strlen(str), str);
str = "\"\n"; Net_QueueUpstream(qtv, strlen(str), str);
}
else if (challenge && strlen(challenge)>=32 && !strcmp(authmethod, "CCITT"))
{
unsigned short crcvalue;
str = "AUTH: CCITT\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "PASSWORD: \""; Net_QueueUpstream(qtv, strlen(str), str);
snprintf(hash, sizeof(hash), "%s%s", challenge, qtv->connectpassword);
crcvalue = QCRC_Block(hash, strlen(hash));
sprintf(hash, "0x%X", (unsigned int)QCRC_Value(crcvalue));
str = hash; Net_QueueUpstream(qtv, strlen(str), str);
str = "\"\n"; Net_QueueUpstream(qtv, strlen(str), str);
}
else if (challenge && strlen(challenge)>=8 && !strcmp(authmethod, "MD4"))
{
unsigned int md4sum[4];
str = "AUTH: MD4\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "PASSWORD: \""; Net_QueueUpstream(qtv, strlen(str), str);
snprintf(hash, sizeof(hash), "%s%s", challenge, qtv->connectpassword);
Com_BlockFullChecksum (hash, strlen(hash), (unsigned char*)md4sum);
sprintf(hash, "%X%X%X%X", md4sum[0], md4sum[1], md4sum[2], md4sum[3]);
str = hash; Net_QueueUpstream(qtv, strlen(str), str);
str = "\"\n"; Net_QueueUpstream(qtv, strlen(str), str);
}
else if (!strcmp(authmethod, "NONE"))
{
str = "AUTH: NONE\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "PASSWORD: \n"; Net_QueueUpstream(qtv, strlen(str), str);
}
else
{
qtv->drop = true;
qtv->upstreambuffersize = 0;
Sys_Printf(qtv->cluster, "Auth method %s was not usable\n", authmethod);
return;
}
}
else
{
str = "AUTH: MD4\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "AUTH: CCITT\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "AUTH: PLAIN\n"; Net_QueueUpstream(qtv, strlen(str), str);
str = "AUTH: NONE\n"; Net_QueueUpstream(qtv, strlen(str), str);
}
} }
} }
str = "\n"; Net_QueueUpstream(qtv, strlen(str), str); str = "\n"; Net_QueueUpstream(qtv, strlen(str), str);
@ -357,7 +387,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip)
netadr_t from; netadr_t from;
unsigned long nonblocking = true; unsigned long nonblocking = true;
if (!NET_StringToAddr(ip+4, &qtv->serveraddress, 27500)) if (!NET_StringToAddr(ip, &qtv->serveraddress, 27500))
{ {
Sys_Printf(qtv->cluster, "Unable to resolve %s\n", ip); Sys_Printf(qtv->cluster, "Unable to resolve %s\n", ip);
return false; return false;
@ -402,7 +432,7 @@ qboolean Net_ConnectToUDPServer(sv_t *qtv, char *ip)
netadr_t from; netadr_t from;
unsigned long nonblocking = true; unsigned long nonblocking = true;
if (!NET_StringToAddr(ip+4, &qtv->serveraddress, 27500)) if (!NET_StringToAddr(ip, &qtv->serveraddress, 27500))
{ {
Sys_Printf(qtv->cluster, "Unable to resolve %s\n", ip); Sys_Printf(qtv->cluster, "Unable to resolve %s\n", ip);
return false; return false;
@ -478,22 +508,56 @@ qboolean DemoFilenameIsOkay(char *fname)
*/ */
} }
qboolean Net_ConnectToServer(sv_t *qtv, char *ip) qboolean Net_ConnectToServer(sv_t *qtv)
{ {
char *at; char *at;
qboolean status; enum {
SRC_BAD,
SRC_DEMO,
SRC_UDP,
SRC_TCP
} type = SRC_BAD;
char *ip = qtv->server;
if (!strncmp(ip, "udp:", 4))
{
type = SRC_UDP;
ip += 4;
}
else if (!strncmp(ip, "tcp:", 4))
{
type = SRC_TCP;
ip += 4;
}
else if (!strncmp(ip, "demo:", 5))
{
type = SRC_DEMO;
ip += 5;
}
else if (!strncmp(ip, "file:", 5))
{
type = SRC_DEMO;
ip += 5;
}
at = strchrrev(ip, '@'); at = strchrrev(ip, '@');
if (at) if (at && (type == SRC_DEMO || type == SRC_TCP))
{
if (type == SRC_DEMO)
type = SRC_TCP;
ip = at+1; ip = at+1;
}
qtv->usequkeworldprotocols = false; qtv->usequkeworldprotocols = false;
if (!strncmp(ip, "file:", 5) || !strncmp(ip, "demo:", 5)) qtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME; //wait half a minuite before trying to reconnect
switch(type)
{ {
case SRC_DEMO:
qtv->sourcesock = INVALID_SOCKET; qtv->sourcesock = INVALID_SOCKET;
if (DemoFilenameIsOkay(ip+5)) if (DemoFilenameIsOkay(ip))
qtv->sourcefile = fopen(ip+5, "rb"); qtv->sourcefile = fopen(ip, "rb");
else else
qtv->sourcefile = NULL; qtv->sourcefile = NULL;
if (qtv->sourcefile) if (qtv->sourcefile)
@ -505,23 +569,19 @@ qboolean Net_ConnectToServer(sv_t *qtv, char *ip)
} }
Sys_Printf(qtv->cluster, "Unable to open file %s\n", ip+5); Sys_Printf(qtv->cluster, "Unable to open file %s\n", ip+5);
return false; return false;
}
qtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME; //wait half a minuite before trying to reconnect
if (!strncmp(ip, "udp:", 4)) case SRC_UDP:
{
qtv->usequkeworldprotocols = true; qtv->usequkeworldprotocols = true;
status = Net_ConnectToUDPServer(qtv, ip); return Net_ConnectToUDPServer(qtv, ip);
}
else if (!strncmp(ip, "tcp:", 4) || at!=NULL) case SRC_TCP:
status = Net_ConnectToTCPServer(qtv, ip); return Net_ConnectToTCPServer(qtv, ip);
else
{ default:
Sys_Printf(qtv->cluster, "Unknown source type %s\n", ip); Sys_Printf(qtv->cluster, "Unknown source type %s\n", ip);
status = false; return false;
} }
return status;
} }
void Net_QueueUpstream(sv_t *qtv, int size, char *buffer) void Net_QueueUpstream(sv_t *qtv, int size, char *buffer)
@ -847,7 +907,7 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl)
memcpy(qtv->server, serverurl, sizeof(qtv->server)-1); memcpy(qtv->server, serverurl, sizeof(qtv->server)-1);
if (!Net_ConnectToServer(qtv, qtv->server)) if (!Net_ConnectToServer(qtv))
{ {
Sys_Printf(qtv->cluster, "Couldn't connect (%s)\n", qtv->server); Sys_Printf(qtv->cluster, "Couldn't connect (%s)\n", qtv->server);
return false; return false;
@ -1566,13 +1626,43 @@ void QTV_Run(sv_t *qtv)
qtv->buffersize = 0; qtv->buffersize = 0;
return; return;
} }
else if (!strcmp(start, "TERROR")) else if (!strcmp(start, "TERROR") || !strcmp(start, "ERROR"))
{ //we don't support compression, we didn't ask for it. { //we don't support compression, we didn't ask for it.
Sys_Printf(qtv->cluster, "\nQTV server error: %s\n\n", colon); Sys_Printf(qtv->cluster, "\nQTV server error: %s\n\n", colon);
qtv->drop = true;
qtv->buffersize = 0; qtv->buffersize = 0;
if (qtv->disconnectwhennooneiswatching)
qtv->drop = true; //if its a user registered stream, drop it immediatly
else
{ //otherwise close the socket (this will result in a timeout and reconnect)
if (qtv->sourcesock != INVALID_SOCKET)
{
closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET;
}
}
return; return;
} }
else if (!strcmp(start, "ASOURCE"))
{
Sys_Printf(qtv->cluster, "SRC: %s\n", colon);
}
else if (!strcmp(start, "ADEMO"))
{
int size;
size = atoi(colon);
colon = strchr(colon, ':');
if (!colon)
colon = "";
else
colon = colon+1;
while(*colon == ' ')
colon++;
if (size > 1024*1024)
Sys_Printf(qtv->cluster, "DEMO: (%3imb) %s\n", size/(1024*1024), colon);
else
Sys_Printf(qtv->cluster, "DEMO: (%3ikb) %s\n", size/1024, colon);
}
else if (!strcmp(start, "PRINT")) else if (!strcmp(start, "PRINT"))
{ {
Sys_Printf(qtv->cluster, "QTV server: %s\n", colon); Sys_Printf(qtv->cluster, "QTV server: %s\n", colon);
@ -1592,7 +1682,14 @@ void QTV_Run(sv_t *qtv)
qtv->buffersize -= length; qtv->buffersize -= length;
memmove(qtv->buffer, qtv->buffer + length, qtv->buffersize); memmove(qtv->buffer, qtv->buffer + length, qtv->buffersize);
if (*authmethod) if (qtv->serverquery)
{
Sys_Printf(qtv->cluster, "End of sources\n", colon);
qtv->drop = true;
qtv->buffersize = 0;
return;
}
else if (*authmethod)
{ //we need to send a challenge response now. { //we need to send a challenge response now.
Net_SendQTVConnectionRequest(qtv, authmethod, challenge); Net_SendQTVConnectionRequest(qtv, authmethod, challenge);
return; return;
@ -1750,7 +1847,7 @@ void QTV_Run(sv_t *qtv)
} }
} }
sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates) sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates, qboolean query)
{ {
sv_t *qtv; sv_t *qtv;
@ -1781,6 +1878,7 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password,
qtv->sourcesock = INVALID_SOCKET; qtv->sourcesock = INVALID_SOCKET;
qtv->disconnectwhennooneiswatching = autoclose; qtv->disconnectwhennooneiswatching = autoclose;
qtv->parsingconnectiondata = true; qtv->parsingconnectiondata = true;
qtv->serverquery = query;
qtv->streamid = ++cluster->nextstreamid; qtv->streamid = ++cluster->nextstreamid;