GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/updaterwindow.cpp Lines: 83 601 13.8 %
Date: 2017-11-29 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-2017  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 <sstream>
59
60
#include "debug.h"
61
62
UpdaterWindow *updaterWindow = nullptr;
63
64
6
const std::string xmlUpdateFile("resources.xml");
65
6
const std::string txtUpdateFile("resources2.txt");
66
6
const std::string updateServer2
67
    ("http://download.manaplus.org/manaplus/updates/");
68
6
const std::string updateServer3
69
    ("http://download2.manaplus.org/manaplus/updates/");
70
6
const std::string updateServer4
71
    ("http://download.evolonline.org/manaplus/updates/");
72
6
const std::string updateServer5
73
    ("http://download3.manaplus.org/manaplus/updates/");
74
75
/**
76
 * Load the given file into a vector of updateFiles.
77
 */
78
static STD_VECTOR<UpdateFile> loadXMLFile(const std::string &fileName,
79
                                           const bool loadMods)
80
{
81
    STD_VECTOR<UpdateFile> files;
82
    XML::Document doc(fileName, UseVirtFs_false, SkipError_false);
83
    XmlNodeConstPtrConst rootNode = doc.rootNode();
84
85
    if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "updates"))
86
    {
87
        logger->log("Error loading update file: %s", fileName.c_str());
88
        return files;
89
    }
90
91
    for_each_xml_child_node(fileNode, rootNode)
92
    {
93
        const bool isMod = xmlNameEqual(fileNode, "mod");
94
        if (!xmlNameEqual(fileNode, "update") && !isMod)
95
            continue;
96
97
        UpdateFile file;
98
        file.name = XML::getProperty(fileNode, "file", "");
99
        file.hash = XML::getProperty(fileNode, "hash", "");
100
        file.type = XML::getProperty(fileNode, "type", "data");
101
        file.desc = XML::getProperty(fileNode, "description", "");
102
        file.group = XML::getProperty(fileNode, "group", "");
103
        if (!file.group.empty() && (!isMod || !loadMods))
104
            continue;
105
106
        const std::string version = XML::getProperty(
107
            fileNode, "version", "");
108
        if (!version.empty())
109
        {
110
            if (version > CHECK_VERSION)
111
                continue;
112
        }
113
        const std::string notVersion = XML::getProperty(
114
            fileNode, "notVersion", "");
115
        if (!notVersion.empty())
116
        {
117
            if (notVersion <= CHECK_VERSION)
118
                continue;
119
        }
120
        if (XML::getProperty(fileNode, "required", "yes") == "yes")
121
            file.required = true;
122
        else
123
            file.required = false;
124
125
        if (checkPath(file.name))
126
            files.push_back(file);
127
    }
128
129
    return files;
130
}
131
132
static STD_VECTOR<UpdateFile> loadTxtFile(const std::string &fileName)
133
{
134
    STD_VECTOR<UpdateFile> files;
135
    std::ifstream fileHandler;
136
    fileHandler.open(fileName.c_str(), std::ios::in);
137
138
    if (fileHandler.is_open())
139
    {
140
        while (fileHandler.good())
141
        {
142
            char name[256];
143
            char hash[50];
144
            fileHandler.getline(name, 256, ' ');
145
            fileHandler.getline(hash, 50);
146
147
            UpdateFile thisFile;
148
            thisFile.name = name;
149
            thisFile.hash = hash;
150
            thisFile.type = "data";
151
            thisFile.group.clear();
152
            thisFile.required = true;
153
            thisFile.desc.clear();
154
155
            if (!thisFile.name.empty() && checkPath(thisFile.name))
156
                files.push_back(thisFile);
157
        }
158
    }
159
    else
160
    {
161
        logger->log("Error loading update file: %s", fileName.c_str());
162
    }
163
    fileHandler.close();
164
165
    return files;
166
}
167
168
2
UpdaterWindow::UpdaterWindow(const std::string &restrict updateHost,
169
                             const std::string &restrict updatesDir,
170
                             const bool applyUpdates,
171
2
                             const UpdateTypeT updateType) :
172
    // TRANSLATORS: updater window name
173
2
    Window(_("Updating..."), Modal_false, nullptr, "update.xml"),
174
    ActionListener(),
175
    KeyListener(),
176
    mDownloadProgress(0.0F),
177
    mUpdateHost(updateHost),
178
    mUpdatesDir(updatesDir),
179
    mUpdatesDirReal(updatesDir),
180
    mCurrentFile("news.txt"),
181
    mNewLabelCaption(),
182
    mDownloadMutex(),
183
    mCurrentChecksum(0),
184
    mMemoryBuffer(nullptr),
185
    mDownload(nullptr),
186
    mUpdateFiles(),
187
    mTempUpdateFiles(),
188
    mUpdateServerPath(mUpdateHost),
189

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

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

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

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

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

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

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






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

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

6
}