VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/networking/UIDownloaderGuestAdditions.cpp

Last change on this file was 103793, checked in by vboxsync, 2 months ago

FE/Qt: UICommon: Move versioning related functionality to UIVersion / UIVersionInfo; Rework user cases accordingly.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 7.8 KB
Line 
1/* $Id: UIDownloaderGuestAdditions.cpp 103793 2024-03-11 19:17:31Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIDownloaderGuestAdditions class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QDir>
30#include <QFile>
31#include <QRegularExpression>
32#include <QVariant>
33
34/* GUI includes: */
35#include "QIFileDialog.h"
36#include "UIDownloaderGuestAdditions.h"
37#include "UIGlobalSession.h"
38#include "UIMessageCenter.h"
39#include "UIModalWindowManager.h"
40#include "UINetworkReply.h"
41#include "UINotificationCenter.h"
42#include "UIVersion.h"
43
44/* Other VBox includes: */
45#include <iprt/sha.h>
46
47
48UIDownloaderGuestAdditions::UIDownloaderGuestAdditions()
49{
50 /* Get version number and adjust it for test and trunk builds. The server only has official releases. */
51 const QString strVersion = UIVersion(UIVersionInfo::vboxVersionStringNormalized()).effectiveReleasedVersion().toString();
52
53 /* Prepare source/target: */
54 const QString strSourceName = QString("%1_%2.iso").arg(GUI_GuestAdditionsName, strVersion);
55 const QString strSourcePath = QString("https://download.virtualbox.org/virtualbox/%1/").arg(strVersion);
56 const QString strSource = strSourcePath + strSourceName;
57 const QString strPathSHA256SumsFile = QString("https://www.virtualbox.org/download/hashes/%1/SHA256SUMS").arg(strVersion);
58 const QString strTarget = QDir(gpGlobalSession->homeFolder()).absoluteFilePath(QString("%1.tmp").arg(strSourceName));
59
60 /* Set source/target: */
61 setSource(strSource);
62 setTarget(strTarget);
63 setPathSHA256SumsFile(strPathSHA256SumsFile);
64}
65
66QString UIDownloaderGuestAdditions::description() const
67{
68 return UIDownloader::description().arg(tr("VirtualBox Guest Additions"));
69}
70
71bool UIDownloaderGuestAdditions::askForDownloadingConfirmation(UINetworkReply *pReply)
72{
73 return msgCenter().confirmDownloadGuestAdditions(source().toString(), pReply->header(UINetworkReply::ContentLengthHeader).toInt());
74}
75
76void UIDownloaderGuestAdditions::handleDownloadedObject(UINetworkReply *pReply)
77{
78 m_receivedData = pReply->readAll();
79}
80
81void UIDownloaderGuestAdditions::handleVerifiedObject(UINetworkReply *pReply)
82{
83 /* Try to verify the SHA-256 checksum: */
84 QString strCalculatedSumm;
85 bool fSuccess = false;
86 do
87 {
88 /* Read received data into the buffer: */
89 const QByteArray receivedData(pReply->readAll());
90 /* Make sure it's not empty: */
91 if (receivedData.isEmpty())
92 break;
93
94 /* Parse buffer contents to dictionary: */
95#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
96 const QStringList dictionary(QString(receivedData).split("\n", Qt::SkipEmptyParts));
97#else
98 const QStringList dictionary(QString(receivedData).split("\n", QString::SkipEmptyParts));
99#endif
100 /* Make sure it's not empty: */
101 if (dictionary.isEmpty())
102 break;
103
104 /* Parse each record to tags, look for the required one: */
105 foreach (const QString &strRecord, dictionary)
106 {
107 const QString strFileName = strRecord.section(" *", 1);
108 const QString strDownloadedSumm = strRecord.section(" *", 0, 0);
109 if (strFileName == source().fileName())
110 {
111 /* Calc the SHA-256 on the bytes, creating a string: */
112 uint8_t abHash[RTSHA256_HASH_SIZE];
113 RTSha256(m_receivedData.constData(), m_receivedData.length(), abHash);
114 char szDigest[RTSHA256_DIGEST_LEN + 1];
115 int rc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
116 if (RT_FAILURE(rc))
117 {
118 AssertRC(rc);
119 szDigest[0] = '\0';
120 }
121 strCalculatedSumm = szDigest;
122 //printf("Downloaded SHA-256 summ: [%s]\n", strDownloadedSumm.toUtf8().constData());
123 //printf("Calculated SHA-256 summ: [%s]\n", strCalculatedSumm.toUtf8().constData());
124 /* Make sure checksum is valid: */
125 fSuccess = strDownloadedSumm == strCalculatedSumm;
126 break;
127 }
128 }
129 }
130 while (false);
131
132 /* If SHA-256 checksum verification failed: */
133 if (!fSuccess)
134 {
135 /* Warn the user about additions-image was downloaded and saved but checksum is invalid: */
136 UINotificationMessage::cannotValidateGuestAdditionsSHA256Sum(source().toString(), QDir::toNativeSeparators(target()));
137 return;
138 }
139
140 /* Make sure temporary file exists. If we have
141 * reached this place, it's already written and verified. */
142 const QString strTempFilename = target();
143 if (!QFile::exists(strTempFilename))
144 {
145 /* But still we are providing a failsafe.
146 * Since we can try to write it again. */
147 QFile file(strTempFilename);
148 if (!file.open(QIODevice::WriteOnly))
149 AssertFailedReturnVoid();
150 file.write(m_receivedData);
151 }
152
153 /* Rename temporary file to target one. This can require a number
154 * of tries to let user choose the place to save file to. */
155 QString strNetTarget = target();
156 strNetTarget.remove(QRegularExpression("\\.tmp$"));
157 setTarget(strNetTarget);
158 while (true)
159 {
160 /* Make sure target file doesn't exist: */
161 bool fTargetFileExists = QFile::exists(target());
162 if (fTargetFileExists)
163 {
164 /* We should ask user about file rewriting (or exit otherwise): */
165 if (!msgCenter().confirmOverridingFile(QDir::toNativeSeparators(target())))
166 break;
167 /* And remove file if rewriting confirmed: */
168 if (QFile::remove(target()))
169 fTargetFileExists = false;
170 }
171
172 /* Try to rename temporary file to target one (would fail if target file still exists): */
173 const bool fFileRenamed = !fTargetFileExists && QFile::rename(strTempFilename, target());
174
175 /* If file renamed: */
176 if (fFileRenamed)
177 {
178 /* Warn the user about additions-image downloaded and saved, propose to mount it (and/or exit in any case): */
179 if (msgCenter().proposeMountGuestAdditions(source().toString(), QDir::toNativeSeparators(target())))
180 emit sigDownloadFinished(target());
181 break;
182 }
183 else
184 {
185 /* Warn the user about additions-image was downloaded but was NOT saved: */
186 msgCenter().cannotSaveGuestAdditions(source().toString(), QDir::toNativeSeparators(target()));
187 /* Ask the user for another location for the additions-image file: */
188 const QString strTarget = QIFileDialog::getExistingDirectory(QFileInfo(target()).absolutePath(),
189 windowManager().mainWindowShown(),
190 tr("Select folder to save Guest Additions image to"), true);
191
192 /* Check if user had really set a new target (and exit in opposite case): */
193 if (!strTarget.isNull())
194 setTarget(QDir(strTarget).absoluteFilePath(QFileInfo(target()).fileName()));
195 else
196 break;
197 }
198 }
199}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use