//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.
// ListenSocket.cpp : implementation file
//

#include "stdafx.h"
#include "emule.h"
#include "ListenSocket.h"
#include "opcodes.h"
#include "KnownFile.h"
#include "sharedfilelist.h"
#include "uploadqueue.h"
#include "updownclient.h"
#include "clientlist.h"

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


// CClientReqSocket
CClientReqSocket::CClientReqSocket(CPreferences* in_prefs,CUpDownClient* in_client){
	app_prefs = in_prefs;
	client = in_client;
	if (in_client)
		client->socket = this;
	theApp.listensocket->AddSocket(this);
	ResetTimeOutTimer();
	deletethis = false;
	deltimer = 0;
}


CClientReqSocket::~CClientReqSocket(){
	if (client)
		client->socket = 0;
	client = 0;
	theApp.listensocket->RemoveSocket(this);

	DEBUG_ONLY (theApp.clientlist->Debug_SocketDeleted(this));
}

void CClientReqSocket::ResetTimeOutTimer(){
	timeout_timer = ::GetTickCount();
};

bool CClientReqSocket::CheckTimeOut(){
	if (::GetTickCount() - timeout_timer > CONNECTION_TIMEOUT){
		timeout_timer = ::GetTickCount();
		Disconnect();
		return true;
	}
	return false;
};

void CClientReqSocket::OnClose(int nErrorCode){
	ASSERT (theApp.listensocket->IsValidSocket(this));
	CEMSocket::OnClose(nErrorCode);
	Disconnect();
};

void CClientReqSocket::Disconnect(){
	AsyncSelect(0);
	byConnected = ES_DISCONNECTED;
	if (!client)
		Safe_Delete();
	else
		client->Disconnected();
};

void CClientReqSocket::Delete_Timed(){
// it seems that MFC Sockets call socketfunctions after they are deleted, even if the socket is closed
// and select(0) is set. So we need to wait some time to make sure this doesn't happens
	if (::GetTickCount() - deltimer > 10000)
		delete this;
}

void CClientReqSocket::Safe_Delete(){
	ASSERT (theApp.listensocket->IsValidSocket(this));
	AsyncSelect(0);
	deltimer = ::GetTickCount();
	if (m_hSocket != INVALID_SOCKET)
		ShutDown(2);
	if (client)
		client->socket = 0;
	client = 0;
	byConnected = ES_DISCONNECTED;
	deletethis = true;
}

bool CClientReqSocket::ProcessPacket(char* packet, uint32 size, uint8 opcode){
	try{
		try{
			if (!client && opcode != OP_HELLO)
				throw GetResString(IDS_ERR_NOHELLO);
			switch(opcode){
				case OP_HELLOANSWER:{
					theApp.downloadqueue->AddDownDataOverheadOther(size);
					client->ProcessHelloAnswer(packet,size);
					if (client)
						client->ConnectionEstablished();
					break;
				}
				case OP_HELLO:{
					theApp.downloadqueue->AddDownDataOverheadOther(size);
					if (!client){
						// create new client to save standart informations
						client = new CUpDownClient(this);
					}
					client->ProcessHelloPacket(packet,size);
					// now we check if we now this client already. if yes this socket will
					// be attached to the known client, the new client will be deleted
					// and the var. "client" will point to the known client.
					// if not we keep our new-constructed client ;)
					if (theApp.clientlist->AttachToAlreadyKnown(&client,this)){
						// update the old client informations
						client->ProcessHelloPacket(packet,size);
					}
					else {
						theApp.clientlist->AddClient(client);
						client->SetCommentDirty();
					}

					// if IP is filtered, dont reply but disconnect...
					if (theApp.ipfilter->IsFiltered(client->GetIP())) {
						theApp.emuledlg->AddDebugLogLine(true,GetResString(IDS_IPFILTERED),client->GetFullIP(),theApp.ipfilter->GetLastHit());
						client->Disconnected();
						theApp.stat_filteredclients++;
						break;
					}

					// send a response packet with standart informations
					if (client->GetClientSoft() == SO_EMULE )
						client->SendMuleInfoPacket(false);
					
					client->SendHelloAnswer();
					if (client)
						client->ConnectionEstablished();

					break;
				}
				case OP_FILEREQUEST:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
					if (size == 16 || (size > 16 && client->GetExtendedRequestsVersion() > 0)){
						if (!client->GetWaitStartTime())
							client->SetWaitStartTime();
						uchar reqfileid[16];
						memcpy(reqfileid,packet,16);
						CKnownFile* reqfile = theApp.sharedfiles->GetFileByID(reqfileid);
						if (!reqfile){
							// DbT:FileRequest
							// send file request no such file packet (0x48)
							Packet* replypacket = new Packet(OP_FILEREQANSNOFIL, 16);
							memcpy(replypacket->pBuffer, packet, 16);
							theApp.uploadqueue->AddUpDataOverheadFileRequest(replypacket->size);
							SendPacket(replypacket, true);
							// DbT:End
							break;
						}
						// if wer are downloading this file, this could be a new source
						if (reqfile->IsPartFile())
							if( theApp.glob_prefs->GetMaxSourcePerFile() > ((CPartFile*)reqfile)->GetSourceCount() ) //<<--
								theApp.downloadqueue->CheckAndAddKnownSource((CPartFile*)reqfile,client);
						
						// check to see if this is a new file they are asking for
						if(memcmp(client->reqfileid, packet, 16) != 0)
							client->SetCommentDirty();

						// send filename etc
						memcpy(client->reqfileid,packet,16);
						CMemFile* data = new CMemFile();
						data->Write(reqfile->GetFileHash(),16);
						uint16 namelength = (uint16)strlen(reqfile->GetFileName());
						data->Write(&namelength,2);
						data->Write(reqfile->GetFileName(),namelength);
						client->ProcessUpFileStatus(packet,size);
						Packet* packet = new Packet(data);
						packet->opcode = OP_FILEREQANSWER;
						delete data;
						theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
						SendPacket(packet,true);
						/* done with "OP_SETREQFILEID" now
						//send filestatus 
						data = new CMemFile();
						data->Write(reqfile->GetFileHash(),16);
						if (reqfile->IsPartFile())
							((CPartFile*)reqfile)->WritePartStatus(data);
						else{
							uint32 null = 0;
							data->Write(&null,3);
						}
						packet = new Packet(data);
						packet->opcode = OP_FILESTATUS;
						delete data;
						theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
						SendPacket(packet,true);
						*/
						client->SendCommentInfo(reqfile);
						break;
					}
					throw GetResString(IDS_ERR_WRONGPACKAGESIZE);
					break;
				}
				case OP_FILEREQANSNOFIL:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
				// DbT:FileRequest
				if (size == 16) {
					// if that client do not have my file maybe has another different
					CPartFile* reqfile = theApp.downloadqueue->GetFileByID((uchar*)packet);
					if (!reqfile)
						break;

					// we try to swap to another file ignoring no needed parts files
					if (client) switch (client->GetDownloadState()) {
						case DS_ONQUEUE:
						case DS_NONEEDEDPARTS:
						if (!client->SwapToAnotherFile(false)) {
							theApp.downloadqueue->RemoveSource(client, true);
						}
						break;
					}
					break;
				}
				throw GetResString(IDS_ERR_WRONGPACKAGESIZE);
				break;
				// DbT:End
				}
				case OP_FILEREQANSWER:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
					client->ProcessFileInfo(packet,size);
					break;
				}
				case OP_FILESTATUS:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
					client->ProcessFileStatus(packet,size);
					break;
				}
				case OP_STARTUPLOADREQ:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
					theApp.uploadqueue->AddClientToQueue(client);
					break;
				}
				case OP_QUEUERANK:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
					CSafeMemFile* data = new CSafeMemFile((BYTE*)packet,size);
					uint32 rank;
					data->Read(&rank,4);
					client->SetRemoteQueueRank(rank);
					delete data;
					break;
				}
				case OP_ACCEPTUPLOADREQ:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
					if (client->GetDownloadState() == DS_ONQUEUE ){
						client->SetDownloadState(DS_DOWNLOADING);
						client->SendBlockRequests();
					}
					break;
				}
				case OP_REQUESTPARTS:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
					CSafeMemFile* data = new CSafeMemFile((BYTE*)packet,size);
					uchar reqfilehash[16];
					data->Read(reqfilehash,16);
					Requested_Block_Struct* reqblock1 = new Requested_Block_Struct;
					Requested_Block_Struct* reqblock2 = new Requested_Block_Struct;
					Requested_Block_Struct* reqblock3 = new Requested_Block_Struct;
					data->Read(&reqblock1->StartOffset,4);
					data->Read(&reqblock2->StartOffset,4);
					data->Read(&reqblock3->StartOffset,4);
					data->Read(&reqblock1->EndOffset,4);
					data->Read(&reqblock2->EndOffset,4);
					data->Read(&reqblock3->EndOffset,4);
					memcpy(&reqblock1->FileID,reqfilehash,16);
					memcpy(&reqblock2->FileID,reqfilehash,16);
					memcpy(&reqblock3->FileID,reqfilehash,16);
					if (reqblock1->EndOffset-reqblock1->StartOffset == 0)			
						delete reqblock1;
					else
						client->AddReqBlock(reqblock1);

					if (reqblock2->EndOffset-reqblock2->StartOffset == 0)			
						delete reqblock2;
					else
						client->AddReqBlock(reqblock2);

					if (reqblock3->EndOffset-reqblock3->StartOffset == 0)			
						delete reqblock3;
					else
						client->AddReqBlock(reqblock3);
					delete data;
					break;
				}
				case OP_CANCELTRANSFER:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
					theApp.uploadqueue->RemoveFromUploadQueue(client);
					break;
				}
				case OP_HASHSETREQUEST:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
					if (size != 16)
						throw GetResString(IDS_ERR_WRONGHPACKAGESIZE);
					client->SendHashsetPacket(packet);
					break;
				}
				case OP_HASHSETANSWER:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
                   if (client->GetDownloadState() != DS_REQHASHSET)
                        throw CString("unwanted hashset");
					client->ProcessHashSet(packet,size);
					break;
				}
				case OP_SENDINGPART:{
//					theApp.downloadqueue->AddDownDataOverheadOther(0, 24);
					client->ProcessBlockPacket(packet,size);
					break;
				}
				case OP_OUTOFPARTREQS:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
					break;
				}
				case OP_SETREQFILEID:{
					theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
				// DbT:FileRequest
				if (size == 16){
					if (!client->GetWaitStartTime())
						client->SetWaitStartTime();

					CKnownFile* reqfile = theApp.sharedfiles->GetFileByID((uchar*)packet);

					if (!reqfile){
						// send file request no such file packet (0x48)
						Packet* replypacket = new Packet(OP_FILEREQANSNOFIL, 16);
						memcpy(replypacket->pBuffer, packet, 16);
						theApp.uploadqueue->AddUpDataOverheadFileRequest(replypacket->size);
						SendPacket(replypacket, true);
						break;
					}

					// if we are downloading this file, this could be a new source
					if (reqfile->IsPartFile())
						if( theApp.glob_prefs->GetMaxSourcePerFile() > ((CPartFile*)reqfile)->GetSourceCount() )
							theApp.downloadqueue->CheckAndAddKnownSource((CPartFile*)reqfile,client);
				            
					// check to see if this is a new file they are asking for
					if(memcmp(client->reqfileid, packet, 16) != 0)
						client->SetCommentDirty();

					//send filestatus
					memcpy(client->reqfileid,packet,16);
					CMemFile* data = new CMemFile();
					data->Write(reqfile->GetFileHash(),16);
					if (reqfile->IsPartFile())
						((CPartFile*)reqfile)->WritePartStatus(data);
					else {
						uint32 null = 0;
						data->Write(&null,3);
					}
					Packet* packet = new Packet(data);
					packet->opcode = OP_FILESTATUS;
					delete data;
					theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
					SendPacket(packet,true);
					break;
				}
				throw GetResString(IDS_ERR_WRONGPACKAGESIZE);
				break;
				// DbT:End
				}
				case OP_MESSAGE:{
					theApp.downloadqueue->AddDownDataOverheadOther(size);
					uint16 length;
					memcpy(&length,packet,2);
					if (length+2 != size)
						throw CString("invalid message packet");
					char* message = new char[length+1];
					memcpy(message,packet+2,length);
					message[length] = 0;
					theApp.emuledlg->chatwnd.chatselector.ProcessMessage(client,message);
					delete[] message;
					break;
				}
				case OP_ASKSHAREDFILES:	{	// client wants to know what we have in share, let's see if we allow him to know that
					theApp.downloadqueue->AddDownDataOverheadOther(size);
					CPtrList list;
					if (theApp.glob_prefs->CanSeeShares() == 0 ||					// everybody
						(theApp.glob_prefs->CanSeeShares() == 1 && client->IsFriend() ) )	// friend
					{
						CCKey bufKey;
						CKnownFile* cur_file;
						for (POSITION pos = theApp.sharedfiles->m_Files_map.GetStartPosition();pos != 0;){
							theApp.sharedfiles->m_Files_map.GetNextAssoc(pos,bufKey,cur_file);
							list.AddTail((void*&)cur_file);
						}
						theApp.emuledlg->AddLogLine(true,GetResString(IDS_REQ_SHAREDFILES),client->GetUserName(),client->GetUserID(),GetResString(IDS_ACCEPTED) );
					} else theApp.emuledlg->AddLogLine(true,GetResString(IDS_REQ_SHAREDFILES),client->GetUserName(),client->GetUserID(),GetResString(IDS_DENIED) );

					// now create the memfile for the packet
					uint32 iTotalCount = list.GetCount();
					CSafeMemFile tempfile(80);
					tempfile.Write(&iTotalCount, 4);
					while (list.GetCount())
					{
						theApp.sharedfiles->CreateOfferedFilePacket((CKnownFile*)list.GetHead(), &tempfile);
						list.RemoveHead();
					}
					// create a packet and send it
					Packet* replypacket = new Packet(&tempfile);
					replypacket->opcode = OP_ASKSHAREDFILESANSWER;
					theApp.uploadqueue->AddUpDataOverheadOther(replypacket->size);
					SendPacket(replypacket, true, true);
					break;
				}
				case OP_ASKSHAREDFILESANSWER:{
					theApp.downloadqueue->AddDownDataOverheadOther(size);
					client->ProcessSharedFileList(packet,size);
					break;
				}
				default:
					theApp.downloadqueue->AddDownDataOverheadOther(size);
					theApp.emuledlg->AddDebugLogLine(false,"unknown opcode: %i %x",opcode,opcode);
				  
			}
		}
		catch(CFileException* error){
			OUTPUT_DEBUG_TRACE();
			error->Delete();	//mf
			throw GetResString(IDS_ERR_INVALIDPACKAGE);
		}
	}
	catch(CString error){
		OUTPUT_DEBUG_TRACE();
		if (client){
			client->SetDownloadState(DS_ERROR);	
			//TODO write this into a debugfile
			theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_ERR_CLIENTERROR)
			,client->GetUserName(),client->GetFullIP(),error.GetBuffer());
		}
		else
			theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_ERR_BADCLIENTACTION),error.GetBuffer());
			
		Disconnect();
		return false;
	}
	return true;
}

bool CClientReqSocket::ProcessExtPacket(char* packet, uint32 size, uint8 opcode){
	try{
		if (!client)
			throw GetResString(IDS_ERR_UNKNOWNCLIENTACTION);
		switch(opcode){
			case OP_EMULEINFO:{
				theApp.downloadqueue->AddDownDataOverheadOther(size);
				client->ProcessMuleInfoPacket(packet,size);
				client->SendMuleInfoPacket(true);
				break;
			}
			case OP_EMULEINFOANSWER:{
				theApp.downloadqueue->AddDownDataOverheadOther(size);
				client->ProcessMuleInfoPacket(packet,size);
				break;
			}
			case OP_COMPRESSEDPART:{
//				theApp.downloadqueue->AddDownDataOverheadOther(24);
				client->ProcessBlockPacket(packet,size,true);
				break;
			}
			case OP_QUEUERANKING:{
				theApp.downloadqueue->AddDownDataOverheadOther(size);
				if (size != 12)
					throw GetResString(IDS_ERR_BADSIZE);
				uint16 newrank;
				memcpy(&newrank,packet+0,2);
				client->SetRemoteQueueFull(false);
				client->SetRemoteQueueRank(newrank);
				break;
			}
 			case OP_REQUESTSOURCES:{
				theApp.downloadqueue->AddDownDataOverheadSourceExchange(size);
				if (client->GetSourceExchangeVersion() >= 1){
					if(size != 16)
						throw GetResString(IDS_ERR_BADSIZE);
	
					//first check shared file list, then download list
					CKnownFile* file = theApp.sharedfiles->GetFileByID((uchar*)packet);
					if(!file)
						file = theApp.downloadqueue->GetFileByID((uchar*)packet);
	
					if(file) {
						DWORD dwTimePassed = ::GetTickCount() - client->GetLastSrcReqTime() + CONNECTION_LATENCY;
						bool bNeverAskedBefore = client->GetLastSrcReqTime() == 0;
	
						if( 
						    //if not complete and file is rare, allow once every 10 minutes
						    ( file->IsPartFile() &&
						     ((CPartFile*)file)->GetSourceCount() <= RARE_FILE * 2 &&
						      (bNeverAskedBefore || dwTimePassed > SOURCECLIENTREASK)
						    ) ||
						    //OR if file is not rare or if file is complete, allow every 90 minutes
						    ( (bNeverAskedBefore || dwTimePassed > SOURCECLIENTREASK * MINCOMMONPENALTY) )
						  ) {
	
							client->SetLastSrcReqTime();
							Packet* tosend = file->CreateSrcInfoPacket(client);
							if(tosend){
								theApp.uploadqueue->AddUpDataOverheadSourceExchange(tosend->size);
								SendPacket(tosend, true, true);
								theApp.emuledlg->AddDebugLogLine( false, "RCV:Source Request User(%s) File(%s)", client->GetUserName(), file->GetFileName() );
							}
						}
						}
				}
				break;
			}
 			case OP_ANSWERSOURCES:{
				theApp.downloadqueue->AddDownDataOverheadSourceExchange(size);
				try{
					CSafeMemFile data((BYTE*)packet,size);
					uchar hash[16];
					data.Read(hash,16);
					CKnownFile* file = theApp.downloadqueue->GetFileByID((uchar*)packet);
					if(file){
						if (file->IsPartFile()){
							//set the client's answer time
							client->SetLastSrcAnswerTime();
							//and set the file's last answer time
							((CPartFile*)file)->SetLastAnsweredTime();
	
							((CPartFile*)file)->AddClientSources(&data);
						}
					}
				}
				catch(CFileException* error){
					OUTPUT_DEBUG_TRACE();
					error->Delete();
					throw GetResString(IDS_ERR_INVALIDPACKAGE);
				}
				break;
			}
			case OP_FILEDESC:{
				theApp.downloadqueue->AddDownDataOverheadOther(size);
				client->ProcessMuleCommentPacket(packet,size);
				break;
			}
			/*case OP_VERIFYUPSREQ:{
				CUpDownClient* vuclient = theApp.clientlist->VUGetRandomClient();
				if (vuclient){
					Packet* answer = new Packet(OP_VERIFYUPSANSWER,6,OP_EMULEPROT);
					uint32 ip = vuclient->GetIP();
					memcpy(answer->pBuffer,ip,4);
					uint16 udpport = vuclient->GetUDPPort();
					memcpy(answer->pBuffer,ip,4);
					theApp.uploadqueue->AddUpDataRateMSOverhead(packet->size);
					SendPacket(answer,true,true);
				}
			}
			case OP_VERIFYUPSANSWER:{
				if (size != 12)
					throw CString("invalid size (OP_VERIFYUPSANSWER)");
			}*/

			default:
				theApp.downloadqueue->AddDownDataOverheadOther(size);
				theApp.emuledlg->AddDebugLogLine(false,"unknown opcode: %i %x",opcode,opcode);
			  
		}
	}
	catch(CString error){
		OUTPUT_DEBUG_TRACE();
		theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_ERR_BADCLIENTACTION),error.GetBuffer());
		if (client) client->SetDownloadState(DS_ERROR);		
		Disconnect();
		return false;
	}
	return true;
}

void CClientReqSocket::OnInit(){
	uint8 tv = 1;
	//SetSockOpt(SO_DONTLINGER,&tv,sizeof(BOOL));
}

void CClientReqSocket::OnSend(int nErrorCode){
	ResetTimeOutTimer();
	CEMSocket::OnSend(nErrorCode);
}

void CClientReqSocket::OnError(int nErrorCode){

	if (client)
		theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_ERR_BADCLIENT2)
		,client->GetUserName(),client->GetFullIP(),nErrorCode);
	else
		theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_ERR_BADCLIENTACTION),"CClientReqSocket::OnError");			
	Disconnect();
}

void CClientReqSocket::PacketReceived(Packet* packet){
	switch (packet->prot){
		case OP_EDONKEYPROT:
			ProcessPacket(packet->pBuffer,packet->size,packet->opcode);
			break;
		case OP_PACKEDPROT:
			if (!packet->UnPackPacket()){
				ASSERT ( false );
				break;
			}
		case OP_EMULEPROT:
			ProcessExtPacket(packet->pBuffer,packet->size,packet->opcode);
			break;
		default:;
	}
}

void CClientReqSocket::OnReceive(int nErrorCode){
	ResetTimeOutTimer();
	CEMSocket::OnReceive(nErrorCode);
}

bool CClientReqSocket::Create(){
	theApp.listensocket->AddConnection();
	BOOL result = CAsyncSocket::Create(0,SOCK_STREAM,FD_WRITE|FD_READ|FD_CLOSE);
	OnInit();
	return result;
}







// CListenSocket
// CListenSocket member functions
CListenSocket::CListenSocket(CPreferences* in_prefs){
	app_prefs = in_prefs;
	opensockets = 0;
	maxconnectionreached = 0;
	m_OpenSocketsInterval = 0;
	m_nPeningConnections = 0;
}

CListenSocket::~CListenSocket(){
	Close();
	KillAllSockets();
}

bool CListenSocket::StartListening(){
	bListening = true;
	return (this->Create(app_prefs->GetPort(),SOCK_STREAM,FD_ACCEPT) && this->Listen());
}

void CListenSocket::ReStartListening(){
	bListening = true;
	if (m_nPeningConnections){
		m_nPeningConnections--;
		OnAccept(0);
	}
}

void CListenSocket::StopListening(){
	bListening = false;
	maxconnectionreached++;
}


void CListenSocket::OnAccept(int nErrorCode){
	if (!nErrorCode){
		m_nPeningConnections++;
		if (m_nPeningConnections < 1){
			ASSERT ( false );
			m_nPeningConnections = 1;
		}
		if (TooManySockets(true) && !theApp.serverconnect->IsConnecting()){
			StopListening();
			return;
		}
		else if ( bListening == false )
			ReStartListening(); //If the client is still at maxconnections, this will allow it to go above it.. But if you don't, you will get a lowID on all servers.
	
		while (m_nPeningConnections){
			m_nPeningConnections--;
			CClientReqSocket* newclient = new CClientReqSocket(app_prefs);
			if (!Accept(*newclient))
				newclient->Safe_Delete();
			else{
				newclient->AsyncSelect(FD_WRITE|FD_READ|FD_CLOSE);
				newclient->OnInit();
			}
			AddConnection();
		}
//		if (TooManySockets(true) && !theApp.serverconnect->IsConnecting())
//			StopListening();
	}

}

void CListenSocket::Process(){
	POSITION pos2;
	m_OpenSocketsInterval = 0;
	opensockets = 0;
	for(POSITION pos1 = socket_list.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
		socket_list.GetNext(pos1);
		CClientReqSocket* cur_sock = socket_list.GetAt(pos2);
		opensockets++;

	   if (cur_sock->deletethis){
		   if (cur_sock->m_hSocket != INVALID_SOCKET){
				cur_sock->Close();
		   }
		   else{
			   cur_sock->Delete_Timed();;
		   }
	   }
	   else
		socket_list.GetAt( pos2 )->CheckTimeOut();
   }
   if ( (GetOpenSockets()+5 < app_prefs->GetMaxConnections() || theApp.serverconnect->IsConnecting()) && !bListening)
	   ReStartListening();
}

void CListenSocket::RecalculateStats(){
	memset(m_ConnectionStates,0,6);
	POSITION pos1,pos2;
	for(pos1 = socket_list.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
		socket_list.GetNext(pos1);
		CClientReqSocket* cur_sock = socket_list.GetAt(pos2);
		switch (cur_sock->GetConState()){
			case ES_DISCONNECTED:
				m_ConnectionStates[0]++;
				break;
			case ES_NOTCONNECTED:
				m_ConnectionStates[1]++;
				break;
			case ES_CONNECTED:
				m_ConnectionStates[2]++;
				break;
		}
   }
}

void CListenSocket::AddSocket(CClientReqSocket* toadd){
	socket_list.AddTail(toadd);
}

void CListenSocket::RemoveSocket(CClientReqSocket* todel){
	POSITION pos2,pos1;
	for(pos1 = socket_list.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
       socket_list.GetNext(pos1);
	   if ( socket_list.GetAt(pos2) == todel )
			socket_list.RemoveAt(pos2);
   }
}

void CListenSocket::KillAllSockets(){
	for (POSITION pos = socket_list.GetHeadPosition();pos != 0;pos = socket_list.GetHeadPosition()){
		CClientReqSocket* cur_socket = socket_list.GetAt(pos);
			if (cur_socket->client)
				delete cur_socket->client;
			else
				delete cur_socket;
	}
}

void CListenSocket::AddConnection(){
	m_OpenSocketsInterval++;
	opensockets++;
}

bool CListenSocket::TooManySockets(bool bIgnoreInterval){
	if (GetOpenSockets() > app_prefs->GetMaxConnections() || (m_OpenSocketsInterval > (theApp.glob_prefs->GetMaxConperFive()*theApp.emuledlg->statisticswnd.GetMaxConperFiveModifier()) && !bIgnoreInterval) ){
		return true;
	}
	else
		return false;
}

bool CListenSocket::IsValidSocket(CClientReqSocket* totest){
	return socket_list.Find(totest);
}

void CListenSocket::Debug_ClientDeleted(CUpDownClient* deleted){
	POSITION pos1, pos2;
	for (pos1 = socket_list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		socket_list.GetNext(pos1);
		CClientReqSocket* cur_sock = socket_list.GetAt(pos2);
		if (!AfxIsValidAddress(cur_sock, sizeof(CClientReqSocket))) {
			AfxDebugBreak(); 
		}
		if (cur_sock->client == deleted){
			AfxDebugBreak();
		}
	}
}
