1
/* $Id: UIUpdateManager.cpp $ */
4
* VBox frontends: Qt4 GUI ("VirtualBox"):
5
* UIUpdateManager class implementation
9
* Copyright (C) 2006-2012 Oracle Corporation
11
* This file is part of VirtualBox Open Source Edition (OSE), as
12
* available from http://www.virtualbox.org. This file is free software;
13
* you can redistribute it and/or modify it under the terms of the GNU
14
* General Public License (GPL) as published by the Free Software
15
* Foundation, in version 2 as it comes in the "COPYING" file of the
16
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
24
#include <VBox/version.h>
27
#include "UIUpdateDefs.h"
28
#include "UIUpdateManager.h"
29
#include "UINetworkManager.h"
30
#include "UINetworkCustomer.h"
31
#include "UINetworkRequest.h"
32
#include "VBoxGlobal.h"
33
#include "UIMessageCenter.h"
34
#include "UIModalWindowManager.h"
35
#include "VBoxUtils.h"
36
#include "UIDownloaderExtensionPack.h"
37
#include "UIGlobalSettingsExtension.h"
38
#include "QIProcess.h"
42
#include "CExtPackManager.h"
44
/* Other VBox includes: */
45
#include <iprt/path.h>
46
#include <iprt/system.h>
48
/* Forward declarations: */
51
/* Queue for processing update-steps: */
52
class UIUpdateQueue : public QObject
58
/* Starting-signal of the queue: */
61
/* Completion-signal of the queue: */
62
void sigQueueFinished();
67
UIUpdateQueue(UIUpdateManager *pParent) : QObject(pParent) {}
70
void start() { emit sigStartQueue(); }
75
bool isEmpty() const { return m_pLastStep.isNull(); }
76
UIUpdateStep* lastStep() const { return m_pLastStep; }
77
void setLastStep(UIUpdateStep *pStep) { m_pLastStep = pStep; }
80
QPointer<UIUpdateStep> m_pLastStep;
83
friend class UIUpdateStep;
86
/* Interface representing update-step: */
87
class UIUpdateStep : public UINetworkCustomer
93
/* Completion-signal of the step: */
94
void sigStepComplete();
99
UIUpdateStep(UIUpdateQueue *pQueue, bool fForceCall) : UINetworkCustomer(pQueue, fForceCall)
101
/* If queue has no steps yet: */
102
if (pQueue->isEmpty())
104
/* Connect starting-signal of the queue to starting-slot of this step: */
105
connect(pQueue, SIGNAL(sigStartQueue()), this, SLOT(sltStartStep()), Qt::QueuedConnection);
107
/* If queue has at least one step already: */
110
/* Reconnect completion-signal of the last-step from completion-signal of the queue to starting-slot of this step: */
111
disconnect(pQueue->lastStep(), SIGNAL(sigStepComplete()), pQueue, SIGNAL(sigQueueFinished()));
112
connect(pQueue->lastStep(), SIGNAL(sigStepComplete()), this, SLOT(sltStartStep()), Qt::QueuedConnection);
115
/* Connect completion-signal of this step to the completion-signal of the queue: */
116
connect(this, SIGNAL(sigStepComplete()), pQueue, SIGNAL(sigQueueFinished()), Qt::QueuedConnection);
117
/* Connect completion-signal of this step to the destruction-slot of this step: */
118
connect(this, SIGNAL(sigStepComplete()), this, SLOT(deleteLater()), Qt::QueuedConnection);
120
/* Remember this step as the last one: */
121
pQueue->setLastStep(this);
126
/* Starting-slot of the step: */
127
virtual void sltStartStep() = 0;
131
/* Network pregress handler dummy: */
132
void processNetworkReplyProgress(qint64, qint64) {}
133
/* Network reply canceled handler dummy: */
134
void processNetworkReplyCanceled(UINetworkReply*) {}
135
/* Network reply canceled handler dummy: */
136
void processNetworkReplyFinished(UINetworkReply*) {}
139
/* Update-step to check for the new VirtualBox version: */
140
class UIUpdateStepVirtualBox : public UIUpdateStep
147
UIUpdateStepVirtualBox(UIUpdateQueue *pQueue, bool fForceCall)
148
: UIUpdateStep(pQueue, fForceCall)
149
, m_url("https://update.virtualbox.org/query.php")
156
void sltStartStep() { prepareNetworkRequest(); }
160
/* Prepare network request: */
161
void prepareNetworkRequest()
163
/* Calculate the count of checks left: */
165
QString strCount = vboxGlobal().virtualBox().GetExtraData(GUI_UpdateCheckCount);
166
if (!strCount.isEmpty())
169
int c = strCount.toLongLong(&ok);
175
url.addQueryItem("platform", vboxGlobal().virtualBox().GetPackageType());
176
/* Check if branding is active: */
177
if (vboxGlobal().brandingIsActive())
179
/* Branding: Check whether we have a local branding file which tells us our version suffix "FOO"
180
(e.g. 3.06.54321_FOO) to identify this installation: */
181
url.addQueryItem("version", QString("%1_%2_%3").arg(vboxGlobal().virtualBox().GetVersion())
182
.arg(vboxGlobal().virtualBox().GetRevision())
183
.arg(vboxGlobal().brandingGetKey("VerSuffix")));
187
/* Use hard coded version set by VBOX_VERSION_STRING: */
188
url.addQueryItem("version", QString("%1_%2").arg(vboxGlobal().virtualBox().GetVersion())
189
.arg(vboxGlobal().virtualBox().GetRevision()));
191
url.addQueryItem("count", QString::number(cCount));
192
url.addQueryItem("branch", VBoxUpdateData(vboxGlobal().virtualBox().GetExtraData(GUI_UpdateDate)).branchName());
193
QString strUserAgent(QString("VirtualBox %1 <%2>").arg(vboxGlobal().virtualBox().GetVersion()).arg(platformInfo()));
195
/* Send GET request: */
196
QNetworkRequest request;
198
request.setRawHeader("User-Agent", strUserAgent.toAscii());
199
createNetworkRequest(request, UINetworkRequestType_GET_Our, tr("Checking for a new VirtualBox version..."));
202
/* Handle network reply canceled: */
203
void processNetworkReplyCanceled(UINetworkReply* /* pReply */)
205
/* Notify about step completion: */
206
emit sigStepComplete();
209
/* Handle network reply: */
210
void processNetworkReplyFinished(UINetworkReply *pReply)
212
/* Deserialize incoming data: */
213
QString strResponseData(pReply->readAll());
215
/* Newer version of necessary package found: */
216
if (strResponseData.indexOf(QRegExp("^\\d+\\.\\d+\\.\\d+(_[0-9A-Z]+)? \\S+$")) == 0)
218
QStringList response = strResponseData.split(" ", QString::SkipEmptyParts);
219
msgCenter().showUpdateSuccess(response[0], response[1]);
221
/* No newer version of necessary package found: */
225
msgCenter().showUpdateNotFound();
228
/* Save left count of checks: */
230
QString strCount = vboxGlobal().virtualBox().GetExtraData(GUI_UpdateCheckCount);
231
if (!strCount.isEmpty())
234
int c = strCount.toLongLong(&ok);
237
vboxGlobal().virtualBox().SetExtraData(GUI_UpdateCheckCount, QString("%1").arg((qulonglong)cCount + 1));
239
/* Notify about step completion: */
240
emit sigStepComplete();
243
/* Platform information getter: */
244
static QString platformInfo()
246
/* Prepare platform report: */
249
#if defined (Q_OS_WIN)
251
#elif defined (Q_OS_LINUX)
252
strPlatform = "linux";
253
#elif defined (Q_OS_MACX)
254
strPlatform = "macosx";
255
#elif defined (Q_OS_OS2)
257
#elif defined (Q_OS_FREEBSD)
258
strPlatform = "freebsd";
259
#elif defined (Q_OS_SOLARIS)
260
strPlatform = "solaris";
262
strPlatform = "unknown";
265
/* The format is <system>.<bitness>: */
266
strPlatform += QString(".%1").arg(ARCH_BITS);
268
/* Add more system information: */
270
#if defined(Q_OS_LINUX)
271
/* On linux we wish to send information about the distribution
272
and such like, so we try invoke a helper script that retrives
273
and formats it for us. */
275
/* Get script path: */
276
char szAppPrivPath[RTPATH_MAX];
277
vrc = RTPathAppPrivateNoArch(szAppPrivPath, sizeof(szAppPrivPath)); AssertRC(vrc);
281
QByteArray result = QIProcess::singleShot(QString(szAppPrivPath) + "/VBoxSysInfo.sh");
282
if (!result.isNull())
283
strPlatform += QString(" [%1]").arg(QString(result).trimmed());
285
vrc = VERR_TRY_AGAIN; /* (take the fallback path) */
290
/* Use RTSystemQueryOSInfo: */
292
QStringList components;
294
vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
295
if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
296
components << QString("Product: %1").arg(szTmp);
298
vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
299
if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
300
components << QString("Release: %1").arg(szTmp);
302
vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
303
if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
304
components << QString("Version: %1").arg(szTmp);
306
vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
307
if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
308
components << QString("SP: %1").arg(szTmp);
310
if (!components.isEmpty())
311
strPlatform += QString(" [%1]").arg(components.join(" | "));
323
/* Update-step to check for the new VirtualBox Extension Pack version: */
324
class UIUpdateStepVirtualBoxExtensionPack : public UIUpdateStep
331
UIUpdateStepVirtualBoxExtensionPack(UIUpdateQueue *pQueue, bool fForceCall)
332
: UIUpdateStep(pQueue, fForceCall)
341
/* Return if already downloading: */
342
if (UIDownloaderExtensionPack::current())
344
emit sigStepComplete();
348
/* Get extension pack: */
349
CExtPack extPack = vboxGlobal().virtualBox().GetExtensionPackManager().Find(GUI_ExtPackName);
350
/* Return if extension pack is NOT installed: */
351
if (extPack.isNull())
353
emit sigStepComplete();
357
/* Get VirtualBox version: */
358
QString strVBoxVersion(vboxGlobal().vboxVersionStringNormalized());
359
QByteArray abVBoxVersion = strVBoxVersion.toUtf8();
360
VBoxVersion vboxVersion(strVBoxVersion);
362
/* Get extension pack version: */
363
QString strExtPackVersion(extPack.GetVersion());
364
QByteArray abExtPackVersion = strExtPackVersion.toUtf8();
366
/* Skip the check in unstable VBox version and if the extension pack
367
is equal to or newer than VBox.
369
Note! Use RTStrVersionCompare for the comparison here as it takes
370
the beta/alpha/preview/whatever tags into consideration when
371
comparing versions. */
372
if ( vboxVersion.z() % 2 != 0
373
|| RTStrVersionCompare(abExtPackVersion.constData(), abVBoxVersion.constData()) >= 0)
375
emit sigStepComplete();
379
QString strExtPackEdition(extPack.GetEdition());
380
if (strExtPackEdition.contains("ENTERPRISE"))
382
/* Inform the user that he should update the extension pack: */
383
msgCenter().askUserToDownloadExtensionPack(GUI_ExtPackName, strExtPackVersion, strVBoxVersion);
384
/* Never try to download for ENTERPRISE version: */
385
emit sigStepComplete();
389
/* Ask the user about extension pack downloading: */
390
if (!msgCenter().warAboutOutdatedExtensionPack(GUI_ExtPackName, strExtPackVersion))
392
emit sigStepComplete();
396
/* Create and configure the Extension Pack downloader: */
397
UIDownloaderExtensionPack *pDl = UIDownloaderExtensionPack::create();
398
/* After downloading finished => propose to install the Extension Pack: */
399
connect(pDl, SIGNAL(sigDownloadFinished(const QString&, const QString&, QString)),
400
this, SLOT(sltHandleDownloadedExtensionPack(const QString&, const QString&, QString)));
401
/* Also, destroyed downloader is a signal to finish the step: */
402
connect(pDl, SIGNAL(destroyed(QObject*)), this, SIGNAL(sigStepComplete()));
403
/* Start downloading: */
407
/* Finishing slot: */
408
void sltHandleDownloadedExtensionPack(const QString &strSource, const QString &strTarget, QString strDigest)
410
/* Warn the user about extension pack was downloaded and saved, propose to install it: */
411
if (msgCenter().proposeInstallExtentionPack(GUI_ExtPackName, strSource, QDir::toNativeSeparators(strTarget)))
412
UIGlobalSettingsExtension::doInstallation(strTarget, strDigest, windowManager().networkManagerOrMainWindowShown(), NULL);
416
/* UIUpdateManager stuff: */
417
UIUpdateManager* UIUpdateManager::m_pInstance = 0;
420
void UIUpdateManager::schedule()
422
/* Ensure instance is NOT created: */
426
/* Create instance: */
431
void UIUpdateManager::shutdown()
433
/* Ensure instance is created: */
437
/* Delete instance: */
441
void UIUpdateManager::sltForceCheck()
443
/* Force call for new version check: */
444
sltCheckIfUpdateIsNecessary(true /* force call */);
447
UIUpdateManager::UIUpdateManager()
448
: m_pQueue(new UIUpdateQueue(this))
449
, m_fIsRunning(false)
450
, m_uTime(1 /* day */ * 24 /* hours */ * 60 /* minutes */ * 60 /* seconds */ * 1000 /* ms */)
452
/* Prepare instance: */
453
if (m_pInstance != this)
456
/* Configure queue: */
457
connect(m_pQueue, SIGNAL(sigQueueFinished()), this, SLOT(sltHandleUpdateFinishing()));
459
#ifdef VBOX_WITH_UPDATE_REQUEST
460
/* Ask updater to check for the first time: */
461
CVirtualBox vbox = vboxGlobal().virtualBox();
462
if (VBoxGlobal::shouldWeAllowApplicationUpdate(vbox) &&
463
!vboxGlobal().isVMConsoleProcess())
464
QTimer::singleShot(0, this, SLOT(sltCheckIfUpdateIsNecessary()));
465
#endif /* VBOX_WITH_UPDATE_REQUEST */
468
UIUpdateManager::~UIUpdateManager()
470
/* Cleanup instance: */
471
if (m_pInstance == this)
475
void UIUpdateManager::sltCheckIfUpdateIsNecessary(bool fForceCall /* = false */)
477
/* If already running: */
480
/* And we have a force-call: */
483
/* Just show Network Access Manager: */
484
gNetworkManager->show();
489
/* Set as running: */
492
/* Load/decode curent update data: */
493
VBoxUpdateData currentData(vboxGlobal().virtualBox().GetExtraData(GUI_UpdateDate));
495
/* If update is really necessary: */
496
if (fForceCall || currentData.isNeedToCheck())
498
/* Prepare update queue: */
499
new UIUpdateStepVirtualBox(m_pQueue, fForceCall);
500
new UIUpdateStepVirtualBoxExtensionPack(m_pQueue, fForceCall);
501
/* Start update queue: */
505
sltHandleUpdateFinishing();
508
void UIUpdateManager::sltHandleUpdateFinishing()
510
/* Load/decode curent update data: */
511
VBoxUpdateData currentData(vboxGlobal().virtualBox().GetExtraData(GUI_UpdateDate));
512
/* Encode/save new update data: */
513
VBoxUpdateData newData(currentData.periodIndex(), currentData.branchIndex());
514
vboxGlobal().virtualBox().SetExtraData(GUI_UpdateDate, newData.data());
516
#ifdef VBOX_WITH_UPDATE_REQUEST
517
/* Ask updater to check for the next time: */
518
QTimer::singleShot(m_uTime, this, SLOT(sltCheckIfUpdateIsNecessary()));
519
#endif /* VBOX_WITH_UPDATE_REQUEST */
521
/* Set as not running: */
522
m_fIsRunning = false;
525
#include "UIUpdateManager.moc"