diff --git a/fteqtv/control.c b/fteqtv/control.c index c6e3ccfe2..74033aabe 100644 --- a/fteqtv/control.c +++ b/fteqtv/control.c @@ -435,6 +435,7 @@ int main(int argc, char **argv) cluster.allownqclients = true; strcpy(cluster.hostname, DEFAULT_HOSTNAME); cluster.buildnumber = build_number(); + cluster.maxproxies = -1; Sys_Printf(&cluster, "QTV Build %i.\n", cluster.buildnumber); diff --git a/fteqtv/forward.c b/fteqtv/forward.c index 41890ac93..b69f8357c 100644 --- a/fteqtv/forward.c +++ b/fteqtv/forward.c @@ -70,7 +70,7 @@ void SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv) if (sock == INVALID_SOCKET) 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','.'}; 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) { int player; @@ -462,28 +556,41 @@ void SV_GenerateNowPlayingHTTP(cluster_t *cluster, oproxy_t *dest) for (streams = cluster->servers; streams; streams = streams->next) { - sprintf(buffer, "%s (%s: %s)
", streams->streamid, streams->server, streams->gamedir, streams->mapname); + sprintf(buffer, "", streams->streamid); 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 = "
"; + Net_ProxySend(cluster, dest, s, strlen(s)); for (player = 0; player < MAX_CLIENTS; player++) { if (*streams->players[player].userinfo) { Info_ValueForKey(streams->players[player].userinfo, "name", plname, sizeof(plname)); - sprintf(buffer, " %s
", plname); + + s = " "; + Net_ProxySend(cluster, dest, s, strlen(s)); + HTMLprintf(buffer, sizeof(buffer), "%s", plname); Net_ProxySend(cluster, dest, buffer, strlen(buffer)); + s = "
"; + Net_ProxySend(cluster, dest, s, strlen(s)); } } } if (!cluster->servers) { - s = "No streams are currently being played
"; Net_ProxySend(cluster, dest, s, strlen(s)); } - s = "
Available Demos"; + s = "
Available Demos
"; Net_ProxySend(cluster, dest, s, strlen(s)); + s = "Admin
"; + Net_ProxySend(cluster, dest, s, strlen(s)); + + sprintf(buffer, "
QTV Version: %i www.fteqw.com
", cluster->buildnumber); + Net_ProxySend(cluster, dest, buffer, strlen(buffer)); sprintf(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'); if (s) *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, "
", 6); if (!s) break; o = s+1; } + s = "
Now Playing
"; + Net_ProxySend(cluster, dest, s, strlen(s)); + s = "Available Demos
"; + Net_ProxySend(cluster, dest, s, strlen(s)); + + sprintf(result, "
QTV Version: %i www.fteqw.com
", cluster->buildnumber); + Net_ProxySend(cluster, dest, result, strlen(result)); + 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) { - int numdemos = 0; + int i; char link[256]; char *s; s = "HTTP/1.1 200 OK\n" @@ -781,68 +897,25 @@ void SV_GenerateQTVDemoListing(cluster_t *cluster, oproxy_t *dest) s = "

QTV Demo listing

"; Net_ProxySend(cluster, dest, s, strlen(s)); -#ifdef _WIN32 + Cluster_BuildAvailableDemoList(cluster); + for (i = 0; i < cluster->availdemoscount; i++) { - WIN32_FIND_DATA ffd; - HANDLE h; - h = FindFirstFile("*.mvd", &ffd); - if (h != INVALID_HANDLE_VALUE) - { - do - { - numdemos++; - snprintf(link, sizeof(link), "%s
", ffd.cFileName, ffd.cFileName); - Net_ProxySend(cluster, dest, link, strlen(link)); - } while(FindNextFile(h, &ffd)); - FindClose(h); - } + snprintf(link, sizeof(link), "%s (%ikb)
", cluster->availdemos[i].name, cluster->availdemos[i].name, cluster->availdemos[i].size/1024); + Net_ProxySend(cluster, dest, link, strlen(link)); } -#else - { - int namelen; - DIR *dir; - struct dirent *oneentry; - dir=opendir("."); - 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), "%s
", 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.
Demo listing is not available."; - Net_ProxySend(cluster, dest, s, strlen(s)); - */ -#endif - - sprintf(link, "

Total: %i demos

", numdemos); + sprintf(link, "

Total: %i demos

", cluster->availdemoscount); Net_ProxySend(cluster, dest, link, strlen(link)); + s = "
Now Playing
"; + Net_ProxySend(cluster, dest, s, strlen(s)); + s = "Admin
"; + Net_ProxySend(cluster, dest, s, strlen(s)); + + sprintf(link, "
QTV Version: %i www.fteqw.com
", cluster->buildnumber); + Net_ProxySend(cluster, dest, link, strlen(link)); + + 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")) - { //lists the demos available on this proxy + { //lists sources that are currently playing + int i; + + Cluster_BuildAvailableDemoList(cluster); + s = "QTVSV 1\n"; Net_ProxySend(cluster, pend, s, strlen(s)); - s = "PERROR: DEMOLIST command not yet implemented\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); + 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; + } s = "\n"; Net_ProxySend(cluster, pend, s, strlen(s)); pend->flushing = true; @@ -1154,7 +1244,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend) if (*s < '0' || *s > '9') break; if (*s) - qtv = QTV_NewServerConnection(cluster, colon, "", false, true, true); + qtv = QTV_NewServerConnection(cluster, colon, "", false, true, true, false); else { //numerical source, use a stream id. @@ -1162,18 +1252,25 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend) if (qtv->streamid == atoi(colon)) 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")) { //starts a demo off the server... source does the same thing though... - s = "QTVSV 1\n" - "PERROR: DEMO command not yet implemented\n" - "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - pend->flushing = true; + char buf[256]; + + sprintf(buf, sizeof(buf), "demo:%s", colon); + 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 } else 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; return false; } - if (cluster->maxproxies && cluster->numproxies >= cluster->maxproxies) + if (cluster->maxproxies>=0 && cluster->numproxies >= cluster->maxproxies) { s = "QTVSV 1\n" "TERROR: This QTV has reached it's connection limit\n" diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h index 81c9cbc66..3e8e29035 100644 --- a/fteqtv/qtv.h +++ b/fteqtv/qtv.h @@ -509,6 +509,7 @@ struct sv_s { //details about a server connection (also known as stream) char connectpassword[64]; //password given to server netadr_t serveraddress; netchan_t netchan; + qboolean serverquery; unsigned char buffer[MAX_PROXY_BUFFER]; //this doesn't cycle. 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, ...); 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); qboolean Net_StopFileProxy(sv_t *qtv); diff --git a/fteqtv/qw.c b/fteqtv/qw.c index 3522fe70e..f0398c8db 100644 --- a/fteqtv/qw.c +++ b/fteqtv/qw.c @@ -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")) { 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) { 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")) { 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) QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message); 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")) { 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) QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message); else @@ -2681,7 +2681,7 @@ tuidemos: else message += 9; 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) { QW_SetMenu(v, MENU_NONE); @@ -2695,7 +2695,7 @@ tuidemos: { message += 6; 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) { QW_SetMenu(v, MENU_NONE); @@ -2710,7 +2710,7 @@ tuidemos: { message += 5; 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) { QW_SetMenu(v, MENU_NONE); @@ -2747,7 +2747,7 @@ tuidemos: { message += 6; 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) { QW_SetMenu(v, MENU_NONE); diff --git a/fteqtv/rcon.c b/fteqtv/rcon.c index affa693fd..76ccdc450 100644 --- a/fteqtv/rcon.c +++ b/fteqtv/rcon.c @@ -27,18 +27,22 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. FTEQTV proxy commands: (build "__DATE__")\n\ ----------------------\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\ - - connect to a server as a player (UDP)\n\ + connect to a server as a player (UDP)\n\ adddemo\n\ - - play a demo from a MVD file\n\ + play a demo from a MVD file\n\ port\n\ - - UDP port for QuakeWorld client connections\n\ + UDP port for QuakeWorld client connections\n\ mvdport\n\ - - specify TCP port for MVD broadcasting\n\ + specify TCP port for MVD broadcasting\n\ maxviewers, maxproxies\n\ -- limit number of connections\n\ -status, choke, late, talking, nobsp, reconnect, exec, password, master, hostname, record, stop, quit\n\n" + limit number of connections\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); 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) { 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); 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 "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); 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 "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); 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 "Source registered\n"; } @@ -847,6 +878,8 @@ const rconcommands_t rconcommands[] = {"port", 0, 1, Cmd_UDPPort}, {"adminpassword",0, 1, Cmd_AdminPassword}, {"rconpassword",0, 1, Cmd_AdminPassword}, + {"qtvlist", 0, 1, Cmd_QTVList}, + {"qtvdemolist", 0, 1, Cmd_QTVDemoList}, {"qtv", 0, 1, Cmd_QTVConnect}, {"addserver", 0, 1, Cmd_QTVConnect}, {"connect", 0, 1, Cmd_QTVConnect}, diff --git a/fteqtv/source.c b/fteqtv/source.c index 6e5fb17db..0aae9cd3e 100644 --- a/fteqtv/source.c +++ b/fteqtv/source.c @@ -272,80 +272,110 @@ void Net_SendQTVConnectionRequest(sv_t *qtv, char *authmethod, char *challenge) str = "QTV\n"; Net_QueueUpstream(qtv, strlen(str), str); str = "VERSION: 1\n"; Net_QueueUpstream(qtv, strlen(str), str); - at = strchrrev(qtv->server, '@'); - if (at) + if (qtv->serverquery) { - *at = '\0'; - 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 (qtv->serverquery == 2) { - 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; - } + str = "DEMOLIST\n"; Net_QueueUpstream(qtv, strlen(str), str); } 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 = "SOURCELIST\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); @@ -357,7 +387,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip) netadr_t from; 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); return false; @@ -402,7 +432,7 @@ qboolean Net_ConnectToUDPServer(sv_t *qtv, char *ip) netadr_t from; 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); 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; - 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, '@'); - if (at) + if (at && (type == SRC_DEMO || type == SRC_TCP)) + { + if (type == SRC_DEMO) + type = SRC_TCP; ip = at+1; + } 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; - if (DemoFilenameIsOkay(ip+5)) - qtv->sourcefile = fopen(ip+5, "rb"); + if (DemoFilenameIsOkay(ip)) + qtv->sourcefile = fopen(ip, "rb"); else qtv->sourcefile = NULL; 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); 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; - status = Net_ConnectToUDPServer(qtv, ip); - } - else if (!strncmp(ip, "tcp:", 4) || at!=NULL) - status = Net_ConnectToTCPServer(qtv, ip); - else - { + return Net_ConnectToUDPServer(qtv, ip); + + case SRC_TCP: + return Net_ConnectToTCPServer(qtv, ip); + + default: 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) @@ -847,7 +907,7 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl) 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); return false; @@ -1566,13 +1626,43 @@ void QTV_Run(sv_t *qtv) qtv->buffersize = 0; 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. Sys_Printf(qtv->cluster, "\nQTV server error: %s\n\n", colon); - qtv->drop = true; 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; } + 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")) { Sys_Printf(qtv->cluster, "QTV server: %s\n", colon); @@ -1592,7 +1682,14 @@ void QTV_Run(sv_t *qtv) qtv->buffersize -= length; 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. Net_SendQTVConnectionRequest(qtv, authmethod, challenge); 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; @@ -1781,6 +1878,7 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qtv->sourcesock = INVALID_SOCKET; qtv->disconnectwhennooneiswatching = autoclose; qtv->parsingconnectiondata = true; + qtv->serverquery = query; qtv->streamid = ++cluster->nextstreamid;