// This file is a part of the xMule Project.
//
// Copyright (c) 2004 Theodore R. Smith (hopeseekr@xmule.ws / http://www.xmule.ws/)
// DSA-1024 Fingerprint: 10A0 6372 9092 85A2 BB7F 907B CB8B 654B E33B F1ED
//
// Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.xmule-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

#ifdef PRECOMP
#	include "xmule-headers.h"
#else
#	include "ClientList.h"
#	include "DownloadQueue.h"
#	include "IPFilter.h"
#	include "KnownFile.h"
#	include "ListenSocket.h"
#include "NewSockets.h"                     // Needed for NewSocket_SendPacketOP
#	include "opcodes.h"
#include "otherstructs.h"                   // Requested_Block_Struct - audited 5 Nov 2004
#include "packets.h"                        // Packet
#include "Preferences.h"                    // CPreferences::shareddirlist
#include "SafeFile.h"                       // CSafeMemFile
#include "server.h"                         // CServer
#include "ServerList.h"                     // CServerList
#	include "SharedFileList.h"
#include "sockets.h"                        // Needed for CServerConnect - audited 5 Nov 2004
#	include "updownclient.h"
#	include "UploadQueue.h"
#	include "wintypes.h"
#	include "xmule.h"
#	include "xmuleDlg.h"
#	include "PartFile.h"
#endif

#include <DynPrefs/DynPrefs.h>              // Needed for DynamicPreferences

#ifndef ID_SOKETTI
#define ID_SOKETTI 7772
#endif

#include <iostream>                         // Needed for std::cout std::endl
#include <wx/listimpl.cpp>

using std::cout;
using std::endl;

extern int newprefs04_opt[];

IMPLEMENT_DYNAMIC_CLASS(CClientReqSocket, CEMSocket)

class ReqSocketEvt: public wxEvtHandler
{
private:
	void HandleSocket(wxSocketEvent& event);
	DECLARE_EVENT_TABLE();
};

BEGIN_EVENT_TABLE(ReqSocketEvt, wxEvtHandler)
	EVT_SOCKET(-1, ReqSocketEvt::HandleSocket)
END_EVENT_TABLE()

void ReqSocketEvt::HandleSocket(wxSocketEvent& event)
{
    CClientReqSocket* soc = dynamic_cast<CClientReqSocket *>(event.GetSocket());

    if (soc == NULL)
    {        
        return;
    }

    switch (event.GetSocketEvent())
    {
        case wxSOCKET_LOST:
        {
//            soc->OnLost(errno);
            if (soc->LastError() != wxSOCKET_NOERROR)
            {
                soc->Disconnect();
                soc->Destroy();
            }

            break;
        }
        case wxSOCKET_INPUT:
        {
            if (theApp.dynprefs->Get<bool>("enable-serverless") == false)
            {
                soc->OnInput(0);
            }
            break;
        }
        case wxSOCKET_OUTPUT:
        {
            soc->OnOutput(0);
            break;
        }
        case wxSOCKET_CONNECTION:
        {
            
        }
        default:
        {
            assert("connection requests should not arrive here... :o");
            break;
        }
    }
}

class ListenSocketEvt: public wxEvtHandler
{
private:
	void HandleSocket(wxSocketEvent& event);
	DECLARE_EVENT_TABLE();
};

BEGIN_EVENT_TABLE(ListenSocketEvt, wxEvtHandler)
	EVT_SOCKET(-1, ListenSocketEvt::HandleSocket)
END_EVENT_TABLE()

void ListenSocketEvt::HandleSocket(wxSocketEvent& event)
{
    CListenSocket* soc = dynamic_cast<CListenSocket *>(event.GetSocket());
    if (event.GetSocketEvent() == wxSOCKET_CONNECTION)
    {
        soc->OnAccept(0);
    }
}

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;
    hotrank = false;

    m_event_handler = new ReqSocketEvt;
    SetEventHandler(*m_event_handler, -1);
    SetNotify(wxSOCKET_CONNECTION_FLAG |wxSOCKET_INPUT_FLAG |wxSOCKET_OUTPUT_FLAG |wxSOCKET_LOST_FLAG);
    Notify(TRUE);
}

CClientReqSocket::~CClientReqSocket()
{
    SetNotify(0);
    Notify(FALSE);

    delete m_event_handler;

    if (client)
    {
        if (client->socket)
        {
            client->socket = NULL;
        }
    }

    theApp.listensocket->RemoveSocket(this);
}

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

;

bool CClientReqSocket::CheckTimeOut()
{
    if ((!hotrank && (::GetTickCount() - timeout_timer > CONNECTION_TIMEOUT))
    || (hotrank && (::GetTickCount() - timeout_timer > (CONNECTION_TIMEOUT + FILEREASKTIME))))
    {
        timeout_timer =::GetTickCount();
        Disconnect();
        return true;
    }
    return false;
}

void CClientReqSocket::OnClose(int nErrorCode)
{
    CEMSocket::OnClose(nErrorCode);
    Disconnect();
}

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

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
    //delete this: >> exception if you delete itself while active:
    if (::GetTickCount() - deltimer > 30000);
}

void CClientReqSocket::Safe_Delete()
{
    deltimer =::GetTickCount();
    if (client)
    {
        client->socket = 0;
    }
    client = 0;
    byConnected = ES_DISCONNECTED;
    deletethis = true;
    Destroy();
}

bool CClientReqSocket::ProcessPacket(char *packet, uint32_t size, uint8_t opcode)
{
    NewSocket_DebugPP(client,opcode, size, (unsigned char *) packet);
    switch (opcode)
    {
    case OP_HELLO:
        {
            bool bIsMuleHello = false;
            theApp.downloadqueue->AddDownDataOverheadOther(size);
            if (!client)
            {
                // create new client to save standart informations
                client = new CUpDownClient(this);
            }
            if (theApp.ipfilter->IsFiltered(client->GetIP()))
            {
                // if IP is filtered, dont reply but disconnect...
                client->Disconnected();
                theApp.stat_filteredclients++;
            }
            else
            {
                bIsMuleHello = 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
                    bIsMuleHello = client->ProcessHelloPacket(packet, size);
                }
                else
                {
                    theApp.clientlist->AddClient(client);
                    client->SetCommentDirty();
                }
                // send a response packet with standart informations
                if ((client->GetClientSoft() == SO_EMULE) && (!bIsMuleHello))
                {
                    client->SendMuleInfoPacket(false);
                }
                client->SendHelloAnswer();
                if (client)
                {
                    client->ConnectionEstablished();
                }
                if (client->GetInfoPacketsReceived() == IP_BOTH)
                {
                    client->InfoPacketsReceived();
                }
            }
            break;
        }
    case OP_HELLOANSWER:
        {
            theApp.downloadqueue->AddDownDataOverheadOther(size);
	    if(client) {
                bool answer = client->ProcessHelloAnswer(packet, size);
	    }
            if (client->GetInfoPacketsReceived() == IP_BOTH)
            {
                bool answer = client->ProcessHelloAnswer(packet, size);
                client->InfoPacketsReceived();
            }
            if (client)
            {
                client->ConnectionEstablished();
            }
            break;
        }
    case OP_FILEREQUEST:
        {
            theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
            if (! (client->IsBanned()))
            {
                if (size == 16 || (size > 16 &&client->GetExtendedRequestsVersion() > 0))
                {
                    if (!client->GetWaitStartTime())
                    {
                        client->SetWaitStartTime();
                    }
                    unsigned char reqfileid[16];
                    md4cpy(reqfileid, packet);
                    CKnownFile *reqfile = theApp.sharedfiles->GetFileByID(reqfileid);
                    if (!reqfile)
                    {
                        // if we've just started a download we may want to use that client as a source
                        CKnownFile *partfile = theApp.downloadqueue->GetFileByID(reqfileid);
                        if (partfile &&partfile->IsPartFile())
                        {
                            //modified by sivka
                            if (((CPartFile *)partfile)->GetMaxSourcesPerFile() >
/*->*/                          ((CPartFile *)partfile)->GetSourceCount())
                            {
                                if (! (((CPartFile *) partfile)->IsStopped()))
                                {
                                    theApp.downloadqueue->CheckAndAddKnownSource((CPartFile *) partfile, client);
                                }
                            }
                        }
                    }
                    if (reqfile)
                    {
                        if (reqfile->IsPartFile())
                        // modified by sivka:
                        if (((CPartFile *)reqfile)->GetMaxSourcesPerFile() > ((CPartFile *)reqfile)->GetSourceCount())
                        theApp.downloadqueue->CheckAndAddKnownSource((CPartFile *) reqfile, client);
                        if (md4cmp(client->GetUploadFileID(), packet) != 0)
                        {
                            client->SetCommentDirty();
                        }
                        client->SetUploadFileID((unsigned char *) packet);
                        CMemFile *data = new CMemFile();
                        data->Write(reqfile->GetFileHash(), 16);
                        uint16_t namelength = (uint16_t) strlen(reqfile->GetFileName());
                        data->Write( &namelength, 2);
                        data->Write(reqfile->GetFileName(), namelength);
                        client->ProcessUpFileStatus(packet, size);
                        client->SendCommentInfo(reqfile);
                        Packet *packet = new Packet(data);
                        delete data;
                        NewSocket_SendPacketOP(3, OP_FILEREQANSWER, packet, (wxUint32) this, OP_FILEREQUEST);
                    }
                }
            }
            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((unsigned char *) 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())
                    {
                        theApp.downloadqueue->RemoveSource(client, true);
                    }
                    break;
                }
                break;
            }
            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_t 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);
            unsigned char 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);
            client->SendHashsetPacket(packet);
            break;
        }
    case OP_HASHSETANSWER:
        {
            theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
            client->ProcessHashSet((unsigned char *) packet, size);
            break;
        }
    case OP_SENDINGPART:
        {
            theApp.downloadqueue->AddDownDataOverheadOther(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((unsigned char *)packet);
                if (!reqfile)
                {
                    // send file request no such file packet (0x48)
                    Packet *replypacket = new Packet(OP_FILEREQANSNOFIL, 16);
                    memcpy(replypacket->pBuffer, packet, 16);
                    NewSocket_SendPacket(3, replypacket, (wxUint32) this, OP_SETREQFILEID);
                }
                else
                {
                    // if we are downloading this file, this could be a new source
                    if (reqfile->IsPartFile())
                    if (theApp.dynprefs->Get<long>("max-sources") > ((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->GetUploadFileID(), packet, 16) != 0)
                    client->SetCommentDirty();
                    //send filestatus
                    client->SetUploadFileID((unsigned char *) packet);
                    CMemFile *data = new CMemFile();
                    data->Write(reqfile->GetFileHash(), 16);
                    if (reqfile->IsPartFile())
                    ((CPartFile *) reqfile)->WritePartStatus(data);
                    else
                    {
                        uint32_t null = 0;
                        data->Write( &null, 3);
                    }
                    Packet *packet = new Packet(data);
                    NewSocket_SendPacketOP(3, OP_FILESTATUS, packet, (wxUint32) this, OP_SETREQFILEID);
                    delete data;
                }
                break;
            }
            break;
            // DbT:End
        }
    case OP_CHANGE_CLIENT_ID:
        {
            //from emule30e
            theApp.downloadqueue->AddDownDataOverheadOther(size);
            CSafeMemFile data((wxByte *) packet, size);
            wxUint32 nNewUserID;
            data.Read( &nNewUserID, 4);
            wxUint32 nNewServerIP;
            data.Read( &nNewServerIP, 4);
            if (nNewUserID < 16777216)
            {
                CServer *pNewServer = theApp.serverlist->GetServerByIP(nNewServerIP);
                if (pNewServer != NULL)
                {
                    client->SetUserID(nNewUserID);
                    client->SetServerIP(nNewServerIP);
                    client->SetServerPort(pNewServer->GetPort());
                }
            }
            else if(nNewUserID == client->GetIP())
            {
                client->SetUserID(nNewUserID);
                CServer *pNewServer = theApp.serverlist->GetServerByIP(nNewServerIP);
                if (pNewServer != NULL)
                {
                    client->SetServerIP(nNewServerIP);
                    client->SetServerPort(pNewServer->GetPort());
                }
            }
            break;
        }
    case OP_MESSAGE:
        {
            theApp.downloadqueue->AddDownDataOverheadOther(size);
            uint16_t length;
            memcpy( &length, packet, 2);
            char *message = new char[length + 1];
            memcpy(message, packet + 2, length);
            message[length] = 0;
            // Ban Spammers
            if (strstr(message, "chez.com/theworld") == 0 || strstr(message, "fullspeed.to/mison") == 0)
            {
                if (client)
                {
                    client->Ban();
                    client->thief = true;
                }
                theApp.xmuledlg->AddLogLine(false, "The spammer '%s' [%s:%i] using %s has just been banished",
                client->GetUserName(), client->GetFullIP(), client->GetUserPort(), client->GetClientVerString() .c_str());
            }
            else
            {
                //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);
            CList < void *, void *> list;
            // everybody:
            if (theApp.dynprefs->Get<long>("see-share") == 0 ||
/*->*/          // friend:
/*->*/          (theApp.dynprefs->Get<long>("see-share") == 1 && client->IsFriend()))
            {
                CKnownFileMap &filemap = theApp.sharedfiles->m_Files_map;
                for (CKnownFileMap::iterator pos = filemap.begin() ;
                pos != filemap.end() ; pos++)
                {
                    list.AddTail((void *&) pos->second);
                }
                theApp.xmuledlg->AddLogLine(true, _("User %s (%u) requested your sharedfiles-list->%s"),
                client->GetUserName(), client->GetUserID(), _("accepted"));
            }
            else theApp.xmuledlg->AddLogLine(true, _("User %s (%u) requested your sharedfiles-list->%s"),
            client->GetUserName(), client->GetUserID(), _("denied"));
            // now create the memfile for the packet
            uint32_t 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);
            NewSocket_SendPacketOP(3, OP_ASKSHAREDFILESANSWER, replypacket, (wxUint32) this, OP_ASKSHAREDFILES);
            break;
        }
    case OP_ASKSHAREDFILESANSWER:
        {
            theApp.downloadqueue->AddDownDataOverheadOther(size);
            client->ProcessSharedFileList(packet, size);
            break;
        }
    case OP_ASKSHAREDDIRS:
        {
            theApp.downloadqueue->AddDownDataOverheadOther(size);
            if (theApp.dynprefs->Get<long>("see-share") == 0 || (theApp.dynprefs->Get<long>("see-share") == 1 && client->IsFriend()))
            {
                theApp.xmuledlg->AddLogLine(true, _("User %s (%u) requested your shareddirectories-list->%s"),
                client->GetUserName(), client->GetUserID(), _("accepted"));
                CSafeMemFile tempfile(80);
                uint32_t uDirs = theApp.glob_prefs->shareddir_list.GetCount();
                tempfile.Write( &uDirs, 4);
                for (uint32_t iDir = 0 ; iDir < uDirs ; iDir++)
                {
                    wxString strDir(theApp.glob_prefs->shareddir_list[iDir]);
                    uint16_t cnt = strDir.length();
                    tempfile.Write( &cnt, 2);
                    tempfile.Write(strDir.c_str(), cnt);
                }
                // and the incoming folders
                for (int ix = 0 ; ix < theApp.glob_prefs->GetCatCount() ; ix++)
                {
                    wxString strDir;
                    strDir = wxString(theApp.glob_prefs->GetCategory(ix)->incomingpath);
                    uint16_t cnt = strDir.length();
                    tempfile.Write( &cnt, 2);
                    tempfile.Write(strDir.c_str(), cnt);
                }
                Packet *replypacket = new Packet( &tempfile);
                NewSocket_SendPacketOP(3, OP_ASKSHAREDDIRSANS, replypacket, (wxUint32) this, OP_ASKSHAREDDIRS);
            }
            else
            {
                theApp.xmuledlg->AddLogLine(true, _("User %s (%u) requested your shareddirectories-list->%s"),
                client->GetUserName(), client->GetUserID(), _("denied"));
                Packet *replypacket = new Packet(OP_ASKSHAREDDENIEDANS, 0);
                NewSocket_SendPacket(3, replypacket, (wxUint32) this, OP_ASKSHAREDDIRS);
            }
            break;
        }
    case OP_ASKSHAREDFILESDIR:
        {
            theApp.downloadqueue->AddDownDataOverheadOther(size);
            CSafeMemFile data((unsigned char *) packet, size, 0);
            if (size > 1)
            {
                uint16_t cnt;
                data.Read( &cnt, 2);
                if ((size - 2) >= cnt)
                {
                    char *strReqDir = new char[cnt + 1];
                    if (data.Read(strReqDir, cnt))
                    strReqDir[cnt] = 0;
                    if (theApp.dynprefs->Get<long>("see-share") == 0 || (theApp.dynprefs->Get<long>("see-share") == 1 && client->IsFriend()))
                    {
                        theApp.xmuledlg->AddLogLine(true, _("User %s (%u) requested your sharedfiles-list for directory %s->%s"),
                        client->GetUserName(), client->GetUserID(), strReqDir, _("accepted"));
                        CTypedPtrList < CPtrList, CKnownFile *> list;
                        theApp.sharedfiles->GetSharedFilesByDirectory(strReqDir, list);
                        CSafeMemFile tempfile(80);
                        uint16_t cnt = strlen(strReqDir);
                        tempfile.Write( &cnt, 2);
                        tempfile.Write(strReqDir, cnt);
                        uint32_t uFiles = list.GetCount();
                        tempfile.Write( &uFiles, 4);
                        while (list.GetCount())
                        {
                            theApp.sharedfiles->CreateOfferedFilePacket(list.GetHead(), &tempfile);
                            list.RemoveHead();
                        }
                        Packet *replypacket = new Packet( &tempfile);
                        NewSocket_SendPacketOP(3, OP_ASKSHAREDFILESDIRANS, replypacket, (wxUint32) this, OP_ASKSHAREDFILESDIR);
                    }
                    else
                    {
                        theApp.xmuledlg->AddLogLine(true, _("User %s (%u) requested your sharedfiles-list for directory %s->%s"),
                        client->GetUserName(), client->GetUserID(), strReqDir, _("denied"));
                        Packet *replypacket = new Packet(OP_ASKSHAREDDENIEDANS, 0);
                        NewSocket_SendPacket(3, replypacket, (wxUint32) this, OP_ASKSHAREDFILESDIR);
                    }
                    delete[] strReqDir;
                }
            }
            break;
        }
    case OP_ASKSHAREDDIRSANS:
        theApp.downloadqueue->AddDownDataOverheadOther(size);
        if (client->GetFileListRequested() == 1)
        {
            CSafeMemFile data((unsigned char *) packet, size, 0);
            uint32_t uDirs;
            if (data.Read( &uDirs, 4) == 4)
            {
                for (uint32_t iDir = 0 ; iDir != uDirs ; iDir++)
                {
                    uint16_t cnt;
                    data.Read( &cnt, 2);
                    if ((size - 4) >= cnt)
                    {
                        char *strDir = new char[cnt + 1];
                        data.Read(strDir, cnt);
                        strDir[cnt] = 0;
                        theApp.xmuledlg->AddLogLine(true, _("User %s (%u) shares directory %s"),
                        client->GetUserName(), client->GetUserID(), strDir);
                        CSafeMemFile tempfile(80);
                        cnt = strlen(strDir);
                        tempfile.Write( &cnt, 2);
                        tempfile.Write(strDir, cnt);
                        Packet *replypacket = new Packet( &tempfile);
                        NewSocket_SendPacketOP(3, OP_ASKSHAREDFILESDIR, replypacket, (wxUint32) this, OP_ASKSHAREDDIRSANS);
                        client->SetFileListRequested(iDir + 1);
                        delete[] strDir;
                    }
                }
            }
        }
        break;
    case OP_ASKSHAREDFILESDIRANS:
        {
            theApp.downloadqueue->AddDownDataOverheadOther(size);
            if (client->GetFileListRequested() > 0)
            {
                CSafeMemFile data((unsigned char *) packet, size, 0);
                uint16_t cnt;
                if (data.Read( &cnt, 2) == 2)
                {
                    if ((size - 2) >= cnt)
                    {
                        char *strDir = new char[cnt + 1];
                        data.Read(strDir, cnt);
                        strDir[cnt] = 0;
                        theApp.xmuledlg->AddLogLine(true, "User %s (%u) sent sharedfiles-list for directory %s",
                        client->GetUserName(), client->GetUserID(), strDir);
                        delete[] strDir;
                        client->ProcessSharedFileList(packet + data.GetPosition(), size - data.GetPosition());
                    }
                }
            }
            break;
        }
    case OP_ASKSHAREDDENIEDANS:
        theApp.downloadqueue->AddDownDataOverheadOther(size);
        theApp.xmuledlg->AddLogLine(true, _("User %s (%u) denied access to shareddirectories/files-list"),
        client->GetUserName(), client->GetUserID());
        break;
    default:
        theApp.downloadqueue->AddDownDataOverheadOther(size);
        break;
    }
    return true;
}

bool CClientReqSocket::ProcessExtPacket(char *packet, uint32_t size, uint8_t opcode)
{
    NewSocket_DebugPP(client,opcode, size, (unsigned char *) packet);
    switch (opcode)
    {
    case OP_EMULEINFO:
        {
	if(client) {
            theApp.downloadqueue->AddDownDataOverheadOther(size);
            client->ProcessMuleInfoPacket(packet, size);
            client->SendMuleInfoPacket(true);
	}
            break;
        }
    case OP_EMULEINFOANSWER:
        {
if(client) {
            theApp.downloadqueue->AddDownDataOverheadOther(size);
            client->SendMuleInfoPacket(true);
            client->ProcessMuleInfoPacket(packet, size);
}
            break;
        }
    case OP_SECIDENTSTATE:
        {
            client->ProcessSecIdentStatePacket((unsigned char *) packet, size);
            if (client->GetSecureIdentState() == IS_SIGNATURENEEDED)
            {
                client->SendSignaturePacket();
            }
            else if(client->GetSecureIdentState() == IS_KEYANDSIGNEEDED)
            {
                client->SendPublicKeyPacket();
                client->SendSignaturePacket();
            }
            break;
        }
    case OP_PUBLICKEY:
        {
            if (!client->IsBanned())
            {
                client->ProcessPublicKeyPacket((unsigned char *) packet, size);
            }
            break;
        }
    case OP_SIGNATURE:
        {
            client->ProcessSignaturePacket((unsigned char *) packet, size);
            break;
        }
    case OP_COMPRESSEDPART:
        {
            client->ProcessBlockPacket(packet, size, true);
            break;
        }
    case OP_QUEUERANKING:
        {
            theApp.downloadqueue->AddDownDataOverheadOther(size);
            if (size == 12)
            {
                uint16_t 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)
                {
                    //first check shared file list, then download list
                    CKnownFile *file = theApp.sharedfiles->GetFileByID((unsigned char *) packet);
                    if (!file)
                    file = theApp.downloadqueue->GetFileByID((unsigned char *) 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)
                            {
                                NewSocket_SendPacket(3, tosend, (wxUint32) this, OP_REQUESTSOURCES);
                            }
                        }
                    }
                }
            }
            break;
        }
    case OP_ANSWERSOURCES:
        {
            theApp.downloadqueue->AddDownDataOverheadSourceExchange(size);
            CSafeMemFile data((BYTE *) packet, size);
            unsigned char hash[16];
            data.Read(hash, 16);
            CKnownFile *file = theApp.downloadqueue->GetFileByID((unsigned char *) 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, client->GetSourceExchangeVersion());
                }
            }
            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_t ip = vuclient->GetIP();
 memcpy(answer->pBuffer,ip,4);
 uint16_t udpport = vuclient->GetUDPPort();
 memcpy(answer->pBuffer,ip,4);
 theApp.uploadqueue->AddUpDataRateMSOverhead(packet->size);
 printf("::ProcessExtPacket OP_VERIFYUPSREQ --> OP_VERIFYUPSANSWER %02x size=%ld\n",opcode,size);
 SendPacket(answer,true,true);
 }
 }
 case OP_VERIFYUPSANSWER:{
 printf("::ProcessExtPacket OP_VERIFYUPSANSWER %02x size=%ld\n",opcode,size);
    }*/
    default:
        theApp.downloadqueue->AddDownDataOverheadOther(size);
        break;
    }
    return true;
}

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

void CClientReqSocket::OnLost(int nErrorCode)
{
    //if connected --> delete client
    Disconnect();
}

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

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

CClientReqSocket::CClientReqSocket()
{
}

bool CClientReqSocket::Close()
{
    return wxSocketBase::Close();
}

void CClientReqSocket::PacketReceived(Packet *packet)
{
    if(client) 
    {
	if(client->deletethis)
	{
	    packet->prot=0;
	    deletethis=true;
	}
    }
    switch (packet->prot)
    {
    case OP_EDONKEYPROT:
        ProcessPacket(packet->pBuffer, packet->size, packet->opcode);
        break;
    case OP_PACKEDPROT:
        if (packet->UnPackPacket())
        {
            ProcessExtPacket(packet->pBuffer, packet->size, packet->opcode);
        }
        break;
    case OP_EMULEPROT:
        ProcessExtPacket(packet->pBuffer, packet->size, packet->opcode);
        break;
    default:;
    }
}

bool CClientReqSocket::Create()
{
    theApp.listensocket->AddConnection();
    OnInit();
    return TRUE;
}

IMPLEMENT_DYNAMIC_CLASS(CListenSocket, wxSocketServer)
// CListenSocket
// CListenSocket member functions
CListenSocket::CListenSocket(CPreferences *in_prefs, wxSockAddress &addr):
#if wxCHECK_VERSION(2,5,3)
    wxSocketServer(addr, wxSOCKET_NOWAIT | wxSOCKET_REUSEADDR)
#else
	wxSocketServer(addr, wxSOCKET_NOWAIT)
#endif

{
    app_prefs = in_prefs;
    opensockets = 0;
    maxconnectionreached = 0;
    m_OpenSocketsInterval = 0;
    m_nPeningConnections = 0;
    bListening = false;

    m_event_handler = new ListenSocketEvt;
    SetEventHandler(*m_event_handler, -1);
    SetNotify(wxSOCKET_CONNECTION_FLAG |wxSOCKET_INPUT_FLAG |wxSOCKET_OUTPUT_FLAG);
    Notify(TRUE);
}

CListenSocket::~CListenSocket()
{
    delete m_event_handler;

    Discard();
    Close();
    KillAllSockets();
}

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

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

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

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)
        //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.:
        ReStartListening();
        while (m_nPeningConnections)
        {
            m_nPeningConnections--;
            CClientReqSocket *newclient = new CClientReqSocket(app_prefs);
            //if (!Accept(*newclient))
            if (!AcceptWith( *newclient, FALSE))
            newclient->Safe_Delete();
            else
            {
                //newclient->AsyncSelect(FD_WRITE|FD_READ|FD_CLOSE);
                newclient->OnInit();
            }
            AddConnection();
        }
    }
}

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)
        {
            cur_sock->Close();
            cur_sock->Destroy();
        }
        else
        {
            socket_list.GetAt(pos2)->CheckTimeOut();
        }
    }
    if ((GetOpenSockets() + 5 < theApp.dynprefs->Get<long>("max-connections") || 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 ;)
    {
        CClientReqSocket *cur_socket = socket_list.GetNext(pos);
        if (cur_socket->client)
        {
            cur_socket->client->Destroy();
        }
        else
        {
            cur_socket->Destroy();
        }
    }
}

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

bool CListenSocket::TooManySockets(bool bIgnoreInterval)
{
    if (GetOpenSockets() > theApp.dynprefs->Get<long>("max-connections")
/*->*/  || (m_OpenSocketsInterval > theApp.dynprefs->Get<long>("max-per-5") 
/*->*/  && !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);
    }
}

