summaryrefslogtreecommitdiff
path: root/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderExtensionPack.cpp
blob: 7a0d4836b242736ad4f6e3fe62e716ec06915203 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/* $Id$ */
/** @file
 * VBox Qt GUI - UIDownloaderExtensionPack class implementation.
 */

/*
 * Copyright (C) 2011-2022 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 */

/* Qt includes: */
#include <QDir>
#include <QFile>
#include <QRegularExpression>
#include <QVariant>

/* GUI includes: */
#include "QIFileDialog.h"
#include "UICommon.h"
#include "UIDownloaderExtensionPack.h"
#include "UIMessageCenter.h"
#include "UIModalWindowManager.h"
#include "UINetworkReply.h"
#include "UINotificationCenter.h"
#include "UIVersion.h"

/* Other VBox includes: */
#include <iprt/sha.h>


UIDownloaderExtensionPack::UIDownloaderExtensionPack()
{
    /* Get version number and adjust it for test and trunk builds. The server only has official releases. */
    const QString strVersion = UIVersion(uiCommon().vboxVersionStringNormalized()).effectiveReleasedVersion().toString();

    /* Prepare source/target: */
    const QString strUnderscoredName = QString(GUI_ExtPackName).replace(' ', '_');
    const QString strSourceName = QString("%1-%2.vbox-extpack").arg(strUnderscoredName, strVersion);
    const QString strSourcePath = QString("https://download.virtualbox.org/virtualbox/%1/").arg(strVersion);
    const QString strSource = strSourcePath + strSourceName;
    const QString strPathSHA256SumsFile = QString("https://www.virtualbox.org/download/hashes/%1/SHA256SUMS").arg(strVersion);
    const QString strTarget = QDir(uiCommon().homeFolder()).absoluteFilePath(strSourceName);

    /* Set source/target: */
    setSource(strSource);
    setTarget(strTarget);
    setPathSHA256SumsFile(strPathSHA256SumsFile);
}

QString UIDownloaderExtensionPack::description() const
{
    return UIDownloader::description().arg(tr("VirtualBox Extension Pack"));
}

bool UIDownloaderExtensionPack::askForDownloadingConfirmation(UINetworkReply *pReply)
{
    return msgCenter().confirmDownloadExtensionPack(GUI_ExtPackName, source().toString(), pReply->header(UINetworkReply::ContentLengthHeader).toInt());
}

void UIDownloaderExtensionPack::handleDownloadedObject(UINetworkReply *pReply)
{
    m_receivedData = pReply->readAll();
}

void UIDownloaderExtensionPack::handleVerifiedObject(UINetworkReply *pReply)
{
    /* Try to verify the SHA-256 checksum: */
    QString strCalculatedSumm;
    bool fSuccess = false;
    do
    {
        /* Read received data into the buffer: */
        const QByteArray receivedData(pReply->readAll());
        /* Make sure it's not empty: */
        if (receivedData.isEmpty())
            break;

        /* Parse buffer contents to dictionary: */
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
        const QStringList dictionary(QString(receivedData).split("\n", Qt::SkipEmptyParts));
#else
        const QStringList dictionary(QString(receivedData).split("\n", QString::SkipEmptyParts));
#endif
        /* Make sure it's not empty: */
        if (dictionary.isEmpty())
            break;

        /* Parse each record to tags, look for the required one: */
        foreach (const QString &strRecord, dictionary)
        {
            QRegularExpression separator(" \\*|  ");
            const QString strFileName = strRecord.section(separator, 1);
            const QString strDownloadedSumm = strRecord.section(separator, 0, 0);
            if (strFileName == source().fileName())
            {
                /* Calc the SHA-256 on the bytes, creating a string: */
                uint8_t abHash[RTSHA256_HASH_SIZE];
                RTSha256(m_receivedData.constData(), m_receivedData.length(), abHash);
                char szDigest[RTSHA256_DIGEST_LEN + 1];
                int rc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
                if (RT_FAILURE(rc))
                {
                    AssertRC(rc);
                    szDigest[0] = '\0';
                }
                strCalculatedSumm = szDigest;
                //printf("Downloaded SHA-256 summ: [%s]\n", strDownloadedSumm.toUtf8().constData());
                //printf("Calculated SHA-256 summ: [%s]\n", strCalculatedSumm.toUtf8().constData());
                /* Make sure checksum is valid: */
                fSuccess = strDownloadedSumm == strCalculatedSumm;
                break;
            }
        }
    }
    while (false);

    /* If SHA-256 checksum verification failed: */
    if (!fSuccess)
    {
        /* Warn the user about additions-image was downloaded and saved but checksum is invalid: */
        UINotificationMessage::cannotValidateExtentionPackSHA256Sum(GUI_ExtPackName, source().toString(), QDir::toNativeSeparators(target()));
        return;
    }

    /* Serialize that buffer into the file: */
    while (true)
    {
        /* Make sure the file already exists.  If we reached
         * this place, it's already written and checked. */
        QFile file(target());
        bool fSuccess = false;
        /* Check step. Try to open file for reading first. */
        if (file.open(QIODevice::ReadOnly))
            fSuccess = true;
        /* Failsafe step. Try to open file for writing otherwise. */
        if (!fSuccess && file.open(QIODevice::WriteOnly))
        {
            /* Write buffer into the file: */
            file.write(m_receivedData);
            file.close();
            fSuccess = true;
        }
        /* If the file already exists or was just written: */
        if (fSuccess)
        {
            /* Warn the listener about extension-pack was downloaded: */
            emit sigDownloadFinished(source().toString(), target(), strCalculatedSumm);
            break;
        }

        /* Warn the user about extension-pack was downloaded but was NOT saved: */
        msgCenter().cannotSaveExtensionPack(GUI_ExtPackName, source().toString(), QDir::toNativeSeparators(target()));

        /* Ask the user for another location for the extension-pack file: */
        QString strTarget = QIFileDialog::getExistingDirectory(QFileInfo(target()).absolutePath(),
                                                               windowManager().mainWindowShown(),
                                                               tr("Select folder to save %1 to").arg(GUI_ExtPackName), true);

        /* Check if user had really set a new target: */
        if (!strTarget.isNull())
            setTarget(QDir(strTarget).absoluteFilePath(QFileInfo(target()).fileName()));
        else
            break;
    }
}