<commit_before>/*
 * qstat 2.8
 * by <NAME>
 *
 * Teamspeak 3 query protocol
 * Copyright 2009 <NAME>
 *
 * 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"

char *decode_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;
}

query_status_t send_ts3_request_packet( struct qserver *server )
{
	char buf[256];
	int serverport;

	debug( 3, "send_ts3_request_packet: state = %ld", server->challenge );

	switch ( server->challenge )
	{
	case 0:
		// Not seen a challenge yet, wait for it
		return INPROGRESS;

	case 1:
		// Select port
		serverport = get_param_i_value( server, "port", 0 ); 
		change_server_port( server, serverport, 1 );
		// NOTE: we use n_servers as an indeicated of how many responses we are expecting to get
		if ( get_player_info )
		{
			server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY;
			server->n_servers = 4;
		}
		else
		{
			server->flags |= TF_STATUS_QUERY;
			server->n_servers = 3;
		}
		sprintf( buf, "use port=%d\015\012", serverport );
		break;

	case 2:
		// Server Info
		sprintf( buf, "serverinfo\015\012" );
		break;

	case 3:
		// Player Info or Quit
		sprintf( buf, ( get_player_info ) ? "clientlist\015\012" : "quit\015\012" );
		break;

	case 4:
		// 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 ) );
}

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;
	char last_char;
	debug( 2, "processing..." );

	server->n_requests++;
	server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );

	if ( 0 == pktlen )
	{
		// Invalid password
		return REQ_ERROR;
	}

	last_char = rawpkt[pktlen-1];
	rawpkt[pktlen-1] = '\0';
	end = &rawpkt[pktlen-1];
	s = rawpkt;

	debug( 3, "packet: combined = %d, challenge = %ld, n_servers = %d", server->combined, server->challenge, server->n_servers );
	if ( ! server->combined )
	{
		// First time packet
		gettimeofday( &server->packet_time1, NULL );

		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;
			}
		}
<commit_msg>Fixed ping measurement for TeamSpeak 3<commit_after>/*
 * qstat 2.8
 * by <NAME>
 *
 * Teamspeak 3 query protocol
 * Copyright 2009 <NAME>
 *
 * 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"

char *decode_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;
}

query_status_t send_ts3_request_packet( struct qserver *server )
{
	char buf[256];
	int serverport;

	debug( 3, "send_ts3_request_packet: state = %ld", server->challenge );

	switch ( server->challenge )
	{
	case 0:
		// Not seen a challenge yet, wait for it
		gettimeofday( &server->packet_time1, NULL);
		return INPROGRESS;

	case 1:
		// Select port
		serverport = get_param_i_value( server, "port", 0 ); 
		change_server_port( server, serverport, 1 );
		// NOTE: we use n_servers as an indeicated of how many responses we are expecting to get
		if ( get_player_info )
		{
			server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY;
			server->n_servers = 4;
		}
		else
		{
			server->flags |= TF_STATUS_QUERY;
			server->n_servers = 3;
		}
		sprintf( buf, "use port=%d\015\012", serverport );
		break;

	case 2:
		// Server Info
		sprintf( buf, "serverinfo\015\012" );
		break;

	case 3:
		// Player Info or Quit
		sprintf( buf, ( get_player_info ) ? "clientlist\015\012" : "quit\015\012" );
		break;

	case 4:
		// 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 ) );
}

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;
	char last_char;
	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;

	debug( 3, "packet: combined = %d, challenge = %ld, n_servers = %d", server->combined, server->challenge, server->n_servers );
	if ( ! server->combined )
	{
		server->retry1 = n_retries;
		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
		server->n_requests++; // Not quite right but gives a good estimate

		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;
			}
		}

{'fraction_non_alphanumeric': 0.16041455446659908, 'fraction_numerical': 0.026360256843528218, 'mean_word_length': 2.98652896273013, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2520406', 'n_tokens_mistral': 3854, 'n_tokens_neox': 3405, 'n_words': 942}