23 #include "autoupdater.h" 25 #include "configuration/doomseekerconfig.h" 26 #include "updater/updatechannel.h" 27 #include "updater/updatepackagefilter.h" 28 #include "updater/updaterinfoparser.h" 29 #include "updater/updaterscriptparser.h" 30 #include "datapaths.h" 34 #include <wadseeker/protocols/fixednetworkaccessmanager.h> 37 #include <QNetworkRequest> 38 #include <QTemporaryFile> 45 QList<QDomDocument> allScripts;
46 bool bDownloadAndInstallRequireConfirmation;
49 bool bPackageDownloadStarted;
53 QStringList downloadedPackagesFilenames;
55 QMap<QString, QList<QString> > ignoredPackagesRevisions;
56 QList<UpdatePackage> newUpdatePackages;
57 QList<UpdatePackage> packagesInDownloadQueue;
58 QTemporaryFile* pCurrentPackageFile;
59 FixedNetworkAccessManager* pNam;
60 QNetworkReply* pNetworkReply;
68 const QString
AutoUpdater::MAIN_PROGRAM_PACKAGE_NAME = "doomseeker-core";
69 const QString
AutoUpdater::FALLBACK_MAIN_PROGRAM_PACKAGE_NAME = "doomseeker";
71 const QString
AutoUpdater::WADSEEKER_PACKAGE_NAME = "wadseeker";
72 const QString
AutoUpdater::UPDATER_INFO_URL_BASE = "http:
77 d->bDownloadAndInstallRequireConfirmation =
false;
78 d->bPackageDownloadStarted =
false;
79 d->bIsRunning =
false;
82 d->pCurrentPackageFile = NULL;
83 d->pNam =
new FixedNetworkAccessManager();
84 d->pNetworkReply = NULL;
87 AutoUpdater::~AutoUpdater()
89 if (d->pCurrentPackageFile != NULL)
91 delete d->pCurrentPackageFile;
93 if (d->pNetworkReply != NULL)
95 d->pNetworkReply->disconnect();
96 d->pNetworkReply->abort();
97 d->pNetworkReply->deleteLater();
99 d->pNam->disconnect();
100 d->pNam->deleteLater();
103 void AutoUpdater::abort()
105 if (d->pNetworkReply != NULL)
107 d->pNetworkReply->disconnect();
108 d->pNetworkReply->abort();
109 d->pNetworkReply->deleteLater();
110 d->pNetworkReply = NULL;
115 QDomDocument AutoUpdater::adjustUpdaterScriptXml(
const QByteArray& xmlSource)
121 if (!xmlDoc.setContent(xmlSource, &xmlError, &xmlErrLine, &xmlErrCol))
123 gLog << tr(
"Failed to parse updater XML script: %1, l: %2, c: %3")
124 .arg(xmlError).arg(xmlErrLine).arg(xmlErrCol);
125 return QDomDocument();
128 QFileInfo currentPackageFileInfo(d->pCurrentPackageFile->fileName());
129 QString scriptParserErrMsg = scriptParser.setPackageName(
130 currentPackageFileInfo.completeBaseName());
131 if (!scriptParserErrMsg.isNull())
133 gLog << tr(
"Failed to modify package name in updater script: %1")
134 .arg(scriptParserErrMsg);
135 return QDomDocument();
145 void AutoUpdater::confirmDownloadAndInstall()
147 d->packagesInDownloadQueue = d->newUpdatePackages;
148 d->bPackageDownloadStarted =
true;
149 startNextPackageDownload();
154 return d->downloadedPackagesFilenames;
157 void AutoUpdater::dumpUpdatePackagesToLog(
const QList<UpdatePackage>& packages)
161 gLog << tr(
"Detected update for package \"%1\" from version \"%2\" to version \"%3\".")
166 void AutoUpdater::emitOverallProgress(
const QString& message)
170 if (!d->newUpdatePackages.isEmpty() && d->bPackageDownloadStarted)
172 total = d->newUpdatePackages.size();
175 current = total - (d->packagesInDownloadQueue.size() + 1);
176 assert(current >= 0 &&
"AutoUpdater::emitOverallProgress()");
178 emit overallProgress(current, total, message);
181 void AutoUpdater::emitStatusMessage(
const QString &message)
183 emit statusMessage(message);
191 QString AutoUpdater::errorCodeToString(
ErrorCode code)
198 return tr(
"Update was aborted.");
200 return tr(
"Update channel is not configured. Please check your configuration.");
202 return tr(
"Failed to download updater info file.");
204 return tr(
"Cannot parse updater info file.");
206 return tr(
"Main program node is missing from updater info file.");
208 return tr(
"Revision info on one of the packages is missing from the " 209 "updater info file. Check the log for details.");
211 return tr(
"Download URL for one of the packages is missing from the " 212 "updater info file. Check the log for details.");
214 return tr(
"Download URL for one of the packages is invalid. " 215 "Check the log for details.");
217 return tr(
"Update package download failed. Check the log for details.");
219 return tr(
"Failed to create directory for updates packages storage.");
221 return tr(
"Failed to save update package.");
223 return tr(
"Failed to save update script.");
225 return tr(
"Unknown error.");
229 QString AutoUpdater::errorString()
const 231 return errorCodeToString(errorCode());
234 void AutoUpdater::finishWithError(
ErrorCode code)
236 d->bIsRunning =
false;
241 bool AutoUpdater::isRunning()
const 243 return d->bIsRunning;
248 if (errorCode() != EC_Ok && d->pNetworkReply != NULL)
250 return d->pNetworkReply->error();
252 return QNetworkReply::NoError;
255 QUrl AutoUpdater::mkVersionDataFileUrl()
262 return d->newUpdatePackages;
265 void AutoUpdater::onPackageDownloadFinish()
267 if (d->pNetworkReply->error() == QNetworkReply::NoError)
269 emitStatusMessage(tr(
"Finished downloading package \"%1\".")
270 .arg(d->currentlyDownloadedPackage.displayName));
271 startPackageScriptDownload(d->currentlyDownloadedPackage);
275 emitStatusMessage(tr(
"Network error when downloading package \"%1\": [%2] %3")
276 .arg(d->currentlyDownloadedPackage.displayName)
277 .arg(d->pNetworkReply->error())
278 .arg(d->pNetworkReply->errorString()));
283 void AutoUpdater::onPackageDownloadReadyRead()
285 const int MAX_CHUNK_SIZE = 2 * 1024 * 1024;
286 QByteArray data = d->pNetworkReply->read(MAX_CHUNK_SIZE);
287 while (!data.isEmpty())
289 d->pCurrentPackageFile->write(data);
290 data = d->pNetworkReply->read(MAX_CHUNK_SIZE);
294 void AutoUpdater::onPackageScriptDownloadFinish()
296 if (d->pNetworkReply->error() == QNetworkReply::NoError)
298 emitStatusMessage(tr(
"Finished downloading package script \"%1\".")
299 .arg(d->currentlyDownloadedPackage.displayName));
300 QByteArray xmlData = d->pNetworkReply->readAll();
301 QDomDocument xmlDoc = adjustUpdaterScriptXml(xmlData);
307 d->allScripts.append(xmlDoc);
309 if (!d->packagesInDownloadQueue.isEmpty())
311 startNextPackageDownload();
315 emitStatusMessage(tr(
"All packages downloaded. Building updater script."));
317 finishWithError(result);
322 emitStatusMessage(tr(
"Network error when downloading package script \"%1\": [%2] %3")
323 .arg(d->currentlyDownloadedPackage.displayName)
324 .arg(d->pNetworkReply->error())
325 .arg(d->pNetworkReply->errorString()));
330 void AutoUpdater::onUpdaterInfoDownloadFinish()
332 if (d->pNetworkReply->error() != QNetworkReply::NoError)
337 QByteArray json = d->pNetworkReply->readAll();
340 if (parseResult == EC_Ok)
343 filter.setIgnoreRevisions(d->ignoredPackagesRevisions);
344 QList<UpdatePackage> packagesList = filter.filter(parser.packages());
345 if (!packagesList.isEmpty())
347 dumpUpdatePackagesToLog(packagesList);
348 d->newUpdatePackages = packagesList;
349 if (d->bDownloadAndInstallRequireConfirmation)
351 emitStatusMessage(tr(
"Requesting update confirmation."));
352 emitOverallProgress(tr(
"Confirm"));
357 confirmDownloadAndInstall();
363 emitStatusMessage(tr(
"No new program updates detected."));
366 emitStatusMessage(tr(
"Some update packages were ignored. To install them " 367 "select \"Check for updates\" option from \"Help\" menu."));
369 finishWithError(EC_Ok);
374 finishWithError(parseResult);
380 QDomDocument xmlDocAllScripts;
382 foreach (
const QDomDocument& doc, d->allScripts)
384 scriptParser.
merge(doc);
387 if (!f.open(QIODevice::WriteOnly))
391 f.write(xmlDocAllScripts.toByteArray());
398 d->channel = updateChannel;
403 d->ignoredPackagesRevisions = packagesRevisions;
408 d->bDownloadAndInstallRequireConfirmation = b;
411 void AutoUpdater::start()
415 qDebug() <<
"Cannot start AutoUpdater more than once.";
418 assert(
false &&
"Cannot start AutoUpdater more than once.");
422 d->bPackageDownloadStarted =
false;
423 if (d->channel.isNull())
428 QDir storageDir(updateStorageDirPath());
429 if (!storageDir.mkpath(
"."))
431 gLog << tr(
"Failed to create directory for updates storage: %1")
432 .arg(storageDir.path());
435 d->bIsRunning =
true;
436 QNetworkRequest request;
438 request.setUrl(mkVersionDataFileUrl());
439 QNetworkReply* pReply = d->pNam->get(request);
442 this->connect(pReply,
444 SLOT(onUpdaterInfoDownloadFinish()));
445 this->connect(pReply,
446 SIGNAL(downloadProgress(qint64, qint64)),
447 SIGNAL(packageDownloadProgress(qint64, qint64)));
448 d->pNetworkReply = pReply;
449 emitOverallProgress(tr(
"Update info"));
452 void AutoUpdater::startNextPackageDownload()
454 assert(!d->packagesInDownloadQueue.isEmpty() &&
"AutoUpdater::startNextPackageDownload()");
456 startPackageDownload(pkg);
459 void AutoUpdater::startPackageDownload(
const UpdatePackage& pkg)
462 if (!url.isValid() || url.isRelative())
466 gLog << tr(
"Invalid download URL for package \"%1\": %2")
471 emitOverallProgress(tr(
"Package: %1").arg(pkg.
displayName));
472 gLog << tr(
"Downloading package \"%1\" from URL: %2.").arg(pkg.
displayName,
475 QString fileNameTemplate = QString(
"%1%2-XXXXXX.zip")
476 .arg(DataPaths::UPDATE_PACKAGE_FILENAME_PREFIX).arg(pkg.
name);
478 qDebug() <<
"filePathTemplate: " << filePathTemplate;
479 if (d->pCurrentPackageFile != NULL)
481 delete d->pCurrentPackageFile;
483 d->pCurrentPackageFile =
new QTemporaryFile(filePathTemplate);
484 d->pCurrentPackageFile->setAutoRemove(
false);
485 if (!d->pCurrentPackageFile->open())
487 gLog << tr(
"Couldn't save file in path: %1").arg(updateStorageDirPath());
488 delete d->pCurrentPackageFile;
489 d->pCurrentPackageFile = NULL;
493 QFileInfo fileInfo(d->pCurrentPackageFile->fileName());
494 d->downloadedPackagesFilenames << fileInfo.fileName();
496 QNetworkRequest request;
499 QNetworkReply* pReply = d->pNam->get(request);
500 d->currentlyDownloadedPackage = pkg;
501 d->pNetworkReply = pReply;
502 this->connect(pReply, SIGNAL(readyRead()),
503 SLOT(onPackageDownloadReadyRead()));
504 this->connect(pReply, SIGNAL(
finished()),
505 SLOT(onPackageDownloadFinish()));
506 this->connect(pReply, SIGNAL(downloadProgress(qint64, qint64)),
507 SIGNAL(packageDownloadProgress(qint64, qint64)));
510 void AutoUpdater::startPackageScriptDownload(
const UpdatePackage& pkg)
513 if (!url.isValid() || url.isRelative())
517 gLog << tr(
"Invalid download URL for package script \"%1\": %2")
522 gLog << tr(
"Downloading package script \"%1\" from URL: %2.").arg(pkg.
displayName,
525 QNetworkRequest request;
528 QNetworkReply* pReply = d->pNam->get(request);
529 d->currentlyDownloadedPackage = pkg;
530 d->pNetworkReply = pReply;
533 this->connect(pReply, SIGNAL(
finished()),
534 SLOT(onPackageScriptDownloadFinish()));
535 this->connect(pReply,
536 SIGNAL(downloadProgress(qint64, qint64)),
537 SIGNAL(packageDownloadProgress(qint64, qint64)));
542 QString dirPath = gDefaultDataPaths->localDataLocationPath(DataPaths::UPDATE_PACKAGES_DIR_NAME);
543 QString name = DataPaths::UPDATE_PACKAGE_FILENAME_PREFIX +
"-updater-script.xml";
547 QString AutoUpdater::updateStorageDirPath()
549 return gDefaultDataPaths->localDataLocationPath(DataPaths::UPDATE_PACKAGES_DIR_NAME);
QUrl downloadScriptUrl
Updater script download URL.
static const QString UPDATER_INFO_URL_BASE
Base URL to the directory where "update-info*" JSON files are contained.
static QString combinePaths(QString pathFront, QString pathEnd)
One of packages has no revision info.
int parse(const QByteArray &json)
Parses updater info JSON and sets certain internal properties which can then be accessed through gett...
QNetworkReply::NetworkError lastNetworkError() const
The network error that caused the updater to fail.
QUrl downloadUrl
Package download URL.
Failed to create directory for updates storage.
static QString updaterScriptPath()
Path to updater script XML file.
No valid UpdateChannel was specified.
const UpdateChannel & channel() const
setChannel() .
Failed to download update package.
Interface to Mendeley updater .xml script files.
QUrl.isValid() for package download URL returned false or QUrl.isRelative() returned true...
QString displayName
Name displayed to the user.
Update script can't be merged and stored on the local filesystem.
const QList< UpdatePackage > & newUpdatePackages() const
List of new update packages to install.
static QString userAgent()
WWW User Agent used for HTTP communications.
One of packages has no download URL.
void setChannel(const UpdateChannel &updateChannel)
Update channel name.
void setIgnoreRevisions(const QMap< QString, QList< QString > > &packagesRevisions)
Revisions set in this map will not be treated as updates even if they differ from the currently insta...
File was parseable but there was no main program information inside.
Deals with program updates/upgrades.
const QStringList & downloadedPackagesFilenames() const
Filenames for packages which are ready to install.
void setRequireDownloadAndInstallConfirmation(bool b)
Controls if the download&installation process is automated.
Network error when downloading updater info file.
Filters UpdatePackage information basing on what is requested by the program.
void merge(const QDomDocument &otherDoc)
Merges other script with current script.
Updater info file can't be parsed.
Update was aborted by the user or by the program.
bool wasAnyUpdatePackageIgnored() const
After filter() flag which says if any package was ignored.
QString displayVersion
Version displayed to the user.
Package file can't be stored on the local filesystem.
void downloadAndInstallConfirmationRequested()
Information on update packages has been received and install confirmation is requested.
QString currentlyInstalledDisplayVersion
Currently installed version, displayed to the user.
QString name
Name of the package (program name or plugin name).
void finished()
AutoUpdater has finished its job.