#include "stdafx.h"
#include "Xfire_voicechat.h"

//konstruktor
Xfire_voicechat::Xfire_voicechat() {
	this->resetCurrentvoicestatus();
	ipport=NULL;
	tsrDLL=NULL;
	tsrGetServerInfo=NULL;
	pid=0;
}

//dekonstruktor
Xfire_voicechat::~Xfire_voicechat()
{
	//geladene tsr remote dll freigeben
	if (tsrDLL) {
		FreeLibrary(tsrDLL);
		tsrDLL=NULL;
	}
}

//init
void Xfire_voicechat::initVoicechat()
{
	//tsremotedll laden
	tsrDLL=this->loadTSR();
}

//pr�ft ob das paket schonmal versendet wurde, soll unn�tigen nwtraffic reduzieren, *�BERLEGUNG* ob wirklich notwendig
BOOL Xfire_voicechat::alreadySend(SendGameStatus2Packet* packet) {
	if (packet==NULL)
		return FALSE;
	
	if (packet->ip[3] != lastpacket.ip[3] ||
	   packet->ip[2] != lastpacket.ip[2] ||
	   packet->ip[1] != lastpacket.ip[1] ||
	   packet->ip[0] != lastpacket.ip[0] ||
	   packet->port != lastpacket.port) {
		   lastpacket=*packet;
		   return TRUE;
	}

	return FALSE;
}

//pr�ft nach laufenden voicechat anwendungen
BOOL Xfire_voicechat::checkVoicechat(SendGameStatus2Packet* packet) {
	//kein g�ltiger verweis?
	if (packet==NULL)
		return FALSE;
	//jeh nach letzten status handeln
	switch(currentvoice) {
		case XFIREVOICECHAT_TS2:
			if (checkforTS2(packet)) {
				return alreadySend(packet);
			}
			else
			{
				//kein ts2 mehr? dann paket restten
				resetSendGameStatus2Packet(packet);
				resetCurrentvoicestatus();
				//in db schreiben
				writeToDatabase(packet);
				return TRUE;
			}
			break;
		case XFIREVOICECHAT_TS3:
			if (checkforTS3(packet)) {
				return alreadySend(packet);
			}
			else
			{
				//kein ts3 mehr? dann paket restten
				resetSendGameStatus2Packet(packet);
				resetCurrentvoicestatus();
				//in db schreiben
				writeToDatabase(packet);
				return TRUE;
			}
			break;
		case XFIREVOICECHAT_MUMBLE:
			if (checkforMumble(packet)) {
				return alreadySend(packet);
			}
			else
			{
				//kein mumble mehr? dann paket restten
				resetSendGameStatus2Packet(packet);
				resetCurrentvoicestatus();
				//in db schreiben
				writeToDatabase(packet);
				return TRUE;
			}
			break;
		default:
			//pr�fe nach ts3
			if (checkforTS3(packet)) {
				return alreadySend(packet);
			}
			//pr�fe nach ts2
			else if (checkforTS2(packet)) {
				return alreadySend(packet);
			}
			//pr�fe nach mumble
			else if (checkforMumble(packet)) {
				return alreadySend(packet);
			}
			break;
	};

	return FALSE;
}

//setzte currentvoice auf 0 zur�ck, falls es einen disconnect gab
void Xfire_voicechat::resetCurrentvoicestatus() {
	currentvoice=XFIREVOICECHAT_NOVOICE;
	lastpacket.ip[3]=0;
	lastpacket.ip[2]=0;
	lastpacket.ip[1]=0;
	lastpacket.ip[0]=0;
	lastpacket.port=0;
	pid=0;
}

//resettet das packet auf 0
void Xfire_voicechat::resetSendGameStatus2Packet(SendGameStatus2Packet* packet) {
	if (packet==NULL)
		return;
	//voiceid
	packet->gameid=XFIREVOICECHAT_NOVOICE;
	//ip zuweisen
	packet->ip[3]=0;
	packet->ip[2]=0;
	packet->ip[1]=0;
	packet->ip[0]=0;
	//port zuweisen
	packet->port=0;
}

//schreibt derzetigen status in die mirandadb f�r variables usw
void Xfire_voicechat::writeToDatabase(SendGameStatus2Packet* packet) {
	//f�r sprintf
	char temp[32]="";

	if (packet==NULL || packet->gameid==XFIREVOICECHAT_NOVOICE) {
		//eintr�ge aus der db entfernen
		db_unset(NULL,protocolname, "VServerIP");
		db_unset(NULL,protocolname, "currentvoicename");
		//zur�ck
		return;
	}
	//ip speichern
	mir_snprintf(temp,32,"%d.%d.%d.%d:%d",(unsigned char)packet->ip[3],(unsigned char)packet->ip[2],(unsigned char)packet->ip[1],(unsigned char)packet->ip[0],packet->port);
	db_set_s(NULL, protocolname, "VServerIP", temp);
	//namen jeh nach id schreiben
	switch(packet->gameid) {
		case XFIREVOICECHAT_TS3:
			db_set_s(NULL, protocolname, "currentvoicename", "Teamspeak 3");
			break;
		case XFIREVOICECHAT_TS2:
			db_set_s(NULL, protocolname, "currentvoicename", "Teamspeak 2");
			break;
		case XFIREVOICECHAT_MUMBLE:
			db_set_s(NULL, protocolname, "currentvoicename", "Mumble");
			break;
		case XFIREVOICECHAT_VENTRILO:
			db_set_s(NULL, protocolname, "currentvoicename", "Ventrilo");
			break;
	};
}

//versucht die TSR zuladen
HMODULE Xfire_voicechat::loadTSR(char* path,BOOL nolocaltest) {
	TCHAR pathtotsr[MAX_PATH] = _T("");

	/*if (path)
		; was tun*/
	_tcscat_s(pathtotsr,MAX_PATH,_T("TSRemote.dll"));
	
	//versuche dll zuladen
	HMODULE tsrDLL = LoadLibrary(pathtotsr);
	//konnte nicht geladen werden 
	if (!tsrDLL)
	{
		XFireLog("TSRemote.dll load failed!");

		//bei keinem lokalen test abbruch
		if (nolocaltest) return NULL;

		//nochmal engl. lokal versuchen
		tsrDLL = LoadLibrary(_T("C:\\Program Files\\Teamspeak2_RC2\\client_sdk\\TSRemote.dll"));

		if (!tsrDLL) {
			XFireLog("TSRemote.dll load faild (using standard installationpath)!");
	
			//deutsches sys?
			tsrDLL = LoadLibrary(_T("C:\\Programme\\Teamspeak2_RC2\\client_sdk\\TSRemote.dll"));

			if (!tsrDLL)
				XFireLog("TSRemote.dll load failed (using standard installationpath2)!");
	
				//aufgeben
				return NULL;
		}
	}

	XFireLog("TSRemote.dll successfully loaded!");

	//getserverinfo funktion holen
	tsrGetServerInfo = (LPtsrGetServerInfo)GetProcAddress(tsrDLL,"tsrGetServerInfo");

	return tsrDLL;
}


//teamspeak 3 detection, ben�tigt ts3plugin
BOOL Xfire_voicechat::checkforTS3(SendGameStatus2Packet* packet) {
	ts3IPPORT* ipport=NULL;
	//kein g�ltiger verweis?
	if (packet==NULL)
		return FALSE;
	//existiert ein filemap?
	HANDLE hMapObject = OpenFileMappingA(FILE_MAP_READ, FALSE, "$ts3info4xfire$");
	//nicht gefunden, dann kein ts3
	if (hMapObject == NULL)
		return FALSE;
	//versuch ipport zubesorgen
	ipport = (ts3IPPORT *) MapViewOfFile(hMapObject, FILE_MAP_READ, 0, 0, sizeof(ts3IPPORT));
	//fehler beim zugriff auf filemap?
	if (ipport==NULL)
	{
		CloseHandle(hMapObject);
		return FALSE;
	}

	//wenn kein port, dann stimmt was mit der ip nicht, paket resetten
	if (ipport->port==0) {
		//packet resetten
		resetSendGameStatus2Packet(packet);
		//in db schreiben
		writeToDatabase(packet);
		return TRUE;
	}

	//voiceid zuweisen
	this->currentvoice=XFIREVOICECHAT_TS3;
	packet->gameid=XFIREVOICECHAT_TS3;
	//ip zuweisen
	packet->ip[3]=ipport->ip[3];
	packet->ip[2]=ipport->ip[2];
	packet->ip[1]=ipport->ip[1];
	packet->ip[0]=ipport->ip[0];
	//port zuweisen
	packet->port=ipport->port;
	//unmap, handle schlie�em
	UnmapViewOfFile(ipport);
	CloseHandle(hMapObject);
	//in db schreiben
	writeToDatabase(packet);
	//ts3 gefunden
	return TRUE; 
}

//teamspeak 2 detection mit hilfe der tsr
BOOL Xfire_voicechat::checkforTS2(SendGameStatus2Packet* packet) {
	TtsrServerInfo serverinfo={0};

	//get funktion ist nicht initialisiert
	if (this->tsrGetServerInfo==NULL || packet==NULL)
	{
		return FALSE;
	}

	//infos holen
	this->tsrGetServerInfo(&serverinfo);

	//auswerten wenn serverip gesetzt
	if (serverinfo.ServerIp[0]!=0)
	{
		char * pos=strrchr(serverinfo.ServerIp,':');
		if (pos==0)
		{
			return FALSE;
		}

		*pos=0;
		unsigned int ip=inet_addr(serverinfo.ServerIp);
		pos++;
		int port=atoi(pos);

		//port zuweisen
		packet->port=port;
		//ip zuweisen
		packet->ip[3]=LOBYTE(LOWORD(ip));
		packet->ip[2]=HIBYTE(LOWORD(ip));
		packet->ip[1]=LOBYTE(HIWORD(ip));
		packet->ip[0]=HIBYTE(HIWORD(ip));		
		//gameid/voice zuweisen
		packet->gameid=XFIREVOICECHAT_TS2;
		this->currentvoice=XFIREVOICECHAT_TS2;
		//ab in die db
		writeToDatabase(packet);

		return TRUE;
	}

	return FALSE;
}

//detection f�r mumble
BOOL Xfire_voicechat::checkforMumble(SendGameStatus2Packet* packet) {
	//kein g�ltiger verweis?
	if (packet==NULL)
		return FALSE;

	//g�ltige pid
	if (this->pid!=0 && !this->isValidPid(this->pid))
	{
		this->pid=0;
		return FALSE;
	}
	else {
		if (!this->getPidByProcessName(_T("mumble.exe"),&this->pid)) {
			return FALSE;
		}
	}

	DWORD size=0;
	MIB_TCPTABLE_OWNER_PID* ptab=NULL;
	//tcptabelle holen
	GetExtendedTcpTable(NULL, &size, FALSE, AF_INET, TCP_TABLE_OWNER_PID_CONNECTIONS, 0);
	//�berhaupt was drin?
	if (size) {
		ptab=(MIB_TCPTABLE_OWNER_PID*)malloc(size);
		//liste auslesen
		if (GetExtendedTcpTable(ptab, &size, FALSE, AF_INET, TCP_TABLE_OWNER_PID_CONNECTIONS, 0) == NO_ERROR)
		{
			for(unsigned int i=0;i<ptab->dwNumEntries;i++)
			{
				if (ptab->table[i].dwOwningPid==this->pid && ptab->table[i].dwLocalAddr!=ptab->table[i].dwRemoteAddr) //verbindung gefunden, hoffentlich
				{
					unsigned char*rip=(unsigned char*)&ptab->table[i].dwRemoteAddr;
					XFireLog("IP %x,%x",ptab->table[i].dwRemoteAddr,ptab->table[i].dwRemotePort);
					//ipzuweisen
					packet->ip[3]=rip[0];
					packet->ip[2]=rip[1];
					packet->ip[1]=rip[2];
					packet->ip[0]=rip[3];
					//portzuweisen
					packet->port=r(ptab->table[i].dwRemotePort);
					//mumble
					packet->gameid=XFIREVOICECHAT_MUMBLE;
					this->currentvoice=XFIREVOICECHAT_MUMBLE;
					//table wieder freigeben
					delete ptab;
					//mumble l�uft + ip gefunden also TRUE
					return TRUE;
				}
			}
		}
		delete ptab;
	}

	return FALSE;
}