GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/updaterwindow.cpp Lines: 83 601 13.8 %
Date: 2018-05-19 03:07:18 Branches: 61 830 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
6
const std::string xmlUpdateFile("resources.xml");
66
6
const std::string txtUpdateFile("resources2.txt");
67
6
const std::string updateServer2
68
    ("http://download.manaplus.org/manaplus/updates/");
69
6
const std::string updateServer3
70
    ("http://download2.manaplus.org/manaplus/updates/");
71
6
const std::string updateServer4
72
    ("http://download.evolonline.org/manaplus/updates/");
73
6
const std::string updateServer5
74
    ("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
2
UpdaterWindow::UpdaterWindow(const std::string &restrict updateHost,
170
                             const std::string &restrict updatesDir,
171
                             const bool applyUpdates,
172
2
                             const UpdateTypeT updateType) :
173
    // TRANSLATORS: updater window name
174
2
    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

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

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

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

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

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

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

4
        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






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

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

6
}