// ****************************************************************** //
// *			 mIRC On-The-Fly Version Modifier v2.05				* //
// *																* //
// *			Copyright ©2000 danielson@centurytel.net			* //
// *				    All Rights Reserved							* //
// ****************************************************************** //

////////////
//Includes//
////////////

#include <windows.h>
#include <stdio.h>
#include <winsock.h>
#include <time.h>
#include "core.h"
#include "mOTFv.h"

///////////////
//Global Vars//
///////////////

BOOL Installed = 0; //Have we been loaded
HINSTANCE Self; //Our HINSTANCE

LINKNODE *OTFHead = NULL; //Head of our linklist
LINKNODE *OTFTail = NULL; //Tail of our linklist

//Holds Version info
VINFO vInfo;

//Was Marking Successful?
SOCKET Marked = 0;

////////
//Code//
////////

//Our Dll's Entry Point
int WINAPI DllMain(HINSTANCE hInstance,DWORD dwReason,LPVOID Reserved) {

	switch (dwReason) {

		//Dll is being loaded
		case DLL_PROCESS_ATTACH: {
			
			//Store our hInstance
			Self = hInstance;


			break;
		}

		//Dll unloading
		case DLL_PROCESS_DETACH: {

			//Clean up internals
			while (OTFHead)
				RemoveNode(OTFHead,&OTFHead,&OTFTail);

			//Clean up DateRape
			CleanUpMess();

			break;
		}
	}

	//Exit
	return 1;
}

//Our stayloaded function
void StayLoaded() {

	//We been loaded before?
	if (!Installed) {
		//Will hold our path
		char Path[MAX_PATH];

		//Locate ourself
		GetModuleFileName(Self,Path,MAX_PATH);

		//Double-load
		LoadLibrary(Path);

		//Installed = 1
		Installed = 1;
	}
}

//Loads the dll
int __stdcall Load(HWND mWnd,HWND aWnd,char *data,char *parms,BOOL Who,BOOL Cares) {

	//Already loaded
	if (Installed)
		strcpy(data,mOTFv_ERROR_1);
	else {

		//Create DateRape Session (Kill session on error)
		DSESSION *Session = DCreateSession(RESET_ON_FAIL);

		if (!Session) {
			//Unable to create session
			strcpy(data,mOTFv_ERROR_100);

			//Return
			return 3;
		}

		//////////////
		//Sink Hooks//
		//////////////

		//Hook into recv
		HookIntoFunction(Session,"recv",(PROC)recv_clone);


		//Hook into send
		HookIntoFunction(Session,"send",(PROC)send_clone);

		//Check for failure (since it's RESET_ON_FAIL, 
		//if either died, the session will fail)
		if (!IsSession(Session))
			strcpy(data,mOTFv_ERROR_101);
		else {

			//Everything checks out

			strcpy(data,mOTFv_GENERAL_OK);

			//Double load
			StayLoaded();
		}
	}

	return 3;
	//Exit
}


//Marks the socket
int __stdcall Mark(HWND mWnd,HWND aWnd,char *data,char *parms,BOOL Who,BOOL Cares) {

	//Not Loaded
	if (!Installed)
		strcpy(data,mOTFv_ERROR_2);
	else {

		//If already marked, undo it
		Marked = 0;

		//Will place in buffer, for mIRC to send down IRCD socket
		//Format 'MARK <hex val>'
		sprintf(data,"$iif($server != $null,.raw -q MARK %x%x)",Self,GetCurrentProcessId());


		//Tell mIRC to evaluate the buffer
		return 2;
	}
	//Already Marked


	return 3;
}

//Unloads the dll
int __stdcall Unload(HWND mWnd,HWND aWnd,char *data,char *parms,BOOL Who,BOOL Cares) {

	//Not Loaded
	if (!Installed)
		strcpy(data,mOTFv_ERROR_2);
	else {

		//Free it
		FreeLibrary(Self);

		//General OK
		strcpy(data,mOTFv_GENERAL_OK);
	}
	//Exit
	return 3;
}

//This is how the user will set their version reply
int __stdcall SetVersion(HWND mWnd,HWND aWnd,char *data,char *parms,BOOL Who,BOOL Cares) {
	if (!Installed)
		strcpy(data,mOTFv_ERROR_2);
	else {

		//Initialize
		memset(&vInfo,0,sizeof VINFO);

		//Copy Version info
		strcpy(vInfo.VERSION,data);

		//Set Length
		vInfo.length = strlen(data);

		//Set
		strcpy(data, ((Marked) ? (mOTFv_GENERAL_OK) : ("OK - mOTFv not Marked, you must Mark before the changes will take effect")));

	}

	//Exit
	return 3;
}

//Returns author information
int __stdcall DLLInfo(HWND mWnd,HWND aWnd,char *data,char *parms,BOOL Who,BOOL Cares) {

	//Copy Author info
	strcpy(data,AUTHORINFO);

	//Return it
	return 3;
}


///////////////////
//Clone Functions//
///////////////////

//recv clone
int __stdcall recv_clone(SOCKET s,char FAR *buf,int len, int flags) {

	//Socket matches marked
	if (s == Marked) {

		/////////////////////////////////////////////////////////
		//Iterate through requests, getting rid of expired ones//
		/////////////////////////////////////////////////////////


		//Will hold time
		time_t timet;

		//Get time
		time(&timet);

		//Get 1st one
		LINKNODE *Current = GetFirstNode(MOTFV_REQUEST,&OTFHead);

		//Cycle
		while (Current) {

			//Reference it
			VREQUEST *vq = (VREQUEST*)Current->pData;

			//Has it Expired?
			if ((timet - vq->time) > mOTFv_EXPIRE_TIME) {

				//Hold this temporarily
				LINKNODE *Temp = GetNextNode(Current,MOTFV_REQUEST);

				//Get rid of current
				RemoveNode(Current,&OTFHead,&OTFTail);

				//change it
				Current = Temp;

				//Cycle
				continue;
			}

			//Get the next one
			Current = GetNextNode(Current,MOTFV_REQUEST);
		}



		//Temp Buffer
		char *ubuf = new char[len+1];

		//Receive
		int length = recv(s,ubuf,len,flags);

		//Check
		if (length == SOCKET_ERROR) {
			delete ubuf;
			return length;
		}

		//Set the Null
		ubuf[length]='\0';

		///////////////
		//Parse Lines//
		///////////////

		//Holds each line
		char line[513];

		//Begin
		int count = 1;

		//While there are remaining lines
		while(GetLine(ubuf,line,count)) {

			//Next Line
			count++;

			char temp[61];
			//Parse Events

			//Parse the Event
			Gettok(line,temp,32,1,60);

			//Not a privmsg, ignore it
			if (stricmp(temp,"PRIVMSG"))
				continue;

			//Parse the String
			Gettok(line,temp,32,3,60);

			if (stricmp(temp,":\001VERSION\001"))
				continue;

			//Parse the Next
			Gettok(line,temp,32,4,60);

			if (temp[0] != 0)
				continue;

			///////////////////////
			//Version Intercepted//
			///////////////////////

			//Create the proper node//

			LINKNODE *Node = AllocateNode(MOTFV_REQUEST,&OTFHead,&OTFTail);

			//Allocate enough data for the struct
			Node->pData = new VREQUEST;

			//Initialize
			memset(Node->pData,0,sizeof VREQUEST);

			//Get The Nick/Address
			Gettok(line,temp,32,0,60);

			//Parse off the colon
			Gettok(temp,temp,58,1,60);

			//Parse off the addres
			Gettok(temp,temp,33,0,60);

			//Copy the nick
			strcpy(((VREQUEST*)Node->pData)->Nick,temp);

			//Set the time
			time(&(((VREQUEST*)Node->pData)->time));

		}


		//Copy
		memcpy(buf,ubuf,length);

		//Deallocate
		delete ubuf;

		//return
		return length;


	}

	return recv(s,buf,len,flags);
}

//send clone
int __stdcall send_clone(SOCKET s,const char FAR *buf,int len, int flags) {

	//Our temp buffer
	char *ubuf = new char[len+1];

	//Copy
	memcpy(ubuf,buf,len);

	//Add null
	ubuf[len]=0;

	//Check for Mark
	if (!Marked) {
		
		

		//Temp Buffer
		char tbuf[61];

		//Tokenize
		Gettok(ubuf,tbuf,32,0,60);

		if (!strcmp(tbuf,"MARK")) {

			//Tokenize 2nd parameter
			Gettok(ubuf,tbuf,32,1,60);

			//Cut off the LF
			GetLine(tbuf,tbuf,1);

			///////////////////////////////
			//Generate a simile parameter//
			///////////////////////////////

			//Will hold Simile data
			char temp[61];

			//Generate it
			sprintf(temp,"%x%x",Self,GetCurrentProcessId());

			//Compare
			if (!strcmp(tbuf,temp)) {
				//It was valid, Mark the socket, and block the packet from reaching
				//the server

				//Set this socket as marked
				Marked = s;

				//Deallocate ubuf
				delete ubuf;

				//return the length (stop the packet from going to the server)
				return len;

			}//if (!strcmp(tbuf,temp)) {


		} //if (!strcmp(tbuf,"MARK")) {


	}//if (!Marked) {

	else if ((Marked == s) && (vInfo.length != 0)) {

		/////////////////////////////////////////////////////////
		//Iterate through requests, getting rid of expired ones//
		/////////////////////////////////////////////////////////


		//Will hold time
		time_t timet;

		//Get time
		time(&timet);

		//Get 1st one
		LINKNODE *Current = GetFirstNode(MOTFV_REQUEST,&OTFHead);

		//Cycle
		while (Current) {

			//Reference it
			VREQUEST *vq = (VREQUEST*)Current->pData;

			//Has it Expired?
			if ((timet - vq->time) > mOTFv_EXPIRE_TIME) {

				//Hold this temporarily
				LINKNODE *Temp = GetNextNode(Current,MOTFV_REQUEST);

				//Get rid of current
				RemoveNode(Current,&OTFHead,&OTFTail);

				//change it
				Current = Temp;

				//Cycle
				continue;
			}

			//Get the next one
			Current = GetNextNode(Current,MOTFV_REQUEST);
		}



		//Parse off the LF
		GetLine(ubuf,ubuf,1);

		//Temp buf
		char tempb[61];

		//Parse outgoing method
		Gettok(ubuf,tempb,32,0,60);

		//Not Notice
		if (stricmp(tempb,"NOTICE")) {
			delete ubuf;
			return send(s,buf,len,flags);
		}

		//Parse text
		Gettok(ubuf,tempb,32,2,60);

		//Not a version reply
		if (stricmp(tempb,":\001VERSION")) {
			delete ubuf;
			return send(s,buf,len,flags);
		}

		
		///////////////////////////
		//Sending a Version Reply//
		///////////////////////////

		//Parse the nick
		Gettok(ubuf,tempb,32,1,60);

		//Iterate through list
		LINKNODE *request = GetFirstNode(MOTFV_REQUEST,&OTFHead);

		while (request) {
			//Reference it
			VREQUEST *vq = (VREQUEST*)request->pData;

			//Did the reply match any non-expired requests?
			if (!stricmp(vq->Nick,tempb)) {

				//Get rid of node
				RemoveNode(request,&OTFHead,&OTFTail);

				char *pack = new char[vInfo.length + 20 + strlen(tempb)+1];

				//Generate Packet
				sprintf(pack,"NOTICE %s :\001VERSION %s\001\n",tempb,vInfo.VERSION);

				//Send it on it's merry way
				send(s,pack,strlen(pack),NULL);

				//Deallocate
				delete ubuf;

				//Deallocate
				delete pack;

				//return original length
				return len;

			}

			request = GetNextNode(request,MOTFV_REQUEST);
		}

	}

	//Get rid of our temporary buffer
	delete ubuf;

	return send(s,buf,len,flags);
}

//////////////////////////////
//Node Deallocation Routines//
//////////////////////////////


void MOTFV_REQUEST(PDATA pData) {

	//Simple, just deallocate
	delete pData;
}

/////////////////////////
//Text Parsing Routines//
/////////////////////////

//Token Parser//

void Gettok(char *string,char *outstring,char sepchar,int whichTok,int maxlen) {
	int count = 0;
	int outputcount = 0;
	int tokencount = 0;
	while (string[count]) {
		if (string[count] == sepchar) {
			if (tokencount == whichTok)
				break;
			tokencount++;
		}
		else if (tokencount == whichTok) { 
			if (outputcount == maxlen)
				break;
			outstring[outputcount] = string[count];
			outputcount++;
		}
		count++;
	}
	outstring[outputcount] = '\0';
}

//Robust Line Parser//
bool GetLine(char *string,char *outstring,int whichline) {
	int line = 0;
	int marker = 0;
	int count = 0;
	int max = (strlen(string)+1);
	while (count < max) {
		////////
		if ((string[count] == 10) || (string[count] == 13) || (string[count] == 0)) {
			line++;
			if (line == whichline) { 
				if (!(count-marker)) { return 0; }
				strncpy(outstring,(string+marker),(count-marker));
				outstring[(count-marker)]=0;
				return 1; 
			}

			while ((string[count]==10) || (string[count]==13))
				count++;

			marker = count;
		}
		else 
			count++;

	}
	return 0;
}



//Output Log shit//