[qstat] update to svn382
Tom Callaway
spot at fedoraproject.org
Thu Dec 12 20:38:38 UTC 2013
commit 598650f84fbcf11b1312aa2ec9a4029a85dddf5d
Author: Tom Callaway <spot at fedoraproject.org>
Date: Thu Dec 12 15:38:43 2013 -0500
update to svn382
qstat-2.11-20131212svn382.patch |32380 +++++++++++++++++++++++++++++++++++++++
qstat.spec | 11 +-
2 files changed, 32387 insertions(+), 4 deletions(-)
---
diff --git a/qstat-2.11-20131212svn382.patch b/qstat-2.11-20131212svn382.patch
new file mode 100644
index 0000000..de54a90
--- /dev/null
+++ b/qstat-2.11-20131212svn382.patch
@@ -0,0 +1,32380 @@
+diff -urP qstat-2.11/a2s.c qstat2/a2s.c
+--- qstat-2.11/a2s.c 2006-05-28 06:48:38.000000000 -0500
++++ qstat2/a2s.c 2011-02-04 06:06:01.579727000 -0500
+@@ -12,6 +12,7 @@
+ #include <sys/types.h>
+ #ifndef _WIN32
+ #include <sys/socket.h>
++#include <arpa/inet.h>
+ #endif
+ #include <stdlib.h>
+ #include <stdio.h>
+@@ -20,13 +21,14 @@
+ #include "qstat.h"
+ #include "packet_manip.h"
+
+-#define A2S_GETCHALLENGE "\xFF\xFF\xFF\xFF\x57"
++#define A2S_GETCHALLENGE "\xFF\xFF\xFF\xFF\x57"
+ #define A2S_CHALLENGERESPONSE 0x41
+ #define A2S_INFO "\xFF\xFF\xFF\xFF\x54Source Engine Query"
+ #define A2S_INFORESPONSE_HL1 0x6D
+ #define A2S_INFORESPONSE_HL2 0x49
+ #define A2S_PLAYER "\xFF\xFF\xFF\xFF\x55"
+ #define A2S_PLAYERRESPONSE 0x44
++#define A2S_PLAYER_INVALID_CHALLENGE "\xFF\xFF\xFF\xFF\x55\xFF\xFF\xFF\xFF"
+ #define A2S_RULES "\xFF\xFF\xFF\xFF\x56"
+ #define A2S_RULESRESPONSE 0x45
+
+@@ -44,12 +46,14 @@
+ unsigned char type;
+ };
+
+-void send_a2s_request_packet(struct qserver *server)
++query_status_t send_a2s_request_packet(struct qserver *server)
+ {
+ struct a2s_status* status = (struct a2s_status*)server->master_query_tag;
+
+ if(qserver_send_initial(server, A2S_INFO, sizeof(A2S_INFO)) == -1)
+- goto error;
++ {
++ return DONE_FORCE;
++ }
+
+ status->sent_info = 1;
+ status->type = 0;
+@@ -57,28 +61,34 @@
+ if(get_server_rules || get_player_info)
+ server->next_rule = ""; // trigger calling send_a2s_rule_request_packet
+
+- return;
+-
+-error:
+- cleanup_qserver(server, 1);
++ return INPROGRESS;
+ }
+
+-void send_a2s_rule_request_packet(struct qserver *server)
++query_status_t send_a2s_rule_request_packet(struct qserver *server)
+ {
+ struct a2s_status* status = (struct a2s_status*)server->master_query_tag;
+
+ if(!get_server_rules && !get_player_info)
+ {
+- goto error;
++ return DONE_FORCE;
+ }
+
+- do
++ while( 1 )
+ {
+ if(!status->have_challenge)
+ {
+ debug(3, "sending challenge");
+- if(qserver_send_initial(server, A2S_GETCHALLENGE, sizeof(A2S_GETCHALLENGE)-1) == -1)
+- goto error;
++
++ // Challenge Request was broken so instead we use a player request with an invalid
++ // challenge which prompts the server to send a valid challenge
++ // This was fixed as of the update 2009-08-26
++ //char buf[sizeof(A2S_PLAYER)-1+4] = A2S_PLAYER;
++ //memcpy( buf + sizeof(A2S_PLAYER)-1, &status->challenge, 4 );
++ //if( SOCKET_ERROR == qserver_send_initial(server, buf, sizeof(buf)) )
++ if( SOCKET_ERROR == qserver_send_initial(server, A2S_GETCHALLENGE, sizeof(A2S_GETCHALLENGE)-1) )
++ {
++ return SOCKET_ERROR;
++ }
+ status->sent_challenge = 1;
+ break;
+ }
+@@ -87,18 +97,22 @@
+ char buf[sizeof(A2S_RULES)-1+4] = A2S_RULES;
+ memcpy(buf+sizeof(A2S_RULES)-1, &status->challenge, 4);
+ debug(3, "sending rule query");
+- if(qserver_send_initial(server, buf, sizeof(buf)) == -1)
+- goto error;
++ if( SOCKET_ERROR == qserver_send_initial(server, buf, sizeof(buf)) )
++ {
++ return SOCKET_ERROR;
++ }
+ status->sent_rules = 1;
+ break;
+ }
+ else if(get_player_info && !status->have_player)
+ {
+ char buf[sizeof(A2S_PLAYER)-1+4] = A2S_PLAYER;
+- memcpy(buf+sizeof(A2S_PLAYER)-1, &status->challenge, 4);
++ memcpy( buf + sizeof(A2S_PLAYER)-1, &status->challenge, 4 );
+ debug(3, "sending player query");
+- if(qserver_send_initial(server, buf, sizeof(buf)) == -1)
+- goto error;
++ if( SOCKET_ERROR == qserver_send_initial(server, buf, sizeof(buf)) )
++ {
++ return SOCKET_ERROR;
++ }
+ status->sent_player = 1;
+ break;
+ }
+@@ -109,15 +123,12 @@
+ status->have_challenge = 0;
+ status->have_rules = 0;
+ }
+- } while(1);
+-
+- return;
++ }
+
+-error:
+- cleanup_qserver(server, 1);
++ return INPROGRESS;
+ }
+
+-void deal_with_a2s_packet(struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_a2s_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+ struct a2s_status* status = (struct a2s_status*)server->master_query_tag;
+ char* pkt = rawpkt;
+@@ -157,14 +168,14 @@
+ pkt += 4;
+
+ // packetId
+- if ( 1 == status->type )
++ if ( 1 == status->type || 200 > server->protocol_version )
+ {
+- // HL2 format
++ // HL1 format
+ // The lower four bits represent the number of packets (2 to 15) and
+ // the upper four bits represent the current packet starting with 0
+ pkt_max = ((unsigned char)*pkt) & 15;
+ pkt_index = ((unsigned char)*pkt) >> 4;
+- debug( 3, "packetid: 0x%hhx => idx: %hhu, max: %hhu", *pkt, pkt_index, pkt_max );
++ debug( 3, "packetid[1]: 0x%hhx => idx: %hhu, max: %hhu", *pkt, pkt_index, pkt_max );
+ pkt++;
+ pktlen -= 9;
+ }
+@@ -172,20 +183,20 @@
+ {
+ // HL2 format
+ // The next two bytes are:
+- // 1. the max packets sent
+- // 2. the index of this packet starting from 0
++ // 1. the max packets sent ( byte )
++ // 2. the index of this packet starting from 0 ( byte )
++ // 3. Size of the split ( short )
+ if(pktlen < 10) goto out_too_short;
+ pkt_max = ((unsigned char)*pkt);
+ pkt_index = ((unsigned char)*(pkt+1));
+- debug( 3, "packetid: 0x%hhx => idx: %hhu, max: %hhu", *pkt, pkt_index, pkt_max );
+- pkt+=2;
+- pktlen -= 10;
++ debug( 3, "packetid[2]: 0x%hhx => idx: %hhu, max: %hhu", *pkt, pkt_index, pkt_max );
++ pkt+=4;
++ pktlen -= 12;
+ }
+ else
+ {
+ malformed_packet( server, "Unable to determine packet format" );
+- cleanup_qserver( server, 1 );
+- return;
++ return PKT_ERROR;
+ }
+
+ // pkt_max is the total number of packets expected
+@@ -210,25 +221,24 @@
+ if ( NULL == sdata->data )
+ {
+ malformed_packet(server, "Out of memory");
+- cleanup_qserver( server, 1 );
+- return;
++ return MEM_ERROR;
+ }
+ memcpy( sdata->data, pkt, sdata->datalen);
+
+ // combine_packets will call us recursively
+- combine_packets( server );
+- return;
++ return combine_packets( server );
+ }
+ else if( 0 != memcmp(pkt, "\xFF\xFF\xFF\xFF", 4) )
+ {
+ malformed_packet(server, "invalid packet header");
+- goto out_error;
++ return PKT_ERROR;
+ }
+
+ pkt += 4;
+ pktlen -= 4;
+
+ pktlen -= 1;
++ debug( 2, "A2S type = %x", *pkt );
+ switch(*pkt++)
+ {
+ case A2S_CHALLENGERESPONSE:
+@@ -434,10 +444,14 @@
+
+ if(pktlen < 9) goto out_too_short;
+
+- // pkt[0], pkt[1] steam id unused
++ // pkt[0], pkt[1] steam appid
++ server->protocol_version = (unsigned short)*pkt;
+ server->num_players = (unsigned char)pkt[2];
+ server->max_players = (unsigned char)pkt[3];
+ // pkt[4] number of bots
++ sprintf( buf, "%hhu", pkt[4] );
++ add_rule( server, "bots", buf, 0 );
++
+ add_rule(server, "dedicated", pkt[5]?"1":"0", 0);
+ if(pkt[6] == 'l')
+ {
+@@ -476,6 +490,57 @@
+ pktlen -= str-pkt+1;
+ pkt += str-pkt+1;
+
++ // EDF
++ if ( 1 <= pktlen )
++ {
++ unsigned char edf = *pkt;
++ pkt++;
++ pktlen--;
++ if ( edf & 0x80 )
++ {
++ // game port
++ unsigned short gameport;
++
++ if(pktlen < 2) goto out_too_short;
++ gameport = swap_short_from_little( pkt );
++ sprintf( buf, "%hu", gameport );
++ add_rule( server, "game_port", buf, 0 );
++ change_server_port( server, gameport, 0 );
++ pkt += 2;
++ pktlen -= 2;
++ }
++
++ if ( edf & 0x40 )
++ {
++ // spectator port
++ unsigned short spectator_port;
++ if(pktlen < 3) goto out_too_short;
++ spectator_port = swap_short_from_little( pkt );
++ sprintf( buf, "%hu", spectator_port );
++ add_rule( server, "spectator_port", buf, 0 );
++ pkt += 2;
++ pktlen -= 2;
++
++ // spectator server name
++ str = memchr(pkt, '\0', pktlen);
++ if(!str) goto out_too_short;
++ add_rule(server, "spectator_server_name", pkt, 0);
++ pktlen -= str-pkt+1;
++ pkt += str-pkt+1;
++ }
++
++ if ( edf & 0x20 )
++ {
++ // game tag
++ str = memchr(pkt, '\0', pktlen);
++ if(!str) goto out_too_short;
++ add_rule(server, "game_tag", pkt, 0);
++ pktlen -= str-pkt+1;
++ pkt += str-pkt+1;
++ }
++ }
++
++
+ status->have_info = 1;
+
+ server->retry1 = n_retries;
+@@ -553,7 +618,7 @@
+
+ if(pktlen < 8) goto out_too_short;
+
+- debug(3, "player index %d", idx);
++ debug(3, "player index %d = %s", idx, name );
+ p = add_player(server, server->n_player_info);
+ if(p)
+ {
+@@ -582,7 +647,7 @@
+
+ default:
+ malformed_packet(server, "invalid packet id %hhx", *--pkt);
+- goto out_error;
++ return PKT_ERROR;
+ }
+
+ if(
+@@ -593,14 +658,11 @@
+ server->next_rule = NULL;
+ }
+
+- cleanup_qserver(server, 0);
+- return;
++ return DONE_AUTO;
+
+ out_too_short:
+ malformed_packet(server, "packet too short");
+-
+-out_error:
+- cleanup_qserver(server, 1);
++ return PKT_ERROR;
+ }
+
+-// vim: sw=4 ts=8 noet
++// vim: sw=4 ts=4 noet
+diff -urP qstat-2.11/a2s.h qstat2/a2s.h
+--- qstat-2.11/a2s.h 2005-06-11 23:47:35.000000000 -0500
++++ qstat2/a2s.h 2009-07-02 06:11:41.032181000 -0400
+@@ -12,8 +12,8 @@
+
+ #include "qserver.h"
+
+-void send_a2s_request_packet(struct qserver *server);
+-void send_a2s_rule_request_packet(struct qserver *server);
+-void deal_with_a2s_packet(struct qserver *server, char *rawpkt, int pktlen);
++query_status_t send_a2s_request_packet(struct qserver *server);
++query_status_t send_a2s_rule_request_packet(struct qserver *server);
++query_status_t deal_with_a2s_packet(struct qserver *server, char *rawpkt, int pktlen);
+
+ #endif
+Only in qstat-2.11: aclocal.m4
+diff -urP qstat-2.11/autogen.sh qstat2/autogen.sh
+--- qstat-2.11/autogen.sh 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/autogen.sh 2008-04-24 18:15:54.223109000 -0400
+@@ -0,0 +1,72 @@
++#!/bin/sh
++
++# Global variables...
++AUTOCONF="autoconf"
++AUTOHEADER="autoheader"
++AUTOM4TE="autom4te"
++AUTOMAKE="automake"
++ACLOCAL="aclocal"
++
++# Please add higher versions first. The last version number is the minimum
++# needed to compile KDE. Do not forget to include the name/version #
++# separator if one is present, e.g. -1.2 where - is the separator.
++AUTOCONF_VERS="-2.61 261 -2.59 259 -2.58 258 -2.57 257 -2.54 254 -2.53 253"
++AUTOMAKE_VERS="-1.9 19 -1.8 18 -1.7 17 -1.6 16 -1.5 15"
++
++# We don't use variable here for remembering the type ... strings. Local
++# variables are not that portable, but we fear namespace issues with our
++# includer.
++check_autoconf()
++{
++ echo "Checking autoconf version..."
++ for ver in $AUTOCONF_VERS; do
++ if test -x "`$WHICH $AUTOCONF$ver 2>/dev/null`"; then
++ AUTOCONF="`$WHICH $AUTOCONF$ver`"
++ AUTOHEADER="`$WHICH $AUTOHEADER$ver`"
++ AUTOM4TE="`$WHICH $AUTOM4TE$ver`"
++ break
++ fi
++ done
++}
++
++check_automake()
++{
++ echo "Checking automake version..."
++ for ver in $AUTOMAKE_VERS; do
++ if test -x "`$WHICH $AUTOMAKE$ver 2>/dev/null`"; then
++ AUTOMAKE="`$WHICH $AUTOMAKE$ver`"
++ ACLOCAL="`$WHICH $ACLOCAL$ver`"
++ break
++ fi
++ done
++
++ if test -n "$UNSERMAKE"; then
++ AUTOMAKE="$UNSERMAKE"
++ fi
++}
++
++check_which()
++{
++ WHICH=""
++ for i in "type -p" "which" "type" ; do
++ T=`$i sh 2> /dev/null`
++ test -x "$T" && WHICH="$i" && break
++ done
++}
++
++check_which
++check_autoconf
++check_automake
++
++export AUTOCONF AUTOHEADER AUTOM4TE AUTOMAKE ACLOCAL
++
++set -e
++
++echo "Running aclocal..."
++$ACLOCAL
++echo "Running autoconf..."
++$AUTOCONF
++echo "Running autoheader..."
++$AUTOHEADER
++echo "Running automake..."
++$AUTOMAKE -a --foreign
+diff -urP qstat-2.11/bfbc2.c qstat2/bfbc2.c
+--- qstat-2.11/bfbc2.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/bfbc2.c 2012-05-16 09:05:27.252070000 -0400
+@@ -0,0 +1,148 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Battlefield Bad Company 2 query protocol
++ * Copyright 2009 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#else
++#include <winsock.h>
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++query_status_t send_bfbc2_request_packet( struct qserver *server )
++{
++ char buf[50];
++ int size = 0;
++ switch ( server->challenge )
++ {
++ case 0:
++ // Initial connect send serverInfo
++ size = 27;
++ memcpy( buf, "\x00\x00\x00\x00\x1b\x00\x00\x00\x01\x00\x00\x00\x0a\x00\x00\x00serverInfo\x00", size );
++ break;
++
++ case 1:
++ // All Done send quit
++ size = 21;
++ memcpy( buf, "\x01\x00\x00\x00\x15\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00quit\x00", size );
++ break;
++
++ case 2:
++ return DONE_FORCE;
++ }
++ debug( 3, "send_bfbc2_request_packet: state = %ld", server->challenge );
++
++ return send_packet( server, buf, size );
++}
++
++query_status_t deal_with_bfbc2_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *s, *end, *crlf;
++ int size, words, word = 0;
++ debug( 2, "processing..." );
++
++ if ( 17 > pktlen )
++ {
++ // Invalid packet
++ return REQ_ERROR;
++ }
++
++ rawpkt[pktlen-1] = '\0';
++ end = &rawpkt[pktlen-1];
++ s = rawpkt;
++
++ server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 );
++ server->n_requests++;
++
++ // Header Sequence
++ s += 4;
++
++ // Packet Size
++ size = *(int*)s;
++ s += 4;
++
++ // Num Words
++ words = *(int*)s;
++ s += 4;
++
++ // Words
++ while ( words > 0 && s + 5 < end )
++ {
++ // Size
++ int ws = *(int*)s;
++ s += 4;
++
++ // Content
++ debug( 6, "word: %s\n", s );
++ switch ( word )
++ {
++ case 0:
++ // Status
++ break;
++ case 1:
++ // Server Name
++ // prevent CR & LF in the server name
++ crlf = strchr( s, '\015' );
++ if ( NULL != crlf )
++ {
++ *crlf = '\0';
++ }
++ crlf = strchr( s, '\012' );
++ if ( NULL != crlf )
++ {
++ *crlf = '\0';
++ }
++ server->server_name = strdup( s );
++ break;
++ case 2:
++ // Player Count
++ server->num_players = atoi( s );
++ break;
++ case 3:
++ // Max Players
++ server->max_players = atoi( s );
++ break;
++ case 4:
++ // Game Mode
++ add_rule( server, "gametype", s, NO_FLAGS );
++ break;
++ case 5:
++ // Map
++ server->map_name = strdup( s );
++ break;
++ }
++ word++;
++
++ s += ws + 1;
++
++ words--;
++ }
++
++ server->challenge++;
++ gettimeofday( &server->packet_time1, NULL );
++
++ if ( 1 == server->challenge )
++ {
++ send_bfbc2_request_packet( server );
++ return INPROGRESS;
++ }
++
++ return DONE_FORCE;
++}
++
+diff -urP qstat-2.11/bfbc2.h qstat2/bfbc2.h
+--- qstat-2.11/bfbc2.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/bfbc2.h 2010-01-14 15:41:32.127318000 -0500
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Battlefield Bad Company 2 protocol
++ * Copyright 2005 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_BFBC2_H
++#define QSTAT_BFBC2_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_bfbc2_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_bfbc2_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/config.c qstat2/config.c
+--- qstat-2.11/config.c 2006-05-24 09:26:00.000000000 -0500
++++ qstat2/config.c 2009-12-11 19:54:11.483068000 -0500
+@@ -39,16 +39,16 @@
+ static int max_config_types;
+ static int next_gametype_id= LAST_BUILTIN_SERVER+1;
+
+-static int load_config_file( char *filename);
+-static int try_load_config_file( char *filename, int show_error);
++static int load_config_file(char const* filename);
++static int try_load_config_file(char const* filename, int const show_error);
+ static int pf_top_level( char *text, void *context);
+ static int pf_gametype_new( char *text, void *context);
+ static int pf_gametype_modify( char *text, void *context);
+
+ static int set_game_type_value( server_type *gametype, int key, char *value);
+ static int modify_game_type_value( server_type *gametype, int key, char *value);
+-static int set_packet_value( server_type *gametype, char *value,
+- char *packet_name, char **packet, int *len);
++static int set_packet_value(server_type* gametype, char const* value,
++ char const* packet_name, char** packet, int* len);
+
+ static char *first_token( char *text);
+ static char *next_token();
+@@ -60,14 +60,14 @@
+ static char *force_lower_case( char *string);
+ static char *force_upper_case( char *string);
+ static char *get_token();
+-static void * memdup( void *mem, unsigned int len);
++static void *memdup( const void *mem, unsigned int len);
+
+ int parse_config_file( FILE *file);
+
+ static int (*parse_func)( char *, void *);
+ static void *parse_context;
+ static int line;
+-static char *current_file_name;
++static char const* current_file_name;
+
+ static char *parse_text;
+ static char *parse_end;
+@@ -103,7 +103,7 @@
+ char *key_name;
+ } ConfigKey;
+
+-static ConfigKey new_keys[] = {
++static ConfigKey const new_keys[] = {
+ { CK_MASTER_PROTOCOL, "master protocol" },
+ { CK_MASTER_QUERY, "master query" },
+ { CK_MASTER_PACKET, "master packet" },
+@@ -121,7 +121,7 @@
+ { 0, NULL },
+ };
+
+-static ConfigKey modify_keys[] = {
++static ConfigKey const modify_keys[] = {
+ { CK_MASTER_PROTOCOL, "master protocol" },
+ { CK_MASTER_QUERY, "master query" },
+ { CK_MASTER_PACKET, "master packet" },
+@@ -136,7 +136,7 @@
+ } ServerFlag;
+
+ #define SERVER_FLAG(x) { #x, x }
+-ServerFlag server_flags[] = {
++ServerFlag const server_flags[] = {
+ SERVER_FLAG(TF_SINGLE_QUERY),
+ SERVER_FLAG(TF_OUTFILE),
+ SERVER_FLAG(TF_MASTER_MULTI_RESPONSE),
+@@ -156,7 +156,7 @@
+ };
+ #undef SERVER_FLAG
+
+-static int get_config_key( char *first_token, ConfigKey *keys);
++static int get_config_key( char *first_token, const ConfigKey *keys);
+ static void add_config_type( server_type *gametype);
+ static server_type * get_config_type( char *game_type);
+ static void copy_server_type( server_type *dest, server_type *source);
+@@ -239,7 +239,7 @@
+ filename= getenv( "HOME");
+ if ( filename != NULL && filename[0] != '\0') {
+ char path[1024];
+- sprintf( path, "%s/%s", filename, HOME_CONFIG_FILE);
++ snprintf( path, (sizeof(path) -1),"%s/%s", var, HOME_CONFIG_FILE);
+ }
+ /* 1. $QSTAT_CONFIG
+ 2. UNIX: $HOME/.qstatrc WIN: $HOME/qstat.cfg
+@@ -254,7 +254,7 @@
+ }
+
+ int
+-qsc_load_config_file( char *filename)
++qsc_load_config_file(char const* filename)
+ {
+ int rc= load_config_file( filename);
+ if ( rc == -2) {
+@@ -275,7 +275,7 @@
+ }
+
+ STATIC int
+-try_load_config_file( char *filename, int show_error)
++try_load_config_file(char const* filename, int const show_error)
+ {
+ int rc= load_config_file( filename);
+ if ( rc == -2 && show_error) {
+@@ -290,7 +290,7 @@
+ }
+
+ STATIC int
+-load_config_file( char *filename)
++load_config_file(char const* filename)
+ {
+ FILE *file;
+ int rc;
+@@ -522,7 +522,7 @@
+ }
+
+ STATIC int
+-get_config_key( char *first_token, ConfigKey *keys)
++get_config_key( char *first_token, const ConfigKey *keys)
+ {
+ char key_name[1024], *token;
+ int key= 0;
+@@ -729,7 +729,7 @@
+ }
+
+ STATIC int
+-set_packet_value( server_type *gametype, char *value, char *packet_name,
++set_packet_value(server_type* gametype, char const* value, char const* packet_name,
+ char **packet, int *len)
+ {
+ if ( gametype->master) {
+@@ -740,10 +740,12 @@
+ REPORT_ERROR((stderr, "Empty %s packet", packet_name));
+ return -1;
+ }
+- if ( *packet == NULL) {
+- REPORT_ERROR((stderr, "Cannot set %s packet; extend game type does not define a %s packet", packet_name, packet_name));
+- return -1;
+- }
++ // Removed as this doesn't seem to be any reason why we can't do this and it works just fine for warsow
++ //if ( *packet == NULL) {
++ // REPORT_ERROR((stderr, "Cannot set %s packet; extend game type does not define a %s packet", packet_name, packet_name));
++ // return -1;
++ //}
++
+ *packet= memdup( value, value_len);
+ *len= value_len;
+ return 0;
+@@ -881,10 +883,15 @@
+ }
+
+ STATIC void *
+-memdup( void *mem, unsigned int len)
++memdup( const void *mem, unsigned int len)
+ {
+ void *result= malloc( len);
+- memcpy( result, mem, len);
++ if ( NULL == result )
++ {
++ REPORT_ERROR(( stderr, "Failed to malloc %d bytes of memory", len ) );
++ return NULL;
++ }
++ memcpy( result, mem, len);
+ return result;
+ }
+
+Only in qstat-2.11: config.guess
+diff -urP qstat-2.11/config.h qstat2/config.h
+--- qstat-2.11/config.h 2002-11-13 21:33:53.000000000 -0500
++++ qstat2/config.h 2009-12-11 19:29:52.155859000 -0500
+@@ -11,7 +11,7 @@
+ #include "qstat.h"
+
+ int qsc_load_default_config_files();
+-int qsc_load_config_file( char *filename);
++int qsc_load_config_file(char const* filename);
+ server_type ** qsc_get_config_server_types( int *n_config_types);
+
+
+Only in qstat-2.11: config.sub
+Only in qstat-2.11: configure
+diff -urP qstat-2.11/configure.ac qstat2/configure.ac
+--- qstat-2.11/configure.ac 2006-10-28 07:36:47.000000000 -0500
++++ qstat2/configure.ac 2012-04-20 08:51:31.440424000 -0400
+@@ -1,4 +1,4 @@
+-AC_INIT([qstat],[2.11],[qstat-users at yahoogroups.com])
++AC_INIT([qstat],[2.12],[qstat-users at yahoogroups.com])
+ AC_CONFIG_SRCDIR([qstat.c])
+ AM_CONFIG_HEADER([gnuconfig.h])
+
+@@ -21,6 +21,42 @@
+ ;;
+ esac
+
++dnl Check for strnstr including broken one on MacOSX 10.4 which crashes
++dnl
++AC_CACHE_CHECK(for strnstr, ac_cv_func_strnstr,
++ AC_TRY_RUN([
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++// we expect this to succeed, or crash on over-run.
++// if it passes otherwise we may need a better check.
++int main(int argc, char **argv)
++{
++ int size = 20;
++ char *str = malloc(size);
++ memset(str, 'x', size);
++ strnstr(str, "fubar", size);
++ return 0;
++}
++ ],ac_cv_func_strnstr="yes",ac_cv_func_strnstr="no")
++)
++if test "$ac_cv_func_strnstr" = "yes" ; then
++ AC_DEFINE(HAVE_STRNSTR,1,[Working strnstr])
++else
++ AC_DEFINE(HAVE_STRNSTR,0,[No or broken strnstr])
++fi
++
++dnl check if user wants debug
++AC_MSG_CHECKING([whether to enable optimization])
++AC_ARG_ENABLE(optimize,[ --disable-optimize turn off optimization])
++if test x$enable_optimize != xno; then
++ AC_MSG_RESULT([yes])
++else
++ CPPFLAGS=""
++ CFLAGS="-ggdb"
++ AC_MSG_RESULT([no])
++fi
++
+ dnl check if user wants debug
+ AC_MSG_CHECKING([whether to enable debug output])
+ AC_ARG_ENABLE(debug,[ --disable-debug turn off debugging code])
+@@ -44,6 +80,14 @@
+ AC_MSG_RESULT([no])
+ fi
+
++AC_ARG_WITH(efence,
++[ --with-efence=<path> Use electric fence for malloc debugging.],
++ if test x$withval != xyes ; then
++ LDFLAGS="${LDFLAGS} -L$withval"
++ fi
++ AC_CHECK_LIB(efence,malloc)
++)
++
+ dnl Use -Wall if we have gcc.
+ changequote(,)dnl
+ if test "x$GCC" = "xyes"; then
+diff -urP qstat-2.11/crysis.c qstat2/crysis.c
+--- qstat-2.11/crysis.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/crysis.c 2012-04-20 08:51:31.440424000 -0400
+@@ -0,0 +1,276 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Crysis query protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#else
++#include <winsock.h>
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "utils.h"
++#include "qstat.h"
++#include "md5.h"
++#include "packet_manip.h"
++
++char *decode_crysis_val( char *val )
++{
++ // Very basic html conversion
++ val = str_replace( val, """, "\"" );
++ return str_replace( val, "&", "&" );
++}
++
++query_status_t send_crysis_request_packet( struct qserver *server )
++{
++ char cmd[256], buf[1024], *password, *md5;
++ debug( 2, "challenge: %ld", server->challenge );
++ switch ( server->challenge )
++ {
++ case 0:
++ // Not seen a challenge yet, request it
++ server->challenge++;
++ sprintf( cmd, "challenge" );
++ break;
++
++ case 1:
++ server->challenge++;
++ password = get_param_value( server, "password", "" );
++ sprintf( cmd, "%s:%s", server->challenge_string, password );
++ md5 = md5_hex( cmd, strlen( cmd ) );
++ sprintf( cmd, "authenticate %s", md5 );
++ free( md5 );
++ break;
++
++ case 2:
++ // NOTE: we currently don't support player info
++ server->challenge++;
++ server->flags |= TF_STATUS_QUERY;
++ server->n_servers = 3;
++ sprintf( cmd, "status" );
++ break;
++
++ case 3:
++ return DONE_FORCE;
++ }
++
++ server->saved_data.pkt_max = -1;
++ sprintf(buf, "POST /RPC2 HTTP/1.1\015\012Keep-Alive: 300\015\012User-Agent: qstat %s\015\012Content-Length: %d\015\012Content-Type: text/xml\015\012\015\012<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>%s</methodName><params /></methodCall>", VERSION, (int)(98 + strlen(cmd)), cmd);
++
++ return send_packet( server, buf, strlen( buf ) );
++}
++
++query_status_t valid_crysis_response( struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *s;
++ int len;
++ int cnt = packet_count( server );
++ if ( 0 == cnt && 0 != strncmp( "HTTP/1.1 200 OK", rawpkt, 15 ) )
++ {
++ // not valid response
++ return REQ_ERROR;
++ }
++
++ s = strnstr(rawpkt, "Content-Length: ", pktlen );
++ if ( NULL == s )
++ {
++ // not valid response
++ return INPROGRESS;
++ }
++ s += 16;
++ if ( 1 != sscanf( s, "%d", &len ) )
++ {
++ return INPROGRESS;
++ }
++
++ s = strnstr(rawpkt, "\015\012\015\012", pktlen );
++ if ( NULL == s )
++ {
++ return INPROGRESS;
++ }
++
++ s += 4;
++ if ( pktlen != ( s - rawpkt + len ) )
++ {
++ return INPROGRESS;
++ }
++
++ return DONE_FORCE;
++}
++
++char* crysis_response( struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *s, *e;
++ int len = pktlen;
++
++ s = strnstr(rawpkt, "<methodResponse><params><param><value><string>", len );
++ if ( NULL == s )
++ {
++ // not valid response
++ return NULL;
++ }
++ s += 46;
++ len += rawpkt - s;
++ e = strnstr(s, "</string></value>", len );
++ if ( NULL == e )
++ {
++ // not valid response
++ return NULL;
++ }
++ *e = '\0';
++
++ return strdup( s );
++}
++
++query_status_t deal_with_crysis_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *s, *val, *line;
++ query_status_t state = INPROGRESS;
++ debug( 2, "processing..." );
++
++ if ( ! server->combined )
++ {
++ state = valid_crysis_response( server, rawpkt, pktlen );
++ server->retry1 = n_retries;
++ if ( 0 == server->n_requests )
++ {
++ server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 );
++ server->n_requests++;
++ }
++
++ switch ( state )
++ {
++ case INPROGRESS:
++ {
++ // response fragment recieved
++ int pkt_id;
++ int pkt_max;
++
++ // We're expecting more to come
++ debug( 5, "fragment recieved..." );
++ pkt_id = packet_count( server );
++ pkt_max = pkt_id++;
++ if ( ! add_packet( server, 0, pkt_id, pkt_max, pktlen, rawpkt, 1 ) )
++ {
++ // fatal error e.g. out of memory
++ return MEM_ERROR;
++ }
++
++ // combine_packets will call us recursively
++ return combine_packets( server );
++ }
++ case DONE_FORCE:
++ break; // single packet response fall through
++ default:
++ return state;
++ }
++ }
++
++ if ( DONE_FORCE != state )
++ {
++ state = valid_crysis_response( server, rawpkt, pktlen );
++ switch ( state )
++ {
++ case DONE_FORCE:
++ break; // actually process
++ default:
++ return state;
++ }
++ }
++
++ debug( 3, "packet: challenge = %ld", server->challenge );
++ switch ( server->challenge )
++ {
++ case 1:
++ s = crysis_response( server, rawpkt, pktlen );
++ if ( NULL != s )
++ {
++ server->challenge_string = s;
++ return send_crysis_request_packet( server );
++ }
++ return REQ_ERROR;
++ case 2:
++ s = crysis_response( server, rawpkt, pktlen );
++ if ( NULL == s )
++ {
++ return REQ_ERROR;
++ }
++ if ( 0 != strncmp( s, "authorized", 10 ) )
++ {
++ free( s );
++ return REQ_ERROR;
++ }
++ free( s );
++ return send_crysis_request_packet( server );
++ case 3:
++ s = crysis_response( server, rawpkt, pktlen );
++ if ( NULL == s )
++ {
++ return REQ_ERROR;
++ }
++ }
++
++ // Correct ping
++ // Not quite right but gives a good estimate
++ server->ping_total = ( server->ping_total * server->n_requests ) / 2;
++
++ debug( 3, "processing response..." );
++
++ s = decode_crysis_val( s );
++ line = strtok( s, "\012" );
++
++ // NOTE: id=XXX and msg=XXX will be processed by the mod following the one they where the response of
++ while ( NULL != line )
++ {
++ debug( 4, "LINE: %s\n", line );
++ val = strstr( line, ":" );
++ if ( NULL != val )
++ {
++ *val = '\0';
++ val+=2;
++ debug( 4, "var: %s, val: %s", line, val );
++ if ( 0 == strcmp( "name", line ) )
++ {
++ server->server_name = strdup( val );
++ }
++ else if ( 0 == strcmp( "level", line ) )
++ {
++ server->map_name = strdup( val );
++ }
++ else if ( 0 == strcmp( "players", line ) )
++ {
++ if ( 2 == sscanf( val, "%d/%d", &server->num_players, &server->max_players) )
++ {
++ }
++ }
++ else if (
++ 0 == strcmp( "version", line ) ||
++ 0 == strcmp( "gamerules", line ) ||
++ 0 == strcmp( "time remaining", line )
++ )
++ {
++ add_rule( server, line, val, NO_FLAGS );
++ }
++ }
++
++ line = strtok( NULL, "\012" );
++ }
++
++ gettimeofday( &server->packet_time1, NULL );
++
++ return DONE_FORCE;
++}
++
+diff -urP qstat-2.11/crysis.h qstat2/crysis.h
+--- qstat-2.11/crysis.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/crysis.h 2012-04-20 06:22:15.250385000 -0400
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Crysis protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_CRYSIS_H
++#define QSTAT_CRYSIS_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_crysis_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_crysis_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/cube2.c qstat2/cube2.c
+--- qstat-2.11/cube2.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/cube2.c 2012-01-20 05:33:19.302785000 -0500
+@@ -0,0 +1,232 @@
++/*
++ * qstat 2.12
++ * by Steve Jankowski
++ *
++ * Cube 2 / Sauerbraten protocol
++ * Copyright 2011 NoisyB
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++struct offset
++{
++ unsigned char *d;
++ int pos;
++ int len;
++};
++
++
++//#define SB_MASTER_SERVER "http://sauerbraten.org/masterserver/retrieve.do?item=list"
++#define SB_PROTOCOL 258
++#define MIN(a,b) ((a)<(b)?(a):(b))
++#define MAX_ATTR 255
++#define MAX_STRING 1024
++
++
++static int
++getint (struct offset * d)
++{
++ int val = 0;
++
++ if ( d->pos >= d->len )
++ {
++ return 0;
++ }
++
++ val = d->d[d->pos++] & 0xff; // 8 bit value
++
++ // except...
++ if ( val == 0x80 && d->pos < d->len - 2 ) // 16 bit value
++ {
++ val = (d->d[d->pos++] & 0xff);
++ val |= (d->d[d->pos++] & 0xff) << 8;
++ }
++ else if ( val == 0x81 && d->pos < d->len - 4 ) // 32 bit value
++ {
++ val = (d->d[d->pos++] & 0xff);
++ val |= (d->d[d->pos++] & 0xff) << 8;
++ val |= (d->d[d->pos++] & 0xff) << 16;
++ val |= (d->d[d->pos++] & 0xff) << 24;
++ }
++
++ return val;
++}
++
++
++static char * getstr( char *dest, int dest_len, struct offset *d )
++{
++ int len = 0;
++
++ if (d->pos >= d->len)
++ {
++ return NULL;
++ }
++
++ len = MIN( dest_len, d->len - d->pos );
++ strncpy( dest, (const char *) d->d + d->pos, len )[len - 1] = 0;
++ d->pos += strlen (dest) + 1;
++
++ return dest;
++}
++
++
++static char* sb_getversion_s (int n)
++{
++ static char *version_s[] =
++ {
++ "Justice",
++ "CTF",
++ "Assassin",
++ "Summer",
++ "Spring",
++ "Gui",
++ "Water",
++ "Normalmap",
++ "Sp",
++ "Occlusion",
++ "Shader",
++ "Physics",
++ "Mp",
++ "",
++ "Agc",
++ "Quakecon",
++ "Independence"
++ };
++
++ n = SB_PROTOCOL - n;
++ if (n >= 0 && (size_t) n < sizeof(version_s) / sizeof(version_s[0]))
++ {
++ return version_s[n];
++ }
++ return "unknown";
++}
++
++
++static char* sb_getmode_s(int n)
++{
++ static char *mode_s[] =
++ {
++ "slowmo SP",
++ "slowmo DMSP",
++ "demo",
++ "SP",
++ "DMSP",
++ "ffa/default",
++ "coopedit",
++ "ffa/duel",
++ "teamplay",
++ "instagib",
++ "instagib team",
++ "efficiency",
++ "efficiency team",
++ "insta arena",
++ "insta clan arena",
++ "tactics arena",
++ "tactics clan arena",
++ "capture",
++ "insta capture",
++ "regen capture",
++ "assassin",
++ "insta assassin",
++ "ctf",
++ "insta ctf"
++ };
++
++ n += 6;
++ if (n >= 0 && (size_t) n < sizeof(mode_s) / sizeof(mode_s[0]))
++ {
++ return mode_s[n];
++ }
++ return "unknown";
++}
++
++
++query_status_t send_cube2_request_packet( struct qserver *server )
++{
++ return send_packet( server, server->type->status_packet, server->type->status_len );
++}
++
++
++query_status_t deal_with_cube2_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++ // skip unimplemented ack, crc, etc
++ int i;
++ int numattr;
++ int attr[MAX_ATTR];
++ char buf[MAX_STRING];
++ enum {
++ MM_OPEN = 0,
++ MM_VETO,
++ MM_LOCKED,
++ MM_PRIVATE
++ };
++ struct offset d;
++ d.d = (unsigned char *) rawpkt;
++ d.pos = 0;
++ d.len = pktlen;
++
++ server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++ getint( &d ); // we have the ping already
++ server->num_players = getint( &d );
++ numattr = getint( &d );
++ for ( i = 0; i < numattr && i < MAX_ATTR; i++ )
++ {
++ attr[i] = getint (&d);
++ }
++
++ server->protocol_version = attr[0];
++
++ sprintf( buf, "%d %s", attr[0], sb_getversion_s (attr[0]) );
++ add_rule( server, "version", buf, NO_FLAGS );
++
++ sprintf( buf, "%d %s", attr[1], sb_getmode_s (attr[1]) );
++ add_rule( server, "mode", buf, NO_FLAGS );
++
++ sprintf( buf, "%d", attr[2] );
++ add_rule( server, "seconds_left", buf, NO_FLAGS );
++
++ server->max_players = attr[3];
++
++ switch ( attr[5] )
++ {
++ case MM_OPEN:
++ sprintf( buf, "%d open", attr[5] );
++ break;
++ case MM_VETO:
++ sprintf( buf, "%d veto", attr[5] );
++ break;
++ case MM_LOCKED:
++ sprintf( buf, "%d locked", attr[5] );
++ break;
++ case MM_PRIVATE:
++ sprintf( buf, "%d private", attr[5] );
++ break;
++ default:
++ sprintf( buf, "%d unknown", attr[5] );
++ }
++ add_rule( server, "mm", buf, NO_FLAGS);
++
++ for ( i = 0; i < numattr && i < MAX_ATTR; i++ )
++ {
++ char buf2[MAX_STRING];
++ sprintf( buf, "attr%d", i );
++ sprintf( buf2, "%d", attr[i] );
++ add_rule( server, buf, buf2, NO_FLAGS );
++ }
++
++ getstr( buf, MAX_STRING, &d );
++ server->map_name = strdup(buf);
++ getstr( buf, MAX_STRING, &d );
++ server->server_name = strdup(buf);
++
++ return DONE_FORCE;
++}
++
++
+diff -urP qstat-2.11/cube2.h qstat2/cube2.h
+--- qstat-2.11/cube2.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/cube2.h 2012-01-20 05:33:19.302785000 -0500
+@@ -0,0 +1,23 @@
++/*
++ * qstat 2.12
++ * by Steve Jankowski
++ *
++ * Cube 2 / Sauerbraten protocol
++ * Copyright 2011 NoisyB
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_CUBE2_H
++#define QSTAT_CUBE2_H
++
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_cube2_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_cube2_request_packet( struct qserver *server );
++
++
++#endif // QSTAT_CUBE2_H
++
++
+diff -urP qstat-2.11/debug.c qstat2/debug.c
+--- qstat-2.11/debug.c 2006-05-24 09:26:00.000000000 -0500
++++ qstat2/debug.c 2009-07-02 06:12:13.391497000 -0400
+@@ -8,13 +8,13 @@
+ * Licensed under the Artistic License, see LICENSE.txt for license terms
+ */
+
+-#include "debug.h"
+-
+ #include <stdio.h>
+ #include <stdlib.h>
+ #ifndef _WIN32
+ #include <unistd.h>
+ #include <sys/param.h>
++#else
++#include <stdarg.h>
+ #endif
+ #include <sys/types.h>
+ #include <sys/stat.h>
+@@ -24,15 +24,26 @@
+ #include <fcntl.h>
+ #include <stdarg.h>
+
++#define QSTAT_DEBUG_C
++#include "debug.h"
++
++#ifdef ENABLE_DUMP
++#ifndef _WIN32
++ #include <sys/socket.h>
++#else
++ #include <winsock.h>
++#endif
++#endif
++
+ #ifdef DEBUG
++
+ void _debug(const char* file, int line, const char* function, int level, const char* fmt, ...)
+ {
+ va_list ap;
+ char buf[9];
+- time_t now;
++ time_t now = time( NULL );
+
+- now = time(NULL);
+- strftime(buf,9,"%T",localtime(&now));
++ strftime(buf,9,"%H:%M:%S", localtime( &now ) );
+
+ fprintf(stderr, "debug(%d) %s %s:%d %s() - ", level, buf, file, line, function);
+
+@@ -90,24 +101,42 @@
+
+ #ifdef ENABLE_DUMP
+ static unsigned count = 0;
+-#endif
+-void dump_packet(const char* buf, int buflen)
++static void _dump_packet(const char* tag, const char* buf, int buflen)
+ {
+-#ifdef ENABLE_DUMP
+ char fn[PATH_MAX] = {0};
+ int fd;
+- sprintf(fn, "dump%03u", count++);
++ sprintf(fn, "%03u_%s.pkt", count++, tag);
+ fprintf(stderr, "dumping to %s\n", fn);
+ fd = open(fn, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if(fd == -1) { perror("open"); return; }
+ if(write(fd, buf, buflen) == -1)
+ perror("write");
+ close(fd);
++}
++
++ssize_t send_dump(int s, const void *buf, size_t len, int flags)
++{
++ if(do_dump)
++ _dump_packet("send", buf, len);
++ return send(s, buf, len, flags);
++}
++#endif
++
++void dump_packet(const char* buf, int buflen)
++{
++#ifdef ENABLE_DUMP
++ _dump_packet("recv", buf, buflen);
+ #endif
+ }
+
+ void
+-print_packet( struct qserver *server, const char *buf, int buflen)
++print_packet( struct qserver *server, const char *buf, int buflen )
++{
++ output_packet( server, buf, buflen, 0 );
++}
++
++void
++output_packet( struct qserver *server, const char *buf, int buflen, int to )
+ {
+ static char *hex= "0123456789abcdef";
+ unsigned char *p= (unsigned char*)buf;
+@@ -115,27 +144,40 @@
+ char line[256];
+
+ if ( server != NULL)
+- fprintf( stderr, "FROM %s len %d\n", server->arg, buflen );
+-
+- for ( i= buflen; i ; offset+= 16) {
+- memset( line, ' ', 256);
+- h= 0;
+- h+= sprintf( line, "%5d:", offset);
+- a= astart = h + 16*2 + 16/4 + 2;
+- for ( b=16; b && i; b--, i--, p++) {
+- if ( (b & 3) == 0)
+- line[h++]= ' ';
+- line[h++]= hex[*p >> 4];
+- line[h++]= hex[*p & 0xf];
+- if ( isprint( *p))
+- line[a++]= *p;
+- else
+- line[a++]= '.';
+- if((a-astart)==8) line[a++] = ' ';
++ {
++ fprintf( stderr, "%s %s len %d\n", ( to ) ? "TO" : "FROM", server->arg, buflen );
+ }
+- line[a]= '\0';
+- fputs( line, stderr);
+- fputs( "\n", stderr);
++
++ for ( i= buflen; i ; offset+= 16)
++ {
++ memset( line, ' ', 256);
++ h= 0;
++ h+= sprintf( line, "%5d:", offset);
++ a= astart = h + 16*2 + 16/4 + 2;
++ for ( b=16; b && i; b--, i--, p++)
++ {
++ if ( (b & 3) == 0)
++ {
++ line[h++]= ' ';
++ }
++ line[h++]= hex[*p >> 4];
++ line[h++]= hex[*p & 0xf];
++ if ( isprint( *p))
++ {
++ line[a++]= *p;
++ }
++ else
++ {
++ line[a++]= '.';
++ }
++ if((a-astart)==8)
++ {
++ line[a++] = ' ';
++ }
++ }
++ line[a]= '\0';
++ fputs( line, stderr);
++ fputs( "\n", stderr);
+ }
+ fputs( "\n", stderr);
+ }
+diff -urP qstat-2.11/debug.h qstat2/debug.h
+--- qstat-2.11/debug.h 2006-05-24 09:26:00.000000000 -0500
++++ qstat2/debug.h 2009-08-14 21:52:06.457883000 -0400
+@@ -10,26 +10,50 @@
+ #ifndef QSTAT_DEBUG_H
+ #define QSTAT_DEBUG_H
+
++#include <sys/types.h>
++
+ #include "qstat.h"
+
+-// NOTE: Windows doesn't support debugging ATM
+-#ifdef _WIN32
+- #define debug 0 &&
+-#else
+- #ifdef DEBUG
+- #include <stdarg.h>
++
++#ifdef DEBUG
++ #include <stdarg.h>
++ #ifdef _WIN32
++ void _debug(const char* file, int line, const char* function, int level, const char* fmt, ...);
++ #define debug(level,fmt,...) \
++ if( level <= get_debug_level() ) \
++ _debug(__FILE__,__LINE__,__FUNCTION__,level,fmt,__VA_ARGS__)
++ #else
+ void _debug(const char* file, int line, const char* function, int level, const char* fmt, ...)
+- GCC_FORMAT_PRINTF(5, 6);
++ GCC_FORMAT_PRINTF(5, 6);
+ #define debug(level,fmt,rem...) \
+- if( level <= get_debug_level() ) \
+- _debug(__FILE__,__LINE__,__FUNCTION__,level,fmt,##rem)
+- #else
+- #define debug(...)
+- #endif
+-#endif
++ if( level <= get_debug_level() ) \
++ _debug(__FILE__,__LINE__,__FUNCTION__,level,fmt,##rem)
++ #endif // _WIN32
++#else
++ #define debug(...)
++#endif // DEBUG
+
+ void dump_packet(const char* buf, int buflen);
+
++#ifdef ENABLE_DUMP
++#ifndef _WIN32
++ #include <sys/mman.h>
++ #include <unistd.h>
++#else
++ #ifdef _MSC_VER
++ #define ssize_t SSIZE_T
++ #define uint32_t UINT32
++ #endif
++#endif
++#include <sys/types.h>
++#include <sys/stat.h>
++int do_dump;
++ssize_t send_dump(int s, const void *buf, size_t len, int flags);
++#ifndef QSTAT_DEBUG_C
++#define send(s, buf, len, flags) send_dump(s, buf, len, flags)
++#endif
++#endif
++
+ /** report a packet decoding error to stderr */
+ void malformed_packet(const struct qserver* server, const char* fmt, ...) GCC_FORMAT_PRINTF(2, 3);
+
+@@ -37,5 +61,6 @@
+ void set_debug_level (int level);
+
+ void print_packet( struct qserver *server, const char *buf, int buflen );
++void output_packet( struct qserver *server, const char *buf, int buflen, int to );
+
+ #endif
+Only in qstat-2.11: depcomp
+diff -urP qstat-2.11/doom3.c qstat2/doom3.c
+--- qstat-2.11/doom3.c 2006-07-11 19:28:49.000000000 -0500
++++ qstat2/doom3.c 2009-08-24 12:54:15.403581000 -0400
+@@ -131,7 +131,7 @@
+ return buf;
+ }
+
+-void send_doom3master_request_packet( struct qserver *server)
++query_status_t send_doom3master_request_packet( struct qserver *server)
+ {
+ int rc = 0;
+ int packet_len = -1;
+@@ -146,7 +146,7 @@
+ rc= send( server->fd, packet, packet_len, 0);
+ if ( rc == SOCKET_ERROR)
+ {
+- perror( "send");
++ return send_error( server, rc );
+ }
+
+ if ( server->retry1 == n_retries)
+@@ -160,9 +160,11 @@
+ }
+ server->retry1--;
+ server->n_packets++;
++
++ return INPROGRESS;
+ }
+
+-void send_quake4master_request_packet( struct qserver *server)
++query_status_t send_quake4master_request_packet( struct qserver *server)
+ {
+ int rc = 0;
+ int packet_len = -1;
+@@ -177,7 +179,7 @@
+ rc= send( server->fd, packet, packet_len, 0);
+ if ( rc == SOCKET_ERROR)
+ {
+- perror( "send");
++ return send_error( server, rc );
+ }
+
+ if ( server->retry1 == n_retries)
+@@ -191,17 +193,17 @@
+ }
+ server->retry1--;
+ server->n_packets++;
++
++ return INPROGRESS;
+ }
+
+ static const char doom3_masterresponse[] = "\xFF\xFFservers";
+
+-void
+-deal_with_doom3master_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_doom3master_packet( struct qserver *server, char *rawpkt, int pktlen)
+ {
+ char* pkt, *dest;
+ int len;
+- server->ping_total+= time_delta( &packet_recv_time,
+- &server->packet_time1);
++ server->ping_total+= time_delta( &packet_recv_time, &server->packet_time1);
+
+ if ( pktlen < sizeof(doom3_masterresponse) + 6 // at least one server
+ || (pktlen - sizeof(doom3_masterresponse)) % 6
+@@ -210,8 +212,7 @@
+ server->server_name= SERVERERROR;
+ server->master_pkt_len = 0;
+ malformed_packet(server, "too short or invalid response");
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+
+ server->retry1 = 0; // received at least one packet so no need to retry
+@@ -239,16 +240,23 @@
+ server->n_servers= server->master_pkt_len / 6;
+
+ debug(2, "%d servers added", server->n_servers);
++
++ return INPROGRESS;
+ }
+
+ static const char doom3_inforesponse[] = "\xFF\xFFinfoResponse";
+ static unsigned MAX_DOOM3_ASYNC_CLIENTS = 32;
++static unsigned MAX_WOLF_ASYNC_CLIENTS = 16;
+
+-static void _deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen, unsigned version )
++static query_status_t _deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen, unsigned version )
+ {
+ char *ptr = rawpkt;
+ char *end = rawpkt + pktlen;
+ int type = 0;
++ int size = 0;
++ int tail_size = 4;
++ int viewers = 0;
++ int tv = 0;
+ unsigned num_players = 0;
+ unsigned challenge = 0;
+ unsigned protocolver = 0;
+@@ -269,11 +277,18 @@
+ memcmp( doom3_inforesponse, ptr, sizeof(doom3_inforesponse)) != 0 )
+ {
+ malformed_packet(server, "too short or invalid response");
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ ptr += sizeof(doom3_inforesponse);
+
++ if ( 5 == version )
++ {
++ // TaskID
++ ptr += 4;
++ // osmask + ranked
++ tail_size++;
++ }
++
+ challenge = swap_long_from_little(ptr);
+ ptr += 4;
+
+@@ -283,20 +298,28 @@
+ snprintf(tmp, sizeof(tmp), "%u.%u", protocolver >> 16, protocolver & 0xFFFF);
+ debug(2, "challenge: 0x%08X, protocol: %s (0x%X)", challenge, tmp, protocolver);
+
++ server->protocol_version = protocolver;
++ add_rule( server, "protocol", tmp, NO_FLAGS );
++
++
++ if ( 5 == version )
++ {
++ // Size Long
++ size = swap_long_from_little(ptr);
++ debug( 2, "Size = %d", size );
++ ptr += 4;
++ }
++
+ // Commented out until we have a better way to specify the expected version
+ // This is due to prey demo requiring version 4 yet prey retail version 3
+ /*
+ if( protocolver >> 16 != version )
+ {
+ malformed_packet(server, "protocol version %u, expected %u", protocolver >> 16, version );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ */
+
+- server->protocol_version = protocolver;
+- add_rule( server, "protocol", tmp, NO_FLAGS );
+-
+ while ( ptr < end )
+ {
+ // server info:
+@@ -310,8 +333,7 @@
+ if ( !ptr )
+ {
+ malformed_packet( server, "no rule key" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ keylen = ptr - key;
+
+@@ -320,19 +342,19 @@
+ if ( !ptr )
+ {
+ malformed_packet( server, "no rule value" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ vallen = ptr - val;
+ ++ptr;
+- debug( 2, "key:%s=%s:", key, val);
+
+- if(keylen == 0 && vallen == 0)
++ if( keylen == 0 && vallen == 0 )
+ {
+ type = 1;
+ break; // end
+ }
+
++ debug( 2, "key:%s=%s:", key, val);
++
+ // Lets see what we've got
+ if ( 0 == strcasecmp( key, "si_name" ) )
+ {
+@@ -346,18 +368,46 @@
+ else if( 0 == strcasecmp( key, "si_version" ) )
+ {
+ // format:
++x
+ // DOOM 1.0.1262.win-x86 Jul 8 2004 16:46:37
+ server->protocol_version = atoi( val+1 );
+ }
+ #endif
+ else if( 0 == strcasecmp( key, "si_map" ) )
+ {
++ if ( 5 == version || 6 == version )
++ {
++ // ET:QW reports maps/<mapname>.entities
++ // so we strip that here if it exists
++ char *tmpp = strstr( val, ".entities" );
++ if ( NULL != tmpp )
++ {
++ *tmpp = '\0';
++ }
++
++ if ( 0 == strncmp( val, "maps/", 5 ) )
++ {
++ val += 5;
++ }
++ }
+ server->map_name = strdup( val );
+ }
+- else if( 0 == strcasecmp( key, "si_maxplayers" ) )
++ else if ( 0 == strcasecmp( key, "si_maxplayers" ) )
++ {
++ server->max_players = atoi( val );
++ }
++ else if ( 0 == strcasecmp( key, "ri_maxViewers" ) )
+ {
++ char max[20];
++ sprintf( max, "%d", server->max_players );
++ add_rule( server, "si_maxplayers", max, NO_FLAGS );
+ server->max_players = atoi( val );
+ }
++ else if ( 0 == strcasecmp( key, "ri_numViewers" ) )
++ {
++ viewers = atoi( val );
++ tv = 1;
++ }
+
+ add_rule( server, key, val, NO_FLAGS );
+ }
+@@ -367,95 +417,221 @@
+ // no more info should be player headers here as we
+ // requested it
+ malformed_packet( server, "player info missing" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+
+ // now each player details
+- while( ptr < end )
++ while( ptr < end - tail_size )
+ {
+ struct player *player;
+ char *val;
+ unsigned char player_id = *ptr++;
+- short prediction = 0;
++ short ping = 0;
+ unsigned rate = 0;
+
+- if(player_id == MAX_DOOM3_ASYNC_CLIENTS)
+- break;
+-
+- /* id's are not steady
+- if(player_id != num_players)
++ if( ( 6 == version && MAX_WOLF_ASYNC_CLIENTS == player_id ) || MAX_DOOM3_ASYNC_CLIENTS == player_id )
+ {
+- malformed_packet(server, "unexpected player id");
+- cleanup_qserver( server, 1);
+- return;
++ break;
+ }
+- */
++ debug( 2, "ID = %d\n", player_id );
+
+- if ( ptr + 7 > end ) // 2 pred + 4 rate + empty player name ('\0')
++ // Note: id's are not steady
++ if ( ptr + 7 > end ) // 2 ping + 4 rate + empty player name ('\0')
+ {
+ // run off the end and shouldnt have
+ malformed_packet( server, "player info too short" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
++/*
++ if ( 6 == version )
++ {
++ // Playerid is broken in wolf its always 0
++ player_id = num_players;
++ }
++*/
+
+ player = add_player( server, player_id );
++ if( ! player )
++ {
++ malformed_packet( server, "duplicate player id" );
++ return PKT_ERROR;
++ }
+
++ // doesnt support score so set a sensible default
+ player->score = 0;
+ player->frags = 0;
+
+- prediction = swap_short_from_little(ptr);
++ // Ping
++ ping = swap_short_from_little(ptr);
++ player->ping = ping;
+ ptr += 2;
+
+- player->ping = prediction; // seems to be ping
+-
+- rate = swap_long_from_little(ptr);
+- ptr += 4;
++ if ( 5 != version || 0xa0013 >= protocolver ) // No Rate in ETQW since v1.4 ( protocol v10.19 )
++ {
++ // Rate
++ rate = swap_long_from_little(ptr);
++ {
++ char buf[16];
++ snprintf(buf, sizeof(buf), "%u", rate);
++ player_add_info(player, "rate", buf, NO_FLAGS);
++ }
++ ptr += 4;
++ }
+
++ if ( 5 == version && ( ( 0xd0009 == protocolver || 0xd000a == protocolver ) && 0 != num_players ) ) // v13.9 or v13.10
+ {
+- char buf[16];
+- snprintf(buf, sizeof(buf), "%u", rate);
+- player_add_info(player, "rate", buf, NO_FLAGS);
++ // Fix the packet offset due to the single bit used for bot
++ // which realigns at the byte boundary for the player name
++ ptr++;
+ }
+
++ // Name
+ val = ptr;
+ ptr = memchr(ptr, '\0', end-ptr);
+ if ( !ptr )
+ {
+ malformed_packet( server, "player name not null terminated" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+- ++ptr;
+ player->name = strdup( val );
++ ptr++;
+
+- if( 2 == version )
++ switch( version )
+ {
++ case 2: // Quake 4
+ val = ptr;
+ ptr = memchr(ptr, '\0', end-ptr);
+ if ( !ptr )
+ {
+ malformed_packet( server, "player clan not null terminated" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+- ++ptr;
+ player->tribe_tag = strdup( val );
+- }
++ ptr++;
++ debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu, clan %s",
++ num_players, player->name, ping, rate, player_id, player->tribe_tag);
++ break;
++
++ case 5: // ETQW
++ case 6: // Wolfenstien
++ if ( 0xa0011 <= protocolver ) // clan tag since 10.17
++ {
++ // clantag position
++ ptr++;
++
++ // clantag
++ val = ptr;
++ ptr = memchr(ptr, '\0', end-ptr);
++ if ( !ptr )
++ {
++ malformed_packet( server, "player clan not null terminated" );
++ return PKT_ERROR;
++ }
++ player->tribe_tag = strdup( val );
++ ptr++;
++ }
+
+- debug( 2, "Player[%d] = %s, prediction %hu, rate %u, id %hhu, clan %s",
+- num_players, player->name, prediction, rate, player_id, player->tribe_tag);
++ // Bot flag
++ if ( 0xd0009 == protocolver || 0xd000a == protocolver ) // v13.9 or v13.10
++ {
++ // Bot flag is a single bit so need to realign everything from here on in :(
++ int i;
++ unsigned char *tp = (unsigned char*)ptr;
++ player->type_flag = (*tp)<<7;
++
++ // alignment is reset at the end
++ for( i = 0; i < 8 && tp < (unsigned char*)end; i++ )
++ {
++ *tp = (*tp)>>1 | *(tp+1)<<7;
++ tp++;
++ }
++ }
++ else
++ {
++ player->type_flag = *ptr++;
++ }
++
++ if ( 0xa0011 <= protocolver ) // clan tag since 10.17
++ {
++ debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu, bot %hhu, clan %s",
++ num_players, player->name, ping, rate, player_id, player->type_flag, player->tribe_tag);
++ }
++ else
++ {
++ debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu, bot %hhu",
++ num_players, player->name, ping, rate, player_id, player->type_flag );
++ }
++ break;
++
++ default:
++ debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu", num_players, player->name, ping, rate, player_id );
++ }
+
+ ++num_players;
+ }
+
+- if(end - ptr >= 4)
++ if( end - ptr >= 4 )
+ {
++ // OS Mask
+ snprintf(tmp, sizeof(tmp), "0x%X", swap_long_from_little(ptr));
+ add_rule( server, "osmask", tmp, NO_FLAGS );
+ debug( 2, "osmask %s", tmp);
+ ptr += 4;
++
++ if ( 5 == version && end - ptr >= 1 )
++ {
++ // Ranked flag
++ snprintf( tmp, sizeof(tmp), "%hhu", *ptr++ );
++ add_rule( server, "ranked", tmp, NO_FLAGS );
++
++ if ( end - ptr >= 5 )
++ {
++ // Time Left
++ snprintf( tmp, sizeof(tmp), "%d", swap_long_from_little( ptr ) );
++ add_rule( server, "timeleft", tmp, NO_FLAGS );
++ ptr += 4;
++
++ // Game State
++ snprintf( tmp, sizeof(tmp), "%hhu", *ptr++ );
++ add_rule( server, "gamestate", tmp, NO_FLAGS );
++
++ if ( end - ptr >= 1 )
++ {
++ // Server Type
++ unsigned char servertype = *ptr++;
++ snprintf( tmp, sizeof(tmp), "%hhu", servertype );
++ add_rule( server, "servertype", tmp, NO_FLAGS );
++
++ switch ( servertype )
++ {
++ case 0: // Regular Server
++ // Interested Clients
++ snprintf( tmp, sizeof(tmp), "%hhu", *ptr++ );
++ add_rule( server, "interested_clients", tmp, NO_FLAGS );
++ break;
++
++ case 1: // TV Server
++ // Connected Clients
++ snprintf( tmp, sizeof(tmp), "%d", swap_long_from_little( ptr ) );
++ ptr+=4;
++ add_rule( server, "interested_clients", tmp, NO_FLAGS );
++
++ // Max Clients
++ snprintf( tmp, sizeof(tmp), "%d", swap_long_from_little( ptr ) );
++ ptr+=4;
++ add_rule( server, "interested_clients", tmp, NO_FLAGS );
++ break;
++
++ default:
++ // Unknown
++ if ( show_errors )
++ {
++ fprintf( stderr, "Unknown server type %d\n", servertype );
++ }
++ }
++ }
++ }
++ }
+ }
+ else
+ {
+@@ -469,28 +645,45 @@
+ }
+ #endif
+
+- server->num_players = num_players;
++ if ( 0 == tv )
++ {
++ debug( 2, "Num players = %d", num_players );
++ server->num_players = num_players;
++ }
++ else
++ {
++ server->num_players = viewers;
++ }
++
++ return DONE_FORCE;
++}
+
+- cleanup_qserver( server, 1 );
+- return;
++query_status_t deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen)
++{
++ return _deal_with_doom3_packet( server, rawpkt, pktlen, 1 );
++}
++
++query_status_t deal_with_quake4_packet( struct qserver *server, char *rawpkt, int pktlen)
++{
++ return _deal_with_doom3_packet( server, rawpkt, pktlen, 2 );
+ }
+
+-void deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_prey_demo_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+- _deal_with_doom3_packet( server, rawpkt, pktlen, 1 );
++ return _deal_with_doom3_packet( server, rawpkt, pktlen, 4 );
+ }
+
+-void deal_with_quake4_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_prey_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+- _deal_with_doom3_packet( server, rawpkt, pktlen, 2 );
++ return _deal_with_doom3_packet( server, rawpkt, pktlen, 3 );
+ }
+
+-void deal_with_prey_demo_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_etqw_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+- _deal_with_doom3_packet( server, rawpkt, pktlen, 4 );
++ return _deal_with_doom3_packet( server, rawpkt, pktlen, 5 );
+ }
+
+-void deal_with_prey_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_wolf_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+- _deal_with_doom3_packet( server, rawpkt, pktlen, 3 );
++ return _deal_with_doom3_packet( server, rawpkt, pktlen, 6 );
+ }
+diff -urP qstat-2.11/doom3.h qstat2/doom3.h
+--- qstat-2.11/doom3.h 2006-06-22 06:13:51.000000000 -0500
++++ qstat2/doom3.h 2009-08-14 21:22:30.738248000 -0400
+@@ -10,8 +10,6 @@
+ #ifndef QSTAT_DOOM3_H
+ #define QSTAT_DOOM3_H
+
+-#include "qstat.h"
+-
+ #define DOOM3_DEFAULT_PORT 27666
+ #define DOOM3_MASTER_DEFAULT_PORT 27650
+
+@@ -21,12 +19,22 @@
+ #define PREY_DEFAULT_PORT 27719
+ #define PREY_MASTER_DEFAULT_PORT 27655
+
+-void send_doom3master_request_packet( struct qserver *server);
+-void deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen);
++#define ETQW_DEFAULT_PORT 27733
++
++#define WOLF_DEFAULT_PORT 27758
++
++query_status_t send_doom3master_request_packet( struct qserver *server);
++query_status_t deal_with_doom3master_packet( struct qserver *server, char *rawpkt, int pktlen);
++
++query_status_t deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen);
++
++query_status_t send_quake4master_request_packet( struct qserver *server);
++query_status_t deal_with_quake4_packet( struct qserver *server, char *rawpkt, int pktlen);
++
++query_status_t deal_with_prey_packet( struct qserver *server, char *rawpkt, int pktlen);
+
+-void send_quake4master_request_packet( struct qserver *server);
+-void deal_with_quake4_packet( struct qserver *server, char *rawpkt, int pktlen);
++query_status_t deal_with_etqw_packet( struct qserver *server, char *rawpkt, int pktlen);
+
+-void deal_with_prey_packet( struct qserver *server, char *rawpkt, int pktlen);
++query_status_t deal_with_wolf_packet( struct qserver *server, char *rawpkt, int pktlen);
+
+ #endif
+diff -urP qstat-2.11/fl.c qstat2/fl.c
+--- qstat-2.11/fl.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/fl.c 2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,512 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Frontlines-Fuel of War protocol
++ * Copyright 2008 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#else
++#include <winsock.h>
++#endif
++
++#include <stdlib.h>
++#include <stdio.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++#define FL_GETCHALLENGE "\xFF\xFF\xFF\xFF\x57"
++#define FL_CHALLENGERESPONSE 0x41
++#define FL_INFO "\xFF\xFF\xFF\xFF\x46LSQ"
++#define FL_INFORESPONSE 0x49
++#define FL_PLAYER "\xFF\xFF\xFF\xFF\x55"
++#define FL_PLAYERRESPONSE 0x44
++#define FL_RULES "\xFF\xFF\xFF\xFF\x56"
++#define FL_RULESRESPONSE 0x45
++
++struct fl_status
++{
++ unsigned sent_challenge : 1;
++ unsigned have_challenge : 1;
++ unsigned sent_info : 1;
++ unsigned have_info : 1;
++ unsigned sent_player : 1;
++ unsigned have_player : 1;
++ unsigned sent_rules : 1;
++ unsigned have_rules : 1;
++ unsigned challenge;
++ unsigned char type;
++};
++
++query_status_t send_fl_request_packet(struct qserver *server)
++{
++ struct fl_status* status = (struct fl_status*)server->master_query_tag;
++
++ if( SOCKET_ERROR == qserver_send_initial(server, FL_INFO, sizeof(FL_INFO) - 1) )
++ {
++ return SOCKET_ERROR;
++ }
++
++ status->sent_info = 1;
++ status->type = 0;
++
++ return INPROGRESS;
++}
++
++query_status_t send_fl_rule_request_packet(struct qserver *server)
++{
++ struct fl_status* status = (struct fl_status*)server->master_query_tag;
++
++ if ( 1 >= status->type )
++ {
++ // Not supported
++ return PKT_ERROR;
++ }
++
++ if(!get_server_rules && !get_player_info)
++ {
++ return DONE_FORCE;
++ }
++
++ do
++ {
++ if(!status->have_challenge)
++ {
++ debug(3, "sending challenge");
++ if( SOCKET_ERROR == qserver_send_initial(server, FL_GETCHALLENGE, sizeof(FL_GETCHALLENGE)-1) )
++ {
++ return SOCKET_ERROR;
++ }
++ status->sent_challenge = 1;
++ break;
++ }
++ else if(get_server_rules && !status->have_rules)
++ {
++ char buf[sizeof(FL_RULES)-1+4] = FL_RULES;
++ memcpy(buf+sizeof(FL_RULES)-1, &status->challenge, 4);
++ debug(3, "sending rule query");
++ if( SOCKET_ERROR == qserver_send_initial(server, buf, sizeof(buf)) )
++ {
++ return SOCKET_ERROR;
++ }
++ status->sent_rules = 1;
++ break;
++ }
++ else if(get_player_info && !status->have_player)
++ {
++ char buf[sizeof(FL_PLAYER)-1+4] = FL_PLAYER;
++ memcpy(buf+sizeof(FL_PLAYER)-1, &status->challenge, 4);
++ debug(3, "sending player query");
++ if( SOCKET_ERROR == qserver_send_initial(server, buf, sizeof(buf)) )
++ {
++ return SOCKET_ERROR;
++ }
++ status->sent_player = 1;
++ break;
++ }
++ else
++ {
++ debug(3, "timeout");
++ // we are probably called due to timeout, restart.
++ status->have_challenge = 0;
++ status->have_rules = 0;
++ }
++ } while(1);
++
++ return INPROGRESS;
++}
++
++query_status_t deal_with_fl_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++ struct fl_status* status = (struct fl_status*)server->master_query_tag;
++ char* pkt = rawpkt;
++ char buf[16];
++ char* str;
++ unsigned cnt;
++ unsigned short tmp_short;
++
++ if(server->server_name == NULL)
++ {
++ server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
++ server->n_requests++;
++ }
++
++ if(pktlen < 5) goto out_too_short;
++
++ if( 0 == memcmp(pkt, "\xFF\xFF\xFF\xFE", 4) )
++ {
++ // fragmented packet
++ unsigned char pkt_index, pkt_max;
++ unsigned int pkt_id;
++ SavedData *sdata;
++
++ if(pktlen < 9) goto out_too_short;
++
++ // format:
++ // int Header
++ // int RequestId
++ // byte PacketNumber
++ // byte NumPackets
++ // Short SizeOfPacketSplits
++
++ // Header
++ pkt += 4;
++
++ // RequestId
++ pkt_id = ntohl( *(long *)pkt );
++ debug( 3, "RequestID: %d", pkt_id );
++ pkt += 4;
++
++ // The next two bytes are:
++ // 1. the max packets sent ( byte )
++ // 2. the index of this packet starting from 0 ( byte )
++ // 3. Size of the split ( short )
++ if(pktlen < 10) goto out_too_short;
++
++ // PacketNumber
++ pkt_index = ((unsigned char)*pkt);
++
++ // NumPackates
++ pkt_max = ((unsigned char)*(pkt+1));
++
++ // SizeOfPacketSplits
++ debug( 3, "packetid[2]: 0x%hhx => idx: %hhu, max: %hhu", *pkt, pkt_index, pkt_max );
++ pkt+=4;
++ pktlen -= 12;
++
++ // pkt_max is the total number of packets expected
++ // pkt_index is a bit mask of the packets received.
++ if ( server->saved_data.data == NULL )
++ {
++ sdata = &server->saved_data;
++ }
++ else
++ {
++ sdata = (SavedData*) calloc( 1, sizeof(SavedData));
++ sdata->next = server->saved_data.next;
++ server->saved_data.next = sdata;
++ }
++
++ sdata->pkt_index = pkt_index;
++ sdata->pkt_max = pkt_max;
++ sdata->pkt_id = pkt_id;
++ sdata->datalen = pktlen;
++ sdata->data= (char*)malloc( pktlen );
++ if ( NULL == sdata->data )
++ {
++ malformed_packet(server, "Out of memory");
++ return MEM_ERROR;
++ }
++
++ memcpy( sdata->data, pkt, sdata->datalen );
++
++ // combine_packets will call us recursively
++ return combine_packets( server );
++ }
++ else if ( 0 != memcmp(pkt, "\xFF\xFF\xFF\xFF", 4) )
++ {
++ malformed_packet(server, "invalid packet header");
++ return PKT_ERROR;
++ }
++
++ pkt += 4;
++ pktlen -= 4;
++
++ pktlen -= 1;
++ debug( 2, "FL type = 0x%x", *pkt );
++ switch(*pkt++)
++ {
++ case FL_CHALLENGERESPONSE:
++ if(pktlen < 4) goto out_too_short;
++ memcpy(&status->challenge, pkt, 4);
++ // do not count challenge as retry
++ if(!status->have_challenge && server->retry1 != n_retries)
++ {
++ ++server->retry1;
++ if(server->n_retries)
++ {
++ --server->n_retries;
++ }
++ }
++ status->have_challenge = 1;
++ debug(3, "challenge %x", status->challenge);
++ break;
++
++ case FL_INFORESPONSE:
++ if(pktlen < 1) goto out_too_short;
++ status->type = *pkt;
++ if ( *pkt > 1 && ( get_server_rules || get_player_info ) )
++ {
++ server->next_rule = ""; // trigger calling send_fl_rule_request_packet
++ }
++ snprintf(buf, sizeof(buf), "%hhX", *pkt);
++ add_rule(server, "protocol", buf, 0);
++ pktlen--;
++ pkt++;
++
++ // ServerName
++ str = memchr(pkt, '\0', pktlen);
++ if(!str) goto out_too_short;
++ server->server_name = strdup(pkt);
++ pktlen -= str-pkt+1;
++ pkt += str-pkt+1;
++
++ // MapName
++ str = memchr(pkt, '\0', pktlen);
++ if(!str) goto out_too_short;
++ server->map_name = strdup(pkt);
++ pktlen -= str-pkt+1;
++ pkt += str-pkt+1;
++
++ // ModName
++ str = memchr(pkt, '\0', pktlen);
++ if(!str) goto out_too_short;
++ server->game = strdup(pkt);
++ add_rule(server, "modname", pkt, 0);
++ pktlen -= str-pkt+1;
++ pkt += str-pkt+1;
++
++ // GameMode
++ str = memchr(pkt, '\0', pktlen);
++ if(!str) goto out_too_short;
++ add_rule(server, "gamemode", pkt, 0);
++ pktlen -= str-pkt+1;
++ pkt += str-pkt+1;
++
++ // GameDescription
++ str = memchr(pkt, '\0', pktlen);
++ if(!str) goto out_too_short;
++ add_rule(server, "gamedescription", pkt, 0);
++ pktlen -= str-pkt+1;
++ pkt += str-pkt+1;
++
++ // GameVersion
++ str = memchr(pkt, '\0', pktlen);
++ if(!str) goto out_too_short;
++ add_rule(server, "gameversion", pkt, 0);
++ pktlen -= str-pkt+1;
++ pkt += str-pkt+1;
++
++ if( pktlen < 13 )
++ {
++ goto out_too_short;
++ }
++
++ // GamePort
++ tmp_short = ((unsigned short)pkt[0] <<8 ) | ((unsigned short)pkt[1]);
++ change_server_port( server, tmp_short, 0 );
++ pkt += 2;
++
++ // Num Players
++ server->num_players = (unsigned char)*pkt++;
++
++ // Max Players
++ server->max_players = (unsigned char)*pkt++;
++
++ // Dedicated
++ add_rule(server, "dedicated", ( 'd' == *pkt++) ? "1" : "0", 0);
++
++ // OS
++ switch( *pkt )
++ {
++ case 'l':
++ add_rule(server, "sv_os", "linux", 0);
++ break;
++
++ case 'w':
++ add_rule(server, "sv_os", "windows", 0);
++ break;
++
++ default:
++ buf[0] = *pkt;
++ buf[1] = '\0';
++ add_rule(server, "sv_os", buf, 0);
++ break;
++ }
++ pkt++;
++
++ // Passworded
++ add_rule(server, "passworded", ( *pkt++ ) ? "1" : "0" , 0);
++
++ // Anticheat
++ add_rule(server, "passworded", ( *pkt++ ) ? "1" : "0" , 0);
++
++ // FrameTime
++ sprintf( buf, "%hhu", *pkt++ );
++ add_rule(server, "frametime", buf , 0);
++
++ // Round
++ sprintf( buf, "%hhu", *pkt++ );
++ add_rule(server, "round", buf , 0);
++
++ // RoundMax
++ sprintf( buf, "%hhu", *pkt++ );
++ add_rule(server, "roundmax", buf , 0);
++
++ // RoundSeconds
++ tmp_short = ((unsigned short)pkt[0] <<8 ) | ((unsigned short)pkt[1]);
++ sprintf( buf, "%hhu", tmp_short );
++ add_rule(server, "roundseconds", buf , 0);
++ pkt += 2;
++
++ status->have_info = 1;
++
++ server->retry1 = n_retries;
++
++ server->next_player_info = server->num_players;
++
++ break;
++
++ case FL_RULESRESPONSE:
++ if(pktlen < 2) goto out_too_short;
++
++ cnt = ((unsigned char)pkt[0] << 8 ) + ((unsigned char)pkt[1]);
++ pktlen -= 2;
++ pkt += 2;
++
++ debug(3, "num_rules: %d", cnt);
++
++ for(;cnt && pktlen > 0; --cnt)
++ {
++ char* key, *value;
++ str = memchr(pkt, '\0', pktlen);
++ if(!str) break;
++ key = pkt;
++ pktlen -= str-pkt+1;
++ pkt += str-pkt+1;
++
++ str = memchr(pkt, '\0', pktlen);
++ if(!str) break;
++ value = pkt;
++ pktlen -= str-pkt+1;
++ pkt += str-pkt+1;
++
++ add_rule(server, key, value, NO_FLAGS);
++ }
++
++ if(cnt)
++ {
++ malformed_packet(server, "packet contains too few rules, missing %d", cnt);
++ server->missing_rules = 1;
++ }
++ if(pktlen)
++ malformed_packet(server, "garbage at end of rules, %d bytes left", pktlen);
++
++ status->have_rules = 1;
++
++ server->retry1 = n_retries;
++
++ break;
++
++ case FL_PLAYERRESPONSE:
++
++ if(pktlen < 1) goto out_too_short;
++
++ cnt = (unsigned char)pkt[0];
++ pktlen -= 1;
++ pkt += 1;
++
++ debug(3, "num_players: %d", cnt);
++
++ for(;cnt && pktlen > 0; --cnt)
++ {
++ unsigned idx;
++ const char* name;
++ struct player* p;
++
++ // Index
++ idx = *pkt++;
++ --pktlen;
++
++ // PlayerName
++ str = memchr(pkt, '\0', pktlen);
++ if(!str) break;
++ name = pkt;
++ pktlen -= str-pkt+1;
++ pkt += str-pkt+1;
++
++ if(pktlen < 8) goto out_too_short;
++
++ debug(3, "player index %d", idx);
++ p = add_player(server, server->n_player_info);
++ if(p)
++ {
++ union
++ {
++ int i;
++ float fl;
++ } temp;
++
++ p->name = strdup(name);
++
++ // Score
++ p->frags = ntohl( *(unsigned int *)pkt );
++
++ // TimeConnected
++ temp.i = ntohl( *(unsigned int *)(pkt+4) );
++ p->connect_time = temp.fl;
++
++ // Ping
++ p->ping = 0;
++ p->ping = ntohs( *(unsigned int *)(pkt+8) );
++ //((unsigned char*)&p->ping)[0] = pkt[9];
++ //((unsigned char*)&p->ping)[1] = pkt[8];
++
++ // ProfileId
++ //p->profileid = ntohl( *(unsigned int *)pkt+10 );
++
++ // Team
++ p->team = *(pkt+14);
++//fprintf( stderr, "Player: '%s', Frags: %u, Time: %u, Ping: %hu, Team: %d\n", p->name, p->frags, p->connect_time, p->ping, p->team );
++ }
++ pktlen -= 15;
++ pkt += 15;
++ }
++
++#if 0 // seems to be a rather normal condition
++ if(cnt)
++ {
++ malformed_packet(server, "packet contains too few players, missing %d", cnt);
++ }
++#endif
++ if(pktlen)
++ malformed_packet(server, "garbage at end of player info, %d bytes left", pktlen);
++
++ status->have_player = 1;
++
++ server->retry1 = n_retries;
++
++ break;
++
++ default:
++ malformed_packet(server, "invalid packet id %hhx", *--pkt);
++ return PKT_ERROR;
++ }
++
++ if(
++ (!get_player_info || (get_player_info && status->have_player)) &&
++ (!get_server_rules || (get_server_rules && status->have_rules))
++ )
++ {
++ server->next_rule = NULL;
++ }
++
++ return DONE_AUTO;
++
++out_too_short:
++ malformed_packet(server, "packet too short");
++
++ return PKT_ERROR;
++}
++
++// vim: sw=4 ts=4 noet
+diff -urP qstat-2.11/fl.h qstat2/fl.h
+--- qstat-2.11/fl.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/fl.h 2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,19 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Frontlines-Fuel of War protocol
++ * Copyright 2008 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_FL_H
++#define QSTAT_FL_H
++
++#include "qserver.h"
++
++query_status_t send_fl_request_packet(struct qserver *server);
++query_status_t send_fl_rule_request_packet(struct qserver *server);
++query_status_t deal_with_fl_packet(struct qserver *server, char *rawpkt, int pktlen);
++
++#endif
+diff -urP qstat-2.11/gitmktar qstat2/gitmktar
+--- qstat-2.11/gitmktar 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/gitmktar 2008-02-04 07:27:02.608985000 -0500
+@@ -0,0 +1,11 @@
++#!/bin/sh
++
++# make snapshot tar archive from git-svn repo
++#
++NAME=qstat
++VERSION=2.11
++LAST_COMMIT=(`git-rev-list --timestamp HEAD^..HEAD`)
++DATE=`date +%Y%m%d%H%M -d "1970-01-01 00:00 UTC $LAST_COMMIT seconds"`
++fn=$NAME-${VERSION}_$DATE.tar.bz2
++git archive --prefix=$NAME-${VERSION}_$DATE/ HEAD | bzip2 > $fn
++echo "version ${VERSION}_$DATE -> $fn"
+Only in qstat-2.11: gnuconfig.h.in
+diff -urP qstat-2.11/gps.c qstat2/gps.c
+--- qstat-2.11/gps.c 2006-06-09 10:30:11.000000000 -0500
++++ qstat2/gps.c 2009-07-02 06:11:41.032181000 -0400
+@@ -59,13 +59,13 @@
+ return 0;
+ }
+
+-void send_gps_request_packet( struct qserver *server )
++query_status_t send_gps_request_packet( struct qserver *server )
+ {
+- send_packet( server, server->type->status_packet, server->type->status_len );
++ return send_packet( server, server->type->status_packet, server->type->status_len );
+ }
+
+
+-void deal_with_gps_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_gps_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+ char *s, *key, *value, *end;
+ struct player *player = NULL;
+@@ -465,6 +465,8 @@
+ ( server->num_players < 0 && id_minor >= 3)
+ )
+ {
+- cleanup_qserver( server, 1);
++ return DONE_FORCE;
+ }
++
++ return INPROGRESS;
+ }
+diff -urP qstat-2.11/gps.h qstat2/gps.h
+--- qstat-2.11/gps.h 2005-06-20 07:43:46.000000000 -0500
++++ qstat2/gps.h 2009-07-02 06:11:41.032181000 -0400
+@@ -13,8 +13,8 @@
+ #include "qserver.h"
+
+ // Packet processing methods
+-void deal_with_gps_packet( struct qserver *server, char *pkt, int pktlen );
+-void send_gps_request_packet( struct qserver *server );
++query_status_t deal_with_gps_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_gps_request_packet( struct qserver *server );
+
+ #endif
+
+diff -urP qstat-2.11/gs2.c qstat2/gs2.c
+--- qstat-2.11/gs2.c 2006-08-24 13:52:25.000000000 -0500
++++ qstat2/gs2.c 2009-07-02 06:11:41.032181000 -0400
+@@ -20,7 +20,7 @@
+ #include "qstat.h"
+ #include "packet_manip.h"
+
+-void send_gs2_request_packet( struct qserver *server )
++query_status_t send_gs2_request_packet( struct qserver *server )
+ {
+ // The below should work but seems to make no difference to what some
+ // servers send
+@@ -35,13 +35,13 @@
+ server->type->status_packet[9] = 0x00;
+ }
+
+- send_packet( server, server->type->status_packet, server->type->status_len );
++ return send_packet( server, server->type->status_packet, server->type->status_len );
+ }
+
+
+ // See the following for protocol details:
+ // http://dev.kquery.com/index.php?article=42
+-void deal_with_gs2_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_gs2_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+ char *ptr = rawpkt;
+ char *end = rawpkt + pktlen;
+@@ -57,8 +57,7 @@
+ if ( pktlen < 15 )
+ {
+ // invalid packet?
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+
+ server->n_servers++;
+@@ -95,8 +94,7 @@
+ {
+ malformed_packet( server, "no player headers" );
+ }
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ ptr += var_len + 1;
+
+@@ -125,6 +123,11 @@
+ {
+ server->map_name = strdup( val );
+ }
++ else if( 0 == strcmp( var, "map" ) )
++ {
++ // For BF2MC compatibility
++ server->map_name = strdup( val );
++ }
+ else if( 0 == strcmp( var, "maxplayers" ) )
+ {
+ server->max_players = atoi( val );
+@@ -154,8 +157,7 @@
+ // no more info should be player headers here as we
+ // requested it
+ malformed_packet( server, "no player headers" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+
+ // player info header
+@@ -169,8 +171,7 @@
+ if ( ptr >= end )
+ {
+ malformed_packet( server, "no player headers" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+
+ while ( 1 == type && ptr < end )
+@@ -188,8 +189,7 @@
+ {
+ free( headers );
+ }
+- cleanup_qserver( server, 1);
+- return;
++ return MEM_ERROR;
+ }
+
+ headers = tmpp;
+@@ -211,8 +211,7 @@
+ // no more info should be player info here as we
+ // requested it
+ malformed_packet( server, "no players" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+
+ while( 2 == type && ptr < end )
+@@ -225,8 +224,7 @@
+ if ( 0 != no_players )
+ {
+ malformed_packet( server, "no players" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ }
+ else
+@@ -242,8 +240,7 @@
+ if ( ptr >= end )
+ {
+ malformed_packet( server, "short player detail" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ val = ptr;
+ val_len = strlen( val );
+@@ -292,8 +289,7 @@
+ if ( total_players > no_players )
+ {
+ malformed_packet( server, "to many players %d > %d", total_players, no_players );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+
+ // check for end of player info
+@@ -302,8 +298,7 @@
+ if ( total_players != no_players )
+ {
+ malformed_packet( server, "bad number of players %d != %d", total_players, no_players );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ type = 3;
+ ptr++;
+@@ -315,8 +310,7 @@
+ // no more info should be team info here as we
+ // requested it
+ malformed_packet( server, "no teams" );
+- cleanup_qserver( server, 1 );
+- return;
++ return PKT_ERROR;
+ }
+
+ no_teams = (unsigned char)*ptr;
+@@ -340,8 +334,7 @@
+ {
+ free( headers );
+ }
+- cleanup_qserver( server, 1);
+- return;
++ return MEM_ERROR;
+ }
+
+ headers = tmpp;
+@@ -362,8 +355,7 @@
+ // no more info should be team info here as we
+ // requested it
+ malformed_packet( server, "no teams" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+
+ while( 4 == type && ptr < end )
+@@ -378,8 +370,7 @@
+ if ( ptr >= end )
+ {
+ malformed_packet( server, "short team detail" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ val = ptr;
+ val_len = strlen( val );
+@@ -398,11 +389,9 @@
+ if ( total_teams > no_teams )
+ {
+ malformed_packet( server, "to many teams" );
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ }
+
+- cleanup_qserver( server, 1);
+- return;
++ return DONE_FORCE;
+ }
+diff -urP qstat-2.11/gs2.h qstat2/gs2.h
+--- qstat-2.11/gs2.h 2005-06-12 07:41:52.000000000 -0500
++++ qstat2/gs2.h 2009-07-02 06:11:41.032181000 -0400
+@@ -13,8 +13,8 @@
+ #include "qserver.h"
+
+ // Packet processing methods
+-void deal_with_gs2_packet( struct qserver *server, char *pkt, int pktlen );
+-void send_gs2_request_packet( struct qserver *server );
++query_status_t deal_with_gs2_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_gs2_request_packet( struct qserver *server );
+
+ #endif
+
+diff -urP qstat-2.11/gs3.c qstat2/gs3.c
+--- qstat-2.11/gs3.c 2006-08-15 14:38:38.000000000 -0500
++++ qstat2/gs3.c 2012-01-20 05:31:58.410876000 -0500
+@@ -51,7 +51,7 @@
+ // query: [0xFE][0xFD][0x00][0x.. 4-byte-instance][0xb3412b5a "-1287574694"]
+ //
+
+-void deal_with_gs3_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_gs3_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+ char *ptr = rawpkt;
+ unsigned int pkt_id;
+@@ -59,14 +59,48 @@
+ unsigned char flag;
+ unsigned int pkti, final;
+
+- debug( 2, "packet..." );
++ debug( 2, "packet n_requests %d, retry1 %d, n_retries %d, delta %d", server->n_requests, server->retry1, n_retries, time_delta( &packet_recv_time, &server->packet_time1 ) );
++ server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++
++ if ( 7 == pktlen && 0x09 == *ptr )
++ {
++ // gs4 query sent to a gs3 server
++ // switch protocols and try again
++
++ // Correct the stats due to two phase protocol
++ int i;
++ server_type *gs3_type = &builtin_types[50];
++ debug( 3, "Attempting gs3 fallback from gs4" );
++ server->retry1++;
++ if ( GAMESPY3_PROTOCOL_SERVER != gs3_type->id )
++ {
++ // Static coding failure do it the hard way
++ debug( 1, "gs3 static lookup failure, using dynamic lookup" );
++ for( i = 0; i < GAMESPY3_PROTOCOL_SERVER; i++ )
++ {
++ if ( GAMESPY3_PROTOCOL_SERVER == builtin_types[i].id )
++ {
++ // found it
++ gs3_type = &builtin_types[i];
++ i = GAMESPY3_PROTOCOL_SERVER;
++ }
++ }
++
++ if ( GAMESPY3_PROTOCOL_SERVER != gs3_type->id )
++ {
++ malformed_packet( server, "GS3 protocol not found" );
++ return PKT_ERROR;
++ }
++ }
++ server->type = gs3_type;
++ return send_gs3_request_packet( server );
++ }
+
+ if ( pktlen < 12 )
+ {
+ // invalid packet?
+ malformed_packet( server, "too short" );
+- cleanup_qserver( server, 1 );
+- return;
++ return PKT_ERROR;
+ }
+
+ if ( 0x09 == *ptr )
+@@ -78,39 +112,20 @@
+ memcpy( &pkt_id, ptr, 4 );
+ ptr += 4;
+ server->challenge = atoi( ptr );
+-
+- // Correct the stats due to two phase protocol
++ debug( 3, "Challenge: %ld", server->challenge );
+ server->retry1++;
+- server->n_packets--;
+- if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST )
+- {
+- server->n_requests--;
+- }
+- else
+- {
+- server->n_retries--;
+- }
+- send_gs3_request_packet( server );
+- return;
++
++ return send_gs3_request_packet( server );
+ }
+
+ if ( 0x00 != *ptr )
+ {
+ malformed_packet( server, "bad initial byte '%hhx'", *ptr );
+- cleanup_qserver( server, 1 );
+- return;
++ return PKT_ERROR;
+ }
+ ptr++;
+
+ server->n_servers++;
+- if ( server->server_name == NULL )
+- {
+- server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
+- }
+- else
+- {
+- gettimeofday( &server->packet_time1, NULL);
+- }
+
+ // Could check the header here should
+ // match the 4 byte id sent
+@@ -123,14 +138,12 @@
+ if ( server->flags & TF_STATUS_QUERY )
+ {
+ // we have the status response
+- deal_with_gs3_status( server, ptr, pktlen - ( ptr - rawpkt ) );
+- return;
++ return deal_with_gs3_status( server, ptr, pktlen - ( ptr - rawpkt ) );
+ }
+ else
+ {
+ malformed_packet( server, "missing splitnum" );
+- cleanup_qserver( server, 1 );
+- return;
++ return PKT_ERROR;
+ }
+ }
+ ptr += 9;
+@@ -167,20 +180,18 @@
+ if ( ! add_packet( server, pkt_id, pkt_index, pkt_max, pktlen, rawpkt, 1 ) )
+ {
+ // fatal error e.g. out of memory
+- return;
++ return MEM_ERROR;
+ }
+
+ // combine_packets will call us recursively
+- combine_packets( server );
+- return;
++ return combine_packets( server );
+ }
+
+ // if we get here we have what should be a full packet
+- process_gs3_packet( server );
+- return;
++ return process_gs3_packet( server );
+ }
+
+-void deal_with_gs3_status( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_gs3_status( struct qserver *server, char *rawpkt, int pktlen )
+ {
+ char *pkt = rawpkt;
+ debug( 1, "status packet" );
+@@ -194,8 +205,90 @@
+ pkt += strlen( pkt ) + 1;
+
+ // map
+- server->map_name = strdup( pkt );
+- pkt += strlen( pkt ) + 1;
++ // UT3 retail compatibility
++ if ( 0 == strncmp( pkt, "OwningPlayerId", 13 ) )
++ {
++ char *end = pkt + strlen( pkt ) + 1;
++ char *var = pkt;
++ while ( NULL != var )
++ {
++ char *next;
++ char *val = strstr( var, "=" );
++ *val = '\0';
++ val++;
++ next = strstr( val, "," );
++ if ( NULL != next )
++ {
++ *next = '\0';
++ next++;
++ }
++
++ if ( 0 == strcmp( var, "mapname" ) )
++ {
++ if ( server->map_name )
++ {
++ free( server->map_name );
++ }
++ server->map_name = strdup( val );
++ }
++ else if ( 0 == strcmp( var, "p1073741825" ) )
++ {
++ if ( server->map_name )
++ {
++ free( server->map_name );
++ }
++ server->map_name = strdup( val );
++ }
++ else if ( 0 == strcmp( var, "p1073741826" ) )
++ {
++ add_rule( server, "gametype", val, OVERWITE_DUPLICATES );
++ }
++ else if ( 0 == strcmp( var, "p268435705" ) )
++ {
++ add_rule( server, "timelimit", val, OVERWITE_DUPLICATES );
++ }
++ else if ( 0 == strcmp( var, "p1073741827" ) )
++ {
++ add_rule( server, "description", val, OVERWITE_DUPLICATES );
++#ifndef UT3_PATCHED
++ if ( 0 != strlen( val ) )
++ {
++ if ( server->server_name )
++ {
++ char *name = (char*)realloc( server->server_name, strlen( server->server_name ) + strlen( val ) + 3 );
++ if ( name )
++ {
++ strcat( name, ": " );
++ strcat( name, val );
++ server->server_name = name;
++ }
++ }
++ server->server_name = strdup( val );
++ }
++#endif
++ }
++ else if ( 0 == strcmp( var, "p268435704" ) )
++ {
++ add_rule( server, "goalscore", val, OVERWITE_DUPLICATES );
++ }
++ else if ( 0 == strcmp( var, "s32779" ) )
++ {
++ add_rule( server, "gamemode", val, OVERWITE_DUPLICATES );
++ }
++ else
++ {
++ add_rule( server, var, val, OVERWITE_DUPLICATES );
++ }
++
++ var = next;
++ }
++ pkt = end;
++ }
++ else
++ {
++ server->map_name = strdup( pkt );
++ pkt += strlen( pkt ) + 1;
++ }
+
+ // num players
+ server->num_players = atoi( pkt );
+@@ -209,7 +302,7 @@
+ change_server_port( server, atoi( pkt ), 0 );
+ pkt += strlen( pkt ) + 1;
+
+- cleanup_qserver( server, 1 );
++ return DONE_FORCE;
+ }
+
+ int process_gs3_packet( struct qserver *server )
+@@ -231,13 +324,17 @@
+ char *end = ptr + pktlen;
+ debug( 2, "processing fragment[%d]...", fragment->pkt_index );
+
++ if( 6 <= get_debug_level() )
++ {
++ print_packet( server, ptr, pktlen );
++ }
++
+ // check we have a full header
+ if ( pktlen < 16 )
+ {
+ // invalid packet?
+ malformed_packet( server, "too short" );
+- cleanup_qserver( server, 1 );
+- return 0;
++ return PKT_ERROR;
+ }
+
+ // skip over the header
+@@ -251,7 +348,15 @@
+ char *var, *val;
+ int var_len, val_len;
+
+- if ( 0x00 == ptr[0] && 0x01 == ptr[1] )
++ if ( ptr + 1 >= end )
++ {
++ debug( 4, "state = %d, %hhx, bytes left = %d", state, ptr[0], (int)(end - ptr) );
++ }
++ else
++ {
++ debug( 4, "state = %d, %hhx, %hhx, bytes left = %d", state, ptr[0], ptr[1], (int)(end - ptr) );
++ }
++ if ( 0x00 == ptr[0] && ( ptr + 1 >= end || 0x01 >= ptr[1] ) )
+ {
+ // not quite sure of the significance of these bytes
+ // but we use them as a check for end of section
+@@ -267,8 +372,7 @@
+ if ( ptr + 1 > end )
+ {
+ malformed_packet( server, "no rule value" );
+- cleanup_qserver( server, 1);
+- return 0;
++ return PKT_ERROR;
+ }
+
+ val = ptr;
+@@ -294,6 +398,89 @@
+ }
+ else if( 0 == strcmp( var, "mapname" ) )
+ {
++ // UT3 retail compatibility
++ if ( 0 == strncmp( val, "OwningPlayerId", 13 ) )
++ {
++ var = val;
++ while ( NULL != var )
++ {
++ char *next;
++ char *val = strstr( var, "=" );
++ *val = '\0';
++ val++;
++ next = strstr( val, "," );
++ if ( NULL != next )
++ {
++ *next = '\0';
++ next++;
++ }
++
++ if ( 0 == strcmp( var, "mapname" ) )
++ {
++ if ( server->map_name )
++ {
++ free( server->map_name );
++ }
++ server->map_name = strdup( val );
++ }
++ else if ( 0 == strcmp( var, "p1073741825" ) )
++ {
++ if ( server->map_name )
++ {
++ free( server->map_name );
++ }
++ server->map_name = strdup( val );
++ }
++ else if ( 0 == strcmp( var, "p1073741826" ) )
++ {
++ add_rule( server, "gametype", val, OVERWITE_DUPLICATES );
++ }
++ else if ( 0 == strcmp( var, "p268435705" ) )
++ {
++ add_rule( server, "timelimit", val, OVERWITE_DUPLICATES );
++ }
++ else if ( 0 == strcmp( var, "p1073741827" ) )
++ {
++#ifndef UT3_PATCHED
++ if ( 0 != strlen( val ) )
++ {
++ if ( server->server_name )
++ {
++ char *name = (char*)realloc( server->server_name, strlen( server->server_name ) + strlen( val ) + 3 );
++ if ( name )
++ {
++ strcat( name, ": " );
++ strcat( name, val );
++ server->server_name = name;
++ }
++ }
++ server->server_name = strdup( val );
++ }
++#endif
++ }
++ else if ( 0 == strcmp( var, "p268435704" ) )
++ {
++ add_rule( server, "goalscore", val, OVERWITE_DUPLICATES );
++ }
++ else if ( 0 == strcmp( var, "s32779" ) )
++ {
++ add_rule( server, "gamemode", val, OVERWITE_DUPLICATES );
++ }
++ else
++ {
++ add_rule( server, var, val, OVERWITE_DUPLICATES );
++ }
++ var = next;
++ }
++ }
++ else
++ {
++ server->map_name = strdup( val );
++ }
++ }
++ else if( 0 == strcmp( var, "map" ) )
++ {
++ // BF2MC compatibility
+ server->map_name = strdup( val );
+ }
+ else if( 0 == strcmp( var, "maxplayers" ) )
+@@ -308,43 +495,98 @@
+ else if( 0 == strcmp( var, "hostport" ) )
+ {
+ change_server_port( server, atoi( val ), 0 );
+- add_rule( server, var, val, NO_FLAGS );
++ }
++ else if ( 0 == strcmp( var, "p1073741825" ) )
++ {
++ // UT3 demo compatibility
++ if ( server->map_name )
++ {
++ free( server->map_name );
++ }
++ server->map_name = strdup( val );
++ }
++ else if ( 0 == strcmp( var, "p1073741826" ) )
++ {
++ // UT3 demo compatibility
++ add_rule( server, "gametype", val, OVERWITE_DUPLICATES );
++ }
++ else if ( 0 == strcmp( var, "p268435705" ) )
++ {
++ // UT3 demo compatibility
++ add_rule( server, "timelimit", val, OVERWITE_DUPLICATES );
++ }
++ else if ( 0 == strcmp( var, "p1073741827" ) )
++ {
++ // UT3 demo compatibility
++ add_rule( server, "description", val, OVERWITE_DUPLICATES );
++#ifndef UT3_PATCHED
++ if ( 0 != strlen( val ) )
++ {
++ if ( server->server_name )
++ {
++ char *name = (char*)realloc( server->server_name, strlen( server->server_name ) + strlen( val ) + 3 );
++ if ( name )
++ {
++ strcat( name, ": " );
++ strcat( name, val );
++ server->server_name = name;
++ }
++ }
++ server->server_name = strdup( val );
++ }
++#endif
++ }
++ else if ( 0 == strcmp( var, "p268435704" ) )
++ {
++ // UT3 demo compatibility
++ add_rule( server, "goalscore", val, OVERWITE_DUPLICATES );
++ }
++ else if ( 0 == strcmp( var, "s32779" ) )
++ {
++ // UT3 demo compatibility
++ add_rule( server, "gamemode", val, OVERWITE_DUPLICATES );
+ }
+ else
+ {
+- add_rule( server, var, val, NO_FLAGS );
++ add_rule( server, var, val, OVERWITE_DUPLICATES );
+ }
+ }
+
++
+ while ( 1 == state && ptr < end )
+ {
+ // first we have the header
+ char *header = ptr;
+ int head_len = strlen( header );
+ int header_type;
+- ptr += head_len + 1;
++
++ debug( 4, "state = %d, bytes left = %d, head_len = %d", state, (int)(end - ptr), head_len );
+
+ if ( 0 == head_len )
+ {
+ // no more info
+ debug( 3, "All done" );
+- cleanup_qserver( server, 1 );
+- return 1;
++ return DONE_FORCE;
+ }
+
+- debug( 2, "player header '%s'", header );
++ ptr += head_len + 1;
+
+- if ( ptr > end )
++ if ( ptr >= end )
+ {
+- malformed_packet( server, "no details for header '%s'", header );
+- cleanup_qserver( server, 1 );
+- return 0;
++ // partial header, should be restarted in the next fragment
++ debug( 5, "partial header '%s'", header );
++ // ensure gt than
++ ptr++;
++
++ break;
+ }
+
++ debug( 2, "player header '%s'", header );
++
+ // the next byte is the starting number
+ total_players = *ptr++;
+
+- if ( 0 == strcmp( header, "player_" ) )
++ if ( 0 == strcmp( header, "player_" ) || 0 == strcmp( header, "name_" ) )
+ {
+ header_type = PLAYER_NAME_HEADER;
+ }
+@@ -384,7 +626,7 @@
+ // check for end of this headers player info
+ if ( 0x00 == *ptr )
+ {
+- debug( 3, "end of '%s' detail", header );
++ debug( 3, "end of '%s' detail, %d bytes left", header, (int)(end - ptr) );
+ ptr++;
+ // Note: can't check ( total_players != no_players ) here as we may have more packets
+ if ( ptr < end && 0x00 == *ptr )
+@@ -406,8 +648,7 @@
+ if ( ptr >= end )
+ {
+ malformed_packet( server, "short player detail" );
+- cleanup_qserver( server, 1);
+- return 0;
++ return PKT_ERROR;
+ }
+ val = ptr;
+ val_len = strlen( val );
+@@ -462,13 +703,14 @@
+ if ( total_players > no_players )
+ {
+ malformed_packet( server, "to many players %d > %d", total_players, no_players );
+- cleanup_qserver( server, 1 );
+- return 0;
++ return PKT_ERROR;
+ }
+ }
+ }
+
+- if ( 2 == state )
++ debug( 4, "state = %d, bytes left = %d", state, (int)(end - ptr) );
++
++ if ( 2 == state && ptr < end )
+ {
+ no_teams = (unsigned char)*ptr;
+ ptr++;
+@@ -489,8 +731,14 @@
+ {
+ // no more info
+ debug( 3, "All done" );
+- cleanup_qserver( server, 1 );
+- return 1;
++ return DONE_FORCE;
++ }
++
++ if ( ptr >= end )
++ {
++ // partial header, should be restarted in the next fragment
++ debug( 5, "partial header '%s'", header );
++ break;
+ }
+
+ debug( 2, "team header '%s'", header );
+@@ -516,8 +764,7 @@
+ if ( ptr >= end )
+ {
+ malformed_packet( server, "short team detail" );
+- cleanup_qserver( server, 1);
+- return 0;
++ return PKT_ERROR;
+ }
+ val = ptr;
+ val_len = strlen( val );
+@@ -552,11 +799,10 @@
+ }
+ }
+
+- cleanup_qserver( server, 1 );
+- return 1;
++ return DONE_FORCE;
+ }
+
+-void send_gs3_request_packet( struct qserver *server )
++query_status_t send_gs3_request_packet( struct qserver *server )
+ {
+ char *packet;
+ char query_buf[128];
+@@ -615,5 +861,5 @@
+ }
+ }
+
+- send_packet( server, packet, len );
++ return send_packet( server, packet, len );
+ }
+diff -urP qstat-2.11/gs3.h qstat2/gs3.h
+--- qstat-2.11/gs3.h 2005-07-16 22:45:08.000000000 -0500
++++ qstat2/gs3.h 2009-07-02 06:11:41.032181000 -0400
+@@ -13,8 +13,8 @@
+ #include "qserver.h"
+
+ // Packet processing methods
+-void deal_with_gs3_packet( struct qserver *server, char *pkt, int pktlen );
+-void deal_with_gs3_status( struct qserver *server, char *rawpkt, int pktlen );
+-void send_gs3_request_packet( struct qserver *server );
++query_status_t deal_with_gs3_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t deal_with_gs3_status( struct qserver *server, char *rawpkt, int pktlen );
++query_status_t send_gs3_request_packet( struct qserver *server );
+
+ #endif
+diff -urP qstat-2.11/haze.c qstat2/haze.c
+--- qstat-2.11/haze.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/haze.c 2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,628 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * New Haze query protocol
++ * Copyright 2005 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <netinet/in.h>
++#include <sys/socket.h>
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++
++// Format:
++// 1 - 8: Challenge Request / Response
++char haze_challenge[] = {
++ 'f', 'r', 'd', 'c', '_', '_', '_', '_'
++};
++
++int process_haze_packet( struct qserver *server );
++
++// Player headers
++#define PLAYER_NAME_HEADER 1
++#define PLAYER_SCORE_HEADER 2
++#define PLAYER_DEATHS_HEADER 3
++#define PLAYER_PING_HEADER 4
++#define PLAYER_KILLS_HEADER 5
++#define PLAYER_TEAM_HEADER 6
++#define PLAYER_OTHER_HEADER 7
++
++// Team headers
++#define TEAM_NAME_HEADER 1
++#define TEAM_OTHER_HEADER 2
++
++// Challenge response algorithum
++// Before sending a qr2 query (type 0x00) the client must first send a
++// challenge request (type 0x09). The host will respond with the same
++// packet type containing a string signed integer.
++//
++// Once the challenge is received the client should convert the string to a
++// network byte order integer and embed it in the keys query.
++//
++// Example:
++//
++// challenge request: [0xFE][0xFD][0x09][0x.. 4-byte-instance]
++// challenge response: [0x09][0x.. 4-byte-instance]["-1287574694"]
++// query: [0xFE][0xFD][0x00][0x.. 4-byte-instance][0xb3412b5a "-1287574694"]
++//
++
++query_status_t deal_with_haze_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *ptr = rawpkt;
++ unsigned int pkt_id;
++ unsigned short len;
++ unsigned char pkt_max, pkt_index;
++
++ debug( 2, "packet..." );
++
++ if ( pktlen < 8 )
++ {
++ // invalid packet
++ malformed_packet( server, "too short" );
++ return PKT_ERROR;
++ }
++
++ if ( 0 == strncmp( ptr, "frdcr", 5 ) )
++ {
++ // challenge response
++ ptr += 8;
++ server->challenge = 1;
++
++ // Correct the stats due to two phase protocol
++ server->retry1++;
++ server->n_packets--;
++ if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST )
++ {
++ //server->n_requests--;
++ }
++ else
++ {
++ server->n_retries--;
++ }
++ return send_haze_request_packet( server );
++ }
++
++ if ( pktlen < 12 )
++ {
++ // invalid packet
++ malformed_packet( server, "too short" );
++ return PKT_ERROR;
++ }
++
++ server->n_servers++;
++ if ( server->server_name == NULL )
++ {
++ server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++ }
++ else
++ {
++ gettimeofday( &server->packet_time1, NULL);
++ }
++
++ // Query version ID
++ ptr += 4;
++
++ // Could check the header here should
++ // match the 4 byte id sent
++ memcpy( &pkt_id, ptr, 4 );
++ ptr += 4;
++
++
++ // Max plackets
++ pkt_max = ((unsigned char)*ptr);
++ ptr++;
++
++ // Packet ID
++ pkt_index = ((unsigned char)*ptr);
++ ptr++;
++
++ // Query Length
++ //len = (unsigned short)ptr[0] | ((unsigned short)ptr[1] << 8);
++ //len = swap_short_from_little( ptr );
++ debug( 1, "%04hx, %04hx", (unsigned short)ptr[0], ((unsigned short)ptr[1] << 8) );
++ //len = (unsigned short)(unsigned short)ptr[0] | ((unsigned short)ptr[1] << 8);
++ // TODO: fix this crap
++ memcpy( &len, ptr+1, 1 );
++ //memcpy( &len+1, ptr, 1 );
++ //memcpy( &len, ptr, 2 );
++ ptr += 2;
++
++ debug( 1, "pkt_index = %d, pkt_max = %d, len = %d", pkt_index, pkt_max, len );
++ if ( 0 != pkt_max )
++ {
++ // not a single packet response or callback
++ debug( 2, "pkt_max %d", pkt_max );
++
++ if ( 0 == pkt_index )
++ {
++ // to prevent reprocessing when we get the call back
++ // override the packet flag so it looks like a single
++ // packet response
++ rawpkt[8] = '\0';
++ }
++
++ // add the packet recalcing maxes
++ if ( ! add_packet( server, pkt_id, pkt_index, pkt_max, pktlen, rawpkt, 1 ) )
++ {
++ // fatal error e.g. out of memory
++ return MEM_ERROR;
++ }
++
++ // combine_packets will call us recursively
++ return combine_packets( server );
++ }
++
++ // if we get here we have what should be a full packet
++ return process_haze_packet( server );
++}
++
++query_status_t deal_with_haze_status( struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *pkt = rawpkt;
++ int len;
++ debug( 1, "status packet" );
++
++
++ // Server name
++ server->server_name = strdup( pkt );
++ pkt += strlen( pkt ) + 1;
++
++ // gametype
++ add_rule( server, "gametype", pkt, NO_FLAGS );
++ pkt += strlen( pkt ) + 1;
++
++ // map
++ len = strlen( pkt );
++ // remove .res from map names
++ if ( 0 == strncmp( pkt + len - 4, ".res", 4 ) )
++ {
++ *(pkt + len - 4) = '\0';
++ }
++ server->map_name = strdup( pkt );
++ pkt += len + 1;
++
++ // num players
++ server->num_players = atoi( pkt );
++ pkt += strlen( pkt ) + 1;
++
++ // max_players
++ server->max_players = atoi( pkt );
++ pkt += strlen( pkt ) + 1;
++
++ // hostport
++ change_server_port( server, atoi( pkt ), 0 );
++ pkt += strlen( pkt ) + 1;
++
++ return DONE_FORCE;
++}
++
++int process_haze_packet( struct qserver *server )
++{
++ unsigned char state = 0;
++ unsigned char no_players = 0;
++ unsigned char total_players = 0;
++ unsigned char no_teams = 0;
++ unsigned char total_teams = 0;
++ int pkt_index = 0;
++ SavedData *fragment;
++
++ debug( 2, "processing packet..." );
++
++ while ( NULL != ( fragment = get_packet_fragment( pkt_index++ ) ) )
++ {
++ int pktlen = fragment->datalen;
++ char *ptr = fragment->data;
++ char *end = ptr + pktlen;
++ debug( 2, "processing fragment[%d]...", fragment->pkt_index );
++
++ // check we have a full header
++ if ( pktlen < 12 )
++ {
++ // invalid packet
++ malformed_packet( server, "too short" );
++ return PKT_ERROR;
++ }
++
++ // skip over the header
++ //server->protocol_version = atoi( val+1 );
++ ptr += 12;
++
++ // 4 * null's signifies the end of a section
++
++ // Basic Info
++ while ( 0 == state && ptr < end )
++ {
++ // name value pairs null seperated
++ char *var, *val;
++ int var_len, val_len;
++
++ if ( ptr+4 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] && 0x00 == ptr[2] && 0x00 == ptr[3] )
++ {
++ // end of rules
++ state++;
++ ptr += 4;
++ break;
++ }
++
++ var = ptr;
++ var_len = strlen( var );
++ ptr += var_len + 1;
++
++ if ( ptr + 1 > end )
++ {
++ malformed_packet( server, "no basic value" );
++ return PKT_ERROR;
++ }
++
++ val = ptr;
++ val_len = strlen( val );
++ ptr += val_len + 1;
++ debug( 2, "var:%s (%d)=%s (%d)\n", var, var_len, val, val_len );
++
++ // Lets see what we've got
++ if ( 0 == strcmp( var, "serverName" ) )
++ {
++ server->server_name = strdup( val );
++ }
++ else if( 0 == strcmp( var, "map" ) )
++ {
++ // remove .res from map names
++ if ( 0 == strncmp( val + val_len - 4, ".res", 4 ) )
++ {
++ *(val + val_len - 4) = '\0';
++ }
++ server->map_name = strdup( val );
++ }
++ else if( 0 == strcmp( var, "maxPlayers" ) )
++ {
++ server->max_players = atoi( val );
++
++ }
++ else if( 0 == strcmp( var, "currentPlayers" ) )
++ {
++ server->num_players = no_players = atoi( val );
++ }
++ else
++ {
++ add_rule( server, var, val, NO_FLAGS );
++ }
++ }
++
++ // rules
++ while ( 1 == state && ptr < end )
++ {
++ // name value pairs null seperated
++ char *var, *val;
++ int var_len, val_len;
++
++ if ( ptr+4 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] && 0x00 == ptr[2] && 0x00 == ptr[3] )
++ {
++ // end of basic
++ state++;
++ ptr += 4;
++ break;
++ }
++ var = ptr;
++ var_len = strlen( var );
++ ptr += var_len + 1;
++
++ if ( ptr + 1 > end )
++ {
++ malformed_packet( server, "no basic value" );
++ return PKT_ERROR;
++ }
++
++ val = ptr;
++ val_len = strlen( val );
++ ptr += val_len + 1;
++ debug( 2, "var:%s (%d)=%s (%d)\n", var, var_len, val, val_len );
++
++ // add the rule
++ add_rule( server, var, val, NO_FLAGS );
++ }
++
++ // players
++ while ( 2 == state && ptr < end )
++ {
++ // first we have the header
++ char *header = ptr;
++ int head_len = strlen( header );
++ ptr += head_len + 1;
++
++ if ( ptr+2 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] )
++ {
++ // end of player headers
++ state++;
++ ptr += 2;
++ break;
++ }
++
++ if ( 0 == head_len )
++ {
++ // no more info
++ debug( 3, "All done" );
++ return DONE_FORCE;
++ }
++
++ debug( 2, "player header '%s'", header );
++
++ if ( ptr > end )
++ {
++ malformed_packet( server, "no details for header '%s'", header );
++ return PKT_ERROR;
++ }
++
++ }
++
++ while ( 3 == state && ptr < end )
++ {
++ char *header = ptr;
++ int head_len = strlen( header );
++ int header_type;
++ // the next byte is the starting number
++ total_players = *ptr++;
++
++ if ( 0 == strcmp( header, "player_" ) || 0 == strcmp( header, "name_" ) )
++ {
++ header_type = PLAYER_NAME_HEADER;
++ }
++ else if ( 0 == strcmp( header, "score_" ) )
++ {
++ header_type = PLAYER_SCORE_HEADER;
++ }
++ else if ( 0 == strcmp( header, "deaths_" ) )
++ {
++ header_type = PLAYER_DEATHS_HEADER;
++ }
++ else if ( 0 == strcmp( header, "ping_" ) )
++ {
++ header_type = PLAYER_PING_HEADER;
++ }
++ else if ( 0 == strcmp( header, "kills_" ) )
++ {
++ header_type = PLAYER_KILLS_HEADER;
++ }
++ else if ( 0 == strcmp( header, "team_" ) )
++ {
++ header_type = PLAYER_TEAM_HEADER;
++ }
++ else
++ {
++ header_type = PLAYER_OTHER_HEADER;
++ }
++
++ while( ptr < end )
++ {
++ // now each player details
++ // add the player
++ struct player *player;
++ char *val;
++ int val_len;
++
++ // check for end of this headers player info
++ if ( 0x00 == *ptr )
++ {
++ debug( 3, "end of '%s' detail", header );
++ ptr++;
++ // Note: can't check ( total_players != no_players ) here as we may have more packets
++ if ( ptr < end && 0x00 == *ptr )
++ {
++ debug( 3, "end of players" );
++ // end of all player headers / detail
++ state = 2;
++ ptr++;
++ }
++ break;
++ }
++
++ player = get_player_by_number( server, total_players );
++ if ( NULL == player )
++ {
++ player = add_player( server, total_players );
++ }
++
++ if ( ptr >= end )
++ {
++ malformed_packet( server, "short player detail" );
++ return PKT_ERROR;
++ }
++ val = ptr;
++ val_len = strlen( val );
++ ptr += val_len + 1;
++
++ debug( 2, "Player[%d][%s]=%s\n", total_players, header, val );
++
++ // lets see what we got
++ switch( header_type )
++ {
++ case PLAYER_NAME_HEADER:
++ player->name = strdup( val );
++ break;
++
++ case PLAYER_SCORE_HEADER:
++ player->score = atoi( val );
++ break;
++
++ case PLAYER_DEATHS_HEADER:
++ player->deaths = atoi( val );
++ break;
++
++ case PLAYER_PING_HEADER:
++ player->ping = atoi( val );
++ break;
++
++ case PLAYER_KILLS_HEADER:
++ player->frags = atoi( val );
++ break;
++
++ case PLAYER_TEAM_HEADER:
++ player->team = atoi( val );
++ break;
++
++ case PLAYER_OTHER_HEADER:
++ default:
++ if ( '_' == header[head_len-1] )
++ {
++ header[head_len-1] = '\0';
++ player_add_info( player, header, val, NO_FLAGS );
++ header[head_len-1] = '_';
++ }
++ else
++ {
++ player_add_info( player, header, val, NO_FLAGS );
++ }
++ break;
++ }
++
++ total_players++;
++
++ if ( total_players > no_players )
++ {
++ malformed_packet( server, "to many players %d > %d", total_players, no_players );
++ return PKT_ERROR;
++ }
++ }
++ }
++
++ if ( 3 == state )
++ {
++ no_teams = (unsigned char)*ptr;
++ ptr++;
++
++ debug( 2, "No teams:%d\n", no_teams );
++ state = 3;
++ }
++
++ while ( 4 == state && ptr < end )
++ {
++ // first we have the header
++ char *header = ptr;
++ int head_len = strlen( header );
++ int header_type;
++ ptr += head_len + 1;
++
++ if ( 0 == head_len )
++ {
++ // no more info
++ debug( 3, "All done" );
++ return DONE_FORCE;
++ }
++
++ debug( 2, "team header '%s'", header );
++ if ( 0 == strcmp( header, "team_t" ) )
++ {
++ header_type = TEAM_NAME_HEADER;
++ }
++ else
++ {
++ header_type = TEAM_OTHER_HEADER;
++ }
++
++ // the next byte is the starting number
++ total_teams = *ptr++;
++
++ while( ptr < end )
++ {
++ // now each teams details
++ char *val;
++ int val_len;
++ char rule[512];
++
++ if ( ptr >= end )
++ {
++ malformed_packet( server, "short team detail" );
++ return PKT_ERROR;
++ }
++ val = ptr;
++ val_len = strlen( val );
++ ptr += val_len + 1;
++
++ debug( 2, "Team[%d][%s]=%s\n", total_teams, header, val );
++
++ // lets see what we got
++ switch ( header_type )
++ {
++ case TEAM_NAME_HEADER:
++ // BF being stupid again teams 1 based instead of 0
++ players_set_teamname( server, total_teams + 1, val );
++ // N.B. yes no break
++
++ case TEAM_OTHER_HEADER:
++ default:
++ // add as a server rule
++ sprintf( rule, "%s%d", header, total_teams );
++ add_rule( server, rule, val, NO_FLAGS );
++ break;
++ }
++
++ total_teams++;
++ if ( 0x00 == *ptr )
++ {
++ // end of this headers teams
++ ptr++;
++ break;
++ }
++ }
++ }
++ }
++
++ return DONE_FORCE;
++}
++
++query_status_t send_haze_request_packet( struct qserver *server )
++{
++ char *packet;
++ char query_buf[128];
++ size_t len;
++ unsigned char required = HAZE_BASIC_INFO;
++
++ if ( get_server_rules )
++ {
++ required |= HAZE_GAME_RULES;
++ server->flags |= TF_PLAYER_QUERY;
++ }
++
++ if ( get_player_info )
++ {
++ required |= HAZE_PLAYER_INFO;
++ required |= HAZE_TEAM_INFO;
++ server->flags |= TF_RULES_QUERY;
++ }
++
++ server->flags |= TF_STATUS_QUERY;
++
++ if ( server->challenge )
++ {
++ // we've recieved a challenge response, send the query + challenge id
++ len = sprintf(
++ query_buf,
++ "frdquery%c%c%c%c%c",
++ (unsigned char)(server->challenge >> 24),
++ (unsigned char)(server->challenge >> 16),
++ (unsigned char)(server->challenge >> 8),
++ (unsigned char)(server->challenge >> 0),
++ required
++ );
++ packet = query_buf;
++ }
++ else
++ {
++ // Either basic v3 protocol or challenge request
++ packet = haze_challenge;
++ len = sizeof( haze_challenge );
++ }
++
++ return send_packet( server, packet, len );
++}
+diff -urP qstat-2.11/haze.h qstat2/haze.h
+--- qstat-2.11/haze.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/haze.h 2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,27 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * New Haze query protocol
++ * Copyright 2007 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_HAZE_H
++#define QSTAT_HAZE_H
++
++
++#include "qserver.h"
++
++#define HAZE_BASIC_INFO 0x01
++#define HAZE_GAME_RULES 0x02
++#define HAZE_PLAYER_INFO 0x04
++#define HAZE_TEAM_INFO 0x08
++
++// Packet processing methods
++query_status_t deal_with_haze_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t deal_with_haze_status( struct qserver *server, char *rawpkt, int pktlen );
++query_status_t send_haze_request_packet( struct qserver *server );
++
++
++#endif
+Only in qstat-2.11/info: Makefile.in
+Only in qstat-2.11: install-sh
+diff -urP qstat-2.11/LICENSE.txt qstat2/LICENSE.txt
+--- qstat-2.11/LICENSE.txt 2002-11-13 21:33:54.000000000 -0500
++++ qstat2/LICENSE.txt 2008-09-09 23:18:37.952285000 -0400
+@@ -1,130 +1,201 @@
+-The Artistic License
++ The Artistic License 2.0
++
++ Copyright (c) 2000-2006, The Perl Foundation.
++
++ Everyone is permitted to copy and distribute verbatim copies
++ of this license document, but changing it is not allowed.
+
+ Preamble
+- The intent of this document is to state the conditions
+- under which a Package may be copied, such that the
+- Copyright Holder maintains some semblance of artistic
+- control over the development of the package, while
+- giving the users of the package the right to use and
+- distribute the Package in a more-or-less customary
+- fashion, plus the right to make reasonable modifications.
+-
+-Definitions:
+- "Package" refers to the collection of files
+- distributed by the Copyright Holder, and derivatives
+- of that collection of files created through textual
+- modification.
+-
+- "Standard Version" refers to such a Package if it has
+- not been modified, or has been modified in accordance
+- with the wishes of the Copyright Holder.
+-
+- "Copyright Holder" is whoever is named in the
+- copyright or copyrights for the package.
+-
+- "You" is you, if you're thinking about copying or
+- distributing this Package.
+-
+- "Reasonable copying fee" is whatever you can justify
+- on the basis of media cost, duplication charges, time
+- of people involved, and so on. (You will not be
+- required to justify it to the Copyright Holder, but
+- only to the computing community at large as a market
+- that must bear the fee.)
+-
+- "Freely Available" means that no fee is charged for
+- the item itself, though there may be fees involved in
+- handling the item. It also means that recipients of
+- the item may redistribute it under the same conditions
+- they received it.
+-
+- 1. You may make and give away verbatim copies of the
+- source form of the Standard Version of this Package
+- without restriction, provided that you duplicate all of
+- the original copyright notices and associated
+- disclaimers.
+-
+- 2. You may apply bug fixes, portability fixes and other
+- modifications derived from the Public Domain or from the
+- Copyright Holder. A Package modified in such a way shall
+- still be considered the Standard Version.
+-
+- 3. You may otherwise modify your copy of this Package in
+- any way, provided that you insert a prominent notice in
+- each changed file stating how and when you changed that
+- file, and provided that you do at least ONE of the
+- following:
+-
+- a) place your modifications in the Public Domain or
+- otherwise make them Freely Available, such as by
+- posting said modifications to Usenet or an equivalent
+- medium, or placing the modifications on a major
+- archive site such as ftp.uu.net, or by allowing the
+- Copyright Holder to include your modifications in the
+- Standard Version of the Package.
+-
+- b) use the modified Package only within your
+- corporation or organization.
+-
+- c) rename any non-standard executables so the names do
+- not conflict with standard executables, which must
+- also be provided, and provide a separate manual page
+- for each non-standard executable that clearly
+- documents how it differs from the Standard Version.
+-
+- d) make other distribution arrangements with the
+- Copyright Holder.
+-
+- 4. You may distribute the programs of this Package in
+- object code or executable form, provided that you do at
+- least ONE of the following:
+-
+- a) distribute a Standard Version of the executables
+- and library files, together with instructions (in the
+- manual page or equivalent) on where to get the
+- Standard Version.
+-
+- b) accompany the distribution with the
+- machine-readable source of the Package with your
+- modifications.
+-
+- c) accompany any non-standard executables with their
+- corresponding Standard Version executables, giving the
+- non-standard executables non-standard names, and
+- clearly documenting the differences in manual pages
+- (or equivalent), together with instructions on where
+- to get the Standard Version.
+-
+- d) make other distribution arrangements with the
+- Copyright Holder.
+-
+- 5. You may charge a reasonable copying fee for any
+- distribution of this Package. You may charge any fee you
+- choose for support of this Package. You may not charge a
+- fee for this Package itself. However, you may distribute
+- this Package in aggregate with other (possibly
+- commercial) programs as part of a larger (possibly
+- commercial) software distribution provided that you do
+- not advertise this Package as a product of your own.
+-
+- 6. The scripts and library files supplied as input to or
+- produced as output from the programs of this Package do
+- not automatically fall under the copyright of this
+- Package, but belong to whomever generated them, and may
+- be sold commercially, and may be aggregated with this
+- Package.
+-
+- 7. C subroutines supplied by you and linked into
+- this Package shall not be considered part of this
+- Package.
+-
+- 8. The name of the Copyright Holder may not be used to
+- endorse or promote products derived from this software
+- without specific prior written permission.
+-
+- 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY
+- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+- LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY
+- AND FITNESS FOR A PARTICULAR PURPOSE.
+
+- The End
++This license establishes the terms under which a given free software
++Package may be copied, modified, distributed, and/or redistributed.
++The intent is that the Copyright Holder maintains some artistic
++control over the development of that Package while still keeping the
++Package available as open source and free software.
++
++You are always permitted to make arrangements wholly outside of this
++license directly with the Copyright Holder of a given Package. If the
++terms of this license do not permit the full use that you propose to
++make of the Package, you should contact the Copyright Holder and seek
++a different licensing arrangement.
++
++Definitions
++
++ "Copyright Holder" means the individual(s) or organization(s)
++ named in the copyright notice for the entire Package.
++
++ "Contributor" means any party that has contributed code or other
++ material to the Package, in accordance with the Copyright Holder's
++ procedures.
++
++ "You" and "your" means any person who would like to copy,
++ distribute, or modify the Package.
++
++ "Package" means the collection of files distributed by the
++ Copyright Holder, and derivatives of that collection and/or of
++ those files. A given Package may consist of either the Standard
++ Version, or a Modified Version.
++
++ "Distribute" means providing a copy of the Package or making it
++ accessible to anyone else, or in the case of a company or
++ organization, to others outside of your company or organization.
++
++ "Distributor Fee" means any fee that you charge for Distributing
++ this Package or providing support for this Package to another
++ party. It does not mean licensing fees.
++
++ "Standard Version" refers to the Package if it has not been
++ modified, or has been modified only in ways explicitly requested
++ by the Copyright Holder.
++
++ "Modified Version" means the Package, if it has been changed, and
++ such changes were not explicitly requested by the Copyright
++ Holder.
++
++ "Original License" means this Artistic License as Distributed with
++ the Standard Version of the Package, in its current version or as
++ it may be modified by The Perl Foundation in the future.
++
++ "Source" form means the source code, documentation source, and
++ configuration files for the Package.
++
++ "Compiled" form means the compiled bytecode, object code, binary,
++ or any other form resulting from mechanical transformation or
++ translation of the Source form.
++
++
++Permission for Use and Modification Without Distribution
++
++(1) You are permitted to use the Standard Version and create and use
++Modified Versions for any purpose without restriction, provided that
++you do not Distribute the Modified Version.
++
++
++Permissions for Redistribution of the Standard Version
++
++(2) You may Distribute verbatim copies of the Source form of the
++Standard Version of this Package in any medium without restriction,
++either gratis or for a Distributor Fee, provided that you duplicate
++all of the original copyright notices and associated disclaimers. At
++your discretion, such verbatim copies may or may not include a
++Compiled form of the Package.
++
++(3) You may apply any bug fixes, portability changes, and other
++modifications made available from the Copyright Holder. The resulting
++Package will still be considered the Standard Version, and as such
++will be subject to the Original License.
++
++
++Distribution of Modified Versions of the Package as Source
++
++(4) You may Distribute your Modified Version as Source (either gratis
++or for a Distributor Fee, and with or without a Compiled form of the
++Modified Version) provided that you clearly document how it differs
++from the Standard Version, including, but not limited to, documenting
++any non-standard features, executables, or modules, and provided that
++you do at least ONE of the following:
++
++ (a) make the Modified Version available to the Copyright Holder
++ of the Standard Version, under the Original License, so that the
++ Copyright Holder may include your modifications in the Standard
++ Version.
++
++ (b) ensure that installation of your Modified Version does not
++ prevent the user installing or running the Standard Version. In
++ addition, the Modified Version must bear a name that is different
++ from the name of the Standard Version.
++
++ (c) allow anyone who receives a copy of the Modified Version to
++ make the Source form of the Modified Version available to others
++ under
++
++ (i) the Original License or
++
++ (ii) a license that permits the licensee to freely copy,
++ modify and redistribute the Modified Version using the same
++ licensing terms that apply to the copy that the licensee
++ received, and requires that the Source form of the Modified
++ Version, and of any works derived from it, be made freely
++ available in that license fees are prohibited but Distributor
++ Fees are allowed.
++
++
++Distribution of Compiled Forms of the Standard Version
++or Modified Versions without the Source
++
++(5) You may Distribute Compiled forms of the Standard Version without
++the Source, provided that you include complete instructions on how to
++get the Source of the Standard Version. Such instructions must be
++valid at the time of your distribution. If these instructions, at any
++time while you are carrying out such distribution, become invalid, you
++must provide new instructions on demand or cease further distribution.
++If you provide valid instructions or cease distribution within thirty
++days after you become aware that the instructions are invalid, then
++you do not forfeit any of your rights under this license.
++
++(6) You may Distribute a Modified Version in Compiled form without
++the Source, provided that you comply with Section 4 with respect to
++the Source of the Modified Version.
++
++
++Aggregating or Linking the Package
++
++(7) You may aggregate the Package (either the Standard Version or
++Modified Version) with other packages and Distribute the resulting
++aggregation provided that you do not charge a licensing fee for the
++Package. Distributor Fees are permitted, and licensing fees for other
++components in the aggregation are permitted. The terms of this license
++apply to the use and Distribution of the Standard or Modified Versions
++as included in the aggregation.
++
++(8) You are permitted to link Modified and Standard Versions with
++other works, to embed the Package in a larger work of your own, or to
++build stand-alone binary or bytecode versions of applications that
++include the Package, and Distribute the result without restriction,
++provided the result does not expose a direct interface to the Package.
++
++
++Items That are Not Considered Part of a Modified Version
++
++(9) Works (including, but not limited to, modules and scripts) that
++merely extend or make use of the Package, do not, by themselves, cause
++the Package to be a Modified Version. In addition, such works are not
++considered parts of the Package itself, and are not subject to the
++terms of this license.
++
++
++General Provisions
++
++(10) Any use, modification, and distribution of the Standard or
++Modified Versions is governed by this Artistic License. By using,
++modifying or distributing the Package, you accept this license. Do not
++use, modify, or distribute the Package, if you do not accept this
++license.
++
++(11) If your Modified Version has been derived from a Modified
++Version made by someone other than you, you are nevertheless required
++to ensure that your Modified Version complies with the requirements of
++this license.
++
++(12) This license does not grant you the right to use any trademark,
++service mark, tradename, or logo of the Copyright Holder.
++
++(13) This license includes the non-exclusive, worldwide,
++free-of-charge patent license to make, have made, use, offer to sell,
++sell, import and otherwise transfer the Package with respect to any
++patent claims licensable by the Copyright Holder that are necessarily
++infringed by the Package. If you institute patent litigation
++(including a cross-claim or counterclaim) against any party alleging
++that the Package constitutes direct or contributory patent
++infringement, then this Artistic License to you shall terminate on the
++date that such litigation is filed.
++
++(14) Disclaimer of Warranty:
++THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
++IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
++WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
++NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
++LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
++BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
++DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
++ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+diff -urP qstat-2.11/Makefile.am qstat2/Makefile.am
+--- qstat-2.11/Makefile.am 2006-08-04 15:42:09.000000000 -0500
++++ qstat2/Makefile.am 2012-04-20 08:51:31.440424000 -0400
+@@ -20,7 +20,20 @@
+ gs2.c gs2.h \
+ gs3.c gs3.h \
+ ts2.c ts2.h \
+- tm.c tm.h
++ tm.c tm.h \
++ haze.c haze.h \
++ ottd.c ottd.h \
++ wic.c wic.h \
++ fl.c fl.h \
++ tee.c tee.h \
++ cube2.c cube2.h \
++ ts3.c ts3.h \
++ bfbc2.c bfbc2.h \
++ ventrilo.c ventrilo.h \
++ mumble.c mumble.h \
++ terraria.c terraria.h \
++ crysis.c crysis.h \
++ utils.c utils.h
+
+ dist_configfiles_DATA = qstat.cfg
+ configfilesdir = $(sysconfdir)
+@@ -33,4 +46,4 @@
+
+ cl:
+ cvs2cl.pl --utc --no-wrap --separate-header --no-times -f ChangeLog.cvs
+- rm -f ChangeLog.cvs.bak
++ rm -f ChangeLog.cvs.bak qstat.core
+Only in qstat-2.11: Makefile.in
+diff -urP qstat-2.11/Makefile.noauto qstat2/Makefile.noauto
+--- qstat-2.11/Makefile.noauto 2006-10-28 07:36:58.000000000 -0500
++++ qstat2/Makefile.noauto 2012-04-20 08:51:31.440424000 -0400
+@@ -5,17 +5,17 @@
+ #LDLIBS =
+
+ #CFLAGS += -Dsysconfdir=\"/etc\"
+-CFLAGS = -DDEBUG=1
++CFLAGS = -DDEBUG=1 -DENABLE_DUMP=1
+
+ ## NOTE: if you get errors when linking qstat (missing symbols or
+ ## libraries), then modify LDFLAGS or LDLIBS
+
+-SRC = config.c debug.c hcache.c md5.c qserver.c qstat.c template.c ut2004.c a2s.c packet_manip.c gs3.c gs2.c gps.c ts2.c doom3.c tm.c
++SRC = config.c debug.c hcache.c md5.c qserver.c qstat.c template.c ut2004.c a2s.c packet_manip.c gs3.c gs2.c gps.c ts2.c doom3.c tm.c haze.c wic.c ottd.c fl.c tee.c ts3.c bfbc2.c ventrilo.c cube2.c mumble.c terraria.c crysis.c utils.c
+ OBJ = $(SRC:.c=.obj)
+ O = $(SRC:.c=.o)
+
+ SOLARIS_LIBS = -lsocket -lnsl
+-WINDOWS_LIBS = /ML wsock32.lib
++WINDOWS_LIBS = wsock32.lib
+ OS2_LIBS = so32dll.lib tcp32dll.lib
+ EMX_LIBS = -lsocket
+
+@@ -39,7 +39,7 @@
+
+ windows_debug: $(SRC)
+ rm -f *.pdb
+- $(CC) $(CFLAGS) /Zi /ML $(SRC) /Feqstat.exe wsock32.lib /link /fixed:no /incremental:no
++ $(CC) $(CFLAGS) /Zi $(SRC) /Feqstat.exe $(WINDOWS_LIBS) /link /fixed:no /incremental:no
+
+ os2: $(SRC)
+ $(CC) /Q /W0 /C+ $(SRC)
+diff -urP qstat-2.11/md5.c qstat2/md5.c
+--- qstat-2.11/md5.c 2004-10-17 15:41:52.000000000 -0500
++++ qstat2/md5.c 2012-04-20 08:51:31.440424000 -0400
+@@ -21,7 +21,7 @@
+ ghost at aladdin.com
+
+ */
+-/* $Id: md5.c,v 1.1 2004/10/17 20:41:52 l-n Exp $ */
++/* $Id: md5.c 375 2012-04-20 12:51:31Z stevenhartland $ */
+ /*
+ Independent implementation of MD5 (RFC 1321).
+
+@@ -379,3 +379,26 @@
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+ }
++
++static const char hexchar[] = "0123456789abcdef";
++
++char* md5_hex(const char *bytes, int nbytes)
++{
++ char out[33];
++ char* o = out+32;
++ char digest[16];
++ char *digestp = digest + 16;
++ md5_state_t md5;
++
++ out[32] = '\0';
++ md5_init(&md5);
++ md5_append(&md5, (unsigned char*)bytes, nbytes);
++ md5_finish(&md5, (unsigned char*)digest);
++ do
++ {
++ *--o = hexchar[*--digestp&0x0F];
++ *--o = hexchar[(*digestp>>4)&0x0F];
++ } while(o != out);
++
++ return strdup(out);
++}
+diff -urP qstat-2.11/md5.h qstat2/md5.h
+--- qstat-2.11/md5.h 2004-10-17 15:41:53.000000000 -0500
++++ qstat2/md5.h 2012-04-20 06:22:15.250385000 -0400
+@@ -21,7 +21,7 @@
+ ghost at aladdin.com
+
+ */
+-/* $Id: md5.h,v 1.1 2004/10/17 20:41:53 l-n Exp $ */
++/* $Id: md5.h 374 2012-04-20 10:22:15Z stevenhartland $ */
+ /*
+ Independent implementation of MD5 (RFC 1321).
+
+@@ -84,6 +84,9 @@
+ /* Finish the message and return the digest. */
+ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
++/* Return an hex md5 of the given string */
++char* md5_hex(const char *bytes, int nbytes);
++
+ #ifdef __cplusplus
+ } /* end extern "C" */
+ #endif
+Only in qstat-2.11: missing
+diff -urP qstat-2.11/mumble.c qstat2/mumble.c
+--- qstat-2.11/mumble.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/mumble.c 2012-02-07 08:55:59.913866000 -0500
+@@ -0,0 +1,70 @@
++/*
++ * qstat 2.11
++ * by Steve Jankowski
++ *
++ * Mumble protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++#ifndef _WIN32
++#include <arpa/inet.h>
++#endif
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++query_status_t send_mumble_request_packet( struct qserver *server )
++{
++ return send_packet( server, server->type->status_packet, server->type->status_len );
++}
++
++query_status_t deal_with_mumble_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++ // skip unimplemented ack, crc, etc
++ char *pkt = rawpkt;
++ char bandwidth[11];
++ char version[11];
++
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++ if ( 24 != pktlen || 0 != memcmp( pkt+4, server->type->status_packet+4, 8 ) )
++ {
++ // unknown packet
++ return PKT_ERROR;
++ }
++
++ // version
++ server->protocol_version = ntohl(*(unsigned long*)pkt);
++ sprintf(version,"%u.%u.%u", (unsigned char)*pkt+1, (unsigned char)*pkt+2, (unsigned char)*pkt+3);
++ add_rule( server, "version", version, NO_FLAGS );
++ pkt += 4;
++
++ // ident
++ pkt += 8;
++
++ // num players
++ server->num_players = ntohl(*(unsigned long*)pkt);
++ pkt += 4;
++
++ // max players
++ server->max_players = ntohl(*(unsigned long*)pkt);
++ pkt += 4;
++
++ // allowed bandwidth
++ sprintf( bandwidth, "%d", ntohl(*(unsigned long*)pkt));
++ add_rule( server, "allowed_bandwidth", bandwidth, NO_FLAGS );
++ pkt += 4;
++
++ // Unknown details
++ server->map_name = strdup( "N/A" );
++ server->server_name = strdup( "Unknown" );
++ add_rule( server, "gametype", "Unknown", NO_FLAGS );
++
++ return DONE_FORCE;
++}
+diff -urP qstat-2.11/mumble.h qstat2/mumble.h
+--- qstat-2.11/mumble.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/mumble.h 2012-01-20 05:33:19.302785000 -0500
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.11
++ * by Steve Jankowski
++ *
++ * Mumble protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_MUMBLE_H
++#define QSTAT_MUMBLE_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_mumble_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_mumble_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/ottd.c qstat2/ottd.c
+--- qstat-2.11/ottd.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ottd.c 2010-01-25 11:39:12.583979000 -0500
+@@ -0,0 +1,344 @@
++/*
++ * qstat 2.11
++ *
++ * opentTTD protocol
++ * Copyright 2007 Ludwig Nussel
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#endif
++#include <stdio.h>
++#include <stdlib.h>
++
++#include "qstat.h"
++#include "qserver.h"
++#include "debug.h"
++
++enum { MAX_VEHICLE_TYPES = 5, MAX_STATION_TYPES = 5 };
++
++static const char* vehicle_types[] = {
++ "num_trains",
++ "num_trucks",
++ "num_busses",
++ "num_aircrafts",
++ "num_ships",
++};
++
++static const char* station_types[] = {
++ "num_stations",
++ "num_truckbays",
++ "num_busstations",
++ "num_airports",
++ "num_docks",
++};
++
++query_status_t deal_with_ottdmaster_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++ unsigned num;
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++ server->server_name = MASTER;
++
++ if(swap_short_from_little(rawpkt) != pktlen)
++ {
++ malformed_packet( server, "invalid packet length" );
++ return PKT_ERROR;
++ }
++ if(rawpkt[2] != 7)
++ {
++ malformed_packet( server, "invalid packet type" );
++ return PKT_ERROR;
++ }
++
++ if(rawpkt[3] != 1)
++ {
++ malformed_packet( server, "invalid packet version" );
++ return PKT_ERROR;
++ }
++
++ num = swap_short_from_little(&rawpkt[4]);
++ rawpkt += 6;
++ pktlen -= 6;
++ if( num && num*6 <= pktlen )
++ {
++ unsigned i;
++ server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen );
++ memset(server->master_pkt + server->master_pkt_len, 0, pktlen );
++ server->master_pkt_len += pktlen;
++ for( i = 0; i < num * 6; i += 6 )
++ {
++ memcpy(&server->master_pkt[i], &rawpkt[i], 4);
++ server->master_pkt[i+4] = rawpkt[i+5];
++ server->master_pkt[i+5] = rawpkt[i+4];
++ }
++ server->n_servers += num;
++ }
++ else
++ {
++ malformed_packet( server, "invalid packet" );
++ return PKT_ERROR;
++ }
++
++ bind_sockets();
++
++ return DONE_AUTO;
++}
++
++#define xstr(s) str(s)
++#define str(s) #s
++
++#define GET_STRING do { \
++ str = (char*)ptr; \
++ ptr = memchr(ptr, '\0', end-ptr); \
++ if ( !ptr ) \
++ { \
++ malformed_packet( server, "%s:%s invalid packet", __FILE__, xstr(__LINE__) ); \
++ return PKT_ERROR; \
++ } \
++ ++ptr; \
++ } while(0)
++
++#define FAIL_IF(cond, msg) \
++ if((cond)) { \
++ malformed_packet( server, "%s:%s %s", __FILE__, xstr(__LINE__), msg ); \
++ return PKT_ERROR; \
++ }
++
++#define INVALID_IF(cond) \
++ FAIL_IF(cond, "invalid packet")
++
++query_status_t deal_with_ottd_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++ unsigned char *ptr = (unsigned char*)rawpkt;
++ unsigned char *end = (unsigned char*)(rawpkt + pktlen);
++ unsigned char type;
++ char* str;
++ char buf[32];
++ unsigned ver;
++
++ server->n_servers++;
++ if ( server->server_name == NULL)
++ {
++ server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
++ server->n_requests++;
++ }
++ else
++ {
++ gettimeofday( &server->packet_time1, NULL);
++ }
++
++ FAIL_IF(pktlen < 4 || swap_short_from_little(rawpkt) > pktlen, "invalid packet");
++
++ type = ptr[2];
++ ver = ptr[3];
++ ptr += 4;
++
++ debug(3, "len %hu type %hhu ver %hhu", swap_short_from_little(rawpkt), type, ver);
++
++ FAIL_IF(ver != 4 && ver != 5, "only version 4 and 5 servers are supported");
++
++ if(type == 1) // info packet
++ {
++ unsigned numgrf = *ptr;
++ FAIL_IF(ptr + numgrf * 20 + 1 > end, "invalid newgrf number");
++ ptr += numgrf * 20 + 1;
++
++ snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
++ add_rule(server, "date_days", buf, NO_FLAGS);
++ ptr += 4;
++
++ snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
++ add_rule(server, "startdate_days", buf, NO_FLAGS);
++ ptr += 4;
++
++ FAIL_IF(ptr + 3 > end, "invalid packet");
++
++ snprintf(buf, sizeof(buf), "%hhu", ptr[0]);
++ add_rule(server, "maxcompanies", buf, NO_FLAGS);
++ snprintf(buf, sizeof(buf), "%hhu", ptr[1]);
++ add_rule(server, "numcompanies", buf, NO_FLAGS);
++ server->max_spectators = ptr[2];
++ ptr += 3;
++
++ GET_STRING;
++ server->server_name = strdup(str);
++
++ GET_STRING;
++ add_rule(server, "version", str, NO_FLAGS);
++
++ FAIL_IF(ptr + 7 > end, "invalid packet");
++
++ {
++ static const char* langs[] = {
++ "any",
++ "English",
++ "German",
++ "French"
++ };
++ unsigned i = *ptr++;
++ if(i > 3) i = 0;
++ add_rule(server, "language", (char*)langs[i], NO_FLAGS);
++ }
++
++ add_rule(server, "password", *ptr++ ? "1" : "0", NO_FLAGS);
++
++ server->max_players = *ptr++;
++ server->num_players = *ptr++;
++ server->num_spectators = *ptr++;
++
++ GET_STRING;
++
++ server->map_name = strdup(str);
++
++ snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
++ add_rule(server, "map_width", buf, NO_FLAGS);
++ snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
++ add_rule(server, "map_height", buf, NO_FLAGS);
++
++ {
++ static const char* sets[] = {
++ "temperate",
++ "arctic",
++ "desert",
++ "toyland"
++ };
++ unsigned i = *ptr++;
++ if(i > 3) i = 0;
++ add_rule(server, "map_set", (char*)sets[i], NO_FLAGS);
++ }
++
++ add_rule(server, "dedicated", *ptr++ ? "1" : "0", NO_FLAGS);
++ }
++ else if(type == 3) // player packet
++ {
++ unsigned i, j;
++ INVALID_IF(ptr + 2 > end);
++
++ server->num_players = *ptr++;
++
++ for(i = 0; i < server->num_players; ++i)
++ {
++ unsigned long long lli;
++ struct player* player;
++ unsigned char nr;
++
++ nr = *ptr++;
++
++ debug(3, "player number %d", nr);
++ player = add_player(server, i);
++ FAIL_IF(!player, "can't allocate player");
++
++ GET_STRING;
++ player->name = strdup(str);
++ debug(3, "name %s", str);
++ player->frags = 0;
++
++ INVALID_IF(ptr + 4 + 3*8 + 2 + 1 + 2*MAX_VEHICLE_TYPES + 2*MAX_STATION_TYPES > end);
++
++ snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
++ player_add_info(player, "startdate", buf, 0);
++ ptr += 4;
++
++ lli = swap_long_from_little(ptr+4);
++ lli <<= 32;
++ lli += swap_long_from_little(ptr);
++ snprintf(buf, sizeof(buf), "%lld", lli);
++ player_add_info(player, "value", buf, 0);
++ ptr += 8;
++
++ lli = swap_long_from_little(ptr+4);
++ lli <<= 32;
++ lli = swap_long_from_little(ptr);
++ snprintf(buf, sizeof(buf), "%lld", lli);
++ player_add_info(player, "money", buf, 0);
++ ptr += 8;
++
++ lli = swap_long_from_little(ptr+4);
++ lli <<= 32;
++ lli += swap_long_from_little(ptr);
++ snprintf(buf, sizeof(buf), "%lld", lli);
++ player_add_info(player, "income", buf, 0);
++ ptr += 8;
++
++ snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
++ player_add_info(player, "performance", buf, 0);
++ ptr += 2;
++
++ player_add_info(player, "password", *ptr?"1":"0", 0);
++ ++ptr;
++
++ for (j = 0; j < MAX_VEHICLE_TYPES; ++j)
++ {
++ snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
++ player_add_info(player, (char*)vehicle_types[j], buf, 0);
++ ptr += 2;
++ }
++ for (j = 0; j < MAX_STATION_TYPES; ++j)
++ {
++ snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
++ player_add_info(player, (char*)station_types[j], buf, 0);
++ ptr += 2;
++ }
++
++ if (ver != 5)
++ {
++ // connections
++ while(ptr + 1 < end && *ptr)
++ {
++ ++ptr;
++ GET_STRING; // client name
++ debug(3, "%s played by %s", str, player->name);
++ GET_STRING; // id
++ INVALID_IF(ptr + 4 > end);
++ ptr += 4;
++ }
++
++ ++ptr; // record terminated by zero byte
++ }
++ }
++
++ // spectators
++ while(ptr + 1 < end && *ptr)
++ {
++ ++ptr;
++ GET_STRING; // client name
++ debug(3, "spectator %s", str);
++ GET_STRING; // id
++ INVALID_IF(ptr + 4 > end);
++ ptr += 4;
++ }
++ ++ptr; // record terminated by zero byte
++
++ server->next_rule = NO_SERVER_RULES; // we're done
++ server->next_player_info = server->num_players; // we're done
++ }
++ else
++ {
++ malformed_packet( server, "invalid type" );
++ return PKT_ERROR;
++ }
++
++ server->retry1 = n_retries; // we're done with this packet, reset retry counter
++
++ return DONE_AUTO;
++}
++
++query_status_t send_ottdmaster_request_packet(struct qserver *server)
++{
++ return qserver_send_initial(server, server->type->master_packet, server->type->master_len);
++}
++
++query_status_t send_ottd_request_packet(struct qserver *server)
++{
++ qserver_send_initial(server, server->type->status_packet, server->type->status_len);
++
++ if(get_server_rules || get_player_info)
++ {
++ server->next_rule = ""; // trigger calling send_a2s_rule_request_packet
++ }
++
++ return INPROGRESS;
++}
+diff -urP qstat-2.11/ottd.h qstat2/ottd.h
+--- qstat-2.11/ottd.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ottd.h 2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.11
++ *
++ * opentTTD protocol
++ * Copyright 2007 Ludwig Nussel
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_OTTD_H
++#define QSTAT_OTTD_H
++
++#include "qstat.h"
++
++query_status_t send_ottdmaster_request_packet(struct qserver *server);
++query_status_t deal_with_ottdmaster_packet(struct qserver *server, char *rawpkt, int pktlen);
++
++query_status_t send_ottd_request_packet(struct qserver *server);
++query_status_t deal_with_ottd_packet(struct qserver *server, char *rawpkt, int pktlen);
++
++#endif
+diff -urP qstat-2.11/packet_manip.c qstat2/packet_manip.c
+--- qstat-2.11/packet_manip.c 2006-08-14 04:51:21.000000000 -0500
++++ qstat2/packet_manip.c 2010-03-03 10:40:22.622169000 -0500
+@@ -15,7 +15,7 @@
+ #include <stdio.h>
+
+ #define MAX_PACKETS 8
+-#define MAX_FAGMENTS 16
++#define MAX_FAGMENTS 128
+
+ int pkt_seq = 0;
+ int pkt_id_index = -1;
+@@ -29,7 +29,7 @@
+ int maxes[MAX_PACKETS];
+ int lengths[MAX_PACKETS];
+ SavedData *sdata = &server->saved_data;
+- int i, p, done = 0;
++ int i, p, ret = INPROGRESS;
+ n_ids = 0;
+
+ memset( &segments[0][0], 0, sizeof(segments) );
+@@ -46,6 +46,13 @@
+ continue;
+ }
+
++ if ( sdata->pkt_index >= MAX_FAGMENTS )
++ {
++ // we only deal up to MAX_FAGMENTS packet fragment
++ fprintf( stderr, "Too many fragments %d for packetid %d max %d\n", sdata->pkt_index, sdata->pkt_id, MAX_FAGMENTS );
++ continue;
++ }
++
+ for ( i = 0; i < n_ids; i++ )
+ {
+ if ( sdata->pkt_id == ids[i])
+@@ -78,12 +85,6 @@
+ if ( segments[i][sdata->pkt_index] == NULL)
+ {
+ // add the packet to the list of segments
+- if ( sdata->pkt_index >= MAX_FAGMENTS )
+- {
+- // we only deal up to MAX_FAGMENTS packet fragment
+- fprintf( stderr, "Too many fragments for packetid %d max %d\n", sdata->pkt_id, MAX_FAGMENTS );
+- continue;
+- }
+ segments[i][sdata->pkt_index]= sdata;
+ counts[i]++;
+ lengths[i] += sdata->datalen;
+@@ -116,7 +117,7 @@
+ // reset to be unusable
+ pkt_id_index = -1;
+ free( combined );
+- return 0;
++ return INPROGRESS;
+ }
+ memcpy( combined + datalen, segments[pkt_id_index][p]->data, segments[pkt_id_index][p]->datalen );
+ datalen += segments[pkt_id_index][p]->datalen;
+@@ -133,13 +134,15 @@
+ {
+ print_packet( server, combined, datalen );
+ }
+- // Call the server's packet processing method
+- done = ( (int (*)()) server->type->packet_func)( server, combined, datalen );
++ // Call the server's packet processing method flagging as a combine call
++ server->combined = 1;
++ ret = ( (int (*)()) server->type->packet_func)( server, combined, datalen );
+ free( combined );
++ server->combined = 0;
+
+ // Note: this is currently invalid as packet processing methods
+ // are void not int
+- if ( done || server->saved_data.data == NULL)
++ if ( INPROGRESS != ret || NULL == server->saved_data.data )
+ {
+ break;
+ }
+@@ -147,9 +150,12 @@
+ // reset to be unusable
+ pkt_id_index = -1;
+
+- return done;
++ return ret;
+ }
+
++// NOTE:
++// pkt_id is the packet aka response identifier
++// pkt_index is the index of the packet fragment
+ int add_packet( struct qserver *server, unsigned int pkt_id, int pkt_index, int pkt_max, int datalen, char *data, int calc_max )
+ {
+ SavedData *sdata;
+@@ -196,7 +202,6 @@
+ if ( NULL == sdata->data )
+ {
+ fprintf( stderr, "Out of memory\n" );
+- cleanup_qserver( server, 1 );
+ return 0;
+ }
+
+@@ -240,3 +245,19 @@
+ }
+ return len;
+ }
++
++unsigned packet_count( struct qserver *server )
++{
++ SavedData *sdata = &server->saved_data;
++ unsigned cnt = 0;
++ if ( NULL == sdata->data )
++ {
++ return 0;
++ }
++
++ for ( ; sdata != NULL; sdata = sdata->next )
++ {
++ cnt++;
++ }
++ return cnt;
++}
+diff -urP qstat-2.11/packet_manip.h qstat2/packet_manip.h
+--- qstat-2.11/packet_manip.h 2006-08-14 04:51:21.000000000 -0500
++++ qstat2/packet_manip.h 2009-12-21 13:38:56.452334000 -0500
+@@ -18,5 +18,6 @@
+ int next_sequence();
+ SavedData* get_packet_fragment( int index );
+ unsigned combined_length( struct qserver *server, int pkt_id );
++unsigned packet_count( struct qserver *server );
+
+ #endif
+diff -urP qstat-2.11/qserver.c qstat2/qserver.c
+--- qstat-2.11/qserver.c 2006-05-24 09:39:41.000000000 -0500
++++ qstat2/qserver.c 2009-07-02 06:12:36.820869000 -0400
+@@ -18,82 +18,106 @@
+ #endif
+ #include <stdio.h>
+ #include <stdlib.h>
++#include <errno.h>
++#include <string.h>
+ #include <sys/types.h>
+
+-int qserver_send_initial(struct qserver* server, const char* data, size_t len)
++// TODO: get rid of this and use send_packet instead, remove n_requests hack from a2s
++query_status_t qserver_send_initial(struct qserver* server, const char* data, size_t len)
+ {
+- int ret = 0;
++ int status = INPROGRESS;
+
+- if(data)
++ if( data )
+ {
+- if ( server->flags & FLAG_BROADCAST)
+- ret = send_broadcast(server, data, len);
+- else
+- ret = send( server->fd, data, len, 0);
++ int ret;
++ debug( 2, "[%s] send", server->type->type_prefix );
++ if( 4 <= get_debug_level() )
++ {
++ output_packet( server, data, len, 1 );
++ }
+
+- if ( ret == SOCKET_ERROR)
+- {
+- perror( "send");
+- }
++ if ( server->flags & FLAG_BROADCAST)
++ {
++ ret = send_broadcast(server, data, len);
++ }
++ else
++ {
++ ret = send( server->fd, data, len, 0);
++ }
++
++ if ( ret == SOCKET_ERROR)
++ {
++ send_error( server, ret );
++ status = SYS_ERROR;
++ }
+ }
+
+ if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST)
+ {
+- gettimeofday( &server->packet_time1, NULL);
++ gettimeofday( &server->packet_time1, NULL);
+ }
+ else
+ {
+- server->n_retries++;
++ server->n_retries++;
+ }
+
+ server->retry1--;
+ server->n_packets++;
+
+- return ret;
++ return status;
+ }
+
+-int qserver_send(struct qserver* server, const char* data, size_t len)
++query_status_t qserver_send(struct qserver* server, const char* data, size_t len)
+ {
+- int ret = 0;
++ int status = INPROGRESS;
+
+ if(data)
+ {
+- if ( server->flags & FLAG_BROADCAST)
+- ret = send_broadcast(server, data, len);
+- else
+- ret = send( server->fd, data, len, 0);
++ int ret;
++ if ( server->flags & FLAG_BROADCAST)
++ {
++ ret = send_broadcast(server, data, len);
++ }
++ else
++ {
++ ret = send( server->fd, data, len, 0);
++ }
+
+- if ( ret == SOCKET_ERROR)
+- {
+- perror( "send");
+- }
++ if ( ret == SOCKET_ERROR)
++ {
++ send_error( server, ret );
++ status = SYS_ERROR;
++ }
+ }
+
+- server->retry1 = n_retries;
++ server->retry1 = n_retries-1;
+ gettimeofday( &server->packet_time1, NULL);
+ server->n_requests++;
+ server->n_packets++;
+
+- return ret;
++ return status;
+
+ }
+
+-int
+-send_broadcast( struct qserver *server, const char *pkt, size_t pktlen)
++int send_broadcast( struct qserver *server, const char *pkt, size_t pktlen)
+ {
+ struct sockaddr_in addr;
+ addr.sin_family= AF_INET;
+ if ( no_port_offset || server->flags & TF_NO_PORT_OFFSET)
++ {
+ addr.sin_port= htons(server->port);
++ }
+ else
++ {
+ addr.sin_port= htons((unsigned short)( server->port + server->type->port_offset ));
++ }
+ addr.sin_addr.s_addr= server->ipaddr;
+ memset( &(addr.sin_zero), 0, sizeof(addr.sin_zero));
+- return sendto( server->fd, (const char*) pkt, pktlen, 0,
+- (struct sockaddr *) &addr, sizeof(addr));
++
++ return sendto( server->fd, (const char*) pkt, pktlen, 0, (struct sockaddr *) &addr, sizeof(addr));
+ }
+
+-void register_send( struct qserver *server )
++int register_send( struct qserver *server )
+ {
+ if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST )
+ {
+@@ -109,19 +133,21 @@
+ gettimeofday( &server->packet_time1, NULL);
+ server->retry1--;
+ server->n_packets++;
++
++ return INPROGRESS;
+ }
+
+-int send_packet( struct qserver* server, const char* data, size_t len )
++query_status_t send_packet( struct qserver* server, const char* data, size_t len )
+ {
+- int ret = 0;
+-
+ debug( 2, "[%s] send", server->type->type_prefix );
+ if( 4 <= get_debug_level() )
+ {
+- print_packet( server, data, len );
++ output_packet( server, data, len, 1 );
+ }
++
+ if( data )
+ {
++ int ret;
+ if ( server->flags & FLAG_BROADCAST )
+ {
+ ret = send_broadcast( server, data, len );
+@@ -133,18 +159,25 @@
+
+ if ( ret == SOCKET_ERROR )
+ {
+- unsigned int ipaddr = ntohl(server->ipaddr) ;
+- fprintf( stderr, "Error on %d.%d.%d.%d\n",
+- (ipaddr>>24)&0xff,
+- (ipaddr>>16)&0xff,
+- (ipaddr>>8)&0xff,
+- ipaddr&0xff
+- );
+- perror( "send" );
++ return send_error( server, ret );
+ }
+ }
+
+ register_send( server );
+
+- return ret;
++ return INPROGRESS;
++}
++
++query_status_t send_error( struct qserver *server, int rc )
++{
++ unsigned int ipaddr = ntohl(server->ipaddr);
++ const char* errstr = strerror(errno);
++ fprintf(stderr, "Error on %d.%d.%d.%d: %s, skipping ...\n",
++ (ipaddr >> 24) &0xff,
++ (ipaddr >> 16) &0xff,
++ (ipaddr >> 8) &0xff,
++ ipaddr &0xff,
++ errstr
++ );
++ return SYS_ERROR;
+ }
+diff -urP qstat-2.11/qserver.h qstat2/qserver.h
+--- qstat-2.11/qserver.h 2006-05-24 09:26:00.000000000 -0500
++++ qstat2/qserver.h 2013-02-13 07:58:41.414304000 -0500
+@@ -27,6 +27,17 @@
+ struct SavedData *next;
+ } SavedData;
+
++typedef enum {
++ STATE_INIT = 0,
++ STATE_CONNECTING = 1,
++ STATE_CONNECTED = 2,
++ STATE_QUERYING = 3,
++ STATE_QUERIED = 4,
++
++ STATE_TIMEOUT = -1,
++ STATE_SYS_ERROR = -2,
++} server_state_t;
++
+ struct qserver {
+ char *arg;
+ char *host_name;
+@@ -34,12 +45,21 @@
+ int flags;
+ server_type * type;
+ int fd;
++ server_state_t state;
+ char *outfilename;
+ char *query_arg;
++ char *challenge_string;
+ struct query_param *params;
+ unsigned long challenge;
+ unsigned short port;
++ unsigned short orig_port; // This port is always constant from creation where as port can be updated based on the query results
+ unsigned short query_port;
++
++ // State variable to flag if the current processing call is a call from combine_packets
++ // This should really by done via a flag to the method itself but that would require changes
++ // to all handlers :(
++ unsigned short combined;
++
+ /** \brief number of retries _left_ for status query or rule query.
+ *
+ * That means
+@@ -103,6 +123,8 @@
+ int max_players;
+ int num_players;
+ int protocol_version;
++ int max_spectators;
++ int num_spectators;
+
+ SavedData saved_data;
+
+@@ -166,7 +188,7 @@
+ * \param len length of data
+ * \returns number of bytes sent or SOCKET_ERROR
+ */
+-int qserver_send_initial(struct qserver* server, const char* data, size_t len);
++query_status_t qserver_send_initial(struct qserver* server, const char* data, size_t len);
+
+ /** \brief send an initial query packet to a server
+ *
+@@ -176,7 +198,7 @@
+ *
+ * @see qserver_send_initial
+ */
+-int qserver_send(struct qserver* server, const char* data, size_t len);
++query_status_t qserver_send(struct qserver* server, const char* data, size_t len);
+
+ int send_broadcast( struct qserver *server, const char *pkt, size_t pktlen);
+
+@@ -185,13 +207,18 @@
+ *
+ * This updates n_requests, n_packets, packet_time1 and decrements n_retries
+ */
+-void register_send( struct qserver *server );
++int register_send( struct qserver *server );
+
+ /**
+ * Sends a packet to the server either direct or via broadcast.
+ *
+ * Once sent calls register_send to register the send of the packet
+ */
+-int send_packet( struct qserver* server, const char* data, size_t len );
++query_status_t send_packet( struct qserver* server, const char* data, size_t len );
++
++/**
++ * Logs the error from a socket send
++ */
++query_status_t send_error( struct qserver *server, int rc );
+
+ #endif
+diff -urP qstat-2.11/qstat.c qstat2/qstat.c
+--- qstat-2.11/qstat.c 2006-10-28 07:37:18.000000000 -0500
++++ qstat2/qstat.c 2013-02-14 13:44:39.932703000 -0500
+@@ -17,19 +17,11 @@
+ * Licensed under the Artistic License, see LICENSE.txt for license terms
+ */
+
+-#ifdef HAVE_CONFIG_H
+-# include "gnuconfig.h"
+-#else
+-# ifndef VERSION
+-# define VERSION "2.11"
+-# endif
+-#endif
+-
+-char *qstat_version= VERSION;
++#define RECV_BUF 204800
+
+ /* OS/2 defines */
+ #ifdef __OS2__
+-#define BSD_SELECT
++ #define BSD_SELECT
+ #endif
+
+ #include <stdio.h>
+@@ -48,210 +40,211 @@
+
+
+ #ifndef _WIN32
+-#include <signal.h>
+-#include <unistd.h>
+-#include <sys/socket.h>
+-#ifndef VMS
+-#include <sys/param.h>
+-#endif
+-#include <sys/time.h>
+-#include <netinet/in.h>
+-#include <netinet/tcp.h>
+-#include <netdb.h>
+-#include <arpa/inet.h>
+-
+-#ifndef F_SETFL
+-#include <fcntl.h>
+-#endif
+-
+-#ifdef __hpux
+-extern int h_errno;
+-#define STATIC static
+-#else
+-#define STATIC
+-#endif
+-
+-#define INVALID_SOCKET -1
+-#ifndef INADDR_NONE
+-#define INADDR_NONE ~0
+-#endif
+-#define sockerr() errno
++ #include <signal.h>
++ #include <unistd.h>
++ #include <sys/socket.h>
++ #ifndef VMS
++ #include <sys/param.h>
++ #endif
++ #include <sys/time.h>
++ #include <netinet/in.h>
++ #include <netinet/tcp.h>
++ #include <netdb.h>
++ #include <arpa/inet.h>
++
++ #ifndef F_SETFL
++ #include <fcntl.h>
++ #endif
++
++ #ifdef __hpux
++ extern int h_errno;
++ #define STATIC static
++ #else
++ #define STATIC
++ #endif
++
++ #define INVALID_SOCKET -1
++ #ifndef INADDR_NONE
++ #define INADDR_NONE ~0
++ #endif
++ #define sockerr() errno
+ #endif /* _WIN32 */
+
+ #ifdef __OS2__
+-#include <sys/socket.h>
+-#include <sys/select.h>
+-#include <sys/time.h>
+-#include <netinet/in.h>
+-#include <netdb.h>
+-#include <utils.h>
+-
+-#define INVALID_SOCKET -1
+-#define SOCKET_ERROR -1
+-#define close(a) soclose(a)
++ #include <sys/socket.h>
++ #include <sys/select.h>
++ #include <sys/time.h>
++ #include <netinet/in.h>
++ #include <netdb.h>
++ #include <utils.h>
++
++ #define INVALID_SOCKET -1
++ #define SOCKET_ERROR -1
++ #define close(a) soclose(a)
+ #endif /* __OS2__ */
+
+
++
+ #ifndef FD_SETSIZE
+-#define FD_SETSIZE 64
++ #define FD_SETSIZE 64
+ #endif
+
+ /* Figure out whether to use poll() or select()
+-*/
++ */
+ #ifndef USE_POLL
+-#ifndef USE_SELECT
++ #ifndef USE_SELECT
+
+-#ifdef sun
+-#define USE_POLL
+-#endif
+-#ifdef linux
+-#define USE_POLL
+-#include <poll.h>
+-#endif
+-#ifdef __linux__
+-#define USE_POLL
+-#include <poll.h>
+-#endif
+-#ifdef __linux
+-#define USE_POLL
+-#include <poll.h>
+-#endif
+-#ifdef __hpux
+-#define USE_POLL
+-#include <sys/poll.h>
+-#endif
+-#ifdef __OpenBSD__
+-#define USE_POLL
+-#include <sys/poll.h>
+-#endif
+-#ifdef _AIX
+-#define USE_POLL
+-#include <poll.h>
+-#endif
+-#ifdef _WIN32
+-#define USE_SELECT
+-#endif
+-#ifdef __EMX__
+-#define USE_SELECT
+-#endif
++ #ifdef sun
++ #define USE_POLL
++ #endif
++ #ifdef linux
++ #define USE_POLL
++ #include <poll.h>
++ #endif
++ #ifdef __linux__
++ #define USE_POLL
++ #include <poll.h>
++ #endif
++ #ifdef __linux
++ #define USE_POLL
++ #include <poll.h>
++ #endif
++ #ifdef __hpux
++ #define USE_POLL
++ #include <sys/poll.h>
++ #endif
++ #ifdef __OpenBSD__
++ #define USE_POLL
++ #include <sys/poll.h>
++ #endif
++ #ifdef _AIX
++ #define USE_POLL
++ #include <poll.h>
++ #endif
++ #ifdef _WIN32
++ #define USE_SELECT
++ #endif
++ #ifdef __EMX__
++ #define USE_SELECT
++ #endif
+
+-#endif /* USE_SELECT */
++ #endif /* USE_SELECT */
+ #endif /* USE_POLL */
+
+ /* If did not chose, then use select()
+-*/
++ */
+ #ifndef USE_POLL
+-#ifndef USE_SELECT
+-#define USE_SELECT
+-#endif
+-#endif
+-
+-#ifdef ENABLE_DUMP
+-#ifndef _WIN32
+-#include <sys/mman.h>
+-#include <unistd.h>
+-#endif
+-#include <sys/types.h>
+-#include <sys/stat.h>
+-static int do_dump;
++ #ifndef USE_SELECT
++ #define USE_SELECT
++ #endif
+ #endif
+
+ #include "debug.h"
+
+ server_type *types;
+ int n_server_types;
++char *qstat_version = VERSION;
+
+ /*
+ * Values set by command-line arguments
+ */
+
+-
+-int hostname_lookup= 0; /* set if -H was specified */
+-int new_style= 1; /* unset if -old was specified */
+-int n_retries= DEFAULT_RETRIES;
+-int retry_interval= DEFAULT_RETRY_INTERVAL;
+-int master_retry_interval= DEFAULT_RETRY_INTERVAL*4;
+-int get_player_info= 0;
+-int get_server_rules= 0;
+-int up_servers_only= 0;
+-int no_full_servers= 0;
+-int no_empty_servers= 0;
+-int no_header_display= 0;
+-int raw_display= 0;
+-char *raw_delimiter= "\t";
++int hostname_lookup = 0; /* set if -H was specified */
++int new_style = 1; /* unset if -old was specified */
++int n_retries = DEFAULT_RETRIES;
++int retry_interval = DEFAULT_RETRY_INTERVAL;
++int master_retry_interval = DEFAULT_RETRY_INTERVAL * 4;
++int syncconnect = 0;
++int get_player_info = 0;
++int get_server_rules = 0;
++int up_servers_only = 0;
++int no_full_servers = 0;
++int no_empty_servers = 0;
++int no_header_display = 0;
++int raw_display = 0;
++char *raw_delimiter = "\t";
+ char *multi_delimiter = "|";
+-int player_address= 0;
+-int hex_player_names= 0;
+-int hex_server_names= 0;
+-int name_xforms= 1;
+-int strip_carets= 1;
+-int max_simultaneous= MAXFD_DEFAULT;
++int player_address = 0;
++int hex_player_names = 0;
++int hex_server_names = 0;
++int name_xforms = 1;
++int name_xforms_strip_unprintable = 0; /* xform strips unprintable chars */
++int strip_carets = 1;
++int max_simultaneous = MAXFD_DEFAULT;
+ int sendinterval = 5;
+-int html_names= -1;
++int html_names = -1;
+ extern int html_mode;
+-int raw_arg= 0;
+-int show_game_in_raw= 0;
+-int progress= 0;
+-int num_servers_total= 0;
+-int num_players_total= 0;
+-int max_players_total= 0;
+-int num_servers_returned= 0;
+-int num_servers_timed_out= 0;
+-int num_servers_down= 0;
+-server_type* default_server_type= NULL;
+-FILE *OF; /* output file */
+-unsigned int source_ip= INADDR_ANY;
+-unsigned short source_port_low= 0;
+-unsigned short source_port_high= 0;
+-unsigned short source_port= 0;
++int raw_arg = 0;
++int show_game_in_raw = 0;
++int progress = 0;
++int num_servers_total = 0;
++int num_players_total = 0;
++int max_players_total = 0;
++int num_servers_returned = 0;
++int num_servers_timed_out = 0;
++int num_servers_down = 0;
++server_type *default_server_type = NULL;
++FILE *OF; /* output file */
++unsigned int source_ip = INADDR_ANY;
++unsigned short source_port_low = 0;
++unsigned short source_port_high = 0;
++unsigned short source_port = 0;
+ int show_game_port = 0;
+ int no_port_offset = 0;
+
+-#define ENCODING_LATIN_1 1
+-#define ENCODING_UTF_8 8
+-int xml_display= 0;
+-int xml_encoding= ENCODING_LATIN_1;
++#define ENCODING_LATIN_1 1
++#define ENCODING_UTF_8 8
++#define UTF8BYTESWAPNOTACHAR 0xFFFE
++#define UTF8NOTACHAR 0xFFFF
++#define UTF8MAXFROMUCS4 0x10FFFF
++
++int output_bom = 0;
++int xml_display = 0;
++int xml_encoding = ENCODING_LATIN_1;
+
+ #define SUPPORTED_SERVER_SORT "pgihn"
+ #define SUPPORTED_PLAYER_SORT "PFTNS"
+ #define SUPPORTED_SORT_KEYS "l" SUPPORTED_SERVER_SORT SUPPORTED_PLAYER_SORT
+ char sort_keys[32];
+-int player_sort= 0;
+-int server_sort= 0;
++int player_sort = 0;
++int server_sort = 0;
+
+-void quicksort( void **array, int i, int j, int (*compare)(void*,void*));
+-int qpartition( void **array, int i, int j, int (*compare)(void*,void*));
+-void sort_servers( struct qserver **array, int size);
+-void sort_players( struct qserver *server);
+-int server_compare( struct qserver *one, struct qserver *two);
+-int player_compare( struct player *one, struct player *two);
+-int type_option_compare( server_type *one, server_type *two );
+-int type_string_compare( server_type *one, server_type *two );
+-STATIC int u2xmp_html_color( short color, char *dest, int *font_tag );
+-STATIC int ut2k4_html_color( const unsigned char *color, char *dest, int *font_tag );
++void quicksort(void **array, int i, int j, int(*compare)(void *, void*));
++int qpartition(void **array, int i, int j, int(*compare)(void *, void*));
++void sort_servers(struct qserver **array, int size);
++void sort_players(struct qserver *server);
++int server_compare(struct qserver *one, struct qserver *two);
++int player_compare(struct player *one, struct player *two);
++int type_option_compare(server_type *one, server_type *two);
++int type_string_compare(server_type *one, server_type *two);
++int process_func_ret( struct qserver *server, int ret );
++int connection_inprogress();
++void clear_socketerror();
++STATIC int u2xmp_html_color(short color, char *dest, int *font_tag);
++STATIC int ut2k4_html_color(const unsigned char *color, char *dest, int *font_tag);
+
+-int show_errors= 0;
++int show_errors = 0;
+ static int noserverdups = 1;
+
+ #define DEFAULT_COLOR_NAMES_RAW 0
+ #define DEFAULT_COLOR_NAMES_DISPLAY 1
+-int color_names= -1;
++int color_names = -1;
+
+ #define SECONDS 0
+ #define CLOCK_TIME 1
+ #define STOPWATCH_TIME 2
+ #define DEFAULT_TIME_FMT_RAW SECONDS
+ #define DEFAULT_TIME_FMT_DISPLAY CLOCK_TIME
+-int time_format= -1;
++int time_format = -1;
+
+-struct qserver *servers= NULL;
+-struct qserver **last_server= &servers;
+-struct qserver **connmap= NULL;
++struct qserver *servers = NULL;
++struct qserver **last_server = &servers;
++struct qserver **connmap = NULL;
+ int max_connmap;
+-struct qserver *last_server_bind= NULL;
+-struct qserver *first_server_bind= NULL;
+-int connected= 0;
+-time_t run_timeout= 0;
++struct qserver *last_server_bind = NULL;
++struct qserver *first_server_bind = NULL;
++int connected = 0;
++time_t run_timeout = 0;
+ time_t start_time;
+ int waiting_for_masters;
+
+@@ -260,725 +253,550 @@
+ static struct qserver **server_hash[ADDRESS_HASH_LENGTH];
+ static unsigned int server_hash_len[ADDRESS_HASH_LENGTH];
+ static void free_server_hash();
+-static void xml_display_player_info_info(struct player* player);
++static void xml_display_player_info_info(struct player *player);
+
+-char *DOWN= "DOWN";
+-char *SYSERROR= "SYSERROR";
+-char *TIMEOUT= "TIMEOUT";
+-char *MASTER= "MASTER";
+-char *SERVERERROR= "ERROR";
+-char *HOSTNOTFOUND= "HOSTNOTFOUND";
+-char *BFRIS_SERVER_NAME= "BFRIS Server";
+-char *GAMESPY_MASTER_NAME= "Gamespy Master";
++char *DOWN = "DOWN";
++char *SYSERROR = "SYSERROR";
++char *TIMEOUT = "TIMEOUT";
++char *MASTER = "MASTER";
++char *SERVERERROR = "ERROR";
++char *HOSTNOTFOUND = "HOSTNOTFOUND";
++char *BFRIS_SERVER_NAME = "BFRIS Server";
++char *GAMESPY_MASTER_NAME = "Gamespy Master";
+
+-int display_prefix= 0;
++int display_prefix = 0;
+ char *current_filename;
+ int current_fileline;
+
+-int count_bits( int n);
++int count_bits(int n);
+
+-static int qserver_get_timeout(struct qserver* server, struct timeval* now);
+-static int wait_for_timeout( unsigned int ms);
++static int qserver_get_timeout(struct qserver *server, struct timeval *now);
++static int wait_for_timeout(unsigned int ms);
+ static void finish_output();
+-static void decode_stefmaster_packet( struct qserver *server, char *pkt, int pktlen);
+-static void decode_q3master_packet( struct qserver *server, char *pkt, int pktlen);
++static int decode_stefmaster_packet(struct qserver *server, char *pkt, int pktlen);
++static int decode_q3master_packet(struct qserver *server, char *ikt, int pktlen);
+
+-char * ut2003_strdup( const char *string, const char *end, char **next );
+-int html_entity( const char c, char *dest );
++char *ut2003_strdup(const char *string, const char *end, char **next);
++int html_entity(const char c, char *dest);
+
+-static const char * unreal_colors[] = {
+- "AliceBlue",
+- "AntiqueWhite",
+- "Aqua",
+- "Aquamarine",
+- "Azure",
+- "Beige",
+- "Bisque",
+- "Black",
+- "BlanchedAlmond",
+- "Blue",
+- "BlueViolet",
+- "Brown",
+- "BurlyWood",
+- "CadetBlue",
+- "Chartreuse",
+- "Chocolate",
+- "Coral",
+- "CornflowerBlue",
+- "Cornsilk",
+- "Crimson",
+- "Cyan",
+- "DarkBlue",
+- "DarkCyan",
+- "DarkGoldenrod",
+- "DarkGray",
+- "DarkGreen",
+- "DarkKhaki",
+- "DarkMagenta",
+- "DarkOliveGreen",
+- "DarkOrange",
+- "DarkOrchid",
+- "DarkRed",
+- "DarkSalmon",
+- "DarkSeaGreen",
+- "DarkSlateBlue",
+- "DarkSlateGray",
+- "DarkTurquoise",
+- "DarkViolet",
+- "DeepPink",
+- "DeepSkyBlue",
+- "DimGray",
+- "DodgerBlue",
+- "FireBrick",
+- "FloralWhite",
+- "ForestGreen",
+- "Fuchsia",
+- "Gainsboro",
+- "GhostWhite",
+- "Gold",
+- "Goldenrod",
+- "Gray",
+- "Green",
+- "GreenYellow",
+- "Honeydew",
+- "HotPink",
+- "IndianRed",
+- "Indigo",
+- "Ivory",
+- "Khaki",
+- "Lavender",
+- "LavenderBlush",
+- "LawnGreen",
+- "LemonChiffon",
+- "LightBlue",
+- "LightCoral",
+- "LightCyan",
+- "LightGoldenrodYellow",
+- "LightGreen",
+- "LightGrey",
+- "LightPink",
+- "LightSalmon",
+- "LightSeaGreen",
+- "LightSkyBlue",
+- "LightSlateGray",
+- "LightSteelBlue",
+- "LightYellow",
+- "Lime",
+- "LimeGreen",
+- "Linen",
+- "Magenta",
+- "Maroon",
+- "MediumAquamarine",
+- "MediumBlue",
+- "MediumOrchid",
+- "MediumPurple",
+- "MediumSeaGreen",
+- "MediumSlateBlue",
+- "MediumSpringGreen",
+- "MediumTurquoise",
+- "MediumVioletRed",
+- "MidnightBlue",
+- "MintCream",
+- "MistyRose",
+- "Moccasin",
+- "NavajoWhite",
+- "Navy",
+- "OldLace",
+- "Olive",
+- "OliveDrab",
+- "Orange",
+- "OrangeRed",
+- "Orchid",
+- "PaleGoldenrod",
+- "PaleGreen",
+- "PaleTurquoise",
+- "PaleVioletRed",
+- "PapayaWhip",
+- "PeachPuff",
+- "Peru",
+- "Pink",
+- "Plum",
+- "PowderBlue",
+- "Purple",
+- "Red",
+- "RosyBrown",
+- "RoyalBlue",
+- "SaddleBrown",
+- "Salmon",
+- "SandyBrown",
+- "SeaGreen",
+- "Seashell",
+- "Sienna",
+- "Silver",
+- "SkyBlue",
+- "SlateBlue",
+- "SlateGray",
+- "Snow",
+- "SpringGreen",
+- "SteelBlue",
+- "Tan",
+- "Teal",
+- "Thistle",
+- "Tomato",
+- "Turquoise",
+- "Violet",
+- "Wheat",
+- "White",
+- "WhiteSmoke",
+- "Yellow",
+- "YellowGreen",
++static const char *unreal_colors[] =
++{
++ "AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "BlueViolet",
++ "Brown", "BurlyWood", "CadetBlue", "Chartreuse", "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan",
++ "DarkBlue", "DarkCyan", "DarkGoldenrod", "DarkGray", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "DarkOrange",
++ "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", "DarkSlateBlue", "DarkSlateGray", "DarkTurquoise", "DarkViolet",
++ "DeepPink", "DeepSkyBlue", "DimGray", "DodgerBlue", "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro",
++ "GhostWhite", "Gold", "Goldenrod", "Gray", "Green", "GreenYellow", "Honeydew", "HotPink", "IndianRed", "Indigo", "Ivory",
++ "Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan",
++ "LightGoldenrodYellow", "LightGreen", "LightGrey", "LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue",
++ "LightSlateGray", "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon", "MediumAquamarine",
++ "MediumBlue", "MediumOrchid", "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise",
++ "MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab",
++ "Orange", "OrangeRed", "Orchid", "PaleGoldenrod", "PaleGreen", "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff",
++ "Peru", "Pink", "Plum", "PowderBlue", "Purple", "Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", "SandyBrown",
++ "SeaGreen", "Seashell", "Sienna", "Silver", "SkyBlue", "SlateBlue", "SlateGray", "Snow", "SpringGreen", "SteelBlue", "Tan",
++ "Teal", "Thistle", "Tomato", "Turquoise", "Violet", "Wheat", "White", "WhiteSmoke", "Yellow", "YellowGreen",
+ };
+
+-static const char *unreal_rgb_colors[] = {
+- "#F0F8FF",
+- "#FAEBD7",
+- "#00FFFF",
+- "#7FFFD4",
+- "#F0FFFF",
+- "#F5F5DC",
+- "#FFE4C4",
+- "#000000",
+- "#FFEBCD",
+- "#0000FF",
+- "#8A2BE2",
+- "#A52A2A",
+- "#DEB887",
+- "#5F9EA0",
+- "#7FFF00",
+- "#D2691E",
+- "#FF7F50",
+- "#6495ED",
+- "#FFF8DC",
+- "#DC143C",
+- "#00FFFF",
+- "#00008B",
+- "#008B8B",
+- "#B8860B",
+- "#A9A9A9",
+- "#006400",
+- "#BDB76B",
+- "#8B008B",
+- "#556B2F",
+- "#FF8C00",
+- "#9932CC",
+- "#8B0000",
+- "#E9967A",
+- "#8FBC8F",
+- "#483D8B",
+- "#2F4F4F",
+- "#00CED1",
+- "#9400D3",
+- "#FF1493",
+- "#00BFFF",
+- "#696969",
+- "#1E90FF",
+- "#B22222",
+- "#FFFAF0",
+- "#228B22",
+- "#FF00FF",
+- "#DCDCDC",
+- "#F8F8FF",
+- "#FFD700",
+- "#DAA520",
+- "#808080",
+- "#008000",
+- "#ADFF2F",
+- "#F0FFF0",
+- "#FF69B4",
+- "#CD5C5C",
+- "#4B0082",
+- "#FFFFF0",
+- "#F0E68C",
+- "#E6E6FA",
+- "#FFF0F5",
+- "#7CFC00",
+- "#FFFACD",
+- "#ADD8E6",
+- "#F08080",
+- "#E0FFFF",
+- "#FAFAD2",
+- "#90EE90",
+- "#D3D3D3",
+- "#FFB6C1",
+- "#FFA07A",
+- "#20B2AA",
+- "#87CEFA",
+- "#778899",
+- "#B0C4DE",
+- "#FFFFE0",
+- "#00FF00",
+- "#32CD32",
+- "#FAF0E6",
+- "#FF00FF",
+- "#800000",
+- "#66CDAA",
+- "#0000CD",
+- "#BA55D3",
+- "#9370DB",
+- "#3CB371",
+- "#7B68EE",
+- "#00FA9A",
+- "#48D1CC",
+- "#C71585",
+- "#191970",
+- "#F5FFFA",
+- "#FFE4E1",
+- "#FFE4B5",
+- "#FFDEAD",
+- "#000080",
+- "#FDF5E6",
+- "#808000",
+- "#6B8E23",
+- "#FFA500",
+- "#FF4500",
+- "#DA70D6",
+- "#EEE8AA",
+- "#98FB98",
+- "#AFEEEE",
+- "#DB7093",
+- "#FFEFD5",
+- "#FFDAB9",
+- "#CD853F",
+- "#FFC0CB",
+- "#DDA0DD",
+- "#B0E0E6",
+- "#800080",
+- "#FF0000",
+- "#BC8F8F",
+- "#4169E1",
+- "#8B4513",
+- "#FA8072",
+- "#F4A460",
+- "#2E8B57",
+- "#FFF5EE",
+- "#A0522D",
+- "#C0C0C0",
+- "#87CEEB",
+- "#6A5ACD",
+- "#708090",
+- "#FFFAFA",
+- "#00FF7F",
+- "#4682B4",
+- "#D2B48C",
+- "#008080",
+- "#D8BFD8",
+- "#FF6347",
+- "#40E0D0",
+- "#EE82EE",
+- "#F5DEB3",
+- "#FFFFFF",
+- "#F5F5F5",
+- "#FFFF00",
+- "#9ACD32",
++static const char *unreal_rgb_colors[] =
++{
++ "#F0F8FF", "#FAEBD7", "#00FFFF", "#7FFFD4", "#F0FFFF", "#F5F5DC", "#FFE4C4", "#000000", "#FFEBCD", "#0000FF", "#8A2BE2", "#A52A2A",
++ "#DEB887", "#5F9EA0", "#7FFF00", "#D2691E", "#FF7F50", "#6495ED", "#FFF8DC", "#DC143C", "#00FFFF", "#00008B", "#008B8B",
++ "#B8860B", "#A9A9A9", "#006400", "#BDB76B", "#8B008B", "#556B2F", "#FF8C00", "#9932CC", "#8B0000", "#E9967A", "#8FBC8F",
++ "#483D8B", "#2F4F4F", "#00CED1", "#9400D3", "#FF1493", "#00BFFF", "#696969", "#1E90FF", "#B22222", "#FFFAF0", "#228B22",
++ "#FF00FF", "#DCDCDC", "#F8F8FF", "#FFD700", "#DAA520", "#808080", "#008000", "#ADFF2F", "#F0FFF0", "#FF69B4", "#CD5C5C",
++ "#4B0082", "#FFFFF0", "#F0E68C", "#E6E6FA", "#FFF0F5", "#7CFC00", "#FFFACD", "#ADD8E6", "#F08080", "#E0FFFF", "#FAFAD2",
++ "#90EE90", "#D3D3D3", "#FFB6C1", "#FFA07A", "#20B2AA", "#87CEFA", "#778899", "#B0C4DE", "#FFFFE0", "#00FF00", "#32CD32",
++ "#FAF0E6", "#FF00FF", "#800000", "#66CDAA", "#0000CD", "#BA55D3", "#9370DB", "#3CB371", "#7B68EE", "#00FA9A", "#48D1CC",
++ "#C71585", "#191970", "#F5FFFA", "#FFE4E1", "#FFE4B5", "#FFDEAD", "#000080", "#FDF5E6", "#808000", "#6B8E23", "#FFA500",
++ "#FF4500", "#DA70D6", "#EEE8AA", "#98FB98", "#AFEEEE", "#DB7093", "#FFEFD5", "#FFDAB9", "#CD853F", "#FFC0CB", "#DDA0DD",
++ "#B0E0E6", "#800080", "#FF0000", "#BC8F8F", "#4169E1", "#8B4513", "#FA8072", "#F4A460", "#2E8B57", "#FFF5EE", "#A0522D",
++ "#C0C0C0", "#87CEEB", "#6A5ACD", "#708090", "#FFFAFA", "#00FF7F", "#4682B4", "#D2B48C", "#008080", "#D8BFD8", "#FF6347",
++ "#40E0D0", "#EE82EE", "#F5DEB3", "#FFFFFF", "#F5F5F5", "#FFFF00", "#9ACD32",
++
+ };
+
+-void free_server( struct qserver *server);
+-void free_player( struct player *player);
+-void free_rule( struct rule *rule);
+-void standard_display_server( struct qserver *server);
++void free_server(struct qserver *server);
++void free_player(struct player *player);
++void free_rule(struct rule *rule);
++void standard_display_server(struct qserver *server);
+
+ /* MODIFY HERE
+ * Change these functions to display however you want
+ */
+-void
+-display_server( struct qserver *server)
++void display_server(struct qserver *server)
+ {
+- if ( player_sort)
+- sort_players( server);
++ if (player_sort)
++ {
++ sort_players(server);
++ }
+
+- if ( raw_display)
+- raw_display_server( server);
+- else if ( xml_display)
+- xml_display_server( server);
+- else if ( have_server_template())
+- template_display_server( server);
+- else
+- standard_display_server( server);
+-
+- free_server( server);
+-}
+-
+-void
+-standard_display_server( struct qserver *server)
+-{
+- char prefix[64];
+- if ( display_prefix)
+- sprintf( prefix, "%-4s ", server->type->type_prefix);
+- else
+- prefix[0]='\0';
+-
+- if ( server->server_name == DOWN) {
+- if ( ! up_servers_only)
+- fprintf( OF, "%s%-16s %10s\n", prefix,
+- (hostname_lookup) ? server->host_name : server->arg, DOWN);
+- return;
+- }
+- if ( server->server_name == TIMEOUT) {
+- if ( server->flags & FLAG_BROADCAST && server->n_servers)
+- fprintf( OF, "%s%-16s %d servers\n", prefix,
+- server->arg, server->n_servers);
+- else if ( ! up_servers_only)
+- fprintf( OF, "%s%-16s no response\n", prefix,
+- (hostname_lookup) ? server->host_name : server->arg);
+- return;
+- }
++ if (raw_display)
++ {
++ raw_display_server(server);
++ }
++ else if (xml_display)
++ {
++ xml_display_server(server);
++ }
++ else if (have_server_template())
++ {
++ template_display_server(server);
++ }
++ else
++ {
++ standard_display_server(server);
++ }
+
+- if ( server->type->master) {
+- display_qwmaster(server);
+- return;
+- }
++ free_server(server);
++}
+
+- if ( no_full_servers && server->num_players >= server->max_players)
+- return;
++void standard_display_server(struct qserver *server)
++{
++ char prefix[64];
++ if (display_prefix)
++ {
++ sprintf(prefix, "%-4s ", server->type->type_prefix);
++ }
++ else
++ {
++ prefix[0] = '\0';
++ }
+
+- if ( no_empty_servers && server->num_players == 0)
+- return;
++ if (server->server_name == DOWN || server->server_name == SYSERROR)
++ {
++ if (!up_servers_only)
++ {
++ fprintf(OF, "%s%-16s %10s\n", prefix, (hostname_lookup) ? server->host_name: server->arg, server->server_name);
++ }
++ return ;
++ }
+
+- if ( server->error != NULL) {
+- fprintf( OF, "%s%-21s ERROR <%s>\n",
+- prefix,
+- (hostname_lookup) ? server->host_name : server->arg,
+- server->error);
+- return;
+- }
++ if (server->server_name == TIMEOUT)
++ {
++ if (server->flags &FLAG_BROADCAST && server->n_servers)
++ {
++ fprintf(OF, "%s%-16s %d servers\n", prefix, server->arg, server->n_servers);
++ }
++ else if (!up_servers_only)
++ {
++ fprintf(OF, "%s%-16s no response\n", prefix, (hostname_lookup) ? server->host_name: server->arg);
++ }
++ return ;
++ }
+
+- if ( new_style) {
+- char *game= get_qw_game( server);
+- int map_name_width= 8, game_width=0;
+- switch( server->type->id) {
+- case QW_SERVER: case Q2_SERVER: case Q3_SERVER:
+- game_width= 9; break;
+- case TRIBES2_SERVER:
+- map_name_width= 14; game_width= 8; break;
+- case GHOSTRECON_SERVER:
+- map_name_width= 15; game_width= 15; break;
+- case HL_SERVER:
+- map_name_width= 12; break;
+- default:
+- break;
++ if (server->type->master)
++ {
++ display_qwmaster(server);
++ return ;
++ }
++
++ if (no_full_servers && server->num_players >= server->max_players)
++ {
++ return ;
++ }
++
++ if (no_empty_servers && server->num_players == 0)
++ {
++ return ;
++ }
++
++ if (server->error != NULL)
++ {
++ fprintf(OF, "%s%-21s ERROR <%s>\n", prefix, (hostname_lookup) ? server->host_name: server->arg, server->error);
++ return ;
++ }
++
++ if (new_style)
++ {
++ char *game = get_qw_game(server);
++ int map_name_width = 8, game_width = 0;
++ switch (server->type->id)
++ {
++ case QW_SERVER:
++ case Q2_SERVER:
++ case Q3_SERVER:
++ game_width = 9;
++ break;
++ case TRIBES2_SERVER:
++ map_name_width = 14;
++ game_width = 8;
++ break;
++ case GHOSTRECON_SERVER:
++ map_name_width = 15;
++ game_width = 15;
++ break;
++ case HL_SERVER:
++ map_name_width = 12;
++ break;
++ default:
++ break;
++ }
++ fprintf(OF,
++ "%s%-21s %2d/%-2d %2d/%-2d %*s %6d / %1d %*s %s\n",
++ prefix,
++ (hostname_lookup) ? server->host_name: server->arg,
++ server->num_players,
++ server->max_players,
++ server->num_spectators,
++ server->max_spectators,
++ map_name_width,
++ (server->map_name) ? xform_name(server->map_name, server): "?",
++ server->n_requests ? server->ping_total / server->n_requests: 999,
++ server->n_retries,
++ game_width,
++ game,
++ xform_name(server->server_name, server)
++ );
++ if (get_server_rules && NULL != server->type->display_rule_func )
++ {
++ server->type->display_rule_func(server);
++ }
++ if (get_player_info && NULL != server->type->display_player_func )
++ {
++ server->type->display_player_func(server);
++ }
++ }
++ else
++ {
++ char name[512];
++ sprintf(name, "\"%s\"", server->server_name);
++ fprintf(OF,
++ "%-16s %10s map %s at %22s %d/%d players %d ms\n",
++ (hostname_lookup) ? server->host_name: server->arg,
++ name,
++ server->map_name,
++ server->address,
++ server->num_players,
++ server->max_players,
++ server->n_requests ? server->ping_total / server->n_requests: 999
++ );
+ }
+- fprintf( OF, "%s%-21s %2d/%2d %*s %6d / %1d %*s %s\n",
+- prefix,
+- (hostname_lookup) ? server->host_name : server->arg,
+- server->num_players, server->max_players,
+- map_name_width, (server->map_name) ? server->map_name : "?",
+- server->n_requests ? server->ping_total/server->n_requests : 999,
+- server->n_retries,
+- game_width, game,
+- xform_name(server->server_name, server));
+- if ( get_server_rules)
+- server->type->display_rule_func( server);
+- if ( get_player_info)
+- server->type->display_player_func( server);
+- }
+- else {
+- char name[512];
+- sprintf( name, "\"%s\"", server->server_name);
+- fprintf( OF, "%-16s %10s map %s at %22s %d/%d players %d ms\n",
+- (hostname_lookup) ? server->host_name : server->arg,
+- name, server->map_name,
+- server->address, server->num_players, server->max_players,
+- server->n_requests ? server->ping_total/server->n_requests : 999);
+- }
+ }
+
+-void
+-display_qwmaster( struct qserver *server)
++void display_qwmaster(struct qserver *server)
+ {
+- char *prefix;
+- prefix= server->type->type_prefix;
++ char *prefix;
++ prefix = server->type->type_prefix;
+
+- if ( server->error != NULL)
+- fprintf( OF, "%s %-17s ERROR <%s>\n", prefix,
+- (hostname_lookup) ? server->host_name : server->arg,
+- server->error);
+- else
+- fprintf( OF, "%s %-17s %d servers %6d / %1d\n", prefix,
+- (hostname_lookup) ? server->host_name : server->arg,
+- server->n_servers,
+- server->n_requests ? server->ping_total/server->n_requests : 999,
+- server->n_retries);
+-}
+-
+-void
+-display_header()
+-{
+- if ( ! no_header_display)
+- fprintf( OF, "%-16s %8s %8s %15s %s\n", "ADDRESS", "PLAYERS", "MAP",
+- "RESPONSE TIME", "NAME");
+-}
+-
+-void
+-display_server_rules( struct qserver *server)
+-{
+- struct rule *rule;
+- int printed= 0;
+- rule= server->rules;
+- for ( ; rule != NULL; rule= rule->next) {
+- if ( (server->type->id != Q_SERVER &&
+- server->type->id != H2_SERVER) || ! is_default_rule( rule)) {
+- fprintf( OF, "%c%s=%s", (printed)?',':'\t', rule->name, rule->value);
+- printed++;
++ if (server->error != NULL)
++ {
++ fprintf(OF,
++ "%s %-17s ERROR <%s>\n",
++ prefix,
++ (hostname_lookup) ? server->host_name: server->arg,
++ server->error
++ );
++ }
++ else
++ {
++ fprintf(OF,
++ "%s %-17s %d servers %6d / %1d\n",
++ prefix,
++ (hostname_lookup) ? server->host_name: server->arg,
++ server->n_servers,
++ server->n_requests ? server->ping_total / server->n_requests: 999,
++ server->n_retries
++ );
+ }
+- }
+- if ( printed)
+- fputs( "\n", OF);
+ }
+
+-void
+-display_q_player_info( struct qserver *server)
++void display_header()
+ {
+- char fmt[128];
+- struct player *player;
+-
+- strcpy( fmt, "\t#%-2d %3d frags %9s ");
+-
+- if ( color_names)
+- strcat( fmt, "%9s:%-9s ");
+- else
+- strcat( fmt, "%2s:%-2s ");
+- if ( player_address)
+- strcat( fmt, "%22s ");
+- else
+- strcat( fmt, "%s");
+- strcat( fmt, "%s\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, fmt,
+- player->number,
+- player->frags,
+- play_time(player->connect_time,1),
+- quake_color(player->shirt_color),
+- quake_color(player->pants_color),
+- (player_address)?player->address:"",
+- xform_name( player->name, server));
+- }
++ if (!no_header_display)
++ {
++ fprintf(OF, "%-16s %8s %8s %15s %s\n", "ADDRESS", "PLAYERS", "MAP", "RESPONSE TIME", "NAME");
++ }
++}
++
++void display_server_rules(struct qserver *server)
++{
++ struct rule *rule;
++ int printed = 0;
++ rule = server->rules;
++ for (; rule != NULL; rule = rule->next)
++ {
++ if ((server->type->id != Q_SERVER && server->type->id != H2_SERVER) || !is_default_rule(rule))
++ {
++ fprintf(OF, "%c%s=%s", (printed) ? ',' : '\t', rule->name, rule->value);
++ printed++;
++ }
++ }
++ if (printed)
++ {
++ fputs("\n", OF);
++ }
+ }
+
+-void
+-display_qw_player_info( struct qserver *server)
++void display_q_player_info(struct qserver *server)
+ {
+- char fmt[128];
+- struct player *player;
++ char fmt[128];
++ struct player *player;
+
+- strcpy( fmt, "\t#%-6d %3d frags %6s@%-5s %8s");
++ strcpy(fmt, "\t#%-2d %3d frags %9s ");
+
+- if ( color_names)
+- strcat( fmt, "%9s:%-9s ");
+- else
+- strcat( fmt, "%2s:%-2s ");
+- strcat( fmt, "%s\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, fmt,
+- player->number,
+- player->frags,
+- play_time(player->connect_time,0),
+- ping_time(player->ping),
+- player->skin ? player->skin : "",
+- quake_color(player->shirt_color),
+- quake_color(player->pants_color),
+- xform_name( player->name, server));
+- }
++ if (color_names)
++ {
++ strcat(fmt, "%9s:%-9s ");
++ }
++ else
++ {
++ strcat(fmt, "%2s:%-2s ");
++ }
++ if (player_address)
++ {
++ strcat(fmt, "%22s ");
++ }
++ else
++ {
++ strcat(fmt, "%s");
++ }
++ strcat(fmt, "%s\n");
++
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt,
++ player->number,
++ player->frags,
++ play_time(player->connect_time, 1),
++ quake_color(player->shirt_color),
++ quake_color(player->pants_color),
++ (player_address) ? player->address: "",
++ xform_name(player->name, server)
++ );
++ }
+ }
+
+-void
+-display_q2_player_info( struct qserver *server)
++void display_qw_player_info(struct qserver *server)
+ {
+- struct player *player;
++ char fmt[128];
++ struct player *player;
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- if ( server->flags & FLAG_PLAYER_TEAMS)
+- fprintf( OF, "\t%3d frags team#%d %8s %s\n",
+- player->frags,
+- player->team,
+- ping_time(player->ping),
+- xform_name( player->name, server));
+- else
+- fprintf( OF, "\t%3d frags %8s %s\n",
+- player->frags,
+- ping_time(player->ping),
+- xform_name( player->name, server));
+- }
++ strcpy(fmt, "\t#%-6d %5d frags %6s@%-5s %8s");
++
++ if (color_names)
++ {
++ strcat(fmt, "%9s:%-9s ");
++ }
++ else
++ {
++ strcat(fmt, "%2s:%-2s ");
++ }
++ strcat(fmt, "%12s %s\n");
++
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt,
++ player->number,
++ player->frags,
++ play_time(player->connect_time, 0),
++ ping_time(player->ping),
++ player->skin ? player->skin: "",
++ quake_color(player->shirt_color),
++ quake_color(player->pants_color),
++ xform_name(player->name, server),
++ xform_name(player->team_name, server)
++ );
++ }
++}
++
++void display_q2_player_info(struct qserver *server)
++{
++ struct player *player;
++
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ if (server->flags &FLAG_PLAYER_TEAMS)
++ {
++ fprintf(OF,"\t%3d frags team#%d %8s %s\n", player->frags, player->team, ping_time(player->ping), xform_name(player->name, server));
++ }
++ else
++ {
++ fprintf(OF, "\t%3d frags %8s %s\n", player->frags, ping_time(player->ping), xform_name(player->name, server));
++ }
++ }
+ }
+
+
+-void
+-display_unreal_player_info( struct qserver *server)
++
++void display_unreal_player_info(struct qserver *server)
+ {
+- struct player *player;
+- static const char * fmt_team_number= "\t%3d frags team#%-3d %7s %s\n";
+- static const char * fmt_team_name= "\t%3d frags %8s %7s %s\n";
+- static const char * fmt_no_team= "\t%3d frags %8s %s\n";
++ struct player *player;
++ static const char *fmt_team_number = "\t%3d frags team#%-3d %7s %s\n";
++ static const char *fmt_team_name = "\t%3d frags %8s %7s %s\n";
++ static const char *fmt_no_team = "\t%3d frags %8s %s\n";
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- if ( server->flags & FLAG_PLAYER_TEAMS)
++ if (server->flags &FLAG_PLAYER_TEAMS)
+ {
+ // we use (player->score) ? player->score : player->frags,
+ // so we get details from halo
+- if ( player->team_name)
++ if (player->team_name)
+ {
+- fprintf( OF, fmt_team_name,
+- (player->score && NA_INT != player->score ) ? player->score : player->frags,
++ fprintf(OF, fmt_team_name,
++ (player->score && NA_INT != player->score) ? player->score: player->frags,
+ player->team_name,
+ ping_time(player->ping),
+- xform_name( player->name, server)
++ xform_name(player->name, server)
+ );
+ }
+ else
+ {
+- fprintf( OF, fmt_team_number,
+- (player->score && NA_INT != player->score ) ? player->score : player->frags,
++ fprintf(OF, fmt_team_number,
++ (player->score && NA_INT != player->score) ? player->score: player->frags,
+ player->team,
+ ping_time(player->ping),
+- xform_name( player->name, server)
++ xform_name(player->name, server)
+ );
+ }
+ }
+ else
+ {
+- fprintf( OF, fmt_no_team,
+- player->frags,
+- ping_time(player->ping),
+- xform_name( player->name, server)
+- );
++ fprintf(OF, fmt_no_team, player->frags, ping_time(player->ping), xform_name(player->name, server));
+ }
+ }
+ }
+
+-void
+-display_shogo_player_info( struct qserver *server)
++void display_shogo_player_info(struct qserver *server)
+ {
+- struct player *player;
++ struct player *player;
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, "\t%3d frags %8s %s\n",
+- player->frags,
+- ping_time(player->ping),
+- xform_name( player->name, server));
+- }
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%3d frags %8s %s\n", player->frags, ping_time(player->ping), xform_name(player->name, server));
++ }
+ }
+
+-void
+-display_halflife_player_info( struct qserver *server)
++void display_halflife_player_info(struct qserver *server)
+ {
+- struct player *player;
++ struct player *player;
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, "\t%3d frags %8s %s\n",
+- player->frags,
+- play_time( player->connect_time,1),
+- xform_name( player->name, server));
+- }
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%3d frags %8s %s\n", player->frags, play_time(player->connect_time, 1), xform_name(player->name, server));
++ }
+ }
+
+-void
+-display_tribes_player_info( struct qserver *server)
++void display_fl_player_info(struct qserver *server)
+ {
+- struct player *player;
++ struct player *player;
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, "\t%4d score team#%d %8s %s\n",
+- player->frags,
+- player->team,
+- ping_time( player->ping),
+- xform_name( player->name, server));
+- }
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%3d frags %8s %8s %s\n", player->frags, ping_time(player->ping), play_time(player->connect_time, 1), xform_name(player->name, server));
++ }
+ }
+
+-void
+-display_tribes2_player_info( struct qserver *server)
++void display_tribes_player_info(struct qserver *server)
+ {
+- struct player *player;
++ struct player *player;
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, "\tscore %4d %14s %s\n",
+- player->frags,
+- player->team_name ? player->team_name : (player->number == TRIBES_TEAM ? "TEAM" : "?"),
+- xform_name( player->name, server));
+- }
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%4d score team#%d %8s %s\n", player->frags, player->team, ping_time(player->ping), xform_name(player->name, server)
++ );
++ }
+ }
+
+-void
+-display_bfris_player_info( struct qserver *server)
++void display_tribes2_player_info(struct qserver *server)
+ {
+- struct player *player;
++ struct player *player;
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, "\ttid: %d, ship: %d, team: %s, ping: %d, score: %d, kills: %d, name: %s\n",
+- player->number,
+- player->ship,
+- player->team_name,
+- player->ping,
+- player->score,
+- player->frags,
+- xform_name( player->name, server));
+- }
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\tscore %4d %14s %s\n",
++ player->frags,
++ player->team_name ? player->team_name: (player->number == TRIBES_TEAM ? "TEAM" : "?"),
++ xform_name(player->name, server)
++ );
++ }
++}
++
++void display_bfris_player_info(struct qserver *server)
++{
++ struct player *player;
++
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\ttid: %d, ship: %d, team: %s, ping: %d, score: %d, kills: %d, name: %s\n",
++ player->number,
++ player->ship,
++ player->team_name,
++ player->ping,
++ player->score,
++ player->frags,
++ xform_name(player->name, server)
++ );
++ }
+ }
+
+-void
+-display_descent3_player_info( struct qserver *server)
++void display_descent3_player_info(struct qserver *server)
+ {
+- struct player *player;
++ struct player *player;
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, "\t%3d frags %3d deaths team#%-3d %7s %s\n",
+- player->frags,
+- player->deaths,
+- player->team,
+- ping_time(player->ping),
+- xform_name( player->name, server));
+- }
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%3d frags %3d deaths team#%-3d %7s %s\n",
++ player->frags,
++ player->deaths,
++ player->team,
++ ping_time(player->ping),
++ xform_name(player->name, server)
++ );
++ }
+ }
+
+-void
+-display_ghostrecon_player_info( struct qserver *server)
++void display_ghostrecon_player_info(struct qserver *server)
+ {
+- struct player *player;
++ struct player *player;
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, "\tdead=%3d team#%-3d %s\n",
+- player->deaths,
+- player->team,
+- xform_name( player->name, server));
+- }
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\tdead=%3d team#%-3d %s\n", player->deaths, player->team, xform_name(player->name, server));
++ }
+ }
+
+-void
+-display_eye_player_info( struct qserver *server)
++void display_eye_player_info(struct qserver *server)
+ {
+- struct player *player;
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- if ( player->team_name)
+- fprintf( OF, "\tscore %4d %6s team %12s %s\n",
+- player->score,
+- ping_time(player->ping),
+- player->team_name,
+- xform_name( player->name, server));
+- else
+- fprintf( OF, "\tscore %4d %6s team#%d %s\n",
+- player->score,
+- ping_time(player->ping),
+- player->team,
+- xform_name( player->name, server));
+- }
++ struct player *player;
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ if (player->team_name)
++ {
++ fprintf(OF, "\tscore %4d %6s team %12s %s\n",
++ player->score,
++ ping_time(player->ping),
++ player->team_name,
++ xform_name(player->name,server)
++ );
++ }
++ else
++ {
++ fprintf(OF, "\tscore %4d %6s team#%d %s\n",
++ player->score,
++ ping_time(player->ping),
++ player->team,
++ xform_name(player->name,server)
++ );
++ }
++ }
+ }
+
+-int
+-calculate_armyops_score( struct player *player)
++int calculate_armyops_score(struct player *player)
+ {
+ /* Calculates a player's score for ArmyOps from the basic components */
+
+@@ -986,7354 +804,8945 @@
+ int kill_score = 0;
+ struct info *info;
+
+- for ( info = player->info; info; info = info->next )
++ for (info = player->info; info; info = info->next)
+ {
+- if ( 0 == strcmp( info->name, "leader") || 0 == strcmp( info->name, "goal") || 0 == strcmp( info->name, "roe") )
+- score += atoi( info->value );
+- else if ( 0 == strcmp( info->name, "kia") || 0 == strcmp( info->name, "enemy") )
+- kill_score += atoi( info->value );
++ if (0 == strcmp(info->name, "leader") || 0 == strcmp(info->name, "goal") || 0 == strcmp(info->name, "roe"))
++ {
++ score += atoi(info->value);
++ }
++ else if (0 == strcmp(info->name, "kia") || 0 == strcmp(info->name, "enemy"))
++ {
++ kill_score += atoi(info->value);
++ }
+ }
+
+- if ( kill_score > 0 )
++ if (kill_score > 0)
++ {
+ score += kill_score;
++ }
+
+ return score;
+ }
+
+-void
+-display_gs2_player_info( struct qserver *server)
++void display_gs2_player_info(struct qserver *server)
+ {
+- struct player *player;
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- if ( player->team_name)
+- fprintf( OF, "\tscore %4d %6s team %12s %s\n",
+- player->score,
+- ping_time(player->ping),
+- player->team_name,
+- xform_name( player->name, server));
+- else
+- fprintf( OF, "\tscore %4d %6s team#%d %s\n",
+- player->score,
+- ping_time(player->ping),
+- player->team,
+- xform_name( player->name, server));
+- }
++ struct player *player;
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ if (player->team_name)
++ {
++ fprintf(OF, "\tscore %4d %6s team %12s %s\n",
++ player->score,
++ ping_time(player->ping),
++ player->team_name,
++ xform_name(player->name,server)
++ );
++ }
++ else
++ {
++ fprintf(OF, "\tscore %4d %6s team#%d %s\n",
++ player->score,
++ ping_time(player->ping),
++ player->team,
++ xform_name(player->name, server)
++ );
++ }
++ }
+ }
+
+-void
+-display_armyops_player_info( struct qserver *server)
++void display_armyops_player_info(struct qserver *server)
+ {
+ struct player *player;
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- player->score = calculate_armyops_score( player );
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ player->score = calculate_armyops_score(player);
+ }
+
+ display_gs2_player_info(server);
+ }
+
+-void
+-display_ts2_player_info( struct qserver *server)
++void display_ts2_player_info(struct qserver *server)
+ {
+- struct player *player;
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
+- {
+- fprintf( OF, "\t%6s %s\n",
+- ping_time(player->ping),
+- xform_name( player->name, server)
+- );
+- }
++ struct player *player;
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%6s %s\n", ping_time(player->ping), xform_name(player->name, server));
++ }
+ }
+
+-void
+-display_tm_player_info( struct qserver *server)
++void display_ts3_player_info(struct qserver *server)
+ {
+- struct player *player;
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
+- {
+- fprintf( OF, "\t%6s %s\n",
+- ping_time(player->ping),
+- xform_name( player->name, server)
+- );
+- }
++ struct player *player;
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%s\n", xform_name(player->name, server));
++ }
+ }
+
+-void
+-display_doom3_player_info( struct qserver *server)
++void display_bfbc2_player_info(struct qserver *server)
+ {
+- struct player *player;
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- if ( player->tribe_tag)
+- fprintf( OF, "\t#%-4d score %4d %6s team %12s %s\n",
+- player->number,
+- player->score,
+- ping_time(player->ping),
+- player->tribe_tag,
+- xform_name( player->name, server));
+- else
+- fprintf( OF, "\t#%-4d score %4d %6s team#%d %s\n",
+- player->number,
+- player->score,
+- ping_time(player->ping),
+- player->team,
+- xform_name( player->name, server));
+- }
+-}
+-
+-void
+-display_hl2_player_info( struct qserver *server )
+-{
+- // ATM this looks like halflife player info
+- display_halflife_player_info( server );
++ struct player *player;
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%s\n", xform_name(player->name, server));
++ }
+ }
+
+-void
+-display_ravenshield_player_info( struct qserver *server)
++void display_wic_player_info(struct qserver *server)
+ {
+- struct player *player = server->players;
+- for ( ; player != NULL; player = player->next )
++ struct player *player;
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t%3d frags %8s %s\n",
+- player->frags,
+- play_time( player->connect_time,1),
+- xform_name( player->name, server));
+- }
++ fprintf(OF, "\t#%-4d score %4d team %12s role %12s %s\n",
++ player->number,
++ player->score,
++ player->team_name,
++ player->tribe_tag ? player->tribe_tag : "",
++ xform_name(player->name, server)
++ );
++ }
+ }
+
+-
+-void
+-display_savage_player_info( struct qserver *server)
++void display_ventrilo_player_info(struct qserver *server)
+ {
+- struct player *player = server->players;
+- for ( ; player != NULL; player = player->next )
++ struct player *player;
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t%3d frags %8s %s\n",
+- player->frags,
+- play_time( player->connect_time,1),
+- xform_name( player->name, server));
+- }
++ fprintf(OF, "\t# %d ping %s time %d cid %i ch %s name %s\n",
++ player->number,
++ ping_time(player->ping),
++ player->connect_time,
++ player->team,
++ player->team_name,
++ xform_name(player->name, server)
++ );
++ }
+ }
+
+-
+-void
+-display_farcry_player_info( struct qserver *server)
++void display_tm_player_info(struct qserver *server)
+ {
+- struct player *player = server->players;
+- for ( ; player != NULL; player = player->next )
++ struct player *player;
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t%3d frags %8s %s\n",
+- player->frags,
+- play_time( player->connect_time,1),
+- xform_name( player->name, server));
+- }
++ fprintf(OF, "\t%6s %s\n", ping_time(player->ping), xform_name(player->name, server));
++ }
+ }
+
+-char *
+-get_qw_game( struct qserver *server)
++void display_doom3_player_info(struct qserver *server)
+ {
+- struct rule *rule;
+- if ( server->type->game_rule == NULL || *server->type->game_rule == '\0')
+- {
+- return "";
+- }
+- rule = server->rules;
+- for ( ; rule != NULL; rule = rule->next)
++ struct player *player;
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- if ( strcmp( rule->name, server->type->game_rule) == 0)
++ if (player->tribe_tag)
+ {
+- if ( server->type->id == Q3_SERVER && strcmp( rule->value, "baseq3") == 0)
+- {
+- return "";
+- }
+- return rule->value;
++ fprintf(OF, "\t#%-4d score %4d %6s team %12s %s\n",
++ player->number,
++ player->score,
++ ping_time(player->ping),
++ player->tribe_tag,
++ xform_name(player->name, server)
++ );
+ }
+- }
+- rule= server->rules;
+- for ( ; rule != NULL; rule = rule->next)
+- {
+- if ( strcmp( rule->name, "game" ) == 0)
++ else
+ {
+- return rule->value;
++ fprintf(OF, "\t#%-4d score %4d %6s team#%d %s\n",
++ player->number,
++ player->score,
++ ping_time(player->ping),
++ player->team,
++ xform_name(player->name, server)
++ );
+ }
+ }
+- return "";
+ }
+
+-/* Raw output for web master types
+- */
+-
+-#define RD raw_delimiter
+-
+-void
+-raw_display_server( struct qserver *server)
++void display_ravenshield_player_info(struct qserver *server)
+ {
+- char *prefix;
+- int ping_time;
+- prefix= server->type->type_prefix;
+-
+- if ( server->n_requests)
+- ping_time= server->ping_total/server->n_requests;
+- else
+- ping_time= 999;
+-
+- if ( server->server_name == DOWN) {
+- if ( ! up_servers_only)
+- fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s\n\n",
+- prefix,
+- raw_arg, RD, raw_arg, server->arg,
+- RD, (hostname_lookup)?server->host_name:server->arg,
+- RD, DOWN);
+- return;
+- }
+- if ( server->server_name == TIMEOUT) {
+- if ( server->flags & FLAG_BROADCAST && server->n_servers)
+- fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%d\n", prefix,
+- raw_arg, RD, raw_arg, server->arg,
+- RD, server->arg,
+- RD, server->n_servers);
+- else if ( ! up_servers_only)
+- fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s\n\n",
+- prefix,
+- raw_arg, RD, raw_arg, server->arg,
+- RD, (hostname_lookup)?server->host_name:server->arg,
+- RD, TIMEOUT);
+- return;
+- }
+-
+- if ( server->error != NULL) {
+- fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s" "%s%s",
+- prefix,
+- raw_arg, RD, raw_arg, server->arg,
+- RD, (hostname_lookup) ? server->host_name : server->arg,
+- RD, "ERROR",
+- RD, server->error);
+- }
+- else if ( server->type->flags & TF_RAW_STYLE_QUAKE) {
+- fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s" "%s%s" "%s%d" "%s%s" "%s%d" "%s%d" "%s%d" "%s%d" "%s%s",
+- prefix,
+- raw_arg, RD, raw_arg, server->arg,
+- RD, (hostname_lookup) ? server->host_name : server->arg,
+- RD, xform_name( server->server_name, server),
+- RD, server->address,
+- RD, server->protocol_version,
+- RD, server->map_name,
+- RD, server->max_players,
+- RD, server->num_players,
+- RD, ping_time,
+- RD, server->n_retries,
+- show_game_in_raw ? RD : "", show_game_in_raw ? get_qw_game(server) : ""
+- );
+- }
+- else if ( server->type->flags & TF_RAW_STYLE_TRIBES) {
+- fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s" "%s%s" "%s%d" "%s%d",
+- prefix,
+- raw_arg, RD, raw_arg, server->arg,
+- RD, (hostname_lookup) ? server->host_name : server->arg,
+- RD, xform_name( server->server_name, server),
+- RD, (server->map_name) ? server->map_name : "?",
+- RD, server->num_players,
+- RD, server->max_players
+- );
+- }
+- else if ( server->type->flags & TF_RAW_STYLE_GHOSTRECON) {
+- fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s" "%s%s" "%s%d" "%s%d",
+- prefix,
+- raw_arg, RD, raw_arg, server->arg,
+- RD, (hostname_lookup) ? server->host_name : server->arg,
+- RD, xform_name( server->server_name, server),
+- RD, (server->map_name) ? server->map_name : "?",
+- RD, server->num_players,
+- RD, server->max_players
+- );
+- }
+- else if ( server->type->master) {
+- fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%d",
+- prefix,
+- raw_arg, RD, raw_arg, server->arg,
+- RD, (hostname_lookup) ? server->host_name : server->arg,
+- RD, server->n_servers
+- );
+- }
+- else {
+- fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s" "%s%s" "%s%d" "%s%d" "%s%d" "%s%d" "%s%s",
+- prefix,
+- raw_arg, RD, raw_arg, server->arg,
+- RD, (hostname_lookup) ? server->host_name : server->arg,
+- RD, xform_name( server->server_name, server),
+- RD, (server->map_name) ? server->map_name : "?",
+- RD, server->max_players,
+- RD, server->num_players,
+- RD, ping_time,
+- RD, server->n_retries,
+- show_game_in_raw ? RD : "", show_game_in_raw ? get_qw_game(server) : ""
+- );
+- }
+- fputs( "\n", OF);
+-
+- if ( server->type->master || server->error != NULL) {
+- fputs( "\n", OF);
+- return;
+- }
+-
+- if ( get_server_rules)
+- server->type->display_raw_rule_func( server);
+- if ( get_player_info)
+- server->type->display_raw_player_func( server);
+- fputs( "\n", OF);
+-}
+-
+-void
+-raw_display_server_rules( struct qserver *server)
+-{
+- struct rule *rule;
+- int printed= 0;
+- rule= server->rules;
+- for ( ; rule != NULL; rule= rule->next) {
+- if ( server->type->id == TRIBES2_SERVER) {
+- char *v;
+- for ( v= rule->value; *v; v++)
+- if ( *v == '\n') *v= ' ';
++ struct player *player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%3d frags %8s %s\n", player->frags, play_time(player->connect_time, 1), xform_name(player->name, server));
+ }
+- fprintf( OF, "%s%s=%s", (printed)?RD:"", rule->name, rule->value);
+- printed++;
+- }
+- if ( server->missing_rules)
+- fprintf( OF, "%s?", (printed)?RD:"");
+- fputs( "\n", OF);
+-}
+-
+-void
+-raw_display_q_player_info( struct qserver *server)
+-{
+- char fmt[] = "%d" "%s%s" "%s%s" "%s%d" "%s%s" "%s%s" "%s%s";
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, fmt,
+- player->number,
+- RD, xform_name( player->name, server),
+- RD, player->address,
+- RD, player->frags,
+- RD, play_time(player->connect_time,1),
+- RD, quake_color(player->shirt_color),
+- RD, quake_color(player->pants_color)
+- );
+- fputs( "\n", OF);
+- }
+-}
+-
+-void
+-raw_display_qw_player_info( struct qserver *server)
+-{
+- char fmt[128];
+- struct player *player;
+-
+- strcpy( fmt, "%d" "%s%s" "%s%d" "%s%s" "%s%s" "%s%s");
+- strcat( fmt, "%s%d" "%s%s");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, fmt,
+- player->number,
+- RD, xform_name( player->name, server),
+- RD, player->frags,
+- RD, play_time(player->connect_time,1),
+- RD, quake_color(player->shirt_color),
+- RD, quake_color(player->pants_color),
+- RD, player->ping,
+- RD, player->skin ? player->skin : ""
+- );
+- fputs( "\n", OF);
+- }
+ }
+
+-void
+-raw_display_q2_player_info( struct qserver *server)
+-{
+- static const char *fmt = "%s" "%s%d" "%s%d";
+- static const char *fmt_team = "%s" "%s%d" "%s%d" "%s%d";
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- if ( server->flags & FLAG_PLAYER_TEAMS)
+- fprintf( OF, fmt_team,
+- xform_name( player->name, server),
+- RD, player->frags,
+- RD, player->ping,
+- RD, player->team
+- );
+- else
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->frags,
+- RD, player->ping
+- );
+- fputs( "\n", OF);
+- }
+-}
+-
+-void
+-raw_display_unreal_player_info( struct qserver *server)
+-{
+- static const char *fmt= "%s" "%s%d" "%s%d" "%s%d" "%s%s" "%s%s" "%s%s";
+- static const char *fmt_team_name= "%s" "%s%d" "%s%d" "%s%s" "%s%s" "%s%s" "%s%s";
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- if ( player->team_name)
+- fprintf( OF, fmt_team_name,
+- xform_name( player->name, server),
+- RD, player->frags,
+- RD, player->ping,
+- RD, player->team_name,
+- RD, player->skin ? player->skin : "",
+- RD, player->mesh ? player->mesh : "",
+- RD, player->face ? player->face : ""
+- );
+- else
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->frags,
+- RD, player->ping,
+- RD, player->team,
+- RD, player->skin ? player->skin : "",
+- RD, player->mesh ? player->mesh : "",
+- RD, player->face ? player->face : ""
+- );
+- fputs( "\n", OF);
+- }
+-}
+
+-void
+-raw_display_halflife_player_info( struct qserver *server)
++void display_savage_player_info(struct qserver *server)
+ {
+- static char fmt[24]= "%s" "%s%d" "%s%s";
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->frags,
+- RD, play_time( player->connect_time,1)
+- );
+- fputs( "\n", OF);
+- }
++ struct player *player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%3d frags %8s %s\n", player->frags, play_time(player->connect_time, 1), xform_name(player->name, server));
++ }
+ }
+
+-void
+-raw_display_tribes_player_info( struct qserver *server)
+-{
+- static char fmt[24]= "%s" "%s%d" "%s%d" "%s%d" "%s%d";
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->frags,
+- RD, player->ping,
+- RD, player->team,
+- RD, player->packet_loss
+- );
+- fputs( "\n", OF);
+- }
+-}
+
+-void
+-raw_display_tribes2_player_info( struct qserver *server)
++void display_farcry_player_info(struct qserver *server)
+ {
+- static char fmt[]= "%s" "%s%d" "%s%d" "%s%s" "%s%s" "%s%s";
+- struct player *player;
+- char *type;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- switch( player->type_flag) {
+- case PLAYER_TYPE_BOT: type= "Bot"; break;
+- case PLAYER_TYPE_ALIAS: type= "Alias"; break;
+- default: type= ""; break;
+- }
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->frags,
+- RD, player->team,
+- RD, player->team_name ? player->team_name : "TEAM",
+- RD, type,
+- RD, player->tribe_tag ? xform_name(player->tribe_tag,server) : ""
+- );
+- fputs( "\n", OF);
+- }
++ struct player *player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%3d frags %8s %s\n", player->frags, play_time(player->connect_time, 1), xform_name(player->name, server));
++ }
+ }
+
+-void
+-raw_display_bfris_player_info( struct qserver *server)
++void display_tee_player_info(struct qserver *server)
+ {
+- static char fmt[] = "%d" "%s%d" "%s%s" "%s%d" "%s%d" "%s%d" "%s%s";
+- struct player *player;
++ struct player *player;
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, fmt,
+- player->number,
+- RD, player->ship,
+- RD, player->team_name,
+- RD, player->ping,
+- RD, player->score,
+- RD, player->frags,
+- RD, xform_name( player->name, server)
+- );
+- fputs( "\n", OF);
+- }
+-}
+-
+-void
+-raw_display_descent3_player_info( struct qserver *server)
+-{
+- static char fmt[]= "%s" "%s%d" "%s%d" "%s%d" "%s%d";
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->frags,
+- RD, player->deaths,
+- RD, player->ping,
+- RD, player->team
+- );
+- fputs( "\n", OF);
+- }
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t%4d score %s\n", player->score, xform_name(player->name, server));
++ }
+ }
+
+-void
+-raw_display_ghostrecon_player_info( struct qserver *server)
++char *get_qw_game(struct qserver *server)
+ {
+- static char fmt[28]= "%s" "%s%d" "%s%d";
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->deaths,
+- RD, player->team
+- );
+- fputs( "\n", OF);
+- }
++ struct rule *rule;
++ char *game_rule = server->type->game_rule;
++ if (game_rule == NULL || *game_rule == '\0')
++ {
++ return "";
++ } rule = server->rules;
++ for (; rule != NULL; rule = rule->next)
++ {
++ if (strcmp(rule->name, game_rule) == 0)
++ {
++ if (server->type->id == Q3_SERVER && strcmp(rule->value, "baseq3") == 0)
++ {
++ return "";
++ }
++ return rule->value;
++ }
++ }
++ rule = server->rules;
++ for (; rule != NULL; rule = rule->next)
++ {
++ if (0 == strcmp(rule->name, "game") || 0 == strcmp(rule->name, "fs_game"))
++ {
++ return rule->value;
++ }
++ }
++ return "";
+ }
+
+-void
+-raw_display_eye_player_info( struct qserver *server)
+-{
+- static const char *fmt= "%s" "%s%d" "%s%d" "%s%d" "%s%s" "%s%s";
+- static const char *fmt_team_name= "%s" "%s%d" "%s%d" "%s%s" "%s%s" "%s%s";
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- if ( player->team_name)
+- fprintf( OF, fmt_team_name,
+- xform_name( player->name, server),
+- RD, player->score,
+- RD, player->ping,
+- RD, player->team_name,
+- RD, player->skin ? player->skin : "",
+- RD, play_time( player->connect_time,1)
+- );
+- else
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->score,
+- RD, player->ping,
+- RD, player->team,
+- RD, player->skin ? player->skin : "",
+- RD, play_time( player->connect_time,1)
+- );
+- fputs( "\n", OF);
+- }
+-}
++/* Raw output for web master types
++ */
+
+-void
+-raw_display_doom3_player_info( struct qserver *server)
+-{
+- static const char *fmt= "%s" "%s%d" "%s%d" "%s%d" "%s%u";
+- static const char *fmt_team_name= "%s" "%s%d" "%s%d" "%s%s" "%s%u";
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- if ( player->tribe_tag)
+- fprintf( OF, fmt_team_name,
+- xform_name( player->name, server),
+- RD, player->score,
+- RD, player->ping,
+- RD, player->tribe_tag,
+- RD, player->number
+- );
+- else
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->score,
+- RD, player->ping,
+- RD, player->team,
+- RD, player->number
+- );
+- fputs( "\n", OF);
+- }
+-}
++#define RD raw_delimiter
+
+-void
+-raw_display_hl2_player_info( struct qserver *server )
++void raw_display_server(struct qserver *server)
+ {
+- // ATM this looks like halflife player info
+- raw_display_halflife_player_info( server );
+-}
++ char *prefix;
++ int ping_time;
++ prefix = server->type->type_prefix;
+
+-void
+-raw_display_gs2_player_info( struct qserver *server)
+-{
+- static const char *fmt= "%s" "%s%d" "%s%d" "%s%d" "%s%s" "%s%s";
+- static const char *fmt_team_name= "%s" "%s%d" "%s%d" "%s%s" "%s%s" "%s%s";
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- if ( player->team_name)
+- fprintf( OF, fmt_team_name,
+- xform_name( player->name, server),
+- RD, player->score,
+- RD, player->ping,
+- RD, player->team_name,
+- RD, player->skin ? player->skin : "",
+- RD, play_time( player->connect_time,1)
+- );
++ if (server->n_requests)
++ {
++ ping_time = server->ping_total / server->n_requests;
++ }
+ else
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->score,
+- RD, player->ping,
+- RD, player->team,
+- RD, player->skin ? player->skin : "",
+- RD, play_time( player->connect_time,1)
+- );
+- fputs( "\n", OF);
+- }
+-}
+-
+-void
+-raw_display_armyops_player_info( struct qserver *server)
+-{
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- player->score = calculate_armyops_score( player );
++ {
++ ping_time = 999;
+ }
+
+- raw_display_gs2_player_info(server);
+-}
++ if (server->server_name == DOWN || server->server_name == SYSERROR)
++ {
++ if (!up_servers_only)
++ {
++ fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s\n\n",
++ prefix,
++ raw_arg, RD,
++ raw_arg,
++ server->arg, RD,
++ (hostname_lookup) ? server->host_name : server->arg, RD,
++ server->server_name
++ );
++ }
++ return ;
++ }
+
+-void
+-raw_display_ts2_player_info( struct qserver *server)
+-{
+- static const char *fmt= "%s" "%s%d" "%s%s" "%s%s";
+- struct player *player;
++ if (server->server_name == TIMEOUT)
++ {
++ if (server->flags &FLAG_BROADCAST && server->n_servers)
++ {
++ fprintf(OF, "%s""%.*s%.*s""%s%s""%s%d\n", prefix, raw_arg, RD, raw_arg, server->arg, RD, server->arg, RD, server->n_servers);
++ }
++ else if (!up_servers_only)
++ {
++ fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s\n\n", prefix, raw_arg, RD, raw_arg, server->arg, RD, (hostname_lookup) ? server->host_name : server->arg, RD, TIMEOUT);
++ }
++ return ;
++ }
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
+- {
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->ping,
+- RD, player->skin ? player->skin : "",
+- RD, play_time( player->connect_time, 1 )
++ if (server->error != NULL)
++ {
++ fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s""%s%s",
++ prefix,
++ raw_arg, RD,
++ raw_arg,
++ server->arg, RD,
++ (hostname_lookup) ? server->host_name: server->arg, RD,
++ "ERROR", RD,
++ server->error
+ );
+- fputs( "\n", OF);
+- }
+-}
+-
+-void
+-raw_display_tm_player_info( struct qserver *server)
+-{
+- static const char *fmt= "%s" "%s%d" "%s%s" "%s%s";
+- struct player *player;
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
+- {
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->ping,
+- RD, player->skin ? player->skin : "",
+- RD, play_time( player->connect_time, 1 )
++ }
++ else if (server->type->flags &TF_RAW_STYLE_QUAKE)
++ {
++ fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s""%s%s""%s%d""%s%s""%s%d""%s%d""%s%d""%s%d""%s%d""%s%d""%s%s",
++ prefix,
++ raw_arg, RD,
++ raw_arg,
++ server->arg, RD,
++ (hostname_lookup) ? server->host_name: server->arg, RD,
++ xform_name(server->server_name, server), RD,
++ server->address, RD,
++ server->protocol_version, RD,
++ server->map_name, RD,
++ server->max_players, RD,
++ server->num_players, RD,
++ server->max_spectators, RD,
++ server->num_spectators, RD,
++ ping_time, RD,
++ server->n_retries,
++ show_game_in_raw ? RD : "",
++ show_game_in_raw ? get_qw_game(server): ""
+ );
+- fputs( "\n", OF);
+- }
+-}
+-
+-void
+-raw_display_ravenshield_player_info( struct qserver *server)
+-{
+- static char fmt[24]= "%s" "%s%d" "%s%s";
+- struct player *player = server->players;
+- for ( ; player != NULL; player= player->next )
+- {
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->frags,
+- RD, play_time( player->connect_time,1)
++ }
++ else if (server->type->flags &TF_RAW_STYLE_TRIBES)
++ {
++ fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s""%s%s""%s%d""%s%d",
++ prefix,
++ raw_arg, RD,
++ raw_arg,
++ server->arg, RD,
++ (hostname_lookup) ? server->host_name: server->arg, RD,
++ xform_name(server->server_name, server), RD,
++ (server->map_name) ? server->map_name: "?", RD,
++ server->num_players, RD,
++ server->max_players
+ );
+- fputs( "\n", OF);
+- }
+-}
+-
+-void
+-raw_display_savage_player_info( struct qserver *server)
+-{
+- static char fmt[24]= "%s" "%s%d" "%s%s";
+- struct player *player = server->players;
+- for ( ; player != NULL; player= player->next )
+- {
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->frags,
+- RD, play_time( player->connect_time,1)
++ }
++ else if (server->type->flags &TF_RAW_STYLE_GHOSTRECON)
++ {
++ fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s""%s%s""%s%d""%s%d",
++ prefix,
++ raw_arg, RD,
++ raw_arg,
++ server->arg, RD,
++ (hostname_lookup) ? server->host_name: server->arg, RD,
++ xform_name(server->server_name, server), RD,
++ (server->map_name) ? server->map_name: "?", RD,
++ server->num_players, RD,
++ server->max_players
+ );
+- fputs( "\n", OF);
+- }
+-}
+-
+-void
+-raw_display_farcry_player_info( struct qserver *server)
+-{
+- static char fmt[24]= "%s" "%s%d" "%s%s";
+- struct player *player = server->players;
+- for ( ; player != NULL; player= player->next )
+- {
+- fprintf( OF, fmt,
+- xform_name( player->name, server),
+- RD, player->frags,
+- RD, play_time( player->connect_time,1)
++ }
++ else if (server->type->master)
++ {
++ fprintf(OF, "%s""%.*s%.*s""%s%s""%s%d",
++ prefix,
++ raw_arg, RD,
++ raw_arg,
++ server->arg, RD,
++ (hostname_lookup) ? server->host_name: server->arg, RD,
++ server->n_servers
+ );
+- fputs( "\n", OF);
+- }
+-}
+-
+-/* XML output
+- * Contributed by <sgarner at gameplanet.co.nz> :-)
+- */
+-
+-void
+-xml_display_server( struct qserver *server)
+-{
+- char *prefix;
+- prefix= server->type->type_prefix;
+-
+- if ( server->server_name == DOWN)
++ }
++ else
+ {
+- if ( ! up_servers_only)
+- {
+- fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n",
+- xml_escape(prefix), xml_escape(server->arg), xml_escape(DOWN));
+- fprintf( OF, "\t\t<hostname>%s</hostname>\n",
+- xml_escape((hostname_lookup)?server->host_name:server->arg));
+- fprintf( OF, "\t</server>\n");
+- }
+- return;
++ fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s""%s%s""%s%d""%s%d""%s%d""%s%d""%s%s",
++ prefix,
++ raw_arg, RD,
++ raw_arg,
++ server->arg, RD,
++ (hostname_lookup) ? server->host_name: server->arg, RD,
++ xform_name(server->server_name, server), RD,
++ (server->map_name) ? xform_name(server->map_name, server): "?", RD,
++ server->max_players, RD,
++ server->num_players, RD,
++ ping_time, RD,
++ server->n_retries,
++ show_game_in_raw ? RD : "",
++ show_game_in_raw ? get_qw_game(server): ""
++ );
+ }
+- if ( server->server_name == TIMEOUT)
++ fputs("\n", OF);
++
++ if (server->type->master || server->error != NULL)
+ {
+- if ( server->flags & FLAG_BROADCAST && server->n_servers)
+- {
+- fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\" servers=\"%d\">\n",
+- xml_escape(prefix), xml_escape(server->arg),
+- xml_escape(TIMEOUT), server->n_servers);
+- fprintf( OF, "\t</server>\n");
+- }
+- else if ( ! up_servers_only)
+- {
+- fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n",
+- xml_escape(prefix), xml_escape(server->arg), xml_escape(TIMEOUT));
+- fprintf( OF, "\t\t<hostname>%s</hostname>\n",
+- xml_escape((hostname_lookup)?server->host_name:server->arg));
+- fprintf( OF, "\t</server>\n");
+- }
+- return;
+- }
++ fputs("\n", OF);
++ return ;
++ }
+
+- if ( server->error != NULL)
++ if (get_server_rules && NULL != server->type->display_raw_rule_func )
+ {
+- fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n",
+- xml_escape(prefix), xml_escape(server->arg), "ERROR");
+- fprintf( OF, "\t\t<hostname>%s</hostname>\n",
+- xml_escape((hostname_lookup)?server->host_name:server->arg));
+- fprintf( OF, "\t\t<error>%s</error>\n",
+- xml_escape(server->error));
++ server->type->display_raw_rule_func(server);
+ }
+- else if ( server->type->master)
++ if (get_player_info && NULL != server->type->display_raw_player_func)
+ {
+- fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\" servers=\"%d\">\n",
+- xml_escape(prefix), xml_escape(server->arg), "UP", server->n_servers);
++ server->type->display_raw_player_func(server);
+ }
+- else {
+- fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n",
+- xml_escape(prefix), xml_escape(server->arg), "UP");
+- fprintf( OF, "\t\t<hostname>%s</hostname>\n",
+- xml_escape((hostname_lookup)?server->host_name:server->arg));
+- fprintf( OF, "\t\t<name>%s</name>\n",
+- xml_escape(xform_name( server->server_name, server)));
+- fprintf( OF, "\t\t<gametype>%s</gametype>\n",
+- xml_escape(get_qw_game(server)));
+- fprintf( OF, "\t\t<map>%s</map>\n",
+- xml_escape(server->map_name));
+- fprintf( OF, "\t\t<numplayers>%d</numplayers>\n",
+- server->num_players);
+- fprintf( OF, "\t\t<maxplayers>%d</maxplayers>\n",
+- server->max_players);
++ fputs("\n", OF);
++}
+
+- if ( !(server->type->flags & TF_RAW_STYLE_TRIBES))
++void raw_display_server_rules(struct qserver *server)
++{
++ struct rule *rule;
++ int printed = 0;
++ rule = server->rules;
++ for (; rule != NULL; rule = rule->next)
++ {
++ if (server->type->id == TRIBES2_SERVER)
+ {
+- fprintf( OF, "\t\t<ping>%d</ping>\n",
+- server->n_requests ? server->ping_total/server->n_requests : 999);
+- fprintf( OF, "\t\t<retries>%d</retries>\n",
+- server->n_retries);
++ char *v;
++ for (v = rule->value; *v; v++)
++ if (*v == '\n')
++ {
++ *v = ' ';
++ }
+ }
+
+- if ( server->type->flags & TF_RAW_STYLE_QUAKE)
+- {
+- fprintf( OF, "\t\t<address>%s</address>\n",
+- xml_escape(server->address));
+- fprintf( OF, "\t\t<protocolversion>%d</protocolversion>\n",
+- server->protocol_version);
+- }
++ fprintf(OF, "%s%s=%s", (printed) ? RD : "", rule->name, rule->value);
++ printed++;
+ }
+-
+- if ( ! server->type->master && server->error == NULL)
++ if (server->missing_rules)
+ {
+- if ( get_server_rules)
+- server->type->display_xml_rule_func( server);
+- if ( get_player_info)
+- server->type->display_xml_player_func( server);
++ fprintf(OF, "%s?", (printed) ? RD : "");
+ }
+-
+- fprintf( OF, "\t</server>\n");
++ fputs("\n", OF);
+ }
+
+-void
+-xml_header()
++void raw_display_q_player_info(struct qserver *server)
+ {
+- fprintf( OF, "<?xml version=\"1.0\" encoding=\"%s\"?>\n<qstat>\n",
+- xml_encoding == ENCODING_LATIN_1 ? "iso-8859-1" : "UTF-8");
+-}
++ char fmt[] = "%d""%s%s""%s%s""%s%d""%s%s""%s%s""%s%s";
++ struct player *player;
+
+-void
+-xml_footer()
+-{
+- fprintf( OF, "</qstat>\n");
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt,
++ player->number, RD,
++ xform_name(player->name, server), RD,
++ player->address, RD,
++ player->frags, RD,
++ play_time(player->connect_time, 1), RD,
++ quake_color(player->shirt_color), RD,
++ quake_color(player->pants_color)
++ );
++ fputs("\n", OF);
++ }
+ }
+
+-void
+-xml_display_server_rules( struct qserver *server)
++void raw_display_qw_player_info(struct qserver *server)
+ {
+- struct rule *rule;
+- rule= server->rules;
++ char fmt[128];
++ struct player *player;
++
++ strcpy(fmt, "%d""%s%s""%s%d""%s%s""%s%s""%s%s");
++ strcat(fmt, "%s%d""%s%s""%s%s");
+
+- fprintf( OF, "\t\t<rules>\n");
+- for ( ; rule != NULL; rule= rule->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<rule name=\"%s\">%s</rule>\n",
+- xml_escape(rule->name), xml_escape(rule->value));
+- }
+- fprintf( OF, "\t\t</rules>\n");
++ fprintf(OF, fmt,
++ player->number, RD,
++ xform_name(player->name, server), RD,
++ player->frags, RD,
++ play_time(player->connect_time, 1), RD,
++ quake_color(player->shirt_color), RD,
++ quake_color(player->pants_color), RD,
++ player->ping, RD,
++ player->skin ? player->skin: "", RD,
++ player->team_name ? player->team_name: ""
++ );
++ fputs("\n", OF);
++ }
+ }
+
+-void
+-xml_display_q_player_info( struct qserver *server)
++void raw_display_q2_player_info(struct qserver *server)
+ {
++ static const char *fmt = "%s""%s%d""%s%d";
++ static const char *fmt_team = "%s""%s%d""%s%d""%s%d";
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player number=\"%d\">\n",
+- player->number);
+-
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<address>%s</address>\n",
+- xml_escape(player->address));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->frags);
+- fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+- xml_escape(play_time(player->connect_time,1)));
+-
+- if ( color_names)
++ if (server->flags &FLAG_PLAYER_TEAMS)
+ {
+- fprintf( OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n",
+- xml_escape(quake_color(player->shirt_color)));
+- fprintf( OF, "\t\t\t\t<color for=\"pants\">%s</color>\n",
+- xml_escape(quake_color(player->pants_color)));
++ fprintf(OF, fmt_team, xform_name(player->name, server), RD, player->frags, RD, player->ping, RD, player->team);
+ }
+ else
+ {
+- fprintf( OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n",
+- quake_color(player->shirt_color));
+- fprintf( OF, "\t\t\t\t<color for=\"pants\">%s</color>\n",
+- quake_color(player->pants_color));
++ fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, player->ping);
+ }
+-
+- fprintf( OF, "\t\t\t</player>\n");
++ fputs("\n", OF);
+ }
+-
+- fprintf( OF, "\t\t</players>\n");
+ }
+
+-void
+-xml_display_qw_player_info( struct qserver *server)
++void raw_display_unreal_player_info(struct qserver *server)
+ {
++ static const char *fmt = "%s""%s%d""%s%d""%s%d""%s%s""%s%s""%s%s";
++ static const char *fmt_team_name = "%s""%s%d""%s%d""%s%s""%s%s""%s%s""%s%s";
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player number=\"%d\">\n",
+- player->number);
+-
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->frags);
+- fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+- xml_escape(play_time(player->connect_time,1)));
+-
+- if ( color_names)
++ if (player->team_name)
+ {
+- fprintf( OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n",
+- xml_escape(quake_color(player->shirt_color)));
+- fprintf( OF, "\t\t\t\t<color for=\"pants\">%s</color>\n",
+- xml_escape(quake_color(player->pants_color)));
++ fprintf(OF, fmt_team_name,
++ xform_name(player->name,server), RD,
++ player->frags, RD,
++ player->ping, RD,
++ player->team_name, RD,
++ player->skin ? player->skin: "", RD,
++ player->mesh ? player->mesh: "", RD,
++ player->face ? player->face: ""
++ );
+ }
+ else
+ {
+- fprintf( OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n",
+- quake_color(player->shirt_color));
+- fprintf( OF, "\t\t\t\t<color for=\"pants\">%s</color>\n",
+- quake_color(player->pants_color));
++ fprintf(OF, fmt,
++ xform_name(player->name, server), RD,
++ player->frags, RD,
++ player->ping, RD,
++ player->team, RD,
++ player->skin ? player->skin: "", RD,
++ player->mesh ? player->mesh: "", RD,
++ player->face ? player->face: ""
++ );
+ }
+-
+- fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+- player->ping);
+- fprintf( OF, "\t\t\t\t<skin>%s</skin>\n",
+- player->skin ? xml_escape(player->skin) : "");
+-
+- fprintf( OF, "\t\t\t</player>\n");
++ fputs("\n", OF);
+ }
+-
+- fprintf( OF, "\t\t</players>\n");
+ }
+
+-void
+-xml_display_q2_player_info( struct qserver *server)
++void raw_display_halflife_player_info(struct qserver *server)
+ {
++ static char fmt[24] = "%s""%s%d""%s%s";
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player>\n");
+-
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n", player->frags);
+- if ( server->flags & FLAG_PLAYER_TEAMS)
+- fprintf( OF, "\t\t\t\t<team>%d</team>\n", player->team);
+- fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+- player->ping);
+-
+- fprintf( OF, "\t\t\t</player>\n");
++ fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, play_time(player->connect_time, 1));
++ fputs("\n", OF);
+ }
+-
+- fprintf( OF, "\t\t</players>\n");
+ }
+
+-void xml_display_player_info_info(struct player* player)
++void raw_display_fl_player_info(struct qserver *server)
+ {
+- struct info *info;
++ static char fmt[24] = "%s""%s%d""%s%s""%s%d""%s%d";
++ struct player *player;
+
+- for (info = player->info; info; info = info->next )
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- if ( info->name )
+- {
+- char *name = xml_escape( info->name );
+- char *value = xml_escape( info->value );
+- fprintf( OF, "\t\t\t\t<%s>%s</%s>\n", name, value, name );
+- }
++ fprintf(
++ OF, fmt,
++ xform_name(player->name, server), RD,
++ player->frags, RD,
++ play_time(player->connect_time, 1), RD,
++ player->ping, RD,
++ player->team
++ );
++ fputs("\n", OF);
+ }
+ }
+
+-void
+-xml_display_unreal_player_info( struct qserver *server)
++void raw_display_tribes_player_info(struct qserver *server)
+ {
++ static char fmt[24] = "%s""%s%d""%s%d""%s%d""%s%d";
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player>\n");
+-
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->frags);
+- if ( -999 != player->deaths )
+- {
+- fprintf( OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
+- }
+- fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+- player->ping);
++ fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, player->ping, RD, player->team, RD, player->packet_loss);
++ fputs("\n", OF);
++ }
++}
+
+- if ( player->team_name)
+- {
+- fprintf( OF, "\t\t\t\t<team>%s</team>\n",
+- xml_escape(player->team_name));
+- }
+- else if ( -1 != player->team )
+- {
+- fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+- player->team);
+- }
++void raw_display_tribes2_player_info(struct qserver *server)
++{
++ static char fmt[] = "%s""%s%d""%s%d""%s%s""%s%s""%s%s";
++ struct player *player;
++ char *type;
+
+- // Some games dont provide
+- // so only display if they do
+- if ( player->skin )
+- {
+- fprintf( OF, "\t\t\t\t<skin>%s</skin>\n",
+- player->skin ? xml_escape(player->skin) : "");
+- }
+- if ( player->mesh )
+- {
+- fprintf( OF, "\t\t\t\t<mesh>%s</mesh>\n",
+- player->mesh ? xml_escape(player->mesh) : "");
+- }
+- if ( player->face )
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ switch (player->type_flag)
+ {
+- fprintf( OF, "\t\t\t\t<face>%s</face>\n",
+- player->face ? xml_escape(player->face) : "");
++ case PLAYER_TYPE_BOT:
++ type = "Bot";
++ break;
++ case PLAYER_TYPE_ALIAS:
++ type = "Alias";
++ break;
++ default:
++ type = "";
++ break;
+ }
+-
+- xml_display_player_info_info(player);
+- fprintf( OF, "\t\t\t</player>\n");
++ fprintf(OF, fmt,
++ xform_name(player->name, server), RD,
++ player->frags, RD,
++ player->team, RD,
++ player->team_name ? player->team_name : "TEAM", RD,
++ type, RD,
++ player->tribe_tag ? xform_name(player->tribe_tag, server): ""
++ );
++ fputs("\n", OF);
+ }
+-
+- fprintf( OF, "\t\t</players>\n");
+ }
+
+-void
+-xml_display_halflife_player_info( struct qserver *server)
++void raw_display_bfris_player_info(struct qserver *server)
+ {
++ static char fmt[] = "%d""%s%d""%s%s""%s%d""%s%d""%s%d""%s%s";
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player>\n");
+-
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->frags);
+- fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+- xml_escape(play_time( player->connect_time,1)));
+-
+- fprintf( OF, "\t\t\t</player>\n");
++ fprintf(OF, fmt,
++ player->number, RD,
++ player->ship, RD,
++ player->team_name, RD,
++ player->ping, RD,
++ player->score, RD,
++ player->frags, RD,
++ xform_name(player->name, server)
++ );
++ fputs("\n", OF);
+ }
+-
+- fprintf( OF, "\t\t</players>\n");
+ }
+
+-void
+-xml_display_tribes_player_info( struct qserver *server)
++void raw_display_descent3_player_info(struct qserver *server)
+ {
++ static char fmt[] = "%s""%s%d""%s%d""%s%d""%s%d";
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player>\n");
++ fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, player->deaths, RD, player->ping, RD, player->team);
++ fputs("\n", OF);
++ }
++}
+
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->frags);
+- fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+- player->team);
+- fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+- player->ping);
+- fprintf( OF, "\t\t\t\t<packetloss>%d</packetloss>\n",
+- player->packet_loss);
++void raw_display_ghostrecon_player_info(struct qserver *server)
++{
++ static char fmt[28] = "%s""%s%d""%s%d";
++ struct player *player;
+
+- fprintf( OF, "\t\t\t</player>\n");
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt, xform_name(player->name, server), RD, player->deaths, RD, player->team);
++ fputs("\n", OF);
+ }
+-
+- fprintf( OF, "\t\t</players>\n");
+ }
+
+-void
+-xml_display_tribes2_player_info( struct qserver *server)
++void raw_display_eye_player_info(struct qserver *server)
+ {
++ static const char *fmt = "%s""%s%d""%s%d""%s%d""%s%s""%s%s";
++ static const char *fmt_team_name = "%s""%s%d""%s%d""%s%s""%s%s""%s%s";
+ struct player *player;
+- char *type;
+-
+- fprintf( OF, "\t\t<players>\n");
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- if ( player->team_name)
++ if (player->team_name)
+ {
+- switch( player->type_flag)
+- {
+- case PLAYER_TYPE_BOT:
+- type= "Bot";
+- break;
+- case PLAYER_TYPE_ALIAS:
+- type= "Alias";
+- break;
+- default:
+- type= "";
+- break;
+- }
+-
+- fprintf( OF, "\t\t\t<player>\n");
+-
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->frags);
+- fprintf( OF, "\t\t\t\t<team number=\"%d\">%s</team>\n",
+- player->team, xml_escape(player->team_name));
+- fprintf( OF, "\t\t\t\t<type>%s</type>\n",
+- xml_escape(type));
+- fprintf( OF, "\t\t\t\t<clan>%s</clan>\n",
+- player->tribe_tag ? xml_escape(xform_name(player->tribe_tag,server)) : "");
+-
+- fprintf( OF, "\t\t\t</player>\n");
++ fprintf(OF, fmt_team_name,
++ xform_name(player->name,server), RD,
++ player->score, RD,
++ player->ping, RD,
++ player->team_name, RD,
++ player->skin ? player->skin: "", RD,
++ play_time(player->connect_time,1)
++ );
+ }
+- }
+-
+- fprintf( OF, "\t\t</players>\n");
++ else
++ {
++ fprintf(OF, fmt,
++ xform_name(player->name, server), RD,
++ player->score, RD,
++ player->ping, RD,
++ player->team, RD,
++ player->skin ? player->skin: "", RD,
++ play_time(player->connect_time, 1)
++ );
++ }
++ fputs("\n", OF);
++ }
+ }
+
+-void
+-xml_display_bfris_player_info( struct qserver *server)
++void raw_display_doom3_player_info(struct qserver *server)
+ {
++ static const char *fmt = "%s""%s%d""%s%d""%s%d""%s%u";
++ static const char *fmt_team_name = "%s""%s%d""%s%d""%s%s""%s%u";
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player number=\"%d\">\n",
+- player->number);
+-
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score type=\"score\">%d</score>\n",
+- player->score);
+- fprintf( OF, "\t\t\t\t<score type=\"frags\">%d</score>\n",
+- player->frags);
+- fprintf( OF, "\t\t\t\t<team>%s</team>\n",
+- xml_escape(player->team_name));
+- fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+- player->ping);
+- fprintf( OF, "\t\t\t\t<ship>%d</ship>\n",
+- player->ship);
+-
+- fprintf( OF, "\t\t\t</player>\n");
++ if (player->tribe_tag)
++ {
++ fprintf(OF, fmt_team_name, xform_name(player->name, server), RD, player->score, RD, player->ping, RD, player->tribe_tag, RD, player->number);
++ }
++ else
++ {
++ fprintf(OF, fmt, xform_name(player->name, server), RD, player->score, RD, player->ping, RD, player->team, RD, player->number);
++ }
++ fputs("\n", OF);
+ }
+-
+- fprintf( OF, "\t\t</players>\n");
+ }
+
+-void
+-xml_display_descent3_player_info( struct qserver *server)
++void raw_display_gs2_player_info(struct qserver *server)
+ {
++ static const char *fmt = "%s""%s%d""%s%d""%s%d""%s%s""%s%s";
++ static const char *fmt_team_name = "%s""%s%d""%s%d""%s%s""%s%s""%s%s";
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player>\n");
+-
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->frags);
+- fprintf( OF, "\t\t\t\t<deaths>%d</deaths>\n",
+- player->deaths);
+- fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+- player->ping);
+- fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+- player->team);
+-
+- fprintf( OF, "\t\t\t</player>\n");
++ if (player->team_name)
++ {
++ fprintf(OF, fmt_team_name, xform_name(player->name, server), RD,
++ player->score, RD,
++ player->ping, RD,
++ player->team_name, RD,
++ player->skin ? player->skin: "", RD,
++ play_time(player->connect_time,1)
++ );
++ }
++ else
++ {
++ fprintf(OF, fmt,
++ xform_name(player->name,server), RD,
++ player->score, RD,
++ player->ping, RD,
++ player->team, RD,
++ player->skin ? player->skin: "", RD,
++ play_time(player->connect_time,1)
++ );
++ }
++ fputs("\n", OF);
+ }
+-
+- fprintf( OF, "\t\t</players>\n");
+ }
+
+-void
+-xml_display_ravenshield_player_info( struct qserver *server)
++void raw_display_armyops_player_info(struct qserver *server)
+ {
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player>\n");
+-
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->frags);
+- fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+- xml_escape(play_time( player->connect_time,1)));
+-
+- fprintf( OF, "\t\t\t</player>\n");
++ player->score = calculate_armyops_score(player);
+ }
+
+- fprintf( OF, "\t\t</players>\n");
++ raw_display_gs2_player_info(server);
+ }
+
+-
+-void
+-xml_display_ghostrecon_player_info( struct qserver *server)
++void raw_display_ts2_player_info(struct qserver *server)
+ {
++ static const char *fmt = "%s""%s%d""%s%s""%s%s";
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player>\n");
++ fprintf(OF, fmt,
++ xform_name(player->name,server), RD,
++ player->ping, RD,
++ player->skin ? player->skin: "", RD,
++ play_time(player->connect_time, 1)
++ );
++ fputs("\n", OF);
++ }
++}
+
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<deaths>%d</deaths>\n",
+- player->deaths);
+- fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+- player->team);
++void raw_display_ts3_player_info(struct qserver *server)
++{
++ static const char *fmt = "%s""%s%s""%s%s";
++ struct player *player;
+
+- fprintf( OF, "\t\t\t</player>\n");
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt,
++ xform_name(player->name,server), RD,
++ player->skin ? player->skin: "", RD,
++ play_time(player->connect_time, 1)
++ );
++ fputs("\n", OF);
+ }
+-
+- fprintf( OF, "\t\t</players>\n");
+ }
+
+-void
+-xml_display_eye_player_info( struct qserver *server)
++void raw_display_bfbc2_player_info(struct qserver *server)
+ {
+- struct player *player;
++ static const char *fmt = "%s""%s%s""%s%s";
++ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt,
++ xform_name(player->name,server), RD,
++ player->skin ? player->skin: "", RD,
++ play_time(player->connect_time, 1)
++ );
++ fputs("\n", OF);
++ }
++}
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, "\t\t\t<player>\n");
++void raw_display_wic_player_info(struct qserver *server)
++{
++ static const char *fmt = "%s""%s%d""%s%s""%s%s";
++ struct player *player;
+
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->score);
+- fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+- player->ping);
+- if ( player->team_name)
+- fprintf( OF, "\t\t\t\t<team>%s</team>\n",
+- xml_escape(player->team_name));
+- else
+- fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+- player->team);
+- if ( player->skin)
+- fprintf( OF, "\t\t\t\t<skin>%s</skin>\n",
+- xml_escape(player->skin));
+- if ( player->connect_time)
+- fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+- xml_escape(play_time( player->connect_time,1)));
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt,
++ xform_name(player->name,server), RD,
++ player->score, RD,
++ player->team_name, RD,
++ player->tribe_tag ? player->tribe_tag : ""
++ );
++ fputs("\n", OF);
++ }
++}
+
+- fprintf( OF, "\t\t\t</player>\n");
+- }
++void raw_display_ventrilo_player_info(struct qserver *server)
++{
++ static const char *fmt = "%s""%s%d""%s%s""%s%s";
++ struct player *player;
+
+- fprintf( OF, "\t\t</players>\n");
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(
++ OF, fmt,
++ xform_name(player->name, server),
++ RD, player->team,
++ RD, player->team_name,
++ RD, play_time(player->connect_time, 1)
++ );
++ fputs("\n", OF);
++ }
+ }
+
+-void
+-xml_display_doom3_player_info( struct qserver *server)
++void raw_display_tm_player_info(struct qserver *server)
+ {
+- struct player *player;
++ static const char *fmt = "%s""%s%d""%s%s""%s%s";
++ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
+-
+- player= server->players;
+- for ( ; player != NULL; player= player->next) {
+- fprintf( OF, "\t\t\t<player>\n");
+-
+- fprintf( OF, "\t\t\t\t<number>%u</number>\n",
+- player->number);
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->score);
+- fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+- player->ping);
+- if ( player->tribe_tag)
+- fprintf( OF, "\t\t\t\t<clan>%s</clan>\n",
+- player->tribe_tag ? xml_escape(xform_name(player->tribe_tag,server)) : "");
+- else
+- fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+- player->team);
+- if ( player->skin)
+- fprintf( OF, "\t\t\t\t<skin>%s</skin>\n",
+- xml_escape(player->skin));
+- if ( player->connect_time)
+- fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+- xml_escape(play_time( player->connect_time,1)));
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt,
++ xform_name(player->name,server), RD,
++ player->ping, RD,
++ player->skin ? player->skin: "", RD,
++ play_time(player->connect_time,1)
++ );
++ fputs("\n", OF);
++ }
++}
+
+- xml_display_player_info_info(player);
++void raw_display_tee_player_info(struct qserver *server)
++{
++ static const char *fmt = "%s";
++ struct player *player;
+
+- fprintf( OF, "\t\t\t</player>\n");
+- }
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt, xform_name(player->name,server) );
++ fputs("\n", OF);
++ }
++}
+
+- fprintf( OF, "\t\t</players>\n");
++void raw_display_ravenshield_player_info(struct qserver *server)
++{
++ static char fmt[24] = "%s""%s%d""%s%s";
++ struct player *player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, play_time(player->connect_time, 1));
++ fputs("\n", OF);
++ }
+ }
+
+-void
+-xml_display_hl2_player_info( struct qserver *server )
++void raw_display_savage_player_info(struct qserver *server)
+ {
+- // ATM this looks like halflife player info
+- xml_display_halflife_player_info( server );
++ static char fmt[24] = "%s""%s%d""%s%s";
++ struct player *player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, play_time(player->connect_time, 1));
++ fputs("\n", OF);
++ }
+ }
+
+-void
+-xml_display_gs2_player_info( struct qserver *server)
++void raw_display_farcry_player_info(struct qserver *server)
+ {
+- struct player *player;
++ static char fmt[24] = "%s""%s%d""%s%s";
++ struct player *player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, play_time(player->connect_time, 1));
++ fputs("\n", OF);
++ }
++}
+
+- fprintf( OF, "\t\t<players>\n");
++/* XML output
++ * Contributed by <sgarner at gameplanet.co.nz> :-)
++ */
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
+- {
+- fprintf( OF, "\t\t\t<player>\n");
++void xml_display_server(struct qserver *server)
++{
++ char *prefix;
++ prefix = server->type->type_prefix;
+
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
+- if ( NA_INT != player->score )
++ if (server->server_name == DOWN)
++ {
++ if (!up_servers_only)
+ {
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n", player->score);
++ fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n", xml_escape(prefix), xml_escape(server->arg), xml_escape(DOWN));
++ fprintf(OF, "\t\t<hostname>%s</hostname>\n", xml_escape((hostname_lookup) ? server->host_name: server->arg));
++ fprintf(OF, "\t</server>\n");
+ }
+- if ( NA_INT != player->deaths )
++ return;
++ }
++ if (server->server_name == TIMEOUT)
++ {
++ if (server->flags &FLAG_BROADCAST && server->n_servers)
+ {
+- fprintf( OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
++ fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\" servers=\"%d\">\n",
++ xml_escape(prefix),
++ xml_escape(server->arg),
++ xml_escape(TIMEOUT),
++ server->n_servers
++ );
++ fprintf(OF, "\t</server>\n");
+ }
+- if ( NA_INT != player->frags )
++ else if (!up_servers_only)
+ {
+- fprintf( OF, "\t\t\t\t<frags>%d</frags>\n", player->frags);
++ fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n", xml_escape(prefix), xml_escape(server->arg), xml_escape(TIMEOUT));
++ fprintf(OF, "\t\t<hostname>%s</hostname>\n", xml_escape((hostname_lookup) ? server->host_name: server->arg));
++ fprintf(OF, "\t</server>\n");
+ }
+- if ( player->team_name )
++ return ;
++ }
++
++ if (server->error != NULL)
++ {
++ fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n", xml_escape(prefix), xml_escape(server->arg), "ERROR");
++ fprintf(OF, "\t\t<hostname>%s</hostname>\n", xml_escape((hostname_lookup) ? server->host_name: server->arg));
++ fprintf(OF, "\t\t<error>%s</error>\n", xml_escape(server->error));
++ }
++ else if (server->type->master)
++ {
++ fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\" servers=\"%d\">\n", xml_escape(prefix), xml_escape(server->arg), "UP", server->n_servers);
++ }
++ else
++ {
++ fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n", xml_escape(prefix), xml_escape(server->arg), "UP");
++ fprintf(OF, "\t\t<hostname>%s</hostname>\n", xml_escape((hostname_lookup) ? server->host_name: server->arg));
++ fprintf(OF, "\t\t<name>%s</name>\n", xml_escape(xform_name(server->server_name, server)));
++ fprintf(OF, "\t\t<gametype>%s</gametype>\n", xml_escape(get_qw_game(server)));
++ fprintf(OF, "\t\t<map>%s</map>\n", xml_escape(xform_name(server->map_name, server)));
++ fprintf(OF, "\t\t<numplayers>%d</numplayers>\n", server->num_players);
++ fprintf(OF, "\t\t<maxplayers>%d</maxplayers>\n", server->max_players);
++ fprintf(OF, "\t\t<numspectators>%d</numspectators>\n", server->num_spectators);
++ fprintf(OF, "\t\t<maxspectators>%d</maxspectators>\n", server->max_spectators);
++
++ if (!(server->type->flags &TF_RAW_STYLE_TRIBES))
+ {
+- fprintf( OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++ fprintf(OF, "\t\t<ping>%d</ping>\n", server->n_requests ? server->ping_total / server->n_requests: 999);
++ fprintf(OF, "\t\t<retries>%d</retries>\n", server->n_retries);
+ }
+- else
++
++ if (server->type->flags &TF_RAW_STYLE_QUAKE)
+ {
+- fprintf( OF, "\t\t\t\t<team>%d</team>\n",player->team);
++ fprintf(OF, "\t\t<address>%s</address>\n", xml_escape(server->address));
++ fprintf(OF, "\t\t<protocolversion>%d</protocolversion>\n", server->protocol_version);
+ }
++ }
+
+- if ( player->skin)
++ if (!server->type->master && server->error == NULL)
++ {
++ if (get_server_rules && NULL != server->type->display_xml_rule_func )
+ {
+- fprintf( OF, "\t\t\t\t<skin>%s</skin>\n", xml_escape(player->skin));
++ server->type->display_xml_rule_func(server);
+ }
+-
+- if ( player->connect_time)
++ if (get_player_info && NULL != server->type->display_xml_player_func )
+ {
+- fprintf( OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time( player->connect_time,1)));
++ server->type->display_xml_player_func(server);
+ }
++ }
+
+- xml_display_player_info_info(player);
++ fprintf(OF, "\t</server>\n");
++}
+
+- fprintf( OF, "\t\t\t</player>\n");
+- }
++void xml_header()
++{
++ if (xml_encoding == ENCODING_LATIN_1)
++ {
++ fprintf(OF, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<qstat>\n");
++ }
++ else if (output_bom)
++ {
++ fprintf(OF, "%c%c%c<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<qstat>\n", 0xEF, 0xBB, 0xBF);
++ }
++ else
++ {
++ fprintf(OF, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<qstat>\n");
++ }
++}
+
+- fprintf( OF, "\t\t</players>\n");
++void xml_footer()
++{
++ fprintf(OF, "</qstat>\n");
+ }
+
+-void
+-xml_display_armyops_player_info( struct qserver *server)
++void xml_display_server_rules(struct qserver *server)
+ {
+- struct player *player;
++ struct rule *rule;
++ rule = server->rules;
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ fprintf(OF, "\t\t<rules>\n");
++ for (; rule != NULL; rule = rule->next)
+ {
+- player->score = calculate_armyops_score( player );
++ fprintf(OF, "\t\t\t<rule name=\"%s\">%s</rule>\n", xml_escape(rule->name), xml_escape(rule->value));
+ }
+-
+- xml_display_gs2_player_info(server);
++ fprintf(OF, "\t\t</rules>\n");
+ }
+
+-void
+-xml_display_ts2_player_info( struct qserver *server)
++void xml_display_q_player_info(struct qserver *server)
+ {
+- struct player *player;
++ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
++ fprintf(OF, "\t\t<players>\n");
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player>\n");
++ fprintf(OF, "\t\t\t<player number=\"%d\">\n", player->number);
+
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<address>%s</address>\n", xml_escape(player->address));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+
+- if ( player->connect_time )
++ if (color_names)
++ {
++ fprintf(OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n", xml_escape(quake_color(player->shirt_color)));
++ fprintf(OF, "\t\t\t\t<color for=\"pants\">%s</color>\n", xml_escape(quake_color(player->pants_color)));
++ }
++ else
+ {
+- fprintf( OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time( player->connect_time,1)));
++ fprintf(OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n", quake_color(player->shirt_color));
++ fprintf(OF, "\t\t\t\t<color for=\"pants\">%s</color>\n", quake_color(player->pants_color));
+ }
+
+- xml_display_player_info_info(player);
+- fprintf( OF, "\t\t\t</player>\n");
+- }
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+- fprintf( OF, "\t\t</players>\n");
++ fprintf(OF, "\t\t</players>\n");
+ }
+
+-void
+-xml_display_tm_player_info( struct qserver *server)
++void xml_display_qw_player_info(struct qserver *server)
+ {
+- struct player *player;
++ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
++ fprintf(OF, "\t\t<players>\n");
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player>\n");
++ fprintf(OF, "\t\t\t<player number=\"%d\">\n", player->number);
+
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+
+- if ( player->connect_time )
++ if (color_names)
++ {
++ fprintf(OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n", xml_escape(quake_color(player->shirt_color)));
++ fprintf(OF, "\t\t\t\t<color for=\"pants\">%s</color>\n", xml_escape(quake_color(player->pants_color)));
++ }
++ else
+ {
+- fprintf( OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time( player->connect_time,1)));
++ fprintf(OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n", quake_color(player->shirt_color));
++ fprintf(OF, "\t\t\t\t<color for=\"pants\">%s</color>\n", quake_color(player->pants_color));
+ }
+
+- xml_display_player_info_info(player);
+- fprintf( OF, "\t\t\t</player>\n");
+- }
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++ fprintf(OF, "\t\t\t\t<skin>%s</skin>\n", player->skin ? xml_escape(player->skin): "");
++ fprintf(OF, "\t\t\t\t<team>%s</team>\n", player->team_name ? xml_escape(player->team_name): "");
+
+- fprintf( OF, "\t\t</players>\n");
+-}
+
++ fprintf(OF, "\t\t\t</player>\n");
++ }
++
++ fprintf(OF, "\t\t</players>\n");
++}
+
+-void
+-xml_display_savage_player_info( struct qserver *server)
++void xml_display_q2_player_info(struct qserver *server)
+ {
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
++ fprintf(OF, "\t\t<players>\n");
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player>\n");
++ fprintf(OF, "\t\t\t<player>\n");
+
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->frags);
+- fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+- xml_escape(play_time( player->connect_time,1)));
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ if (server->flags &FLAG_PLAYER_TEAMS)
++ {
++ fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++ }
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
+
+- fprintf( OF, "\t\t\t</player>\n");
++ fprintf(OF, "\t\t\t</player>\n");
+ }
+
+- fprintf( OF, "\t\t</players>\n");
++ fprintf(OF, "\t\t</players>\n");
+ }
+
+-void
+-xml_display_farcry_player_info( struct qserver *server)
++void xml_display_player_info_info(struct player *player)
++{
++ struct info *info;
++
++ for (info = player->info; info; info = info->next)
++ {
++ if (info->name)
++ {
++ char *name = xml_escape(info->name);
++ char *value = xml_escape(info->value);
++ fprintf(OF, "\t\t\t\t<%s>%s</%s>\n", name, value, name);
++ }
++ }
++}
++
++void xml_display_unreal_player_info(struct qserver *server)
+ {
+ struct player *player;
+
+- fprintf( OF, "\t\t<players>\n");
++ fprintf(OF, "\t\t<players>\n");
+
+- player= server->players;
+- for ( ; player != NULL; player= player->next)
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- fprintf( OF, "\t\t\t<player>\n");
++ fprintf(OF, "\t\t\t<player>\n");
++
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ if ( - 999 != player->deaths)
++ {
++ fprintf(OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
++ } fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++
++ if (player->team_name)
++ {
++ fprintf(OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++ }
++ else if ( - 1 != player->team)
++ {
++ fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++ }
+
+- fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+- xml_escape(xform_name( player->name, server)));
+- fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+- player->frags);
+- fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+- xml_escape(play_time( player->connect_time,1)));
++ // Some games dont provide
++ // so only display if they do
++ if (player->skin)
++ {
++ fprintf(OF, "\t\t\t\t<skin>%s</skin>\n", player->skin ? xml_escape(player->skin): "");
++ }
++ if (player->mesh)
++ {
++ fprintf(OF, "\t\t\t\t<mesh>%s</mesh>\n", player->mesh ? xml_escape(player->mesh): "");
++ }
++ if (player->face)
++ {
++ fprintf(OF, "\t\t\t\t<face>%s</face>\n", player->face ? xml_escape(player->face): "");
++ }
+
+- fprintf( OF, "\t\t\t</player>\n");
++ xml_display_player_info_info(player);
++ fprintf(OF, "\t\t\t</player>\n");
+ }
+
+- fprintf( OF, "\t\t</players>\n");
++ fprintf(OF, "\t\t</players>\n");
+ }
+
+-
+-void
+-display_progress()
++void xml_display_halflife_player_info(struct qserver *server)
+ {
+- static struct timeval rate_start= {0,0};
+- char rate[32];
+- struct timeval now;
++ struct player *player;
+
+- gettimeofday( &now, NULL);
++ fprintf(OF, "\t\t<players>\n");
+
+- if ( ! rate_start.tv_sec)
+- {
+- rate_start= now;
+- rate[0]='\0';
+- }
+- else
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- int delta= time_delta( &now, &rate_start);
+- if ( delta > 1500 )
+- sprintf( rate, " %d servers/sec ", (num_servers_returned+num_servers_timed_out)*1000 / delta);
+- else
+- rate[0]='\0';
+- }
++ fprintf(OF, "\t\t\t<player>\n");
+
+- // only print out every 'progress' number of servers.
+- if (
+- 0 != num_servers_returned+num_servers_timed_out &&
+- ( progress == 1 ||
+- (num_servers_returned+num_servers_timed_out) % progress == 0 )
+- )
+- {
+- fprintf( stderr, "\r%d/%d (%d timed out, %d down)%s",
+- num_servers_returned+num_servers_timed_out,
+- num_servers_total,
+- num_servers_timed_out,
+- num_servers_down,
+- rate
+- );
+- }
+-}
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+
+-/* ----- END MODIFICATION ----- Don't need to change anything below here. */
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
++ fprintf(OF, "\t\t</players>\n");
++}
+
+-void set_non_blocking( int fd);
+-int set_fds( fd_set *fds);
+-void get_next_timeout( struct timeval *timeout);
++void xml_display_fl_player_info(struct qserver *server)
++{
++ struct player *player;
+
+-void set_file_descriptors();
+-int wait_for_file_descriptors( struct timeval *timeout);
+-struct qserver * get_next_ready_server();
++ fprintf(OF, "\t\t<players>\n");
+
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
+
+-/* Misc flags
+- */
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++ fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+
+-struct timeval packet_recv_time;
+-int one_server_type_id= ~ MASTER_SERVER;
+-static int one= 1;
+-static int little_endian;
+-static int big_endian;
+-unsigned int swap_long( void *);
+-unsigned short swap_short( void *);
+-float swap_float_from_little( void *f);
+-char * strndup( const char *string, size_t len);
+-#define FORCE 1
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+-/* Print an error message and the program usage notes
+- */
++ fprintf(OF, "\t\t</players>\n");
++}
+
+-void
+-usage( char *msg, char **argv, char *a1)
++void xml_display_tribes_player_info(struct qserver *server)
+ {
+- int i;
+- server_type *type;
+- server_type **sorted_types;
+-
+- if ( msg)
+- fprintf( stderr, msg, a1);
++ struct player *player;
+
+- printf( "Usage: %s [options ...]\n", argv[0]);
+- printf( "\t[-default server-type] [-cfg file] [-f file] [host[:port]] ...\n");
+- printf( "Where host is an IP address or host name\n");
++ fprintf(OF, "\t\t<players>\n");
+
+- sorted_types = (server_type **) malloc( sizeof(server_type *) * n_server_types );
+- type = &types[0];
+- for ( i = 0; type->id != Q_UNKNOWN_TYPE; type++, i++ )
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- sorted_types[i] = type;
++ fprintf(OF, "\t\t\t<player>\n");
++
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++ fprintf(OF, "\t\t\t\t<packetloss>%d</packetloss>\n", player->packet_loss);
++
++ fprintf(OF, "\t\t\t</player>\n");
+ }
+- quicksort( (void**)sorted_types, 0, n_server_types-1, (int (*)(void*,void*)) type_option_compare );
+
++ fprintf(OF, "\t\t</players>\n");
++}
+
+- for ( i = 0; i < n_server_types; i++ )
+- {
+- type = sorted_types[i];
+- printf( "%s\t\tquery %s server\n", type->type_option, type->game_name );
+- }
+-
+- quicksort( (void**)sorted_types, 0, n_server_types-1, (int (*)(void*,void*)) type_string_compare );
+- printf( "-default\tset default server type:");
+- for ( i = 0; i < n_server_types; type++, i++ )
+- {
+- type = sorted_types[i];
+- printf( " %s", type->type_string );
+- }
+- puts("");
+- printf( "-nocfg\t\tIgnore qstat configuration loaded from any default location. Must be the first option on the command-line.\n" );
+- printf( "-cfg\t\tread the extended types from given file not the default one\n");
+- printf( "-f\t\tread hosts from file\n");
+- printf( "-R\t\tfetch and display server rules\n");
+- printf( "-P\t\tfetch and display player info\n");
+- printf( "-sort\t\tsort servers and/or players\n");
+- printf( "-u\t\tonly display servers that are up\n");
+- printf( "-nf\t\tdo not display full servers\n");
+- printf( "-ne\t\tdo not display empty servers\n");
+- printf( "-nh\t\tdo not display header line.\n" );
+- printf( "-cn\t\tdisplay color names instead of numbers\n");
+- printf( "-ncn\t\tdisplay color numbers instead of names\n");
+- printf( "-hc\t\tdisplay colors in #rrggbb format\n");
+- printf( "-tc\t\tdisplay time in clock format (DhDDmDDs)\n");
+- printf( "-tsw\t\tdisplay time in stop-watch format (DD:DD:DD)\n");
+- printf( "-ts\t\tdisplay time in seconds\n");
+- printf( "-pa\t\tdisplay player address\n");
+- printf( "-hpn\t\tdisplay player names in hex\n");
+- printf( "-hsn\t\tdisplay server names in hex\n");
+- printf( "-nh\t\tdo not display header\n");
+- printf( "-old\t\told style display\n");
+- printf( "-progress\tdisplay progress meter (text only)\n");
+- printf( "-retry\t\tnumber of retries, default is %d\n", DEFAULT_RETRIES);
+- printf( "-interval\tinterval between retries, default is %.2f seconds\n",
+- DEFAULT_RETRY_INTERVAL / 1000.0);
+- printf( "-mi\t\tinterval between master server retries, default is %.2f seconds\n",
+- (DEFAULT_RETRY_INTERVAL*4) / 1000.0);
+- printf( "-timeout\ttotal time in seconds before giving up\n");
+- printf( "-maxsim\t\tset maximum simultaneous queries\n");
+- printf( "-sendinterval\t\tset time in ms between sending packets, default %u\n", sendinterval);
+- printf( "-errors\t\tdisplay errors\n");
+- printf( "-allowserverdups\t\tallow adding multiple servers with same ip:port (needed for ts2)\n");
+- printf( "-of\t\toutput file\n");
+- printf( "-af\t\tLike -of, but append to the file\n" );
+- printf( "-raw <delim>\toutput in raw format using <delim> as delimiter\n");
+- printf( "-mdelim <delim>\tFor rules with multi values use <delim> as delimiter\n");
+- printf( "-xml\t\toutput status data as an XML document\n");
+- printf( "-Th,-Ts,-Tpt\toutput templates: header, server and player\n");
+- printf( "-Tr,-Tt\t\toutput templates: rule, and trailer\n");
+- printf( "-srcport <range>\tSend packets from these network ports\n");
+- printf( "-srcip <IP>\tSend packets using this IP address\n");
+- printf( "-H\t\tresolve host names\n");
+- printf( "-Hcache\t\thost name cache file\n");
+- printf( "-carets\t\tDisplay carets in Quake 3 player names\n" );
+- printf( "-d\t\tEnable debug options. Specify multiple times to increase debug level.\n");
+-#ifdef ENABLE_DUMP
+- printf( "-dump\t\twrite received raw packets to dumpNNN files which must not exist before\n");
+- printf( "-pkt <file>\tuse file as server reply instead of quering the server. Works only with TF_SINGLE_QUERY servers\n");
+-#endif
+- printf( "-htmlmode\tConvert <, >, and & to the equivalent HTML entities\n" );
+- printf( "-htmlnames\tColorize Quake 3 and Tribes 2 player names using html font tags\n" );
+- printf( "-nohtmlnames\tDo not colorize Quake 3 and Tribes 2 player names even if $HTML is used in an output template.\n" );
+- printf( "-showgameport\tAlways display the game port in QStat output.\n" );
+- printf( "-noportoffset\tDont use builtin status port offsets ( assume query port was specified ).\n" );
+- printf( "-raw-arg\tWhen used with -raw, always display the server address as it appeared in a file or on the command-line.\n" );
+- printf( "-utf8\t\tUse the UTF-8 character encoding for XML output.\n" );
+-#ifdef _WIN32
+- printf( "-noconsole\t\tFree the console\n" );
+-#endif
+- printf( "\n");
+- printf( "Sort keys:\n");
+- printf( " servers: p=by-ping, g=by-game, i=by-IP-address, h=by-hostname, n=by-#-players, l=by-list-order\n");
+- printf( " players: P=by-ping, F=by-frags, T=by-team, N=by-name\n");
+- printf( "\nqstat version %s\n", VERSION);
+- exit(0);
+-}
++void xml_display_tribes2_player_info(struct qserver *server)
++{
++ struct player *player;
++ char *type;
+
+-struct server_arg {
+- int type_id;
+- server_type *type;
+- char *arg;
+- char *outfilename;
+- char *query_arg;
+-};
++ fprintf(OF, "\t\t<players>\n");
+
+-server_type*
+-find_server_type_id( int type_id)
+-{
+- server_type *type= &types[0];
+- for ( ; type->id != Q_UNKNOWN_TYPE; type++)
+- if ( type->id == type_id)
+- return type;
+- return NULL;
+-}
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ if (player->team_name)
++ {
++ switch (player->type_flag)
++ {
++ case PLAYER_TYPE_BOT:
++ type = "Bot";
++ break;
++ case PLAYER_TYPE_ALIAS:
++ type = "Alias";
++ break;
++ default:
++ type = "";
++ break;
++ }
+
+-server_type*
+-find_server_type_string( char* type_string)
+-{
+- server_type *type= &types[0];
+- char *t= type_string;
+- for ( ; *t; t++) *t= tolower( *t);
++ fprintf(OF, "\t\t\t<player>\n");
++
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ fprintf(OF, "\t\t\t\t<team number=\"%d\">%s</team>\n", player->team, xml_escape(player->team_name));
++ fprintf(OF, "\t\t\t\t<type>%s</type>\n", xml_escape(type));
++ fprintf(OF, "\t\t\t\t<clan>%s</clan>\n", player->tribe_tag ? xml_escape(xform_name(player->tribe_tag, server)): "");
++
++ fprintf(OF, "\t\t\t</player>\n");
++ }
++ }
+
+- for ( ; type->id != Q_UNKNOWN_TYPE; type++)
+- if ( strcmp( type->type_string, type_string) == 0)
+- return type;
+- return NULL;
++ fprintf(OF, "\t\t</players>\n");
+ }
+
+-server_type*
+-find_server_type_option( char* option)
++void xml_display_bfris_player_info(struct qserver *server)
+ {
+- server_type *type= &types[0];
+- for ( ; type->id != Q_UNKNOWN_TYPE; type++)
+- if ( strcmp( type->type_option, option) == 0)
+- return type;
+- return NULL;
++ struct player *player;
++
++ fprintf(OF, "\t\t<players>\n");
++
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player number=\"%d\">\n", player->number);
++
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score type=\"score\">%d</score>\n", player->score);
++ fprintf(OF, "\t\t\t\t<score type=\"frags\">%d</score>\n", player->frags);
++ fprintf(OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++ fprintf(OF, "\t\t\t\t<ship>%d</ship>\n", player->ship);
++
++ fprintf(OF, "\t\t\t</player>\n");
++ }
++
++ fprintf(OF, "\t\t</players>\n");
+ }
+
+-server_type*
+-parse_server_type_option( char* option, int *outfile, char **query_arg)
++void xml_display_descent3_player_info(struct qserver *server)
+ {
+- server_type *type= &types[0];
+- char *comma, *arg;
+- int len;
++ struct player *player;
+
+- *outfile= 0;
+- *query_arg= 0;
++ fprintf(OF, "\t\t<players>\n");
+
+- comma= strchr( option, ',');
+- if ( comma)
+- *comma++= '\0';
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
+
+- for ( ; type->id != Q_UNKNOWN_TYPE; type++)
+- if ( strcmp( type->type_option, option) == 0)
+- break;
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ fprintf(OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++ fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
+
+- if ( type->id == Q_UNKNOWN_TYPE)
+- return NULL;
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+- if ( ! comma)
+- return type;
++ fprintf(OF, "\t\t</players>\n");
++}
+
+- if ( strcmp( comma, "outfile") == 0) {
+- *outfile= 1;
+- comma= strchr( comma, ',');
+- if ( ! comma)
+- return type;
+- *comma++= '\0';
+- }
++void xml_display_ravenshield_player_info(struct qserver *server)
++{
++ struct player *player;
+
+- *query_arg= strdup(comma);
+- arg= comma;
+- do {
+- comma= strchr( arg, ',');
+- if (comma)
+- len= comma-arg;
+- else
+- len= strlen( arg);
+- if ( strncmp( arg, "outfile", len) == 0)
+- *outfile= 1;
+- arg= comma+1;
+- } while ( comma);
+- return type;
+-}
+-
+-void
+-add_server_arg( char *arg, int type, char *outfilename, char *query_arg,
+- struct server_arg **args, int *n, int *max)
+-{
+- if ( *n == *max) {
+- if ( *max == 0) {
+- *max= 4;
+- *args= (struct server_arg*)malloc(sizeof(struct server_arg) * (*max));
+- }
+- else {
+- (*max)*= 2;
+- *args= (struct server_arg*) realloc( *args,
+- sizeof(struct server_arg) * (*max));
++ fprintf(OF, "\t\t<players>\n");
++
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
++
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
++
++ fprintf(OF, "\t\t\t</player>\n");
+ }
+- }
+- (*args)[*n].type_id= type;
+-/* (*args)[*n].type= find_server_type_id( type); */
+- (*args)[*n].type= NULL;
+- (*args)[*n].arg= arg;
+- (*args)[*n].outfilename= outfilename;
+- (*args)[*n].query_arg= query_arg;
+- (*n)++;
++
++ fprintf(OF, "\t\t</players>\n");
+ }
+
+
+-void
+-add_query_param( struct qserver *server, char *arg)
++void xml_display_ghostrecon_player_info(struct qserver *server)
+ {
+- char *equal;
+- struct query_param *param;
++ struct player *player;
+
+- equal= strchr( arg, '=');
+- *equal++= '\0';
++ fprintf(OF, "\t\t<players>\n");
+
+- param= (struct query_param *) malloc( sizeof(struct query_param));
+- param->key= arg;
+- param->value= equal;
+- sscanf( equal, "%i", ¶m->i_value);
+- sscanf( equal, "%i", ¶m->ui_value);
+- param->next= server->params;
+- server->params= param;
+-}
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
+
+-char *
+-get_param_value( struct qserver *server, const char *key, char *default_value)
+-{
+- struct query_param *p= server->params;
+- for ( ; p; p= p->next)
+- if ( strcasecmp( key, p->key) == 0)
+- return p->value;
+- return default_value;
+-}
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
++ fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
+
+-int
+-get_param_i_value( struct qserver *server, char *key, int default_value)
+-{
+- struct query_param *p= server->params;
+- for ( ; p; p= p->next)
+- if ( strcasecmp( key, p->key) == 0)
+- return p->i_value;
+- return default_value;
+-}
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+-unsigned int
+-get_param_ui_value( struct qserver *server, char *key,
+- unsigned int default_value)
+-{
+- struct query_param *p= server->params;
+- for ( ; p; p= p->next)
+- if ( strcasecmp( key, p->key) == 0)
+- return p->ui_value;
+- return default_value;
++ fprintf(OF, "\t\t</players>\n");
+ }
+
+-int
+-parse_source_address( char *addr, unsigned int *ip, unsigned short *port)
++void xml_display_eye_player_info(struct qserver *server)
+ {
+- char *colon;
+- colon= strchr( addr, ':');
+- if ( colon) {
+- *colon= '\0';
+- *port= atoi( colon+1);
+- if ( colon == addr)
+- return 0;
+- }
+- else
+- *port= 0;
++ struct player *player;
+
+- *ip= inet_addr( addr);
+- if ( *ip == INADDR_NONE && !isdigit( (unsigned char)*ip))
+- *ip= hcache_lookup_hostname( addr);
+- if ( *ip == INADDR_NONE) {
+- fprintf( stderr, "%s: Not an IP address or unknown host name\n", addr);
+- return -1;
+- }
+- *ip= ntohl( *ip);
+- return 0;
+-}
++ fprintf(OF, "\t\t<players>\n");
+
+-int
+-parse_source_port( char *port, unsigned short *low, unsigned short *high)
+-{
+- char *dash;
+- *low= atoi( port);
+- dash= strchr( port, '-');
+- *high= 0;
+- if ( dash)
+- *high= atoi( dash+1);
+- if ( *high == 0)
+- *high= *low;
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
+
+- if ( *high < *low) {
+- fprintf( stderr, "%s: Invalid port range\n", port);
+- return -1;
+- }
+- return 0;
+-}
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->score);
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++ if (player->team_name)
++ {
++ fprintf(OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++ }
++ else
++ {
++ fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++ }
++ if (player->skin)
++ {
++ fprintf(OF, "\t\t\t\t<skin>%s</skin>\n", xml_escape(player->skin));
++ }
++ if (player->connect_time)
++ {
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 1)));
++ }
+
+-void
+-add_config_server_types()
+-{
+- int n_config_types, n_builtin_types, i;
+- server_type **config_types;
+- server_type *new_types, *type;
+- config_types= qsc_get_config_server_types( &n_config_types);
+-
+- if ( n_config_types == 0)
+- return;
+-
+- n_builtin_types= (sizeof( builtin_types) / sizeof(server_type)) - 1;
+- new_types= (server_type*) malloc( sizeof(server_type) * (n_builtin_types +
+- n_config_types + 1));
+-
+- memcpy( new_types, &builtin_types[0], n_builtin_types*sizeof(server_type));
+- type= &new_types[n_builtin_types];
+- for ( i= n_config_types; i; i--, config_types++, type++)
+- *type= **config_types;
+- n_server_types= n_builtin_types + n_config_types;
+- new_types[n_server_types].id= Q_UNKNOWN_TYPE;
+- if ( types != &builtin_types[0])
+- free( types);
+- types= new_types;
+-}
+-
+-void
+-revert_server_types()
+-{
+- if ( types != &builtin_types[0])
+- free( types);
+- n_server_types= (sizeof( builtin_types) / sizeof(server_type)) - 1;
+- types= &builtin_types[0];
+-}
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+-#ifdef ENABLE_DUMP
+-unsigned pkt_dump_pos = 0;
+-const char* pkt_dumps[64] = {0};
+-static void add_pkt_from_file(const char* file)
+-{
+- if(pkt_dump_pos >= sizeof(pkt_dumps)/sizeof(pkt_dumps[0]))
+- return;
+- pkt_dumps[pkt_dump_pos++] = file;
++ fprintf(OF, "\t\t</players>\n");
+ }
+
+-static void replay_pkt_dumps()
++void xml_display_doom3_player_info(struct qserver *server)
+ {
+- struct qserver* server = servers;
+- char* pkt = NULL;
+- int fd;
+- int bytes_read = 0; // should be ssize_t but for ease with win32
+- int i;
+- struct stat statbuf;
+- gettimeofday( &packet_recv_time, NULL);
++ struct player *player;
++
++ fprintf(OF, "\t\t<players>\n");
+
+- for ( i = 0; i < pkt_dump_pos; i++ )
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- if((fd = open(pkt_dumps[i], O_RDONLY)) == -1)
++ fprintf(OF, "\t\t\t<player>\n");
++
++ fprintf(OF, "\t\t\t\t<number>%u</number>\n", player->number);
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->score);
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++ if (player->tribe_tag)
+ {
+- goto err;
++ fprintf(OF, "\t\t\t\t<clan>%s</clan>\n", player->tribe_tag ? xml_escape(xform_name(player->tribe_tag, server)): "");
+ }
+- if( fstat( fd, &statbuf) == -1 )
++ else
+ {
+- goto err;
++ fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
+ }
+- pkt = malloc( statbuf.st_size );
+- if ( NULL == pkt )
++ if (player->skin)
+ {
+- goto err;
++ fprintf(OF, "\t\t\t\t<skin>%s</skin>\n", xml_escape(player->skin));
+ }
+- bytes_read = read( fd, pkt, statbuf.st_size );
+- if ( bytes_read != statbuf.st_size )
++ if (player->type_flag)
+ {
+- fprintf( stderr, "Failed to read entire packet from disk got %d of %ld bytes\n", bytes_read, (long)statbuf.st_size );
+- goto err;
++ fprintf(OF, "\t\t\t\t<type>bot</type>\n");
++ }
++ else
++ {
++ fprintf(OF, "\t\t\t\t<type>player</type>\n");
+ }
+
+- server->type->packet_func( server, pkt, statbuf.st_size);
+- close(fd);
+- fd = 0;
++ if (player->connect_time)
++ {
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
++ }
++
++ xml_display_player_info_info(player);
++
++ fprintf(OF, "\t\t\t</player>\n");
+ }
+- goto out;
+
+-err:
+- perror(__FUNCTION__);
+- close(fd);
+-out:
+- fd = 0; // NOP
++ fprintf(OF, "\t\t</players>\n");
+ }
+-#endif // ENABLE_DUMP
+
+-struct rcv_pkt
++void xml_display_player_info(struct qserver *server)
+ {
+- struct qserver* server;
+- struct sockaddr_in addr;
+- struct timeval recv_time;
+- char data[PACKET_LEN];
+- int len;
+- int _errno;
+-};
++ struct player *player;
+
+-void do_work(void)
+-{
+- int pktlen, rc, fd;
+- char *pkt= NULL;
+- int bind_retry= 0;
+- struct timeval timeout;
+- struct rcv_pkt* buffer;
+- unsigned buffill = 0, i = 0;
+- unsigned bufsize = max_simultaneous*2;
++ fprintf(OF, "\t\t<players>\n");
+
+- struct timeval t,ts;
+- gettimeofday(&t, NULL);
+- ts = t;
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
+
+- buffer = malloc(sizeof(struct rcv_pkt) * bufsize);
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ if (NA_INT != player->ping)
++ {
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++ }
++ if (NA_INT != player->score)
++ {
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->score);
++ }
++ if (NA_INT != player->deaths)
++ {
++ fprintf(OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
++ }
++ if (NA_INT != player->frags)
++ {
++ fprintf(OF, "\t\t\t\t<frags>%d</frags>\n", player->frags);
++ }
++ if (player->team_name)
++ {
++ fprintf(OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++ }
++ else if (NA_INT != player->team)
++ {
++ fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++ }
+
+- if(!buffer) return;
++ if (player->skin)
++ {
++ fprintf(OF, "\t\t\t\t<skin>%s</skin>\n", xml_escape(player->skin));
++ }
+
+-#ifdef ENABLE_DUMP
+- if(pkt_dump_pos)
+- {
+- replay_pkt_dumps();
++ if (player->connect_time)
++ {
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 1)));
++ }
++
++ xml_display_player_info_info(player);
++
++ fprintf(OF, "\t\t\t</player>\n");
+ }
+- else
+-#endif
++
++ fprintf(OF, "\t\t</players>\n");
++}
++
++void xml_display_armyops_player_info(struct qserver *server)
++{
++ struct player *player;
++
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- bind_retry = bind_sockets();
++ player->score = calculate_armyops_score(player);
+ }
+
+- send_packets();
++ xml_display_player_info(server);
++}
+
+- debug(2, "connected: %d", connected);
++void xml_display_ts2_player_info(struct qserver *server)
++{
++ struct player *player;
++
++ fprintf(OF, "\t\t<players>\n");
+
+- while ( connected || (!connected && bind_retry==-2))
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- if ( ! connected && bind_retry==-2)
++ fprintf(OF, "\t\t\t<player>\n");
++
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++
++ if (player->connect_time)
+ {
+- rc= wait_for_timeout( 60);
+- bind_retry= bind_sockets();
+- continue;
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ }
+- bind_retry= 0;
+
+- set_file_descriptors();
++ xml_display_player_info_info(player);
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+- if ( progress)
+- display_progress();
++ fprintf(OF, "\t\t</players>\n");
++}
++
++void xml_display_ts3_player_info(struct qserver *server)
++{
++ struct player *player;
+
+- get_next_timeout( &timeout);
++ fprintf(OF, "\t\t<players>\n");
+
+- rc= wait_for_file_descriptors( &timeout);
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
+
+- debug(2, "rc %d", rc);
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
+
+- if ( rc == SOCKET_ERROR)
++ if (player->connect_time)
+ {
+-#ifndef _WIN32
+- if ( errno == EINTR)
+- continue;
+-#endif
+- perror("select");
+- break;
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ }
+
+- fd= 0;
+- for ( ; rc && buffill < bufsize; rc--)
+- {
+- int addrlen= sizeof(buffer[buffill].addr);
+- struct qserver* server= get_next_ready_server();
+- if ( server == NULL)
+- break;
+- gettimeofday( &buffer[buffill].recv_time, NULL);
++ xml_display_player_info_info(player);
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+- pktlen = recvfrom( server->fd,
+- buffer[buffill].data,
+- sizeof(buffer[buffill].data),
+- 0,
+- (struct sockaddr*)&buffer[buffill].addr,
+- (void*)&addrlen);
++ fprintf(OF, "\t\t</players>\n");
++}
+
+- if ( pktlen == SOCKET_ERROR)
+- {
+- if(errno == EAGAIN)
+- {
+- malformed_packet(server, "EAGAIN on UDP socket, probably incorrect checksum");
+- }
+- else if ( connection_refused())
+- {
+- server->server_name= DOWN;
+- num_servers_down++;
+- cleanup_qserver( server, 1);
+- }
+- continue;
+- }
++void xml_display_bfbc2_player_info(struct qserver *server)
++{
++ struct player *player;
+
+- debug(1, "recv %3d %3d %d.%d.%d.%d:%hu\n",
+- time_delta(&buffer[buffill].recv_time, &ts),
+- time_delta(&buffer[buffill].recv_time, &t),
+- server->ipaddr&0xff,
+- (server->ipaddr>>8)&0xff,
+- (server->ipaddr>>16)&0xff,
+- (server->ipaddr>>24)&0xff,
+- server->port);
++ fprintf(OF, "\t\t<players>\n");
+
+- t = buffer[buffill].recv_time;
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
+
+- buffer[buffill].server = server;
+- buffer[buffill].len = pktlen;
+- ++buffill;
+- }
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
+
+- for(i = 0; i < buffill; ++i)
++ if (player->connect_time)
+ {
+- struct qserver* server = buffer[i].server;
+- pkt = buffer[i].data;
+- pktlen = buffer[i].len;
+- memcpy(&packet_recv_time, &buffer[i].recv_time, sizeof(packet_recv_time));
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
++ }
+
+- if ( get_debug_level() > 0 )
+- print_packet( server, pkt, pktlen);
++ xml_display_player_info_info(player);
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+-#ifdef ENABLE_DUMP
+- if (do_dump)
+- dump_packet(pkt, pktlen);
+-#endif
++ fprintf(OF, "\t\t</players>\n");
++}
+
+- if ( server->flags & FLAG_BROADCAST)
+- {
+- struct qserver *broadcast= server;
+- unsigned short port= ntohs(buffer[i].addr.sin_port);
+- /* create new server and init */
+- if ( ! (no_port_offset || server->flags & TF_NO_PORT_OFFSET))
+- {
+- port-= server->type->port_offset;
+- }
+- server= add_qserver_byaddr( ntohl(buffer[i].addr.sin_addr.s_addr), port, server->type, NULL);
+- if ( server == NULL)
+- {
+- server= find_server_by_address( buffer[i].addr.sin_addr.s_addr,
+- ntohs(buffer[i].addr.sin_port));
+- if ( server == NULL)
+- {
+- continue;
+- }
+-/*
+- if ( show_errors)
+- {
+- fprintf(stderr,
+- "duplicate or invalid packet received from 0x%08x:%hu\n",
+- ntohl(buffer[i].addr.sin_addr.s_addr), ntohs(buffer[i].addr.sin_port));
+- print_packet( NULL, pkt, pktlen);
+- }
+- continue;
+-*/
+- }
+- else
+- {
+- server->packet_time1= broadcast->packet_time1;
+- server->packet_time2= broadcast->packet_time2;
+- server->ping_total= broadcast->ping_total;
+- server->n_requests= broadcast->n_requests;
+- server->n_packets= broadcast->n_packets;
+- broadcast->n_servers++;
+- }
+- }
++void xml_display_wic_player_info(struct qserver *server)
++{
++ struct player *player;
+
+- debug(2, "connected: %d", connected);
+- server->type->packet_func( server, pkt, pktlen);
+- debug(2, "connected: %d", connected);
+- }
+- buffill = 0;
++ fprintf(OF, "\t\t<players>\n");
++
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
+
+- if ( run_timeout && time(0)-start_time >= run_timeout)
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->score);
++ fprintf(OF, "\t\t\t\t<team>%s</team>\n", player->team_name);
++ fprintf(OF, "\t\t\t\t<bot>%d</bot>\n", player->type_flag );
++ if ( player->tribe_tag )
+ {
+- debug(2, "run timeout reached");
+- break;
++ fprintf(OF, "\t\t\t\t<role>%s</role>\n", player->tribe_tag );
+ }
+
+- send_packets();
+- if ( connected < max_simultaneous)
+- bind_retry= bind_sockets();
+
+- debug(2, "connected: %d", connected);
++ xml_display_player_info_info(player);
++ fprintf(OF, "\t\t\t</player>\n");
+ }
+
+- free(buffer);
+-}
+-#ifndef _WIN32
+-void sigpipe( int sig )
+-{
+- fprintf( stderr, "SIG: %d\n", sig );
++ fprintf(OF, "\t\t</players>\n");
+ }
+-#endif
+
+-int
+-main( int argc, char *argv[])
++void xml_display_ventrilo_player_info(struct qserver *server)
+ {
+- int arg, n_files, i;
+- char **files, *outfilename, *query_arg;
+- struct server_arg *server_args= NULL;
+- int n_server_args= 0, max_server_args= 0;
+- int default_server_type_id;
++ struct player *player;
+
+-#ifdef _WIN32
+- WORD version= MAKEWORD(1,1);
+- WSADATA wsa_data;
+- if ( WSAStartup(version,&wsa_data) != 0) {
+- fprintf( stderr, "Could not open winsock\n");
+- exit(1);
+- }
+-#else
+- signal( SIGPIPE, &sigpipe );
+-#endif
++ fprintf(OF, "\t\t<players>\n");
+
+- types= &builtin_types[0];
+- n_server_types= (sizeof( builtin_types) / sizeof(server_type)) - 1;
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
+
+- i= qsc_load_default_config_files();
+- if ( i == -1)
+- return 1;
+- else if ( i == 0)
+- add_config_server_types();
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++ fprintf(OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+- if ( argc == 1)
+- usage(NULL,argv,NULL);
++ fprintf(OF, "\t\t</players>\n");
++}
+
+- OF= stdout;
++void xml_display_tm_player_info(struct qserver *server)
++{
++ struct player *player;
+
+- files= (char **) malloc( sizeof(char*) * (argc/2));
+- n_files= 0;
+-
+- default_server_type_id= Q_SERVER;
+- little_endian= ((char*)&one)[0];
+- big_endian= !little_endian;
+-
+- for ( arg= 1; arg < argc; arg++) {
+- if ( argv[arg][0] != '-')
+- break;
+- outfilename= NULL;
+- if ( strcmp( argv[arg], "-nocfg") == 0 && arg == 1) {
+- revert_server_types();
+- }
+- else if ( strcmp( argv[arg], "--help") == 0) {
+- usage(NULL,argv,NULL);
+- }
+- else if ( strcmp( argv[arg], "-f") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for -f\n", argv,NULL);
+- files[n_files++]= argv[arg];
+- }
+- else if ( strcmp( argv[arg], "-retry") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for -retry\n", argv,NULL);
+- n_retries= atoi( argv[arg]);
+- if ( n_retries <= 0) {
+- fprintf( stderr, "retries must be greater than zero\n");
+- exit(1);
+- }
+- }
+- else if ( strcmp( argv[arg], "-interval") == 0) {
+- double value= 0.0;
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for -interval\n", argv,NULL);
+- sscanf( argv[arg], "%lf", &value);
+- if ( value < 0.1) {
+- fprintf( stderr, "retry interval must be greater than 0.1\n");
+- exit(1);
+- }
+- retry_interval= (int)(value * 1000);
+- }
+- else if ( strcmp( argv[arg], "-mi") == 0) {
+- double value= 0.0;
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for -mi\n", argv,NULL);
+- sscanf( argv[arg], "%lf", &value);
+- if ( value < 0.1) {
+- fprintf( stderr, "interval must be greater than 0.1\n");
+- exit(1);
+- }
+- master_retry_interval= (int)(value * 1000);
+- }
+- else if ( strcmp( argv[arg], "-H") == 0)
+- hostname_lookup= 1;
+- else if ( strcmp( argv[arg], "-u") == 0)
+- up_servers_only= 1;
+- else if ( strcmp( argv[arg], "-nf") == 0)
+- no_full_servers= 1;
+- else if ( strcmp( argv[arg], "-ne") == 0)
+- no_empty_servers= 1;
+- else if ( strcmp( argv[arg], "-nh") == 0)
+- no_header_display= 1;
+- else if ( strcmp( argv[arg], "-old") == 0)
+- new_style= 0;
+- else if ( strcmp( argv[arg], "-P") == 0)
+- get_player_info= 1;
+- else if ( strcmp( argv[arg], "-R") == 0)
+- get_server_rules= 1;
+- else if ( strncmp( argv[arg], "-raw", 4) == 0) {
+- if ( argv[arg][4] == ',')
+- {
+- if ( strcmp( &argv[arg][5], "game") == 0)
+- {
+- show_game_in_raw= 1;
+- }
+- else
+- {
+- usage( "Unknown -raw option\n", argv, NULL);
+- }
+- }
+- arg++;
+- if ( arg >= argc)
+- {
+- usage( "missing argument for -raw\n", argv,NULL);
+- }
+- raw_delimiter= argv[arg];
++ fprintf(OF, "\t\t<players>\n");
+
+- // Check the multi rule delimiter isnt the same
+- // If it is fix to maintain backwards compatibility
+- if ( 0 == strcmp( raw_delimiter, multi_delimiter ) &&
+- 0 == strcmp( raw_delimiter, "|" )
+- )
+- {
+- multi_delimiter = ":";
+- }
+- raw_display= 1;
+- }
+- else if ( strcmp( argv[arg], "-mdelim" ) == 0 )
++ player = server->players;
++ for (; player != NULL; player = player->next)
+ {
+- arg++;
+- if ( arg >= argc)
++ fprintf(OF, "\t\t\t<player>\n");
++
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++
++ if (player->connect_time)
+ {
+- usage( "missing argument for -mdelim\n", argv,NULL);
+- }
+- multi_delimiter = argv[arg];
+- }
+- else if ( strcmp( argv[arg], "-xml") == 0) {
+- xml_display= 1;
+- if (raw_display == 1)
+- usage( "cannot specify both -raw and -xml\n", argv,NULL);
+- }
+- else if ( strcmp( argv[arg], "-utf8") == 0) {
+- xml_encoding= ENCODING_UTF_8;
+- name_xforms= 0;
+- }
+- else if ( strcmp( argv[arg], "-ncn") == 0) {
+- color_names= 0;
+- }
+- else if ( strcmp( argv[arg], "-cn") == 0) {
+- color_names= 1;
+- }
+- else if ( strcmp( argv[arg], "-hc") == 0) {
+- color_names= 2;
+- }
+- else if ( strcmp( argv[arg], "-nx") == 0) {
+- name_xforms= 1;
+- }
+- else if ( strcmp( argv[arg], "-nnx") == 0) {
+- name_xforms= 0;
+- }
+- else if ( strcmp( argv[arg], "-tc") == 0) {
+- time_format= CLOCK_TIME;
+- }
+- else if ( strcmp( argv[arg], "-tsw") == 0) {
+- time_format= STOPWATCH_TIME;
+- }
+- else if ( strcmp( argv[arg], "-ts") == 0) {
+- time_format= SECONDS;
+- }
+- else if ( strcmp( argv[arg], "-pa") == 0) {
+- player_address= 1;
+- }
+- else if ( strcmp( argv[arg], "-hpn") == 0) {
+- hex_player_names= 1;
+- }
+- else if ( strcmp( argv[arg], "-hsn") == 0) {
+- hex_server_names= 1;
+- }
+- else if ( strncmp( argv[arg], "-maxsimultaneous", 7) == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for -maxsimultaneous\n", argv,NULL);
+- max_simultaneous= atoi(argv[arg]);
+- if ( max_simultaneous <= 0)
+- usage( "value for -maxsimultaneous must be > 0\n", argv,NULL);
+- if ( max_simultaneous > FD_SETSIZE)
+- max_simultaneous= FD_SETSIZE;
+- }
+- else if ( strcmp( argv[arg], "-sendinterval") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for -sendinterval\n", argv,NULL);
+- sendinterval= atoi(argv[arg]);
+- if ( sendinterval < 0)
+- usage( "value for -sendinterval must be >= 0\n", argv,NULL);
+- }
+- else if ( strcmp( argv[arg], "-raw-arg") == 0) {
+- raw_arg= 1000;
+- }
+- else if ( strcmp( argv[arg], "-timeout") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for -timeout\n", argv,NULL);
+- run_timeout= atoi( argv[arg]);
+- if ( run_timeout <= 0)
+- usage( "value for -timeout must be > 0\n", argv,NULL);
+- }
+- else if ( strncmp(argv[arg], "-progress", sizeof("-progress")-1) == 0) {
+- char *p= argv[arg] + sizeof("-progress")-1;
+- progress= 1;
+- if ( *p == ',')
+- progress= atoi(p+1);
+- }
+- else if ( strcmp( argv[arg], "-Hcache") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for -Hcache\n", argv,NULL);
+- if ( hcache_open( argv[arg], 0) == -1)
+- return 1;
+- }
+- else if ( strcmp( argv[arg], "-default") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for -default\n", argv,NULL);
+- default_server_type= find_server_type_string( argv[arg]);
+- if ( default_server_type == NULL) {
+- char opt[256], *o= &opt[0];
+- sprintf( opt, "-%s", argv[arg]);
+- for ( ; *o; o++) *o= tolower(*o);
+- default_server_type= find_server_type_option( opt);
+- }
+- if ( default_server_type == NULL) {
+- fprintf( stderr, "unknown server type \"%s\"\n", argv[arg]);
+- usage( NULL, argv,NULL);
+- }
+- default_server_type_id= default_server_type->id;
+- default_server_type= NULL;
+- }
+- else if ( strncmp( argv[arg], "-Tserver", 3) == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for %s\n", argv, argv[arg-1]);
+- if ( read_qserver_template( argv[arg]) == -1)
+- return 1;
+- }
+- else if ( strncmp( argv[arg], "-Trule", 3) == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for %s\n", argv, argv[arg-1]);
+- if ( read_rule_template( argv[arg]) == -1)
+- return 1;
+- }
+- else if ( strncmp( argv[arg], "-Theader", 3) == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for %s\n", argv, argv[arg-1]);
+- if ( read_header_template( argv[arg]) == -1)
+- return 1;
+- }
+- else if ( strncmp( argv[arg], "-Ttrailer", 3) == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for %s\n", argv, argv[arg-1]);
+- if ( read_trailer_template( argv[arg]) == -1)
+- return 1;
+- }
+- else if ( strncmp( argv[arg], "-Tplayer", 3) == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for %s\n", argv, argv[arg-1]);
+- if ( read_player_template( argv[arg]) == -1)
+- return 1;
+- }
+- else if ( strcmp( argv[arg], "-sort") == 0) {
+- size_t pos;
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for -sort\n", argv, NULL);
+- strncpy( sort_keys, argv[arg], sizeof(sort_keys)-1);
+- pos= strspn( sort_keys, SUPPORTED_SORT_KEYS);
+- if ( pos != strlen( sort_keys)) {
+- fprintf( stderr, "Unknown sort key \"%c\", valid keys are \"%s\"\n",
+- sort_keys[pos], SUPPORTED_SORT_KEYS);
+- return 1;
+- }
+- server_sort= strpbrk( sort_keys, SUPPORTED_SERVER_SORT) != NULL;
+- if ( strchr( sort_keys, 'l'))
+- server_sort= 1;
+- player_sort= strpbrk( sort_keys, SUPPORTED_PLAYER_SORT) != NULL;
+- }
+- else if ( strcmp( argv[arg], "-errors") == 0) {
+- show_errors++;
+- }
+- else if ( strcmp( argv[arg], "-of") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for %s\n", argv, argv[arg-1]);
+- if ( argv[arg][0] == '-' && argv[arg][1] == '\0')
+- OF= stdout;
+- else
+- OF= fopen( argv[arg], "w");
+- if ( OF == NULL) {
+- perror( argv[arg]);
+- return 1;
+- }
+- }
+- else if ( strcmp( argv[arg], "-af") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for %s\n", argv, argv[arg-1]);
+- if ( argv[arg][0] == '-' && argv[arg][1] == '\0')
+- OF= stdout;
+- else
+- OF= fopen( argv[arg], "a");
+- if ( OF == NULL) {
+- perror( argv[arg]);
+- return 1;
+- }
+- }
+- else if ( strcmp( argv[arg], "-htmlnames") == 0) {
+- html_names= 1;
+- }
+- else if ( strcmp( argv[arg], "-nohtmlnames") == 0) {
+- html_names= 0;
+- }
+- else if ( strcmp( argv[arg], "-htmlmode") == 0) {
+- html_mode= 1;
+- }
+- else if ( strcmp( argv[arg], "-carets") == 0) {
+- strip_carets= 0;
+- }
+- else if ( strcmp( argv[arg], "-d") == 0) {
+- set_debug_level(get_debug_level() + 1);
+- }
+- else if ( strcmp( argv[arg], "-showgameport") == 0) {
+- show_game_port= 1;
+- }
+- else if ( strcmp( argv[arg], "-noportoffset") == 0) {
+- no_port_offset= 1;
+- }
+- else if ( strcmp( argv[arg], "-srcip") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for %s\n", argv, argv[arg-1]);
+- if ( parse_source_address( argv[arg], &source_ip, &source_port) == -1)
+- return 1;
+- if ( source_port) {
+- source_port_low= source_port;
+- source_port_high= source_port;
+- }
+- }
+- else if ( strcmp( argv[arg], "-srcport") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for %s\n", argv, argv[arg-1]);
+- if ( parse_source_port( argv[arg], &source_port_low, &source_port_high) == -1)
+- return 1;
+- source_port= source_port_low;
+- }
+- else if ( strcmp( argv[arg], "-cfg") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for %s\n", argv, argv[arg-1]);
+- if ( qsc_load_config_file( argv[arg]) == -1)
+- return 1;
+- add_config_server_types();
+- }
+- else if ( strcmp( argv[arg], "-allowserverdups") == 0) {
+- noserverdups = 0;
+- }
+-#ifdef ENABLE_DUMP
+- else if ( strcmp( argv[arg], "-dump") == 0) {
+- do_dump = 1;
+- }
+- else if ( strcmp( argv[arg], "-pkt") == 0) {
+- arg++;
+- if ( arg >= argc)
+- usage( "missing argument for %s\n", argv, argv[arg-1]);
+- add_pkt_from_file(argv[arg]);
+- }
+-#endif
+-#ifdef _WIN32
+- else if ( strcmp( argv[arg], "-noconsole") == 0) {
+- FreeConsole();
+- }
+-#endif
+- else {
+- int outfile;
+- server_type *type;
+- arg++;
+- if ( arg >= argc) {
+- fprintf( stderr, "missing argument for \"%s\"\n", argv[arg-1]);
+- return 1;
+- }
+- type= parse_server_type_option( argv[arg-1], &outfile, &query_arg);
+- if ( type == NULL) {
+- fprintf( stderr, "unknown option \"%s\"\n", argv[arg-1]);
+- return 1;
+- }
+- outfilename= NULL;
+- if ( outfile) {
+- outfilename= strchr( argv[arg], ',');
+- if ( outfilename == NULL) {
+- fprintf( stderr, "missing file name for \"%s,outfile\"\n",
+- argv[arg-1]);
+- return 1;
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ }
+- *outfilename++= '\0';
+- }
+-/*
+- if ( query_arg && !(type->flags & TF_QUERY_ARG)) {
+- fprintf( stderr, "option flag \"%s\" not allowed for this server type\n",
+- query_arg);
+- return 1;
+- }
+-*/
+- if ( type->flags & TF_QUERY_ARG_REQUIRED && !query_arg) {
+- fprintf( stderr, "option flag missing for server type \"%s\"\n",
+- argv[arg-1]);
+- return 1;
+- }
+- add_server_arg( argv[arg], type->id, outfilename, query_arg,
+- &server_args, &n_server_args, &max_server_args);
+- }
+- }
+
+- start_time= time(0);
+-
+- default_server_type= find_server_type_id( default_server_type_id);
++ xml_display_player_info_info(player);
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+- for ( i= 0; i < n_files; i++)
+- add_file( files[i]);
++ fprintf(OF, "\t\t</players>\n");
++}
+
+- for ( ; arg < argc; arg++)
+- add_qserver( argv[arg], default_server_type, NULL, NULL);
+
+- for ( i= 0; i < n_server_args; i++) {
+- server_type *server_type= find_server_type_id( server_args[i].type_id);
+- add_qserver( server_args[i].arg, server_type,
+- server_args[i].outfilename, server_args[i].query_arg);
+- }
++void xml_display_savage_player_info(struct qserver *server)
++{
++ struct player *player;
+
+- free( server_args);
++ fprintf(OF, "\t\t<players>\n");
+
+- if ( servers == NULL)
+- exit(1);
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
+
+- max_connmap= max_simultaneous + 10;
+- connmap= (struct qserver**) calloc( 1, sizeof(struct qserver*) * max_connmap);
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+
+- if ( color_names == -1)
+- color_names= ( raw_display) ? DEFAULT_COLOR_NAMES_RAW :
+- DEFAULT_COLOR_NAMES_DISPLAY;
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+- if ( time_format == -1)
+- time_format= ( raw_display) ? DEFAULT_TIME_FMT_RAW :
+- DEFAULT_TIME_FMT_DISPLAY;
++ fprintf(OF, "\t\t</players>\n");
++}
+
+- if ( (one_server_type_id & MASTER_SERVER) || one_server_type_id == 0)
+- display_prefix= 1;
++void xml_display_farcry_player_info(struct qserver *server)
++{
++ struct player *player;
+
+- if ( xml_display)
+- xml_header();
+- else if ( new_style && ! raw_display && ! have_server_template())
+- display_header();
+- else if ( have_header_template())
+- template_display_header();
++ fprintf(OF, "\t\t<players>\n");
+
+- q_serverinfo.length= htons( q_serverinfo.length);
+- h2_serverinfo.length= htons( h2_serverinfo.length);
+- q_player.length= htons( q_player.length);
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
+
+- do_work();
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++ fprintf(OF, "\t\t\t\t<time>%su</time>\n", xml_escape(play_time(player->connect_time, 2)));
+
+- finish_output();
+- free_server_hash();
+- free(files);
+- free(connmap);
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+- return 0;
++ fprintf(OF, "\t\t</players>\n");
+ }
+
+-void
+-finish_output()
++void xml_display_tee_player_info(struct qserver *server)
+ {
+- int i;
+- hcache_update_file();
++ struct player *player;
+
+- if ( progress) {
+- display_progress();
+- fputs( "\n", stderr);
+- }
++ fprintf(OF, "\t\t<players>\n");
+
+- if ( server_sort) {
+- struct qserver **array, *server, *next_server;
+- if ( strchr( sort_keys, 'l') &&
+- strpbrk( sort_keys, SUPPORTED_SERVER_SORT) == NULL) {
+- server= servers;
+- for ( ; server; server= next_server) {
+- next_server= server->next;
+- display_server( server);
+- }
+- }
+- else {
+- array= (struct qserver **) malloc( sizeof(struct qserver *) *
+- num_servers_total);
+- server= servers;
+- for ( i= 0; server != NULL; i++) {
+- array[i]= server;
+- server= server->next;
+- }
+- sort_servers( array, num_servers_total);
+- if ( progress)
+- fprintf( stderr, "\n");
+- for ( i= 0; i < num_servers_total; i++)
+- display_server( array[i]);
+- free( array);
+- }
+- }
+- else {
+- struct qserver *server, *next_server;
+- server= servers;
+- for ( ; server; server= next_server) {
+- next_server= server->next;
+- if ( server->server_name == HOSTNOTFOUND)
+- display_server( server);
+- }
+- }
++ player = server->players;
++ for (; player != NULL; player = player->next)
++ {
++ fprintf(OF, "\t\t\t<player>\n");
++
++ fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++ fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->score);
+
+- if ( xml_display)
+- xml_footer();
+- else if ( have_trailer_template())
+- template_display_trailer();
++ fprintf(OF, "\t\t\t</player>\n");
++ }
+
+- if ( OF != stdout)
+- fclose( OF);
++ fprintf(OF, "\t\t</players>\n");
+ }
+
+
+-void
+-add_file( char *filename)
++void display_progress()
+ {
+- FILE *file;
+- char name[200], *comma, *query_arg = NULL;
+- server_type* type;
+-
+- if ( strcmp( filename, "-") == 0)
++ static struct timeval rate_start =
+ {
+- file= stdin;
+- current_filename= NULL;
+- }
+- else
+- {
+- file= fopen( filename, "r");
+- current_filename= filename;
+- }
+- current_fileline= 1;
++ 0, 0
++ };
++ char rate[32];
++ struct timeval now;
++
++ gettimeofday(&now, NULL);
+
+- if ( file == NULL)
++ if (!rate_start.tv_sec)
+ {
+- perror( filename);
+- return;
++ rate_start = now;
++ rate[0] = '\0';
+ }
+- for ( ; fscanf( file, "%s", name) == 1; current_fileline++)
++ else
+ {
+- comma= strchr( name, ',');
+- if ( comma)
+- {
+- *comma++= '\0';
+- query_arg= strdup( comma);
+- }
+- type = find_server_type_string( name);
+- if ( type == NULL)
++ int delta = time_delta(&now, &rate_start);
++ if (delta > 1500)
+ {
+- add_qserver( name, default_server_type, NULL, NULL);
++ sprintf(rate, " %d servers/sec ", (num_servers_returned + num_servers_timed_out) *1000 / delta);
+ }
+- else if ( fscanf( file, "%s", name) == 1)
++ else
+ {
+- if ( type->flags & TF_QUERY_ARG && comma && *query_arg)
+- {
+- add_qserver( name, type, NULL, query_arg );
+- }
+- else
+- {
+- add_qserver( name, type, NULL, NULL );
+- }
++ rate[0] = '\0';
+ }
+ }
+
+- if ( file != stdin)
+- fclose(file);
+-
+- current_fileline= 0;
++ // only print out every 'progress' number of servers.
++ if (0 != num_servers_returned + num_servers_timed_out && (progress == 1 || (num_servers_returned + num_servers_timed_out) % progress == 0))
++ {
++ fprintf(stderr, "\r%d/%d (%d timed out, %d down)%s", num_servers_returned + num_servers_timed_out, num_servers_total, num_servers_timed_out, num_servers_down, rate);
++ }
+ }
+
+-void
+-print_file_location()
++/* ----- END MODIFICATION ----- Don't need to change anything below here. */
++
++
++void set_non_blocking(int fd);
++int set_fds(fd_set *fds);
++void get_next_timeout(struct timeval *timeout);
++
++void set_file_descriptors();
++int wait_for_file_descriptors(struct timeval *timeout);
++struct qserver *get_next_ready_server();
++
++
++/* Misc flags
++ */
++
++struct timeval packet_recv_time;
++int one_server_type_id = ~MASTER_SERVER;
++static int one = 1;
++static int little_endian;
++static int big_endian;
++unsigned int swap_long(void*);
++unsigned short swap_short(void*);
++float swap_float_from_little(void *f);
++char *strndup(const char *string, size_t len);
++#define FORCE 1
++
++/* Print an error message and the program usage notes
++ */
++
++void usage(char *msg, char **argv, char *a1)
+ {
+- if ( current_fileline != 0)
+- fprintf( stderr, "%s:%d: ", current_filename?current_filename:"<stdin>",
+- current_fileline);
++ int i;
++ server_type *type;
++ server_type **sorted_types;
++
++ if (msg)
++ {
++ fprintf(stderr, msg, a1);
++ }
++
++ printf("Usage: %s [options ...]\n", argv[0]);
++ printf("\t[-default server-type] [-cfg file] [-f file] [host[:port]] ...\n");
++ printf("Where host is an IP address or host name\n");
++
++ sorted_types = (server_type **)malloc(sizeof(server_type*) * n_server_types);
++ type = &types[0];
++ for (i = 0; type->id != Q_UNKNOWN_TYPE; type++, i++)
++ {
++ sorted_types[i] = type;
++ }
++
++ quicksort((void **)sorted_types, 0, n_server_types - 1, (int(*)(void *, void*))type_option_compare);
++
++
++ for (i = 0; i < n_server_types; i++)
++ {
++ type = sorted_types[i];
++ printf("%s\t\tquery %s server\n", type->type_option, type->game_name);
++ }
++
++ quicksort((void **)sorted_types, 0, n_server_types - 1, (int(*)(void *, void*))type_string_compare);
++ printf("-default\tset default server type:");
++ for (i = 0; i < n_server_types; type++, i++)
++ {
++ type = sorted_types[i];
++ printf(" %s", type->type_string);
++ }
++ puts("");
++ printf("-nocfg\t\tIgnore qstat configuration loaded from any default location. Must be the first option on the command-line.\n");
++ printf("-cfg\t\tread the extended types from given file not the default one\n");
++ printf("-f\t\tread hosts from file\n");
++ printf("-R\t\tfetch and display server rules\n");
++ printf("-P\t\tfetch and display player info\n");
++ printf("-sort\t\tsort servers and/or players\n");
++ printf("-u\t\tonly display servers that are up\n");
++ printf("-nf\t\tdo not display full servers\n");
++ printf("-ne\t\tdo not display empty servers\n");
++ printf("-nh\t\tdo not display header line.\n");
++ printf("-cn\t\tdisplay color names instead of numbers\n");
++ printf("-ncn\t\tdisplay color numbers instead of names\n");
++ printf("-hc\t\tdisplay colors in #rrggbb format\n");
++ printf("-tc\t\tdisplay time in clock format (DhDDmDDs)\n");
++ printf("-tsw\t\tdisplay time in stop-watch format (DD:DD:DD)\n");
++ printf("-ts\t\tdisplay time in seconds\n");
++ printf("-pa\t\tdisplay player address\n");
++ printf("-hpn\t\tdisplay player names in hex\n");
++ printf("-hsn\t\tdisplay server names in hex\n");
++ printf("-nh\t\tdo not display header\n");
++ printf("-old\t\told style display\n");
++ printf("-progress\tdisplay progress meter (text only)\n");
++ printf("-retry\t\tnumber of retries, default is %d\n", DEFAULT_RETRIES);
++ printf("-interval\tinterval between retries, default is %.2f seconds\n", DEFAULT_RETRY_INTERVAL / 1000.0);
++ printf("-mi\t\tinterval between master server retries, default is %.2f seconds\n", (DEFAULT_RETRY_INTERVAL *4) / 1000.0);
++ printf("-timeout\ttotal time in seconds before giving up\n");
++ printf("-maxsim\t\tset maximum simultaneous queries\n");
++ printf("-sendinterval\t\tset time in ms between sending packets, default %u\n", sendinterval);
++ printf("-errors\t\tdisplay errors\n");
++ printf("-allowserverdups\t\tallow adding multiple servers with same ip:port (needed for ts2)\n");
++ printf("-of\t\toutput file\n");
++ printf("-af\t\tLike -of, but append to the file\n");
++ printf("-raw <delim>\toutput in raw format using <delim> as delimiter\n");
++ printf("-mdelim <delim>\tFor rules with multi values use <delim> as delimiter\n");
++ printf("-xml\t\toutput status data as an XML document\n");
++ printf("-Th,-Ts,-Tpt\toutput templates: header, server and player\n");
++ printf("-Tr,-Tt\t\toutput templates: rule, and trailer\n");
++ printf("-srcport <range>\tSend packets from these network ports\n");
++ printf("-srcip <IP>\tSend packets using this IP address\n");
++ printf("-H\t\tresolve host names\n");
++ printf("-Hcache\t\thost name cache file\n");
++ printf("-carets\t\tDisplay carets in Quake 3 player names\n");
++ printf("-d\t\tEnable debug options. Specify multiple times to increase debug level.\n");
++#ifdef ENABLE_DUMP
++ printf("-dump\t\twrite received raw packets to dumpNNN files which must not exist before\n");
++ printf("-pkt <file>\tuse file as server reply instead of quering the server. Works only with TF_SINGLE_QUERY servers\n");
++#endif
++ printf("-htmlmode\tConvert <, >, and & to the equivalent HTML entities\n");
++ printf("-htmlnames\tColorize Quake 3 and Tribes 2 player names using html font tags\n");
++ printf("-nohtmlnames\tDo not colorize Quake 3 and Tribes 2 player names even if $HTML is used in an output template.\n");
++ printf("-syncconnect\tProcess connect initialisation synchronously.\n");
++ printf("-stripunprintable\tDisable stripping of unprintable characters.\n");
++ printf("-showgameport\tAlways display the game port in QStat output.\n");
++ printf("-noportoffset\tDont use builtin status port offsets ( assume query port was specified ).\n");
++ printf("-raw-arg\tWhen used with -raw, always display the server address as it appeared in a file or on the command-line.\n");
++ printf("-utf8\t\tUse the UTF-8 character encoding for XML output.\n");
++ printf("-bom\t\tOutput Byte-Order-Mark for XML output.\n");
++#ifdef _WIN32
++ printf("-noconsole\t\tFree the console\n");
++#endif
++ printf("\n");
++ printf("Sort keys:\n");
++ printf(" servers: p=by-ping, g=by-game, i=by-IP-address, h=by-hostname, n=by-#-players, l=by-list-order\n");
++ printf(" players: P=by-ping, F=by-frags, T=by-team, N=by-name\n");
++ printf("\nqstat version %s\n", VERSION);
++ exit(0);
+ }
+
+-void
+-parse_query_params( struct qserver *server, char *params)
++struct server_arg
++{
++ int type_id;
++ server_type *type;
++ char *arg;
++ char *outfilename;
++ char *query_arg;
++};
++
++server_type *find_server_type_id(int type_id)
+ {
+- char *comma, *arg= params;
+- do {
+- comma= strchr(arg,',');
+- if ( comma)
+- *comma= '\0';
+- if ( strchr( arg, '='))
+- add_query_param( server, arg);
+- else if ( strcmp( arg, "noportoffset") == 0 || strcmp( arg, "qp") == 0)
+- server->flags |= TF_NO_PORT_OFFSET;
+- else if ( strcmp( arg, "showgameport") == 0 || strcmp( arg, "gp") == 0)
+- server->flags |= TF_SHOW_GAME_PORT;
+- arg= comma+1;
+- } while ( comma);
++ server_type *type = &types[0];
++ for (; type->id != Q_UNKNOWN_TYPE; type++)
++ if (type->id == type_id)
++ {
++ return type;
++ }
++ return NULL;
+ }
+
+-int
+-add_qserver( char *arg, server_type* type, char *outfilename, char *query_arg)
++server_type *find_server_type_string(char *type_string)
+ {
+- struct qserver *server, *prev_server;
+- int flags= 0;
+- char *colon= NULL, *arg_copy, *hostname= NULL;
+- unsigned int ipaddr;
+- unsigned short port, port_max;
+- int portrange = 0;
+- unsigned colonpos = 0;
+-
+- if ( run_timeout && time(0)-start_time >= run_timeout)
++ server_type *type = &types[0];
++ char *t = type_string;
++ for (; *t; t++)
+ {
+- finish_output();
+- exit(0);
++ *t = tolower(*t);
+ }
+
+- port = port_max = type->default_port;
++ for (; type->id != Q_UNKNOWN_TYPE; type++)
++ if (strcmp(type->type_string, type_string) == 0)
++ {
++ return type;
++ }
++ return NULL;
++}
+
+- if ( outfilename && strcmp( outfilename, "-") != 0)
++server_type *find_server_type_option(char *option)
++{
++ server_type *type = &types[0];
++ for (; type->id != Q_UNKNOWN_TYPE; type++)
++ if (strcmp(type->type_option, option) == 0)
+ {
+- FILE *outfile= fopen( outfilename, "r+");
+- if ( outfile == NULL && (errno == EACCES || errno == EISDIR ||
+- errno == ENOSPC || errno == ENOTDIR))
+- {
+- perror( outfilename);
+- return -1;
+- }
+- if ( outfile)
+- {
+- fclose(outfile);
+- }
+- }
++ return type;
++ }
++ return NULL;
++}
+
+- arg_copy= strdup(arg);
++server_type *parse_server_type_option(char *option, int *outfile, char **query_arg)
++{
++ server_type *type = &types[0];
++ char *comma, *arg;
++ int len;
+
+- colon= strchr( arg, ':');
+- if ( colon != NULL)
+- {
+- if(sscanf( colon+1, "%hu-%hu", &port, &port_max) == 2)
+- {
+- portrange = 1;
+- }
+- else
+- {
+- port_max = port;
+- }
+- *colon= '\0';
+- colonpos = colon-arg;
+- }
++ *outfile = 0;
++ *query_arg = 0;
+
+- if ( *arg == '+')
+- {
+- flags|= FLAG_BROADCAST;
+- arg++;
+- }
++ comma = strchr(option, ',');
++ if (comma)
++ {
++ *comma++ = '\0';
++ }
+
+- ipaddr= inet_addr(arg);
+- if ( ipaddr == INADDR_NONE)
+- {
+- if ( strcmp( arg, "255.255.255.255") != 0)
+- {
+- ipaddr= htonl( hcache_lookup_hostname(arg));
+- }
+- }
+- else if ( hostname_lookup && !(flags&FLAG_BROADCAST))
+- {
+- hostname= hcache_lookup_ipaddr( ntohl(ipaddr));
++ for (; type->id != Q_UNKNOWN_TYPE; type++)
++ if (strcmp(type->type_option, option) == 0)
++ {
++ break;
+ }
+
+- if ( (ipaddr == INADDR_NONE || ipaddr == 0) && strcmp( arg, "255.255.255.255") != 0)
++ if (type->id == Q_UNKNOWN_TYPE)
+ {
+- print_file_location();
+- fprintf( stderr, "%s: %s\n", arg, strherror(h_errno));
+- server= (struct qserver *) calloc( 1, sizeof( struct qserver));
+- for(;port <= port_max; ++port)
+- {
+- init_qserver( server, type);
+- if(portrange)
+- {
+- server->arg = ( port==port_max ) ? arg_copy : strdup(arg_copy);
+- }
+- if(portrange)
+- {
+- sprintf(server->arg+colonpos+1, "%hu", port);
+- }
+- server->server_name= HOSTNOTFOUND;
+- server->error= strdup( strherror(h_errno));
+- server->query_port = server->port= port;
+- if ( last_server != &servers)
+- {
+- prev_server= (struct qserver*) ((char*)last_server - ((char*)&server->next - (char*)server));
+- server->prev= prev_server;
+- }
+- *last_server= server;
+- last_server= & server->next;
+- if ( one_server_type_id == ~MASTER_SERVER)
+- {
+- one_server_type_id= type->id;
+- }
+- else if ( one_server_type_id != type->id)
+- {
+- one_server_type_id= 0;
+- }
+- }
+- return -1;
++ return NULL;
+ }
+
+- for(; port > 0 && port <= port_max; ++port)
++ if (!comma)
+ {
+- if ( noserverdups && find_server_by_address( ipaddr, port) != NULL)
++ return type;
++ }
++
++ if (strcmp(comma, "outfile") == 0)
++ {
++ *outfile = 1;
++ comma = strchr(comma, ',');
++ if (!comma)
+ {
+- continue;
++ return type;
+ }
++ *comma++ = '\0';
++ }
+
+- server= (struct qserver *) calloc( 1, sizeof( struct qserver));
+- server->arg= port==port_max?arg_copy:strdup(arg_copy);
+- if(portrange)
++ *query_arg = strdup(comma);
++ arg = comma;
++ do
++ {
++ comma = strchr(arg, ',');
++ if (comma)
+ {
+- sprintf(server->arg+colonpos+1, "%hu", port);
++ len = comma - arg;
+ }
+- if ( hostname && colon)
++ else
+ {
+- server->host_name= (char*)malloc( strlen(hostname) + 5 +2);
+- sprintf( server->host_name, "%s:%hu", hostname, port);
++ len = strlen(arg);
+ }
+- else
++ if (strncmp(arg, "outfile", len) == 0)
+ {
+- server->host_name= strdup((hostname)?hostname:arg);
++ *outfile = 1;
+ }
++ arg = comma + 1;
++ }
++ while (comma);
++ return type;
++}
+
+- server->ipaddr= ipaddr;
+- server->query_port = server->port = port;
+- server->type= type;
+- server->outfilename= outfilename;
+- server->flags= flags;
+- if (query_arg)
++void add_server_arg(char *arg, int type, char *outfilename, char *query_arg, struct server_arg **args, int *n, int *max)
++{
++ if (*n == *max)
++ {
++ if (*max == 0)
+ {
+- server->query_arg = ( port == port_max ) ? query_arg : strdup(query_arg);
+- parse_query_params( server, server->query_arg);
++ *max = 4;
++ *args = (struct server_arg*)malloc(sizeof(struct server_arg)*(*max));
+ }
+ else
+ {
+- server->query_arg = NULL;
++ (*max) *= 2;
++ *args = (struct server_arg*)realloc(*args, sizeof(struct server_arg)*(*max));
+ }
+- init_qserver( server, type);
++ }
++ (*args)[ *n].type_id = type;
++ /* (*args)[*n].type= find_server_type_id( type); */
++ (*args)[ *n].type = NULL;
++ (*args)[ *n].arg = arg;
++ (*args)[ *n].outfilename = outfilename;
++ (*args)[ *n].query_arg = query_arg;
++ (*n)++;
++}
+
+- if ( server->type->master)
+- {
+- waiting_for_masters++;
+- }
+
+- if ( num_servers_total % 10 == 0)
+- {
+- hcache_update_file();
+- }
+-
+- if ( last_server != &servers )
+- {
+- prev_server= (struct qserver*) ((char*)last_server - ((char*)&server->next - (char*)server));
+- server->prev= prev_server;
+- }
+- *last_server= server;
+- last_server= &server->next;
++void add_query_param(struct qserver *server, char *arg)
++{
++ char *equal;
++ struct query_param *param;
+
+- add_server_to_hash( server );
++ equal = strchr(arg, '=');
++ *equal++ = '\0';
+
+- if ( one_server_type_id == ~MASTER_SERVER)
+- {
+- one_server_type_id= type->id;
+- }
+- else if ( one_server_type_id != type->id)
+- {
+- one_server_type_id= 0;
+- }
++ param = (struct query_param*)malloc(sizeof(struct query_param));
++ param->key = arg;
++ param->value = equal;
++ sscanf(equal, "%i", ¶m->i_value);
++ sscanf(equal, "%i", ¶m->ui_value);
++ param->next = server->params;
++ server->params = param;
++}
+
+- ++num_servers;
+- }
+- return 0;
++char *get_param_value(struct qserver *server, const char *key, char *default_value)
++{
++ struct query_param *p = server->params;
++ for (; p; p = p->next)
++ if (strcasecmp(key, p->key) == 0)
++ {
++ return p->value;
++ } return default_value;
+ }
+
+-struct qserver *
+-add_qserver_byaddr( unsigned int ipaddr, unsigned short port,
+- server_type* type, int *new_server)
++int get_param_i_value(struct qserver *server, char *key, int default_value)
+ {
+- char arg[36];
+- struct qserver *server, *prev_server;
+- char *hostname= NULL;
++ struct query_param *p = server->params;
++ for (; p; p = p->next)
++ if (strcasecmp(key, p->key) == 0)
++ {
++ return p->i_value;
++ } return default_value;
++}
+
+- if ( run_timeout && time(0)-start_time >= run_timeout)
++unsigned int get_param_ui_value(struct qserver *server, char *key, unsigned int default_value)
++{
++ struct query_param *p = server->params;
++ for (; p; p = p->next)
++ if (strcasecmp(key, p->key) == 0)
+ {
+- finish_output();
+- exit(0);
+- }
++ return p->ui_value;
++ } return default_value;
++}
+
+- if ( new_server)
++int parse_source_address(char *addr, unsigned int *ip, unsigned short *port)
++{
++ char *colon;
++ colon = strchr(addr, ':');
++ if (colon)
+ {
+- *new_server= 0;
++ *colon = '\0';
++ *port = atoi(colon + 1);
++ if (colon == addr)
++ {
++ return 0;
++ }
+ }
+- ipaddr= htonl(ipaddr);
+- if ( ipaddr == 0)
++ else
+ {
+- return 0;
++ *port = 0;
+ }
+
+- // TODO: this prevents servers with the same ip:port being queried
+- // and hence breaks virtual servers support e.g. Teamspeak 2
+- if ( find_server_by_address( ipaddr, port) != NULL)
++ *ip = inet_addr(addr);
++ if (*ip == INADDR_NONE && !isdigit((unsigned char) *ip))
+ {
+- return 0;
++ *ip = hcache_lookup_hostname(addr);
+ }
+-
+- if ( new_server)
++ if (*ip == INADDR_NONE)
+ {
+- *new_server= 1;
++ fprintf(stderr, "%s: Not an IP address or unknown host name\n", addr);
++ return -1;
+ }
++ *ip = ntohl(*ip);
++ return 0;
++}
+
+- server= (struct qserver *) calloc( 1, sizeof( struct qserver));
+- server->ipaddr= ipaddr;
+- ipaddr= ntohl(ipaddr);
+- sprintf( arg, "%d.%d.%d.%d:%hu", ipaddr>>24, (ipaddr>>16)&0xff,
+- (ipaddr>>8)&0xff, ipaddr&0xff, port);
+- server->arg= strdup(arg);
+-
+- if ( hostname_lookup)
++int parse_source_port(char *port, unsigned short *low, unsigned short *high)
++{
++ char *dash;
++ *low = atoi(port);
++ dash = strchr(port, '-');
++ *high = 0;
++ if (dash)
+ {
+- hostname= hcache_lookup_ipaddr( ipaddr);
++ *high = atoi(dash + 1);
+ }
+-
+- if ( hostname)
++ if (*high == 0)
+ {
+- server->host_name= (char*)malloc( strlen(hostname) + 6 + 2);
+- sprintf( server->host_name, "%s:%hu", hostname, port);
++ *high = *low;
+ }
+- else
++
++ if (*high < *low)
+ {
+- server->host_name= strdup( arg);
++ fprintf(stderr, "%s: Invalid port range\n", port);
++ return -1;
+ }
++ return 0;
++}
+
+- server->query_port = server->port = port;
+- init_qserver( server, type);
++void add_config_server_types()
++{
++ int n_config_types, n_builtin_types, i;
++ server_type **config_types;
++ server_type *new_types, *type;
++ config_types = qsc_get_config_server_types(&n_config_types);
+
+- if ( num_servers_total % 10 == 0)
++ if (n_config_types == 0)
+ {
+- hcache_update_file();
++ return ;
+ }
+
+- if ( last_server != &servers)
++ n_builtin_types = (sizeof(builtin_types) / sizeof(server_type)) - 1;
++ new_types = (server_type*)malloc(sizeof(server_type)*(n_builtin_types + n_config_types + 1));
++
++ memcpy(new_types, &builtin_types[0], n_builtin_types *sizeof(server_type));
++ type = &new_types[n_builtin_types];
++ for (i = n_config_types; i; i--, config_types++, type++)
+ {
+- prev_server= (struct qserver*) ((char*)last_server - ((char*)&server->next - (char*)server));
+- server->prev= prev_server;
++ *type = **config_types;
+ }
+- *last_server= server;
+- last_server= & server->next;
+-
+- add_server_to_hash( server);
+-
+- ++num_servers;
+-
+- return server;
++ n_server_types = n_builtin_types + n_config_types;
++ new_types[n_server_types].id = Q_UNKNOWN_TYPE;
++ if (types != &builtin_types[0])
++ {
++ free(types);
++ }
++ types = new_types;
+ }
+
+-void
+-add_servers_from_masters()
++void revert_server_types()
+ {
+- struct qserver *server;
+- unsigned int ipaddr, i;
+- unsigned short port;
+- int n_servers, new_server, port_adjust= 0;
+- char *pkt;
+- server_type* server_type;
+- FILE *outfile;
+-
+- for ( server= servers; server != NULL; server= server->next) {
+- if ( !server->type->master || server->master_pkt == NULL)
+- continue;
+- pkt= server->master_pkt;
+-
+- if ( server->query_arg && server->type->id == GAMESPY_MASTER) {
+- server_type= find_server_type_string( server->query_arg);
+- if ( server_type == NULL)
+- server_type= find_server_type_id( server->type->master);
+- }
+- else
+- server_type= find_server_type_id( server->type->master);
+-
+- if ( server->type->id == GAMESPY_MASTER && server_type) {
+- if ( server_type->id == UN_SERVER)
+- port_adjust= -1;
+- else if ( server_type->id == KINGPIN_SERVER)
+- port_adjust= 10;
+- }
+-
+- outfile= NULL;
+- if ( server->outfilename) {
+- if ( strcmp( server->outfilename, "-") == 0)
+- outfile= stdout;
+- else
+- outfile= fopen( server->outfilename, "w");
+- if ( outfile == NULL) {
+- perror( server->outfilename);
+- continue;
+- }
+- }
+- n_servers= 0;
+- for ( i= 0; i < server->master_pkt_len; i+= 6) {
+- memcpy( &ipaddr, &pkt[i], 4);
+- memcpy( &port, &pkt[i+4], 2);
+- ipaddr= ntohl( ipaddr);
+- port= ntohs( port) + port_adjust;
+- new_server= 1;
+- if ( outfile)
+- fprintf( outfile, "%s %d.%d.%d.%d:%hu\n",
+- server_type ? server_type->type_string : "",
+- (ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+- (ipaddr>>8)&0xff, ipaddr&0xff, port);
+- else if ( server_type == NULL)
+- fprintf( OF, "%d.%d.%d.%d:%hu\n",
+- (ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+- (ipaddr>>8)&0xff, ipaddr&0xff, port);
+- else
+- add_qserver_byaddr( ipaddr, port, server_type, &new_server);
+- n_servers+= new_server;
+- }
+- free( server->master_pkt);
+- server->master_pkt= NULL;
+- server->master_pkt_len= 0;
+- server->n_servers= n_servers;
+- if ( outfile)
+- fclose( outfile);
+- }
+- if ( hostname_lookup)
+- hcache_update_file();
+- bind_sockets();
++ if (types != &builtin_types[0])
++ {
++ free(types);
++ }
++ n_server_types = (sizeof(builtin_types) / sizeof(server_type)) - 1;
++ types = &builtin_types[0];
+ }
+
+-void
+-init_qserver( struct qserver *server, server_type* type)
++#ifdef ENABLE_DUMP
++unsigned pkt_dump_pos = 0;
++const char *pkt_dumps[64] = { 0 };
++static void add_pkt_from_file(const char *file)
+ {
+- server->server_name= NULL;
+- server->map_name= NULL;
+- server->game= NULL;
+- server->num_players= 0;
+- server->fd= -1;
+- if ( server->flags & FLAG_BROADCAST) {
+- server->retry1= 1;
+- server->retry2= 1;
+- }
+- else {
+- server->retry1= n_retries;
+- server->retry2= n_retries;
+- }
+- server->n_retries= 0;
+- server->ping_total= 0;
+- server->n_packets= 0;
+- server->n_requests= 0;
+-
+- server->n_servers= 0;
+- server->master_pkt_len= 0;
+- server->master_pkt= NULL;
+- server->error= NULL;
+-
+- server->saved_data.data= NULL;
+- server->saved_data.datalen= 0;
+- server->saved_data.pkt_index= -1;
+- server->saved_data.pkt_max= 0;
+- server->saved_data.next= NULL;
+-
+- server->type = type;
+- server->next_rule= (get_server_rules) ? "" : NO_SERVER_RULES;
+- server->next_player_info= (get_player_info &&
+- type->player_packet) ? 0 : NO_PLAYER_INFO;
+-
+- server->n_player_info= 0;
+- server->players= NULL;
+- server->n_rules= 0;
+- server->rules= NULL;
+- server->last_rule= &server->rules;
+- server->missing_rules= 0;
+-
+- num_servers_total++;
++ if (pkt_dump_pos >= sizeof(pkt_dumps) / sizeof(pkt_dumps[0]))
++ {
++ return ;
++ }
++ pkt_dumps[pkt_dump_pos++] = file;
+ }
+
+-// ipaddr should be network byte-order
+-// port should be host byte-order
+-struct qserver *
+-find_server_by_address( unsigned int ipaddr, unsigned short port)
++static void replay_pkt_dumps()
+ {
+- struct qserver **hashed;
+- unsigned int hash, i;
+- hash= (ipaddr + port) % ADDRESS_HASH_LENGTH;
+-
+- if ( ipaddr == 0) {
+- for ( hash= 0; hash < ADDRESS_HASH_LENGTH; hash++)
+- printf( "%3d %d\n", hash, server_hash_len[hash]);
+- return NULL;
+- }
++ struct qserver *server = servers;
++ char *pkt = NULL;
++ int fd;
++ int bytes_read = 0; // should be ssize_t but for ease with win32
++ int i;
++ struct stat statbuf;
++ gettimeofday(&packet_recv_time, NULL);
+
+- hashed = server_hash[hash];
+- for ( i= server_hash_len[hash]; i; i--, hashed++)
+- if ( *hashed && (*hashed)->ipaddr == ipaddr && (*hashed)->port == port)
+- return *hashed;
+- return NULL;
+-}
+-
+-void
+-add_server_to_hash( struct qserver *server)
+-{
+- unsigned int hash;
+- hash = (server->ipaddr + server->port) % ADDRESS_HASH_LENGTH;
+-
+- if ( server_hash_len[hash] % 16 == 0)
+- {
+- server_hash[hash]= (struct qserver**) realloc( server_hash[hash],
+- sizeof( struct qserver **) * (server_hash_len[hash]+16));
+- memset( &server_hash[hash][server_hash_len[hash]], 0,
+- sizeof( struct qserver **) * 16);
+- }
+- server_hash[hash][server_hash_len[hash]]= server;
+- server_hash_len[hash]++;
+-}
++ for (i = 0; i < pkt_dump_pos; i++)
++ {
++ if ((fd = open(pkt_dumps[i], O_RDONLY)) == -1)
++ {
++ goto err;
++ }
+
+-void
+-remove_server_from_hash( struct qserver *server)
+-{
+- struct qserver **hashed;
+- unsigned int hash, i, ipaddr;
+- unsigned short port;
+- hash= (server->ipaddr + server->port) % ADDRESS_HASH_LENGTH;
++ if (fstat(fd, &statbuf) == -1)
++ {
++ goto err;
++ }
++ pkt = malloc(statbuf.st_size);
++ if (NULL == pkt)
++ {
++ goto err;
++ }
++ bytes_read = read(fd, pkt, statbuf.st_size);
++ if (bytes_read != statbuf.st_size)
++ {
++ fprintf(stderr, "Failed to read entire packet from disk got %d of %ld bytes\n", bytes_read, (long)statbuf.st_size);
++ goto err;
++ }
++ close(fd);
++ fd = 0;
+
+- ipaddr= server->ipaddr;
+- port= server->port;
+- hashed= server_hash[hash];
+- for ( i= server_hash_len[hash]; i; i--, hashed++)
+- if ( *hashed && (*hashed)->ipaddr == ipaddr && (*hashed)->port == port) {
+- *hashed= NULL;
+- break;
++ debug(2, "replay, pre-packet_func");
++ process_func_ret( server, server->type->packet_func( server, pkt, statbuf.st_size ) );
++ debug(2, "replay, post-packet_func");
+ }
+-}
++ goto out;
+
+-void
+-free_server_hash()
+-{
+- int i;
+- for ( i= 0; i < ADDRESS_HASH_LENGTH; i++)
+- if ( server_hash[i]) free( server_hash[i]);
++ err: perror(__FUNCTION__);
++ close(fd);
++ out: fd = 0; // NOP
+ }
++#endif // ENABLE_DUMP
+
++struct rcv_pkt
++{
++ struct qserver *server;
++ struct sockaddr_in addr;
++ struct timeval recv_time;
++ char data[PACKET_LEN];
++ int len;
++ int _errno;
++};
+
+-/* Functions for binding sockets to Quake servers
+- */
+-int
+-bind_qserver( struct qserver *server)
++void do_work(void)
+ {
+- struct sockaddr_in addr;
+- static int one= 1;
++ int pktlen, rc, fd;
++ char *pkt = NULL;
++ int bind_retry = 0;
++ struct timeval timeout;
++ struct rcv_pkt *buffer;
++ unsigned buffill = 0, i = 0;
++ unsigned bufsize = max_simultaneous * 2;
+
+- if ( server->type->flags & TF_TCP_CONNECT)
+- server->fd= socket( AF_INET, SOCK_STREAM, 0);
+- else
+- server->fd= socket( AF_INET, SOCK_DGRAM, 0);
+-
+- if ( server->fd == INVALID_SOCKET) {
+- if ( sockerr() == EMFILE) {
+- server->fd= -1;
+- return -2;
+- }
+- perror( "socket" );
+- server->server_name= SYSERROR;
+- return -1;
+- }
++ struct timeval t, ts;
++ gettimeofday(&t, NULL);
++ ts = t;
+
+- addr.sin_family = AF_INET;
+- addr.sin_addr.s_addr = htonl( source_ip);
+- if ( server->type->id == Q2_MASTER)
+- addr.sin_port= htons(26500);
+- else if ( source_port == 0)
+- addr.sin_port= 0;
+- else {
+- addr.sin_port= htons( source_port);
+- source_port++;
+- if ( source_port > source_port_high)
+- source_port= source_port_low;
+- }
+- memset( &(addr.sin_zero), 0, sizeof(addr.sin_zero) );
++ buffer = malloc(sizeof(struct rcv_pkt) *bufsize);
+
+- if ( bind( server->fd, (struct sockaddr *)&addr,
+- sizeof(struct sockaddr)) == SOCKET_ERROR) {
+- if ( sockerr() != EADDRINUSE) {
+- perror( "bind" );
+- server->server_name= SYSERROR;
++ if (!buffer)
++ {
++ return ;
+ }
+- close(server->fd);
+- server->fd= -1;
+- return -1;
+- }
+
+- if ( server->flags & FLAG_BROADCAST)
+- setsockopt( server->fd, SOL_SOCKET, SO_BROADCAST, (char*)&one,
+- sizeof(one));
+-
+- if ( server->type->id != Q2_MASTER &&
+- !(server->flags & FLAG_BROADCAST)) {
+- addr.sin_family= AF_INET;
+- if ( no_port_offset || server->flags & TF_NO_PORT_OFFSET)
+- addr.sin_port= htons(server->port);
+- else
+- addr.sin_port= htons( (unsigned short)( server->port + server->type->port_offset ) );
+- addr.sin_addr.s_addr= server->ipaddr;
+- memset( &(addr.sin_zero), 0, sizeof(addr.sin_zero) );
+-
+- if ( connect( server->fd, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) {
+- char error[50];
+- if ( server->type->id == UN_MASTER) {
+- if ( connection_refused()) {
+- /* server->fd= -2; */
+- /* set up for connect retry */
+- }
+- }
+- sprintf( error, "connect:%s:%u", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );
+- perror( error );
+- server->server_name= SYSERROR;
+- close(server->fd);
+- server->fd= -1;
+- return -1;
++#ifdef ENABLE_DUMP
++ if (pkt_dump_pos)
++ {
++ replay_pkt_dumps();
+ }
+- }
+-
+- // we need nonblocking always. poll on an udp socket would wake
+- // up and recv blocks if a packet with incorrect checksum is
+- // received
+- set_non_blocking( server->fd);
+-
+- if ( server->type->flags & TF_TCP_CONNECT) {
+- int one= 1;
+- setsockopt( server->fd, IPPROTO_TCP, TCP_NODELAY,
+- (char*) &one, sizeof(one));
+- }
+-
+-#ifndef _WIN32
+- if ( server->fd >= max_connmap) {
+- int old_max= max_connmap;
+- max_connmap= server->fd + 32;
+- connmap= (struct qserver **) realloc( connmap, max_connmap *
+- sizeof( struct qserver *));
+- memset( &connmap[old_max], 0, (max_connmap - old_max) *
+- sizeof( struct qserver *));
+- }
+- connmap[server->fd]= server;
++ else
+ #endif
+-#ifdef _WIN32
+- { int i;
+- for ( i= 0; i < max_connmap; i++) {
+- if ( connmap[i] == NULL) {
+- connmap[i]= server;
+- break;
++ {
++ bind_retry = bind_sockets();
+ }
+- }
+- if ( i >= max_connmap) printf( "could not put server in connmap\n");
+- }
+-#endif
+-
+- return 0;
+-}
+
+-static struct timeval t_lastsend = {0,0};
++ send_packets();
+
+-int
+-bind_sockets()
+-{
+- struct qserver *server;
+- struct timeval now;
+- int rc, retry_count= 0;;
++ debug(2, "connected: %d", connected);
+
+- gettimeofday(&now, NULL);
+- if(connected && sendinterval && time_delta(&now, &t_lastsend) < sendinterval)
+- {
+- server = NULL;
+- }
+- else if ( !waiting_for_masters)
++ while (connected || (!connected && bind_retry == -2))
+ {
+- if ( last_server_bind == NULL)
++ if (!connected && bind_retry == -2)
+ {
+- last_server_bind= servers;
++ rc = wait_for_timeout(60);
++ bind_retry = bind_sockets();
++ continue;
+ }
+- server= last_server_bind;
+- }
+- else
+- {
+- server= servers;
+- }
++ bind_retry = 0;
+
+- for ( ; server != NULL && connected < max_simultaneous; server= server->next)
+- {
+- if ( server->server_name == NULL && server->fd == -1)
++ set_file_descriptors();
++
++ if (progress)
++ {
++ display_progress();
++ }
++
++ get_next_timeout(&timeout);
++
++ rc = wait_for_file_descriptors(&timeout);
++
++ debug(2, "rc %d", rc);
++
++ if (rc == SOCKET_ERROR)
+ {
+- if ( waiting_for_masters && !server->type->master)
++#ifndef _WIN32
++ if (errno == EINTR)
+ {
+ continue;
+ }
++#endif
++ perror("select");
++ break;
++ }
+
+- if ( (rc= bind_qserver( server)) == 0)
++ fd = 0;
++ for (; rc && buffill < bufsize; rc--)
++ {
++ int addrlen = sizeof(buffer[buffill].addr);
++ struct qserver *server = get_next_ready_server();
++ if (server == NULL)
+ {
+- debug(1, "send %d.%d.%d.%d:%hu\n",
+- server->ipaddr&0xff,
+- (server->ipaddr>>8)&0xff,
+- (server->ipaddr>>16)&0xff,
+- (server->ipaddr>>24)&0xff,
+- server->port);
+-
+- gettimeofday(&t_lastsend, NULL);
+- server->type->status_query_func( server);
+- connected++;
+- if ( !waiting_for_masters)
+- {
+- last_server_bind= server;
+- }
+ break;
+ }
+- else if ( rc == -2 && ++retry_count > 2)
+- {
+- return -2;
+- }
+- }
+- }
+
+- if ( ! connected && retry_count)
+- {
+- return -2;
+- }
++ gettimeofday(&buffer[buffill].recv_time, NULL);
+
+- return 0;
+-}
++ pktlen = recvfrom(server->fd, buffer[buffill].data, sizeof(buffer[buffill].data), 0, (struct sockaddr*) &buffer[buffill].addr, (void*) &addrlen);
+
++ debug( 2, "recvfrom: %d", pktlen );
+
+-/*
+- * Functions for sending packets
+- */
+-// this is so broken, someone please rewrite the timeout handling
+-void
+-send_packets()
+-{
+- struct qserver *server;
+- struct timeval now;
+- int interval, n_sent=0, prev_n_sent;
+- unsigned i;
+- debug( 3, "processing..." );
++ // pktlen == 0 is no error condition! happens on remote tcp socket close
++ if (pktlen == SOCKET_ERROR)
++ {
++ if (connection_would_block())
++ {
++ malformed_packet(server, "EAGAIN on UDP socket, probably incorrect checksum");
++ }
++ else if (connection_refused() || connection_reset())
++ {
++ server->server_name = DOWN;
++ num_servers_down++;
++ cleanup_qserver( server, FORCE );
++ }
++ continue;
++ }
+
+- gettimeofday( &now, NULL);
++ debug(1, "recv %3d %3d %d.%d.%d.%d:%hu\n",
++ time_delta(&buffer[buffill].recv_time, &ts),
++ time_delta(&buffer[buffill].recv_time, &t),
++ server->ipaddr &0xff,
++ (server->ipaddr >> 8) &0xff,
++ (server->ipaddr >> 16) &0xff,
++ (server->ipaddr >> 24) &0xff,
++ server->port
++ );
+
+- if(!t_lastsend.tv_sec)
+- {
+- // nothing
+- }
+- else if(connected && sendinterval && time_delta(&now, &t_lastsend) < sendinterval)
+- {
+- return;
+- }
++ t = buffer[buffill].recv_time;
+
+- for ( i = 0; i < max_connmap; ++i )
+- {
+- server = connmap[i];
+- if( ! server )
+- {
+- continue;
++ buffer[buffill].server = server;
++ buffer[buffill].len = pktlen;
++ ++buffill;
+ }
+
+- if(server->fd == -1)
+- {
+- debug(0, "invalid entry in connmap\n");
+- }
++ debug( 2, "fill: %d < %d", buffill, bufsize) ;
+
+- if ( server->type->id & MASTER_SERVER)
++ for (i = 0; i < buffill; ++i)
+ {
+- interval = master_retry_interval;
+- }
+- else
+- {
+- interval = retry_interval;
+- }
++ struct qserver *server = buffer[i].server;
++ pkt = buffer[i].data;
++ pktlen = buffer[i].len;
++ memcpy(&packet_recv_time, &buffer[i].recv_time, sizeof(packet_recv_time));
+
+- debug(2, "server %p, name %s, retry1 %d, next_rule %p, next_player_info %d, num_players %d, n_retries %d", server, server->server_name, server->retry1, server->next_rule, server->next_player_info, server->num_players, n_retries);
+- prev_n_sent = n_sent;
+- if ( server->server_name == NULL )
+- {
+- // We havent seen the server yet?
+- if (
+- server->retry1 != n_retries &&
+- time_delta( &now, &server->packet_time1) < (interval*(n_retries-server->retry1+1))
+- )
++ if (get_debug_level() > 2)
+ {
+- continue;
++ print_packet(server, pkt, pktlen);
+ }
+
+- if ( ! server->retry1 )
++#ifdef ENABLE_DUMP
++ if (do_dump)
+ {
+- // No more retries
+- cleanup_qserver( server, 1);
+- continue;
++ dump_packet(pkt, pktlen);
+ }
+-
+- if( qserver_get_timeout(server, &now) <= 0 )
++#endif
++ if (server->flags &FLAG_BROADCAST)
+ {
+- // Query status
+- debug(2, "calling status_query_func for %p", server);
+- server->type->status_query_func( server);
+- gettimeofday(&t_lastsend, NULL);
+- n_sent++;
+- continue;
+- }
+- }
+-
+- if ( server->next_rule != NO_SERVER_RULES )
+- {
+- // We want server rules
+- if (
+- server->retry1 != n_retries &&
+- time_delta( &now, &server->packet_time1) < (interval*(n_retries-server->retry1+1))
+- )
++ struct qserver *broadcast = server;
++ unsigned short port = ntohs(buffer[i].addr.sin_port);
++ /* create new server and init */
++ if (!(no_port_offset || server->flags &TF_NO_PORT_OFFSET))
++ {
++ port -= server->type->port_offset;
++ }
++ server = add_qserver_byaddr(ntohl(buffer[i].addr.sin_addr.s_addr), port, server->type, NULL);
++ if (server == NULL)
++ {
++ server = find_server_by_address(buffer[i].addr.sin_addr.s_addr, ntohs(buffer[i].addr.sin_port));
++ if (server == NULL)
++ {
++ continue;
++ }
++ /*
++ if ( show_errors)
++ {
++ fprintf(stderr,
++ "duplicate or invalid packet received from 0x%08x:%hu\n",
++ ntohl(buffer[i].addr.sin_addr.s_addr), ntohs(buffer[i].addr.sin_port));
++ print_packet( NULL, pkt, pktlen);
++ }
++ continue;
++ */
++ }
++ else
++ {
++ server->packet_time1 = broadcast->packet_time1;
++ server->packet_time2 = broadcast->packet_time2;
++ server->ping_total = broadcast->ping_total;
++ server->n_requests = broadcast->n_requests;
++ server->n_packets = broadcast->n_packets;
++ broadcast->n_servers++;
++ }
++ }
++
++ debug(2, "connected, pre-packet_func: %d", connected);
++ process_func_ret( server, server->type->packet_func(server, pkt, pktlen) );
++ debug(2, "connected, post-packet_func: %d", connected);
++ }
++ buffill = 0;
++
++ if (run_timeout && time(0) - start_time >= run_timeout)
++ {
++ debug(2, "run timeout reached");
++ break;
++ }
++
++ send_packets();
++ if (connected < max_simultaneous)
++ {
++ bind_retry = bind_sockets();
++ }
++
++ debug(2, "connected: %d", connected);
++ }
++
++ free(buffer);
++}
++
++int main(int argc, char *argv[])
++{
++ int arg, n_files, i;
++ char **files, *outfilename, *query_arg;
++ struct server_arg *server_args = NULL;
++ int n_server_args = 0, max_server_args = 0;
++ int default_server_type_id;
++
++#ifdef _WIN32
++ WORD version = MAKEWORD(1, 1);
++ WSADATA wsa_data;
++ if (WSAStartup(version, &wsa_data) != 0)
++ {
++ fprintf(stderr, "Could not open winsock\n");
++ exit(1);
++ }
++#else
++ signal(SIGPIPE, SIG_IGN);
++#endif
++
++ types = &builtin_types[0];
++ n_server_types = (sizeof(builtin_types) / sizeof(server_type)) - 1;
++
++ i = qsc_load_default_config_files();
++ if (i == -1)
++ {
++ return 1;
++ }
++ else if (i == 0)
++ {
++ add_config_server_types();
++ }
++
++ if (argc == 1)
++ {
++ usage(NULL, argv, NULL);
++ }
++
++ OF = stdout;
++
++ files = (char **)malloc(sizeof(char*)*(argc / 2));
++ n_files = 0;
++
++ default_server_type_id = Q_SERVER;
++ little_endian = ((char*) &one)[0];
++ big_endian = !little_endian;
++
++ for (arg = 1; arg < argc; arg++)
++ {
++ if (argv[arg][0] != '-')
++ {
++ break;
++ }
++ outfilename = NULL;
++ if (strcmp(argv[arg], "-nocfg") == 0 && arg == 1)
++ {
++ revert_server_types();
++ }
++ else if (strcmp(argv[arg], "--help") == 0)
++ {
++ usage(NULL, argv, NULL);
++ }
++ else if (strcmp(argv[arg], "-f") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -f\n", argv, NULL);
++ }
++ files[n_files++] = argv[arg];
++ }
++ else if (strcmp(argv[arg], "-retry") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -retry\n", argv, NULL);
++ }
++ n_retries = atoi(argv[arg]);
++ if (n_retries <= 0)
++ {
++ fprintf(stderr, "retries must be greater than zero\n");
++ exit(1);
++ }
++ }
++ else if (strcmp(argv[arg], "-interval") == 0)
++ {
++ double value = 0.0;
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -interval\n", argv, NULL);
++ }
++ sscanf(argv[arg], "%lf", &value);
++ if (value < 0.1)
++ {
++ fprintf(stderr, "retry interval must be greater than 0.1\n");
++ exit(1);
++ }
++ retry_interval = (int)(value *1000);
++ }
++ else if (strcmp(argv[arg], "-mi") == 0)
++ {
++ double value = 0.0;
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -mi\n", argv, NULL);
++ }
++ sscanf(argv[arg], "%lf", &value);
++ if (value < 0.1)
++ {
++ fprintf(stderr, "interval must be greater than 0.1\n");
++ exit(1);
++ }
++ master_retry_interval = (int)(value *1000);
++ }
++ else if (strcmp(argv[arg], "-H") == 0)
++ {
++ hostname_lookup = 1;
++ }
++ else if (strcmp(argv[arg], "-u") == 0)
++ {
++ up_servers_only = 1;
++ }
++ else if (strcmp(argv[arg], "-nf") == 0)
++ {
++ no_full_servers = 1;
++ }
++ else if (strcmp(argv[arg], "-ne") == 0)
++ {
++ no_empty_servers = 1;
++ }
++ else if (strcmp(argv[arg], "-nh") == 0)
++ {
++ no_header_display = 1;
++ }
++ else if (strcmp(argv[arg], "-old") == 0)
++ {
++ new_style = 0;
++ }
++ else if (strcmp(argv[arg], "-P") == 0)
++ {
++ get_player_info = 1;
++ }
++ else if (strcmp(argv[arg], "-R") == 0)
++ {
++ get_server_rules = 1;
++ }
++ else if (strncmp(argv[arg], "-raw", 4) == 0)
++ {
++ if (argv[arg][4] == ',')
++ {
++ if (strcmp(&argv[arg][5], "game") == 0)
++ {
++ show_game_in_raw = 1;
++ }
++ else
++ {
++ usage("Unknown -raw option\n", argv, NULL);
++ }
++ }
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -raw\n", argv, NULL);
++ }
++ raw_delimiter = argv[arg];
++
++ // Check the multi rule delimiter isnt the same
++ // If it is fix to maintain backwards compatibility
++ if (0 == strcmp(raw_delimiter, multi_delimiter) && 0 == strcmp(raw_delimiter, "|"))
++ {
++ multi_delimiter = ":";
++ }
++ raw_display = 1;
++ }
++ else if (strcmp(argv[arg], "-mdelim") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -mdelim\n", argv, NULL);
++ }
++ multi_delimiter = argv[arg];
++ }
++ else if (strcmp(argv[arg], "-xml") == 0)
++ {
++ xml_display = 1;
++ if (raw_display == 1)
++ {
++ usage("cannot specify both -raw and -xml\n", argv, NULL);
++ }
++ }
++ else if (strcmp(argv[arg], "-utf8") == 0)
++ {
++ xml_encoding = ENCODING_UTF_8;
++ name_xforms = 0;
++ }
++ else if (strcmp(argv[arg], "-ncn") == 0)
++ {
++ color_names = 0;
++ }
++ else if (strcmp(argv[arg], "-cn") == 0)
++ {
++ color_names = 1;
++ }
++ else if (strcmp(argv[arg], "-hc") == 0)
++ {
++ color_names = 2;
++ }
++ else if (strcmp(argv[arg], "-nx") == 0)
++ {
++ name_xforms = 1;
++ }
++ else if (strcmp(argv[arg], "-nnx") == 0)
++ {
++ name_xforms = 0;
++ }
++ else if (strcmp(argv[arg], "-tc") == 0)
++ {
++ time_format = CLOCK_TIME;
++ }
++ else if (strcmp(argv[arg], "-tsw") == 0)
++ {
++ time_format = STOPWATCH_TIME;
++ }
++ else if (strcmp(argv[arg], "-ts") == 0)
++ {
++ time_format = SECONDS;
++ }
++ else if (strcmp(argv[arg], "-pa") == 0)
++ {
++ player_address = 1;
++ }
++ else if (strcmp(argv[arg], "-hpn") == 0)
++ {
++ hex_player_names = 1;
++ }
++ else if (strcmp(argv[arg], "-hsn") == 0)
++ {
++ hex_server_names = 1;
++ }
++ else if (strncmp(argv[arg], "-maxsimultaneous", 7) == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -maxsimultaneous\n", argv, NULL);
++ }
++ max_simultaneous = atoi(argv[arg]);
++ if (max_simultaneous <= 0)
++ {
++ usage("value for -maxsimultaneous must be > 0\n", argv, NULL);
++ }
++ if (max_simultaneous > FD_SETSIZE)
++ {
++ max_simultaneous = FD_SETSIZE;
++ }
++ }
++ else if (strcmp(argv[arg], "-sendinterval") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -sendinterval\n", argv, NULL);
++ }
++ sendinterval = atoi(argv[arg]);
++ if (sendinterval < 0)
++ {
++ usage("value for -sendinterval must be >= 0\n", argv, NULL);
++ }
++ }
++ else if (strcmp(argv[arg], "-raw-arg") == 0)
++ {
++ raw_arg = 1000;
++ }
++ else if (strcmp(argv[arg], "-timeout") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -timeout\n", argv, NULL);
++ }
++ run_timeout = atoi(argv[arg]);
++ if (run_timeout <= 0)
++ {
++ usage("value for -timeout must be > 0\n", argv, NULL);
++ }
++ }
++ else if (strncmp(argv[arg], "-progress", sizeof("-progress") - 1) == 0)
++ {
++ char *p = argv[arg] + sizeof("-progress") - 1;
++ progress = 1;
++ if (*p == ',')
++ {
++ progress = atoi(p + 1);
++ }
++ }
++ else if (strcmp(argv[arg], "-Hcache") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -Hcache\n", argv, NULL);
++ }
++ if (hcache_open(argv[arg], 0) == -1)
++ {
++ return 1;
++ }
++ }
++ else if (strcmp(argv[arg], "-default") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -default\n", argv, NULL);
++ }
++ default_server_type = find_server_type_string(argv[arg]);
++ if (default_server_type == NULL)
++ {
++ char opt[256], *o = &opt[0];
++ sprintf(opt, "-%s", argv[arg]);
++ for (; *o; o++)
++ {
++ *o = tolower(*o);
++ }
++ default_server_type = find_server_type_option(opt);
++ }
++ if (default_server_type == NULL)
++ {
++ fprintf(stderr, "unknown server type \"%s\"\n", argv[arg]);
++ usage(NULL, argv, NULL);
++ }
++ default_server_type_id = default_server_type->id;
++ default_server_type = NULL;
++ }
++ else if (strncmp(argv[arg], "-Tserver", 3) == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for %s\n", argv, argv[arg - 1]);
++ }
++ if (read_qserver_template(argv[arg]) == -1)
++ {
++ return 1;
++ }
++ }
++ else if (strncmp(argv[arg], "-Trule", 3) == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for %s\n", argv, argv[arg - 1]);
++ }
++ if (read_rule_template(argv[arg]) == -1)
++ {
++ return 1;
++ }
++ }
++ else if (strncmp(argv[arg], "-Theader", 3) == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for %s\n", argv, argv[arg - 1]);
++ }
++ if (read_header_template(argv[arg]) == -1)
++ {
++ return 1;
++ }
++ }
++ else if (strncmp(argv[arg], "-Ttrailer", 3) == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for %s\n", argv, argv[arg - 1]);
++ }
++ if (read_trailer_template(argv[arg]) == -1)
++ {
++ return 1;
++ }
++ }
++ else if (strncmp(argv[arg], "-Tplayer", 3) == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for %s\n", argv, argv[arg - 1]);
++ }
++ if (read_player_template(argv[arg]) == -1)
++ {
++ return 1;
++ }
++ }
++ else if (strcmp(argv[arg], "-sort") == 0)
++ {
++ size_t pos;
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for -sort\n", argv, NULL);
++ }
++ strncpy(sort_keys, argv[arg], sizeof(sort_keys) - 1);
++ pos = strspn(sort_keys, SUPPORTED_SORT_KEYS);
++ if (pos != strlen(sort_keys))
++ {
++ fprintf(stderr, "Unknown sort key \"%c\", valid keys are \"%s\"\n", sort_keys[pos], SUPPORTED_SORT_KEYS);
++ return 1;
++ }
++ server_sort = strpbrk(sort_keys, SUPPORTED_SERVER_SORT) != NULL;
++ if (strchr(sort_keys, 'l'))
++ {
++ server_sort = 1;
++ }
++ player_sort = strpbrk(sort_keys, SUPPORTED_PLAYER_SORT) != NULL;
++ }
++ else if (strcmp(argv[arg], "-errors") == 0)
++ {
++ show_errors++;
++ }
++ else if (strcmp(argv[arg], "-of") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for %s\n", argv, argv[arg - 1]);
++ }
++ if (argv[arg][0] == '-' && argv[arg][1] == '\0')
++ {
++ OF = stdout;
++ }
++ else
++ {
++ OF = fopen(argv[arg], "w");
++ }
++ if (OF == NULL)
++ {
++ perror(argv[arg]);
++ return 1;
++ }
++ }
++ else if (strcmp(argv[arg], "-af") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for %s\n", argv, argv[arg - 1]);
++ }
++ if (argv[arg][0] == '-' && argv[arg][1] == '\0')
++ {
++ OF = stdout;
++ }
++ else
++ {
++ OF = fopen(argv[arg], "a");
++ }
++ if (OF == NULL)
++ {
++ perror(argv[arg]);
++ return 1;
++ }
++ }
++ else if (strcmp(argv[arg], "-htmlnames") == 0)
++ {
++ html_names = 1;
++ }
++ else if (strcmp(argv[arg], "-nohtmlnames") == 0)
++ {
++ html_names = 0;
++ }
++ else if (strcmp(argv[arg], "-htmlmode") == 0)
++ {
++ html_mode = 1;
++ }
++ else if (strcmp(argv[arg], "-carets") == 0)
++ {
++ strip_carets = 0;
++ }
++ else if (strcmp(argv[arg], "-d") == 0)
++ {
++ set_debug_level(get_debug_level() + 1);
++ }
++ else if (strcmp(argv[arg], "-showgameport") == 0)
++ {
++ show_game_port = 1;
++ }
++ else if (strcmp(argv[arg], "-noportoffset") == 0)
++ {
++ no_port_offset = 1;
++ }
++ else if (strcmp(argv[arg], "-srcip") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for %s\n", argv, argv[arg - 1]);
++ }
++ if (parse_source_address(argv[arg], &source_ip, &source_port) == -1)
++ {
++ return 1;
++ }
++ if (source_port)
++ {
++ source_port_low = source_port;
++ source_port_high = source_port;
++ }
++ }
++ else if (strcmp(argv[arg], "-syncconnect") == 0)
++ {
++ syncconnect = 1;
++ }
++ else if (strcmp(argv[arg], "-stripunprintable") == 0)
++ {
++ name_xforms_strip_unprintable = 1;
++ }
++ else if (strcmp(argv[arg], "-srcport") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for %s\n", argv, argv[arg - 1]);
++ }
++ if (parse_source_port(argv[arg], &source_port_low, &source_port_high) == -1)
++ {
++ return 1;
++ }
++ source_port = source_port_low;
++ }
++ else if (strcmp(argv[arg], "-cfg") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for %s\n", argv, argv[arg - 1]);
++ }
++ if (qsc_load_config_file(argv[arg]) == -1)
++ {
++ return 1;
++ }
++ add_config_server_types();
++ }
++ else if (strcmp(argv[arg], "-allowserverdups") == 0)
++ {
++ noserverdups = 0;
++ }
++ else if (strcmp(argv[arg], "-bom") == 0)
++ {
++ output_bom = 1;
++ }
++#ifdef ENABLE_DUMP
++ else if (strcmp(argv[arg], "-dump") == 0)
++ {
++ do_dump = 1;
++ }
++ else if (strcmp(argv[arg], "-pkt") == 0)
++ {
++ arg++;
++ if (arg >= argc)
++ {
++ usage("missing argument for %s\n", argv, argv[arg - 1]);
++ }
++ add_pkt_from_file(argv[arg]);
++ }
++#endif
++#ifdef _WIN32
++ else if (strcmp(argv[arg], "-noconsole") == 0)
++ {
++ FreeConsole();
++ }
++#endif
++ else
++ {
++ int outfile;
++ server_type *type;
++ arg++;
++ if (arg >= argc)
++ {
++ fprintf(stderr, "missing argument for \"%s\"\n", argv[arg - 1]);
++ return 1;
++ }
++ type = parse_server_type_option(argv[arg - 1], &outfile, &query_arg);
++ if (type == NULL)
++ {
++ fprintf(stderr, "unknown option \"%s\"\n", argv[arg - 1]);
++ return 1;
++ }
++ outfilename = NULL;
++ if (outfile)
++ {
++ outfilename = strchr(argv[arg], ',');
++ if (outfilename == NULL)
++ {
++ fprintf(stderr, "missing file name for \"%s,outfile\"\n", argv[arg - 1]);
++ return 1;
++ }
++ *outfilename++ = '\0';
++ }
++ /*
++ if ( query_arg && !(type->flags & TF_QUERY_ARG)) {
++ fprintf( stderr, "option flag \"%s\" not allowed for this server type\n",
++ query_arg);
++ return 1;
++ }
++ */
++ if (type->flags &TF_QUERY_ARG_REQUIRED && !query_arg )
++ {
++ fprintf(stderr, "option flag missing for server type \"%s\"\n", argv[arg - 1]);
++ return 1;
++ }
++ add_server_arg(argv[arg], type->id, outfilename, query_arg, &server_args, &n_server_args, &max_server_args);
++ }
++ }
++
++ start_time = time(0);
++
++ default_server_type = find_server_type_id(default_server_type_id);
++
++ for (i = 0; i < n_files; i++)
++ {
++ add_file(files[i]);
++ }
++
++ for (; arg < argc; arg++)
++ {
++ add_qserver(argv[arg], default_server_type, NULL, NULL);
++ }
++
++ for (i = 0; i < n_server_args; i++)
++ {
++ server_type *server_type = find_server_type_id(server_args[i].type_id);
++ add_qserver(server_args[i].arg, server_type, server_args[i].outfilename, server_args[i].query_arg);
++ }
++
++ free(server_args);
++
++ if (servers == NULL)
++ {
++ exit(1);
++ }
++
++ max_connmap = max_simultaneous + 10;
++ connmap = (struct qserver **)calloc(1, sizeof(struct qserver*) * max_connmap);
++
++ if (color_names == -1)
++ {
++ color_names = (raw_display) ? DEFAULT_COLOR_NAMES_RAW : DEFAULT_COLOR_NAMES_DISPLAY;
++ }
++
++ if (time_format == -1)
++ {
++ time_format = (raw_display) ? DEFAULT_TIME_FMT_RAW : DEFAULT_TIME_FMT_DISPLAY;
++ }
++
++ if ((one_server_type_id &MASTER_SERVER) || one_server_type_id == 0)
++ {
++ display_prefix = 1;
++ }
++
++ if (xml_display)
++ {
++ xml_header();
++ }
++ else if (new_style && !raw_display && !have_server_template())
++ {
++ display_header();
++ }
++ else if (have_header_template())
++ {
++ template_display_header();
++ }
++
++ q_serverinfo.length = htons(q_serverinfo.length);
++ h2_serverinfo.length = htons(h2_serverinfo.length);
++ q_player.length = htons(q_player.length);
++
++ do_work();
++
++ finish_output();
++ free_server_hash();
++ free(files);
++ free(connmap);
++
++ return 0;
++}
++
++void finish_output()
++{
++ int i;
++ hcache_update_file();
++
++ if (progress)
++ {
++ display_progress();
++ fputs("\n", stderr);
++ }
++
++ if (server_sort)
++ {
++ struct qserver **array, *server, *next_server;
++ if (strchr(sort_keys, 'l') && strpbrk(sort_keys, SUPPORTED_SERVER_SORT) == NULL)
++ {
++ server = servers;
++ for (; server; server = next_server)
++ {
++ next_server = server->next;
++ display_server(server);
++ }
++ }
++ else
++ {
++ array = (struct qserver **)malloc(sizeof(struct qserver*) * num_servers_total);
++ server = servers;
++ for (i = 0; server != NULL; i++)
++ {
++ array[i] = server;
++ server = server->next;
++ }
++ sort_servers(array, num_servers_total);
++ if (progress)
++ {
++ fprintf(stderr, "\n");
++ }
++ for (i = 0; i < num_servers_total; i++)
++ {
++ display_server(array[i]);
++ }
++ free(array);
++ }
++ }
++ else
++ {
++ struct qserver *server, *next_server;
++ server = servers;
++ for (; server; server = next_server)
++ {
++ next_server = server->next;
++ if (server->server_name == HOSTNOTFOUND)
++ {
++ display_server(server);
++ }
++ }
++ }
++
++ if (xml_display)
++ {
++ xml_footer();
++ }
++ else if (have_trailer_template())
++ {
++ template_display_trailer();
++ }
++
++ if (OF != stdout)
++ {
++ fclose(OF);
++ }
++}
++
++
++void add_file(char *filename)
++{
++ FILE *file;
++ char name[200], *comma, *query_arg = NULL;
++ server_type *type;
++ debug(4, "Loading servers from '%s'...\n", filename);
++
++ if (strcmp(filename, "-") == 0)
++ {
++ file = stdin;
++ current_filename = NULL;
++ }
++ else
++ {
++ file = fopen(filename, "r");
++ current_filename = filename;
++ }
++ current_fileline = 1;
++
++ if (file == NULL)
++ {
++ perror(filename);
++ return ;
++ }
++ for (; fscanf(file, "%s", name) == 1; current_fileline++)
++ {
++ comma = strchr(name, ',');
++ if (comma)
++ {
++ *comma++ = '\0';
++ query_arg = strdup(comma);
++ }
++ type = find_server_type_string(name);
++ if (type == NULL)
++ {
++ add_qserver(name, default_server_type, NULL, NULL);
++ }
++ else if (fscanf(file, "%s", name) == 1)
++ {
++ if (type->flags &TF_QUERY_ARG && comma && *query_arg)
++ {
++ add_qserver(name, type, NULL, query_arg);
++ }
++ else
++ {
++ add_qserver(name, type, NULL, NULL);
++ }
++ }
++ }
++
++ if (file != stdin)
++ {
++ fclose(file);
++ }
++ debug(4, "Loaded servers from '%s'\n", filename);
++
++ current_fileline = 0;
++}
++
++void print_file_location()
++{
++ if (current_fileline != 0)
++ {
++ fprintf(stderr, "%s:%d: ", current_filename ? current_filename : "<stdin>", current_fileline);
++ }
++}
++
++void parse_query_params(struct qserver *server, char *params)
++{
++ char *comma, *arg = params;
++ do
++ {
++ comma = strchr(arg, ',');
++ if (comma)
++ {
++ *comma = '\0';
++ } if (strchr(arg, '='))
++ {
++ add_query_param(server, arg);
++ }
++ else if (strcmp(arg, "noportoffset") == 0 || strcmp(arg, "qp") == 0)
++ {
++ server->flags |= TF_NO_PORT_OFFSET;
++ }
++ else if (strcmp(arg, "showgameport") == 0 || strcmp(arg, "gp") == 0)
++ {
++ server->flags |= TF_SHOW_GAME_PORT;
++ }
++ arg = comma + 1;
++ }
++ while (comma);
++}
++
++int add_qserver(char *arg, server_type *type, char *outfilename, char *query_arg)
++{
++ struct qserver *server, *prev_server;
++ int flags = 0;
++ char *colon = NULL, *arg_copy, *hostname = NULL;
++ unsigned int ipaddr;
++ unsigned short port, port_max;
++ int portrange = 0;
++ unsigned colonpos = 0;
++
++ debug(4, "%s, %s, %s, %s\n", arg, (NULL != type) ? type->type_string : "unknown", outfilename, query_arg);
++
++ if (run_timeout && time(0) - start_time >= run_timeout)
++ {
++ finish_output();
++ exit(0);
++ }
++
++ port = port_max = type->default_port;
++
++ if (outfilename && strcmp(outfilename, "-") != 0)
++ {
++ FILE *outfile = fopen(outfilename, "r+");
++ if (outfile == NULL && (errno == EACCES || errno == EISDIR || errno == ENOSPC || errno == ENOTDIR))
++ {
++ perror(outfilename);
++ return -1;
++ }
++ if (outfile)
++ {
++ fclose(outfile);
++ }
++ }
++
++ arg_copy = strdup(arg);
++
++ colon = strchr(arg, ':');
++ if (colon != NULL)
++ {
++ if (sscanf(colon + 1, "%hu-%hu", &port, &port_max) == 2)
++ {
++ portrange = 1;
++ }
++ else
++ {
++ port_max = port;
++ }
++ *colon = '\0';
++ colonpos = colon - arg;
++ }
++
++ if (*arg == '+')
++ {
++ flags |= FLAG_BROADCAST;
++ arg++;
++ }
++
++ ipaddr = inet_addr(arg);
++ if (ipaddr == INADDR_NONE)
++ {
++ if (strcmp(arg, "255.255.255.255") != 0)
++ {
++ ipaddr = htonl(hcache_lookup_hostname(arg));
++ }
++ }
++ else if (hostname_lookup && !(flags &FLAG_BROADCAST))
++ {
++ hostname = hcache_lookup_ipaddr(ntohl(ipaddr));
++ }
++
++ if ((ipaddr == INADDR_NONE || ipaddr == 0) && strcmp(arg, "255.255.255.255") != 0)
++ {
++ if (show_errors)
++ {
++ print_file_location();
++ fprintf(stderr, "%s: %s\n", arg, strherror(h_errno));
++ }
++ server = (struct qserver*)calloc(1, sizeof(struct qserver));
++ // NOTE: 0 != port to prevent infinite loop due to lack of range on unsigned short
++ for (; port <= port_max && 0 != port; ++port)
++ {
++ init_qserver(server, type);
++ if (portrange)
++ {
++ server->arg = (port == port_max) ? arg_copy : strdup(arg_copy);
++ // NOTE: arg_copy and therefore server->arg will always have enough space as it was a port range
++ sprintf(server->arg + colonpos + 1, "%hu", port);
++ }
++ else
++ {
++ server->arg = arg_copy;
++ }
++ server->server_name = HOSTNOTFOUND;
++ server->error = strdup(strherror(h_errno));
++ server->orig_port = server->query_port = server->port = port;
++ if (last_server != &servers)
++ {
++ prev_server = (struct qserver*)((char*)last_server - ((char*) &server->next - (char*)server));
++ server->prev = prev_server;
++ }
++ *last_server = server;
++ last_server = &server->next;
++ if (one_server_type_id == ~MASTER_SERVER)
++ {
++ one_server_type_id = type->id;
++ }
++ else if (one_server_type_id != type->id)
++ {
++ one_server_type_id = 0;
++ }
++ }
++ return -1;
++ }
++
++ // NOTE: 0 != port to prevent infinite loop due to lack of range on unsigned short
++ for (; port <= port_max && 0 != port; ++port)
++ {
++ if (noserverdups && find_server_by_address(ipaddr, port) != NULL)
++ {
++ continue;
++ }
++
++ server = (struct qserver*)calloc(1, sizeof(struct qserver));
++ server->arg = port == port_max ? arg_copy : strdup(arg_copy);
++ if (portrange)
++ {
++ sprintf(server->arg + colonpos + 1, "%hu", port);
++ }
++ if (hostname && colon)
++ {
++ server->host_name = (char*)malloc(strlen(hostname) + 5+2);
++ sprintf(server->host_name, "%s:%hu", hostname, port);
++ }
++ else
++ {
++ server->host_name = strdup((hostname) ? hostname : arg);
++ }
++
++ server->ipaddr = ipaddr;
++ server->orig_port = server->query_port = server->port = port;
++ server->type = type;
++ server->outfilename = outfilename;
++ server->flags = flags;
++ server->state = STATE_INIT;
++ if (query_arg)
++ {
++ server->query_arg = (port == port_max) ? query_arg : strdup(query_arg);
++ parse_query_params(server, server->query_arg);
++ }
++ else
++ {
++ server->query_arg = NULL;
++ }
++ init_qserver(server, type);
++
++ if (server->type->master)
++ {
++ waiting_for_masters++;
++ }
++
++ if (num_servers_total % 10 == 0)
++ {
++ hcache_update_file();
++ }
++
++ if (last_server != &servers)
++ {
++ prev_server = (struct qserver*)((char*)last_server - ((char*) &server->next - (char*)server));
++ server->prev = prev_server;
++ }
++ *last_server = server;
++ last_server = &server->next;
++
++ add_server_to_hash(server);
++
++ if (one_server_type_id == ~MASTER_SERVER)
++ {
++ one_server_type_id = type->id;
++ }
++ else if (one_server_type_id != type->id)
++ {
++ one_server_type_id = 0;
++ }
++
++ ++num_servers;
++ }
++ return 0;
++}
++
++struct qserver *add_qserver_byaddr(unsigned int ipaddr, unsigned short port, server_type *type, int *new_server)
++{
++ char arg[36];
++ struct qserver *server, *prev_server;
++ char *hostname = NULL;
++
++ if (run_timeout && time(0) - start_time >= run_timeout)
++ {
++ finish_output();
++ exit(0);
++ }
++
++ if (new_server)
++ {
++ *new_server = 0;
++ }
++ ipaddr = htonl(ipaddr);
++ if (ipaddr == 0)
++ {
++ return 0;
++ }
++
++ // TODO: this prevents servers with the same ip:port being queried
++ // and hence breaks virtual servers support e.g. Teamspeak 2
++ if (find_server_by_address(ipaddr, port) != NULL)
++ {
++ return 0;
++ }
++
++ if (new_server)
++ {
++ *new_server = 1;
++ }
++
++ server = (struct qserver*)calloc(1, sizeof(struct qserver));
++ server->ipaddr = ipaddr;
++ ipaddr = ntohl(ipaddr);
++ sprintf(arg, "%d.%d.%d.%d:%hu", ipaddr >> 24, (ipaddr >> 16) &0xff, (ipaddr >> 8) &0xff, ipaddr &0xff, port);
++ server->arg = strdup(arg);
++
++ if (hostname_lookup)
++ {
++ hostname = hcache_lookup_ipaddr(ipaddr);
++ }
++
++ if (hostname)
++ {
++ server->host_name = (char*)malloc(strlen(hostname) + 6+2);
++ sprintf(server->host_name, "%s:%hu", hostname, port);
++ }
++ else
++ {
++ server->host_name = strdup(arg);
++ }
++
++ server->orig_port = server->query_port = server->port = port;
++ init_qserver(server, type);
++
++ if (num_servers_total % 10 == 0)
++ {
++ hcache_update_file();
++ }
++
++ if (last_server != &servers)
++ {
++ prev_server = (struct qserver*)((char*)last_server - ((char*) &server->next - (char*)server));
++ server->prev = prev_server;
++ }
++ *last_server = server;
++ last_server = &server->next;
++
++ add_server_to_hash(server);
++
++ ++num_servers;
++
++ return server;
++}
++
++void add_servers_from_masters()
++{
++ struct qserver *server;
++ unsigned int ipaddr, i;
++ unsigned short port;
++ int n_servers, new_server, port_adjust = 0;
++ char *pkt;
++ server_type *server_type;
++ FILE *outfile;
++
++ for (server = servers; server != NULL; server = server->next)
++ {
++ if (!server->type->master || server->master_pkt == NULL)
++ {
++ continue;
++ }
++ pkt = server->master_pkt;
++
++ if (server->query_arg && server->type->id == GAMESPY_MASTER)
++ {
++ server_type = find_server_type_string(server->query_arg);
++ if (server_type == NULL)
++ {
++ server_type = find_server_type_id(server->type->master);
++ }
++ }
++ else
++ {
++ server_type = find_server_type_id(server->type->master);
++ }
++
++ if (server->type->id == GAMESPY_MASTER && server_type)
++ {
++ if (server_type->id == UN_SERVER)
++ {
++ port_adjust = - 1;
++ }
++ else if (server_type->id == KINGPIN_SERVER)
++ {
++ port_adjust = 10;
++ }
++ }
++
++ outfile = NULL;
++ if (server->outfilename)
++ {
++ if (strcmp(server->outfilename, "-") == 0)
++ {
++ outfile = stdout;
++ }
++ else
++ {
++ outfile = fopen(server->outfilename, "w");
++ }
++ if (outfile == NULL)
++ {
++ perror(server->outfilename);
++ continue;
++ }
++ }
++ n_servers = 0;
++ for (i = 0; i < server->master_pkt_len; i += 6)
++ {
++ memcpy(&ipaddr, &pkt[i], 4);
++ memcpy(&port, &pkt[i + 4], 2);
++ ipaddr = ntohl(ipaddr);
++ port = ntohs(port) + port_adjust;
++ new_server = 1;
++ if (outfile)
++ {
++ fprintf(outfile, "%s %d.%d.%d.%d:%hu\n",
++ server_type ? server_type->type_string: "",
++ (ipaddr >> 24) &0xff,
++ (ipaddr >> 16) &0xff,
++ (ipaddr >> 8) &0xff,
++ ipaddr &0xff, port
++ );
++ }
++ else if (server_type == NULL)
++ {
++ fprintf(OF, "%d.%d.%d.%d:%hu\n", (ipaddr >> 24) &0xff, (ipaddr >> 16) &0xff, (ipaddr >> 8) &0xff, ipaddr &0xff, port);
++ }
++ else
++ {
++ add_qserver_byaddr(ipaddr, port, server_type, &new_server);
++ }
++ n_servers += new_server;
++ }
++ free(server->master_pkt);
++ server->master_pkt = NULL;
++ server->master_pkt_len = 0;
++ server->n_servers = n_servers;
++ if (outfile)
++ {
++ fclose(outfile);
++ }
++ }
++ if (hostname_lookup)
++ {
++ hcache_update_file();
++ }
++ bind_sockets();
++}
++
++void init_qserver(struct qserver *server, server_type *type)
++{
++ server->server_name = NULL;
++ server->map_name = NULL;
++ server->game = NULL;
++ server->num_players = 0;
++ server->num_spectators = 0;
++ server->fd = -1;
++ server->state = STATE_INIT;
++ if (server->flags &FLAG_BROADCAST)
++ {
++ server->retry1 = 1;
++ server->retry2 = 1;
++ }
++ else
++ {
++ server->retry1 = n_retries;
++ server->retry2 = n_retries;
++ }
++ server->n_retries = 0;
++ server->ping_total = 0;
++ server->n_packets = 0;
++ server->n_requests = 0;
++
++ server->n_servers = 0;
++ server->master_pkt_len = 0;
++ server->master_pkt = NULL;
++ server->error = NULL;
++
++ server->saved_data.data = NULL;
++ server->saved_data.datalen = 0;
++ server->saved_data.pkt_index = -1;
++ server->saved_data.pkt_max = 0;
++ server->saved_data.next = NULL;
++
++ server->type = type;
++ server->next_rule = (get_server_rules) ? "" : NO_SERVER_RULES;
++ server->next_player_info = (get_player_info && type->player_packet) ? 0 : NO_PLAYER_INFO;
++
++ server->n_player_info = 0;
++ server->players = NULL;
++ server->n_rules = 0;
++ server->rules = NULL;
++ server->last_rule = &server->rules;
++ server->missing_rules = 0;
++
++ num_servers_total++;
++}
++
++// ipaddr should be network byte-order
++// port should be host byte-order
++// NOTE: This will return the first matching server, which is not nessacarily correct
++// depending on if duplicate ports are allowed
++struct qserver *find_server_by_address(unsigned int ipaddr, unsigned short port)
++{
++ struct qserver **hashed;
++ unsigned int hash, i;
++
++ if ( ! noserverdups && show_errors )
++ {
++ fprintf( stderr, "error: find_server_by_address while duplicates are allowed, this is unsafe!" );
++ }
++
++ hash = (ipaddr + port) % ADDRESS_HASH_LENGTH;
++
++ if (ipaddr == 0)
++ {
++ for (hash = 0; hash < ADDRESS_HASH_LENGTH; hash++)
++ {
++ printf("%3d %d\n", hash, server_hash_len[hash]);
++ }
++ return NULL;
++ }
++
++ hashed = server_hash[hash];
++ for (i = server_hash_len[hash]; i; i--, hashed++)
++ {
++ if (*hashed && (*hashed)->ipaddr == ipaddr && (*hashed)->port == port)
++ {
++ return *hashed;
++ }
++ }
++ return NULL;
++}
++
++void add_server_to_hash(struct qserver *server)
++{
++ unsigned int hash;
++ hash = (server->ipaddr + server->port) % ADDRESS_HASH_LENGTH;
++
++ if (server_hash_len[hash] % 16 == 0)
++ {
++ server_hash[hash] = (struct qserver **)realloc(server_hash[hash], sizeof(struct qserver **)*(server_hash_len[hash] + 16));
++ memset(&server_hash[hash][server_hash_len[hash]], 0, sizeof(struct qserver **) * 16);
++ }
++ server_hash[hash][server_hash_len[hash]] = server;
++ server_hash_len[hash]++;
++}
++
++void remove_server_from_hash(struct qserver *server)
++{
++ struct qserver **hashed;
++ unsigned int hash, i, ipaddr = server->ipaddr;
++ unsigned short port = server->orig_port;
++ hash = ( ipaddr + port ) % ADDRESS_HASH_LENGTH;
++
++ hashed = server_hash[hash];
++ for (i = server_hash_len[hash]; i; i--, hashed++)
++ {
++ // NOTE: we use direct pointer checks here to prevent issues with duplicate port servers e.g. teamspeak 2 and 3
++ if ( *hashed == server )
++ {
++ *hashed = NULL;
++ break;
++ }
++ }
++}
++
++void free_server_hash()
++{
++ int i;
++ for (i = 0; i < ADDRESS_HASH_LENGTH; i++)
++ {
++ if (server_hash[i])
++ {
++ free(server_hash[i]);
++ }
++ }
++}
++
++
++/*
++ * Functions for binding sockets to Quake servers
++ */
++int bind_qserver_post(struct qserver *server)
++{
++ server->state = STATE_CONNECTED;
++
++ if (server->type->flags &TF_TCP_CONNECT)
++ {
++ int one = 1;
++ if ( -1 == setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one)) )
++ {
++ perror( "Failed to set TCP no delay" );
++ }
++ }
++
++ if ( server->type->id & MASTER_SERVER )
++ {
++ // Use a large buffer so we dont miss packets
++ int sockbuf = RECV_BUF;
++ if ( -1 == setsockopt(server->fd, SOL_SOCKET, SO_RCVBUF, (void*)&sockbuf, sizeof(sockbuf)) )
++ {
++ perror( "Failed to set socket buffer" );
++ }
++ }
++
++#ifndef _WIN32
++ if (server->fd >= max_connmap)
++ {
++ int old_max = max_connmap;
++ max_connmap = server->fd + 32;
++ connmap = (struct qserver **)realloc(connmap, max_connmap *sizeof(struct qserver*));
++ memset(&connmap[old_max], 0, (max_connmap - old_max) *sizeof(struct qserver*));
++ }
++ connmap[server->fd] = server;
++#endif
++#ifdef _WIN32
++ {
++ int i;
++ for (i = 0; i < max_connmap; i++)
++ {
++ if (connmap[i] == NULL)
++ {
++ connmap[i] = server;
++ break;
++ }
++ }
++ if (i >= max_connmap)
++ {
++ printf("could not put server in connmap\n");
++ }
++ }
++#endif
++
++ return 0;
++}
++
++void add_ms_to_timeval(struct timeval *a, unsigned long interval_ms, struct timeval *result)
++{
++ result->tv_sec = a->tv_sec + (interval_ms / 1000);
++ result->tv_usec = a->tv_usec + ((interval_ms % 1000) * 1000);
++ if (result->tv_usec > 1000000)
++ {
++ result->tv_usec -= 1000000;
++ result->tv_sec++;
++ }
++}
++
++void qserver_sockaddr(struct qserver *server, struct sockaddr_in *addr)
++{
++ addr->sin_family = AF_INET;
++ addr->sin_port = (no_port_offset || server->flags &TF_NO_PORT_OFFSET) ?
++ htons(server->port) :
++ htons((unsigned short)(server->port + server->type->port_offset));
++ addr->sin_addr.s_addr = server->ipaddr;
++ memset(&(addr->sin_zero), 0, sizeof(addr->sin_zero));
++}
++
++
++int connected_qserver(struct qserver *server, int polling)
++{
++ struct sockaddr_in addr;
++ char error[50];
++ int ret;
++ struct timeval tv, now, to;
++ fd_set connect_set;
++
++ error[0] = '\0';
++ gettimeofday(&now, NULL);
++ add_ms_to_timeval(&server->packet_time1, retry_interval * server->retry1, &to);
++
++ if (polling)
++ {
++ // No delay
++ tv.tv_sec = 0;
++ tv.tv_usec = 0;
++ }
++ else
++ {
++ // Wait until the server would timeout
++ tv.tv_sec = to.tv_sec;
++ tv.tv_usec = to.tv_usec;
++ }
++
++ while( 1 )
++ {
++ FD_ZERO( &connect_set );
++ FD_SET( server->fd, &connect_set );
++
++ // NOTE: We may need to check exceptfds here on windows instead of writefds
++ ret = select( server->fd + 1, NULL, &connect_set, NULL, &tv );
++ if ( 0 == ret )
++ {
++ // Time limit expired
++ if (polling)
++ {
++ // Check if we have run out of time to connect
++ gettimeofday(&now, NULL);
++ if (0 < time_delta(&to, &now))
++ {
++ // We still have time to connect
++ return -2;
++ }
++ }
++
++ qserver_sockaddr(server, &addr);
++ sprintf(error, "connect:%s:%u - timeout", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
++ server->server_name = TIMEOUT;
++ server->state = STATE_TIMEOUT;
++ goto connect_error;
++ }
++ else if ( 0 < ret )
++ {
++ // Socket selected for write so either connected or error
++ int sockerr, orig_errno;
++ unsigned int lon = sizeof(int);
++
++ orig_errno = errno;
++ if ( 0 != getsockopt( server->fd, SOL_SOCKET, SO_ERROR, (void*)(&sockerr), &lon) )
++ {
++ // Restore the original error
++ errno = orig_errno;
++ goto connect_error;
++ }
++
++ if (sockerr)
++ {
++ // set the real error
++ errno = sockerr;
++ goto connect_error;
++ }
++
++ // Connection success
++ break;
++ }
++ else
++ {
++ // select failed
++ if ( errno != EINTR )
++ {
++ goto connect_error;
++ }
++ }
++ }
++
++
++ return bind_qserver_post(server);
++
++connect_error:
++ if (STATE_CONNECTING == server->state)
++ {
++ // Default error case
++ if (0 == strlen(error))
++ {
++ qserver_sockaddr(server, &addr);
++ sprintf(error, "connect: %s:%u", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
++ }
++ server->server_name = SYSERROR;
++ server->state = STATE_SYS_ERROR;
++ }
++
++ if ( show_errors )
++ {
++ perror(error);
++ }
++ close(server->fd);
++ server->fd = -1;
++ connected--;
++
++ return -1;
++}
++
++int bind_qserver2(struct qserver *server, int wait)
++{
++ struct sockaddr_in addr;
++ static int one = 1;
++
++ debug(1, "start %p @ %d.%d.%d.%d:%hu\n",
++ server,
++ server->ipaddr &0xff,
++ (server->ipaddr >> 8) &0xff,
++ (server->ipaddr >> 16) &0xff,
++ (server->ipaddr >> 24) &0xff,
++ server->port
++ );
++
++ if (server->type->flags &TF_TCP_CONNECT)
++ {
++ server->fd = socket(AF_INET, SOCK_STREAM, 0);
++ }
++ else
++ {
++ server->fd = socket(AF_INET, SOCK_DGRAM, 0);
++ }
++
++ if (server->fd == INVALID_SOCKET)
++ {
++ if (sockerr() == EMFILE)
++ {
++ // Per process descriptor table is full - retry
++ server->fd = -1;
++ return -2;
++ }
++
++ perror("socket");
++ server->server_name = SYSERROR;
++ server->state = STATE_SYS_ERROR;
++ return -1;
++ }
++
++ addr.sin_family = AF_INET;
++ addr.sin_addr.s_addr = htonl(source_ip);
++ if (server->type->id == Q2_MASTER)
++ {
++ addr.sin_port = htons(26500);
++ }
++ else if (source_port == 0)
++ {
++ addr.sin_port = 0;
++ }
++ else
++ {
++ addr.sin_port = htons(source_port);
++ source_port++;
++ if (source_port > source_port_high)
++ {
++ source_port = source_port_low;
++ }
++ }
++ memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero));
++
++ if (bind(server->fd, (struct sockaddr*) &addr, sizeof(struct sockaddr)) == SOCKET_ERROR)
++ {
++ perror("bind");
++ server->server_name = SYSERROR;
++ server->state = STATE_SYS_ERROR;
++ close(server->fd);
++ server->fd = -1;
++ return -1;
++ }
++
++ if (server->flags &FLAG_BROADCAST)
++ {
++ if ( -1 == setsockopt(server->fd, SOL_SOCKET, SO_BROADCAST, (char*) &one, sizeof(one)) )
++ {
++ perror( "Failed to set broadcast" );
++ server->server_name = SYSERROR;
++ server->state = STATE_SYS_ERROR;
++ close(server->fd);
++ server->fd = -1;
++ return -1;
++ }
++ }
++
++ // we need nonblocking always. poll on an udp socket would wake
++ // up and recv blocks if a packet with incorrect checksum is
++ // received
++ set_non_blocking(server->fd);
++
++ if (server->type->id != Q2_MASTER && !(server->flags &FLAG_BROADCAST))
++ {
++
++ if ( server->type->flags & TF_TCP_CONNECT )
++ {
++ // TCP set packet_time1 so it can be used for ping calculations for protocols with an initial response
++ gettimeofday( &server->packet_time1, NULL );
++ }
++
++ qserver_sockaddr(server, &addr);
++ server->state = STATE_CONNECTING;
++
++ if (connect(server->fd, (struct sockaddr*) &addr, sizeof(addr)) == SOCKET_ERROR)
++ {
++ if ( connection_inprogress() )
++ {
++ int ret;
++
++ // Ensure we don't detect the same error twice, specifically on a different server
++ clear_socketerror();
++
++ if ( ! wait )
++ {
++ debug(2, "connect:%s:%u - in progress", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
++ return -3;
++ }
++ ret = connected_qserver( server, 0 );
++ if (0 != ret)
++ {
++ return ret;
++ }
++ }
++ else
++ {
++ if ( show_errors )
++ {
++ char error[50];
++ sprintf(error, "connect:%s:%u", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
++ perror(error);
++ }
++ server->server_name = SYSERROR;
++ server->state = STATE_SYS_ERROR;
++ close(server->fd);
++ server->fd = -1;
++ return -1;
++ }
++ }
++ }
++
++ return bind_qserver_post(server);
++}
++
++
++int bind_qserver(struct qserver *server)
++{
++ return bind_qserver2(server, 1);
++}
++
++static struct timeval t_lastsend = { 0, 0 };
++
++int bind_sockets()
++{
++ struct qserver *server, *next_server, *first_server, *last_server;
++ struct timeval now;
++ int rc, retry_count = 0, inprogress = 0;
++
++ gettimeofday(&now, NULL);
++ if (connected && sendinterval && time_delta(&now, &t_lastsend) < sendinterval)
++ {
++ server = NULL;
++ }
++ else if (!waiting_for_masters)
++ {
++ if (last_server_bind == NULL)
++ {
++ last_server_bind = servers;
++ }
++ server = last_server_bind;
++ }
++ else
++ {
++ server = servers;
++ }
++
++ first_server = server;
++
++ for (; server != NULL && connected < max_simultaneous; )
++ {
++ // note the next server for use as process_func can free the server
++ next_server = server->next;
++ if (server->server_name == NULL && server->fd == -1)
++ {
++ if (waiting_for_masters && !server->type->master)
++ {
++ server = next_server;
++ continue;
++ }
++
++ if ((rc = bind_qserver2(server, syncconnect ? 1 : 0)) == 0)
++ {
++ debug(1, "send %d.%d.%d.%d:%hu\n",
++ server->ipaddr &0xff,
++ (server->ipaddr >> 8) &0xff,
++ (server->ipaddr >> 16) &0xff,
++ (server->ipaddr >> 24) &0xff,
++ server->port
++ );
++
++ gettimeofday(&t_lastsend, NULL);
++ debug(2, "calling status_query_func for %p - connect", server);
++ process_func_ret( server, server->type->status_query_func(server) );
++
++ connected++;
++ if (!waiting_for_masters)
++ {
++ last_server_bind = server;
++ }
++ break;
++ }
++ else if (rc == -3)
++ {
++ // Connect in progress
++
++ // We add to increment connected as we need to know the total
++ // amount of connections in progress not just those that have
++ // successfuly completed their connection otherwise we could
++ // blow FD_SETSIZE
++ connected++;
++ inprogress++;
++ }
++ else if (rc == -2 && ++retry_count > 2)
++ {
++ return -2;
++ }
++ else if (-1 == rc)
++ {
++ cleanup_qserver(server, FORCE);
++ }
++ }
++
++ server = next_server;
++ }
++
++ // Wait for all connections to succeed or fail
++ last_server = server;
++ while (inprogress)
++ {
++ inprogress = 0;
++ server = first_server;
++ for (; server != last_server;)
++ {
++ next_server = server->next;
++ if (STATE_CONNECTING == server->state)
++ {
++ rc = connected_qserver(server, 1);
++ switch(rc)
++ {
++ case 0:
++ // Connected
++ gettimeofday(&t_lastsend, NULL);
++ debug(2, "calling status_query_func for %p - in progress", server);
++ process_func_ret( server, server->type->status_query_func(server) );
++
++ // NOTE: connected is already incremented
++ if (!waiting_for_masters)
++ {
++ last_server_bind = server;
++ }
++ break;
++ case -2:
++ // In progress
++ inprogress++;
++ break;
++ case -1:
++ cleanup_qserver(server, FORCE);
++ break;
++ }
++ }
++ server = next_server;
++ }
++ }
++
++ if (NULL != last_server || (!connected && retry_count))
++ {
++ // Retry later, more to process
++ return -2;
++ }
++
++ return 0;
++}
++
++int process_func_ret( struct qserver *server, int ret )
++{
++ debug( 3, "%p, %d", server, ret );
++ switch ( ret )
++ {
++ case INPROGRESS:
++ return ret;
++
++ case DONE_AUTO:
++ cleanup_qserver( server, NO_FORCE );
++ return ret;
++
++ case DONE_FORCE:
++ case SYS_ERROR:
++ case MEM_ERROR:
++ case PKT_ERROR:
++ case ORD_ERROR:
++ case REQ_ERROR:
++ cleanup_qserver( server, FORCE );
++ return ret;
++ }
++
++ debug(0, "unhandled return code %d, please report!", ret);
++
++ return ret;
++}
++
++
++/*
++ * Functions for sending packets
++ */
++// this is so broken, someone please rewrite the timeout handling
++void send_packets()
++{
++ struct qserver *server;
++ struct timeval now;
++ int interval, n_sent = 0, prev_n_sent;
++ unsigned i;
++ debug(3, "processing...");
++
++ gettimeofday(&now, NULL);
++
++ if (!t_lastsend.tv_sec)
++ {
++ // nothing
++ }
++ else if (connected && sendinterval && time_delta(&now, &t_lastsend) < sendinterval)
++ {
++ return ;
++ }
++
++ for (i = 0; i < max_connmap; ++i)
++ {
++ server = connmap[i];
++ if (!server)
++ {
++ continue;
++ }
++
++ if (server->fd == -1)
++ {
++ debug(0, "invalid entry in connmap\n");
++ }
++
++ if (server->type->id &MASTER_SERVER)
++ {
++ interval = master_retry_interval;
++ }
++ else
++ {
++ interval = retry_interval;
++ }
++
++ debug(2, "server %p, name %s, retry1 %d, next_rule %p, next_player_info %d, num_players %d, n_retries %d",
++ server,
++ server->server_name,
++ server->retry1,
++ server->next_rule,
++ server->next_player_info,
++ server->num_players,
++ n_retries
++ );
++ prev_n_sent = n_sent;
++ if (server->server_name == NULL)
++ {
++ // We havent seen the server yet?
++ if (server->retry1 != n_retries && time_delta(&now, &server->packet_time1) < (interval *(n_retries - server->retry1 + 1)))
++ {
++ continue;
++ }
++
++ if (!server->retry1)
++ {
++ // No more retries
++ cleanup_qserver( server, FORCE );
++ continue;
++ }
++
++ if ( qserver_get_timeout(server, &now) <= 0 && ! ( server->type->flags & TF_TCP_CONNECT ) )
++ {
++ // Query status
++ debug(2, "calling status_query_func for %p", server);
++ process_func_ret( server, server->type->status_query_func(server) );
++ gettimeofday(&t_lastsend, NULL);
++ n_sent++;
++ continue;
++ }
++ }
++
++ if (server->next_rule != NO_SERVER_RULES)
++ {
++ // We want server rules
++ if (server->retry1 != n_retries && time_delta(&now, &server->packet_time1) < (interval *(n_retries - server->retry1 + 1)))
++ {
++ continue;
++ }
++
++ if (!server->retry1)
++ {
++ // no more retries
++ server->next_rule = NULL;
++ server->missing_rules = 1;
++ cleanup_qserver( server, NO_FORCE );
++ continue;
++ }
++ send_rule_request_packet(server);
++ gettimeofday(&t_lastsend, NULL);
++ n_sent++;
++ }
++
++ if (server->next_player_info < server->num_players)
++ {
++ // Expecting player details
++ if (server->retry2 != n_retries && time_delta(&now, &server->packet_time2) < (interval *(n_retries - server->retry2 + 1)))
++ {
++ continue;
++ }
++ if (!server->retry2)
++ {
++ server->next_player_info++;
++ if (server->next_player_info >= server->num_players)
++ {
++ // no more retries
++ cleanup_qserver( server, FORCE );
++ continue;
++ }
++ server->retry2 = n_retries;
++ }
++ send_player_request_packet(server);
++ gettimeofday(&t_lastsend, NULL);
++ n_sent++;
++ }
++
++ if (prev_n_sent == n_sent)
++ {
++ // we didnt send any additional queries
++ debug(2, "no queries sent: %d %d", time_delta(&now, &server->packet_time1), (interval *(n_retries + 1)));
++ if (!server->retry1)
++ {
++ // no retries left
++ if (time_delta(&now, &server->packet_time1) > (interval *(n_retries + 1)))
++ {
++ cleanup_qserver( server, FORCE );
++ }
++ }
++ else
++ {
++ // decrement as we didnt send any packets
++ server->retry1--;
++ }
++ }
++ }
++
++ debug(3, "done");
++}
++
++/* server starts sending data immediately, so we need not do anything */
++query_status_t send_bfris_request_packet(struct qserver *server)
++{
++ return register_send(server);
++}
++
++
++/* First packet for a normal Quake server
++ */
++query_status_t send_qserver_request_packet(struct qserver *server)
++{
++ return send_packet(server, server->type->status_packet, server->type->status_len);
++}
++
++/* First packet for a QuakeWorld server
++ */
++query_status_t send_qwserver_request_packet(struct qserver *server)
++{
++ int rc;
++
++ if (server->flags &FLAG_BROADCAST)
++ {
++ rc = send_broadcast(server, server->type->status_packet, server->type->status_len);
++ }
++ else if (server->server_name == NULL)
++ {
++ rc = send(server->fd, server->type->status_packet, server->type->status_len, 0);
++ }
++ else if (server->server_name != NULL && server->type->rule_packet)
++ {
++ rc = send(server->fd, server->type->rule_packet, server->type->rule_len, 0);
++ }
++ else
++ {
++ rc = SOCKET_ERROR;
++ }
++
++ if (rc == SOCKET_ERROR)
++ {
++ return send_error( server, rc );
++ }
++
++ if (server->retry1 == n_retries || server->flags &FLAG_BROADCAST)
++ {
++ gettimeofday(&server->packet_time1, NULL);
++ server->n_requests++;
++ }
++ else if (server->server_name == NULL)
++ {
++ server->n_retries++;
++ }
++ server->retry1--;
++ if (server->server_name == NULL)
++ {
++ server->n_packets++;
++ }
++ server->next_player_info = NO_PLAYER_INFO; // we don't have a player packet
++
++ return INPROGRESS;
++}
++
++// First packet for an Unreal Tournament 2003 server
++query_status_t send_ut2003_request_packet(struct qserver *server)
++{
++ int ret = send_packet(server, server->type->status_packet, server->type->status_len);
++ server->next_player_info = NO_PLAYER_INFO;
++
++ return ret;
++}
++
++// First packet for an Half-Life 2 server
++query_status_t send_hl2_request_packet(struct qserver *server)
++{
++ return send_packet(server, server->type->status_packet, server->type->status_len);
++}
++
++/* First packet for an Unreal master
++ */
++query_status_t send_unrealmaster_request_packet(struct qserver *server)
++{
++ return send_packet(server, server->type->status_packet, server->type->status_len);
++}
++
++static const char *steam_region[] =
++{
++ "US East Coast", "US West Coast", "South America", "Europe", "Asia", "Australia", "Middle East", "Africa", NULL
++};
++
++char *build_hlmaster_packet(struct qserver *server, int *len)
++{
++ static char packet[1600];
++ char *pkt, *r, *sep = "";
++ char *gamedir, *map, *flags;
++ int flen;
++
++ pkt = &packet[0];
++ memcpy(pkt, server->type->master_packet, *len);
++
++ if (server->type->flags &TF_MASTER_STEAM)
++ {
++ // default the region to 0xff
++ const char *regionstring = get_param_value(server, "region", NULL);
++ int region = 0xFF;
++ if (regionstring)
++ {
++ char *tmp = NULL;
++ region = strtol(regionstring, &tmp, 10);
++ if (tmp == regionstring)
++ {
++ int i = 0;
++ region = 0xFF;
++ for (; steam_region[i]; ++i)
++ {
++ if (!strcmp(regionstring, steam_region[i]))
++ {
++ region = i;
++ break;
++ }
++ }
++ }
++ }
++ *(pkt + 1) = region;
++ }
++
++ pkt += *len;
++
++ gamedir = get_param_value(server, "game", NULL);
++ if (gamedir)
++ {
++ pkt += sprintf(pkt, "\\gamedir\\%s", gamedir);
++ }
++
++ // not valid for steam?
++ map = get_param_value(server, "map", NULL);
++ if (map)
++ {
++ pkt += sprintf(pkt, "\\map\\%s", map);
++ }
++
++ // steam
++ flags = get_param_value(server, "napp", NULL);
++ if ( flags )
++ {
++ pkt += sprintf( pkt, "\\napp\\%s", flags );
++ }
++
++ // not valid for steam?
++ flags = get_param_value(server, "status", NULL);
++ r = flags;
++ while (flags && sep)
++ {
++ sep = strchr(r, ':');
++ if (sep)
++ {
++ flen = sep - r;
++ }
++ else
++ {
++ flen = strlen(r);
++ }
++
++ if (strncmp(r, "notempty", flen) == 0)
++ {
++ pkt += sprintf(pkt, "\\empty\\1");
++ }
++ else if (strncmp(r, "notfull", flen) == 0)
++ {
++ pkt += sprintf(pkt, "\\full\\1");
++ }
++ else if (strncmp(r, "dedicated", flen) == 0)
++ {
++ if (server->type->flags &TF_MASTER_STEAM)
++ {
++ pkt += sprintf(pkt, "\\type\\d");
++ }
++ else
++ {
++ pkt += sprintf(pkt, "\\dedicated\\1");
++ }
++ }
++ else if (strncmp(r, "linux", flen) == 0)
++ {
++ pkt += sprintf(pkt, "\\linux\\1");
++ }
++ else if (strncmp(r, "proxy", flen) == 0)
++ // steam
++ {
++ pkt += sprintf(pkt, "\\proxy\\1");
++ }
++ else if (strncmp(r, "secure", flen) == 0)
++ // steam
++ {
++ pkt += sprintf(pkt, "\\secure\\1");
++ }
++ r = sep + 1;
++ }
++
++ // always need null terminator
++ *pkt = 0x00;
++ pkt++;
++
++ *len = pkt - packet;
++
++ return packet;
++}
++
++/* First packet for a QuakeWorld master server
++ */
++query_status_t send_qwmaster_request_packet(struct qserver *server)
++{
++ int rc = 0;
++
++ server->next_player_info = NO_PLAYER_INFO;
++
++ if (server->type->id == Q2_MASTER)
++ {
++ struct sockaddr_in addr;
++ addr.sin_family = AF_INET;
++ if (no_port_offset || server->flags &TF_NO_PORT_OFFSET)
++ {
++ addr.sin_port = htons(server->port);
++ }
++ else
++ {
++ addr.sin_port = htons((unsigned short)(server->port + server->type->port_offset));
++ }
++ addr.sin_addr.s_addr = server->ipaddr;
++ memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero));
++ rc = sendto(server->fd, server->type->master_packet, server->type->master_len, 0, (struct sockaddr*) &addr, sizeof(addr));
++ }
++ else
++ {
++ char *packet;
++ int packet_len;
++ char query_buf[4096] = { 0 };
++
++ packet = server->type->master_packet;
++ packet_len = server->type->master_len;
++
++ if (server->type->id == HL_MASTER)
++ {
++ memcpy(server->type->master_packet + 1, server->master_query_tag, 3);
++ if (server->query_arg)
++ {
++ packet_len = server->type->master_len;
++ packet = build_hlmaster_packet(server, &packet_len);
++ }
++ }
++ else if (server->type->flags &TF_MASTER_STEAM)
++ {
++ // region
++ int tag_len = strlen(server->master_query_tag);
++ if (tag_len < 9)
++ {
++ // initial case
++ tag_len = 9;
++ strcpy(server->master_query_tag, "0.0.0.0:0");
++ }
++
++ // 1 byte packet id
++ // 1 byte region
++ // ip:port
++ // 1 byte null
++ packet_len = 2+tag_len + 1;
++
++ if (server->query_arg)
++ {
++ // build_hlmaster_packet uses server->type->master_packet
++ // as the basis so copy from server->master_query_tag
++ strcpy(server->type->master_packet + 2, server->master_query_tag);
++ packet = build_hlmaster_packet(server, &packet_len);
++ }
++ else
++ {
++ // default region
++ *(packet + 1) = 0xff;
++ memcpy(packet + 2, server->master_query_tag, tag_len);
++
++ // filter null
++ *(packet + packet_len) = 0x00;
++ packet_len++;
++ }
++ }
++ else if (server->type->flags &TF_QUERY_ARG)
++ {
++ // fill in the master protocol details
++ char *master_protocol = server->query_arg;
++ if (master_protocol == NULL)
++ {
++ master_protocol = server->type->master_protocol;
++ }
++ packet_len = sprintf(query_buf, server->type->master_packet,
++ master_protocol ? master_protocol : "",
++ server->type->master_query ? server->type->master_query: ""
++ );
++ packet = query_buf;
++ }
++
++ rc = send(server->fd, packet, packet_len, 0);
++ }
++
++ if (rc == SOCKET_ERROR)
++ {
++ return send_error( server, rc );
++ }
++
++ if (server->retry1 == n_retries)
++ {
++ gettimeofday(&server->packet_time1, NULL);
++ server->n_requests++;
++ }
++ else
++ {
++ server->n_retries++;
++ }
++ server->retry1--;
++ server->n_packets++;
++
++ return INPROGRESS;
++}
++
++
++query_status_t send_tribes_request_packet(struct qserver *server)
++{
++ return send_packet(server, server->type->player_packet, server->type->player_len);
++}
++
++query_status_t send_tribes2_request_packet(struct qserver *server)
++{
++ int rc;
++
++ if (server->flags &FLAG_BROADCAST && server->server_name == NULL)
++ {
++ rc = send_broadcast(server, server->type->status_packet, server->type->status_len);
++ }
++ else if (server->server_name == NULL)
++ {
++ rc = send(server->fd, server->type->status_packet, server->type->status_len, 0);
++ }
++ else
++ {
++ rc = send(server->fd, server->type->player_packet, server->type->player_len, 0);
++ }
++
++ if (rc == SOCKET_ERROR)
++ {
++ return send_error( server, rc );
++ }
++
++ register_send(server);
++
++ return rc;
++}
++
++query_status_t send_savage_request_packet(struct qserver *server)
++{
++ int len;
++ char *pkt;
++
++ if (get_player_info)
++ {
++ pkt = server->type->player_packet;
++ len = server->type->player_len;
++ }
++ else
++ {
++ pkt = server->type->status_packet;
++ len = server->type->status_len;
++ }
++
++ return send_packet(server, pkt, len);
++}
++
++query_status_t send_farcry_request_packet(struct qserver *server)
++{
++ int len;
++ char *pkt;
++
++ if (get_player_info)
++ {
++ pkt = server->type->player_packet;
++ len = server->type->player_len;
++ }
++ else
++ {
++ pkt = server->type->status_packet;
++ len = server->type->status_len;
++ }
++
++ return send_packet(server, pkt, len);
++}
++
++query_status_t send_tribes2master_request_packet(struct qserver *server)
++{
++ int rc;
++ unsigned char packet[1600], *pkt;
++ unsigned int len, min_players, max_players, region_mask = 0;
++ unsigned int build_version, max_bots, min_cpu, status;
++ char *game, *mission, *buddies;
++ static char *region_list[] =
++ {
++ "naeast", "nawest", "sa", "aus", "asia", "eur", NULL
++ };
++ static char *status_list[] =
++ {
++ "dedicated", "nopassword", "linux"
++ };
++
++ if (strcmp(get_param_value(server, "query", ""), "types") == 0)
++ {
++ rc = send(server->fd, tribes2_game_types_request, sizeof(tribes2_game_types_request), 0);
++ goto send_done;
++ }
++
++ memcpy(packet, server->type->master_packet, server->type->master_len);
++
++ pkt = packet + 7;
++
++ game = get_param_value(server, "game", "any");
++ len = strlen(game);
++ if (len > 255)
++ {
++ len = 255;
++ }
++ *pkt++ = len;
++ memcpy(pkt, game, len);
++ pkt += len;
++
++ mission = get_param_value(server, "mission", "any");
++ len = strlen(mission);
++ if (len > 255)
++ {
++ len = 255;
++ }
++ *pkt++ = len;
++ memcpy(pkt, mission, len);
++ pkt += len;
++
++ min_players = get_param_ui_value(server, "minplayers", 0);
++ max_players = get_param_ui_value(server, "maxplayers", 255);
++ *pkt++ = min_players;
++ *pkt++ = max_players;
++
++ region_mask = get_param_ui_value(server, "regions", 0xffffffff);
++ if (region_mask == 0)
++ {
++ char *regions = get_param_value(server, "regions", "");
++ char *r = regions;
++ char **list, *sep;
++ do
++ {
++ sep = strchr(r, ':');
++ if (sep)
++ {
++ len = sep - r;
++ }
++ else
++ {
++ len = strlen(r);
++ }
++ for (list = region_list; *list; list++)
++ if (strncasecmp(r, *list, len) == 0)
++ {
++ break;
++ }
++ if (*list)
++ {
++ region_mask |= 1 << (list - region_list);
++ }
++ r = sep + 1;
++ }
++ while (sep);
++ }
++ if (little_endian)
++ {
++ memcpy(pkt, ®ion_mask, 4);
++ }
++ else
++ {
++ pkt[0] = region_mask &0xff;
++ pkt[1] = (region_mask >> 8) &0xff;
++ pkt[2] = (region_mask >> 16) &0xff;
++ pkt[3] = (region_mask >> 24) &0xff;
++ }
++ pkt += 4;
++
++ build_version = get_param_ui_value(server, "build", 0);
++ /*
++ if ( build_version && build_version < 22337) {
++ packet[1]= 0;
++ build_version= 0;
++ }
++ */
++ if (little_endian)
++ {
++ memcpy(pkt, &build_version, 4);
++ }
++ else
++ {
++ pkt[0] = build_version &0xff;
++ pkt[1] = (build_version >> 8) &0xff;
++ pkt[2] = (build_version >> 16) &0xff;
++ pkt[3] = (build_version >> 24) &0xff;
++ }
++ pkt += 4;
++
++ status = get_param_ui_value(server, "status", - 1);
++ if (status == 0)
++ {
++ char *flags = get_param_value(server, "status", "");
++ char *r = flags;
++ char **list, *sep;
++ do
++ {
++ sep = strchr(r, ':');
++ if (sep)
++ {
++ len = sep - r;
++ }
++ else
++ {
++ len = strlen(r);
++ }
++ for (list = status_list; *list; list++)
++ if (strncasecmp(r, *list, len) == 0)
++ {
++ break;
++ }
++ if (*list)
++ {
++ status |= 1 << (list - status_list);
++ }
++ r = sep + 1;
++ }
++ while (sep);
++ }
++ else if (status == -1)
++ {
++ status = 0;
++ }
++ *pkt++ = status;
++
++ max_bots = get_param_ui_value(server, "maxbots", 255);
++ *pkt++ = max_bots;
++
++ min_cpu = get_param_ui_value(server, "mincpu", 0);
++ if (little_endian)
++ {
++ memcpy(pkt, &min_cpu, 2);
++ }
++ else
++ {
++ pkt[0] = min_cpu &0xff;
++ pkt[1] = (min_cpu >> 8) &0xff;
++ }
++ pkt += 2;
++
++ buddies = get_param_value(server, "buddies", NULL);
++ if (buddies)
++ {
++ char *b = buddies, *sep;
++ unsigned int buddy, n_buddies = 0;
++ unsigned char *n_loc = pkt++;
++ do
++ {
++ sep = strchr(b, ':');
++ if (sscanf(b, "%u", &buddy))
++ {
++ n_buddies++;
++ if (little_endian)
++ {
++ memcpy(pkt, &buddy, 4);
++ }
++ else
++ {
++ pkt[0] = buddy &0xff;
++ pkt[1] = (buddy >> 8) &0xff;
++ pkt[2] = (buddy >> 16) &0xff;
++ pkt[3] = (buddy >> 24) &0xff;
++ }
++ pkt += 4;
++ }
++ b = sep + 1;
++ }
++ while (sep && n_buddies < 255);
++ *n_loc = n_buddies;
++ }
++ else
++ {
++ *pkt++ = 0;
++ }
++
++ rc = send(server->fd, (char*)packet, pkt - packet, 0);
++
++send_done:
++ if (rc == SOCKET_ERROR)
++ {
++ return send_error( server, rc );
++ }
++
++ if (server->retry1 == n_retries)
++ {
++ gettimeofday(&server->packet_time1, NULL);
++ server->n_requests++;
++ }
++ else
++ {
++ server->n_retries++;
++ }
++ server->retry1--;
++ server->n_packets++;
++
++ return INPROGRESS;
++}
++
++static struct _gamespy_query_map
++{
++ char *qstat_type;
++ char *gamespy_type;
++}
++gamespy_query_map[] =
++{
++ { "qws", "quakeworld" },
++ { "q2s", "quake2" },
++ { "q3s", "quake3" },
++ { "tbs", "tribes" },
++ { "uns", "ut" },
++ { "sgs", "shogo" },
++ { "hls", "halflife" },
++ { "kps", "kingpin" },
++ { "hrs", "heretic2" },
++ { "sfs", "sofretail" },
++ { NULL, NULL }
++};
++
++query_status_t send_gamespy_master_request(struct qserver *server)
++{
++ int rc, i;
++ char request[1024];
++
++ if (server->n_packets)
++ {
++ return DONE_AUTO;
++ }
++
++ // WARNING: This only works for masters which don't check the value of validate
++ // e.g. unreal.epicgames.com
++ //
++ // What we should be doing here is recieving the challenge from the master
++ // processing the secure key and then using that as the value for validate.
++ //
++ // The details of this can be seen in gslist:
++ // http://aluigi.altervista.org/papers.htm#gslist
++ rc = send(server->fd, server->type->master_packet, server->type->master_len, 0);
++ if (rc != server->type->master_len)
++ {
++ return send_error( server, rc );
++ }
++
++ strcpy(request, server->type->status_packet);
++
++ for (i = 0; gamespy_query_map[i].qstat_type; i++)
++ {
++ if (strcasecmp(server->query_arg, gamespy_query_map[i].qstat_type) == 0)
++ {
++ break;
++ }
++ }
++
++ if (gamespy_query_map[i].gamespy_type == NULL)
++ {
++ strcat(request, server->query_arg);
++ }
++ else
++ {
++ strcat(request, gamespy_query_map[i].gamespy_type);
++ }
++ strcat(request, "\\final\\");
++ assert(strlen(request) < sizeof(request));
++
++ rc = send(server->fd, request, strlen(request), 0);
++ if (rc != strlen(request))
++ {
++ return send_error( server, rc );
++ }
++
++ if (server->retry1 == n_retries)
++ {
++ gettimeofday(&server->packet_time1, NULL);
++ server->n_requests++;
++ }
++
++ server->n_packets++;
++
++ return INPROGRESS;
++}
++
++query_status_t send_rule_request_packet(struct qserver *server)
++{
++ int rc, len;
++
++ debug(3, "send_rule_request_packet: %p", server);
++
++ /* Server created via broadcast, so bind it */
++ if (server->fd == -1)
++ {
++ if (bind_qserver(server) < 0)
++ {
++ goto setup_retry;
++ }
++ }
++
++ if (server->type->rule_query_func && server->type->rule_query_func != send_rule_request_packet)
++ {
++ return server->type->rule_query_func(server);
++ }
++
++ if (server->type->id == Q_SERVER)
++ {
++ strcpy((char*)q_rule.data, server->next_rule);
++ len = Q_HEADER_LEN + strlen((char*)q_rule.data) + 1;
++ q_rule.length = htons((short)len);
++ }
++ else
++ {
++ len = server->type->rule_len;
++ }
++
++ rc = send(server->fd, (const char*)server->type->rule_packet, len, 0);
++ if (rc == SOCKET_ERROR)
++ {
++ return send_error( server, rc );
++ }
++
++setup_retry:
++ if (server->retry1 == n_retries)
++ {
++ gettimeofday(&server->packet_time1, NULL);
++ server->n_requests++;
++ }
++ else if (server->server_name == NULL)
++ {
++ server->n_retries++;
++ }
++ server->retry1--;
++ if (server->server_name == NULL)
++ {
++ server->n_packets++;
++ }
++
++ return DONE_AUTO;
++}
++
++query_status_t send_player_request_packet(struct qserver *server)
++{
++ int rc;
++
++ debug( 3, "send_player_request_packet %p", server );
++
++ if (!server->type->player_packet)
++ {
++ return 0;
++ }
++
++ /* Server created via broadcast, so bind it */
++ if (server->fd == -1)
++ {
++ if (bind_qserver(server) < 0)
++ {
++ goto setup_retry;
++ }
++ }
++
++ if (server->type->player_query_func && server->type->player_query_func != send_player_request_packet)
++ {
++ return server->type->player_query_func(server);
++ }
++
++ if (!server->type->player_packet)
++ {
++ debug(0, "error: server %p has no player_packet", server);
++ return 0;
++ }
++
++ if (server->type->id == Q_SERVER)
++ {
++ q_player.data[0] = server->next_player_info;
++ }
++ rc = send(server->fd, (const char*)server->type->player_packet, server->type->player_len, 0);
++ if (rc == SOCKET_ERROR)
++ {
++ return send_error( server, rc );
++ }
++
++setup_retry:
++ if (server->retry2 == n_retries)
++ {
++ gettimeofday(&server->packet_time2, NULL);
++ server->n_requests++;
++ }
++ else
++ {
++ server->n_retries++;
++ }
++ server->retry2--;
++ server->n_packets++;
++
++ return 1;
++}
++
++void qserver_disconnect(struct qserver *server)
++{
++#ifdef _WIN32
++ int i;
++#endif
++ if (server->fd != - 1)
++ {
++ close(server->fd);
++#ifndef _WIN32
++ connmap[server->fd] = NULL;
++#else
++ for (i = 0; i < max_connmap; i++)
++ {
++ if (connmap[i] == server)
++ {
++ connmap[i] = NULL;
++ break;
++ }
++ }
++#endif
++ server->fd = - 1;
++ connected--;
++ }
++}
++
++/* Functions for figuring timeouts and when to give up
++ * Returns 1 if the query is done (server may be freed) and 0 if not.
++ */
++int cleanup_qserver(struct qserver *server, int force)
++{
++ int close_it = force;
++ debug( 3, "cleanup_qserver %p, %d", server, force );
++ if ( server->server_name == NULL )
++ {
++ debug(3, "server has no name, forcing close");
++ close_it = 1;
++ if (server->type->id &MASTER_SERVER && server->master_pkt != NULL)
++ {
++ server->server_name = MASTER;
++ }
++ else
++ {
++ server->server_name = TIMEOUT;
++ num_servers_timed_out++;
++ }
++ }
++ else if (server->type->flags &TF_SINGLE_QUERY)
++ {
++ debug(3, "TF_SINGLE_QUERY, forcing close");
++ close_it = 1;
++ }
++ else if (server->next_rule == NO_SERVER_RULES && server->next_player_info >= server->num_players)
++ {
++ debug(3, "no server rules and next_player_info >= num_players, forcing close");
++ close_it = 1;
++ }
++
++ debug(3, "close_it %d", close_it);
++ if (close_it)
++ {
++ if (server->saved_data.data)
++ {
++ SavedData *sdata = server->saved_data.next;
++ free(server->saved_data.data);
++ server->saved_data.data = NULL;
++ while (sdata != NULL)
++ {
++ SavedData *next;
++ free(sdata->data);
++ next = sdata->next;
++ free(sdata);
++ sdata = next;
++ }
++ server->saved_data.next = NULL;
++ }
++
++ qserver_disconnect(server);
++
++ if (server->server_name != TIMEOUT)
++ {
++ num_servers_returned++;
++ if (server->server_name != DOWN)
++ {
++ num_players_total += server->num_players;
++ max_players_total += server->max_players;
++ }
++ }
++ if (server->server_name == TIMEOUT || server->server_name == DOWN)
++ {
++ server->ping_total = 999999;
++ }
++ if (server->type->master)
++ {
++ waiting_for_masters--;
++ if (waiting_for_masters == 0)
++ {
++ add_servers_from_masters();
++ }
++ }
++ if (!server_sort)
++ {
++ display_server(server);
++ }
++ return 1;
++ }
++ return 0;
++}
++
++/** must be called only on connected servers
++ * @returns time in ms until server needs timeout handling. timeout handling is needed if <= zero
++ */
++static int qserver_get_timeout(struct qserver *server, struct timeval *now)
++{
++ int diff, diff1, diff2, interval;
++
++ if (server->type->id &MASTER_SERVER)
++ {
++ interval = master_retry_interval;
++ }
++ else
++ {
++ interval = retry_interval;
++ }
++
++ diff2 = 0xffff;
++
++ diff1 = interval *(n_retries - server->retry1 + 1) - time_delta(now, &server->packet_time1);
++
++ if (server->next_player_info < server->num_players)
++ {
++ diff2 = interval *(n_retries - server->retry2 + 1) - time_delta(now, &server->packet_time2);
++ }
++
++ debug(2, "timeout for %p is diff1 %d diff2 %d", server, diff1, diff2);
++
++
++ diff = (diff1 < diff2) ? diff1 : diff2;
++
++ return diff;
++}
++
++void get_next_timeout(struct timeval *timeout)
++{
++ struct qserver *server = servers;
++ struct timeval now;
++ int diff, smallest = retry_interval + master_retry_interval;
++ int bind_count = 0;
++
++ if (first_server_bind == NULL)
++ {
++ first_server_bind = servers;
++ }
++
++ server = first_server_bind;
++
++ for (; server != NULL && server->fd == -1; server = server->next)
++ ;
++
++ /* if there are unconnected servers and slots left we retry in 10ms */
++ if (server == NULL || (num_servers > connected && connected < max_simultaneous))
++ {
++ timeout->tv_sec = 0;
++ timeout->tv_usec = 10 * 1000;
++ return ;
++ }
++
++ first_server_bind = server;
++
++ gettimeofday(&now, NULL);
++ for (; server != NULL && bind_count < connected; server = server->next)
++ {
++ if (server->fd == -1)
++ {
++ continue;
++ }
++
++ diff = qserver_get_timeout(server, &now);
++ if (diff < smallest)
++ {
++ smallest = diff;
++ }
++ bind_count++;
++ }
++
++ if (smallest < 10)
++ {
++ smallest = 10;
++ }
++
++ timeout->tv_sec = smallest / 1000;
++ timeout->tv_usec = (smallest % 1000) *1000;
++}
++
++
++#ifdef USE_SELECT
++static fd_set select_read_fds;
++static int select_maxfd;
++static int select_cursor;
++
++int set_fds(fd_set *fds)
++{
++ int maxfd = - 1, i;
++
++ for (i = 0; i < max_connmap; i++)
++ {
++ if (connmap[i] != NULL)
++ {
++ FD_SET(connmap[i]->fd, fds);
++ if (connmap[i]->fd > maxfd)
++ {
++ maxfd = connmap[i]->fd;
++ }
++ }
++ }
++
++ return maxfd;
++}
++
++void set_file_descriptors()
++{
++ FD_ZERO(&select_read_fds);
++ select_maxfd = set_fds(&select_read_fds);
++}
++
++int wait_for_file_descriptors(struct timeval *timeout)
++{
++ select_cursor = 0;
++ return select(select_maxfd + 1, &select_read_fds, NULL, NULL, timeout);
++}
++
++struct qserver *get_next_ready_server()
++{
++ while (select_cursor < max_connmap && (connmap[select_cursor] == NULL || !FD_ISSET(connmap[select_cursor]->fd, &select_read_fds)))
++ {
++ select_cursor++;
++ }
++
++ if (select_cursor >= max_connmap)
++ {
++ return NULL;
++ }
++ return connmap[select_cursor++];
++}
++
++int wait_for_timeout(unsigned int ms)
++{
++ struct timeval timeout;
++ timeout.tv_sec = ms / 1000;
++ timeout.tv_usec = (ms % 1000) *1000;
++ return select(0, 0, NULL, NULL, &timeout);
++}
++
++#endif /* USE_SELECT */
++
++#ifdef USE_POLL
++static struct pollfd *pollfds;
++static int n_pollfds;
++static int max_pollfds = 0;
++static int poll_cursor;
++
++void set_file_descriptors()
++{
++ struct pollfd *p;
++ int i;
++
++ if (max_connmap > max_pollfds)
++ {
++ max_pollfds = max_connmap;
++ pollfds = (struct pollfd*)realloc(pollfds, max_pollfds *sizeof(struct pollfd));
++ }
++
++ p = pollfds;
++ for (i = 0; i < max_connmap; i++)
++ if (connmap[i] != NULL)
++ {
++ p->fd = connmap[i]->fd;
++ p->events = POLLIN;
++ p->revents = 0;
++ p++;
++ }
++ n_pollfds = p - pollfds;
++}
++
++int wait_for_file_descriptors(struct timeval *timeout)
++{
++ poll_cursor = 0;
++ return poll(pollfds, n_pollfds, timeout->tv_sec *1000+timeout->tv_usec / 1000);
++}
++
++struct qserver *get_next_ready_server()
++{
++ for (; poll_cursor < n_pollfds; poll_cursor++)
++ {
++ if (pollfds[poll_cursor].revents)
++ {
++ break;
++ }
++ }
++
++ if (poll_cursor >= n_pollfds)
++ {
++ return NULL;
++ }
++ return connmap[pollfds[poll_cursor++].fd];
++}
++
++int wait_for_timeout(unsigned int ms)
++{
++ return poll(0, 0, ms);
++}
++
++#endif /* USE_POLL */
++
++
++void free_server(struct qserver *server)
++{
++ struct player *player, *next_player;
++ struct rule *rule, *next_rule;
++
++ /* remove from servers list */
++ if (server == servers)
++ {
++ servers = server->next;
++ if (servers)
++ {
++ servers->prev = NULL;
++ }
++ }
++
++ if ((void*) &server->next == (void*)last_server)
++ {
++ if (server->prev)
++ {
++ last_server = &server->prev->next;
++ }
++ else
++ {
++ last_server = &servers;
++ }
++ }
++ if (server == first_server_bind)
++ {
++ first_server_bind = server->next;
++ }
++ if (server == last_server_bind)
++ {
++ last_server_bind = server->next;
++ }
++
++ if (server->prev)
++ {
++ server->prev->next = server->next;
++ }
++ if (server->next)
++ {
++ server->next->prev = server->prev;
++ }
++
++ /* remove from server hash table */
++ remove_server_from_hash(server);
++
++ /* free all the data */
++ for (player = server->players; player; player = next_player)
++ {
++ next_player = player->next;
++ free_player(player);
++ }
++
++ for (rule = server->rules; rule; rule = next_rule)
++ {
++ next_rule = rule->next;
++ free_rule(rule);
++ }
++
++ if (server->arg)
++ {
++ free(server->arg);
++ }
++ if (server->host_name)
++ {
++ free(server->host_name);
++ }
++ if (server->error)
++ {
++ free(server->error);
++ }
++ if (server->address)
++ {
++ free(server->address);
++ }
++ if (server->map_name)
++ {
++ free(server->map_name);
++ }
++ if (!(server->flags &FLAG_DO_NOT_FREE_GAME) && server->game)
++ {
++ free(server->game);
++ }
++ if (server->master_pkt)
++ {
++ free(server->master_pkt);
++ }
++ if ( server->query_arg )
++ {
++ free(server->query_arg);
++ }
++ if ( server->challenge_string )
++ {
++ free(server->challenge_string);
++ }
++
++ /* These fields are never malloc'd: outfilename
++ */
++
++ if (
++ server->server_name != NULL &&
++ server->server_name != DOWN &&
++ server->server_name != HOSTNOTFOUND &&
++ server->server_name != SYSERROR &&
++ server->server_name != MASTER &&
++ server->server_name != SERVERERROR &&
++ server->server_name != TIMEOUT &&
++ server->server_name != GAMESPY_MASTER_NAME &&
++ server->server_name != BFRIS_SERVER_NAME
++ )
++ {
++ free(server->server_name);
++ }
++
++ /*
++ params ...
++ saved_data ...
++ */
++
++ free(server);
++ --num_servers;
++}
++
++void free_player(struct player *player)
++{
++ if (player->name)
++ {
++ free(player->name);
++ }
++ if (!(player->flags &PLAYER_FLAG_DO_NOT_FREE_TEAM) && player->team_name)
++ {
++ free(player->team_name);
++ }
++ if (player->address)
++ {
++ free(player->address);
++ }
++ if (player->tribe_tag)
++ {
++ free(player->tribe_tag);
++ }
++ if (player->skin)
++ {
++ free(player->skin);
++ }
++ if (player->mesh)
++ {
++ free(player->mesh);
++ }
++ if (player->face)
++ {
++ free(player->face);
++ }
++ free(player);
++}
++
++void free_rule(struct rule *rule)
++{
++ if (rule->name)
++ {
++ free(rule->name);
++ }
++
++ if (rule->value)
++ {
++ free(rule->value);
++ }
++ free(rule);
++}
++
++/* Functions for handling response packets
++ */
++
++/* Packet from normal Quake server
++ */
++query_status_t deal_with_q_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++ struct q_packet *pkt = (struct q_packet*)rawpkt;
++ int rc;
++
++ debug( 2, "deal_with_q_packet %p, %d", server, pktlen );
++
++ if (ntohs(pkt->length) != pktlen)
++ {
++ fprintf(stderr, "%s Ignoring bogus packet; length %d != %d\n", server->arg, ntohs(pkt->length), pktlen);
++ return PKT_ERROR;
++ }
++
++ rawpkt[pktlen] = '\0';
++
++ switch (pkt->op_code)
++ {
++ case Q_CCREP_ACCEPT:
++ case Q_CCREP_REJECT:
++ return 0;
++ case Q_CCREP_SERVER_INFO:
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++ rc = server_info_packet(server, pkt, pktlen - Q_HEADER_LEN);
++ break;
++ case Q_CCREP_PLAYER_INFO:
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time2);
++ rc = player_info_packet(server, pkt, pktlen - Q_HEADER_LEN);
++ break;
++ case Q_CCREP_RULE_INFO:
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++ rc = rule_info_packet(server, pkt, pktlen - Q_HEADER_LEN);
++ break;
++ case Q_CCREQ_CONNECT:
++ case Q_CCREQ_SERVER_INFO:
++ case Q_CCREQ_PLAYER_INFO:
++ case Q_CCREQ_RULE_INFO:
++ default:
++ return 0;
++ }
++
++ if ( SOCKET_ERROR == rc )
++ {
++ fprintf(stderr, "%s error on packet opcode %x\n", server->arg, (int)pkt->op_code);
++ }
++
++ return rc;
++}
++
++
++/* Packet from QuakeWorld server
++ */
++query_status_t deal_with_qw_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++ debug( 2, "deal_with_qw_packet %p, %d", server, pktlen );
++ if (server->server_name == NULL)
++ {
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++ }
++
++ if (((rawpkt[0] != '\377' && rawpkt[0] != '\376') || rawpkt[1] != '\377' || rawpkt[2] != '\377' || rawpkt[3] != '\377') && show_errors)
++ {
++ unsigned int ipaddr = ntohl(server->ipaddr);
++ fprintf(stderr, "Odd packet from server %d.%d.%d.%d:%hu, processing ...\n",
++ (ipaddr >> 24) &0xff,
++ (ipaddr >> 16) &0xff,
++ (ipaddr >> 8) &0xff,
++ ipaddr &0xff,
++ ntohs(server->port)
++ );
++ print_packet(server, rawpkt, pktlen);
++ }
++
++ rawpkt[pktlen] = '\0';
++
++ if (rawpkt[4] == 'n')
++ {
++ if (server->type->id != QW_SERVER)
++ {
++ server->type = find_server_type_id(QW_SERVER);
++ }
++ return deal_with_q1qw_packet(server, rawpkt, pktlen);
++ }
++ else if (rawpkt[4] == '\377' && rawpkt[5] == 'n')
++ {
++ if (server->type->id != HW_SERVER)
++ {
++ server->type = find_server_type_id(HW_SERVER);
++ }
++ return deal_with_q1qw_packet(server, rawpkt, pktlen);
++ }
++ else if (strncmp(&rawpkt[4], "print\n\\", 7) == 0)
++ {
++ return deal_with_q2_packet(server, rawpkt + 10, pktlen - 10 );
++ }
++ else if (strncmp(&rawpkt[4], "print\n", 6) == 0)
++ {
++ /* work-around for occasional bug in Quake II status packets
++ */
++ char *c, *p;
++ p = c = &rawpkt[10];
++ while (*p != '\\' && (c = strchr(p, '\n')))
++ {
++ p = c + 1;
++ }
++ if (*p == '\\' && c != NULL)
++ {
++ return deal_with_q2_packet(server, p, pktlen - (p - rawpkt) );
++ }
++ }
++ else if (strncmp(&rawpkt[4], "infoResponse", 12) == 0 || (rawpkt[4] == '\001' && strncmp(&rawpkt[5], "infoResponse", 12) == 0))
++ {
++ /* quake3 info response */
++ int ret;
++ if (rawpkt[4] == '\001')
++ {
++ rawpkt++;
++ pktlen--;
++ }
++ rawpkt += 12;
++ pktlen -= 12;
++ for (; pktlen && *rawpkt != '\\'; pktlen--, rawpkt++)
++ ;
++ if (!pktlen)
++ {
++ return INPROGRESS;
++ }
++ if (rawpkt[pktlen - 1] == '"')
++ {
++ rawpkt[pktlen - 1] = '\0';
++ pktlen--;
++ }
++ if (get_player_info || get_server_rules)
++ {
++ server->next_rule = "";
++ }
++
++ ret = deal_with_q2_packet(server, rawpkt, pktlen );
++ if ( DONE_AUTO == ret && ( get_player_info || get_server_rules ) )
++ {
++ send_rule_request_packet( server);
++ server->retry1= n_retries-1;
++ return INPROGRESS;
++ }
++
++ return ret;
++ }
++ else if (strncmp(&rawpkt[4], "statusResponse\n", 15) == 0 || (rawpkt[4] == '\001' && strncmp(&rawpkt[5], "statusResponse\n", 15) == 0))
++ {
++ /* quake3 status response */
++ server->next_rule = NO_SERVER_RULES;
++ server->retry1 = 0;
++ if (rawpkt[4] == '\001')
++ {
++ rawpkt++;
++ pktlen--;
++ }
++ server->flags |= CHECK_DUPLICATE_RULES;
++ return deal_with_q2_packet(server, rawpkt + 19, pktlen - 19 );
++ }
++ else if (strncmp(&rawpkt[4], "infostringresponse", 19) == 0)
++ {
++ return deal_with_q2_packet(server, rawpkt + 23, pktlen - 23 );
++ }
++
++ if (show_errors)
++ {
++ unsigned int ipaddr = ntohl(server->ipaddr);
++ fprintf(stderr, "Odd packet from server %d.%d.%d.%d:%hu, ignoring ...\n",
++ (ipaddr >> 24) &0xff,
++ (ipaddr >> 16) &0xff,
++ (ipaddr >> 8) &0xff,
++ ipaddr &0xff,
++ ntohs(server->port)
++ );
++ print_packet(server, rawpkt, pktlen);
++ return PKT_ERROR;
++ }
++
++ return DONE_AUTO;
++}
++
++query_status_t deal_with_q1qw_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++ char *key, *value, *end, *users;
++ struct player *player = NULL, **last_player = &server->players;
++ int len, rc, complete = 0;
++ int number, frags, connect_time, ping;
++ char *pkt = &rawpkt[5];
++
++ debug( 2, "deal_with_q1qw_packet %p, %d", server, pktlen );
++
++ if (server->type->id == HW_SERVER)
++ {
++ pkt = &rawpkt[6];
++ }
++
++ *(users = strchr(pkt, '\n')) = '\0';
++ while (*pkt && pkt - rawpkt < pktlen)
++ {
++ if (*pkt == '\\')
++ {
++ pkt++;
++ end = strchr(pkt, '\\');
++ if (end == NULL)
++ {
++ break;
++ }
++ *end = '\0';
++ key = pkt;
++ pkt += strlen(pkt) + 1;
++ end = strchr(pkt, '\\');
++ if (end == NULL)
++ {
++ end = users;
++ }
++ value = (char*)malloc(end - pkt + 1);
++ memcpy(value, pkt, end - pkt);
++ value[end - pkt] = '\0';
++ pkt = end;
++ if (strcmp(key, "hostname") == 0)
++ {
++ server->server_name = value;
++ }
++ else if (strcmp(key, "map") == 0)
++ {
++ server->map_name = value;
++ }
++ else if (strcmp(key, "maxclients") == 0)
++ {
++ server->max_players = atoi(value);
++ free(value);
++ }
++ else if (strcmp(key, "maxspectators") == 0)
++ {
++ server->max_spectators = atoi(value);
++ free(value);
++ }
++ else if (get_server_rules || strncmp(key, "*game", 5) == 0)
++ {
++ add_rule(server, key, value, NO_VALUE_COPY);
++ if (strcmp(key, "*gamedir") == 0)
++ {
++ server->game = value;
++ server->flags |= FLAG_DO_NOT_FREE_GAME;
++ }
++ }
++ }
++ else
++ {
++ pkt++;
++ }
++ complete = 1;
++ }
++ *pkt = '\n';
++ while (*pkt && pkt - rawpkt < pktlen)
++ {
++ if (*pkt == '\n')
++ {
++ pkt++;
++ if (pkt - rawpkt >= pktlen || *pkt == '\0')
++ {
++ break;
++ }
++ rc = sscanf(pkt, "%d %d %d %d %n", &number, &frags, &connect_time, &ping, &len);
++ if (rc != 4)
++ {
++ char *nl; /* assume it's an error packet */
++ server->error = (char*)malloc(pktlen + 1);
++ nl = strchr(pkt, '\n');
++ if (nl != NULL)
++ {
++ strncpy(server->error, pkt, nl - pkt);
++ server->error[nl - pkt] = '\0';
++ }
++ else
++ {
++ strcpy(server->error, pkt);
++ }
++ server->server_name = SERVERERROR;
++ complete = 1;
++ break;
++ }
++ if (get_player_info)
++ {
++ player = (struct player*)calloc(1, sizeof(struct player));
++ player->number = number;
++ player->frags = frags;
++ player->connect_time = connect_time * 60;
++ player->ping = ping > 0 ? ping : - ping;
++ }
++ else
++ {
++ player = NULL;
++ }
++
++ pkt += len;
++
++ if (*pkt != '"')
++ {
++ break;
++ }
++ pkt += ping > 0 ? 1 : 4; // if 4 then no "\s\" in spectators name
++ // protocol "under construction"
++ end = strchr(pkt, '"');
++ if (end == NULL)
++ {
++ break;
++ }
++ if (player != NULL)
++ {
++ player->name = (char*)malloc(end - pkt + 1);
++ memcpy(player->name, pkt, end - pkt);
++ player->name[end - pkt] = '\0';
++ }
++
++ pkt = end + 2;
++
++ if (*pkt != '"')
++ {
++ break;
++ }
++ pkt++;
++ end = strchr(pkt, '"');
++ if (end == NULL)
++ {
++ break;
++ }
++ if (player != NULL)
++ {
++ player->skin = (char*)malloc(end - pkt + 1);
++ memcpy(player->skin, pkt, end - pkt);
++ player->skin[end - pkt] = '\0';
++ }
++ pkt = end + 2;
++
++ if (player != NULL)
++ {
++ sscanf(pkt, "%d %d%n", &player->shirt_color, &player->pants_color, &len);
++ *last_player = player;
++ last_player = &player->next;
++ }
++ else
++ {
++ sscanf(pkt, "%*d %*d%n", &len);
++ }
++ pkt += len;
++
++ if (pkt + 3 < rawpkt + pktlen && *pkt == ' ')
++ // mvdsv is at last rev 377, 23.06.2006
++ {
++ pkt++;
++
++ if (*pkt != '"')
++ {
++ break;
++ }
++ pkt++;
++ end = strchr(pkt, '"');
++ if (end == NULL)
++ {
++ break;
++ }
++
++ if (player != NULL)
++ {
++ player->team_name = (char*)malloc(end - pkt + 1);
++ memcpy(player->team_name, pkt, end - pkt);
++ player->team_name[end - pkt] = '\0';
++ }
++ pkt = end + 1;
++ }
++
++ if (ping > 0)
++ {
++ server->num_players++;
++ }
++ else
++ {
++ server->num_spectators++;
++ }
++ }
++ else
++ {
++ pkt++;
++ }
++ complete = 1;
++ }
++
++ if (!complete)
++ {
++ if (rawpkt[4] != 'n' || rawpkt[5] != '\0')
++ {
++ fprintf(stderr, "Odd packet from QW server %d.%d.%d.%d:%hu ...\n",
++ (server->ipaddr >> 24) &0xff,
++ (server->ipaddr >> 16) &0xff,
++ (server->ipaddr >> 8) &0xff,
++ server->ipaddr &0xff,
++ ntohs(server->port)
++ );
++ print_packet(server, rawpkt, pktlen);
++ }
++ }
++ else if (server->server_name == NULL)
++ {
++ server->server_name = strdup("");
++ }
++
++ return DONE_AUTO;
++}
++
++query_status_t deal_with_q2_packet(struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *key, *value, *end;
++ struct player *player = NULL;
++ struct player **last_player = &server->players;
++ int len, rc, complete = 0;
++ int frags = 0, ping = 0, num_players = 0;
++ char *pkt = rawpkt;
++
++ debug( 2, "deal_with_q2_packet %p, %d", server, pktlen );
++
++ while (*pkt && pkt - rawpkt < pktlen)
++ {
++ // we have variable, value pairs seperated by slash
++ if (*pkt == '\\')
++ {
++ pkt++;
++ if (*pkt == '\n' && server->type->id == SOF_SERVER)
++ {
++ goto player_info;
++ }
++
++ // Find the key
++ end = strchr(pkt, '\\');
++ if (NULL == end)
++ {
++ break;
++ }
++ *end = '\0';
++ key = pkt;
++ pkt += strlen(key) + 1;
++
++ // Find the value
++ end = strpbrk(pkt, "\\\n");
++ if (NULL == end)
++ {
++ // Last value
++ end = rawpkt + pktlen;
++ }
++
++ // Make a copy of the value
++ value = (char*)malloc(end - pkt + 1);
++ memcpy(value, pkt, end - pkt);
++ value[end - pkt] = '\0';
++ pkt = end;
++ debug(3, "%s = %s", key, value);
++ if (server->server_name == NULL && (strcmp(key, "hostname") == 0 || strcmp(key, "sv_hostname") == 0))
++ {
++ // Server name
++ server->server_name = value;
++ }
++ else if (strcmp(key, "mapname") == 0 || (strcmp(key, "map") == 0 && server->map_name == NULL))
++ {
++ // Map name
++ if (NULL != server->map_name)
++ {
++ free(server->map_name);
++ }
++ server->map_name = value;
++ }
++ else if (strcmp(key, "maxclients") == 0 || strcmp(key, "sv_maxclients") == 0 || strcmp(key, "max") == 0)
++ {
++ // Max Players
++ server->max_players = atoi(value);
++ // Note: COD 4 infoResponse returns max as sv_maxclients - sv_privateClients where as statusResponse returns the true max
++ // MOHAA Q3 protocol max players is always 0
++ if (0 == server->max_players)
++ {
++ server->max_players = - 1;
++ }
++ free(value);
++ }
++ else if (strcmp(key, "clients") == 0 || strcmp(key, "players") == 0)
++ {
++ // Num Players
++ server->num_players = atoi(value);
++ free(value);
++ }
++ else if (server->server_name == NULL && strcmp(key, "pure") == 0)
++ {
++ add_rule(server, key, value, NO_VALUE_COPY);
++ }
++ else if (get_server_rules || strncmp(key, "game", 4) == 0)
++ {
++ int dofree = 0;
++ int flags = ( server->flags & CHECK_DUPLICATE_RULES ) ? CHECK_DUPLICATE_RULES | NO_VALUE_COPY : NO_VALUE_COPY;
++ if (add_rule(server, key, value, flags ) == NULL)
++ {
++ // duplicate, so free value
++ dofree = 1;
++ }
++
++ if (server->game == NULL && strcmp(key, server->type->game_rule) == 0)
++ {
++ server->game = value;
++ if (0 == dofree)
++ {
++ server->flags |= FLAG_DO_NOT_FREE_GAME;
++ }
++ }
++ else if (1 == dofree)
++ {
++ free(value);
++ }
++ }
++ else
++ {
++ free(value);
++ }
++ }
++ else if (*pkt == '\n')
++ {
++ player_info: debug(3, "player info");
++ pkt++;
++ if (*pkt == '\0')
++ {
++ break;
++ }
++
++ if (0 == strncmp(pkt, "\\challenge\\", 11))
+ {
+- continue;
++ // qfusion
++ // This doesnt support getstatus looking at warsow source:
++ // server/sv_main.c: SV_ConnectionlessPacket
++ server->next_rule = NO_SERVER_RULES;
++ debug(3, "no more server rules");
++ break;
+ }
+
+- if ( ! server->retry1 )
++ rc = sscanf(pkt, "%d %n", &frags, &len);
++ if (rc == 1 && pkt[len] != '"')
+ {
+- // no more retries
+- server->next_rule= NULL;
+- server->missing_rules = 1;
+- cleanup_qserver( server, 0);
+- continue;
++ pkt += len;
++ rc = sscanf(pkt, "%d %n", &ping, &len);
++ }
++ else if (rc == 1)
++ {
++ /* MOHAA Q3 protocol only provides player ping */
++ ping = frags;
++ frags = 0;
+ }
+- send_rule_request_packet( server);
+- gettimeofday(&t_lastsend, NULL);
+- n_sent++;
+- }
+
+- if ( server->next_player_info < server->num_players)
+- {
+- // Expecting player details
+- if (
+- server->retry2 != n_retries &&
+- time_delta( &now, &server->packet_time2) < (interval*(n_retries-server->retry2+1))
+- )
++ if (rc != 1)
+ {
+- continue;
++ char *nl; /* assume it's an error packet */
++ server->error = (char*)malloc(pktlen + 1);
++ nl = strchr(pkt, '\n');
++ if (nl != NULL)
++ {
++ strncpy(server->error, pkt, nl - pkt);
++ }
++ else
++ {
++ strcpy(server->error, pkt);
++ }
++ server->server_name = SERVERERROR;
++ complete = 1;
++ break;
+ }
+- if ( ! server->retry2 )
++
++ if (get_player_info)
+ {
+- server->next_player_info++;
+- if ( server->next_player_info >= server->num_players )
++ player = (struct player*)calloc(1, sizeof(struct player));
++ player->number = 0;
++ player->connect_time = - 1;
++ player->frags = frags;
++ player->ping = ping;
++ }
++ else
++ {
++ player = NULL;
++ }
++
++ pkt += len;
++
++ if (isdigit((unsigned char) *pkt))
++ {
++ /* probably an SOF2 1.01 server, includes team # */
++ int team;
++ rc = sscanf(pkt, "%d %n", &team, &len);
++ if (rc == 1)
+ {
+- // no more retries
+- cleanup_qserver( server, 0);
+- continue;
++ pkt += len;
++ if (player)
++ {
++ player->team = team;
++ server->flags |= FLAG_PLAYER_TEAMS;
++ }
+ }
+- server->retry2 = n_retries;
+ }
+- send_player_request_packet( server);
+- gettimeofday(&t_lastsend, NULL);
+- n_sent++;
+- }
+
+- if ( prev_n_sent == n_sent )
+- {
+- // we didnt send any additional queries
+- debug(2, "%d %d", time_delta( &now, &server->packet_time1 ), (interval*(n_retries+1)));
+- if ( ! server->retry1 )
++ if (*pkt != '"')
+ {
+- // no retries left
+- if ( time_delta( &now, &server->packet_time1 ) > (interval*(n_retries+1) ) )
++ break;
++ }
++
++ pkt++;
++ end = strchr(pkt, '"');
++ if (end == NULL)
++ {
++ break;
++ }
++ if (player != NULL)
++ {
++ player->name = (char*)malloc(end - pkt + 1);
++ memcpy(player->name, pkt, end - pkt);
++ player->name[end - pkt] = '\0';
++ }
++ pkt = end + 1;
++
++ //WarSoW team number
++ if (*pkt != '\n')
++ {
++ int team;
++ rc = sscanf(pkt, "%d%n", &team, &len);
++ if (rc == 1)
+ {
+- cleanup_qserver( server, 1 );
++ pkt += len;
++ if (player)
++ {
++ player->team = team;
++ server->flags |= FLAG_PLAYER_TEAMS;
++ }
+ }
+ }
+- else
++
++ if (player != NULL)
+ {
+- // decrement as we didnt send any packets
+- server->retry1--;
++ player->skin = NULL;
++ player->shirt_color = - 1;
++ player->pants_color = - 1;
++ *last_player = player;
++ last_player = &player->next;
+ }
++ num_players++;
++ }
++ else
++ {
++ pkt++;
+ }
++ complete = 1;
++ }
++
++ if (server->num_players == 0 || num_players > server->num_players)
++ {
++ server->num_players = num_players;
+ }
++
++ if (!complete)
++ {
++ return PKT_ERROR;
++ }
++ else if (server->server_name == NULL)
++ {
++ server->server_name = strdup("");
++ }
++
++ return DONE_AUTO;
+ }
+
+-/* server starts sending data immediately, so we need not do anything */
+-void
+-send_bfris_request_packet( struct qserver *server)
++int ack_descent3master_packet(struct qserver *server, char *curtok)
+ {
+- register_send( server );
+-}
++ int rc;
++ char packet[0x1e];
++ memcpy(packet, descent3_masterquery, 0x1a);
++ packet[1] = 0x1d;
++ packet[0x16] = 1;
++ memcpy(packet + 0x1a, curtok, 4);
++ rc = send(server->fd, packet, sizeof(packet), 0);
++ if (rc == SOCKET_ERROR)
++ {
++ return send_error( server, rc );
++ }
+
++ return rc;
++}
+
+-/* First packet for a normal Quake server
++/* Packet from Descent3 master server (PXO)
+ */
+-void
+-send_qserver_request_packet( struct qserver *server)
++query_status_t deal_with_descent3master_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
++ int i = 0, lastpacket = 0;
++ char *names = rawpkt + 0x1f;
++ char *ips = rawpkt + 0x29f;
++ char *ports = rawpkt + 0x2ef;
+
+- int rc = send_packet( server, server->type->status_packet, server->type->status_len );
++ debug( 2, "deal_with_descent3master_packet %p, %d", server, pktlen );
+
+- if ( rc == SOCKET_ERROR )
++ while (i < 20)
+ {
+- cleanup_qserver( server, 1 );
+- }
++ if (*names)
++ {
++ char *c;
++ server->master_pkt_len += 6;
++ server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len);
++ c = server->master_pkt + server->master_pkt_len - 6;
++ memcpy(c, ips, 4);
++ memcpy(c + 4, ports, 2);
++ }
++ else if (i > 0)
++ {
++ lastpacket = 1;
++ }
++ names += 0x20;
++ ips += 4;
++ ports += 2;
++ i++;
++ }
++
++ ack_descent3master_packet(server, rawpkt + 0x1a);
++
++ server->n_servers = server->master_pkt_len / 6;
++
++ server->next_player_info = - 1;
++ server->retry1 = 0;
++
++ if (lastpacket)
++ {
++ return DONE_AUTO;
++ }
++
++ return INPROGRESS;
+ }
+
+-/* First packet for a QuakeWorld server
++/* Packet from QuakeWorld master server
+ */
+-void
+-send_qwserver_request_packet( struct qserver *server)
++query_status_t deal_with_qwmaster_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- int rc;
++ int ret = 0;
+
+- if ( server->flags & FLAG_BROADCAST)
+- rc= send_broadcast( server, server->type->status_packet,
+- server->type->status_len);
+- else if ( server->server_name == NULL)
+- rc= send( server->fd, server->type->status_packet,
+- server->type->status_len, 0);
+- else if ( server->server_name != NULL && server->type->rule_packet)
+- rc= send( server->fd, server->type->rule_packet,
+- server->type->rule_len, 0);
+- else
+- rc= SOCKET_ERROR;
+-
+- if ( rc == SOCKET_ERROR) {
+- unsigned int ipaddr= ntohl(server->ipaddr);
+- fprintf( stderr,
+- "Error on %d.%d.%d.%d, skipping ...\n",
+- (ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+- (ipaddr>>8)&0xff, ipaddr&0xff);
+- perror( "send");
+- cleanup_qserver( server, 1);
+- return;
+- }
+- if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST) {
+- gettimeofday( &server->packet_time1, NULL);
+- server->n_requests++;
+- }
+- else if ( server->server_name == NULL)
+- server->n_retries++;
+- server->retry1--;
+- if ( server->server_name == NULL)
+- server->n_packets++;
+- server->next_player_info = NO_PLAYER_INFO; // we don't have a player packet
++ debug( 2, "deal_with_qwmaster_packet %p, %d", server, pktlen );
++
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++
++ if (rawpkt[0] == QW_NACK)
++ {
++ server->error = strdup(&rawpkt[2]);
++ server->server_name = SERVERERROR;
++ return PKT_ERROR;
++ }
++
++ if (*((unsigned int*)rawpkt) == 0xffffffff)
++ {
++ rawpkt += 4; /* QW 1.5 */
++ pktlen -= 4;
++ if (rawpkt[0] == '\377' && rawpkt[1] == QW_SERVERS)
++ {
++ rawpkt++; /* hwmaster */
++ pktlen--;
++ }
++ }
++
++ if (rawpkt[0] == QW_SERVERS && rawpkt[1] == QW_NEWLINE)
++ {
++ rawpkt += 2;
++ pktlen -= 2;
++ }
++ else if (rawpkt[0] == HL_SERVERS && rawpkt[1] == 0x0d)
++ {
++ // 2 byte id + 4 byte sequence
++ memcpy(server->master_query_tag, rawpkt + 2, 3);
++ rawpkt += 6;
++ pktlen -= 6;
++ }
++ else if (rawpkt[0] == HL_SERVERS && rawpkt[1] == 0x0a)
++ {
++ // no sequence id for steam
++ // instead we use the ip:port of the last recieved server
++ struct in_addr *sin_addr = (struct in_addr*)(rawpkt + pktlen - 6);
++ char *ip = inet_ntoa(*sin_addr);
++ unsigned short port = htons(*((unsigned short*)(rawpkt + pktlen - 2)));
++
++ //fprintf( stderr, "NEXT IP=%s:%u\n", ip, port );
++ sprintf(server->master_query_tag, "%s:%u", ip, port);
++
++ // skip over the 2 byte id
++ rawpkt += 2;
++ pktlen -= 2;
++ }
++ else if (strncmp(rawpkt, "servers", 7) == 0)
++ {
++ rawpkt += 8;
++ pktlen -= 8;
++ }
++ else if (strncmp(rawpkt, "getserversResponse", 18) == 0)
++ {
++ rawpkt += 18;
++ pktlen -= 18;
++
++ for (; *rawpkt != '\\' && pktlen; pktlen--, rawpkt++){}
++
++ if (!pktlen)
++ {
++ return 1;
++ }
++ rawpkt++;
++ pktlen--;
++
++ debug( 2, "q3m pktlen %d lastchar %x\n", pktlen, (unsigned int)rawpkt[pktlen - 1]);
++
++ server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen + 1);
++
++ if (server->type->id == STEF_MASTER)
++ {
++ ret = decode_stefmaster_packet(server, rawpkt, pktlen);
++ }
++ else
++ {
++ ret = decode_q3master_packet(server, rawpkt, pktlen);
++ }
++ debug( 2, "q3m %d servers\n", server->n_servers);
++
++ return ret;
++ }
++ else if (show_errors)
++ {
++ unsigned int ipaddr = ntohl(server->ipaddr);
++ fprintf(stderr, "Odd packet from QW master %d.%d.%d.%d, processing ...\n",
++ (ipaddr >> 24) &0xff,
++ (ipaddr >> 16) &0xff,
++ (ipaddr >> 8) &0xff,
++ ipaddr &0xff
++ );
++ print_packet(server, rawpkt, pktlen);
++ }
++
++ server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen + 1);
++ rawpkt[pktlen] = '\0';
++ memcpy(server->master_pkt + server->master_pkt_len, rawpkt, pktlen + 1);
++ server->master_pkt_len += pktlen;
++
++ server->n_servers = server->master_pkt_len / 6;
++
++ if (server->type->flags &TF_MASTER_MULTI_RESPONSE)
++ {
++ server->next_player_info = - 1;
++ server->retry1 = 0;
++ }
++ else if (server->type->id == HL_MASTER)
++ {
++ if (server->master_query_tag[0] == 0 && server->master_query_tag[1] == 0 && server->master_query_tag[2] == 0)
++ {
++ // all done
++ server->server_name = MASTER;
++ bind_sockets();
++ ret = DONE_FORCE;
++ }
++ else
++ {
++ // more to come
++ server->retry1++;
++ send_qwmaster_request_packet(server);
++ }
++ }
++ else if (server->type->flags &TF_MASTER_STEAM)
++ {
++ // should the HL_MASTER be the same as this?
++ int i;
++ for (i = pktlen - 6; i < pktlen && 0x00 == rawpkt[i]; i++){}
++
++ if (i == pktlen)
++ {
++ // last 6 bytes where 0x00 so we have reached the last packet
++ server->n_servers--;
++ server->master_pkt_len -= 6;
++ server->server_name = MASTER;
++ bind_sockets();
++ ret = DONE_FORCE;
++ }
++ else
++ {
++ // more to come
++ server->retry1++;
++ send_qwmaster_request_packet(server);
++ }
++ }
++ else
++ {
++ server->server_name = MASTER;
++ ret = DONE_AUTO;
++ bind_sockets();
++ }
++
++ return ret;
+ }
+
+-// First packet for an Unreal Tournament 2003 server
+-void
+-send_ut2003_request_packet( struct qserver *server)
++int decode_q3master_packet(struct qserver *server, char *pkt, int pktlen)
+ {
+- send_packet( server, server->type->status_packet, server->type->status_len );
+- server->next_player_info = NO_PLAYER_INFO;
++ char *p;
++ char *end = pkt + pktlen;
++ char *last = end - 6;
++
++ pkt[pktlen] = 0;
++ p = pkt;
++
++ while ( p < last )
++ {
++ // IP & Port
++ memcpy(server->master_pkt + server->master_pkt_len, &p[0], 6);
++ server->master_pkt_len += 6;
++ p += 6;
++ // Sometimes we get some bad IP's so we search for the entry terminator '\' to avoid issues with this
++ while ( p < end && *p != '\\' )
++ {
++ p++;
++ }
++
++ if ( p < end )
++ {
++ // Skip over the '\'
++ p++;
++ }
++
++ if ( *p && p + 3 == end && 0 == strncmp( "EOF", p, 3 ) )
++ {
++ // Last packet ID ( seen in COD4 )
++ server->n_servers = server->master_pkt_len / 6;
++ server->retry1 = 0; // received at least one packet so no need to retry
++ return DONE_FORCE;
++ }
++ }
++
++ server->n_servers = server->master_pkt_len / 6;
++ // server->next_player_info= -1; evil, causes busy loop!
++ server->retry1 = 0; // received at least one packet so no need to retry
++
++ return INPROGRESS;
+ }
+
+-// First packet for an Half-Life 2 server
+-void
+-send_hl2_request_packet( struct qserver *server)
++int decode_stefmaster_packet(struct qserver *server, char *pkt, int pktlen)
+ {
+- send_packet( server, server->type->status_packet, server->type->status_len );
++ unsigned char *p, *m, *end;
++ unsigned int i, b;
++
++ pkt[pktlen] = 0;
++
++ p = (unsigned char*)pkt;
++ m = (unsigned char*)server->master_pkt + server->master_pkt_len;
++ end = (unsigned char*) &pkt[pktlen - 12];
++ while (*p && p < end)
++ {
++ for (i = 6; i; i--)
++ {
++ sscanf((char*)p, "%2x", &b);
++ p += 2;
++ *m++ = b;
++ }
++ server->master_pkt_len += 6;
++ while (*p && *p == '\\')
++ {
++ p++;
++ }
++ }
++ server->n_servers = server->master_pkt_len / 6;
++ server->next_player_info = - 1;
++ server->retry1 = 0;
++
++ return 1;
+ }
+
+-/* First packet for an Unreal master
++/* Packet from Tribes master server
+ */
+-void
+-send_unrealmaster_request_packet( struct qserver *server)
++query_status_t deal_with_tribesmaster_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- send_packet( server, server->type->status_packet, server->type->status_len );
+-}
++ unsigned char *upkt = (unsigned char*)rawpkt;
++ int packet_number = upkt[2];
++ int n_packets = upkt[3];
++ unsigned char *p;
++ char *mpkt;
++ int len;
++ unsigned int ipaddr;
+
+-static const char* steam_region[] =
+-{
+- "US East Coast",
+- "US West Coast",
+- "South America",
+- "Europe",
+- "Asia",
+- "Australia",
+- "Middle East",
+- "Africa",
+- NULL
+-};
++ debug( 2, "deal_with_tribesmaster_packet %p, %d", server, pktlen );
+
+-char *
+-build_hlmaster_packet( struct qserver *server, int *len)
+-{
+- static char packet[1600];
+- char *pkt, *r, *sep= "";
+- char *gamedir, *map, *flags;
+- int flen;
++ if (memcmp(rawpkt, tribes_master_response, sizeof(tribes_master_response)) != 0)
++ {
++ fprintf(stderr, "Odd packet from Tribes master server\n");
++ print_packet(server, rawpkt, pktlen);
++ }
++
++ /* 0x1006
++ 01 packet number
++ 08 # packets
++ 02
++ 0000
++ 66
++ 0d length of following string
++ "Tribes Master"
++ 3c length of following string
++ "Check out the Starsiege demo now! www.starsiegeplayers.com"
++ 0035
++ 06 d143 4764 812c
++ 06 d1e2 8df3 616d
++ 06 1804 6d50 616d
++ 06 d81c 6dc0 616d
++ */
++ /* 0x1006
++ 02
++ 08
++ 02 0000
++ 66
++ 00 3f
++ 06 cf88 344c 1227
++ */
+
+- pkt= &packet[0];
+- memcpy( pkt, server->type->master_packet, *len );
++ /* printf( "packet_number %d n_packets %d\n", packet_number, n_packets);
++ */
+
+- if ( server->type->flags & TF_MASTER_STEAM )
++ len = upkt[8];
++ if (len > 0)
+ {
+- // default the region to 0xff
+- const char* regionstring = get_param_value( server, "region", NULL );
+- int region = 0xFF;
+- if(regionstring)
++ p = (unsigned char*)rawpkt + 9;
++ // printf( "%.*s\n", len, p);
++ p += len;
++ len = upkt[8+len + 1];
++ // printf( "%.*s\n", len, p+1);
++ p += len + 1;
++ p += 2;
++ }
++ else
++ {
++ p = (unsigned char*)rawpkt + 10;
++ }
++
++ if (server->master_pkt == NULL)
++ {
++ server->master_pkt = (char*)malloc(n_packets *64 * 6);
++ mpkt = server->master_pkt;
++ }
++ else
++ {
++ mpkt = server->master_pkt + server->n_servers *6;
++ }
++
++ while ((char*)p < rawpkt + pktlen)
++ {
++ if (*p != 0x6)
+ {
+- char* tmp = NULL;
+- region = strtol( regionstring, &tmp, 10);
+- if(tmp == regionstring)
+- {
+- int i = 0;
+- region = 0xFF;
+- for(;steam_region[i]; ++i)
+- {
+- if(!strcmp(regionstring, steam_region[i]))
+- {
+- region = i;
+- break;
+- }
+- }
+- }
++ printf("*p %u\n", (unsigned) *p);
++ }
++ memcpy(mpkt, p + 1, sizeof(ipaddr));
++ if (0)
++ {
++ mpkt[4] = p[5];
++ mpkt[5] = p[6];
+ }
+- *(pkt+1) = region;
++ else
++ {
++ mpkt[5] = p[5];
++ mpkt[4] = p[6];
++ }
++ //printf( "%08x:%hu %u.%u.%u.%u:%hu\n", ipaddr, port, ipaddr>>24, (ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff, port);
++ p += 7;
++ mpkt += 6;
+ }
++ /*
++ if ( (char*)p != rawpkt+pktlen)
++ printf( "%x %x\n", p, rawpkt+pktlen);
++ */
++ server->master_pkt_len = mpkt - server->master_pkt;
++ server->n_servers = server->master_pkt_len / 6;
++ server->server_name = MASTER;
++ server->next_player_info = - 1;
+
+- pkt+= *len;
+-
+- gamedir = get_param_value( server, "game", NULL);
+- if ( gamedir )
++ if (packet_number >= n_packets)
+ {
+- pkt+= sprintf( pkt, "\\gamedir\\%s", gamedir);
++ return DONE_FORCE;
+ }
+-
+- // not valid for steam?
+- map = get_param_value( server, "map", NULL);
+- if ( map )
++ else
+ {
+- pkt+= sprintf( pkt, "\\map\\%s", map);
++ return DONE_AUTO;
+ }
++}
+
+- // not valid for steam?
+- flags= get_param_value( server, "status", NULL);
+- r= flags;
+- while ( flags && sep )
+- {
+- sep= strchr( r, ':');
+- if ( sep )
+- flen= sep-r;
+- else
+- flen= strlen( r);
+-
+- if ( strncmp( r, "notempty", flen) == 0)
+- pkt+= sprintf( pkt, "\\empty\\1");
+- else if ( strncmp( r, "notfull", flen) == 0)
+- pkt+= sprintf( pkt, "\\full\\1");
+- else if ( strncmp( r, "dedicated", flen) == 0)
++char *display_tribes2_string_list(unsigned char *pkt)
++{
++ char *delim = "";
++ unsigned int count, len;
++ count = *pkt;
++ pkt++;
++ for (; count; count--)
++ {
++ len = *pkt;
++ pkt++;
++ if (len > 0)
+ {
+- if ( server->type->flags & TF_MASTER_STEAM )
+- pkt+= sprintf( pkt, "\\type\\d");
++ if (raw_display)
++ {
++ fprintf(OF, "%s%.*s", delim, (int)len, pkt);
++ delim = raw_delimiter;
++ }
+ else
+- pkt+= sprintf( pkt, "\\dedicated\\1");
++ {
++ fprintf(OF, "%.*s\n", (int)len, pkt);
++ }
+ }
+- else if ( strncmp( r, "linux", flen) == 0)
+- pkt+= sprintf( pkt, "\\linux\\1");
+- else if ( strncmp( r, "proxy", flen) == 0) // steam
+- pkt+= sprintf( pkt, "\\proxy\\1");
+- else if ( strncmp( r, "secure", flen) == 0) // steam
+- pkt+= sprintf( pkt, "\\secure\\1");
+- r= sep+1;
++ pkt += len;
+ }
+-
+- // always need null terminator
+- *pkt = 0x00;
+- pkt++;
+-
+- *len = pkt - packet;
+-
+- return packet;
++ if (raw_display)
++ {
++ fputs("\n", OF);
++ }
++ return (char*)pkt;
+ }
+
+-/* First packet for a QuakeWorld master server
+- */
+-void
+-send_qwmaster_request_packet( struct qserver *server)
++query_status_t deal_with_tribes2master_packet(struct qserver *server, char *pkt, int pktlen)
+ {
+- int rc= 0;
++ unsigned int n_servers, index, total, server_limit;
++ char *p, *mpkt;
+
+- server->next_player_info = NO_PLAYER_INFO;
++ debug( 2, "deal_with_tribes2master_packet %p, %d", server, pktlen );
+
+- if ( server->type->id == Q2_MASTER)
++ if (pkt[0] == TRIBES2_RESPONSE_GAME_TYPES)
+ {
+- struct sockaddr_in addr;
+- addr.sin_family= AF_INET;
+- if ( no_port_offset || server->flags & TF_NO_PORT_OFFSET)
++ pkt += 6;
++ if (raw_display)
+ {
+- addr.sin_port= htons(server->port);
++ fprintf(OF, "%s%s%s%s", server->type->type_prefix, raw_delimiter, server->arg, raw_delimiter);
+ }
+ else
+ {
+- addr.sin_port= htons((unsigned short)( server->port + server->type->port_offset ));
+- }
+- addr.sin_addr.s_addr= server->ipaddr;
+- memset( &(addr.sin_zero), 0, sizeof(addr.sin_zero));
+- rc= sendto( server->fd, server->type->master_packet,
+- server->type->master_len, 0,
+- (struct sockaddr *) &addr, sizeof(addr));
+- }
+- else
+- {
+- char *packet;
+- int packet_len;
+- char query_buf[4096] = {0};
+-
+- packet= server->type->master_packet;
+- packet_len= server->type->master_len;
+-
+- if ( server->type->id == HL_MASTER)
+- {
+- memcpy( server->type->master_packet+1, server->master_query_tag, 3);
+- if ( server->query_arg)
+- {
+- packet_len = server->type->master_len;
+- packet= build_hlmaster_packet( server, &packet_len);
+- }
++ fprintf(OF, "Game Types\n");
++ fprintf(OF, "----------\n");
+ }
+- else if ( server->type->flags & TF_MASTER_STEAM)
++ pkt = display_tribes2_string_list((unsigned char*)pkt);
++ if (raw_display)
+ {
+- // region
+- int tag_len = strlen( server->master_query_tag );
+- if ( tag_len < 9 )
+- {
+- // initial case
+- tag_len = 9;
+- strcpy( server->master_query_tag, "0.0.0.0:0" );
+- }
+-
+- // 1 byte packet id
+- // 1 byte region
+- // ip:port
+- // 1 byte null
+- packet_len = 2 + tag_len + 1;
+-
+- if ( server->query_arg )
+- {
+- // build_hlmaster_packet uses server->type->master_packet
+- // as the basis so copy from server->master_query_tag
+- strcpy( server->type->master_packet+2, server->master_query_tag );
+- packet = build_hlmaster_packet( server, &packet_len );
+- }
+- else
+- {
+- // default region
+- *(packet + 1) = 0xff;
+- memcpy( packet+2, server->master_query_tag, tag_len );
+-
+- // filter null
+- *(packet + packet_len ) = 0x00;
+- packet_len++;
+- }
++ fprintf(OF, "%s%s%s%s", server->type->type_prefix, raw_delimiter, server->arg, raw_delimiter);
+ }
+- else if ( server->type->flags & TF_QUERY_ARG)
++ else
+ {
+- // fill in the master protocol details
+- char *master_protocol= server->query_arg;
+- if ( master_protocol == NULL)
+- {
+- master_protocol= server->type->master_protocol;
+- }
+- packet_len = sprintf( query_buf, server->type->master_packet,
+- master_protocol?master_protocol:"",
+- server->type->master_query?server->type->master_query:"");
+- packet = query_buf;
++ fprintf(OF, "\nMission Types\n");
++ fprintf(OF, "-------------\n");
+ }
++ display_tribes2_string_list((unsigned char*)pkt);
+
+- rc= send( server->fd, packet, packet_len, 0);
++ server->master_pkt_len = 0;
++ server->n_servers = 0;
++ server->server_name = MASTER;
++ server->next_player_info = - 1;
++ return DONE_FORCE;
+ }
+
+- if ( rc == SOCKET_ERROR)
++ if (pkt[0] != TRIBES2_RESPONSE_MASTER)
+ {
+- perror( "send");
++ /* error */
++ return PKT_ERROR;
+ }
+
+- if ( server->retry1 == n_retries)
++ server_limit = get_param_ui_value(server, "limit", ~0);
++
++ n_servers = little_endian ? *(unsigned short*)(pkt + 8): swap_short(pkt + 8);
++ index = *(unsigned char*)(pkt + 6);
++ total = *(unsigned char*)(pkt + 7);
++ if (server->master_pkt == NULL)
+ {
+- gettimeofday( &server->packet_time1, NULL);
+- server->n_requests++;
++ server->master_pkt = (char*)malloc(total *n_servers * 6);
++ mpkt = server->master_pkt;
+ }
+ else
+ {
+- server->n_retries++;
++ mpkt = server->master_pkt + server->n_servers *6;
+ }
+- server->retry1--;
+- server->n_packets++;
+-}
+-
+-void
+-send_tribes_request_packet( struct qserver *server )
+-{
+- send_packet( server, server->type->player_packet, server->type->player_len );
+-}
+-
+-void
+-send_tribes2_request_packet( struct qserver *server )
+-{
+- int rc;
+-
+- if ( server->flags & FLAG_BROADCAST && server->server_name == NULL)
+- rc= send_broadcast( server, server->type->status_packet,
+- server->type->status_len);
+- else if ( server->server_name == NULL)
+- rc= send( server->fd, server->type->status_packet,
+- server->type->status_len, 0);
+- else
+- rc= send( server->fd, server->type->player_packet,
+- server->type->player_len, 0);
+
+- if ( rc == SOCKET_ERROR)
+- perror( "send");
++ p = pkt + 10;
++ for (; n_servers && ((char*)mpkt - server->master_pkt) / 6 < server_limit; n_servers--, p += 6, mpkt += 6)
++ {
++ memcpy(mpkt, p, 4);
++ mpkt[4] = p[5];
++ mpkt[5] = p[4];
++ }
++ server->master_pkt_len = (char*)mpkt - server->master_pkt;
++ server->n_servers = server->master_pkt_len / 6;
++ server->server_name = MASTER;
++ server->next_player_info = - 1;
+
+- register_send( server );
+-}
++ if (index >= total - 1 || server->n_servers >= server_limit)
++ {
++ return PKT_ERROR;
++ }
+
+-void
+-send_ghostrecon_request_packet( struct qserver *server)
+-{
+- send_packet( server, server->type->status_packet, server->type->status_len );
++ return DONE_AUTO;
+ }
+
+-void
+-send_eye_request_packet( struct qserver *server)
++int server_info_packet(struct qserver *server, struct q_packet *pkt, int datalen)
+ {
+- send_packet( server, server->type->status_packet, server->type->status_len );
+-}
++ int off = 0;
+
+-void
+-send_ravenshield_request_packet( struct qserver *server)
+-{
+- send_packet( server, server->type->status_packet, server->type->status_len );
+-}
++ /* ignore duplicate packets */
++ if (server->server_name != NULL)
++ {
++ return 0;
++ }
+
+-void
+-send_savage_request_packet( struct qserver *server)
+-{
+- int len;
+- char* pkt;
++ server->address = strdup((char*) &pkt->data[off]);
++ off += strlen(server->address) + 1;
++ if (off >= datalen)
++ {
++ return -1;
++ }
+
+- if ( get_player_info )
++ server->server_name = strdup((char*) &pkt->data[off]);
++ off += strlen(server->server_name) + 1;
++ if (off >= datalen)
+ {
+- pkt = server->type->player_packet;
+- len = server->type->player_len;
++ return -1;
+ }
+- else
++
++ server->map_name = strdup((char*) &pkt->data[off]);
++ off += strlen(server->map_name) + 1;
++ if (off > datalen)
+ {
+- pkt = server->type->status_packet;
+- len = server->type->status_len;
++ return -1;
+ }
+
+- send_packet( server, pkt, len );
+-}
++ server->num_players = pkt->data[off++];
++ server->max_players = pkt->data[off++];
++ server->protocol_version = pkt->data[off++];
+
+-void
+-send_farcry_request_packet( struct qserver *server)
+-{
+- int len;
+- char* pkt;
++ server->retry1 = n_retries;
+
+- if ( get_player_info )
++ if (get_server_rules)
+ {
+- pkt = server->type->player_packet;
+- len = server->type->player_len;
++ send_rule_request_packet(server);
+ }
+- else
++ if (get_player_info)
+ {
+- pkt = server->type->status_packet;
+- len = server->type->status_len;
++ send_player_request_packet(server);
+ }
+
+- send_packet( server, pkt, len );
++ return 0;
+ }
+
+-void
+-send_tribes2master_request_packet( struct qserver *server)
++int player_info_packet(struct qserver *server, struct q_packet *pkt, int datalen)
+ {
+- int rc;
+- unsigned char packet[1600], *pkt;
+- unsigned int len, min_players, max_players, region_mask=0;
+- unsigned int build_version, max_bots, min_cpu, status;
+- char *game, *mission, *buddies;
+- static char *region_list[]= { "naeast", "nawest", "sa", "aus", "asia", "eur", NULL };
+- static char *status_list[]= { "dedicated", "nopassword", "linux" };
+-
+- if ( strcmp( get_param_value( server, "query", ""), "types") == 0) {
+- rc= send( server->fd, tribes2_game_types_request,
+- sizeof(tribes2_game_types_request), 0);
+- goto send_done;
+- }
+-
+- memcpy( packet, server->type->master_packet, server->type->master_len);
++ char *name, *address;
++ int off, colors, frags, connect_time, player_number;
++ struct player *player, *last;
++
++ off = 0;
++ player_number = pkt->data[off++];
++ name = (char*) &pkt->data[off];
++ off += strlen(name) + 1;
++ if (off >= datalen)
++ {
++ return -1;
++ }
+
+- pkt= packet + 7;
++ colors = pkt->data[off + 3];
++ colors = (colors << 8) + pkt->data[off + 2];
++ colors = (colors << 8) + pkt->data[off + 1];
++ colors = (colors << 8) + pkt->data[off];
++ off += sizeof(colors);
++
++ frags = pkt->data[off + 3];
++ frags = (frags << 8) + pkt->data[off + 2];
++ frags = (frags << 8) + pkt->data[off + 1];
++ frags = (frags << 8) + pkt->data[off];
++ off += sizeof(frags);
++
++ connect_time = pkt->data[off + 3];
++ connect_time = (connect_time << 8) + pkt->data[off + 2];
++ connect_time = (connect_time << 8) + pkt->data[off + 1];
++ connect_time = (connect_time << 8) + pkt->data[off];
++ off += sizeof(connect_time);
++
++ address = (char*) &pkt->data[off];
++ off += strlen(address) + 1;
++ if (off > datalen)
++ {
++ return -1;
++ }
+
+- game= get_param_value( server, "game", "any");
+- len= strlen(game);
+- if ( len > 255) len= 255;
+- *pkt++= len;
+- memcpy( pkt, game, len);
+- pkt+= len;
+-
+- mission= get_param_value( server, "mission", "any");
+- len= strlen(mission);
+- if ( len > 255) len= 255;
+- *pkt++= len;
+- memcpy( pkt, mission, len);
+- pkt+= len;
+-
+- min_players= get_param_ui_value( server, "minplayers", 0);
+- max_players= get_param_ui_value( server, "maxplayers", 255);
+- *pkt++= min_players;
+- *pkt++= max_players;
+-
+- region_mask= get_param_ui_value( server, "regions", 0xffffffff);
+- if ( region_mask == 0) {
+- char *regions= get_param_value( server, "regions", "");
+- char *r= regions;
+- char **list, *sep;
+- do {
+- sep= strchr( r, ':');
+- if ( sep)
+- len= sep-r;
+- else
+- len= strlen( r);
+- for ( list= region_list; *list; list++)
+- if ( strncasecmp( r, *list, len) == 0)
+- break;
+- if ( *list)
+- region_mask|= 1<<(list-region_list);
+- r= sep+1;
+- } while ( sep);
+- }
+- if ( little_endian)
+- memcpy( pkt, ®ion_mask, 4);
+- else {
+- pkt[0]= region_mask&0xff;
+- pkt[1]= (region_mask>>8)&0xff;
+- pkt[2]= (region_mask>>16)&0xff;
+- pkt[3]= (region_mask>>24)&0xff;
+- }
+- pkt+= 4;
++ last = server->players;
++ while (last != NULL && last->next != NULL)
++ {
++ if (last->number == player_number)
++ {
++ return 0;
++ }
++ last = last->next;
++ }
+
+- build_version= get_param_ui_value( server, "build", 0);
+-/*
+- if ( build_version && build_version < 22337) {
+- packet[1]= 0;
+- build_version= 0;
+- }
+-*/
+- if ( little_endian)
+- memcpy( pkt, &build_version, 4);
+- else {
+- pkt[0]= build_version&0xff;
+- pkt[1]= (build_version>>8)&0xff;
+- pkt[2]= (build_version>>16)&0xff;
+- pkt[3]= (build_version>>24)&0xff;
+- }
+- pkt+= 4;
++ if (last != NULL && last->number == player_number)
++ {
++ return 0;
++ }
+
+- status= get_param_ui_value( server, "status", -1);
+- if ( status == 0) {
+- char *flags= get_param_value( server, "status", "");
+- char *r= flags;
+- char **list, *sep;
+- do {
+- sep= strchr( r, ':');
+- if ( sep)
+- len= sep-r;
+- else
+- len= strlen( r);
+- for ( list= status_list; *list; list++)
+- if ( strncasecmp( r, *list, len) == 0)
+- break;
+- if ( *list)
+- status|= 1<<(list-status_list);
+- r= sep+1;
+- } while ( sep);
+- }
+- else if ( status == -1)
+- status= 0;
+- *pkt++= status;
+-
+- max_bots= get_param_ui_value( server, "maxbots", 255);
+- *pkt++= max_bots;
+-
+- min_cpu= get_param_ui_value( server, "mincpu", 0);
+- if ( little_endian)
+- memcpy( pkt, &min_cpu, 2);
+- else {
+- pkt[0]= min_cpu&0xff;
+- pkt[1]= (min_cpu>>8)&0xff;
+- }
+- pkt+= 2;
++ player = (struct player*)calloc(1, sizeof(struct player));
++ player->number = player_number;
++ player->name = strdup(name);
++ player->address = strdup(address);
++ player->connect_time = connect_time;
++ player->frags = frags;
++ player->shirt_color = colors >> 4;
++ player->pants_color = colors &0xf;
++ player->next = NULL;
+
+- buddies= get_param_value( server, "buddies", NULL);
+- if ( buddies) {
+- char *b= buddies, *sep;
+- unsigned int buddy, n_buddies= 0;
+- unsigned char *n_loc= pkt++;
+- do {
+- sep= strchr( b, ':');
+- if ( sscanf( b, "%u", &buddy)) {
+- n_buddies++;
+- if ( little_endian)
+- memcpy( pkt, &buddy, 4);
+- else {
+- pkt[0]= buddy&0xff;
+- pkt[1]= (buddy>>8)&0xff;
+- pkt[2]= (buddy>>16)&0xff;
+- pkt[3]= (buddy>>24)&0xff;
+- }
+- pkt+= 4;
+- }
+- b= sep+1;
+- } while ( sep && n_buddies < 255);
+- *n_loc= n_buddies;
+- }
+- else
+- *pkt++= 0;
++ if (last == NULL)
++ {
++ server->players = player;
++ }
++ else
++ {
++ last->next = player;
++ }
+
+- rc= send( server->fd, (char*)packet, pkt-packet, 0);
++ server->next_player_info++;
++ server->retry2 = n_retries;
++ if (server->next_player_info < server->num_players)
++ {
++ send_player_request_packet(server);
++ }
+
+-send_done:
+- if ( rc == SOCKET_ERROR)
+- perror( "send");
+- if ( server->retry1 == n_retries) {
+- gettimeofday( &server->packet_time1, NULL);
+- server->n_requests++;
+- }
+- else
+- server->n_retries++;
+- server->retry1--;
+- server->n_packets++;
+-}
+-
+-static struct _gamespy_query_map {
+- char *qstat_type;
+- char *gamespy_type;
+-} gamespy_query_map[] = {
+- { "qws", "quakeworld" },
+- { "q2s", "quake2" },
+- { "q3s", "quake3" },
+- { "tbs", "tribes" },
+- { "uns", "ut" },
+- { "sgs", "shogo" },
+- { "hls", "halflife" },
+- { "kps", "kingpin" },
+- { "hrs", "heretic2" },
+- { "sfs", "sofretail" },
+- { NULL, NULL }
+-};
++ return 0;
++}
+
+-void
+-send_gamespy_master_request( struct qserver *server)
++int rule_info_packet(struct qserver *server, struct q_packet *pkt, int datalen)
+ {
+- int rc, i;
+- char request[1024];
++ int off = 0;
++ struct rule *rule, *last;
++ char *name, *value;
+
+- if ( server->n_packets)
+- return;
+-
+- rc= send( server->fd, server->type->master_packet,
+- server->type->master_len, 0);
+- if ( rc != server->type->master_len)
+- perror( "send");
+-
+- strcpy( request, server->type->status_packet);
+-
+- for ( i= 0; gamespy_query_map[i].qstat_type; i++)
+- if ( strcasecmp( server->query_arg, gamespy_query_map[i].qstat_type) == 0)
+- break;
+- if ( gamespy_query_map[i].gamespy_type == NULL)
+- strcat( request, server->query_arg);
+- else
+- strcat( request, gamespy_query_map[i].gamespy_type);
+- strcat(request, "\\final\\");
+- assert(strlen(request) < sizeof(request));
+-
+- rc= send( server->fd, request, strlen( request), 0);
+- if ( rc != strlen( request))
+- perror( "send");
++ /* Straggler packet after we've already given up fetching rules */
++ if (server->next_rule == NULL)
++ {
++ return 0;
++ }
+
+- if ( server->retry1 == n_retries) {
+- gettimeofday( &server->packet_time1, NULL);
+- server->n_requests++;
+- }
+- server->n_packets++;
+-}
++ if (ntohs(pkt->length) == Q_HEADER_LEN)
++ {
++ server->next_rule = NULL;
++ return 0;
++ }
+
+-void
+-send_rule_request_packet( struct qserver *server)
+-{
+- int rc, len;
++ name = (char*) &pkt->data[off];
++ off += strlen(name) + 1;
++ if (off >= datalen)
++ {
++ return -1;
++ }
+
+- debug(3, "%p", server);
++ value = (char*) &pkt->data[off];
++ off += strlen(value) + 1;
++ if (off > datalen)
++ {
++ return -1;
++ }
+
+- /* Server created via broadcast, so bind it */
+- if ( server->fd == -1) {
+- if ( bind_qserver( server) < 0)
+- goto setup_retry;
+- }
++ last = server->rules;
++ while (last != NULL && last->next != NULL)
++ {
++ if (strcmp(last->name, name) == 0)
++ {
++ return 0;
++ }
++ last = last->next;
++ }
++ if (last != NULL && strcmp(last->name, name) == 0)
++ {
++ return 0;
++ }
+
+- if(server->type->rule_query_func && server->type->rule_query_func != send_rule_request_packet)
+- {
+- server->type->rule_query_func(server);
+- return;
+- }
++ rule = (struct rule*)malloc(sizeof(struct rule));
++ rule->name = strdup(name);
++ rule->value = strdup(value);
++ rule->next = NULL;
+
+- if ( server->type->id == Q_SERVER) {
+- strcpy( (char*)q_rule.data, server->next_rule);
+- len= Q_HEADER_LEN + strlen((char*)q_rule.data) + 1;
+- q_rule.length= htons( (short)len);
+- }
+- else
+- len= server->type->rule_len;
++ if (last == NULL)
++ {
++ server->rules = rule;
++ }
++ else
++ {
++ last->next = rule;
++ }
+
+- rc= send( server->fd, (const char *) server->type->rule_packet, len, 0);
+- if ( rc == SOCKET_ERROR)
+- perror( "send");
++ server->n_rules++;
++ server->next_rule = rule->name;
++ server->retry1 = n_retries;
++ send_rule_request_packet(server);
+
+-setup_retry:
+- if ( server->retry1 == n_retries) {
+- gettimeofday( &server->packet_time1, NULL);
+- server->n_requests++;
+- }
+- else if ( server->server_name == NULL)
+- server->n_retries++;
+- server->retry1--;
+- if ( server->server_name == NULL)
+- server->n_packets++;
++ return 0;
+ }
+
+-void
+-send_player_request_packet( struct qserver *server)
++struct info *player_add_info(struct player *player, char *key, char *value, int flags)
+ {
+- int rc;
+-
+- if(!server->type->player_packet)
+- return;
++ struct info *info;
++ if ( flags & OVERWITE_DUPLICATES )
++ {
++ for (info = player->info; info; info = info->next)
++ {
++ if (0 == strcmp(info->name, key))
++ {
++ // We should be able to free this
++ free(info->value);
++ if ( flags & NO_VALUE_COPY )
++ {
++ info->value = value;
++ }
++ else
++ {
++ info->value = strdup(value);
++ }
+
+- /* Server created via broadcast, so bind it */
+- if ( server->fd == -1) {
+- if ( bind_qserver( server) < 0)
+- goto setup_retry;
+- }
++ return info;
++ }
++ }
++ }
+
+- if(server->type->player_query_func && server->type->player_query_func != send_player_request_packet)
+- {
+- server->type->player_query_func(server);
+- return;
+- }
++ if ( flags & CHECK_DUPLICATE_RULES )
++ {
++ for (info = player->info; info; info = info->next)
++ {
++ if (0 == strcmp(info->name, key))
++ {
++ return NULL;
++ }
++ }
++ }
+
+- if(!server->type->player_packet)
+- {
+- debug(0, "error: server %p has no player_packet", server);
+- return;
+- }
++ if ( flags & COMBINE_VALUES )
++ {
++ for (info = player->info; info; info = info->next)
++ {
++ if (0 == strcmp(info->name, key))
++ {
++ char *full_value = (char*)calloc(sizeof(char), strlen(info->value) + strlen(value) + 2);
++ if (NULL == full_value)
++ {
++ fprintf(stderr, "Failed to malloc combined value\n");
++ exit(1);
++ }
++ sprintf(full_value, "%s%s%s", info->value, multi_delimiter, value);
+
+- if ( server->type->id == Q_SERVER)
+- q_player.data[0]= server->next_player_info;
+- rc= send( server->fd, (const char *) server->type->player_packet,
+- server->type->player_len, 0);
+- if ( rc == SOCKET_ERROR)
+- perror( "send");
++ // We should be able to free this
++ free(info->value);
++ info->value = full_value;
+
+-setup_retry:
+- if ( server->retry2 == n_retries) {
+- gettimeofday( &server->packet_time2, NULL);
+- server->n_requests++;
+- }
+- else
+- server->n_retries++;
+- server->retry2--;
+- server->n_packets++;
+-}
+
+-void qserver_disconnect(struct qserver* server)
+-{
+-#ifdef _WIN32
+- int i;
+-#endif
+- if ( server->fd != -1) {
+- close( server->fd);
+-#ifndef _WIN32
+- connmap[server->fd]= NULL;
+-#else
+- for ( i= 0; i < max_connmap; i++) {
+- if ( connmap[i] == server) {
+- connmap[i]= NULL;
+- break;
+- }
++ return info;
++ }
++ }
+ }
+-#endif
+- server->fd= -1;
+- connected--;
+- }
+-}
+
+-/* Functions for figuring timeouts and when to give up
+- * Returns 1 if the query is done (server may be freed) and 0 if not.
+- */
+-int
+-cleanup_qserver( struct qserver *server, int force )
+-{
+- int close_it = force;
+- if ( server->server_name == NULL)
++ info = (struct info*)malloc(sizeof(struct info));
++ if ( flags & NO_KEY_COPY )
++ {
++ info->name = key;
++ }
++ else
++ {
++ info->name = strdup(key);
++ }
++ if ( flags & NO_VALUE_COPY )
+ {
+- close_it = 1;
+- if ( server->type->id & MASTER_SERVER && server->master_pkt != NULL)
+- {
+- server->server_name = MASTER;
+- }
+- else
+- {
+- server->server_name = TIMEOUT;
+- num_servers_timed_out++;
+- }
++ info->value = value;
+ }
+- else if ( server->type->flags & TF_SINGLE_QUERY )
++ else
+ {
+- close_it = 1;
++ info->value = strdup(value);
+ }
+- else if (
+- server->next_rule == NO_SERVER_RULES &&
+- server->next_player_info >= server->num_players
+- )
++ info->next = NULL;
++
++ if (NULL == player->info)
+ {
+- close_it = 1;
++ player->info = info;
++ }
++ else
++ {
++ *player->last_info = info;
+ }
++ player->last_info = &info->next;
++ player->n_info++;
++
++ return info;
++}
+
+- debug( 3, "close_it %d", close_it );
+- if ( close_it )
++struct rule *add_rule(struct qserver *server, char *key, char *value, int flags)
++{
++ struct rule *rule;
++ if ( flags & OVERWITE_DUPLICATES )
+ {
+- if ( server->saved_data.data)
++ for (rule = server->rules; rule; rule = rule->next)
+ {
+- SavedData *sdata= server->saved_data.next;
+- free(server->saved_data.data);
+- server->saved_data.data= NULL;
+- while ( sdata != NULL)
++ if ( 0 == strcmp( rule->name, key) )
+ {
+- SavedData *next;
+- free(sdata->data);
+- next= sdata->next;
+- free(sdata);
+- sdata= next;
++ // We should be able to free this
++ free(rule->value);
++ if ( flags & NO_VALUE_COPY )
++ {
++ rule->value = value;
++ }
++ else
++ {
++ rule->value = strdup(value);
++ }
++
++ return rule;
+ }
+- server->saved_data.next= NULL;
+ }
++ }
+
+- qserver_disconnect(server);
+-
+- if ( server->server_name != TIMEOUT)
++ if ( flags & CHECK_DUPLICATE_RULES )
++ {
++ for (rule = server->rules; rule; rule = rule->next)
+ {
+- num_servers_returned++;
+- if ( server->server_name != DOWN)
++ if (0 == strcmp(rule->name, key))
+ {
+- num_players_total+= server->num_players;
+- max_players_total+= server->max_players;
++ return NULL;
+ }
+ }
+- if ( server->server_name == TIMEOUT || server->server_name == DOWN)
+- {
+- server->ping_total= 999999;
+- }
+- if ( server->type->master)
++ }
++
++ if ( flags & COMBINE_VALUES )
++ {
++ for (rule = server->rules; rule; rule = rule->next)
+ {
+- waiting_for_masters--;
+- if ( waiting_for_masters == 0)
++ if (0 == strcmp(rule->name, key))
+ {
+- add_servers_from_masters();
++ char *full_value = (char*)calloc(sizeof(char), strlen(rule->value) + strlen(value) + strlen(multi_delimiter) + 1);
++ if (NULL == full_value)
++ {
++ fprintf(stderr, "Failed to malloc combined value\n");
++ exit(1);
++ }
++ sprintf(full_value, "%s%s%s", rule->value, multi_delimiter, value);
++
++ // We should be able to free this
++ free(rule->value);
++ rule->value = full_value;
++
++ return rule;
+ }
+ }
+- if ( ! server_sort)
+- {
+- display_server( server);
+- }
+- return 1;
+ }
+- return 0;
+-}
+-
+-/** must be called only on connected servers
+- * @returns time in ms until server needs timeout handling. timeout handling is needed if <= zero
+- */
+-static int qserver_get_timeout(struct qserver* server, struct timeval* now)
+-{
+- int diff, diff1, diff2, interval;
+
+- if ( server->type->id & MASTER_SERVER)
+- interval= master_retry_interval;
++ rule = (struct rule*)malloc(sizeof(struct rule));
++ if ( flags & NO_KEY_COPY )
++ {
++ rule->name = key;
++ }
+ else
+- interval= retry_interval;
+-
+- diff2= 0xffff;
+-
+- diff1= interval*(n_retries-server->retry1+1) -
+- time_delta( now, &server->packet_time1);
+-
+- if (server->next_player_info < server->num_players)
+ {
+- diff2= interval*(n_retries-server->retry2+1) -
+- time_delta( now, &server->packet_time2);
++ rule->name = strdup(key);
+ }
+
+- debug(2, "timeout for %p is diff1 %d diff2 %d", server, diff1, diff2);
+-
+-
+- diff= (diff1<diff2)?diff1:diff2;
++ if ( flags &NO_VALUE_COPY )
++ {
++ rule->value = value;
++ }
++ else
++ {
++ rule->value = strdup(value);
++ }
++ rule->next = NULL;
++ *server->last_rule = rule;
++ server->last_rule = &rule->next;
++ server->n_rules++;
+
+- return diff;
++ return rule;
+ }
+
+-void
+-get_next_timeout( struct timeval *timeout)
++void add_nrule(struct qserver *server, char *key, char *value, int len)
+ {
+- struct qserver *server= servers;
+- struct timeval now;
+- int diff, smallest= retry_interval+master_retry_interval;
+- int bind_count= 0;
+-
+- if ( first_server_bind == NULL)
+- first_server_bind= servers;
+-
+- server= first_server_bind;
+-
+- for ( ; server != NULL && server->fd == -1; server= server->next)
+- ;
+-
+- /* if there are unconnected servers and slots left we retry in 10ms */
+- if ( server == NULL || (num_servers > connected && connected < max_simultaneous)) {
+- timeout->tv_sec= 0;
+- timeout->tv_usec= 10 * 1000;
+- return;
+- }
+- first_server_bind= server;
+-
+- gettimeofday( &now, NULL);
+- for ( ; server != NULL && bind_count < connected; server= server->next) {
+- if ( server->fd == -1)
+- continue;
+-
+- diff = qserver_get_timeout(server, &now);
+- if ( diff < smallest)
+- smallest= diff;
+- bind_count++;
+- }
+-
+- if ( smallest < 10)
+- smallest= 10;
++ struct rule *rule;
++ for (rule = server->rules; rule; rule = rule->next)
++ if (strcmp(rule->name, key) == 0)
++ {
++ return ;
++ }
+
+- timeout->tv_sec= smallest / 1000;
+- timeout->tv_usec= (smallest % 1000) * 1000;
++ rule = (struct rule*)malloc(sizeof(struct rule));
++ rule->name = strdup(key);
++ rule->value = strndup(value, len);
++ rule->next = NULL;
++ *server->last_rule = rule;
++ server->last_rule = &rule->next;
++ server->n_rules++;
+ }
+
+-
+-#ifdef USE_SELECT
+-static fd_set select_read_fds;
+-static int select_maxfd;
+-static int select_cursor;
+-
+-int
+-set_fds( fd_set *fds)
++struct player *add_player(struct qserver *server, int player_number)
+ {
+- int maxfd= -1, i;
++ struct player *player;
+
+- for ( i= 0; i < max_connmap; i++)
++ for (player = server->players; player; player = player->next)
+ {
+- if ( connmap[i] != NULL)
++ if (player->number == player_number)
+ {
+- FD_SET( connmap[i]->fd, fds);
+- if ( connmap[i]->fd > maxfd)
+- {
+- maxfd= connmap[i]->fd;
+- }
++ return NULL;
+ }
+ }
+
+- return maxfd;
+-}
+-
+-void
+-set_file_descriptors()
+-{
+- FD_ZERO( &select_read_fds);
+- select_maxfd= set_fds( &select_read_fds);
++ player = (struct player*)calloc(1, sizeof(struct player));
++ player->number = player_number;
++ player->next = server->players;
++ player->n_info = 0;
++ player->ping = NA_INT;
++ player->team = NA_INT;
++ player->score = NA_INT;
++ player->deaths = NA_INT;
++ player->frags = NA_INT;
++ player->last_info = NULL;
++ server->players = player;
++ server->n_player_info++;
++ return player;
+ }
+
+-int
+-wait_for_file_descriptors( struct timeval *timeout)
++STATIC struct player *get_player_by_number(struct qserver *server, int player_number)
+ {
+- select_cursor= 0;
+- return select( select_maxfd+1, &select_read_fds, NULL, NULL, timeout);
++ struct player *player;
++ for (player = server->players; player; player = player->next)
++ if (player->number == player_number)
++ {
++ return player;
++ } return NULL;
+ }
+
+-struct qserver *
+-get_next_ready_server()
++// Updates a servers port information.
++// Sets the rules:
++// _queryport <queryport>
++// hostport <port>
++void change_server_port(struct qserver *server, unsigned short port, int force)
+ {
+- while ( select_cursor < max_connmap &&
+- ( connmap[select_cursor] == NULL ||
+- ! FD_ISSET( connmap[select_cursor]->fd, &select_read_fds)) )
++ if (port > 0 && port != server->port)
+ {
+- select_cursor++;
+- }
+- if ( select_cursor >= max_connmap)
+- return NULL;
+- return connmap[select_cursor++];
+-}
++ // valid port and changing
++ char arg[64];
++ unsigned int ipaddr = ntohl(server->ipaddr);
+
+-int
+-wait_for_timeout( unsigned int ms)
+-{
+- struct timeval timeout;
+- timeout.tv_sec= ms/1000;
+- timeout.tv_usec= (ms%1000) * 1000;
+- return select( 0, 0, NULL, NULL, &timeout);
+-}
++ // Update the servers hostname as required
++ sprintf(arg, "%d.%d.%d.%d:%hu", ipaddr >> 24, (ipaddr >> 16) &0xff, (ipaddr >> 8) &0xff, ipaddr &0xff, port);
+
+-#endif /* USE_SELECT */
++ if (show_game_port || force || server->flags &TF_SHOW_GAME_PORT)
++ {
++ // Update the server arg
++ free(server->arg);
++ server->arg = strdup(arg);
+
+-#ifdef USE_POLL
+-static struct pollfd *pollfds;
+-static int n_pollfds;
+-static int max_pollfds= 0;
+-static int poll_cursor;
++ // Add a rule noting the previous query port
++ sprintf(arg, "%hu", server->port);
++ add_rule(server, "_queryport", arg, NO_FLAGS);
+
+-void
+-set_file_descriptors()
+-{
+- struct pollfd *p;
+- int i;
++ // Update the servers port
++ server->port = port;
++ }
+
+- if ( max_connmap > max_pollfds) {
+- max_pollfds= max_connmap;
+- pollfds= (struct pollfd *) realloc( pollfds, max_pollfds *
+- sizeof(struct pollfd));
+- }
++ if ( 0 != strcmp(server->arg, server->host_name) )
++ {
++ // hostname isnt the query arg
++ char *colon = strchr(server->host_name, ':');
++ // dns hostname or hostname:port
++ char *hostname = malloc( strlen(server->host_name) + 7 );
++ if ( NULL == hostname )
++ {
++ fprintf(stderr, "Failed to malloc hostname memory\n");
++ }
++ else
++ {
++ if (colon)
++ {
++ *colon = '\0';
++ }
++ sprintf(hostname, "%s:%hu", server->host_name, port);
++ free(server->host_name);
++ server->host_name = hostname;
++ }
++ }
+
+- p= pollfds;
+- for ( i= 0; i < max_connmap; i++)
+- if ( connmap[i] != NULL) {
+- p->fd= connmap[i]->fd;
+- p->events= POLLIN;
+- p->revents= 0;
+- p++;
++ // Add a rule noting the servers hostport
++ sprintf(arg, "%hu", port);
++ add_rule(server, "hostport", arg, OVERWITE_DUPLICATES);
+ }
+- n_pollfds= p - pollfds;
+ }
+
+-int
+-wait_for_file_descriptors( struct timeval *timeout)
++STATIC void players_set_teamname(struct qserver *server, int teamid, char *teamname)
+ {
+- poll_cursor= 0;
+- return poll( pollfds, n_pollfds, timeout->tv_sec*1000 + timeout->tv_usec/1000);
++ struct player *player;
++ for (player = server->players; player; player = player->next)
++ {
++ if (player->team == teamid)
++ {
++ player->team_name = strdup(teamname);
++ }
++ }
+ }
+
+-struct qserver *
+-get_next_ready_server()
++STATIC char *dup_nstring(const char *pkt, const char *end, char **next)
+ {
+- for ( ; poll_cursor < n_pollfds; poll_cursor++)
++ char *pt = (char*)pkt;
++ int len = ((unsigned char*)pkt)[0];
++ pt++;
++ if (*pt == '\1')
+ {
+- if ( pollfds[ poll_cursor].revents)
+- break;
++ len++;
+ }
+- if ( poll_cursor >= n_pollfds)
++ if (pt + len > end)
++ {
+ return NULL;
+- return connmap[pollfds[poll_cursor++].fd];
+-}
+-
+-int
+-wait_for_timeout( unsigned int ms)
+-{
+- return poll( 0, 0, ms);
+-}
+-
+-#endif /* USE_POLL */
+-
+-
+-void
+-free_server( struct qserver *server)
+-{
+- struct player *player, *next_player;
+- struct rule *rule, *next_rule;
+-
+- /* remove from servers list */
+- if ( server == servers) {
+- servers= server->next;
+- if ( servers)
+- servers->prev= NULL;
+- }
+- if ( (void*) &server->next == (void*) last_server) {
+- if ( server->prev)
+- last_server= & server->prev->next;
+- else
+- last_server= & servers;
+- }
+- if ( server == first_server_bind)
+- first_server_bind= server->next;
+- if ( server == last_server_bind)
+- last_server_bind= server->next;
+-
+- if ( server->prev)
+- server->prev->next= server->next;
+- if ( server->next)
+- server->next->prev= server->prev;
+-
+- /* remove from server hash table */
+- remove_server_from_hash( server);
+-
+- /* free all the data */
+- for ( player= server->players; player; player= next_player) {
+- next_player= player->next;
+- free_player( player);
+- }
+-
+- for ( rule= server->rules; rule; rule= next_rule) {
+- next_rule= rule->next;
+- free_rule( rule);
+- }
+-
+- if ( server->arg) free( server->arg);
+- if ( server->host_name) free( server->host_name);
+- if ( server->error) free( server->error);
+- if ( server->address) free( server->address);
+- if ( server->map_name) free( server->map_name);
+- if ( !(server->flags&FLAG_DO_NOT_FREE_GAME) && server->game)
+- free(server->game);
+- if ( server->master_pkt) free( server->master_pkt);
+-
+- free(server->query_arg);
+-
+- /* These fields are never malloc'd: outfilename
+- */
+-
+- if ( server->server_name != NULL &&
+- server->server_name != DOWN &&
+- server->server_name != HOSTNOTFOUND &&
+- server->server_name != SYSERROR &&
+- server->server_name != MASTER &&
+- server->server_name != SERVERERROR &&
+- server->server_name != TIMEOUT &&
+- server->server_name != GAMESPY_MASTER_NAME &&
+- server->server_name != BFRIS_SERVER_NAME) {
+- free( server->server_name);
+- }
+-
+-/*
+-params ...
+-saved_data ...
+-*/
+-
+- free( server);
+- --num_servers;
+-}
+-
+-void
+-free_player( struct player *player)
+-{
+- if ( player->name) free( player->name);
+- if ( ! (player->flags&PLAYER_FLAG_DO_NOT_FREE_TEAM) && player->team_name)
+- free( player->team_name);
+- if ( player->address) free( player->address);
+- if ( player->tribe_tag) free( player->tribe_tag);
+- if ( player->skin) free( player->skin);
+- if ( player->mesh) free( player->mesh);
+- if ( player->face) free( player->face);
+- free( player);
+-}
+-
+-void
+-free_rule( struct rule *rule)
+-{
+- if ( rule->name) free( rule->name);
+- if ( rule->value) free( rule->value);
+- free( rule);
+-}
+-
+-/* Functions for handling response packets
+- */
+-
+-/* Packet from normal Quake server
+- */
+-void
+-deal_with_q_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
+- struct q_packet *pkt= (struct q_packet *)rawpkt;
+- int rc;
+-
+- if ( ntohs( pkt->length) != pktlen) {
+- fprintf( stderr, "%s Ignoring bogus packet; length %d != %d\n",
+- server->arg, ntohs( pkt->length), pktlen);
+- cleanup_qserver(server,FORCE);
+- return;
+- }
+-
+- rawpkt[pktlen]= '\0';
+-
+- switch ( pkt->op_code) {
+- case Q_CCREP_ACCEPT:
+- case Q_CCREP_REJECT:
+- return;
+- case Q_CCREP_SERVER_INFO:
+- server->ping_total+= time_delta( &packet_recv_time,
+- &server->packet_time1);
+- rc= server_info_packet( server, pkt, pktlen-Q_HEADER_LEN);
+- break;
+- case Q_CCREP_PLAYER_INFO:
+- server->ping_total+= time_delta( &packet_recv_time,
+- &server->packet_time2);
+- rc= player_info_packet( server, pkt, pktlen-Q_HEADER_LEN);
+- break;
+- case Q_CCREP_RULE_INFO:
+- server->ping_total+= time_delta( &packet_recv_time,
+- &server->packet_time1);
+- rc= rule_info_packet( server, pkt, pktlen-Q_HEADER_LEN);
+- break;
+- case Q_CCREQ_CONNECT:
+- case Q_CCREQ_SERVER_INFO:
+- case Q_CCREQ_PLAYER_INFO:
+- case Q_CCREQ_RULE_INFO:
+- default:
+- return;
+- }
+-
+- if ( rc == -1)
+- fprintf( stderr, "%s error on packet opcode %x\n", server->arg,
+- (int)pkt->op_code);
++ }
+
+- cleanup_qserver( server, (rc == -1) ? FORCE : 0);
++ *next = pt + len;
++ return strndup(pt, len);
+ }
+
+-/* Packet from QuakeWorld server
+- */
+-void
+-deal_with_qw_packet( struct qserver *server, char *rawpkt, int pktlen)
++STATIC char *dup_n1string(char *pkt, char *end, char **next)
+ {
+- if ( server->server_name == NULL)
+- server->ping_total+= time_delta( &packet_recv_time,
+- &server->packet_time1);
+-
+- if ( ((rawpkt[0] != '\377' && rawpkt[0] != '\376') || rawpkt[1] != '\377' ||
+- rawpkt[2] != '\377' || rawpkt[3] != '\377') && show_errors) {
+- unsigned int ipaddr= ntohl(server->ipaddr);
+- fprintf( stderr,
+- "Odd packet from server %d.%d.%d.%d:%hu, processing ...\n",
+- (ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+- (ipaddr>>8)&0xff, ipaddr&0xff, ntohs(server->port));
+- print_packet( server, rawpkt, pktlen);
+- }
++ unsigned len;
+
+- rawpkt[pktlen]= '\0';
+-
+- if ( rawpkt[4] == 'n') {
+- if ( server->type->id != QW_SERVER)
+- server->type= find_server_type_id( QW_SERVER);
+- deal_with_q1qw_packet( server, rawpkt, pktlen);
+- return;
+- }
+- else if ( rawpkt[4] == '\377' && rawpkt[5] == 'n') {
+- if ( server->type->id != HW_SERVER)
+- server->type= find_server_type_id( HW_SERVER);
+- deal_with_q1qw_packet( server, rawpkt, pktlen);
+- return;
+- }
+- else if ( strncmp( &rawpkt[4], "print\n\\", 7) == 0) {
+- deal_with_q2_packet( server, rawpkt+10, pktlen-10, 0);
+- return;
+- }
+- else if ( strncmp( &rawpkt[4], "print\n", 6) == 0) {
+- /* work-around for occasional bug in Quake II status packets
+- */
+- char *c, *p;
+- p= c= &rawpkt[10];
+- while ( *p != '\\' && (c= strchr( p, '\n')))
+- p= c+1;
+- if ( *p == '\\' && c != NULL) {
+- deal_with_q2_packet( server, p, pktlen-(p-rawpkt), 0);
+- return;
++ if (!pkt || pkt >= end)
++ {
++ return NULL;
+ }
+- }
+- else if ( strncmp( &rawpkt[4], "infoResponse", 12) == 0 ||
+- (rawpkt[4] == '\001' && strncmp( &rawpkt[5], "infoResponse", 12) == 0) ) {
+- /* quake3 info response */
+- if ( rawpkt[4] == '\001') {
+- rawpkt++;
+- pktlen--;
+- }
+- rawpkt+= 12;
+- pktlen-= 12;
+- for ( ; pktlen && *rawpkt != '\\'; pktlen--, rawpkt++)
+- ;
+- if ( !pktlen)
+- return;
+- if ( rawpkt[pktlen-1] == '"') {
+- rawpkt[pktlen-1]= '\0';
+- pktlen--;
+- }
+- if ( get_player_info || get_server_rules)
+- server->next_rule= "";
+- deal_with_q2_packet( server, rawpkt, pktlen, 0);
+- if ( (get_player_info || get_server_rules) &&
+- ( server->flags & FLAG_BROADCAST || server->fd != -1)) {
+- send_rule_request_packet( server);
+- server->retry1= n_retries-1;
++
++ len = (unsigned char)pkt[0] - 1;
++ pkt++;
++ if (pkt + len > end)
++ {
++ return NULL;
+ }
+- return;
+- }
+- else if ( strncmp( &rawpkt[4], "statusResponse\n", 15) == 0 ||
+- (rawpkt[4] == '\001' && strncmp( &rawpkt[5], "statusResponse\n", 15) == 0) ) {
+- /* quake3 status response */
+- server->next_rule= NO_SERVER_RULES;
+- server->retry1 = 0;
+- if ( rawpkt[4] == '\001') {
+- rawpkt++;
+- pktlen--;
+- }
+- deal_with_q2_packet( server, rawpkt + 19, pktlen - 19,
+- CHECK_DUPLICATE_RULES);
+- return;
+- }
+- else if ( strncmp( &rawpkt[4], "infostringresponse", 19) == 0) {
+- deal_with_q2_packet( server, rawpkt+23, pktlen-23, 0);
+- return;
+- }
+
+- if ( show_errors) {
+- unsigned int ipaddr= ntohl(server->ipaddr);
+- fprintf( stderr,
+- "Odd packet from server %d.%d.%d.%d:%hu, ignoring ...\n",
+- (ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+- (ipaddr>>8)&0xff, ipaddr&0xff, ntohs(server->port));
+- print_packet( server, rawpkt, pktlen);
+- cleanup_qserver( server, 1);
+- }
+- else
+- cleanup_qserver( server, 0);
++ *next = pkt + len;
++ return strndup(pkt, len);
+ }
+
+-void
+-deal_with_q1qw_packet( struct qserver *server, char *rawpkt, int pktlen)
++STATIC int pariah_basic_packet(struct qserver *server, char *rawpkt, char *end)
+ {
+- char *key, *value, *end;
+- struct player *player= NULL, **last_player= &server->players;
+- int len, rc, complete= 0;
+- int number, frags, connect_time, ping;
+- char *pkt= &rawpkt[5];
+-
+- if ( server->type->id == HW_SERVER)
+- pkt= &rawpkt[6];
+-
+- while ( *pkt && pkt-rawpkt < pktlen) {
+- if ( *pkt == '\\') {
+- pkt++;
+- end= strchr( pkt, '\\');
+- if ( end == NULL)
+- break;
+- *end= '\0';
+- key= pkt;
+- pkt+= strlen(pkt)+1;
+- end= strchr( pkt, '\\');
+- if ( end == NULL)
+- end= strchr( pkt, '\n');
+- value= (char*) malloc(end-pkt+1);
+- memcpy( value, pkt, end-pkt);
+- value[end-pkt]= '\0';
+- pkt= end;
+- if ( strcmp( key, "hostname") == 0)
+- server->server_name= value;
+- else if ( strcmp( key, "map") == 0)
+- server->map_name= value;
+- else if ( strcmp( key, "maxclients") == 0) {
+- server->max_players= atoi(value);
+- free( value);
+- }
+- else if ( get_server_rules || strncmp( key, "*game", 5) == 0) {
+- add_rule( server, key, value, NO_VALUE_COPY);
+- if ( strcmp( key, "*gamedir") == 0) {
+- server->game= value;
+- server->flags |= FLAG_DO_NOT_FREE_GAME;
+- }
+- }
+- }
+- else if ( *pkt == '\n') {
+- pkt++;
+- if ( pkt-rawpkt>=pktlen || *pkt == '\0')
+- break;
+- rc= sscanf( pkt, "%d %d %d %d %n", &number, &frags, &connect_time,
+- &ping, &len);
+- if ( rc != 4) {
+- char *nl; /* assume it's an error packet */
+- server->error= (char*)malloc( pktlen+1);
+- nl= strchr( pkt, '\n');
+- if ( nl != NULL) {
+- strncpy( server->error, pkt, nl-pkt);
+- server->error[nl-pkt]= '\0';
+- }
+- else
+- strcpy( server->error, pkt);
+- server->server_name= SERVERERROR;
+- complete= 1;
+- break;
+- }
+- if ( get_player_info) {
+- player= (struct player *) calloc( 1, sizeof( struct player));
+- player->number= number;
+- player->frags= frags;
+- player->connect_time= connect_time * 60;
+- player->ping= ping;
+- }
+- else
+- player= NULL;
+-
+- pkt+= len;
+-
+- if ( *pkt != '"') break;
+- pkt++;
+- end= strchr( pkt, '"');
+- if ( end == NULL) break;
+- if ( player != NULL) {
+- player->name= (char*) malloc(end-pkt+1);
+- memcpy( player->name, pkt, end-pkt);
+- player->name[end-pkt]= '\0';
+- }
+- pkt= end+2;
+-
+- if ( *pkt != '"') break;
+- pkt++;
+- end= strchr( pkt, '"');
+- if ( end == NULL) break;
+- if ( player != NULL) {
+- player->skin= (char*) malloc(end-pkt+1);
+- memcpy( player->skin, pkt, end-pkt);
+- player->skin[end-pkt]= '\0';
+- }
+- pkt= end+2;
+-
+- if ( player != NULL) {
+- sscanf( pkt, "%d %d%n", &player->shirt_color,
+- &player->pants_color, &len);
+- *last_player= player;
+- last_player= & player->next;
+- }
+- else
+- sscanf( pkt, "%*d %*d%n", &len);
+- pkt+= len;
+-
+- server->num_players++;
+- }
+- else
+- pkt++;
+- complete= 1;
+- }
+-
+- if ( !complete) {
+- if ( rawpkt[4] != 'n' || rawpkt[5] != '\0') {
+- fprintf( stderr,
+- "Odd packet from QW server %d.%d.%d.%d:%hu ...\n",
+- (server->ipaddr>>24)&0xff, (server->ipaddr>>16)&0xff,
+- (server->ipaddr>>8)&0xff, server->ipaddr&0xff,
+- ntohs(server->port));
+- print_packet( server, rawpkt, pktlen);
++ char *next;
++ char *string;
++ change_server_port(server, swap_short_from_little(&rawpkt[14]), 0);
++ if (NULL == (string = ut2003_strdup(&rawpkt[18], end, &next)))
++ {
++ return -1;
+ }
+- }
+- else if ( server->server_name == NULL)
+- server->server_name= strdup("");
+-
+- cleanup_qserver( server, 0);
+-}
+
+-void
+-deal_with_q2_packet( struct qserver *server, char *rawpkt, int pktlen,
+- int check_duplicate_rules)
+-{
+- char *key, *value, *end;
+- struct player *player= NULL;
+- struct player **last_player= & server->players;
+- int len, rc, complete= 0;
+- int frags=0, ping=0, num_players= 0;
+- char *pkt= rawpkt;
+-
+- while ( *pkt && pkt-rawpkt < pktlen) {
+- if ( *pkt == '\\') {
+- pkt++;
+- if ( *pkt == '\n' && server->type->id == SOF_SERVER)
+- goto player_info;
+- end= strchr( pkt, '\\');
+- if ( end == NULL)
+- break;
+- *end= '\0';
+- key= pkt;
+- pkt+= strlen(pkt)+1;
+- end= strpbrk( pkt, "\\\n");
+- if(!end) {
+- end= rawpkt+pktlen;
+- }
+- value= (char*) malloc(end-pkt+1);
+- memcpy( value, pkt, end-pkt);
+- value[end-pkt]= '\0';
+- pkt= end;
+- debug(3, "%s = %s", key, value);
+- if ( server->server_name == NULL &&
+- (strcmp( key, "hostname") == 0 ||
+- strcmp( key, "sv_hostname") == 0))
+- server->server_name= value;
+- else if ( strcmp( key, "mapname") == 0 ||
+- (strcmp( key, "map") == 0 && server->map_name == NULL)) {
+- if ( server->map_name != NULL)
+- free( server->map_name);
+- server->map_name= value;
+- }
+- else if ( strcmp( key, "maxclients") == 0 ||
+- strcmp( key, "sv_maxclients") == 0 ||
+- strcmp( key, "max") == 0) {
+- server->max_players= atoi(value);
+- /* MOHAA Q3 protocol max players is always 0 */
+- if ( server->max_players == 0)
+- server->max_players= -1;
+- free(value);
+- }
+- else if ( strcmp( key, "clients") == 0 ||
+- strcmp( key, "players") == 0) {
+- server->num_players= atoi(value);
+- free(value);
+- }
+- else if ( server->server_name == NULL &&
+- strcmp( key, "pure") == 0) {
+- add_rule( server, key, value, NO_VALUE_COPY);
+- }
+- else if ( get_server_rules || strncmp( key, "game", 4) == 0)
+- {
+- if ( add_rule( server, key, value, NO_VALUE_COPY|check_duplicate_rules) == NULL)
+- {
+- free(value); /* duplicate, so free value */
+- }
+- if ( server->game == NULL && strcmp( key, server->type->game_rule) == 0)
+- {
+- server->game= value;
+- server->flags |= FLAG_DO_NOT_FREE_GAME;
+- }
+- }
+- else
+- free(value);
+- }
+- else if ( *pkt == '\n') {
+-player_info:
+- pkt++;
+- if ( *pkt == '\0')
+- break;
+- if(!strncmp(pkt, "\\challenge\\", 11))
+- {
+- // qfusion
+- // This doesnt support getstatus looking at warsow source:
+- // server/sv_main.c: SV_ConnectionlessPacket
+- server->next_rule = NO_SERVER_RULES;
+- break;
+- }
+- rc= sscanf( pkt, "%d %n", &frags, &len);
+- if ( rc == 1 && pkt[len] != '"') {
+- pkt+= len;
+- rc= sscanf( pkt, "%d %n", &ping, &len);
+- }
+- else if ( rc == 1) {
+- /* MOHAA Q3 protocol only provides player ping */
+- ping= frags;
+- frags= 0;
+- }
+- if ( rc != 1) {
+- char *nl; /* assume it's an error packet */
+- server->error= (char*)malloc( pktlen+1);
+- nl= strchr( pkt, '\n');
+- if ( nl != NULL)
+- strncpy( server->error, pkt, nl-pkt);
+- else
+- strcpy( server->error, pkt);
+- server->server_name= SERVERERROR;
+- complete= 1;
+- break;
+- }
+- if ( get_player_info) {
+- player= (struct player *) calloc( 1, sizeof( struct player));
+- player->number= 0;
+- player->connect_time= -1;
+- player->frags= frags;
+- player->ping= ping;
+- }
+- else
+- player= NULL;
+-
+- pkt+= len;
+-
+- if ( isdigit((unsigned char)*pkt)) {
+- /* probably an SOF2 1.01 server, includes team # */
+- int team;
+- rc= sscanf( pkt, "%d %n", &team, &len);
+- if ( rc == 1) {
+- pkt+= len;
+- if ( player) {
+- player->team= team;
+- server->flags|= FLAG_PLAYER_TEAMS;
+- }
+- }
+- }
+-
+- if ( *pkt != '"') break;
+-
+- pkt++;
+- end= strchr( pkt, '"');
+- if ( end == NULL) break;
+- if ( player != NULL) {
+- player->name= (char*) malloc(end-pkt+1);
+- memcpy( player->name, pkt, end-pkt);
+- player->name[end-pkt]= '\0';
+- }
+- pkt= end+1;
+-
+- //WarSoW team number
+- if ( *pkt != '\n') {
+- int team;
+- rc= sscanf( pkt, "%d%n", &team, &len);
+- if ( rc == 1) {
+- pkt+= len;
+- if ( player) {
+- player->team= team;
+- server->flags|= FLAG_PLAYER_TEAMS;
+- }
+- }
+- }
+-
+- if ( player != NULL) {
+- player->skin= NULL;
+- player->shirt_color= -1;
+- player->pants_color= -1;
+- *last_player= player;
+- last_player= & player->next;
+- }
+- num_players++;
++ if (server->server_name == NULL)
++ {
++ server->server_name = string;
+ }
+ else
+- pkt++;
+- complete= 1;
+- }
+-
+- if ( server->num_players == 0 || num_players > server->num_players)
+- server->num_players= num_players;
+-
+- if ( !complete) {
+- cleanup_qserver( server, 1);
+- return;
+- }
+- else if ( server->server_name == NULL)
+- server->server_name= strdup("");
+-
+- cleanup_qserver( server, 0);
+-}
+-
+-void
+-ack_descent3master_packet( struct qserver *server, char *curtok )
+-{
+- int rc;
+- char packet[0x1e];
+- memcpy( packet, descent3_masterquery,0x1a);
+- packet[1]= 0x1d;
+- packet[0x16]= 1;
+- memcpy( packet + 0x1a, curtok, 4);
+- rc= send( server->fd, packet, sizeof(packet), 0);
+- if ( rc == SOCKET_ERROR)
+- perror( "send");
+-}
+-
+-/* Packet from Descent3 master server (PXO)
+- */
+-void
+-deal_with_descent3master_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
+- int i= 0, lastpacket= 0;
+- char *names= rawpkt + 0x1f;
+- char *ips= rawpkt + 0x29f;
+- char *ports= rawpkt + 0x2ef;
+- /* printf ("s=%p p=%p l=%i\n",server,rawpkt,pktlen); */
+- while ( i<20) {
+- if ( *names) {
+- char *c;
+- server->master_pkt_len += 6;
+- server->master_pkt= (char*)realloc( server->master_pkt,
+- server->master_pkt_len);
+- c= server->master_pkt + server->master_pkt_len - 6;
+- memcpy( c, ips, 4);
+- memcpy( c + 4, ports, 2);
+- }else if (i>0)
+- lastpacket= 1;
+- names+= 0x20;
+- ips+= 4;
+- ports+= 2;
+- i++;
+- }
+-
+- ack_descent3master_packet( server, rawpkt+0x1a);
+-
+- server->n_servers= server->master_pkt_len / 6;
+-
+- server->next_player_info= -1;
+- server->retry1= 0;
+-
+- if (lastpacket) {
+- cleanup_qserver( server, 0);
+- }
+-}
+-
+-/* Packet from QuakeWorld master server
+- */
+-void
+-deal_with_qwmaster_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
+- server->ping_total+= time_delta( &packet_recv_time,
+- &server->packet_time1);
+-
+- if ( rawpkt[0] == QW_NACK)
+ {
+- server->error= strdup( &rawpkt[2]);
+- server->server_name= SERVERERROR;
+- cleanup_qserver( server, 1);
+- return;
++ free(string);
+ }
+
+- if ( *((unsigned int*)rawpkt) == 0xffffffff)
++ if (NULL == (string = ut2003_strdup(next, end, &next)))
+ {
+- rawpkt+= 4; /* QW 1.5 */
+- pktlen-= 4;
++ return -1;
+ }
+
+- if ( rawpkt[0] == QW_SERVERS && rawpkt[1] == QW_NEWLINE)
++ if (server->map_name == NULL)
+ {
+- rawpkt+= 2;
+- pktlen-= 2;
++ server->map_name = string;
+ }
+- else if ( rawpkt[0] == HL_SERVERS && rawpkt[1] == 0x0d )
++ else
+ {
+- // 2 byte id + 4 byte sequence
+- memcpy( server->master_query_tag, rawpkt+2, 3);
+- rawpkt+= 6;
+- pktlen-= 6;
++ free(string);
+ }
+- else if ( rawpkt[0] == HL_SERVERS && rawpkt[1] == 0x0a )
+- {
+- // no sequence id for steam
+- // instead we use the ip:port of the last recieved server
+- struct in_addr *sin_addr = (struct in_addr*)(rawpkt+pktlen-6);
+- char *ip = inet_ntoa( *sin_addr );
+- unsigned short port = htons( *((unsigned short*)(rawpkt+pktlen-2)) );
+-
+- //fprintf( stderr, "NEXT IP=%s:%u\n", ip, port );
+- sprintf( server->master_query_tag, "%s:%u", ip, port );
+
+- // skip over the 2 byte id
+- rawpkt+= 2;
+- pktlen-= 2;
++ if (NULL == (string = ut2003_strdup(next, end, &next)))
++ {
++ return -1;
+ }
+- else if ( strncmp( rawpkt, "servers", 7) == 0)
++
++ if (server->game == NULL)
+ {
+- rawpkt+= 8;
+- pktlen-= 8;
++ server->game = string;
++ add_rule(server, "gametype", server->game, NO_FLAGS | CHECK_DUPLICATE_RULES);
+ }
+- else if ( strncmp( rawpkt, "getserversResponse", 18) == 0)
++ else
+ {
+- static int q3m_debug= 0;
+-
+- rawpkt+= 18;
+- pktlen-= 18;
+-
+- for ( ; *rawpkt != '\\' && pktlen; pktlen--, rawpkt++)
+- {
+- }
+-
+- if ( !pktlen)
+- {
+- return;
+- }
+- rawpkt++;
+- pktlen--;
++ free(string);
++ }
+
+- if ( q3m_debug) printf( "q3m pktlen %d lastchar %x\n", pktlen, (unsigned int)rawpkt[pktlen-1]);
++ server->num_players = (unsigned char)next[0];
++ server->max_players = (unsigned char)next[1];
+
+- server->master_pkt= (char*)realloc( server->master_pkt,
+- server->master_pkt_len + pktlen+1);
++ return 0;
++}
+
+- if ( server->type->id == STEF_MASTER)
+- {
+- decode_stefmaster_packet( server, rawpkt, pktlen);
+- }
+- else
+- {
+- decode_q3master_packet( server, rawpkt, pktlen);
+- }
+- if ( q3m_debug) printf( "q3m %d servers\n", server->n_servers);
++STATIC int ut2003_basic_packet(struct qserver *server, char *rawpkt, char *end)
++{
++ char *next;
++ char *string;
++ change_server_port(server, swap_short_from_little(&rawpkt[6]), 0);
+
+- return;
+- }
+- else if ( show_errors)
++ if (NULL == (string = ut2003_strdup(&rawpkt[14], end, &next)))
+ {
+- unsigned int ipaddr= ntohl(server->ipaddr);
+- fprintf( stderr,
+- "Odd packet from QW master %d.%d.%d.%d, processing ...\n",
+- (ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+- (ipaddr>>8)&0xff, ipaddr&0xff
+- );
+- print_packet( server, rawpkt, pktlen);
++ return -1;
+ }
+
+- server->master_pkt= (char*)realloc( server->master_pkt,
+- server->master_pkt_len+pktlen+1);
+- rawpkt[pktlen]= '\0';
+- memcpy( server->master_pkt+server->master_pkt_len, rawpkt, pktlen+1);
+- server->master_pkt_len+= pktlen;
+-
+- server->n_servers= server->master_pkt_len / 6;
+-
+- if ( server->type->flags & TF_MASTER_MULTI_RESPONSE)
++ if (server->server_name == NULL)
+ {
+- server->next_player_info= -1;
+- server->retry1= 0;
++ server->server_name = string;
+ }
+- else if ( server->type->id == HL_MASTER )
++ else
+ {
+- if ( server->master_query_tag[0] == 0 &&
+- server->master_query_tag[1] == 0 &&
+- server->master_query_tag[2] == 0
+- )
+- {
+- // all done
+- server->server_name = MASTER;
+- cleanup_qserver( server, 1);
+- bind_sockets();
+- }
+- else
+- {
+- // more to come
+- server->retry1++;
+- send_qwmaster_request_packet( server);
+- }
++ free(string);
+ }
+- else if ( server->type->flags & TF_MASTER_STEAM )
++
++ if (NULL == (string = ut2003_strdup(next, end, &next)))
+ {
+- // should the HL_MASTER be the same as this?
+- int i;
+- for ( i = pktlen - 6; i < pktlen && 0x00 == rawpkt[i] ; i++ )
+- {
+- }
++ return -1;
++ }
+
+- if ( i == pktlen )
+- {
+- // last 6 bytes where 0x00 so we have reached the last packet
+- server->n_servers--;
+- server->master_pkt_len -= 6;
+- server->server_name = MASTER;
+- cleanup_qserver( server, 1);
+- bind_sockets();
+- }
+- else
+- {
+- // more to come
+- server->retry1++;
+- send_qwmaster_request_packet( server );
+- }
++ if (server->map_name == NULL)
++ {
++ server->map_name = string;
+ }
+ else
+ {
+- server->server_name = MASTER;
+- cleanup_qserver( server, 0);
+- bind_sockets();
++ free(string);
+ }
+-}
+-
+-void
+-decode_q3master_packet( struct qserver *server, char *pkt, int pktlen)
+-{
+- char *p;
+-
+- pkt[pktlen]= 0;
+- p= pkt;
+-
+- while ( *p && p < &pkt[pktlen-6]) {
+- memcpy( server->master_pkt + server->master_pkt_len, &p[0], 4);
+- memcpy( server->master_pkt + server->master_pkt_len + 4, &p[4], 2);
+- server->master_pkt_len += 6;
+- p+= 6;
+- while ( *p && *p == '\\')
+- p++;
+- }
+- server->n_servers= server->master_pkt_len / 6;
+-// server->next_player_info= -1; evil, causes busy loop!
+- server->retry1 = 0; // received at least one packet so no need to retry
+-}
+-
+-void
+-decode_stefmaster_packet( struct qserver *server, char *pkt, int pktlen)
+-{
+- unsigned char *p, *m, *end;
+- unsigned int i, b;
+-
+- pkt[pktlen]= 0;
+-
+- p= (unsigned char*) pkt;
+- m= (unsigned char*) server->master_pkt + server->master_pkt_len;
+- end= (unsigned char*) &pkt[pktlen-12];
+- while ( *p && p < end) {
+- for ( i= 6; i; i--) {
+- sscanf( (char*)p, "%2x", &b);
+- p+= 2;
+- *m++= b;
+- }
+- server->master_pkt_len += 6;
+- while ( *p && *p == '\\')
+- p++;
+- }
+- server->n_servers= server->master_pkt_len / 6;
+- server->next_player_info= -1;
+- server->retry1= 0;
+-}
+-
+-/* Packet from Tribes master server
+- */
+-void
+-deal_with_tribesmaster_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
+- unsigned char *upkt= (unsigned char*) rawpkt;
+- int packet_number= upkt[2];
+- int n_packets= upkt[3];
+- unsigned char *p;
+- char *mpkt;
+- int len;
+- unsigned int ipaddr;
+-
+- if ( memcmp( rawpkt, tribes_master_response, sizeof(tribes_master_response)) != 0) {
+- fprintf( stderr, "Odd packet from Tribes master server\n");
+- print_packet( server, rawpkt, pktlen);
+- }
+
+- /* 0x1006
+- 01 packet number
+- 08 # packets
+- 02
+- 0000
+- 66
+- 0d length of following string
+- "Tribes Master"
+- 3c length of following string
+- "Check out the Starsiege demo now! www.starsiegeplayers.com"
+- 0035
+- 06 d143 4764 812c
+- 06 d1e2 8df3 616d
+- 06 1804 6d50 616d
+- 06 d81c 6dc0 616d
+-*/
+-/* 0x1006
+- 02
+- 08
+- 02 0000
+- 66
+- 00 3f
+- 06 cf88 344c 1227
+-*/
+-
+-/* printf( "packet_number %d n_packets %d\n", packet_number, n_packets);
+-*/
+-
+- len= upkt[8];
+- if ( len > 0) {
+- p= (unsigned char*) rawpkt+9;
+-/* printf( "%.*s\n", len, p);
+-*/
+- p+= len;
+- len= upkt[8+len+1];
+-/* printf( "%.*s\n", len, p+1);
+-*/
+- p+= len+1;
+- p+= 2;
+- }
+- else
+- p= (unsigned char*) rawpkt+10;
+-
+- if ( server->master_pkt == NULL) {
+- server->master_pkt= (char*)malloc( n_packets*64*6);
+- mpkt= server->master_pkt;
+- }
+- else
+- mpkt= server->master_pkt + server->n_servers*6;
++ if (NULL == (string = ut2003_strdup(next, end, &next)))
++ {
++ return -1;
++ }
+
+- while ( (char*)p < rawpkt+pktlen) {
+- if ( *p != 0x6) printf( "*p %u\n", (unsigned)*p);
+- memcpy( mpkt, p+1, sizeof(ipaddr));
+- if ( 0) {
+- mpkt[4]= p[5];
+- mpkt[5]= p[6];
+- }
+- else {
+- mpkt[5]= p[5];
+- mpkt[4]= p[6];
++ if (server->game == NULL)
++ {
++ server->game = string;
++ add_rule(server, "gametype", server->game, NO_FLAGS | CHECK_DUPLICATE_RULES);
+ }
+-/*
+- printf( "%08x:%hu %u.%u.%u.%u:%hu\n", ipaddr, port, ipaddr>>24,
+- (ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff, port);
+-*/
+- p+= 7;
+- mpkt+= 6;
+- }
+-/*
+-if ( (char*)p != rawpkt+pktlen)
+-printf( "%x %x\n", p, rawpkt+pktlen);
+-*/
+- server->master_pkt_len= mpkt - server->master_pkt;
+- server->n_servers= server->master_pkt_len / 6;
+- server->server_name= MASTER;
+- server->next_player_info= -1;
+-
+- if ( packet_number >= n_packets)
+- cleanup_qserver( server, 1);
+- else
+- cleanup_qserver( server, 0);
+-}
+-
+-char *
+-display_tribes2_string_list( unsigned char *pkt)
+-{
+- char *delim="";
+- unsigned int count, len;
+- count= *pkt;
+- pkt++;
+- for ( ; count; count--) {
+- len= *pkt;
+- pkt++;
+- if ( len > 0) {
+- if ( raw_display) {
+- fprintf( OF, "%s%.*s", delim, (int)len, pkt);
+- delim= raw_delimiter;
+- }
+- else
+- fprintf( OF, "%.*s\n", (int)len, pkt);
++ else
++ {
++ free(string);
+ }
+- pkt+= len;
+- }
+- if ( raw_display)
+- fputs( "\n", OF);
+- return (char*)pkt;
+-}
+-
+-void
+-deal_with_tribes2master_packet( struct qserver *server, char *pkt, int pktlen)
+-{
+- unsigned int n_servers, index, total, server_limit;
+- char *p, *mpkt;
+-
+- if ( pkt[0] == TRIBES2_RESPONSE_GAME_TYPES) {
+- pkt+= 6;
+- if ( raw_display) {
+- fprintf( OF, "%s%s%s%s", server->type->type_prefix, raw_delimiter,
+- server->arg, raw_delimiter);
+- }
+- else {
+- fprintf( OF, "Game Types\n");
+- fprintf( OF, "----------\n");
+- }
+- pkt= display_tribes2_string_list( (unsigned char *)pkt);
+- if ( raw_display) {
+- fprintf( OF, "%s%s%s%s", server->type->type_prefix, raw_delimiter,
+- server->arg, raw_delimiter);
+- }
+- else {
+- fprintf( OF, "\nMission Types\n");
+- fprintf( OF, "-------------\n");
+- }
+- display_tribes2_string_list( (unsigned char *)pkt);
+-
+- server->master_pkt_len= 0;
+- server->n_servers= 0;
+- server->server_name= MASTER;
+- server->next_player_info= -1;
+- cleanup_qserver( server, 1);
+- return;
+- }
+-
+- if ( pkt[0] != TRIBES2_RESPONSE_MASTER) {
+- /* error */
+- cleanup_qserver( server, 1);
+- return;
+- }
+-
+- server_limit= get_param_ui_value( server, "limit", ~0);
+-
+- n_servers= little_endian ? *(unsigned short*)(pkt+8) :
+- swap_short(pkt+8);
+- index= *(unsigned char*)(pkt+6);
+- total= *(unsigned char*)(pkt+7);
+- if ( server->master_pkt == NULL) {
+- server->master_pkt= (char*)malloc( total * n_servers * 6);
+- mpkt= server->master_pkt;
+- }
+- else
+- mpkt= server->master_pkt + server->n_servers*6;
+-
+- p= pkt+10;
+- for ( ; n_servers && ((char*)mpkt - server->master_pkt)/6 < server_limit;
+- n_servers--, p+= 6, mpkt+= 6) {
+- memcpy( mpkt, p, 4);
+- mpkt[4]= p[5];
+- mpkt[5]= p[4];
+- }
+- server->master_pkt_len= (char*)mpkt - server->master_pkt;
+- server->n_servers= server->master_pkt_len / 6;
+- server->server_name= MASTER;
+- server->next_player_info= -1;
+
+- if ( index >= total-1 || server->n_servers >= server_limit)
+- cleanup_qserver( server, 1);
+- else
+- cleanup_qserver( server, 0);
++ server->num_players = swap_long_from_little(next);
++ next += 4;
++ server->max_players = swap_long_from_little(next);
++ return 0;
+ }
+
+-int
+-server_info_packet( struct qserver *server, struct q_packet *pkt, int datalen)
++STATIC int pariah_rule_packet(struct qserver *server, char *rawpkt, char *end)
+ {
+- int off= 0;
++ char *key, *value;
+
+- /* ignore duplicate packets */
+- if ( server->server_name != NULL)
+- return 0;
++ unsigned char no_rules = (unsigned char)rawpkt[1];
++ unsigned char seen = 0;
+
+- server->address= strdup((char*)&pkt->data[off]);
+- off+= strlen(server->address) + 1;
+- if ( off >= datalen)
+- return -1;
++ // type + no_rules
++ rawpkt += 2;
+
+- server->server_name= strdup((char*)&pkt->data[off]);
+- off+= strlen(server->server_name) + 1;
+- if ( off >= datalen)
+- return -1;
++ // we get size encoded key = value pairs
++ while (rawpkt < end && no_rules > seen)
++ {
++ // first byte is the rule count
++ seen = (unsigned char)rawpkt[0];
++ rawpkt++;
++ if (NULL == (key = ut2003_strdup(rawpkt, end, &rawpkt)))
++ {
++ break;
++ }
+
+- server->map_name= strdup((char*)&pkt->data[off]);
+- off+= strlen(server->map_name) + 1;
+- if ( off > datalen)
+- return -1;
++ if ('\0' == rawpkt[0])
++ {
++ value = strdup("");
++ rawpkt++;
++ }
++ else if (NULL == (value = ut2003_strdup(rawpkt, end, &rawpkt)))
++ {
++ break;
++ }
+
+- server->num_players= pkt->data[off++];
+- server->max_players= pkt->data[off++];
+- server->protocol_version= pkt->data[off++];
++ if (NULL == add_rule(server, key, value, NO_KEY_COPY | NO_VALUE_COPY | COMBINE_VALUES))
++ {
++ /* duplicate, so free key and value */
++ free(value);
++ free(key);
++ }
+
+- server->retry1= n_retries;
++ seen++;
++ }
+
+- if ( get_server_rules)
+- send_rule_request_packet( server);
+- if ( get_player_info)
+- send_player_request_packet( server);
++ if (no_rules == seen)
++ {
++ // all done
++ server->next_rule = NULL;
++ return 1;
++ }
+
+- return 0;
++ return 0;
+ }
+
+-int
+-player_info_packet( struct qserver *server, struct q_packet *pkt, int datalen)
++STATIC int ut2003_rule_packet(struct qserver *server, char *rawpkt, char *end)
+ {
+- char *name, *address;
+- int off, colors, frags, connect_time, player_number;
+- struct player *player, *last;
+-
+- off= 0;
+- player_number= pkt->data[off++];
+- name= (char*) &pkt->data[off];
+- off+= strlen(name)+1;
+- if ( off >= datalen)
+- return -1;
+-
+- colors= pkt->data[off+3];
+- colors= (colors<<8) + pkt->data[off+2];
+- colors= (colors<<8) + pkt->data[off+1];
+- colors= (colors<<8) + pkt->data[off];
+- off+= sizeof(colors);
+-
+- frags= pkt->data[off+3];
+- frags= (frags<<8) + pkt->data[off+2];
+- frags= (frags<<8) + pkt->data[off+1];
+- frags= (frags<<8) + pkt->data[off];
+- off+= sizeof(frags);
+-
+- connect_time= pkt->data[off+3];
+- connect_time= (connect_time<<8) + pkt->data[off+2];
+- connect_time= (connect_time<<8) + pkt->data[off+1];
+- connect_time= (connect_time<<8) + pkt->data[off];
+- off+= sizeof(connect_time);
+-
+- address= (char*) &pkt->data[off];
+- off+= strlen(address)+1;
+- if ( off > datalen)
+- return -1;
+-
+- last= server->players;
+- while ( last != NULL && last->next != NULL) {
+- if ( last->number == player_number)
+- return 0;
+- last= last->next;
+- }
+- if ( last != NULL && last->number == player_number)
+- return 0;
+-
+- player= (struct player *) calloc( 1, sizeof(struct player));
+- player->number= player_number;
+- player->name= strdup( name);
+- player->address= strdup( address);
+- player->connect_time= connect_time;
+- player->frags= frags;
+- player->shirt_color= colors>>4;
+- player->pants_color= colors&0xf;
+- player->next= NULL;
+-
+- if ( last == NULL)
+- server->players= player;
+- else
+- last->next= player;
+-
+- server->next_player_info++;
+- server->retry2= n_retries;
+- if ( server->next_player_info < server->num_players)
+- send_player_request_packet( server);
+-
+- return 0;
+-}
+-
+-int
+-rule_info_packet( struct qserver *server, struct q_packet *pkt, int datalen)
+-{
+- int off= 0;
+- struct rule *rule, *last;
+- char *name, *value;
+-
+- /* Straggler packet after we've already given up fetching rules */
+- if ( server->next_rule == NULL)
+- return 0;
+-
+- if ( ntohs(pkt->length) == Q_HEADER_LEN) {
+- server->next_rule= NULL;
+- return 0;
+- }
+-
+- name= (char*)&pkt->data[off];
+- off+= strlen( name)+1;
+- if ( off >= datalen)
+- return -1;
++ char *key, *value;
++ int result = 0;
+
+- value= (char*)&pkt->data[off];
+- off+= strlen( value)+1;
+- if ( off > datalen)
+- return -1;
++ // Packet Type
++ rawpkt++;
+
+- last= server->rules;
+- while ( last != NULL && last->next != NULL) {
+- if ( strcmp( last->name, name) == 0)
+- return 0;
+- last= last->next;
+- }
+- if ( last != NULL && strcmp( last->name, name) == 0)
+- return 0;
++ // we get size encoded key = value pairs
++ while (rawpkt < end)
++ {
++ if (NULL == (key = ut2003_strdup(rawpkt, end, &rawpkt)))
++ {
++ break;
++ }
+
+- rule= (struct rule *) malloc( sizeof( struct rule));
+- rule->name= strdup( name);
+- rule->value= strdup( value);
+- rule->next= NULL;
++ if (NULL == (value = ut2003_strdup(rawpkt, end, &rawpkt)))
++ {
++ break;
++ }
+
+- if ( last == NULL)
+- server->rules= rule;
+- else
+- last->next= rule;
++ if (strcmp(key, "minplayers") == 0)
++ {
++ result = atoi(value);
++ }
+
+- server->n_rules++;
+- server->next_rule= rule->name;
+- server->retry1= n_retries;
+- send_rule_request_packet( server);
++ if (NULL == add_rule(server, key, value, NO_KEY_COPY | NO_VALUE_COPY | COMBINE_VALUES))
++ {
++ /* duplicate, so free key and value */
++ free(value);
++ free(key);
++ }
++ }
+
+- return 0;
++ return result;
+ }
+
+-struct info *
+-player_add_info( struct player *player, char *key, char *value, int flags)
++char *ut2003_strdup(const char *string, const char *end, char **next)
+ {
+- struct info *info;
+- if ( flags & CHECK_DUPLICATE_RULES )
++ unsigned char len = string[0];
++ char *result = NULL;
++
++ if (len < 128)
++ {
++ // type 1 string
++ //fprintf( stderr, "Type 1:" );
++ result = dup_nstring(string, end, next);
++ }
++ else
+ {
+- for ( info = player->info; info; info = info->next )
++ // type 2 string
++ //fprintf( stderr, "Type 2:\n" );
++ const char *last;
++ char *resp, *pos;
++ // minus indicator
++ len -= 128;
++ // double byte chars so * 2
++ len = len * 2;
++ last = string + len;
++ if (last > end)
+ {
+- if ( 0 == strcmp( info->name, key ) )
+- {
+- return NULL;
+- }
++ *next = (char*)end;
++ fprintf(stderr, "Type 2 string format error ( too short )\n");
++ return NULL;
+ }
+- }
+
+- if ( flags & COMBINE_VALUES )
+- {
+- for ( info = player->info; info; info = info->next )
++ *next = (char*)last + 1;
++ if (NULL == (result = (char*)calloc(last - string, sizeof(char))))
+ {
+- if ( 0 == strcmp( info->name, key ) )
++ fprintf(stderr, "Failed to malloc string memory\n");
++ return NULL;
++ }
++ resp = result;
++ pos = (char*)string + 1;
++ while (pos <= last)
++ {
++ // check for a color code
++ if (pos + 6 <= last && 0 == memcmp(pos, "^\0#\0", 4))
+ {
+- char *full_value = (char*)calloc( sizeof(char), strlen( info->value ) + strlen( value ) + 2 );
+- if ( NULL == full_value )
+- {
+- fprintf( stderr, "Failed to malloc combined value\n" );
+- exit( 1 );
+- }
+- sprintf( full_value, "%s%s%s", info->value, multi_delimiter, value );
+-
+- // We should be able to free this
+- free( info->value );
+- info->value = full_value;
+-
+- return info;
++ // we have a color code
++ //fprintf( stderr, "color:%02hhx%02hhx\n", pos[4], pos[5] );
++ // indicator transformed to ^\1
++ *resp = *pos;
++ resp++;
++ pos++;
++ *resp = '\1';
++ resp++;
++ pos += 3;
++ // color byte
++ *resp = *pos;
++ resp++;
++ pos += 2;
++ //pos += 6;
+ }
+- }
+- }
+
+- info = (struct info *) malloc( sizeof( struct info));
+- if ( flags & NO_KEY_COPY)
+- {
+- info->name = key;
+- }
+- else
+- {
+- info->name = strdup(key);
+- }
+- if ( flags & NO_VALUE_COPY)
+- {
+- info->value = value;
+- }
+- else
+- {
+- info->value = strdup(value);
++ // standard char
++ //fprintf( stderr, "char: %02hhx\n", *pos );
++ *resp = *pos;
++ resp++;
++ pos += 2;
++ }
+ }
+- info->next = NULL;
+
+- if ( NULL == player->info )
+- {
+- player->info = info;
+- }
+- else
+- {
+- *player->last_info = info;
+- }
+- player->last_info = &info->next;
+- player->n_info++;
++ //fprintf( stderr, "'%s'\n", result );
+
+- return info;
++ return result;
+ }
+
+-struct rule *
+-add_rule( struct qserver *server, char *key, char *value, int flags)
++
++STATIC int pariah_player_packet(struct qserver *server, char *rawpkt, char *end)
+ {
+- struct rule *rule;
+- if ( flags & CHECK_DUPLICATE_RULES )
+- {
+- for ( rule = server->rules; rule; rule = rule->next )
+- {
+- if ( 0 == strcmp( rule->name, key ) )
+- {
+- return NULL;
+- }
+- }
+- }
++ unsigned char no_players = rawpkt[1];
++ unsigned char seen = 0; /* XXX: cannot work this way, it takes only
++ this packet into consideration. What if
++ player info is spread across multiple
++ packets? */
+
+- if ( flags & COMBINE_VALUES )
++ // type + no_players + some unknown preamble
++ rawpkt += 3;
++ while (rawpkt < end && seen < no_players)
+ {
+- for ( rule = server->rules; rule; rule = rule->next )
+- {
+- if ( 0 == strcmp( rule->name, key ) )
+- {
+- char *full_value = (char*)calloc( sizeof(char), strlen( rule->value ) + strlen( value ) + 2 );
+- if ( NULL == full_value )
+- {
+- fprintf( stderr, "Failed to malloc combined value\n" );
+- exit( 1 );
+- }
+- sprintf( full_value, "%s%s%s", rule->value, multi_delimiter, value );
++ struct player *player;
+
+- // We should be able to free this
+- free( rule->value );
+- rule->value = full_value;
++ // Player Number
++ rawpkt += 4;
+
+- return rule;
+- }
++ // Create a player
++ if (NULL == (player = add_player(server, server->n_player_info)))
++ {
++ return 0;
+ }
+- }
+
+- rule = (struct rule *) malloc( sizeof( struct rule));
+- if ( flags & NO_KEY_COPY)
+- {
+- rule->name = key;
+- }
+- else
+- {
+- rule->name = strdup(key);
+- }
+- if ( flags & NO_VALUE_COPY)
+- {
+- rule->value = value;
+- }
+- else
+- {
+- rule->value = strdup(value);
+- }
+- rule->next = NULL;
+- *server->last_rule = rule;
+- server->last_rule = & rule->next;
+- server->n_rules++;
++ // Name ( min 3 bytes )
++ player->name = ut2003_strdup(rawpkt, end, &rawpkt);
+
+- return rule;
+-}
++ // Ping
++ player->ping = swap_long_from_little(rawpkt);
++ rawpkt += 4;
+
+-void
+-add_nrule( struct qserver *server, char *key, char *value, int len)
+-{
+- struct rule *rule;
+- for ( rule= server->rules; rule; rule= rule->next)
+- if ( strcmp( rule->name, key) == 0)
+- return;
++ // Frags
++ player->frags = (unsigned char)rawpkt[0];
++ rawpkt++;
+
+- rule= (struct rule *) malloc( sizeof( struct rule));
+- rule->name= strdup(key);
+- rule->value= strndup(value,len);
+- rule->next= NULL;
+- *server->last_rule= rule;
+- server->last_rule= & rule->next;
+- server->n_rules++;
+-}
++ // unknown
++ rawpkt++;
+
+-struct player *
+-add_player( struct qserver *server, int player_number )
+-{
+- struct player *player;
++ seen++;
++ }
+
+- for ( player = server->players; player; player = player->next )
+- {
+- if ( player->number == player_number)
++ if (no_players == seen)
+ {
+- return NULL;
++ // all done
++ server->num_players = server->n_player_info;
++ return 1;
+ }
+- }
+
+- player = (struct player *) calloc( 1, sizeof( struct player));
+- player->number = player_number;
+- player->next = server->players;
+- player->n_info = 0;
+- player->score = NA_INT;
+- player->deaths = NA_INT;
+- player->frags = NA_INT;
+- player->last_info = NULL;
+- server->players = player;
+- server->n_player_info++;
+- return player;
+-}
+-
+-STATIC struct player *
+-get_player_by_number( struct qserver *server, int player_number)
+-{
+- struct player *player;
+- for ( player= server->players; player; player= player->next)
+- if ( player->number == player_number)
+- return player;
+- return NULL;
++ // possibly more to come
++
++ return 0;
+ }
+
+-// Updates a servers port information.
+-// Sets the rules:
+-// _queryport <queryport>
+-// hostport <port>
+-void
+-change_server_port( struct qserver *server, unsigned short port, int force )
++STATIC int ut2003_player_packet(struct qserver *server, char *rawpkt, char *end)
+ {
+- if ( port > 0 && port != server->port )
++ // skip type
++ rawpkt++;
++ switch (server->protocol_version)
+ {
+- // valid port and changing
+- char arg[64];
+-
+- if ( show_game_port || force || server->flags & TF_SHOW_GAME_PORT )
+- {
+- unsigned int ipaddr = ntohl(server->ipaddr);
+-
+- // Update the servers hostname as required
+- sprintf( arg, "%d.%d.%d.%d:%hu", ipaddr>>24, (ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff, port );
+- if ( 0 != strcmp( server->arg, server->host_name ))
+- {
+- // hostname isnt the query arg
+- char *colon = strchr( server->host_name, ':' );
+- if ( colon )
+- {
+- // dns hostname or hostname:port
+- char *hostname = malloc( strlen(server->host_name) + 6 );
+- *colon= '\0';
+- sprintf( hostname, "%s:%hu", server->host_name, port);
+- free( server->host_name);
+- server->host_name= hostname;
++ case 0x7e:
++ // XMP packet
++ //fprintf( stderr, "XMP packet\n" );
++ while (rawpkt < end)
++ {
++ struct player *player;
++ char *var, *val;
++ unsigned char no_props;
++ if (rawpkt + 24 > end)
++ {
++ malformed_packet(server, "player info too short");
++ rawpkt = end;
++ return 1;
+ }
+- }
+- // Update the server arg
+- free( server->arg );
+- server->arg = strdup( arg );
+
+- // Add a rule noting the previous query port
+- sprintf( arg, "%hu", server->port );
+- add_rule( server, "_queryport", arg, NO_FLAGS);
++ // Player Number never set
++ rawpkt += 4;
+
+- // Update the servers port
+- server->port = port;
+- }
++ // Player ID never set
++ rawpkt += 4;
+
+- // Add a rule noting the servers hostport
+- sprintf( arg, "%hu", port );
+- add_rule( server, "hostport", arg, NO_FLAGS);
+- }
+-}
++ if (NULL == (player = add_player(server, server->n_player_info)))
++ {
++ return 0;
++ }
+
+-STATIC void
+-players_set_teamname( struct qserver *server, int teamid, char *teamname )
+-{
+- struct player *player;
+- for ( player= server->players; player; player= player->next)
+- {
+- if ( player->team == teamid )
+- {
+- player->team_name = strdup( teamname );
+- }
+- }
+-}
++ // Name ( min 3 bytes )
++ player->name = ut2003_strdup(rawpkt, end, &rawpkt);
+
+-STATIC char *
+-dup_nstring( const char *pkt, const char *end, char **next)
+-{
+- char *pt = (char*)pkt;
+- int len= ((unsigned char*)pkt)[0];
+- pt++;
+- if ( *pt == '\1')
+- {
+- len++;
+- }
+- if ( pt + len > end)
+- {
+- return NULL;
+- }
++ // Ping
++ player->ping = swap_long_from_little(rawpkt);
++ rawpkt += 4;
+
+- *next = pt+len;
+- return strndup( pt, len );
+-}
++ // Frags
++ player->frags = swap_long_from_little(rawpkt);
++ rawpkt += 4;
+
+-STATIC char *
+-dup_n1string( char *pkt, char *end, char **next)
+-{
+- unsigned len;
++ // Stat ID never set
++ rawpkt += 4;
+
+- if(!pkt || pkt >= end)
+- return NULL;
++ // Player properties
++ no_props = rawpkt[0];
++ //fprintf( stderr, "noprops %d\n", no_props );
++ rawpkt++;
++ while (rawpkt < end && no_props > 0)
++ {
++ if (NULL == (var = ut2003_strdup(rawpkt, end, &rawpkt)))
++ {
++ break;
++ }
++ if (NULL == (val = ut2003_strdup(rawpkt, end, &rawpkt)))
++ {
++ break;
++ }
++ //fprintf( stderr, "attrib: %s = %s\n", var, val );
+
+- len = (unsigned char)pkt[0]-1;
+- pkt++;
+- if ( pkt + len > end)
+- return NULL;
++ // Things we can use
++ if (0 == strcmp(var, "team"))
++ {
++ player->team_name = val;
++ }
++ else if (0 == strcmp(var, "class"))
++ {
++ player->skin = val;
++ }
++ else
++ {
++ free(val);
++ }
+
+- *next= pkt+len;
+- return strndup( pkt, len);
+-}
++ free(var);
++ no_props--;
++ }
++ }
++ break;
++ default:
++ while (rawpkt < end)
++ {
++ struct player *player;
+
+-STATIC int
+-pariah_basic_packet( struct qserver *server, char *rawpkt, char *end)
+-{
+- char *next;
+- char *string;
+- change_server_port( server, swap_short_from_little( &rawpkt[14]), 0 );
+- if ( NULL == ( string = ut2003_strdup( &rawpkt[18], end, &next) ) )
+- {
+- return -1;
+- }
++ if (rawpkt + 4 > end)
++ {
++ malformed_packet(server, "player packet too short");
++ return 1;
++ }
+
+- if ( server->server_name == NULL)
+- {
+- server->server_name = string;
+- }
+- else
+- {
+- free(string);
+- }
++ if (NULL == (player = add_player(server, swap_long_from_little(rawpkt))))
++ {
++ return 0;
++ }
+
+- if ( NULL == ( string = ut2003_strdup( next, end, &next) ) )
+- {
+- return -1;
++ player->name = ut2003_strdup(rawpkt + 4, end, &rawpkt);
++ if (rawpkt + 8 > end)
++ {
++ malformed_packet(server, "player packet too short");
++ return 1;
++ }
++ player->ping = swap_long_from_little(rawpkt);
++ rawpkt += 4;
++ player->frags = swap_long_from_little(rawpkt);
++ rawpkt += 4;
++ {
++ unsigned team = swap_long_from_little(rawpkt);
++ rawpkt += 4;
++ player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++ if (team &1 << 29)
++ {
++ player->team_name = "red";
++ }
++ else if (team &1 << 30)
++ {
++ player->team_name = "blue";
++ }
++ }
++ }
+ }
+
+- if ( server->map_name == NULL)
+- {
+- server->map_name = string;
+- }
+- else
+- {
+- free(string);
+- }
++ return 0;
++}
+
+- if ( NULL == ( string= ut2003_strdup( next, end, &next) ) )
+- {
+- return -1;
+- }
+
+- if ( server->game == NULL)
+- {
+- server->game = string;
+- add_rule( server, "gametype", server->game, NO_FLAGS | CHECK_DUPLICATE_RULES);
+- }
+- else
+- {
+- free(string);
++char *get_rule(struct qserver *server, char *name)
++{
++ struct rule *rule;
++ rule = server->rules;
++ for (; rule != NULL; rule = rule->next)
++ {
++ if (strcmp(name, rule->name) == 0)
++ {
++ return rule->value;
++ }
+ }
+
+- server->num_players = (unsigned char)next[0];
+- server->max_players = (unsigned char)next[1];
+-
+- return 0;
++ return NULL;
+ }
+
+-STATIC int
+-ut2003_basic_packet( struct qserver *server, char *rawpkt, char *end)
++query_status_t deal_with_ut2003_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- char *next;
+- char *string;
+- change_server_port( server, swap_short_from_little( &rawpkt[6]), 0 );
+
+- if ( NULL == ( string = ut2003_strdup( &rawpkt[14], end, &next) ) )
+- {
+- return -1;
+- }
++ // For protocol spec see:
++ // http://unreal.student.utwente.nl/UT2003-queryspec.html
+
+- if ( server->server_name == NULL)
+- {
+- server->server_name = string;
+- }
+- else
+- {
+- free(string);
+- }
++ char *end;
++ int error = 0, before;
++ unsigned int packet_header;
+
+- if ( NULL == ( string = ut2003_strdup( next, end, &next) ) )
+- {
+- return -1;
+- }
++ debug( 2, "deal_with_ut2003_packet %p, %d", server, pktlen );
+
+- if ( server->map_name == NULL)
+- {
+- server->map_name= string;
+- }
+- else
+- {
+- free(string);
+- }
++ rawpkt[pktlen] = '\0';
++ end = &rawpkt[pktlen];
+
+- if ( NULL == ( string= ut2003_strdup( next, end, &next) ) )
+- {
+- return -1;
+- }
++ packet_header = swap_long_from_little(&rawpkt[0]);
++ rawpkt += 4;
+
+- if ( server->game == NULL)
+- {
+- server->game= string;
+- add_rule( server, "gametype", server->game, NO_FLAGS | CHECK_DUPLICATE_RULES);
+- }
+- else
+- {
+- free(string);
++ server->protocol_version = packet_header;
++ if (
++ packet_header != 0x77 // Pariah Demo?
++ && packet_header != 0x78 // UT2003 Demo
++ && packet_header != 0x79 // UT2003 Retail
++ && packet_header != 0x7e // Unreal2 XMP
++ && packet_header != 0x7f // UT2004 Demo
++ && packet_header != 0x80 // UT2004 Retail
++ )
++ {
++ malformed_packet(server, "Unknown type 0x%x", packet_header);
+ }
+
+- server->num_players= swap_long_from_little( next);
+- next+= 4;
+- server->max_players= swap_long_from_little( next);
+- return 0;
+-}
++ switch (rawpkt[0])
++ {
++ case 0x00:
++ // Server info
++ if (server->server_name == NULL)
++ {
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++ }
+
+-STATIC int
+-pariah_rule_packet( struct qserver *server, char *rawpkt, char *end )
+-{
+- char *key, *value;
++ error = ut2003_basic_packet(server, rawpkt, end);
++ if (!error)
++ {
++ if (get_server_rules || get_player_info)
++ {
++ int requests = server->n_requests;
++ server->next_rule = "";
++ server->retry1 = n_retries;
++ server->retry2 = 0; // don't wait for player packet
++ send_rule_request_packet(server);
++ server->n_requests = requests; // would produce wrong ping
++ }
++ }
++ break;
+
+- unsigned char no_rules = (unsigned char)rawpkt[1];
+- unsigned char seen = 0;
++ case 0x01:
++ // Game info
++ ut2003_rule_packet(server, rawpkt, end);
++ server->next_rule = "";
++ server->retry1 = 0; /* we received at least one rule packet so
++ no need to retry. We'd get double
++ entries otherwise. */
++ break;
+
+- // type + no_rules
+- rawpkt+=2;
++ case 0x02:
++ // Player info
++ before = server->n_player_info;
++ error = ut2003_player_packet(server, rawpkt, end);
++ if (before == server->n_player_info)
++ {
++ error = 1;
++ }
++ break;
+
+- // we get size encoded key = value pairs
+- while ( rawpkt < end && no_rules > seen )
+- {
+- // first byte is the rule count
+- seen = (unsigned char)rawpkt[0];
+- rawpkt++;
+- if ( NULL == ( key = ut2003_strdup( rawpkt, end, &rawpkt) ) )
+- {
++ case 0x10:
++ // Pariah Server info
++ if (server->server_name == NULL)
++ {
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++ }
++
++ error = pariah_basic_packet(server, rawpkt, end);
++ if (!error)
++ {
++ // N.B. pariah always sends a rules and players packet
++ int requests = server->n_requests;
++ server->next_rule = "";
++ server->retry1 = n_retries;
++ server->retry2 = 0;
++ server->n_requests = requests; // would produce wrong ping
++ }
+ break;
+- }
+
+- if ( '\0' == rawpkt[0] )
+- {
+- value = strdup( "" );
+- rawpkt++;
+- }
+- else if ( NULL == ( value = ut2003_strdup( rawpkt, end, &rawpkt) ) )
+- {
++ case 0x11:
++ // Game info
++ pariah_rule_packet(server, rawpkt, end);
++ server->retry1 = 0; /* we received at least one rule packet so
++ no need to retry. We'd get double
++ entries otherwise. */
+ break;
+- }
+
+- if ( NULL == add_rule( server, key, value, NO_KEY_COPY | NO_VALUE_COPY | COMBINE_VALUES ) )
+- {
+- /* duplicate, so free key and value */
+- free(value);
+- free(key);
+- }
++ case 0x12:
++ // Player info
++ before = server->n_player_info;
++ pariah_player_packet(server, rawpkt, end);
++ if (before == server->n_player_info)
++ {
++ error = 1;
++ }
++ break;
+
+- seen++;
+- }
+
+- if ( no_rules == seen )
++ default:
++ malformed_packet(server, "Unknown packet type 0x%x", (unsigned)rawpkt[0]);
++ break;
++ }
++
++ /* don't cleanup if we fetch server rules. We would lose
++ * rule packets as we don't know how many we get
++ * We do clean up if we don't fetch server rules so we don't
++ * need to wait for timeout.
++ */
++ if (
++ error ||
++ (!get_server_rules && !get_player_info) ||
++ (!get_server_rules && server->num_players == server->n_player_info) ||
++ (server->next_rule == NULL && server->num_players == server->n_player_info)
++ )
+ {
+- // all done
+- server->next_rule = NULL;
+- return 1;
++ return DONE_FORCE;
+ }
+
+- return 0;
++ return INPROGRESS;
+ }
+
+-STATIC int
+-ut2003_rule_packet( struct qserver *server, char *rawpkt, char *end )
++int deal_with_unrealmaster_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- char *key, *value;
+- int result= 0;
+-
+- // Packet Type
+- rawpkt++;
++ debug( 2, "deal_with_unrealmaster_packet %p, %d", server, pktlen );
+
+- // we get size encoded key = value pairs
+- while ( rawpkt < end )
+- {
+- if ( NULL == ( key = ut2003_strdup( rawpkt, end, &rawpkt) ) )
+- {
+- break;
+- }
+-
+- if ( NULL == ( value = ut2003_strdup( rawpkt, end, &rawpkt) ) )
+- {
+- break;
+- }
++ if (pktlen == 0)
++ {
++ return PKT_ERROR;
++ }
++ print_packet(server, rawpkt, pktlen);
++ puts("--");
++ return 0;
++}
+
+- if ( strcmp( key, "minplayers") == 0)
+- {
+- result = atoi(value);
+- }
++/* Returns 1 if the query is done (server may be freed) and 0 if not.
++ */
++query_status_t deal_with_halflife_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++ char *pkt;
++ char *end = &rawpkt[pktlen];
++ int pkt_index = 0, pkt_max = 0;
++ char number[16];
++ short pkt_id;
+
+- if ( NULL == add_rule( server, key, value, NO_KEY_COPY | NO_VALUE_COPY | COMBINE_VALUES ) )
+- {
+- /* duplicate, so free key and value */
+- free(value);
+- free(key);
+- }
+- }
++ debug( 2, "deal_with_halflife_packet %p, %d", server, pktlen );
+
+- return result;
+-}
++ if (server->server_name == NULL)
++ {
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++ }
+
+-char *
+-ut2003_strdup( const char *string, const char *end, char **next )
+-{
+- unsigned char len = string[0];
+- char *result = NULL;
++ if (pktlen < 5)
++ {
++ return PKT_ERROR;
++ }
+
+- if ( len < 128 )
++ if (((rawpkt[0] != '\377' && rawpkt[0] != '\376') || rawpkt[1] != '\377' || rawpkt[2] != '\377' || rawpkt[3] != '\377') && show_errors)
+ {
+- // type 1 string
+- //fprintf( stderr, "Type 1:" );
+- result = dup_nstring( string, end, next );
++ unsigned int ipaddr = ntohl(server->ipaddr);
++ fprintf(stderr, "Odd packet from server %d.%d.%d.%d:%hu, processing ...\n",
++ (ipaddr >> 24) &0xff,
++ (ipaddr >> 16) &0xff,
++ (ipaddr >> 8) &0xff,
++ ipaddr &0xff,
++ ntohs(server->port)
++ );
++ print_packet(server, rawpkt, pktlen);
+ }
+- else
++
++ if (((unsigned char*)rawpkt)[0] == 0xfe)
+ {
+- // type 2 string
+- //fprintf( stderr, "Type 2:\n" );
+- const char *last;
+- char *resp, *pos;
+- // minus indicator
+- len -= 128;
+- // double byte chars so * 2
+- len = len * 2;
+- last = string + len;
+- if ( last > end )
+- {
+- *next = (char*)end;
+- fprintf( stderr, "Type 2 string format error ( too short )\n" );
+- return NULL;
+- }
++ SavedData *sdata;
++ pkt_index = ((unsigned char*)rawpkt)[8] >> 4;
++ pkt_max = ((unsigned char*)rawpkt)[8] &0xf;
++ memcpy(&pkt_id, &rawpkt[4], 2);
+
+- *next = (char*)last+1;
+- if ( NULL == ( result = (char*)calloc( last - string, sizeof(char) ) ) )
++ if (server->saved_data.data == NULL)
+ {
+- fprintf( stderr, "Failed to malloc string memory\n" );
+- return NULL;
++ sdata = &server->saved_data;
+ }
+- resp = result;
+- pos = (char*)string + 1;
+- while ( pos <= last )
++ else
+ {
+- // check for a color code
+- if ( pos + 6 <= last && 0 == memcmp( pos, "^\0#\0", 4 ) )
+- {
+- // we have a color code
+- //fprintf( stderr, "color:%02hhx%02hhx\n", pos[4], pos[5] );
+- // indicator transformed to ^\1
+- *resp = *pos;
+- resp++;
+- pos++;
+- *resp = '\1';
+- resp++;
+- pos+=3;
+- // color byte
+- *resp = *pos;
+- resp++;
+- pos+=2;
+- //pos += 6;
+- }
+-
+- // standard char
+- //fprintf( stderr, "char: %02hhx\n", *pos );
+- *resp = *pos;
+- resp++;
+- pos += 2;
++ sdata = (SavedData*)calloc(1, sizeof(SavedData));
++ sdata->next = server->saved_data.next;
++ server->saved_data.next = sdata;
+ }
+- }
+-
+- //fprintf( stderr, "'%s'\n", result );
+
+- return result;
+-}
++ sdata->pkt_index = pkt_index;
++ sdata->pkt_max = pkt_max;
++ sdata->pkt_id = pkt_id;
++ sdata->datalen = pktlen - 9;
++ sdata->data = (char*)malloc(pktlen - 9);
++ memcpy(sdata->data, &rawpkt[9], pktlen - 9);
+
++ /* combine_packets will call us recursively */
++ return combine_packets(server);
+
+-STATIC int
+-pariah_player_packet( struct qserver *server, char *rawpkt, char *end)
+-{
+- unsigned char no_players = rawpkt[1];
+- unsigned char seen = 0; /* XXX: cannot work this way, it takes only
+- this packet into consideration. What if
+- player info is spread across multiple
+- packets? */
++ /*
++ fprintf( OF, "pkt_index %d pkt_max %d\n", pkt_index, pkt_max);
++ rawpkt+= 9;
++ pktlen-= 9;
++ */
++ }
+
+- // type + no_players + some unknown preamble
+- rawpkt += 3;
+- while ( rawpkt < end && seen < no_players )
++ /* 'info' response */
++ if (rawpkt[4] == 'C' || rawpkt[4] == 'm')
+ {
+- struct player *player;
+-
+- // Player Number
+- rawpkt += 4;
+-
+- // Create a player
+- if ( NULL == ( player = add_player( server, server->n_player_info ) ) )
++ if (server->server_name != NULL)
+ {
+ return 0;
+ }
++ pkt = &rawpkt[5];
++ server->address = strdup(pkt);
++ pkt += strlen(pkt) + 1;
++ server->server_name = strdup(pkt);
++ pkt += strlen(pkt) + 1;
++ server->map_name = strdup(pkt);
++ pkt += strlen(pkt) + 1;
+
+- // Name ( min 3 bytes )
+- player->name = ut2003_strdup( rawpkt, end, &rawpkt );
+-
+- // Ping
+- player->ping = swap_long_from_little( rawpkt );
+- rawpkt += 4;
+-
+- // Frags
+- player->frags = (unsigned char)rawpkt[0];
+- rawpkt++;
+-
+- // unknown
+- rawpkt++;
+-
+- seen++;
+- }
+-
+- if ( no_players == seen )
+- {
+- // all done
+- server->num_players = server->n_player_info;
+- return 1;
+- }
+-
+- // possibly more to come
++ if (*pkt)
++ {
++ add_rule(server, "gamedir", pkt, NO_FLAGS);
++ }
++ if (*pkt && strcmp(pkt, "valve") != 0)
++ {
++ server->game = add_rule(server, "game", pkt, NO_FLAGS)->value;
++ server->flags |= FLAG_DO_NOT_FREE_GAME;
++ }
++ pkt += strlen(pkt) + 1;
++ if (*pkt)
++ {
++ add_rule(server, "gamename", pkt, NO_FLAGS);
++ }
++ pkt += strlen(pkt) + 1;
+
+- return 0;
+-}
++ server->num_players = (unsigned int)pkt[0];
++ server->max_players = (unsigned int)pkt[1];
++ pkt += 2;
++ if (pkt < end)
++ {
++ int protocol = *((unsigned char*)pkt);
++ sprintf(number, "%d", protocol);
++ add_rule(server, "protocol", number, NO_FLAGS);
++ pkt++;
++ }
+
+-STATIC int
+-ut2003_player_packet( struct qserver *server, char *rawpkt, char *end)
+-{
+- // skip type
+- rawpkt++;
+- switch ( server->protocol_version )
+- {
+- case 0x7e:
+- // XMP packet
+- //fprintf( stderr, "XMP packet\n" );
+- while ( rawpkt < end )
++ if (rawpkt[4] == 'm')
+ {
+- struct player *player;
+- char *var, *val;
+- unsigned char no_props;
+- if ( rawpkt + 24 > end )
++ if (*pkt == 'd')
+ {
+- malformed_packet( server, "player info too short" );
+- rawpkt = end;
+- return 1;
++ add_rule(server, "sv_type", "dedicated", NO_FLAGS);
+ }
+-
+- // Player Number never set
+- rawpkt += 4;
+-
+- // Player ID never set
+- rawpkt += 4;
+-
+- if ( NULL == ( player = add_player( server, server->n_player_info ) ) )
++ else if (*pkt == 'l')
+ {
+- return 0;
++ add_rule(server, "sv_type", "listen", NO_FLAGS);
+ }
+-
+- // Name ( min 3 bytes )
+- player->name = ut2003_strdup( rawpkt, end, &rawpkt );
+-
+- // Ping
+- player->ping = swap_long_from_little( rawpkt );
+- rawpkt += 4;
+-
+- // Frags
+- player->frags = swap_long_from_little( rawpkt );
+- rawpkt += 4;
+-
+- // Stat ID never set
+- rawpkt += 4;
+-
+- // Player properties
+- no_props = rawpkt[0];
+- //fprintf( stderr, "noprops %d\n", no_props );
+- rawpkt++;
+- while( rawpkt < end && no_props > 0 )
++ else
+ {
+- if ( NULL == ( var = ut2003_strdup( rawpkt, end, &rawpkt ) ) )
+- {
+- break;
+- }
+- if ( NULL == ( val = ut2003_strdup( rawpkt, end, &rawpkt ) ) )
+- {
+- break;
+- }
+- //fprintf( stderr, "attrib: %s = %s\n", var, val );
+-
+- // Things we can use
+- if ( 0 == strcmp( var, "team" ) )
+- {
+- player->team_name = val;
+- }
+- else if ( 0 == strcmp( var, "class" ) )
+- {
+- player->skin = val;
+- }
+- else
++ add_rule(server, "sv_type", "?", NO_FLAGS);
++ }
++ pkt++;
++ if (*pkt == 'w')
++ {
++ add_rule(server, "sv_os", "windows", NO_FLAGS);
++ }
++ else if (*pkt == 'l')
++ {
++ add_rule(server, "sv_os", "linux", NO_FLAGS);
++ }
++ else
++ {
++ char str[2] = "\0";
++ str[0] = *pkt;
++ add_rule(server, "sv_os", str, NO_FLAGS);
++ }
++ pkt++;
++ add_rule(server, "sv_password", *pkt ? "1" : "0", NO_FLAGS);
++ pkt++;
++ add_rule(server, "mod", *pkt ? "1" : "0", NO_FLAGS);
++ if (*pkt)
++ {
++ int n;
++ /* pull out the mod infomation */
++ pkt++;
++ add_rule(server, "mod_info_url", pkt, NO_FLAGS);
++ pkt += strlen(pkt) + 1;
++ if (*pkt)
++ {
++ add_rule(server, "mod_download_url", pkt, NO_FLAGS);
++ }
++ pkt += strlen(pkt) + 1;
++ if (*pkt)
++ {
++ add_rule(server, "mod_detail", pkt, NO_FLAGS);
++ }
++ pkt += strlen(pkt) + 1;
++ n = swap_long_from_little(pkt);
++ sprintf(number, "%d", n);
++ add_rule(server, "modversion", number, NO_FLAGS);
++ pkt += 4;
++ n = swap_long_from_little(pkt);
++ sprintf(number, "%d", n);
++ add_rule(server, "modsize", number, NO_FLAGS);
++ pkt += 4;
++ add_rule(server, "svonly", *pkt ? "1" : "0", NO_FLAGS);
++ pkt++;
++ add_rule(server, "cldll", *pkt ? "1" : "0", NO_FLAGS);
++ pkt++;
++ if (pkt < end)
+ {
+- free( val );
++ add_rule(server, "secure", *pkt ? "1" : "0", NO_FLAGS);
+ }
+-
+- free( var );
+- no_props--;
+ }
+ }
+- break;
+- default:
+- while ( rawpkt < end )
+- {
+- struct player *player;
+
+- if(rawpkt+4 > end)
++ if (get_player_info && server->num_players)
++ {
++ int requests = server->n_requests;
++ server->next_player_info = server->num_players - 1;
++ send_player_request_packet(server);
++ server->n_requests = requests; // prevent wrong ping
++ }
++ if (get_server_rules)
++ {
++ int requests = server->n_requests;
++ server->next_rule = "";
++ server->retry1 = n_retries;
++ send_rule_request_packet(server);
++ server->n_requests = requests; // prevent wrong ping
++ }
++ }
++ /* 'players' response */
++ else if (rawpkt[4] == 'D' && server->players == NULL)
++ {
++ unsigned int n = 0, temp;
++ struct player *player;
++ struct player **last_player = &server->players;
++ if ((unsigned int)rawpkt[5] > server->num_players)
++ {
++ server->num_players = (unsigned int)rawpkt[5];
++ } pkt = &rawpkt[6];
++ rawpkt[pktlen] = '\0';
++ while (1)
++ {
++ if (*pkt != n + 1)
+ {
+- malformed_packet(server, "player packet too short");
+- return 1;
++ break;
+ }
+-
+- if ( NULL == ( player = add_player( server, swap_long_from_little(rawpkt) ) ) )
++ n++;
++ pkt++;
++ player = (struct player*)calloc(1, sizeof(struct player));
++ player->name = strdup(pkt);
++ pkt += strlen(pkt) + 1;
++ memcpy(&player->frags, pkt, 4);
++ pkt += 4;
++ memcpy(&temp, pkt, 4);
++ pkt += 4;
++ if (big_endian)
++ {
++ player->frags = swap_long(&player->frags);
++ }
++ player->connect_time = swap_float_from_little(&temp);
++ *last_player = player;
++ last_player = &player->next;
++ }
++ if (n > server->num_players)
++ {
++ server->num_players = n;
++ }
++ server->next_player_info = server->num_players;
++ }
++ /* 'rules' response */
++ else if (rawpkt[4] == 'E' && server->next_rule != NULL)
++ {
++ int n = 0;
++ n = ((unsigned char*)rawpkt)[5] + ((unsigned char*)rawpkt)[6] *256;
++ pkt = &rawpkt[7];
++ while (n)
++ {
++ char *key = pkt;
++ char *value;
++ pkt += strlen(pkt) + 1;
++ if (pkt > end)
+ {
+- return 0;
++ break;
+ }
+-
+- player->name = ut2003_strdup( rawpkt+4, end, &rawpkt );
+- if(rawpkt+8 > end)
++ value = pkt;
++ pkt += strlen(pkt) + 1;
++ if (pkt > end)
+ {
+- malformed_packet(server, "player packet too short");
+- return 1;
++ break;
+ }
+- player->ping = swap_long_from_little(rawpkt);
+- rawpkt+= 4;
+- player->frags = swap_long_from_little(rawpkt);
+- rawpkt+= 4;
+- {
+- unsigned team = swap_long_from_little(rawpkt);
+- rawpkt+= 4;
+- player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+- if(team & 1<<29)
+- player->team_name = "red";
+- else if(team & 1<<30)
+- player->team_name = "blue";
++ if (key[0] == 's' && strcmp(key, "sv_password") == 0)
++ {
++ add_rule(server, key, value, CHECK_DUPLICATE_RULES);
++ }
++ else
++ {
++ add_rule(server, key, value, NO_FLAGS);
+ }
++ n--;
+ }
++ server->next_rule = NULL;
++ }
++ else if (rawpkt[4] != 'E' && rawpkt[4] != 'D' && rawpkt[4] != 'm' && rawpkt[4] != 'C' && show_errors)
++ {
++ /* if ( pkt_count) { rawpkt-= 9; pktlen+= 9; } */
++ fprintf(stderr, "Odd packet from HL server %s (packet len %d)\n", server->arg, pktlen);
++ print_packet(server, rawpkt, pktlen);
+ }
+
+- return 0;
++ return DONE_AUTO;
+ }
+
+
+-char *
+-get_rule( struct qserver *server, char *name)
++query_status_t deal_with_tribes_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- struct rule *rule;
+- rule= server->rules;
+- for ( ; rule != NULL; rule= rule->next) {
+- if ( strcmp( name, rule->name) == 0)
+- return rule->value;
+- }
+- return NULL;
+-}
++ unsigned char *pkt, *end;
++ int len, pnum, ping, packet_loss, n_teams, t;
++ struct player *player;
++ struct player **teams = NULL;
++ struct player **last_player = &server->players;
++ char buf[24];
+
+-void
+-deal_with_ut2003_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
++ debug( 2, "deal_with_tribes_packet %p, %d", server, pktlen );
+
+- // For protocol spec see:
+- // http://unreal.student.utwente.nl/UT2003-queryspec.html
++ if (server->server_name == NULL)
++ {
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++ }
++ else
++ {
++ gettimeofday(&server->packet_time1, NULL);
++ }
+
+- char *end;
+- int error = 0, before;
+- unsigned int packet_header;
++ if (pktlen < sizeof(tribes_info_reponse))
++ {
++ return PKT_ERROR;
++ }
+
+- rawpkt[pktlen]= '\0';
+- end = &rawpkt[pktlen];
++ if (strncmp(rawpkt, tribes_players_reponse, sizeof(tribes_players_reponse)) != 0)
++ {
++ return PKT_ERROR;
++ }
+
+- packet_header = swap_long_from_little( &rawpkt[0] );
+- rawpkt += 4;
++ pkt = (unsigned char*) &rawpkt[sizeof(tribes_info_reponse)];
+
+- server->protocol_version = packet_header;
+- if (
+- packet_header != 0x77 // Pariah Demo?
+- && packet_header != 0x78 // UT2003 Demo
+- && packet_header != 0x79 // UT2003 Retail
+- && packet_header != 0x7e // Unreal2 XMP
+- && packet_header != 0x7f // UT2004 Demo
+- && packet_header != 0x80 // UT2004 Retail
+- )
++ len = *pkt; /* game name: "Tribes" */
++ add_nrule(server, "gamename", (char*)pkt + 1, len);
++ pkt += len + 1;
++ len = *pkt; /* version */
++ add_nrule(server, "version", (char*)pkt + 1, len);
++ pkt += len + 1;
++ len = *pkt; /* server name */
++ server->server_name = strndup((char*)pkt + 1, len);
++ pkt += len + 1;
++ add_rule(server, "dedicated", *pkt ? "1" : "0", NO_FLAGS);
++ pkt++; /* flag: dedicated server */
++ add_rule(server, "needpass", *pkt ? "1" : "0", NO_FLAGS);
++ pkt++; /* flag: password on server */
++ server->num_players = *pkt++;
++ server->max_players = *pkt++;
++
++ sprintf(buf, "%u", (unsigned int)pkt[0] + (unsigned int)pkt[1] *256);
++ add_rule(server, "cpu", buf, NO_FLAGS);
++ pkt++; /* cpu speed, lsb */
++ pkt++; /* cpu speed, msb */
++
++ len = *pkt; /* Mod (game) */
++ add_nrule(server, "mods", (char*)pkt + 1, len);
++ pkt += len + 1;
++
++ len = *pkt; /* game (mission): "C&H" */
++ add_nrule(server, "game", (char*)pkt + 1, len);
++ pkt += len + 1;
++
++ len = *pkt; /* Mission (map) */
++ server->map_name = strndup((char*)pkt + 1, len);
++ pkt += len + 1;
++
++ len = *pkt; /* description (contains Admin: and Email: ) */
++ debug( 2, "%.*s\n", len, pkt + 1);
++ pkt += len + 1;
++
++ n_teams = *pkt++; /* number of teams */
++ if (n_teams == 255)
+ {
+- malformed_packet(server, "Unknown type 0x%x", packet_header);
++ return PKT_ERROR;
+ }
++ sprintf(buf, "%d", n_teams);
++ add_rule(server, "numteams", buf, NO_FLAGS);
++
++ len = *pkt; /* first title */
++ debug( 2, "%.*s\n", len, pkt + 1);
++ pkt += len + 1;
++
++ len = *pkt; /* second title */
++ debug( 2, "%.*s\n", len, pkt + 1);
++ pkt += len + 1;
+
+- switch( rawpkt[0] )
++ if (n_teams > 1)
+ {
+- case 0x00:
+- // Server info
+- if ( server->server_name == NULL )
++ teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams);
++ for (t = 0; t < n_teams; t++)
+ {
+- server->ping_total+= time_delta( &packet_recv_time, &server->packet_time1);
+- }
++ teams[t] = (struct player*)calloc(1, sizeof(struct player));
++ teams[t]->number = TRIBES_TEAM;
++ teams[t]->team = t;
++ len = *pkt; /* team name */
++ teams[t]->name = strndup((char*)pkt + 1, len);
++ debug( 2, "team#0 <%.*s>\n", len, pkt + 1);
++ pkt += len + 1;
+
+- error = ut2003_basic_packet( server, rawpkt, end );
+- if ( ! error )
+- {
+- if(get_server_rules || get_player_info)
++ len = *pkt; /* team score */
++ if (len > 2)
+ {
+- int requests = server->n_requests;
+- server->next_rule = "";
+- server->retry1 = n_retries;
+- server->retry2 = 0; // don't wait for player packet
+- send_rule_request_packet( server);
+- server->n_requests = requests; // would produce wrong ping
++ strncpy(buf, (char*)pkt + 1+3, len - 3);
++ buf[len - 3] = '\0';
++ }
++ else
++ {
++ debug( 2, "%s score len %d\n", server->arg, len);
++ buf[0] = '\0';
+ }
++ teams[t]->frags = atoi(buf);
++ debug( 2, "team#0 <%.*s>\n", len - 3, pkt + 1+3);
++ pkt += len + 1;
+ }
+- break;
+-
+- case 0x01:
+- // Game info
+- ut2003_rule_packet( server, rawpkt, end );
+- server->next_rule = "";
+- server->retry1 = 0; /* we received at least one rule packet so
+- no need to retry. We'd get double
+- entries otherwise. */
+- break;
++ }
++ else
++ {
++ len = *pkt; /* DM team? */
++ debug( 2, "%.*s\n", len, pkt + 1);
++ pkt += len + 1;
++ pkt++;
++ n_teams = 0;
++ }
+
+- case 0x02:
+- // Player info
+- before = server->n_player_info;
+- error = ut2003_player_packet( server, rawpkt, end);
+- if (before == server->n_player_info )
++ pnum = 0;
++ while ((char*)pkt < (rawpkt + pktlen))
++ {
++ ping = (unsigned int) *pkt << 2;
++ pkt++;
++ packet_loss = *pkt;
++ pkt++;
++ debug( 2, "player#%d, team #%d\n", pnum, (int) *pkt);
++ pkt++;
++ len = *pkt;
++ if ((char*)pkt + len > (rawpkt + pktlen))
+ {
+- error = 1;
++ break;
+ }
+- break;
+-
+- case 0x10:
+- // Pariah Server info
+- if ( server->server_name == NULL )
++ player = (struct player*)calloc(1, sizeof(struct player));
++ player->team = pkt[ - 1];
++ if (n_teams && player->team < n_teams)
+ {
+- server->ping_total+= time_delta( &packet_recv_time, &server->packet_time1);
++ player->team_name = teams[player->team]->name;
+ }
+-
+- error = pariah_basic_packet( server, rawpkt, end );
+- if ( ! error )
++ else if (player->team == 255 && n_teams)
+ {
+- // N.B. pariah always sends a rules and players packet
+- int requests = server->n_requests;
+- server->next_rule = "";
+- server->retry1 = n_retries;
+- server->retry2 = 0;
+- server->n_requests = requests; // would produce wrong ping
++ player->team_name = "Unknown";
+ }
+- break;
+-
+- case 0x11:
+- // Game info
+- pariah_rule_packet( server, rawpkt, end );
+- server->retry1 = 0; /* we received at least one rule packet so
+- no need to retry. We'd get double
+- entries otherwise. */
+- break;
+-
+- case 0x12:
+- // Player info
+- before = server->n_player_info;
+- pariah_player_packet( server, rawpkt, end );
+- if ( before == server->n_player_info )
++ player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++ player->ping = ping;
++ player->packet_loss = packet_loss;
++ player->name = strndup((char*)pkt + 1, len);
++ debug( 2, "player#%d, name %.*s\n", pnum, len, pkt + 1);
++ pkt += len + 1;
++ len = *pkt;
++ debug( 2, "player#%d, info <%.*s>\n", pnum, len, pkt + 1);
++ end = (unsigned char*)strchr((char*)pkt + 9, 0x9);
++ if (end)
+ {
+- error = 1;
++ strncpy(buf, (char*)pkt + 9, end - (pkt + 9));
++ buf[end - (pkt + 9)] = '\0';
++ player->frags = atoi(buf);
++ debug( 2, "player#%d, score <%.*s>\n", pnum, (unsigned)(end - (pkt + 9)), pkt + 9);
+ }
+- break;
+
++ *last_player = player;
++ last_player = &player->next;
+
+- default:
+- malformed_packet(server, "Unknown packet type 0x%x", (unsigned)rawpkt[0]);
+- break;
++ pkt += len + 1;
++ pnum++;
+ }
+
+- /* don't cleanup if we fetch server rules. We would lose
+- * rule packets as we don't know how many we get
+- * We do clean up if we don't fetch server rules so we don't
+- * need to wait for timeout.
+- */
+- if (error
+- || (!get_server_rules && !get_player_info)
+- || (!get_server_rules && server->num_players == server->n_player_info)
+- || (server->next_rule == NULL && server->num_players == server->n_player_info))
++ for (t = n_teams; t;)
+ {
+- cleanup_qserver( server, 1 );
++ t--;
++ teams[t]->next = server->players;
++ server->players = teams[t];
+ }
++ free(teams);
++
++ return DONE_AUTO;
+ }
+
+-int
+-deal_with_unrealmaster_packet( struct qserver *server, char *rawpkt, int pktlen)
++void get_tribes2_player_type(struct player *player)
+ {
+- if ( pktlen == 0) {
+- cleanup_qserver( server, 1);
+- return 0;
+- }
+- print_packet( server, rawpkt, pktlen);
+- puts( "--");
+- return 0;
++ char *name = player->name;
++ for (; *name; name++)
++ {
++ switch (*name)
++ {
++ case 0x8:
++ player->type_flag = PLAYER_TYPE_NORMAL;
++ continue;
++ case 0xc:
++ player->type_flag = PLAYER_TYPE_ALIAS;
++ continue;
++ case 0xe:
++ player->type_flag = PLAYER_TYPE_BOT;
++ continue;
++ case 0xb:
++ break;
++ default:
++ continue;
++ }
++ name++;
++ if (isprint(*name))
++ {
++ char *n = name;
++ for (; isprint(*n); n++)
++ ;
++ player->tribe_tag = strndup(name, n - name);
++ name = n;
++ }
++ if (! *name)
++ {
++ break;
++ }
++ }
+ }
+
+-/* Returns 1 if the query is done (server may be freed) and 0 if not.
+- */
+-int
+-deal_with_halflife_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_tribes2_packet(struct qserver *server, char *pkt, int pktlen)
+ {
+- char *pkt;
+- char *end= &rawpkt[pktlen];
+- int pkt_index= 0, pkt_max= 0;
+- char number[16];
+- short pkt_id;
+-
+- if ( server->server_name == NULL)
+- server->ping_total+= time_delta( &packet_recv_time,
+- &server->packet_time1);
+-
+- if ( pktlen < 5)
+- return cleanup_qserver( server, 1);
+-
+- if ( ((rawpkt[0] != '\377' && rawpkt[0] != '\376') || rawpkt[1] != '\377' ||
+- rawpkt[2] != '\377' || rawpkt[3] != '\377') && show_errors) {
+- unsigned int ipaddr= ntohl(server->ipaddr);
+- fprintf( stderr,
+- "Odd packet from server %d.%d.%d.%d:%hu, processing ...\n",
+- (ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+- (ipaddr>>8)&0xff, ipaddr&0xff, ntohs(server->port));
+- print_packet( server, rawpkt, pktlen);
+- }
+-
+- if ( ((unsigned char*)rawpkt)[0] == 0xfe) {
+- SavedData *sdata;
+- pkt_index= ((unsigned char*)rawpkt)[8] >> 4;
+- pkt_max= ((unsigned char*)rawpkt)[8] & 0xf;
+- memcpy( &pkt_id, &rawpkt[4], 2);
+-
+- if ( server->saved_data.data == NULL)
+- sdata= & server->saved_data;
+- else {
+- sdata= (SavedData*) calloc( 1, sizeof(SavedData));
+- sdata->next= server->saved_data.next;
+- server->saved_data.next= sdata;
+- }
+-
+- sdata->pkt_index= pkt_index;
+- sdata->pkt_max= pkt_max;
+- sdata->pkt_id= pkt_id;
+- sdata->datalen= pktlen-9;
+- sdata->data= (char*) malloc( pktlen-9);
+- memcpy( sdata->data, &rawpkt[9], pktlen-9);
++ char str[256], *pktstart = pkt, *term, *start;
++ unsigned int minimum_net_protocol, build_version, i, t, len, s, status;
++ unsigned int net_protocol;
++ unsigned short cpu_speed;
++ int n_teams = 0, n_players;
++ struct player **teams = NULL, *player;
++ struct player **last_player = &server->players;
++ int query_version;
+
+- /* combine_packets will call us recursively */
+- return combine_packets( server);
++ debug( 2, "deal_with_tribes2_packet %p, %d", server, pktlen );
+
+-/*
+-fprintf( OF, "pkt_index %d pkt_max %d\n", pkt_index, pkt_max);
+- rawpkt+= 9;
+- pktlen-= 9;
+-*/
+- }
++ pkt[pktlen] = '\0';
+
+- /* 'info' response */
+- if ( rawpkt[4] == 'C' || rawpkt[4] == 'm') {
+- if ( server->server_name != NULL)
+- return 0;
+- pkt= &rawpkt[5];
+- server->address= strdup( pkt);
+- pkt+= strlen(pkt)+1;
+- server->server_name= strdup( pkt);
+- pkt+= strlen(pkt)+1;
+- server->map_name= strdup( pkt);
+- pkt+= strlen(pkt)+1;
+-
+- if ( *pkt)
+- add_rule( server, "gamedir", pkt, NO_FLAGS);
+- if ( *pkt && strcmp( pkt, "valve") != 0) {
+- server->game= add_rule( server, "game", pkt, NO_FLAGS)->value;
+- server->flags |= FLAG_DO_NOT_FREE_GAME;
+- }
+- pkt+= strlen(pkt)+1;
+- if ( *pkt)
+- add_rule( server, "gamename", pkt, NO_FLAGS);
+- pkt+= strlen(pkt)+1;
+-
+- server->num_players= (unsigned int)pkt[0];
+- server->max_players= (unsigned int)pkt[1];
+- pkt+= 2;
+- if ( pkt < end) {
+- int protocol= *((unsigned char *)pkt);
+- sprintf( number, "%d", protocol);
+- add_rule( server, "protocol", number, NO_FLAGS);
+- pkt++;
+- }
+-
+- if ( rawpkt[4] == 'm') {
+- if ( *pkt == 'd')
+- add_rule( server, "sv_type", "dedicated", NO_FLAGS);
+- else if ( *pkt == 'l')
+- add_rule( server, "sv_type", "listen", NO_FLAGS);
+- else
+- add_rule( server, "sv_type", "?", NO_FLAGS);
+- pkt++;
+- if ( *pkt == 'w')
+- add_rule( server, "sv_os", "windows", NO_FLAGS);
+- else if ( *pkt == 'l')
+- add_rule( server, "sv_os", "linux", NO_FLAGS);
+- else {
+- char str[2]= "\0";
+- str[0]= *pkt;
+- add_rule( server, "sv_os", str, NO_FLAGS);
+- }
+- pkt++;
+- add_rule( server, "sv_password", *pkt ? "1" : "0", NO_FLAGS);
+- pkt++;
+- add_rule( server, "mod", *pkt ? "1" : "0", NO_FLAGS);
+- if ( *pkt) {
+- int n;
+- /* pull out the mod infomation */
+- pkt++;
+- add_rule( server, "mod_info_url", pkt, NO_FLAGS);
+- pkt+= strlen( pkt)+1;
+- if ( *pkt)
+- add_rule( server, "mod_download_url", pkt, NO_FLAGS);
+- pkt+= strlen( pkt)+1;
+- if ( *pkt)
+- add_rule( server, "mod_detail", pkt, NO_FLAGS);
+- pkt+= strlen( pkt)+1;
+- n= swap_long_from_little( pkt);
+- sprintf( number, "%d", n);
+- add_rule( server, "modversion", number, NO_FLAGS);
+- pkt+= 4;
+- n= swap_long_from_little( pkt);
+- sprintf( number, "%d", n);
+- add_rule( server, "modsize", number, NO_FLAGS);
+- pkt+= 4;
+- add_rule( server, "svonly", *pkt ? "1" : "0", NO_FLAGS);
+- pkt++;
+- add_rule( server, "cldll", *pkt ? "1" : "0", NO_FLAGS);
+- pkt++;
+- if ( pkt < end)
+- add_rule( server, "secure", *pkt ? "1" : "0", NO_FLAGS);
+- }
+- }
+-
+- if ( get_player_info && server->num_players) {
+- int requests = server->n_requests;
+- server->next_player_info= server->num_players-1;
+- send_player_request_packet( server);
+- server->n_requests = requests; // prevent wrong ping
+- }
+- if ( get_server_rules) {
+- int requests = server->n_requests;
+- server->next_rule= "";
+- server->retry1= n_retries;
+- send_rule_request_packet( server);
+- server->n_requests = requests; // prevent wrong ping
+- }
+- }
+- /* 'players' response */
+- else if ( rawpkt[4] == 'D' && server->players == NULL) {
+- unsigned int n= 0, temp;
+- struct player *player;
+- struct player **last_player= & server->players;
+- if ( (unsigned int)rawpkt[5] > server->num_players)
+- server->num_players= (unsigned int)rawpkt[5];
+- pkt= &rawpkt[6];
+- rawpkt[pktlen]= '\0';
+- while (1) {
+- if ( *pkt != n+1)
+- break;
+- n++;
+- pkt++;
+- player= (struct player*) calloc( 1, sizeof(struct player));
+- player->name= strdup( pkt);
+- pkt+= strlen(pkt)+1;
+- memcpy( &player->frags, pkt, 4);
+- pkt+= 4;
+- memcpy( &temp, pkt, 4);
+- pkt+= 4;
+- if ( big_endian) {
+- player->frags= swap_long( &player->frags);
+- }
+- player->connect_time= swap_float_from_little(&temp);
+- *last_player= player;
+- last_player= & player->next;
+- }
+- if ( n > server->num_players)
+- server->num_players= n;
+- server->next_player_info= server->num_players;
+- }
+- /* 'rules' response */
+- else if ( rawpkt[4] == 'E' && server->next_rule != NULL) {
+- int n= 0;
+- n= ((unsigned char*)rawpkt)[5] + ((unsigned char *)rawpkt)[6]*256;
+- pkt= &rawpkt[7];
+- while ( n) {
+- char *key= pkt;
+- char *value;
+- pkt+= strlen(pkt)+1;
+- if ( pkt > end)
+- break;
+- value= pkt;
+- pkt+= strlen(pkt)+1;
+- if ( pkt > end)
+- break;
+- if ( key[0] == 's' && strcmp( key, "sv_password") == 0)
+- add_rule( server, key, value, CHECK_DUPLICATE_RULES);
+- else
+- add_rule( server, key, value, NO_FLAGS);
+- n--;
++ if (server->server_name == NULL)
++ {
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+ }
+- server->next_rule= NULL;
+- }
+- else if ( rawpkt[4] != 'E' && rawpkt[4] != 'D' && rawpkt[4] != 'm' &&
+- rawpkt[4] != 'C' && show_errors) {
+-/* if ( pkt_count) { rawpkt-= 9; pktlen+= 9; } */
+- fprintf( stderr, "Odd packet from HL server %s (packet len %d)\n",
+- server->arg, pktlen);
+- print_packet( server, rawpkt, pktlen);
+- }
++ /*
++ else
++ gettimeofday( &server->packet_time1, NULL);
++ */
+
+- return cleanup_qserver( server, 0);
+-}
++ if (pkt[0] == TRIBES2_RESPONSE_PING)
++ {
++ if (pkt[6] < 4 || pkt[6] > 12 || strncmp(pkt + 7, "VER", 3) != 0)
++ {
++ return PKT_ERROR;
++ }
+
++ strncpy(str, pkt + 10, pkt[6] - 3);
++ str[pkt[6] - 3] = '\0';
++ query_version = atoi(str);
++ add_nrule(server, "queryversion", pkt + 7, pkt[6]);
++ pkt += 7+pkt[6];
+
+-static int tribes_debug= 0;
++ server->protocol_version = query_version;
++ if (query_version != 3 && query_version != 5)
++ {
++ server->server_name = strdup("Unknown query version");
++ return PKT_ERROR;
++ }
+
+-void
+-deal_with_tribes_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
+- unsigned char *pkt, *end;
+- int len, pnum, ping, packet_loss, n_teams, t;
+- struct player *player;
+- struct player **teams= NULL;
+- struct player **last_player= & server->players;
+- char buf[24];
++ if (query_version == 5)
++ {
++ net_protocol = swap_long_from_little(pkt);
++ sprintf(str, "%u", net_protocol);
++ add_rule(server, "net_protocol", str, NO_FLAGS);
++ pkt += 4;
++ }
++ minimum_net_protocol = swap_long_from_little(pkt);
++ sprintf(str, "%u", minimum_net_protocol);
++ add_rule(server, "minimum_net_protocol", str, NO_FLAGS);
++ pkt += 4;
++ build_version = swap_long_from_little(pkt);
++ sprintf(str, "%u", build_version);
++ add_rule(server, "build_version", str, NO_FLAGS);
++ pkt += 4;
+
+- if ( server->server_name == NULL)
+- {
+- server->ping_total+= time_delta( &packet_recv_time,
+- &server->packet_time1);
++ server->server_name = strndup(pkt + 1, *(unsigned char*)(pkt));
++
++ /* Always send the player request because the ping packet
++ * contains very little information */
++ send_player_request_packet(server);
++ return 0;
+ }
+- else
++ else if (pkt[0] != TRIBES2_RESPONSE_INFO)
+ {
+- gettimeofday( &server->packet_time1, NULL);
++ return PKT_ERROR;
+ }
+
+- if ( pktlen < sizeof( tribes_info_reponse))
++ pkt += 6;
++ for (i = 0; i < *(unsigned char*)pkt; i++)
++ if (!isprint(pkt[i + 1]))
+ {
+- cleanup_qserver( server, 1);
+- return;
+- }
++ return PKT_ERROR;
++ }
++ add_nrule(server, server->type->game_rule, pkt + 1, *(unsigned char*)pkt);
++ server->game = strndup(pkt + 1, *(unsigned char*)pkt);
++ pkt += *pkt + 1;
++ add_nrule(server, "mission", pkt + 1, *(unsigned char*)pkt);
++ pkt += *pkt + 1;
++ server->map_name = strndup(pkt + 1, *(unsigned char*)pkt);
++ pkt += *pkt + 1;
+
+- if ( strncmp( rawpkt, tribes_players_reponse,
+- sizeof( tribes_players_reponse)) != 0)
++ status = *(unsigned char*)pkt;
++ sprintf(str, "%u", status);
++ add_rule(server, "status", str, NO_FLAGS);
++ if (status &TRIBES2_STATUS_DEDICATED)
+ {
+- cleanup_qserver( server, 1);
+- return;
++ add_rule(server, "dedicated", "1", NO_FLAGS);
+ }
+-
+- pkt= (unsigned char*) &rawpkt[sizeof( tribes_info_reponse)];
+-
+- len= *pkt; /* game name: "Tribes" */
+- add_nrule( server, "gamename", (char*)pkt+1, len);
+- pkt+= len+1;
+- len= *pkt; /* version */
+- add_nrule( server, "version", (char*)pkt+1, len);
+- pkt+= len+1;
+- len= *pkt; /* server name */
+- server->server_name= strndup( (char*)pkt+1, len);
+- pkt+= len+1;
+- add_rule( server, "dedicated", *pkt?"1":"0", NO_FLAGS);
+- pkt++; /* flag: dedicated server */
+- add_rule( server, "needpass", *pkt?"1":"0", NO_FLAGS);
+- pkt++; /* flag: password on server */
+- server->num_players= *pkt++;
+- server->max_players= *pkt++;
+-
+- sprintf( buf, "%u", (unsigned int)pkt[0] + (unsigned int)pkt[1]*256);
+- add_rule( server, "cpu", buf, NO_FLAGS);
+- pkt++; /* cpu speed, lsb */
+- pkt++; /* cpu speed, msb */
+-
+- len= *pkt; /* Mod (game) */
+- add_nrule( server, "mods", (char*)pkt+1, len);
+- pkt+= len+1;
+-
+- len= *pkt; /* game (mission): "C&H" */
+- add_nrule( server, "game", (char*)pkt+1, len);
+- pkt+= len+1;
+-
+- len= *pkt; /* Mission (map) */
+- server->map_name= strndup( (char*)pkt+1, len);
+- pkt+= len+1;
+-
+- len= *pkt; /* description (contains Admin: and Email: ) */
+-if ( tribes_debug) printf( "%.*s\n", len, pkt+1);
+- pkt+= len+1;
+-
+- n_teams= *pkt++; /* number of teams */
+- if ( n_teams == 255) {
+- cleanup_qserver( server, 1);
+- return;
+- }
+- sprintf( buf, "%d", n_teams);
+- add_rule( server, "numteams", buf, NO_FLAGS);
+-
+- len= *pkt; /* first title */
+-if ( tribes_debug) printf( "%.*s\n", len, pkt+1);
+- pkt+= len+1;
+-
+- len= *pkt; /* second title */
+-if ( tribes_debug) printf( "%.*s\n", len, pkt+1);
+- pkt+= len+1;
+-
+- if ( n_teams > 1) {
+- teams= (struct player**) calloc( 1, sizeof(struct player*) * n_teams);
+- for ( t= 0; t < n_teams; t++) {
+- teams[t]= (struct player*) calloc(1, sizeof(struct player));
+- teams[t]->number= TRIBES_TEAM;
+- teams[t]->team= t;
+- len= *pkt; /* team name */
+- teams[t]->name= strndup( (char*)pkt+1, len);
+-if ( tribes_debug) printf( "team#0 <%.*s>\n", len, pkt+1);
+- pkt+= len+1;
+-
+- len= *pkt; /* team score */
+-if ( len <= 2 && tribes_debug) printf( "%s score len %d\n", server->arg, len);
+- if ( len > 2) {
+- strncpy( buf, (char*)pkt+1+3, len-3);
+- buf[len-3]= '\0';
+- }
+- else
+- buf[0]= '\0';
+- teams[t]->frags= atoi( buf);
+-if ( tribes_debug) printf( "team#0 <%.*s>\n", len-3, pkt+1+3);
+- pkt+= len+1;
++ if (status &TRIBES2_STATUS_PASSWORD)
++ {
++ add_rule(server, "password", "1", NO_FLAGS);
++ }
++ if (status &TRIBES2_STATUS_LINUX)
++ {
++ add_rule(server, "linux", "1", NO_FLAGS);
++ }
++ if (status &TRIBES2_STATUS_TEAMDAMAGE)
++ {
++ add_rule(server, "teamdamage", "1", NO_FLAGS);
++ }
++ if (server->protocol_version == 3)
++ {
++ if (status &TRIBES2_STATUS_TOURNAMENT_VER3)
++ {
++ add_rule(server, "tournament", "1", NO_FLAGS);
++ }
++ if (status &TRIBES2_STATUS_NOALIAS_VER3)
++ {
++ add_rule(server, "no_aliases", "1", NO_FLAGS);
++ }
++ }
++ else
++ {
++ if (status &TRIBES2_STATUS_TOURNAMENT)
++ {
++ add_rule(server, "tournament", "1", NO_FLAGS);
++ }
++ if (status &TRIBES2_STATUS_NOALIAS)
++ {
++ add_rule(server, "no_aliases", "1", NO_FLAGS);
++ }
+ }
+- }
+- else {
+- len= *pkt; /* DM team? */
+-if ( tribes_debug) printf( "%.*s\n", len, pkt+1);
+- pkt+= len+1;
+ pkt++;
+- n_teams= 0;
+- }
+-
+- pnum= 0;
+- while ( (char*)pkt < (rawpkt+pktlen)) {
+- ping= (unsigned int)*pkt << 2;
++ server->num_players = *(unsigned char*)pkt;
+ pkt++;
+- packet_loss= *pkt;
++ server->max_players = *(unsigned char*)pkt;
+ pkt++;
+-if ( tribes_debug) printf( "player#%d, team #%d\n", pnum, (int)*pkt);
++ sprintf(str, "%u", *(unsigned char*)pkt);
++ add_rule(server, "bot_count", str, NO_FLAGS);
+ pkt++;
+- len= *pkt;
+- if ( (char*)pkt+len > (rawpkt+pktlen))
+- break;
+- player= (struct player*) calloc( 1, sizeof(struct player));
+- player->team= pkt[-1];
+- if ( n_teams && player->team < n_teams)
+- player->team_name= teams[player->team]->name;
+- else if ( player->team == 255 && n_teams)
+- player->team_name= "Unknown";
+- player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+- player->ping= ping;
+- player->packet_loss= packet_loss;
+- player->name= strndup( (char*)pkt+1, len);
+-if ( tribes_debug) printf( "player#%d, name %.*s\n", pnum, len, pkt+1);
+- pkt+= len+1;
+- len= *pkt;
+-if ( tribes_debug) printf( "player#%d, info <%.*s>\n", pnum, len, pkt+1);
+- end= (unsigned char*) strchr( (char*)pkt+9, 0x9);
+- if ( end) {
+- strncpy( buf, (char*)pkt+9, end-(pkt+9));
+- buf[end-(pkt+9)]= '\0';
+- player->frags= atoi( buf);
+-if ( tribes_debug) printf( "player#%d, score <%.*s>\n", pnum, (unsigned)(end-(pkt+9)), pkt+9);
+- }
+- *last_player= player;
+- last_player= & player->next;
++ cpu_speed = swap_short_from_little(pkt);
++ sprintf(str, "%hu", cpu_speed);
++ add_rule(server, "cpu_speed", str, NO_FLAGS);
++ pkt += 2;
+
+- pkt+= len+1;
+- pnum++;
+- }
++ if (strcmp(server->server_name, "VER3") == 0)
++ {
++ free(server->server_name);
++ server->server_name = strndup(pkt + 1, *(unsigned char*)pkt);
++ }
++ else
++ {
++ add_nrule(server, "info", pkt + 1, *(unsigned char*)pkt);
++ }
+
+- for ( t= n_teams; t;) {
+- t--;
+- teams[t]->next= server->players;
+- server->players= teams[t];
+- }
+- free( teams);
++ pkt += *(unsigned char*)pkt + 1;
++ len = swap_short_from_little(pkt);
++ pkt += 2;
++ start = pkt;
++ if (len + (pkt - pktstart) > pktlen)
++ {
++ len -= (len + (pkt - pktstart)) - pktlen;
++ }
+
+- cleanup_qserver( server, 0);
+-}
++ if (len == 0 || pkt - pktstart >= pktlen)
++ {
++ goto info_done;
++ }
+
+-void
+-get_tribes2_player_type( struct player *player)
+-{
+- char *name= player->name;
+- for ( ; *name; name++) {
+- switch ( *name) {
+- case 0x8: player->type_flag= PLAYER_TYPE_NORMAL; continue;
+- case 0xc: player->type_flag= PLAYER_TYPE_ALIAS; continue;
+- case 0xe: player->type_flag= PLAYER_TYPE_BOT; continue;
+- case 0xb: break;
+- default: continue;
+- }
+- name++;
+- if ( isprint( *name)) {
+- char *n= name;
+- for ( ; isprint(*n); n++)
+- ;
+- player->tribe_tag= strndup( name, n-name);
+- name= n;
++ term = strchr(pkt, 0xa);
++ if (!term)
++ {
++ goto info_done;
+ }
+- if ( !*name)
+- break;
+- }
+-}
++ *term = '\0';
++ n_teams = atoi(pkt);
++ sprintf(str, "%d", n_teams);
++ add_rule(server, "numteams", str, NO_FLAGS);
++ pkt = term + 1;
+
+-void
+-deal_with_tribes2_packet( struct qserver *server, char *pkt, int pktlen)
+-{
+- char str[256], *pktstart= pkt, *term, *start;
+- unsigned int minimum_net_protocol, build_version, i, t, len, s, status;
+- unsigned int net_protocol;
+- unsigned short cpu_speed;
+- int n_teams= 0, n_players;
+- struct player **teams= NULL, *player;
+- struct player **last_player= & server->players;
+- int query_version;
+-
+- pkt[pktlen]= '\0';
+-
+- if ( server->server_name == NULL)
+- server->ping_total+= time_delta( &packet_recv_time,
+- &server->packet_time1);
+-/*
+- else
+- gettimeofday( &server->packet_time1, NULL);
+-*/
++ if (pkt - pktstart >= pktlen)
++ {
++ goto info_done;
++ }
+
+- if ( pkt[0] == TRIBES2_RESPONSE_PING) {
+- if ( pkt[6] < 4 || pkt[6] > 12 || strncmp( pkt+7, "VER", 3) != 0) {
+- cleanup_qserver( server, 1);
+- return;
+- }
+- strncpy( str, pkt+10, pkt[6]-3);
+- str[ pkt[6]-3]= '\0';
+- query_version= atoi( str);
+- add_nrule(server,"queryversion", pkt+7, pkt[6]);
+- pkt+= 7 + pkt[6];
+-
+- server->protocol_version= query_version;
+- if ( query_version != 3 && query_version != 5) {
+- server->server_name= strdup( "Unknown query version");
+- cleanup_qserver( server, 1);
+- return;
+- }
+-
+- if ( query_version == 5) {
+- net_protocol= swap_long_from_little( pkt);
+- sprintf( str, "%u", net_protocol);
+- add_rule(server,"net_protocol",str, NO_FLAGS);
+- pkt+= 4;
+- }
+- minimum_net_protocol= swap_long_from_little( pkt);
+- sprintf( str, "%u", minimum_net_protocol);
+- add_rule(server,"minimum_net_protocol",str, NO_FLAGS);
+- pkt+= 4;
+- build_version= swap_long_from_little( pkt);
+- sprintf( str, "%u", build_version);
+- add_rule(server,"build_version",str, NO_FLAGS);
+- pkt+= 4;
+-
+- server->server_name= strndup( pkt+1, *(unsigned char*)(pkt));
+-
+- /* Always send the player request because the ping packet
+- * contains very little information */
+- send_player_request_packet(server);
+- return;
+- }
+- else if ( pkt[0] != TRIBES2_RESPONSE_INFO) {
+- cleanup_qserver( server, 1);
+- return;
+- }
++ teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams);
++ for (t = 0; t < n_teams; t++)
++ {
++ teams[t] = (struct player*)calloc(1, sizeof(struct player));
++ teams[t]->number = TRIBES_TEAM;
++ teams[t]->team = t;
++ /* team name */
++ term = strchr(pkt, 0x9);
++ if (!term)
++ {
++ n_teams = t;
++ goto info_done;
++ } teams[t]->name = strndup(pkt, term - pkt);
++ pkt = term + 1;
++ term = strchr(pkt, 0xa);
++ if (!term)
++ {
++ n_teams = t;
++ goto info_done;
++ }
++ *term = '\0';
++ teams[t]->frags = atoi(pkt);
++ pkt = term + 1;
++ if (pkt - pktstart >= pktlen)
++ {
++ goto info_done;
++ }
++ }
+
+- pkt+= 6;
+- for ( i= 0; i < *(unsigned char *)pkt; i++)
+- if ( !isprint(pkt[i+1])) {
+- cleanup_qserver( server, 1);
+- return;
+- }
+- add_nrule( server, server->type->game_rule, pkt+1, *(unsigned char *)pkt);
+- server->game= strndup( pkt+1, *(unsigned char *)pkt);
+- pkt+= *pkt + 1;
+- add_nrule( server, "mission", pkt+1, *(unsigned char *)pkt);
+- pkt+= *pkt + 1;
+- server->map_name= strndup( pkt+1, *(unsigned char *)pkt);
+- pkt+= *pkt + 1;
+-
+- status= *(unsigned char *)pkt;
+- sprintf( str, "%u", status);
+- add_rule( server, "status", str, NO_FLAGS);
+- if ( status & TRIBES2_STATUS_DEDICATED)
+- add_rule( server, "dedicated", "1", NO_FLAGS);
+- if ( status & TRIBES2_STATUS_PASSWORD)
+- add_rule( server, "password", "1", NO_FLAGS);
+- if ( status & TRIBES2_STATUS_LINUX)
+- add_rule( server, "linux", "1", NO_FLAGS);
+- if ( status & TRIBES2_STATUS_TEAMDAMAGE)
+- add_rule( server, "teamdamage", "1", NO_FLAGS);
+- if ( server->protocol_version == 3) {
+- if ( status & TRIBES2_STATUS_TOURNAMENT_VER3)
+- add_rule( server, "tournament", "1", NO_FLAGS);
+- if ( status & TRIBES2_STATUS_NOALIAS_VER3)
+- add_rule( server, "no_aliases", "1", NO_FLAGS);
+- }
+- else {
+- if ( status & TRIBES2_STATUS_TOURNAMENT)
+- add_rule( server, "tournament", "1", NO_FLAGS);
+- if ( status & TRIBES2_STATUS_NOALIAS)
+- add_rule( server, "no_aliases", "1", NO_FLAGS);
+- }
+- pkt++;
+- server->num_players= *(unsigned char *)pkt;
+- pkt++;
+- server->max_players= *(unsigned char *)pkt;
+- pkt++;
+- sprintf( str, "%u", *(unsigned char *)pkt);
+- add_rule( server, "bot_count", str, NO_FLAGS);
+- pkt++;
+- cpu_speed= swap_short_from_little( pkt);
+- sprintf( str, "%hu", cpu_speed);
+- add_rule( server, "cpu_speed", str, NO_FLAGS);
+- pkt+= 2;
+-
+- if ( strcmp( server->server_name, "VER3") == 0) {
+- free( server->server_name);
+- server->server_name= strndup( pkt+1, *(unsigned char*)pkt);
+- }
+- else
+- add_nrule( server, "info", pkt+1, *(unsigned char*)pkt);
++ term = strchr(pkt, 0xa);
++ if (!term || term - start >= len)
++ {
++ goto info_done;
++ }
++ *term = '\0';
++ n_players = atoi(pkt);
++ pkt = term + 1;
+
+- pkt+= *(unsigned char*)pkt + 1;
+- len= swap_short_from_little( pkt);
+- pkt+= 2;
+- start= pkt;
+- if ( len+(pkt-pktstart) > pktlen)
+- len-= (len+(pkt-pktstart)) - pktlen;
+-
+- if ( len == 0 || pkt-pktstart >= pktlen) goto info_done;
+-
+- term= strchr( pkt, 0xa);
+- if ( !term) goto info_done;
+- *term= '\0';
+- n_teams= atoi( pkt);
+- sprintf( str, "%d", n_teams);
+- add_rule( server, "numteams", str, NO_FLAGS);
+- pkt= term + 1;
+-
+- if ( pkt-pktstart >= pktlen) goto info_done;
+-
+- teams= (struct player**) calloc( 1, sizeof(struct player*) * n_teams);
+- for ( t= 0; t < n_teams; t++) {
+- teams[t]= (struct player*) calloc(1, sizeof(struct player));
+- teams[t]->number= TRIBES_TEAM;
+- teams[t]->team= t;
+- /* team name */
+- term= strchr( pkt, 0x9);
+- if ( !term) { n_teams= t; goto info_done; }
+- teams[t]->name= strndup( pkt, term-pkt);
+- pkt= term+1;
+- term= strchr( pkt, 0xa);
+- if ( !term) { n_teams= t; goto info_done; }
+- *term='\0';
+- teams[t]->frags= atoi(pkt);
+- pkt= term+1;
+- if ( pkt-pktstart >= pktlen) goto info_done;
+- }
++ for (i = 0; i < n_players && pkt - start < len; i++)
++ {
++ pkt++; /* skip first byte (0x10) */
++ if (pkt - start >= len)
++ {
++ break;
++ }
++ player = (struct player*)calloc(1, sizeof(struct player));
++ term = strchr(pkt, 0x11);
++ if (!term || term - start >= len)
++ {
++ free(player);
++ break;
++ } player->name = strndup(pkt, term - pkt);
++ get_tribes2_player_type(player);
++ pkt = term + 1;
++ pkt++; /* skip 0x9 */
++ if (pkt - start >= len)
++ {
++ break;
++ }
++ term = strchr(pkt, 0x9);
++ if (!term || term - start >= len)
++ {
++ free(player->name);
++ free(player);
++ break;
++ }
++ for (t = 0; t < n_teams; t++)
++ {
++ if (term - pkt == strlen(teams[t]->name) && strncmp(pkt, teams[t]->name, term - pkt) == 0)
++ {
++ break;
++ }
++ }
++ if (t == n_teams)
++ {
++ player->team = - 1;
++ player->team_name = "Unassigned";
++ }
++ else
++ {
++ player->team = t;
++ player->team_name = teams[t]->name;
++ }
++ player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++ pkt = term + 1;
++ for (s = 0; *pkt != 0xa && pkt - start < len; pkt++)
++ {
++ str[s++] = *pkt;
++ }
++ str[s] = '\0';
++ player->frags = atoi(str);
++ if (*pkt == 0xa)
++ {
++ pkt++;
++ }
+
+- term= strchr( pkt, 0xa);
+- if ( !term || term-start >= len) goto info_done;
+- *term= '\0';
+- n_players= atoi( pkt);
+- pkt= term + 1;
+-
+- for ( i= 0; i < n_players && pkt-start < len; i++) {
+- pkt++; /* skip first byte (0x10) */
+- if ( pkt-start >= len) break;
+- player= (struct player*) calloc( 1, sizeof(struct player));
+- term= strchr( pkt, 0x11);
+- if ( !term || term-start >= len) {
+- free( player);
+- break;
+- }
+- player->name= strndup( pkt, term-pkt);
+- get_tribes2_player_type( player);
+- pkt= term+1;
+- pkt++; /* skip 0x9 */
+- if ( pkt-start >= len) break;
+- term= strchr( pkt, 0x9);
+- if ( !term || term-start >= len) {
+- free( player->name);
+- free( player);
+- break;
+- }
+- for ( t= 0; t < n_teams; t++) {
+- if ( term-pkt == strlen(teams[t]->name) &&
+- strncmp( pkt, teams[t]->name, term-pkt) == 0)
+- break;
++ *last_player = player;
++ last_player = &player->next;
+ }
+- if ( t == n_teams) {
+- player->team= -1;
+- player->team_name= "Unassigned";
+- }
+- else {
+- player->team= t;
+- player->team_name= teams[t]->name;
+- }
+- player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+- pkt= term+1;
+- for ( s= 0; *pkt != 0xa && pkt-start < len; pkt++)
+- str[s++]= *pkt;
+- str[s]= '\0';
+- player->frags= atoi(str);
+- if ( *pkt == 0xa)
+- pkt++;
+-
+- *last_player= player;
+- last_player= & player->next;
+- }
+
+ info_done:
+- for ( t= n_teams; t;) {
+- t--;
+- teams[t]->next= server->players;
+- server->players= teams[t];
+- }
+- if ( teams) free( teams);
++ for (t = n_teams; t;)
++ {
++ t--;
++ teams[t]->next = server->players;
++ server->players = teams[t];
++ }
++ if (teams)
++ {
++ free(teams);
++ }
+
+- cleanup_qserver( server, 1);
+- return;
++ return DONE_FORCE;
+ }
+
+-static const char GrPacketHead[]={'\xc0','\xde','\xf1','\x11'};
+-static const char PacketStart='\x42';
+-static char Dat2Reply1_2_10[]={'\xf4','\x03','\x14','\x02','\x0a','\x41','\x02','\x0a','\x41','\x00','\x00','\x78','\x30','\x63'};
+-static char Dat2Reply1_3[] ={'\xf4','\x03','\x14','\x03','\x05','\x41','\x03','\x05','\x41','\x00','\x00','\x78','\x30','\x63'};
+-static char Dat2Reply1_4[] ={'\xf4','\x03','\x14','\x04','\x00','\x41','\x04','\x00','\x41','\x00','\x00','\x78','\x30','\x63'};
++static const char GrPacketHead[] =
++{
++ '\xc0', '\xde', '\xf1', '\x11'
++};
++static const char PacketStart = '\x42';
++static char Dat2Reply1_2_10[] =
++{
++ '\xf4', '\x03', '\x14', '\x02', '\x0a', '\x41', '\x02', '\x0a', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63'
++};
++static char Dat2Reply1_3[] =
++{
++ '\xf4', '\x03', '\x14', '\x03', '\x05', '\x41', '\x03', '\x05', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63'
++};
++static char Dat2Reply1_4[] =
++{
++ '\xf4', '\x03', '\x14', '\x04', '\x00', '\x41', '\x04', '\x00', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63'
++};
+ //static char HDat2[]={'\xea','\x03','\x02','\x00','\x14'};
+
+ #define SHORT_GR_LEN 75
+@@ -8343,349 +9752,394 @@
+ #define VERSION_1_3 2
+ #define VERSION_1_4 3
+
+-void
+-deal_with_ghostrecon_packet( struct qserver *server, char *pkt, int pktlen)
++query_status_t deal_with_ghostrecon_packet(struct qserver *server, char *pkt, int pktlen)
+ {
+- char str[256], *start, *end, StartFlag, *lpszIgnoreServerPlayer;
+- char *lpszMission;
+- unsigned int iIgnoreServerPlayer, iDedicatedServer, iUseStartTimer;
+- unsigned short GrPayloadLen;
+- int i;
+- struct player *player;
+- int iLen, iTemp;
++ char str[256], *start, *end, StartFlag, *lpszIgnoreServerPlayer;
++ char *lpszMission;
++ unsigned int iIgnoreServerPlayer, iDedicatedServer, iUseStartTimer;
++ unsigned short GrPayloadLen;
++ int i;
++ struct player *player;
++ int iLen, iTemp;
+ short sLen;
+ int iSecsPlayed;
+ long iSpawnType;
+- int ServerVersion=UNKNOWN_VERSION;
++ int ServerVersion = UNKNOWN_VERSION;
+ float flStartTimerSetPoint;
+
++ debug( 2, "deal_with_ghostrecon_packet %p, %d", server, pktlen );
++
+ start = pkt;
+- end=&pkt[pktlen];
+- pkt[pktlen]= '\0';
++ end = &pkt[pktlen];
++ pkt[pktlen] = '\0';
+
+-/*
+- This function walks a packet that is recieved from a ghost recon server - default from port 2348. It does quite a few
+- sanity checks along the way as the structure is not documented. The packet is mostly binary in nature with many string
+- fields being variable in length, ie the length is listed foloowed by that many bytes. There are two structure arrays
+- that have an array size followed by structure size * number of elements (player name and player data). This routine
++ /*
++ This function walks a packet that is recieved from a ghost recon server - default from port 2348. It does quite a few
++ sanity checks along the way as the structure is not documented. The packet is mostly binary in nature with many string
++ fields being variable in length, ie the length is listed foloowed by that many bytes. There are two structure arrays
++ that have an array size followed by structure size * number of elements (player name and player data). This routine
+ walks this packet and increments a pointer "pkt" to extract the info.
+-*/
++ */
+
+
+- if ( server->server_name == NULL)
+- server->ping_total+= time_delta( &packet_recv_time,
+- &server->packet_time1);
++ if (server->server_name == NULL)
++ {
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++ }
+
+ /* sanity check against packet */
+- if (memcmp(pkt,GrPacketHead,sizeof(GrPacketHead))!=0)
++ if (memcmp(pkt, GrPacketHead, sizeof(GrPacketHead)) != 0)
+ {
+- server->server_name= strdup( "Unknown Packet Header");
+- cleanup_qserver( server, 1);
+- return;
+- };
++ server->server_name = strdup("Unknown Packet Header");
++ return PKT_ERROR;
++ }
+
+ pkt += sizeof(GrPacketHead);
+- StartFlag=pkt[0];
++ StartFlag = pkt[0];
+ pkt += 1;
+ if (StartFlag != 0x42)
+ {
+- server->server_name= strdup( "Unknown Start Flag");
+- cleanup_qserver( server, 1);
+- return;
+- };
++ server->server_name = strdup("Unknown Start Flag");
++ return PKT_ERROR;
++ }
+
+ /* compare packet length recieved to included size - header info */
+ sLen = swap_short_from_little(pkt);
+ pkt += 2;
+- GrPayloadLen = pktlen - sizeof(GrPacketHead) -3; // 3 = size slen + size start flag
++ GrPayloadLen = pktlen - sizeof(GrPacketHead) - 3;
++ // 3 = size slen + size start flag
+
+ if (sLen != GrPayloadLen)
+ {
+- server->server_name= strdup( "Packet Size Mismatch");
+- cleanup_qserver( server, 1);
+- return;
+- };
++ server->server_name = strdup("Packet Size Mismatch");
++ return PKT_ERROR;
++ }
+
+-/*
+-Will likely need to verify and add to this "if" construct with every patch / add-on.
+-*/
+- if (memcmp(pkt, Dat2Reply1_2_10, sizeof(Dat2Reply1_2_10)) == 0) ServerVersion=VERSION_1_2_10;
+- else if (memcmp(pkt, Dat2Reply1_3, sizeof(Dat2Reply1_3)) == 0) ServerVersion=VERSION_1_3;
+- else if (memcmp(pkt, Dat2Reply1_4, sizeof(Dat2Reply1_4)) == 0) ServerVersion=VERSION_1_4;
++ /*
++ Will likely need to verify and add to this "if" construct with every patch / add-on.
++ */
++ if (memcmp(pkt, Dat2Reply1_2_10, sizeof(Dat2Reply1_2_10)) == 0)
++ {
++ ServerVersion = VERSION_1_2_10;
++ }
++ else if (memcmp(pkt, Dat2Reply1_3, sizeof(Dat2Reply1_3)) == 0)
++ {
++ ServerVersion = VERSION_1_3;
++ }
++ else if (memcmp(pkt, Dat2Reply1_4, sizeof(Dat2Reply1_4)) == 0)
++ {
++ ServerVersion = VERSION_1_4;
++ }
+
+ if (ServerVersion == UNKNOWN_VERSION)
+ {
+- server->server_name= strdup( "Unknown GR Version");
+- cleanup_qserver( server, 1);
+- return;
+- };
++ server->server_name = strdup("Unknown GR Version");
++ return PKT_ERROR;
++ }
+
+ switch (ServerVersion)
+ {
+ case VERSION_1_2_10:
+- {
+- strcpy(str,"1.2.10");
+- pkt+=sizeof(Dat2Reply1_2_10);
+- break;
+- };
++ strcpy(str, "1.2.10");
++ pkt += sizeof(Dat2Reply1_2_10);
++ break;
++
+ case VERSION_1_3:
+- {
+- strcpy(str,"1.3");
+- pkt+=sizeof(Dat2Reply1_3);
+- break;
+- };
++ strcpy(str, "1.3");
++ pkt += sizeof(Dat2Reply1_3);
++ break;
++
+ case VERSION_1_4:
+- {
+- strcpy(str,"1.4");
+- pkt+=sizeof(Dat2Reply1_4);
+- break;
+- };
++ strcpy(str, "1.4");
++ pkt += sizeof(Dat2Reply1_4);
++ break;
+
+- };
++ }
+ add_rule(server, "patch", str, NO_FLAGS);
+
+ /* have player packet */
+
+- // Ghost recon has one of the player slots filled up with the server program itself. By default we will
+- // drop the first player listed. This causes a bit of a mess here and below but makes for the best display
++ // Ghost recon has one of the player slots filled up with the server program itself. By default we will
++ // drop the first player listed. This causes a bit of a mess here and below but makes for the best display
+ // a user can specify -grs,ignoreserverplayer=no to override this behaviour.
+
+- lpszIgnoreServerPlayer = get_param_value( server, "ignoreserverplayer", "yes");
+- for (i=0; i<4; i++) str[i]=tolower(lpszIgnoreServerPlayer[i]);
+- if (strcmp(str,"yes")==0)iIgnoreServerPlayer=1;
+- else iIgnoreServerPlayer=0;
++ lpszIgnoreServerPlayer = get_param_value(server, "ignoreserverplayer", "yes");
++ for (i = 0; i < 4; i++)
++ {
++ str[i] = tolower(lpszIgnoreServerPlayer[i]);
++ }
++ if (strcmp(str, "yes") == 0)
++ {
++ iIgnoreServerPlayer = 1;
++ }
++ else
++ {
++ iIgnoreServerPlayer = 0;
++ }
+
+- pkt+=4; /* unknown */
++ pkt += 4; /* unknown */
+
+
+- // this is the first of many variable strings. get the length,
++ // this is the first of many variable strings. get the length,
+ // increment pointer over length, check for sanity,
+ // get the string, increment the pointer over string (using length)
+
+ iLen = swap_long_from_little(pkt);
+ pkt += 4;
+- if ((iLen<1) || (iLen >SHORT_GR_LEN))
++ if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ {
+- server->server_name= strdup( "Server Name too Long");
+- cleanup_qserver(server, 1);
+- return;
+- };
+- server->server_name = strndup( pkt, iLen);
++ server->server_name = strdup("Server Name too Long");
++ return PKT_ERROR;
++ }
++ server->server_name = strndup(pkt, iLen);
+ pkt += iLen;
+
+ iLen = swap_long_from_little(pkt);
+ pkt += 4;
+- if ((iLen<1) || (iLen >SHORT_GR_LEN))
++ if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ {
+ add_rule(server, "error", "Map Name too Long", NO_FLAGS);
+- cleanup_qserver(server, 1);
+- return;
+- };
+- server->map_name = strndup( pkt, iLen);
++ return PKT_ERROR;
++ }
++ server->map_name = strndup(pkt, iLen);
+ pkt += iLen;
+
+ iLen = swap_long_from_little(pkt);
+ pkt += 4;
+- if ((iLen<1) || (iLen >SHORT_GR_LEN))
++ if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ {
+- add_rule(server, "error", "Mission Name too Long", NO_FLAGS);
+- cleanup_qserver(server, 1);
+- return;
+- };
+- /* mission does not make sense unless a coop game type. Since
+- we dont know that now, we will save the mission and set
+- the rule and free memory below when we know game type */
+- lpszMission = strndup( pkt, iLen);
++ add_rule(server, "error", "Mission Name too Long", NO_FLAGS);
++ return PKT_ERROR;
++ }
++ /* mission does not make sense unless a coop game type. Since
++ we dont know that now, we will save the mission and set
++ the rule and free memory below when we know game type */
++ lpszMission = strndup(pkt, iLen);
+ pkt += iLen;
+
+
+ iLen = swap_long_from_little(pkt);
+ pkt += 4;
+- if ((iLen<1) || (iLen >SHORT_GR_LEN))
++ if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ {
+- add_rule(server, "error", "Mission Type too Long", NO_FLAGS);
+- cleanup_qserver(server, 1);
+- return;
+- };
+- add_nrule( server, "missiontype", pkt, iLen);
++ add_rule(server, "error", "Mission Type too Long", NO_FLAGS);
++ return PKT_ERROR;
++ }
++ add_nrule(server, "missiontype", pkt, iLen);
+ pkt += iLen;
+
+- if ( pkt[1])
++ if (pkt[1])
+ {
+- add_rule( server, "password", "Yes", NO_FLAGS);
+- } else
++ add_rule(server, "password", "Yes", NO_FLAGS);
++ }
++ else
+ {
+- add_rule( server, "password", "No", NO_FLAGS);
+- };
++ add_rule(server, "password", "No", NO_FLAGS);
++ }
+ pkt += 2;
+
+- server->max_players= swap_long_from_little(pkt);
++ server->max_players = swap_long_from_little(pkt);
+ pkt += 4;
+ if (server->max_players > 36)
+ {
+- add_rule(server, "error", "Max players more then 36", NO_FLAGS);
+- cleanup_qserver(server, 1);
+- return;
+- };
++ add_rule(server, "error", "Max players more then 36", NO_FLAGS);
++ return PKT_ERROR;
++ }
+
+- server->num_players= swap_long_from_little(pkt);
++ server->num_players = swap_long_from_little(pkt);
+ pkt += 4;
+ if (server->num_players > server->max_players)
+ {
+ add_rule(server, "error", "More then MAX Players", NO_FLAGS);
+- cleanup_qserver(server, 1);
+- return;
+- };
++ return PKT_ERROR;
++ }
+
+- if (iIgnoreServerPlayer) // skip past first player
++ if (iIgnoreServerPlayer)
++ // skip past first player
+ {
+ server->num_players--;
+ server->max_players--;
+ iLen = swap_long_from_little(pkt);
+ pkt += 4;
+ pkt += iLen;
+- };
++ }
+
+- for (i=0;i<server->num_players;i++) // read each player name
++ for (i = 0; i < server->num_players; i++)
++ // read each player name
+ {
+ iLen = swap_long_from_little(pkt);
+ pkt += 4;
+
+- player= (struct player*) calloc( 1, sizeof(struct player));
++ player = (struct player*)calloc(1, sizeof(struct player));
+
+- if ((iLen<1) || (iLen >SHORT_GR_LEN))
++ if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ {
+ add_rule(server, "error", "Player Name too Long", NO_FLAGS);
+- cleanup_qserver(server, 1);
+- return;
+- };
+- player->name= strndup( pkt, iLen);
+- pkt += iLen; /* player name */
+- player->team= i; // tag so we can find this record when we have player dat.
+- player->team_name= "Unassigned";
++ return PKT_ERROR;
++ }
++ player->name = strndup(pkt, iLen);
++ pkt += iLen; /* player name */
++ player->team = i; // tag so we can find this record when we have player dat.
++ player->team_name = "Unassigned";
+ player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+- player->frags=0;
++ player->frags = 0;
+
+- player->next= server->players;
+- server->players= player;
+- };
++ player->next = server->players;
++ server->players = player;
++ }
+
+ pkt += 17;
+
+ iLen = swap_long_from_little(pkt);
+ pkt += 4;
+- if ((iLen<1) || (iLen >SHORT_GR_LEN))
++ if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ {
+- add_rule(server, "error", "Version too Long", NO_FLAGS);
+- cleanup_qserver(server, 1);
+- return;
+- };
+- strncpy(str,pkt,iLen);
+- add_rule( server, "version", str, NO_FLAGS);
+- pkt += iLen; /* version */
++ add_rule(server, "error", "Version too Long", NO_FLAGS);
++ return PKT_ERROR;
++ }
++ strncpy(str, pkt, iLen);
++ add_rule(server, "version", str, NO_FLAGS);
++ pkt += iLen; /* version */
+
+ iLen = swap_long_from_little(pkt);
+ pkt += 4;
+- if ((iLen<1) || (iLen >LONG_GR_LEN))
++ if ((iLen < 1) || (iLen > LONG_GR_LEN))
+ {
+- add_rule(server, "error", "Mods too Long", NO_FLAGS);
+- cleanup_qserver(server, 1);
+- return;
+- };
+- server->game= strndup( pkt, iLen);
++ add_rule(server, "error", "Mods too Long", NO_FLAGS);
++ return PKT_ERROR;
++ }
++ server->game = strndup(pkt, iLen);
+
+- for (i=0;i<(int)strlen(server->game)-5;i++) // clean the "/mods/" part from every entry
++ for (i = 0; i < (int)strlen(server->game) - 5; i++)
++ // clean the "/mods/" part from every entry
+ {
+- if (memcmp(&server->game[i],"\\mods\\",6)==0)
++ if (memcmp(&server->game[i], "\\mods\\", 6) == 0)
+ {
+- server->game[i]=' ';
+- strcpy(&server->game[i+1],&server->game[i+6]);
+- };
+- };
+- add_rule(server, "game", server->game, NO_FLAGS);
++ server->game[i] = ' ';
++ strcpy(&server->game[i + 1], &server->game[i + 6]);
++ }
++ }
++ add_rule(server, "game", server->game, NO_FLAGS);
+
+- pkt += iLen; /* mods */
++ pkt += iLen; /* mods */
+
+- iDedicatedServer=pkt[0];
+- if ( iDedicatedServer)
+- add_rule( server, "dedicated", "Yes", NO_FLAGS);
++ iDedicatedServer = pkt[0];
++ if (iDedicatedServer)
++ {
++ add_rule(server, "dedicated", "Yes", NO_FLAGS);
++ }
+ else
+- add_rule( server, "dedicated", "No", NO_FLAGS);
++ {
++ add_rule(server, "dedicated", "No", NO_FLAGS);
++ }
+
+- pkt += 1; /* unknown */
++ pkt += 1; /* unknown */
+
+ iSecsPlayed = swap_float_from_little(pkt);
+
+- add_rule(server, "timeplayed", play_time(iSecsPlayed,2), NO_FLAGS);
++ add_rule(server, "timeplayed", play_time(iSecsPlayed, 2), NO_FLAGS);
+
+- pkt += 4; /* time played */
++ pkt += 4; /* time played */
+
+- switch (pkt[0]) {
+- case 3 : strcpy(str,"Joining"); break;
+- case 4 : strcpy(str,"Playing"); break;
+- case 5 : strcpy(str,"Debrief"); break;
+- default : strcpy(str,"Undefined");
+- };
+- add_rule( server, "status", str, NO_FLAGS);
++ switch (pkt[0])
++ {
++ case 3:
++ strcpy(str, "Joining");
++ break;
++ case 4:
++ strcpy(str, "Playing");
++ break;
++ case 5:
++ strcpy(str, "Debrief");
++ break;
++ default:
++ strcpy(str, "Undefined");
++ }
++ add_rule(server, "status", str, NO_FLAGS);
+
+ pkt += 1;
+
+- pkt += 3; /* unknown */
++ pkt += 3; /* unknown */
+
+
+- switch (pkt[0]) {
+- case 2: strcpy(str,"COOP"); break;
+- case 3: strcpy(str,"SOLO"); break;
+- case 4: strcpy(str,"TEAM"); break;
+- default: sprintf(str,"UNKOWN %u",pkt[0]); break;
+- };
++ switch (pkt[0])
++ {
++ case 2:
++ strcpy(str, "COOP");
++ break;
++ case 3:
++ strcpy(str, "SOLO");
++ break;
++ case 4:
++ strcpy(str, "TEAM");
++ break;
++ default:
++ sprintf(str, "UNKOWN %u", pkt[0]);
++ break;
++ }
+
+- add_rule( server, "gamemode", str, NO_FLAGS);
++ add_rule(server, "gamemode", str, NO_FLAGS);
+
+- if (pkt[0]==2)
+- add_rule( server, "mission", lpszMission, NO_FLAGS);
++ if (pkt[0] == 2)
++ {
++ add_rule(server, "mission", lpszMission, NO_FLAGS);
++ }
+ else
+- add_rule( server, "mission", "No Mission", NO_FLAGS);
++ {
++ add_rule(server, "mission", "No Mission", NO_FLAGS);
++ }
+
+ free(lpszMission);
+
+- pkt += 1; /* Game Mode */
++ pkt += 1; /* Game Mode */
+
+- pkt += 3; /* unknown */
++ pkt += 3; /* unknown */
+
+ iLen = swap_long_from_little(pkt);
+ pkt += 4;
+- if ((iLen<1) || (iLen >LONG_GR_LEN))
++ if ((iLen < 1) || (iLen > LONG_GR_LEN))
+ {
+- add_rule(server, "error", "MOTD too Long", NO_FLAGS);
+- cleanup_qserver(server, 1);
+- return;
+- };
+- strncpy(str,pkt,sizeof(str));
+- str[sizeof(str)-1]=0;
++ add_rule(server, "error", "MOTD too Long", NO_FLAGS);
++ return PKT_ERROR;
++ }
++ strncpy(str, pkt, sizeof(str));
++ str[sizeof(str) - 1] = 0;
+ add_rule(server, "motd", str, NO_FLAGS);
+- pkt += iLen; /* MOTD */
++ pkt += iLen; /* MOTD */
+
+ iSpawnType = swap_long_from_little(pkt);
+
+- switch (iSpawnType) {
+- case 0: strcpy(str,"None"); break;
+- case 1: strcpy(str,"Individual"); break;
+- case 2: strcpy(str,"Team"); break;
+- case 3: strcpy(str,"Infinite"); break;
+- default:strcpy(str,"Unknown");
+- };
++ switch (iSpawnType)
++ {
++ case 0:
++ strcpy(str, "None");
++ break;
++ case 1:
++ strcpy(str, "Individual");
++ break;
++ case 2:
++ strcpy(str, "Team");
++ break;
++ case 3:
++ strcpy(str, "Infinite");
++ break;
++ default:
++ strcpy(str, "Unknown");
++ }
+
+- add_rule( server, "spawntype", str, NO_FLAGS);
+- pkt += 4; /* spawn type */
++ add_rule(server, "spawntype", str, NO_FLAGS);
++ pkt += 4; /* spawn type */
+
+ iTemp = swap_float_from_little(pkt);
+- add_rule(server, "gametime", play_time(iTemp,2), NO_FLAGS);
++ add_rule(server, "gametime", play_time(iTemp, 2), NO_FLAGS);
+
+- iTemp = iTemp-iSecsPlayed;
++ iTemp = iTemp - iSecsPlayed;
+
+- if (iTemp <= 0) iTemp=0;
+- add_rule(server, "remainingtime", play_time(iTemp,2), NO_FLAGS);
++ if (iTemp <= 0)
++ {
++ iTemp = 0;
++ }
++ add_rule(server, "remainingtime", play_time(iTemp, 2), NO_FLAGS);
+ pkt += 4; /* Game time */
+
+
+@@ -8693,238 +10147,281 @@
+ if (iIgnoreServerPlayer)
+ {
+ iTemp--;
+- };
++ }
+ if (iTemp != server->num_players)
+ {
+- add_rule(server, "error", "Number of Players Mismatch", NO_FLAGS);
+- };
++ add_rule(server, "error", "Number of Players Mismatch", NO_FLAGS);
++ }
+
+
+ pkt += 4; /* player count 2 */
+
+ if (iIgnoreServerPlayer)
+ {
+- pkt+=5; // skip first player data
+- };
++ pkt += 5; // skip first player data
++ }
+
+- for (i=0;i<server->num_players;i++) // for each player get binary data
++ for (i = 0; i < server->num_players; i++)
++ // for each player get binary data
+ {
+- player=server->players; // first we must find the player - lets look for the tag
+- while (player && (player->team != i)) player=player->next; /* get to player - linked list is in reverse order */
++ player = server->players;
++ // first we must find the player - lets look for the tag
++ while (player && (player->team != i))
++ {
++ player = player->next;
++ }
++ /* get to player - linked list is in reverse order */
+
+ if (player)
+ {
+- player->team= pkt[2];
++ player->team = pkt[2];
+ switch (player->team)
+ {
+- case 1: player->team_name= "Red"; break;
+- case 2: player->team_name= "Blue"; break;
+- case 3: player->team_name= "Yellow"; break;
+- case 4: player->team_name= "Green"; break;
+- case 5: player->team_name= "Unassigned"; break;
+- default: player->team_name= "Not Known"; break;
+- };
++ case 1:
++ player->team_name = "Red";
++ break;
++ case 2:
++ player->team_name = "Blue";
++ break;
++ case 3:
++ player->team_name = "Yellow";
++ break;
++ case 4:
++ player->team_name = "Green";
++ break;
++ case 5:
++ player->team_name = "Unassigned";
++ break;
++ default:
++ player->team_name = "Not Known";
++ break;
++ }
+ player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+- player->deaths=pkt[1];
+- };
++ player->deaths = pkt[1];
++ }
+ pkt += 5; /* player data*/
+- };
++ }
+
+- for (i=0;i<5;i++)
++ for (i = 0; i < 5; i++)
+ {
+ pkt += 8; /* team data who knows what they have in here */
+- };
++ }
+
+- pkt +=1;
+- iUseStartTimer=pkt[0]; // UseStartTimer
++ pkt += 1;
++ iUseStartTimer = pkt[0]; // UseStartTimer
+
+- pkt +=1;
++ pkt += 1;
+
+- iTemp = flStartTimerSetPoint = swap_float_from_little(pkt);// Start Timer Set Point
++ iTemp = flStartTimerSetPoint = swap_float_from_little(pkt);
++ // Start Timer Set Point
+ pkt += 4;
+
+ if (iUseStartTimer)
+ {
+- add_rule( server, "usestarttime", "Yes", NO_FLAGS);
+- add_rule( server, "starttimeset", play_time(iTemp,2), NO_FLAGS);
+- } else
++ add_rule(server, "usestarttime", "Yes", NO_FLAGS);
++ add_rule(server, "starttimeset", play_time(iTemp, 2), NO_FLAGS);
++ }
++ else
+ {
+- add_rule( server, "usestarttime", "No", NO_FLAGS);
+- add_rule( server, "starttimeset", play_time(0,2), NO_FLAGS);
+- };
++ add_rule(server, "usestarttime", "No", NO_FLAGS);
++ add_rule(server, "starttimeset", play_time(0, 2), NO_FLAGS);
++ }
+
+- if ((ServerVersion == VERSION_1_3) || // stuff added in patch 1.3
+- (ServerVersion == VERSION_1_4))
++ if ((ServerVersion == VERSION_1_3) || // stuff added in patch 1.3
++ (ServerVersion == VERSION_1_4))
+ {
+- iTemp = swap_float_from_little(pkt);// Debrief Time
+- add_rule( server, "debrieftime", play_time(iTemp,2), NO_FLAGS);
++ iTemp = swap_float_from_little(pkt); // Debrief Time
++ add_rule(server, "debrieftime", play_time(iTemp, 2), NO_FLAGS);
+ pkt += 4;
+
+- iTemp = swap_float_from_little(pkt);// Respawn Min
+- add_rule( server, "respawnmin", play_time(iTemp,2), NO_FLAGS);
++ iTemp = swap_float_from_little(pkt); // Respawn Min
++ add_rule(server, "respawnmin", play_time(iTemp, 2), NO_FLAGS);
+ pkt += 4;
+
+- iTemp = swap_float_from_little(pkt);// Respawn Max
+- add_rule( server, "respawnmax", play_time(iTemp,2), NO_FLAGS);
++ iTemp = swap_float_from_little(pkt); // Respawn Max
++ add_rule(server, "respawnmax", play_time(iTemp, 2), NO_FLAGS);
+ pkt += 4;
+
+- iTemp = swap_float_from_little(pkt);// Respawn Invulnerable
+- add_rule( server, "respawnsafe", play_time(iTemp,2), NO_FLAGS);
++ iTemp = swap_float_from_little(pkt); // Respawn Invulnerable
++ add_rule(server, "respawnsafe", play_time(iTemp, 2), NO_FLAGS);
+ pkt += 4;
+- } else
++ }
++ else
+ {
+- add_rule( server, "debrieftime", "Undefined", NO_FLAGS);
+- add_rule( server, "respawnmin", "Undefined", NO_FLAGS);
+- add_rule( server, "respawnmax", "Undefined", NO_FLAGS);
+- add_rule( server, "respawnsafe", "Undefined", NO_FLAGS);
++ add_rule(server, "debrieftime", "Undefined", NO_FLAGS);
++ add_rule(server, "respawnmin", "Undefined", NO_FLAGS);
++ add_rule(server, "respawnmax", "Undefined", NO_FLAGS);
++ add_rule(server, "respawnsafe", "Undefined", NO_FLAGS);
+
+- };
++ }
+
+
+- pkt+=4; // 4
+- iTemp=pkt[0]; // Spawn Count
++ pkt += 4; // 4
++ iTemp = pkt[0]; // Spawn Count
+
+- if ((iSpawnType == 1) || (iSpawnType == 2)) /* Individual or team */
+- sprintf( str, "%u", iTemp);
+- else /* else not used */
+- sprintf( str, "%u", 0);
+- add_rule( server, "spawncount", str, NO_FLAGS);
++ if ((iSpawnType == 1) || (iSpawnType == 2))
++ /* Individual or team */
++ {
++ sprintf(str, "%u", iTemp);
++ }
++ else
++ /* else not used */
++ {
++ sprintf(str, "%u", 0);
++ }
++ add_rule(server, "spawncount", str, NO_FLAGS);
+ pkt += 1; // 5
+
+- pkt +=4; // 9
++ pkt += 4; // 9
+
+- iTemp=pkt[0]; // Allow Observers
++ iTemp = pkt[0]; // Allow Observers
+ if (iTemp)
+- strcpy( str, "Yes");
+- else /* else not used */
+- strcpy( str, "No");
+- add_rule( server, "allowobservers", str, NO_FLAGS);
+- pkt +=1; // 10
++ {
++ strcpy(str, "Yes");
++ }
++ else
++ /* else not used */
++ {
++ strcpy(str, "No");
++ }
++ add_rule(server, "allowobservers", str, NO_FLAGS);
++ pkt += 1; // 10
+
+- pkt +=3; // 13
++ pkt += 3; // 13
+
+ // pkt += 13;
+
+ if (iUseStartTimer)
+ {
+- iTemp=swap_float_from_little(pkt); // Start Timer Count
+- add_rule( server, "startwait", play_time(iTemp,2), NO_FLAGS);
++ iTemp = swap_float_from_little(pkt); // Start Timer Count
++ add_rule(server, "startwait", play_time(iTemp, 2), NO_FLAGS);
+ }
+ else
+ {
+- add_rule( server, "startwait", play_time(0,2), NO_FLAGS);
+- };
+- pkt += 4; //17
++ add_rule(server, "startwait", play_time(0, 2), NO_FLAGS);
++ }
++ pkt += 4; //17
+
+- iTemp = pkt[0]; // IFF
++ iTemp = pkt[0]; // IFF
+ switch (iTemp)
+ {
+- case 0 : strcpy(str,"None"); break;
+- case 1 : strcpy(str,"Reticule"); break;
+- case 2 : strcpy(str,"Names"); break;
+- default : strcpy(str,"Unknown"); break;
+- };
+- add_rule( server, "iff", str, NO_FLAGS);
++ case 0:
++ strcpy(str, "None");
++ break;
++ case 1:
++ strcpy(str, "Reticule");
++ break;
++ case 2:
++ strcpy(str, "Names");
++ break;
++ default:
++ strcpy(str, "Unknown");
++ break;
++ }
++ add_rule(server, "iff", str, NO_FLAGS);
+ pkt += 1; // 18
+
+- iTemp = pkt [0]; // Threat Indicator
+- if (iTemp) add_rule(server, "ti", "ON ", NO_FLAGS);
+- else add_rule(server, "ti", "OFF", NO_FLAGS);
++ iTemp = pkt[0]; // Threat Indicator
++ if (iTemp)
++ {
++ add_rule(server, "ti", "ON ", NO_FLAGS);
++ }
++ else
++ {
++ add_rule(server, "ti", "OFF", NO_FLAGS);
++ }
+
+- pkt += 1; // 19
++ pkt += 1; // 19
+
+- pkt += 5; // 24
++ pkt += 5; // 24
+
+
+ iLen = swap_long_from_little(pkt);
+ pkt += 4;
+- if ((iLen<1) || (iLen >SHORT_GR_LEN) )
++ if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ {
+- add_rule(server, "error", "Restrictions too Long", NO_FLAGS);
+- cleanup_qserver(server, 1);
+- return;
+- };
+- add_rule( server, "restrict", pkt, NO_FLAGS);
+- pkt += iLen; /* restrictions */
++ add_rule(server, "error", "Restrictions too Long", NO_FLAGS);
++ return PKT_ERROR;
++ }
++ add_rule(server, "restrict", pkt, NO_FLAGS);
++ pkt += iLen; /* restrictions */
+
+ pkt += 23;
+
+ /*
+ if ( ghostrecon_debug) print_packet( pkt, GrPayloadLen);
+- */
++ */
+
+- cleanup_qserver( server, 1);
+- return;
++ return DONE_FORCE;
+ }
+
+-char*
+-find_ravenshield_game( char *gameno )
++char *find_ravenshield_game(char *gameno)
+ {
+- switch( atoi( gameno ) )
++ switch (atoi(gameno))
+ {
+- case 8:
+- return strdup( "Team Deathmatch" );
+- break;
+- case 13:
+- return strdup( "Deathmatch" );
+- break;
+- case 14:
+- return strdup( "Team Deathmatch" );
+- break;
+- case 15:
+- return strdup( "Bomb" );
+- break;
+- case 16:
+- return strdup( "Escort Pilot" );
+- break;
+- default:
+- // 1.50 and above actually uses a string so
+- // return that
+- return strdup( gameno );
+- break;
++ case 8:
++ return strdup("Team Deathmatch");
++ break;
++ case 13:
++ return strdup("Deathmatch");
++ break;
++ case 14:
++ return strdup("Team Deathmatch");
++ break;
++ case 15:
++ return strdup("Bomb");
++ break;
++ case 16:
++ return strdup("Escort Pilot");
++ break;
++ default:
++ // 1.50 and above actually uses a string so
++ // return that
++ return strdup(gameno);
++ break;
+ }
+ }
+
+-char*
+-find_savage_game( char *gametype )
++char *find_savage_game(char *gametype)
+ {
+- if ( 0 == strcmp( "RTSS", gametype ) )
++ if (0 == strcmp("RTSS", gametype))
+ {
+- return strdup( "RTSS" );
++ return strdup("RTSS");
+ }
+ else
+ {
+- return strdup( "Unknown" );
++ return strdup("Unknown");
+ }
+ }
+
+-void
+-deal_with_ravenshield_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_ravenshield_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- char *s, *key, *value;
++ char *s, *key, *value;
++
++ debug( 2, "deal_with_ravenshield_packet %p, %d", server, pktlen );
+
+ server->n_servers++;
+- if ( NULL == server->server_name )
++ if (NULL == server->server_name)
+ {
+- server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+ }
+- else
++ else
+ {
+- gettimeofday( &server->packet_time1, NULL );
++ gettimeofday(&server->packet_time1, NULL);
+ }
+
+- rawpkt[pktlen]= '\0';
++ rawpkt[pktlen] = '\0';
+
+- s = rawpkt;
+- while ( *s )
++ s = rawpkt;
++ while (*s)
+ {
+ // Find the seperator
+- while ( *s && *s != '\xB6' )
++ while (*s && *s != '\xB6')
+ {
+ s++;
+ }
+
+- if ( !*s )
++ if (! *s)
+ {
+ // Hit the end no more
+ break;
+@@ -8932,11 +10429,11 @@
+
+ // key start
+ key = ++s;
+- while ( *s && *s != ' ' )
++ while (*s && *s != ' ')
+ {
+ s++;
+ }
+- if ( *s != ' ')
++ if (*s != ' ')
+ {
+ // malformed
+ break;
+@@ -8946,189 +10443,189 @@
+ // value start
+ value = s;
+
+- while ( *s && *s != '\xB6' )
++ while (*s && *s != '\xB6')
+ {
+ s++;
+ }
+
+- if ( *s == '\xB6' )
++ if (*s == '\xB6')
+ {
+- *(s-1) = '\0';
++ *(s - 1) = '\0';
+ }
+
+ // Decode current key par
+- if ( 0 == strcmp( "A1", key ) )
++ if (0 == strcmp("A1", key))
+ {
+ // Max players
+- server->max_players = atoi( value );
++ server->max_players = atoi(value);
+ }
+- else if ( 0 == strcmp( "A2", key ) )
++ else if (0 == strcmp("A2", key))
+ {
+ // TeamKillerPenalty
+- add_rule( server, "TeamKillerPenalty", value, NO_FLAGS );
++ add_rule(server, "TeamKillerPenalty", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "B1", key ) )
++ else if (0 == strcmp("B1", key))
+ {
+ // Current players
+- server->num_players = atoi( value );
++ server->num_players = atoi(value);
+ }
+- else if ( 0 == strcmp( "B2", key ) )
++ else if (0 == strcmp("B2", key))
+ {
+ // AllowRadar
+- add_rule( server, "AllowRadar", value, NO_FLAGS );
++ add_rule(server, "AllowRadar", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "D2", key ) )
++ else if (0 == strcmp("D2", key))
+ {
+ // Version info
+- add_rule( server, "Version", value, NO_FLAGS );
++ add_rule(server, "Version", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "E1", key ) )
++ else if (0 == strcmp("E1", key))
+ {
+ // Current map
+- server->map_name = strdup( value );
++ server->map_name = strdup(value);
+ }
+- else if ( 0 == strcmp( "E2", key ) )
++ else if (0 == strcmp("E2", key))
+ {
+ // Unknown
+ }
+- else if ( 0 == strcmp( "F1", key ) )
++ else if (0 == strcmp("F1", key))
+ {
+ // Game type
+- server->game = find_ravenshield_game( value );
+- add_rule( server, server->type->game_rule, server->game, NO_FLAGS );
++ server->game = find_ravenshield_game(value);
++ add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "F2", key ) )
++ else if (0 == strcmp("F2", key))
+ {
+ // Unknown
+ }
+- else if ( 0 == strcmp( "G1", key ) )
++ else if (0 == strcmp("G1", key))
+ {
+ // Password
+- add_rule( server, "Password", value, NO_FLAGS );
++ add_rule(server, "Password", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "G2", key ) )
++ else if (0 == strcmp("G2", key))
+ {
+ // Query port
+ }
+- else if ( 0 == strcmp( "H1", key ) )
++ else if (0 == strcmp("H1", key))
+ {
+ // Unknown
+ }
+- else if ( 0 == strcmp( "H2", key ) )
++ else if (0 == strcmp("H2", key))
+ {
+ // Number of Terrorists
+- add_rule( server, "nbTerro", value, NO_FLAGS );
++ add_rule(server, "nbTerro", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "I1", key ) )
++ else if (0 == strcmp("I1", key))
+ {
+ // Server name
+- server->server_name = strdup( value );
++ server->server_name = strdup(value);
+ }
+- else if ( 0 == strcmp( "I2", key ) )
++ else if (0 == strcmp("I2", key))
+ {
+ // Unknown
+ }
+- else if ( 0 == strcmp( "J1", key ) )
++ else if (0 == strcmp("J1", key))
+ {
+ // Game Type Order
+ // Not pretty ignore for now
+ //add_rule( server, "Game Type Order", value, NO_FLAGS );
+ }
+- else if ( 0 == strcmp( "J2", key ) )
++ else if (0 == strcmp("J2", key))
+ {
+ // RotateMap
+- add_rule( server, "RotateMap", value, NO_FLAGS);
++ add_rule(server, "RotateMap", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "K1", key ) )
++ else if (0 == strcmp("K1", key))
+ {
+ // Map Cycle
+ // Not pretty ignore for now
+ //add_rule( server, "Map Cycle", value, NO_FLAGS );
+ }
+- else if ( 0 == strcmp( "K2", key ) )
++ else if (0 == strcmp("K2", key))
+ {
+ // Force First Person Weapon
+- add_rule( server, "ForceFPersonWeapon", value, NO_FLAGS);
++ add_rule(server, "ForceFPersonWeapon", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "L1", key ) )
++ else if (0 == strcmp("L1", key))
+ {
+ // Players names
+ int player_number = 0;
+ char *n = value;
+
+- if ( *n == '/' )
++ if (*n == '/')
+ {
+ // atleast 1 player
+ n++;
+- while( *n && *n != '\xB6' )
++ while (*n && *n != '\xB6')
+ {
+ char *player_name = n;
+- while ( *n && *n != '/' && *n != '\xB6' )
++ while (*n && *n != '/' && *n != '\xB6')
+ {
+ n++;
+ }
+
+- if ( *n == '/' )
++ if (*n == '/')
+ {
+ *n++ = '\0';
+ }
+- else if ( *n == '\xB6' )
++ else if (*n == '\xB6')
+ {
+- *(n-1) = '\0';
++ *(n - 1) = '\0';
+ }
+
+- if ( 0 != strlen( player_name ) )
++ if (0 != strlen(player_name))
+ {
+- struct player *player = add_player( server, player_number );
+- if ( NULL != player )
++ struct player *player = add_player(server, player_number);
++ if (NULL != player)
+ {
+- player->name = strdup( player_name );
++ player->name = strdup(player_name);
+ }
+ player_number++;
+ }
+ }
+ }
+ }
+- else if ( 0 == strcmp( "L3", key ) )
++ else if (0 == strcmp("L3", key))
+ {
+ // PunkBuster state
+- add_rule( server, "PunkBuster", value, NO_FLAGS );
++ add_rule(server, "PunkBuster", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "M1", key ) )
++ else if (0 == strcmp("M1", key))
+ {
+ // Players times
+ int player_number = 0;
+ char *n = value;
+- if ( *n == '/' )
++ if (*n == '/')
+ {
+ // atleast 1 player
+ n++;
+- while( *n && *n != '\xB6' )
++ while (*n && *n != '\xB6')
+ {
+ char *time = n;
+- while ( *n && *n != '/' && *n != '\xB6' )
++ while (*n && *n != '/' && *n != '\xB6')
+ {
+ n++;
+ }
+
+- if ( *n == '/' )
++ if (*n == '/')
+ {
+ *n++ = '\0';
+ }
+- else if ( *n == '\xB6' )
++ else if (*n == '\xB6')
+ {
+- *(n-1) = '\0';
++ *(n - 1) = '\0';
+ }
+
+- if ( 0 != strlen( time ) )
++ if (0 != strlen(time))
+ {
+ int mins, seconds;
+- if ( 2 == sscanf( time, "%d:%d", &mins, &seconds ) )
++ if (2 == sscanf(time, "%d:%d", &mins, &seconds))
+ {
+- struct player *player = get_player_by_number( server, player_number );
+- if ( NULL != player )
++ struct player *player = get_player_by_number(server, player_number);
++ if (NULL != player)
+ {
+- player->connect_time = mins * 60 + seconds;
++ player->connect_time = mins * 60+seconds;
+ }
+ }
+ player_number++;
+@@ -9136,164 +10633,162 @@
+ }
+ }
+ }
+- else if ( 0 == strcmp( "N1", key ) )
++ else if (0 == strcmp("N1", key))
+ {
+ // Players ping
+ int player_number = 0;
+ char *n = value;
+- if ( *n == '/' )
++ if (*n == '/')
+ {
+ // atleast 1 player
+ n++;
+- while( *n && *n != '\xB6' )
++ while (*n && *n != '\xB6')
+ {
+ char *ping = n;
+- while ( *n && *n != '/' && *n != '\xB6' )
++ while (*n && *n != '/' && *n != '\xB6')
+ {
+ n++;
+ }
+
+- if ( *n == '/' )
++ if (*n == '/')
+ {
+ *n++ = '\0';
+ }
+- else if ( *n == '\xB6' )
++ else if (*n == '\xB6')
+ {
+- *(n-1) = '\0';
++ *(n - 1) = '\0';
+ }
+
+- if ( 0 != strlen( ping ) )
++ if (0 != strlen(ping))
+ {
+- struct player *player = get_player_by_number( server, player_number );
+- if ( NULL != player )
++ struct player *player = get_player_by_number(server, player_number);
++ if (NULL != player)
+ {
+- player->ping = atoi( ping );
+- }
+- player_number++;
++ player->ping = atoi(ping);
++ } player_number++;
+ }
+ }
+ }
+ }
+- else if ( 0 == strcmp( "O1", key ) )
++ else if (0 == strcmp("O1", key))
+ {
+ // Players fags
+ int player_number = 0;
+ char *n = value;
+- if ( *n == '/' )
++ if (*n == '/')
+ {
+ // atleast 1 player
+ n++;
+- while( *n && *n != '\xB6' )
++ while (*n && *n != '\xB6')
+ {
+ char *frags = n;
+- while ( *n && *n != '/' && *n != '\xB6' )
++ while (*n && *n != '/' && *n != '\xB6')
+ {
+ n++;
+ }
+
+- if ( *n == '/' )
++ if (*n == '/')
+ {
+ *n++ = '\0';
+ }
+- else if ( *n == '\xB6' )
++ else if (*n == '\xB6')
+ {
+- *(n-1) = '\0';
++ *(n - 1) = '\0';
+ }
+
+- if ( 0 != strlen( frags ) )
++ if (0 != strlen(frags))
+ {
+- struct player *player = get_player_by_number( server, player_number );
+- if ( NULL != player )
++ struct player *player = get_player_by_number(server, player_number);
++ if (NULL != player)
+ {
+- player->frags = atoi( frags );
+- }
+- player_number++;
++ player->frags = atoi(frags);
++ } player_number++;
+ }
+ }
+ }
+ }
+- else if ( 0 == strcmp( "P1", key ) )
++ else if (0 == strcmp("P1", key))
+ {
+ // Game port
+ // Not pretty ignore for now
+ /*
+ change_server_port( server, atoi( value ), 0 );
+- */
++ */
+ }
+- else if ( 0 == strcmp( "Q1", key ) )
++ else if (0 == strcmp("Q1", key))
+ {
+ // RoundsPerMatch
+- add_rule( server, "RoundsPerMatch", value, NO_FLAGS );
++ add_rule(server, "RoundsPerMatch", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "R1", key ) )
++ else if (0 == strcmp("R1", key))
+ {
+ // RoundTime
+- add_rule( server, "RoundTime", value, NO_FLAGS );
++ add_rule(server, "RoundTime", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "S1", key ) )
++ else if (0 == strcmp("S1", key))
+ {
+ // BetweenRoundTime
+- add_rule( server, "BetweenRoundTime", value, NO_FLAGS );
++ add_rule(server, "BetweenRoundTime", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "T1", key ) )
++ else if (0 == strcmp("T1", key))
+ {
+ // BombTime
+- add_rule( server, "BombTime", value, NO_FLAGS );
++ add_rule(server, "BombTime", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "W1", key ) )
++ else if (0 == strcmp("W1", key))
+ {
+ // ShowNames
+- add_rule( server, "ShowNames", value, NO_FLAGS );
++ add_rule(server, "ShowNames", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "X1", key ) )
++ else if (0 == strcmp("X1", key))
+ {
+ // InternetServer
+- add_rule( server, "InternetServer", value, NO_FLAGS );
++ add_rule(server, "InternetServer", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "Y1", key ) )
++ else if (0 == strcmp("Y1", key))
+ {
+ // FriendlyFire
+- add_rule( server, "FriendlyFire", value, NO_FLAGS );
++ add_rule(server, "FriendlyFire", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "Z1", key ) )
++ else if (0 == strcmp("Z1", key))
+ {
+ // Autobalance
+- add_rule( server, "Autobalance", value, NO_FLAGS );
++ add_rule(server, "Autobalance", value, NO_FLAGS);
+ }
+ }
+
+- cleanup_qserver( server, 1 );
+- return;
++ return DONE_FORCE;
+ }
+
+-void
+-deal_with_savage_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_savage_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- char *s, *key, *value, *end;
++ char *s, *key, *value, *end;
++
++ debug( 2, "deal_with_savage_packet %p, %d", server, pktlen );
+
+ server->n_servers++;
+- if ( NULL == server->server_name )
++ if (NULL == server->server_name)
+ {
+- server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+ }
+- else
++ else
+ {
+- gettimeofday( &server->packet_time1, NULL );
++ gettimeofday(&server->packet_time1, NULL);
+ }
+
+- rawpkt[pktlen]= '\0';
++ rawpkt[pktlen] = '\0';
+
+- end = s = rawpkt;
++ end = s = rawpkt;
+ end += pktlen;
+- while ( *s )
++ while (*s)
+ {
+ // Find the seperator
+- while ( s <= end && *s != '\xFF' )
++ while (s <= end && *s != '\xFF')
+ {
+ s++;
+ }
+
+- if ( s >= end )
++ if (s >= end)
+ {
+ // Hit the end no more
+ break;
+@@ -9301,11 +10796,11 @@
+
+ // key start
+ key = ++s;
+- while ( s < end && *s != '\xFE' )
++ while (s < end && *s != '\xFE')
+ {
+ s++;
+ }
+- if ( *s != '\xFE')
++ if (*s != '\xFE')
+ {
+ // malformed
+ break;
+@@ -9315,75 +10810,75 @@
+ // value start
+ value = s;
+
+- while ( s < end && *s != '\xFF' )
++ while (s < end && *s != '\xFF')
+ {
+ s++;
+ }
+
+- if ( *s == '\xFF' )
++ if (*s == '\xFF')
+ {
+ *s = '\0';
+ }
+ //fprintf( stderr, "'%s' = '%s'\n", key, value );
+
+ // Decode current key par
+- if ( 0 == strcmp( "cmax", key ) )
++ if (0 == strcmp("cmax", key))
+ {
+ // Max players
+- server->max_players = atoi( value );
++ server->max_players = atoi(value);
+ }
+- else if ( 0 == strcmp( "cnum", key ) )
++ else if (0 == strcmp("cnum", key))
+ {
+ // Current players
+- server->num_players = atoi( value );
++ server->num_players = atoi(value);
+ }
+- else if ( 0 == strcmp( "bal", key ) )
++ else if (0 == strcmp("bal", key))
+ {
+ // Balance
+- add_rule( server, "Balance", value, NO_FLAGS );
++ add_rule(server, "Balance", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "world", key ) )
++ else if (0 == strcmp("world", key))
+ {
+ // Current map
+- server->map_name = strdup( value );
++ server->map_name = strdup(value);
+ }
+- else if ( 0 == strcmp( "gametype", key ) )
++ else if (0 == strcmp("gametype", key))
+ {
+ // Game type
+- server->game = find_savage_game( value );
+- add_rule( server, server->type->game_rule, server->game, NO_FLAGS );
++ server->game = find_savage_game(value);
++ add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "pure", key ) )
++ else if (0 == strcmp("pure", key))
+ {
+ // Pure
+- add_rule( server, "Pure", value, NO_FLAGS );
++ add_rule(server, "Pure", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "time", key ) )
++ else if (0 == strcmp("time", key))
+ {
+ // Current game time
+- add_rule( server, "Time", value, NO_FLAGS );
++ add_rule(server, "Time", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "notes", key ) )
++ else if (0 == strcmp("notes", key))
+ {
+ // Notes
+- add_rule( server, "Notes", value, NO_FLAGS );
++ add_rule(server, "Notes", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "needcmdr", key ) )
++ else if (0 == strcmp("needcmdr", key))
+ {
+ // Need Commander
+- add_rule( server, "Need Commander", value, NO_FLAGS );
++ add_rule(server, "Need Commander", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "name", key ) )
++ else if (0 == strcmp("name", key))
+ {
+ // Server name
+- server->server_name = strdup( value );
++ server->server_name = strdup(value);
+ }
+- else if ( 0 == strcmp( "fw", key ) )
++ else if (0 == strcmp("fw", key))
+ {
+ // Firewalled
+- add_rule( server, "Firewalled", value, NO_FLAGS );
++ add_rule(server, "Firewalled", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "players", key ) )
++ else if (0 == strcmp("players", key))
+ {
+
+ // Players names
+@@ -9394,12 +10889,12 @@
+
+ // team name
+ n++;
+- while( *n && *n != '\x0a' )
++ while (*n && *n != '\x0a')
+ {
+ n++;
+ }
+
+- if ( *n != '\x0a' )
++ if (*n != '\x0a')
+ {
+ // Broken data
+ break;
+@@ -9407,14 +10902,14 @@
+ *n = '\0';
+
+ player_name = ++n;
+- while( *n )
++ while (*n)
+ {
+- while( *n && *n != '\x0a' )
++ while (*n && *n != '\x0a')
+ {
+ n++;
+ }
+
+- if ( *n != '\x0a' )
++ if (*n != '\x0a')
+ {
+ // Broken data
+ break;
+@@ -9422,23 +10917,22 @@
+ *n = '\0';
+ n++;
+
+- if ( 0 == strncmp( "Team ", player_name, 5 ) )
++ if (0 == strncmp("Team ", player_name, 5))
+ {
+ team_name = player_name;
+ team_number++;
+ }
+ else
+ {
+- if ( 0 != strlen( player_name ) )
++ if (0 != strlen(player_name))
+ {
+- struct player *player = add_player( server, player_number );
+- if ( NULL != player )
++ struct player *player = add_player(server, player_number);
++ if (NULL != player)
+ {
+- player->name = strdup( player_name );
++ player->name = strdup(player_name);
+ player->team = team_number;
+- player->team_name = strdup( team_name );
+- }
+- player_number++;
++ player->team_name = strdup(team_name);
++ } player_number++;
+ }
+ }
+ player_name = n;
+@@ -9448,38 +10942,38 @@
+ *s = '\xFF';
+ }
+
+- cleanup_qserver( server, 1 );
+- return;
++ return DONE_FORCE;
+ }
+
+-void
+-deal_with_farcry_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_farcry_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- char *s, *key, *value, *end;
++ char *s, *key, *value, *end;
++
++ debug( 2, "deal_with_farcry_packet %p, %d", server, pktlen );
+
+ server->n_servers++;
+- if ( NULL == server->server_name )
++ if (NULL == server->server_name)
+ {
+- server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+ }
+- else
++ else
+ {
+- gettimeofday( &server->packet_time1, NULL );
++ gettimeofday(&server->packet_time1, NULL);
+ }
+
+- rawpkt[pktlen]= '\0';
++ rawpkt[pktlen] = '\0';
+
+- end = s = rawpkt;
++ end = s = rawpkt;
+ end += pktlen;
+- while ( *s )
++ while (*s)
+ {
+ // Find the seperator
+- while ( s <= end && *s != '\xFF' )
++ while (s <= end && *s != '\xFF')
+ {
+ s++;
+ }
+
+- if ( s >= end )
++ if (s >= end)
+ {
+ // Hit the end no more
+ break;
+@@ -9487,11 +10981,11 @@
+
+ // key start
+ key = ++s;
+- while ( s < end && *s != '\xFE' )
++ while (s < end && *s != '\xFE')
+ {
+ s++;
+ }
+- if ( *s != '\xFE')
++ if (*s != '\xFE')
+ {
+ // malformed
+ break;
+@@ -9501,75 +10995,75 @@
+ // value start
+ value = s;
+
+- while ( s < end && *s != '\xFF' )
++ while (s < end && *s != '\xFF')
+ {
+ s++;
+ }
+
+- if ( *s == '\xFF' )
++ if (*s == '\xFF')
+ {
+ *s = '\0';
+ }
+ //fprintf( stderr, "'%s' = '%s'\n", key, value );
+
+ // Decode current key par
+- if ( 0 == strcmp( "cmax", key ) )
++ if (0 == strcmp("cmax", key))
+ {
+ // Max players
+- server->max_players = atoi( value );
++ server->max_players = atoi(value);
+ }
+- else if ( 0 == strcmp( "cnum", key ) )
++ else if (0 == strcmp("cnum", key))
+ {
+ // Current players
+- server->num_players = atoi( value );
++ server->num_players = atoi(value);
+ }
+- else if ( 0 == strcmp( "bal", key ) )
++ else if (0 == strcmp("bal", key))
+ {
+ // Balance
+- add_rule( server, "Balance", value, NO_FLAGS );
++ add_rule(server, "Balance", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "world", key ) )
++ else if (0 == strcmp("world", key))
+ {
+ // Current map
+- server->map_name = strdup( value );
++ server->map_name = strdup(value);
+ }
+- else if ( 0 == strcmp( "gametype", key ) )
++ else if (0 == strcmp("gametype", key))
+ {
+ // Game type
+- server->game = find_savage_game( value );
+- add_rule( server, server->type->game_rule, server->game, NO_FLAGS );
++ server->game = find_savage_game(value);
++ add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "pure", key ) )
++ else if (0 == strcmp("pure", key))
+ {
+ // Pure
+- add_rule( server, "Pure", value, NO_FLAGS );
++ add_rule(server, "Pure", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "time", key ) )
++ else if (0 == strcmp("time", key))
+ {
+ // Current game time
+- add_rule( server, "Time", value, NO_FLAGS );
++ add_rule(server, "Time", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "notes", key ) )
++ else if (0 == strcmp("notes", key))
+ {
+ // Notes
+- add_rule( server, "Notes", value, NO_FLAGS );
++ add_rule(server, "Notes", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "needcmdr", key ) )
++ else if (0 == strcmp("needcmdr", key))
+ {
+ // Need Commander
+- add_rule( server, "Need Commander", value, NO_FLAGS );
++ add_rule(server, "Need Commander", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "name", key ) )
++ else if (0 == strcmp("name", key))
+ {
+ // Server name
+- server->server_name = strdup( value );
++ server->server_name = strdup(value);
+ }
+- else if ( 0 == strcmp( "fw", key ) )
++ else if (0 == strcmp("fw", key))
+ {
+ // Firewalled
+- add_rule( server, "Firewalled", value, NO_FLAGS );
++ add_rule(server, "Firewalled", value, NO_FLAGS);
+ }
+- else if ( 0 == strcmp( "players", key ) )
++ else if (0 == strcmp("players", key))
+ {
+
+ // Players names
+@@ -9580,12 +11074,12 @@
+
+ // team name
+ n++;
+- while( *n && *n != '\x0a' )
++ while (*n && *n != '\x0a')
+ {
+ n++;
+ }
+
+- if ( *n != '\x0a' )
++ if (*n != '\x0a')
+ {
+ // Broken data
+ break;
+@@ -9593,14 +11087,14 @@
+ *n = '\0';
+
+ player_name = ++n;
+- while( *n )
++ while (*n)
+ {
+- while( *n && *n != '\x0a' )
++ while (*n && *n != '\x0a')
+ {
+ n++;
+ }
+
+- if ( *n != '\x0a' )
++ if (*n != '\x0a')
+ {
+ // Broken data
+ break;
+@@ -9608,23 +11102,22 @@
+ *n = '\0';
+ n++;
+
+- if ( 0 == strncmp( "Team ", player_name, 5 ) )
++ if (0 == strncmp("Team ", player_name, 5))
+ {
+ team_name = player_name;
+ team_number++;
+ }
+ else
+ {
+- if ( 0 != strlen( player_name ) )
++ if (0 != strlen(player_name))
+ {
+- struct player *player = add_player( server, player_number );
+- if ( NULL != player )
++ struct player *player = add_player(server, player_number);
++ if (NULL != player)
+ {
+- player->name = strdup( player_name );
++ player->name = strdup(player_name);
+ player->team = team_number;
+- player->team_name = strdup( team_name );
+- }
+- player_number++;
++ player->team_name = strdup(team_name);
++ } player_number++;
+ }
+ }
+ player_name = n;
+@@ -9634,574 +11127,624 @@
+ *s = '\xFF';
+ }
+
+- cleanup_qserver( server, 1 );
+- return;
++ return DONE_FORCE;
+ }
+
+-
+-
+ /* postions of map name, player name (in player substring), zero-based */
+ #define BFRIS_MAP_POS 18
+ #define BFRIS_PNAME_POS 11
+-void
+-deal_with_bfris_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_bfris_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- int i, player_data_pos, nplayers;
+- SavedData *sdata;
+- unsigned char *saved_data;
+- int saved_data_size;
+-
+- server->ping_total += time_delta( &packet_recv_time,
+- &server->packet_time1);
+-
+- /* add to the data previously saved */
+- sdata= & server->saved_data;
+- if (! sdata->data)
+- sdata->data = (char*)malloc(pktlen);
+- else
+- sdata->data = (char*)realloc(sdata->data,sdata->datalen + pktlen);
+-
+- memcpy(sdata->data + sdata->datalen, rawpkt, pktlen);
+- sdata->datalen += pktlen;
+-
+- saved_data= (unsigned char*) sdata->data;
+- saved_data_size= sdata->datalen;
+-
+- /* after we get the server portion of the data, server->game != NULL */
+- if (!server->game) {
+-
+- /* server data goes up to map name */
+- if (sdata->datalen <= BFRIS_MAP_POS)
+- return;
+-
+- /* see if map name is complete */
+- player_data_pos=0;
+- for (i=BFRIS_MAP_POS; i<saved_data_size; i++) {
+- if (saved_data[i] == '\0') {
+- player_data_pos = i+1;
+- /* data must extend beyond map name */
+- if (saved_data_size <= player_data_pos)
+- return;
+- break;
+- }
+- }
++ int i, player_data_pos, nplayers;
++ SavedData *sdata;
++ unsigned char *saved_data;
++ int saved_data_size;
+
+- /* did we find beginning of player data? */
+- if (!player_data_pos)
+- return;
+-
+- /* now we can go ahead and fill in server data */
+- server->map_name = strdup((char*)saved_data + BFRIS_MAP_POS);
+- server->max_players = saved_data[12];
+- server->protocol_version = saved_data[11];
+-
+- /* save game type */
+- switch (saved_data[13] & 15) {
+- case 0: server->game = "FFA"; break;
+- case 5: server->game = "Rover"; break;
+- case 6: server->game = "Occupation"; break;
+- case 7: server->game = "SPAAL"; break;
+- case 8: server->game = "CTF"; break;
+- default:
+- server->game = "unknown"; break;
+- }
+- server->flags |= FLAG_DO_NOT_FREE_GAME;
+- add_rule(server,server->type->game_rule,server->game, NO_FLAGS);
++ debug( 2, "deal_with_bfris_packet %p, %d", server, pktlen );
++
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++
++ /* add to the data previously saved */
++ sdata = &server->saved_data;
++ if (!sdata->data)
++ {
++ sdata->data = (char*)malloc(pktlen);
++ }
++ else
++ {
++ sdata->data = (char*)realloc(sdata->data, sdata->datalen + pktlen);
++ }
+
+- if (get_server_rules) {
+- char buf[24];
++ memcpy(sdata->data + sdata->datalen, rawpkt, pktlen);
++ sdata->datalen += pktlen;
+
+- /* server revision */
+- sprintf(buf,"%d",(unsigned int)saved_data[11]);
+- add_rule(server,"Revision",buf, NO_FLAGS);
+-
+- /* latency */
+- sprintf(buf,"%d",(unsigned int)saved_data[10]);
+- add_rule(server,"Latency",buf, NO_FLAGS);
++ saved_data = (unsigned char*)sdata->data;
++ saved_data_size = sdata->datalen;
+
+- /* player allocation */
+- add_rule(server,"Allocation",saved_data[13] & 16 ? "Automatic" : "Manual", NO_FLAGS);
++ /* after we get the server portion of the data, server->game != NULL */
++ if (!server->game)
++ {
+
+- }
++ /* server data goes up to map name */
++ if (sdata->datalen <= BFRIS_MAP_POS)
++ {
++ return INPROGRESS;
++ }
++
++ /* see if map name is complete */
++ player_data_pos = 0;
++ for (i = BFRIS_MAP_POS; i < saved_data_size; i++)
++ {
++ if (saved_data[i] == '\0')
++ {
++ player_data_pos = i + 1;
++ /* data must extend beyond map name */
++ if (saved_data_size <= player_data_pos)
++ {
++ return INPROGRESS;
++ }
++ break;
++ }
++ }
++
++ /* did we find beginning of player data? */
++ if (!player_data_pos)
++ {
++ return INPROGRESS;
++ }
+
+- }
++ /* now we can go ahead and fill in server data */
++ server->map_name = strdup((char*)saved_data + BFRIS_MAP_POS);
++ server->max_players = saved_data[12];
++ server->protocol_version = saved_data[11];
+
+- /* If we got this far, we know the data saved goes at least to the start of
+- the player information, and that the server data is taken care of.
+- */
++ /* save game type */
++ switch (saved_data[13] &15)
++ {
++ case 0:
++ server->game = "FFA";
++ break;
++ case 5:
++ server->game = "Rover";
++ break;
++ case 6:
++ server->game = "Occupation";
++ break;
++ case 7:
++ server->game = "SPAAL";
++ break;
++ case 8:
++ server->game = "CTF";
++ break;
++ default:
++ server->game = "unknown";
++ break;
++ }
++ server->flags |= FLAG_DO_NOT_FREE_GAME;
++ add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
+
+- /* start of player data */
+- player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data+BFRIS_MAP_POS) + 1;
++ if (get_server_rules)
++ {
++ char buf[24];
+
+- /* ensure all player data have arrived */
+- nplayers = 0;
+- while (saved_data[player_data_pos] != '\0') {
++ /* server revision */
++ sprintf(buf, "%d", (unsigned int)saved_data[11]);
++ add_rule(server, "Revision", buf, NO_FLAGS);
+
+- player_data_pos += BFRIS_PNAME_POS;
++ /* latency */
++ sprintf(buf, "%d", (unsigned int)saved_data[10]);
++ add_rule(server, "Latency", buf, NO_FLAGS);
+
+- /* does player data extend to player name? */
+- if (saved_data_size <= player_data_pos + 1)
+- return;
++ /* player allocation */
++ add_rule(server, "Allocation", saved_data[13] &16 ? "Automatic" : "Manual", NO_FLAGS);
+
+- /* does player data extend to end of player name? */
+- for (i=0; player_data_pos + i < saved_data_size; i++) {
++ }
+
+- if (saved_data_size == player_data_pos + i + 1)
+- return;
++ }
+
+- if (saved_data[player_data_pos + i] == '\0') {
+- player_data_pos += i + 1;
+- nplayers++;
+- break;
+- }
+- }
+- }
+- /* all player data are complete */
++ /* If we got this far, we know the data saved goes at least to the start of
++ the player information, and that the server data is taken care of.
++ */
+
+- server->num_players = nplayers;
++ /* start of player data */
++ player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1;
+
+- if (get_player_info) {
++ /* ensure all player data have arrived */
++ nplayers = 0;
++ while (saved_data[player_data_pos] != '\0')
++ {
+
+- /* start of player data */
+- player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data+BFRIS_MAP_POS) + 1;
+-
+- for (i=0; i<nplayers; i++) {
+- struct player *player;
+- player= add_player( server, saved_data[player_data_pos]);
+-
+- player->ship = saved_data[player_data_pos + 1];
+- player->ping = saved_data[player_data_pos + 2];
+- player->frags = saved_data[player_data_pos + 3];
+- player->team = saved_data[player_data_pos + 4];
+- switch (player->team) {
+- case 0: player->team_name = "silver"; break;
+- case 1: player->team_name = "red"; break;
+- case 2: player->team_name = "blue"; break;
+- case 3: player->team_name = "green"; break;
+- case 4: player->team_name = "purple"; break;
+- case 5: player->team_name = "yellow"; break;
+- case 6: player->team_name = "cyan"; break;
+- default:
+- player->team_name = "unknown"; break;
+- }
+- player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+- player->room = saved_data[player_data_pos + 5];
+-
+- /* score is little-endian integer */
+- player->score =
+- saved_data[player_data_pos+7] +
+- (saved_data[player_data_pos+8] << 8) +
+- (saved_data[player_data_pos+9] << 16) +
+- (saved_data[player_data_pos+10] << 24);
+-
+- /* for archs with > 4-byte int */
+- if (player->score & 0x80000000)
+- player->score = -(~(player->score)) - 1;
++ player_data_pos += BFRIS_PNAME_POS;
+
++ /* does player data extend to player name? */
++ if (saved_data_size <= player_data_pos + 1)
++ {
++ return INPROGRESS;
++ }
+
+- player_data_pos += BFRIS_PNAME_POS;
+- player->name = strdup((char*)saved_data + player_data_pos);
++ /* does player data extend to end of player name? */
++ for (i = 0; player_data_pos + i < saved_data_size; i++)
++ {
+
+- player_data_pos += strlen(player->name) + 1;
+- }
++ if (saved_data_size == player_data_pos + i + 1)
++ {
++ return INPROGRESS;
++ }
+
+- }
++ if (saved_data[player_data_pos + i] == '\0')
++ {
++ player_data_pos += i + 1;
++ nplayers++;
++ break;
++ }
++ }
++ }
++ /* all player data are complete */
++
++ server->num_players = nplayers;
++
++ if (get_player_info)
++ {
++
++ /* start of player data */
++ player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1;
++
++ for (i = 0; i < nplayers; i++)
++ {
++ struct player *player;
++ player = add_player(server, saved_data[player_data_pos]);
++
++ player->ship = saved_data[player_data_pos + 1];
++ player->ping = saved_data[player_data_pos + 2];
++ player->frags = saved_data[player_data_pos + 3];
++ player->team = saved_data[player_data_pos + 4];
++ switch (player->team)
++ {
++ case 0:
++ player->team_name = "silver";
++ break;
++ case 1:
++ player->team_name = "red";
++ break;
++ case 2:
++ player->team_name = "blue";
++ break;
++ case 3:
++ player->team_name = "green";
++ break;
++ case 4:
++ player->team_name = "purple";
++ break;
++ case 5:
++ player->team_name = "yellow";
++ break;
++ case 6:
++ player->team_name = "cyan";
++ break;
++ default:
++ player->team_name = "unknown";
++ break;
++ }
++ player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++ player->room = saved_data[player_data_pos + 5];
++
++ /* score is little-endian integer */
++ player->score = saved_data[player_data_pos + 7] +
++ (saved_data[player_data_pos + 8] << 8) +
++ (saved_data[player_data_pos + 9] << 16) +
++ (saved_data[player_data_pos + 10] << 24);
++
++ /* for archs with > 4-byte int */
++ if (player->score &0x80000000)
++ {
++ player->score = - (~(player->score)) - 1;
++ }
+
+- server->server_name = BFRIS_SERVER_NAME;
+- cleanup_qserver(server, 1);
+- return;
++
++ player_data_pos += BFRIS_PNAME_POS;
++ player->name = strdup((char*)saved_data + player_data_pos);
++
++ player_data_pos += strlen(player->name) + 1;
++ }
++
++ }
++
++ server->server_name = BFRIS_SERVER_NAME;
++
++ return DONE_FORCE;
+ }
+
+-struct rule *
+-add_uchar_rule( struct qserver *server, char *key, unsigned char value)
++struct rule *add_uchar_rule(struct qserver *server, char *key, unsigned char value)
+ {
+- char buf[24];
+- sprintf( buf, "%u", (unsigned)value);
+- return add_rule( server, key, buf, NO_FLAGS);
++ char buf[24];
++ sprintf(buf, "%u", (unsigned)value);
++ return add_rule(server, key, buf, NO_FLAGS);
+ }
+
+-void
+-deal_with_descent3_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_descent3_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- char *pkt;
+- char buf[24];
++ char *pkt;
++ char buf[24];
+
+- if ( server->server_name == NULL)
+- server->ping_total += time_delta( &packet_recv_time,
+- &server->packet_time1);
++ debug( 2, "deal_with_descent3_packet %p, %d", server, pktlen );
+
+- if ( pktlen < 4) {
+- fprintf( stderr, "short descent3 packet\n");
+- print_packet( server, rawpkt, pktlen);
+- cleanup_qserver( server, 1);
+- return;
+- }
++ if (server->server_name == NULL)
++ {
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++ }
+
+- /* 'info' response */
+- if ( rawpkt[1] == 0x1f) {
+- if ( server->server_name != NULL)
+- {
+- cleanup_qserver( server, 1);
+- return;
++ if (pktlen < 4)
++ {
++ fprintf(stderr, "short descent3 packet\n");
++ print_packet(server, rawpkt, pktlen);
++ return PKT_ERROR;
+ }
+
++ /* 'info' response */
++ if (rawpkt[1] == 0x1f)
++ {
++ if (server->server_name != NULL)
++ {
++ return PKT_ERROR;
++ }
++
++
++ pkt = &rawpkt[0x15];
++ server->server_name = strdup(pkt);
++ pkt += strlen(pkt) + 2;
++ server->map_name = strdup(pkt); /* mission name (blah.mn3) */
++ pkt += strlen(pkt) + 2;
++ add_rule(server, "level_name", pkt, NO_FLAGS);
++ pkt += strlen(pkt) + 2;
++ add_rule(server, "gametype", pkt, NO_FLAGS);
++ pkt += strlen(pkt) + 1;
++
++ sprintf(buf, "%hu", swap_short_from_little(pkt));
++ add_rule(server, "level_num", buf, NO_FLAGS);
++ pkt += 2;
++ server->num_players = swap_short_from_little(pkt);
++ pkt += 2;
++ server->max_players = swap_short_from_little(pkt);
++ pkt += 2;
++
++ /* unknown/undecoded fields.. stuff like permissible, banned items/ships, etc */
++ add_uchar_rule(server, "u0", pkt[0]);
++ add_uchar_rule(server, "u1", pkt[1]);
++ add_uchar_rule(server, "u2", pkt[2]);
++ add_uchar_rule(server, "u3", pkt[3]);
++ add_uchar_rule(server, "u4", pkt[4]);
++ add_uchar_rule(server, "u5", pkt[5]);
++ add_uchar_rule(server, "u6", pkt[6]);
++ add_uchar_rule(server, "u7", pkt[7]);
++ add_uchar_rule(server, "u8", pkt[8]);
++
++ add_uchar_rule(server, "randpowerup", (unsigned char)!(pkt[4] &1)); /*
++ randomize powerup spawn */
++ add_uchar_rule(server, "acccollisions", (unsigned char)((pkt[5] &4) > 0));
++ /* accurate collision detection */
++ add_uchar_rule(server, "brightships", (unsigned char)((pkt[5] &16) > 0));
++ /* bright player ships */
++ add_uchar_rule(server, "mouselook", (unsigned char)((pkt[6] &1) > 0)); /*
++ mouselook enabled */
++ sprintf(buf, "%s%s", (pkt[4] &16) ? "PP" : "CS", (pkt[6] &1) ? "-ML" : "");
++ add_rule(server, "servertype", buf, NO_FLAGS);
++
++ sprintf(buf, "%hhu", pkt[9]);
++ add_rule(server, "difficulty", buf, NO_FLAGS);
++
++ /* unknown/undecoded fields after known flags removed */
++ add_uchar_rule(server, "x4", (unsigned char)(pkt[4] &~(1+16)));
++ add_uchar_rule(server, "x5", (unsigned char)(pkt[5] &~(4+16)));
++ add_uchar_rule(server, "x6", (unsigned char)(pkt[6] &~1));
++
++ if (get_player_info && server->num_players)
++ {
++ server->next_player_info = 0;
++ send_player_request_packet(server);
++ return INPROGRESS;
++ }
+
+- pkt= &rawpkt[0x15];
+- server->server_name= strdup( pkt);
+- pkt+= strlen(pkt)+2;
+- server->map_name= strdup( pkt); /* mission name (blah.mn3) */
+- pkt+= strlen(pkt)+2;
+- add_rule( server, "level_name", pkt, NO_FLAGS);
+- pkt+= strlen(pkt)+2;
+- add_rule( server, "gametype", pkt, NO_FLAGS);
+- pkt+= strlen(pkt)+1;
+-
+- sprintf( buf, "%hu", swap_short_from_little(pkt));
+- add_rule( server, "level_num", buf, NO_FLAGS);
+- pkt+=2;
+- server->num_players= swap_short_from_little(pkt);
+- pkt+=2;
+- server->max_players= swap_short_from_little(pkt);
+- pkt+=2;
+-
+- /* unknown/undecoded fields.. stuff like permissible, banned items/ships, etc */
+- add_uchar_rule( server, "u0", pkt[0]);
+- add_uchar_rule( server, "u1", pkt[1]);
+- add_uchar_rule( server, "u2", pkt[2]);
+- add_uchar_rule( server, "u3", pkt[3]);
+- add_uchar_rule( server, "u4", pkt[4]);
+- add_uchar_rule( server, "u5", pkt[5]);
+- add_uchar_rule( server, "u6", pkt[6]);
+- add_uchar_rule( server, "u7", pkt[7]);
+- add_uchar_rule( server, "u8", pkt[8]);
+-
+- add_uchar_rule( server, "randpowerup", !(pkt[4]&1)); /* randomize powerup spawn */
+- add_uchar_rule( server, "acccollisions", (pkt[5]&4) > 0); /* accurate collision detection */
+- add_uchar_rule( server, "brightships", (pkt[5]&16) > 0); /* bright player ships */
+- add_uchar_rule( server, "mouselook", (pkt[6]&1) > 0); /* mouselook enabled */
+- sprintf( buf, "%s%s", (pkt[4]&16)?"PP":"CS", (pkt[6]&1)?"-ML":"");
+- add_rule( server, "servertype", buf, NO_FLAGS);
+-
+- sprintf( buf, "%hhu", pkt[9]);
+- add_rule( server, "difficulty", buf, NO_FLAGS);
+-
+- /* unknown/undecoded fields after known flags removed */
+- add_uchar_rule( server, "x4", pkt[4] & ~(1+16));
+- add_uchar_rule( server, "x5", pkt[5] & ~(4+16));
+- add_uchar_rule( server, "x6", pkt[6] & ~1);
+-
+- if ( get_player_info && server->num_players) {
+- server->next_player_info= 0;
+- send_player_request_packet( server);
+- cleanup_qserver( server, 0);
+- return;
+ }
++ /* MP_PLAYERLIST_DATA */
++ else if (rawpkt[1] == 0x73)
++ {
++ struct player *player;
++ struct player **last_player = &server->players;
+
+- }
+- /* MP_PLAYERLIST_DATA */
+- else if ( rawpkt[1] == 0x73) {
+- struct player *player;
+- struct player **last_player= & server->players;
++ if (server->players != NULL)
++ {
++ return PKT_ERROR;
++ }
+
+- if ( server->players != NULL)
++ pkt = &rawpkt[0x4];
++ while (*pkt)
++ {
++ player = (struct player*)calloc(1, sizeof(struct player));
++ player->name = strdup(pkt);
++ pkt += strlen(pkt) + 1;
++ *last_player = player;
++ last_player = &player->next;
++ }
++ server->next_player_info = NO_PLAYER_INFO;
++ }
++ else
+ {
+- cleanup_qserver( server, 1);
+- return;
++ fprintf(stderr, "unknown d3 packet\n");
++ print_packet(server, rawpkt, pktlen);
+ }
+
+- pkt= &rawpkt[0x4];
+- while (*pkt) {
+- player= (struct player*) calloc( 1, sizeof(struct player));
+- player->name= strdup( pkt);
+- pkt+= strlen(pkt) + 1;
+- *last_player= player;
+- last_player= & player->next;
+- }
+- server->next_player_info= NO_PLAYER_INFO;
+- }
+- else {
+- fprintf( stderr, "unknown d3 packet\n");
+- print_packet( server, rawpkt, pktlen);
+- }
+- cleanup_qserver( server, 1);
+- return;
++ return DONE_FORCE;
+ }
+
+
+-#define EYE_NAME_MASK 1
+-#define EYE_TEAM_MASK 2
+-#define EYE_SKIN_MASK 4
+-#define EYE_SCORE_MASK 8
+-#define EYE_PING_MASK 16
+-#define EYE_TIME_MASK 32
++#define EYE_NAME_MASK 1
++#define EYE_TEAM_MASK 2
++#define EYE_SKIN_MASK 4
++#define EYE_SCORE_MASK 8
++#define EYE_PING_MASK 16
++#define EYE_TIME_MASK 32
+
+-void
+-deal_with_eye_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_eye_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- char *next, *end, *value, *key;
+- struct player **last_player;
+- unsigned char pkt_index, pkt_max;
+- unsigned int pkt_id;
++ char *next, *end, *value, *key;
++ struct player **last_player;
++ unsigned char pkt_index, pkt_max;
++ unsigned int pkt_id;
++
++ debug( 2, "deal_with_eye_packet %p, %d", server, pktlen );
+
+- if ( pktlen < 4)
++ if (pktlen < 4)
+ {
+- cleanup_qserver( server, 1);
+- return;
+- }
++ return PKT_ERROR;
++ }
+
+- if ( rawpkt[0] != 'E' || rawpkt[1] != 'Y' || rawpkt[2] != 'E')
++ if (rawpkt[0] != 'E' || rawpkt[1] != 'Y' || rawpkt[2] != 'E')
+ {
+- cleanup_qserver( server, 1);
+- return;
+- }
++ return PKT_ERROR;
++ }
+
+- server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+
+- end= rawpkt + pktlen;
+- pkt_index= rawpkt[3] - '0';
++ end = rawpkt + pktlen;
++ pkt_index = rawpkt[3] - '0';
+
+- if ( pktlen == 1364 || pkt_index != 1)
++ if (pktlen == 1364 || pkt_index != 1)
+ {
+ /* fragmented packet */
+ SavedData *sdata;
+- /* EYE doesn't tell us how many packets to expect. Two packets
++ /* EYE doesn't tell us how many packets to expect. Two packets
+ * is enough for 100+ players on a BF1942 server with standard
+ * server rules.
+ */
+- pkt_max= 2;
+- memcpy( &pkt_id, &rawpkt[pktlen-4], 4);
++ pkt_max = 2;
++ memcpy(&pkt_id, &rawpkt[pktlen - 4], 4);
+
+- if ( server->saved_data.data == NULL)
++ if (server->saved_data.data == NULL)
+ {
+- sdata= & server->saved_data;
++ sdata = &server->saved_data;
+ }
+ else
+ {
+- sdata= (SavedData*) calloc( 1, sizeof(SavedData));
+- sdata->next= server->saved_data.next;
+- server->saved_data.next= sdata;
++ sdata = (SavedData*)calloc(1, sizeof(SavedData));
++ sdata->next = server->saved_data.next;
++ server->saved_data.next = sdata;
+ }
+
+- sdata->pkt_index= pkt_index-1;
+- sdata->pkt_max= pkt_max;
+- sdata->pkt_id= pkt_id;
+- if ( pkt_index == 1)
++ sdata->pkt_index = pkt_index - 1;
++ sdata->pkt_max = pkt_max;
++ sdata->pkt_id = pkt_id;
++ if (pkt_index == 1)
+ {
+- sdata->datalen= pktlen-4;
++ sdata->datalen = pktlen - 4;
+ }
+ else
+ {
+- sdata->datalen= pktlen-8;
++ sdata->datalen = pktlen - 8;
+ }
+- sdata->data= (char*) malloc( sdata->datalen);
++ sdata->data = (char*)malloc(sdata->datalen);
+
+- if ( NULL == sdata->data )
++ if (NULL == sdata->data)
+ {
+- fprintf( stderr, "Out of memory\n" );
+- cleanup_qserver( server, 1 );
+- return;
++ return MEM_ERROR;
+ }
+- if ( pkt_index == 1)
++
++ if (pkt_index == 1)
+ {
+- memcpy( sdata->data, &rawpkt[0], sdata->datalen);
++ memcpy(sdata->data, &rawpkt[0], sdata->datalen);
+ }
+ else
+ {
+- memcpy( sdata->data, &rawpkt[4], sdata->datalen);
++ memcpy(sdata->data, &rawpkt[4], sdata->datalen);
+ }
+
+ /* combine_packets will call us recursively */
+- combine_packets( server);
+- return;
+- }
++ return combine_packets(server);
++ }
+
+- value= dup_n1string( &rawpkt[4], end, &next);
+- if ( value == NULL)
++ value = dup_n1string(&rawpkt[4], end, &next);
++ if (value == NULL)
+ {
+- goto eye_protocol_error;
++ return MEM_ERROR;
+ }
+- add_rule( server, "gamename", value, NO_VALUE_COPY);
++ add_rule(server, "gamename", value, NO_VALUE_COPY);
+
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+- goto eye_protocol_error;
++ return MEM_ERROR;
+ }
+- add_rule( server, "hostport", value, NO_VALUE_COPY);
++ add_rule(server, "hostport", value, NO_VALUE_COPY);
+
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+- goto eye_protocol_error;
++ return MEM_ERROR;
+ }
+- server->server_name= value;
++ server->server_name = value;
+
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+- goto eye_protocol_error;
++ return MEM_ERROR;
+ }
+- server->game= value;
+- add_rule( server, server->type->game_rule, value, NO_FLAGS);
++ server->game = value;
++ add_rule(server, server->type->game_rule, value, NO_FLAGS);
+
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+- goto eye_protocol_error;
++ return MEM_ERROR;
+ }
+- server->map_name= value;
++ server->map_name = value;
+
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+- goto eye_protocol_error;
++ return MEM_ERROR;
+ }
+- add_rule( server, "_version", value, NO_VALUE_COPY);
++ add_rule(server, "_version", value, NO_VALUE_COPY);
+
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+- goto eye_protocol_error;
++ return MEM_ERROR;
+ }
+- add_rule( server, "_password", value, NO_VALUE_COPY);
++ add_rule(server, "_password", value, NO_VALUE_COPY);
+
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+- goto eye_protocol_error;
++ return MEM_ERROR;
+ }
+- server->num_players= atoi(value);
+- free(value);
++ server->num_players = atoi(value);
++ free(value);
+
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+- goto eye_protocol_error;
++ return MEM_ERROR;
+ }
+- server->max_players= atoi(value);
+- free(value);
++ server->max_players = atoi(value);
++ free(value);
+
+- /* rule1,value1,rule2,value2, ... empty string */
++ /* rule1,value1,rule2,value2, ... empty string */
+
+- do
++ do
+ {
+- key= dup_n1string( next, end, &next);
+- if ( key == NULL)
++ key = dup_n1string(next, end, &next);
++ if (key == NULL)
+ {
+ break;
+ }
+- else if ( key[0] == '\0')
++ else if (key[0] == '\0')
+ {
+ free(key);
+ break;
+ }
+
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+ free(key);
+ break;
+ }
+
+- add_rule( server, key, value, NO_VALUE_COPY | NO_KEY_COPY);
+- } while ( 1);
++ add_rule(server, key, value, NO_VALUE_COPY | NO_KEY_COPY);
++ }
++ while (1);
+
+- /* [mask1]<name1><team1><skin1><score1><ping1><time1>[mask2]... */
++ /* [mask1]<name1><team1><skin1><score1><ping1><time1>[mask2]... */
+
+- last_player= & server->players;
+- while ( next && next < end )
++ last_player = &server->players;
++ while (next && next < end)
+ {
+ struct player *player;
+- unsigned mask= *((unsigned char*)next);
++ unsigned mask = *((unsigned char*)next);
+ next++;
+- if ( next >= end)
++ if (next >= end)
+ {
+ break;
+ }
+- if ( mask == 0)
++ if (mask == 0)
+ {
+ break;
+ }
+- player= (struct player*) calloc( 1, sizeof(struct player));
+- if ( player == NULL)
++ player = (struct player*)calloc(1, sizeof(struct player));
++ if (player == NULL)
+ {
+ break;
+ }
+- if ( mask & EYE_NAME_MASK)
++ if (mask &EYE_NAME_MASK)
+ {
+- player->name= dup_n1string( next, end, &next);
++ player->name = dup_n1string(next, end, &next);
+ //fprintf( stderr, "Player '%s'\n", player->name );
+- if ( player->name == NULL)
++ if (player->name == NULL)
+ {
+ break;
+ }
+ }
+- if ( mask & EYE_TEAM_MASK)
++ if (mask &EYE_TEAM_MASK)
+ {
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+ break;
+ }
+- if ( isdigit((unsigned char)value[0]))
++ if (isdigit((unsigned char)value[0]))
+ {
+- player->team= atoi(value);
++ player->team = atoi(value);
+ free(value);
+ }
+ else
+ {
+- player->team_name= value;
++ player->team_name = value;
+ }
+ }
+- if ( mask & EYE_SKIN_MASK)
++ if (mask &EYE_SKIN_MASK)
+ {
+- player->skin= dup_n1string( next, end, &next);
+- if ( player->skin == NULL)
++ player->skin = dup_n1string(next, end, &next);
++ if (player->skin == NULL)
+ {
+ break;
+ }
+ }
+- if ( mask & EYE_SCORE_MASK)
++ if (mask &EYE_SCORE_MASK)
+ {
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+ break;
+ }
+- player->score= atoi(value);
+- player->frags= player->score;
++ player->score = atoi(value);
++ player->frags = player->score;
+ free(value);
+ }
+- if ( mask & EYE_PING_MASK)
++ if (mask &EYE_PING_MASK)
+ {
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+ break;
+ }
+- player->ping= atoi(value);
++ player->ping = atoi(value);
+ free(value);
+ }
+- if ( mask & EYE_TIME_MASK)
++ if (mask &EYE_TIME_MASK)
+ {
+- value= dup_n1string( next, end, &next);
+- if ( value == NULL)
++ value = dup_n1string(next, end, &next);
++ if (value == NULL)
+ {
+ break;
+ }
+- player->connect_time= atoi(value);
++ player->connect_time = atoi(value);
+ free(value);
+ }
+- *last_player= player;
+- last_player= & player->next;
++ *last_player = player;
++ last_player = &player->next;
+ //fprintf( stderr, "Player '%s'\n", player->name );
+- }
+-
+- cleanup_qserver( server, 1);
+- return;
++ }
+
+-eye_protocol_error:
+- cleanup_qserver( server, 1);
++ return DONE_FORCE;
+ }
+
+ static const char hl2_statusresponse[] = "\xFF\xFF\xFF\xFF\x49";
+@@ -10211,9 +11754,7 @@
+ #define HL2_STATUS 1
+ #define HL2_PLAYERS 2
+ #define HL2_RULES 3
+-
+-void
+-deal_with_hl2_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_hl2_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+ char *ptr = rawpkt;
+ char *end = rawpkt + pktlen;
+@@ -10222,659 +11763,865 @@
+ unsigned char protocolver = 0;
+ int n_sent = 0;
+
++ debug( 2, "deal_with_hl2_packet %p, %d", server, pktlen );
++
+ server->n_servers++;
+- if ( server->server_name == NULL)
++ if (server->server_name == NULL)
+ {
+- server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+ }
+ else
+ {
+- gettimeofday( &server->packet_time1, NULL);
++ gettimeofday(&server->packet_time1, NULL);
+ }
+
+ // Check if correct reply
+- if ( pktlen < hl2_response_size )
++ if (pktlen < hl2_response_size)
+ {
+ malformed_packet(server, "short response type");
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ else
+ {
+- if ( 0 == memcmp( hl2_statusresponse, ptr, hl2_response_size ) )
++ if (0 == memcmp(hl2_statusresponse, ptr, hl2_response_size))
+ {
+- if ( pktlen < hl2_response_size + 20 )
++ if (pktlen < hl2_response_size + 20)
+ {
+ malformed_packet(server, "short packet");
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ type = HL2_STATUS;
+ }
+- else if ( 0 == memcmp( hl2_playersresponse, ptr, hl2_response_size ) )
++ else if (0 == memcmp(hl2_playersresponse, ptr, hl2_response_size))
+ {
+ type = HL2_PLAYERS;
+ }
+- else if ( 0 == memcmp( hl2_rulesresponse, ptr, hl2_response_size ) )
++ else if (0 == memcmp(hl2_rulesresponse, ptr, hl2_response_size))
+ {
+ type = HL2_RULES;
+ }
+ else
+ {
+ malformed_packet(server, "unknown response");
+- cleanup_qserver( server, 1);
+- return;
++ return PKT_ERROR;
+ }
+ }
+
+ // header
+ ptr += hl2_response_size;
+
+- switch( type )
++ switch (type)
+ {
+- case HL2_STATUS:
++ case HL2_STATUS:
+
+- // protocol version
+- protocolver = *ptr;
+- ptr++;
++ // protocol version
++ protocolver = *ptr;
++ ptr++;
+
+- debug(2, "protocol: 0x%02X", protocolver );
+- // Commented out till out of beta
+-/*
+- if( '\x02' != protocolver )
+- {
+- malformed_packet(server, "protocol version != 0x02");
+- cleanup_qserver( server, 1);
+- return;
+- }
+-*/
++ debug(2, "protocol: 0x%02X", protocolver);
++ // Commented out till out of beta
++ /*
++ if( '\x02' != protocolver )
++ {
++ malformed_packet(server, "protocol version != 0x02");
++ return PKT_ERROR;
++ }
++ */
+
+- server->protocol_version = protocolver;
+- sprintf( temp, "%d", protocolver );
+- add_rule( server, "protocol", temp, NO_FLAGS);
++ server->protocol_version = protocolver;
++ sprintf(temp, "%d", protocolver);
++ add_rule(server, "protocol", temp, NO_FLAGS);
+
+- // server name
+- server->server_name = strdup( ptr );
+- ptr += strlen( ptr ) + 1;
++ // server name
++ server->server_name = strdup(ptr);
++ ptr += strlen(ptr) + 1;
+
+- // map
+- server->map_name = strdup( ptr );
+- ptr += strlen( ptr ) + 1;
++ // map
++ server->map_name = strdup(ptr);
++ ptr += strlen(ptr) + 1;
+
+- // gamedir
+- server->game = strdup( ptr );
+- add_rule( server, "gamedir", ptr, NO_FLAGS );
+- ptr += strlen( ptr ) + 1;
++ // gamedir
++ server->game = strdup(ptr);
++ add_rule(server, "gamedir", ptr, NO_FLAGS);
++ ptr += strlen(ptr) + 1;
+
+- // description
+- add_rule( server, "description", ptr, NO_FLAGS );
+- ptr += strlen( ptr ) + 1;
++ // description
++ add_rule(server, "description", ptr, NO_FLAGS);
++ ptr += strlen(ptr) + 1;
+
+- // appid
+- ptr += 2;
++ // appid
++ ptr += 2;
+
+- // num players
+- server->num_players = *ptr;
+- ptr++;
++ // num players
++ server->num_players = *ptr;
++ ptr++;
+
+- // max players
+- server->max_players = *ptr;
+- ptr++;
++ // max players
++ server->max_players = *ptr;
++ ptr++;
+
+- // bot players
+- sprintf( temp, "%hhu", (*ptr) );
+- add_rule( server, "bot_players", temp, NO_FLAGS );
+- ptr++;
++ // bot players
++ sprintf(temp, "%hhu", (*ptr));
++ add_rule(server, "bot_players", temp, NO_FLAGS);
++ ptr++;
+
+- // dedicated
+- if ( 'd' == *ptr )
+- {
+- add_rule( server, "sv_type", "dedicated", NO_FLAGS );
+- }
+- else if ( 'l' == *ptr )
+- {
+- add_rule( server, "sv_type", "listen", NO_FLAGS );
+- }
+- else
+- {
+- char tmp[2] = { *ptr, '\0' };
+- add_rule( server, "sv_type", tmp, NO_FLAGS );
+- }
+- ptr++;
++ // dedicated
++ if ('d' == *ptr)
++ {
++ add_rule(server, "sv_type", "dedicated", NO_FLAGS);
++ }
++ else if ('l' == *ptr)
++ {
++ add_rule(server, "sv_type", "listen", NO_FLAGS);
++ }
++ else
++ {
++ char tmp[2] =
++ {
++ *ptr, '\0'
++ }
++ ;
++ add_rule(server, "sv_type", tmp, NO_FLAGS);
++ }
++ ptr++;
+
+- // OS
+- if ( 'l' == *ptr )
+- {
+- add_rule( server, "sv_os", "linux", NO_FLAGS );
+- }
+- else if ( 'w' == *ptr )
+- {
+- add_rule( server, "sv_os", "windows", NO_FLAGS );
+- }
+- else
+- {
+- char tmp[2] = { *ptr, '\0' };
+- add_rule( server, "sv_os", tmp, NO_FLAGS );
+- }
+- ptr++;
++ // OS
++ if ('l' == *ptr)
++ {
++ add_rule(server, "sv_os", "linux", NO_FLAGS);
++ }
++ else if ('w' == *ptr)
++ {
++ add_rule(server, "sv_os", "windows", NO_FLAGS);
++ }
++ else
++ {
++ char tmp[2] =
++ {
++ *ptr, '\0'
++ }
++ ;
++ add_rule(server, "sv_os", tmp, NO_FLAGS);
++ }
++ ptr++;
+
+- // passworded
+- add_rule( server, "sv_password", *ptr ? "1" : "0", NO_FLAGS);
+- ptr++;
++ // passworded
++ add_rule(server, "sv_password", *ptr ? "1" : "0", NO_FLAGS);
++ ptr++;
+
+- // secure
+- add_rule( server, "secure", *ptr ? "1" : "0", NO_FLAGS);
+- ptr++;
++ // secure
++ add_rule(server, "secure", *ptr ? "1" : "0", NO_FLAGS);
++ ptr++;
+
+- // send the other request packets if wanted
+- if ( get_server_rules )
+- {
+- int requests = server->n_requests;
+- send_rule_request_packet( server );
+- server->n_requests = requests; // prevent wrong ping
+- n_sent++;
+- }
+- else if ( get_player_info )
+- {
+- int requests = server->n_requests;
+- send_player_request_packet( server ) ;
+- server->n_requests = requests; // prevent wrong ping
+- n_sent++;
+- }
+- break;
++ // send the other request packets if wanted
++ if (get_server_rules)
++ {
++ int requests = server->n_requests;
++ send_rule_request_packet(server);
++ server->n_requests = requests; // prevent wrong ping
++ n_sent++;
++ }
++ else if (get_player_info)
++ {
++ int requests = server->n_requests;
++ send_player_request_packet(server);
++ server->n_requests = requests; // prevent wrong ping
++ n_sent++;
++ }
++ break;
+
+- case HL2_RULES:
+- // num_players
+- ptr++;
++ case HL2_RULES:
++ // num_players
++ ptr++;
+
+- // max_players
+- ptr++;
+- while ( ptr < end )
+- {
+- char *var = ptr;
+- char *val;
+- ptr += strlen( var ) + 1;
+- val = ptr;
+- ptr += strlen( val ) + 1;
+- add_rule( server, var, val, NO_FLAGS );
+- }
++ // max_players
++ ptr++;
++ while (ptr < end)
++ {
++ char *var = ptr;
++ char *val;
++ ptr += strlen(var) + 1;
++ val = ptr;
++ ptr += strlen(val) + 1;
++ add_rule(server, var, val, NO_FLAGS);
++ }
+
+- if ( get_player_info )
+- {
+- send_player_request_packet( server ) ;
+- n_sent++;
+- }
+- break;
++ if (get_player_info)
++ {
++ send_player_request_packet(server);
++ n_sent++;
++ }
++ break;
+
+- case HL2_PLAYERS:
+- // num_players
+- ptr++;
++ case HL2_PLAYERS:
++ // num_players
++ ptr++;
+
+- while ( ptr < end )
+- {
+- struct player *player = add_player( server, server->n_player_info );
++ while (ptr < end)
++ {
++ struct player *player = add_player(server, server->n_player_info);
+
+- // player no
+- ptr++;
++ // player no
++ ptr++;
+
+- // name
+- player->name = strdup( ptr );
+- ptr += strlen( ptr ) + 1;
+-
+- // frags
+- player->frags = swap_long_from_little( ptr );
+- ptr += 4;
+-
+- // time
+- player->connect_time = swap_float_from_little( ptr );
+- ptr += 4;
+- }
++ // name
++ player->name = strdup(ptr);
++ ptr += strlen(ptr) + 1;
+
+- break;
++ // frags
++ player->frags = swap_long_from_little(ptr);
++ ptr += 4;
+
+- default:
+- malformed_packet( server, "unknown response" );
+- cleanup_qserver( server, 1);
+- return;
+- }
++ // time
++ player->connect_time = swap_float_from_little(ptr);
++ ptr += 4;
++ }
+
++ break;
+
+- if ( 0 == n_sent )
+- {
+- cleanup_qserver( server, 1 );
++ default:
++ malformed_packet(server, "unknown response");
++ return PKT_ERROR;
+ }
+- return;
++
++ return ( 0 == n_sent ) ? DONE_FORCE : INPROGRESS;
+ }
+
+-void
+-deal_with_gamespy_master_response( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_gamespy_master_response(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- if ( pktlen == 0) {
+- int len= server->saved_data.datalen;
+- char *data= server->saved_data.data;
+- char *ip, *portstr;
+- unsigned int ipaddr;
+- unsigned short port;
+- int master_pkt_max;
++ debug( 2, "deal_with_gamespy_master_response %p, %d", server, pktlen );
+
+- server->server_name= GAMESPY_MASTER_NAME;
++ if ( 0 == pktlen || ( pktlen > 6 && 0 == strncmp( rawpkt + pktlen - 6, "final\\", 6 ) ) )
++ {
++ int len = server->saved_data.datalen;
++ char *data = server->saved_data.data;
++ char *ip, *portstr;
++ unsigned int ipaddr;
++ unsigned short port;
++ int master_pkt_max;
+
+- master_pkt_max= (len / 20) * 6;
+- server->master_pkt= (char*) malloc( master_pkt_max);
+- server->master_pkt_len= 0;
+-
+- while ( len) {
+- for ( ; len && *data == '\\'; data++, len--) ;
+- if ( len < 3) break;
+- if ( data[0] == 'i' && data[1] == 'p' && data[2] == '\\') {
+- data+= 3;
+- len-= 3;
+- ip= data;
+- portstr= NULL;
+- for ( ; len && *data != '\\'; data++, len--) {
+- if ( *data == ':') {
+- portstr= data+1;
+- *data= '\0';
+- }
+- }
+- if ( len == 0)
+- break;
+- *data++= '\0';
+- len--;
+- ipaddr= inet_addr( ip);
+- if ( portstr)
+- port= htons( (unsigned short)atoi( portstr));
+- else
+- port= htons( 28000); /* ## default port */
+- if ( server->master_pkt_len >= master_pkt_max) {
+- master_pkt_max+= 20*6;
+- server->master_pkt= (char*) realloc( server->master_pkt,
+- master_pkt_max);
+- }
+- memcpy( server->master_pkt + server->master_pkt_len,
+- &ipaddr, 4);
+- memcpy( server->master_pkt + server->master_pkt_len + 4,
+- &port, 2);
+- server->master_pkt_len+= 6;
+- }
+- else
+- for ( ; len && *data != '\\'; data++, len--) ;
+- }
+-
+- server->n_servers= server->master_pkt_len / 6;
+- server->next_player_info= -1;
+- server->retry1= 0;
++ server->server_name = GAMESPY_MASTER_NAME;
+
+- cleanup_qserver( server, 1);
+- return;
+- }
++ master_pkt_max = (len / 20) *6;
++ server->master_pkt = (char*)malloc(master_pkt_max);
++ server->master_pkt_len = 0;
++
++ while (len)
++ {
++ for (; len && *data == '\\'; data++, len--)
++ ;
++ if (len < 3)
++ {
++ break;
++ }
++ if (data[0] == 'i' && data[1] == 'p' && data[2] == '\\')
++ {
++ data += 3;
++ len -= 3;
++ ip = data;
++ portstr = NULL;
++ for (; len && *data != '\\'; data++, len--)
++ {
++ if (*data == ':')
++ {
++ portstr = data + 1;
++ *data = '\0';
++ }
++ }
++ if (len == 0)
++ {
++ break;
++ }
++ *data++ = '\0';
++ len--;
++ ipaddr = inet_addr(ip);
++ if (portstr)
++ {
++ port = htons((unsigned short)atoi(portstr));
++ }
++ else
++ {
++ port = htons(28000);
++ }
++ /* ## default port */
++ if (server->master_pkt_len >= master_pkt_max)
++ {
++ master_pkt_max += 20 * 6;
++ server->master_pkt = (char*)realloc(server->master_pkt, master_pkt_max);
++ }
++ memcpy(server->master_pkt + server->master_pkt_len, &ipaddr, 4);
++ memcpy(server->master_pkt + server->master_pkt_len + 4, &port, 2);
++ server->master_pkt_len += 6;
++ }
++ else
++ {
++ for (; len && *data != '\\'; data++, len--)
++ ;
++ }
++ }
++
++ server->n_servers = server->master_pkt_len / 6;
++ server->next_player_info = - 1;
++ server->retry1 = 0;
+
+- if (! server->saved_data.data)
+- server->saved_data.data= (char*)malloc( pktlen);
+- else
+- server->saved_data.data= (char*)realloc( server->saved_data.data,
+- server->saved_data.datalen + pktlen);
++ return DONE_FORCE;
++ }
++
++ if (!server->saved_data.data)
++ {
++ server->saved_data.data = (char*)malloc(pktlen);
++ }
++ else
++ {
++ server->saved_data.data = (char*)realloc(server->saved_data.data, server->saved_data.datalen + pktlen);
++ }
+
+- memcpy( server->saved_data.data + server->saved_data.datalen, rawpkt, pktlen);
+- server->saved_data.datalen+= pktlen;
++ memcpy(server->saved_data.data + server->saved_data.datalen, rawpkt, pktlen);
++ server->saved_data.datalen += pktlen;
+
++ return INPROGRESS;
+ }
+
+ /* Misc utility functions
+ */
+
+-char *
+-strndup( const char *string, size_t len)
++char *strndup(const char *string, size_t len)
+ {
+- char *result;
+- result= (char*) malloc( len+1);
+- memcpy( result, string, len);
+- result[len]= '\0';
+- return result;
++ char *result;
++ result = (char*)malloc(len + 1);
++ memcpy(result, string, len);
++ result[len] = '\0';
++ return result;
+ }
+
+-unsigned int
+-swap_long( void *l)
++unsigned int swap_long(void *l)
+ {
+- unsigned char *b= (unsigned char *) l;
+- return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
++ unsigned char *b = (unsigned char*)l;
++ return b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24);
+ }
+
+-unsigned short
+-swap_short( void *l)
++unsigned short swap_short(void *l)
+ {
+- unsigned char *b= (unsigned char *) l;
+- return (unsigned short)b[0] | (b[1]<<8);
++ unsigned char *b = (unsigned char*)l;
++ return (unsigned short)b[0] | (b[1] << 8);
+ }
+
+-unsigned int
+-swap_long_from_little( void *l)
++unsigned int swap_long_from_little(void *l)
+ {
+- unsigned char *b= (unsigned char *) l;
+- unsigned int result;
+- if ( little_endian)
+- memcpy( &result, l, 4);
+- else
+- result= (unsigned int)b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
+- return result;
++ unsigned char *b = (unsigned char*)l;
++ unsigned int result;
++ if (little_endian)
++ {
++ memcpy(&result, l, 4);
++ }
++ else
++ {
++ result = (unsigned int)b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24);
++ }
++ return result;
+ }
+
+-float
+-swap_float_from_little( void *f )
++float swap_float_from_little(void *f)
+ {
+- union {
++ union
++ {
+ int i;
+ float fl;
+ } temp;
+
+ temp.i = swap_long_from_little(f);
+ return temp.fl;
+-};
++}
+
+-unsigned short
+-swap_short_from_little( void *l)
++unsigned short swap_short_from_little(void *l)
+ {
+- unsigned char *b= (unsigned char *) l;
+- unsigned short result;
+- if ( little_endian)
+- memcpy( &result, l, 2);
+- else
+- result= (unsigned short)b[0] | ((unsigned short)b[1]<<8);
+- return result;
++ unsigned char *b = (unsigned char*)l;
++ unsigned short result;
++ if (little_endian)
++ {
++ memcpy(&result, l, 2);
++ }
++ else
++ {
++ result = (unsigned short)b[0] | ((unsigned short)b[1] << 8);
++ }
++ return result;
+ }
+
+ /** write four byte to buf */
+-void put_long_little(unsigned val, char* buf)
++void put_long_little(unsigned val, char *buf)
+ {
+- buf[0] = val & 0xFF;
+- buf[1] = (val >> 8) & 0xFF;
+- buf[2] = (val >> 16) & 0xFF;
+- buf[3] = (val >> 24) & 0xFF;
++ buf[0] = val &0xFF;
++ buf[1] = (val >> 8) &0xFF;
++ buf[2] = (val >> 16) &0xFF;
++ buf[3] = (val >> 24) &0xFF;
+ }
+
+ #define MAXSTRLEN 2048
+
+-char *
+-xml_escape( char *string)
++char *xml_escape(char *string)
+ {
+- static char _buf[4][MAXSTRLEN+8];
+- static int _buf_index= 0;
+- char *result, *b, *end;
+- unsigned char c;
++ static unsigned char _buf[4][MAXSTRLEN + 8];
++ static int _buf_index = 0;
++ unsigned char *result, *b, *end;
++ unsigned int c;
+
+- if ( string == NULL)
+- return "";
++ if (string == NULL)
++ {
++ return "";
++ }
+
+- result= &_buf[_buf_index][0];
+- _buf_index= (_buf_index+1) % 4;
++ result = &_buf[_buf_index][0];
++ _buf_index = (_buf_index + 1) % 4;
+
+- end= &result[MAXSTRLEN];
++ end = &result[MAXSTRLEN];
+
+- b= result;
+- for ( ; *string && b < end; string++) {
+- c= *string;
+- switch ( c) {
+- case '&':
+- *b++= '&';
+- *b++= 'a';
+- *b++= 'm';
+- *b++= 'p';
+- *b++= ';';
+- continue;
+- case '<':
+- *b++= '&';
+- *b++= 'l';
+- *b++= 't';
+- *b++= ';';
+- continue;
+- case '>':
+- *b++= '&';
+- *b++= 'g';
+- *b++= 't';
+- *b++= ';';
+- continue;
+- default:
+- break;
+- }
+- if ( ! name_xforms) {
+- *b++= c;
+- }
+- else if ( xml_encoding == ENCODING_LATIN_1) {
+- if ( isprint(c))
+- *b++= c;
+- else
+- b+= sprintf( b, "&#%u;", c);
+- }
+- else if ( xml_encoding == ENCODING_UTF_8) {
+- if ( (c & 0x80) == 0)
+- *b++= c;
+- else {
+- *b++= 0xC0 | (0x03 & (c >> 6)) ;
+- *b++= 0x80 | (0x3F & c) ;
+- }
++ b = result;
++ for (; *string && b < end; string++)
++ {
++ c = *string;
++ switch (c)
++ {
++ case '&':
++ *b++ = '&';
++ *b++ = 'a';
++ *b++ = 'm';
++ *b++ = 'p';
++ *b++ = ';';
++ continue;
++ case '\'':
++ *b++ = '&';
++ *b++ = 'a';
++ *b++ = 'p';
++ *b++ = 'o';
++ *b++ = 's';
++ *b++ = ';';
++ continue;
++ case '"':
++ *b++ = '&';
++ *b++ = 'q';
++ *b++ = 'u';
++ *b++ = 'o';
++ *b++ = 't';
++ *b++ = ';';
++ continue;
++ case '<':
++ *b++ = '&';
++ *b++ = 'l';
++ *b++ = 't';
++ *b++ = ';';
++ continue;
++ case '>':
++ *b++ = '&';
++ *b++ = 'g';
++ *b++ = 't';
++ *b++ = ';';
++ continue;
++ default:
++ break;
++ }
++
++ // Validate character
++ // http://www.w3.org/TR/2000/REC-xml-20001006#charsets
++ if ( !
++ (
++ 0x09 == c ||
++ 0xA == c ||
++ 0xD == c ||
++ ( 0x20 <= c && 0xD7FF >= c ) ||
++ ( 0xE000 <= c && 0xFFFD >= c ) ||
++ ( 0x10000 <= c && 0x10FFFF >= c )
++ )
++ )
++ {
++ if ( show_errors )
++ {
++ fprintf(stderr, "Encoding error (%d) for U+%x, D+%d\n", 1, c, c);
++ }
++ }
++ else if (xml_encoding == ENCODING_LATIN_1)
++ {
++ if (!name_xforms)
++ {
++ *b++ = c;
++ }
++ else
++ {
++ if (isprint(c))
++ {
++ *b++ = c;
++ }
++ else
++ {
++ b += sprintf( (char *)b, "&#%u;", c);
++ }
++ }
++ }
++ else if (xml_encoding == ENCODING_UTF_8)
++ {
++ unsigned char tempbuf[10] =
++ {
++ 0
++ };
++ unsigned char *buf = &tempbuf[0];
++ int bytes = 0;
++ int error = 1;
++
++ // Valid character ranges
++ if (
++ 0x09 == c ||
++ 0xA == c ||
++ 0xD == c ||
++ ( 0x20 <= c && 0xD7FF >= c ) ||
++ ( 0xE000 <= c && 0xFFFD >= c ) ||
++ ( 0x10000 <= c && 0x10FFFF >= c )
++ )
++ {
++ error = 0;
++ }
++
++ if (c < 0x80)
++ /* 0XXX XXXX one byte */
++ {
++ buf[0] = c;
++ bytes = 1;
++ }
++ else if (c < 0x0800)
++ /* 110X XXXX two bytes */
++ {
++ buf[0] = 0xC0 | (0x03 &(c >> 6));
++ buf[1] = 0x80 | (0x3F &c);
++ bytes = 2;
++ }
++ else if (c < 0x10000)
++ /* 1110 XXXX three bytes */
++ {
++ buf[0] = 0xE0 | (c >> 12);
++ buf[1] = 0x80 | ((c >> 6) &0x3F);
++ buf[2] = 0x80 | (c &0x3F);
++
++ bytes = 3;
++ if (c == UTF8BYTESWAPNOTACHAR || c == UTF8NOTACHAR)
++ {
++ error = 3;
++ }
++
++ }
++ else if (c < 0x10FFFF)
++ /* 1111 0XXX four bytes */
++ {
++ buf[0] = 0xF0 | (c >> 18);
++ buf[1] = 0x80 | ((c >> 12) &0x3F);
++ buf[2] = 0x80 | ((c >> 6) &0x3F);
++ buf[3] = 0x80 | (c &0x3F);
++ bytes = 4;
++ if (c > UTF8MAXFROMUCS4)
++ {
++ error = 4;
++ }
++
++ }
++ else if (c < 0x4000000)
++ /* 1111 10XX five bytes */
++ {
++ buf[0] = 0xF8 | (c >> 24);
++ buf[1] = 0x80 | (c >> 18);
++ buf[2] = 0x80 | ((c >> 12) &0x3F);
++ buf[3] = 0x80 | ((c >> 6) &0x3F);
++ buf[4] = 0x80 | (c &0x3F);
++ bytes = 5;
++ error = 5;
++ }
++ else if (c < 0x80000000)
++ /* 1111 110X six bytes */
++ {
++ buf[0] = 0xFC | (c >> 30);
++ buf[1] = 0x80 | ((c >> 24) &0x3F);
++ buf[2] = 0x80 | ((c >> 18) &0x3F);
++ buf[3] = 0x80 | ((c >> 12) &0x3F);
++ buf[4] = 0x80 | ((c >> 6) &0x3F);
++ buf[5] = 0x80 | (c &0x3F);
++ bytes = 6;
++ error = 6;
++ }
++ else
++ {
++ error = 7;
++ }
++
++ if (error)
++ {
++ int i;
++ fprintf(stderr, "UTF-8 encoding error (%d) for U+%x, D+%d : ", error, c, c);
++ for (i = 0; i < bytes; i++)
++ {
++ fprintf(stderr, "0x%02x ", buf[i]);
++ }
++ fprintf(stderr, "\n");
++ }
++ else
++ {
++ int i;
++ for (i = 0; i < bytes; ++i)
++ {
++ *b++ = buf[i];
++ }
++ }
++
++ }
+ }
+- }
+- *b= '\0';
+- return result;
++ *b = '\0';
++ return (char*)result;
+ }
+
+
+-static char *quake3_escape_colors[8] = {
+-"black", "red", "green", "yellow", "blue", "cyan", "magenta", "white"
++static char *quake3_escape_colors[8] =
++{
++ "black", "red", "green", "yellow", "blue", "cyan", "magenta", "white"
+ };
+
+-static char *sof_colors[32] = {
+-"FFFFFF","FFFFFF","FF0000","00FF00","FFFF00","0000FF","FF00FF","00FFFF",
+-"000000","7F7F7F","702D07","7F0000","007F00","FFFFFF","007F7F","00007F",
+-"564D28","4C5E36","370B65","005572","54647E","1E2A63","66097B","705E61",
+-"980053","960018","702D07","54492A","61A997","CB8F39","CF8316","FF8020"
++static char *sof_colors[32] =
++{
++ "FFFFFF", "FFFFFF", "FF0000", "00FF00", "FFFF00", "0000FF", "FF00FF", "00FFFF", "000000", "7F7F7F", "702D07", "7F0000", "007F00",
++ "FFFFFF", "007F7F", "00007F", "564D28", "4C5E36", "370B65", "005572", "54647E", "1E2A63", "66097B", "705E61", "980053",
++ "960018", "702D07", "54492A", "61A997", "CB8F39", "CF8316", "FF8020"
+ };
+
+
+-char *
+-xform_name( char *string, struct qserver *server)
++char *xform_name(char *string, struct qserver *server)
+ {
+- static char _buf1[2048], _buf2[2048];
+- static char *_q= &_buf1[0];
+- unsigned char *s= (unsigned char*) string;
++ static char _buf1[2048], _buf2[2048], _buf3[2048], _buf4[2048];
++ static char *_q = &_buf1[0];
++ static char *_q2 = &_buf3[0];
++ unsigned char *s = (unsigned char*)string;
+ char *q;
+ int is_server_name = (string == server->server_name);
+- int font_tag= 0;
++ int font_tag = 0;
++
++ _q = _q == _buf1 ? _buf2 : _buf1;
++ q = _q;
+
+- _q= _q == _buf1 ? _buf2 : _buf1;
+- q= _q;
++ if (s == NULL)
++ {
++ q[0] = '?';
++ q[1] = '\0';
++ return _q;
++ }
+
+- if ( s == NULL)
++ strcpy(_q, string);
++ if (!name_xforms)
+ {
+- q[0]= '?';
+- q[1]= '\0';
+ return _q;
+ }
++ if (name_xforms_strip_unprintable)
++ {
++ // We're double processing so need to use the second set of buffers
++ _q2 = (_q2 == _buf3) ? _buf4 : _buf3;
++ q = _q2;
++ for (; *s; s++)
++ {
++ if (isprint(*s))
++ {
++ *q = *s;
++ q++;
++ }
++ }
++ *q = '\0';
++ s = (unsigned char *)_q2;
++ q = _q;
+
+- if ( ! name_xforms) {
+- strcpy( _q, string);
+- return _q;
++ if (*_q2 == '\0')
++ {
++ q[0] = '?';
++ q[1] = '\0';
++ return _q2;
++ }
+ }
+
+- if ( (hex_player_names && !is_server_name) || (hex_server_names && is_server_name))
++ if ((hex_player_names && !is_server_name) || (hex_server_names && is_server_name))
+ {
+- for ( ; *s; s++, q+= 2)
++ for (; *s; s++, q += 2)
+ {
+- sprintf( q, "%02x", *s);
++ sprintf(q, "%02x", *s);
+ }
+- *q= '\0';
++ *q = '\0';
+ return _q;
+ }
+- if ( server->type->flags & TF_QUAKE3_NAMES)
++ if (server->type->flags &TF_QUAKE3_NAMES)
+ {
+- for ( ; *s; s++)
++ for (; *s; s++)
+ {
+- if ( *s == '^' && *(s+1) != '^')
++ if (*s == '^' && *(s + 1) != '^')
+ {
+- if ( *(s+1) == '\0')
++ if (*(s + 1) == '\0')
+ {
+ break;
+ }
+- if ( html_names == 1)
++ if (html_names == 1)
+ {
+- q+= sprintf( q, "%s<font color=\"%s\">",
+- font_tag?"</font>":"",
+- quake3_escape_colors[ *(s+1) & 0x7]);
++ q += sprintf(q, "%s<font color=\"%s\">", font_tag ? "</font>" : "", quake3_escape_colors[*(s + 1) &0x7]);
+ s++;
+- font_tag= 1;
++ font_tag = 1;
+ }
+- else if ( strip_carets)
++ else if (strip_carets)
+ {
+ s++;
+ }
+ else
+ {
+- *q++= *s;
++ *q++ = *s;
+ }
+ }
+ else
+ {
+- int inc = html_entity( *s, q );
+- if ( 0 != inc )
++ int inc = html_entity(*s, q);
++ if (0 != inc)
+ {
+ q += inc;
+ }
+- else if ( isprint(*s))
+- {
+- *q++= *s;
+- }
+- else if ( *s == '\033')
++ else if (isprint(*s))
+ {
++ *q++ = *s;
+ }
+- else if ( *s == 0x80)
++ else if (*s == '\033'){}
++ else if (*s == 0x80)
+ {
+- *q++= '(';
++ *q++ = '(';
+ }
+- else if ( *s == 0x81)
++ else if (*s == 0x81)
+ {
+- *q++= '=';
++ *q++ = '=';
+ }
+- else if ( *s == 0x82)
++ else if (*s == 0x82)
+ {
+- *q++= ')';
++ *q++ = ')';
+ }
+- else if ( *s == 0x10 || *s == 0x90)
++ else if (*s == 0x10 || *s == 0x90)
+ {
+- *q++= '[';
++ *q++ = '[';
+ }
+- else if ( *s == 0x11 || *s == 0x91)
++ else if (*s == 0x11 || *s == 0x91)
+ {
+- *q++= ']';
++ *q++ = ']';
+ }
+- else if ( *s >= 0x92 && *s <= 0x9a)
++ else if (*s >= 0x92 && *s <= 0x9a)
+ {
+- *q++= *s - 98;
++ *q++ = *s - 98;
+ }
+- else if ( *s >= 0xa0 && *s <= 0xe0)
++ else if (*s >= 0xa0 && *s <= 0xe0)
+ {
+- *q++= *s - 128;
++ *q++ = *s - 128;
+ }
+- else if ( *s >= 0xe1 && *s <= 0xfa)
++ else if (*s >= 0xe1 && *s <= 0xfa)
+ {
+- *q++= *s - 160;
++ *q++ = *s - 160;
+ }
+- else if ( *s >= 0xfb && *s <= 0xfe)
++ else if (*s >= 0xfb && *s <= 0xfe)
+ {
+- *q++= *s - 128;
++ *q++ = *s - 128;
+ }
+ }
+ }
+- *q= '\0';
++ *q = '\0';
+ }
+- else if ( !is_server_name && (server->type->flags & TF_TRIBES2_NAMES))
++ else if (!is_server_name && (server->type->flags &TF_TRIBES2_NAMES))
+ {
+- for ( ; *s; s++)
++ for (; *s; s++)
+ {
+- int inc = html_entity( *s, q );
+- if ( 0 != inc )
++ int inc = html_entity(*s, q);
++ if (0 != inc)
+ {
+ q += inc;
+ continue;
+ }
+- else if ( isprint(*s))
++ else if (isprint(*s))
+ {
+- *q++= *s;
++ *q++ = *s;
+ continue;
+ }
+- if ( html_names == 1 && s[1] != '\0')
++ if (html_names == 1 && s[1] != '\0')
+ {
+ char *font_color;
+- switch( *s)
++ switch (*s)
+ {
+- case 0x8: font_color= "white"; break; /* normal */
+- case 0xb: font_color= "yellow"; break; /* tribe tag */
+- case 0xc: font_color= "blue"; break; /* alias */
+- case 0xe: font_color= "green"; break; /* bot */
+- default: font_color= NULL;
++ case 0x8:
++ font_color = "white";
++ break; /* normal */
++ case 0xb:
++ font_color = "yellow";
++ break; /* tribe tag */
++ case 0xc:
++ font_color = "blue";
++ break; /* alias */
++ case 0xe:
++ font_color = "green";
++ break; /* bot */
++ default:
++ font_color = NULL;
+ }
+- if ( font_color)
++ if (font_color)
+ {
+- q+= sprintf( q, "%s<font color=\"%s\">",
+- font_tag?"</font>":"",
+- font_color);
+- font_tag= 1;
++ q += sprintf(q, "%s<font color=\"%s\">", font_tag ? "</font>" : "", font_color);
++ font_tag = 1;
+ }
+ }
+ }
+- *q= '\0';
++ *q = '\0';
+ }
+- else if ( server->type->flags & TF_U2_NAMES )
++ else if (server->type->flags &TF_U2_NAMES)
+ {
+- for ( ; *s; s++)
++ for (; *s; s++)
+ {
+- if ( 0 == memcmp( s, "^\1", 2 ) )
++ if (0 == memcmp(s, "^\1", 2))
+ {
+ // xmp Color follows
+ s += 2;
+- q += u2xmp_html_color( (unsigned char)*s, q, &font_tag );
++ q += u2xmp_html_color((unsigned char) *s, q, &font_tag);
+ }
+- else if ( 0 == memcmp( s, "\x1b", 1 ) )
++ else if (0 == memcmp(s, "\x1b", 1))
+ {
+ // Color follows
+ s += 1;
+- q += ut2k4_html_color( s, q, &font_tag );
++ q += ut2k4_html_color(s, q, &font_tag);
+ s += 2;
+ }
+ else
+ {
+- int inc = html_entity( *s, q );
+- if ( 0 != inc )
++ int inc = html_entity(*s, q);
++ if (0 != inc)
+ {
+ q += inc;
+ }
+- else if ( isprint(*s))
++ else if (isprint(*s))
+ {
+- *q++= *s;
++ *q++ = *s;
+ }
+- else if ( 0xa0 == *s )
++ else if (0xa0 == *s)
+ {
+- *q++= ' ';
++ *q++ = ' ';
+ }
+ }
+ }
+ *q = '\0';
+ }
+- else if ( server->type->flags & TF_TM_NAMES )
++ else if (server->type->flags &TF_TM_NAMES)
+ {
+ int open = 0;
+- for ( ; *s; s++)
++ char c1, c2, c3;
++
++ for (; *s; s++)
+ {
+- if ( *s == '$' )
++ if (*s == '$')
+ {
+ s++;
+- switch ( *s )
++ switch (*s)
+ {
+ case 'i':
+ case 'I':
+ // italic
+- if ( 1 == html_names )
++ if (1 == html_names)
+ {
+- strcat( q, "<span style=\"font-style:italic\">" );
++ strcat(q, "<span style=\"font-style:italic\">");
+ q += 32;
+ open++;
+ }
+@@ -10894,37 +12641,49 @@
+ case 'm':
+ case 'M':
+ // normal
+- if ( 1 == html_names )
++ if (1 == html_names)
+ {
+- strcat( q, "<span style=\"font-style:normal\">" );
++ strcat(q, "<span style=\"font-style:normal\">");
+ q += 32;
+ open++;
+ }
+ break;
++ case 'o':
++ case 'O':
++ // bold
++ if (1 == html_names)
++ {
++ strcat(q, "<span style=\"font-style:bold\">");
++ q += 30;
++ open++;
++ }
++ break;
+ case 'g':
+ case 'G':
+ // default color
+- strcat( q, "<span style=\"color:black\">" );
+- q += 26;
+- open++;
++ if (1 == html_names)
++ {
++ strcat(q, "<span style=\"color:black\">");
++ q += 26;
++ open++;
++ }
+ break;
+ case 'z':
+ case 'Z':
+ // reset all
+- while ( open )
++ while (open)
+ {
+- strcat( q, "</span>" );
++ strcat(q, "</span>");
+ q += 7;
+ open--;
+ }
+- open = 0;
+ break;
+ case 't':
+ case 'T':
+ // capitalise
+- if ( 1 == html_names )
++ if (1 == html_names)
+ {
+- strcat( q, "<span style=\"text-transform:capitalize\">" );
++ strcat(q, "<span style=\"text-transform:capitalize\">");
+ q += 40;
+ open++;
+ }
+@@ -10933,21 +12692,25 @@
+ // literal $
+ *q++ = '$';
+ break;
++ case '\0':
++ // Unexpected end
++ break;
+ default:
+ // color
+- if ( 1 == html_names )
+- {
+- sprintf( q, "<span style=\"color:%c%c%c%c%c%c\">", *s, *s, *(s+1), *(s+1), *(s+2), *(s+2) );
+- q += 27;
+- open++;
+- }
+- if ( *(s+1) )
+- {
+- s++;
+- }
+- if ( *(s+1) )
++ c3 = '\0';
++ c1 = *s;
++ s++;
++ c2 = *s;
++ if (c2)
+ {
+ s++;
++ c3 = *s;
++ if (c3 && 1 == html_names)
++ {
++ sprintf(q, "<span style=\"color:#%c%c%c%c%c%c\">", c1, c1, c2, c2, c3, c3);
++ q += 28;
++ open++;
++ }
+ }
+ break;
+ }
+@@ -10958,89 +12721,80 @@
+ }
+ }
+
+- while ( open )
++ while (open)
+ {
+- strcat( q, "</span>" );
++ strcat(q, "</span>");
+ q += 7;
+ open--;
+ }
+ *q = '\0';
+ }
+- else if ( !is_server_name || server->type->flags & TF_SOF_NAMES )
++ else if (!is_server_name || server->type->flags &TF_SOF_NAMES)
+ {
+ // Catch all for NOT is_server_name OR TF_SOF_NAMES
+ // The may not be the intention but is needed for q1 at least
+- for ( ; *s; s++)
++ for (; *s; s++)
+ {
+- int inc = html_entity( *s, q );
+- if ( 0 != inc )
++ int inc = html_entity(*s, q);
++ if (0 != inc)
+ {
+ q += inc;
+ continue;
+ }
+
+- if ( *s < ' ' )
++ if (*s < ' ')
+ {
+- if ( html_names == 1)
++ if (html_names == 1)
+ {
+- q+= sprintf( q, "%s<font color=\"#%s\">",
+- font_tag?"</font>":"",
+- sof_colors[ *(s) ]);
+- font_tag= 1;
++ q += sprintf(q, "%s<font color=\"#%s\">", font_tag ? "</font>" : "", sof_colors[*(s)]);
++ font_tag = 1;
+ }
+ }
+- else if ( isprint(*s))
++ else if (isprint(*s))
+ {
+- *q++= *s;
++ *q++ = *s;
+ }
+ // ## more fixes below; double check against real sof servers
+- else if ( *s >= 0xa0)
++ else if (*s >= 0xa0)
+ {
+- *q++= *s & 0x7f;
++ *q++ = *s &0x7f;
+ }
+- else if ( *s >= 0x92 && *s < 0x9c)
++ else if (*s >= 0x92 && *s < 0x9c)
+ {
+- *q++= '0' + (*s - 0x92);
++ *q++ = '0' + (*s - 0x92);
+ }
+- else if ( *s >= 0x12 && *s < 0x1c)
++ else if (*s >= 0x12 && *s < 0x1c)
+ {
+- *q++= '0' + (*s - 0x12);
++ *q++ = '0' + (*s - 0x12);
+ }
+- else if ( *s == 0x90 || *s == 0x10)
++ else if (*s == 0x90 || *s == 0x10)
+ {
+- *q++= '[';
++ *q++ = '[';
+ }
+- else if ( *s == 0x91 || *s == 0x11)
++ else if (*s == 0x91 || *s == 0x11)
+ {
+- *q++= ']';
++ *q++ = ']';
+ }
+- else if ( *s == 0xa || *s == 0xc || *s == 0xd)
++ else if (*s == 0xa || *s == 0xc || *s == 0xd)
+ {
+- *q++= ']';
++ *q++ = ']';
+ }
+ }
+- *q= '\0';
+- }
+- else
+- {
+- strcpy( _q, string);
++ *q = '\0';
+ }
+
+- if ( font_tag )
++ if (font_tag)
+ {
+- q+= sprintf( q, "</font>");
++ q += sprintf(q, "</font>");
+ }
+
+ return _q;
+ }
+-
+-int u2xmp_html_color( short color, char *dest, int *font_tag )
++int u2xmp_html_color(short color, char *dest, int *font_tag)
+ {
+- if ( 1 == html_names )
++ if (1 == html_names)
+ {
+- int len = sprintf( dest, "%s<font color=\"%s\">",
+- *font_tag ? "</font>" : "", unreal_rgb_colors[ color - 1 ]
+- );
++ int len = sprintf(dest, "%s<font color=\"%s\">", *font_tag ? "</font>" : "", unreal_rgb_colors[color - 1]);
+ *font_tag = 1;
+
+ return len;
+@@ -11048,16 +12802,11 @@
+ return 0;
+ }
+
+-int ut2k4_html_color( const unsigned char *color, char *dest, int *font_tag )
++int ut2k4_html_color(const unsigned char *color, char *dest, int *font_tag)
+ {
+- if ( 1 == html_names )
++ if (1 == html_names)
+ {
+- int len = sprintf( dest, "%s<font color=\"#%02hhx%02hhx%02hhx\">",
+- *font_tag ? "</font>" : "",
+- color[0],
+- color[1],
+- color[2]
+- );
++ int len = sprintf(dest, "%s<font color=\"#%02hhx%02hhx%02hhx\">", *font_tag ? "</font>" : "", color[0], color[1], color[2]);
+ *font_tag = 1;
+
+ return len;
+@@ -11065,188 +12814,225 @@
+ return 0;
+ }
+
+-int
+-is_default_rule( struct rule *rule)
++int is_default_rule(struct rule *rule)
+ {
+- if ( strcmp( rule->name, "sv_maxspeed") == 0)
+- return strcmp( rule->value, Q_DEFAULT_SV_MAXSPEED) == 0;
+- if ( strcmp( rule->name, "sv_friction") == 0)
+- return strcmp( rule->value, Q_DEFAULT_SV_FRICTION) == 0;
+- if ( strcmp( rule->name, "sv_gravity") == 0)
+- return strcmp( rule->value, Q_DEFAULT_SV_GRAVITY) == 0;
+- if ( strcmp( rule->name, "noexit") == 0)
+- return strcmp( rule->value, Q_DEFAULT_NOEXIT) == 0;
+- if ( strcmp( rule->name, "teamplay") == 0)
+- return strcmp( rule->value, Q_DEFAULT_TEAMPLAY) == 0;
+- if ( strcmp( rule->name, "timelimit") == 0)
+- return strcmp( rule->value, Q_DEFAULT_TIMELIMIT) == 0;
+- if ( strcmp( rule->name, "fraglimit") == 0)
+- return strcmp( rule->value, Q_DEFAULT_FRAGLIMIT) == 0;
+- return 0;
+-}
+-
+-int html_entity( const char c, char *dest )
+-{
+- if ( html_mode )
+- {
+- switch( c )
+- {
+- case '<':
+- strcpy( dest, "<" );
+- return 4;
+- case '>':
+- strcpy( dest, ">" );
+- return 4;
+- case '&':
+- strcpy( dest, "&" );
+- return 5;
+- default:
+- break;
++ if (strcmp(rule->name, "sv_maxspeed") == 0)
++ {
++ return strcmp(rule->value, Q_DEFAULT_SV_MAXSPEED) == 0;
++ } if (strcmp(rule->name, "sv_friction") == 0)
++ {
++ return strcmp(rule->value, Q_DEFAULT_SV_FRICTION) == 0;
++ }
++ if (strcmp(rule->name, "sv_gravity") == 0)
++ {
++ return strcmp(rule->value, Q_DEFAULT_SV_GRAVITY) == 0;
++ }
++ if (strcmp(rule->name, "noexit") == 0)
++ {
++ return strcmp(rule->value, Q_DEFAULT_NOEXIT) == 0;
++ }
++ if (strcmp(rule->name, "teamplay") == 0)
++ {
++ return strcmp(rule->value, Q_DEFAULT_TEAMPLAY) == 0;
++ }
++ if (strcmp(rule->name, "timelimit") == 0)
++ {
++ return strcmp(rule->value, Q_DEFAULT_TIMELIMIT) == 0;
++ }
++ if (strcmp(rule->name, "fraglimit") == 0)
++ {
++ return strcmp(rule->value, Q_DEFAULT_FRAGLIMIT) == 0;
++ }
++ return 0;
++}
++
++int html_entity(const char c, char *dest)
++{
++ if (html_mode)
++ {
++ switch (c)
++ {
++ case '<':
++ strcpy(dest, "<");
++ return 4;
++ case '>':
++ strcpy(dest, ">");
++ return 4;
++ case '&':
++ strcpy(dest, "&");
++ return 5;
++ default:
++ break;
+ }
+ }
+
+ return 0;
+ }
+
+-char *
+-strherror( int h_err)
++char *strherror(int h_err)
+ {
+- static char msg[100];
+- switch (h_err) {
+- case HOST_NOT_FOUND: return "host not found";
+- case TRY_AGAIN: return "try again";
+- case NO_RECOVERY: return "no recovery";
+- case NO_ADDRESS: return "no address";
+- default: sprintf( msg, "%d", h_err); return msg;
+- }
++ static char msg[100];
++ switch (h_err)
++ {
++ case HOST_NOT_FOUND:
++ return "host not found";
++ case TRY_AGAIN:
++ return "try again";
++ case NO_RECOVERY:
++ return "no recovery";
++ case NO_ADDRESS:
++ return "no address";
++ default:
++ sprintf(msg, "%d", h_err);
++ return msg;
++ }
+ }
+
+-int
+-time_delta( struct timeval *later, struct timeval *past)
++int time_delta(struct timeval *later, struct timeval *past)
+ {
+- if ( later->tv_usec < past->tv_usec) {
+- later->tv_sec--;
+- later->tv_usec+= 1000000;
+- }
+- return (later->tv_sec - past->tv_sec) * 1000 +
+- (later->tv_usec - past->tv_usec) / 1000;
++ if (later->tv_usec < past->tv_usec)
++ {
++ later->tv_sec--;
++ later->tv_usec += 1000000;
++ }
++ return (later->tv_sec - past->tv_sec) *1000+(later->tv_usec - past->tv_usec) / 1000;
++}
++
++int connection_inprogress()
++{
++#ifdef _WIN32
++ return WSAGetLastError() == WSAEWOULDBLOCK;
++#else
++ return errno == EINPROGRESS;
++#endif
++}
++
++int connection_refused()
++{
++#ifdef _WIN32
++ return WSAGetLastError() == WSAECONNABORTED;
++#else
++ return errno == ECONNREFUSED;
++#endif
++}
++
++int connection_would_block()
++{
++#ifdef _WIN32
++ return WSAGetLastError() == WSAEWOULDBLOCK;
++#else
++ return errno == EAGAIN;
++#endif
++}
++
++int connection_reset()
++{
++#ifdef _WIN32
++ return WSAGetLastError() == WSAECONNRESET;
++#else
++ return errno == ECONNRESET;
++#endif
+ }
+
+-int
+-connection_refused()
++void clear_socketerror()
+ {
+ #ifdef _WIN32
+- return WSAGetLastError() == WSAECONNABORTED;
++ WSASetLastError(0);
+ #else
+- return errno == ECONNREFUSED;
++ errno = 0;
+ #endif
+ }
+
+-void
+-set_non_blocking( int fd)
++void set_non_blocking(int fd)
+ {
+ #ifdef _WIN32
+- int one= 1;
+- ioctlsocket( fd, FIONBIO, (unsigned long*)&one);
++ int one = 1;
++ ioctlsocket(fd, FIONBIO, (unsigned long*) &one);
+ #else
+ #ifdef O_NONBLOCK
+- fcntl( fd, F_SETFL, O_NONBLOCK);
++ fcntl(fd, F_SETFL, O_NONBLOCK);
+ #else
+- fcntl( fd, F_SETFL, O_NDELAY);
++ fcntl(fd, F_SETFL, O_NDELAY);
+ #endif // O_NONBLOCK
+ #endif // _WIN32
+ }
+
+-char *
+-quake_color( int color)
++char *quake_color(int color)
+ {
+- static char *colors[] = {
+- "White", /* 0 */
+- "Brown", /* 1 */
+- "Lavender", /* 2 */
+- "Khaki", /* 3 */
+- "Red", /* 4 */
+- "Lt Brown", /* 5 */
+- "Peach", /* 6 */
+- "Lt Peach", /* 7 */
+- "Purple", /* 8 */
+- "Dk Purple", /* 9 */
+- "Tan", /* 10 */
+- "Green", /* 11 */
+- "Yellow", /* 12 */
+- "Blue", /* 13 */
+- "Blue", /* 14 */
+- "Blue" /* 15 */
+- };
++ static char *colors[] =
++ {
++ "White", /* 0 */
++ "Brown", /* 1 */
++ "Lavender", /* 2 */
++ "Khaki", /* 3 */
++ "Red", /* 4 */
++ "Lt Brown", /* 5 */
++ "Peach", /* 6 */
++ "Lt Peach", /* 7 */
++ "Purple", /* 8 */
++ "Dk Purple",/* 9 */
++ "Tan", /* 10 */
++ "Green", /* 11 */
++ "Yellow", /* 12 */
++ "Blue", /* 13 */
++ "Blue", /* 14 */
++ "Blue" /* 15 */
++ };
+
+- static char *rgb_colors[] = {
+- "#ffffff", /* 0 */
+- "#8b4513", /* 1 */
+- "#e6e6fa", /* 2 */
+- "#f0e68c", /* 3 */
+- "#ff0000", /* 4 */
+- "#deb887", /* 5 */
+- "#eecbad", /* 6 */
+- "#ffdab9", /* 7 */
+- "#9370db", /* 8 */
+- "#5d478b", /* 9 */
+- "#d2b48c", /* 10 */
+- "#00ff00", /* 11 */
+- "#ffff00", /* 12 */
+- "#0000ff", /* 13 */
+- "#0000ff", /* 14 */
+- "#0000ff" /* 15 */
+- };
++ static char *rgb_colors[] =
++ {
++ "#ffffff", /* 0 */
++ "#8b4513", /* 1 */
++ "#e6e6fa", /* 2 */
++ "#f0e68c", /* 3 */
++ "#ff0000", /* 4 */
++ "#deb887", /* 5 */
++ "#eecbad", /* 6 */
++ "#ffdab9", /* 7 */
++ "#9370db", /* 8 */
++ "#5d478b", /* 9 */
++ "#d2b48c", /* 10 */
++ "#00ff00", /* 11 */
++ "#ffff00", /* 12 */
++ "#0000ff", /* 13 */
++ "#0000ff", /* 14 */
++ "#0000ff" /* 15 */
++ };
+
+- static char *color_nr[] = {
+- "0",
+- "1",
+- "2",
+- "3",
+- "4",
+- "5",
+- "6",
+- "7",
+- "8",
+- "9",
+- "10",
+- "11",
+- "12",
+- "13",
+- "14",
+- "15"
+- };
++ static char *color_nr[] =
++ {
++ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"
++ };
+
+- if ( color_names )
++ if (color_names)
+ {
+- if ( color_names == 1)
++ if (color_names == 1)
+ {
+- return colors[color&0xf];
++ return colors[color &0xf];
+ }
+ else
+ {
+- return rgb_colors[color&0xf];
++ return rgb_colors[color &0xf];
+ }
+ }
+ else
+ {
+- return color_nr[color&0xf];
++ return color_nr[color &0xf];
+ }
+ }
+
+
+-const char *
+-unreal_color( int color )
++const char *unreal_color(int color)
+ {
+- if ( color_names )
++ if (color_names)
+ {
+- if ( color_names == 1)
++ if (color_names == 1)
+ {
+- return unreal_colors[color&0xf];
++ return unreal_colors[color &0xf];
+ }
+ else
+ {
+- return unreal_rgb_colors[color&0xf];
++ return unreal_rgb_colors[color &0xf];
+ }
+ }
+
+@@ -11260,296 +13046,368 @@
+ #define FMT_SECOND_1 "%2ds"
+ #define FMT_SECOND_2 "%ds"
+
+-char *
+-play_time( int seconds, int show_seconds)
++char *play_time(int seconds, int show_seconds)
+ {
+- static char time_string[24];
+- if ( time_format == CLOCK_TIME) {
+- char *fmt_hour= show_seconds==2 ? FMT_HOUR_2 : FMT_HOUR_1;
+- char *fmt_minute= show_seconds==2 ? FMT_MINUTE_2 : FMT_MINUTE_1;
+- char *fmt_second= show_seconds==2 ? FMT_SECOND_2 : FMT_SECOND_1;
+- time_string[0]= '\0';
+- if ( seconds/3600)
+- sprintf( time_string, fmt_hour, seconds/3600);
+- else if ( show_seconds < 2)
+- strcat( time_string, " ");
+- if ( (seconds%3600)/60 || seconds/3600)
+- sprintf( time_string+strlen(time_string), fmt_minute,
+- (seconds%3600)/60);
+- else if ( ! show_seconds)
+- sprintf( time_string+strlen(time_string), " 0m");
+- else if ( show_seconds < 2)
+- strcat( time_string, " ");
+- if ( show_seconds)
+- sprintf( time_string+strlen(time_string), fmt_second, seconds%60);
+- }
+- else if ( time_format == STOPWATCH_TIME) {
+- if ( show_seconds)
+- sprintf( time_string, "%02d:%02d:%02d", seconds/3600,
+- (seconds%3600)/60, seconds % 60);
++ static char time_string[24];
++ if (time_format == CLOCK_TIME)
++ {
++ char *fmt_hour = show_seconds == 2 ? FMT_HOUR_2 : FMT_HOUR_1;
++ char *fmt_minute = show_seconds == 2 ? FMT_MINUTE_2 : FMT_MINUTE_1;
++ char *fmt_second = show_seconds == 2 ? FMT_SECOND_2 : FMT_SECOND_1;
++ time_string[0] = '\0';
++ if (seconds / 3600)
++ {
++ sprintf(time_string, fmt_hour, seconds / 3600);
++ }
++ else if (show_seconds < 2)
++ {
++ strcat(time_string, " ");
++ }
++ if ((seconds % 3600) / 60 || seconds / 3600)
++ {
++ sprintf(time_string + strlen(time_string), fmt_minute, (seconds % 3600) / 60);
++ }
++ else if (!show_seconds)
++ {
++ sprintf(time_string + strlen(time_string), " 0m");
++ }
++ else if (show_seconds < 2)
++ {
++ strcat(time_string, " ");
++ }
++ if (show_seconds)
++ {
++ sprintf(time_string + strlen(time_string), fmt_second, seconds % 60);
++ }
++ }
++ else if (time_format == STOPWATCH_TIME)
++ {
++ if (show_seconds)
++ {
++ sprintf(time_string, "%02d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, seconds % 60);
++ }
++ else
++ {
++ sprintf(time_string, "%02d:%02d", seconds / 3600, (seconds % 3600) / 60);
++ }
++ }
+ else
+- sprintf( time_string, "%02d:%02d", seconds/3600,
+- (seconds%3600)/60);
+- }
+- else
+- sprintf( time_string, "%d", seconds);
++ {
++ sprintf(time_string, "%d", seconds);
++ }
+
+- return time_string;
++ return time_string;
+ }
+
+-char *
+-ping_time( int ms)
++char *ping_time(int ms)
+ {
+- static char time_string[24];
+- if ( ms < 1000)
+- sprintf( time_string, "%dms", ms);
+- else if ( ms < 1000000)
+- sprintf( time_string, "%ds", ms/1000);
+- else
+- sprintf( time_string, "%dm", ms/1000/60);
+- return time_string;
++ static char time_string[24];
++ if (ms < 1000)
++ {
++ sprintf(time_string, "%dms", ms);
++ }
++ else if (ms < 1000000)
++ {
++ sprintf(time_string, "%ds", ms / 1000);
++ }
++ else
++ {
++ sprintf(time_string, "%dm", ms / 1000 / 60);
++ }
++ return time_string;
+ }
+
+-int
+-count_bits( int n)
++int count_bits(int n)
+ {
+- int b= 0;
+- for ( ; n; n>>=1)
+- if ( n&1)
+- b++;
+- return b;
++ int b = 0;
++ for (; n; n >>= 1)
++ if (n &1)
++ {
++ b++;
++ }
++ return b;
+ }
+
+-int
+-strcmp_withnull( char *one, char *two)
++int strcmp_withnull(char *one, char *two)
+ {
+- if ( one == NULL && two == NULL)
+- return 0;
+- if ( one != NULL && two == NULL)
+- return -1;
+- if ( one == NULL)
+- return 1;
+- return strcasecmp( one, two);
++ if (one == NULL && two == NULL)
++ {
++ return 0;
++ }
++ if (one != NULL && two == NULL)
++ {
++ return -1;
++ }
++ if (one == NULL)
++ {
++ return 1;
++ }
++ return strcasecmp(one, two);
+ }
+
+ /*
+ * Sorting functions
+ */
+
+-void
+-sort_servers( struct qserver **array, int size)
++void sort_servers(struct qserver **array, int size)
+ {
+- quicksort( (void**)array, 0, size-1, (int (*)(void*,void*)) server_compare);
++ quicksort((void **)array, 0, size - 1, (int(*)(void *, void*))server_compare);
+ }
+
+-void
+-sort_players( struct qserver *server)
++void sort_players(struct qserver *server)
+ {
+- struct player **array, *player, *last_team= NULL, **next;
+- int np, i;
++ struct player **array, *player, *last_team = NULL, **next;
++ int np, i;
+
+- if ( server->num_players == 0 || server->players == NULL)
+- return;
++ if (server->num_players == 0 || server->players == NULL)
++ {
++ return ;
++ }
+
+- player= server->players;
+- for ( ; player != NULL && player->number == TRIBES_TEAM; ) {
+- last_team= player;
+- player= player->next;
+- }
+- if ( player == NULL)
+- return;
++ player = server->players;
++ for (; player != NULL && player->number == TRIBES_TEAM;)
++ {
++ last_team = player;
++ player = player->next;
++ }
+
+- array= (struct player **) malloc( sizeof(struct player *) *
+- server->num_players);
+- for ( np= 0; player != NULL && np < server->num_players; np++) {
+- array[np]= player;
+- player= player->next;
+- }
+- quicksort( (void**)array, 0, np-1, (int (*)(void*,void*)) player_compare);
++ if (player == NULL)
++ {
++ return ;
++ }
+
+- if ( last_team)
+- next= &last_team->next;
+- else
+- next= &server->players;
+-
+- for ( i= 0; i < np; i++) {
+- *next= array[i];
+- array[i]->next= NULL;
+- next= &array[i]->next;
+- }
++ array = (struct player **)malloc(sizeof(struct player*)*(server->num_players + server->num_spectators));
++ for (np = 0; player != NULL && np < server->num_players + server->num_spectators; np++)
++ {
++ array[np] = player;
++ player = player->next;
++ } quicksort((void **)array, 0, np - 1, (int(*)(void *, void*))player_compare);
++
++ if (last_team)
++ {
++ next = &last_team->next;
++ }
++ else
++ {
++ next = &server->players;
++ }
++
++ for (i = 0; i < np; i++)
++ {
++ *next = array[i];
++ array[i]->next = NULL;
++ next = &array[i]->next;
++ }
+
+- free( array);
++ free(array);
+ }
+
+-int
+-server_compare( struct qserver *one, struct qserver *two)
++int server_compare(struct qserver *one, struct qserver *two)
+ {
+- int rc;
++ int rc;
+
+- char *key= sort_keys;
+-
+- for ( ; *key; key++) {
+- switch( *key) {
+- case 'g':
+- rc= strcmp_withnull( one->game, two->game);
+- if ( rc)
+- return rc;
+- break;
+- case 'p':
+- if ( one->n_requests == 0)
+- return two->n_requests;
+- else if ( two->n_requests == 0)
+- return -1;
+- rc= one->ping_total/one->n_requests -
+- two->ping_total/two->n_requests;
+- if ( rc)
+- return rc;
+- break;
+- case 'i':
+- if ( one->ipaddr > two->ipaddr)
+- return 1;
+- else if ( one->ipaddr < two->ipaddr)
+- return -1;
+- else if ( one->port > two->port)
+- return 1;
+- else if ( one->port < two->port)
+- return -1;
+- break;
+- case 'h':
+- rc= strcmp_withnull( one->host_name, two->host_name);
+- if ( rc)
+- return rc;
+- break;
+- case 'n':
+- rc= two->num_players - one->num_players;
+- if ( rc)
+- return rc;
+- break;
++ char *key = sort_keys;
++
++ for (; *key; key++)
++ {
++ switch (*key)
++ {
++ case 'g':
++ rc = strcmp_withnull(one->game, two->game);
++ if (rc)
++ {
++ return rc;
++ }
++ break;
++ case 'p':
++ if (one->n_requests == 0)
++ {
++ return two->n_requests;
++ }
++ else if (two->n_requests == 0)
++ {
++ return -1;
++ }
++ rc = one->ping_total / one->n_requests - two->ping_total / two->n_requests;
++ if (rc)
++ {
++ return rc;
++ }
++ break;
++ case 'i':
++ if (one->ipaddr > two->ipaddr)
++ {
++ return 1;
++ }
++ else if (one->ipaddr < two->ipaddr)
++ {
++ return -1;
++ }
++ else if (one->port > two->port)
++ {
++ return 1;
++ }
++ else if (one->port < two->port)
++ {
++ return -1;
++ }
++ break;
++ case 'h':
++ rc = strcmp_withnull(one->host_name, two->host_name);
++ if (rc)
++ {
++ return rc;
++ }
++ break;
++ case 'n':
++ rc = two->num_players - one->num_players;
++ if (rc)
++ {
++ return rc;
++ }
++ break;
++ }
+ }
+- }
+
+- return 0;
++ return 0;
++}
++
++int type_option_compare(server_type *one, server_type *two)
++{
++ return strcmp_withnull(one->type_option, two->type_option);
+ }
+
+-int
+-type_option_compare( server_type *one, server_type *two )
++int type_string_compare(server_type *one, server_type *two)
+ {
+- return strcmp_withnull( one->type_option, two->type_option );
++ return strcmp_withnull(one->type_string, two->type_string);
+ }
+
+-int
+-type_string_compare( server_type *one, server_type *two )
+-{
+- return strcmp_withnull( one->type_string, two->type_string );
+-}
+-
+-int
+-player_compare( struct player *one, struct player *two)
+-{
+- int rc;
+-
+- char *key= sort_keys;
+-
+- for ( ; *key; key++) {
+- switch( *key) {
+- case 'P':
+- rc= one->ping - two->ping;
+- if ( rc)
+- return rc;
+- break;
+- case 'F':
+- rc= two->frags - one->frags;
+- if ( rc)
+- return rc;
+- break;
+- case 'S':
+- rc= two->score - one->score;
+- if ( rc)
+- return rc;
+- break;
+- case 'T':
+- rc= one->team - two->team;
+- if ( rc)
+- return rc;
+- rc = strcmp_withnull( one->team_name, two->team_name );
+- if ( rc )
+- return rc;
+- break;
+- case 'N':
+- rc= strcmp_withnull( one->name, two->name );
+- if ( rc )
+- return rc;
+- return one->number - two->number;
+- break;
++int player_compare(struct player *one, struct player *two)
++{
++ int rc;
++
++ char *key = sort_keys;
++
++ for (; *key; key++)
++ {
++ switch (*key)
++ {
++ case 'P':
++ rc = one->ping - two->ping;
++ if (rc)
++ {
++ return rc;
++ }
++ break;
++ case 'F':
++ rc = two->frags - one->frags;
++ if (rc)
++ {
++ return rc;
++ }
++ break;
++ case 'S':
++ rc = two->score - one->score;
++ if (rc)
++ {
++ return rc;
++ }
++ break;
++ case 'T':
++ rc = one->team - two->team;
++ if (rc)
++ {
++ return rc;
++ }
++ rc = strcmp_withnull(one->team_name, two->team_name);
++ if (rc)
++ {
++ return rc;
++ }
++ break;
++ case 'N':
++ rc = strcmp_withnull(one->name, two->name);
++ if (rc)
++ {
++ return rc;
++ }
++ return one->number - two->number;
++ break;
++ }
+ }
+- }
+- return 0;
++ return 0;
+ }
+
+-void
+-quicksort( void **array, int i, int j, int (*compare)(void*,void*))
++void quicksort(void **array, int i, int j, int(*compare)(void *, void*))
+ {
+- int q= 0;
++ int q = 0;
+
+- if ( i < j) {
+- q = qpartition(array,i,j, compare);
+- quicksort(array,i,q, compare);
+- quicksort(array,q+1,j, compare);
+- }
++ if (i < j)
++ {
++ q = qpartition(array, i, j, compare);
++ quicksort(array, i, q, compare);
++ quicksort(array, q + 1, j, compare);
++ }
+ }
+
+-int
+-qpartition( void **array, int a, int b, int (*compare)(void*,void*))
++int qpartition(void **array, int a, int b, int(*compare)(void *, void*))
+ {
+- /* this is our comparison point. when we are done
+- splitting this array into 2 parts, we want all the
+- elements on the left side to be less then or equal
+- to this, all the elements on the right side need to
+- be greater then or equal to this
+- */
+- void *z;
+-
+- /* indicies into the array to sort. Used to calculate a partition
+- point
+- */
+- int i = a-1;
+- int j = b+1;
+-
+- /* temp pointer used to swap two array elements */
+- void * tmp = NULL;
+-
+- z = array[a];
+-
+- while (1) {
+-
+- /* move the right indice over until the value of that array
+- elem is less than or equal to z. Stop if we hit the left
+- side of the array (ie, j == a);
+- */
+- do {
+- j--;
+- } while( j > a && compare(array[j],z) > 0);
+-
+- /* move the left indice over until the value of that
+- array elem is greater than or equal to z, or until
+- we hit the right side of the array (ie i == j)
+- */
+- do {
+- i++;
+- } while( i <= j && compare(array[i],z) < 0);
+-
+- /* if i is less then j, we need to switch those two array
+- elements, if not then we are done partitioning this array
+- section
+- */
+- if(i < j) {
+- tmp = array[i];
+- array[i] = array[j];
+- array[j] = tmp;
++ /* this is our comparison point. when we are done
++ splitting this array into 2 parts, we want all the
++ elements on the left side to be less then or equal
++ to this, all the elements on the right side need to
++ be greater then or equal to this
++ */
++ void *z;
++
++ /* indicies into the array to sort. Used to calculate a partition
++ point
++ */
++ int i = a - 1;
++ int j = b + 1;
++
++ /* temp pointer used to swap two array elements */
++ void *tmp = NULL;
++
++ z = array[a];
++
++ while (1)
++ {
++
++ /* move the right indice over until the value of that array
++ elem is less than or equal to z. Stop if we hit the left
++ side of the array (ie, j == a);
++ */
++ do
++ {
++ j--;
++ }
++ while (j > a && compare(array[j], z) > 0);
++
++ /* move the left indice over until the value of that
++ array elem is greater than or equal to z, or until
++ we hit the right side of the array (ie i == j)
++ */
++ do
++ {
++ i++;
++ }
++ while (i <= j && compare(array[i], z) < 0);
++
++ /* if i is less then j, we need to switch those two array
++ elements, if not then we are done partitioning this array
++ section
++ */
++ if (i < j)
++ {
++ tmp = array[i];
++ array[i] = array[j];
++ array[j] = tmp;
++ }
++ else
++ {
++ return j;
++ }
+ }
+- else
+- return j;
+- }
+ }
+
+-// vim: sw=8 ts=8 noet
+diff -urP qstat-2.11/qstat.cfg qstat2/qstat.cfg
+--- qstat-2.11/qstat.cfg 2006-09-14 15:51:01.000000000 -0500
++++ qstat2/qstat.cfg 2009-12-11 18:40:26.531870000 -0500
+@@ -90,6 +90,7 @@
+ status2 packet = \377\377\377\377\002getstatus xxx
+ end
+
++# Config for Call of Duty
+ gametype CODS new extend Q3S
+ name = Call of Duty
+ default port = 28960
+@@ -106,6 +107,48 @@
+ master query = full empty
+ end
+
++# Config for Call of Duty 2
++gametype COD2S new extend Q3S
++ name = Call of Duty 2
++ default port = 28960
++ template var = CALLOFDUTY2
++ game rule = gamename
++end
++
++gametype COD2M new extend Q3M
++ name = Call of Duty 2 Master
++ default port = 20710
++ template var = COD2MASTER
++ master protocol = 118
++ master for gametype = COD2S
++ master query = full empty
++end
++
++# Config for Call of Duty 4
++gametype COD4S new extend Q3S
++ name = Call of Duty 4
++ default port = 28960
++ template var = CALLOFDUTY4
++ game rule = gamename
++end
++
++gametype COD4M new extend Q3M
++ name = Call of Duty 4 Master
++ default port = 20810
++ template var = COD4MASTER
++ master protocol = 6
++ master for gametype = COD4S
++ master query = full empty
++end
++
++# Config for Call of Duty Word at War
++gametype WAWS new extend Q3S
++ name = Call of Duty World at War
++ default port = 28960
++ template var = CALLOFDUTYWAW
++ game rule = gametype
++end
++
+ # enemy territory
+ gametype WOETS new extend Q3S
+ name = Enemy Territory
+@@ -174,6 +217,7 @@
+ template var = WARSOW
+ game rule = gamename
+ status packet = \377\377\377\377getinfo
++ status2 packet = \377\377\377\377getstatus
+ end
+ gametype WARSOWM new extend Q3M
+ name = Warsow Master
+@@ -215,3 +259,43 @@
+ master for gametype = PREYS
+ end
+
++gametype UT3S new extend GS4
++ name = UT3
++ default port = 6500
++ template var = UT3
++end
++
++
++# ioq3 fork
++gametype OPENARENAS new extend Q3S
++ name = OpenArena
++ template var = OPENARENA
++ game rule = gamename
++end
++
++gametype OPENARENAM new extend Q3M
++ name = OpenArena Master
++ template var = OPENARENAMASTER
++ default port = 27950
++ master packet = \377\377\377\377getservers %s %s
++ master protocol = 70
++ master query = empty full
++ master for gametype = OPENARENAS
++end
++
++# ioq3 fork
++gametype IOURTS new extend Q3S
++ name = ioUrbanTerror
++ template var = IOURT
++ game rule = gamename
++end
++
++gametype IOURTM new extend Q3M
++ name = ioUrbanTerror Master
++ template var = IOURTMASTER
++ default port = 27950
++ master packet = \377\377\377\377getservers %s %s
++ master protocol = 68
++ master query = empty full
++ master for gametype = IOURTS
++end
+diff -urP qstat-2.11/qstatdoc.html qstat2/qstatdoc.html
+--- qstat-2.11/qstatdoc.html 2005-11-17 00:06:04.000000000 -0500
++++ qstat2/qstatdoc.html 2009-07-02 06:13:04.183025000 -0400
+@@ -120,6 +120,7 @@
+ <tr><td>-tbs<td>tbs<td>28001<td>Starsiege: Tribes
+ <tr><td>-t2s<td>t2s<td>28000<td>Tribes 2
+ <tr><td>-qwm<td>qwm<td>27000<td>QuakeWorld master
++<tr><td>-hwm<td>hwm<td>26900<td>HexenWorld master
+ <tr><td>-q2m<td>q2m<td>27900<td>Quake II master
+ <tr><td>-hlm<td>hlm<td>27010<td>Half-Life master
+ <tr><td>-stm<td>stm<td>27010<td>Half-Life master (Steam)
+@@ -147,6 +148,7 @@
+ <tr><td>-efs<td>efs<td>29070<td>Jedi Knight: Jedi Academy
+ <tr><td>-efm<td>efm<td>29060<td>Jedi Knight: Jedi Academy master
+ <tr><td>-grs<td>grs<td>2346<td>Ghost Recon
++<tr><td>-etqws<td>etqws<td>27733<td>QuakeWars server
+ </table>
+ <p>
+ The command-line options can be specified multiple times, one for
+@@ -485,7 +487,7 @@
+ You can set these filters with QStat by appending query arguments
+ to the server type. The general syntax is:
+ <blockquote>
+-<b>hlm,</b><i>query-arg</i><b>=</b><i>value</i><b>,</b> ...
++<b>dm3m,</b><i>query-arg</i><b>=</b><i>value</i><b>,</b> ...
+ </blockquote>
+ <table border=1>
+ <tr><th>Query Argument</th><th>Values</th><th>Description</th></tr>
+@@ -507,7 +509,7 @@
+ to the server type. The general syntax is:
+
+ <blockquote>
+- <b>hlm,</b><i>query-arg</i><b>=</b><i>value</i><b>,</b> ...
++ <b>ut2004m,</b><i>query-arg</i><b>=</b><i>value</i><b>,</b> ...
+ </blockquote>
+ <table border=1>
+ <tr>
+@@ -543,7 +545,13 @@
+ </tr>
+ </table>
+
+-See the Tribes 2 master server above for example usage.<br>
++You may need to specify at least one filter option like status or gametype for
++the master to actually return any server.<br>
++
++Example:
++<blockquote>
++<b>qstat -ut2004m,cdkey=/usr/local/games/ut2004/System/cdkey,status=nostandard ut2004master1.epicgames.com:28902<br>
++</blockquote>
+
+ <H4>Option Usage</H4>
+
+diff -urP qstat-2.11/qstat.h qstat2/qstat.h
+--- qstat-2.11/qstat.h 2006-08-04 15:42:09.000000000 -0500
++++ qstat2/qstat.h 2012-10-10 14:37:04.603408000 -0400
+@@ -11,7 +11,11 @@
+ #define __H_QSTAT
+
+ #ifdef HAVE_CONFIG_H
+-#include "gnuconfig.h"
++ #include "gnuconfig.h"
++#else
++ #ifndef VERSION
++ #define VERSION "2.13"
++ #endif
+ #endif
+
+ #ifdef __EMX__
+@@ -62,17 +66,45 @@
+ #define GCC_FORMAT_PRINTF(a, b)
+ #endif
+
++typedef enum {
++ INPROGRESS = 0,
++ DONE_AUTO = 1,
++ DONE_FORCE = 2,
++
++ SYS_ERROR = -1,
++ MEM_ERROR = -2,
++ PKT_ERROR = -3,
++ ORD_ERROR = -4,
++ REQ_ERROR = -5
++} query_status_t;
++
+ #include "qserver.h"
+
++typedef void (*DisplayFunc)( struct qserver *);
++typedef query_status_t (*QueryFunc)( struct qserver *);
++typedef query_status_t (*PacketFunc)( struct qserver *, char *rawpkt, int pktlen);
++
+ // Packet modules
+ #include "ut2004.h"
+ #include "doom3.h"
+ #include "a2s.h"
++#include "fl.h"
+ #include "gps.h"
+ #include "gs2.h"
+ #include "gs3.h"
++#include "haze.h"
+ #include "ts2.h"
++#include "ts3.h"
+ #include "tm.h"
++#include "wic.h"
++#include "ottd.h"
++#include "tee.h"
++#include "cube2.h"
++#include "bfbc2.h"
++#include "ventrilo.h"
++#include "mumble.h"
++#include "terraria.h"
++#include "crysis.h"
+
+ /*
+ * Various magic numbers.
+@@ -87,6 +119,7 @@
+ #define QW_DEFAULT_PORT 27500
+ #define QW_MASTER_DEFAULT_PORT 27000
+ #define HW_DEFAULT_PORT 26950
++#define HW_MASTER_DEFAULT_PORT 26900
+ #define UNREAL_DEFAULT_PORT 7777
+ #define UNREAL_MASTER_DEFAULT_PORT 28900
+ #define HALFLIFE_DEFAULT_PORT 27015
+@@ -119,7 +152,16 @@
+ #define HL2_DEFAULT_PORT 27015
+ #define HL2_MASTER_DEFAULT_PORT 27011
+ #define TS2_DEFAULT_PORT 51234
++#define TS3_DEFAULT_PORT 10011
++#define BFBC2_DEFAULT_PORT 48888
+ #define TM_DEFAULT_PORT 5000
++#define WIC_DEFAULT_PORT 5000 // Default is actually disabled
++#define FL_DEFAULT_PORT 5478
++#define VENTRILO_DEFAULT_PORT 3784
++#define CUBE2_DEFAULT_PORT 28785
++#define MUMBLE_DEFAULT_PORT 64738
++#define TERRARIA_DEFAULT_PORT 7777
++#define CRYSIS_DEFAULT_PORT 64087
+
+
+ #define Q_UNKNOWN_TYPE 0
+@@ -183,8 +225,24 @@
+ #define GAMESPY4_PROTOCOL_SERVER 55
+ #define PREY_SERVER 56
+ #define TM_PROTOCOL_SERVER 57
++#define ETQW_SERVER 58
++#define HAZE_SERVER 59
++#define HW_MASTER (60 | MASTER_SERVER)
++#define WIC_PROTOCOL_SERVER 61
++#define OTTD_SERVER 62
++#define OTTD_MASTER (63 | MASTER_SERVER)
++#define FL_SERVER 64
++#define WOLF_SERVER 65
++#define TEE_SERVER 66
++#define TS3_PROTOCOL_SERVER 67
++#define BFBC2_PROTOCOL_SERVER 68
++#define VENTRILO_PROTOCOL_SERVER 69
++#define CUBE2_SERVER 70
++#define MUMBLE_PROTOCOL_SERVER 71
++#define TERRARIA_PROTOCOL_SERVER 72
++#define CRYSIS_PROTOCOL_SERVER 73
+
+-#define LAST_BUILTIN_SERVER 57
++#define LAST_BUILTIN_SERVER 73
+
+ #define TF_SINGLE_QUERY (1<<1)
+ #define TF_OUTFILE (1<<2)
+@@ -217,10 +275,6 @@
+
+ struct q_packet;
+
+-typedef void (*DisplayFunc)( struct qserver *);
+-typedef void (*QueryFunc)( struct qserver *);
+-typedef void (*PacketFunc)( struct qserver *, char *rawpkt, int pktlen);
+-
+ /*
+ * Output and formatting functions
+ */
+@@ -247,9 +301,14 @@
+ void display_armyops_player_info( struct qserver *server);
+ void display_gs2_player_info( struct qserver *server);
+ void display_doom3_player_info( struct qserver *server);
+-void display_hl2_player_info( struct qserver *server);
+ void display_ts2_player_info( struct qserver *server);
++void display_ts3_player_info( struct qserver *server);
++void display_bfbc2_player_info( struct qserver *server);
+ void display_tm_player_info( struct qserver *server);
++void display_wic_player_info( struct qserver *server);
++void display_fl_player_info( struct qserver *server);
++void display_tee_player_info( struct qserver *server);
++void display_ventrilo_player_info( struct qserver *server);
+
+ void raw_display_server( struct qserver *server);
+ void raw_display_server_rules( struct qserver *server);
+@@ -271,9 +330,14 @@
+ void raw_display_armyops_player_info( struct qserver *server);
+ void raw_display_gs2_player_info( struct qserver *server);
+ void raw_display_doom3_player_info( struct qserver *server);
+-void raw_display_hl2_player_info( struct qserver *server);
+ void raw_display_ts2_player_info( struct qserver *server);
++void raw_display_ts3_player_info( struct qserver *server);
++void raw_display_bfbc2_player_info( struct qserver *server);
+ void raw_display_tm_player_info( struct qserver *server);
++void raw_display_wic_player_info( struct qserver *server);
++void raw_display_fl_player_info( struct qserver *server);
++void raw_display_tee_player_info( struct qserver *server);
++void raw_display_ventrilo_player_info( struct qserver *server);
+
+ void xml_display_server( struct qserver *server);
+ void xml_header();
+@@ -295,64 +359,55 @@
+ void xml_display_ghostrecon_player_info( struct qserver *server);
+ void xml_display_eye_player_info( struct qserver *server);
+ void xml_display_armyops_player_info( struct qserver *server);
+-void xml_display_gs2_player_info( struct qserver *server);
++void xml_display_player_info( struct qserver *server);
+ void xml_display_doom3_player_info( struct qserver *server);
+-void xml_display_hl2_player_info( struct qserver *server);
+ void xml_display_ts2_player_info( struct qserver *server);
++void xml_display_ts3_player_info( struct qserver *server);
++void xml_display_bfbc2_player_info( struct qserver *server);
+ void xml_display_tm_player_info( struct qserver *server);
++void xml_display_wic_player_info( struct qserver *server);
++void xml_display_fl_player_info( struct qserver *server);
++void xml_display_tee_player_info( struct qserver *server);
++void xml_display_ventrilo_player_info( struct qserver *server);
+ char *xml_escape( char*);
+ char *str_replace( char *, char *, char *);
+
+-void send_server_request_packet( struct qserver *server);
+-void send_qserver_request_packet( struct qserver *server);
+-void send_qwserver_request_packet( struct qserver *server);
+-void send_ut2003_request_packet( struct qserver *server);
+-void send_tribes_request_packet( struct qserver *server);
+-void send_qwmaster_request_packet( struct qserver *server);
+-void send_bfris_request_packet( struct qserver *server);
+-void send_player_request_packet( struct qserver *server);
+-void send_rule_request_packet( struct qserver *server);
+-void send_ravenshield_request_packet( struct qserver *server);
+-void send_savage_request_packet( struct qserver *server);
+-void send_farcry_request_packet( struct qserver *server);
+-void send_gamespy_master_request( struct qserver *server);
+-void send_tribes2_request_packet( struct qserver *server);
+-void send_tribes2master_request_packet( struct qserver *server);
+-void send_ghostrecon_request_packet( struct qserver *server);
+-void send_eye_request_packet( struct qserver *server);
+-void send_gs2_request_packet( struct qserver *server);
+-void send_doom3_request_packet( struct qserver *server);
+-void send_hl2_request_packet( struct qserver *server);
+-void send_ts2_request_packet( struct qserver *server);
+-void send_tm_request_packet( struct qserver *server);
+-
+-void deal_with_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_q_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_qw_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_q1qw_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_q2_packet( struct qserver *server, char *pkt, int pktlen,
+- int check_duplicate_rules);
+-void deal_with_doom3master_packet( struct qserver *server, char *rawpkt, int pktlen);
+-void deal_with_qwmaster_packet( struct qserver *server, char *pkt, int pktlen);
+-int deal_with_halflife_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_ut2003_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_tribes_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_tribesmaster_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_bfris_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_gamespy_master_response( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_ravenshield_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_savage_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_farcry_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_tribes2_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_tribes2master_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_descent3_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_descent3master_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_ghostrecon_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_eye_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_doom3_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_hl2_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_ts2_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_tm_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t send_qserver_request_packet( struct qserver *server);
++query_status_t send_qwserver_request_packet( struct qserver *server);
++query_status_t send_ut2003_request_packet( struct qserver *server);
++query_status_t send_tribes_request_packet( struct qserver *server);
++query_status_t send_qwmaster_request_packet( struct qserver *server);
++query_status_t send_bfris_request_packet( struct qserver *server);
++query_status_t send_player_request_packet( struct qserver *server);
++query_status_t send_rule_request_packet( struct qserver *server);
++query_status_t send_savage_request_packet( struct qserver *server);
++query_status_t send_farcry_request_packet( struct qserver *server);
++query_status_t send_gamespy_master_request( struct qserver *server);
++query_status_t send_tribes2_request_packet( struct qserver *server);
++query_status_t send_tribes2master_request_packet( struct qserver *server);
++query_status_t send_hl2_request_packet( struct qserver *server);
++
++query_status_t deal_with_q_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_qw_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_q1qw_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_q2_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t deal_with_qwmaster_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_halflife_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_ut2003_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_tribes_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_tribesmaster_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_bfris_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_gamespy_master_response( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_ravenshield_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_savage_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_farcry_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_tribes2_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_tribes2master_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_descent3_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_descent3master_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_ghostrecon_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_eye_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_hl2_packet( struct qserver *server, char *pkt, int pktlen);
+
+ struct _server_type {
+ int id;
+@@ -436,9 +491,9 @@
+ /* QUAKE WORLD */
+ struct {
+ char prefix[4];
+- char command[7];
++ char command[10];
+ } qw_serverstatus =
+-{ { '\377', '\377', '\377', '\377' }, { 's', 't', 'a', 't', 'u', 's', '\n' } };
++{ { '\377', '\377', '\377', '\377' }, { 's', 't', 'a', 't', 'u', 's', ' ', '2', '3', '\n' } };
+
+ /* QUAKE3 */
+ struct {
+@@ -512,6 +567,10 @@
+ #define QW_GET_SERVERS 'c'
+ char qw_masterquery[] = { QW_GET_SERVERS, '\n', '\0' };
+
++/* HEXENWORLD MASTER */
++#define HW_GET_SERVERS 'c'
++char hw_masterquery[] = { '\377', HW_GET_SERVERS, '\0' };
++
+ /* QUAKE 2 MASTER */
+ char q2_masterquery[] = { 'q', 'u', 'e', 'r', 'y', '\n', '\0' };
+
+@@ -656,7 +715,9 @@
+ // 10: Team information (00 to disable)
+ // 11: Request new format
+ unsigned char gs3_player_query[] = {
+- 0xfe,0xfd,0x00,0x10,0x20,0x30,0x40,0xff,0xff,0xff,0x01
++ 0xfe,0xfd,0x00,
++ 0x10,0x20,0x30,0x40,
++ 0xff,0xff,0xff,0x01
+ };
+
+ // Format:
+@@ -687,7 +748,40 @@
+ // 1 - 3: query head
+ // 4 - 7: queryid
+ unsigned char gs3_challenge[] = {
+- 0xfe,0xfd,0x09,0x10,0x20,0x30,0x40
++ 0xfe,0xfd,0x09,
++ 0x10,0x20,0x30,0x40
++};
++
++// Format:
++// 1 - 8: Query Request
++// 9 - 12: Query Header
++// 13: Query ID
++
++// Query ID is made up of the following
++// 0x01: Basic Info
++// 0x02: Game Rules
++// 0x03: Player Information
++// 0x04: Team Information
++unsigned char haze_status_query[] = {
++ 'f', 'r', 'd', 'q', 'u', 'e', 'r', 'y',
++ 0x10,0x20,0x30,0x40,
++ 0x0A
++};
++
++// Format:
++// 1 - 8: Query Request
++// 9 - 12: Query Header
++// 13: Query ID
++
++// Query ID is made up of the following
++// 0x01: Basic Info
++// 0x02: Game Rules
++// 0x03: Player Information
++// 0x04: Team Information
++unsigned char haze_player_query[] = {
++ 'f', 'r', 'd', 'q', 'u', 'e', 'r', 'y',
++ 0x10,0x20,0x30,0x40,
++ 0x03
+ };
+
+
+@@ -746,7 +840,39 @@
+
+ char ravenshield_serverquery[] = "REPORT";
+
+-unsigned char ts2_status_query[] = "si";
++char ottd_master_query[] = {
++ 0x04, 0x00, // packet length
++ 0x06, // packet type
++ 0x01 // packet version
++};
++
++char ottd_serverinfo[] = {
++ 0x03, 0x00, // packet length
++ 0x00, // packet type
++};
++
++char ottd_serverdetails[] = {
++ 0x03, 0x00, // packet length
++ 0x02, // packet type
++};
++
++/* Teeworlds */
++
++char tee_serverstatus[14] = { '\x20', '\0', '\0', '\0', '\0', '\0', '\xFF', '\xFF', '\xFF', '\xFF', 'g', 'i', 'e', 'f' };
++
++/* Cube2 */
++
++char cube2_serverstatus[3] = {'\x80', '\x10', '\x27'};
++
++/* Mumble */
++char mumble_serverstatus[12] = {'\x00', '\x00', '\x00', '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08' };
++
++/* Terraria */
++char terraria_serverstatus[] = "GET /v2/server/status HTTP/1.1\x0d\x0a\x0d\x0a";
++
++
++/* SERVER BUILTIN TYPES */
++
+
+ server_type builtin_types[] = {
+ {
+@@ -1010,11 +1136,11 @@
+ 0, /* master_len */
+ NULL, /* master_protocol */
+ NULL, /* master_query */
+- display_hl2_player_info, /* display_player_func */
++ display_halflife_player_info, /* display_player_func */
+ display_server_rules, /* display_rule_func */
+- raw_display_hl2_player_info, /* display_raw_player_func */
++ raw_display_halflife_player_info, /* display_raw_player_func */
+ raw_display_server_rules, /* display_raw_rule_func */
+- xml_display_hl2_player_info, /* display_xml_player_func */
++ xml_display_halflife_player_info, /* display_xml_player_func */
+ xml_display_server_rules, /* display_xml_rule_func */
+ send_hl2_request_packet, /* status_query_func */
+ NULL, /* rule_query_func */
+@@ -1223,7 +1349,6 @@
+ send_qwserver_request_packet,/* status_query_func */
+ NULL, /* rule_query_func */
+ NULL, /* player_query_func */
+- (void (*)( struct qserver *, char *, int))
+ deal_with_halflife_packet, /* packet_func */
+ },
+ {
+@@ -1663,7 +1788,7 @@
+ raw_display_server_rules, /* display_raw_rule_func */
+ xml_display_ghostrecon_player_info, /* display_xml_player_func */
+ xml_display_server_rules, /* display_xml_rule_func */
+- send_ghostrecon_request_packet, /* status_query_func */
++ send_qserver_request_packet, /* status_query_func */
+ NULL, /* rule_query_func */
+ NULL, /* player_query_func */
+ deal_with_ghostrecon_packet, /* packet_func */
+@@ -1697,7 +1822,7 @@
+ raw_display_server_rules, /* display_raw_rule_func */
+ xml_display_eye_player_info, /* display_xml_player_func */
+ xml_display_server_rules, /* display_xml_rule_func */
+- send_eye_request_packet, /* status_query_func */
++ send_qserver_request_packet, /* status_query_func */
+ NULL, /* rule_query_func */
+ NULL, /* player_query_func */
+ deal_with_eye_packet, /* packet_func */
+@@ -1729,7 +1854,7 @@
+ display_server_rules, /* display_rule_func */
+ raw_display_gs2_player_info, /* display_raw_player_func */
+ raw_display_server_rules, /* display_raw_rule_func */
+- xml_display_gs2_player_info, /* display_xml_player_func */
++ xml_display_player_info, /* display_xml_player_func */
+ xml_display_server_rules, /* display_xml_rule_func */
+ send_gs2_request_packet, /* status_query_func */
+ NULL, /* rule_query_func */
+@@ -1799,7 +1924,7 @@
+ raw_display_server_rules, /* display_raw_rule_func */
+ xml_display_ravenshield_player_info, /* display_xml_player_func */
+ xml_display_server_rules, /* display_xml_rule_func */
+- send_ravenshield_request_packet, /* status_query_func */
++ send_qserver_request_packet, /* status_query_func */
+ NULL, /* rule_query_func */
+ NULL, /* player_query_func */
+ deal_with_ravenshield_packet, /* packet_func */
+@@ -1910,6 +2035,40 @@
+ deal_with_qwmaster_packet, /* packet_func */
+ },
+ {
++ /* HEXEN2WORLD MASTER */
++ HW_MASTER, /* id */
++ "HWM", /* type_prefix */
++ "hwm", /* type_string */
++ "-hwm", /* type_option */ /* ## also "-qw" */
++ "HexenWorld Master", /* game_name */
++ HW_SERVER, /* master */
++ HW_MASTER_DEFAULT_PORT, /* default_port */
++ 0, /* port_offset */
++ TF_SINGLE_QUERY|TF_OUTFILE, /* flags */
++ "", /* game_rule */
++ "HWMASTER", /* template_var */
++ NULL, /* status_packet */
++ 0, /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ (char*) &hw_masterquery, /* master_packet */
++ sizeof( hw_masterquery), /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_qwmaster, /* display_player_func */
++ NULL, /* display_rule_func */
++ NULL, /* display_raw_player_func */
++ NULL, /* display_raw_rule_func */
++ NULL, /* display_xml_player_func */
++ NULL, /* display_xml_rule_func */
++ send_qwmaster_request_packet,/* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_qwmaster_packet, /* packet_func */
++},
++{
+ /* QUAKE 2 MASTER */
+ Q2_MASTER, /* id */
+ "Q2M", /* type_prefix */
+@@ -2375,11 +2534,11 @@
+ 0, /* master_len */
+ NULL, /* master_protocol */
+ NULL, /* master_query */
+- display_hl2_player_info, /* display_player_func */
++ display_halflife_player_info, /* display_player_func */
+ display_server_rules, /* display_rule_func */
+- raw_display_hl2_player_info, /* display_raw_player_func */
++ raw_display_halflife_player_info, /* display_raw_player_func */
+ raw_display_server_rules, /* display_raw_rule_func */
+- xml_display_hl2_player_info, /* display_xml_player_func */
++ xml_display_halflife_player_info, /* display_xml_player_func */
+ xml_display_server_rules, /* display_xml_rule_func */
+ send_a2s_request_packet, /* status_query_func */
+ send_a2s_rule_request_packet, /* rule_query_func */
+@@ -2447,7 +2606,7 @@
+ display_server_rules, /* display_rule_func */
+ raw_display_gs2_player_info, /* display_raw_player_func */
+ raw_display_server_rules, /* display_raw_rule_func */
+- xml_display_gs2_player_info, /* display_xml_player_func */
++ xml_display_player_info, /* display_xml_player_func */
+ xml_display_server_rules, /* display_xml_rule_func */
+ send_gs3_request_packet, /* status_query_func */
+ NULL, /* rule_query_func */
+@@ -2493,10 +2652,10 @@
+ QUAKE4_SERVER, /* id */
+ "Q4S", /* type_prefix */
+ "q4s", /* type_string */
+- "-q4s", /* type_option */
++ "-q4s", /* type_option */
+ "Quake 4", /* game_name */
+ 0, /* master */
+- QUAKE4_DEFAULT_PORT, /* default_port */
++ QUAKE4_DEFAULT_PORT, /* default_port */
+ 0, /* port_offset */
+ TF_QUAKE3_NAMES, /* flags */
+ "fs_game", /* game_rule */
+@@ -2520,7 +2679,7 @@
+ send_qwserver_request_packet, /* status_query_func */
+ NULL, /* rule_query_func */
+ NULL, /* player_query_func */
+- deal_with_quake4_packet, /* packet_func */
++ deal_with_quake4_packet, /* packet_func */
+ },
+ {
+ /* QUAKE 4 MASTER */
+@@ -2583,7 +2742,7 @@
+ display_server_rules, /* display_rule_func */
+ raw_display_gs2_player_info, /* display_raw_player_func */
+ raw_display_server_rules, /* display_raw_rule_func */
+- xml_display_gs2_player_info, /* display_xml_player_func */
++ xml_display_player_info, /* display_xml_player_func */
+ xml_display_server_rules, /* display_xml_rule_func */
+ send_gs3_request_packet, /* status_query_func */
+ NULL, /* rule_query_func */
+@@ -2659,7 +2818,517 @@
+ deal_with_tm_packet, /* packet_func */
+ },
+ {
+- Q_UNKNOWN_TYPE, /* id */
++ /* Enemy Territory : QuakeWars */
++ ETQW_SERVER, /* id */
++ "ETQWS", /* type_prefix */
++ "etqws", /* type_string */
++ "-etqws", /* type_option */
++ "QuakeWars", /* game_name */
++ 0, /* master */
++ ETQW_DEFAULT_PORT, /* default_port */
++ 0, /* port_offset */
++ TF_QUAKE3_NAMES, /* flags */
++ "fs_game", /* game_rule */
++ "QUAKE4", /* template_var */
++ (char*) &doom3_serverinfo, /* status_packet */
++ sizeof( doom3_serverinfo), /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_doom3_player_info, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ raw_display_doom3_player_info, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_doom3_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_qwserver_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_etqw_packet, /* packet_func */
++},
++{
++ /* HAZE PROTOCOL */
++ HAZE_SERVER, /* id */
++ "HAZES", /* type_prefix */
++ "hazes", /* type_string */
++ "-hazes", /* type_option */
++ "Haze Protocol", /* game_name */
++ 0, /* master */
++ 0, /* default_port */
++ 0, /* port_offset */
++ TF_SINGLE_QUERY, /* flags */
++ "gametype", /* game_rule */
++ "HAZE", /* template_var */
++ (char*) &haze_status_query, /* status_packet */
++ sizeof( haze_status_query), /* status_len */
++ (char*) &haze_player_query, /* player_packet */
++ sizeof( haze_player_query), /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_gs2_player_info, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ raw_display_gs2_player_info, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_haze_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_haze_packet, /* packet_func */
++},
++{
++ /* World in Confict PROTOCOL */
++ WIC_PROTOCOL_SERVER, /* id */
++ "WICS", /* type_prefix */
++ "wics", /* type_string */
++ "-wics", /* type_option */
++ "World in Conflict", /* game_name */
++ 0, /* master */
++ 0, /* default_port */
++ 0, /* port_offset */
++ TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG, /* flags */
++ "N/A", /* game_rule */
++ "WICPROTOCOL", /* template_var */
++ NULL, /* status_packet */
++ 0, /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_wic_player_info, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ raw_display_wic_player_info, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_wic_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_wic_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_wic_packet, /* packet_func */
++},
++{
++ /* openTTD */
++ OTTD_SERVER, /* id */
++ "OTTDS", /* type_prefix */
++ "ottds", /* type_string */
++ "-ottds", /* type_option */
++ "OpenTTD", /* game_name */
++ 0, /* master */
++ 3979, /* default_port */
++ 0, /* port_offset */
++ 0, /* flags */
++ "", /* game_rule */
++ "OPENTTD", /* template_var */
++ (char*) &ottd_serverinfo, /* status_packet */
++ sizeof( ottd_serverinfo), /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ (char*) &ottd_serverdetails,/* rule_packet */
++ sizeof( ottd_serverdetails), /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_q2_player_info, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ raw_display_q2_player_info, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_player_info,/* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_ottd_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_ottd_packet, /* packet_func */
++},
++{
++ /* openTTD Master */
++ OTTD_MASTER, /* id */
++ "OTTDM", /* type_prefix */
++ "ottdm", /* type_string */
++ "-ottdm", /* type_option */
++ "openTTD Master", /* game_name */
++ OTTD_SERVER, /* master */
++ 3978, /* default_port */
++ 0, /* port_offset */
++ TF_OUTFILE | TF_QUERY_ARG, /* flags */
++ "", /* game_rule */
++ "OTTDMASTER", /* template_var */
++ NULL, /* status_packet */
++ 0, /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ ottd_master_query, /* master_packet */
++ sizeof(ottd_master_query),/* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_qwmaster, /* display_player_func */
++ NULL, /* display_rule_func */
++ NULL, /* display_raw_player_func */
++ NULL, /* display_raw_rule_func */
++ NULL, /* display_xml_player_func */
++ NULL, /* display_xml_rule_func */
++ send_ottdmaster_request_packet,/* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_ottdmaster_packet, /* packet_func */
++},
++{
++ /* Frontlines-Fuel of War */
++ FL_SERVER, /* id */
++ "FLS", /* type_prefix */
++ "fls", /* type_string */
++ "-fls", /* type_option */
++ "Frontlines-Fuel of War", /* game_name */
++ 0, /* master */
++ FL_DEFAULT_PORT, /* default_port */
++ 0, /* port_offset */
++ TF_QUAKE3_NAMES, /* flags */
++ "gamedir", /* game_rule */
++ "FLS", /* template_var */
++ NULL, /* status_packet */
++ 0, /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_fl_player_info, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ raw_display_fl_player_info, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_fl_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_fl_request_packet, /* status_query_func */
++ send_fl_rule_request_packet, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_fl_packet, /* packet_func */
++},
++{
++ /* Wolfenstein */
++ WOLF_SERVER, /* id */
++ "WOLF", /* type_prefix */
++ "wolfs", /* type_string */
++ "-wolfs", /* type_option */
++ "Wolfenstein", /* game_name */
++ 0, /* master */
++ WOLF_DEFAULT_PORT, /* default_port */
++ 0, /* port_offset */
++ TF_QUAKE3_NAMES, /* flags */
++ "fs_game", /* game_rule */
++ "QUAKE4", /* template_var */
++ (char*) &doom3_serverinfo, /* status_packet */
++ sizeof( doom3_serverinfo), /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_doom3_player_info, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ raw_display_doom3_player_info, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_doom3_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_qwserver_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_wolf_packet, /* packet_func */
++},
++{
++ /* Teeworlds */
++ TEE_SERVER, /* id */
++ "TEE", /* type_prefix */
++ "tee", /* type_string */
++ "-tee", /* type_option */
++ "Teeworlds", /* game_name */
++ 0, /* master */
++ 35515, /* default_port */
++ 0, /* port_offset */
++ 0, /* flags */
++ "gametype", /* game_rule */
++ "TEE", /* template_var */
++ tee_serverstatus, /* status_packet */
++ sizeof(tee_serverstatus), /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_tee_player_info, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ raw_display_tee_player_info, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_tee_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_tee_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_tee_packet, /* packet_func */
++},
++{
++ /* TEAMSPEAK 3 PROTOCOL */
++ TS3_PROTOCOL_SERVER, /* id */
++ "TS3", /* type_prefix */
++ "ts3", /* type_string */
++ "-ts3", /* type_option */
++ "Teamspeak 3", /* game_name */
++ 0, /* master */
++ 0, /* default_port */
++ 0, /* port_offset */
++ TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG, /* flags */
++ "N/A", /* game_rule */
++ "TS3PROTOCOL", /* template_var */
++ NULL, /* status_packet */
++ 0, /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_ts3_player_info, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ raw_display_ts3_player_info, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_ts3_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_ts3_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_ts3_packet, /* packet_func */
++},
++{
++ /* BATTLEFIELD BAD COMPANY 2 PROTOCOL */
++ BFBC2_PROTOCOL_SERVER, /* id */
++ "BFBC2", /* type_prefix */
++ "bfbc2", /* type_string */
++ "-bfbc2", /* type_option */
++ "Battlefield Bad Company 2", /* game_name */
++ 0, /* master */
++ BFBC2_DEFAULT_PORT, /* default_port */
++ 0, /* port_offset */
++ TF_TCP_CONNECT, /* flags */
++ "gametype", /* game_rule */
++ "BFBC2PROTOCOL", /* template_var */
++ NULL, /* status_packet */
++ 0, /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_bfbc2_player_info, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ raw_display_bfbc2_player_info, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_bfbc2_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_bfbc2_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_bfbc2_packet, /* packet_func */
++},
++{
++ /* VENTRILO PROTOCOL */
++ VENTRILO_PROTOCOL_SERVER, /* id */
++ "VENTRILO", /* type_prefix */
++ "ventrilo", /* type_string */
++ "-vent", /* type_option */
++ "Ventrilo", /* game_name */
++ 0, /* master */
++ VENTRILO_DEFAULT_PORT, /* default_port */
++ 0, /* port_offset */
++ 0, /* flags */
++ "gametype", /* game_rule */
++ "VENTRILO", /* template_var */
++ NULL, /* status_packet */
++ 0, /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ display_ventrilo_player_info, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ raw_display_ventrilo_player_info, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_ventrilo_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_ventrilo_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_ventrilo_packet, /* packet_func */
++},
++{
++ /* Cube 2/Sauerbraten/Blood Frontier */
++ CUBE2_SERVER, /* id */
++ "CUBE2", /* type_prefix */
++ "cube2", /* type_string */
++ "-cubes", /* type_option */
++ "Sauerbraten", /* game_name */
++ 0, /* master */
++ CUBE2_DEFAULT_PORT, /* default_port */
++ 1, /* port_offset */
++ 0, /* flags */
++ "", /* game_rule */
++ "CUBE2", /* template_var */
++ cube2_serverstatus, /* status_packet */
++ sizeof(cube2_serverstatus), /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ NULL, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ NULL, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ NULL, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_cube2_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_cube2_packet, /* packet_func */
++},
++{
++ /* MUMBLE PROTOCOL */
++ MUMBLE_PROTOCOL_SERVER, /* id */
++ "MUMBLE", /* type_prefix */
++ "mumble", /* type_string */
++ "-mumble", /* type_option */
++ "Mumble", /* game_name */
++ 0, /* master */
++ MUMBLE_DEFAULT_PORT, /* default_port */
++ 0, /* port_offset */
++ 0, /* flags */
++ "gametype", /* game_rule */
++ "MUMBLEPROTOCOL", /* template_var */
++ mumble_serverstatus, /* status_packet */
++ 12, /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ NULL, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ NULL, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_mumble_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_mumble_packet, /* packet_func */
++},
++{
++ /* TERRARIA PROTOCOL */
++ TERRARIA_PROTOCOL_SERVER, /* id */
++ "TERRARIA", /* type_prefix */
++ "terraria", /* type_string */
++ "-terraria", /* type_option */
++ "Terraria", /* game_name */
++ 0, /* master */
++ TERRARIA_DEFAULT_PORT, /* default_port */
++ 0, /* port_offset */
++ TF_TCP_CONNECT, /* flags */
++ "gametype", /* game_rule */
++ "TERRARIPROTOCOL", /* template_var */
++ terraria_serverstatus, /* status_packet */
++ sizeof(terraria_serverstatus), /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ NULL, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ NULL, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_mumble_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_terraria_packet, /* packet_func */
++},
++{
++ /* CRYSIS PROTOCOL */
++ CRYSIS_PROTOCOL_SERVER, /* id */
++ "CRYSIS", /* type_prefix */
++ "crysis", /* type_string */
++ "-crysis", /* type_option */
++ "Crysis", /* game_name */
++ 0, /* master */
++ 0, /* default_port */
++ 0, /* port_offset */
++ TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG, /* flags */
++ "gamerules", /* game_rule */
++ "CRYSISPROTOCOL", /* template_var */
++ NULL, /* status_packet */
++ 0, /* status_len */
++ NULL, /* player_packet */
++ 0, /* player_len */
++ NULL, /* rule_packet */
++ 0, /* rule_len */
++ NULL, /* master_packet */
++ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
++ NULL, /* display_player_func */
++ display_server_rules, /* display_rule_func */
++ NULL, /* display_raw_player_func */
++ raw_display_server_rules, /* display_raw_rule_func */
++ xml_display_player_info, /* display_xml_player_func */
++ xml_display_server_rules, /* display_xml_rule_func */
++ send_crysis_request_packet, /* status_query_func */
++ NULL, /* rule_query_func */
++ NULL, /* player_query_func */
++ deal_with_crysis_packet, /* packet_func */
++},
++{
++ Q_UNKNOWN_TYPE, /* id */
+ "", /* type_prefix */
+ "", /* type_string */
+ "", /* type_option */
+@@ -2676,8 +3345,10 @@
+ 0, /* player_len */
+ NULL, /* rule_packet */
+ 0, /* rule_len */
+- (char*) NULL, /* master_packet */
++ (char*) NULL, /* master_packet */
+ 0, /* master_len */
++ NULL, /* master_protocol */
++ NULL, /* master_query */
+ NULL, /* display_player_func */
+ NULL, /* display_rule_func */
+ NULL, /* display_raw_player_func */
+@@ -2873,6 +3544,8 @@
+ int time_delta( struct timeval *later, struct timeval *past);
+ char * strherror( int h_err);
+ int connection_refused();
++int connection_would_block();
++int connection_reset();
+
+ void add_file( char *filename);
+ int add_qserver( char *arg, server_type* type, char *outfilename, char *query_arg);
+@@ -2889,6 +3562,7 @@
+ #define CHECK_DUPLICATE_RULES 2
+ #define NO_KEY_COPY 4
+ #define COMBINE_VALUES 8
++#define OVERWITE_DUPLICATES 16
+
+ struct player* get_player_by_number( struct qserver *server, int player_number );
+ struct rule* add_rule( struct qserver *server, char *key, char *value, int flags) ;
+@@ -2949,4 +3623,7 @@
+ #define NO_PLAYER_INFO 0xffff
+ #define NO_SERVER_RULES NULL
+
++#define FORCE 1
++#define NO_FORCE 0
++
+ #endif
+diff -urP qstat-2.11/tee.c qstat2/tee.c
+--- qstat-2.11/tee.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/tee.c 2009-12-21 13:38:56.452334000 -0500
+@@ -0,0 +1,92 @@
++/*
++ * qstat 2.11
++ * by Steve Jankowski
++ *
++ * Teeworlds protocol
++ * Copyright 2008 ? Emiliano Leporati
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++char tee_serverinfo[8] = { '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', 'o' };
++
++query_status_t send_tee_request_packet( struct qserver *server )
++{
++ return send_packet( server, server->type->status_packet, server->type->status_len );
++}
++
++query_status_t deal_with_tee_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++ // skip unimplemented ack, crc, etc
++ char *pkt = rawpkt + 6;
++ char *tok = NULL, *version = NULL;
++ int i;
++ struct player* player;
++
++ server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++
++ if (0 == memcmp( pkt, tee_serverinfo, 8))
++ {
++ pkt += 8;
++ // version
++ version = strdup(pkt); pkt += strlen(pkt) + 1;
++ // server name
++ server->server_name = strdup(pkt); pkt += strlen(pkt) + 1;
++ // map name
++ server->map_name = strdup(pkt); pkt += strlen(pkt) + 1;
++ // game type
++ switch(atoi(pkt)) {
++ case 0:
++ add_rule( server, server->type->game_rule, "dm", NO_FLAGS);
++ break;
++ case 1:
++ add_rule( server, server->type->game_rule, "tdm", NO_FLAGS);
++ break;
++ case 2:
++ add_rule( server, server->type->game_rule, "ctf", NO_FLAGS);
++ break;
++ default:
++ add_rule( server, server->type->game_rule, "unknown", NO_FLAGS);
++ break;
++ }
++ pkt += strlen(pkt) + 1;
++ pkt += strlen(pkt) + 1;
++ pkt += strlen(pkt) + 1;
++ // num players
++ server->num_players = atoi(pkt); pkt += strlen(pkt) + 1;
++ // max players
++ server->max_players = atoi(pkt); pkt += strlen(pkt) + 1;
++ // players
++ for(i = 0; i < server->num_players; i++)
++ {
++ player = add_player( server, i );
++ player->name = strdup(pkt); pkt += strlen(pkt) + 1;
++ player->score = atoi(pkt); pkt += strlen(pkt) + 1;
++ }
++ // version reprise
++ server->protocol_version = 0;
++
++ if (NULL == (tok = strtok(version, "."))) return -1;
++ server->protocol_version |= (atoi(tok) & 0x000F) << 12;
++ if (NULL == (tok = strtok(NULL, "."))) return -1;
++ server->protocol_version |= (atoi(tok) & 0x000F) << 8;
++ if (NULL == (tok = strtok(NULL, "."))) return -1;
++ server->protocol_version |= (atoi(tok) & 0x00FF);
++
++ free(version);
++
++ return DONE_FORCE;
++ }
++
++ // unknown packet type
++ return PKT_ERROR;
++}
+diff -urP qstat-2.11/tee.h qstat2/tee.h
+--- qstat-2.11/tee.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/tee.h 2009-12-21 13:38:56.452334000 -0500
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.11
++ * by Steve Jankowski
++ *
++ * Teeworlds protocol
++ * Copyright 2008 ? Emiliano Leporati
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_TEE_H
++#define QSTAT_TEE_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_tee_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_tee_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/template/ghostrecon.lst qstat2/template/ghostrecon.lst
+--- qstat-2.11/template/ghostrecon.lst 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/template/ghostrecon.lst 2002-11-13 21:33:53.000000000 -0500
+@@ -0,0 +1,2 @@
++grs 208.144.248.104
++grs,ignoreserverplayer=yes 208.144.248.105
+Only in qstat-2.11/template: Makefile.in
+diff -urP qstat-2.11/template.c qstat2/template.c
+--- qstat-2.11/template.c 2006-02-22 08:44:52.000000000 -0500
++++ qstat2/template.c 2009-12-11 19:26:07.553341000 -0500
+@@ -21,7 +21,7 @@
+ #include <ctype.h>
+ #include <time.h>
+
+-#if defined(__hpux) || defined(_AIX) || defined(__FreeBSD__)
++#if defined(__hpux) || defined(_AIX) || defined(__FreeBSD__) || defined(__MidnightBSD__)
+ #include <sys/types.h>
+ #endif
+
+diff -urP qstat-2.11/terraria.c qstat2/terraria.c
+--- qstat-2.11/terraria.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/terraria.c 2012-05-16 09:02:58.011773000 -0400
+@@ -0,0 +1,198 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Terraria / TShock query protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#define strtok_ret strtok_r
++#else
++#include <winsock.h>
++#define strtok_ret strtok_s
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++query_status_t send_terraria_request_packet( struct qserver *server )
++{
++ return send_packet( server, server->type->status_packet, server->type->status_len );
++}
++
++query_status_t deal_with_terraria_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *s, *end, *key, *val, *sep, *linep, *varp;
++ int complete_response = 0;
++ char last_char;
++ unsigned short port = 0;
++ debug( 2, "processing..." );
++
++ if ( 0 == pktlen )
++ {
++ // Disconnect?
++ return REQ_ERROR;
++ }
++
++ complete_response = ( 0 == strncmp( "HTTP/1.1 200", rawpkt, 12 ) && '}' == rawpkt[pktlen-1] ) ? 1 : 0;
++ last_char = rawpkt[pktlen-1];
++ rawpkt[pktlen-1] = '\0';
++
++ debug( 3, "packet: combined = %d, complete = %d", server->combined, complete_response );
++ if ( ! complete_response )
++ {
++ if ( ! server->combined )
++ {
++ // response fragment recieved
++ int pkt_id;
++ int pkt_max;
++ server->retry1 = n_retries;
++ if ( 0 == server->n_requests )
++ {
++ server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 );
++ server->n_requests++;
++ }
++
++ // We're expecting more to come
++ debug( 5, "fragment recieved..." );
++ pkt_id = packet_count( server );
++ pkt_max = pkt_id+1;
++ rawpkt[pktlen-1] = last_char; // restore the last character
++ if ( ! add_packet( server, 0, pkt_id, pkt_max, pktlen, rawpkt, 1 ) )
++ {
++ // fatal error e.g. out of memory
++ return MEM_ERROR;
++ }
++
++ if ( 0 == pkt_id )
++ {
++ return INPROGRESS;
++ }
++
++ // combine_packets will call us recursively
++ return combine_packets( server );
++ }
++
++ // recursive call which is still incomplete
++ return INPROGRESS;
++ }
++
++ // find the end of the headers
++ s = strstr( rawpkt, "\x0d\x0a\x0d\x0a" );
++
++ if ( NULL == s )
++ {
++ debug(1, "Error: missing end of headers");
++ return REQ_ERROR;
++ }
++
++ s += 4;
++
++ end = &rawpkt[pktlen-1];
++
++ // Correct ping
++ // Not quite right but gives a good estimate
++ server->ping_total = ( server->ping_total * server->n_requests ) / 2;
++
++ debug( 3, "processing response..." );
++
++ s = strtok_ret( s, "\x0d\x0a", &linep );
++
++ // NOTE: id=XXX and msg=XXX will be processed by the mod following the one they where the response of
++ while ( NULL != s )
++ {
++ debug( 2, "LINE: %s\n", s );
++ if ( 0 == strcmp( s, "{" ) )
++ {
++ s = strtok_ret( NULL, "\x0d\x0a", &linep );
++ continue;
++ }
++
++ s = strtok_ret( s, "\"", &varp );
++ key = strtok_ret( NULL, "\"", &varp );
++ sep = strtok_ret( NULL, " ", &varp );
++ val = strtok_ret( NULL, "\"", &varp );
++ if ( NULL == val )
++ {
++ // world etc may be empty which results in NULL val
++ s = strtok_ret( NULL, "\x0d\x0a", &linep );
++ continue;
++ }
++ //if ( NULL == val && sep
++ debug( 2, "var: '%s' = '%s', sep: '%s'\n", key, val, sep );
++ if ( 0 == strcmp( key, "name" ) )
++ {
++ server->server_name = strdup( val );
++ }
++ else if ( 0 == strcmp( key, "port" ) )
++ {
++ port = atoi( val );
++ change_server_port( server, port, 0 );
++ add_rule( server, key, val, NO_FLAGS );
++ }
++ else if ( 0 == strcmp( key, "playercount" ) )
++ {
++ server->num_players = atoi( val );
++ }
++ else if ( 0 == strcmp( key, "maxplayers" ) )
++ {
++ server->max_players = atoi( val );
++ }
++ else if ( 0 == strcmp( key, "world" ) )
++ {
++ server->map_name = strdup( val );
++ }
++ else if ( 0 == strcmp( key, "players" ) )
++ {
++ // Players are ", " seperated but potentially player names can have spaces
++ // so we manually check for the leading space and fix if found
++ char *playersp;
++ char *player_name = strtok_ret( val, ",", &playersp );
++ while ( NULL != player_name )
++ {
++ struct player *player = add_player( server, server->n_player_info );
++ if ( NULL != player )
++ {
++ if ( ' ' == player_name[0] )
++ {
++ player_name++;
++ }
++ player->name = strdup( player_name );
++ debug( 4, "Player: %s\n", player->name );
++ }
++ player_name = strtok_ret( NULL, ",", &playersp );
++ }
++ }
++ else if ( 0 == strcmp( key, "status" ) )
++ {
++ if ( 200 != atoi( val ) )
++ {
++ server->server_name = DOWN;
++ return DONE_FORCE;
++ }
++ }
++ else
++ {
++ add_rule( server, key, val, NO_FLAGS);
++ }
++
++ s = strtok_ret( NULL, "\x0d\x0a", &linep );
++ }
++
++ gettimeofday( &server->packet_time1, NULL );
++
++ return DONE_FORCE;
++}
++
+diff -urP qstat-2.11/terraria.h qstat2/terraria.h
+--- qstat-2.11/terraria.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/terraria.h 2012-02-07 08:55:59.913866000 -0500
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Terraria / TShock protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_TERRARIA_H
++#define QSTAT_TERRARIA_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_terraria_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_terraria_request_packet( struct qserver *server );
++
++#endif
++
+Binary files qstat-2.11/tests/etqw_1.4_test1 and qstat2/tests/etqw_1.4_test1 differ
+Binary files qstat-2.11/tests/etqw_1.4_test2 and qstat2/tests/etqw_1.4_test2 differ
+diff -urP qstat-2.11/tests/etqws.pl qstat2/tests/etqws.pl
+--- qstat-2.11/tests/etqws.pl 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/tests/etqws.pl 2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,53 @@
++#!/usr/bin/perl -w
++# simple responder for doom3/quake4/etqw queries
++
++use strict;
++use IO::Socket::INET;
++use IO::File;
++
++my $sock = IO::Socket::INET->new(
++ LocalAddr => 'localhost',
++ Proto => 'udp');
++
++print $sock->sockport(),"\n";
++
++my $rin = '';
++vec($rin, $sock->fileno, 1) = 1;
++
++my @files = @ARGV;
++die "USAGE: $0 <file>" unless @files;
++
++sub _stat_size($)
++{
++ my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
++ $atime,$mtime,$ctime,$blksize,$blocks) = stat($_[0]);
++ return $size;
++}
++
++sub readfile($)
++{
++ my $fn = shift;
++ print "reading $fn\n";
++ my $f = IO::File->new($fn, "r");
++ my $buf;
++ $f->read($buf, _stat_size($f));
++ $f->close;
++ return $buf;
++}
++
++while (select(my $rout = $rin, undef, undef, undef)) {
++ my $data = '';
++ my $hispaddr;
++ $hispaddr = $sock->recv($data, 0xffff, 0) || die "recv: $!";
++ my ($port, $hisiaddr) = sockaddr_in($hispaddr);
++ printf '%d bytes from %s:%d'."\n", length($data), inet_ntoa($hisiaddr), $port;
++
++ if($data !~ /^\xff\xffgetInfo/) {
++ printf 'invalid packet from %s:%d'."\n", inet_ntoa($hisiaddr), $port;
++ next;
++ }
++
++ my $buf = readfile($files[0]);
++
++ $sock->send($buf, 0, $hispaddr);
++}
+diff -urP qstat-2.11/tests/xml.pl qstat2/tests/xml.pl
+--- qstat-2.11/tests/xml.pl 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/tests/xml.pl 2008-04-27 12:06:26.613995000 -0400
+@@ -0,0 +1,65 @@
++#!/usr/bin/perl -w
++# simple qstat xml output parser, prints values specified as command
++# line arguments
++#
++# Author: Ludwig Nussel
++#
++# Usage Examples:
++#
++# Print server name:
++# "qstat/server/name"
++#
++# Print second server rule (numbered from zero):
++# "qstat/server/rules/rule/1"
++#
++# Print the rule with name "gamename":
++# "qstat/server/rules/rule/[name=gamename]"
++#
++# Print name of sixth player:
++# "qstat/server/players/player/5/name"
++#
++# Print clan of player with name "suCk3r":
++# "qstat/server/players/player/[name=suCk3r]/clan"
++
++use strict;
++use XML::Bare;
++use Data::Dumper;
++
++sub getvalue {
++ my $x = shift;
++ my @a = split(/\//, shift);
++ for my $n (@a) {
++ if ($n =~ /^[[:digit:]]+$/) {
++ return undef unless exists $x->[$n];
++ $x = $x->[$n];
++ } elsif ($n =~ /\[(.*)=(.*)\]/) {
++ my ($k, $v) = ($1, $2);
++ my $r;
++ for my $i (@$x) {
++ next unless exists $i->{$k};
++ if($i->{$k}->{value} eq $v) {
++ $r = $i;
++ last;
++ }
++ }
++ return undef unless $r;
++ $x = $r;
++ } else {
++ return undef unless exists $x->{$n};
++ $x = $x->{$n};
++ }
++ }
++ return $x->{value};
++}
++
++sub printvalue {
++ my $val = getvalue(@_);
++ $val = "(undefined)" unless defined $val;
++ print $val, "\n"
++}
++
++my $xml = XML::Bare->new(text => join('', <STDIN>))->parse();
++
++for (@ARGV) {
++ printvalue($xml, $_);
++}
+diff -urP qstat-2.11/tm.c qstat2/tm.c
+--- qstat-2.11/tm.c 2006-08-14 04:51:21.000000000 -0500
++++ qstat2/tm.c 2009-07-02 06:11:41.032181000 -0400
+@@ -22,35 +22,48 @@
+ #include "qstat.h"
+ #include "packet_manip.h"
+
++#define TM_XML_PREFIX "<?xml version=\"1.0\"?>\n<methodCall>\n<methodName>system.multicall</methodName>\n<params><param><value><array><data>\n"
++#define TM_XML_SUFFIX "</data></array></value></param></params>\n</methodCall>"
++#define TM_SERVERINFO "<value><struct><member><name>methodName</name><value>GetServerOptions</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n<value><struct><member><name>methodName</name><value>GetCurrentChallengeInfo</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n"
++#define TM_PLAYERLIST "<value><struct><member><name>methodName</name><value>GetPlayerList</value></member><member><name>params</name><value><array><data><value><i4>100</i4></value><value><i4>0</i4></value></data></array></value></member></struct></value>\n"
++#define TM_AUTH_TEMPLATE "<value><struct>\n<member><name>methodName</name><value><string>Authenticate</string></value></member>\n<member><name>params</name><value><array><data>\n<value><string>%s</string></value>\n<value><string>%s</string></value></data></array></value></member></struct></value>\n"
+
+-void send_tm_request_packet( struct qserver *server )
++query_status_t send_tm_request_packet( struct qserver *server )
+ {
+ char buf[2048];
+ char *xmlp = buf + 8;
+ unsigned int len;
++ char *user = get_param_value( server, "user", NULL );
++ char *password = get_param_value( server, "password", NULL );
+
+ if ( ! server->protocol_version )
+ {
+ // No seen the version yet wait
+ // register_send here to ensure that timeouts function correctly
+- register_send( server );
+- return;
++ return register_send( server );
+ }
+
+- if ( get_player_info )
++ // build the query xml
++ len = sprintf( xmlp, TM_XML_PREFIX );
++
++ if ( user != NULL && password != NULL )
+ {
+- server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY;
+- // TODO: add more calls to get full player info?
+- strcpy( xmlp, "<?xml version=\"1.0\"?>\n<methodCall>\n<methodName>system.multicall</methodName>\n<params><param><value><array><data>\n<value><struct><member><name>methodName</name><value>GetServerOptions</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n<value><struct><member><name>methodName</name><value>GetCurrentChallengeInfo</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n<value><struct><member><name>methodName</name><value>GetPlayerList</value></member><member><name>params</name><value><array><data><value><i4>100</i4></value><value><i4>0</i4></value></data></array></value></member></struct></value>\n</data></array></value></param></params>\n</methodCall>" );
++ len += sprintf( xmlp + len, TM_AUTH_TEMPLATE, user, password );
+ }
+ else
+ {
+- server->flags |= TF_STATUS_QUERY;
+- strcpy( xmlp, "<?xml version=\"1.0\"?>\n<methodCall>\n<methodName>system.multicall</methodName>\n<params><param><value><array><data>\n<value><struct><member><name>methodName</name><value>GetServerOptions</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n<value><struct><member><name>methodName</name><value>GetCurrentChallengeInfo</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n<value><struct><member><name>methodName</name><value>GetPlayerList</value></member><member><name>params</name><value><array><data><value><i4>100</i4></value><value><i4>0</i4></value></data></array></value></member></struct></value>\n</data></array></value></param></params>\n</methodCall>" );
++ // Default to User / User
++ len += sprintf( xmlp + len, TM_AUTH_TEMPLATE, "User", "User" );
+ }
+
++ // Always get Player info otherwise player count is invalid
++ // TODO: add more calls to get full player info?
++ server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY;
++ len += sprintf( xmlp + len, TM_SERVERINFO );
++ len += sprintf( xmlp + len, TM_PLAYERLIST );
++ len += sprintf( xmlp + len, TM_XML_SUFFIX );
++
+ // First 4 bytes is the length of the request
+- len = strlen( xmlp );
+ memcpy( buf, &len, 4 );
+ // Second 4 bytes is the handle identifier ( id )
+ memcpy( buf+4, &server->challenge, 4 );
+@@ -59,13 +72,14 @@
+ // we expect at least 1 packet response
+ server->saved_data.pkt_max = 1;
+
+- send_packet( server, buf, len + 8 );
++ return send_packet( server, buf, len + 8 );
+ }
+
+
+-void deal_with_tm_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_tm_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+- char *s, *end;
++ char *s;
++ char *pkt = rawpkt;
+ char *key = NULL, *value = NULL, *tmpp = NULL;
+ char fullname[256];
+ struct player *player = NULL;
+@@ -75,20 +89,30 @@
+ debug( 2, "processing..." );
+
+ s = rawpkt;
+- if ( 4 == pktlen && 0 == memcmp( rawpkt, "\x0b\x00\x00\x00", 4 ) )
++
++ // We may get the setup handle and the protocol version in one packet we may not
++ // So we continue to parse if we see the handle
++ if ( 4 <= pktlen && 0 == memcmp( pkt, "\x0b\x00\x00\x00", 4 ) )
+ {
+ // setup handle identifier
+ // greater 2^31 = XML-RPC, less = callback
+ server->challenge = 0x80000001;
+- return;
++ if ( 4 == pktlen )
++ {
++ return 0;
++ }
++ pktlen -= 4;
++ pkt += 4;
+ }
+- else if ( 10 < pktlen && 1 == sscanf( rawpkt, "GBXRemote %d", &server->protocol_version ) )
++
++ if ( 11 <= pktlen && 1 == sscanf( pkt, "GBXRemote %d", &server->protocol_version ) )
+ {
+ // Got protocol version send request
+ send_tm_request_packet( server );
+- return;
++ return 0;
+ }
+- else if ( 8 <= pktlen && 0 == memcmp( rawpkt+4, &server->challenge, 4 ) )
++
++ if ( 8 <= pktlen && 0 == memcmp( pkt+4, &server->challenge, 4 ) )
+ {
+ // first 4 bytes = the length
+ // Note: We use pkt_id to store the length of the expected packet
+@@ -104,9 +128,9 @@
+ if ( ! add_packet( server, len, 0, 2, pktlen, rawpkt, 1 ) )
+ {
+ // fatal error e.g. out of memory
+- return;
++ return -1;
+ }
+- return;
++ return 0;
+ }
+ else
+ {
+@@ -117,7 +141,8 @@
+ }
+
+ total_len = combined_length( server, server->saved_data.pkt_id );
+- expected_len = server->saved_data.pkt_id;
++ expected_len = server->saved_data.pkt_id;
++ debug( 2, "total: %d, expected: %d\n", total_len, expected_len );
+ if ( total_len < expected_len + 8 )
+ {
+ // we dont have a complete response add the packet
+@@ -136,15 +161,15 @@
+ if ( ! add_packet( server, server->saved_data.pkt_id, pkt_max - 1, new_max, pktlen, rawpkt, 1 ) )
+ {
+ // fatal error e.g. out of memory
+- return;
++ return -1;
+ }
+
+ if ( last )
+ {
+ // we are the last packet run combine to call us back
+- combine_packets( server );
++ return combine_packets( server );
+ }
+- return;
++ return 0;
+ }
+
+ server->n_servers++;
+@@ -157,11 +182,18 @@
+ gettimeofday( &server->packet_time1, NULL);
+ }
+
+- rawpkt[pktlen]= '\0';
+- end = &rawpkt[pktlen];
++ // Terminate the packet data
++ pkt = (char*)malloc( pktlen + 1 );
++ if ( NULL == pkt )
++ {
++ debug( 0, "Failed to malloc memory for packet terminator\n" );
++ return MEM_ERROR;
++ }
++ memcpy( pkt, rawpkt, pktlen );
++ pkt[pktlen] = '\0';
+
+ //fprintf( stderr, "S=%s\n", s );
+- s = strtok( s, "\015\012" );
++ s = strtok( pkt + 8, "\015\012" );
+ while ( NULL != s )
+ {
+ //fprintf( stderr, "S=%s\n", s );
+@@ -177,7 +209,7 @@
+ value = NULL;
+ continue;
+ }
+- else if ( 0 == strncmp( s, "<value>", 7 ) )
++ else if ( NULL != key && 0 == strncmp( s, "<value>", 7 ) )
+ {
+ // value
+ s += 7;
+@@ -223,7 +255,7 @@
+ method_response++;
+ }
+
+- if ( NULL != value )
++ if ( NULL != value && NULL != key )
+ {
+ switch( method_response )
+ {
+@@ -297,9 +329,13 @@
+ s = strtok( NULL, "\015\012" );
+ }
+
++ free( pkt );
++
+ if ( 0 == strncmp( rawpkt + pktlen - 19, "</methodResponse>", 17 ) )
+ {
+ // last packet seen
+- cleanup_qserver( server, 1 );
++ return DONE_FORCE;
+ }
++
++ return INPROGRESS;
+ }
+diff -urP qstat-2.11/tm.h qstat2/tm.h
+--- qstat-2.11/tm.h 2006-08-04 15:42:09.000000000 -0500
++++ qstat2/tm.h 2009-07-02 06:11:41.032181000 -0400
+@@ -7,14 +7,14 @@
+ *
+ * Licensed under the Artistic License, see LICENSE.txt for license terms
+ */
+-#ifndef QSTAT_GPS_H
+-#define QSTAT_GPS_H
++#ifndef QSTAT_TM_H
++#define QSTAT_TM_H
+
+ #include "qserver.h"
+
+ // Packet processing methods
+-void deal_with_tm_packet( struct qserver *server, char *pkt, int pktlen );
+-void send_tm_request_packet( struct qserver *server );
++query_status_t deal_with_tm_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_tm_request_packet( struct qserver *server );
+
+ #endif
+
+diff -urP qstat-2.11/ts2.c qstat2/ts2.c
+--- qstat-2.11/ts2.c 2006-05-24 09:26:00.000000000 -0500
++++ qstat2/ts2.c 2009-09-30 14:28:16.595305000 -0400
+@@ -2,7 +2,7 @@
+ * qstat 2.8
+ * by Steve Jankowski
+ *
+- * Gamespy query protocol
++ * Teamspeak 2 query protocol
+ * Copyright 2005 Steven Hartland
+ *
+ * Licensed under the Artistic License, see LICENSE.txt for license terms
+@@ -22,7 +22,7 @@
+ #include "packet_manip.h"
+
+
+-void send_ts2_request_packet( struct qserver *server )
++query_status_t send_ts2_request_packet( struct qserver *server )
+ {
+ char buf[256];
+
+@@ -42,25 +42,25 @@
+ server->saved_data.pkt_index = 1;
+ }
+
+- send_packet( server, buf, strlen( buf ) );
++ return send_packet( server, buf, strlen( buf ) );
+ }
+
+
+-void deal_with_ts2_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_ts2_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+ char *s, *end;
+- int ping, connect_time;
++ int ping, connect_time, mode = 0;
+ char name[256];
+ debug( 2, "processing..." );
+
+ server->n_servers++;
+- if ( server->server_name == NULL)
+- {
+- server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
+- }
+- else
++ server->n_requests++;
++ server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++
++ if ( 0 == pktlen )
+ {
+- gettimeofday( &server->packet_time1, NULL);
++ // Invalid password
++ return REQ_ERROR;
+ }
+
+ rawpkt[pktlen]= '\0';
+@@ -71,67 +71,96 @@
+
+ while ( NULL != s )
+ {
+- char *key = s;
+- char *value = strchr( key, '=' );
+- if ( NULL != value )
++ if ( 0 == mode )
+ {
+- // Server Rule
+- *value = '\0';
+- value++;
+- if ( 0 == strcmp( "server_name", key ) )
+- {
+- server->server_name = strdup( value );
++ // Rules
++ char *key = s;
++ char *value = strchr( key, '=' );
++ if ( NULL != value )
++ {
++ // Server Rule
++ *value = '\0';
++ value++;
++ if ( 0 == strcmp( "server_name", key ) )
++ {
++ server->server_name = strdup( value );
++ }
++ else if ( 0 == strcmp( "server_udpport", key ) )
++ {
++ change_server_port( server, atoi( value ), 0 );
++ add_rule( server, key, value, NO_FLAGS );
++ }
++ else if ( 0 == strcmp( "server_maxusers", key ) )
++ {
++ server->max_players = atoi( value );
++ }
++ else if ( 0 == strcmp( "server_currentusers", key ) )
++ {
++ server->num_players = atoi( value);
++ }
++ else
++ {
++ add_rule( server, key, value, NO_FLAGS);
++ }
++ }
++ else if ( 0 == strcmp( "OK", s ) )
++ {
++ // end of rules request
++ server->saved_data.pkt_index--;
++ mode++;
++ }
++ else if ( 0 == strcmp( "[TS]", s ) )
++ {
++ // nothing to do
++ }
++ else if ( 0 == strcmp( "ERROR, invalid id", s ) )
++ {
++ // bad server
++ server->server_name = DOWN;
++ server->saved_data.pkt_index = 0;
+ }
+- else if ( 0 == strcmp( "server_udpport", key ) )
++ }
++ else if ( 1 == mode )
++ {
++ // Player info
++ if ( 3 == sscanf( s, "%*d %*d %*d %*d %*d %*d %*d %d %d %*d %*d %*d %*d \"0.0.0.0\" \"%255[^\"]", &ping, &connect_time, name ) )
+ {
+- change_server_port( server, atoi( value ), 0 );
+- add_rule( server, key, value, NO_FLAGS );
++ // Player info
++ struct player *player = add_player( server, server->n_player_info );
++ if ( NULL != player )
++ {
++ player->name = strdup( name );
++ player->ping = ping;
++ player->connect_time = connect_time;
++ }
+ }
+- else if ( 0 == strcmp( "server_maxusers", key ) )
++ else if ( 0 == strcmp( "OK", s ) )
+ {
+- server->max_players = atoi( value );
++ // end of rules request
++ server->saved_data.pkt_index--;
++ mode++;
+ }
+- else if ( 0 == strcmp( "server_currentusers", key ) )
++ else if ( 0 == strcmp( "[TS]", s ) )
+ {
+- server->num_players = atoi( value);
++ // nothing to do
+ }
+- else
++ else if ( 0 == strcmp( "ERROR, invalid id", s ) )
+ {
+- add_rule( server, key, value, NO_FLAGS);
++ // bad server
++ server->server_name = DOWN;
++ server->saved_data.pkt_index = 0;
+ }
+ }
+- else if ( 3 == sscanf( s, "%*d %*d %*d %*d %*d %*d %*d %d %d %*d %*d %*d %*d \"0.0.0.0\" \"%255[^\"]", &ping, &connect_time, name ) )
+- {
+- // Player info
+- struct player *player = add_player( server, server->n_player_info );
+- if ( NULL != player )
+- {
+- player->name = strdup( name );
+- player->ping = ping;
+- player->connect_time = connect_time;
+- }
+- }
+- else if ( 0 == strcmp( "OK", s ) )
+- {
+- // end of request result
+- server->saved_data.pkt_index--;
+- }
+- else if ( 0 == strcmp( "[TS]", s ) )
+- {
+- // nothing to do
+- }
+- else if ( 0 == strcmp( "ERROR, invalid id", s ) )
+- {
+- // bad server
+- server->server_name = DOWN;
+- server->saved_data.pkt_index = 0;
+- }
+ s = strtok( NULL, "\015\012" );
+ }
+
++ gettimeofday( &server->packet_time1, NULL );
++
+ if ( 0 == server->saved_data.pkt_index )
+ {
+ server->map_name = strdup( "N/A" );
+- cleanup_qserver( server, 1 );
++ return DONE_FORCE;
+ }
++
++ return INPROGRESS;
+ }
+diff -urP qstat-2.11/ts2.h qstat2/ts2.h
+--- qstat-2.11/ts2.h 2005-09-21 09:46:11.000000000 -0500
++++ qstat2/ts2.h 2009-07-02 06:11:41.032181000 -0400
+@@ -7,14 +7,14 @@
+ *
+ * Licensed under the Artistic License, see LICENSE.txt for license terms
+ */
+-#ifndef QSTAT_GPS_H
+-#define QSTAT_GPS_H
++#ifndef QSTAT_TS2_H
++#define QSTAT_TS2_H
+
+ #include "qserver.h"
+
+ // Packet processing methods
+-void deal_with_ts2_packet( struct qserver *server, char *pkt, int pktlen );
+-void send_ts2_request_packet( struct qserver *server );
++query_status_t deal_with_ts2_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_ts2_request_packet( struct qserver *server );
+
+ #endif
+
+diff -urP qstat-2.11/ts3.c qstat2/ts3.c
+--- qstat-2.11/ts3.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ts3.c 2012-10-10 14:34:51.376884000 -0400
+@@ -0,0 +1,476 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Teamspeak 3 query protocol
++ * Copyright 2009 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#else
++#include <winsock.h>
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++char *decode_ts3_val( char *val )
++{
++ val = str_replace( val, "\\\\", "\\" );
++ val = str_replace( val, "\\/", "/" );
++ val = str_replace( val, "\\s", " " );
++ val = str_replace( val, "\\p", "|" );
++ val = str_replace( val, "\\a", "\007" );
++ val = str_replace( val, "\\b", "\010" );
++ val = str_replace( val, "\\f", "\014" );
++ val = str_replace( val, "\\n", "\012" );
++ val = str_replace( val, "\\r", "\015" );
++ val = str_replace( val, "\\t", "\011" );
++
++ return str_replace( val, "\\v", "\013" );
++}
++
++// NOTE: replace must be smaller or equal in size to find
++char *str_replace( char *source, char *find, char *replace )
++{
++ char *s = strstr( source, find );
++ int rlen = strlen( replace );
++ int flen = strlen( find );
++ while( NULL != s )
++ {
++ strncpy( s, replace, rlen );
++ strcpy( s + rlen, s + flen );
++ s += rlen;
++ s = strstr( s, find );
++ }
++
++ return source;
++}
++
++int all_ts3_servers( struct qserver *server )
++{
++ return ( 1 == get_param_i_value( server, "allservers", 0 ) ) ? 1 : 0;
++}
++
++query_status_t send_ts3_all_servers_packet( struct qserver *server )
++{
++ char buf[256], *password, *username;
++ switch ( server->challenge )
++ {
++ case 0:
++ // Not seen a challenge yet, wait for it
++ server->n_servers = 999;
++ return INPROGRESS;
++
++ case 1:
++ password = get_param_value( server, "password", "" );
++ if ( 0 != strlen( password ) )
++ {
++ username = get_param_value( server, "username", "serveradmin" );
++ sprintf( buf, "login %s %s\015\012", username, password );
++ break;
++ }
++ // NOTE: no break so we fall through
++ server->challenge++;
++
++ case 2:
++ // NOTE: we currently don't support player info
++ server->flags |= TF_STATUS_QUERY;
++ server->n_servers = 3;
++ sprintf( buf, "serverlist\015\012" );
++ break;
++
++ case 3:
++ sprintf( buf, "quit\015\012" );
++ break;
++
++ case 4:
++ return DONE_FORCE;
++ }
++
++ server->saved_data.pkt_max = -1;
++
++ return send_packet( server, buf, strlen( buf ) );
++}
++
++
++query_status_t send_ts3_single_server_packet( struct qserver *server )
++{
++ char buf[256], *password, *username;
++ int serverport;
++ switch ( server->challenge )
++ {
++ case 0:
++ // Not seen a challenge yet, wait for it
++ server->n_servers = 999;
++ return INPROGRESS;
++
++ case 1:
++ // Login if needed
++ password = get_param_value( server, "password", "" );
++ if ( 0 != strlen( password ) )
++ {
++ username = get_param_value( server, "username", "serveradmin" );
++ sprintf( buf, "login %s %s\015\012", password, username );
++ break;
++ }
++ // NOTE: no break so we fall through
++ server->challenge++;
++
++ case 2:
++ // Select port
++ serverport = get_param_i_value( server, "port", 0 );
++ change_server_port( server, serverport, 1 );
++ // NOTE: we use n_servers as an indication of how many responses we are expecting to get
++ if ( get_player_info )
++ {
++ server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY;
++ server->n_servers = 5;
++ }
++ else
++ {
++ server->flags |= TF_STATUS_QUERY;
++ server->n_servers = 4;
++ }
++ sprintf( buf, "use port=%d\015\012", serverport );
++ break;
++
++ case 3:
++ // Server Info
++ sprintf( buf, "serverinfo\015\012" );
++ break;
++
++ case 4:
++ // Player Info, Quit or Done
++ sprintf( buf, ( get_player_info ) ? "clientlist\015\012" : "quit\015\012" );
++ break;
++
++ case 5:
++ // Quit or Done
++ if ( get_player_info )
++ {
++ sprintf( buf, "quit\015\012" );
++ }
++ else
++ {
++ return DONE_FORCE;
++ }
++ break;
++ }
++ server->saved_data.pkt_max = -1;
++
++ return send_packet( server, buf, strlen( buf ) );
++}
++
++query_status_t send_ts3_request_packet( struct qserver *server )
++{
++ debug( 3, "send_ts3_request_packet: state = %ld", server->challenge );
++ return ( all_ts3_servers( server ) ) ? send_ts3_all_servers_packet( server ) : send_ts3_single_server_packet( server );
++}
++
++int valid_ts3_response( struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *end = &rawpkt[pktlen-1];
++ char *s = rawpkt;
++
++ if ( 0 == strncmp( "TS3", s, 3 ) )
++ {
++ // Challenge
++ return 1;
++ }
++
++ while ( NULL != s )
++ {
++ // use response
++ if ( 0 == strncmp( "error", s, 5 ) )
++ {
++ // end cmd response
++ if ( 0 == strncmp( "error id=0", s, 9 ) )
++ {
++ return 1;
++ }
++ else
++ {
++ // bad server
++ server->server_name = DOWN;
++ server->saved_data.pkt_index = 0;
++ return DONE_FORCE;
++ }
++ }
++ s = strstr( s, "\012\015" );
++ if ( NULL != s )
++ {
++ s = ( s + 2 < end ) ? s + 2 : NULL;
++ }
++ }
++
++ return 0;
++}
++
++query_status_t deal_with_ts3_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *s, *end, *player_name = "unknown";
++ int valid_response = 0, mode = 0, all_servers = 0;
++ char last_char;
++ unsigned short port = 0, down = 0, auth_seen = 0;
++ debug( 2, "processing..." );
++
++ if ( 0 == pktlen )
++ {
++ // Invalid password
++ return REQ_ERROR;
++ }
++
++ last_char = rawpkt[pktlen-1];
++ rawpkt[pktlen-1] = '\0';
++ end = &rawpkt[pktlen-1];
++ s = rawpkt;
++ all_servers = all_ts3_servers( server );
++
++ debug( 3, "packet: combined = %d, challenge = %ld, n_servers = %d", server->combined, server->challenge, server->n_servers );
++ if ( ! server->combined )
++ {
++ server->retry1 = n_retries;
++ if ( 0 == server->n_requests )
++ {
++ server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 );
++ server->n_requests++;
++ }
++
++ valid_response = valid_ts3_response( server, rawpkt, pktlen );
++
++ if ( 0 > valid_response )
++ {
++ // Error occured
++ return valid_response;
++ }
++
++ // only update on the original packed not on a combined call
++ server->challenge += valid_response;
++
++ debug( 3, "new packet: valid_response = %d, challenge = %ld, n_servers = %d", valid_response, server->challenge, server->n_servers );
++
++ if ( valid_response )
++ {
++ // Got a valid response, send the next request
++ int ret = send_ts3_request_packet( server );
++ if ( 0 != ret )
++ {
++ // error sending packet
++ debug( 4, "Request failed: %d", ret );
++ return ret;
++ }
++ }
++
++ if ( server->n_servers >= server->challenge )
++ {
++ // response fragment recieved
++ int pkt_id;
++ int pkt_max;
++
++ // We're expecting more to come
++ debug( 5, "fragment recieved..." );
++ pkt_id = packet_count( server );
++ pkt_max = ( 0 == pkt_id ) ? 2 : pkt_id + 1;
++ rawpkt[pktlen-1] = last_char; // restore the last character
++ if ( ! add_packet( server, 0, pkt_id, pkt_max, pktlen, rawpkt, 1 ) )
++ {
++ // fatal error e.g. out of memory
++ return MEM_ERROR;
++ }
++
++ // combine_packets will call us recursively
++ return combine_packets( server );
++ }
++ }
++ else if ( server->n_servers > server->challenge )
++ {
++ // recursive call which is still incomplete
++ return INPROGRESS;
++ }
++
++ // Correct ping
++ // Not quite right but gives a good estimate
++ server->ping_total = ( server->ping_total * server->n_requests ) / 2;
++
++ debug( 3, "processing response..." );
++
++ s = strtok( rawpkt, "\012\015 |" );
++
++ // NOTE: id=XXX and msg=XXX will be processed by the mod following the one they where the response of
++ while ( NULL != s )
++ {
++ debug( 2, "LINE: %d, %s\n", mode, s );
++ switch ( mode )
++ {
++ case 0:
++ // prompt, use or serverlist response
++ if ( 0 == strcmp( "TS3", s ) )
++ {
++ // nothing to do unless in all servers mode
++ if ( 1 == all_servers )
++ {
++ mode++;
++ }
++ }
++ else if ( 0 == strncmp( "error", s, 5 ) )
++ {
++ // end of use response
++ mode++;
++ }
++ break;
++
++ case 1:
++ // serverinfo or serverlist response including condition authentication
++ if ( 0 == auth_seen && 0 != strlen( get_param_value( server, "password", "" ) ) && 0 == strncmp( "error", s, 5 ) )
++ {
++ // end of auth response
++ auth_seen = 1;
++ }
++ else if ( 0 == strncmp( "error", s, 5 ) )
++ {
++ // end of serverinfo response
++ mode++;
++ }
++ else
++ {
++ // Server Rule
++ char *key = s;
++ char *value = strchr( key, '=' );
++ if ( NULL != value )
++ {
++ *value = '\0';
++ value++;
++ debug( 6, "Rule: %s = %s\n", key, value );
++ if ( 0 == strcmp( "virtualserver_name", key ) )
++ {
++ if ( 1 == all_servers )
++ {
++ struct qserver *new_server = add_qserver_byaddr( ntohl( server->ipaddr ), port, server->type, NULL );
++ if ( NULL != new_server )
++ {
++ if ( down )
++ {
++ // Status indicates this server is actually offline
++ new_server->server_name = DOWN;
++ }
++ else
++ {
++ new_server->max_players = server->max_players;
++ new_server->num_players = server->num_players;
++ new_server->server_name = strdup( decode_ts3_val( value ) );
++ new_server->map_name = strdup( "N/A" );
++ new_server->ping_total = server->ping_total;
++ new_server->n_requests = server->n_requests;
++ }
++ cleanup_qserver( new_server, FORCE );
++ }
++ down = 0;
++ }
++ else
++ {
++ server->server_name = strdup( decode_ts3_val( value ) );
++ }
++ }
++ else if ( 0 == strcmp( "virtualserver_port", key ) )
++ {
++ port = atoi( value );
++ change_server_port( server, port, 0 );
++ add_rule( server, key, value, NO_FLAGS );
++ }
++ else if ( 0 == strcmp( "virtualserver_maxclients", key ) )
++ {
++ server->max_players = atoi( value );
++ }
++ else if ( 0 == strcmp( "virtualserver_clientsonline", key ) )
++ {
++ server->num_players = atoi( value );
++ }
++ else if ( 0 == strcmp( "virtualserver_queryclientsonline", key ) )
++ {
++ // clientsonline includes queryclientsonline so remove these from our count
++ server->num_players -= atoi( value );
++ }
++ else if ( 0 == strcmp( "virtualserver_status", key ) && 0 != strcmp( "online", value ) )
++ {
++ // Server is actually offline to client so display as down
++ down = 1;
++ if ( 1 != all_servers )
++ {
++ server->server_name = DOWN;
++ //server->saved_data.pkt_index = 0;
++ return DONE_FORCE;
++ }
++ }
++ else if ( 0 == strcmp( "id", key ) || 0 == strcmp( "msg", key ) )
++ {
++ // Ignore details from the response code
++ }
++ else if ( 1 != all_servers )
++ {
++ add_rule( server, key, value, NO_FLAGS);
++ }
++ }
++ }
++ break;
++
++ case 2:
++ // clientlist response
++ if ( 0 == strncmp( "error", s, 5 ) )
++ {
++ // end of serverinfo response
++ mode++;
++ }
++ else
++ {
++ // Client
++ char *key = s;
++ char *value = strchr( key, '=' );
++ if ( NULL != value )
++ {
++ *value = '\0';
++ value++;
++ debug( 6, "Player: %s = %s\n", key, value );
++ if ( 0 == strcmp( "client_nickname", key ) )
++ {
++ player_name = value;
++ }
++ else if ( 0 == strcmp( "clid", key ) )
++ {
++ }
++ else if ( 0 == strcmp( "client_type", key ) && 0 == strcmp( "0", value ) )
++ {
++ struct player *player = add_player( server, server->n_player_info );
++ if ( NULL != player )
++ {
++ player->name = strdup( decode_ts3_val( player_name ) );
++ }
++ }
++ else if ( 0 == strcmp( "id", key ) || 0 == strcmp( "msg", key ) )
++ {
++ // Ignore details from the response code
++ }
++ }
++ }
++ break;
++ }
++ s = strtok( NULL, "\012\015 |" );
++ }
++
++ gettimeofday( &server->packet_time1, NULL );
++
++ server->map_name = strdup( "N/A" );
++ return DONE_FORCE;
++}
++
+diff -urP qstat-2.11/ts3.h qstat2/ts3.h
+--- qstat-2.11/ts3.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ts3.h 2009-12-21 13:38:56.452334000 -0500
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Teamspeak 3 protocol
++ * Copyright 2005 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_TS3_H
++#define QSTAT_TS3_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_ts3_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_ts3_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/ut2004.c qstat2/ut2004.c
+--- qstat-2.11/ut2004.c 2006-05-28 06:48:38.000000000 -0500
++++ qstat2/ut2004.c 2009-07-02 06:12:25.035239000 -0400
+@@ -30,47 +30,47 @@
+ */
+ static void bin2hex(const char* in, size_t len, char* out);
+
+-#define CD_KEY_LENGTH 23
++#define CD_KEY_LENGTH 23
+
+ // arbitrary
+ #define MAX_LISTING_RECORD_LEN 0x04FF
+
+-#define RESPONSE_OFFSET_CDKEY 5
++#define RESPONSE_OFFSET_CDKEY 5
+ #define RESPONSE_OFFSET_CHALLENGE 39
+
+ static const char challenge_response[] = {
+- 0x68, 0x00, 0x00, 0x00, '!', 0xCD, 0xCD, 0xCD,
++ 0x68, 0x00, 0x00, 0x00, '!', 0xCD, 0xCD, 0xCD,
+ /* length | ! MD5SUM, CD is placeholder */
+- 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
+- 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
+- 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
++ 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
++ 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
++ 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
+ 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0x00,
+- '!', 0xCD,
+- 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
+- 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
+- 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
+- 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0x00,
++ '!', 0xCD,
++ 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
++ 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
++ 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
++ 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0x00,
+
+ 0x0c, 'U', 'T', '2', 'K', '4', 'C', 'L',
+ /*^^ 12 byte string */
+- 'I', 'E', 'N', 'T', 0x00, 0xfb, 0x0c, 0x00,
++ 'I', 'E', 'N', 'T', 0x00, 0xfb, 0x0c, 0x00,
+ /* | unknown */
+- 0x00, 0x06, 0x04, 'i', 'n', 't', 0x00, 0x00,
+- /* | ^^ 4 byte string | ? */
+- 0x00, 0x00, 0x00, 0xee, 0xee, 0x00, 0x00, 0x11,
++ 0x00, 0x06, 0x04, 'i', 'n', 't', 0x00, 0x00,
++ /* | ^^ 4 byte string | ? */
++ 0x00, 0x00, 0x00, 0xee, 0xee, 0x00, 0x00, 0x11,
+ /* unknown */
+ 0x00, 0x00, 0x00, 0x01 };
+
+ static const char approved[] = {
+ 0x0e, 0x00, 0x00, 0x00, 0x09, 'A', 'P', 'P',
+- 'R', 'O', 'V', 'E', 'D', 0x00, 0x03, 0x00,
++ 'R', 'O', 'V', 'E', 'D', 0x00, 0x03, 0x00,
+ 0x00, 0x00 };
+
+ static const char approved_response[] = {
+- 0x22, 0x00, 0x00, 0x00, '!', '0', '0', '0',
+- '0', '0', '0', '0', '0', '0', '0', '0',
+- '0', '0', '0', '0', '0', '0', '0', '0',
+- '0', '0', '0', '0', '0', '0', '0', '0',
++ 0x22, 0x00, 0x00, 0x00, '!', '0', '0', '0',
++ '0', '0', '0', '0', '0', '0', '0', '0',
++ '0', '0', '0', '0', '0', '0', '0', '0',
++ '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', 0x00 };
+
+ static const char verified[] = {
+@@ -79,167 +79,171 @@
+
+ #if 0
+ struct server_listing_record_head {
+- unsigned len;
+- unsigned ip;
+- short port;
+- short queryport;
+- char name[];
+- // char map[]
++ unsigned len;
++ unsigned ip;
++ short port;
++ short queryport;
++ char name[];
++ // char map[]
+ };
+
+ struct server_listing_record_foot {
+- unsigned char marker1[3];
+- unsigned char unknown1;
+- unsigned char maxplayers;
+- unsigned char unknown2[4];
+- unsigned char marker2[3];
++ unsigned char marker1[3];
++ unsigned char unknown1;
++ unsigned char maxplayers;
++ unsigned char unknown2[4];
++ unsigned char marker2[3];
+ };
+ #endif
+
+ static char cdkey[CD_KEY_LENGTH+1] = "";
+
+ enum ut2004_state {
+- STATE_CHALLENGE = 0x00,
+- STATE_APPROVED = 0x01,
+- STATE_VERIFIED = 0x02,
+- STATE_LISTING = 0x03,
++ STATE_CHALLENGE = 0x00,
++ STATE_APPROVED = 0x01,
++ STATE_VERIFIED = 0x02,
++ STATE_LISTING = 0x03,
+ };
+
+-void send_ut2004master_request_packet(struct qserver *server)
++query_status_t send_ut2004master_request_packet(struct qserver *server)
+ {
+- if(server->n_packets)
+- {
+- cleanup_qserver(server, 1);
+- return;
+- }
+-
+- if(!*cdkey)
+- {
+- char* param = get_param_value( server, "cdkey", NULL);
+- if(!param)
+- {
+- debug(0, "Error: missing cdkey parameter");
+- server->server_name = SYSERROR;
+- cleanup_qserver(server, 1);
+- return;
+- }
+-
+- if(*param == '/')
+- {
+- FILE* fp = fopen(param, "r");
+- if(!fp || fread(cdkey, 1, CD_KEY_LENGTH, fp) != CD_KEY_LENGTH)
+- {
+- debug(0, "Error: can't key from %s", param);
+- server->server_name = SYSERROR;
+- cleanup_qserver(server, 1);
+- if(fp) fclose(fp);
+- return;
+- }
+- fclose(fp);
+- }
+- else if(strchr(param, '-') && strlen(param) == CD_KEY_LENGTH)
++ int ret;
++ if(server->n_packets)
+ {
+- memcpy(cdkey, param, CD_KEY_LENGTH);
++ return DONE_FORCE;
+ }
+- else if( *param == '$'
+- && (param = getenv(param+1)) // replaces param!
+- && strlen(param) == CD_KEY_LENGTH)
+- {
+- memcpy(cdkey, param, CD_KEY_LENGTH);
+- }
+- else
++
++ if(!*cdkey)
+ {
+- debug(0, "Error: invalid cdkey parameter");
+- server->server_name = SYSERROR;
+- cleanup_qserver(server, 1);
+- return;
++ char* param = get_param_value( server, "cdkey", NULL);
++ if(!param)
++ {
++ debug(0, "Error: missing cdkey parameter");
++ server->server_name = SYSERROR;
++ return PKT_ERROR;
++ }
++
++ if(*param == '/')
++ {
++ FILE* fp = fopen(param, "r");
++ if(!fp || fread(cdkey, 1, CD_KEY_LENGTH, fp) != CD_KEY_LENGTH)
++ {
++ debug(0, "Error: can't key from %s", param);
++ server->server_name = SYSERROR;
++ if(fp)
++ {
++ fclose(fp);
++ }
++ return PKT_ERROR;
++ }
++ fclose(fp);
++ }
++ else if(strchr(param, '-') && strlen(param) == CD_KEY_LENGTH)
++ {
++ memcpy(cdkey, param, CD_KEY_LENGTH);
++ }
++ else if( *param == '$'
++ && (param = getenv(param+1)) // replaces param!
++ && strlen(param) == CD_KEY_LENGTH)
++ {
++ memcpy(cdkey, param, CD_KEY_LENGTH);
++ }
++ else
++ {
++ debug(0, "Error: invalid cdkey parameter");
++ server->server_name = SYSERROR;
++ return PKT_ERROR;
++ }
+ }
+- }
+
+- qserver_send(server, NULL, 0);
++ ret = qserver_send(server, NULL, 0);
+
+ #if 0
+- // XXX since we do not send but rather expect a reply directly after
+- // connect it's pointless to retry doing nothing
+- debug(0, "retry1: %d", server->retry1);
+- server->retry1 = 0;
++ // XXX since we do not send but rather expect a reply directly after
++ // connect it's pointless to retry doing nothing
++ debug(0, "retry1: %d", server->retry1);
++ server->retry1 = 0;
+ #endif
+
+- server->master_query_tag[0] = STATE_CHALLENGE;
++ server->master_query_tag[0] = STATE_CHALLENGE;
++ return ret;
+ }
+
+ static void ut2004_server_done(struct qserver* server)
+ {
+- if(server->saved_data.next)
+- {
+- debug(0, "%d bytes of unprocessed data left. Premature EOF!?",
+- server->saved_data.next->datalen);
+- free(server->saved_data.next->data);
+- free(server->saved_data.next);
+- server->saved_data.next = NULL;
+- }
++ if(server->saved_data.next)
++ {
++ debug(0, "%d bytes of unprocessed data left. Premature EOF!?", server->saved_data.next->datalen);
++ free(server->saved_data.next->data);
++ free(server->saved_data.next);
++ server->saved_data.next = NULL;
++ }
+ }
+
+ // we use n_servers to store number of used bytes in master_pkt so
+ // it needs to be divided by 6 when finished
+ static void ut2004_parse_record(struct qserver* server, char* pkt)
+ {
+- char* dest;
++ char* dest;
+
+ #if 0
+- unsigned ip;
+- unsigned short port;
++ unsigned ip;
++ unsigned short port;
+
+- memcpy(&ip, pkt+4, 4);
+- port = swap_short_from_little(pkt+4+4);
++ memcpy(&ip, pkt+4, 4);
++ port = swap_short_from_little(pkt+4+4);
+
+- debug(2, "got %d.%d.%d.%d:%hu", ip&0xff, (ip>>8)&0xff, (ip>>16)&0xff, (ip>>24)&0xff, port);
++ debug(2, "got %d.%d.%d.%d:%hu", ip&0xff, (ip>>8)&0xff, (ip>>16)&0xff, (ip>>24)&0xff, port);
+ #endif
+
+- if(server->n_servers+6 > server->master_pkt_len)
+- {
+- if(!server->master_pkt_len)
+- server->master_pkt_len = 180;
+- else
+- server->master_pkt_len *= 2;
+- server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len);
+- }
+-
+- dest = server->master_pkt + server->n_servers;
+-
+- memcpy(dest, pkt+4, 4 );
+- dest[4] = pkt[9];
+- dest[5] = pkt[8];
+- server->n_servers += 6;
++ if(server->n_servers+6 > server->master_pkt_len)
++ {
++ if(!server->master_pkt_len)
++ {
++ server->master_pkt_len = 180;
++ }
++ else
++ {
++ server->master_pkt_len *= 2;
++ }
++ server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len);
++ }
++
++ dest = server->master_pkt + server->n_servers;
++
++ memcpy(dest, pkt+4, 4 );
++ dest[4] = pkt[9];
++ dest[5] = pkt[8];
++ server->n_servers += 6;
+ }
+
+ static char* put_bytes(char* buf, const char* bytes, size_t len, size_t* left)
+ {
+- if(!buf || len > *left)
++ if(!buf || len > *left)
+ return NULL;
+
+- memcpy(buf, bytes, len);
+- *left -= len;
++ memcpy(buf, bytes, len);
++ *left -= len;
+
+- return buf+len;
++ return buf+len;
+ }
+
+ static char* put_string(char* buf, const char* string, size_t* left)
+ {
+- size_t len = strlen(string)+1;
+- char l;
++ size_t len = strlen(string)+1;
++ char l;
+
+- if(!buf || len > 0xFF || *left < len+1)
+- return NULL;
++ if(!buf || len > 0xFF || *left < len+1)
++ return NULL;
+
+- l = len;
++ l = len;
+
+- buf = put_bytes(buf, &l, 1, left);
+- return put_bytes(buf, string, len, left);
++ buf = put_bytes(buf, &l, 1, left);
++ return put_bytes(buf, string, len, left);
+ }
+
+ /** \brief assemble the server filter and send the master query
+-
++
+ the query consists of four bytes length (excluding the four length bytes), a
+ null byte and then the number of item pairs that follow.
+
+@@ -249,416 +253,409 @@
+ */
+ static int ut2004_send_query(struct qserver* server)
+ {
+- char buf[4096] = {0};
+- size_t left = sizeof(buf);
+- char *b = buf;
+- char *param, *r, *sep= "";
+- unsigned flen = 0;
+- unsigned char items = 0;
+-
+- // header is done later
+- b += 6;
+- left -= 6;
+-
+- param = get_param_value( server, "gametype", NULL);
+- if(param)
+- {
+- ++items;
+- b = put_string(b, "gametype", &left);
+- b = put_string(b, param, &left);
+- b = put_bytes(b, "", 1, &left);
+- }
+-
+- param = get_param_value( server, "status", NULL);
+- r = param;
+- while ( param && sep )
+- {
+- sep= strchr( r, ':');
+- if ( sep )
+- flen= sep-r;
+- else
+- flen= strlen(r);
+-
+- if ( strncmp( r, "standard", flen) == 0
+- || strncmp( r, "nostandard", flen) == 0)
+- {
+- ++items;
+- b = put_string(b, "standard", &left);
+- if(*r == 'n')
+- b = put_string(b, "false", &left);
+- else
+- b = put_string(b, "true", &left);
+- b = put_bytes(b, "", 1, &left);
+- }
+- else if ( strncmp( r, "password", flen) == 0
+- || strncmp( r, "nopassword", flen) == 0)
+- {
+- ++items;
+- b = put_string(b, "password", &left);
+- if(*r == 'n')
+- b = put_string(b, "false", &left);
+- else
+- b = put_string(b, "true", &left);
+- b = put_bytes(b, "", 1, &left);
+- }
+- else if ( strncmp( r, "notempty", flen) == 0)
+- {
+- ++items;
+- b = put_string(b, "currentplayers", &left);
+- b = put_string(b, "0", &left);
+- b = put_bytes(b, "\x04", 1, &left);
+- }
+- else if ( strncmp( r, "notfull", flen) == 0)
+- {
+- ++items;
+- b = put_string(b, "freespace", &left);
+- b = put_string(b, "0", &left);
+- b = put_bytes(b, "\x04", 1, &left);
+- }
+- else if ( strncmp( r, "nobots", flen) == 0)
+- {
+- ++items;
+- b = put_string(b, "nobots", &left);
+- b = put_string(b, "true", &left);
+- b = put_bytes(b, "", 1, &left);
+- }
+- else if ( strncmp( r, "stats", flen) == 0
+- || strncmp( r, "nostats", flen) == 0)
+- {
+- ++items;
+- b = put_string(b, "stats", &left);
+- if(*r == 'n')
+- b = put_string(b, "false", &left);
+- else
+- b = put_string(b, "true", &left);
+- b = put_bytes(b, "", 1, &left);
+- }
+- else if ( strncmp( r, "weaponstay", flen) == 0
+- || strncmp( r, "noweaponstay", flen) == 0)
+- {
+- ++items;
+- b = put_string(b, "weaponstay", &left);
+- if(*r == 'n')
+- b = put_string(b, "false", &left);
+- else
+- b = put_string(b, "true", &left);
+- b = put_bytes(b, "", 1, &left);
+- }
+- else if ( strncmp( r, "transloc", flen) == 0
+- || strncmp( r, "notransloc", flen) == 0)
+- {
+- ++items;
+- b = put_string(b, "transloc", &left);
+- if(*r == 'n')
+- b = put_string(b, "false", &left);
+- else
+- b = put_string(b, "true", &left);
+- b = put_bytes(b, "", 1, &left);
+- }
+- r= sep+1;
+- }
++ char buf[4096] = {0};
++ size_t left = sizeof(buf);
++ char *b = buf;
++ char *param, *r, *sep= "";
++ unsigned flen = 0;
++ unsigned char items = 0;
++
++ // header is done later
++ b += 6;
++ left -= 6;
++
++ param = get_param_value( server, "gametype", NULL);
++ if(param)
++ {
++ ++items;
++ b = put_string(b, "gametype", &left);
++ b = put_string(b, param, &left);
++ b = put_bytes(b, "", 1, &left);
++ }
++
++ param = get_param_value( server, "status", NULL);
++ r = param;
++ while ( param && sep )
++ {
++ sep= strchr( r, ':');
++ if ( sep )
++ flen= sep-r;
++ else
++ flen= strlen(r);
+
+- param = get_param_value( server, "mutator", NULL);
+- r = param;
+- sep = "";
+- while ( param && sep )
+- {
+- char neg = '\0';
+- unsigned char l;
+- sep= strchr( r, ':');
+- if ( sep )
+- flen= sep-r;
+- else
+- flen= strlen(r);
+-
+- if(*r == '-')
+- {
+- neg = '\x04';
+- ++r;
+- --flen;
++ if ( strncmp( r, "standard", flen) == 0
++ || strncmp( r, "nostandard", flen) == 0)
++ {
++ ++items;
++ b = put_string(b, "standard", &left);
++ if(*r == 'n')
++ b = put_string(b, "false", &left);
++ else
++ b = put_string(b, "true", &left);
++ b = put_bytes(b, "", 1, &left);
++ }
++ else if ( strncmp( r, "password", flen) == 0
++ || strncmp( r, "nopassword", flen) == 0)
++ {
++ ++items;
++ b = put_string(b, "password", &left);
++ if(*r == 'n')
++ b = put_string(b, "false", &left);
++ else
++ b = put_string(b, "true", &left);
++ b = put_bytes(b, "", 1, &left);
++ }
++ else if ( strncmp( r, "notempty", flen) == 0)
++ {
++ ++items;
++ b = put_string(b, "currentplayers", &left);
++ b = put_string(b, "0", &left);
++ b = put_bytes(b, "\x04", 1, &left);
++ }
++ else if ( strncmp( r, "notfull", flen) == 0)
++ {
++ ++items;
++ b = put_string(b, "freespace", &left);
++ b = put_string(b, "0", &left);
++ b = put_bytes(b, "\x04", 1, &left);
++ }
++ else if ( strncmp( r, "nobots", flen) == 0)
++ {
++ ++items;
++ b = put_string(b, "nobots", &left);
++ b = put_string(b, "true", &left);
++ b = put_bytes(b, "", 1, &left);
++ }
++ else if ( strncmp( r, "stats", flen) == 0
++ || strncmp( r, "nostats", flen) == 0)
++ {
++ ++items;
++ b = put_string(b, "stats", &left);
++ if(*r == 'n')
++ b = put_string(b, "false", &left);
++ else
++ b = put_string(b, "true", &left);
++ b = put_bytes(b, "", 1, &left);
++ }
++ else if ( strncmp( r, "weaponstay", flen) == 0
++ || strncmp( r, "noweaponstay", flen) == 0)
++ {
++ ++items;
++ b = put_string(b, "weaponstay", &left);
++ if(*r == 'n')
++ b = put_string(b, "false", &left);
++ else
++ b = put_string(b, "true", &left);
++ b = put_bytes(b, "", 1, &left);
++ }
++ else if ( strncmp( r, "transloc", flen) == 0
++ || strncmp( r, "notransloc", flen) == 0)
++ {
++ ++items;
++ b = put_string(b, "transloc", &left);
++ if(*r == 'n')
++ b = put_string(b, "false", &left);
++ else
++ b = put_string(b, "true", &left);
++ b = put_bytes(b, "", 1, &left);
++ }
++ r= sep+1;
+ }
+
+- if(!flen)
+- continue;
++ param = get_param_value( server, "mutator", NULL);
++ r = param;
++ sep = "";
++ while ( param && sep )
++ {
++ char neg = '\0';
++ unsigned char l;
++ sep= strchr( r, ':');
++ if ( sep )
++ flen= sep-r;
++ else
++ flen= strlen(r);
+
+- b = put_string(b, "mutator", &left);
+- l = flen+1;
+- b = put_bytes(b, (char*)&l, 1, &left);
+- b = put_bytes(b, r, flen, &left);
+- b = put_bytes(b, "", 1, &left);
+- b = put_bytes(b, &neg, 1, &left);
+- ++items;
+-
+- r= sep+1;
+- }
+-
+- if(!b)
+- {
+- debug(0, "Error: query buffer too small. Please file a bug report!");
+- return 0;
+- }
++ if(*r == '-')
++ {
++ neg = '\x04';
++ ++r;
++ --flen;
++ }
++
++ if(!flen)
++ continue;
++
++ b = put_string(b, "mutator", &left);
++ l = flen+1;
++ b = put_bytes(b, (char*)&l, 1, &left);
++ b = put_bytes(b, r, flen, &left);
++ b = put_bytes(b, "", 1, &left);
++ b = put_bytes(b, &neg, 1, &left);
++ ++items;
++
++ r= sep+1;
++ }
++
++ if(!b)
++ {
++ debug(0, "Error: query buffer too small. Please file a bug report!");
++ return 0;
++ }
+
+- put_long_little(b-buf-4, buf);
+- buf[5] = items;
++ put_long_little(b-buf-4, buf);
++ buf[5] = items;
+
+- return (qserver_send(server, buf, sizeof(buf)-left) > 0);
++ return (qserver_send(server, buf, sizeof(buf)-left) > 0);
+ }
+
+-void deal_with_ut2004master_packet(struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_ut2004master_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+- unsigned char* state = (unsigned char*)&server->master_query_tag[0];
++ unsigned char* state = (unsigned char*)&server->master_query_tag[0];
+
+- md5_state_t md5;
++ md5_state_t md5;
+
+- if(!pktlen)
+- {
+- ut2004_server_done(server);
+- goto cleanup_out;
+- }
++ if(!pktlen)
++ {
++ ut2004_server_done(server);
++ goto cleanup_out;
++ }
+
+- server->ping_total+= time_delta( &packet_recv_time, &server->packet_time1);
++ server->ping_total+= time_delta( &packet_recv_time, &server->packet_time1);
+
+- switch(*state)
+- {
++ switch(*state)
++ {
+ case STATE_CHALLENGE:
+- // ensure at least one byte challenge, fit into buffer,
+- // match challenge, null terminated
+- if( pktlen < 4 +1 +1 +1
++ // ensure at least one byte challenge, fit into buffer,
++ // match challenge, null terminated
++ if( pktlen < 4 +1 +1 +1
+ || pktlen > 4 +1 +8 +1
+ || rawpkt[pktlen-1] != '\0')
+- {
+- malformed_packet(server, "invalid challenge" );
+- goto cleanup_out;
+- }
+- else
+- {
+- char response[sizeof(challenge_response)];
+- char* challenge = rawpkt+5;
+- char sum[16];
+-
+- memcpy(response, challenge_response, sizeof(challenge_response));
+-
+- debug(2, "challenge: %s", challenge);
+-
+- md5_init(&md5);
+- md5_append(&md5, (unsigned char*)cdkey, CD_KEY_LENGTH);
+- md5_finish(&md5, (unsigned char*)sum);
+- bin2hex(sum, 16, response+RESPONSE_OFFSET_CDKEY);
+-
+- md5_init(&md5);
+- md5_append(&md5, (unsigned char*)cdkey, CD_KEY_LENGTH);
+- md5_append(&md5, (unsigned char*)challenge, strlen(challenge));
+- md5_finish(&md5, (unsigned char*)sum);
+- bin2hex(sum, 16, response+RESPONSE_OFFSET_CHALLENGE);
+-
+- qserver_send(server, response, sizeof(response));
+-
+- server->server_name = MASTER;
+-
+- *state = STATE_APPROVED;
+- }
+- break;
++ {
++ malformed_packet(server, "invalid challenge" );
++ goto cleanup_out;
++ }
++ else
++ {
++ char response[sizeof(challenge_response)];
++ char* challenge = rawpkt+5;
++ char sum[16];
++
++ memcpy(response, challenge_response, sizeof(challenge_response));
++
++ debug(2, "challenge: %s", challenge);
++
++ md5_init(&md5);
++ md5_append(&md5, (unsigned char*)cdkey, CD_KEY_LENGTH);
++ md5_finish(&md5, (unsigned char*)sum);
++ bin2hex(sum, 16, response+RESPONSE_OFFSET_CDKEY);
++
++ md5_init(&md5);
++ md5_append(&md5, (unsigned char*)cdkey, CD_KEY_LENGTH);
++ md5_append(&md5, (unsigned char*)challenge, strlen(challenge));
++ md5_finish(&md5, (unsigned char*)sum);
++ bin2hex(sum, 16, response+RESPONSE_OFFSET_CHALLENGE);
++
++ if(qserver_send(server, response, sizeof(response)))
++ goto cleanup_out;
++
++ server->server_name = MASTER;
++
++ *state = STATE_APPROVED;
++ }
++ break;
+
+ case STATE_APPROVED:
+
+- if(pktlen != sizeof(approved)
++ if(pktlen != sizeof(approved)
+ || 0 != memcmp(rawpkt, approved, pktlen))
+- {
+- malformed_packet(server, "CD key not approved" );
+- goto cleanup_out;
+- }
++ {
++ malformed_packet(server, "CD key not approved" );
++ goto cleanup_out;
++ }
++
++ debug(2, "got approval, sending verify");
+
+- debug(2, "got approval, sending verify");
++ if(qserver_send(server, approved_response, sizeof(approved_response)))
++ goto cleanup_out;
+
+- qserver_send(server, approved_response, sizeof(approved_response));
+- *state = STATE_VERIFIED;
++ *state = STATE_VERIFIED;
+
+- break;
++ break;
+
+ case STATE_VERIFIED:
+
+- if(pktlen != sizeof(verified)
++ if(pktlen != sizeof(verified)
+ || 0 != memcmp(rawpkt, verified, pktlen))
+- {
+- malformed_packet(server, "CD key not verified" );
+- goto cleanup_out;
+- }
++ {
++ malformed_packet(server, "CD key not verified" );
++ goto cleanup_out;
++ }
+
+- if(!ut2004_send_query(server))
+- goto cleanup_out;
++ debug(2, "CD key verified, sending query");
+
+- *state = STATE_LISTING;
++ if(ut2004_send_query(server))
++ goto cleanup_out;
++
++ *state = STATE_LISTING;
+
+- break;
++ break;
+ case STATE_LISTING:
+- // first packet. contains number of servers to expect
+- if(!server->saved_data.pkt_id)
+- {
++ // first packet. contains number of servers to expect
++ if(!server->saved_data.pkt_id)
++ {
+ /*
+- server->saved_data.data = malloc(pktlen);
+- memcpy(server->saved_data.data, rawpkt, pktlen);
+- server->saved_data.datalen = pktlen;
+- */
+- server->saved_data.pkt_id = 1;
++ server->saved_data.data = malloc(pktlen);
++ memcpy(server->saved_data.data, rawpkt, pktlen);
++ server->saved_data.datalen = pktlen;
++ */
++ server->saved_data.pkt_id = 1;
+
+- if(pktlen == 9)
+- {
+- unsigned num = swap_long_from_little(rawpkt+4);
+- debug(2, "expect %u servers", num);
++ if(pktlen == 9)
++ {
++ unsigned num = swap_long_from_little(rawpkt+4);
++ debug(2, "expect %u servers", num);
+ #if 1
+- if(num < 10000)
+- {
+- server->master_pkt_len = num*6;
+- server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len);
+- }
++ if(num < 10000)
++ {
++ server->master_pkt_len = num*6;
++ server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len);
++ }
+ #endif
+- }
+- }
+- else if(pktlen < 4)
+- {
+- malformed_packet(server, "packet too short");
+- goto cleanup_out;
+- }
+- else
+- {
+- char* p = rawpkt;
+- unsigned recordlen = 0;
+-
+- if(server->saved_data.next)
+- {
+- unsigned need = 0;
+- SavedData* data = server->saved_data.next;
+- // nasty, four bytes of record length are split up. since
+- // we alloc'ed at least four bytes we just copy the 4-x
+- // bytes to data->data
+- if(data->datalen < 4)
+- {
+- need = 4 - data->datalen;
+- debug(2, "need %d bytes more for recordlen", need);
+- if( need > pktlen)
+- {
+- // XXX ok, im lazy now. Stupid server can't even
+- // send four bytes in a row
+- malformed_packet(server, "chunk too small");
+- goto cleanup_out;
+ }
+- memcpy(data->data+data->datalen, p, need);
+- p += need;
+- data->datalen = 4;
+- }
+-
+- recordlen = swap_long_from_little(data->data);
+-
+- if(!recordlen || recordlen > MAX_LISTING_RECORD_LEN)
+- {
+- malformed_packet(server,
+- "record lengthx %x out of range, position %d", recordlen, (int)(p-rawpkt));
++ }
++ else if(pktlen < 4)
++ {
++ malformed_packet(server, "packet too short");
+ goto cleanup_out;
+- }
+-
+- need = 4+recordlen - data->datalen;
+-
+- debug(2, "recordlen: %d, saved: %d, pkglen: %d, needed: %d", recordlen, data->datalen, pktlen, need);
+-
+- if( need <= pktlen)
+- {
+- data->data = realloc(data->data, 4+recordlen);
+- memcpy(data->data + data->datalen, p, need);
+- ut2004_parse_record(server, data->data);
+- p += need;
+-
+- free(data->data);
+- free(data);
+- server->saved_data.next = NULL;
+- }
+ }
+-
+- while(!server->saved_data.next && p-rawpkt+4 < pktlen)
++ else
+ {
+- recordlen = swap_long_from_little(p);
++ char* p = rawpkt;
++ unsigned recordlen = 0;
+
+- // record too large
+- if(!recordlen || recordlen > MAX_LISTING_RECORD_LEN)
+- {
+- malformed_packet(server,
+- "record length %x out of range, position %d", recordlen, (int)(p-rawpkt));
+- goto cleanup_out;
+- }
+- // recordlen itself is four bytes
+- recordlen += 4;
+-
+- // record fully inside packet
+- if(p-rawpkt+recordlen <= pktlen)
+- {
+- ut2004_parse_record(server, p);
+- p += recordlen;
+- }
+- else
+- break;
+- }
+-
+- // record continues in next packet. save it.
+- if(p-rawpkt < pktlen)
+- {
+- SavedData* data = server->saved_data.next;
+- unsigned tosave = pktlen - (p-rawpkt);
+- if(!data)
+- {
+- data = malloc(sizeof(SavedData));
+- data->data = malloc(tosave<4?4:tosave); // alloc at least four bytes
+- data->datalen = tosave;
+- memcpy(data->data, p, data->datalen);
+- data->next = NULL;
+- server->saved_data.next = data;
+-
+- debug(1, "saved %d bytes", data->datalen );
+- }
+- else
+- {
+- data->data = realloc(data->data, data->datalen + tosave );
+- memcpy(data->data+data->datalen, p, tosave);
+- data->datalen += tosave;
+-
+- debug(1, "saved %d bytes (+)", data->datalen );
+- }
+- }
+- }
+- break;
+- }
++ if(server->saved_data.next)
++ {
++ unsigned need = 0;
++ SavedData* data = server->saved_data.next;
++ // nasty, four bytes of record length are split up. since
++ // we alloc'ed at least four bytes we just copy the 4-x
++ // bytes to data->data
++ if(data->datalen < 4)
++ {
++ need = 4 - data->datalen;
++ debug(2, "need %d bytes more for recordlen", need);
++ if( need > pktlen)
++ {
++ // XXX ok, im lazy now. Stupid server can't even
++ // send four bytes in a row
++ malformed_packet(server, "chunk too small");
++ goto cleanup_out;
++ }
++ memcpy(data->data+data->datalen, p, need);
++ p += need;
++ data->datalen = 4;
++ }
++
++ recordlen = swap_long_from_little(data->data);
++
++ if(!recordlen || recordlen > MAX_LISTING_RECORD_LEN)
++ {
++ malformed_packet(server,
++ "record lengthx %x out of range, position %d", recordlen, (int)(p-rawpkt));
++ goto cleanup_out;
++ }
++
++ need = 4+recordlen - data->datalen;
++
++ debug(2, "recordlen: %d, saved: %d, pkglen: %d, needed: %d", recordlen, data->datalen, pktlen, need);
++
++ if( need <= pktlen)
++ {
++ data->data = realloc(data->data, 4+recordlen);
++ memcpy(data->data + data->datalen, p, need);
++ ut2004_parse_record(server, data->data);
++ p += need;
++
++ free(data->data);
++ free(data);
++ server->saved_data.next = NULL;
++ }
++ }
+
+-#if 0 // would cause extremely short timeout
+- server->next_player_info= -1; // would clean up otherwise <- not true?
+-#endif
++ while(!server->saved_data.next && p-rawpkt+4 < pktlen)
++ {
++ recordlen = swap_long_from_little(p);
+
++ // record too large
++ if(!recordlen || recordlen > MAX_LISTING_RECORD_LEN)
++ {
++ malformed_packet(server,
++ "record length %x out of range, position %d", recordlen, (int)(p-rawpkt));
++ goto cleanup_out;
++ }
++ // recordlen itself is four bytes
++ recordlen += 4;
++
++ // record fully inside packet
++ if(p-rawpkt+recordlen <= pktlen)
++ {
++ ut2004_parse_record(server, p);
++ p += recordlen;
++ }
++ else
++ break;
++ }
+
+- debug(2, "%d servers total", server->n_servers/6);
++ // record continues in next packet. save it.
++ if(p-rawpkt < pktlen)
++ {
++ SavedData* data = server->saved_data.next;
++ unsigned tosave = pktlen - (p-rawpkt);
++ if(!data)
++ {
++ data = malloc(sizeof(SavedData));
++ data->data = malloc(tosave<4?4:tosave); // alloc at least four bytes
++ data->datalen = tosave;
++ memcpy(data->data, p, data->datalen);
++ data->next = NULL;
++ server->saved_data.next = data;
++
++ debug(1, "saved %d bytes", data->datalen );
++ }
++ else
++ {
++ data->data = realloc(data->data, data->datalen + tosave );
++ memcpy(data->data+data->datalen, p, tosave);
++ data->datalen += tosave;
+
+-#if 0 // harms now with new ping scheduling
+- server->retry1= 0;
+- cleanup_qserver( server, 0);
+- bind_sockets();
+-#endif
++ debug(1, "saved %d bytes (+)", data->datalen );
++ }
++ }
++ }
++ break;
++ }
++
++ debug(2, "%d servers total", server->n_servers/6);
+
+- return;
++ return INPROGRESS;
+
+ cleanup_out:
+- server->master_pkt_len = server->n_servers;
+- server->n_servers /= 6;
+- cleanup_qserver(server, 1);
+- return;
++ server->master_pkt_len = server->n_servers;
++ server->n_servers /= 6;
++ return DONE_FORCE;
+ }
+
+ static const char hexchar[] = "0123456789abcdef";
+
+ static void bin2hex(const char* in, size_t len, char* out)
+ {
+- char* o = out+len*2;
+- in += len;
+- do
+- {
+- *--o = hexchar[*--in&0x0F];
+- *--o = hexchar[(*in>>4)&0x0F];
+- } while(o != out);
++ char* o = out+len*2;
++ in += len;
++ do
++ {
++ *--o = hexchar[*--in&0x0F];
++ *--o = hexchar[(*in>>4)&0x0F];
++ } while(o != out);
+ }
+
+-// vim: sw=4 ts=8 noet
++// vim: sw=4 ts=4 noet
+diff -urP qstat-2.11/ut2004.h qstat2/ut2004.h
+--- qstat-2.11/ut2004.h 2004-10-17 15:41:53.000000000 -0500
++++ qstat2/ut2004.h 2009-07-02 06:11:41.032181000 -0400
+@@ -12,7 +12,7 @@
+
+ #include "qstat.h"
+
+-void send_ut2004master_request_packet(struct qserver *server);
+-void deal_with_ut2004master_packet(struct qserver *server, char *rawpkt, int pktlen);
++query_status_t send_ut2004master_request_packet(struct qserver *server);
++query_status_t deal_with_ut2004master_packet(struct qserver *server, char *rawpkt, int pktlen);
+
+ #endif
+diff -urP qstat-2.11/utils.c qstat2/utils.c
+--- qstat-2.11/utils.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/utils.c 2012-04-20 08:51:31.440424000 -0400
+@@ -0,0 +1,70 @@
++/*
++ * Utility Functions
++ *
++ */
++
++#include "utils.h"
++
++#if !HAVE_STRNSTR
++/*
++ * strnstr -
++ * Copyright (c) 2001 Mike Barcroft <mike at FreeBSD.org>
++ * Copyright (c) 1990, 1993
++ * The Regents of the University of California. All rights reserved.
++ *
++ * This code is derived from software contributed to Berkeley by
++ * Chris Torek.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * This product includes software developed by the University of
++ * California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include <string.h>
++
++char *
++qstat_strnstr(const char *s, const char *find, size_t slen)
++{
++ char c, sc;
++ size_t len;
++
++ if ((c = *find++) != '\0') {
++ len = strlen(find);
++ do {
++ do {
++ if (slen-- < 1 || (sc = *s++) == '\0')
++ return (NULL);
++ } while (sc != c);
++ if (len > slen)
++ return (NULL);
++ } while (strncmp(s, find, len) != 0);
++ s--;
++ }
++ return ((char *)s);
++}
++
++#endif /* HAVE_STRNSTR */
+diff -urP qstat-2.11/utils.h qstat2/utils.h
+--- qstat-2.11/utils.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/utils.h 2012-04-20 08:51:31.440424000 -0400
+@@ -0,0 +1,23 @@
++/*
++ * Utility Functions
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_UTILS_H
++#define QSTAT_UTILS_H
++
++// BSD has strnstr
++#if defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__OpenBSD__)
++#ifndef HAVE_STRNSTR
++#define HAVE_STRNSTR 1
++#endif /* HAVE_STRNSTR */
++#endif
++
++#if !HAVE_STRNSTR
++#include <string.h>
++char * qstat_strnstr(const char *s, const char *find, size_t slen);
++#define strnstr(s,find,slen) qstat_strnstr(s,find,slen)
++#endif
++
++#endif
+diff -urP qstat-2.11/ventrilo.c qstat2/ventrilo.c
+--- qstat-2.11/ventrilo.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ventrilo.c 2011-02-23 16:58:56.672759000 -0500
+@@ -0,0 +1,367 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Ventrilo query protocol
++ * Algorithm by Luigi Auriemma <www.aluigi.org>
++ * QStat port 2010 by Michael Willigens <michael at willigens.de>
++ * Fixes by Steven Hartland <steven.hartland at multiplay.co.uk>
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#define strtok_ret strtok_r
++#define VENTRILO_RAND random()
++#else
++#include <winsock.h>
++#define strtok_ret strtok_s
++#define VENTRILO_RAND rand()
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++
++int VENTRILO_COMMAND_GENERIC_INFO = 1;
++int VENTRILO_COMMAND_DETAILED_INFO = 2; // includes generic infos
++int VENTRILO_COMMAND_DIFFERENT = 7;
++
++typedef struct {
++ unsigned short pckkey; // key for decoding this header
++ unsigned short zero; // ever 0
++ unsigned short cmd; // command number: 1 generic info, 2 for details
++ unsigned short id; // packet ID used for tracking the replies
++ unsigned short totlen; // total data size (for data splitted in packets)
++ unsigned short len; // size of the data in this packet (max 492)
++ unsigned short totpck; // total amount of packets (max 32)
++ unsigned short pck; // current packet number
++ unsigned short datakey; // key for decoding the data
++ unsigned short crc; // checksum of the total plain-text data
++} ventrilo_udp_head;
++
++const static unsigned char ventrilo_udp_encdata_head[] =
++ "\x80\xe5\x0e\x38\xba\x63\x4c\x99\x88\x63\x4c\xd6\x54\xb8\x65\x7e"
++ "\xbf\x8a\xf0\x17\x8a\xaa\x4d\x0f\xb7\x23\x27\xf6\xeb\x12\xf8\xea"
++ "\x17\xb7\xcf\x52\x57\xcb\x51\xcf\x1b\x14\xfd\x6f\x84\x38\xb5\x24"
++ "\x11\xcf\x7a\x75\x7a\xbb\x78\x74\xdc\xbc\x42\xf0\x17\x3f\x5e\xeb"
++ "\x74\x77\x04\x4e\x8c\xaf\x23\xdc\x65\xdf\xa5\x65\xdd\x7d\xf4\x3c"
++ "\x4c\x95\xbd\xeb\x65\x1c\xf4\x24\x5d\x82\x18\xfb\x50\x86\xb8\x53"
++ "\xe0\x4e\x36\x96\x1f\xb7\xcb\xaa\xaf\xea\xcb\x20\x27\x30\x2a\xae"
++ "\xb9\x07\x40\xdf\x12\x75\xc9\x09\x82\x9c\x30\x80\x5d\x8f\x0d\x09"
++ "\xa1\x64\xec\x91\xd8\x8a\x50\x1f\x40\x5d\xf7\x08\x2a\xf8\x60\x62"
++ "\xa0\x4a\x8b\xba\x4a\x6d\x00\x0a\x93\x32\x12\xe5\x07\x01\x65\xf5"
++ "\xff\xe0\xae\xa7\x81\xd1\xba\x25\x62\x61\xb2\x85\xad\x7e\x9d\x3f"
++ "\x49\x89\x26\xe5\xd5\xac\x9f\x0e\xd7\x6e\x47\x94\x16\x84\xc8\xff"
++ "\x44\xea\x04\x40\xe0\x33\x11\xa3\x5b\x1e\x82\xff\x7a\x69\xe9\x2f"
++ "\xfb\xea\x9a\xc6\x7b\xdb\xb1\xff\x97\x76\x56\xf3\x52\xc2\x3f\x0f"
++ "\xb6\xac\x77\xc4\xbf\x59\x5e\x80\x74\xbb\xf2\xde\x57\x62\x4c\x1a"
++ "\xff\x95\x6d\xc7\x04\xa2\x3b\xc4\x1b\x72\xc7\x6c\x82\x60\xd1\x0d";
++
++const static unsigned char ventrilo_udp_encdata_data[] =
++ "\x82\x8b\x7f\x68\x90\xe0\x44\x09\x19\x3b\x8e\x5f\xc2\x82\x38\x23"
++ "\x6d\xdb\x62\x49\x52\x6e\x21\xdf\x51\x6c\x76\x37\x86\x50\x7d\x48"
++ "\x1f\x65\xe7\x52\x6a\x88\xaa\xc1\x32\x2f\xf7\x54\x4c\xaa\x6d\x7e"
++ "\x6d\xa9\x8c\x0d\x3f\xff\x6c\x09\xb3\xa5\xaf\xdf\x98\x02\xb4\xbe"
++ "\x6d\x69\x0d\x42\x73\xe4\x34\x50\x07\x30\x79\x41\x2f\x08\x3f\x42"
++ "\x73\xa7\x68\xfa\xee\x88\x0e\x6e\xa4\x70\x74\x22\x16\xae\x3c\x81"
++ "\x14\xa1\xda\x7f\xd3\x7c\x48\x7d\x3f\x46\xfb\x6d\x92\x25\x17\x36"
++ "\x26\xdb\xdf\x5a\x87\x91\x6f\xd6\xcd\xd4\xad\x4a\x29\xdd\x7d\x59"
++ "\xbd\x15\x34\x53\xb1\xd8\x50\x11\x83\x79\x66\x21\x9e\x87\x5b\x24"
++ "\x2f\x4f\xd7\x73\x34\xa2\xf7\x09\xd5\xd9\x42\x9d\xf8\x15\xdf\x0e"
++ "\x10\xcc\x05\x04\x35\x81\xb2\xd5\x7a\xd2\xa0\xa5\x7b\xb8\x75\xd2"
++ "\x35\x0b\x39\x8f\x1b\x44\x0e\xce\x66\x87\x1b\x64\xac\xe1\xca\x67"
++ "\xb4\xce\x33\xdb\x89\xfe\xd8\x8e\xcd\x58\x92\x41\x50\x40\xcb\x08"
++ "\xe1\x15\xee\xf4\x64\xfe\x1c\xee\x25\xe7\x21\xe6\x6c\xc6\xa6\x2e"
++ "\x52\x23\xa7\x20\xd2\xd7\x28\x07\x23\x14\x24\x3d\x45\xa5\xc7\x90"
++ "\xdb\x77\xdd\xea\x38\x59\x89\x32\xbc\x00\x3a\x6d\x61\x4e\xdb\x29";
++
++const static unsigned short ventrilo_crc_table[] =
++{
++ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
++ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
++ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
++ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
++ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
++ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
++ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
++ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
++ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
++ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
++ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
++ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
++ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
++ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
++ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
++ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
++ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
++ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
++ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
++ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
++ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
++ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
++ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
++ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
++ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
++ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
++ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
++ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
++ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
++ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
++ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
++ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
++};
++
++void ventrilo_udp_head_dec(unsigned char *data)
++{
++ int i;
++ unsigned short *p;
++ unsigned char a1;
++ unsigned char a2;
++
++ p = (unsigned short *)data;
++ data += 2;
++
++ *p = ntohs(*p);
++ a1 = *p;
++ if( ! a1 )
++ {
++ return;
++ }
++
++ a2 = *p >> 8;
++ for( i = 0; i < 18; i++ )
++ {
++ data[i] -= ventrilo_udp_encdata_head[a2] + (i % 5);
++ a2 += a1;
++ }
++
++ for( i = 0; i < 9; i++ )
++ {
++ p++;
++ *p = ntohs(*p);
++ }
++}
++
++void ventrilo_udp_head_enc(unsigned char *data)
++{
++ int i;
++ unsigned short *p;
++ unsigned char a1;
++ unsigned char a2;
++
++ p = (unsigned short *)data;
++ data += 2;
++
++ *p = (((VENTRILO_RAND * 0x343fd) + 0x269ec3) >> 16) & 0x7fff;
++ a1 = *p;
++ a2 = *p >> 8;
++ if( ! a2 )
++ {
++ a2 = 69;
++ *p |= (a2 << 8);
++ }
++
++ for( i = 0; i < 10; i++ )
++ {
++ *p = htons(*p);
++ p++;
++ }
++
++ for( i = 0; i < 18; i++ )
++ {
++ data[i] += ventrilo_udp_encdata_head[a2] + (i % 5);
++ a2 += a1;
++ }
++}
++
++void ventrilo_udp_data_dec(unsigned char *data, int len, unsigned short key)
++{
++ int i;
++ unsigned char a1;
++ unsigned char a2;
++
++ a1 = key;
++ if( ! a1 )
++ {
++ return;
++ }
++
++ a2 = key >> 8;
++ for( i = 0; i < len; i++ )
++ {
++ data[i] -= ventrilo_udp_encdata_data[a2] + (i % 72);
++ a2 += a1;
++ }
++}
++
++unsigned short ventrilo_udp_data_enc(unsigned char *data, int len)
++{
++ int i;
++ unsigned short key;
++ unsigned char a1;
++ unsigned char a2;
++
++ key = (((VENTRILO_RAND * 0x343fd) + 0x269ec3) >> 16) & 0x7fff;
++ a1 = key;
++ a2 = key >> 8;
++ if( ! a2 )
++ {
++ a2 = 1;
++ key |= (a2 << 8);
++ }
++
++ for( i = 0; i < len; i++ )
++ {
++ data[i] += ventrilo_udp_encdata_data[a2] + (i % 72);
++ a2 += a1;
++ }
++
++ return key;
++}
++
++
++
++unsigned short ventrilo_udp_crc(unsigned char *data, int len)
++{
++ unsigned short crc = 0;
++
++ while( len-- )
++ {
++ crc = ventrilo_crc_table[crc >> 8] ^ *data ^ (crc << 8);
++ data++;
++ }
++
++ return(crc);
++}
++
++int buildVentriloRequest(unsigned char *buf, int cmd, char *pass, int id)
++{
++ ventrilo_udp_head *stat = (ventrilo_udp_head *) buf;
++ unsigned char *data = buf + 20;
++
++ strncpy((char *) data, pass, 16);
++
++ stat->zero = 0;
++ stat->cmd = cmd;
++ stat->id = id;
++ stat->totlen = 16;
++ stat->len = 16;
++ stat->totpck = 1;
++ stat->pck = 0;
++ stat->crc = ventrilo_udp_crc(data, 16);
++ stat->datakey = ventrilo_udp_data_enc(data, 16);
++ ventrilo_udp_head_enc(buf);
++
++ return 20+16;
++}
++
++query_status_t send_ventrilo_request_packet( struct qserver *server )
++{
++ char buf[1500];
++ char *password = get_param_value( server, "password", "" );
++ int size = buildVentriloRequest((unsigned char *) buf, VENTRILO_COMMAND_DETAILED_INFO, password, server->challenge);
++ server->n_requests++;
++ gettimeofday( &server->packet_time1, NULL );
++
++ debug( 2, "send status request");
++
++ return send_packet( server, buf, size );
++}
++
++query_status_t deal_with_ventrilo_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *line, *last_line;
++
++ debug( 3, "deal_with_ventrilo_packet: state = %ld", server->challenge );
++
++ if( 20 > pktlen )
++ {
++ debug( 2, "wrong or incomplete packet received. retrying...");
++ return INPROGRESS;
++ }
++
++ /* add to the data previously saved to get a full packet*/
++ if ( ! server->combined )
++ {
++ ventrilo_udp_head *header = (ventrilo_udp_head *) rawpkt;
++ unsigned char *data = (unsigned char *) header+20;
++ ventrilo_udp_head_dec( (unsigned char*) header );
++ ventrilo_udp_data_dec(data, header->len, header->datakey);
++
++ if (header->id != server->challenge)
++ {
++ debug( 2, "ignoring unnecessary packet...");
++ return INPROGRESS;
++ }
++
++ debug( 3, "decoded ventrilo fragment: id:%i - len:%i - totlen:%i - totpck:%i - zero:%i - cmd:%i", header->id, header->len, header->totlen, header->totpck, header->zero, header->cmd);
++
++ add_packet(server, header->id, packet_count(server), header->totpck, header->len, (char *) data, 0);
++ return combine_packets( server );
++ }
++
++ debug( 4, "combined ventrilo packet: %s", rawpkt);
++
++ server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++
++ line = strtok_ret( rawpkt, "\n", &last_line );
++ debug( 3, "processing detailed response..." );
++ while (line != NULL)
++ {
++ debug( 4, "processing line: %s", line );
++ if ( 0 == strncmp( line, "NAME: ", 6 ) )
++ {
++ server->server_name = strdup( line + 6 );
++ }
++ else if ( 0 == strncmp( line, "MAXCLIENTS: ", 12 ) )
++ {
++ server->max_players = atoi( line + 12 );
++ }
++ else if ( 0 == strncmp( line, "CLIENTCOUNT: ", 13 ) )
++ {
++ server->num_players = atoi( line + 13 );
++ }
++ else if ( 0 == strncmp( line, "CLIENT: ", 8 ) )
++ {
++ // Client e.g.
++ // CLIENT: UID=406,ADMIN=0,CID=17,PHAN=0,PING=73,SEC=2245,NAME=Darrimu,COMM=
++ struct player *player = add_player( server, server->n_player_info );
++ char *last_info = NULL;
++ char *player_info = strtok_ret( line + 8, ",", &last_info );
++ while ( NULL != player_info )
++ {
++ debug( 5, "player info: %s", player_info );
++ if ( 0 == strncmp( player_info, "NAME=", 5 ) )
++ {
++ player->name = strdup( player_info + 5 );
++ }
++ else if ( strncmp( player_info, "PING=", 5 ) )
++ {
++ player->ping = atoi( player_info + 5 );
++ }
++ else if ( 0 == strncmp( player_info, "CID=", 4 ) )
++ {
++ player->team = atoi( player_info + 4 );
++ }
++ else if ( strncmp( player_info, "SEC=", 4 ) )
++ {
++ player->connect_time = atoi( player_info + 4 );
++ }
++ player_info = strtok_ret( NULL, ",", &last_info );
++ }
++ }
++
++ line = strtok_ret( NULL, "\n", &last_line );
++ }
++ server->map_name = strdup( "N/A" );
++
++ return DONE_FORCE;
++}
+diff -urP qstat-2.11/ventrilo.h qstat2/ventrilo.h
+--- qstat-2.11/ventrilo.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ventrilo.h 2011-02-23 16:58:56.672759000 -0500
+@@ -0,0 +1,21 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Ventrilo query protocol
++ * Algorithm by Luigi Auriemma <www.aluigi.org> (Reversing and first version in c)
++ * Copyright 2010 Michael Willigens <michael at willigens.de>
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_VENTRILO_H
++#define QSTAT_VENTRILO_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_ventrilo_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_ventrilo_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/wic.c qstat2/wic.c
+--- qstat-2.11/wic.c 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/wic.c 2009-09-14 19:22:08.993154000 -0400
+@@ -0,0 +1,191 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * World in Conflict Protocol
++ * Copyright 2007 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++
++query_status_t send_wic_request_packet( struct qserver *server )
++{
++ char buf[256];
++
++ int serverport = get_param_i_value( server, "port", 0 );
++ char *password = get_param_value( server, "password", "N/A" );
++ change_server_port( server, serverport, 1 );
++
++ if ( get_player_info )
++ {
++ server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY;
++ sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/listplayers\x0d\x0a/exit\x0d\x0a", password );
++ server->saved_data.pkt_index = 2;
++ }
++ else
++ {
++ server->flags |= TF_STATUS_QUERY;
++ sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/exit\x0d\x0a", password );
++ server->saved_data.pkt_index = 1;
++ }
++
++ return send_packet( server, buf, strlen( buf ) );
++}
++
++
++query_status_t deal_with_wic_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++ char *s, *end, *team = NULL;
++ int mode = server->n_servers, slot, score;
++ char name[256], role[256];
++
++ debug( 2, "processing n_requests %d, retry1 %d, n_retries %d, delta %d", server->n_requests, server->retry1, n_retries, time_delta( &packet_recv_time, &server->packet_time1 ) );
++
++ server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++ server->n_requests++;
++
++ if ( 0 == pktlen )
++ {
++ // Invalid password
++ return REQ_ERROR;
++ }
++
++ gettimeofday( &server->packet_time1, NULL);
++
++ rawpkt[pktlen]= '\0';
++ end = &rawpkt[pktlen];
++
++ s = rawpkt;
++
++ while ( NULL != s )
++ {
++ int len = strlen( s );
++ *(s + len - 2) = '\0'; // strip off \x0D\x0A
++ debug( 2, "Line[%d]: %s", mode, s );
++
++ if ( 0 == mode )
++ {
++ // Settings
++ // TODO: make parse safe
++ if ( 0 == strncmp( s, "Settings: ", 9 ) )
++ {
++ // Server Rule
++ char *key = s + 10;
++ char *value = strchr( key, ' ' );
++ *value = '\0';
++ value++;
++ debug( 2, "key: '%s' = '%s'", key, value );
++ if ( 0 == strcmp( "myGameName", key ) )
++ {
++ server->server_name = strdup( value );
++ }
++ else if ( 0 == strcmp( "myMapFilename", key ) )
++ {
++ server->map_name = strdup( value );
++ }
++ else if ( 0 == strcmp( "myMaxPlayers", key ) )
++ {
++ server->max_players = atoi( value );
++ }
++ else if ( 0 == strcmp( "myCurrentNumberOfPlayers", key ) )
++ {
++ server->num_players = atoi( value);
++ }
++ else
++ {
++ add_rule( server, key, value, NO_FLAGS );
++ }
++ }
++ else if ( 0 == strcmp( "Listing players", s ) )
++ {
++ // end of rules request
++ server->saved_data.pkt_index--;
++ mode++;
++ }
++ else if ( 0 == strcmp( "Exit confirmed.", s ) )
++ {
++ server->n_servers = mode;
++ return DONE_FORCE;
++ }
++ }
++ else if ( 1 == mode )
++ {
++ // Player info
++ if ( 0 == strncmp( s, "Team: ", 6 ) )
++ {
++ team = s + 6;
++ debug( 2, "Team: %s", team );
++ }
++ else if ( 4 == sscanf( s, "Slot: %d Role: %s Score: %d Name: %255[^\x0d\x0a]", &slot, role, &score, name ) )
++ {
++ // Player info
++ struct player *player = add_player( server, server->n_player_info );
++ if ( NULL != player )
++ {
++ player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++ player->name = strdup( name );
++ player->score = score;
++ player->team_name = team;
++ player->tribe_tag = strdup( role );
++ // Indicate if its a bot
++ player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0;
++ }
++ debug( 2, "player %d, role %s, score %d, name %s", slot, role, score, name );
++ }
++ else if ( 3 == sscanf( s, "Slot: %d Role: Score: %d Name: %255[^\x0d\x0a]", &slot, &score, name ) )
++ {
++ // Player info
++ struct player *player = add_player( server, server->n_player_info );
++ if ( NULL != player )
++ {
++ player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++ player->name = strdup( name );
++ player->score = score;
++ player->team_name = team;
++ // Indicate if its a bot
++ player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0;
++ }
++ debug( 2, "player %d, score %d, name %s", slot, score, name );
++ }
++ else if ( 0 == strcmp( "Exit confirmed.", s ) )
++ {
++ server->n_servers = mode;
++ return DONE_FORCE;
++ }
++ }
++
++ s += len;
++ if ( s + 1 < end )
++ {
++ s++; // next line
++ }
++ else
++ {
++ s = NULL;
++ }
++ }
++
++ server->n_servers = mode;
++
++ if ( 0 == server->saved_data.pkt_index )
++ {
++ server->map_name = strdup( "N/A" );
++ return DONE_FORCE;
++ }
++
++ return INPROGRESS;
++}
+diff -urP qstat-2.11/wic.h qstat2/wic.h
+--- qstat-2.11/wic.h 1969-12-31 19:00:00.000000000 -0500
++++ qstat2/wic.h 2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * World in Conflict Protocol
++ * Copyright 2007 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_WIC_H
++#define QSTAT_WIC_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_wic_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_wic_request_packet( struct qserver *server );
++
++#endif
++
diff --git a/qstat.spec b/qstat.spec
index 9b3d1cb..d1ae65e 100644
--- a/qstat.spec
+++ b/qstat.spec
@@ -1,14 +1,14 @@
Summary: Real-time Game Server Status for FPS game servers
Name: qstat
Version: 2.11
-Release: 15.20080912svn311%{?dist}
+Release: 16.20131212svn382%{?dist}
License: Artistic 2.0
Group: Amusements/Games
URL: http://sourceforge.net/projects/qstat/
Source: http://downloads.sourceforge.net/qstat/qstat-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-Patch0: qstat-2.11-20080912svn311.patch
-Patch1: qstat-2.11-format-security.patch
+Patch0: qstat-2.11-20131212svn382.patch
+BuildRequires: autoconf, automake, libtool
%description
QStat is a command-line program that gathers real-time statistics
@@ -18,7 +18,7 @@ person shooter variety (Quake, Half-Life, etc)
%prep
%setup -q
%patch0 -p1
-%patch1 -p1 -b .format-security
+autoreconf -ifv
%build
%configure
@@ -46,6 +46,9 @@ rm -rf %{buildroot}
%{_bindir}/quakestat
%changelog
+* Thu Dec 12 2013 Tom Callaway <spot at fedoraproject.org> - 2.11-16.20131212svn382
+- update to svn 382
+
* Thu Dec 12 2013 Tom Callaway <spot at fedoraproject.org> - 2.11-15.20080912svn311
- apply fixes for format-security issues
More information about the scm-commits
mailing list