// 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.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.

#ifdef PRECOMP
    #include "xmule-headers.h"
#endif

#include "ServerSocket4661.h"                // Needed for this Interface's Prototype


#include "ClientList.h"
#include "DownloadQueue.h"
#include "IPFilter.h"
#include "KnownFile.h"
#include "NewSockets.h"
#include "opcodes.h"
#include "SharedFileList.h"
#include "UploadQueue.h"
#include "wintypes.h"
#include "xmule.h"
#include "xmuleDlg.h"
#include "PartFile.h"

#ifndef ID_SOKETTI
#define ID_SOKETTI 7772
#endif

#include <wx/listimpl.cpp>

extern int newprefs04_opt[];
extern wxUint32 glip, glport;

IMPLEMENT_DYNAMIC_CLASS(CServerReqSocket4661, CEMSocket)

    CServerReqSocket4661::CServerReqSocket4661(CPreferences * in_prefs/*, CUpDownClient * in_client*/

)
{
    app_prefs = in_prefs;
    //wxGetApp().serversocket->AddSocket(this);
    timeout_timer =::GetTickCount();
    deletethis = false;
    deltimer = 0;
    hotrank = false;
    SetEventHandler( * wxGetApp().xmuledlg, ID_SOKETTI);
    SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG | wxSOCKET_LOST_FLAG);
    Notify(TRUE);
    struct sockaddr_in sockAddr;
    memset( & sockAddr, 0, sizeof(sockAddr));
    uint32_t nSockAddrLen = sizeof(sockAddr);
    wxIPV4address address;
    GetPeer(address);
    sockAddr.sin_addr.s_addr = GAddress_INET_GetHostAddress(address.GetAddress());
    n_SourceIP = sockAddr.sin_addr.s_addr;
    n_SourcePort = 0;
    n_TempPort = sockAddr.sin_port;
    n_compressionflag = 0;
    a_ClientName ="";
    ssocket = NULL;
}

CServerReqSocket4661::~ CServerReqSocket4661()
{
    if (ssocket)
    {
        ssocket->n_CurrentUsers--;
    }
    SetNotify(0);
    Notify(FALSE);
    wxGetApp().serversocket->RemoveSocket(this);
}

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

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

void CServerReqSocket4661::Disconnect()
{
    byConnected = ES_DISCONNECTED;
    Safe_Delete();
}

void CServerReqSocket4661::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 CServerReqSocket4661::Safe_Delete()
{
    deltimer =::GetTickCount();
    byConnected = ES_DISCONNECTED;
    deletethis = true;
}

void PrintPacket(unsigned char * packet, uint32_t size, uint8_t opcode)
{
    int s = 0, z = 0, li = 0;
    unsigned char c = 0;
    printf("opcode=0x%02x(%u): ", opcode, opcode);
    while (s < size)
    {
        if (! (s% 16))
        {
            li = s;
            printf("\n%04x-%04x: ", s, s + 15);
        }
        printf("%02x ", packet[s]);
        s++;
        if (s == size)
        {
            while (s < (li + 16))
            {
                printf("   ");
                s++;
            }
        }
        if ((! (s% 16)) || (s >= size))
        {
            printf("    ");
            for (int i = li ; i < (li + 16) ; i++)
            {
                if (i <= size)
                {
                    c = packet[i];
                    if (c >= 'a' && c <= 'z')
                    {
                        printf("%c", c);
                    }
                    else if(c >= 'A' && c <= 'Z')
                    {
                        printf("%c", c);
                    }
                    else if(c >= '0' && c <= '9')
                    {
                        printf("%c", c);
                    }
                    else
                    {
                        printf(".");
                    }
                }
                else
                {
                    printf(" ");
                }
            }
        }
    }
    printf("\n");
}

bool CServerReqSocket4661::ToClient_0x01_HELLOANSWER()
{
    wxUint32 n_ServerIP;
    wxUint16 n_ServerPort;
    wxUint8 n_HashSize = 0x10;
    n_ServerIP = wxGetApp().serverconnect->GetLocalIP();
    n_ServerPort = wxGetApp().glob_prefs->GetPort() - 1;
    int i;
    unsigned char serverhash[16];
    for (i = 0 ; i < 16 ; i++)
    {
        serverhash[i] = rand() & 255;
    }
    wxUint32 tagcount = 2;
    CMemFile * data = new CMemFile();
    data->Write( & n_HashSize, 1);
    data->Write( & serverhash, 16);
    data->Write( & n_ServerIP, 4);
    data->Write( & n_ServerPort, 2);
    data->Write( & tagcount, 4);
    CTag tagName(CT_NAME, "eserver");
    tagName.WriteTagToFile(data);
    CTag tagVersion(CT_VERSION, EDONKEYVERSION);
    tagVersion.WriteTagToFile(data);
    Packet * packet = new Packet(data);
    packet->opcode = 0x01;
    SendPacket(packet, true, true);
    delete data;
    return true;
}

bool CServerReqSocket4661::ToClient_0x33_SEARCHRESULT()
{
    if (ssocket)
    {
  	wxUint32 count=0;
        CMemFile * data = new CMemFile();
        data->Write( & count, 4);
        Packet * packet = new Packet(data);
        packet->opcode = 0x33;
        SendPacket(packet, true, true);
        //printf("0x33_SEARCHRESULT to client: %08x:%d\n", n_SourceIP, n_SourcePort);
        delete data;
        return true;
    }
    else
    {
        return false;
    }
}
bool CServerReqSocket4661::ToClient_0x34_SERVERSTATUS()
{
    if (ssocket)
    {
        CMemFile * data = new CMemFile();
        data->Write( & ssocket->n_CurrentUsers, 4);
        data->Write( & ssocket->n_CurrentFiles, 4);
        Packet * packet = new Packet(data);
        packet->opcode = 0x34;
        SendPacket(packet, true, true);
        delete data;
        return true;
    }
    else
    {
        return false;
    }
}

bool CServerReqSocket4661::ToClient_0x38_SERVERMESSAGE(char *info)
{
    wxUint16 length;
    length=strlen(info);
    CMemFile * data = new CMemFile();
    data->Write( & length, 2);
    data->Write( info,length);
    Packet * packet = new Packet(data);
    packet->opcode = 0x38;
    SendPacket(packet, true, true);
    delete data;
    return true;
}

bool CServerReqSocket4661::ToClient_0x40_IDCHANGE()
{
    wxUint32 ip;
    CMemFile * data = new CMemFile();
    ip = n_SourceIP;
    data->Write( & ip, 4);
    Packet * packet = new Packet(data);
    packet->opcode = 0x40;
    SendPacket(packet, true, true);
    delete data;
    return true;
}

bool CServerReqSocket4661::ToClient_0x41_SERVERIDENT()
{
    unsigned char a_ServerHash[16];
    wxUint32 nTags=2;
    wxUint32 n_ServerIP;
    wxUint16 n_ServerPort;
    int i;
    n_ServerIP = wxGetApp().serverconnect->GetLocalIP();
    n_ServerPort = wxGetApp().glob_prefs->GetPort() - 1;
    for(i=0;i<16;i++) {
	a_ServerHash[i]=rand()%0xff;
    }
    CMemFile * data = new CMemFile();
    data->Write( a_ServerHash, 16);
    data->Write(&n_ServerIP,4);
    data->Write(&n_ServerPort,2);
    data->Write(&nTags,4);
    CTag servername(ST_SERVERNAME,"xMule 1.8.2e");
    servername.WriteTagToFile(data);
    CTag serverdesc(ST_DESCRIPTION,"http://www.xmule.ws/");
    serverdesc.WriteTagToFile(data);
    Packet * packet = new Packet(data);
    packet->opcode = 0x41;
    SendPacket(packet, true, true);
    delete data;
    return true;
}


void CServerReqSocket4661::FromClient_0x01_HELLO(unsigned char * packet, wxUint32 size, wxUint8 opcode)
{
    wxUint32 ip, tagcount;
    wxUint16 port;
    CMemFile * data = new CMemFile((Byte *) packet, size);
    if (data->Read( & b_UserHash, 16) == 16)
    {
        if (data->Read( & ip, 4) == 4)
        {
            if (data->Read( & port, 2) == 2)
            {
                n_SourcePort = port;
                if (data->Read( & tagcount, 4) == 4)
                {
                    int i = 0;
                    while (i < tagcount)
                    {
                        CTag * temptag = new CTag(data);
                        if (temptag->tag)
                        {
                            switch (temptag->tag->specialtag)
                            {
                            case CT_NAME:
                                a_ClientName = temptag->tag->stringvalue;
                                //faz:printf("CT_NAME    %s\n",a_ClientName.GetData());
                                break;
                            case CT_PORT:
                                n_SourcePort = temptag->tag->intvalue;
                                //faz:printf("CT_PORT    %u\n",n_SourcePort);
                                break;
                            case CT_VERSION:
                                n_ClientVersion = temptag->tag->intvalue;
                                //faz:printf("CT_VERSION %02x(%u)\n",n_ClientVersion,n_ClientVersion);
                                break;
                            case ET_COMPRESSION:
                                n_compressionflag = temptag->tag->intvalue;
                                //faz:printf("ET_COMPRESSION %08x\n",n_compressionflag);
                                break;
                            default:
                                printf("tagcount=%u(%u): ", i + 1, tagcount);
                                printf("UNKNOWN specialtag: %u(%02x)\n", temptag->tag->specialtag, temptag->tag->specialtag);
                                break;
                            }
                            i++;
                        }
                        else
                        {
                            printf("    error on CTag\n");
                            tagcount = 0;
                        }
                    }
                    if (tagcount)
                    {
                        //(ToClient_0x01_HELLOANSWER())
                        if (ToClient_0x40_IDCHANGE())
                        {
                            (void) ToClient_0x34_SERVERSTATUS();
    			    (void) ToClient_0x38_SERVERMESSAGE("server version xMule 1.8.2e");
    			    (void) ToClient_0x41_SERVERIDENT();
                        }
                    }
                }
            }
        }
    }
    delete data;
}

void CServerReqSocket4661::FromClient_0x14_GETSERVERLIST(unsigned char * packet, wxUint32 size, wxUint8 opcode)
{
}
void CServerReqSocket4661::FromClient_0x16_SEARCHREQUEST(unsigned char * packet, wxUint32 size, wxUint8 opcode)
{
    CMemFile * data = new CMemFile((Byte *) packet, size);
    CTag * temptag = new CTag(data);
    if(temptag->tag) {
	switch(temptag->tag->specialtag) 
	{
	case 1:
		break;
	}
    }
    ToClient_0x33_SEARCHRESULT();
    delete data;
    delete temptag;
}

void CServerReqSocket4661::FromClient_0x19_GETSOURCES(unsigned char * packet, wxUint32 size, wxUint8 opcode)
{
}

bool CServerReqSocket4661::ProcessPacket(unsigned char * packet, wxUint32 size, wxUint8 opcode)
{
    NewSocket_DebugPP(NULL, opcode, size, (unsigned char *) packet);
    switch (opcode)
    {
    case OP_HELLO:
        FromClient_0x01_HELLO(packet, size, opcode);
        break;
    case OP_GETSERVERLIST:
        FromClient_0x14_GETSERVERLIST(packet, size, opcode);
        break;
    case OP_SEARCHREQUEST:
        FromClient_0x16_SEARCHREQUEST(packet, size, opcode);
        break;
    case OP_GETSOURCES:
        FromClient_0x19_GETSOURCES(packet, size, opcode);
        break;
    default:
        printf("CServerReqSocket4661::ProcessPacket   Unknown Opcode %02x(%u)\n", opcode, opcode);
        wxGetApp().downloadqueue->AddDownDataOverheadOther(size);
        break;
    }
    return true;
}

bool CServerReqSocket4661::ProcessExtPacket(unsigned char * packet, uint32_t size, uint8_t opcode)
{
    //printf("CServerReqSocket4661: ProcessExtPacket opcode=%02x size=%u\n", opcode, size);
    NewSocket_DebugPP(NULL, opcode, size, (unsigned char *) packet);
    switch (opcode)
    {
    case OP_EMULEINFO:
        //printf("CServerReqSocket4661::ProcessExtPacket   Unknown Opcode %02x(%u)\n", opcode, opcode);
        break;
    case OP_EMULEINFOANSWER:
        //printf("CServerReqSocket4661::ProcessExtPacket   Unknown Opcode %02x(%u)\n", opcode, opcode);
        break;
    default:
        printf("CServerReqSocket4661::ProcessExtPacket   Unknown Opcode %02x(%u)\n", opcode, opcode);
        wxGetApp().downloadqueue->AddDownDataOverheadOther(size);
        break;
    }
    return true;
}

void CServerReqSocket4661::OnInit()
{
    if (ssocket)
    {
        ssocket->n_CurrentUsers++;
    }
}

void CServerReqSocket4661::OnLost(int nErrorCode)
{
    Disconnect();
}

void CServerReqSocket4661::OnInput(int nErrorCode)
{
    timeout_timer =::GetTickCount();
    CEMSocket::OnReceive(nErrorCode);
}

void CServerReqSocket4661::OnOutput(int nErrorCode)
{
    timeout_timer =::GetTickCount();
    CEMSocket::OnSend(nErrorCode);
}

CServerReqSocket4661::CServerReqSocket4661()
{
}

void CServerReqSocket4661::PacketReceived(Packet * packet)
{
    if (!n_SourceIP)
    {
        n_SourceIP = glip;
        if (!n_SourcePort)
        {
            n_SourcePort = glport;
        }
    }
    switch (packet->prot)
    {
    case OP_EDONKEYPROT:
        ProcessPacket((unsigned char *) packet->pBuffer, packet->size, packet->opcode);
        break;
    case OP_PACKEDPROT:
        if (packet->UnPackPacket())
        {
            ProcessExtPacket((unsigned char *) packet->pBuffer, packet->size, packet->opcode);
        }
        break;
    case OP_EMULEPROT:
        ProcessExtPacket((unsigned char *) packet->pBuffer, packet->size, packet->opcode);
        break;
    default:;
    }
}

bool CServerReqSocket4661::Create()
{
    wxGetApp().serversocket->AddConnection();
    OnInit();
    return TRUE;
}

IMPLEMENT_DYNAMIC_CLASS(CServerSocket4661, wxSocketServer)

CServerSocket4661::CServerSocket4661(CPreferences * in_prefs, wxSockAddress & addr)
: wxSocketServer(addr, wxSOCKET_NOWAIT)
{
    app_prefs = in_prefs;
    opensockets = 0;
    maxconnectionreached = 0;
    m_OpenSocketsInterval = 0;
    m_nPendingConnections = 0;
    bListening = true;
    SetEventHandler( * wxGetApp().xmuledlg, ID_SOKETTI);
    SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG);
    Notify(TRUE);
    n_lowidcounter = 0;
    n_CurrentUsers = 0;
    n_CurrentFiles = 0;
}

CServerSocket4661::~ CServerSocket4661()
{
    Discard();
    Close();
    KillAllSockets();
}

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

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

void CServerSocket4661::ReStartListening()
{
    bListening = true;
    if (m_nPendingConnections)
    {
        m_nPendingConnections--;
        OnAccept(0);
    }
}

void CServerSocket4661::OnAccept(int nErrorCode)
{
    if (!nErrorCode)
    {
        m_nPendingConnections++;
        if (m_nPendingConnections < 1)
        {
            m_nPendingConnections = 1;
        }
        if (TooManySockets(true) && !wxGetApp().serverconnect->IsConnecting())
        {
            StopListening();
        }
        else
        {
            if (!bListening)
            {
                ReStartListening();
            }
            while (m_nPendingConnections)
            {
                m_nPendingConnections--;
                CServerReqSocket4661 * newclient = new CServerReqSocket4661(app_prefs);
                if (!AcceptWith( * newclient, FALSE))
                {
                    newclient->Safe_Delete();
                }
                else
                {
                    newclient->ssocket = this;
                    n_CurrentFiles = 7;
                    newclient->OnInit();
                }
                AddConnection();
            }
        }
    }
}

void CServerSocket4661::Process()
{
    POSITION pos2;
    m_OpenSocketsInterval = 0;
    opensockets = 0;
    for (POSITION pos1 = socket_list.GetHeadPosition() ; (pos2 = pos1) != NULL ;)
    {
        socket_list.GetNext(pos1);
        CServerReqSocket4661 * cur_sock = socket_list.GetAt(pos2);
        opensockets++;
        if (cur_sock->deletethis)
        {
            delete cur_sock;
            cur_sock = NULL;
        }
        else
        {
            socket_list.GetAt(pos2)->CheckTimeOut();
        }
    }
    if ((GetOpenSockets() + 5 < app_prefs->GetMaxConnections() || wxGetApp().serverconnect->IsConnecting()) && !bListening)
    ReStartListening();
}

void CServerSocket4661::RecalculateStats()
{
    memset(m_ConnectionStates, 0, 6);
    POSITION pos1, pos2;
    for (pos1 = socket_list.GetHeadPosition() ; (pos2 = pos1) != NULL ;)
    {
        socket_list.GetNext(pos1);
        CServerReqSocket4661 * 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 CServerSocket4661::AddSocket(CServerReqSocket4661 * toadd)
{
    socket_list.AddTail(toadd);
}

void CServerSocket4661::RemoveSocket(CServerReqSocket4661 * 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 CServerSocket4661::KillAllSockets()
{
    for (POSITION pos = socket_list.GetHeadPosition() ; pos != 0 ;)
    {
        CServerReqSocket4661 * cur_socket = socket_list.GetNext(pos);
        delete cur_socket;
    }
}

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

bool CServerSocket4661::TooManySockets(bool bIgnoreInterval)
{
    if (GetOpenSockets() > app_prefs->GetMaxConnections() || (m_OpenSocketsInterval > wxGetApp().glob_prefs->GetMaxConperFive() && !bIgnoreInterval))
    {
        return true;
    }
    else
    return false;
}

bool CServerSocket4661::IsValidSocket(CServerReqSocket4661 * totest)
{
    return socket_list.Find(totest);
}

void CServerSocket4661::Debug_ClientDeleted(CUpDownClient * deleted)
{
    POSITION pos1, pos2;
    for (pos1 = socket_list.GetHeadPosition() ; (pos2 = pos1) != NULL ;)
    {
        socket_list.GetNext(pos1);
        CServerReqSocket4661 * cur_sock = socket_list.GetAt(pos2);
    }
}

