diff options
35 files changed, 401 insertions, 108 deletions
@@ -1,3 +1,21 @@ +3.0.3 +- Set correct proxy type (QTBUG-65143) +- Add no-proxy option (QTIFW-1085) +- Fix maintenancetool icon visibility in Mac (QTIFW-1074) +- Fix EnvironmentVariable operation in Windows (QTIFW-794) +- Fix crash when downloadable package is missing (QTIFW-1064) +- Documentation fixes + +3.0.2 +- Add possibility to reopen admin query (QTIFW-988) +- Implement package download resume (QTIFW-5) +- Use QDir::rmdir and not rmpath as that will remove more than it should +- Enable HighDPI support only in Windows +- Do not reset core when pressing 'Restart' (QTIFW-1017) +- Update italian translation +- Update russian translation +- Fixed building with dynamically linked Qt (QTIFW-993) + 3.0.1 - Fix install type if --online-only passed to binarycreator - Fix install fail if there are missing repositories diff --git a/doc/installerfw-using.qdoc b/doc/installerfw-using.qdoc index 36df3bed9..178957dbd 100644 --- a/doc/installerfw-using.qdoc +++ b/doc/installerfw-using.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -349,9 +349,8 @@ \section1 Specifying Proxy Settings - By default, the installer does not use any proxy settings. End users can - select to use the system proxy settings or specify the proxy settings - manually. + By default, the installer uses system proxy settings. End users can + select to use no proxy or specify the proxy settings manually. \image ifw-settings-network.png "Network tab on Settings page" diff --git a/doc/noninteractive.qdoc b/doc/noninteractive.qdoc index a2bb5f901..887b32ad3 100644 --- a/doc/noninteractive.qdoc +++ b/doc/noninteractive.qdoc @@ -436,7 +436,7 @@ installer.addWizardPage(component, "SomePageWidget", QInstaller.TargetDirectory) } - Controller.prototype.DynamicSomePageWidgetCallback() + Controller.prototype.DynamicSomePageWidgetCallback = function() { var page = gui.pageWidgetByObjectName("DynamicSomePageWidget"); page.myButton.click, //direct child of the UI file's widget diff --git a/doc/scripting-api/packagemanagercore.qdoc b/doc/scripting-api/packagemanagercore.qdoc index ed1972e13..b4fe4c0fb 100644 --- a/doc/scripting-api/packagemanagercore.qdoc +++ b/doc/scripting-api/packagemanagercore.qdoc @@ -334,7 +334,7 @@ */ /*! - \qmlmethod boolean installer::readFile(string filePath, string codecName) + \qmlmethod string installer::readFile(string filePath, string codecName) Returns the contents of the file \a filePath using the encoding specified by \a codecName. The file is read in the text mode, that is, end-of-line diff --git a/doc/scripting-api/qdesktopservices.qdoc b/doc/scripting-api/qdesktopservices.qdoc index 4fa403dbd..037a0b6dd 100644 --- a/doc/scripting-api/qdesktopservices.qdoc +++ b/doc/scripting-api/qdesktopservices.qdoc @@ -80,6 +80,11 @@ Uses the URL scheme \c file to open the specified \a url with a suitable application. + + \warning A return value of \c true indicates that the installer has successfully + requested the operating system to open the URL in an external application. It may + still fail to launch or fail to open the requested URL. This result will not be + reported back to the installer. */ /*! diff --git a/installerfw.pro b/installerfw.pro index 1cec9c6a1..10414ad5b 100644 --- a/installerfw.pro +++ b/installerfw.pro @@ -2,6 +2,8 @@ TEMPLATE = subdirs SUBDIRS += src tools tools.depends = src +requires(!cross_compile) + include (installerfw.pri) include (doc/doc.pri) diff --git a/src/libs/installer/copydirectoryoperation.cpp b/src/libs/installer/copydirectoryoperation.cpp index cdd6da6ab..e83af07e1 100644 --- a/src/libs/installer/copydirectoryoperation.cpp +++ b/src/libs/installer/copydirectoryoperation.cpp @@ -85,7 +85,7 @@ bool CopyDirectoryOperation::performOperation() if (!dir.exists() || !dir.isDir()) { setError(InvalidArguments); setErrorString(tr("Invalid argument in %1: Directory \"%2\" is invalid.").arg(name()) - .arg(QDir::toNativeSeparators(sourcePath))); + .arg(QDir::toNativeSeparators(dir.absolutePath()))); return false; } } @@ -157,7 +157,7 @@ bool CopyDirectoryOperation::undoOperation() setErrorString(tr("Cannot remove file \"%1\".").arg(QDir::toNativeSeparators(file))); return false; } - dir.rmpath(QFileInfo(file).absolutePath()); + dir.rmdir(QFileInfo(file).absolutePath()); emit outputTextChanged(file); } diff --git a/src/libs/installer/environmentvariablesoperation.cpp b/src/libs/installer/environmentvariablesoperation.cpp index b80951d2b..7a8dbfb5a 100644 --- a/src/libs/installer/environmentvariablesoperation.cpp +++ b/src/libs/installer/environmentvariablesoperation.cpp @@ -65,6 +65,48 @@ static void broadcastEnvironmentChange() namespace { +bool handleRegExpandSz(const QString ®Path, const QString &name, + const QString &value, QString *errorString, + bool *error) +{ + bool setAsExpandSZ = false; +#ifdef Q_OS_WIN + // Account for when it is originally REG_EXPAND_SZ as we don't want + // to lose this setting (see Path environment variable) + const bool isLocalKey = regPath.startsWith(QStringLiteral("HKEY_LOCAL")); + HKEY hkey = isLocalKey ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + // Drop the HKEY...\\ part + const QString keyPath = regPath.mid(isLocalKey ? 19 : 18, -1); + HKEY handle; + LONG res = RegOpenKeyEx(hkey, reinterpret_cast<const wchar_t *>(keyPath.utf16()), 0, + KEY_READ, &handle); + if (res == ERROR_SUCCESS) { + DWORD dataType; + DWORD dataSize; + res = RegQueryValueEx(handle, reinterpret_cast<const wchar_t *>(name.utf16()), 0, + &dataType, 0, &dataSize); + setAsExpandSZ = (res == ERROR_SUCCESS) && (dataType == REG_EXPAND_SZ); + if (setAsExpandSZ) { + RegCloseKey(handle); + res = RegOpenKeyEx(hkey, reinterpret_cast<const wchar_t *>(keyPath.utf16()), 0, + KEY_SET_VALUE, &handle); + if (res == ERROR_SUCCESS) { + const QByteArray data(reinterpret_cast<const char *>(value.utf16()), + (value.length() + 1) * 2); + res = RegSetValueEx(handle, reinterpret_cast<const wchar_t *>(name.utf16()), 0, REG_EXPAND_SZ, + reinterpret_cast<const unsigned char*>(data.constData()), data.size()); + RegCloseKey(handle); + } + if (res != ERROR_SUCCESS) { + *errorString = UpdateOperation::tr("Cannot write to registry path %1.").arg(regPath); + *error = true; + } + } + } +#endif + return setAsExpandSZ; +} + template <typename SettingsType> UpdateOperation::Error writeSetting(const QString ®Path, const QString &name, @@ -82,6 +124,10 @@ UpdateOperation::Error writeSetting(const QString ®Path, // remember old value for undo *oldValue = registry.value(name).toString(); + bool error = false; + if (handleRegExpandSz(regPath, name, value, errorString, &error)) + return error ? UpdateOperation::UserDefinedError : UpdateOperation::NoError; + // set the new value registry.setValue(name, value); registry.sync(); @@ -108,6 +154,11 @@ UpdateOperation::Error undoSetting(const QString ®Path, } if (actual != value) //key changed, don't undo return UpdateOperation::UserDefinedError; + + bool error = false; + if (handleRegExpandSz(regPath, name, oldValue, errorString, &error)) + return error ? UpdateOperation::UserDefinedError : UpdateOperation::NoError; + QString dontcare; return writeSetting<SettingsType>(regPath, name, oldValue, errorString, &dontcare); } diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index 58f7a8145..9b9c986e0 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.cpp @@ -1324,6 +1324,12 @@ bool PackageManagerCore::setDefaultPageVisible(int page, bool visible) Sets a validator for the custom page specified by \a name and \a callbackName for the component \a component. + When using this, \a name has to match a dynamic page starting with \c Dynamic. For example, if the page + is called DynamicReadyToInstallWidget, then \a name should be set to \c ReadyToInstallWidget. The + \a callbackName should be set to a function that returns a boolean. When the \c Next button is pressed + on the custom page, then it will call the \a callbackName function. If this returns \c true, then it will + move to the next page. + \sa {installer::setValidatorForCustomPage}{installer.setValidatorForCustomPage} \sa setValidatorForCustomPageRequested() */ diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index fb1082294..57f430778 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -1197,8 +1197,8 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper performOperationThreaded(op, Backup); performOperationThreaded(op); - // copy application icons if it exists - const QString icon = QFileInfo(QCoreApplication::applicationFilePath()).baseName() + // copy application icons if it exists. + const QString icon = QFileInfo(QCoreApplication::applicationFilePath()).fileName() + QLatin1String(".icns"); op = createOwnedOperation(QLatin1String("Copy")); op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Resources/") + icon) diff --git a/src/libs/installer/packagemanagerproxyfactory.cpp b/src/libs/installer/packagemanagerproxyfactory.cpp index 98aef7d9a..93135aa1f 100644 --- a/src/libs/installer/packagemanagerproxyfactory.cpp +++ b/src/libs/installer/packagemanagerproxyfactory.cpp @@ -62,13 +62,6 @@ QList<QNetworkProxy> PackageManagerProxyFactory::queryProxy(const QNetworkProxyQ QList<QNetworkProxy> list; if (settings.proxyType() == Settings::SystemProxy) { -#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) - QUrl proxyUrl = QUrl::fromUserInput(QString::fromUtf8(qgetenv("http_proxy"))); - if (proxyUrl.isValid()) { - return list << QNetworkProxy(QNetworkProxy::HttpProxy, proxyUrl.host(), proxyUrl.port(), - proxyUrl.userName(), proxyUrl.password()); - } -#endif QList<QNetworkProxy> systemProxies = systemProxyForQuery(query); auto proxyIter = systemProxies.begin(); diff --git a/src/libs/installer/remoteclient_p.h b/src/libs/installer/remoteclient_p.h index d1e873d43..ed1731342 100644 --- a/src/libs/installer/remoteclient_p.h +++ b/src/libs/installer/remoteclient_p.h @@ -125,32 +125,38 @@ public: if (!started) { if (m_authorizationFallbackDisabled) { - MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), - QLatin1String("AuthorizationError"), - QCoreApplication::translate("RemoteClient", "Cannot get authorization."), - QCoreApplication::translate("RemoteClient", - "Cannot get authorization that is needed for continuing the installation.\n\n" - "Please start the setup program as a user with the appropriate rights.\n" - "Or accept the elevation of access rights if being asked.")); - return; + QMessageBox::Button res = QMessageBox::Retry; + while (res == QMessageBox::Retry && !started) { + res = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), + QLatin1String("AuthorizationError"), + QCoreApplication::translate("RemoteClient", "Cannot get authorization."), + QCoreApplication::translate("RemoteClient", + "Cannot get authorization that is needed for continuing the installation.\n\n" + "Please start the setup program as a user with the appropriate rights.\n" + "Or accept the elevation of access rights if being asked."), + QMessageBox::Abort | QMessageBox::Retry, QMessageBox::Retry); + if (res == QMessageBox::Retry) + started = AdminAuthorization::execute(0, m_serverCommand, m_serverArguments); + } + } else { + // something went wrong with authorizing, either user pressed cancel or entered + // wrong password + const QString fallback = m_serverCommand + QLatin1String(" ") + m_serverArguments + .join(QLatin1String(" ")); + + const QMessageBox::Button res = + MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), + QLatin1String("AuthorizationError"), + QCoreApplication::translate("RemoteClient", "Cannot get authorization."), + QCoreApplication::translate("RemoteClient", "Cannot get authorization that " + "is needed for continuing the installation.\n Either abort the " + "installation or use the fallback solution by running\n\n%1\n\nas a user " + "with the appropriate rights and then clicking OK.").arg(fallback), + QMessageBox::Abort | QMessageBox::Ok, QMessageBox::Ok); + + if (res == QMessageBox::Ok) + started = true; } - // something went wrong with authorizing, either user pressed cancel or entered - // wrong password - const QString fallback = m_serverCommand + QLatin1String(" ") + m_serverArguments - .join(QLatin1String(" ")); - - const QMessageBox::Button res = - MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), - QLatin1String("AuthorizationError"), - QCoreApplication::translate("RemoteClient", "Cannot get authorization."), - QCoreApplication::translate("RemoteClient", "Cannot get authorization that " - "is needed for continuing the installation.\n Either abort the " - "installation or use the fallback solution by running\n\n%1\n\nas a user " - "with the appropriate rights and then clicking OK.").arg(fallback), - QMessageBox::Abort | QMessageBox::Ok, QMessageBox::Ok); - - if (res == QMessageBox::Ok) - started = true; } } else { started = QInstaller::startDetached(m_serverCommand, m_serverArguments, diff --git a/src/libs/installer/scriptengine.cpp b/src/libs/installer/scriptengine.cpp index 59f71a52c..28f91a394 100644 --- a/src/libs/installer/scriptengine.cpp +++ b/src/libs/installer/scriptengine.cpp @@ -401,8 +401,9 @@ QJSValue ScriptEngine::loadInContext(const QString &context, const QString &file if (scriptContext.isError()) { throw Error(tr("Exception while loading the component script \"%1\": %2").arg( QDir::toNativeSeparators(QFileInfo(file).absoluteFilePath()), - scriptContext.toString().isEmpty() ? - tr("Unknown error.") : scriptContext.toString())); + scriptContext.toString().isEmpty() ? tr("Unknown error.") : scriptContext.toString() + + QStringLiteral(" ") + tr("on line number: ") + + scriptContext.property(QStringLiteral("lineNumber")).toString())); } return scriptContext; } diff --git a/src/libs/kdtools/filedownloader.cpp b/src/libs/kdtools/filedownloader.cpp index 1a0a8f1c2..87dfd21b4 100644 --- a/src/libs/kdtools/filedownloader.cpp +++ b/src/libs/kdtools/filedownloader.cpp @@ -181,8 +181,13 @@ struct KDUpdater::FileDownloader::Private , m_assumedSha1Sum("") , autoRemove(true) , m_speedTimerInterval(100) + , m_downloadDeadlineTimerInterval(30000) + , m_downloadPaused(false) + , m_downloadResumed(false) , m_bytesReceived(0) , m_bytesToReceive(0) + , m_bytesBeforeResume(0) + , m_totalBytesBeforeResume(0) , m_currentSpeedBin(0) , m_sampleIndex(0) , m_downloadSpeed(0) @@ -207,11 +212,18 @@ struct KDUpdater::FileDownloader::Private bool autoRemove; bool followRedirect; - QBasicTimer m_timer; + QBasicTimer m_speedIntervalTimer; int m_speedTimerInterval; + QBasicTimer m_downloadDeadlineTimer; + int m_downloadDeadlineTimerInterval; + bool m_downloadPaused; + bool m_downloadResumed; + qint64 m_bytesReceived; qint64 m_bytesToReceive; + qint64 m_bytesBeforeResume; + qint64 m_totalBytesBeforeResume; mutable qint64 m_samples[50]; mutable qint64 m_currentSpeedBin; @@ -382,8 +394,8 @@ void KDUpdater::FileDownloader::cancelDownload() */ void KDUpdater::FileDownloader::runDownloadSpeedTimer() { - if (!d->m_timer.isActive()) - d->m_timer.start(d->m_speedTimerInterval, this); + if (!d->m_speedIntervalTimer.isActive()) + d->m_speedIntervalTimer.start(d->m_speedTimerInterval, this); } /*! @@ -391,7 +403,97 @@ void KDUpdater::FileDownloader::runDownloadSpeedTimer() */ void KDUpdater::FileDownloader::stopDownloadSpeedTimer() { - d->m_timer.stop(); + d->m_speedIntervalTimer.stop(); +} + +/*! + Restarts the download deadline timer. +*/ +void KDUpdater::FileDownloader::runDownloadDeadlineTimer() +{ + stopDownloadDeadlineTimer(); + d->m_downloadDeadlineTimer.start(d->m_downloadDeadlineTimerInterval, this); +} + +/*! + Stops the download deadline timer. +*/ +void KDUpdater::FileDownloader::stopDownloadDeadlineTimer() +{ + d->m_downloadDeadlineTimer.stop(); +} + +/*! + Sets the download into a paused state. +*/ +void KDUpdater::FileDownloader::setDownloadPaused(bool paused) +{ + d->m_downloadPaused = paused; +} + +/*! + Gets the download paused state. +*/ +bool KDUpdater::FileDownloader::isDownloadPaused() +{ + return d->m_downloadPaused; +} + +/*! + Sets the download into a paused state. +*/ +void KDUpdater::FileDownloader::setDownloadResumed(bool resumed) +{ + d->m_downloadResumed = resumed; +} + +/*! + Gets the download resumed state. +*/ +bool KDUpdater::FileDownloader::isDownloadResumed() +{ + return d->m_downloadResumed; +} + +/*! + Gets the amount of bytes downloaded before download resume. +*/ +qint64 KDUpdater::FileDownloader::bytesDownloadedBeforeResume() +{ + return d->m_bytesBeforeResume; +} + +/*! + Gets the total amount of bytes downloaded before download resume. +*/ +qint64 KDUpdater::FileDownloader::totalBytesDownloadedBeforeResume() +{ + return d->m_totalBytesBeforeResume; +} + +/*! + Clears the amount of bytes downloaded before download resume. +*/ +void KDUpdater::FileDownloader::clearBytesDownloadedBeforeResume() +{ + d->m_bytesBeforeResume = 0; + d->m_totalBytesBeforeResume = 0; +} + +/*! + Updates the amount of bytes downloaded before download resume. +*/ +void KDUpdater::FileDownloader::updateBytesDownloadedBeforeResume(qint64 bytes) +{ + d->m_bytesBeforeResume += bytes; +} + +/*! + Updates the total amount of bytes downloaded before download resume. +*/ +void KDUpdater::FileDownloader::updateTotalBytesDownloadedBeforeResume() +{ + d->m_totalBytesBeforeResume = d->m_bytesBeforeResume; } /*! @@ -407,7 +509,15 @@ void KDUpdater::FileDownloader::addSample(qint64 sample) */ int KDUpdater::FileDownloader::downloadSpeedTimerId() const { - return d->m_timer.timerId(); + return d->m_speedIntervalTimer.timerId(); +} + +/*! + Returns the download deadline timer ID. +*/ +int KDUpdater::FileDownloader::downloadDeadlineTimerId() const +{ + return d->m_downloadDeadlineTimer.timerId(); } /*! @@ -792,7 +902,6 @@ void KDUpdater::LocalFileDownloader::timerEvent(QTimerEvent *event) } addSample(numRead); addCheckSumData(buffer.data(), numRead); - if (numRead > 0) { setProgress(d->source->pos(), d->source->size()); emit downloadProgress(calcProgress(d->source->pos(), d->source->size())); @@ -1050,21 +1159,31 @@ struct KDUpdater::HttpDownloader::Private HttpDownloader *const q; QNetworkAccessManager manager; QNetworkReply *http; + QUrl sourceUrl; QFile *destination; QString destFileName; bool downloaded; bool aborted; int m_authenticationCount; - void shutDown() + void shutDown(bool closeDestination = true) { - disconnect(http, &QNetworkReply::finished, q, &HttpDownloader::httpReqFinished); - http->deleteLater(); + if (http) { + disconnect(http, &QNetworkReply::finished, q, &HttpDownloader::httpReqFinished); + disconnect(http, &QNetworkReply::downloadProgress, + q, &HttpDownloader::httpReadProgress); + void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error; + + disconnect(http, errorSignal, q, &HttpDownloader::httpError); + http->deleteLater(); + } http = 0; - destination->close(); - destination->deleteLater(); - destination = 0; - q->resetCheckSumData(); + if (closeDestination) { + destination->close(); + destination->deleteLater(); + destination = 0; + q->resetCheckSumData(); + } } }; @@ -1081,6 +1200,9 @@ KDUpdater::HttpDownloader::HttpDownloader(QObject *parent) #endif connect(&d->manager, &QNetworkAccessManager::authenticationRequired, this, &HttpDownloader::onAuthenticationRequired); + connect(&d->manager, &QNetworkAccessManager::networkAccessibleChanged, + this, &HttpDownloader::onNetworkAccessibleChanged); + } /*! @@ -1123,6 +1245,7 @@ void KDUpdater::HttpDownloader::doDownload() startDownload(url()); runDownloadSpeedTimer(); + runDownloadDeadlineTimer(); } /*! @@ -1152,6 +1275,8 @@ KDUpdater::HttpDownloader *KDUpdater::HttpDownloader::clone(QObject *parent) con void KDUpdater::HttpDownloader::httpReadyRead() { + if (d->http == 0 || d->destination == 0) + return; static QByteArray buffer(16384, '\0'); while (d->http->bytesAvailable()) { const qint64 read = d->http->read(buffer.data(), buffer.size()); @@ -1170,6 +1295,7 @@ void KDUpdater::HttpDownloader::httpReadyRead() } addSample(written); addCheckSumData(buffer.data(), read); + updateBytesDownloadedBeforeResume(written); } } @@ -1194,6 +1320,10 @@ void KDUpdater::HttpDownloader::cancelDownload() void KDUpdater::HttpDownloader::httpDone(bool error) { if (error) { + if (isDownloadResumed()) { + d->shutDown(false); + return; + } QString err; if (d->http) { err = d->http->errorString(); @@ -1206,10 +1336,11 @@ void KDUpdater::HttpDownloader::httpDone(bool error) d->aborted = false; setDownloadCanceled(); } else { - setDownloadAborted(err); + d->shutDown(false); + return; } } - //PENDING: what about the non-error case?? + setDownloadResumed(false); } /*! @@ -1223,6 +1354,7 @@ void KDUpdater::HttpDownloader::onError() delete d->destination; d->destination = 0; stopDownloadSpeedTimer(); + stopDownloadDeadlineTimer(); } /*! @@ -1232,12 +1364,16 @@ void KDUpdater::HttpDownloader::onError() void KDUpdater::HttpDownloader::onSuccess() { d->downloaded = true; - d->destFileName = d->destination->fileName(); - if (QTemporaryFile *file = dynamic_cast<QTemporaryFile *>(d->destination)) - file->setAutoRemove(false); + if (d->destination) { + d->destFileName = d->destination->fileName(); + if (QTemporaryFile *file = dynamic_cast<QTemporaryFile *>(d->destination)) + file->setAutoRemove(false); + } delete d->destination; d->destination = 0; stopDownloadSpeedTimer(); + stopDownloadDeadlineTimer(); + setDownloadResumed(false); } void KDUpdater::HttpDownloader::httpReqFinished() @@ -1266,9 +1402,11 @@ void KDUpdater::HttpDownloader::httpReqFinished() } } httpReadyRead(); - d->destination->flush(); + if (d->destination) + d->destination->flush(); setDownloadCompleted(); - d->http->deleteLater(); + if (d->http) + d->http->deleteLater(); d->http = 0; } } @@ -1281,8 +1419,16 @@ void KDUpdater::HttpDownloader::httpReadProgress(qint64 done, qint64 total) return; // if we are a redirection, do not emit the progress } - setProgress(done, total); - emit downloadProgress(calcProgress(done, total)); + if (isDownloadResumed()) + setProgress(done + totalBytesDownloadedBeforeResume(), + total + totalBytesDownloadedBeforeResume()); + else + setProgress(done, total); + runDownloadDeadlineTimer(); + if (isDownloadResumed()) + emit downloadProgress(calcProgress(done + totalBytesDownloadedBeforeResume(), total + totalBytesDownloadedBeforeResume())); + else + emit downloadProgress(calcProgress(done, total)); } /*! @@ -1295,15 +1441,19 @@ void KDUpdater::HttpDownloader::timerEvent(QTimerEvent *event) emitDownloadStatus(); emitDownloadProgress(); emitEstimatedDownloadTime(); + } else if (event->timerId() == downloadDeadlineTimerId()) { + d->shutDown(false); + resumeDownload(); } } void KDUpdater::HttpDownloader::startDownload(const QUrl &url) { + d->sourceUrl = url; d->m_authenticationCount = 0; d->manager.setProxyFactory(proxyFactory()); + clearBytesDownloadedBeforeResume(); d->http = d->manager.get(QNetworkRequest(url)); - connect(d->http, &QIODevice::readyRead, this, &HttpDownloader::httpReadyRead); connect(d->http, &QNetworkReply::downloadProgress, this, &HttpDownloader::httpReadProgress); @@ -1329,6 +1479,28 @@ void KDUpdater::HttpDownloader::startDownload(const QUrl &url) } } +void KDUpdater::HttpDownloader::resumeDownload() +{ + updateTotalBytesDownloadedBeforeResume(); + d->m_authenticationCount = 0; + QNetworkRequest request(d->sourceUrl); + + request.setRawHeader(QByteArray("Range"), + QString(QStringLiteral("bytes=%1-")) + .arg(bytesDownloadedBeforeResume()) + .toLatin1()); + setDownloadResumed(true); + d->http = d->manager.get(request); + connect(d->http, &QIODevice::readyRead, this, &HttpDownloader::httpReadyRead); + connect(d->http, &QNetworkReply::downloadProgress, + this, &HttpDownloader::httpReadProgress); + connect(d->http, &QNetworkReply::finished, this, &HttpDownloader::httpReqFinished); + void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error; + connect(d->http, errorSignal, this, &HttpDownloader::httpError); + runDownloadSpeedTimer(); + runDownloadDeadlineTimer(); +} + void KDUpdater::HttpDownloader::onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) { Q_UNUSED(reply) @@ -1366,6 +1538,21 @@ void KDUpdater::HttpDownloader::onAuthenticationRequired(QNetworkReply *reply, Q } } +void KDUpdater::HttpDownloader::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible) +{ + if (accessible == QNetworkAccessManager::NotAccessible) { + d->shutDown(false); + setDownloadPaused(true); + setDownloadResumed(false); + stopDownloadDeadlineTimer(); + } else if (accessible == QNetworkAccessManager::Accessible) { + if (isDownloadPaused()) { + setDownloadPaused(false); + resumeDownload(); + } + } +} + #ifndef QT_NO_SSL #include "messageboxhandler.h" diff --git a/src/libs/kdtools/filedownloader.h b/src/libs/kdtools/filedownloader.h index 8c73cbeb0..ede20dcfa 100644 --- a/src/libs/kdtools/filedownloader.h +++ b/src/libs/kdtools/filedownloader.h @@ -115,8 +115,21 @@ protected: void runDownloadSpeedTimer(); void stopDownloadSpeedTimer(); + void runDownloadDeadlineTimer(); + void stopDownloadDeadlineTimer(); + void setDownloadPaused(bool paused); + bool isDownloadPaused(); + void setDownloadResumed(bool resumed); + bool isDownloadResumed(); + qint64 bytesDownloadedBeforeResume(); + qint64 totalBytesDownloadedBeforeResume(); + void clearBytesDownloadedBeforeResume(); + void updateBytesDownloadedBeforeResume(qint64 bytes); + void updateTotalBytesDownloadedBeforeResume(); + void addSample(qint64 sample); int downloadSpeedTimerId() const; + int downloadDeadlineTimerId() const; void setProgress(qint64 bytesReceived, qint64 bytesToReceive); void emitDownloadSpeed(); diff --git a/src/libs/kdtools/filedownloader_p.h b/src/libs/kdtools/filedownloader_p.h index f5a80c432..41a430554 100644 --- a/src/libs/kdtools/filedownloader_p.h +++ b/src/libs/kdtools/filedownloader_p.h @@ -32,6 +32,7 @@ #include "filedownloader.h" #include <QtNetwork/QNetworkReply> +#include <QNetworkAccessManager> // these classes are not a part of the public API @@ -128,11 +129,13 @@ private Q_SLOTS: void httpDone(bool error); void httpReqFinished(); void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); + void onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible); #ifndef QT_NO_SSL void onSslErrors(QNetworkReply* reply, const QList<QSslError> &errors); #endif private: void startDownload(const QUrl &url); + void resumeDownload(); private: struct Private; diff --git a/src/sdk/commandlineparser.cpp b/src/sdk/commandlineparser.cpp index fbaa91328..2f1568786 100644 --- a/src/sdk/commandlineparser.cpp +++ b/src/sdk/commandlineparser.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -53,6 +53,9 @@ CommandLineParser::CommandLineParser() m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::Proxy), QLatin1String("Use system proxy on Windows and Linux. This option has no effect on OS X."))); + m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::NoProxy), + QLatin1String("Do not use system proxy."))); + m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::Script), QLatin1String("Execute the script given as argument."), QLatin1String("file"))); diff --git a/src/sdk/constants.h b/src/sdk/constants.h index ce447ac4b..d46af2db4 100644 --- a/src/sdk/constants.h +++ b/src/sdk/constants.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -38,6 +38,7 @@ const char FrameworkVersion[] = "framework-version"; const char VerboseShort[] = "v"; const char VerboseLong[] = "verbose"; const char Proxy[] = "proxy"; +const char NoProxy[] = "no-proxy"; const char Script[] = "script"; const char CheckUpdates[] = "checkupdates"; const char Updater[] = "updater"; diff --git a/src/sdk/installerbase.cpp b/src/sdk/installerbase.cpp index 043d7beca..6004d286e 100644 --- a/src/sdk/installerbase.cpp +++ b/src/sdk/installerbase.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -164,7 +164,14 @@ int InstallerBase::run() + m_core->settings().controlScript(); } - if (parser.isSet(QLatin1String(CommandLineOptions::Proxy))) { + // From Qt5.8 onwards a separate command line option --proxy is not needed as system + // proxy is used by default. If Qt is built with QT_USE_SYSTEM_PROXIES false + // then system proxies are not used by default. + if ((parser.isSet(QLatin1String(CommandLineOptions::Proxy)) +#if QT_VERSION > 0x050800 + || QNetworkProxyFactory::usesSystemConfiguration() +#endif + ) && !parser.isSet(QLatin1String(CommandLineOptions::NoProxy))) { m_core->settings().setProxyType(QInstaller::Settings::SystemProxy); KDUpdater::FileDownloaderFactory::instance().setProxyFactory(m_core->proxyFactory()); } @@ -257,7 +264,7 @@ int InstallerBase::run() QCoreApplication::instance()->installTranslator(qtTranslator.take()); QScopedPointer<QTranslator> ifwTranslator(new QTranslator(QCoreApplication::instance())); - if (ifwTranslator->load(locale, QString(), QString(), directory)) + if (ifwTranslator->load(locale, QLatin1String("ifw"), QLatin1String("_"), directory)) QCoreApplication::instance()->installTranslator(ifwTranslator.take()); // To stop loading other translations it's sufficient that diff --git a/src/sdk/main.cpp b/src/sdk/main.cpp index 70c675f91..819d512c2 100644 --- a/src/sdk/main.cpp +++ b/src/sdk/main.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -58,7 +58,13 @@ static const char PLACEHOLDER[32] = "MY_InstallerCreateDateTime_MY"; int main(int argc, char *argv[]) { - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#if defined(Q_OS_WIN) + if (!qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") + && !qEnvironmentVariableIsSet("QT_SCALE_FACTOR") + && !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) { + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + } +#endif // increase maximum numbers of file descriptors #if defined (Q_OS_OSX) QCoreApplication::setSetuidAllowed(true); @@ -194,18 +200,12 @@ int main(int argc, char *argv[]) if (parser.isSet(QLatin1String(CommandLineOptions::Proxy))) { // Make sure we honor the system's proxy settings -#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) - QUrl proxyUrl(QString::fromLatin1(qgetenv("http_proxy"))); - if (proxyUrl.isValid()) { - QNetworkProxy proxy(QNetworkProxy::HttpProxy, proxyUrl.host(), proxyUrl.port(), - proxyUrl.userName(), proxyUrl.password()); - QNetworkProxy::setApplicationProxy(proxy); - } -#else QNetworkProxyFactory::setUseSystemConfiguration(true); -#endif } + if (parser.isSet(QLatin1String(CommandLineOptions::NoProxy))) + QNetworkProxyFactory::setUseSystemConfiguration(false); + if (parser.isSet(QLatin1String(CommandLineOptions::CheckUpdates))) return UpdateChecker(argc, argv).check(); diff --git a/src/sdk/sdk.pro b/src/sdk/sdk.pro index 6af36feb2..677f187a7 100644 --- a/src/sdk/sdk.pro +++ b/src/sdk/sdk.pro @@ -22,16 +22,16 @@ CONFIG(static, static|shared) { DESTDIR = $$IFW_APP_PATH exists($$LRELEASE) { - IB_TRANSLATIONS = $$files($$PWD/translations/??.ts) $$files($$PWD/translations/??_??.ts) - IB_TRANSLATIONS -= $$PWD/translations/en.ts + IB_TRANSLATIONS = $$files($$PWD/translations/*_??.ts) + IB_TRANSLATIONS -= $$PWD/translations/ifw_en.ts wd = $$toNativeSeparators($$IFW_SOURCE_TREE) sources = src lupdate_opts = -locations relative -no-ui-lines -no-sort - IB_ALL_TRANSLATIONS = $$IB_TRANSLATIONS $$PWD/translations/untranslated.ts + IB_ALL_TRANSLATIONS = $$IB_TRANSLATIONS $$PWD/translations/ifw_untranslated.ts for(file, IB_ALL_TRANSLATIONS) { - lang = $$replace(file, .*/([^/]*)\\.ts, \\1) + lang = $$replace(file, .*_([^/]*)\\.ts, \\1) v = ts-$${lang}.commands $$v = cd $$wd && $$LUPDATE $$lupdate_opts $$sources -ts $$file QMAKE_EXTRA_TARGETS += ts-$$lang @@ -42,31 +42,31 @@ exists($$LRELEASE) { isEqual(QMAKE_DIR_SEP, /) { commit-ts.commands = \ cd $$wd; \ - git add -N src/sdk/translations/??.ts src/sdk/translations/??_??.ts && \ - for f in `git diff-files --name-only src/sdk/translations/??.ts src/sdk/translations/??_??.ts`; do \ + git add -N src/sdk/translations/*_??.ts && \ + for f in `git diff-files --name-only src/sdk/translations/*_??.ts`; do \ $$LCONVERT -locations none -i \$\$f -o \$\$f; \ done; \ - git add src/sdk/translations/??.ts src/sdk/translations/??_??.ts && git commit + git add src/sdk/translations/*_??.ts && git commit } else { commit-ts.commands = \ cd $$wd && \ - git add -N src/sdk/translations/??.ts src/sdk/translations/??_??.ts && \ - for /f usebackq %%f in (`git diff-files --name-only src/sdk/translations/??.ts src/sdk/translations/??_??.ts`) do \ + git add -N src/sdk/translations/*_??.ts && \ + for /f usebackq %%f in (`git diff-files --name-only src/sdk/translations/*_??.ts`) do \ $$LCONVERT -locations none -i %%f -o %%f $$escape_expand(\\n\\t) \ - cd $$wd && git add src/sdk/translations/??.ts src/sdk/translations/??_??.ts && git commit + cd $$wd && git add src/sdk/translations/*_??.ts && git commit } QMAKE_EXTRA_TARGETS += commit-ts empty_ts = "<TS></TS>" - write_file($$OUT_PWD/translations/en.ts, empty_ts)|error("Aborting.") - IB_TRANSLATIONS += $$OUT_PWD/translations/en.ts - QMAKE_DISTCLEAN += translations/en.ts + write_file($$OUT_PWD/translations/ifw_en.ts, empty_ts)|error("Aborting.") + IB_TRANSLATIONS += $$OUT_PWD/translations/ifw_en.ts + QMAKE_DISTCLEAN += translations/ifw_en.ts qrc_cont = \ "<RCC>" \ " <qresource prefix=\"/\">" for (file, IB_TRANSLATIONS) { - lang = $$replace(file, .*/([^/]*)\\.ts, \\1) + lang = $$replace(file, .*_([^/]*)\\.ts, \\1) qfile = $$[QT_INSTALL_TRANSLATIONS]/qtbase_$${lang}.qm !exists($$qfile) { qfile = $$[QT_INSTALL_TRANSLATIONS]/qt_$${lang}.qm @@ -76,10 +76,10 @@ exists($$LRELEASE) { } } qrc_cont += \ - " <file>translations/$${lang}.qm</file>" \ + " <file>translations/ifw_$${lang}.qm</file>" \ " <file alias=\"translations/qt_$${lang}.qm\">$$qfile</file>" ACTIVE_IB_TRANSLATIONS += $$file - RESOURCE_DEPS += $$qfile translations/$${lang}.qm + RESOURCE_DEPS += $$qfile translations/ifw_$${lang}.qm } qrc_cont += \ " </qresource>" \ diff --git a/src/sdk/tabcontroller.cpp b/src/sdk/tabcontroller.cpp index 1ebdbdbeb..760e7dc38 100644 --- a/src/sdk/tabcontroller.cpp +++ b/src/sdk/tabcontroller.cpp @@ -143,8 +143,8 @@ int TabController::init() void TabController::restartWizard() { - d->m_core->reset(d->m_params); if (d->m_networkSettingsChanged) { + d->m_core->reset(d->m_params); d->m_networkSettingsChanged = false; d->m_core->settings().setFtpProxy(d->m_settings.ftpProxy()); diff --git a/src/sdk/translations/README b/src/sdk/translations/README index 2bade2c54..e43b99382 100644 --- a/src/sdk/translations/README +++ b/src/sdk/translations/README @@ -4,7 +4,7 @@ otherwise your language won't be loaded at runtime. To add a new language: 1) Run 'cd src/sdk' (change to the parent directory of this file) 2) Run 'make ts-untranslated' -3) Rename translations/untranslated.ts to translations/<lang>.ts +3) Rename translations/ifw_untranslated.ts to translations/ifw_<lang>.ts 4) Run 'make qmake' 5) Do your translation. Just run 'make' whenever you want to test it. 6) Run 'make commit-ts' diff --git a/src/sdk/translations/da.ts b/src/sdk/translations/ifw_da.ts index 42a742217..42a742217 100644 --- a/src/sdk/translations/da.ts +++ b/src/sdk/translations/ifw_da.ts diff --git a/src/sdk/translations/de.ts b/src/sdk/translations/ifw_de.ts index 23aa045b1..23aa045b1 100644 --- a/src/sdk/translations/de.ts +++ b/src/sdk/translations/ifw_de.ts diff --git a/src/sdk/translations/es.ts b/src/sdk/translations/ifw_es.ts index 1ce64b86e..1ce64b86e 100644 --- a/src/sdk/translations/es.ts +++ b/src/sdk/translations/ifw_es.ts diff --git a/src/sdk/translations/fr.ts b/src/sdk/translations/ifw_fr.ts index c0351120f..c0351120f 100644 --- a/src/sdk/translations/fr.ts +++ b/src/sdk/translations/ifw_fr.ts diff --git a/src/sdk/translations/it.ts b/src/sdk/translations/ifw_it.ts index 514013f57..514013f57 100644 --- a/src/sdk/translations/it.ts +++ b/src/sdk/translations/ifw_it.ts diff --git a/src/sdk/translations/ja.ts b/src/sdk/translations/ifw_ja.ts index d994bcd72..d994bcd72 100644 --- a/src/sdk/translations/ja.ts +++ b/src/sdk/translations/ifw_ja.ts diff --git a/src/sdk/translations/pl.ts b/src/sdk/translations/ifw_pl.ts index 98af6f62d..98af6f62d 100644 --- a/src/sdk/translations/pl.ts +++ b/src/sdk/translations/ifw_pl.ts diff --git a/src/sdk/translations/ru.ts b/src/sdk/translations/ifw_ru.ts index 2783f3eef..2783f3eef 100644 --- a/src/sdk/translations/ru.ts +++ b/src/sdk/translations/ifw_ru.ts diff --git a/src/sdk/translations/zh_CN.ts b/src/sdk/translations/ifw_zh_CN.ts index a826448d9..a826448d9 100644 --- a/src/sdk/translations/zh_CN.ts +++ b/src/sdk/translations/ifw_zh_CN.ts diff --git a/tests/auto/installer/scriptengine/tst_scriptengine.cpp b/tests/auto/installer/scriptengine/tst_scriptengine.cpp index 186e341c7..2049e6323 100644 --- a/tests/auto/installer/scriptengine/tst_scriptengine.cpp +++ b/tests/auto/installer/scriptengine/tst_scriptengine.cpp @@ -277,7 +277,7 @@ private slots: // ignore Output from script setExpectedScriptOutput("function receive()"); - QTest::ignoreMessage(QtWarningMsg, ":43: ReferenceError: foo is not defined"); + QTest::ignoreMessage(QtWarningMsg, ":38: ReferenceError: foo is not defined"); emiter.produceSignal(); const QJSValue value = m_scriptEngine->evaluate(""); diff --git a/tests/auto/installer/settings/tst_settings.cpp b/tests/auto/installer/settings/tst_settings.cpp index fc49f4ed8..0edf3fd7b 100644 --- a/tests/auto/installer/settings/tst_settings.cpp +++ b/tests/auto/installer/settings/tst_settings.cpp @@ -119,9 +119,6 @@ void tst_Settings::loadNotExistingConfig() if (!file.open(QIODevice::ReadOnly)) { errorString = file.errorString(); } - QTest::ignoreMessage(QtDebugMsg, QString::fromLatin1("create Error-Exception: \"Cannot open" - " settings file %1 for reading: %2\"") - .arg(configFile).arg(errorString).toLatin1()); try { Settings::fromFileAndPrefix(configFile, ":/data"); } catch (const Error &error) { diff --git a/tools/common/repositorygen.cpp b/tools/common/repositorygen.cpp index 295dea4d0..a1144a005 100644 --- a/tools/common/repositorygen.cpp +++ b/tools/common/repositorygen.cpp @@ -312,9 +312,10 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met const QJSValue value = testScriptEngine.evaluate(scriptContent, scriptFile.fileName()); if (value.isError()) { throw QInstaller::Error(QString::fromLatin1("Exception while loading component " - "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()), - value.toString().isEmpty() ? - QString::fromLatin1("Unknown error.") : value.toString())); + "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()), + value.toString().isEmpty() ? QString::fromLatin1("Unknown error.") : + value.toString() + QStringLiteral(" on line number: ") + + value.property(QStringLiteral("lineNumber")).toString())); } const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script)); |