//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#include "StdAfx.h"
#include "updownclient.h"
#include "emule.h"
#include "uploadqueue.h"
#include "Clientlist.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


//	members of CUpDownClient
//	which are used by down and uploading functions 

CUpDownClient::CUpDownClient(CClientReqSocket* sender){
	socket = sender;
	reqfile = 0;
	Init();
}

CUpDownClient::CUpDownClient(uint16 in_port, uint32 in_userid,uint32 in_serverip, uint16 in_serverport,CPartFile* in_reqfile){
	socket = 0;
	Init();
	m_nUserID = in_userid;
	m_nUserPort = in_port;
	sourcesslot=m_nUserID%SOURCESSLOTS;
	if (!HasLowID())
		sprintf(m_szFullUserIP,"%i.%i.%i.%i",(uint8)m_nUserID,(uint8)(m_nUserID>>8),(uint8)(m_nUserID>>16),(uint8)(m_nUserID>>24));
	m_dwServerIP = in_serverip;
	m_nServerPort = in_serverport;
	reqfile = in_reqfile;
	ReGetClientSoft();
}

void CUpDownClient::Init(){
	memset(m_szFullUserIP,0,21);
	credits = 0;
	sumavgDDR = 0; // By BadWolf - Accurate Speed Measurement
	sumavgUDR = 0; // by BadWolf - Accurate Speed Measurement
	AddNextConnect = false;  // VQB Fix for LowID slots only on connection
	m_nAvDownDatarate = 0;
	m_nAvUpDatarate = 0;
	m_nChatstate = MS_NONE;
	m_cShowDR = 0;
	m_nUDPPort = 0;
	m_cFailed = 0;
	m_dwBanTime = 0;
	m_nMaxSendAllowed = 0;
	m_nTransferedUp = 0;
	m_cSendblock = 0;
	m_cAsked = 0;
	m_cDownAsked = 0;
	dataratems = 0;
	m_nUpDatarate = 0;
	m_pszUsername = 0;
	m_dwUserIP = 0;
	m_nUserID = 0;
	m_nServerPort = 0;
	m_bBanned = false;
    m_iFileListRequested = 0;
	m_dwLastUpRequest = 0;
	m_bEmuleProtocol = false;
	usedcompressiondown = false;
	m_bUsedComprUp = false;
	m_bCompleteSource = false;
	m_bFriendSlot = false;
	m_bCommentDirty = false;
	m_bReaskPending = false;
	m_bUDPPending = false;
	m_byEmuleVersion = 0;
	m_nUserPort = 0;
	m_nPartCount = 0;
	m_nUpPartCount = 0;
	m_abyPartStatus = 0;
	m_abyUpPartStatus = 0;
	m_dwLastAskedTime = 0;
	m_nDownloadState = DS_NONE;
	m_pszClientFilename = 0;
	m_dwUploadTime = 0;
	m_nTransferedDown = 0;
	m_nDownDatarate = 0;
	m_nDownDataRateMS = 0;
	m_nUploadState = US_NONE;
	m_dwLastBlockReceived = 0;
	m_byEmuleVersion = 0;
	m_byDataCompVer = 0;
	m_byUDPVer = 0;
	m_bySourceExchangeVer = 0;
	m_byAcceptCommentVer = 0;
	m_byExtendedRequestsVer = 0;
	m_nRemoteQueueRank = 0;
	m_dwLastSourceRequest = 0;
	m_dwLastSourceAnswer = 0;
	m_byCompatibleClient = 0;
	m_bIsHybrid = false;
	m_bIsML=false;
	m_Friend = NULL;
	m_iRate=0;
	m_bMsgFiltered=false;
	m_strComment="";
	m_nCurSessionUp = 0;
	m_nSumForAvgDownDataRate = 0;
	m_nSumForAvgUpDataRate = 0;
	m_clientSoft=SO_UNKNOWN;
	m_bRemoteQueueFull = false;
	md4clr(m_achUserHash);
	SetWaitStartTime();
	if (socket){
		SOCKADDR_IN sockAddr;
		memset(&sockAddr, 0, sizeof(sockAddr));
		uint32 nSockAddrLen = sizeof(sockAddr);
		socket->GetPeerName((SOCKADDR*)&sockAddr,(int*)&nSockAddrLen);
		m_dwUserIP = sockAddr.sin_addr.S_un.S_addr;
		strcpy(m_szFullUserIP,inet_ntoa(sockAddr.sin_addr));
	}
	sourcesslot=0;
	m_fHashsetRequesting = 0;
	m_fSharedDirectories = 0;

    m_random_update_wait = (uint32)(rand()/(RAND_MAX/1000));

    m_bHasPriorPart = false;
    m_iPriorPartNumber = 0;
}

CUpDownClient::~CUpDownClient(){
	//Beep(400,5);
	theApp.clientlist->RemoveClient(this);
	if (m_Friend){
		m_Friend->SetLinkedClient(NULL);
		theApp.friendlist->RefreshFriend(m_Friend);
		m_Friend = NULL;
	}
	if (m_pszClientFilename)
		delete[] m_pszClientFilename;
	if (socket){
		socket->client = 0;
		socket->Safe_Delete();
	}
	if (m_pszUsername)
		delete[] m_pszUsername;
	if (m_abyPartStatus)
		delete[] m_abyPartStatus;
	if (m_abyUpPartStatus)
		delete[] m_abyUpPartStatus;
	ClearUploadBlockRequests();

	for (POSITION pos = m_DownloadBlocks_list.GetHeadPosition();pos != 0;m_DownloadBlocks_list.GetNext(pos))
		delete m_DownloadBlocks_list.GetAt(pos);
	m_DownloadBlocks_list.RemoveAll();
	for (POSITION pos = m_RequestedFiles_list.GetHeadPosition();pos != 0;m_RequestedFiles_list.GetNext(pos))
		delete m_RequestedFiles_list.GetAt(pos);
	m_RequestedFiles_list.RemoveAll();
	for (POSITION pos = m_PendingBlocks_list.GetHeadPosition();pos != 0;m_PendingBlocks_list.GetNext(pos)){
		Pending_Block_Struct *pending = m_PendingBlocks_list.GetAt(pos);
		delete pending->block;
		// Not always allocated
		if (pending->zStream){
			inflateEnd(pending->zStream);
			delete pending->zStream;
		}
		delete pending;
	}

	if (m_iRate>0 || m_strComment.GetLength()>0) {
		m_iRate=0; m_strComment="";
		reqfile->UpdateFileRatingCommentAvail();
	}

	m_PendingBlocks_list.RemoveAll();
	m_AvarageUDR_list.RemoveAll();
	m_AvarageDDR_list.RemoveAll();
	DEBUG_ONLY (theApp.listensocket->Debug_ClientDeleted(this));
	this->SetUploadFileID(NULL);
}

void CUpDownClient::ProcessHelloPacket(char* pachPacket, uint32 nSize){
	CSafeMemFile data((BYTE*)pachPacket,nSize);
	uint8 hashsize;
	data.Read(&hashsize,1);
	ProcessHelloTypePacket(&data);
}

void CUpDownClient::ProcessHelloAnswer(char* pachPacket, uint32 nSize){
	CSafeMemFile data((BYTE*)pachPacket,nSize);
	ProcessHelloTypePacket(&data);
}

void CUpDownClient::ProcessHelloTypePacket(CSafeMemFile* data){
	
	m_bIsHybrid = false;
	m_bIsML = false;
	data->Read(&m_achUserHash,16);
	uint32 nUserID;
	data->Read(&nUserID,4);
	if (!m_nUserID)
		m_nUserID = nUserID;
	data->Read(&m_nUserPort,2); // hmm clientport is sent twice - why?
	uint32	tagcount;
	data->Read(&tagcount,4);
	for (uint32 i = 0;i < tagcount; i++){
		CTag temptag(data);
		switch(temptag.tag.specialtag){
			case CT_NAME:
				if (m_pszUsername){
					delete[] m_pszUsername;
					m_pszUsername = NULL; // needed, in case 'nstrdup' fires an exception!!
				}
				if( temptag.tag.stringvalue )
					m_pszUsername = nstrdup(temptag.tag.stringvalue);
				break;
			case CT_VERSION:
				m_nClientVersion = temptag.tag.intvalue;
				break;
			case CT_PORT:
				m_nUserPort = temptag.tag.intvalue;
				break;
		}
	}
	data->Read(&m_dwServerIP,4);
	data->Read(&m_nServerPort,2);
	// Hybrid now has an extra uint32.. What is it for?
	// Also, many clients seem to send an extra 6? These are not eDonkeys or Hybrids..
	if ( data->GetLength() - data->GetPosition() == 4 ){
		uint32 test;
		data->Read(&test,4);
		if (test=='KDLM') 
			m_bIsML=true;
		else{
			m_bIsHybrid = true;
			m_fSharedDirectories = 1;
		}
	}
	if( m_nClientVersion > 10000 && m_nClientVersion < 100000 )
		m_nClientVersion = m_nClientVersion - (m_nClientVersion/10000)*10000;
	if( m_nClientVersion > 1000 )
		m_nClientVersion = m_nClientVersion - (m_nClientVersion/1000)*1000;
	if( m_nClientVersion < 100 )
		m_nClientVersion *= 10;
	// tecxx 1609 2002 - add client's servet to serverlist (Moved to uploadqueue.cpp)

	SOCKADDR_IN sockAddr;
	memset(&sockAddr, 0, sizeof(sockAddr));
	uint32 nSockAddrLen = sizeof(sockAddr);
	socket->GetPeerName((SOCKADDR*)&sockAddr,(int*)&nSockAddrLen);
	m_dwUserIP = sockAddr.sin_addr.S_un.S_addr;
	strcpy(m_szFullUserIP,inet_ntoa(sockAddr.sin_addr));

	// get client credits
	// key, 255.255.0.0 subnetmask

	if (theApp.glob_prefs->AddServersFromClient()){
		in_addr addhost;
		addhost.S_un.S_addr = m_dwServerIP;
		CServer* addsrv = new CServer(m_nServerPort, inet_ntoa(addhost));
		addsrv->SetListName(addsrv->GetAddress());

		if (!theApp.emuledlg->serverwnd.serverlistctrl.AddServer(addsrv, true))
			delete addsrv;
		/*else
			theApp.emuledlg->AddLogLine(false,"Added new server: %s:%d", srv->GetFullIP(), srv->GetPort());*/
	}
	if(!HasLowID() && m_nUserID != m_dwUserIP)
		m_nUserID = m_dwUserIP;

	uchar key[16];
	md4cpy(key,m_achUserHash);
	credits = theApp.clientcredits->GetCredit(key);
	if ((m_Friend = theApp.friendlist->SearchFriend(key, m_dwUserIP, m_nUserPort)) != NULL){
		// Link the friend to that client
		if (m_Friend->GetLinkedClient()){
			if (m_Friend->GetLinkedClient() != this){
				bool bFriendSlot = m_Friend->GetLinkedClient()->GetFriendSlot();
				// avoid that an unwanted client instance keeps a friend slot
				//m_Friend->GetLinkedClient()->SetFriendSlot(false);
				m_Friend->GetLinkedClient()->m_Friend = NULL;
				m_Friend->SetLinkedClient(this);
				// move an assigned friend slot between different client instances which are/were also friends
				//m_Friend->GetLinkedClient()->SetFriendSlot(bFriendSlot);
			}
		}
		else
			m_Friend->SetLinkedClient(this);
		md4cpy(m_Friend->m_abyUserhash,GetUserHash());
		m_Friend->m_dwHasHash = md4cmp(m_Friend->m_abyUserhash, CFriend::sm_abyNullHash) ? 1 : 0;
		m_Friend->m_strName = m_pszUsername;
		m_Friend->m_dwLastUsedIP = m_dwUserIP;
		m_Friend->m_nLastUsedPort = m_nUserPort;
		m_Friend->m_dwLastSeen = time(NULL);
		theApp.friendlist->RefreshFriend(m_Friend);
	}
	else{
		// avoid that an unwanted client instance keeps a friend slot
		SetFriendSlot(false);
	}
	ReGetClientSoft();
}

void CUpDownClient::SendHelloPacket(){
	// if IP is filtered, dont greet him but disconnect...
	if (socket){
		SOCKADDR_IN sockAddr;
		memset(&sockAddr, 0, sizeof(sockAddr));
		uint32 nSockAddrLen = sizeof(sockAddr);
		socket->GetPeerName((SOCKADDR*)&sockAddr,(int*)&nSockAddrLen);
		if ( theApp.ipfilter->IsFiltered(sockAddr.sin_addr.S_un.S_addr)) {
			theApp.emuledlg->AddDebugLogLine(true,GetResString(IDS_IPFILTERED),GetFullIP(),theApp.ipfilter->GetLastHit());
			Disconnected();
			theApp.stat_filteredclients++;
			return;
		}
	}

	CSafeMemFile data(128);
	uint8 hashsize = 16;
	data.Write(&hashsize,1);
	SendHelloTypePacket(&data);
	Packet* packet = new Packet(&data);
	packet->opcode = OP_HELLO;
	if (socket){
		theApp.uploadqueue->AddUpDataOverheadOther(packet->size);
		socket->SendPacket(packet,true);
	}
}

void CUpDownClient::SendMuleInfoPacket(bool bAnswer){
	CSafeMemFile data(128);
	uint8 version = CURRENT_VERSION_SHORT;
	data.Write(&version,1);
	uint8 protversion = EMULE_PROTOCOL;
	data.Write(&protversion,1);
	uint32 tagcount = 6;
	data.Write(&tagcount,4);
	CTag tag(ET_COMPRESSION,1);
	tag.WriteTagToFile(&data);
	CTag tag2(ET_UDPVER,2);
	tag2.WriteTagToFile(&data);
	CTag tag3(ET_UDPPORT,theApp.glob_prefs->GetUDPPort());
	tag3.WriteTagToFile(&data);
	CTag tag4(ET_SOURCEEXCHANGE,2);
	tag4.WriteTagToFile(&data);
	CTag tag5(ET_COMMENTS,1);
	tag5.WriteTagToFile(&data);
	CTag tag6(ET_EXTENDEDREQUEST,1);
	tag6.WriteTagToFile(&data);
	Packet* packet = new Packet(&data,OP_EMULEPROT);
	if (!bAnswer)
		packet->opcode = OP_EMULEINFO;
	else
		packet->opcode = OP_EMULEINFOANSWER;
	if (socket){
		theApp.uploadqueue->AddUpDataOverheadOther(packet->size);
		socket->SendPacket(packet,true,true);
	} else
		delete packet;
}

void CUpDownClient::ProcessMuleInfoPacket(char* pachPacket, uint32 nSize){
	CSafeMemFile data((BYTE*)pachPacket,nSize);
	m_byCompatibleClient = 0;
	//The version number part of this packet will soon be useless since it is only able to go to v.99.
	//Why the version is a uint8 and why it was not done as a tag like the eDonkey hello packet is not known..
	//Therefore, sooner or later, we are going to have to switch over to using the eDonkey hello packet to set the version.
	//No sense making a third value sent for versions..
	data.Read(&m_byEmuleVersion,1);
	if( m_byEmuleVersion == 0x2B )
		m_byEmuleVersion = 0x22;
	uint8 protversion;
	data.Read(&protversion,1);

	//implicitly supported options by older clients
	if (protversion == EMULE_PROTOCOL) {
		//in the future do not use version to guess about new features

		if(m_byEmuleVersion < 0x25 && m_byEmuleVersion > 0x22)
			m_byUDPVer = 1;

		if(m_byEmuleVersion < 0x25 && m_byEmuleVersion > 0x21)
			m_bySourceExchangeVer = 1;

		if(m_byEmuleVersion == 0x24)
			m_byAcceptCommentVer = 1;

		// Shared directories are requested from eMule 0.28+ because eMule 0.27 has a bug in 
		// the OP_ASKSHAREDFILESDIR handler, which does not return the shared files for a 
		// directory which has a trailing backslash.
		if(m_byEmuleVersion >= 0x28 && !m_bIsML) // MLdonkey currently does not support shared directories
			m_fSharedDirectories = 1;

	} else {
		return;
	}
	m_bEmuleProtocol = true;

	uint32 tagcount;
	data.Read(&tagcount,4);
	for (uint32 i = 0;i < tagcount; i++){
		CTag temptag(&data);
		switch(temptag.tag.specialtag){
			case ET_COMPRESSION:
				m_byDataCompVer = temptag.tag.intvalue;
				break;
			case ET_UDPPORT:
				m_nUDPPort = temptag.tag.intvalue;
				break;
			case ET_UDPVER:
				m_byUDPVer = temptag.tag.intvalue;
				break;
			case ET_SOURCEEXCHANGE:
				m_bySourceExchangeVer = temptag.tag.intvalue;
				break;
			case ET_COMMENTS:
				m_byAcceptCommentVer = temptag.tag.intvalue;
				break;
			case ET_EXTENDEDREQUEST:
				m_byExtendedRequestsVer = temptag.tag.intvalue;
				break;
			case ET_COMPATIBLECLIENT:
				m_byCompatibleClient = temptag.tag.intvalue;
				break;
		}
	}
	if( m_byDataCompVer == 0 ){
		m_bySourceExchangeVer = 0;
		m_byExtendedRequestsVer = 0;
		m_byAcceptCommentVer = 0;
		m_nUDPPort = 0;
	}
	ReGetClientSoft();
}

void CUpDownClient::SendHelloAnswer(){
	CSafeMemFile data(128);
	SendHelloTypePacket(&data);
	Packet* packet = new Packet(&data);
	packet->opcode = OP_HELLOANSWER;
	if (socket){
		theApp.uploadqueue->AddUpDataOverheadOther(packet->size);
		socket->SendPacket(packet,true);
	}
}

void CUpDownClient::SendHelloTypePacket(CMemFile* data){
	data->Write(theApp.glob_prefs->GetUserHash(),16);
	uint32 clientid = theApp.serverconnect->GetClientID();
	data->Write(&clientid,4);
	uint16 nPort = theApp.glob_prefs->GetPort();
	data->Write(&nPort,2);
	uint32 tagcount = 2;
	data->Write(&tagcount,4);
	CTag tagName(CT_NAME,theApp.glob_prefs->GetUserNick());
	tagName.WriteTagToFile(data);
	CTag tagVersion(CT_VERSION,EDONKEYVERSION);
	tagVersion.WriteTagToFile(data);
	uint32 dwIP;
	if (theApp.serverconnect->IsConnected()){
		dwIP = theApp.serverconnect->GetCurrentServer()->GetIP();
		nPort = theApp.serverconnect->GetCurrentServer()->GetPort();
	}
	else{
		nPort = 0;
		dwIP = 0;
	}
	data->Write(&dwIP,4);
	data->Write(&nPort,2);
}

void CUpDownClient::ProcessMuleCommentPacket(char* pachPacket, uint32 nSize){
	if( reqfile ){
		if( reqfile->IsPartFile()){
			int length;
			if (nSize>(sizeof(m_iRate)+sizeof(length)-1)){
				CSafeMemFile data((BYTE*)pachPacket,nSize);
				data.Read(&m_iRate,sizeof(m_iRate));
				data.Read(&length,sizeof(length));
				reqfile->SetHasRating(true);
				theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_RATINGRECV),m_pszClientFilename,m_iRate);
				if ( length > data.GetLength() - data.GetPosition() ){
					length = data.GetLength() - data.GetPosition();
				}
				if (length>50) length=50;
				if (length>0){
					char* desc=new char[length+1];
					memset(desc,0,length+1);
					data.Read(desc,length);
					theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_DESCRIPTIONRECV), m_pszClientFilename, desc);
					m_strComment.Format("%s",desc);
					reqfile->SetHasComment(true);
					
					// test if comment is filtered
					if (theApp.glob_prefs->GetCommentFilter().GetLength()>0) {
						CString resToken;
						CString strlink=theApp.glob_prefs->GetCommentFilter();
						strlink.MakeLower();
						int curPos=0;
						resToken= strlink.Tokenize("|",curPos);
						while (resToken != "") {
							if (m_strComment.MakeLower().Find(resToken)>-1) {
								m_strComment="";
								m_iRate=0;
								reqfile->SetHasRating(false);
								reqfile->SetHasComment(false);
								break;
							}
							resToken= strlink.Tokenize("|",curPos);
						}
		
					}
					delete desc;
				}
			}
			if (reqfile->HasRating() || reqfile->HasComment()) theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(reqfile);
		}
	}
}
void CUpDownClient::Disconnected(){
	// There is at least one case where this ASSERT will give a false warning. When a new client instance is created
	// on receiving OP_HELLO and the packet is parsed, it may throw an exception which let us delete/Disconnect that
	// client which is not yet in the client-list.
	ASSERT(theApp.clientlist->Debug_IsValidClient(this));

	if (GetUploadState() == US_UPLOADING)
		theApp.uploadqueue->RemoveFromUploadQueue(this);
	if (GetDownloadState() == DS_DOWNLOADING){
		SetDownloadState(DS_ONQUEUE);
        ClearPriorPartNumber();
	}

	// The remote client does not have to answer with OP_HASHSETANSWER *immediatly* 
	// after we've sent OP_HASHSETREQUEST. It may occure that a (buggy) remote client 
	// is sending use another OP_FILESTATUS which would let us change to DL-state to DS_ONQUEUE.
	if (((GetDownloadState() == DS_REQHASHSET) || m_fHashsetRequesting) && (reqfile))
        reqfile->hashsetneeded = true;

	// There is at least one case where this ASSERT will give a false warning. When a new client instance is created
	// on receiving OP_HELLO and the packet is parsed, it may throw an exception which let us delete/Disconnect that
	// client which is not yet in the client-list.
	ASSERT(theApp.clientlist->Debug_IsValidClient(this));

	//check if this client is needed in any way, if not delete it
	bool bDelete = true;
	switch(m_nUploadState){
		case US_ONUPLOADQUEUE:
			bDelete = false;
//		default:
//			this->SetUploadFileID(NULL);
	};
	switch(m_nDownloadState){
		case DS_ONQUEUE:
		case DS_TOOMANYCONNS:
		case DS_NONEEDEDPARTS:
		case DS_LOWTOLOWIP:
			bDelete = false;
	};

	switch(m_nUploadState){
		case US_CONNECTING:
		case US_WAITCALLBACK:
		case US_ERROR:
			bDelete = true;
	};
	switch(m_nDownloadState){
		case DS_CONNECTING:
		{
			m_cFailed++;
			if (m_cFailed <= 2){
				TryToConnect();
				return;
			}
		}
		case DS_WAITCALLBACK:
		case DS_ERROR:
			bDelete = true;
	};
	

	if (GetChatState()){
		bDelete = false;
		theApp.emuledlg->chatwnd.chatselector.ConnectingResult(this,false);
	}
	if (socket){
		ASSERT (theApp.listensocket->IsValidSocket(socket));
		socket->Safe_Delete();
	}
	socket = 0;
    if (m_iFileListRequested){
		theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_SHAREDFILES_FAILED),GetUserName());
        m_iFileListRequested = 0;
	}
    if (m_Friend) {
		theApp.friendlist->RefreshFriend(m_Friend);

        // If friends with friendslots are deleted, the friendslot setting is
        // forgotten. Prevent that.
        if(GetFriendSlot()) {
            bDelete = false;
        }
    }

	theApp.emuledlg->transferwnd.clientlistctrl.RefreshClient(this);
	if (bDelete){
		delete this;
	}
	else{
		m_fHashsetRequesting = 0;
	}
}

bool CUpDownClient::TryToConnect(bool bIgnoreMaxCon){
	if (theApp.listensocket->TooManySockets() && !bIgnoreMaxCon && !(socket && socket->IsConnected())){
		Disconnected();
		return false;
	}
	if ((theApp.serverconnect->GetClientID() < 16777216) && HasLowID()){
		if (GetDownloadState() == DS_CONNECTING)
			SetDownloadState(DS_LOWTOLOWIP);
		else if (GetDownloadState() == DS_REQHASHSET){
			SetDownloadState(DS_ONQUEUE);
			reqfile->hashsetneeded = true;
		}
		if (GetUploadState() == US_CONNECTING){
			Disconnected();
		}
	}

	if (!socket){
		socket = new CClientReqSocket(theApp.glob_prefs,this);
		if (!socket->Create()){
			socket->Safe_Delete();
			return false;
		}
	}
	else if (!socket->IsConnected()){
		socket->Safe_Delete();
		socket = new CClientReqSocket(theApp.glob_prefs,this);
		if (!socket->Create()){
			socket->Safe_Delete();
			return false;
		}
	}
	else{
		ConnectionEstablished();
		return true;
	}
	// MOD Note: Do not change this part - Merkur
	if (HasLowID()){
		if (GetDownloadState() == DS_CONNECTING)
			SetDownloadState(DS_WAITCALLBACK);
		if (GetUploadState() == US_CONNECTING){
			Disconnected();
			return false;
		}

		if (theApp.serverconnect->IsLocalServer(m_dwServerIP,m_nServerPort)){
			Packet* packet = new Packet(OP_CALLBACKREQUEST,4);
			memcpy(packet->pBuffer,&m_nUserID,4);
			theApp.uploadqueue->AddUpDataOverheadServer(packet->size);
			theApp.serverconnect->SendPacket(packet);
		}
		else{
			if (GetUploadState() == US_NONE && (!GetRemoteQueueRank() || m_bReaskPending) ){
				theApp.downloadqueue->RemoveSource(this);
				Disconnected();
				return false;
			}
			else{
				if (GetDownloadState() == DS_WAITCALLBACK){
					m_bReaskPending = true;
					SetDownloadState(DS_ONQUEUE);
				}
			}
		}
	}
	// MOD Note - end
	else{
		socket->Connect(GetFullIP(),GetUserPort());
		SendHelloPacket();
	}

    return true;
}

void CUpDownClient::ConnectionEstablished(){
	m_cFailed = 0;
	// ok we have a connection, lets see if we want anything from this client
	if (GetChatState() == MS_CONNECTING)
		theApp.emuledlg->chatwnd.chatselector.ConnectingResult(this,true);
	switch(GetDownloadState()){
		case DS_CONNECTING:
		case DS_WAITCALLBACK:
			m_bReaskPending = false;
			SetDownloadState(DS_CONNECTED);
			SendFileRequest();
	}
	if (m_bReaskPending){
		m_bReaskPending = false;
		if (GetDownloadState() != DS_NONE && GetDownloadState() != DS_DOWNLOADING){
			SetDownloadState(DS_CONNECTED);
			SendFileRequest();
		}
	}
	switch(GetUploadState()){
		case US_CONNECTING:
		case US_WAITCALLBACK:
			if (theApp.uploadqueue->IsDownloading(this)){
				SetUploadState(US_UPLOADING);
				Packet* packet = new Packet(OP_ACCEPTUPLOADREQ,0);
				theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
				socket->SendPacket(packet,true);
			}
	}
    if (m_iFileListRequested == 1){
        Packet* packet = new Packet(m_fSharedDirectories ? OP_ASKSHAREDDIRS : OP_ASKSHAREDFILES,0);
		theApp.uploadqueue->AddUpDataOverheadOther(packet->size);
		socket->SendPacket(packet,true,true);
	}
}

void CUpDownClient::ReGetClientSoft(){
	if (!m_pszUsername) {m_clientSoft=SO_UNKNOWN;return;}
	int iHashType = GetHashType();
	if (iHashType == SO_OLDEMULE) {m_clientSoft= SO_OLDEMULE;return;}
	if (iHashType == SO_EMULE){
		if( m_byEmuleVersion!=0x99 ){
			CString temp;
			temp.Format( "%02X", m_byEmuleVersion );
			m_nClientVersion = 	atoi(temp.GetBuffer())*10;
		}
		switch(m_byCompatibleClient){
			case SO_CDONKEY:
				m_clientSoft = SO_CDONKEY;
				break;
			case SO_LMULE:
				m_clientSoft = SO_LMULE;
				break;
			case SO_SHAREAZA:
				m_clientSoft = SO_SHAREAZA;
				break;
			default:
				if (m_bIsML)
					m_clientSoft = SO_MLDONKEY;
				else if (m_bIsHybrid)
					m_clientSoft = SO_EDONKEYHYBRID;
				else
					m_clientSoft = SO_EMULE;
		}
		return;
	}
	if (m_bIsML || iHashType == SO_MLDONKEY) {m_clientSoft= SO_MLDONKEY;return;}
	if( m_bIsHybrid )
		m_clientSoft=SO_EDONKEYHYBRID;
	else
		m_clientSoft=SO_EDONKEY;
}

int CUpDownClient::GetHashType()
{
	if (m_achUserHash[5] == 13 && m_achUserHash[14] == 110)
		return SO_OLDEMULE;
	else if (m_achUserHash[5] == 14 && m_achUserHash[14] == 111)
		return SO_EMULE;
 	else if (m_achUserHash[5] == 'M' && m_achUserHash[14] == 'L')
		return SO_MLDONKEY;
	else
		return SO_UNKNOWN;
}

void CUpDownClient::SetUserName(char* pszNewName){
	if (m_pszUsername){
		delete[] m_pszUsername;
		m_pszUsername = NULL;// needed, in case 'nstrdup' fires an exception!!
	}
	if( pszNewName )
		m_pszUsername = nstrdup(pszNewName);
}

void CUpDownClient::RequestSharedFileList(){
	if (m_iFileListRequested == 0){
		theApp.emuledlg->AddDebugLogLine(true,GetResString(IDS_SHAREDFILES_REQUEST),GetUserName());
    	m_iFileListRequested = 1;
		TryToConnect(true);
	}
	else
		theApp.emuledlg->AddDebugLogLine(true,_T("Requesting shared files from user %s (%u) is already in progress"),GetUserName(),GetUserID());
}

void CUpDownClient::ProcessSharedFileList(char* pachPacket, uint32 nSize, LPCTSTR pszDirectory){
    if (m_iFileListRequested > 0){
        m_iFileListRequested--;
		theApp.searchlist->ProcessSearchanswer(pachPacket,nSize,this,pszDirectory);
	}
}

void CUpDownClient::SetUserHash(uchar* m_achTempUserHash){
	if( m_achTempUserHash == NULL ){
		md4clr(m_achUserHash);
		return;
	}
	md4cpy(m_achUserHash,m_achTempUserHash);
}
