VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/net/UIUpdateManager.cpp@ 82781

Last change on this file since 82781 was 79365, checked in by vboxsync, 5 years ago

Renaming VBoxGlobal to UICommon for bugref:9049 as planned.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.9 KB
Line 
1/* $Id: UIUpdateManager.cpp 79365 2019-06-26 15:57:32Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIUpdateManager class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QDir>
20#include <QPointer>
21#include <QTimer>
22#include <QUrl>
23#include <QUrlQuery>
24
25/* GUI includes: */
26#include "QIProcess.h"
27#include "UICommon.h"
28#include "VBoxUtils.h"
29#include "UIDownloaderExtensionPack.h"
30#include "UIExtraDataManager.h"
31#include "UIMessageCenter.h"
32#include "UIModalWindowManager.h"
33#include "UINetworkManager.h"
34#include "UINetworkCustomer.h"
35#include "UINetworkRequest.h"
36#include "UIUpdateDefs.h"
37#include "UIUpdateManager.h"
38
39/* COM includes: */
40#include "CExtPack.h"
41#include "CExtPackManager.h"
42#include "CSystemProperties.h"
43
44/* Other VBox includes: */
45//# include <iprt/err.h>
46#include <iprt/path.h>
47#include <iprt/system.h>
48#include <VBox/version.h>
49
50/* enable to test the version update check */
51//#define VBOX_NEW_VERSION_TEST "5.1.12_0 http://unknown.unknown.org/0.0.0/VirtualBox-0.0.0-0-unknown.pkg"
52
53/* Forward declarations: */
54class UIUpdateStep;
55
56
57/** QObject subclass providing GUI with
58 * an object to manage queue of update-steps. */
59class UIUpdateQueue : public QObject
60{
61 Q_OBJECT;
62
63signals:
64
65 /** Starts the queue. */
66 void sigStartQueue();
67
68 /** Notifies about queue is finished. */
69 void sigQueueFinished();
70
71public:
72
73 /** Constructs update queue passing @a pParent to the base-class. */
74 UIUpdateQueue(UIUpdateManager *pParent) : QObject(pParent) {}
75
76 /** Starts the queue. */
77 void start() { emit sigStartQueue(); }
78
79private:
80
81 /** Returns whether queue is empty. */
82 bool isEmpty() const { return m_pLastStep.isNull(); }
83
84 /** Defines the last queued @a pStep. */
85 void setLastStep(UIUpdateStep *pStep) { m_pLastStep = pStep; }
86 /** Returns the last queued step. */
87 UIUpdateStep *lastStep() const { return m_pLastStep; }
88
89 /** Holds the last queued step reference. */
90 QPointer<UIUpdateStep> m_pLastStep;
91
92 /** Allows step to manage queue. */
93 friend class UIUpdateStep;
94};
95
96
97/** Update step interface.
98 * UINetworkCustomer extension which allows background network updates. */
99class UIUpdateStep : public UINetworkCustomer
100{
101 Q_OBJECT;
102
103signals:
104
105 /** Notifies about step is finished. */
106 void sigStepComplete();
107
108public:
109
110 /** Constructs update step passing @a pQueue and @a fForceCall to the base-class. */
111 UIUpdateStep(UIUpdateQueue *pQueue, bool fForceCall);
112
113protected:
114
115 /** Handles network reply progress for @a iReceived amount of bytes among @a iTotal. */
116 virtual void processNetworkReplyProgress(qint64 iReceived, qint64 iTotal) /* override */;
117 /** Handles network reply canceling for a passed @a pReply. */
118 virtual void processNetworkReplyCanceled(UINetworkReply *pReply) /* override */;
119 /** Handles network reply finishing for a passed @a pReply. */
120 virtual void processNetworkReplyFinished(UINetworkReply *pReply) /* override */;
121
122protected slots:
123
124 /** Starts the step. */
125 virtual void sltStartStep() = 0;
126};
127
128
129/** Update step subclass to check for the new VirtualBox version. */
130class UIUpdateStepVirtualBox : public UIUpdateStep
131{
132 Q_OBJECT;
133
134public:
135
136 /** Constructs update step passing @a pQueue and @a fForceCall to the base-class. */
137 UIUpdateStepVirtualBox(UIUpdateQueue *pQueue, bool fForceCall)
138 : UIUpdateStep(pQueue, fForceCall)
139 , m_url("https://update.virtualbox.org/query.php")
140 {}
141
142protected:
143
144 /** Handles network reply canceling for a passed @a pReply. */
145 virtual void processNetworkReplyCanceled(UINetworkReply *pReply) /* override */;
146 /** Handles network reply finishing for a passed @a pReply. */
147 virtual void processNetworkReplyFinished(UINetworkReply *pReply) /* override */;
148
149 /** Returns description of the current network operation. */
150 virtual const QString description() const /* override */;
151
152protected slots:
153
154 /** Starts the step. */
155 virtual void sltStartStep() /* override */;
156
157private:
158
159 /** Generates platform information. */
160 static QString platformInfo();
161
162 /** Holds the update step URL. */
163 QUrl m_url;
164};
165
166
167/** Update step subclass to check for the new VirtualBox Extension Pack version. */
168class UIUpdateStepVirtualBoxExtensionPack : public UIUpdateStep
169{
170 Q_OBJECT;
171
172public:
173
174 /** Constructs update step passing @a pQueue and @a fForceCall to the base-class. */
175 UIUpdateStepVirtualBoxExtensionPack(UIUpdateQueue *pQueue, bool fForceCall)
176 : UIUpdateStep(pQueue, fForceCall)
177 {}
178
179protected slots:
180
181 /** Starts the step. */
182 virtual void sltStartStep() /* override */;
183
184private slots:
185
186 /** Handles downloaded Extension Pack.
187 * @param strSource Brings the EP source.
188 * @param strTarget Brings the EP target.
189 * @param strDigest Brings the EP digest. */
190 void sltHandleDownloadedExtensionPack(const QString &strSource,
191 const QString &strTarget,
192 const QString &strDigest);
193};
194
195
196/*********************************************************************************************************************************
197* Class UIUpdateStep implementation. *
198*********************************************************************************************************************************/
199
200UIUpdateStep::UIUpdateStep(UIUpdateQueue *pQueue, bool fForceCall)
201 : UINetworkCustomer(pQueue, fForceCall)
202{
203 /* If queue has no steps yet: */
204 if (pQueue->isEmpty())
205 {
206 /* Connect starting-signal of the queue to starting-slot of this step: */
207 connect(pQueue, &UIUpdateQueue::sigStartQueue, this, &UIUpdateStep::sltStartStep, Qt::QueuedConnection);
208 }
209 /* If queue has at least one step already: */
210 else
211 {
212 /* Reconnect completion-signal of the last-step from completion-signal of the queue to starting-slot of this step: */
213 disconnect(pQueue->lastStep(), &UIUpdateStep::sigStepComplete, pQueue, &UIUpdateQueue::sigQueueFinished);
214 connect(pQueue->lastStep(), &UIUpdateStep::sigStepComplete, this, &UIUpdateStep::sltStartStep, Qt::QueuedConnection);
215 }
216
217 /* Connect completion-signal of this step to the completion-signal of the queue: */
218 connect(this, &UIUpdateStep::sigStepComplete, pQueue, &UIUpdateQueue::sigQueueFinished, Qt::QueuedConnection);
219 /* Connect completion-signal of this step to the destruction-slot of this step: */
220 connect(this, &UIUpdateStep::sigStepComplete, this, &UIUpdateStep::deleteLater, Qt::QueuedConnection);
221
222 /* Remember this step as the last one: */
223 pQueue->setLastStep(this);
224}
225
226void UIUpdateStep::processNetworkReplyProgress(qint64, qint64)
227{
228}
229
230void UIUpdateStep::processNetworkReplyCanceled(UINetworkReply *)
231{
232}
233
234void UIUpdateStep::processNetworkReplyFinished(UINetworkReply *)
235{
236}
237
238
239/*********************************************************************************************************************************
240* Class UIUpdateStepVirtualBox implementation. *
241*********************************************************************************************************************************/
242
243void UIUpdateStepVirtualBox::processNetworkReplyCanceled(UINetworkReply *pReply)
244{
245 Q_UNUSED(pReply);
246
247 /* Notify about step completion: */
248 emit sigStepComplete();
249}
250
251void UIUpdateStepVirtualBox::processNetworkReplyFinished(UINetworkReply *pReply)
252{
253 /* Deserialize incoming data: */
254 QString strResponseData(pReply->readAll());
255
256#ifdef VBOX_NEW_VERSION_TEST
257 strResponseData = VBOX_NEW_VERSION_TEST;
258#endif
259 /* Newer version of necessary package found: */
260 if (strResponseData.indexOf(QRegExp("^\\d+\\.\\d+\\.\\d+(_[0-9A-Z]+)? \\S+$")) == 0)
261 {
262 QStringList response = strResponseData.split(" ", QString::SkipEmptyParts);
263 msgCenter().showUpdateSuccess(response[0], response[1]);
264 }
265 /* No newer version of necessary package found: */
266 else
267 {
268 if (isItForceCall())
269 msgCenter().showUpdateNotFound();
270 }
271
272 /* Increment update check counter: */
273 gEDataManager->incrementApplicationUpdateCheckCounter();
274
275 /* Notify about step completion: */
276 emit sigStepComplete();
277}
278
279const QString UIUpdateStepVirtualBox::description() const
280{
281 return tr("Checking for a new VirtualBox version...");
282}
283
284void UIUpdateStepVirtualBox::sltStartStep()
285{
286 /* Compose query: */
287 QUrlQuery url;
288 url.addQueryItem("platform", uiCommon().virtualBox().GetPackageType());
289 /* Check if branding is active: */
290 if (uiCommon().brandingIsActive())
291 {
292 /* Branding: Check whether we have a local branding file which tells us our version suffix "FOO"
293 (e.g. 3.06.54321_FOO) to identify this installation: */
294 url.addQueryItem("version", QString("%1_%2_%3").arg(uiCommon().virtualBox().GetVersion())
295 .arg(uiCommon().virtualBox().GetRevision())
296 .arg(uiCommon().brandingGetKey("VerSuffix")));
297 }
298 else
299 {
300 /* Use hard coded version set by VBOX_VERSION_STRING: */
301 url.addQueryItem("version", QString("%1_%2").arg(uiCommon().virtualBox().GetVersion())
302 .arg(uiCommon().virtualBox().GetRevision()));
303 }
304 url.addQueryItem("count", QString::number(gEDataManager->applicationUpdateCheckCounter()));
305 url.addQueryItem("branch", VBoxUpdateData(gEDataManager->applicationUpdateData()).branchName());
306 QString strUserAgent(QString("VirtualBox %1 <%2>").arg(uiCommon().virtualBox().GetVersion()).arg(platformInfo()));
307
308 /* Send GET request: */
309 UserDictionary headers;
310 headers["User-Agent"] = strUserAgent;
311 QUrl fullUrl(m_url);
312 fullUrl.setQuery(url);
313 createNetworkRequest(UINetworkRequestType_GET, QList<QUrl>() << fullUrl, QString(), headers);
314}
315
316/* static */
317QString UIUpdateStepVirtualBox::platformInfo()
318{
319 /* Prepare platform report: */
320 QString strPlatform;
321
322#if defined (Q_OS_WIN)
323 strPlatform = "win";
324#elif defined (Q_OS_LINUX)
325 strPlatform = "linux";
326#elif defined (Q_OS_MACX)
327 strPlatform = "macosx";
328#elif defined (Q_OS_OS2)
329 strPlatform = "os2";
330#elif defined (Q_OS_FREEBSD)
331 strPlatform = "freebsd";
332#elif defined (Q_OS_SOLARIS)
333 strPlatform = "solaris";
334#else
335 strPlatform = "unknown";
336#endif
337
338 /* The format is <system>.<bitness>: */
339 strPlatform += QString(".%1").arg(ARCH_BITS);
340
341 /* Add more system information: */
342 int vrc;
343#ifdef Q_OS_LINUX
344 // WORKAROUND:
345 // On Linux we try to generate information using script first of all..
346
347 /* Get script path: */
348 char szAppPrivPath[RTPATH_MAX];
349 vrc = RTPathAppPrivateNoArch(szAppPrivPath, sizeof(szAppPrivPath));
350 AssertRC(vrc);
351 if (RT_SUCCESS(vrc))
352 {
353 /* Run script: */
354 QByteArray result = QIProcess::singleShot(QString(szAppPrivPath) + "/VBoxSysInfo.sh");
355 if (!result.isNull())
356 strPlatform += QString(" [%1]").arg(QString(result).trimmed());
357 else
358 vrc = VERR_TRY_AGAIN; /* (take the fallback path) */
359 }
360 if (RT_FAILURE(vrc))
361#endif /* Q_OS_LINUX */
362 {
363 /* Use RTSystemQueryOSInfo: */
364 char szTmp[256];
365 QStringList components;
366
367 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
368 if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
369 components << QString("Product: %1").arg(szTmp);
370
371 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
372 if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
373 components << QString("Release: %1").arg(szTmp);
374
375 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
376 if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
377 components << QString("Version: %1").arg(szTmp);
378
379 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
380 if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
381 components << QString("SP: %1").arg(szTmp);
382
383 if (!components.isEmpty())
384 strPlatform += QString(" [%1]").arg(components.join(" | "));
385 }
386
387 return strPlatform;
388}
389
390
391/*********************************************************************************************************************************
392* Class UIUpdateStepVirtualBoxExtensionPack implementation. *
393*********************************************************************************************************************************/
394
395void UIUpdateStepVirtualBoxExtensionPack::sltStartStep()
396{
397 /* Return if Selector UI issued a direct request to install EP: */
398 if (gUpdateManager->isEPInstallationRequested())
399 {
400 emit sigStepComplete();
401 return;
402 }
403
404 /* Return if already downloading: */
405 if (UIDownloaderExtensionPack::current())
406 {
407 emit sigStepComplete();
408 return;
409 }
410
411 /* Get extension pack: */
412 CExtPack extPack = uiCommon().virtualBox().GetExtensionPackManager().Find(GUI_ExtPackName);
413 /* Return if extension pack is NOT installed: */
414 if (extPack.isNull())
415 {
416 emit sigStepComplete();
417 return;
418 }
419
420 /* Get VirtualBox version: */
421 UIVersion vboxVersion(uiCommon().vboxVersionStringNormalized());
422 /* Get extension pack version: */
423 QString strExtPackVersion(extPack.GetVersion());
424
425 /* If this version being developed: */
426 if (vboxVersion.z() % 2 == 1)
427 {
428 /* If this version being developed on release branch (we use released one): */
429 if (vboxVersion.z() < 97)
430 vboxVersion.setZ(vboxVersion.z() - 1);
431 /* If this version being developed on trunk (we skip check at all): */
432 else
433 {
434 emit sigStepComplete();
435 return;
436 }
437 }
438
439 /* Get updated VirtualBox version: */
440 const QString strVBoxVersion = vboxVersion.toString();
441
442 /* Skip the check if the extension pack is equal to or newer than VBox. */
443 if (UIVersion(strExtPackVersion) >= vboxVersion)
444 {
445 emit sigStepComplete();
446 return;
447 }
448
449 QString strExtPackEdition(extPack.GetEdition());
450 if (strExtPackEdition.contains("ENTERPRISE"))
451 {
452 /* Inform the user that he should update the extension pack: */
453 msgCenter().askUserToDownloadExtensionPack(GUI_ExtPackName, strExtPackVersion, strVBoxVersion);
454 /* Never try to download for ENTERPRISE version: */
455 emit sigStepComplete();
456 return;
457 }
458
459 /* Ask the user about extension pack downloading: */
460 if (!msgCenter().warnAboutOutdatedExtensionPack(GUI_ExtPackName, strExtPackVersion))
461 {
462 emit sigStepComplete();
463 return;
464 }
465
466 /* Create and configure the Extension Pack downloader: */
467 UIDownloaderExtensionPack *pDl = UIDownloaderExtensionPack::create();
468 /* After downloading finished => propose to install the Extension Pack: */
469 connect(pDl, &UIDownloaderExtensionPack::sigDownloadFinished,
470 this, &UIUpdateStepVirtualBoxExtensionPack::sltHandleDownloadedExtensionPack);
471 /* Also, destroyed downloader is a signal to finish the step: */
472 connect(pDl, &UIDownloaderExtensionPack::destroyed,
473 this, &UIUpdateStepVirtualBoxExtensionPack::sigStepComplete);
474 /* Start downloading: */
475 pDl->start();
476}
477
478void UIUpdateStepVirtualBoxExtensionPack::sltHandleDownloadedExtensionPack(const QString &strSource,
479 const QString &strTarget,
480 const QString &strDigest)
481{
482 /* Warn the user about extension pack was downloaded and saved, propose to install it: */
483 if (msgCenter().proposeInstallExtentionPack(GUI_ExtPackName, strSource, QDir::toNativeSeparators(strTarget)))
484 uiCommon().doExtPackInstallation(strTarget, strDigest, windowManager().networkManagerOrMainWindowShown(), NULL);
485 /* Propose to delete the downloaded extension pack: */
486 if (msgCenter().proposeDeleteExtentionPack(QDir::toNativeSeparators(strTarget)))
487 {
488 /* Delete the downloaded extension pack: */
489 QFile::remove(QDir::toNativeSeparators(strTarget));
490 /* Get the list of old extension pack files in VirtualBox homefolder: */
491 const QStringList oldExtPackFiles = QDir(uiCommon().homeFolder()).entryList(QStringList("*.vbox-extpack"),
492 QDir::Files);
493 /* Propose to delete old extension pack files if there are any: */
494 if (oldExtPackFiles.size())
495 {
496 if (msgCenter().proposeDeleteOldExtentionPacks(oldExtPackFiles))
497 {
498 foreach (const QString &strExtPackFile, oldExtPackFiles)
499 {
500 /* Delete the old extension pack file: */
501 QFile::remove(QDir::toNativeSeparators(QDir(uiCommon().homeFolder()).filePath(strExtPackFile)));
502 }
503 }
504 }
505 }
506}
507
508
509/*********************************************************************************************************************************
510* Class UIUpdateManager implementation. *
511*********************************************************************************************************************************/
512
513/* static */
514UIUpdateManager* UIUpdateManager::s_pInstance = 0;
515
516UIUpdateManager::UIUpdateManager()
517 : m_pQueue(new UIUpdateQueue(this))
518 , m_fIsRunning(false)
519 , m_uTime(1 /* day */ * 24 /* hours */ * 60 /* minutes */ * 60 /* seconds */ * 1000 /* ms */)
520 , m_fEPInstallationRequested(false)
521{
522 /* Prepare instance: */
523 if (s_pInstance != this)
524 s_pInstance = this;
525
526 /* Configure queue: */
527 connect(m_pQueue, &UIUpdateQueue::sigQueueFinished, this, &UIUpdateManager::sltHandleUpdateFinishing);
528
529#ifdef VBOX_WITH_UPDATE_REQUEST
530 /* Ask updater to check for the first time, for Selector UI only: */
531 if (gEDataManager->applicationUpdateEnabled() && uiCommon().uiType() == UICommon::UIType_SelectorUI)
532 QTimer::singleShot(0, this, SLOT(sltCheckIfUpdateIsNecessary()));
533#endif /* VBOX_WITH_UPDATE_REQUEST */
534}
535
536UIUpdateManager::~UIUpdateManager()
537{
538 /* Cleanup instance: */
539 if (s_pInstance == this)
540 s_pInstance = 0;
541}
542
543/* static */
544void UIUpdateManager::schedule()
545{
546 /* Ensure instance is NOT created: */
547 if (s_pInstance)
548 return;
549
550 /* Create instance: */
551 new UIUpdateManager;
552}
553
554/* static */
555void UIUpdateManager::shutdown()
556{
557 /* Ensure instance is created: */
558 if (!s_pInstance)
559 return;
560
561 /* Delete instance: */
562 delete s_pInstance;
563}
564
565void UIUpdateManager::sltForceCheck()
566{
567 /* Force call for new version check: */
568 sltCheckIfUpdateIsNecessary(true /* force call */);
569}
570
571void UIUpdateManager::sltCheckIfUpdateIsNecessary(bool fForceCall /* = false */)
572{
573 /* If already running: */
574 if (m_fIsRunning)
575 {
576 /* And we have a force-call: */
577 if (fForceCall)
578 {
579 /* Just show Network Access Manager: */
580 gNetworkManager->show();
581 }
582 return;
583 }
584
585 /* Set as running: */
586 m_fIsRunning = true;
587
588 /* Load/decode curent update data: */
589 VBoxUpdateData currentData(gEDataManager->applicationUpdateData());
590
591 /* If update is really necessary: */
592 if (
593#ifdef VBOX_NEW_VERSION_TEST
594 true ||
595#endif
596 fForceCall || currentData.isNeedToCheck())
597 {
598 /* Prepare update queue: */
599 new UIUpdateStepVirtualBox(m_pQueue, fForceCall);
600 new UIUpdateStepVirtualBoxExtensionPack(m_pQueue, fForceCall);
601 /* Start update queue: */
602 m_pQueue->start();
603 }
604 else
605 sltHandleUpdateFinishing();
606}
607
608void UIUpdateManager::sltHandleUpdateFinishing()
609{
610 /* Load/decode curent update data: */
611 VBoxUpdateData currentData(gEDataManager->applicationUpdateData());
612 /* Encode/save new update data: */
613 VBoxUpdateData newData(currentData.periodIndex(), currentData.branchIndex());
614 gEDataManager->setApplicationUpdateData(newData.data());
615
616#ifdef VBOX_WITH_UPDATE_REQUEST
617 /* Ask updater to check for the next time: */
618 QTimer::singleShot(m_uTime, this, SLOT(sltCheckIfUpdateIsNecessary()));
619#endif /* VBOX_WITH_UPDATE_REQUEST */
620
621 /* Set as not running: */
622 m_fIsRunning = false;
623}
624
625#include "UIUpdateManager.moc"
626
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use