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

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif // HAVE_CONFIG_H

// Test if we have _GNU_SOURCE before the next step will mess up
// setting __USE_GNU
// (only needed for gcc-2.95 compatibility, gcc 3.2 always defines it)
#include "wx/setup.h"

// Mario Sergio Fujikawa Ferreira <lioux@FreeBSD.org>
// to detect if this is a *BSD system
#if defined(HAVE_SYS_PARAM_H)
#include <sys/param.h>
#endif

#ifdef PRECOMP
#	include "xmule-headers.h"
#else
#include "ServerList.h"

#include "config.h"                         // HAVE_CRYPTOPP
#	include "DownloadQueue.h"
#	include "HTTPDownloadDlg.h"
#include "ListenSocket.h"                   // CListenSocket
#include "otherfunctions.h"                 // Needed for GetResString - audited 5 Nov 2004
#include "otherstructs.h"                   // Needed for ServerMet_Struct - audited 5 Nov 2004
#include "packets.h"                        // Packet
#include "PartFile.h"                       // Needed for SRV_PR_HIGH - audited 5 Nov 2004
#include "Preferences.h"                    // CPreferences::addresses_list
#include "SafeFile.h"                       // CSafeMemFile
#include "ServerList.h"                     // CServerList
#	include "ServerWnd.h"
#include "sockets.h"                        // Needed for CServerConnect - audited 5 Nov 2004
#	include "xmule.h"
#	include "xmuleDlg.h"
#endif

#if !HAVE_CRYPTOPP
    #include "CryptoXMpp.h"
#else
    #include <cryptopp/cryptlib.h>
    #include <cryptopp/rsa.h>
    #define CryptoXMpp CryptoPP
#endif

#ifdef WORDS_BIGENDIAN
    #define LE_BE(X) CryptoXMpp::ByteReverse(X)
#else
    #define LE_BE(X) X
#endif

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

#include <wx/filename.h>                    // FileExists wxFileName
#include <wx/msgdlg.h>
#include <wx/listimpl.cpp>
//WX_DEFINE_LIST(CServerListList);

#include <iostream>
using std::cout;
using std::endl;

CServerList::CServerList(CPreferences *in_prefs):
list(wxKEY_NONE)
{
    servercount = 0;
    version = 0;
    serverpos = 0;
    searchserverpos = 0;
    statserverpos = 0;
    app_prefs = in_prefs;
    //udp_timer = 0;
    udp_timer.SetOwner(theApp.xmuledlg, 4322);
    delservercount = 0;
    m_nLastSaved =::GetTickCount();
}

void CServerList::AutoUpdate()
{
    if (app_prefs->addresses_list.empty() == true)
    {
        wxMessageBox(GetResString(IDS_ERR_EMPTYADRESSESDAT), GetResString(IDS_ERR_EMPTYADRESSESDAT_TITLE), wxICON_ERROR |wxCENTRE |wxOK);
        return;
    }
    bool bDownloaded = false;

    wxString servermetdownload(theApp.dynprefs->Get<wxString>("app-directory") + wxT("server_met.download"));
    wxString servermet(theApp.dynprefs->Get<wxString>("app-directory") + wxT("server.met"));
    wxString strURLToDownload;
    BackupFile(servermet, ".old");
    wxRemoveFile(servermetdownload);

    std::list<wxString>::iterator it = app_prefs->addresses_list.begin();
    while (!bDownloaded && (it != app_prefs->addresses_list.end()))
    {
        CHTTPDownloadDlg *dlgDownload;
        strURLToDownload = *it;
        //dlgDownload.m_sURLToDownload = strURLToDownload.GetBuffer();
        //dlgDownload.m_sFileToDownloadInto = servermetdownload;
        dlgDownload = new CHTTPDownloadDlg(theApp.xmuledlg, strURLToDownload, servermetdownload);

        if (dlgDownload->ShowModal() == 0)
        {
            bDownloaded = true;
        }
        else
        {
            theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_FAILEDDOWNLOADMET), strURLToDownload.c_str());
        }

        delete dlgDownload;
        ++it;
    }
    if (bDownloaded)
    {
        wxRenameFile(servermet, servermetdownload);
        wxRenameFile(servermet + ".old", servermet);
    }
    else
    {
        wxRemoveFile(servermet);
        wxRenameFile(servermet + ".old", servermet);
    }
}

bool CServerList::Init()
{
    // auto update the list by using an url
    if (theApp.dynprefs->Get<bool>("autoupdate-server"))
    AutoUpdate();
    // Load Metfile
#if defined(__DEBUG__)
    printf("*** reading servers\n");
#endif
    wxString strPath(theApp.dynprefs->Get<wxString>("app-directory") + wxT("server.met"));
    bool bRes = AddServermetToList(strPath, false);

    if (theApp.dynprefs->Get<bool>("autoupdate-server"))
    {
        bool bRes2 = AddServermetToList(strPath);
        if (!bRes &&bRes2)
        bRes = true;
    }
    // insert static servers from textfile
    strPath = theApp.dynprefs->Get<wxString>("app-directory") + wxT("staticservers.dat");
    AddServersFromTextFile(strPath);
    return bRes;
}

bool CServerList::AddServermetToList(wxString strFile, bool merge)
{
    if (!merge)
    {
        theApp.xmuledlg->serverwnd->serverlistctrl->DeleteAllItems();
        this->RemoveAllServers();
    }
    CSafeFile servermet;
    //  /*try*/{
    if (!wxFileExists(strFile))
    {
        theApp.xmuledlg->AddLogLine(false, _("Failed to load server.met!"));
        return false;
    }
    if (!servermet.Open(strFile, CFile::read))
    {
        //CFile::modeRead|CFile::osSequentialScan)){
        theApp.xmuledlg->AddLogLine(false, _("Failed to load server.met!"));
        return false;
    }
    if ((1 != servermet.Read( &version, 1)) || (version != 0xE0 && version != MET_HEADER))
    {
        servermet.Close();
        theApp.xmuledlg->AddLogLine(false, GetResString(IDS_ERR_BADSERVERMETVERSION), version);
        return false;
    }
    try
    {
        uint32_t fservercount;
        if (4 != servermet.Read( &fservercount, 4))
        throw CInvalidPacket();
	fservercount = LE_BE((CryptoXMpp::word32)fservercount);
        ServerMet_Struct sbuffer;
        uint32_t iAddCount = 0;
        theApp.xmuledlg->serverwnd->serverlistctrl->Freeze();
        for (uint32_t j = 0 ; j < fservercount ; j++)
        {
            // get server
            if (sizeof(ServerMet_Struct) != servermet.Read( &sbuffer, sizeof(ServerMet_Struct)))
            throw CInvalidPacket();
            CServer *newserver = new CServer( &sbuffer);
            //add tags
            for (uint32_t i = 0; i < LE_BE((CryptoXMpp::word32)sbuffer.tagcount); i++)
            {
                newserver->AddTagFromFile(&servermet);
            }

            // set listname for server
            if (!newserver->GetListName())
            {
                char *listname = new char[strlen(newserver->GetAddress()) + 8];
                sprintf(listname, "Server %s", newserver->GetAddress());
                newserver->SetListName(listname);
                delete listname;
            }
            if (!theApp.xmuledlg->serverwnd->serverlistctrl->AddServer(newserver, true))
            {
                CServer *update = theApp.serverlist->GetServerByAddress(newserver->GetAddress(), newserver->GetPort());
                if (update)
                {
                    update->SetListName(newserver->GetListName());
                    if (newserver->GetDescription())
                    update->SetDescription(newserver->GetDescription());
                    theApp.xmuledlg->serverwnd->serverlistctrl->RefreshServer(update);
                }
                delete newserver;
            }
            else
            iAddCount++;
            // don't yield all the time
            if (j%75 == 0)
            {
                theApp.Yield();
            }
        }
        theApp.xmuledlg->serverwnd->serverlistctrl->Thaw();
        if (!merge)
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_SERVERSFOUND), fservercount);
        else
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_SERVERSADDED), iAddCount, fservercount - iAddCount);
    }
    catch(CInvalidPacket)
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_BADSERVERLIST));
        servermet.Close();
        return false;
    }
    servermet.Close();
#if 0
    catch(CFileException *error)
    {
        if (error->m_cause == CFileException::endOfFile)
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_BADSERVERLIST));
        else
        {
            char buffer[150];
            error->GetErrorMessage(buffer, 150);
            theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_FILEERROR_SERVERMET), buffer);
        }
        //memleak fix:
        error->Delete();
    }
#endif
    return true;
}

bool CServerList::AddServer(CServer *in_server)
{
    if (theApp.dynprefs->Get<bool>("filter-bad-ip") == true)
    if (!IsGoodServerIP(in_server))
    return false;
    CServer *test_server = GetServerByAddress(in_server->GetAddress(), in_server->GetPort());
    if (test_server)
    {
        test_server->ResetFailedCount();
        theApp.xmuledlg->serverwnd->serverlistctrl->RefreshServer(test_server);
        return false;
    }
    //AddTail(in_server);:
    list.AddTail(in_server);
    return true;
}

void CServerList::ServerStats()
{
    if (theApp.serverconnect->IsConnected() &&list.GetCount() > 0)
    {
        CServer *ping_server = GetNextStatServer();
        CServer *test = ping_server;
        if (!ping_server)
        return;
        while (ping_server->GetLastPinged() != 0 && (::GetTickCount() - ping_server->GetLastPinged()) < UDPSERVSTATREASKTIME)
        {
            ping_server = this->GetNextStatServer();
            if (ping_server == test)
            return;
        }
        if (ping_server->GetFailedCount() >= theApp.dynprefs->Get<long>("server-connect-tries") && theApp.dynprefs->Get<bool>("remove-dead-server"))
        {
            theApp.xmuledlg->serverwnd->serverlistctrl->RemoveServer(ping_server);
            return;
        }
        Packet *packet = new Packet(OP_GLOBSERVSTATREQ, 4);
        srand((unsigned) time(NULL));
        uint32_t time = 0x55AA0000 + (uint16_t) rand();
        ping_server->SetChallenge(time);
        memcpy(packet->pBuffer, &time, 4);
        ping_server->SetLastPinged(::GetTickCount());
        ping_server->AddFailedCount();
        theApp.xmuledlg->serverwnd->serverlistctrl->RefreshServer(ping_server);
#if defined(__DEBUG__)
//	printf("CServerList::ServerStats --> OP_GLOBSERVSTATREQ size=%d\n",packet->size);
#endif
        theApp.serverconnect->SendUDPPacket(packet, ping_server, true);
    }
}

 /*filter Servers with invalid IP's / Port
 0.*
 10.*
 172.16.0.0 - 172.31.255.255
 192.168.0.0 - 192.168.255.255
 127.*
 2.1.*  - Kry
    */

bool CServerList::IsGoodServerIP(CServer *in_server)
{
    if (in_server->HasDynIP())
    return true;
    char *pch;
#if !defined(__DEBUG__)
    char ipx[24];
    sprintf(ipx, "%s", in_server->GetFullIP());
    // Determine first and second number if the given IP
    pch = strtok(ipx, ".");
    wxString first = pch;
    pch = strtok(NULL, ".");
    wxString second = pch;
    int secondI = atoi(second);
    if (first == "172" && (secondI >= 16 &&secondI <= 31)) return false;
    if (first == "192" &&secondI == 168) return false;
    if (first == "2" &&secondI == 1) return false;
    if (first == "0" || first == "10" || first == "127"
    || in_server->GetPort() == 0) return false;
#endif
    return true;
}

void CServerList::RemoveServer(CServer *out_server)
{
    for (POSITION pos = list.GetHeadPosition() ; pos != NULL ;)
    {
        POSITION pos2 = pos;
        CServer *test_server = list.GetNext(pos);
        if (test_server == out_server)
        {
            if (theApp.downloadqueue->cur_udpserver == out_server)
            theApp.downloadqueue->cur_udpserver = 0;
            list.RemoveAt(pos2);
            delservercount++;
            delete test_server;
            return;
        }
    }
}

void CServerList::RemoveAllServers()
{
    for (POSITION pos = list.GetHeadPosition() ; pos != NULL ; pos = list.GetHeadPosition())
    {
        delete list.GetAt(pos);
        list.RemoveAt(pos);
        delservercount++;
    }
}

void CServerList::GetStatus(uint32_t &total, uint32_t &failed, uint32_t &user, uint32_t &file, uint32_t &tuser, uint32_t &tfile, float &occ)
{
    total = list.GetCount();
    failed = 0;
    user = 0;
    file = 0;
    tuser = 0;
    tfile = 0;
    occ = 0;
    uint32_t maxusers = 0;
    uint32_t tuserk = 0;
    CServer *curr;
    for (POSITION pos = list.GetHeadPosition() ; pos != 0 ; list.GetNext(pos))
    {
        curr = (CServer *) list.GetAt(pos);
        if (curr->GetFailedCount())
        {
            failed++;
        }
        else
        {
            user += curr->GetUsers();
            file += curr->GetFiles();
        }
        tuser += curr->GetUsers();
        tfile += curr->GetFiles();
        if (curr->GetMaxUsers())
        {
            // total users on servers with known maximum:
            tuserk += curr->GetUsers();
            maxusers += curr->GetMaxUsers();
        }
    }
    if (maxusers > 0) occ = (float)(tuserk *100) /maxusers;
}

void CServerList::GetUserFileStatus(uint32_t &user, uint32_t &file)
{
    user = 0;
    file = 0;
    CServer *curr;
    for (POSITION pos = list.GetHeadPosition() ; pos != 0 ; list.GetNext(pos))
    {
        curr = (CServer *) list.GetAt(pos);
        if (!curr->GetFailedCount())
        {
            user += curr->GetUsers();
            file += curr->GetFiles();
        }
    }
}

CServerList::~CServerList()
{
    SaveServermetToFile();
    for (POSITION pos = list.GetHeadPosition() ; pos != NULL ; pos = list.GetHeadPosition())
    {
        delete list.GetAt(pos);
        list.RemoveAt(pos);
    }
    udp_timer.Stop();
}

#include <wx/txtstrm.h>
#include <wx/wfstream.h>

#ifndef MAX_PATH
// *BSD compatibility
#  if (defined(BSD) && (BSD >= 199103))
#     define MAX_PATH MAXPATHLEN
#  else
#     define MAX_PATH 1024
#  endif
#endif

void CServerList::AddServersFromTextFile(wxString strFilename, bool isstaticserver)
{
    wxString strLine;
    //CStdioFile f;
    //wxFFile f;
    //if (!f.Open(strFilename, CFile::modeRead | CFile::typeText))
    if (!wxFileName::FileExists(strFilename))
    {
        // no file. do nothing.
        return;
    }
    wxFileInputStream stream(strFilename);
    if (!stream.Ok())
    return;
    wxTextInputStream f(stream);
#if 0
    if (!f.Open(strFilename, "r"))
    return;
#endif
    while (strLine = f.ReadLine())
    {
        // stop iteration if end of file is met..:
        if (stream.Eof()) break;
        // (while won't do it for us)
        // format is host:port,Name
        if (strLine.Length() < 5)
        continue;
        if (strLine.GetChar(0) == '#' || strLine.GetChar(0) == '/')
        continue;
        // fetch host
        int pos = strLine.Find(":");
        if (pos == - 1)
        {
            pos = strLine.Find(",");
            if (pos == - 1)
            continue;
        }
        wxString strHost = strLine.Left(pos);
        strLine = strLine.Mid(pos + 1);
        // fetch  port
        pos = strLine.Find(",");
        if (pos == - 1)
        continue;
        wxString strPort = strLine.Left(pos);
        strLine = strLine.Mid(pos + 1);
        // Barry - fetch priority
        pos = strLine.Find(",");
        int priority = SRV_PR_HIGH;
        if (pos == 1)
        {
            wxString strPriority = strLine.Left(pos);
            try
            {
                priority = atoi(strPriority.GetData());
                if ((priority < 0) || (priority > 2))
                priority = SRV_PR_HIGH;
            }
            catch(...)
            {
            }
            strLine = strLine.Mid(pos + 1);
        }
        // fetch name
        wxString strName = strLine;
        strName.Replace("\r", "");
        strName.Replace("\n", "");
        // create server object and add it to the list
        CServer *nsrv = new CServer(atoi(strPort), (char *) strHost.GetData());
        nsrv->SetListName((char *) strName.GetData());
        nsrv->SetIsStaticMember(true);
        // Barry - Was always high
        nsrv->SetPreference(priority);
        if (!theApp.xmuledlg->serverwnd->serverlistctrl->AddServer(nsrv, true))
        {
            delete nsrv;
            CServer *srvexisting = GetServerByAddress((char *) strHost.GetData(), atoi(strPort));
            if (srvexisting)
            {
                srvexisting->SetListName((char *) strName.GetData());
                srvexisting->SetIsStaticMember(true);
                // Barry - Was always high
                srvexisting->SetPreference(priority);
                if (theApp.xmuledlg->serverwnd) theApp.xmuledlg->serverwnd->serverlistctrl->RefreshServer(srvexisting);
            }
        }
    }
    //f.Close();
    //printf("todo. addserversfromtextfile needs work\n");
}

void CServerList::MoveServerDown(CServer *aServer)
{
    POSITION pos1, pos2;
    uint16_t i = 0;
    for (pos1 = list.GetHeadPosition() ; (pos2 = pos1) != NULL ;)
    {
        list.GetNext(pos1);
        CServer *cur_server = list.GetAt(pos2);
        if (cur_server == aServer)
        {
            list.AddTail(cur_server);
            list.RemoveAt(pos2);
            return;
        }
        i++;
        if (i == list.GetCount())
        break;
    }
}

void CServerList::Sort()
{
    POSITION pos1, pos2;
    uint16_t i = 0;
    for (pos1 = list.GetHeadPosition() ; (pos2 = pos1) != NULL ;)
    {
        list.GetNext(pos1);
        CServer *cur_server = list.GetAt(pos2);
        if (cur_server->GetPreferences() == PR_HIGH)
        {
            list.AddHead(cur_server);
            list.RemoveAt(pos2);
        }
        else if(cur_server->GetPreferences() == PR_LOW)
        {
            list.AddTail(cur_server);
            list.RemoveAt(pos2);
        }
        i++;
        if (i == list.GetCount())
        break;
    }
}

CServer *CServerList::GetNextServer()
{
    CServer *nextserver = 0;
    uint32_t i = 0;
    if (serverpos >= ((uint32_t) list.GetCount())) return 0;
    while (!nextserver &&i != list.GetCount())
    {
        POSITION posIndex = list.FindIndex(serverpos);
        if (posIndex == NULL)
        {
            // check if search position is still valid (could be corrupted by server delete operation)
            posIndex = list.GetHeadPosition();
            //<<--9/27/02 zg:
            serverpos = 0;
        }
        nextserver = list.GetAt(posIndex);
        serverpos++;
        i++;
        // TODO: Add more option to filter bad server like min ping, min users etc
        //if (nextserver->preferences = ?)
        //	nextserver = 0;
        //if (serverpos == list.GetCount()) return 0;//			serverpos = 0;
    }
    return nextserver;
}

CServer *CServerList::GetNextSearchServer()
{
    CServer *nextserver = 0;
    uint32_t i = 0;
    while (!nextserver &&i != list.GetCount())
    {
        POSITION posIndex = list.FindIndex(searchserverpos);
        if (posIndex == NULL)
        {
            // check if search position is still valid (could be corrupted by server delete operation)
            posIndex = list.GetHeadPosition();
            searchserverpos = 0;
        }
        nextserver = list.GetAt(posIndex);
        searchserverpos++;
        i++;
        if (searchserverpos == list.GetCount())
        searchserverpos = 0;
    }
    return nextserver;
}

CServer *CServerList::GetNextStatServer()
{
    CServer *nextserver = 0;
    uint32_t i = 0;
    while (!nextserver &&i != list.GetCount())
    {
        POSITION posIndex = list.FindIndex(statserverpos);
        if (posIndex == NULL)
        {
            // check if search position is still valid (could be corrupted by server delete operation)
            posIndex = list.GetHeadPosition();
            statserverpos = 0;
        }
        nextserver = list.GetAt(posIndex);
        statserverpos++;
        i++;
        if (statserverpos == list.GetCount())
        statserverpos = 0;
    }
    return nextserver;
}

bool CServerList::BroadCastPacket(Packet *packet)
{
    // unused atm . but might be useful later
    if (udp_timer.IsRunning())
    return false;
    //udp_timer = SetTimer(0,4322,UDPSEARCHSPEED,CServerList::UDPTimerProc);
    udp_timer.Start(UDPSEARCHSPEED);
    broadcastpos = list.GetHeadPosition();
    broadcastpacket = packet;
    return true;
}

void CServerList::SendNextPacket()
{
    if (theApp.listensocket->TooManySockets())
    {
        //KillTimer(0,udp_timer);
        udp_timer.Stop();
        delete broadcastpacket;
        return;
    }
    if (broadcastpos != 0)
    {
        CServer *cur_server = list.GetAt(broadcastpos);
        if (cur_server != theApp.serverconnect->GetCurrentServer())
#if defined(__DEBUG__)
	printf("CServerList::ServerStats --> %02 size=%d\n",broadcastpacket->opcode,broadcastpacket->size);
#endif
        theApp.serverconnect->SendUDPPacket(broadcastpacket, cur_server, false);
        list.GetNext(broadcastpos);
    }
    else
    {
        //illTimer(0,udp_timer);
        udp_timer.Stop();
        delete broadcastpacket;
    }
}

void CServerList::CancelUDPBroadcast()
{
    if (udp_timer.IsRunning())
    {
        //KillTimer(0,udp_timer);
        //udp_timer = 0;
        udp_timer.Stop();
        delete broadcastpacket;
    }
}

#if 0
void CALLBACK CServerList::UDPTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
    theApp.serverlist->SendNextPacket();
}

#endif

CServer *CServerList::GetNextServer(CServer *lastserver)
{
    if (list.IsEmpty())
    return 0;
    if (!lastserver) return list.GetHead();
    POSITION pos = list.Find(lastserver);
    if (!pos)
    {
        return list.GetHead();
    }
    list.GetNext(pos);
    if (!pos)
    return NULL;
    else
    return list.GetAt(pos);
}

CServer *CServerList::GetServerByIP(wxUint32 nIP)
{
    POSITION pos = list.GetHeadPosition();
    while (pos)
    {
        CServer *s = list.GetNext(pos);
        if (s->GetIP() == nIP) return s;
    }
    return NULL;
}

CServer *CServerList::GetServerByIP(wxUint32 nIP, wxUint16 nPort)
{
    POSITION pos = list.GetHeadPosition();
    while (pos)
    {
        CServer *s = list.GetNext(pos);
        if (s->GetIP() == nIP &&s->GetPort() == nPort) return s;
    }
    return NULL;
}

CServer *CServerList::GetServerByAddress(char *address, uint16_t port)
{
    for (POSITION pos = list.GetHeadPosition() ; pos != 0 ; list.GetNext(pos))
    {
        // i_a: small speed optimization:
        CServer *s = list.GetAt(pos);
        if (port == s->GetPort() && !strcmp(s->GetAddress(), address))
        return s;
    }
    return NULL;
}

bool CServerList::SaveServermetToFile()
{
    // oops.. don't save ALL the time :):
    m_nLastSaved =::GetTickCount();
    char *newservermet = new char[theApp.dynprefs->Get<wxString>("app-directory").length() + MAX_PATH];
    sprintf(newservermet, "%sserver.met.new", static_cast<const char *>(theApp.dynprefs->Get<wxString>("app-directory").c_str()));
    //	theApp.xmuledlg->AddLogLine( false, "%s", newservermet );
    {
        FILE *servermet = fopen(newservermet, "wb");
        if (!servermet)
        {
            theApp.xmuledlg->AddLogLine(false, GetResString(IDS_ERR_SAVESERVERMET));
            //mf:
            delete[] newservermet;
            return false;
        }
        version = 0xE0;
        version = LE_BE((CryptoXMpp::word32)version);

        fwrite( &version, 1, 1, servermet);
        
        uint32_t fservercount = list.GetCount();
        uint32_t w_fservercount = LE_BE((CryptoXMpp::word32)fservercount);
        fwrite(&w_fservercount, 4, 1, servermet);

        ServerMet_Struct sbuffer;
        CServer *nextserver;
        for (uint32_t j = 0 ; j != fservercount ; j++)
        {
            nextserver = this->GetServerAt(j);
            sbuffer.ip = LE_BE((CryptoXMpp::word32)nextserver->GetIP());
            sbuffer.port = LE_BE((CryptoXMpp::word32)nextserver->GetPort());
            uint16_t tagcount = 11;
            if (nextserver->GetListName())
            tagcount++;
            if (nextserver->GetDynIP())
            tagcount++;
            if (nextserver->GetDescription())
            tagcount++;
            sbuffer.tagcount = LE_BE((CryptoXMpp::word32)tagcount);
            fwrite( &sbuffer, 1, sizeof(sbuffer), servermet);
            if (nextserver->GetListName())
            {
                CTag servername(ST_SERVERNAME, nextserver->GetListName());
                servername.WriteTagToFile(servermet);
            }
            if (nextserver->GetDynIP())
            {
                CTag serverdynip(ST_DYNIP, nextserver->GetDynIP());
                serverdynip.WriteTagToFile(servermet);
            }
            if (nextserver->GetDescription())
            {
                CTag serverdesc(ST_DESCRIPTION, nextserver->GetDescription());
                serverdesc.WriteTagToFile(servermet);
            }
            CTag serverfail(ST_FAIL, nextserver->GetFailedCount());
            serverfail.WriteTagToFile(servermet);
            CTag serverpref(ST_PREFERENCE, nextserver->GetPreferences());
            serverpref.WriteTagToFile(servermet);
            CTag serveruser("users", nextserver->GetUsers());
            serveruser.WriteTagToFile(servermet);
            CTag serverfiles("files", nextserver->GetFiles());
            serverfiles.WriteTagToFile(servermet);
            CTag serverping(ST_PING, nextserver->GetPing());
            serverping.WriteTagToFile(servermet);
            CTag serverlastp(ST_LASTPING, nextserver->GetLastPinged());
            serverlastp.WriteTagToFile(servermet);
            CTag servermaxusers(ST_MAXUSERS, nextserver->GetMaxUsers());
            servermaxusers.WriteTagToFile(servermet);
            CTag softfiles(ST_SOFTFILES, nextserver->GetSoftFiles());
            softfiles.WriteTagToFile(servermet);
            CTag hardfiles(ST_HARDFILES, nextserver->GetHardFiles());
            hardfiles.WriteTagToFile(servermet);
            CTag version(ST_VERSION, (char *) nextserver->GetVersion() .c_str());
            version.WriteTagToFile(servermet);
            CTag tagUDPFlags(ST_UDPFLAGS, nextserver->GetUDPFlags());
            tagUDPFlags.WriteTagToFile(servermet);
        }
        fclose(servermet);
        char *curservermet = new char[theApp.dynprefs->Get<wxString>("app-directory").length() + 20];
        char *oldservermet = new char[theApp.dynprefs->Get<wxString>("app-directory").length() + 20];
        sprintf(curservermet, "%sserver.met", static_cast<const char *>(theApp.dynprefs->Get<wxString>("app-directory").c_str()));
        sprintf(oldservermet, "%sserver_met.old", static_cast<const char *>(theApp.dynprefs->Get<wxString>("app-directory").c_str()));
        wxRemoveFile(oldservermet);
        wxRenameFile(curservermet, oldservermet);
        wxRenameFile(newservermet, curservermet);
        delete[] oldservermet;
        delete[] curservermet;
    }
#if 0
    catch(CFileException *error)
    {
        OUTPUT_DEBUG_TRACE();
        theApp.xmuledlg->AddLogLine(false, GetResString(IDS_ERR_SAVESERVERMET2));
        //mf:
        delete[] newservermet;
        error->Delete();
        return false;
    }
#endif
    delete[] newservermet;
    return true;
}

void CServerList::Process()
{
    if (::GetTickCount() - m_nLastSaved > 180000)
    this->SaveServermetToFile();
}

