summaryrefslogblamecommitdiff
path: root/src/gui/windows/serverdialog.cpp
blob: 0b4574993dda3caa0123eb7051df787585aecae2 (plain) (tree)
1
2
3
4
5
6
7
8
  
                       

                                                            

                                                     
  
                                             














                                                                         
                                     
 
                       

                          
                 
                     
 
                         
 

                     
                                 
                                     
                                

                                         
                                         
 
                               

                               
                                       
                                   
 
                          
                          
                        
 

                  

            
                
 

                                     
                                                             


                 
                                   
                   
                               

                      
                      

                       
                                 
                             
                               
                           
                
                                 



                      
                                                                



                 

                                 
                        
                                 
                                   
                        


     

                                                        
                                       
                                                                        
                     
                  
                        
             

                            
                                                 
                                         
                                                                        
                                         

                                                            
                                         
                                                                               
                                         

                                                             
                                         
                                                                              
                                         
                                                                        
                                         
                                                                        
                                                             
                                                                         
                       
                            

                                   
                                                        

                   

                                           
                                                               
     


                                                             
                                           


                                                                          


                                  

                         
                                              
                                               


                                               


                        

                                         
                                                                     
                                                                 
                                 
                                                                  
 


                                             
                                                








                                             

                                           
                                    
 

                                                                           

                     
 
                                      

                                  

                      
                                                                         




                         
 
 

                             
                       
                             
 
                                   
 
                      
 
                                                                         
 
                            
                             

                                                    



                             
                             

                            
                          
     
                              

 
                                            
 
                                                    
               
 
                                                  

                  
 
                             
                            
 


                                      
 

                                            
                                                  



                                                  
                                                  
                                                      
                                                
                                                          
                             
                                                    
                                            
                                                      
                                                      
                                                  

                                                        
                                    
                                              
 



                                                        
                              
                                                         
 

                                   
 


                                                                   
         
                                               
                                   
                                           
         
     
 

                                             
                                            

 
                                                   




                                               
     
                               
     
                
     
                               


                             
                                   
     
                                                               
     
                                    
     
                                                      
                       
         



                                          
         
     
                                 
     
                                                      



                                                     

                                           
         
     




                                                      


                                                              


                                    

 
                                              
 

                                                    
                                
     
                                     
                            
                                          

                   

                                      
                            
                                       


                                                     
                                     
                                                                   

                   
                                     
         
                                                          



                                                         

                                               



                   
                                        
         
                                                          
                           
             

                                                                        
             


                   

                  
     
                                

                                        

 
                                                                     






                                                  
                                    

 
                                                  
 
                                               
     


                                              



                                                     




                          
                                      
     
                                      
                                                                    
         
                              
                                                               
                                                    
                                                
         
                                                                            
         
                                                
                                                                             
                                                                           
         
                                                                     
         
                                                

                                                                 
                                                                          
         
                                                

                                                              
                                                                      
         
                                                
                                                                         
                                                              



                    
                                    

 



                                                                       
                                                                         
 
                                                      
                        
                         
                                                        
 
                             

                            
                          

     
                                                 
                                             
                                     

                                                     

                                        
                       

                                                          

 
                                                


                                              
                                        




                                                


                                              
                     
         














                                                                  
                                                       
                                                                  



































                                                                  
                                                 
 
                                    
                                                     
                        
                        
                                              
 

                                              




                                                   
                                                             
                 

                                                                        
                         


               
                                            
                                                          
 

                                                 
                                                

                     

                                                  
                          
                                                  


                                                                 

                                      
                                               






                                                                          


                                                                            
                                                                             





                                         

                                                
                                                    
         
            

                                                
                                                                    
         
 
                                                
 

                                                    
                                                    

                                                                            

                                                      
                                       
                                                          

                                                                






                                                                              
                                                  
             


























                                                                             



                                                                
             
                                                 
             
                                                
             
                                                                
             


                                                            
             



                                                      



                                                            

         
                                                       

                                        
                                      

                                                                       

                                                




                                                         
                                               
                                                       
                                                     
                                                             
                                                             
                                                                 
                                                           
                                                         
                                                             
                                                               
                                                                 
                                                                     
                                                             
                                                                 

                                                                   
                                               




                                                                


                                       

                                        






                                              




                                                                       
                                          
                                                 
                                                                  

                                                    

                          

                                                          
                                                                     
                                                       
                                                                          
                                              
                                                    
                                                                    

                                                                      
                               

                                                   
                              
                              
                     





                                   
                                                                     
                                                     



                                                      
                                                             



                                            
         
                                                         
             




                                        
             
                                                             
         



                             

                                                            






                                                           
                                                           




                                                                     
                                          
                                               
                                                                

                                                  
 



                                                                

                                                                  
                                                              
                                                                
                            






                                                                               
                                           
                                                              
                                              
                                                        
 
                                                                

                  
                                                                  

                          
                                 

                  
                                           


                        
                                  


                                                         
                                                                



                                                       
                        

                                                  
                                                    
                            

                                 
 
                                       
                                                                      




                                         
                                       
                                                                   



             
 
                                                                          


                                     
 
                                            








                                                                      


                          
                             
                            
                                        
                    
 
/*
 *  The ManaPlus Client
 *  Copyright (C) 2004-2009  The Mana World Development Team
 *  Copyright (C) 2009-2010  The Mana Developers
 *  Copyright (C) 2011-2020  The ManaPlus Developers
 *  Copyright (C) 2020-2023  The ManaVerse Developers
 *
 *  This file is part of The ManaPlus Client.
 *
 *  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
 *  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, see <http://www.gnu.org/licenses/>.
 */

#include "gui/windows/serverdialog.h"

#include "chatlogger.h"
#include "client.h"
#include "configuration.h"
#include "main.h"
#include "settings.h"

#include "net/download.h"

#include "fs/paths.h"

#include "gui/widgets/checkbox.h"
#include "gui/widgets/createwidget.h"
#include "gui/widgets/desktop.h"
#include "gui/windows/editserverdialog.h"
#include "gui/windows/logindialog.h"
#include "gui/windows/serverinfowindow.h"

#include "gui/widgets/button.h"
#include "gui/widgets/label.h"
#include "gui/widgets/layout.h"
#include "gui/widgets/serverslistbox.h"
#include "gui/widgets/scrollarea.h"

#include "utils/delete2.h"
#include "utils/foreach.h"
#include "utils/langs.h"

#include "debug.h"

#ifdef WIN32
#undef ERROR
#endif  // WIN32

static const int MAX_SERVERLIST = 15;

static std::string serverTypeToString(const ServerTypeT type)
{
    switch (type)
    {
        case ServerType::TMWATHENA:
#ifdef TMWA_SUPPORT
            return "TmwAthena";
#else  // TMWA_SUPPORT

            return "";
#endif  // TMWA_SUPPORT

        case ServerType::EATHENA:
            return "eAthena";
        case ServerType::EVOL2:
            return "Evol2";
        default:
        case ServerType::UNKNOWN:
            return "";
    }
}

static uint16_t defaultPortForServerType(const ServerTypeT type)
{
    switch (type)
    {
        default:
        case ServerType::EATHENA:
        case ServerType::EVOL2:
            return 6900;
        case ServerType::UNKNOWN:
        case ServerType::TMWATHENA:
            return 6901;
    }
}

ServerDialog::ServerDialog(ServerInfo *const serverInfo,
                           const std::string &dir) :
    // TRANSLATORS: servers dialog name
    Window(_("Choose Your Server"), Modal_false, nullptr, "server.xml"),
    ActionListener(),
    KeyListener(),
    SelectionListener(),
    mMutex(),
    mServers(ServerInfos()),
    mDir(dir),
    mDescription(new Label(this, std::string())),
    // TRANSLATORS: servers dialog button
    mQuitButton(new Button(this, _("Quit"), "quit", BUTTON_SKIN, this)),
    // TRANSLATORS: servers dialog button
    mConnectButton(new Button(this, _("Connect"), "connect",
        BUTTON_SKIN, this)),
    // TRANSLATORS: servers dialog button
    mAddEntryButton(new Button(this, _("Add"), "addEntry", BUTTON_SKIN, this)),
    // TRANSLATORS: servers dialog button
    mEditEntryButton(new Button(this, _("Edit"), "editEntry",
        BUTTON_SKIN, this)),
    // TRANSLATORS: servers dialog button
    mDeleteButton(new Button(this, _("Delete"), "remove", BUTTON_SKIN, this)),
    // TRANSLATORS: servers dialog button
    mLoadButton(new Button(this, _("Load"), "load", BUTTON_SKIN, this)),
    // TRANSLATORS: servers dialog button
    mInfoButton(new Button(this, _("Info"), "info", BUTTON_SKIN, this)),
    mServersListModel(new ServersListModel(&mServers, this)),
    mServersList(CREATEWIDGETR(ServersListBox, this, mServersListModel)),
    mDownload(nullptr),
    mServerInfo(serverInfo),
    mPersistentIPCheckBox(nullptr),
    mDownloadProgress(-1.0F),
    mDownloadStatus(ServerDialogDownloadStatus::UNKNOWN)
{
    if (isSafeMode)
    {
        // TRANSLATORS: servers dialog name
        setCaption(_("Choose Your Server  *** SAFE MODE ***"));
    }
#ifdef USE_OPENGL
    else if (config.getIntValue("opengl") == RENDER_SOFTWARE)
    {
        // TRANSLATORS: servers dialog name
        setCaption(_("Choose Your Server  *** SOFTWARE RENDER MODE ***"));
    }
#endif  // USE_OPENGL

    setWindowName("ServerDialog");

    setCloseButton(true);

    mPersistentIPCheckBox = new CheckBox(this,
        // TRANSLATORS: servers dialog checkbox
        _("Use same ip for game sub servers"),
        config.getBoolValue("usePersistentIP"),
        this, "persitent ip");

    loadCustomServers();

    mServersList->addMouseListener(this);

    ScrollArea *const usedScroll = new ScrollArea(this, mServersList,
        fromBool(getOptionBool("showbackground", false), Opaque),
        "server_background.xml");
    usedScroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);

    mServersList->addSelectionListener(this);
    usedScroll->setVerticalScrollAmount(0);

    place(0, 0, usedScroll, 8, 5).setPadding(3);
    place(0, 5, mDescription, 8, 1);
    place(0, 6, mPersistentIPCheckBox, 8, 1);
    place(0, 7, mInfoButton, 1, 1);
    place(1, 7, mAddEntryButton, 1, 1);
    place(2, 7, mEditEntryButton, 1, 1);
    place(3, 7, mLoadButton, 1, 1);
    place(4, 7, mDeleteButton, 1, 1);
    place(6, 7, mQuitButton, 1, 1);
    place(7, 7, mConnectButton, 1, 1);

    // Make sure the list has enough height
    getLayout().setRowHeight(0, 80);

    // Do this manually instead of calling reflowLayout so we can enforce a
    // minimum width.
    int width = 500;
    int height = 350;

    getLayout().reflow(width, height);
    setContentSize(width, height);

    setMinWidth(310);
    setMinHeight(220);
    setDefaultSize(getWidth(), getHeight(), ImagePosition::CENTER, 0, 0);

    setResizable(true);
    addKeyListener(this);

    loadWindowState();
}

void ServerDialog::postInit()
{
    Window::postInit();
    setVisible(Visible_true);

    mConnectButton->requestFocus();

    loadServers(true);

    mServersList->setSelected(0);  // Do this after for the Delete button

    if (needUpdateServers())
        downloadServerList();
    else
        logger->log("Skipping servers list update");
}

ServerDialog::~ServerDialog()
{
    if (mDownload != nullptr)
    {
        mDownload->cancel();
        delete2(mDownload)
    }
    delete2(mServersListModel)
}

void ServerDialog::connectToSelectedServer()
{
    if (client->getState() == State::CONNECT_SERVER)
        return;

    const int index = mServersList->getSelected();
    if (index < 0)
        return;

    if (mDownload != nullptr)
        mDownload->cancel();

    mQuitButton->setEnabled(false);
    mConnectButton->setEnabled(false);
    mLoadButton->setEnabled(false);

    ServerInfo server = mServers.at(index);
    mServerInfo->hostname = server.hostname;
    mServerInfo->althostname = server.althostname;
    mServerInfo->port = server.port;
    mServerInfo->type = server.type;
    mServerInfo->name = server.name;
    mServerInfo->description = server.description;
    mServerInfo->registerUrl = server.registerUrl;
    mServerInfo->onlineListUrl = server.onlineListUrl;
    mServerInfo->supportUrl = server.supportUrl;
    mServerInfo->defaultHostName = server.defaultHostName;
    mServerInfo->save = true;
    mServerInfo->persistentIp = server.persistentIp;
    mServerInfo->freeType = server.freeType;
    mServerInfo->updateMirrors = server.updateMirrors;
    mServerInfo->packetVersion = server.packetVersion;
    mServerInfo->updateHosts = server.updateHosts;
    mServerInfo->freeSources = server.freeSources;
    mServerInfo->nonFreeSources = server.nonFreeSources;
    mServerInfo->docs = server.docs;
    mServerInfo->serverUrl = server.serverUrl;

    settings.persistentIp = mServerInfo->persistentIp;
    settings.supportUrl = mServerInfo->supportUrl;
    settings.updateMirrors = mServerInfo->updateMirrors;

    if (chatLogger != nullptr)
        chatLogger->setServerName(mServerInfo->hostname);

    saveCustomServers(*mServerInfo,
        -1);

    if (!LoginDialog::savedPasswordKey.empty())
    {
        if (mServerInfo->hostname != LoginDialog::savedPasswordKey)
        {
            LoginDialog::savedPassword.clear();
            if (desktop != nullptr)
                desktop->reloadWallpaper();
        }
    }

    config.setValue("usePersistentIP",
        mPersistentIPCheckBox->isSelected());
    client->setState(State::CONNECT_SERVER);
}

void ServerDialog::action(const ActionEvent &event)
{
    const std::string &eventId = event.getId();
    if (eventId == "connect")
    {
        connectToSelectedServer();
    }
    else if (eventId == "quit")
    {
        close();
    }
    else if (eventId == "load")
    {
        downloadServerList();
    }
    else if (eventId == "addEntry")
    {
        CREATEWIDGET(EditServerDialog, this, ServerInfo(), -1);
    }
    else if (eventId == "editEntry")
    {
        const int index = mServersList->getSelected();
        if (index >= 0)
        {
            CREATEWIDGET(EditServerDialog,
                this,
                mServers.at(index),
                index);
        }
    }
    else if (eventId == "remove")
    {
        const int index = mServersList->getSelected();
        if (index >= 0)
        {
            mServersList->setSelected(0);
            mServers.erase(mServers.begin() + index);
            saveCustomServers(ServerInfo(),
                -1);
        }
    }
    else if (eventId == "info")
    {
        const int index = mServersList->getSelected();
        if (index >= 0)
        {
            if (serverInfoWindow != nullptr)
                serverInfoWindow->scheduleDelete();
            serverInfoWindow = CREATEWIDGETR(ServerInfoWindow,
                mServers.at(index));
        }
    }
}

void ServerDialog::keyPressed(KeyEvent &event)
{
    PRAGMA45(GCC diagnostic push)
    PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
    switch (event.getActionId())
    {
        case InputAction::GUI_CANCEL:
            event.consume();
            client->setState(State::EXIT);
            return;

        case InputAction::GUI_SELECT:
        case InputAction::GUI_SELECT2:
            event.consume();
            action(ActionEvent(nullptr,
                mConnectButton->getActionEventId()));
            return;

        case InputAction::GUI_INSERT:
            CREATEWIDGET(EditServerDialog, this, ServerInfo(), -1);
            return;

        case InputAction::GUI_DELETE:
        {
            const int index = mServersList->getSelected();
            if (index >= 0)
            {
                mServersList->setSelected(0);
                mServers.erase(mServers.begin() + index);
                saveCustomServers(ServerInfo(),
                    -1);
            }
            return;
        }

        case InputAction::GUI_BACKSPACE:
        {
            const int index = mServersList->getSelected();
            if (index >= 0)
            {
                CREATEWIDGET(EditServerDialog, this, mServers.at(index),
                    index);
            }
            return;
        }

        default:
            break;
    }
    PRAGMA45(GCC diagnostic pop)
    if (!event.isConsumed())
        mServersList->keyPressed(event);
}

void ServerDialog::valueChanged(const SelectionEvent &event A_UNUSED)
{
    const int index = mServersList->getSelected();
    if (index == -1)
    {
        mDeleteButton->setEnabled(false);
        return;
    }
    mDeleteButton->setEnabled(true);
}

void ServerDialog::mouseClicked(MouseEvent &event)
{
    if (event.getButton() == MouseButton::LEFT)
    {
        event.consume();
        if (event.getClickCount() == 2 &&
            event.getSource() == mServersList)
        {
            action(ActionEvent(mConnectButton,
                mConnectButton->getActionEventId()));
        }
    }
}

void ServerDialog::logic()
{
    BLOCK_START("ServerDialog::logic")
    {
        MutexLocker tempLock(&mMutex);
        if (mDownloadStatus == ServerDialogDownloadStatus::COMPLETE)
        {
            loadServers(true);
            mDownloadStatus = ServerDialogDownloadStatus::OVER;
            mDescription->setCaption(std::string());
            logger->log("Servers list updated");
        }
        else if (mDownloadStatus == ServerDialogDownloadStatus::IN_PROGRESS)
        {
            // TRANSLATORS: servers dialog label
            mDescription->setCaption(strprintf(_("Downloading server list..."
                "%2.2f%%"), static_cast<double>(mDownloadProgress * 100)));
        }
        else if (mDownloadStatus == ServerDialogDownloadStatus::IDLE)
        {
            // TRANSLATORS: servers dialog label
            mDescription->setCaption(_("Waiting for server..."));
        }
        else if (mDownloadStatus == ServerDialogDownloadStatus::PREPARING)
        {
            // TRANSLATORS: servers dialog label
            mDescription->setCaption(_("Preparing download"));
        }
        else if (mDownloadStatus == ServerDialogDownloadStatus::ERROR)
        {
            // TRANSLATORS: servers dialog label
            mDescription->setCaption(_("Error retreiving server list!"));
            logger->log("Error: servers list updating error");
        }
    }

    Window::logic();
    BLOCK_END("ServerDialog::logic")
}

void ServerDialog::downloadServerList()
{
    // Try to load the configuration value for the onlineServerList
    std::string listFile = branding.getStringValue("onlineServerList");
    std::string listFile2 = branding.getStringValue("onlineServerList2");

    // Fall back to manaplus.org when neither branding
    // nor config set it
    if (listFile.empty())
        listFile = "http://manaplus.org/serverlist.xml";

    if (mDownload != nullptr)
    {
        mDownload->cancel();
        delete2(mDownload)
    }

    mDownload = new Net::Download(this, listFile,
        &downloadUpdate, false, false, true);
    mDownload->setFile(pathJoin(mDir,
        branding.getStringValue("onlineServerFile")),
        -1);
    if (!listFile2.empty())
        mDownload->addMirror(listFile2);
    mDownload->start();

    config.setValue("serverslistupdate", getDateString());
}

static void loadHostsGroup(XmlNodeConstPtr node,
                           ServerInfo &server)
{
    HostsGroup group;
    group.name = XML::langProperty(node,
        "name",
        // TRANSLATORS: unknown hosts group name
        _("Unknown"));
    for_each_xml_child_node(hostNode, node)
    {
        if (!xmlNameEqual(hostNode, "host") ||
            !XmlHaveChildContent(hostNode))
        {
            continue;
        }
        const std::string host = XmlChildContent(hostNode);
        if (host.empty())
            continue;
        if (!checkPath(host))
        {
            logger->log1("Warning: incorrect update server name");
            continue;
        }

        group.hosts.push_back(host);
    }
    if (!group.hosts.empty())
        server.updateHosts.push_back(group);
}

static void loadServerSourcesList(XmlNodeConstPtr node,
                                  STD_VECTOR<ServerUrlInfo> &list)
{
    for_each_xml_child_node(urlNode, node)
    {
        if (!xmlNameEqual(urlNode, "url") ||
            !XmlHaveChildContent(urlNode))
        {
            continue;
        }
        const std::string name = XML::langProperty(urlNode,
            "name",
            "");
        if (name.empty())
            continue;
        const std::string url = XmlChildContent(urlNode);
        if (url.empty())
            continue;
        list.push_back(ServerUrlInfo(name, url));
    }
}

static void loadServerSources(XmlNodeConstPtr node,
                              ServerInfo &server)
{
    for_each_xml_child_node(subNode, node)
    {
        if (xmlNameEqual(subNode, "free"))
        {
            loadServerSourcesList(subNode, server.freeSources);
        }
        else if (xmlNameEqual(subNode, "nonfree"))
        {
            loadServerSourcesList(subNode, server.nonFreeSources);
        }
    }
}

void ServerDialog::loadServers(const bool addNew)
{
    XML::Document doc(pathJoin(mDir,
        branding.getStringValue("onlineServerFile")),
        UseVirtFs_false,
        SkipError_true);
    XmlNodeConstPtr rootNode = doc.rootNode();

    if (rootNode == nullptr ||
        !xmlNameEqual(rootNode, "serverlist"))
    {
        logger->log1("Error loading server list!");
        return;
    }

    const int ver = XML::getProperty(rootNode, "version", 0);
    if (ver != 1)
    {
        logger->log("Error: unsupported online server list version: %d",
                    ver);
        return;
    }

    const std::string lang = getLangShort();
    const std::string description2("description_" + lang);

    for_each_xml_child_node(serverNode, rootNode)
    {
        if (!xmlNameEqual(serverNode, "server"))
            continue;

        const std::string type = XML::getProperty(
            serverNode, "type", "unknown");
        ServerInfo server;
        server.type = ServerInfo::parseType(type);
        const std::string licenseType = XML::getProperty(
            serverNode, "licenseType", "notset");
        server.freeType = ServerInfo::parseFreeType(licenseType);

        // Ignore unknown server types
        if (server.type == ServerType::UNKNOWN)
        {
            logger->log("Ignoring server entry with unknown type: %s",
                        type.c_str());
            continue;
        }

        server.name = XML::getProperty(serverNode, "name", std::string());
        std::string version = XML::getProperty(serverNode, "minimumVersion",
                                               std::string());

        const bool meetsMinimumVersion = (compareStrI(version, SMALL_VERSION)
                                   <= 0);

        // For display in the list
        if (meetsMinimumVersion)
            version.clear();
        else if (version.empty())
        {
            // TRANSLATORS: servers dialog label
            version = _("requires a newer version");
        }
        else
        {
            // TRANSLATORS: servers dialog label
            version = strprintf(_("requires v%s"), version.c_str());
        }

        const Font *const font = gui->getFont();

        for_each_xml_child_node(subNode, serverNode)
        {
            if (xmlNameEqual(subNode, "connection"))
            {
                server.hostname = XML::getProperty(subNode, "hostname", "");
                server.althostname = XML::getProperty(
                    subNode, "althostname", "");
                server.port = CAST_U16(
                    XML::getProperty(subNode, "port", 0));
                server.packetVersion = XML::getProperty(subNode,
                    "packetVersion", 0);

                if (server.port == 0)
                {
                    // If no port is given, use the default for the given type
                    server.port = defaultPortForServerType(server.type);
                }
            }
            else if (XmlHaveChildContent(subNode))
            {
                if ((xmlNameEqual(subNode, "description")
                    && server.description.empty()) || (!lang.empty()
                    && xmlNameEqual(subNode, description2.c_str())))
                {
                    server.description = XmlChildContent(subNode);
                }
                else if (xmlNameEqual(subNode, "registerurl"))
                {
                    server.registerUrl = XmlChildContent(subNode);
                }
                else if (xmlNameEqual(subNode, "onlineListUrl"))
                {
                    server.onlineListUrl = XmlChildContent(subNode);
                }
                else if (xmlNameEqual(subNode, "support"))
                {
                    server.supportUrl = XmlChildContent(subNode);
                }
                else if (xmlNameEqual(subNode, "persistentIp"))
                {
                    std::string text = XmlChildContent(subNode);
                    server.persistentIp = (text == "1" || text == "true");
                }
                else if (xmlNameEqual(subNode, "updateMirror"))
                {
                    server.updateMirrors.push_back(XmlChildContent(subNode));
                }
                else if (xmlNameEqual(subNode, "site"))
                {
                    server.serverUrl = XmlChildContent(subNode);
                }
            }
            if (xmlNameEqual(subNode, "updates"))
            {
                loadHostsGroup(subNode, server);
            }
            else if (xmlNameEqual(subNode, "defaultUpdateHost"))
            {
                server.defaultHostName = XML::langProperty(
                    // TRANSLATORS: default hosts group name
                    subNode, "name", _("default"));
            }
            else if (xmlNameEqual(subNode, "sources"))
            {
                loadServerSources(subNode, server);
            }
            else if (xmlNameEqual(subNode, "docs"))
            {
                loadServerSourcesList(subNode, server.docs);
            }
        }

        server.version.first = font->getWidth(version);
        server.version.second = version;

        MutexLocker tempLock(&mMutex);
        // Add the server to the local list if it's not already present
        bool found = false;
        for (unsigned int i = 0, fsz = CAST_U32(
             mServers.size()); i < fsz; i++)
        {
            if (mServers[i] == server)
            {
                // Use the name listed in the server list
                mServers[i].name = server.name;
                mServers[i].type = server.type;
                mServers[i].freeType = server.freeType;
                mServers[i].version = server.version;
                mServers[i].description = server.description;
                mServers[i].registerUrl = server.registerUrl;
                mServers[i].onlineListUrl = server.onlineListUrl;
                mServers[i].supportUrl = server.supportUrl;
                mServers[i].serverUrl = server.serverUrl;
                mServers[i].althostname = server.althostname;
                mServers[i].persistentIp = server.persistentIp;
                mServers[i].updateMirrors = server.updateMirrors;
                mServers[i].defaultHostName = server.defaultHostName;
                mServers[i].updateHosts = server.updateHosts;
                mServers[i].packetVersion = server.packetVersion;
                mServers[i].freeSources = server.freeSources;
                mServers[i].nonFreeSources = server.nonFreeSources;
                mServers[i].docs = server.docs;
                mServersListModel->setVersionString(i, version);
                found = true;
                break;
            }
        }
        if (!found && addNew)
            mServers.push_back(server);
    }
    if (mServersList->getSelected() < 0)
        mServersList->setSelected(0);
}

void ServerDialog::loadCustomServers()
{
    for (int i = 0; i < MAX_SERVERLIST; ++i)
    {
        const std::string index = toString(i);
        const std::string nameKey("MostUsedServerDescName" + index);
        const std::string descKey("MostUsedServerDescription" + index);
        const std::string hostKey("MostUsedServerName" + index);
        const std::string typeKey("MostUsedServerType" + index);
        const std::string portKey("MostUsedServerPort" + index);
        const std::string onlineListUrlKey
            ("MostUsedServerOnlineList" + index);
        const std::string persistentIpKey("persistentIp" + index);
        const std::string packetVersionKey
            ("MostUsedServerPacketVersion" + index);

        ServerInfo server;
        server.name = config.getValue(nameKey, "");
        server.description = config.getValue(descKey, "");
        server.onlineListUrl = config.getValue(onlineListUrlKey, "");
        server.hostname = config.getValue(hostKey, "");
        server.type = ServerInfo::parseType(config.getValue(typeKey, ""));
        server.persistentIp = config.getValue(
            persistentIpKey, 0) != 0 ? true : false;
        server.packetVersion = config.getValue(packetVersionKey, 0);

        const int defaultPort = defaultPortForServerType(server.type);
        server.port = CAST_U16(
            config.getValue(portKey, defaultPort));

        // skip invalid server
        if (!server.isValid())
            continue;

        server.save = true;
        mServers.push_back(server);
    }
}

void ServerDialog::saveCustomServers(const ServerInfo &currentServer,
                                     const int index)
{
    // Make sure the current server is mentioned first
    if (currentServer.isValid())
    {
        if (index >= 0 && CAST_SIZE(index) < mServers.size())
        {
            mServers[index] = currentServer;
        }
        else
        {
            FOR_EACH (ServerInfos::iterator, i, mServers)
            {
                if (*i == currentServer)
                {
                    mServers.erase(i);
                    break;
                }
            }
            mServers.insert(mServers.begin(), currentServer);
        }
    }

    int savedServerCount = 0;

    for (unsigned i = 0, fsz = CAST_U32(mServers.size());
         i < fsz && savedServerCount < MAX_SERVERLIST; ++ i)
    {
        const ServerInfo &server = mServers.at(i);

        // Only save servers that were loaded from settings
        if (!(server.save && server.isValid()))
            continue;

        const std::string num = toString(savedServerCount);
        const std::string nameKey("MostUsedServerDescName" + num);
        const std::string descKey("MostUsedServerDescription" + num);
        const std::string hostKey("MostUsedServerName" + num);
        const std::string typeKey("MostUsedServerType" + num);
        const std::string portKey("MostUsedServerPort" + num);
        const std::string onlineListUrlKey
            ("MostUsedServerOnlineList" + num);
        const std::string persistentIpKey("persistentIp" + num);
        const std::string packetVersionKey
            ("MostUsedServerPacketVersion" + num);

        config.setValue(nameKey, server.name);
        config.setValue(descKey, server.description);
        config.setValue(onlineListUrlKey, server.onlineListUrl);
        config.setValue(hostKey, server.hostname);
        config.setValue(typeKey, serverTypeToString(server.type));
        config.setValue(portKey, toString(server.port));
        config.setValue(persistentIpKey, server.persistentIp);
        config.setValue(packetVersionKey, server.packetVersion);
        ++ savedServerCount;
    }

    // Insert an invalid entry at the end to make the loading stop there
    if (savedServerCount < MAX_SERVERLIST)
        config.setValue("MostUsedServerName" + toString(savedServerCount), "");
}

int ServerDialog::downloadUpdate(void *ptr,
                                 const DownloadStatusT status,
                                 size_t total,
                                 const size_t remaining)
{
    if ((ptr == nullptr) || status == DownloadStatus::Cancelled)
        return -1;

    ServerDialog *const sd = reinterpret_cast<ServerDialog*>(ptr);
    bool finished = false;

    if (sd->mDownload == nullptr)
        return -1;

    if (status == DownloadStatus::Complete)
    {
        finished = true;
    }
    else if (CAST_S32(status) < 0)
    {
        logger->log("Error retreiving server list: %s\n",
                    sd->mDownload->getError());
        sd->mDownloadStatus = ServerDialogDownloadStatus::ERROR;
    }
    else
    {
        float progress = static_cast<float>(remaining);
        if (total != 0U)
            progress /= static_cast<float>(total);

        if (progress != progress || progress < 0.0F)
            progress = 0.0F;
        else if (progress > 1.0F)
            progress = 1.0F;

        MutexLocker lock1(&sd->mMutex);
        sd->mDownloadStatus = ServerDialogDownloadStatus::IN_PROGRESS;
        sd->mDownloadProgress = progress;
    }

    if (finished)
    {
        MutexLocker lock1(&sd->mMutex);
        sd->mDownloadStatus = ServerDialogDownloadStatus::COMPLETE;
    }

    return 0;
}

void ServerDialog::updateServer(const ServerInfo &server, const int index)
{
    saveCustomServers(server, index);
}

bool ServerDialog::needUpdateServers() const
{
    if (mServers.empty() || config.getStringValue("serverslistupdate")
        != getDateString())
    {
        return true;
    }

    return false;
}

void ServerDialog::close()
{
    if (mDownload != nullptr)
        mDownload->cancel();
    client->setState(State::FORCE_QUIT);
    Window::close();
}