GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/updaterwindow.cpp Lines: 88 609 14.4 %
Date: 2018-11-12 Branches: 60 826 7.3 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2018  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "gui/windows/updaterwindow.h"
24
25
#include "client.h"
26
#include "configuration.h"
27
#include "main.h"
28
#include "settings.h"
29
30
#include "enums/gui/layouttype.h"
31
32
#include "fs/files.h"
33
#include "fs/mkdir.h"
34
#include "fs/paths.h"
35
36
#include "fs/virtfs/fs.h"
37
38
#include "gui/widgets/button.h"
39
#include "gui/widgets/containerplacer.h"
40
#include "gui/widgets/itemlinkhandler.h"
41
#include "gui/widgets/label.h"
42
#include "gui/widgets/layout.h"
43
#include "gui/widgets/progressbar.h"
44
#include "gui/widgets/scrollarea.h"
45
#include "gui/widgets/staticbrowserbox.h"
46
47
#include "net/download.h"
48
#include "net/updatetypeoperators.h"
49
50
#include "resources/db/moddb.h"
51
52
#include "utils/delete2.h"
53
#include "utils/foreach.h"
54
#include "utils/gettext.h"
55
56
#include <sys/stat.h>
57
58
#include <fstream>
59
#include <sstream>
60
61
#include "debug.h"
62
63
UpdaterWindow *updaterWindow = nullptr;
64
65
3
const std::string xmlUpdateFile("resources.xml");
66
3
const std::string txtUpdateFile("resources2.txt");
67
2
const std::string updateServer2
68
2
    ("http://download.manaplus.org/manaplus/updates/");
69
2
const std::string updateServer3
70
2
    ("http://download2.manaplus.org/manaplus/updates/");
71
2
const std::string updateServer4
72
2
    ("http://download.evolonline.org/manaplus/updates/");
73
2
const std::string updateServer5
74
2
    ("http://download3.manaplus.org/manaplus/updates/");
75
76
/**
77
 * Load the given file into a vector of updateFiles.
78
 */
79
static STD_VECTOR<UpdateFile> loadXMLFile(const std::string &fileName,
80
                                          const bool loadMods)
81
{
82
    STD_VECTOR<UpdateFile> files;
83
    XML::Document doc(fileName, UseVirtFs_false, SkipError_false);
84
    XmlNodeConstPtrConst rootNode = doc.rootNode();
85
86
    if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "updates"))
87
    {
88
        logger->log("Error loading update file: %s", fileName.c_str());
89
        return files;
90
    }
91
92
    for_each_xml_child_node(fileNode, rootNode)
93
    {
94
        const bool isMod = xmlNameEqual(fileNode, "mod");
95
        if (!xmlNameEqual(fileNode, "update") && !isMod)
96
            continue;
97
98
        UpdateFile file;
99
        file.name = XML::getProperty(fileNode, "file", "");
100
        file.hash = XML::getProperty(fileNode, "hash", "");
101
        file.type = XML::getProperty(fileNode, "type", "data");
102
        file.desc = XML::getProperty(fileNode, "description", "");
103
        file.group = XML::getProperty(fileNode, "group", "");
104
        if (!file.group.empty() && (!isMod || !loadMods))
105
            continue;
106
107
        const std::string version = XML::getProperty(
108
            fileNode, "version", "");
109
        if (!version.empty())
110
        {
111
            if (version > CHECK_VERSION)
112
                continue;
113
        }
114
        const std::string notVersion = XML::getProperty(
115
            fileNode, "notVersion", "");
116
        if (!notVersion.empty())
117
        {
118
            if (notVersion <= CHECK_VERSION)
119
                continue;
120
        }
121
        if (XML::getProperty(fileNode, "required", "yes") == "yes")
122
            file.required = true;
123
        else
124
            file.required = false;
125
126
        if (checkPath(file.name))
127
            files.push_back(file);
128
    }
129
130
    return files;
131
}
132
133
static STD_VECTOR<UpdateFile> loadTxtFile(const std::string &fileName)
134
{
135
    STD_VECTOR<UpdateFile> files;
136
    std::ifstream fileHandler;
137
    fileHandler.open(fileName.c_str(), std::ios::in);
138
139
    if (fileHandler.is_open())
140
    {
141
        while (fileHandler.good())
142
        {
143
            char name[256];
144
            char hash[50];
145
            fileHandler.getline(name, 256, ' ');
146
            fileHandler.getline(hash, 50);
147
148
            UpdateFile thisFile;
149
            thisFile.name = name;
150
            thisFile.hash = hash;
151
            thisFile.type = "data";
152
            thisFile.group.clear();
153
            thisFile.required = true;
154
            thisFile.desc.clear();
155
156
            if (!thisFile.name.empty() && checkPath(thisFile.name))
157
                files.push_back(thisFile);
158
        }
159
    }
160
    else
161
    {
162
        logger->log("Error loading update file: %s", fileName.c_str());
163
    }
164
    fileHandler.close();
165
166
    return files;
167
}
168
169
1
UpdaterWindow::UpdaterWindow(const std::string &restrict updateHost,
170
                             const std::string &restrict updatesDir,
171
                             const bool applyUpdates,
172
1
                             const UpdateTypeT updateType) :
173
    // TRANSLATORS: updater window name
174
1
    Window(_("Updating..."), Modal_false, nullptr, "update.xml"),
175
    ActionListener(),
176
    KeyListener(),
177
    mDownloadProgress(0.0F),
178
    mUpdateHost(updateHost),
179
    mUpdatesDir(updatesDir),
180
    mUpdatesDirReal(updatesDir),
181
    mCurrentFile("news.txt"),
182
    mNewLabelCaption(),
183
    mDownloadMutex(),
184
    mCurrentChecksum(0),
185
    mMemoryBuffer(nullptr),
186
    mDownload(nullptr),
187
    mUpdateFiles(),
188
    mTempUpdateFiles(),
189
    mUpdateServerPath(mUpdateHost),
190

1
    mItemLinkHandler(new ItemLinkHandler),
191
    // TRANSLATORS: updater window label
192

2
    mLabel(new Label(this, _("Connecting..."))),
193
    // TRANSLATORS: updater window button
194

2
    mCancelButton(new Button(this, _("Cancel"), "cancel", BUTTON_SKIN, this)),
195
    // TRANSLATORS: updater window button
196

2
    mPlayButton(new Button(this, _("Play"), "play", BUTTON_SKIN, this)),
197
    mProgressBar(new ProgressBar(this, 0.0, 310, 0,
198
        ProgressColorId::PROG_UPDATE,
199

1
        "updateprogressbar.xml", "updateprogressbar_fill.xml")),
200
    mBrowserBox(new StaticBrowserBox(this, Opaque_true,
201

1
        "browserbox.xml")),
202
1
    mScrollArea(new ScrollArea(this, mBrowserBox,
203

2
        Opaque_true, "update_background.xml")),
204
    mDownloadStatus(UpdateDownloadStatus::UPDATE_NEWS),
205
    mDownloadedBytes(0),
206
    mUpdateIndex(0),
207
    mUpdateIndexOffset(0),
208
    mUpdateType(updateType),
209
    mStoreInMemory(true),
210
    mDownloadComplete(true),
211
    mUserCancel(false),
212
    mLoadUpdates(applyUpdates),
213
    mValidateXml(false),
214






53
    mSkipPatches(false)
215
{
216
5
    setWindowName("UpdaterWindow");
217
1
    setResizable(true);
218
1
    setDefaultSize(450, 400, ImagePosition::CENTER, 0, 0);
219
1
    setMinWidth(310);
220
1
    setMinHeight(220);
221
222
2
    mProgressBar->setSmoothProgress(false);
223
2
    mBrowserBox->setOpaque(Opaque_false);
224
1
    mBrowserBox->setLinkHandler(mItemLinkHandler);
225
2
    mBrowserBox->setProcessVars(true);
226
2
    mBrowserBox->setEnableKeys(true);
227
2
    mBrowserBox->setEnableTabs(true);
228
2
    mBrowserBox->setEnableImages(true);
229
2
    mPlayButton->setEnabled(false);
230
231
1
    ContainerPlacer placer(nullptr, nullptr);
232
1
    placer = getPlacer(0, 0);
233
234
2
    placer(0, 0, mScrollArea, 5, 3).setPadding(3);
235
1
    placer(0, 3, mLabel, 5, 1);
236
1
    placer(0, 4, mProgressBar, 5, 1);
237
1
    placer(3, 5, mCancelButton, 1, 1);
238
1
    placer(4, 5, mPlayButton, 1, 1);
239
240
1
    Layout &layout = getLayout();
241
1
    layout.setRowHeight(0, LayoutType::SET);
242
243
1
    addKeyListener(this);
244
245
2
    if (mUpdateHost.empty())
246
    {
247
1
        const STD_VECTOR<std::string> &mirrors = settings.updateMirrors;
248
4
        if (mirrors.begin() != mirrors.end())
249
            mUpdateHost = *mirrors.begin();
250
1
        mSkipPatches = true;
251
    }
252
253
1
    loadWindowState();
254
1
}
255
256
1
void UpdaterWindow::postInit()
257
{
258
1
    Window::postInit();
259
1
    setVisible(Visible_true);
260
1
    mCancelButton->requestFocus();
261
1
    removeProtocol(mUpdateServerPath);
262
263
1
    download();
264
1
}
265
266
12
UpdaterWindow::~UpdaterWindow()
267
{
268
1
    if (mLoadUpdates)
269
        loadUpdates();
270
271
1
    if (mDownload != nullptr)
272
    {
273
1
        mDownload->cancel();
274
275
1
        delete2(mDownload)
276
    }
277
1
    free(mMemoryBuffer);
278
1
    delete2(mItemLinkHandler);
279
2
}
280
281
void UpdaterWindow::setProgress(const float p)
282
{
283
    // Do delayed progress bar update, since Guichan isn't thread-safe
284
    MutexLocker lock(&mDownloadMutex);
285
    mDownloadProgress = p;
286
}
287
288
1
void UpdaterWindow::setLabel(const std::string &str)
289
{
290
    // Do delayed label text update, since Guichan isn't thread-safe
291
3
    MutexLocker lock(&mDownloadMutex);
292
2
    mNewLabelCaption = str;
293
1
}
294
295
void UpdaterWindow::enable()
296
{
297
    mCancelButton->setEnabled(false);
298
    mPlayButton->setEnabled(true);
299
    mPlayButton->requestFocus();
300
301
    if (client->getState() != State::GAME)
302
    {
303
        if ((mUpdateType & UpdateType::Close) != 0)
304
            client->setState(State::LOAD_DATA);
305
    }
306
    else
307
    {
308
        deleteSelf();
309
    }
310
}
311
312
void UpdaterWindow::action(const ActionEvent &event)
313
{
314
    const std::string &eventId = event.getId();
315
    if (eventId == "cancel")
316
    {
317
        // Register the user cancel
318
        mUserCancel = true;
319
        // Skip the updating process
320
        if (mDownloadStatus != UpdateDownloadStatus::UPDATE_COMPLETE)
321
        {
322
            if (mDownload != nullptr)
323
                mDownload->cancel();
324
            mDownloadStatus = UpdateDownloadStatus::UPDATE_ERROR;
325
        }
326
    }
327
    else if (eventId == "play")
328
    {
329
        if (client->getState() != State::GAME)
330
            client->setState(State::LOAD_DATA);
331
        else
332
            deleteSelf();
333
    }
334
}
335
336
void UpdaterWindow::keyPressed(KeyEvent &event)
337
{
338
    const InputActionT actionId = event.getActionId();
339
    if (actionId == InputAction::GUI_CANCEL)
340
    {
341
        action(ActionEvent(nullptr, mCancelButton->getActionEventId()));
342
        if (client->getState() != State::GAME)
343
            client->setState(State::LOGIN);
344
        else
345
            deleteSelf();
346
    }
347
    else if (actionId == InputAction::GUI_SELECT ||
348
             actionId == InputAction::GUI_SELECT2)
349
    {
350
        if (mDownloadStatus == UpdateDownloadStatus::UPDATE_COMPLETE ||
351
            mDownloadStatus == UpdateDownloadStatus::UPDATE_ERROR ||
352
            mDownloadStatus == UpdateDownloadStatus::UPDATE_IDLE)
353
        {
354
            action(ActionEvent(nullptr, mPlayButton->getActionEventId()));
355
        }
356
        else
357
        {
358
            action(ActionEvent(nullptr, mCancelButton->getActionEventId()));
359
        }
360
    }
361
}
362
363
void UpdaterWindow::loadNews()
364
{
365
    if (mMemoryBuffer == nullptr)
366
    {
367
        logger->log1("Couldn't load news");
368
        return;
369
    }
370
371
    // Reallocate and include terminating 0 character
372
    mMemoryBuffer = static_cast<char*>(realloc(
373
        mMemoryBuffer, mDownloadedBytes + 1));
374
    if (mMemoryBuffer == nullptr)
375
    {
376
        logger->log1("Couldn't load news");
377
        return;
378
    }
379
    mMemoryBuffer[mDownloadedBytes] = '\0';
380
    mBrowserBox->clearRows();
381
382
    std::string newsName = mUpdatesDir + "/local/help/news.txt";
383
    mkdir_r((mUpdatesDir + "/local/help/").c_str());
384
    bool firstLine(true);
385
    std::ofstream file;
386
    std::stringstream ss(mMemoryBuffer);
387
    std::string line;
388
    file.open(newsName.c_str(), std::ios::out);
389
    int cnt = 0;
390
    const int maxNews = 50;
391
    while (std::getline(ss, line, '\n'))
392
    {
393
        cnt ++;
394
        if (firstLine)
395
        {
396
            firstLine = false;
397
            const size_t i = line.find("##9 Latest client version: ##6");
398
            if (i == 0U)
399
                continue;
400
401
            if (file.is_open())
402
                file << line << std::endl;
403
            if (cnt < maxNews)
404
                mBrowserBox->addRow(line, false);
405
        }
406
        else
407
        {
408
            if (file.is_open())
409
                file << line << std::endl;
410
            if (cnt < maxNews)
411
                mBrowserBox->addRow(line, false);
412
        }
413
    }
414
415
    file.close();
416
    if (cnt > maxNews)
417
    {
418
        mBrowserBox->addRow("", false);
419
        // TRANSLATORS: updater window checkbox
420
        mBrowserBox->addRow("news", _("Show all news (can be slow)"));
421
        mBrowserBox->addRow("", false);
422
    }
423
    // Free the memory buffer now that we don't need it anymore
424
    free(mMemoryBuffer);
425
    mMemoryBuffer = nullptr;
426
    mDownloadedBytes = 0;
427
428
    mBrowserBox->updateHeight();
429
    mScrollArea->setVerticalScrollAmount(0);
430
}
431
432
void UpdaterWindow::loadPatch()
433
{
434
    if (mMemoryBuffer == nullptr)
435
    {
436
        logger->log1("Couldn't load patch");
437
        return;
438
    }
439
440
    // Reallocate and include terminating 0 character
441
    mMemoryBuffer = static_cast<char*>(
442
        realloc(mMemoryBuffer, mDownloadedBytes + 1));
443
    if (mMemoryBuffer == nullptr)
444
    {
445
        logger->log1("Couldn't load patch");
446
        return;
447
    }
448
    mMemoryBuffer[mDownloadedBytes] = '\0';
449
450
    std::string version;
451
452
    // Tokenize and add each line separately
453
    char *line = strtok(mMemoryBuffer, "\n");
454
    if (line != nullptr)
455
    {
456
        version = line;
457
        if (serverVersion == 0)
458
        {
459
            line = strtok(nullptr, "\n");
460
            if (line != nullptr)
461
            {
462
                mBrowserBox->addRow(strprintf("##9 Latest client version: "
463
                    "##6ManaPlus %s##0", line), true);
464
            }
465
        }
466
        if (version > CHECK_VERSION)
467
        {
468
            mBrowserBox->addRow("", true);
469
#if defined(ANDROID)
470
            const std::string url = "androidDownloadUrl";
471
            const std::string text = "androidDownloadUrl";
472
#elif defined(WIN32)
473
            const std::string url = "windowsDownloadUrl";
474
            const std::string text = "windowsDownloadUrl";
475
#else  // defined(ANDROID)
476
477
            const std::string url = "otherDownloadUrl";
478
            const std::string text = "otherDownloadUrl";
479
#endif  // defined(ANDROID)
480
481
            mBrowserBox->addRow(std::string("  ##1[@@").append(
482
                branding.getStringValue(url)).append("|").append(
483
                branding.getStringValue(text)).append("@@]"), true);
484
            mBrowserBox->addRow("##1You can download it from", true);
485
            mBrowserBox->addRow("##1ManaPlus updated.", true);
486
        }
487
        else
488
        {
489
            mBrowserBox->addRow("You have latest client version.", true);
490
        }
491
    }
492
493
    // Free the memory buffer now that we don't need it anymore
494
    free(mMemoryBuffer);
495
    mMemoryBuffer = nullptr;
496
    mDownloadedBytes = 0;
497
498
    mBrowserBox->updateHeight();
499
    mScrollArea->setVerticalScrollAmount(0);
500
}
501
502
int UpdaterWindow::updateProgress(void *ptr,
503
                                  const DownloadStatusT status,
504
                                  size_t dt,
505
                                  const size_t dn)
506
{
507
    UpdaterWindow *const uw = reinterpret_cast<UpdaterWindow *>(ptr);
508
    if (uw == nullptr)
509
        return -1;
510
511
    if (status == DownloadStatus::Complete)
512
    {
513
        uw->mDownloadComplete = true;
514
    }
515
    else if (status == DownloadStatus::Error ||
516
             status == DownloadStatus::Cancelled)
517
    {
518
        if (uw->mDownloadStatus == UpdateDownloadStatus::UPDATE_COMPLETE ||
519
            uw->mDownloadStatus == UpdateDownloadStatus::UPDATE_NEWS)
520
        {   // ignoring error in last state (was UPDATE_PATCH)
521
            uw->mDownloadStatus = UpdateDownloadStatus::UPDATE_COMPLETE;
522
            uw->mDownloadComplete = true;
523
            free(uw->mMemoryBuffer);
524
            uw->mMemoryBuffer = nullptr;
525
        }
526
        else
527
        {
528
            uw->mDownloadStatus = UpdateDownloadStatus::UPDATE_ERROR;
529
        }
530
    }
531
532
    if (dt == 0U)
533
        dt = 1;
534
535
    float progress = static_cast<float>(dn) /
536
                     static_cast<float>(dt);
537
538
    if (progress != progress)
539
        progress = 0.0F;  // check for NaN
540
    if (progress < 0.0F)
541
        progress = 0.0F;  // no idea how this could ever happen,
542
                          // but why not check for it anyway.
543
    if (progress > 1.0F)
544
        progress = 1.0F;
545
546
    uw->setLabel(std::string(uw->mCurrentFile).append(" (")
547
        .append(toString(CAST_S32(progress * 100))).append("%)"));
548
549
    uw->setProgress(progress);
550
551
    if ((client->getState() != State::UPDATE &&
552
        client->getState() != State::GAME) ||
553
        uw->mDownloadStatus == UpdateDownloadStatus::UPDATE_ERROR)
554
    {
555
        // If the action was canceled return an error code to stop the mThread
556
        return -1;
557
    }
558
559
    return 0;
560
}
561
562
size_t UpdaterWindow::memoryWrite(void *ptr, size_t size,
563
                                  size_t nmemb, void *stream)
564
{
565
    UpdaterWindow *const uw = reinterpret_cast<UpdaterWindow *>(stream);
566
    const size_t totalMem = size * nmemb;
567
    if (uw == nullptr)
568
        return 0;
569
    uw->mMemoryBuffer = static_cast<char*>(realloc(uw->mMemoryBuffer,
570
        CAST_SIZE(uw->mDownloadedBytes) + totalMem));
571
    if (uw->mMemoryBuffer != nullptr)
572
    {
573
        memcpy(&(uw->mMemoryBuffer[uw->mDownloadedBytes]), ptr, totalMem);
574
        uw->mDownloadedBytes += CAST_S32(totalMem);
575
    }
576
577
    return totalMem;
578
}
579
580
1
void UpdaterWindow::download()
581
{
582
1
    if (mDownload != nullptr)
583
    {
584
        mDownload->cancel();
585
        delete mDownload;
586
    }
587
1
    if (mDownloadStatus == UpdateDownloadStatus::UPDATE_PATCH)
588
    {
589
        mDownload = new Net::Download(this,
590
            branding.getStringValue("updateMirror1") + mCurrentFile,
591
            &updateProgress,
592
            true, false, mValidateXml);
593
        for (int f = 2; f < 8; f ++)
594
        {
595
            const std::string url = branding.getStringValue(
596
                "updateMirror" + toString(f));
597
            if (!url.empty())
598
                mDownload->addMirror(url + mCurrentFile);
599
        }
600
    }
601
    else
602
    {
603
1
        mDownload = new Net::Download(this,
604
4
            urlJoin(mUpdateHost, mCurrentFile),
605
            &updateProgress,
606

1
            false, false, mValidateXml);
607
608
1
        if (mDownloadStatus == UpdateDownloadStatus::UPDATE_LIST2 ||
609
            mDownloadStatus == UpdateDownloadStatus::UPDATE_RESOURCES2)
610
        {
611
            const std::string str = urlJoin(mUpdateServerPath, mCurrentFile);
612
            mDownload->addMirror(updateServer3 + str);
613
            mDownload->addMirror(updateServer4 + str);
614
            mDownload->addMirror(updateServer5 + str);
615
        }
616
        else
617
        {
618
1
            const STD_VECTOR<std::string> &mirrors = settings.updateMirrors;
619
4
            FOR_EACH (STD_VECTOR<std::string>::const_iterator, it, mirrors)
620
            {
621
                mDownload->addMirror(pathJoin(*it,
622
                    mCurrentFile));
623
            }
624
        }
625
    }
626
627
1
    if (mStoreInMemory)
628
    {
629
1
        mDownload->setWriteFunction(&UpdaterWindow::memoryWrite);
630
    }
631
    else
632
    {
633
        if (mDownloadStatus == UpdateDownloadStatus::UPDATE_RESOURCES)
634
        {
635
            mDownload->setFile(pathJoin(mUpdatesDir, mCurrentFile),
636
                mCurrentChecksum);
637
        }
638
        else
639
        {
640
            mDownload->setFile(pathJoin(mUpdatesDir,
641
                mCurrentFile),
642
                -1);
643
        }
644
    }
645
646
1
    if (mDownloadStatus != UpdateDownloadStatus::UPDATE_RESOURCES)
647
1
        mDownload->noCache();
648
649
2
    setLabel(mCurrentFile + " (0%)");
650
1
    mDownloadComplete = false;
651
652
1
    mDownload->start();
653
1
}
654
655
void UpdaterWindow::loadUpdates()
656
{
657
    if (mUpdateFiles.empty())
658
    {   // updates not downloaded
659
        mUpdateFiles = loadXMLFile(pathJoin(mUpdatesDir, xmlUpdateFile),
660
            false);
661
        if (mUpdateFiles.empty())
662
        {
663
            logger->log("Warning this server does not have a"
664
                        " %s file falling back to %s", xmlUpdateFile.c_str(),
665
                        txtUpdateFile.c_str());
666
            mUpdateFiles = loadTxtFile(pathJoin(mUpdatesDir,
667
                txtUpdateFile));
668
        }
669
    }
670
671
    std::string fixPath = mUpdatesDir + "/fix";
672
    const unsigned sz = CAST_U32(mUpdateFiles.size());
673
    for (mUpdateIndex = 0; mUpdateIndex < sz; mUpdateIndex++)
674
    {
675
        const UpdateFile &file = mUpdateFiles[mUpdateIndex];
676
        if (!file.group.empty())
677
            continue;
678
        UpdaterWindow::addUpdateFile(mUpdatesDir,
679
            fixPath,
680
            file.name,
681
            Append_false);
682
    }
683
    loadManaPlusUpdates(mUpdatesDir);
684
    loadMods(mUpdatesDir, mUpdateFiles);
685
}
686
687
void UpdaterWindow::loadLocalUpdates(const std::string &dir)
688
{
689
    STD_VECTOR<UpdateFile> updateFiles = loadXMLFile(
690
        pathJoin(dir, xmlUpdateFile),
691
        false);
692
693
    if (updateFiles.empty())
694
    {
695
        logger->log("Warning this server does not have a"
696
                    " %s file falling back to %s", xmlUpdateFile.c_str(),
697
                    txtUpdateFile.c_str());
698
        updateFiles = loadTxtFile(pathJoin(dir,
699
            txtUpdateFile));
700
    }
701
702
    const std::string fixPath = dir + "/fix";
703
    for (unsigned int updateIndex = 0,
704
         fsz = CAST_U32(updateFiles.size());
705
         updateIndex < fsz;
706
         updateIndex++)
707
    {
708
        const UpdateFile &file = updateFiles[updateIndex];
709
        if (!file.group.empty())
710
            continue;
711
        UpdaterWindow::addUpdateFile(dir,
712
            fixPath,
713
            file.name,
714
            Append_false);
715
    }
716
    loadManaPlusUpdates(dir);
717
    loadMods(dir, updateFiles);
718
}
719
720
void UpdaterWindow::unloadUpdates(const std::string &dir)
721
{
722
    STD_VECTOR<UpdateFile> updateFiles = loadXMLFile(
723
        pathJoin(dir, xmlUpdateFile),
724
        true);
725
726
    if (updateFiles.empty())
727
    {
728
        updateFiles = loadTxtFile(pathJoin(dir,
729
            txtUpdateFile));
730
    }
731
732
    const std::string fixPath = dir + "/fix";
733
    for (unsigned int updateIndex = 0,
734
         fsz = CAST_U32(updateFiles.size());
735
         updateIndex < fsz;
736
         updateIndex++)
737
    {
738
        UpdaterWindow::removeUpdateFile(dir,
739
            fixPath,
740
            updateFiles[updateIndex].name);
741
    }
742
    unloadManaPlusUpdates(dir);
743
}
744
745
void UpdaterWindow::loadManaPlusUpdates(const std::string &dir)
746
{
747
    std::string fixPath = dir + "/fix";
748
    STD_VECTOR<UpdateFile> updateFiles = loadXMLFile(
749
        pathJoin(fixPath, xmlUpdateFile),
750
        false);
751
752
    for (unsigned int updateIndex = 0,
753
         fsz = CAST_U32(updateFiles.size());
754
         updateIndex < fsz;
755
         updateIndex++)
756
    {
757
        const UpdateFile &file = updateFiles[updateIndex];
758
        if (!file.group.empty())
759
            continue;
760
        const std::string name = file.name;
761
        if (strStartWith(name, "manaplus_"))
762
        {
763
            struct stat statbuf;
764
            std::string fileName = pathJoin(fixPath,
765
                name);
766
            if (stat(fileName.c_str(), &statbuf) == 0)
767
            {
768
                VirtFs::mountZip(fileName,
769
                    Append_false);
770
            }
771
        }
772
    }
773
}
774
775
void UpdaterWindow::unloadManaPlusUpdates(const std::string &dir)
776
{
777
    const std::string fixPath = dir + "/fix";
778
    const STD_VECTOR<UpdateFile> updateFiles = loadXMLFile(
779
        pathJoin(fixPath, xmlUpdateFile),
780
        true);
781
782
    for (unsigned int updateIndex = 0,
783
         fsz = CAST_U32(updateFiles.size());
784
         updateIndex < fsz;
785
         updateIndex++)
786
    {
787
        std::string name = updateFiles[updateIndex].name;
788
        if (strStartWith(name, "manaplus_"))
789
        {
790
            struct stat statbuf;
791
            const std::string file = pathJoin(
792
                fixPath, name);
793
            if (stat(file.c_str(), &statbuf) == 0)
794
                VirtFs::unmountZip(file);
795
        }
796
    }
797
}
798
799
void UpdaterWindow::addUpdateFile(const std::string &restrict path,
800
                                  const std::string &restrict fixPath,
801
                                  const std::string &restrict file,
802
                                  const Append append)
803
{
804
    const std::string tmpPath = pathJoin(path, file);
805
    if (append == Append_false)
806
        VirtFs::mountZip(tmpPath, append);
807
808
    const std::string fixFile = pathJoin(fixPath, file);
809
    struct stat statbuf;
810
    if (stat(fixFile.c_str(), &statbuf) == 0)
811
        VirtFs::mountZip(fixFile, append);
812
813
    if (append == Append_true)
814
        VirtFs::mountZip(tmpPath, append);
815
}
816
817
void UpdaterWindow::removeUpdateFile(const std::string &restrict path,
818
                                     const std::string &restrict fixPath,
819
                                     const std::string &restrict file)
820
{
821
    VirtFs::unmountZip(pathJoin(path, file));
822
    const std::string fixFile = pathJoin(fixPath, file);
823
    struct stat statbuf;
824
    if (stat(fixFile.c_str(), &statbuf) == 0)
825
        VirtFs::unmountZip(fixFile);
826
}
827
828
void UpdaterWindow::logic()
829
{
830
    BLOCK_START("UpdaterWindow::logic")
831
    // Update Scroll logic
832
    mScrollArea->logic();
833
834
    // Synchronize label caption when necessary
835
    {
836
        MutexLocker lock(&mDownloadMutex);
837
838
        if (mLabel->getCaption() != mNewLabelCaption)
839
        {
840
            mLabel->setCaption(mNewLabelCaption);
841
            mLabel->adjustSize();
842
        }
843
844
        mProgressBar->setProgress(mDownloadProgress);
845
        if (!mUpdateFiles.empty() &&
846
            CAST_SIZE(mUpdateIndex) <= mUpdateFiles.size())
847
        {
848
            mProgressBar->setText(strprintf("%u/%u", mUpdateIndex
849
                + mUpdateIndexOffset + 1, CAST_U32(
850
                mUpdateFiles.size()) + CAST_S32(
851
                mTempUpdateFiles.size()) + 1));
852
        }
853
        else
854
        {
855
            mProgressBar->setText("");
856
        }
857
    }
858
859
    switch (mDownloadStatus)
860
    {
861
        case UpdateDownloadStatus::UPDATE_ERROR:
862
            mBrowserBox->addRow("", false);
863
            // TRANSLATORS: update message
864
            mBrowserBox->addRow(_("##1  The update process is incomplete."),
865
                false);
866
            // TRANSLATORS: Continues "The update process is incomplete.".
867
            mBrowserBox->addRow(_("##1  It is strongly recommended that"),
868
                false);
869
            // TRANSLATORS: Begins "It is strongly recommended that".
870
            mBrowserBox->addRow(_("##1  you try again later."),
871
                false);
872
            if (mDownload != nullptr)
873
                mBrowserBox->addRow(mDownload->getError(), false);
874
            mBrowserBox->updateHeight();
875
            mScrollArea->setVerticalScrollAmount(
876
                    mScrollArea->getVerticalMaxScroll());
877
            mDownloadStatus = UpdateDownloadStatus::UPDATE_COMPLETE;
878
            break;
879
        case UpdateDownloadStatus::UPDATE_NEWS:
880
            if (mDownloadComplete)
881
            {
882
                // Parse current memory buffer as news and dispose of the data
883
                loadNews();
884
885
                mValidateXml = true;
886
                mCurrentFile = xmlUpdateFile;
887
                mStoreInMemory = false;
888
                mDownloadStatus = UpdateDownloadStatus::UPDATE_LIST;
889
                download();  // download() changes mDownloadComplete to false
890
            }
891
            break;
892
        case UpdateDownloadStatus::UPDATE_PATCH:
893
            if (mDownloadComplete)
894
            {
895
                // Parse current memory buffer as news and dispose of the data
896
                loadPatch();
897
898
                mUpdateHost = updateServer2 + mUpdateServerPath;
899
                mUpdatesDir = pathJoin(mUpdatesDir, "fix");
900
                mCurrentFile = xmlUpdateFile;
901
                mValidateXml = true;
902
                mStoreInMemory = false;
903
                mDownloadStatus = UpdateDownloadStatus::UPDATE_LIST2;
904
                download();
905
            }
906
            break;
907
908
        case UpdateDownloadStatus::UPDATE_LIST:
909
            if (mDownloadComplete)
910
            {
911
                if (mCurrentFile == xmlUpdateFile)
912
                {
913
                    mUpdateFiles = loadXMLFile(pathJoin(
914
                        mUpdatesDir, xmlUpdateFile),
915
                        true);
916
917
                    if (mUpdateFiles.empty())
918
                    {
919
                        logger->log("Warning this server does not have a %s"
920
                                    " file falling back to %s",
921
                                    xmlUpdateFile.c_str(),
922
                                    txtUpdateFile.c_str());
923
924
                        // If the resources.xml file fails,
925
                        // fall back onto a older version
926
                        mCurrentFile = txtUpdateFile;
927
                        mValidateXml = false;
928
                        mStoreInMemory = false;
929
                        mDownloadStatus = UpdateDownloadStatus::UPDATE_LIST;
930
                        download();
931
                        break;
932
                    }
933
                }
934
                else if (mCurrentFile == txtUpdateFile)
935
                {
936
                    mValidateXml = true;
937
                    mUpdateFiles = loadTxtFile(pathJoin(mUpdatesDir,
938
                        txtUpdateFile));
939
                }
940
                mStoreInMemory = false;
941
                mDownloadStatus = UpdateDownloadStatus::UPDATE_RESOURCES;
942
            }
943
            break;
944
        case UpdateDownloadStatus::UPDATE_RESOURCES:
945
            if (mDownloadComplete)
946
            {
947
                if (CAST_SIZE(mUpdateIndex) < mUpdateFiles.size())
948
                {
949
                    UpdateFile thisFile = mUpdateFiles[mUpdateIndex];
950
                    if (thisFile.type == "music"
951
                        && !config.getBoolValue("download-music"))
952
                    {
953
                        mUpdateIndex++;
954
                        break;
955
                    }
956
                    mCurrentFile = thisFile.name;
957
                    std::string checksum;
958
                    checksum = thisFile.hash;
959
                    std::stringstream ss(checksum);
960
                    ss >> std::hex >> mCurrentChecksum;
961
962
                    std::ifstream temp(pathJoin(mUpdatesDir,
963
                        mCurrentFile).c_str());
964
965
                    mValidateXml = false;
966
                    if (!temp.is_open() || !validateFile(pathJoin(
967
                        mUpdatesDir, mCurrentFile),
968
                        mCurrentChecksum))
969
                    {
970
                        temp.close();
971
                        download();
972
                    }
973
                    else
974
                    {
975
                        temp.close();
976
                        logger->log("%s already here", mCurrentFile.c_str());
977
                    }
978
                    mUpdateIndex++;
979
                }
980
                else
981
                {
982
                    if (!mSkipPatches)
983
                    {
984
                        // Download of updates completed
985
                        mCurrentFile = "latest.txt";
986
                        mStoreInMemory = true;
987
                        mDownloadStatus = UpdateDownloadStatus::UPDATE_PATCH;
988
                        mValidateXml = false;
989
                        download();  // download() changes
990
                                     // mDownloadComplete to false
991
                    }
992
                    else
993
                    {
994
                        mDownloadStatus =
995
                            UpdateDownloadStatus::UPDATE_COMPLETE;
996
                    }
997
                }
998
            }
999
            break;
1000
        case UpdateDownloadStatus::UPDATE_LIST2:
1001
            if (mDownloadComplete)
1002
            {
1003
                if (mCurrentFile == xmlUpdateFile)
1004
                {
1005
                    mTempUpdateFiles = loadXMLFile(pathJoin(
1006
                        mUpdatesDir, xmlUpdateFile),
1007
                        true);
1008
                }
1009
                mUpdateIndexOffset = mUpdateIndex;
1010
                mUpdateIndex = 0;
1011
                mValidateXml = true;
1012
                mStoreInMemory = false;
1013
                mDownloadStatus = UpdateDownloadStatus::UPDATE_RESOURCES2;
1014
                download();
1015
            }
1016
            break;
1017
        case UpdateDownloadStatus::UPDATE_RESOURCES2:
1018
            if (mDownloadComplete)
1019
            {
1020
                mValidateXml = false;
1021
                if (CAST_SIZE(mUpdateIndex)
1022
                    < mTempUpdateFiles.size())
1023
                {
1024
                    const UpdateFile thisFile = mTempUpdateFiles[mUpdateIndex];
1025
                    mCurrentFile = thisFile.name;
1026
                    std::string checksum;
1027
                    checksum = thisFile.hash;
1028
                    std::stringstream ss(checksum);
1029
                    ss >> std::hex >> mCurrentChecksum;
1030
1031
                    std::ifstream temp((pathJoin(mUpdatesDir,
1032
                        mCurrentFile)).c_str());
1033
1034
                    if (!temp.is_open() || !validateFile(pathJoin(
1035
                        mUpdatesDir, mCurrentFile),
1036
                        mCurrentChecksum))
1037
                    {
1038
                        temp.close();
1039
                        download();
1040
                    }
1041
                    else
1042
                    {
1043
                        temp.close();
1044
                        logger->log("%s already here", mCurrentFile.c_str());
1045
                    }
1046
                    mUpdateIndex++;
1047
                }
1048
                else
1049
                {
1050
                    mUpdatesDir = mUpdatesDirReal;
1051
                    mDownloadStatus = UpdateDownloadStatus::UPDATE_COMPLETE;
1052
                }
1053
            }
1054
            break;
1055
        case UpdateDownloadStatus::UPDATE_COMPLETE:
1056
            mUpdatesDir = mUpdatesDirReal;
1057
            enable();
1058
            // TRANSLATORS: updater window label
1059
            setLabel(_("Completed"));
1060
            mDownloadStatus = UpdateDownloadStatus::UPDATE_IDLE;
1061
            break;
1062
        case UpdateDownloadStatus::UPDATE_IDLE:
1063
            break;
1064
        default:
1065
            logger->log("UpdaterWindow::logic unknown status: "
1066
                        + toString(CAST_U32(mDownloadStatus)));
1067
            break;
1068
    }
1069
    BLOCK_END("UpdaterWindow::logic")
1070
}
1071
1072
bool UpdaterWindow::validateFile(const std::string &filePath,
1073
                                 const unsigned long hash)
1074
{
1075
    FILE *const file = fopen(filePath.c_str(), "rb");
1076
    if (file == nullptr)
1077
        return false;
1078
1079
    const unsigned long adler = Net::Download::fadler32(file);
1080
    fclose(file);
1081
    return adler == hash;
1082
}
1083
1084
unsigned long UpdaterWindow::getFileHash(const std::string &filePath)
1085
{
1086
    int size = 0;
1087
    const char *const buf = VirtFs::loadFile(filePath, size);
1088
    if (buf == nullptr)
1089
        return 0;
1090
    unsigned long res = Net::Download::adlerBuffer(buf, size);
1091
    delete [] buf;
1092
    return res;
1093
}
1094
1095
void UpdaterWindow::loadFile(std::string file)
1096
{
1097
    mBrowserBox->clearRows();
1098
    trim(file);
1099
1100
    StringVect lines;
1101
    Files::loadTextFileLocal(mUpdatesDir + "/local/help/news.txt", lines);
1102
1103
    for (size_t i = 0, sz = lines.size(); i < sz; ++i)
1104
        mBrowserBox->addRow(lines[i], false);
1105
    mBrowserBox->updateHeight();
1106
}
1107
1108
void UpdaterWindow::loadMods(const std::string &dir,
1109
                             const STD_VECTOR<UpdateFile> &updateFiles)
1110
{
1111
    ModDB::load();
1112
    std::string modsString = serverConfig.getValue("mods", "");
1113
    std::set<std::string> modsList;
1114
    splitToStringSet(modsList, modsString, '|');
1115
1116
    const std::string fixPath = dir + "/fix";
1117
    for (unsigned int updateIndex = 0,
1118
         fsz = CAST_U32(updateFiles.size());
1119
         updateIndex < fsz;
1120
         updateIndex ++)
1121
    {
1122
        const UpdateFile &file = updateFiles[updateIndex];
1123
        if (file.group.empty())
1124
            continue;
1125
        const std::set<std::string>::const_iterator
1126
            it = modsList.find(file.group);
1127
        if (it != modsList.end())
1128
        {
1129
            UpdaterWindow::addUpdateFile(dir,
1130
                fixPath,
1131
                file.name,
1132
                Append_false);
1133
        }
1134
    }
1135
1136
    STD_VECTOR<UpdateFile> updateFiles2 = loadXMLFile(
1137
        pathJoin(fixPath, xmlUpdateFile),
1138
        true);
1139
1140
    for (unsigned int updateIndex = 0,
1141
         fsz = CAST_U32(updateFiles2.size());
1142
         updateIndex < fsz;
1143
         updateIndex++)
1144
    {
1145
        const UpdateFile &file = updateFiles2[updateIndex];
1146
        if (file.group.empty())
1147
            continue;
1148
        std::string name = file.name;
1149
        if (strStartWith(name, "manaplus_"))
1150
        {
1151
            const std::set<std::string>::const_iterator
1152
                it = modsList.find(file.group);
1153
            if (it != modsList.end())
1154
            {
1155
                struct stat statbuf;
1156
                std::string fileName = pathJoin(fixPath,
1157
                    name);
1158
                if (stat(fileName.c_str(), &statbuf) == 0)
1159
                {
1160
                    VirtFs::mountZip(fileName,
1161
                        Append_false);
1162
                }
1163
            }
1164
        }
1165
    }
1166
1167
    loadDirMods(dir + "/local/");
1168
}
1169
1170
void UpdaterWindow::loadDirMods(const std::string &dir)
1171
{
1172
    ModDB::load();
1173
    const ModInfos &mods = ModDB::getAll();
1174
1175
    std::string modsString = serverConfig.getValue("mods", "");
1176
    StringVect modsList;
1177
    splitToStringVector(modsList, modsString, '|');
1178
    FOR_EACH (StringVectCIter, it, modsList)
1179
    {
1180
        const std::string &name = *it;
1181
        const ModInfoCIterator modIt = mods.find(name);
1182
        if (modIt == mods.end())
1183
            continue;
1184
        const ModInfo *const mod = (*modIt).second;
1185
        if (mod != nullptr)
1186
        {
1187
            const std::string &localDir = mod->getLocalDir();
1188
            if (!localDir.empty())
1189
            {
1190
                VirtFs::mountDir(pathJoin(dir, localDir),
1191
                    Append_false);
1192
            }
1193
        }
1194
    }
1195
}
1196
1197
void UpdaterWindow::unloadMods(const std::string &dir)
1198
{
1199
    const ModInfos &mods = ModDB::getAll();
1200
    std::string modsString = serverConfig.getValue("mods", "");
1201
    StringVect modsList;
1202
    splitToStringVector(modsList, modsString, '|');
1203
    FOR_EACH (StringVectCIter, it, modsList)
1204
    {
1205
        const std::string &name = *it;
1206
        const ModInfoCIterator modIt = mods.find(name);
1207
        if (modIt == mods.end())
1208
            continue;
1209
        const ModInfo *const mod = (*modIt).second;
1210
        if (mod != nullptr)
1211
        {
1212
            const std::string &localDir = mod->getLocalDir();
1213
            if (!localDir.empty())
1214
                VirtFs::unmountDir(pathJoin(dir, localDir));
1215
        }
1216
    }
1217
}
1218
1219
void UpdaterWindow::deleteSelf()
1220
{
1221
    scheduleDelete();
1222
    updaterWindow = nullptr;
1223

3
}