diff options
Diffstat (limited to 'src/libs/installer')
127 files changed, 3768 insertions, 3068 deletions
diff --git a/src/libs/installer/adminauthorization_win.cpp b/src/libs/installer/adminauthorization_win.cpp index 721c36de1..3ad193b5e 100644 --- a/src/libs/installer/adminauthorization_win.cpp +++ b/src/libs/installer/adminauthorization_win.cpp @@ -106,14 +106,14 @@ bool AdminAuthorization::execute(QWidget *, const QString &program, const QStrin shellExecuteInfo.lpParameters = (wchar_t *)args.utf16(); shellExecuteInfo.fMask = SEE_MASK_NOASYNC; - qDebug() << QString::fromLatin1("Starting elevated process %1 with arguments: %2.").arg(file, args); + qDebug() << "Starting elevated process" << file << "with arguments" << args; if (ShellExecuteExW(&shellExecuteInfo)) { qDebug() << "Finished starting elevated process."; return true; } else { - qWarning() << QString::fromLatin1("Error while starting elevated process: %1, " - "Error: %2").arg(program, QInstaller::windowsErrorString(GetLastError())); + qWarning() << "Error while starting elevated process" << program + << ":" << QInstaller::windowsErrorString(GetLastError()); } return false; } diff --git a/src/libs/installer/adminauthorization_x11.cpp b/src/libs/installer/adminauthorization_x11.cpp index 1146519d2..7068bb2c6 100644 --- a/src/libs/installer/adminauthorization_x11.cpp +++ b/src/libs/installer/adminauthorization_x11.cpp @@ -55,6 +55,7 @@ #include <sys/ioctl.h> #include <sys/types.h> #include <sys/wait.h> +#include <errno.h> #include <iostream> @@ -130,10 +131,14 @@ bool AdminAuthorization::execute(QWidget *parent, const QString &program, const if (pipe(pipedData) != 0) return false; - int flags = ::fcntl(pipedData[0], F_GETFD); + int flags = ::fcntl(pipedData[0], F_GETFL); if (flags != -1) ::fcntl(pipedData[0], F_SETFL, flags | O_NONBLOCK); + flags = ::fcntl(masterFD, F_GETFL); + if (flags != -1) + ::fcntl(masterFD, F_SETFL, flags | O_NONBLOCK); + pid_t child = fork(); if (child < -1) { @@ -151,25 +156,36 @@ bool AdminAuthorization::execute(QWidget *parent, const QString &program, const ::close(pipedData[1]); QRegExp re(QLatin1String("[Pp]assword.*:")); + QByteArray data; QByteArray errData; - flags = ::fcntl(masterFD, F_GETFD); int bytes = 0; int errBytes = 0; char buf[1024]; char errBuf[1024]; + int status; + bool statusValid = false; while (bytes >= 0) { - int state; - if (::waitpid(child, &state, WNOHANG) == -1) + const pid_t waitResult = ::waitpid(child, &status, WNOHANG); + if (waitResult == -1) { break; + } + if (waitResult == child) { + statusValid = true; + break; + } bytes = ::read(masterFD, buf, 1023); + if (bytes == -1 && errno == EAGAIN) + bytes = 0; + else if (bytes > 0) + data.append(buf, bytes); errBytes = ::read(pipedData[0], errBuf, 1023); if (errBytes > 0) { - errData.append(buf, errBytes); + errData.append(errBuf, errBytes); errBytes=0; } if (bytes > 0) { - const QString line = QString::fromLatin1(buf, bytes); + const QString line = QString::fromLatin1(data); if (re.indexIn(line) != -1) { const QString password = getPassword(parent); if (password.isEmpty()) { @@ -189,20 +205,28 @@ bool AdminAuthorization::execute(QWidget *parent, const QString &program, const if (bytes == 0) ::usleep(100000); } - if (!errData.isEmpty()) { - printError(parent, QString::fromLocal8Bit(errData.constData())); - return false; + + while (true) { + errBytes = ::read(pipedData[0], errBuf, 1023); + if (errBytes == -1 && errno == EAGAIN) { + ::usleep(100000); + continue; + } + + if (errBytes <= 0) + break; + + errData.append(errBuf, errBytes); } - int status; - child = ::wait(&status); - const int exited = WIFEXITED(status); - const int exitStatus = WEXITSTATUS(status); - ::close(pipedData[1]); - if (exited) - return exitStatus == 0; + const bool success = statusValid && WIFEXITED(status) && WEXITSTATUS(status) == 0; - return false; + if (!success && !errData.isEmpty()) { + printError(parent, QString::fromLocal8Bit(errData.constData())); + } + + ::close(pipedData[0]); + return success; } // child process @@ -229,7 +253,7 @@ bool AdminAuthorization::execute(QWidget *parent, const QString &program, const for (int i = 3; i < static_cast<int>(rlp.rlim_cur); ++i) ::close(i); - char **argp = (char **) ::malloc(arguments.count() + 4 * sizeof(char *)); + char **argp = (char **) ::malloc((arguments.count() + 4) * sizeof(char *)); QList<QByteArray> args; args.push_back(SU_COMMAND); args.push_back("-b"); @@ -245,8 +269,10 @@ bool AdminAuthorization::execute(QWidget *parent, const QString &program, const ::unsetenv("LANG"); ::unsetenv("LC_ALL"); - ::execv(SU_COMMAND, argp); - _exit(0); + int exitStatus = 0; + if (::execv(SU_COMMAND, argp) == -1) + exitStatus = -errno; + _exit(exitStatus); return false; } } diff --git a/src/libs/installer/binarycontent.cpp b/src/libs/installer/binarycontent.cpp index d32be6238..6112704b0 100644 --- a/src/libs/installer/binarycontent.cpp +++ b/src/libs/installer/binarycontent.cpp @@ -113,7 +113,7 @@ BinaryLayout BinaryContent::binaryLayout(QFile *file, quint64 magicCookie) const qint64 posOfMetaDataCount = layout.endOfBinaryContent - (4 * sizeof(qint64)); if (!file->seek(posOfMetaDataCount)) { throw QInstaller::Error(QCoreApplication::translate("BinaryLayout", - "Could not seek to %1 to read the embedded meta data count.").arg(posOfMetaDataCount)); + "Cannot seek to %1 to read the embedded meta data count.").arg(posOfMetaDataCount)); } // read the meta resources count @@ -124,7 +124,7 @@ BinaryLayout BinaryContent::binaryLayout(QFile *file, quint64 magicCookie) + (8 * sizeof(qint64))); // meta count, offset/length collection index, marker, cookie... if (!file->seek(posOfResourceCollectionsSegment)) { throw Error(QCoreApplication::translate("BinaryLayout", - "Could not seek to %1 to read the resource collection segment.") + "Cannot seek to %1 to read the resource collection segment.") .arg(posOfResourceCollectionsSegment)); } @@ -199,7 +199,7 @@ void BinaryContent::readBinaryContent(QFile *file, QList<OperationBlob> *operati const qint64 posOfOperationsBlock = layout.operationsSegment.start(); if (!file->seek(posOfOperationsBlock)) { throw Error(QCoreApplication::translate("BinaryContent", - "Could not seek to %1 to read the operation data.").arg(posOfOperationsBlock)); + "Cannot seek to %1 to read the operation data.").arg(posOfOperationsBlock)); } // read the operations count qint64 operationsCount = QInstaller::retrieveInt64(file); @@ -216,7 +216,7 @@ void BinaryContent::readBinaryContent(QFile *file, QList<OperationBlob> *operati if (manager) { // read the collection index and data const qint64 posOfResourceCollectionBlock = layout.resourceCollectionsSegment.start(); if (!file->seek(posOfResourceCollectionBlock)) { - throw Error(QCoreApplication::translate("BinaryContent", "Could not seek to %1 to " + throw Error(QCoreApplication::translate("BinaryContent", "Cannot seek to %1 to " "read the resource collection block.").arg(posOfResourceCollectionBlock)); } manager->read(file, layout.endOfExectuable); @@ -255,7 +255,7 @@ void BinaryContent::writeBinaryContent(QFile *out, const QList<OperationBlob> &o const bool isOpen = resource->isOpen(); if ((!isOpen) && (!resource->open())) { throw Error(QCoreApplication::translate("BinaryContent", - "Could not open meta resource. Error: %1").arg(resource->errorString())); + "Cannot open meta resource %1.").arg(resource->errorString())); } resource->seek(0); diff --git a/src/libs/installer/binaryformat.cpp b/src/libs/installer/binaryformat.cpp index d9ca133c0..42c94ab1d 100644 --- a/src/libs/installer/binaryformat.cpp +++ b/src/libs/installer/binaryformat.cpp @@ -164,7 +164,7 @@ bool Resource::open() } if (!QIODevice::open(QIODevice::ReadOnly)) { - setErrorString(tr("Could not open Resource '%1' read-only.").arg(QString::fromUtf8(m_name))); + setErrorString(tr("Cannot open resource %1 for reading.").arg(QString::fromUtf8(m_name))); return false; } return true; @@ -392,7 +392,7 @@ Range<qint64> ResourceCollectionManager::write(QFileDevice *out, qint64 offset) foreach (const QSharedPointer<Resource> &resource, collection.resources()) { if (!resource->open()) { - throw QInstaller::Error(tr("Could not open resource %1: %2") + throw QInstaller::Error(tr("Cannot open resource %1: %2") .arg(QString::fromUtf8(resource->name()), resource->errorString())); } resource->copyData(out); diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp index 38e8cd844..da0230ade 100644 --- a/src/libs/installer/component.cpp +++ b/src/libs/installer/component.cpp @@ -38,8 +38,7 @@ #include "settings.h" #include "utils.h" -#include <kdupdaterupdatesourcesinfo.h> -#include <kdupdaterupdateoperationfactory.h> +#include "updateoperationfactory.h" #include <productkeycheck.h> @@ -51,18 +50,22 @@ #include <QtUiTools/QUiLoader> +#include <private/qv8engine_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4object_p.h> + #include <algorithm> using namespace QInstaller; static const QLatin1String scScriptTag("Script"); -static const QLatin1String scAutoDependOn("AutoDependOn"); static const QLatin1String scVirtual("Virtual"); static const QLatin1String scInstalled("Installed"); static const QLatin1String scUpdateText("UpdateText"); static const QLatin1String scUninstalled("Uninstalled"); static const QLatin1String scCurrentState("CurrentState"); static const QLatin1String scForcedInstallation("ForcedInstallation"); +static const QLatin1String scCheckable("Checkable"); /*! \inmodule QtInstallerFramework @@ -208,7 +211,7 @@ Component::Component(PackageManagerCore *core) { setPrivate(d); - connect(this, SIGNAL(valueChanged(QString, QString)), this, SLOT(updateModelData(QString, QString))); + connect(this, &Component::valueChanged, this, &Component::updateModelData); qRegisterMetaType<QList<QInstaller::Component*> >("QList<QInstaller::Component*>"); } @@ -237,10 +240,9 @@ Component::~Component() /*! Sets variables according to the values set in the package.xml file of a local \a package. */ -void Component::loadDataFromPackage(const LocalPackage &package) +void Component::loadDataFromPackage(const KDUpdater::LocalPackage &package) { setValue(scName, package.name); - // pixmap ??? setValue(scDisplayName, package.title); setValue(scDescription, package.description); setValue(scVersion, package.version); @@ -249,14 +251,8 @@ void Component::loadDataFromPackage(const LocalPackage &package) setValue(QLatin1String("LastUpdateDate"), package.lastUpdateDate.toString()); setValue(QLatin1String("InstallDate"), package.installDate.toString()); setValue(scUncompressedSize, QString::number(package.uncompressedSize)); - - QString dependstr; - foreach (const QString &val, package.dependencies) - dependstr += val + QLatin1String(","); - - if (package.dependencies.count() > 0) - dependstr.chop(1); - setValue(scDependencies, dependstr); + setValue(scDependencies, package.dependencies.join(QLatin1String(","))); + setValue(scAutoDependOn, package.autoDependencies.join(QLatin1String(","))); setValue(scForcedInstallation, package.forcedInstallation ? scTrue : scFalse); if (package.forcedInstallation & !PackageManagerCore::noForceInstallation()) { @@ -265,6 +261,7 @@ void Component::loadDataFromPackage(const LocalPackage &package) } setValue(scVirtual, package.virtualComp ? scTrue : scFalse); setValue(scCurrentState, scInstalled); + setValue(scCheckable, package.checkable ? scTrue : scFalse); } /*! @@ -282,7 +279,7 @@ void Component::loadDataFromPackage(const Package &package) setValue(scAutoDependOn, package.data(scAutoDependOn).toString()); setValue(scCompressedSize, package.data(scCompressedSize).toString()); setValue(scUncompressedSize, package.data(scUncompressedSize).toString()); - setValue(scRemoteVersion, package.data(scRemoteVersion).toString()); + setValue(scVersion, package.data(scVersion).toString()); setValue(scInheritVersion, package.data(scInheritVersion).toString()); setValue(scDependencies, package.data(scDependencies).toString()); setValue(scDownloadableArchives, package.data(scDownloadableArchives).toString()); @@ -297,6 +294,7 @@ void Component::loadDataFromPackage(const Package &package) setValue(scScriptTag, package.data(scScriptTag).toString()); setValue(scReplaces, package.data(scReplaces).toString()); setValue(scReleaseDate, package.data(scReleaseDate).toString()); + setValue(scCheckable, package.data(scCheckable).toString()); QString forced = package.data(scForcedInstallation, scFalse).toString().toLower(); if (PackageManagerCore::noForceInstallation()) @@ -307,7 +305,7 @@ void Component::loadDataFromPackage(const Package &package) setCheckState(Qt::Checked); } - setLocalTempPath(QInstaller::pathFromUrl(package.sourceInfoUrl())); + setLocalTempPath(QInstaller::pathFromUrl(package.packageSource().url)); const QStringList uis = package.data(QLatin1String("UserInterfaces")).toString() .split(QInstaller::commaRegExp(), QString::SkipEmptyParts); if (!uis.isEmpty()) @@ -388,6 +386,8 @@ void Component::setValue(const QString &key, const QString &value) if (key == scName) d->m_componentName = normalizedValue; + if (key == scCheckable) + this->setCheckable(normalizedValue.toLower() == scTrue); d->m_vars[key] = normalizedValue; emit valueChanged(key, normalizedValue); @@ -570,8 +570,8 @@ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis while (it.hasNext()) { QFile file(it.next()); if (!file.open(QIODevice::ReadOnly)) { - throw Error(tr("Could not open the requested UI file '%1'. Error: %2").arg(it.fileName(), - file.errorString())); + throw Error(tr("Cannot open the requested UI file \"%1\": %2").arg( + it.fileName(), file.errorString())); } static QUiLoader loader; @@ -579,8 +579,8 @@ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis loader.setLanguageChangeEnabled(true); QWidget *const widget = loader.load(&file, 0); if (!widget) { - throw Error(tr("Could not load the requested UI file '%1'. Error: %2").arg(it.fileName(), - loader.errorString())); + throw Error(tr("Cannot load the requested UI file \"%1\": %2").arg( + it.fileName(), loader.errorString())); } d->scriptEngine()->newQObject(widget); d->m_userInterfaces.insert(widget->objectName(), widget); @@ -624,7 +624,7 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar QFile file(fileInfo.filePath()); if (!file.open(QIODevice::ReadOnly)) { - throw Error(tr("Could not open the requested license file '%1'. Error: %2").arg( + throw Error(tr("Cannot open the requested license file \"%1\": %2").arg( file.fileName(), file.errorString())); } QTextStream stream(&file); @@ -698,11 +698,11 @@ void Component::createOperationsForPath(const QString &path) if (fi.isFile()) { static const QString copy = QString::fromLatin1("Copy"); - addOperation(copy, fi.filePath(), target); + addOperation(copy, QStringList() << fi.filePath() << target); } else if (fi.isDir()) { qApp->processEvents(); static const QString mkdir = QString::fromLatin1("Mkdir"); - addOperation(mkdir, target); + addOperation(mkdir, QStringList(target)); QDirIterator it(fi.filePath()); while (it.hasNext()) @@ -742,7 +742,7 @@ void Component::createOperationsForArchive(const QString &archive) if (isZip) { // archives get completely extracted per default (if the script isn't doing other stuff) - addOperation(QLatin1String("Extract"), archive, QLatin1String("@TargetDir@")); + addOperation(QLatin1String("Extract"), QStringList() << archive << QLatin1String("@TargetDir@")); } else { createOperationsForPath(archive); } @@ -823,7 +823,7 @@ void Component::addDownloadableArchive(const QString &path) Q_ASSERT(isFromOnlineRepository()); qDebug() << "addDownloadable" << path; - d->m_downloadableArchives.append(d->m_vars.value(scRemoteVersion) + path); + d->m_downloadableArchives.append(d->m_vars.value(scVersion) + path); } /*! @@ -902,15 +902,14 @@ OperationList Component::operations() const if (!d->m_minimumProgressOperation) { d->m_minimumProgressOperation = KDUpdater::UpdateOperationFactory::instance() - .create(QLatin1String("MinimumProgress")); + .create(QLatin1String("MinimumProgress"), d->m_core); d->m_minimumProgressOperation->setValue(QLatin1String("component"), name()); d->m_operations.append(d->m_minimumProgressOperation); } if (!d->m_licenses.isEmpty()) { d->m_licenseOperation = KDUpdater::UpdateOperationFactory::instance() - .create(QLatin1String("License")); - d->m_licenseOperation->setValue(QLatin1String("installer"), QVariant::fromValue(d->m_core)); + .create(QLatin1String("License"), d->m_core); d->m_licenseOperation->setValue(QLatin1String("component"), name()); QVariantMap licenses; @@ -984,11 +983,12 @@ Operation *Component::createOperation(const QString &operationName, const QStrin Operation *Component::createOperation(const QString &operationName, const QStringList ¶meters) { - Operation *operation = KDUpdater::UpdateOperationFactory::instance().create(operationName); + Operation *operation = KDUpdater::UpdateOperationFactory::instance().create(operationName, + d->m_core); if (operation == 0) { const QMessageBox::StandardButton button = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), - QLatin1String("OperationDoesNotExistError"), tr("Error"), tr("Error: Operation %1 does not exist") + QLatin1String("OperationDoesNotExistError"), tr("Error"), tr("Error: Operation %1 does not exist.") .arg(operationName), QMessageBox::Abort | QMessageBox::Ignore); if (button == QMessageBox::Abort) d->m_operationsCreatedSuccessfully = false; @@ -997,33 +997,50 @@ Operation *Component::createOperation(const QString &operationName, const QStrin if (operation->name() == QLatin1String("Delete")) operation->setValue(QLatin1String("performUndo"), false); - operation->setValue(QLatin1String("installer"), qVariantFromValue(d->m_core)); operation->setArguments(d->m_core->replaceVariables(parameters)); operation->setValue(QLatin1String("component"), name()); return operation; } -/*! - Convenience method for calling the operation \a operation with up to ten parameters: - \a parameter1, \a parameter2, \a parameter3, \a parameter4, \a parameter5, \a parameter6, - \a parameter7, \a parameter8, \a parameter9, and \a parameter10. - - Returns \c true if the operation succeeds, otherwise returns \c false. +namespace { - \sa {component::addOperation}{component.addOperation} -*/ -bool Component::addOperation(const QString &operation, const QString ¶meter1, const QString ¶meter2, - const QString ¶meter3, const QString ¶meter4, const QString ¶meter5, const QString ¶meter6, - const QString ¶meter7, const QString ¶meter8, const QString ¶meter9, const QString ¶meter10) +inline bool convert(QQmlV4Function *func, QStringList *toArgs) { + if (func->length() < 2) + return false; - if (Operation *op = createOperation(operation, parameter1, parameter2, parameter3, parameter4, parameter5, - parameter6, parameter7, parameter8, parameter9, parameter10)) { - addOperation(op); - return true; + QV4::Scope scope(func->v4engine()); + QV4::ScopedValue val(scope); + val = (*func)[0]; + + *toArgs << val->toQString(); + for (int i = 1; i < func->length(); i++) { + val = (*func)[i]; + if (val->isObject() && val->as<QV4::Object>()->isArrayObject()) { + QV4::ScopedValue valtmp(scope); + QV4::Object *array = val->as<QV4::Object>(); + uint length = array->getLength(); + for (uint ii = 0; ii < length; ++ii) { + valtmp = array->getIndexed(ii); + *toArgs << valtmp->toQStringNoThrow(); + } + } else { + *toArgs << val->toQString(); + } } + return true; +} +} +/*! + \internal +*/ +bool Component::addOperation(QQmlV4Function *func) +{ + QStringList args; + if (convert(func, &args)) + return addOperation(args[0], args.mid(1)); return false; } @@ -1032,7 +1049,7 @@ bool Component::addOperation(const QString &operation, const QString ¶meter1 The variables that the parameters contain, such as \c @TargetDir@, are replaced with their values. - Returns \c true if the operation succeeds, otherwise returns \c false. + \sa {component::addOperation}{component.addOperation} */ bool Component::addOperation(const QString &operation, const QStringList ¶meters) { @@ -1045,25 +1062,13 @@ bool Component::addOperation(const QString &operation, const QStringList ¶me } /*! - Convenience method for calling the elevated operation \a operation with up to ten parameters: - \a parameter1, \a parameter2, \a parameter3, \a parameter4, \a parameter5, \a parameter6, - \a parameter7, \a parameter8, \a parameter9, and \a parameter10. - - \sa {component::addElevatedOperation}{component.addElevatedOperation} - - Returns \c true if the operation succeeds, otherwise returns \c false. + \internal */ -bool Component::addElevatedOperation(const QString &operation, const QString ¶meter1, - const QString ¶meter2, const QString ¶meter3, const QString ¶meter4, const QString ¶meter5, - const QString ¶meter6, const QString ¶meter7, const QString ¶meter8, const QString ¶meter9, - const QString ¶meter10) +bool Component::addElevatedOperation(QQmlV4Function *func) { - if (Operation *op = createOperation(operation, parameter1, parameter2, parameter3, parameter4, parameter5, - parameter6, parameter7, parameter8, parameter9, parameter10)) { - addElevatedOperation(op); - return true; - } - + QStringList args; + if (convert(func, &args)) + return addElevatedOperation(args[0], args.mid(1)); return false; } @@ -1072,8 +1077,7 @@ bool Component::addElevatedOperation(const QString &operation, const QString &pa The variables that the parameters contain, such as \c @TargetDir@, are replaced with their values. The operation is executed with elevated rights. - Returns \c true if the operation succeeds, otherwise returns \c false. - + \sa {component::addElevatedOperation}{component.addElevatedOperation} */ bool Component::addElevatedOperation(const QString &operation, const QStringList ¶meters) { @@ -1086,9 +1090,8 @@ bool Component::addElevatedOperation(const QString &operation, const QStringList } /*! - Returns whether operations should be automatically created when the - installation starts and createOperations() is called. If you set this to - \c false, it is completely up + Specifies whether operations should be automatically created when the installation starts. This + would be done by calling createOperations(). If you set this to \c false, it is completely up to the component's script to create all operations. \sa {component::autoCreateOperations}{component.autoCreateOperations} @@ -1239,9 +1242,13 @@ bool Component::isDefault() const return d->m_vars.value(scDefault).compare(scTrue, Qt::CaseInsensitive) == 0; } -bool Component::isInstalled() const +bool Component::isInstalled(const QString version) const { - return scInstalled == d->m_vars.value(scCurrentState); + if (version.isEmpty()) { + return scInstalled == d->m_vars.value(scCurrentState); + } else { + return d->m_vars.value(scInstalledVersion) == version; + } } /*! diff --git a/src/libs/installer/component.h b/src/libs/installer/component.h index 4f4623aba..01622548f 100644 --- a/src/libs/installer/component.h +++ b/src/libs/installer/component.h @@ -39,11 +39,7 @@ #include <QtCore/QUrl> QT_FORWARD_DECLARE_CLASS(QDebug) - -namespace KDUpdater { - class Update; - struct PackageInfo; -} +QT_FORWARD_DECLARE_CLASS(QQmlV4Function) namespace QInstaller { @@ -96,7 +92,7 @@ public: }; void loadDataFromPackage(const Package &package); - void loadDataFromPackage(const LocalPackage &package); + void loadDataFromPackage(const KDUpdater::LocalPackage &package); QHash<QString, QString> variables() const; Q_INVOKABLE void setValue(const QString &key, const QString &value); @@ -133,22 +129,12 @@ public: OperationList operations() const; void addOperation(Operation *operation); - Q_INVOKABLE bool addOperation(const QString &operation, const QString ¶meter1 = QString(), - const QString ¶meter2 = QString(), const QString ¶meter3 = QString(), - const QString ¶meter4 = QString(), const QString ¶meter5 = QString(), - const QString ¶meter6 = QString(), const QString ¶meter7 = QString(), - const QString ¶meter8 = QString(), const QString ¶meter9 = QString(), - const QString ¶meter10 = QString()); - Q_INVOKABLE bool addOperation(const QString &operation, const QStringList ¶meters); + Q_INVOKABLE bool addOperation(QQmlV4Function *args); + bool addOperation(const QString &operation, const QStringList ¶meters); void addElevatedOperation(Operation *operation); - Q_INVOKABLE bool addElevatedOperation(const QString &operation, - const QString ¶meter1 = QString(), const QString ¶meter2 = QString(), - const QString ¶meter3 = QString(), const QString ¶meter4 = QString(), - const QString ¶meter5 = QString(), const QString ¶meter6 = QString(), - const QString ¶meter7 = QString(), const QString ¶meter8 = QString(), - const QString ¶meter9 = QString(), const QString ¶meter10 = QString()); - Q_INVOKABLE bool addElevatedOperation(const QString &operation, const QStringList ¶meters); + Q_INVOKABLE bool addElevatedOperation(QQmlV4Function *args); + bool addElevatedOperation(const QString &operation, const QStringList ¶meters); QStringList downloadableArchives() const; Q_INVOKABLE void addDownloadableArchive(const QString &path); @@ -180,7 +166,7 @@ public: Q_INVOKABLE bool isAutoDependOn(const QSet<QString> &componentsToInstall) const; Q_INVOKABLE void setInstalled(); - Q_INVOKABLE bool isInstalled() const; + Q_INVOKABLE bool isInstalled(const QString version = QString()) const; Q_INVOKABLE bool installationRequested() const; bool isSelectedForInstallation() const; diff --git a/src/libs/installer/componentchecker.cpp b/src/libs/installer/componentchecker.cpp index d905786f3..e21bc6696 100644 --- a/src/libs/installer/componentchecker.cpp +++ b/src/libs/installer/componentchecker.cpp @@ -45,6 +45,12 @@ QStringList ComponentChecker::checkComponent(Component *component) if (!core) return checkResult; + if (component->childCount() && !component->archives().isEmpty()) { + checkResult << QString::fromLatin1("Component %1 contains data to be installed " + "while having child components. This may not work properly.") + .arg(component->name()); + } + const bool defaultPropertyScriptValue = component->variables().value(scDefault).compare(scScript, Qt::CaseInsensitive) == 0; const bool defaultPropertyValue = component->variables().value(scDefault).compare(scTrue, Qt::CaseInsensitive) == 0; const QStringList autoDependencies = component->autoDependencies(); @@ -122,13 +128,13 @@ QStringList ComponentChecker::checkComponent(Component *component) Moreover, the "Next" button will be disabled. */ checkResult << QString::fromLatin1("Component %1 auto depends on other components " - "while having children components. This will not work properly.") + "while having child components. This will not work properly.") .arg(component->name()); } if (!component->dependencies().isEmpty()) { checkResult << QString::fromLatin1("Component %1 depends on other components " - "while having children components. This will not work properly.") + "while having child components. This will not work properly.") .arg(component->name()); } @@ -148,7 +154,7 @@ QStringList ComponentChecker::checkComponent(Component *component) Moreover, the "Next" button will be disabled. */ checkResult << QString::fromLatin1("Other components depend on component %1 " - "which has children components. This will not work properly.") + "which has child components. This will not work properly.") .arg(component->name()); } } diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp index ba4c2d238..e0cc3fcd8 100644 --- a/src/libs/installer/componentmodel.cpp +++ b/src/libs/installer/componentmodel.cpp @@ -77,6 +77,10 @@ class IconCache { public: IconCache() { + m_icons.insert(ComponentModelHelper::Install, QIcon(QLatin1String(":/install.png"))); + m_icons.insert(ComponentModelHelper::Uninstall, QIcon(QLatin1String(":/uninstall.png"))); + m_icons.insert(ComponentModelHelper::KeepInstalled, QIcon(QLatin1String(":/keepinstalled.png"))); + m_icons.insert(ComponentModelHelper::KeepUninstalled, QIcon(QLatin1String(":/keepuninstalled.png"))); } QIcon icon(ComponentModelHelper::InstallAction action) const { @@ -97,7 +101,7 @@ ComponentModel::ComponentModel(int columns, PackageManagerCore *core) , m_modelState(DefaultChecked) { m_headerData.insert(0, columns, QVariant()); - connect(this, SIGNAL(modelReset()), this, SLOT(slotModelReset())); + connect(this, &QAbstractItemModel::modelReset, this, &ComponentModel::slotModelReset); } /*! @@ -403,7 +407,7 @@ void ComponentModel::setRootComponents(QList<QInstaller::Component*> rootCompone // show virtual components only in case we run as updater or if the core engine is set to show them const bool showVirtuals = m_core->isUpdater() || m_core->virtualComponentsVisible(); foreach (Component *const component, rootComponents) { - connect(component, SIGNAL(virtualStateChanged()), this, SLOT(onVirtualStateChanged())); + connect(component, &Component::virtualStateChanged, this, &ComponentModel::onVirtualStateChanged); if ((!showVirtuals) && component->isVirtual()) continue; m_rootComponentList.append(component); @@ -463,7 +467,7 @@ void ComponentModel::slotModelReset() foreach (Component *const component, components) { if (component->checkState() == Qt::Checked) checked.insert(component); - connect(component, SIGNAL(virtualStateChanged()), this, SLOT(onVirtualStateChanged())); + connect(component, &Component::virtualStateChanged, this, &ComponentModel::onVirtualStateChanged); } updateCheckedState(checked, Qt::Checked); @@ -571,7 +575,13 @@ QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compone // we can start in descending order to check node and tri-state nodes properly for (int i = sortedNodes.count(); i > 0; i--) { Component * const node = sortedNodes.at(i - 1); - if (!node->isCheckable() || !node->isEnabled() || !node->autoDependencies().isEmpty()) + + bool checkable = true; + if (node->value(scCheckable, scTrue).toLower() == scFalse) { + checkable = false; + } + + if ((!node->isCheckable() && checkable) || !node->isEnabled() || !node->autoDependencies().isEmpty()) continue; Qt::CheckState newState = state; diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h index 6022b074b..f1eac9926 100644 --- a/src/libs/installer/constants.h +++ b/src/libs/installer/constants.h @@ -41,7 +41,6 @@ static const QLatin1String scScript("script"); static const QLatin1String scName("Name"); static const QLatin1String scVersion("Version"); static const QLatin1String scDefault("Default"); -static const QLatin1String scRemoteVersion("Version"); static const QLatin1String scDisplayVersion("DisplayVersion"); static const QLatin1String scRemoteDisplayVersion("RemoteDisplayVersion"); static const QLatin1String scInheritVersion("inheritVersionFrom"); @@ -53,6 +52,7 @@ static const QLatin1String scReleaseDate("ReleaseDate"); static const QLatin1String scDescription("Description"); static const QLatin1String scDisplayName("DisplayName"); static const QLatin1String scDependencies("Dependencies"); +static const QLatin1String scAutoDependOn("AutoDependOn"); static const QLatin1String scNewComponent("NewComponent"); static const QLatin1String scRepositories("Repositories"); static const QLatin1String scCompressedSize("CompressedSize"); @@ -64,6 +64,7 @@ static const QLatin1String scRequiresAdminRights("RequiresAdminRights"); // constants used throughout the components class static const QLatin1String scVirtual("Virtual"); static const QLatin1String scSortingPriority("SortingPriority"); +static const QLatin1String scCheckable("Checkable"); // constants used throughout the settings and package manager core class static const QLatin1String scTitle("Title"); @@ -75,13 +76,21 @@ static const QLatin1String scRemoveTargetDir("RemoveTargetDir"); static const QLatin1String scRunProgramDescription("RunProgramDescription"); static const QLatin1String scTargetConfigurationFile("TargetConfigurationFile"); static const QLatin1String scAllowNonAsciiCharacters("AllowNonAsciiCharacters"); +static const QLatin1String scDisableAuthorizationFallback("DisableAuthorizationFallback"); static const QLatin1String scRepositorySettingsPageVisible("RepositorySettingsPageVisible"); static const QLatin1String scAllowSpaceInPath("AllowSpaceInPath"); static const QLatin1String scWizardStyle("WizardStyle"); +static const QLatin1String scStyleSheet("StyleSheet"); static const QLatin1String scTitleColor("TitleColor"); static const QLatin1String scWizardDefaultWidth("WizardDefaultWidth"); static const QLatin1String scWizardDefaultHeight("WizardDefaultHeight"); +static const QLatin1String scUrlQueryString("UrlQueryString"); static const QLatin1String scProductUUID("ProductUUID"); +static const QLatin1String scAllUsers("AllUsers"); +static const QLatin1String scSupportsModify("SupportsModify"); + +const char scRelocatable[] = "@RELOCATABLE_PATH@"; + } #endif // CONSTANTS_H diff --git a/src/libs/installer/consumeoutputoperation.cpp b/src/libs/installer/consumeoutputoperation.cpp index 8bd9d59f1..43c78193c 100644 --- a/src/libs/installer/consumeoutputoperation.cpp +++ b/src/libs/installer/consumeoutputoperation.cpp @@ -37,7 +37,8 @@ using namespace QInstaller; -ConsumeOutputOperation::ConsumeOutputOperation() +ConsumeOutputOperation::ConsumeOutputOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("ConsumeOutput")); } @@ -53,15 +54,12 @@ bool ConsumeOutputOperation::performOperation() // 2. executable path // 3. argument for the executable // 4. more arguments possible ... - if (arguments().count() < 3) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.").arg(name()).arg( - arguments().count()).arg(tr("at least 2"), QLatin1String("(<to be saved installer key name>, " - "<executable>, [argument1], [argument2], ...)"))); + + if (!checkArgumentCount(2, INT_MAX, tr("<to be saved installer key name> " + "<executable> [argument1] [argument2] [...]"))) return false; - } - PackageManagerCore *const core = value(QLatin1String("installer")).value<PackageManagerCore*>(); + PackageManagerCore *const core = packageManager(); if (!core) { setError(UserDefinedError); setErrorString(tr("Needed installer object in %1 operation is empty.").arg(name())); @@ -71,8 +69,8 @@ bool ConsumeOutputOperation::performOperation() const QString installerKeyName = arguments().at(0); if (installerKeyName.isEmpty()) { setError(UserDefinedError); - setErrorString(tr("Can not save the output of %1 to an empty installer key value.").arg( - arguments().at(1))); + setErrorString(tr("Cannot save the output of \"%1\" to an empty installer key value.").arg( + QDir::toNativeSeparators(arguments().at(1)))); return false; } @@ -85,7 +83,7 @@ bool ConsumeOutputOperation::performOperation() if (!executable.exists() || !executable.isExecutable()) { setError(UserDefinedError); - setErrorString(tr("File '%1' does not exist or is not an executable binary.").arg( + setErrorString(tr("File \"%1\" does not exist or is not an executable binary.").arg( QDir::toNativeSeparators(executable.absoluteFilePath()))); return false; } @@ -106,7 +104,7 @@ bool ConsumeOutputOperation::performOperation() << "standard output: " << process.readAllStandardOutput() << "error output: " << process.readAllStandardError(); setError(UserDefinedError); - setErrorString(tr("Running '%1' resulted in a crash.").arg( + setErrorString(tr("Running \"%1\" resulted in a crash.").arg( QDir::toNativeSeparators(executable.absoluteFilePath()))); return false; } @@ -124,8 +122,7 @@ bool ConsumeOutputOperation::performOperation() } if (executableOutput.isEmpty()) { - qWarning() << QString::fromLatin1("Cannot get any query output from executable: '%1'").arg( - executable.absoluteFilePath()); + qWarning() << "Cannot get any query output from executable" << executable.absoluteFilePath(); } core->setValue(installerKeyName, QString::fromLocal8Bit(executableOutput)); return true; @@ -140,9 +137,3 @@ bool ConsumeOutputOperation::testOperation() { return true; } - -Operation *ConsumeOutputOperation::clone() const -{ - return new ConsumeOutputOperation(); -} - diff --git a/src/libs/installer/consumeoutputoperation.h b/src/libs/installer/consumeoutputoperation.h index ff7629e6e..a1f8a09ff 100644 --- a/src/libs/installer/consumeoutputoperation.h +++ b/src/libs/installer/consumeoutputoperation.h @@ -37,13 +37,12 @@ class INSTALLER_EXPORT ConsumeOutputOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::ConsumeOutputOperation) public: - ConsumeOutputOperation(); + explicit ConsumeOutputOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; private: }; diff --git a/src/libs/installer/copydirectoryoperation.cpp b/src/libs/installer/copydirectoryoperation.cpp index 20358ca66..cdd6da6ab 100644 --- a/src/libs/installer/copydirectoryoperation.cpp +++ b/src/libs/installer/copydirectoryoperation.cpp @@ -46,7 +46,8 @@ public: }; -CopyDirectoryOperation::CopyDirectoryOperation() +CopyDirectoryOperation::CopyDirectoryOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("CopyDirectory")); } @@ -57,13 +58,10 @@ void CopyDirectoryOperation::backup() bool CopyDirectoryOperation::performOperation() { - const QStringList args = arguments(); - if (args.count() < 2 || args.count() > 3) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("2 or 3"), tr(" (<source> <target> [forceOverwrite])"))); + if (!checkArgumentCount(2, 3, tr("<source> <target> [\"forceOverwrite\"]"))) return false; - } + + const QStringList args = arguments(); const QString sourcePath = args.at(0); const QString targetPath = args.at(1); bool overwrite = false; @@ -74,19 +72,22 @@ bool CopyDirectoryOperation::performOperation() overwrite = true; } else { setError(InvalidArguments); - setErrorString(tr("Invalid argument in %0: Third argument needs to be forceOverwrite, " - "if specified").arg(name())); + setErrorString(tr("Invalid argument in %1: Third argument needs to be forceOverwrite, " + "if specified.").arg(name())); return false; } } const QFileInfo sourceInfo(sourcePath); const QFileInfo targetInfo(targetPath); - if (!sourceInfo.exists() || !sourceInfo.isDir() || !targetInfo.exists() || !targetInfo.isDir()) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: Directories are invalid: %1 %2").arg(name()) - .arg(sourcePath).arg(targetPath)); - return false; + + foreach (const QFileInfo &dir, QList<QFileInfo>() << sourceInfo << targetInfo) { + if (!dir.exists() || !dir.isDir()) { + setError(InvalidArguments); + setErrorString(tr("Invalid argument in %1: Directory \"%2\" is invalid.").arg(name()) + .arg(QDir::toNativeSeparators(sourcePath))); + return false; + } } const QDir sourceDir = sourceInfo.absoluteDir(); @@ -117,22 +118,24 @@ bool CopyDirectoryOperation::performOperation() } else if (itemInfo.isDir()) { if (!targetDir.mkpath(targetDir.absoluteFilePath(relativePath))) { setError(InvalidArguments); - setErrorString(tr("Could not create %0").arg(targetDir.absoluteFilePath(relativePath))); + setErrorString(tr("Cannot create directory \"%1\".").arg( + QDir::toNativeSeparators(targetDir.absoluteFilePath(relativePath)))); return false; } } else { const QString absolutePath = targetDir.absoluteFilePath(relativePath); if (overwrite && QFile::exists(absolutePath) && !deleteFileNowOrLater(absolutePath)) { setError(UserDefinedError); - setErrorString(tr("Failed to overwrite %1").arg(absolutePath)); + setErrorString(tr("Failed to overwrite \"%1\".").arg(QDir::toNativeSeparators(absolutePath))); return false; } QFile file(sourceDir.absoluteFilePath(itemName)); if (!file.copy(absolutePath)) { setError(UserDefinedError); - setErrorString(tr("Could not copy %0 to %1, error was: %3").arg(sourceDir.absoluteFilePath(itemName), - targetDir.absoluteFilePath(relativePath), - file.errorString())); + setErrorString(tr("Cannot copy file \"%1\" to \"%2\": %3").arg( + QDir::toNativeSeparators(sourceDir.absoluteFilePath(itemName)), + QDir::toNativeSeparators(targetDir.absoluteFilePath(relativePath)), + file.errorString())); return false; } autoPush.m_files.prepend(targetDir.absoluteFilePath(relativePath)); @@ -151,7 +154,7 @@ bool CopyDirectoryOperation::undoOperation() foreach (const QString &file, files) { if (!QFile::remove(file)) { setError(InvalidArguments); - setErrorString(tr("Could not remove %0").arg(file)); + setErrorString(tr("Cannot remove file \"%1\".").arg(QDir::toNativeSeparators(file))); return false; } dir.rmpath(QFileInfo(file).absolutePath()); @@ -166,8 +169,3 @@ bool CopyDirectoryOperation::testOperation() { return true; } - -Operation *CopyDirectoryOperation::clone() const -{ - return new CopyDirectoryOperation(); -} diff --git a/src/libs/installer/copydirectoryoperation.h b/src/libs/installer/copydirectoryoperation.h index 57169e936..f934f8b91 100644 --- a/src/libs/installer/copydirectoryoperation.h +++ b/src/libs/installer/copydirectoryoperation.h @@ -40,13 +40,12 @@ class INSTALLER_EXPORT CopyDirectoryOperation : public QObject, public Operation Q_OBJECT public: - CopyDirectoryOperation(); + explicit CopyDirectoryOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; Q_SIGNALS: void outputTextChanged(const QString &progress); diff --git a/src/libs/installer/copyfiletask.cpp b/src/libs/installer/copyfiletask.cpp index 84287678a..b3cd61aa2 100644 --- a/src/libs/installer/copyfiletask.cpp +++ b/src/libs/installer/copyfiletask.cpp @@ -28,6 +28,7 @@ #include "copyfiletask.h" #include "observer.h" +#include <QDir> #include <QFileInfo> #include <QTemporaryFile> @@ -63,8 +64,8 @@ void CopyFileTask::doTask(QFutureInterface<FileTaskResult> &fi) QFile source(item.source()); if (!source.open(QIODevice::ReadOnly)) { - fi.reportException(TaskException(tr("Could not open source '%1' for read. Error: %2.") - .arg(source.fileName(), source.errorString()))); + fi.reportException(TaskException(tr("Cannot open file \"%1\" for reading: %2") + .arg(QDir::toNativeSeparators(source.fileName()), source.errorString()))); fi.reportFinished(); return; // error } observer.setBytesToTransfer(source.size()); @@ -79,8 +80,8 @@ void CopyFileTask::doTask(QFutureInterface<FileTaskResult> &fi) file.reset(new QFile(target)); } if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate)) { - fi.reportException(TaskException(tr("Could not open target '%1' for write. Error: %2.") - .arg(file->fileName(), file->errorString()))); + fi.reportException(TaskException(tr("Cannot open file \"%1\" for writing: %2") + .arg(QDir::toNativeSeparators(file->fileName()), file->errorString()))); fi.reportFinished(); return; // error } @@ -96,8 +97,8 @@ void CopyFileTask::doTask(QFutureInterface<FileTaskResult> &fi) while (written < read) { const qint64 toWrite = file->write(buffer.constData() + written, read - written); if (toWrite < 0) { - fi.reportException(TaskException(tr("Writing to target '%1' failed. Error: %2.") - .arg(file->fileName(), file->errorString()))); + fi.reportException(TaskException(tr("Writing to file \"%1\" failed: %2") + .arg(QDir::toNativeSeparators(file->fileName()), file->errorString()))); } written += toWrite; } diff --git a/src/libs/installer/createdesktopentryoperation.cpp b/src/libs/installer/createdesktopentryoperation.cpp index a3bf045de..ed55cdbf2 100644 --- a/src/libs/installer/createdesktopentryoperation.cpp +++ b/src/libs/installer/createdesktopentryoperation.cpp @@ -91,7 +91,8 @@ QString CreateDesktopEntryOperation::absoluteFileName() return QDir(directory).absoluteFilePath(filename); } -CreateDesktopEntryOperation::CreateDesktopEntryOperation() +CreateDesktopEntryOperation::CreateDesktopEntryOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("CreateDesktopEntry")); } @@ -116,32 +117,27 @@ void CreateDesktopEntryOperation::backup() } if (!file.copy(value(QLatin1String("backupOfExistingDesktopEntry")).toString())) - setErrorString(tr("Could not backup file %1: %2").arg(filename, file.errorString())); + setErrorString(tr("Cannot backup file \"%1\": %2").arg(QDir::toNativeSeparators(filename), file.errorString())); } bool CreateDesktopEntryOperation::performOperation() { - const QStringList args = arguments(); - if (args.count() != 2) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("exactly 2"), QLatin1String(""))); + if (!checkArgumentCount(2)) return false; - } const QString filename = absoluteFileName(); - const QString &values = args[1]; + const QString &values = arguments().at(1); QFile file(filename); if (file.exists() && !file.remove()) { setError(UserDefinedError); - setErrorString(tr("Failed to overwrite %1").arg(filename)); + setErrorString(tr("Failed to overwrite file \"%1\".").arg(QDir::toNativeSeparators(filename))); return false; } if(!file.open(QIODevice::WriteOnly)) { setError(UserDefinedError); - setErrorString(tr("Could not write Desktop Entry at %1").arg(filename)); + setErrorString(tr("Cannot write desktop entry to \"%1\".").arg(QDir::toNativeSeparators(filename))); return false; } @@ -167,7 +163,7 @@ bool CreateDesktopEntryOperation::undoOperation() // first remove the link QFile file(filename); if (file.exists() && !file.remove()) { - qWarning() << "Could not delete file" << filename << file.errorString(); + qWarning() << "Cannot delete file" << filename << ":" << file.errorString(); return true; } @@ -177,13 +173,13 @@ bool CreateDesktopEntryOperation::undoOperation() QFile backupFile(value(QLatin1String("backupOfExistingDesktopEntry")).toString()); if (!backupFile.exists()) { // do not treat this as a real error: The backup file might have been just nuked by the user. - qWarning() << "Could not restore original desktop entry at" << filename + qWarning() << "Cannot restore original desktop entry at" << filename << ": Backup file" << backupFile.fileName() << "does not exist anymore."; return true; } if (!backupFile.rename(filename)) - qWarning() << "Could not restore the file" << filename << ":" << backupFile.errorString(); + qWarning() << "Cannot restore the file" << filename << ":" << backupFile.errorString(); return true; } @@ -192,8 +188,3 @@ bool CreateDesktopEntryOperation::testOperation() { return true; } - -Operation *CreateDesktopEntryOperation::clone() const -{ - return new CreateDesktopEntryOperation(); -} diff --git a/src/libs/installer/createdesktopentryoperation.h b/src/libs/installer/createdesktopentryoperation.h index 6a7ab5c25..793d1db16 100644 --- a/src/libs/installer/createdesktopentryoperation.h +++ b/src/libs/installer/createdesktopentryoperation.h @@ -37,14 +37,13 @@ class INSTALLER_EXPORT CreateDesktopEntryOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::CreateDesktopEntryOperation) public: - CreateDesktopEntryOperation(); + explicit CreateDesktopEntryOperation(PackageManagerCore *core); ~CreateDesktopEntryOperation(); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation* clone() const; QString absoluteFileName(); }; diff --git a/src/libs/installer/createlinkoperation.cpp b/src/libs/installer/createlinkoperation.cpp index 1ca609237..851290ba8 100644 --- a/src/libs/installer/createlinkoperation.cpp +++ b/src/libs/installer/createlinkoperation.cpp @@ -29,11 +29,13 @@ #include "link.h" +#include <QDir> #include <QFileInfo> using namespace QInstaller; -CreateLinkOperation::CreateLinkOperation() +CreateLinkOperation::CreateLinkOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("CreateLink")); } @@ -44,22 +46,18 @@ void CreateLinkOperation::backup() bool CreateLinkOperation::performOperation() { - QStringList args = arguments(); - - if (args.count() != 2) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("exactly 2"), QLatin1String(""))); + if (!checkArgumentCount(2)) return false; - } + const QStringList args = arguments(); const QString& linkPath = args.at(0); const QString& targetPath = args.at(1); Link link = Link::create(linkPath, targetPath); if (!link.exists()) { setError(UserDefinedError); - setErrorString(tr("Could not create link from %1 to %2.").arg(linkPath, targetPath)); + setErrorString(tr("Cannot create link from \"%1\" to \"%2\".").arg( + QDir::toNativeSeparators(linkPath), QDir::toNativeSeparators(targetPath))); return false; } @@ -79,7 +77,8 @@ bool CreateLinkOperation::undoOperation() } if (!link.remove()) { setError(UserDefinedError); - setErrorString(tr("Could not remove link from %1 to %2.").arg(linkPath, targetPath)); + setErrorString(tr("Cannot remove link from \"%1\" to \"%2\".").arg( + QDir::toNativeSeparators(linkPath), QDir::toNativeSeparators(targetPath))); return false; } @@ -90,8 +89,3 @@ bool CreateLinkOperation::testOperation() { return true; } - -Operation *CreateLinkOperation::clone() const -{ - return new CreateLinkOperation(); -} diff --git a/src/libs/installer/createlinkoperation.h b/src/libs/installer/createlinkoperation.h index 8b27014bd..2e4ece9aa 100644 --- a/src/libs/installer/createlinkoperation.h +++ b/src/libs/installer/createlinkoperation.h @@ -37,13 +37,12 @@ class INSTALLER_EXPORT CreateLinkOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::CreateLinkOperation) public: - CreateLinkOperation(); + explicit CreateLinkOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; }; } diff --git a/src/libs/installer/createlocalrepositoryoperation.cpp b/src/libs/installer/createlocalrepositoryoperation.cpp index 6a78e68e3..8f6ab2eaa 100644 --- a/src/libs/installer/createlocalrepositoryoperation.cpp +++ b/src/libs/installer/createlocalrepositoryoperation.cpp @@ -34,11 +34,12 @@ #include "fileio.h" #include "fileutils.h" #include "copydirectoryoperation.h" +#include "lib7z_create.h" #include "lib7z_facade.h" #include "packagemanagercore.h" #include "productkeycheck.h" -#include "kdupdaterupdateoperations.h" +#include "updateoperations.h" #include <QtCore/QDir> #include <QtCore/QDirIterator> @@ -82,8 +83,8 @@ static void fixPermissions(const QString &repoPath) if (!QFile::setPermissions(it.filePath(), QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther)) { - throw Error(CreateLocalRepositoryOperation::tr("Could not set file permissions %1!") - .arg(it.filePath())); + throw Error(CreateLocalRepositoryOperation::tr("Cannot set permissions for file \"%1\".") + .arg(QDir::toNativeSeparators(it.filePath()))); } } } @@ -103,8 +104,8 @@ static void removeFiles(const QString &path, AutoHelper *const helper) if (fi.isSymLink() || fi.isFile()) { QFile f(fi.filePath()); if (!f.remove()) { - throw Error(CreateLocalRepositoryOperation::tr("Could not remove file %1: %2") - .arg(f.fileName(), f.errorString())); + throw Error(CreateLocalRepositoryOperation::tr("Cannot remove file \"%1\": %2") + .arg(QDir::toNativeSeparators(f.fileName()), f.errorString())); } helper->m_files.removeAll(f.fileName()); } @@ -121,8 +122,9 @@ static QString createArchive(const QString repoPath, const QString &sourceDir, c Lib7z::createArchive(&archive, QStringList() << sourceDir); removeFiles(sourceDir, helper); // cleanup the files we compressed if (!archive.rename(sourceDir + fileName)) { - throw Error(CreateLocalRepositoryOperation::tr("Could not move file %1 to %2. Error: %3") - .arg(archive.fileName(), sourceDir + fileName, archive.errorString())); + throw Error(CreateLocalRepositoryOperation::tr("Cannot move file \"%1\" to \"%2\": %3") + .arg(QDir::toNativeSeparators(archive.fileName()), + QDir::toNativeSeparators(sourceDir + fileName), archive.errorString())); } return archive.fileName(); } @@ -132,7 +134,8 @@ static QString createArchive(const QString repoPath, const QString &sourceDir, c // -- CreateLocalRepositoryOperation -CreateLocalRepositoryOperation::CreateLocalRepositoryOperation() +CreateLocalRepositoryOperation::CreateLocalRepositoryOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("CreateLocalRepository")); } @@ -146,24 +149,20 @@ bool CreateLocalRepositoryOperation::performOperation() AutoHelper helper(this); emit progressChanged(0.0); - const QStringList args = arguments(); - - if (args.count() != 2) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("exactly 2"), QLatin1String(""))); + if (!checkArgumentCount(2)) return false; - } try { + const QStringList args = arguments(); + const QString binaryPath = QFileInfo(args.at(0)).absoluteFilePath(); // Note the "/" at the end, important to make copy directory operation behave well const QString repoPath = QFileInfo(args.at(1)).absoluteFilePath() + QLatin1Char('/'); // check if this is an offline version, otherwise there will be no binary data - PackageManagerCore *const core = value(QLatin1String("installer")).value<PackageManagerCore*>(); + PackageManagerCore *const core = packageManager(); if (core && !core->isOfflineOnly()) { - throw QInstaller::Error(tr("Installer needs to be an offline version: %1.") + throw QInstaller::Error(tr("Installer at \"%1\" needs to be an offline one.") .arg(QDir::toNativeSeparators(binaryPath))); } @@ -185,9 +184,10 @@ bool CreateLocalRepositoryOperation::performOperation() setValue(QLatin1String("createddir"), mkDirOp.value(QLatin1String("createddir"))); // copy the whole meta data into local repository - CopyDirectoryOperation copyDirOp; + CopyDirectoryOperation copyDirOp(core); copyDirOp.setArguments(QStringList() << QLatin1String(":/metadata/") << repoPath); - connect(©DirOp, SIGNAL(outputTextChanged(QString)), this, SIGNAL(outputTextChanged(QString))); + connect(©DirOp, &CopyDirectoryOperation::outputTextChanged, + this, &CreateLocalRepositoryOperation::outputTextChanged); const bool success = copyDirOp.performOperation(); helper.m_files = copyDirOp.value(QLatin1String("files")).toStringList(); @@ -206,13 +206,15 @@ bool CreateLocalRepositoryOperation::performOperation() // open the updates xml file we previously copied QFile updatesXml(repoPath + QLatin1String("Updates.xml")); if (!updatesXml.exists() || !updatesXml.open(QIODevice::ReadOnly)) - throw QInstaller::Error(tr("Could not open file: %1").arg(updatesXml.fileName())); + throw QInstaller::Error(tr("Cannot open file \"%1\" for reading.").arg( + QDir::toNativeSeparators(updatesXml.fileName()))); // read the content of the updates xml QString error; QDomDocument doc; if (!doc.setContent(&updatesXml, &error)) - throw QInstaller::Error(tr("Could not read: %1. Error: %2").arg(updatesXml.fileName(), error)); + throw QInstaller::Error(tr("Cannot read file \"%1\": %2").arg( + QDir::toNativeSeparators(updatesXml.fileName()), error)); // build for each available package a name - version mapping QHash<QString, QString> nameVersionHash; @@ -242,8 +244,9 @@ bool CreateLocalRepositoryOperation::performOperation() QFile file(binaryPath); if (!file.open(QIODevice::ReadOnly)) { - throw QInstaller::Error(tr("Could not open file: %1. Error: %2").arg(file.fileName(), - file.errorString())); + throw QInstaller::Error(tr("Cannot open file \"%1\" for reading: %2").arg( + QDir::toNativeSeparators(file.fileName()), + file.errorString())); } // start to read the binary layout @@ -260,8 +263,8 @@ bool CreateLocalRepositoryOperation::performOperation() for (int i = 0; i < names.count(); ++i) { const QString name = names.at(i); if (!repo.mkpath(name)) { - throw QInstaller::Error(tr("Could not create target dir: %1.") - .arg(repo.filePath(name))); + throw QInstaller::Error(tr("Cannot create target directory: \"%1\".") + .arg(QDir::toNativeSeparators(repo.filePath(name)))); } // zip the meta files that come with the offline installer helper.m_files.prepend(Static::createArchive(repoPath, @@ -329,10 +332,10 @@ bool CreateLocalRepositoryOperation::undoOperation() QDir dir; const QStringList files = value(QLatin1String("files")).toStringList(); foreach (const QString &file, files) { - emit outputTextChanged(tr("Removing file: %0").arg(file)); + emit outputTextChanged(tr("Removing file \"%1\".").arg(QDir::toNativeSeparators(file))); if (!QFile::remove(file)) { setError(InvalidArguments); - setErrorString(tr("Could not remove %0.").arg(file)); + setErrorString(tr("Cannot remove file \"%1\".").arg(QDir::toNativeSeparators(file))); return false; } dir.rmpath(QFileInfo(file).absolutePath()); @@ -351,12 +354,12 @@ bool CreateLocalRepositoryOperation::undoOperation() #if defined(Q_OS_WIN) && !defined(Q_CC_MINGW) char msg[128]; if (strerror_s(msg, sizeof msg, errno) != 0) { - setError(UserDefinedError, tr("Cannot remove directory %1: %2").arg(createdDir.path(), - QString::fromLocal8Bit(msg))); + setError(UserDefinedError, tr("Cannot remove directory \"%1\": %2").arg( + QDir::toNativeSeparators(createdDir.path()), QString::fromLocal8Bit(msg))); } #else - setError(UserDefinedError, tr("Cannot remove directory %1: %2").arg(createdDir.path(), - QString::fromLocal8Bit(strerror(errno)))); + setError(UserDefinedError, tr("Cannot remove directory \"%1\": %2").arg( + QDir::toNativeSeparators(createdDir.path()), QString::fromLocal8Bit(strerror(errno)))); #endif } setValue(QLatin1String("files"), QStringList()); @@ -369,11 +372,6 @@ bool CreateLocalRepositoryOperation::testOperation() return true; } -Operation *CreateLocalRepositoryOperation::clone() const -{ - return new CreateLocalRepositoryOperation(); -} - void CreateLocalRepositoryOperation::emitFullProgress() { emit progressChanged(1.0); diff --git a/src/libs/installer/createlocalrepositoryoperation.h b/src/libs/installer/createlocalrepositoryoperation.h index c89fd2ca6..275d7a409 100644 --- a/src/libs/installer/createlocalrepositoryoperation.h +++ b/src/libs/installer/createlocalrepositoryoperation.h @@ -41,13 +41,12 @@ class INSTALLER_EXPORT CreateLocalRepositoryOperation : public QObject, public O friend struct AutoHelper; public: - CreateLocalRepositoryOperation(); + explicit CreateLocalRepositoryOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; signals: void progressChanged(double progress); diff --git a/src/libs/installer/createshortcutoperation.cpp b/src/libs/installer/createshortcutoperation.cpp index 754ba317e..2c2e96eff 100644 --- a/src/libs/installer/createshortcutoperation.cpp +++ b/src/libs/installer/createshortcutoperation.cpp @@ -41,6 +41,7 @@ using namespace QInstaller; #ifdef Q_OS_WIN #include <qt_windows.h> #include <shlobj.h> +#include <Intshcut.h> #ifndef PIDLIST_ABSOLUTE typedef ITEMIDLIST *PIDLIST_ABSOLUTE; @@ -93,39 +94,62 @@ static QString takeArgument(const QString argument, QStringList *arguments) static bool createLink(const QString &fileName, const QString &linkName, QString workingDir, const QString &arguments = QString(), const QString &iconPath = QString(), - const QString &iconId = QString()) + const QString &iconId = QString(), const QString &description = QString()) { #ifdef Q_OS_WIN - bool success = QFile::link(fileName, linkName); + // CoInitialize cleanup object + DeCoInitializer _; - if (!success) - return success; + IUnknown *iunkn = NULL; - if (workingDir.isEmpty()) - workingDir = QFileInfo(fileName).absolutePath(); - workingDir = QDir::toNativeSeparators(workingDir); + if (fileName.toLower().startsWith(QLatin1String("http:")) + || fileName.toLower().startsWith(QLatin1String("ftp:"))) { + IUniformResourceLocator *iurl = NULL; + if (FAILED(CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, + IID_IUniformResourceLocator, (LPVOID*)&iurl))) { + return false; + } - // CoInitialize cleanup object - DeCoInitializer _; + if (FAILED(iurl->SetURL((wchar_t *)fileName.utf16(), IURL_SETURL_FL_GUESS_PROTOCOL))) { + iurl->Release(); + return false; + } + iunkn = iurl; + } else { + bool success = QFile::link(fileName, linkName); + + if (!success) { + return success; + } + + if (workingDir.isEmpty()) + workingDir = QFileInfo(fileName).absolutePath(); + workingDir = QDir::toNativeSeparators(workingDir); - IShellLink *psl = NULL; - if (FAILED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl))) - return success; + IShellLink *psl = NULL; + if (FAILED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + IID_IShellLink, (LPVOID*)&psl))) { + return success; + } - // TODO: implement this server side, since there's not Qt equivalent to set working dir and arguments - psl->SetPath((wchar_t *)QDir::toNativeSeparators(fileName).utf16()); - psl->SetWorkingDirectory((wchar_t *)workingDir.utf16()); - if (!arguments.isNull()) - psl->SetArguments((wchar_t*)arguments.utf16()); - if (!iconPath.isNull()) - psl->SetIconLocation((wchar_t*)(iconPath.utf16()), iconId.toInt()); + // TODO: implement this server side, since there's not Qt equivalent to set working dir and arguments + psl->SetPath((wchar_t *)QDir::toNativeSeparators(fileName).utf16()); + psl->SetWorkingDirectory((wchar_t *)workingDir.utf16()); + if (!arguments.isNull()) + psl->SetArguments((wchar_t*)arguments.utf16()); + if (!iconPath.isNull()) + psl->SetIconLocation((wchar_t*)(iconPath.utf16()), iconId.toInt()); + if (!description.isNull()) + psl->SetDescription((wchar_t*)(description.utf16())); + iunkn = psl; + } IPersistFile *ppf = NULL; - if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, (void **)&ppf))) { + if (SUCCEEDED(iunkn->QueryInterface(IID_IPersistFile, (void **)&ppf))) { ppf->Save((wchar_t*)QDir::toNativeSeparators(linkName).utf16(), true); ppf->Release(); } - psl->Release(); + iunkn->Release(); PIDLIST_ABSOLUTE pidl; // Force start menu cache update if (SUCCEEDED(SHGetFolderLocation(0, CSIDL_STARTMENU, 0, 0, &pidl))) { @@ -137,7 +161,7 @@ static bool createLink(const QString &fileName, const QString &linkName, QString CoTaskMemFree(pidl); } - return success; + return true; #else Q_UNUSED(arguments) Q_UNUSED(workingDir) @@ -152,13 +176,17 @@ static bool createLink(const QString &fileName, const QString &linkName, QString // -- CreateShortcutOperation -CreateShortcutOperation::CreateShortcutOperation() +CreateShortcutOperation::CreateShortcutOperation(PackageManagerCore *core) + : UpdateOperation(core) + , m_optionalArgumentsRead(false) { setName(QLatin1String("CreateShortcut")); } void CreateShortcutOperation::backup() { + ensureOptionalArgumentsRead(); + QDir linkPath(QFileInfo(arguments().at(1)).absolutePath()); QStringList directoriesToCreate; @@ -171,25 +199,38 @@ void CreateShortcutOperation::backup() setValue(QLatin1String("createddirs"), directoriesToCreate); } -bool CreateShortcutOperation::performOperation() +void CreateShortcutOperation::ensureOptionalArgumentsRead() { + if (m_optionalArgumentsRead) + return; + + m_optionalArgumentsRead = true; + QStringList args = arguments(); - const QString iconId = takeArgument(QString::fromLatin1("iconId="), &args); - const QString iconPath = takeArgument(QString::fromLatin1("iconPath="), &args); - const QString workingDir = takeArgument(QString::fromLatin1("workingDirectory="), &args); + m_iconId = takeArgument(QString::fromLatin1("iconId="), &args); + m_iconPath = takeArgument(QString::fromLatin1("iconPath="), &args); + m_workingDir = takeArgument(QString::fromLatin1("workingDirectory="), &args); + m_description = takeArgument(QString::fromLatin1("description="), &args); + + setArguments(args); +} + +bool CreateShortcutOperation::performOperation() +{ + ensureOptionalArgumentsRead(); - if (args.count() != 2 && args.count() != 3) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("2 or 3"), - tr(" (optional: 'workingDirectory=...', 'iconPath=...', 'iconId=...')"))); + if (!checkArgumentCount(2, 3, tr("<target> <link location> [target arguments] " + "[\"workingDirectory=...\"] [\"iconPath=...\"] [\"iconId=...\"] " + "[\"description=...\"]"))) { return false; } + QStringList args = arguments(); + const QString linkTarget = args.at(0); const QString linkLocation = args.at(1); - const QString targetArguments = args.value(2); //used value because it could be not existing + const QString targetArguments = args.value(2); // value() used since it's optional const QString linkPath = QFileInfo(linkLocation).absolutePath().trimmed(); const bool created = QDir(linkPath).exists() || QDir::root().mkpath(linkPath); @@ -199,11 +240,11 @@ bool CreateShortcutOperation::performOperation() #if defined(Q_OS_WIN) && !defined(Q_CC_MINGW) char msg[128]; if (strerror_s(msg, sizeof msg, errno) != 0) { - setErrorString(tr("Could not create folder %1: %2.").arg(QDir::toNativeSeparators(linkPath), + setErrorString(tr("Cannot create directory \"%1\": %2").arg(QDir::toNativeSeparators(linkPath), QString::fromLocal8Bit(msg))); } #else - setErrorString(tr("Could not create folder %1: %2.").arg(QDir::toNativeSeparators(linkPath), + setErrorString(tr("Cannot create directory \"%1\": %2").arg(QDir::toNativeSeparators(linkPath), QString::fromLocal8Bit(strerror(errno)))); #endif return false; @@ -213,15 +254,16 @@ bool CreateShortcutOperation::performOperation() QString errorString; if (QFile::exists(linkLocation) && !deleteFileNowOrLater(linkLocation, &errorString)) { setError(UserDefinedError); - setErrorString(tr("Failed to overwrite %1: %2").arg(QDir::toNativeSeparators(linkLocation), + setErrorString(tr("Failed to overwrite \"%1\": %2").arg(QDir::toNativeSeparators(linkLocation), errorString)); return false; } - const bool linked = createLink(linkTarget, linkLocation, workingDir, targetArguments, iconPath, iconId); + const bool linked = createLink(linkTarget, linkLocation, m_workingDir, targetArguments, m_iconPath, m_iconId, + m_description); if (!linked) { setError(UserDefinedError); - setErrorString(tr("Could not create link %1: %2").arg(QDir::toNativeSeparators(linkLocation), + setErrorString(tr("Cannot create link \"%1\": %2").arg(QDir::toNativeSeparators(linkLocation), qt_error_string())); return false; } @@ -230,6 +272,8 @@ bool CreateShortcutOperation::performOperation() bool CreateShortcutOperation::undoOperation() { + ensureOptionalArgumentsRead(); + const QString &linkLocation = arguments().at(1); if (!deleteFileNowOrLater(linkLocation) ) qDebug() << "Cannot delete:" << linkLocation; @@ -271,8 +315,3 @@ bool CreateShortcutOperation::testOperation() { return true; } - -Operation *CreateShortcutOperation::clone() const -{ - return new CreateShortcutOperation(); -} diff --git a/src/libs/installer/createshortcutoperation.h b/src/libs/installer/createshortcutoperation.h index 6cd5dc064..dc2ac6ddb 100644 --- a/src/libs/installer/createshortcutoperation.h +++ b/src/libs/installer/createshortcutoperation.h @@ -37,13 +37,21 @@ class INSTALLER_EXPORT CreateShortcutOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::CreateShortcutOperation) public: - CreateShortcutOperation(); + explicit CreateShortcutOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; + +private: + void ensureOptionalArgumentsRead(); + + bool m_optionalArgumentsRead; + QString m_iconId; + QString m_iconPath; + QString m_workingDir; + QString m_description; }; } diff --git a/src/libs/installer/downloadarchivesjob.cpp b/src/libs/installer/downloadarchivesjob.cpp index 4f153219e..8fd8a40a9 100644 --- a/src/libs/installer/downloadarchivesjob.cpp +++ b/src/libs/installer/downloadarchivesjob.cpp @@ -33,8 +33,8 @@ #include "packagemanagercore.h" #include "utils.h" -#include "kdupdaterfiledownloader.h" -#include "kdupdaterfiledownloaderfactory.h" +#include "filedownloader.h" +#include "filedownloaderfactory.h" #include <QtCore/QFile> #include <QtCore/QTimerEvent> @@ -47,7 +47,7 @@ using namespace KDUpdater; Creates a new DownloadArchivesJob with \a parent. */ DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core) - : KDJob(core) + : Job(core) , m_core(core) , m_downloader(0) , m_archivesDownloaded(0) @@ -120,8 +120,8 @@ void DownloadArchivesJob::fetchNextArchiveHash() return; } - connect(m_downloader, SIGNAL(downloadCompleted()), this, SLOT(finishedHashDownload()), - Qt::QueuedConnection); + connect(m_downloader, &FileDownloader::downloadCompleted, + this, &DownloadArchivesJob::finishedHashDownload, Qt::QueuedConnection); m_downloader->download(); } else { QMetaObject::invokeMethod(this, "fetchNextArchive", Qt::QueuedConnection); @@ -159,7 +159,7 @@ void DownloadArchivesJob::fetchNextArchive() if (m_downloader != 0) m_downloader->deleteLater(); - m_downloader = setupDownloader(QString(), m_core->value(QLatin1String("UrlQueryString"))); + m_downloader = setupDownloader(QString(), m_core->value(scUrlQueryString)); if (!m_downloader) { m_archivesToDownload.removeFirst(); QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection); @@ -168,7 +168,8 @@ void DownloadArchivesJob::fetchNextArchive() emit progressChanged(double(m_archivesDownloaded) / m_archivesToDownloadCount); connect(m_downloader, SIGNAL(downloadProgress(double)), this, SLOT(emitDownloadProgress(double))); - connect(m_downloader, SIGNAL(downloadCompleted()), this, SLOT(registerFile()), Qt::QueuedConnection); + connect(m_downloader, &FileDownloader::downloadCompleted, + this, &DownloadArchivesJob::registerFile, Qt::QueuedConnection); m_downloader->download(); } @@ -215,7 +216,7 @@ void DownloadArchivesJob::registerFile() QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel); if (res == QMessageBox::Cancel) { - finishWithError(tr("Could not verify Hash")); + finishWithError(tr("Cannot verify Hash")); return; } } else { @@ -235,7 +236,7 @@ void DownloadArchivesJob::registerFile() void DownloadArchivesJob::downloadCanceled() { - emitFinishedWithError(KDJob::Canceled, m_downloader->errorString()); + emitFinishedWithError(Job::Canceled, m_downloader->errorString()); } void DownloadArchivesJob::downloadFailed(const QString &error) @@ -245,7 +246,7 @@ void DownloadArchivesJob::downloadFailed(const QString &error) const QMessageBox::StandardButton b = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), - QLatin1String("archiveDownloadError"), tr("Download Error"), tr("Could not download archive: %1 : %2") + QLatin1String("archiveDownloadError"), tr("Download Error"), tr("Cannot download archive %1: %2") .arg(m_archivesToDownload.first().second, error), QMessageBox::Retry | QMessageBox::Cancel); if (b == QMessageBox::Retry) @@ -257,7 +258,7 @@ void DownloadArchivesJob::downloadFailed(const QString &error) void DownloadArchivesJob::finishWithError(const QString &error) { const FileDownloader *const dl = qobject_cast<const FileDownloader*> (sender()); - const QString msg = tr("Could not fetch archives: %1\nError while loading %2"); + const QString msg = tr("Cannot fetch archives: %1\nError while loading %2"); if (dl != 0) emitFinishedWithError(QInstaller::DownloadError, msg.arg(error, dl->url().toString())); else @@ -286,24 +287,23 @@ KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &s auth.setPassword(component->value(QLatin1String("password"))); downloader->setAuthenticator(auth); - connect(downloader, SIGNAL(downloadCanceled()), this, SLOT(downloadCanceled())); - connect(downloader, SIGNAL(downloadAborted(QString)), this, SLOT(downloadFailed(QString)), + connect(downloader, &FileDownloader::downloadCanceled, this, &DownloadArchivesJob::downloadCanceled); + connect(downloader, &FileDownloader::downloadAborted, this, &DownloadArchivesJob::downloadFailed, Qt::QueuedConnection); - connect(downloader, SIGNAL(downloadStatus(QString)), this, SIGNAL(downloadStatusChanged(QString))); + connect(downloader, &FileDownloader::downloadStatus, this, &DownloadArchivesJob::downloadStatusChanged); if (FileDownloaderFactory::isSupportedScheme(scheme)) { downloader->setDownloadedFileName(component->localTempPath() + QLatin1Char('/') + component->name() + QLatin1Char('/') + fi.fileName() + suffix); } - emit outputTextChanged(tr("Downloading archive '%1' for component: %2") + emit outputTextChanged(tr("Downloading archive \"%1\" for component %2.") .arg(fi.fileName() + suffix, component->displayName())); } else { - emit outputTextChanged(tr("Scheme not supported: %1 (%2)").arg(scheme, url.toString())); + emit outputTextChanged(tr("Scheme %1 not supported (URL: %2).").arg(scheme, url.toString())); } } else { - emit outputTextChanged(tr("Could not find component for: %1.").arg(QFileInfo(fi.path()) - .fileName())); + emit outputTextChanged(tr("Cannot find component for %1.").arg(QFileInfo(fi.path()).fileName())); } return downloader; } diff --git a/src/libs/installer/downloadarchivesjob.h b/src/libs/installer/downloadarchivesjob.h index 35c62b3a2..8f2392064 100644 --- a/src/libs/installer/downloadarchivesjob.h +++ b/src/libs/installer/downloadarchivesjob.h @@ -29,7 +29,7 @@ #ifndef DOWNLOADARCHIVESJOB_H #define DOWNLOADARCHIVESJOB_H -#include <kdjob.h> +#include "job.h" #include <QtCore/QPair> @@ -46,7 +46,7 @@ namespace QInstaller { class MessageBoxHandler; class PackageManagerCore; -class DownloadArchivesJob : public KDJob +class DownloadArchivesJob : public Job { Q_OBJECT diff --git a/src/libs/installer/downloadfiletask.cpp b/src/libs/installer/downloadfiletask.cpp index b9b560383..2c99e04e1 100644 --- a/src/libs/installer/downloadfiletask.cpp +++ b/src/libs/installer/downloadfiletask.cpp @@ -31,12 +31,12 @@ #include "downloadfiletask_p.h" #include <QCoreApplication> +#include <QDir> #include <QEventLoop> #include <QFileInfo> #include <QNetworkProxyFactory> #include <QSslError> #include <QTemporaryFile> -#include <QTimer> namespace QInstaller { @@ -49,7 +49,8 @@ AuthenticationRequiredException::AuthenticationRequiredException(Type type, cons Downloader::Downloader() : m_finished(0) { - connect(&m_nam, SIGNAL(finished(QNetworkReply*)), SLOT(onFinished(QNetworkReply*))); + connect(&m_timer, &QTimer::timeout, this, &Downloader::onTimeout); + connect(&m_nam, &QNetworkAccessManager::finished, this, &Downloader::onFinished); } Downloader::~Downloader() @@ -72,15 +73,17 @@ void Downloader::download(QFutureInterface<FileTaskResult> &fi, const QList<File fi.setExpectedResultCount(items.count()); m_nam.setProxyFactory(networkProxyFactory); - connect(&m_nam, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, - SLOT(onAuthenticationRequired(QNetworkReply*,QAuthenticator*))); - connect(&m_nam, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), this, - SLOT(onProxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); - QTimer::singleShot(0, this, SLOT(doDownload())); + connect(&m_nam, &QNetworkAccessManager::authenticationRequired, this, + &Downloader::onAuthenticationRequired); + connect(&m_nam, &QNetworkAccessManager::proxyAuthenticationRequired, this, + &Downloader::onProxyAuthenticationRequired); + QTimer::singleShot(0, this, &Downloader::doDownload); } void Downloader::doDownload() { + m_timer.start(1000); // Use a timer to check for canceled downloads. + foreach (const FileTaskItem &item, m_items) { if (!startDownload(item)) break; @@ -120,15 +123,17 @@ void Downloader::onReadyRead() } if (file->exists() && (!QFileInfo(file->fileName()).isFile())) { - m_futureInterface->reportException(TaskException(tr("Target file '%1' already exists " + m_futureInterface->reportException(TaskException(tr("Target file \"%1\" already exists " "but is not a file.").arg(file->fileName()))); return; } if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate)) { //: %2 is a sentence describing the error - m_futureInterface->reportException(TaskException(tr("Could not open target '%1' for " - "write. Error: %2.").arg(file->fileName(), file->errorString()))); + m_futureInterface->reportException( + TaskException(tr("Cannot open file \"%1\" for writing: %2").arg( + QDir::toNativeSeparators(file->fileName()), + file->errorString()))); return; } data.file = std::move(file); @@ -137,8 +142,9 @@ void Downloader::onReadyRead() if (!data.file->isOpen()) { //: %2 is a sentence describing the error. m_futureInterface->reportException( - TaskException(tr("Target '%1' not open for write. Error: %2.").arg( - data.file->fileName(), data.file->errorString()))); + TaskException(tr("File \"%1\" not open for writing: %2").arg( + QDir::toNativeSeparators(data.file->fileName()), + data.file->errorString()))); return; } @@ -156,8 +162,9 @@ void Downloader::onReadyRead() if (toWrite < 0) { //: %2 is a sentence describing the error. m_futureInterface->reportException( - TaskException(tr("Writing to target '%1' failed. Error: %2.").arg( - data.file->fileName(), data.file->errorString()))); + TaskException(tr("Writing to file \"%1\" failed: %2").arg( + QDir::toNativeSeparators(data.file->fileName()), + data.file->errorString()))); return; } written += toWrite; @@ -203,7 +210,7 @@ void Downloader::onFinished(QNetworkReply *reply) reply->deleteLater(); return; } else { - m_futureInterface->reportException(TaskException(tr("Redirect loop detected '%1'.") + m_futureInterface->reportException(TaskException(tr("Redirect loop detected for \"%1\".") .arg(url.toString()))); return; } @@ -220,7 +227,7 @@ void Downloader::onFinished(QNetworkReply *reply) const QByteArray expectedCheckSum = data.taskItem.value(TaskRole::Checksum).toByteArray(); if (!expectedCheckSum.isEmpty()) { if (expectedCheckSum != data.observer->checkSum().toHex()) { - m_futureInterface->reportException(TaskException(tr("Checksum mismatch detected '%1'.") + m_futureInterface->reportException(TaskException(tr("Checksum mismatch detected for \"%1\".") .arg(reply->url().toString()))); } } @@ -262,7 +269,7 @@ void Downloader::onError(QNetworkReply::NetworkError error) } else { //: %1 is a sentence describing the error m_futureInterface->reportException( - TaskException(tr("Unknown network error while downloading: %1.").arg(error))); + TaskException(tr("Unknown network error while downloading \"%1\".").arg(error))); } } @@ -272,7 +279,7 @@ void Downloader::onSslErrors(const QList<QSslError> &sslErrors) Q_UNUSED(sslErrors); #else foreach (const QSslError &error, sslErrors) - qDebug() << QString::fromLatin1("SSL error: %s").arg(error.errorString()); + qDebug() << "SSL error:" << error.errorString(); #endif } @@ -319,6 +326,28 @@ void Downloader::onProxyAuthenticationRequired(const QNetworkProxy &proxy, QAuth } +/*! + \internal + + Canceling from the outside will not get noticed if we are waiting on a connection that + does not create any events. QNam will drop after 45 seconds, though the user might have + canceled the download before. In that case we block until the QNam timeout is reached, + worst case resulting in deadlock while the application is shutting down at the same time. +*/ +void Downloader::onTimeout() +{ + if (testCanceled()) { + // Inject exception, we can't use QFuturInterface::reportException() as the exception + // store is "frozen" once cancel was called. On the other hand, client code could use + // QFutureWatcherBase::isCanceled() or QFuture::isCanceled() to check for canceled futures. + m_futureInterface->exceptionStore() + .setException(TaskException(tr("Network transfers canceled."))); + m_futureInterface->reportFinished(); + emit finished(); + } +} + + // -- private bool Downloader::testCanceled() @@ -337,7 +366,7 @@ QNetworkReply *Downloader::startDownload(const FileTaskItem &item) QUrl const source = item.source(); if (!source.isValid()) { //: %2 is a sentence describing the error - m_futureInterface->reportException(TaskException(tr("Invalid source '%1'. Error: %2.") + m_futureInterface->reportException(TaskException(tr("Invalid source URL \"%1\": %2") .arg(source.toString(), source.errorString()))); return 0; } @@ -346,14 +375,13 @@ QNetworkReply *Downloader::startDownload(const FileTaskItem &item) std::unique_ptr<Data> data(new Data(item)); m_downloads[reply] = std::move(data); - connect(reply, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + connect(reply, &QIODevice::readyRead, this, &Downloader::onReadyRead); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError))); #ifndef QT_NO_SSL - connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(onSslErrors(QList<QSslError>))); + connect(reply, &QNetworkReply::sslErrors, this, &Downloader::onSslErrors); #endif - connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onDownloadProgress(qint64, - qint64))); + connect(reply, &QNetworkReply::downloadProgress, this, &Downloader::onDownloadProgress); return reply; } @@ -400,7 +428,7 @@ void DownloadFileTask::doTask(QFutureInterface<FileTaskResult> &fi) { QEventLoop el; Downloader downloader; - connect(&downloader, SIGNAL(finished()), &el, SLOT(quit())); + connect(&downloader, &Downloader::finished, &el, &QEventLoop::quit); QList<FileTaskItem> items = taskItems(); if (!m_authenticator.isNull()) { diff --git a/src/libs/installer/downloadfiletask.h b/src/libs/installer/downloadfiletask.h index a682321f1..21908549d 100644 --- a/src/libs/installer/downloadfiletask.h +++ b/src/libs/installer/downloadfiletask.h @@ -30,7 +30,7 @@ #define DOWNLOADFILETASK_H #include "abstractfiletask.h" -#include "kdupdaterfiledownloaderfactory.h" +#include "filedownloaderfactory.h" #include <QAuthenticator> Q_DECLARE_METATYPE(QAuthenticator) diff --git a/src/libs/installer/downloadfiletask_p.h b/src/libs/installer/downloadfiletask_p.h index 0e44e2aa3..3dfce27b4 100644 --- a/src/libs/installer/downloadfiletask_p.h +++ b/src/libs/installer/downloadfiletask_p.h @@ -36,6 +36,7 @@ #include <QNetworkAccessManager> #include <QNetworkReply> #include <QNetworkRequest> +#include <QTimer> #include <memory> #include <unordered_map> @@ -90,7 +91,7 @@ private slots: void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); void onProxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator); - + void onTimeout(); private: bool testCanceled(); @@ -99,6 +100,7 @@ private: private: QFutureInterface<FileTaskResult> *m_futureInterface; + QTimer m_timer; int m_finished; QNetworkAccessManager m_nam; QList<FileTaskItem> m_items; diff --git a/src/libs/installer/elevatedexecuteoperation.cpp b/src/libs/installer/elevatedexecuteoperation.cpp index 1241fc567..2cc988a4d 100644 --- a/src/libs/installer/elevatedexecuteoperation.cpp +++ b/src/libs/installer/elevatedexecuteoperation.cpp @@ -59,8 +59,9 @@ public: bool showStandardError; }; -ElevatedExecuteOperation::ElevatedExecuteOperation() - : d(new Private(this)) +ElevatedExecuteOperation::ElevatedExecuteOperation(PackageManagerCore *core) + : UpdateOperation(core) + , d(new Private(this)) { // this operation has to "overwrite" the Execute operation from KDUpdater setName(QLatin1String("Execute")); @@ -75,12 +76,9 @@ bool ElevatedExecuteOperation::performOperation() { // This operation receives only one argument. It is the complete // command line of the external program to execute. - if (arguments().isEmpty()) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("at least 1"), QLatin1String(""))); + if (!checkArgumentCount(1, INT_MAX)) return false; - } + QStringList args; foreach (const QString &argument, arguments()) { if (argument!=QLatin1String("UNDOEXECUTE")) @@ -140,7 +138,7 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments) const bool success = QProcessWrapper::startDetached(args.front(), args.mid(1)); if (!success) { q->setError(UserDefinedError); - q->setErrorString(tr("Execution failed: Could not start detached: \"%1\"").arg(callstr)); + q->setErrorString(tr("Cannot start detached: \"%1\"").arg(callstr)); } return success; } @@ -159,12 +157,12 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments) if (showStandardError) process->setProcessChannelMode(QProcessWrapper::MergedChannels); - connect(q, SIGNAL(cancelProcess()), process, SLOT(cancel())); + connect(q, &ElevatedExecuteOperation::cancelProcess, process, &QProcessWrapper::cancel); //we still like the none blocking possibility to perform this operation without threads QEventLoop loop; if (QThread::currentThread() == qApp->thread()) { - QObject::connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit())); + QObject::connect(process, &QProcessWrapper::finished, &loop, &QEventLoop::quit); } //readProcessOutput should only called from this current Thread -> Qt::DirectConnection QObject::connect(process, SIGNAL(readyRead()), q, SLOT(readProcessOutput()), Qt::DirectConnection); @@ -183,7 +181,7 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments) if (!success) { q->setError(UserDefinedError); //TODO: pass errorString() through the wrapper */ - q->setErrorString(tr("Execution failed: Could not start: \"%1\"(%2)").arg(callstr, + q->setErrorString(tr("Cannot start: \"%1\": %2").arg(callstr, process->errorString())); returnValue = false; } @@ -199,14 +197,14 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments) if (process->exitStatus() == QProcessWrapper::CrashExit) { q->setError(UserDefinedError); - q->setErrorString(tr("Execution failed(Crash): \"%1\"").arg(callstr)); + q->setErrorString(tr("Program crashed: \"%1\"").arg(callstr)); returnValue = false; } if (!allowedExitCodes.contains(process->exitCode())) { q->setError(UserDefinedError); if (customErrorMessage.isEmpty()) { - q->setErrorString(tr("Execution failed(Unexpected exit code: %1): \"%2\"") + q->setErrorString(tr("Execution failed (Unexpected exit code: %1): \"%2\"") .arg(QString::number(process->exitCode()), callstr)); } else { q->setErrorString(customErrorMessage); @@ -215,13 +213,14 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments) QByteArray standardErrorOutput = process->readAllStandardError(); // in error case it would be useful to see something in verbose output if (!standardErrorOutput.isEmpty()) - qWarning() << standardErrorOutput; + qWarning().noquote() << standardErrorOutput; returnValue = false; } Q_ASSERT(process); - process->deleteLater(); + Q_ASSERT(process->state() == QProcessWrapper::NotRunning); + delete process; process = 0; return returnValue; @@ -276,11 +275,6 @@ bool ElevatedExecuteOperation::testOperation() return true; } -Operation *ElevatedExecuteOperation::clone() const -{ - return new ElevatedExecuteOperation; -} - void ElevatedExecuteOperation::backup() { } diff --git a/src/libs/installer/elevatedexecuteoperation.h b/src/libs/installer/elevatedexecuteoperation.h index fc9b42c66..5585e77a8 100644 --- a/src/libs/installer/elevatedexecuteoperation.h +++ b/src/libs/installer/elevatedexecuteoperation.h @@ -38,14 +38,13 @@ class INSTALLER_EXPORT ElevatedExecuteOperation : public QObject, public Operati Q_OBJECT public: - ElevatedExecuteOperation(); + explicit ElevatedExecuteOperation(PackageManagerCore *core); ~ElevatedExecuteOperation(); - - virtual void backup(); - virtual bool performOperation(); - virtual bool undoOperation(); - virtual bool testOperation(); - virtual Operation *clone() const; + + void backup() Q_DECL_OVERRIDE; + bool performOperation() Q_DECL_OVERRIDE; + bool undoOperation() Q_DECL_OVERRIDE; + bool testOperation() Q_DECL_OVERRIDE; Q_SIGNALS: void cancelProcess(); diff --git a/src/libs/installer/environmentvariablesoperation.cpp b/src/libs/installer/environmentvariablesoperation.cpp index 00444f870..b80951d2b 100644 --- a/src/libs/installer/environmentvariablesoperation.cpp +++ b/src/libs/installer/environmentvariablesoperation.cpp @@ -40,7 +40,8 @@ using namespace QInstaller; using namespace KDUpdater; -EnvironmentVariableOperation::EnvironmentVariableOperation() +EnvironmentVariableOperation::EnvironmentVariableOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("EnvironmentVariable")); } @@ -74,7 +75,7 @@ UpdateOperation::Error writeSetting(const QString ®Path, oldValue->clear(); SettingsType registry(regPath, QSettingsWrapper::NativeFormat); if (!registry.isWritable()) { - *errorString = UpdateOperation::tr("Registry path %1 is not writable").arg(regPath); + *errorString = UpdateOperation::tr("Registry path %1 is not writable.").arg(regPath); return UpdateOperation::UserDefinedError; } @@ -86,7 +87,7 @@ UpdateOperation::Error writeSetting(const QString ®Path, registry.sync(); if (registry.status() != QSettingsWrapper::NoError) { - *errorString = UpdateOperation::tr("Could not write to registry path %1").arg(regPath); + *errorString = UpdateOperation::tr("Cannot write to registry path %1.").arg(regPath); return UpdateOperation::UserDefinedError; } @@ -115,21 +116,16 @@ UpdateOperation::Error undoSetting(const QString ®Path, bool EnvironmentVariableOperation::performOperation() { - QStringList args = arguments(); - if (args.count() < 2 || args.count() > 4) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("2 to 4"), QLatin1String(""))); + if (!checkArgumentCount(2, 4)) return false; - } - const QString name = arguments().at(0); - const QString value = arguments().at(1); - bool isPersistent = false; + const QStringList args = arguments(); + const QString name = args.at(0); + const QString value = args.at(1); #ifdef Q_OS_WIN - isPersistent = arguments().count() >= 3 ? arguments().at(2) == QLatin1String("true") : true; - const bool isSystemWide = arguments().count() >= 4 ? arguments().at(3) == QLatin1String("true") : false; + const bool isPersistent = arguments().count() > 2 ? arguments().at(2) == QLatin1String("true") : true; + const bool isSystemWide = arguments().count() > 3 ? arguments().at(3) == QLatin1String("true") : false; QString oldvalue; if (isPersistent) { const QString regPath = isSystemWide ? QLatin1String("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet" @@ -152,9 +148,8 @@ bool EnvironmentVariableOperation::performOperation() setValue(QLatin1String("oldvalue"), oldvalue); return true; } -#endif Q_ASSERT(!isPersistent); - Q_UNUSED(isPersistent) +#endif setValue(QLatin1String("oldvalue"), Environment::instance().value(name)); Environment::instance().setTemporaryValue(name, value); @@ -209,8 +204,3 @@ bool EnvironmentVariableOperation::testOperation() { return true; } - -Operation *EnvironmentVariableOperation::clone() const -{ - return new EnvironmentVariableOperation(); -} diff --git a/src/libs/installer/environmentvariablesoperation.h b/src/libs/installer/environmentvariablesoperation.h index bda0dd2cd..3c5252bc6 100644 --- a/src/libs/installer/environmentvariablesoperation.h +++ b/src/libs/installer/environmentvariablesoperation.h @@ -37,13 +37,12 @@ class INSTALLER_EXPORT EnvironmentVariableOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::EnvironmentVariableOperation) public: - EnvironmentVariableOperation(); + explicit EnvironmentVariableOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; }; } diff --git a/src/libs/installer/errors.h b/src/libs/installer/errors.h index 7c66d09c2..117eb5119 100644 --- a/src/libs/installer/errors.h +++ b/src/libs/installer/errors.h @@ -39,13 +39,15 @@ namespace QInstaller { class Error : public std::exception { public: - Error() {} + Error() + {} + explicit Error(const QString &message) : m_message(message) - { - qDebug() << "create Error-Exception:" << message; - } - virtual ~Error() throw() {} + {} + + virtual ~Error() throw() + {} QString message() const { return m_message; } diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp index 230d0f883..c41f029e3 100644 --- a/src/libs/installer/extractarchiveoperation.cpp +++ b/src/libs/installer/extractarchiveoperation.cpp @@ -26,17 +26,15 @@ ** **************************************************************************/ -#include "extractarchiveoperation.h" #include "extractarchiveoperation_p.h" -#include <QtCore/QEventLoop> -#include <QtCore/QThread> -#include <QtCore/QThreadPool> +#include <QEventLoop> +#include <QThreadPool> -using namespace QInstaller; +namespace QInstaller { - -ExtractArchiveOperation::ExtractArchiveOperation() +ExtractArchiveOperation::ExtractArchiveOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("Extract")); } @@ -48,55 +46,46 @@ void ExtractArchiveOperation::backup() bool ExtractArchiveOperation::performOperation() { - const QStringList args = arguments(); - if (args.count() != 2) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("exactly 2"), QLatin1String(""))); + if (!checkArgumentCount(2)) return false; - } - const QString archivePath = args.first(); + const QStringList args = arguments(); + const QString archivePath = args.at(0); const QString targetDir = args.at(1); Receiver receiver; Callback callback; - connect(&callback, SIGNAL(currentFileChanged(QString)), this, SLOT(fileFinished(QString))); - connect(&callback, SIGNAL(progressChanged(double)), this, SIGNAL(progressChanged(double))); + connect(&callback, &Callback::currentFileChanged, this, &ExtractArchiveOperation::fileFinished); + connect(&callback, &Callback::progressChanged, this, &ExtractArchiveOperation::progressChanged); - if (PackageManagerCore *core = this->value(QLatin1String("installer")).value<PackageManagerCore*>()) { - connect(core, SIGNAL(statusChanged(QInstaller::PackageManagerCore::Status)), &callback, - SLOT(statusChanged(QInstaller::PackageManagerCore::Status))); + if (PackageManagerCore *core = packageManager()) { + connect(core, &PackageManagerCore::statusChanged, &callback, &Callback::statusChanged); } - //Runnable is derived from QRunable which will be deleted by the ThreadPool -> no parent is needed Runnable *runnable = new Runnable(archivePath, targetDir, &callback); - connect(runnable, SIGNAL(finished(bool,QString)), &receiver, SLOT(runnableFinished(bool,QString)), + connect(runnable, &Runnable::finished, &receiver, &Receiver::runnableFinished, Qt::QueuedConnection); QEventLoop loop; - connect(&receiver, SIGNAL(finished()), &loop, SLOT(quit())); + connect(&receiver, &Receiver::finished, &loop, &QEventLoop::quit); if (QThreadPool::globalInstance()->tryStart(runnable)) { loop.exec(); } else { - // in case there is no availabe thread we should call it directly this is more a hack + // HACK: In case there is no availabe thread we should call it directly. runnable->run(); receiver.runnableFinished(true, QString()); } - typedef QPair<QString, QString> StringPair; - QVector<StringPair> backupFiles = callback.backupFiles; - - //TODO use backups for rollback, too? doesn't work for uninstallation though + // TODO: Use backups for rollback, too? Doesn't work for uninstallation though. - //delete all backups we can delete right now, remember the rest - foreach (const StringPair &i, backupFiles) + // delete all backups we can delete right now, remember the rest + foreach (const Backup &i, callback.backupFiles()) deleteFileNowOrLater(i.second); - if (!receiver.success) { + if (!receiver.success()) { setError(UserDefinedError); - setErrorString(receiver.errorString); + setErrorString(receiver.errorString()); return false; } return true; @@ -105,17 +94,16 @@ bool ExtractArchiveOperation::performOperation() bool ExtractArchiveOperation::undoOperation() { Q_ASSERT(arguments().count() == 2); - //const QString archivePath = arguments().first(); - //const QString targetDir = arguments().last(); - const QStringList files = value(QLatin1String("files")).toStringList(); WorkerThread *const thread = new WorkerThread(this, files); - connect(thread, SIGNAL(currentFileChanged(QString)), this, SIGNAL(outputTextChanged(QString))); - connect(thread, SIGNAL(progressChanged(double)), this, SIGNAL(progressChanged(double))); + connect(thread, &WorkerThread::currentFileChanged, this, + &ExtractArchiveOperation::outputTextChanged); + connect(thread, &WorkerThread::progressChanged, this, + &ExtractArchiveOperation::progressChanged); QEventLoop loop; - connect(thread, SIGNAL(finished()), &loop, SLOT(quit()), Qt::QueuedConnection); + connect(thread, &QThread::finished, &loop, &QEventLoop::quit, Qt::QueuedConnection); thread->start(); loop.exec(); thread->deleteLater(); @@ -127,18 +115,16 @@ bool ExtractArchiveOperation::testOperation() return true; } -Operation *ExtractArchiveOperation::clone() const -{ - return new ExtractArchiveOperation(); -} - /*! - This slot is direct connected to the caller so please don't call it from another thread in the same time. + This slot is direct connected to the caller so please don't call it from another thread in the + same time. */ void ExtractArchiveOperation::fileFinished(const QString &filename) { QStringList files = value(QLatin1String("files")).toStringList(); files.prepend(filename); setValue(QLatin1String("files"), files); - emit outputTextChanged(filename); + emit outputTextChanged(QDir::toNativeSeparators(filename)); } + +} // namespace QInstaller diff --git a/src/libs/installer/extractarchiveoperation.h b/src/libs/installer/extractarchiveoperation.h index ab22f4000..45c67a9cb 100644 --- a/src/libs/installer/extractarchiveoperation.h +++ b/src/libs/installer/extractarchiveoperation.h @@ -41,13 +41,12 @@ class INSTALLER_EXPORT ExtractArchiveOperation : public QObject, public Operatio friend class WorkerThread; public: - ExtractArchiveOperation(); + explicit ExtractArchiveOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; Q_SIGNALS: void outputTextChanged(const QString &progress); diff --git a/src/libs/installer/extractarchiveoperation_p.h b/src/libs/installer/extractarchiveoperation_p.h index 03d45a6e3..f333da366 100644 --- a/src/libs/installer/extractarchiveoperation_p.h +++ b/src/libs/installer/extractarchiveoperation_p.h @@ -31,24 +31,23 @@ #include "extractarchiveoperation.h" #include "fileutils.h" +#include "lib7z_extract.h" #include "lib7z_facade.h" #include "packagemanagercore.h" -#include <QtCore/QDir> -#include <QtCore/QFile> -#include <QtCore/QPair> -#include <QtCore/QThread> -#include <QtCore/QVector> +#include <QRunnable> +#include <QThread> namespace QInstaller { class WorkerThread : public QThread { Q_OBJECT + Q_DISABLE_COPY(WorkerThread) + public: - WorkerThread(ExtractArchiveOperation *op, const QStringList &files, QObject *parent = 0) - : QThread(parent) - , m_files(files) + WorkerThread(ExtractArchiveOperation *op, const QStringList &files) + : m_files(files) , m_op(op) { setObjectName(QLatin1String("ExtractArchive")); @@ -56,26 +55,25 @@ public: void run() { - ExtractArchiveOperation *const op = m_op;//dynamic_cast< ExtractArchiveOperation* >(parent()); - Q_ASSERT(op != 0); + Q_ASSERT(m_op != 0); int removedCounter = 0; foreach (const QString &file, m_files) { removedCounter++; + const QFileInfo fi(file); - emit currentFileChanged(file); + emit currentFileChanged(QDir::toNativeSeparators(file)); emit progressChanged(double(removedCounter) / m_files.count()); if (fi.isFile() || fi.isSymLink()) { - op->deleteFileNowOrLater(fi.absoluteFilePath()); + m_op->deleteFileNowOrLater(fi.absoluteFilePath()); } else if (fi.isDir()) { - const QDir d = fi.dir(); removeSystemGeneratedFiles(file); - d.rmdir(file); // directory may not exist + fi.dir().rmdir(file); // directory may not exist } } } -Q_SIGNALS: +signals: void currentFileChanged(const QString &filename); void progressChanged(double); @@ -84,44 +82,42 @@ private: ExtractArchiveOperation *m_op; }; +typedef QPair<QString, QString> Backup; +typedef QVector<Backup> BackupFiles; class ExtractArchiveOperation::Callback : public QObject, public Lib7z::ExtractCallback { Q_OBJECT + Q_DISABLE_COPY(Callback) public: - HRESULT state; - bool createBackups; - QVector<QPair<QString, QString> > backupFiles; - - Callback() : state(S_OK), createBackups(true) {} + Callback() = default; -Q_SIGNALS: - void currentFileChanged(const QString &filename); - void progressChanged(double progress); + BackupFiles backupFiles() const { + return m_backupFiles; + } -public Q_SLOTS: +public slots: void statusChanged(QInstaller::PackageManagerCore::Status status) { switch(status) { case PackageManagerCore::Canceled: - state = E_ABORT; + m_state = E_ABORT; break; case PackageManagerCore::Failure: - state = E_FAIL; + m_state = E_FAIL; break; - default: // fall through - // PackageManagerCore::Unfinished, PackageManagerCore::Success, PackageManagerCore::Running - // PackageManagerCore::ForceUpdate - - // already set - //state = S_OK; + default: // ignore all other status values break; } } -protected: - void setCurrentFile(const QString &filename) +signals: + void currentFileChanged(const QString &filename); + void progressChanged(double progress); + +private: + void setCurrentFile(const QString &filename) Q_DECL_OVERRIDE { emit currentFileChanged(QDir::toNativeSeparators(filename)); } @@ -136,94 +132,106 @@ protected: return res; } - bool prepareForFile(const QString &filename) + bool prepareForFile(const QString &filename) Q_DECL_OVERRIDE { - if (!createBackups) - return true; if (!QFile::exists(filename)) return true; const QString backup = generateBackupName(filename); QFile f(filename); const bool renamed = f.rename(backup); if (f.exists() && !renamed) { - qCritical("Could not rename %s to %s: %s", qPrintable(filename), qPrintable(backup), + qCritical("Cannot rename %s to %s: %s", qPrintable(filename), qPrintable(backup), qPrintable(f.errorString())); return false; } - backupFiles.push_back(qMakePair(filename, backup)); + m_backupFiles.append(qMakePair(filename, backup)); return true; } - HRESULT setCompleted(quint64 completed, quint64 total) + HRESULT setCompleted(quint64 completed, quint64 total) Q_DECL_OVERRIDE { emit progressChanged(double(completed) / total); - return state; + return m_state; } + +private: + HRESULT m_state = S_OK; + BackupFiles m_backupFiles; }; class ExtractArchiveOperation::Runnable : public QObject, public QRunnable { Q_OBJECT + Q_DISABLE_COPY(Runnable) public: - Runnable(const QString &archivePath_, const QString &targetDir_, ExtractArchiveOperation::Callback *callback_) - : QObject() - , QRunnable() - , archivePath(archivePath_) - , targetDir(targetDir_) - , callback(callback_) {} + Runnable(const QString &archivePath, const QString &targetDir, + ExtractArchiveOperation::Callback *callback) + : m_archivePath(archivePath) + , m_targetDir(targetDir) + , m_callback(callback) + {} void run() { - QFile archive(archivePath); + QFile archive(m_archivePath); if (!archive.open(QIODevice::ReadOnly)) { - - emit finished(false, tr("Could not open %1 for reading: %2.").arg(archivePath, archive.errorString())); + emit finished(false, tr("Cannot open archive \"%1\" for reading: %2").arg(m_archivePath, + archive.errorString())); return; } try { - Lib7z::extractArchive(&archive, targetDir, callback); + Lib7z::extractArchive(&archive, m_targetDir, m_callback); emit finished(true, QString()); } catch (const Lib7z::SevenZipException& e) { - emit finished(false, tr("Error while extracting '%1': %2").arg(archivePath, e.message())); + emit finished(false, tr("Error while extracting archive \"%1\": %2").arg(m_archivePath, + e.message())); } catch (...) { - emit finished(false, tr("Unknown exception caught while extracting %1.").arg(archivePath)); + emit finished(false, tr("Unknown exception caught while extracting \"%1\".") + .arg(m_archivePath)); } } -Q_SIGNALS: +signals: void finished(bool success, const QString &errorString); private: - const QString archivePath; - const QString targetDir; - ExtractArchiveOperation::Callback *const callback; + QString m_archivePath; + QString m_targetDir; + ExtractArchiveOperation::Callback *m_callback; }; - class ExtractArchiveOperation::Receiver : public QObject { Q_OBJECT + Q_DISABLE_COPY(Receiver) + public: - explicit Receiver(QObject *parent = 0) - : QObject(parent) - , success(false) {} + Receiver() = default; -public Q_SLOTS: + bool success() const { + return m_success; + } + + QString errorString() const { + return m_errorString; + } + +public slots: void runnableFinished(bool ok, const QString &msg) { - success = ok; - errorString = msg; + m_success = ok; + m_errorString = msg; emit finished(); } -Q_SIGNALS: +signals: void finished(); -public: - bool success; - QString errorString; +private: + bool m_success = false; + QString m_errorString; }; } diff --git a/src/libs/installer/fakestopprocessforupdateoperation.cpp b/src/libs/installer/fakestopprocessforupdateoperation.cpp index a1dc60057..69870fcab 100644 --- a/src/libs/installer/fakestopprocessforupdateoperation.cpp +++ b/src/libs/installer/fakestopprocessforupdateoperation.cpp @@ -34,7 +34,8 @@ using namespace KDUpdater; using namespace QInstaller; -FakeStopProcessForUpdateOperation::FakeStopProcessForUpdateOperation() +FakeStopProcessForUpdateOperation::FakeStopProcessForUpdateOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("FakeStopProcessForUpdate")); } @@ -51,15 +52,12 @@ bool FakeStopProcessForUpdateOperation::performOperation() bool FakeStopProcessForUpdateOperation::undoOperation() { setError(KDUpdater::UpdateOperation::NoError); - if (arguments().size() != 1) { - setError(KDUpdater::UpdateOperation::InvalidArguments, tr("Number of arguments does not " - "match: one is required")); + if (!checkArgumentCount(1)) return false; - } - PackageManagerCore *const core = value(QLatin1String("installer")).value<PackageManagerCore*>(); + PackageManagerCore *const core = packageManager(); if (!core) { - setError(KDUpdater::UpdateOperation::UserDefinedError, tr("Could not get package manager " + setError(KDUpdater::UpdateOperation::UserDefinedError, tr("Cannot get package manager " "core.")); return false; } @@ -88,8 +86,3 @@ bool FakeStopProcessForUpdateOperation::testOperation() { return true; } - -Operation *FakeStopProcessForUpdateOperation::clone() const -{ - return new FakeStopProcessForUpdateOperation(); -} diff --git a/src/libs/installer/fakestopprocessforupdateoperation.h b/src/libs/installer/fakestopprocessforupdateoperation.h index ba5de745c..6fd5da44b 100644 --- a/src/libs/installer/fakestopprocessforupdateoperation.h +++ b/src/libs/installer/fakestopprocessforupdateoperation.h @@ -38,13 +38,12 @@ class INSTALLER_EXPORT FakeStopProcessForUpdateOperation : public QObject, publi Q_OBJECT public: - FakeStopProcessForUpdateOperation(); + explicit FakeStopProcessForUpdateOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; }; } diff --git a/src/libs/installer/fileio.cpp b/src/libs/installer/fileio.cpp index 60ebd1011..dac2e4c50 100644 --- a/src/libs/installer/fileio.cpp +++ b/src/libs/installer/fileio.cpp @@ -33,6 +33,7 @@ #include <QCoreApplication> #include <QByteArray> +#include <QDir> #include <QFileDevice> #include <QString> @@ -102,7 +103,8 @@ void QInstaller::openForRead(QFileDevice *dev) Q_ASSERT(dev); if (!dev->open(QIODevice::ReadOnly)) { throw Error(QCoreApplication::translate("QInstaller", - "Cannot open file %1 for reading: %2").arg(dev->fileName(), dev->errorString())); + "Cannot open file \"%1\" for reading: %2").arg( + QDir::toNativeSeparators(dev->fileName()), dev->errorString())); } } @@ -111,7 +113,8 @@ void QInstaller::openForWrite(QFileDevice *dev) Q_ASSERT(dev); if (!dev->open(QIODevice::WriteOnly)) { throw Error(QCoreApplication::translate("QInstaller", - "Cannot open file %1 for writing: %2").arg(dev->fileName(), dev->errorString())); + "Cannot open file \"%1\" for writing: %2").arg( + QDir::toNativeSeparators(dev->fileName()), dev->errorString())); } } @@ -120,7 +123,8 @@ void QInstaller::openForAppend(QFileDevice *dev) Q_ASSERT(dev); if (!dev->open(QIODevice::WriteOnly | QIODevice::Append)) { throw Error(QCoreApplication::translate("QInstaller", - "Cannot open file %1 for writing: %2").arg(dev->fileName(), dev->errorString())); + "Cannot open file \"%1\" for writing: %2").arg( + QDir::toNativeSeparators(dev->fileName()), dev->errorString())); } } @@ -154,7 +158,7 @@ qint64 QInstaller::blockingCopy(QFileDevice *in, QFileDevice *out, qint64 size) size -= actual; actual = qMin(blockSize, size); } catch (const Error &error) { - throw Error(QCoreApplication::translate("QInstaller", "Copy failed. Error: %1") + throw Error(QCoreApplication::translate("QInstaller", "Copy failed: %1") .arg(error.message())); } } diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp index 60b912b6e..58744b3c2 100644 --- a/src/libs/installer/fileutils.cpp +++ b/src/libs/installer/fileutils.cpp @@ -176,10 +176,11 @@ void QInstaller::removeFiles(const QString &path, bool ignoreErrors) QFile f(fi.filePath()); if (!f.remove()) { const QString errorMessage = QCoreApplication::translate("QInstaller", - "Could not remove file %1: %2").arg(f.fileName(), f.errorString()); + "Cannot remove file \"%1\": %2").arg( + QDir::toNativeSeparators(f.fileName()), f.errorString()); if (!ignoreErrors) throw Error(errorMessage); - qWarning() << errorMessage; + qWarning().noquote() << errorMessage; } } } @@ -217,10 +218,11 @@ void QInstaller::removeDirectory(const QString &path, bool ignoreErrors) errno = 0; if (d.exists(path) && !d.rmdir(dir)) { const QString errorMessage = QCoreApplication::translate("QInstaller", - "Could not remove folder %1: %2").arg(dir, errnoToQString(errno)); + "Cannot remove directory \"%1\": %2").arg(QDir::toNativeSeparators(dir), + errnoToQString(errno)); if (!ignoreErrors) throw Error(errorMessage); - qWarning() << errorMessage; + qWarning().noquote() << errorMessage; } } } @@ -264,7 +266,7 @@ void QInstaller::removeDirectoryThreaded(const QString &path, bool ignoreErrors) { RemoveDirectoryThread thread(path, ignoreErrors); QEventLoop loop; - QObject::connect(&thread, SIGNAL(finished()), &loop, SLOT(quit())); + QObject::connect(&thread, &RemoveDirectoryThread::finished, &loop, &QEventLoop::quit); thread.start(); loop.exec(); if (!thread.error().isEmpty()) @@ -287,8 +289,8 @@ void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString & Q_ASSERT(QFileInfo(sourceDir).isDir()); Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir()); if (!QDir().mkpath(targetDir)) { - throw Error(QCoreApplication::translate("QInstaller", "Could not create folder %1") - .arg(targetDir)); + throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\".") + .arg(QDir::toNativeSeparators(targetDir))); } QDirIterator it(sourceDir, QDir::NoDotAndDotDot | QDir::AllEntries); while (it.hasNext()) { @@ -301,8 +303,10 @@ void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString & const QString target = QDir(targetDir).absoluteFilePath(i.fileName()); if (!f.copy(target)) { throw Error(QCoreApplication::translate("QInstaller", - "Could not copy file from %1 to %2: %3").arg(f.fileName(), target, - f.errorString())); + "Cannot copy file from \"%1\" to \"%2\": %3").arg( + QDir::toNativeSeparators(f.fileName()), + QDir::toNativeSeparators(target), + f.errorString())); } } } @@ -313,8 +317,8 @@ void QInstaller::moveDirectoryContents(const QString &sourceDir, const QString & Q_ASSERT(QFileInfo(sourceDir).isDir()); Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir()); if (!QDir().mkpath(targetDir)) { - throw Error(QCoreApplication::translate("QInstaller", "Could not create folder %1") - .arg(targetDir)); + throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\".") + .arg(QDir::toNativeSeparators(targetDir))); } QDirIterator it(sourceDir, QDir::NoDotAndDotDot | QDir::AllEntries); while (it.hasNext()) { @@ -330,8 +334,10 @@ void QInstaller::moveDirectoryContents(const QString &sourceDir, const QString & const QString target = QDir(targetDir).absoluteFilePath(i.fileName()); if (!f.rename(target)) { throw Error(QCoreApplication::translate("QInstaller", - "Could not move file from %1 to %2: %3").arg(f.fileName(), target, - f.errorString())); + "Cannot move file from \"%1\" to \"%2\": %3").arg( + QDir::toNativeSeparators(f.fileName()), + QDir::toNativeSeparators(target), + f.errorString())); } } } @@ -341,8 +347,8 @@ void QInstaller::mkdir(const QString &path) { errno = 0; if (!QDir().mkdir(QFileInfo(path).absoluteFilePath())) { - throw Error(QCoreApplication::translate("QInstaller", "Could not create folder %1: %2") - .arg(path, errnoToQString(errno))); + throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\": %2") + .arg(QDir::toNativeSeparators(path), errnoToQString(errno))); } } @@ -350,8 +356,8 @@ void QInstaller::mkpath(const QString &path) { errno = 0; if (!QDir().mkpath(QFileInfo(path).absoluteFilePath())) { - throw Error(QCoreApplication::translate("QInstaller", "Could not create folder %1: %2") - .arg(path, errnoToQString(errno))); + throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\": %2") + .arg(QDir::toNativeSeparators(path), errnoToQString(errno))); } } @@ -361,7 +367,7 @@ QString QInstaller::generateTemporaryFileName(const QString &templ) QTemporaryFile f; if (!f.open()) { throw Error(QCoreApplication::translate("QInstaller", - "Could not open temporary file: %1").arg(f.errorString())); + "Cannot open temporary file: %1").arg(f.errorString())); } return f.fileName(); } @@ -380,7 +386,7 @@ QString QInstaller::generateTemporaryFileName(const QString &templ) QFile f(tmp.arg(templ, suffix).arg(count)); if (!f.open(QIODevice::WriteOnly)) { throw Error(QCoreApplication::translate("QInstaller", - "Could not open temporary file for template %1: %2").arg(templ, f.errorString())); + "Cannot open temporary file for template %1: %2").arg(templ, f.errorString())); } f.remove(); return f.fileName(); @@ -481,14 +487,13 @@ void QInstaller::setApplicationIcon(const QString &application, const QString &i { QFile iconFile(icon); if (!iconFile.open(QIODevice::ReadOnly)) { - qWarning() << QString::fromLatin1("Could not use '%1' as application icon: %2.") - .arg(icon, iconFile.errorString()); + qWarning() << "Cannot use" << icon << "as an application icon:" << iconFile.errorString(); return; } if (QImageReader::imageFormat(icon) != "ico") { - qWarning() << QString::fromLatin1("Could not use '%1' as application icon, unsupported format %2.") - .arg(icon, QLatin1String(QImageReader::imageFormat(icon))); + qWarning() << "Cannot use" << icon << "as an application icon, unsupported format" + << QImageReader::imageFormat(icon).constData(); return; } @@ -574,3 +579,19 @@ bool QInstaller::isInBundle(const QString &path, QString *bundlePath) #endif return false; } + +/*! + Replaces the path \a before with the path \a after at the beginning of \a path and returns + the replaced path. If \a before cannot be found in \a path, the original value is returned. +*/ +QString QInstaller::replacePath(const QString &path, const QString &before, const QString &after) +{ + if (path.isEmpty() || before.isEmpty()) + return path; + + QString pathToPatch = QDir::cleanPath(path); + const QString pathToReplace = QDir::cleanPath(before); + if (pathToPatch.startsWith(pathToReplace)) + return QDir::cleanPath(after) + pathToPatch.mid(pathToReplace.size()); + return path; +} diff --git a/src/libs/installer/fileutils.h b/src/libs/installer/fileutils.h index 74436bef7..ae08f5ff3 100644 --- a/src/libs/installer/fileutils.h +++ b/src/libs/installer/fileutils.h @@ -94,6 +94,8 @@ private: quint64 INSTALLER_EXPORT fileSize(const QFileInfo &info); bool INSTALLER_EXPORT isInBundle(const QString &path, QString *bundlePath = 0); + QString replacePath(const QString &path, const QString &pathBefore, const QString &pathAfter); + #ifdef Q_OS_WIN QString INSTALLER_EXPORT getLongPathName(const QString &name); QString INSTALLER_EXPORT getShortPathName(const QString &name); diff --git a/src/libs/installer/globals.cpp b/src/libs/installer/globals.cpp index df97c11b0..5f3009905 100644 --- a/src/libs/installer/globals.cpp +++ b/src/libs/installer/globals.cpp @@ -30,6 +30,7 @@ const char IFW_COMPONENT_CHECKER[] = "ifw.componentChecker"; const char IFW_RESOURCES[] = "ifw.resources"; const char IFW_TRANSLATIONS[] = "ifw.translations"; +const char IFW_NETWORK[] = "ifw.network"; namespace QInstaller { @@ -37,13 +38,15 @@ namespace QInstaller Q_LOGGING_CATEGORY(lcComponentChecker, IFW_COMPONENT_CHECKER) Q_LOGGING_CATEGORY(lcResources, IFW_RESOURCES) Q_LOGGING_CATEGORY(lcTranslations, IFW_TRANSLATIONS) +Q_LOGGING_CATEGORY(lcNetwork, IFW_NETWORK) QStringList loggingCategories() { static QStringList categories = QStringList() << QLatin1String(IFW_COMPONENT_CHECKER) << QLatin1String(IFW_RESOURCES) - << QLatin1String(IFW_TRANSLATIONS); + << QLatin1String(IFW_TRANSLATIONS) + << QLatin1String(IFW_NETWORK); return categories; } diff --git a/src/libs/installer/globals.h b/src/libs/installer/globals.h index fd45b61f2..a5bd47ac8 100644 --- a/src/libs/installer/globals.h +++ b/src/libs/installer/globals.h @@ -38,6 +38,7 @@ namespace QInstaller { INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcComponentChecker) INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcResources) INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcTranslations) +INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcNetwork) QStringList INSTALLER_EXPORT loggingCategories(); diff --git a/src/libs/installer/globalsettingsoperation.cpp b/src/libs/installer/globalsettingsoperation.cpp index 12b57413b..f8f7c112d 100644 --- a/src/libs/installer/globalsettingsoperation.cpp +++ b/src/libs/installer/globalsettingsoperation.cpp @@ -31,7 +31,8 @@ using namespace QInstaller; -GlobalSettingsOperation::GlobalSettingsOperation() +GlobalSettingsOperation::GlobalSettingsOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("GlobalConfig")); } @@ -49,7 +50,7 @@ bool GlobalSettingsOperation::performOperation() if (!settings->isWritable()) { setError(UserDefinedError); - setErrorString(tr("Settings are not writable")); + setErrorString(tr("Settings are not writable.")); return false; } @@ -59,7 +60,7 @@ bool GlobalSettingsOperation::performOperation() if (settings->status() != QSettingsWrapper::NoError) { setError(UserDefinedError); - setErrorString(tr("Failed to write settings")); + setErrorString(tr("Failed to write settings.")); return false; } @@ -92,24 +93,15 @@ bool GlobalSettingsOperation::testOperation() return true; } -Operation *GlobalSettingsOperation::clone() const -{ - return new GlobalSettingsOperation(); -} - QSettingsWrapper *GlobalSettingsOperation::setup(QString *key, QString *value, const QStringList &arguments) { - if (arguments.count() != 3 && arguments.count() != 4 && arguments.count() != 5) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments.count()).arg(tr("3, 4 or 5"), QLatin1String(""))); + if (!checkArgumentCount(3, 5)) return 0; - } if (arguments.count() == 5) { QSettingsWrapper::Scope scope = QSettingsWrapper::UserScope; if (arguments.at(0) == QLatin1String("SystemScope")) - scope = QSettingsWrapper::SystemScope; + scope = QSettingsWrapper::SystemScope; const QString &company = arguments.at(1); const QString &application = arguments.at(2); *key = arguments.at(3); diff --git a/src/libs/installer/globalsettingsoperation.h b/src/libs/installer/globalsettingsoperation.h index be6e79c5d..fe5d14edb 100644 --- a/src/libs/installer/globalsettingsoperation.h +++ b/src/libs/installer/globalsettingsoperation.h @@ -38,13 +38,12 @@ class INSTALLER_EXPORT GlobalSettingsOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::GlobalSettingsOperation) public: - GlobalSettingsOperation(); + explicit GlobalSettingsOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; private: QSettingsWrapper *setup(QString *key, QString *value, const QStringList &args); diff --git a/src/libs/installer/init.cpp b/src/libs/installer/init.cpp index cf3ed918d..285c7ebc9 100644 --- a/src/libs/installer/init.cpp +++ b/src/libs/installer/init.cpp @@ -48,75 +48,20 @@ #include "settingsoperation.h" #include "consumeoutputoperation.h" - +#include "lib7z_facade.h" #include "utils.h" -#include "kdupdaterupdateoperationfactory.h" -#include "kdupdaterfiledownloaderfactory.h" - -#include "7zCrc.h" +#include "updateoperationfactory.h" +#include "filedownloaderfactory.h" #include <QtPlugin> #include <QElapsedTimer> #include <iostream> -namespace NArchive { - namespace NXz { - void registerArcxz(); - } - namespace NSplit { - void registerArcSplit(); - } - namespace NLzma { - namespace NLzmaAr { - void registerArcLzma(); - } - namespace NLzma86Ar { - void registerArcLzma86(); - } - } -} - -void registerArc7z(); - -void registerCodecBCJ(); -void registerCodecBCJ2(); - -void registerCodecCopy(); -void registerCodecLZMA(); -void registerCodecLZMA2(); - -void registerCodecDelta(); -void registerCodecBranch(); -void registerCodecByteSwap(); - using namespace KDUpdater; using namespace QInstaller; -static void initArchives() -{ - CrcGenerateTable(); - - registerArc7z(); - - registerCodecBCJ(); - registerCodecBCJ2(); - - registerCodecCopy(); - registerCodecLZMA(); - registerCodecLZMA2(); - - registerCodecDelta(); - registerCodecBranch(); - registerCodecByteSwap(); - - NArchive::NXz::registerArcxz(); - NArchive::NSplit::registerArcSplit(); - NArchive::NLzma::NLzmaAr::registerArcLzma(); - NArchive::NLzma::NLzma86Ar::registerArcLzma86(); -} - #if defined(QT_STATIC) static void initResources() { @@ -193,7 +138,8 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt void QInstaller::init() { - ::initArchives(); + Lib7z::initSevenZ(); + #if defined(QT_STATIC) ::initResources(); #endif diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro index 53daf978d..d641ff1fc 100644 --- a/src/libs/installer/installer.pro +++ b/src/libs/installer/installer.pro @@ -34,7 +34,8 @@ QT += \ xml \ concurrent \ widgets \ - core-private + core-private \ + qml-private win32:QT += winextras HEADERS += packagemanagercore.h \ @@ -124,7 +125,11 @@ HEADERS += packagemanagercore.h \ serverauthenticationdialog.h \ keepaliveobject.h \ systeminfo.h \ - localsocket.h + packagesource.h \ + lib7z_guid.h \ + lib7z_create.h \ + lib7z_extract.h \ + lib7z_list.h SOURCES += packagemanagercore.cpp \ packagemanagercore_p.cpp \ @@ -198,7 +203,8 @@ SOURCES += packagemanagercore.cpp \ proxycredentialsdialog.cpp \ serverauthenticationdialog.cpp \ keepaliveobject.cpp \ - systeminfo.cpp + systeminfo.cpp \ + packagesource.cpp FORMS += proxycredentialsdialog.ui \ serverauthenticationdialog.ui diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp index 65713dc74..064dbafa5 100644 --- a/src/libs/installer/installercalculator.cpp +++ b/src/libs/installer/installercalculator.cpp @@ -71,7 +71,7 @@ QString InstallerCalculator::installReason(Component *component) const "Components added as automatic dependencies:"); case Dependent: return QCoreApplication::translate("InstallerCalculator", "Components added as " - "dependency for '%1':").arg(installReasonReferencedComponent(component)); + "dependency for \"%1\":").arg(installReasonReferencedComponent(component)); case Resolved: return QCoreApplication::translate("InstallerCalculator", "Components that have resolved dependencies:"); @@ -92,9 +92,9 @@ QString InstallerCalculator::componentsToInstallError() const return m_componentsToInstallError; } -void InstallerCalculator::realAppendToInstallComponents(Component *component) +void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version) { - if (!component->isInstalled() || component->updateRequested()) { + if (!component->isInstalled(version) || component->updateRequested()) { m_orderedComponentsToInstall.append(component); m_toInstallComponentIds.insert(component->name()); } @@ -102,8 +102,8 @@ void InstallerCalculator::realAppendToInstallComponents(Component *component) QString InstallerCalculator::recursionError(Component *component) { - return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component '%1' " - "already added with reason: '%2'").arg(component->name(), installReason(component)); + return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" " + "already added with reason: \"%2\"").arg(component->name(), installReason(component)); } bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &components) @@ -115,7 +115,7 @@ bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &co foreach (Component *component, components){ if (m_toInstallComponentIds.contains(component->name())) { const QString errorMessage = recursionError(component); - qWarning() << errorMessage; + qWarning().noquote() << errorMessage; m_componentsToInstallError.append(errorMessage); Q_ASSERT_X(!m_toInstallComponentIds.contains(component->name()), Q_FUNC_INFO, qPrintable(errorMessage)); @@ -154,10 +154,10 @@ bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &co return true; } -bool InstallerCalculator::appendComponentToInstall(Component *component) +bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version) { QSet<QString> allDependencies = component->dependencies().toSet(); - + QString requiredDependencyVersion = version; foreach (const QString &dependencyComponentName, allDependencies) { // PackageManagerCore::componentByName returns 0 if dependencyComponentName contains a // version which is not available @@ -165,36 +165,56 @@ bool InstallerCalculator::appendComponentToInstall(Component *component) PackageManagerCore::componentByName(dependencyComponentName, m_allComponents); if (!dependencyComponent) { const QString errorMessage = QCoreApplication::translate("InstallerCalculator", - "Cannot find missing dependency '%1' for '%2'.").arg(dependencyComponentName, + "Cannot find missing dependency \"%1\" for \"%2\".").arg(dependencyComponentName, component->name()); - qWarning() << errorMessage; + qWarning().noquote() << errorMessage; m_componentsToInstallError.append(errorMessage); return false; } + //Check if component requires higher version than what might be already installed + bool isUpdateRequired = false; + if (dependencyComponentName.contains(QChar::fromLatin1('-')) && + !dependencyComponent->value(scInstalledVersion).isEmpty()) { + QRegExp compEx(QLatin1String("([<=>]+)(.*)")); + const QString installedVersion = compEx.exactMatch(dependencyComponent->value(scInstalledVersion)) ? + compEx.cap(2) : dependencyComponent->value(scInstalledVersion); + + QString requiredVersion = dependencyComponentName.section(QLatin1Char('-'), 1); + requiredVersion = compEx.exactMatch(requiredVersion) ? compEx.cap(2) : requiredVersion; + + if (KDUpdater::compareVersion(requiredVersion, installedVersion) >= 1 ) { + isUpdateRequired = true; + requiredDependencyVersion = requiredVersion; + } + } + //Check dependencies only if + //- Dependency is not installed or update requested, nor newer version of dependency component required + //- And dependency component is not already added for install + //- And component is not already added for install, then dependencies are already resolved + if (((!dependencyComponent->isInstalled() || dependencyComponent->updateRequested()) + || isUpdateRequired) && (!m_toInstallComponentIds.contains(dependencyComponent->name()) + && !m_toInstallComponentIds.contains(component->name()))) { + if (m_visitedComponents.value(component).contains(dependencyComponent)) { + const QString errorMessage = recursionError(component); + qWarning().noquote() << errorMessage; + m_componentsToInstallError = errorMessage; + Q_ASSERT_X(!m_visitedComponents.value(component).contains(dependencyComponent), + Q_FUNC_INFO, qPrintable(errorMessage)); + return false; + } + m_visitedComponents[component].insert(dependencyComponent); + + // add needed dependency components to the next run + insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, + component->name()); - if ((!dependencyComponent->isInstalled() || dependencyComponent->updateRequested()) - && !m_toInstallComponentIds.contains(dependencyComponent->name())) { - if (m_visitedComponents.value(component).contains(dependencyComponent)) { - const QString errorMessage = recursionError(component); - qWarning() << errorMessage; - m_componentsToInstallError = errorMessage; - Q_ASSERT_X(!m_visitedComponents.value(component).contains(dependencyComponent), - Q_FUNC_INFO, qPrintable(errorMessage)); - return false; - } - m_visitedComponents[component].insert(dependencyComponent); - - // add needed dependency components to the next run - insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, - component->name()); - - if (!appendComponentToInstall(dependencyComponent)) - return false; + if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion)) + return false; } } if (!m_toInstallComponentIds.contains(component->name())) { - realAppendToInstallComponents(component); + realAppendToInstallComponents(component, requiredDependencyVersion); insertInstallReason(component, InstallerCalculator::Resolved); } return true; diff --git a/src/libs/installer/installercalculator.h b/src/libs/installer/installercalculator.h index 08dd47f78..b2d05bdbe 100644 --- a/src/libs/installer/installercalculator.h +++ b/src/libs/installer/installercalculator.h @@ -64,8 +64,8 @@ private: void insertInstallReason(Component *component, InstallReasonType installReasonType, const QString &referencedComponentName = QString()); - void realAppendToInstallComponents(Component *component); - bool appendComponentToInstall(Component *components); + void realAppendToInstallComponents(Component *component, const QString &version = QString()); + bool appendComponentToInstall(Component *components, const QString &version = QString()); QString recursionError(Component *component); QList<Component*> m_allComponents; diff --git a/src/libs/installer/installiconsoperation.cpp b/src/libs/installer/installiconsoperation.cpp index fe6127941..ecd21be86 100644 --- a/src/libs/installer/installiconsoperation.cpp +++ b/src/libs/installer/installiconsoperation.cpp @@ -86,7 +86,8 @@ QString InstallIconsOperation::targetDirectory() return directory; } -InstallIconsOperation::InstallIconsOperation() +InstallIconsOperation::InstallIconsOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("InstallIcons")); } @@ -103,20 +104,16 @@ void InstallIconsOperation::backup() bool InstallIconsOperation::performOperation() { - const QStringList args = arguments(); - if ((args.count() != 1) && (args.count() != 2)) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("1 or 2"), tr(" (Sourcepath, [Vendorprefix])"))); + if (!checkArgumentCount(1, 2, tr("<source path> [vendor prefix]"))) return false; - } + const QStringList args = arguments(); const QString source = args.at(0); - const QString vendor = args.value(1); + const QString vendor = args.value(1); // value() used since it's optional if (source.isEmpty()) { setError(InvalidArguments); - setErrorString(tr("Invalid Argument: source folder must not be empty.")); + setErrorString(tr("Invalid Argument: source directory must not be empty.")); return false; } @@ -127,7 +124,7 @@ bool InstallIconsOperation::performOperation() QStringList backupFiles; QStringList createdDirectories; - PackageManagerCore *const core = value(QLatin1String("installer")).value<PackageManagerCore*>(); + PackageManagerCore *const core = packageManager(); // iterate a second time to get the actual work done QDirIterator it(sourceDir.path(), QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot, @@ -165,7 +162,8 @@ bool InstallIconsOperation::performOperation() QFile bf(target); if (!bf.copy(backup)) { setError(UserDefinedError); - setErrorString(tr("Could not backup file %1: %2").arg(target, bf.errorString())); + setErrorString(tr("Cannot backup file \"%1\": %2").arg( + QDir::toNativeSeparators(target), bf.errorString())); undoOperation(); return false; } @@ -178,7 +176,8 @@ bool InstallIconsOperation::performOperation() QString errStr; if (!deleteFileNowOrLater(target, &errStr)) { setError(UserDefinedError); - setErrorString(tr("Failed to overwrite %1: %2").arg(target, errStr)); + setErrorString(tr("Failed to overwrite \"%1\": %2").arg( + QDir::toNativeSeparators(target), errStr)); undoOperation(); return false; } @@ -189,7 +188,8 @@ bool InstallIconsOperation::performOperation() QFile cf(source); if (!cf.copy(target)) { setError(UserDefinedError); - setErrorString(tr("Failed to copy file %1: %2").arg(target, cf.errorString())); + setErrorString(tr("Failed to copy file \"%1\": %2").arg( + QDir::toNativeSeparators(target), cf.errorString())); undoOperation(); return false; } @@ -199,7 +199,8 @@ bool InstallIconsOperation::performOperation() setValue(QLatin1String("files"), files); } else if (fi.isDir() && !QDir(target).exists()) { if (!QDir().mkpath(target)) { - setErrorString(tr("Could not create folder at %1: %2").arg(target, qt_error_string())); + setErrorString(tr("Cannot create directory \"%1\": %2").arg( + QDir::toNativeSeparators(target), qt_error_string())); undoOperation(); return false; } @@ -231,7 +232,7 @@ bool InstallIconsOperation::undoOperation() QFile installedTarget(target); if (installedTarget.exists() && !(installedTarget.copy(source) && installedTarget.remove())) { - warningMessages << QString::fromLatin1("Could not move file from '%1' to '%2', error: %3)").arg( + warningMessages << QString::fromLatin1("Cannot move file from \"%1\" to \"%2\": %3)").arg( target, source, installedTarget.errorString()); } } @@ -247,13 +248,13 @@ bool InstallIconsOperation::undoOperation() deleteFileNowOrLater(target); // then copy the backup onto the target if (!QFile::copy(backup, target)) { - warningMessages << QString::fromLatin1("Could not restore the backup '%1' to '%2'").arg( + warningMessages << QString::fromLatin1("Cannot restore the backup \"%1\" to \"%2\".").arg( backup, target); } // finally remove the backp if (!deleteFileNowOrLater(backup)) - warningMessages << QString::fromLatin1("Could not remove the backup '%1'").arg(backup); + warningMessages << QString::fromLatin1("Cannot remove the backup \"%1\".").arg(backup); } @@ -263,14 +264,14 @@ bool InstallIconsOperation::undoOperation() const QDir dir(*it); removeSystemGeneratedFiles(dir.absolutePath()); if (dir.exists() && !QDir::root().rmdir(dir.path())) - warningMessages << QString::fromLatin1("Could not remove directory '%1'").arg(dir.path()); + warningMessages << QString::fromLatin1("Cannot remove directory \"%1\".").arg(dir.path()); } if (!warningMessages.isEmpty()) { - qWarning() << QString::fromLatin1("Undo of operation '%1' with arguments '%2' had some problems.").arg( - name(), arguments().join(QLatin1String(", "))); + qWarning() << "Undo of operation" << name() << "with arguments" + << arguments().join(QLatin1String(", ")) << "had some problems."; foreach (const QString &message, warningMessages) { - qWarning() << message; + qWarning().noquote() << message; } } @@ -281,8 +282,3 @@ bool InstallIconsOperation::testOperation() { return true; } - -Operation *InstallIconsOperation::clone() const -{ - return new InstallIconsOperation(); -} diff --git a/src/libs/installer/installiconsoperation.h b/src/libs/installer/installiconsoperation.h index d8170fef2..5f8dff514 100644 --- a/src/libs/installer/installiconsoperation.h +++ b/src/libs/installer/installiconsoperation.h @@ -39,14 +39,13 @@ class INSTALLER_EXPORT InstallIconsOperation : public QObject, public Operation { Q_OBJECT public: - InstallIconsOperation(); + explicit InstallIconsOperation(PackageManagerCore *core); ~InstallIconsOperation(); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; Q_SIGNALS: void outputTextChanged(const QString &progress); diff --git a/src/libs/installer/keepaliveobject.cpp b/src/libs/installer/keepaliveobject.cpp index 148987280..6f56d7823 100644 --- a/src/libs/installer/keepaliveobject.cpp +++ b/src/libs/installer/keepaliveobject.cpp @@ -28,8 +28,8 @@ #include "keepaliveobject.h" #include "remoteclient.h" -#include "localsocket.h" +#include <QLocalSocket> #include <QTimer> namespace QInstaller { @@ -43,7 +43,7 @@ KeepAliveObject::KeepAliveObject() void KeepAliveObject::start() { m_timer = new QTimer(this); - m_socket = new LocalSocket(this); + m_socket = new QLocalSocket(this); connect(m_timer, &QTimer::timeout, [this]() { if (m_socket->state() != QLocalSocket::UnconnectedState) diff --git a/src/libs/installer/localsocket.h b/src/libs/installer/lib7z_create.h index 146547258..ad308ffd1 100644 --- a/src/libs/installer/localsocket.h +++ b/src/libs/installer/lib7z_create.h @@ -1,7 +1,7 @@ -/************************************************************************** +/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** @@ -24,45 +24,54 @@ ** ** $QT_END_LICENSE$ ** -**************************************************************************/ +****************************************************************************/ -#ifndef LOCALSOCKET_H -#define LOCALSOCKET_H +#ifndef LIB7Z_CREATE_H +#define LIB7Z_CREATE_H -#include <QLocalSocket> +#include "installer_global.h" -#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK(5,5,0) +#include <Common/MyCom.h> +#include <7zip/UI/Common/Update.h> -// This is a crude hack to work around QLocalSocket::waitForReadyRead returning instantly -// if there are still bytes left in the buffer. This has been fixed in Qt 5.5.0 ... -class LocalSocket : public QLocalSocket +QT_BEGIN_NAMESPACE +class QFileDevice; +class QStringList; +QT_END_NAMESPACE + +namespace Lib7z { -public: - LocalSocket(QObject *parent = 0) : QLocalSocket(parent), inReadyRead(false) - { - } + enum struct QTmpFile { + No, + Yes + }; - qint64 bytesAvailable() const { - if (inReadyRead) - return 0; - return QLocalSocket::bytesAvailable(); - } + enum struct Compression { + Non = 0, + Fastest = 1, + Fast = 3, + Normal = 5, + Maximum = 7, + Ultra = 9 + }; - bool waitForReadyRead(int msecs = 30000) { - inReadyRead = true; - bool result = QLocalSocket::waitForReadyRead(msecs); - inReadyRead = false; - return result; - } + class INSTALLER_EXPORT UpdateCallback : public IUpdateCallbackUI2, public CMyUnknownImp + { + Q_DISABLE_COPY(UpdateCallback) -private: - bool inReadyRead; -}; + public: + UpdateCallback() = default; + virtual ~UpdateCallback() {} -#else + MY_UNKNOWN_IMP + INTERFACE_IUpdateCallbackUI2(;) + }; -typedef QLocalSocket LocalSocket; + void INSTALLER_EXPORT createArchive(QFileDevice *archive, const QStringList &sources, + Compression level = Compression::Normal, UpdateCallback *callback = 0); + void INSTALLER_EXPORT createArchive(const QString &archive, const QStringList &sources, + QTmpFile mode, Compression level = Compression::Normal, UpdateCallback *callback = 0); -#endif +} // namespace Lib7z -#endif // LOCALSOCKET_H +#endif // LIB7Z_CREATE_H diff --git a/src/libs/installer/lib7z_extract.h b/src/libs/installer/lib7z_extract.h new file mode 100644 index 000000000..f6182c727 --- /dev/null +++ b/src/libs/installer/lib7z_extract.h @@ -0,0 +1,85 @@ +/************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ +#ifndef LIB7Z_EXTRACT_H +#define LIB7Z_EXTRACT_H + +#include "installer_global.h" + +#include <Common/MyCom.h> +#include <7zip/Archive/IArchive.h> + +#include <QString> + +class CArc; + +QT_BEGIN_NAMESPACE +class QFileDevice; +QT_END_NAMESPACE + +namespace Lib7z +{ + class INSTALLER_EXPORT ExtractCallback : public IArchiveExtractCallback, public CMyUnknownImp + { + Q_DISABLE_COPY(ExtractCallback) + + public: + ExtractCallback() = default; + virtual ~ExtractCallback() = default; + + void setArchive(CArc *carc) { arc = carc; } + void setTarget(const QString &dir) { targetDir = dir; } + + MY_UNKNOWN_IMP + INTERFACE_IArchiveExtractCallback(;) + + protected: + virtual bool prepareForFile(const QString & /*filename*/) { return true; } + virtual void setCurrentFile(const QString &filename) { Q_UNUSED(filename) } + virtual HRESULT setCompleted(quint64 /*completed*/, quint64 /*total*/) { return S_OK; } + + private: + CArc *arc = 0; + + QString targetDir; + quint64 total = 0; + quint64 completed = 0; + quint32 currentIndex = 0; + }; + + void INSTALLER_EXPORT extractArchive(QFileDevice *archive, const QString &targetDirectory, + ExtractCallback *callback = 0); + +} // namespace Lib7z + +#endif // LIB7Z_EXTRACT_H diff --git a/src/libs/installer/lib7z_facade.cpp b/src/libs/installer/lib7z_facade.cpp index ebff609b4..f5561af2f 100644 --- a/src/libs/installer/lib7z_facade.cpp +++ b/src/libs/installer/lib7z_facade.cpp @@ -25,106 +25,199 @@ ** $QT_END_LICENSE$ ** **************************************************************************/ + #include "lib7z_facade.h" #include "errors.h" #include "fileio.h" +#include "lib7z_create.h" +#include "lib7z_extract.h" +#include "lib7z_list.h" +#include "lib7z_guid.h" + #ifndef Q_OS_WIN # include "StdAfx.h" #endif -#include "Common/MyInitGuid.h" +#include <7zCrc.h> + +#include <7zip/Archive/IArchive.h> -#include "7zip/Archive/IArchive.h" -#include "7zip/UI/Common/OpenArchive.h" -#include "7zip/UI/Common/Update.h" +#include <7zip/UI/Common/ArchiveCommandLine.h> +#include <7zip/UI/Common/OpenArchive.h> -#include "Windows/FileIO.h" -#include "Windows/PropVariant.h" -#include "Windows/PropVariantConversions.h" +#include <Windows/FileDir.h> +#include <Windows/FileIO.h> +#include <Windows/PropVariant.h> +#include <Windows/PropVariantConv.h> +#include <QCoreApplication> #include <QDir> #include <QFileInfo> #include <QIODevice> -#include <QtCore/QMutexLocker> #include <QPointer> -#include <QTemporaryFile> #include <QReadWriteLock> +#include <QTemporaryFile> -#ifdef _MSC_VER -#pragma warning(disable:4297) -#endif +#include <mutex> +#include <memory> #ifdef Q_OS_WIN +HINSTANCE g_hInstance = 0; + +# define S_IFMT 00170000 +# define S_IFLNK 0120000 +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +# define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */ +# if !defined(Q_CC_MINGW) +# include <time.h> // for localtime_s +# endif +#else +extern "C" int global_use_utf16_conversion; + +#include <myWindows/config.h> +#include <sys/stat.h> +#endif + +namespace NArchive { + namespace N7z { + void registerArcDec7z(); + } + namespace NXz { + void registerArcxz(); + } + namespace NSplit { + void registerArcSplit(); + } + namespace NLzma { + namespace NLzmaAr { + void registerArcLzma(); + } + namespace NLzma86Ar { + void registerArcLzma86(); + } + } +} +using namespace NWindows; + +void registerCodecBCJ(); +void registerCodecBCJ2(); + +void registerCodecLZMA(); +void registerCodecLZMA2(); + +void registerCodecCopy(); +void registerCodecDelta(); +void registerCodecBranch(); +void registerCodecByteSwap(); + +namespace Lib7z { + -#include <time.h> -#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */ -#define S_IFMT 00170000 -#define S_IFLNK 0120000 -#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +// -- 7z init codecs, archives -typedef BOOL (WINAPI *CREATEHARDLINK)(LPCSTR dst, LPCSTR str, LPSECURITY_ATTRIBUTES sa); +std::once_flag gOnceFlag; -bool CreateHardLinkWrapper(const QString &dest, const QString &file) +void initSevenZ() { - static HMODULE module = 0; - static CREATEHARDLINK proc = 0; + std::call_once(gOnceFlag, [] { + CrcGenerateTable(); - if (module == 0) - module = LoadLibrary(L"kernel32.dll"); - if (module == 0) - return false; - if (proc == 0) - proc = (CREATEHARDLINK) GetProcAddress(module, "CreateHardLinkA"); - if (proc == 0) - return false; - QString target = file; - if (!QFileInfo(file).isAbsolute()) - { - target = QFileInfo(dest).dir().absoluteFilePath(file); - } - const QString link = QDir::toNativeSeparators(dest); - const QString existingFile = QDir::toNativeSeparators(target); - return proc(link.toLocal8Bit(), existingFile.toLocal8Bit(), 0); -} + registerCodecBCJ(); + registerCodecBCJ2(); -#else -#include <sys/stat.h> + registerCodecLZMA(); + registerCodecLZMA2(); + + registerCodecCopy(); + registerCodecDelta(); + registerCodecBranch(); + registerCodecByteSwap(); + + NArchive::N7z::registerArcDec7z(); + NArchive::NXz::registerArcxz(); + NArchive::NSplit::registerArcSplit(); + NArchive::NLzma::NLzmaAr::registerArcLzma(); + NArchive::NLzma::NLzma86Ar::registerArcLzma86(); + +#ifndef Q_OS_WIN +# ifdef ENV_HAVE_LOCALE + const QByteArray locale = qgetenv("LC_ALL").toUpper(); + if (!locale.isEmpty() && (locale != "C") && (locale != "POSIX")) + global_use_utf16_conversion = 1; +# elif defined(LOCALE_IS_UTF8) + global_use_utf16_conversion = 1; +# else + global_use_utf16_conversion = 0; +# endif #endif + }); +} -#include <memory> -#include <cassert> +// -- error handling -using namespace Lib7z; -using namespace NWindows; +Q_GLOBAL_STATIC(QString, getLastErrorString) +Q_GLOBAL_STATIC(QReadWriteLock, lastErrorReadWriteLock) +QString lastError() +{ + QReadLocker locker(lastErrorReadWriteLock()); + Q_UNUSED(locker) + return *getLastErrorString(); +} -namespace Lib7z { - Q_GLOBAL_STATIC(QReadWriteLock, lastErrorReadWriteLock) - Q_GLOBAL_STATIC(QString, getLastErrorString) +void setLastError(const QString &errorString) +{ + QWriteLocker locker(lastErrorReadWriteLock()); + Q_UNUSED(locker) + *getLastErrorString() = errorString; +} - QString lastError() - { - QReadLocker locker(lastErrorReadWriteLock()); - Q_UNUSED(locker) - return *getLastErrorString(); - } +QString errorMessageFrom7zResult(const LONG &extractResult) +{ + if (!lastError().isEmpty()) + return lastError(); - void setLastError(const QString &errorString) - { - QWriteLocker locker(lastErrorReadWriteLock()); - Q_UNUSED(locker) - *getLastErrorString() = errorString; + QString errorMessage = QCoreApplication::translate("Lib7z", "internal code: %1"); + switch (extractResult) { + case S_OK: + qFatal("S_OK value is not a valid error code."); + break; + case E_NOTIMPL: + errorMessage = errorMessage.arg(QLatin1String("E_NOTIMPL")); + break; + case E_NOINTERFACE: + errorMessage = errorMessage.arg(QLatin1String("E_NOINTERFACE")); + break; + case E_ABORT: + errorMessage = errorMessage.arg(QLatin1String("E_ABORT")); + break; + case E_FAIL: + errorMessage = errorMessage.arg(QLatin1String("E_FAIL")); + break; + case STG_E_INVALIDFUNCTION: + errorMessage = errorMessage.arg(QLatin1String("STG_E_INVALIDFUNCTION")); + break; + case E_OUTOFMEMORY: + errorMessage = QCoreApplication::translate("Lib7z", "not enough memory"); + break; + case E_INVALIDARG: + errorMessage = errorMessage.arg(QLatin1String("E_INVALIDARG")); + break; + default: + errorMessage = QCoreApplication::translate("Lib7z", "Error: %1").arg(extractResult); + break; } + return errorMessage; } -namespace { -/** -* RAII class to create a directory (tryCreate()) and delete it on destruction unless released. +/*! + RAII class to create a directory (tryCreate()) and delete it on destruction unless released. */ -struct DirectoryGuard { +struct DirectoryGuard +{ explicit DirectoryGuard(const QString &path) : m_path(path) , m_created(false) @@ -139,14 +232,15 @@ struct DirectoryGuard { return; QDir dir(m_path); if (!dir.rmdir(m_path)) - qWarning() << "Could not delete directory " << m_path; + qWarning() << "Cannot delete directory " << m_path; } - /** - * Tries to create the directorie structure. - * Returns a list of every directory created. + /*! + Tries to create the directory structure. + Returns a list of every directory created. */ - QStringList tryCreate() { + QStringList tryCreate() + { if (m_path.isEmpty()) return QStringList(); @@ -155,13 +249,12 @@ struct DirectoryGuard { return QStringList(); if (fi.exists() && !fi.isDir()) { throw SevenZipException(QCoreApplication::translate("DirectoryGuard", - "Path exists but is not a folder: %1").arg(m_path)); + "Path \"%1\" exists but is not a directory.").arg(QDir::toNativeSeparators(m_path))); } QStringList created; QDir toCreate(m_path); - while (!toCreate.exists()) - { + while (!toCreate.exists()) { QString p = toCreate.absolutePath(); created.push_front(p); p = p.section(QLatin1Char('/'), 0, -2); @@ -172,12 +265,13 @@ struct DirectoryGuard { m_created = dir.mkpath(m_path); if (!m_created) { throw SevenZipException(QCoreApplication::translate("DirectoryGuard", - "Could not create folder: %1").arg(m_path)); + "Cannot create directory \"%1\".").arg(QDir::toNativeSeparators(m_path))); } return created; } - void release() { + void release() + { m_released = true; } @@ -185,7 +279,6 @@ struct DirectoryGuard { bool m_created; bool m_released; }; -} static UString QString2UString(const QString &str) { @@ -197,32 +290,12 @@ static QString UString2QString(const UString& str) return QString::fromStdWString(static_cast<const wchar_t*>(str)); } -static QString generateTempFileName() -{ - QTemporaryFile tmp; - if (!tmp.open()) { - throw SevenZipException(QCoreApplication::translate("QInstaller", - "Could not create temporary file")); - } - return QDir::toNativeSeparators(tmp.fileName()); -} - -/* -static QStringList UStringVector2QStringList(const UStringVector& vec) -{ -QStringList res; -for (int i = 0; i < vec.Size(); ++i) -res += UString2QString(vec[i]); -return res; -} -*/ - -static NCOM::CPropVariant readProperty(IInArchive* archive, int index, int propId) +static NCOM::CPropVariant readProperty(IInArchive *archive, int index, int propId) { NCOM::CPropVariant prop; if (archive->GetProperty(index, propId, &prop) != S_OK) { - throw SevenZipException(QCoreApplication::translate("QInstaller", - "Could not retrieve property %1 for item %2").arg(QString::number(propId), + throw SevenZipException(QCoreApplication::translate("Lib7z", + "Cannot retrieve property %1 for item %2.").arg(QString::number(propId), QString::number(index))); } return prop; @@ -233,193 +306,134 @@ static bool IsFileTimeZero(const FILETIME *lpFileTime) return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0); } -static bool IsDST(const QDateTime& datetime = QDateTime()) -{ - const time_t seconds = static_cast< time_t >(datetime.isValid() ? datetime.toTime_t() - : QDateTime::currentDateTime().toTime_t()); -#if defined(Q_OS_WIN) && !defined(Q_CC_MINGW) - struct tm t; - localtime_s(&t, &seconds); -#else - const struct tm &t = *localtime(&seconds); -#endif - return t.tm_isdst; -} - -static bool getFileTimeFromProperty(IInArchive* archive, int index, int propId, FILETIME *fileTime) +static bool getFileTimeFromProperty(IInArchive* archive, int index, int propId, FILETIME *ft) { const NCOM::CPropVariant prop = readProperty(archive, index, propId); if (prop.vt != VT_FILETIME) { - throw SevenZipException(QCoreApplication::translate("QInstaller", - "Property %1 for item %2 not of type VT_FILETIME but %3").arg(QString::number(propId), + throw SevenZipException(QCoreApplication::translate("Lib7z", + "Property %1 for item %2 not of type VT_FILETIME but %3.").arg(QString::number(propId), QString::number(index), QString::number(prop.vt))); } - *fileTime = prop.filetime; - - if (IsFileTimeZero(fileTime)) - return false; - return true; + *ft = prop.filetime; + return !IsFileTimeZero(ft); } -static -QDateTime getDateTimeProperty(IInArchive* archive, int index, int propId, const QDateTime& defaultValue) +static bool getDateTimeProperty(IInArchive *arc, int index, int id, QDateTime *value) { - FILETIME fileTime; - if (!getFileTimeFromProperty(archive, index, propId, &fileTime)) - return defaultValue; + FILETIME ft7z; + if (!getFileTimeFromProperty(arc, index, id, &ft7z)) + return false; - FILETIME localFileTime; - if (!FileTimeToLocalFileTime(&fileTime, &localFileTime)) { - throw SevenZipException(QCoreApplication::translate("QInstaller", - "Could not convert file time to local time")); - } SYSTEMTIME st; - if (!BOOLToBool(FileTimeToSystemTime(&localFileTime, &st))) { - throw SevenZipException(QCoreApplication::translate("QInstaller", - "Could not convert local file time to system time")); + if (!BOOLToBool(FileTimeToSystemTime(&ft7z, &st))) { + throw SevenZipException(QCoreApplication::translate("Lib7z", + "Cannot convert UTC file time to system time.")); } - const QDate date(st.wYear, st.wMonth, st.wDay); - const QTime time(st.wHour, st.wMinute, st.wSecond); - QDateTime result(date, time); - - // fix daylight saving time - const bool dst = IsDST(); - if (dst != IsDST(result)) - result = result.addSecs(dst ? -3600 : 3600); - - return result; + *value = QDateTime(QDate(st.wYear, st.wMonth, st.wDay), QTime(st.wHour, st.wMinute, + st.wSecond), Qt::UTC); + return value->isValid(); } -static quint64 getUInt64Property(IInArchive* archive, int index, int propId, quint64 defaultValue=0) +static quint64 getUInt64Property(IInArchive *archive, int index, int propId, quint64 defaultValue) { - const NCOM::CPropVariant prop = readProperty(archive, index, propId); - if (prop.vt == VT_EMPTY) - return defaultValue; - return static_cast<quint64>(ConvertPropVariantToUInt64(prop)); + UInt64 value; + if (ConvertPropVariantToUInt64(readProperty(archive, index, propId), value)) + return value; + return defaultValue; } -static quint32 getUInt32Property(IInArchive* archive, int index, int propId, quint32 defaultValue=0) +static quint32 getUInt32Property(IInArchive *archive, int index, int propId, quint32 defaultValue) { const NCOM::CPropVariant prop = readProperty(archive, index, propId); if (prop.vt == VT_EMPTY) return defaultValue; - return static_cast< quint32 >(prop.ulVal); + return static_cast<quint32>(prop.ulVal); } -static QFile::Permissions getPermissions(IInArchive* archive, int index, bool* hasPermissions = 0) +static QFile::Permissions getPermissions(IInArchive *archive, int index, bool *hasPermissions) { quint32 attributes = getUInt32Property(archive, index, kpidAttrib, 0); QFile::Permissions permissions = 0; if (attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) { if (hasPermissions != 0) *hasPermissions = true; - // filter the unix permissions + // filter the Unix permissions attributes = (attributes >> 16) & 0777; - permissions |= static_cast< QFile::Permissions >((attributes & 0700) << 2); // owner rights - permissions |= static_cast< QFile::Permissions >((attributes & 0070) << 1); // group - permissions |= static_cast< QFile::Permissions >((attributes & 0007) << 0); // and world rights + permissions |= static_cast<QFile::Permissions>((attributes & 0700) << 2); // owner rights + permissions |= static_cast<QFile::Permissions>((attributes & 0070) << 1); // group + permissions |= static_cast<QFile::Permissions>((attributes & 0007) << 0); // and world rights } else if (hasPermissions != 0) { *hasPermissions = false; } return permissions; } -namespace Lib7z { +#define LIB7Z_ASSERTS(X, MODE) \ + Q_ASSERT(X); \ + Q_ASSERT(X->isOpen()); \ + Q_ASSERT(X->is ## MODE()); \ + Q_ASSERT(!X->isSequential()); + class QIODeviceSequentialOutStream : public ISequentialOutStream, public CMyUnknownImp { -public: - enum Behavior { - KeepDeviceUntouched, - CloseAndDeleteDevice - }; + Q_DISABLE_COPY(QIODeviceSequentialOutStream) +public: MY_UNKNOWN_IMP - explicit QIODeviceSequentialOutStream(QIODevice* device, Behavior behavior); - ~QIODeviceSequentialOutStream(); - QString errorString() const; - /* reimp */ STDMETHOD(Write)(const void* data, UInt32 size, UInt32* processedSize); - -private: - Behavior m_behavior; - QString m_errorString; - QPointer<QIODevice> m_device; -}; - -QIODeviceSequentialOutStream::QIODeviceSequentialOutStream(QIODevice* device, Behavior behavior) - : ISequentialOutStream() - , CMyUnknownImp() - , m_behavior(behavior) - , m_device(device) -{ - Q_ASSERT(m_device); - - if (!device->isOpen() && !m_device->open(QIODevice::WriteOnly)) - m_errorString = m_device->errorString(); -} - -QIODeviceSequentialOutStream::~QIODeviceSequentialOutStream() -{ - if (m_behavior == CloseAndDeleteDevice) { - m_device->close(); - delete m_device; - m_device = 0; + explicit QIODeviceSequentialOutStream(std::unique_ptr<QIODevice> device) + : ISequentialOutStream() + , m_device(std::move(device)) + { + LIB7Z_ASSERTS(m_device, Writable) } -} -QString QIODeviceSequentialOutStream::errorString() const -{ - return m_errorString; -} + QString errorString() const { + return m_errorString; + } -HRESULT QIODeviceSequentialOutStream::Write(const void* data, UInt32 size, UInt32* processedSize) -{ - if (!m_device) { + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize) + { if (processedSize) *processedSize = 0; - m_errorString = QCoreApplication::translate("QIODeviceSequentialOutStream", - "No device set for output stream"); - return E_FAIL; - } - if (!m_device->isOpen()) { - const bool opened = m_device->open(QIODevice::WriteOnly); - if (!opened) { - if (processedSize) - *processedSize = 0; + + const qint64 written = m_device->write(reinterpret_cast<const char*>(data), size); + if (written == -1) { m_errorString = m_device->errorString(); return E_FAIL; } - } - const qint64 written = m_device->write(reinterpret_cast<const char*>(data), size); - if (written == -1) { if (processedSize) - *processedSize = 0; - m_errorString = m_device->errorString(); - return E_FAIL; + *processedSize = written; + m_errorString.clear(); + return S_OK; } - if (processedSize) - *processedSize = written; - m_errorString = QString(); - return S_OK; -} +private: + QString m_errorString; + std::unique_ptr<QIODevice> m_device; +}; class QIODeviceInStream : public IInStream, public CMyUnknownImp { + Q_DISABLE_COPY(QIODeviceInStream) + public: MY_UNKNOWN_IMP - explicit QIODeviceInStream(QIODevice* device) : IInStream(), CMyUnknownImp(), m_device(device) + explicit QIODeviceInStream(QIODevice *device) + : IInStream() + , CMyUnknownImp() + , m_device(device) { - assert(m_device); - assert(!m_device->isSequential()); + LIB7Z_ASSERTS(m_device, Readable) } - /* reimp */ STDMETHOD(Read)(void* data, UInt32 size, UInt32* processedSize) + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize) { - assert(m_device); - assert(m_device->isReadable()); + if (m_device.isNull()) + return E_FAIL; + const qint64 actual = m_device->read(reinterpret_cast<char*>(data), size); Q_ASSERT(actual != 0 || m_device->atEnd()); if (processedSize) @@ -427,29 +441,28 @@ public: return actual >= 0 ? S_OK : E_FAIL; } - /* reimp */ STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64* newPosition) + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { - assert(m_device); - assert(!m_device->isSequential()); - assert(m_device->isReadable()); + if (m_device.isNull()) + return E_FAIL; if (seekOrigin > STREAM_SEEK_END) return STG_E_INVALIDFUNCTION; UInt64 np = 0; - switch(seekOrigin) { - case STREAM_SEEK_SET: - np = offset; - break; - case STREAM_SEEK_CUR: - np = m_device->pos() + offset; - break; - case STREAM_SEEK_END: - np = m_device->size() + offset; - break; - default: - return STG_E_INVALIDFUNCTION; + switch (seekOrigin) { + case STREAM_SEEK_SET: + np = offset; + break; + case STREAM_SEEK_CUR: + np = m_device->pos() + offset; + break; + case STREAM_SEEK_END: + np = m_device->size() + offset; + break; + default: + return STG_E_INVALIDFUNCTION; } - np = qBound(static_cast<UInt64>(0), np, static_cast<UInt64>(m_device->size() - 1)); + np = qBound(static_cast<UInt64>(0), np, static_cast<UInt64>(m_device->size())); const bool ok = m_device->seek(np); if (newPosition) *newPosition = np; @@ -459,1129 +472,652 @@ public: private: QPointer<QIODevice> m_device; }; -} - -File::File() - : permissions(0) - , uncompressedSize(0) - , compressedSize(0) - , isDirectory(false) -{ -} - -QVector<File> File::subtreeInPreorder() const -{ - QVector<File> res; - res += *this; - Q_FOREACH (const File& child, children) - res += child.subtreeInPreorder(); - return res; -} - -bool File::operator<(const File& other) const -{ - if (path != other.path) - return path < other.path; - if (mtime != other.mtime) - return mtime < other.mtime; - if (uncompressedSize != other.uncompressedSize) - return uncompressedSize < other.uncompressedSize; - if (compressedSize != other.compressedSize) - return compressedSize < other.compressedSize; - if (isDirectory != other.isDirectory) - return !isDirectory; - if (permissions != other.permissions) - return permissions < other.permissions; - return false; -} - -bool File::operator==(const File& other) const -{ - return mtime == other.mtime - && path == other.path - && uncompressedSize == other.uncompressedSize - && compressedSize == other.compressedSize - && isDirectory == other.isDirectory - && children == other.children - && (permissions == other.permissions - || permissions == static_cast< QFile::Permissions >(-1) - || other.permissions == static_cast< QFile::Permissions >(-1)); -} - -QByteArray Lib7z::formatKeyValuePairs(const QVariantList& l) -{ - assert(l.size() % 2 == 0); - QByteArray res; - for (QVariantList::ConstIterator it = l.constBegin(); it != l.constEnd(); ++it) { - if (!res.isEmpty()) - res += ", "; - res += qPrintable(it->toString()) + QByteArray(" = "); - ++it; - res += qPrintable(it->toString()); - } - return res; -} - -class Job::Private -{ -public: - Private() - : error(Lib7z::NoError) - {} - - int error; - QString errorString; -}; - -Job::Job(QObject* parent) - : QObject(parent) - , QRunnable() - , d(new Private) -{ -} - -Job::~Job() -{ - delete d; -} - -void Job::emitResult() -{ - emit finished(this); -} - -void Job::setError(int code) -{ - d->error = code; -} -void Job::setErrorString(const QString &str) +bool operator==(const File &lhs, const File &rhs) { - d->errorString = str; + return lhs.path == rhs.path + && lhs.utcTime == rhs.utcTime + && lhs.isDirectory == rhs.isDirectory + && lhs.compressedSize == rhs.compressedSize + && lhs.uncompressedSize == rhs.uncompressedSize + && (lhs.permissions == rhs.permissions + || lhs.permissions == static_cast<QFile::Permissions>(-1) + || rhs.permissions == static_cast<QFile::Permissions>(-1)); } -void Job::emitProgress(qint64 completed, qint64 total) +QVector<File> listArchive(QFileDevice *archive) { - emit progress(completed, total); -} - -int Job::error() const -{ - return d->error; -} + LIB7Z_ASSERTS(archive, Readable) -bool Job::hasError() const -{ - return d->error != NoError; -} - -void Job::run() -{ - doStart(); -} - -QString Job::errorString() const -{ - return d->errorString; -} + const qint64 initialPos = archive->pos(); + try { + CCodecs codecs; + if (codecs.Load() != S_OK) + throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs.")); -void Job::start() -{ - QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); -} + COpenOptions op; + op.codecs = &codecs; -class ListArchiveJob::Private -{ -public: - QPointer<QFileDevice> archive; - QVector<File> files; -}; + CObjectVector<COpenType> types; + op.types = &types; // Empty, because we use a stream. -ListArchiveJob::ListArchiveJob(QObject* parent) - : Job(parent) - , d(new Private) -{ -} + CIntVector excluded; + op.excludedFormats = &excluded; -ListArchiveJob::~ListArchiveJob() -{ - delete d; -} - -QFileDevice* ListArchiveJob::archive() const -{ - return d->archive; -} - -void ListArchiveJob::setArchive(QFileDevice* device) -{ - d->archive = device; -} + const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive); + op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). -QVector<File> ListArchiveJob::index() const -{ - return d->files; -} + CObjectVector<CProperty> properties; + op.props = &properties; -class OpenArchiveInfo -{ -private: - OpenArchiveInfo(QFileDevice* device) - : codecs(new CCodecs) - { - if (codecs->Load() != S_OK) { - throw SevenZipException(QCoreApplication::translate("OpenArchiveInfo", - "Could not load codecs")); - } - if (!codecs->FindFormatForArchiveType(L"", formatIndices)) { - throw SevenZipException(QCoreApplication::translate("OpenArchiveInfo", - "Could not retrieve default format")); - } - stream = new QIODeviceInStream(device); - if (archiveLink.Open2(codecs.data(), formatIndices, false, stream, UString(), 0) != S_OK) { - throw SevenZipException(QCoreApplication::translate("OpenArchiveInfo", - "Could not open archive")); + CArchiveLink archiveLink; + if (archiveLink.Open2(op, nullptr) != S_OK) { + throw SevenZipException(QCoreApplication::translate("Lib7z", + "Cannot open archive \"%1\".").arg(archive->fileName())); } - if (archiveLink.Arcs.Size() == 0) - throw SevenZipException(QCoreApplication::translate("OpenArchiveInfo", "No CArc found")); - - m_cleaner = new OpenArchiveInfoCleaner(); - m_cleaner->moveToThread(device->thread()); - QObject::connect(device, SIGNAL(destroyed(QObject*)), m_cleaner, SLOT(deviceDestroyed(QObject*))); - } - -public: - ~OpenArchiveInfo() - { - m_cleaner->deleteLater(); - } - - static OpenArchiveInfo* value(QFileDevice* device) - { - QMutexLocker _(&m_mutex); - if (!instances.contains(device)) - instances.insert(device, new OpenArchiveInfo(device)); - return instances.value(device); - } - - static OpenArchiveInfo* take(QFileDevice *device) - { - QMutexLocker _(&m_mutex); - if (instances.contains(device)) - return instances.take(device); - return 0; - } - - CArchiveLink archiveLink; - -private: - CIntVector formatIndices; - CMyComPtr<IInStream> stream; - QScopedPointer<CCodecs> codecs; - OpenArchiveInfoCleaner *m_cleaner; - - static QMutex m_mutex; - static QHash< QIODevice*, OpenArchiveInfo* > instances; -}; - -QMutex OpenArchiveInfo::m_mutex; -QHash< QIODevice*, OpenArchiveInfo* > OpenArchiveInfo::instances; - -void OpenArchiveInfoCleaner::deviceDestroyed(QObject* dev) -{ - QFileDevice* device = static_cast<QFileDevice*>(dev); - Q_ASSERT(device); - delete OpenArchiveInfo::take(device); -} - -QVector<File> Lib7z::listArchive(QFileDevice* archive) -{ - assert(archive); - try { - const OpenArchiveInfo* const openArchive = OpenArchiveInfo::value(archive); QVector<File> flat; - - for (int i = 0; i < openArchive->archiveLink.Arcs.Size(); ++i) { - const CArc& arc = openArchive->archiveLink.Arcs[i]; - IInArchive* const arch = arc.Archive; - + for (unsigned i = 0; i < archiveLink.Arcs.Size(); ++i) { + IInArchive *const arch = archiveLink.Arcs[i].Archive; UInt32 numItems = 0; if (arch->GetNumberOfItems(&numItems) != S_OK) { throw SevenZipException(QCoreApplication::translate("Lib7z", - "Could not retrieve number of items in archive")); + "Cannot retrieve number of items in archive.")); } flat.reserve(flat.size() + numItems); for (uint item = 0; item < numItems; ++item) { UString s; - if (arc.GetItemPath(item, s) != S_OK) { + if (archiveLink.Arcs[i].GetItemPath(item, s) != S_OK) { throw SevenZipException(QCoreApplication::translate("Lib7z", - "Could not retrieve path of archive item %1").arg(item)); + "Cannot retrieve path of archive item \"%1\".").arg(item)); } File f; f.archiveIndex.setX(i); f.archiveIndex.setY(item); f.path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')); - IsArchiveItemFolder(arch, item, f.isDirectory); - f.permissions = getPermissions(arch, item); - f.mtime = getDateTimeProperty(arch, item, kpidMTime, QDateTime()); + Archive_IsItem_Folder(arch, item, f.isDirectory); + f.permissions = getPermissions(arch, item, 0); + getDateTimeProperty(arch, item, kpidMTime, &(f.utcTime)); f.uncompressedSize = getUInt64Property(arch, item, kpidSize, 0); f.compressedSize = getUInt64Property(arch, item, kpidPackSize, 0); - flat.push_back(f); - qApp->processEvents(); + flat.append(f); } } return flat; - } catch (const SevenZipException& e) { - throw e; } catch (const char *err) { + archive->seek(initialPos); throw SevenZipException(err); + } catch (const SevenZipException &e) { + archive->seek(initialPos); + throw e; // re-throw unmodified } catch (...) { + archive->seek(initialPos); throw SevenZipException(QCoreApplication::translate("Lib7z", - "Unknown exception caught (%1)").arg(QString::fromLatin1(Q_FUNC_INFO))); + "Unknown exception caught (%1).").arg(QString::fromLatin1(Q_FUNC_INFO))); } return QVector<File>(); // never reached } -void ListArchiveJob::doStart() + +// -- ExtractCallback + +STDMETHODIMP ExtractCallback::SetTotal(UInt64 t) { - try { - if (!d->archive) - throw SevenZipException(tr("Could not list archive: QIODevice already destroyed.")); - d->files = listArchive(d->archive); - } catch (const SevenZipException& e) { - setError(Failed); - setErrorString(e.message()); - } catch (...) { - setError(Failed); - setErrorString(tr("Unknown exception caught (%1)").arg(tr("Failed"))); - } - emitResult(); + total = t; + return S_OK; } -class Lib7z::ExtractCallbackImpl : public IArchiveExtractCallback, public CMyUnknownImp +STDMETHODIMP ExtractCallback::SetCompleted(const UInt64 *c) { -public: - MY_UNKNOWN_IMP - explicit ExtractCallbackImpl(ExtractCallback* qq) - : q(qq) - , currentIndex(0) - , arc(0) - , total(0) - , completed(0) - , device(0) - { - } + completed = *c; + if (total > 0) + return setCompleted(completed, total); + return S_OK; +} - void setTarget(QIODevice* dev) - { - device = dev; - } +// this method will be called by CFolderOutStream::OpenFile to stream via +// CDecoder::CodeSpec extracted content to an output stream. +STDMETHODIMP ExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 /*askExtractMode*/) +{ + *outStream = 0; + if (targetDir.isEmpty()) + return E_FAIL; - void setTarget(const QString &targetDirectory) - { - targetDir = targetDirectory; - } + Q_ASSERT(arc); + currentIndex = index; - // this method will be called by CFolderOutStream::OpenFile to stream via - // CDecoder::CodeSpec extracted content to an output stream. - /* reimp */ STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream** outStream, Int32 askExtractMode) - { - Q_UNUSED(askExtractMode) - *outStream = 0; - if (device != 0) { - QIODeviceSequentialOutStream *qOutStream = new QIODeviceSequentialOutStream(device, - QIODeviceSequentialOutStream::KeepDeviceUntouched); - if (!qOutStream->errorString().isEmpty()) { - Lib7z::setLastError(qOutStream->errorString()); - return E_FAIL; - } - CMyComPtr<ISequentialOutStream> stream = qOutStream; - *outStream = stream.Detach(); - return S_OK; - } else if (!targetDir.isEmpty()) { - assert(arc); - - currentIndex = index; - - UString s; - if (arc->GetItemPath(index, s) != S_OK) { - Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl", - "Could not retrieve path of archive item %1").arg(index)); - return E_FAIL; - } + UString s; + if (arc->GetItemPath(index, s) != S_OK) { + setLastError(QCoreApplication::translate("ExtractCallbackImpl", + "Cannot retrieve path of archive item %1.").arg(index)); + return E_FAIL; + } - const QString path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')); - const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, path)); + const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, UString2QString(s))); - DirectoryGuard guard(fi.absolutePath()); - const QStringList directories = guard.tryCreate(); + DirectoryGuard guard(fi.absolutePath()); + const QStringList directories = guard.tryCreate(); - bool isDir = false; - IsArchiveItemFolder(arc->Archive, index, isDir); - if (isDir) - QDir(fi.absolutePath()).mkdir(fi.fileName()); + bool isDir = false; + Archive_IsItem_Folder(arc->Archive, index, isDir); + if (isDir) + QDir(fi.absolutePath()).mkdir(fi.fileName()); - // this makes sure that all directories created get removed as well - foreach (const QString &directory, directories) - q->setCurrentFile(directory); + // this makes sure that all directories created get removed as well + foreach (const QString &directory, directories) + setCurrentFile(directory); - if (!isDir && !q->prepareForFile(fi.absoluteFilePath())) - return E_FAIL; + if (!isDir && !prepareForFile(fi.absoluteFilePath())) + return E_FAIL; - q->setCurrentFile(fi.absoluteFilePath()); + setCurrentFile(fi.absoluteFilePath()); - if (!isDir) { + if (!isDir) { #ifndef Q_OS_WIN - // do not follow symlinks, so we need to remove an existing one - if (fi.isSymLink() && (!QFile::remove(fi.absoluteFilePath()))) { - Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl", - "Could not remove already existing symlink. %1").arg(fi.absoluteFilePath())); - return E_FAIL; - } + // do not follow symlinks, so we need to remove an existing one + if (fi.isSymLink() && (!QFile::remove(fi.absoluteFilePath()))) { + setLastError(QCoreApplication::translate("ExtractCallbackImpl", + "Cannot remove already existing symlink %1.").arg(fi.absoluteFilePath())); + return E_FAIL; + } #endif - QIODeviceSequentialOutStream *qOutStream = new QIODeviceSequentialOutStream( - new QFile(fi.absoluteFilePath()), QIODeviceSequentialOutStream::CloseAndDeleteDevice); - if (!qOutStream->errorString().isEmpty()) { - Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl", - "Could not open file: %1 (%2)").arg(fi.absoluteFilePath(), - qOutStream->errorString())); - return E_FAIL; - } - CMyComPtr<ISequentialOutStream> stream = qOutStream; - *outStream = stream; - stream.Detach(); - } - - guard.release(); - return S_OK; + std::unique_ptr<QFile> file(new QFile(fi.absoluteFilePath())); + if (!file->open(QIODevice::WriteOnly)) { + setLastError(QCoreApplication::translate("ExtractCallbackImpl", + "Cannot open file \"%1\" for writing: %2").arg( + QDir::toNativeSeparators(fi.absoluteFilePath()), file->errorString())); + return E_FAIL; } - return E_FAIL; + CMyComPtr<ISequentialOutStream> stream = + new QIODeviceSequentialOutStream(std::move(file)); + *outStream = stream.Detach(); // CMyComPtr is needed, otherwise it crashes in Write(). } - /* reimp */ STDMETHOD(PrepareOperation)(Int32 askExtractMode) - { - Q_UNUSED(askExtractMode) + guard.release(); + return S_OK; +} + +STDMETHODIMP ExtractCallback::PrepareOperation(Int32 /*askExtractMode*/) +{ + return S_OK; +} + +STDMETHODIMP ExtractCallback::SetOperationResult(Int32 /*resultEOperationResult*/) +{ + if (targetDir.isEmpty()) return S_OK; - } - /* reimp */ STDMETHOD(SetOperationResult)(Int32 resultEOperationResult) - { - Q_UNUSED(resultEOperationResult) + UString s; + if (arc->GetItemPath(currentIndex, s) != S_OK) { + setLastError(QCoreApplication::translate("ExtractCallbackImpl", + "Cannot retrieve path of archive item %1.").arg(currentIndex)); + return E_FAIL; + } - if (!targetDir.isEmpty()) { - bool hasPerm = false; - const QFile::Permissions permissions = getPermissions(arc->Archive, currentIndex, &hasPerm); + const QString absFilePath = QFileInfo(QString::fromLatin1("%1/%2").arg(targetDir, + UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')))).absoluteFilePath(); - UString s; - if (arc->GetItemPath(currentIndex, s) != S_OK) { - Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl", - "Could not retrieve path of archive item %1").arg(currentIndex)); - return E_FAIL; - } - const QString path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')); - const QString absFilePath = QFileInfo(QString::fromLatin1("%1/%2").arg(targetDir, path)) - .absoluteFilePath(); - - // do we have a symlink? - const quint32 attributes = getUInt32Property(arc->Archive, currentIndex, kpidAttrib, 0); - struct stat stat_info; - stat_info.st_mode = attributes >> 16; - if (S_ISLNK(stat_info.st_mode)) { + // do we have a symlink? + const quint32 attributes = getUInt32Property(arc->Archive, currentIndex, kpidAttrib, 0); + struct stat stat_info; + stat_info.st_mode = attributes >> 16; + if (S_ISLNK(stat_info.st_mode)) { #ifdef Q_OS_WIN - qFatal(QString::fromLatin1("Creating a link from archive is not implemented for windows. " - "Link filename: %1").arg(absFilePath).toLatin1()); - // TODO -// if (!CreateHardLinkWrapper(absFilePath, QLatin1String(symlinkTarget))) { -// return S_FALSE; -// } + qFatal(QString::fromLatin1("Creating a link from archive is not implemented for " + "windows. Link filename: %1").arg(absFilePath).toLatin1()); + // TODO + //if (!NFile::NDir::MyCreateHardLink(CFSTR(QDir::toNativeSeparators(absFilePath).utf16()), + // CFSTR(QDir::toNativeSeparators(symlinkTarget).utf16())) { + // return S_FALSE; + //} #else - QFileInfo symlinkPlaceHolderFileInfo(absFilePath); - if (symlinkPlaceHolderFileInfo.isSymLink()) { - Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl", - "Could not create symlink at '%1'. Another one is already existing.") - .arg(absFilePath)); - return E_FAIL; - } - QFile symlinkPlaceHolderFile(absFilePath); - if (!symlinkPlaceHolderFile.open(QIODevice::ReadOnly)) { - Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl", - "Could not read symlink target from file '%1'.").arg(absFilePath)); - return E_FAIL; - } + QFileInfo symlinkPlaceHolderFileInfo(absFilePath); + if (symlinkPlaceHolderFileInfo.isSymLink()) { + setLastError(QCoreApplication::translate("ExtractCallbackImpl", + "Cannot create symlink at \"%1\". Another one is already existing.") + .arg(absFilePath)); + return E_FAIL; + } + QFile symlinkPlaceHolderFile(absFilePath); + if (!symlinkPlaceHolderFile.open(QIODevice::ReadOnly)) { + setLastError(QCoreApplication::translate("ExtractCallbackImpl", + "Cannot read symlink target from file \"%1\".").arg(absFilePath)); + return E_FAIL; + } - const QByteArray symlinkTarget = symlinkPlaceHolderFile.readAll(); - symlinkPlaceHolderFile.close(); - symlinkPlaceHolderFile.remove(); - QFile targetFile(QString::fromLatin1(symlinkTarget)); - if (!targetFile.link(absFilePath)) { - Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl", - "Could not create symlink at %1. %2").arg(absFilePath, - targetFile.errorString())); - return E_FAIL; - } - return S_OK; + const QByteArray symlinkTarget = symlinkPlaceHolderFile.readAll(); + symlinkPlaceHolderFile.close(); + symlinkPlaceHolderFile.remove(); + QFile targetFile(QString::fromLatin1(symlinkTarget)); + if (!targetFile.link(absFilePath)) { + setLastError(QCoreApplication::translate("ExtractCallbackImpl", + "Cannot create symlink at %1: %2").arg(absFilePath, + targetFile.errorString())); + return E_FAIL; + } + return S_OK; #endif - } + } - try { - if (!absFilePath.isEmpty()) { - // This might fail for archives without all properties, we can only be sure about - // modification time, as it's always stored by default in 7z archives. Also note that - // we restore modification time on Unix only, as access time and change time are - // supposed to be set to the time of installation. - FILETIME mTime; - if (getFileTimeFromProperty(arc->Archive, currentIndex, kpidMTime, &mTime)) { - NWindows::NFile::NIO::COutFile file; - if (file.Open(QString2UString(absFilePath), 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL)) - file.SetTime(&mTime, &mTime, &mTime); - } + try { // Note: This part might also fail while running a elevated installation. + if (!absFilePath.isEmpty()) { + // This might fail for archives without all properties, we can only be sure + // about modification time, as it's always stored by default in 7z archives. + // Also note that we restore modification time on Unix only, as access time + // and change time are supposed to be set to the time of installation. + FILETIME mTime; + const UString fileName = QString2UString(absFilePath); + if (getFileTimeFromProperty(arc->Archive, currentIndex, kpidMTime, &mTime)) { + NWindows::NFile::NIO::COutFile file; + if (file.Open(fileName, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL)) + file.SetTime(&mTime, &mTime, &mTime); + } #ifdef Q_OS_WIN - FILETIME cTime, aTime; - bool success = getFileTimeFromProperty(arc->Archive, currentIndex, kpidCTime, &cTime); - if (success && getFileTimeFromProperty(arc->Archive, currentIndex, kpidATime, &aTime)) { - NWindows::NFile::NIO::COutFile file; - if (file.Open(QString2UString(absFilePath), 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL)) - file.SetTime(&cTime, &aTime, &mTime); - } + FILETIME cTime, aTime; + if (getFileTimeFromProperty(arc->Archive, currentIndex, kpidCTime, &cTime) + && getFileTimeFromProperty(arc->Archive, currentIndex, kpidATime, &aTime)) { + NWindows::NFile::NIO::COutFile file; + if (file.Open(fileName, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL)) + file.SetTime(&cTime, &aTime, &mTime); + } #endif - } - } catch (...) {} - - if (hasPerm) - QFile::setPermissions(absFilePath, permissions); } + } catch (...) {} - return S_OK; - } - - /* reimp */ STDMETHOD(SetTotal)(UInt64 t) - { - total = t; - return S_OK; - } - - /* reimp */ STDMETHOD(SetCompleted)(const UInt64* c) - { - completed = *c; - if (total > 0) - return q->setCompleted(completed, total); - return S_OK; - } - - void setArchive(const CArc* archive) - { - arc = archive; - } + bool hasPerm = false; + const QFile::Permissions permissions = getPermissions(arc->Archive, currentIndex, &hasPerm); + if (hasPerm) + QFile::setPermissions(absFilePath, permissions); + return S_OK; +} -private: - ExtractCallback* const q; - UInt32 currentIndex; - const CArc* arc; - UInt64 total; - UInt64 completed; - QPointer<QIODevice> device; - QString targetDir; -}; +/*! + \namespace Lib7z + \inmodule QtInstallerFramework + \brief The Lib7z namespace contains miscellaneous identifiers used throughout the Lib7z library. +*/ +/*! + \fn virtual bool Lib7z::ExtractCallback::prepareForFile(const QString &filename) -class Lib7z::ExtractCallbackPrivate -{ -public: - explicit ExtractCallbackPrivate(ExtractCallback* qq) - { - impl = new ExtractCallbackImpl(qq); - } + Implement to prepare for file \a filename to be extracted, e.g. by renaming existing files. + Return \c true if the preparation was successful and extraction can be continued. If \c false + is returned, the extraction will be aborted. The default implementation returns \c true. +*/ - CMyComPtr<ExtractCallbackImpl> impl; -}; -ExtractCallback::ExtractCallback() - : d(new ExtractCallbackPrivate(this)) -{ -} +// -- UpdateCallback -ExtractCallback::~ExtractCallback() +HRESULT UpdateCallback::SetTotal(UInt64) { - delete d; + return S_OK; } -ExtractCallbackImpl* ExtractCallback::impl() +HRESULT UpdateCallback::SetCompleted(const UInt64*) { - return d->impl; + return S_OK; } -const ExtractCallbackImpl* ExtractCallback::impl() const +HRESULT UpdateCallback::SetRatioInfo(const UInt64*, const UInt64*) { - return d->impl; + return S_OK; } -void ExtractCallback::setTarget(QFileDevice* dev) +HRESULT UpdateCallback::CheckBreak() { - d->impl->setTarget(dev); + return S_OK; } -void ExtractCallback::setTarget(const QString &dir) +HRESULT UpdateCallback::Finilize() { - d->impl->setTarget(dir); + return S_OK; } -HRESULT ExtractCallback::setCompleted(quint64, quint64) +HRESULT UpdateCallback::SetNumFiles(UInt64) { return S_OK; } -void ExtractCallback::setCurrentFile(const QString&) +HRESULT UpdateCallback::GetStream(const wchar_t*, bool) { + return S_OK; } -bool ExtractCallback::prepareForFile(const QString&) +HRESULT UpdateCallback::OpenFileError(const wchar_t*, DWORD) { - return true; + return S_OK; } -class Lib7z::ExtractCallbackJobImpl : public ExtractCallback -{ -public: - explicit ExtractCallbackJobImpl(ExtractItemJob* j) - : ExtractCallback() - , job(j) - {} - -private: - /* reimp */ HRESULT setCompleted(quint64 c, quint64 t) - { - emit job->progress(c, t); - return S_OK; - } - - ExtractItemJob* const job; -}; - -class Lib7z::UpdateCallbackImpl : public IUpdateCallbackUI2, public CMyUnknownImp -{ -public: - MY_UNKNOWN_IMP - UpdateCallbackImpl() - { - } - virtual ~UpdateCallbackImpl() - { - } - /** - * \reimp - */ - HRESULT SetTotal(UInt64) - { - return S_OK; - } - /** - * \reimp - */ - HRESULT SetCompleted(const UInt64*) - { - return S_OK; - } - HRESULT SetRatioInfo(const UInt64*, const UInt64*) - { - return S_OK; - } - HRESULT CheckBreak() - { - return S_OK; - } - HRESULT Finilize() - { - return S_OK; - } - HRESULT SetNumFiles(UInt64) - { - return S_OK; - } - HRESULT GetStream(const wchar_t*, bool) - { - return S_OK; - } - HRESULT OpenFileError(const wchar_t*, DWORD) - { - return S_OK; - } - HRESULT CryptoGetTextPassword2(Int32* passwordIsDefined, OLECHAR** password) - { - *password = 0; - *passwordIsDefined = false; - return S_OK; - } - HRESULT CryptoGetTextPassword(OLECHAR**) - { - return E_NOTIMPL; - } - HRESULT OpenResult(const wchar_t*, LONG) - { - return S_OK; - } - HRESULT StartScanning() - { - return S_OK; - } - HRESULT ScanProgress(UInt64, UInt64, const wchar_t*) - { - return S_OK; - } - HRESULT CanNotFindError(const wchar_t*, DWORD) - { - return S_OK; - } - HRESULT FinishScanning() - { - return S_OK; - } - HRESULT StartArchive(const wchar_t*, bool) - { - return S_OK; - } - HRESULT FinishArchive() - { - return S_OK; - } - - /** - * \reimp - */ - HRESULT SetOperationResult(Int32) - { - // TODO! - return S_OK; - } - void setSourcePaths(const QStringList &paths) - { - sourcePaths = paths; - } - void setTarget(QIODevice* archive) - { - target = archive; - } - -private: - QIODevice* target; - QStringList sourcePaths; -}; - -class Lib7z::UpdateCallbackPrivate -{ -public: - UpdateCallbackPrivate() - { - m_impl = new UpdateCallbackImpl; - } - - UpdateCallbackImpl* impl() - { - return m_impl; - } - -private: - CMyComPtr< UpdateCallbackImpl > m_impl; -}; - -UpdateCallback::UpdateCallback() - : d(new UpdateCallbackPrivate) +HRESULT UpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) { + *password = 0; + *passwordIsDefined = false; + return S_OK; } -UpdateCallback::~UpdateCallback() +HRESULT UpdateCallback::CryptoGetTextPassword(BSTR *password) { - delete d; + *password = 0; + return E_NOTIMPL; } -UpdateCallbackImpl* UpdateCallback::impl() +HRESULT UpdateCallback::OpenResult(const wchar_t*, HRESULT, const wchar_t*) { - return d->impl(); + return S_OK; } -void UpdateCallback::setSourcePaths(const QStringList &paths) +HRESULT UpdateCallback::StartScanning() { - d->impl()->setSourcePaths(paths); + return S_OK; } -void UpdateCallback::setTarget(QFileDevice* target) +HRESULT UpdateCallback::ScanProgress(UInt64, UInt64, UInt64, const wchar_t*, bool) { - d->impl()->setTarget(target); + return S_OK; } -class ExtractItemJob::Private -{ -public: - Private(ExtractItemJob* qq) - : q(qq) - , target(0) - , callback(new ExtractCallbackJobImpl(q)) - { - } - - ExtractItemJob* q; - File item; - QPointer<QFileDevice> archive; - QString targetDirectory; - QFileDevice* target; - ExtractCallback* callback; -}; - -ExtractItemJob::ExtractItemJob(QObject* parent) - : Job(parent) - , d(new Private(this)) +HRESULT UpdateCallback::CanNotFindError(const wchar_t*, DWORD) { + return S_OK; } -ExtractItemJob::~ExtractItemJob() +HRESULT UpdateCallback::FinishScanning() { - delete d; + return S_OK; } -File ExtractItemJob::item() const +HRESULT UpdateCallback::StartArchive(const wchar_t*, bool) { - return d->item; + return S_OK; } -void ExtractItemJob::setItem(const File& item) +HRESULT UpdateCallback::FinishArchive() { - d->item = item; + return S_OK; } -QFileDevice* ExtractItemJob::archive() const +HRESULT UpdateCallback::SetOperationResult(Int32) { - return d->archive; + return S_OK; } -void ExtractItemJob::setArchive(QFileDevice* archive) +/*! + Function to create an empty 7z container. Using a temporary file only is not working, since + 7z checks the output file for a valid signature, otherwise it rejects overwriting the file. +*/ +static QString createTmp7z() { - d->archive = archive; -} + QTemporaryFile file; + if (!file.open()) { + throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot create " + "temporary file: %1").arg(file.errorString())); + } -QString ExtractItemJob::targetDirectory() const -{ - return d->targetDirectory; + file.write(QByteArray::fromHex("377A.BCAF.271C" // Signature. + ".0003.8D9B.D50F.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000" // Crc + Data. + )); + file.setAutoRemove(false); + return file.fileName(); } -void ExtractItemJob::setTargetDirectory(const QString &dir) -{ - d->targetDirectory = dir; - d->target = 0; -} +/*! + Creates an archive using the given file device \a archive. \a sourcePaths can contain one or + more files, one or more directories or a combination of files and folders. The \c * wildcard + is supported also. The value of \a level specifies the compression ratio, the default is set + to \c 5 (Normal compression). The \a callback can be used to get information about the archive + creation process. If no \a callback is given, an empty implementation is used. -void ExtractItemJob::setTarget(QFileDevice* dev) + \note Throws SevenZipException on error. + \note Filenames are stored case-sensitive with UTF-8 encoding. + \note The ownership of \a callback is transferred to the function and gets delete on exit. +*/ +void INSTALLER_EXPORT createArchive(QFileDevice *archive, const QStringList &sources, + Compression level, UpdateCallback *callback) { - d->target = dev; -} + LIB7Z_ASSERTS(archive, Writable) -namespace{ - QString errorMessageFrom7zResult(const LONG & extractResult) - { - if (!Lib7z::lastError().isEmpty()) - return Lib7z::lastError(); - - QString errorMessage = QCoreApplication::translate("Lib7z", "internal code: %1"); - switch (extractResult) { - case S_OK: - qFatal("S_OK value is not a valid error code."); - break; - case E_NOTIMPL: - errorMessage = errorMessage.arg(QLatin1String("E_NOTIMPL")); - break; - case E_NOINTERFACE: - errorMessage = errorMessage.arg(QLatin1String("E_NOINTERFACE")); - break; - case E_ABORT: - errorMessage = errorMessage.arg(QLatin1String("E_ABORT")); - break; - case E_FAIL: - errorMessage = errorMessage.arg(QLatin1String("E_FAIL")); - break; - case STG_E_INVALIDFUNCTION: - errorMessage = errorMessage.arg(QLatin1String("STG_E_INVALIDFUNCTION")); - break; - case E_OUTOFMEMORY: - errorMessage = QCoreApplication::translate("Lib7z", "not enough memory"); - break; - case E_INVALIDARG: - errorMessage = errorMessage.arg(QLatin1String("E_INVALIDARG")); - break; - default: - errorMessage = QCoreApplication::translate("Lib7z", "Error: %1").arg(extractResult); - break; - } - return errorMessage; - } -} + const QString tmpArchive = createTmp7z(); + Lib7z::createArchive(tmpArchive, sources, QTmpFile::No, level, callback); -void Lib7z::createArchive(QFileDevice* archive, const QStringList &sourcePaths, UpdateCallback* callback) + try { + QFile source(tmpArchive); + QInstaller::openForRead(&source); + QInstaller::blockingCopy(&source, archive, source.size()); + } catch (const QInstaller::Error &error) { + throw SevenZipException(error.message()); + } +} + +/*! + Creates an archive with the given filename \a archive. \a sourcePaths can contain one or more + files, one or more directories or a combination of files and folders. Also the \c * wildcard + is supported. To be able to use the function during an elevated installation, set \a mode to + \c QTmpFile::Yes. The value of \a level specifies the compression ratio, the default is set + to \c 5 (Normal compression). The \a callback can be used to get information about the archive + creation process. If no \a callback is given, an empty implementation is used. + + \note Throws SevenZipException on error. + \note If \a archive exists, it will be overwritten. + \note Filenames are stored case-sensitive with UTF-8 encoding. + \note The ownership of \a callback is transferred to the function and gets delete on exit. +*/ +void createArchive(const QString &archive, const QStringList &sources, QTmpFile mode, + Compression level, UpdateCallback *callback) { - assert(archive); - - QScopedPointer<UpdateCallback> dummyCallback(callback ? 0 : new UpdateCallback); - if (!callback) - callback = dummyCallback.data(); - try { - callback->setTarget(archive); - - QScopedPointer<CCodecs> codecs(new CCodecs); - if (codecs->Load() != S_OK) - throw SevenZipException(QCoreApplication::translate("Lib7z", "Could not load codecs")); - - CIntVector formatIndices; - - if (!codecs.data()->FindFormatForArchiveType(L"", formatIndices)) { - throw SevenZipException(QCoreApplication::translate("Lib7z", - "Could not retrieve default format")); - } - - // yes this is crap, but there seems to be no streaming solution to this... - - const QString tempFile = generateTempFileName(); - - NWildcard::CCensor censor; - foreach (const QString &path, sourcePaths) { - const QString cleanPath = QDir::toNativeSeparators(QDir::cleanPath(path)); - const UString nativePath = QString2UString(cleanPath); - if (UString2QString(nativePath) != cleanPath) { - throw SevenZipException(QCoreApplication::translate("Lib7z", "Could not convert" - " path: %1.").arg(path)); - } - censor.AddItem(true /* always include item */, nativePath, false /* never recurse*/); + QString target = archive; + if (mode == QTmpFile::Yes) + target = createTmp7z(); + + CArcCmdLineOptions options; + try { + UStringVector commandStrings; + commandStrings.Add(L"a"); // mode: add + commandStrings.Add(L"-t7z"); // type: 7z + commandStrings.Add(L"-mtm=on"); // time: modeifier|creation|access + commandStrings.Add(L"-mtc=on"); + commandStrings.Add(L"-mta=on"); + commandStrings.Add(L"-mmt=on"); // threads: multi-threaded +#ifdef Q_OS_WIN + commandStrings.Add(L"-sccUTF-8"); // files: case-sensitive|UTF8 +#endif + commandStrings.Add(QString2UString(QString::fromLatin1("-mx=%1").arg(int(level)))); // compression: level + commandStrings.Add(QString2UString(QDir::toNativeSeparators(target))); + foreach (const QString &source, sources) + commandStrings.Add(QString2UString(source)); + + CArcCmdLineParser parser; + parser.Parse1(commandStrings, options); + parser.Parse2(options); + } catch (const CArcCmdLineException &e) { + throw SevenZipException(UString2QString(e)); } - callback->setSourcePaths(sourcePaths); - - CArchivePath archivePath; - archivePath.ParseFromPath(QString2UString(tempFile)); - CUpdateArchiveCommand command; - command.ArchivePath = archivePath; - command.ActionSet = NUpdateArchive::kAddActionSet; + CCodecs codecs; + if (codecs.Load() != S_OK) + throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs.")); - CUpdateOptions options; - options.Commands.Add(command); - options.ArchivePath = archivePath; - options.MethodMode.FormatIndex = codecs->FindFormatForArchiveType(L"7z"); - - // preserve creation time - CProperty tc; - tc.Name = UString(L"TC"); - tc.Value = UString(L"ON"); - options.MethodMode.Properties.Add(tc); - - // preserve access time - CProperty ta; - ta.Name = UString(L"TA"); - ta.Value = UString(L"ON"); - options.MethodMode.Properties.Add(ta); + CObjectVector<COpenType> types; + if (!ParseOpenTypes(codecs, options.ArcType, types)) + throw SevenZipException(QCoreApplication::translate("Lib7z", "Unsupported archive type.")); CUpdateErrorInfo errorInfo; - const HRESULT res = UpdateArchive(codecs.data(), censor, options, errorInfo, 0, callback->impl()); - if (res != S_OK || !QFile::exists(tempFile)) { - throw SevenZipException(QCoreApplication::translate("Lib7z", - "Could not create archive %1. %2").arg(tempFile, errorMessageFrom7zResult(res))); + CMyComPtr<UpdateCallback> comCallback = callback == 0 ? new UpdateCallback : callback; + const HRESULT res = UpdateArchive(&codecs, types, options.ArchiveName, options.Censor, + options.UpdateOptions, errorInfo, nullptr, comCallback, true); + + const QFile tempFile(UString2QString(options.ArchiveName)); + if (res != S_OK || !tempFile.exists()) { + QString errorMsg; + if (res == S_OK) { + errorMsg = QCoreApplication::translate("Lib7z", "Cannot create archive \"%1\"") + .arg(QDir::toNativeSeparators(tempFile.fileName())); + } else { + errorMsg = QCoreApplication::translate("Lib7z", "Cannot create archive \"%1\": %2") + .arg(QDir::toNativeSeparators(tempFile.fileName()), errorMessageFrom7zResult(res)); + } + throw SevenZipException(errorMsg); } - { - //TODO remove temp file even if one the following throws - QFile file(tempFile); - QInstaller::openForRead(&file); - QInstaller::blockingCopy(&file, archive, file.size()); - } + if (mode == QTmpFile::Yes) { + QFile org(archive); + if (org.exists() && !org.remove()) { + throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot remove " + "old archive \"%1\": %2").arg(QDir::toNativeSeparators(org.fileName()), + org.errorString())); + } - QFile file(tempFile); - if (!file.remove()) { - qWarning("%s: Could not remove temporary file %s: %s", Q_FUNC_INFO, qPrintable(tempFile), - qPrintable(file.errorString())); + QFile arc(UString2QString(options.ArchiveName)); + if(!arc.rename(archive)) { + throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot rename " + "temporary archive \"%1\" to \"%2\": %3").arg( + QDir::toNativeSeparators(arc.fileName()), + QDir::toNativeSeparators(archive), + arc.errorString())); + } } } catch (const char *err) { - qDebug() << err; throw SevenZipException(err); + } catch (SevenZipException &e) { + throw e; // re-throw unmodified } catch (const QInstaller::Error &err) { throw SevenZipException(err.message()); } catch (...) { - throw SevenZipException(QCoreApplication::translate("Lib7z", "Unknown exception caught (%1)") - .arg(QString::fromLatin1(Q_FUNC_INFO))); + throw SevenZipException(QCoreApplication::translate("Lib7z", + "Unknown exception caught (%1)").arg(QString::fromLatin1(Q_FUNC_INFO))); } } -void Lib7z::extractFileFromArchive(QFileDevice* archive, const File& item, QFileDevice* target, - ExtractCallback* callback) +/*! + Extracts the given \a archive content into target directory \a directory using the provided + extract callback \a callback. The output filenames are deduced from the \a archive content. + + \note Throws SevenZipException on error. + \note The ownership of \a callback is not transferred to the function. +*/ +void extractArchive(QFileDevice *archive, const QString &directory, ExtractCallback *callback) { - assert(archive); - assert(target); + LIB7Z_ASSERTS(archive, Readable) - std::auto_ptr<ExtractCallback> dummyCallback(callback ? 0 : new ExtractCallback); - if (!callback) - callback = dummyCallback.get(); + // Guard a given object against unwanted delete. + CMyComPtr<ExtractCallback> externCallback = callback; + + CMyComPtr<ExtractCallback> localCallback; + if (!externCallback) { + callback = new ExtractCallback; + localCallback = callback; + } + DirectoryGuard outDir(QFileInfo(directory).absolutePath()); try { - const OpenArchiveInfo* const openArchive = OpenArchiveInfo::value(archive); + outDir.tryCreate(); - const int arcIdx = item.archiveIndex.x(); - if (arcIdx < 0 || arcIdx >= openArchive->archiveLink.Arcs.Size()) { - throw SevenZipException(QCoreApplication::translate("Lib7z", - "CArc index %1 out of bounds [0, %2]").arg(openArchive->archiveLink.Arcs.Size() - 1)); - } - const CArc& arc = openArchive->archiveLink.Arcs[arcIdx]; - IInArchive* const parchive = arc.Archive; + CCodecs codecs; + if (codecs.Load() != S_OK) + throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs.")); - const UInt32 itemIdx = item.archiveIndex.y(); - UInt32 numItems = 0; - if (parchive->GetNumberOfItems(&numItems) != S_OK) { - throw SevenZipException(QCoreApplication::translate("Lib7z", - "Could not retrieve number of items in archive")); - } + COpenOptions op; + op.codecs = &codecs; - if (itemIdx >= numItems) { - throw SevenZipException(QCoreApplication::translate("Lib7z", - "Item index %1 out of bounds [0, %2]").arg(itemIdx).arg(numItems - 1)); - } + CObjectVector<COpenType> types; + op.types = &types; // Empty, because we use a stream. - UString s; - if (arc.GetItemPath(itemIdx, s) != S_OK) { - throw SevenZipException(QCoreApplication::translate("Lib7z", - "Could not retrieve path of archive item %1").arg(itemIdx)); - } - assert(item.path == UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/'))); + CIntVector excluded; + op.excludedFormats = &excluded; - callback->setTarget(target); - const LONG extractResult = parchive->Extract(&itemIdx, 1, /*testmode=*/0, callback->impl()); + const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive); + op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). - if (extractResult != S_OK) - throw SevenZipException(errorMessageFrom7zResult(extractResult)); + CObjectVector<CProperty> properties; + op.props = &properties; - } catch (const char *err) { - throw SevenZipException(err); - } catch (const Lib7z::SevenZipException& e) { - throw e; - } catch (...) { - throw SevenZipException(QCoreApplication::translate("Lib7z", "Unknown exception caught (%1)") - .arg(QString::fromLatin1(Q_FUNC_INFO))); - } -} - -void Lib7z::extractFileFromArchive(QFileDevice* archive, const File& item, - const QString &targetDirectory, ExtractCallback* callback) -{ - assert(archive); + CArchiveLink archiveLink; + if (archiveLink.Open2(op, nullptr) != S_OK) { + throw SevenZipException(QCoreApplication::translate("Lib7z", + "Cannot open archive \"%1\".").arg(archive->fileName())); + } - QScopedPointer<ExtractCallback> dummyCallback(callback ? 0 : new ExtractCallback); - if (!callback) - callback = dummyCallback.data(); + callback->setTarget(directory); + for (unsigned a = 0; a < archiveLink.Arcs.Size(); ++a) { + callback->setArchive(&archiveLink.Arcs[a]); + IInArchive *const arch = archiveLink.Arcs[a].Archive; - QFileInfo fi(targetDirectory + QLatin1String("/") + item.path); - DirectoryGuard outDir(fi.absolutePath()); - outDir.tryCreate(); - QFile out(fi.absoluteFilePath()); - if (!out.open(QIODevice::WriteOnly)) { //TODO use tmp file + const LONG result = arch->Extract(0, static_cast<UInt32>(-1), false, callback); + if (result != S_OK) + throw SevenZipException(errorMessageFrom7zResult(result)); + } + } catch (const SevenZipException &e) { + externCallback.Detach(); + throw e; // re-throw unmodified + } catch (...) { + externCallback.Detach(); throw SevenZipException(QCoreApplication::translate("Lib7z", - "Could not create output file for writing: %1").arg(fi.absoluteFilePath())); + "Unknown exception caught (%1).").arg(QString::fromLatin1(Q_FUNC_INFO))); } - callback->setTarget(&out); - extractFileFromArchive(archive, item, &out, callback); - if (item.permissions) - out.setPermissions(item.permissions); outDir.release(); + externCallback.Detach(); } -void Lib7z::extractArchive(QFileDevice* archive, const QString &targetDirectory, - ExtractCallback* callback) -{ - assert(archive); - - QScopedPointer<ExtractCallback> dummyCallback(callback ? 0 : new ExtractCallback); - if (!callback) - callback = dummyCallback.data(); - - callback->setTarget(targetDirectory); - - const QFileInfo fi(targetDirectory); - DirectoryGuard outDir(fi.absolutePath()); - outDir.tryCreate(); - - const OpenArchiveInfo* const openArchive = OpenArchiveInfo::value(archive); - - for (int a = 0; a < openArchive->archiveLink.Arcs.Size(); ++a) - { - const CArc& arc = openArchive->archiveLink.Arcs[a]; - IInArchive* const arch = arc.Archive; - callback->impl()->setArchive(&arc); - const LONG extractResult = arch->Extract(0, static_cast< UInt32 >(-1), false, callback->impl()); - - if (extractResult != S_OK) - throw SevenZipException(errorMessageFrom7zResult(extractResult)); - } - - outDir.release(); -} +/*! + Returns \c true if the given \a archive is supported; otherwise returns \c false. -bool Lib7z::isSupportedArchive(const QString &archive) + \note Throws SevenZipException on error. +*/ +bool isSupportedArchive(QFileDevice *archive) { - QFile file(archive); - if (!file.open(QIODevice::ReadOnly)) - return false; + LIB7Z_ASSERTS(archive, Readable) - return isSupportedArchive(&file); -} - -bool Lib7z::isSupportedArchive(QFileDevice* archive) -{ - assert(archive); - assert(!archive->isSequential()); const qint64 initialPos = archive->pos(); try { - QScopedPointer<CCodecs> codecs(new CCodecs); - if (codecs->Load() != S_OK) - throw SevenZipException(QCoreApplication::translate("Lib7z", "Could not load codecs")); + CCodecs codecs; + if (codecs.Load() != S_OK) + throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs.")); - CIntVector formatIndices; + COpenOptions op; + op.codecs = &codecs; + + CObjectVector<COpenType> types; + op.types = &types; // Empty, because we use a stream. + + CIntVector excluded; + op.excludedFormats = &excluded; - if (!codecs->FindFormatForArchiveType(L"", formatIndices)) { - throw SevenZipException(QCoreApplication::translate("Lib7z", - "Could not retrieve default format")); - } - CArchiveLink archiveLink; - //CMyComPtr is needed, otherwise it crashes in OpenStream() const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive); + op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). + + CObjectVector<CProperty> properties; + op.props = &properties; - const HRESULT result = archiveLink.Open2(codecs.data(), formatIndices, /*stdInMode*/false, stream, - UString(), 0); + CArchiveLink archiveLink; + const HRESULT result = archiveLink.Open2(op, nullptr); archive->seek(initialPos); return result == S_OK; - } catch (const SevenZipException& e) { - archive->seek(initialPos); - throw e; } catch (const char *err) { archive->seek(initialPos); throw SevenZipException(err); + } catch (const SevenZipException &e) { + archive->seek(initialPos); + throw e; // re-throw unmodified } catch (...) { archive->seek(initialPos); - throw SevenZipException(QCoreApplication::translate("Lib7z", "Unknown exception caught (%1)") - .arg(QString::fromLatin1(Q_FUNC_INFO))); + throw SevenZipException(QCoreApplication::translate("Lib7z", + "Unknown exception caught (%1).").arg(QString::fromLatin1(Q_FUNC_INFO))); } return false; // never reached } -void ExtractItemJob::doStart() +/*! + Returns \c true if the given \a archive is supported; otherwise returns \c false. + + \note Throws SevenZipException on error. +*/ +bool isSupportedArchive(const QString &archive) { - try { - if (!d->archive) - throw SevenZipException(tr("Could not list archive: QIODevice not set or already destroyed.")); - if (d->target) - extractFileFromArchive(d->archive, d->item, d->target, d->callback); - else if (!d->item.path.isEmpty()) - extractFileFromArchive(d->archive, d->item, d->targetDirectory, d->callback); - else - extractArchive(d->archive, d->targetDirectory, d->callback); - } catch (const SevenZipException& e) { - setError(Failed); - setErrorString(tr("Error while extracting '%1': %2").arg(d->item.path, e.message())); - } catch (...) { - setError(Failed); - setErrorString(tr("Unknown exception caught (%1)").arg(tr("Failed"))); - } - emitResult(); + QFile file(archive); + if (!file.open(QIODevice::ReadOnly)) + return false; + return isSupportedArchive(&file); } + +} // namespace Lib7z diff --git a/src/libs/installer/lib7z_facade.h b/src/libs/installer/lib7z_facade.h index f0599b4ce..1c8811cfa 100644 --- a/src/libs/installer/lib7z_facade.h +++ b/src/libs/installer/lib7z_facade.h @@ -29,253 +29,48 @@ #define LIB7Z_FACADE_H #include "installer_global.h" +#include "errors.h" -#include <QCoreApplication> -#include <QDateTime> -#include <QFile> -#include <QPoint> -#include <QRunnable> -#include <QString> -#include <QVariant> -#include <QVector> - -#include "Common/MyWindows.h" - -#include <stdexcept> -#include <string> +#include <Common/MyWindows.h> +#include <7zip/UI/Console/PercentPrinter.h> QT_BEGIN_NAMESPACE -class QStringList; -template <typename T> class QVector; +class QFileDevice; QT_END_NAMESPACE -namespace Lib7z { - class INSTALLER_EXPORT SevenZipException : public std::runtime_error { - public: - explicit SevenZipException( const QString& msg ) : std::runtime_error( msg.toStdString() ), m_message( msg ) {} - explicit SevenZipException( const char* msg ) : std::runtime_error( msg ), m_message( QString::fromLocal8Bit( msg ) ) {} - explicit SevenZipException( const std::string& msg ) : std::runtime_error( msg ), m_message( QString::fromLocal8Bit( msg.c_str() ) ) {} - - ~SevenZipException() throw() {} - QString message() const { return m_message; } - private: - QString m_message; - }; - - class INSTALLER_EXPORT File { - public: - File(); - QVector<File> subtreeInPreorder() const; - - bool operator<( const File& other ) const; - bool operator==( const File& other ) const; - - QFile::Permissions permissions; - QString path; - QDateTime mtime; - quint64 uncompressedSize; - quint64 compressedSize; - bool isDirectory; - QVector<File> children; - QPoint archiveIndex; - }; - - class ExtractCallbackPrivate; - class ExtractCallbackImpl; - - class ExtractCallback { - friend class ::Lib7z::ExtractCallbackImpl; - public: - ExtractCallback(); - virtual ~ExtractCallback(); - - void setTarget(QFileDevice* archive); - void setTarget(const QString& dir ); - - protected: - /** - * Reimplement to prepare for file @p filename to be extracted, e.g. by renaming existing files. - * @return @p true if the preparation was successful and extraction can be continued. - * If @p false is returned, the extraction will be aborted. Default implementation returns @p true. - */ - virtual bool prepareForFile( const QString& filename ); - virtual void setCurrentFile( const QString& filename ); - virtual HRESULT setCompleted( quint64 completed, quint64 total ); +namespace Lib7z +{ + void INSTALLER_EXPORT initSevenZ(); + bool INSTALLER_EXPORT isSupportedArchive(QFileDevice *archive); + bool INSTALLER_EXPORT isSupportedArchive(const QString &archive); - public: //for internal use - const ExtractCallbackImpl* impl() const; - ExtractCallbackImpl* impl(); - - private: - ExtractCallbackPrivate* const d; - }; - - class UpdateCallbackPrivate; - class UpdateCallbackImpl; - - class UpdateCallback + class INSTALLER_EXPORT SevenZipException : public QInstaller::Error { - friend class ::Lib7z::UpdateCallbackImpl; public: - UpdateCallback(); - virtual ~UpdateCallback(); - - void setTarget(QFileDevice* archive); - void setSourcePaths(const QStringList& paths); - - virtual UpdateCallbackImpl* impl(); - - private: - UpdateCallbackPrivate* const d; - }; - - class OpenArchiveInfoCleaner : public QObject { - Q_OBJECT - public: - OpenArchiveInfoCleaner() {} - private Q_SLOTS: - void deviceDestroyed(QObject*); - }; - - /*! - Extracts the given File \a file from \a archive into output device \a out using the - provided extract callback \a callback. - - Throws Lib7z::SevenZipException on error. - */ - void INSTALLER_EXPORT extractFileFromArchive(QFileDevice* archive, const File& item, - QFileDevice* out, ExtractCallback* callback=0 ); - - /*! - Extracts the given File \a file from \a archive into target directory \a targetDirectory - using the provided extract callback \a callback. The output filename is deduced from the - \a file path name. - - Throws Lib7z::SevenZipException on error. - */ - void INSTALLER_EXPORT extractFileFromArchive(QFileDevice* archive, const File& item, - const QString& targetDirectory, ExtractCallback* callback = 0); - - /*! - Extracts the given \a archive content into target directory \a targetDirectory using the - provided extract callback \a callback. The output filenames are deduced from the \a archive - content. + explicit SevenZipException(const QString &msg) + : QInstaller::Error(msg) + {} - Throws Lib7z::SevenZipException on error. - */ - void INSTALLER_EXPORT extractArchive(QFileDevice* archive, const QString& targetDirectory, - ExtractCallback* callback = 0); - - /* - * @thows Lib7z::SevenZipException - */ - void INSTALLER_EXPORT createArchive(QFileDevice* archive, const QStringList& sourcePaths, - UpdateCallback* callback = 0 ); - - /* - * @throws Lib7z::SevenZipException - */ - QVector<File> INSTALLER_EXPORT listArchive(QFileDevice* archive); - - /* - * @throws Lib7z::SevenZipException - */ - bool INSTALLER_EXPORT isSupportedArchive(QFileDevice* archive); - - /* - * @throws Lib7z::SevenZipException - */ - bool INSTALLER_EXPORT isSupportedArchive(const QString& archive); - - enum Error { - NoError=0, - Failed=1, - UserDefinedError=128 + explicit SevenZipException(const char *msg) + : QInstaller::Error(QString::fromLocal8Bit(msg)) + {} }; - class ExtractCallbackJobImpl; - - class INSTALLER_EXPORT Job : public QObject, public QRunnable + class INSTALLER_EXPORT PercentPrinter : public CPercentPrinter { - friend class ::Lib7z::ExtractCallbackJobImpl; - Q_OBJECT - public: - - explicit Job( QObject* parent=0 ); - ~Job(); - void start(); - int error() const; - bool hasError() const; - QString errorString() const; - - /* reimp */ void run(); - - protected: - void emitResult(); - void setError( int code ); - void setErrorString( const QString& err ); - void emitProgress( qint64 completed, qint64 total ); - - Q_SIGNALS: - void finished( Lib7z::Job* job ); - void progress( qint64 completed, qint64 total ); - - private Q_SLOTS: - virtual void doStart() = 0; - - private: - class Private; - Private* const d; - }; - - class INSTALLER_EXPORT ListArchiveJob : public Job { - Q_OBJECT public: - - explicit ListArchiveJob( QObject* parent=0 ); - ~ListArchiveJob(); - - QFileDevice* archive() const; - void setArchive(QFileDevice* archive); - - QVector<File> index() const; - - private: - /* reimp */ void doStart(); - - private: - class Private; - Private* const d; - }; - - class INSTALLER_EXPORT ExtractItemJob : public Job { - Q_OBJECT - friend class ::Lib7z::ExtractCallback; - public: - - explicit ExtractItemJob( QObject* parent=0 ); - ~ExtractItemJob(); - - File item() const; - void setItem( const File& item ); - - QFileDevice* archive() const; - void setArchive(QFileDevice* archive); - - QString targetDirectory() const; - void setTargetDirectory( const QString& dir ); - - void setTarget(QFileDevice* dev); - - private: - /* reimp */ void doStart(); - - private: - class Private; - Private* const d; + PercentPrinter() : CPercentPrinter(1 << 16) { + OutStream = &g_StdOut; + } + + void PrintRatio() { CPercentPrinter::PrintRatio(); } + void ClosePrint() { CPercentPrinter::ClosePrint(); } + void RePrintRatio() { CPercentPrinter::RePrintRatio(); } + void PrintNewLine() { CPercentPrinter::PrintNewLine(); } + void PrintString(const char *s) { CPercentPrinter::PrintString(s); } + void PrintString(const wchar_t *s) { CPercentPrinter::PrintString(s); } }; - QByteArray INSTALLER_EXPORT formatKeyValuePairs( const QVariantList& l ); -} +} // namespace Lib7z #endif // LIB7Z_FACADE_H diff --git a/src/libs/installer/lib7z_guid.h b/src/libs/installer/lib7z_guid.h new file mode 100644 index 000000000..16434558c --- /dev/null +++ b/src/libs/installer/lib7z_guid.h @@ -0,0 +1,97 @@ +/************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ +#ifndef LIB7Z_GUID_H +#define LIB7Z_GUID_H + +#include <Common/MyInitGuid.h> + +#ifdef __cplusplus +#define DEFINE_7Z_GUID(name, b4, b5, b6) extern "C" const GUID IID_ ## name = { 0x23170F69, \ + 0x40C1, 0x278A, { 0x00, 0x00, 0x00, b4, b5, b6, 0x00, 0x00 } } +#else +#define DEFINE_7Z_GUID(name, b4, b5, b6) extern const GUID IID_ ## name = { 0x23170F69, \ + 0x40C1, 0x278A, { 0x00, 0x00, 0x00, b4, b5, b6, 0x00, 0x00 } } +#endif + +DEFINE_7Z_GUID(IInArchiveGetStream, 0x06, 0x00, 0x40); +DEFINE_7Z_GUID(IInArchive, 0x06, 0x00, 0x60); + +DEFINE_7Z_GUID(IOutStream, 0x03, 0x00, 0x04); +DEFINE_7Z_GUID(IOutStreamFlush, 0x03, 0x00, 0x07); +DEFINE_7Z_GUID(IOutArchive, 0x06, 0x00, 0xA0); + +DEFINE_7Z_GUID(IInStream, 0x03, 0x00, 0x03); + +DEFINE_7Z_GUID(ISetProperties, 0x06, 0x00, 0x03); + +DEFINE_7Z_GUID(ISequentialInStream, 0x03, 0x00, 0x01); +DEFINE_7Z_GUID(ISequentialOutStream, 0x03, 0x00, 0x02); + +DEFINE_7Z_GUID(IStreamGetSize, 0x03, 0x00, 0x06); +DEFINE_7Z_GUID(IStreamGetProps, 0x03, 0x00, 0x08); +DEFINE_7Z_GUID(IStreamGetProps2, 0x03, 0x00, 0x09); + +DEFINE_7Z_GUID(IArchiveKeepModeForNextOpen, 0x06, 0x00, 0x04); +DEFINE_7Z_GUID(IArchiveAllowTail, 0x06, 0x00, 0x05); +DEFINE_7Z_GUID(IArchiveOpenCallback, 0x06, 0x00, 0x10); +DEFINE_7Z_GUID(IArchiveExtractCallback, 0x06, 0x00, 0x20); +DEFINE_7Z_GUID(IArchiveOpenVolumeCallback, 0x06, 0x00, 0x30); +DEFINE_7Z_GUID(IArchiveOpenSetSubArchiveName, 0x06, 0x00, 0x50); +DEFINE_7Z_GUID(IArchiveOpenSeq, 0x06, 0x00, 0x61); +DEFINE_7Z_GUID(IArchiveGetRawProps, 0x06, 0x00, 0x70); +DEFINE_7Z_GUID(IArchiveGetRootProps, 0x06, 0x00, 0x71); +DEFINE_7Z_GUID(IArchiveUpdateCallback, 0x06, 0x00, 0x80); +DEFINE_7Z_GUID(IArchiveUpdateCallback2, 0x06, 0x00, 0x82); + +DEFINE_7Z_GUID(ICompressProgressInfo, 0x04, 0x00, 0x04); +DEFINE_7Z_GUID(ICompressCoder, 0x04, 0x00, 0x05); +DEFINE_7Z_GUID(ICompressSetCoderProperties, 0x04, 0x00, 0x20); +DEFINE_7Z_GUID(ICompressSetDecoderProperties2, 0x04, 0x00, 0x22); +DEFINE_7Z_GUID(ICompressWriteCoderProperties, 0x04, 0x00, 0x23); +DEFINE_7Z_GUID(ICompressGetInStreamProcessedSize, 0x04, 0x00, 0x24); +DEFINE_7Z_GUID(ICompressSetCoderMt, 0x04, 0x00, 0x25); +DEFINE_7Z_GUID(ICompressSetOutStream, 0x04, 0x00, 0x32); +DEFINE_7Z_GUID(ICompressSetInStream, 0x04, 0x00, 0x31); +DEFINE_7Z_GUID(ICompressSetOutStreamSize, 0x04, 0x00, 0x34); +DEFINE_7Z_GUID(ICompressSetBufSize, 0x04, 0x00, 0x35); +DEFINE_7Z_GUID(ICompressGetSubStreamSize, 0x04, 0x00, 0x30); +DEFINE_7Z_GUID(ICryptoResetInitVector, 0x04, 0x00, 0x8C); +DEFINE_7Z_GUID(ICryptoSetPassword, 0x04, 0x00, 0x90); + +DEFINE_7Z_GUID(ICryptoGetTextPassword, 0x05, 0x00, 0x10); +DEFINE_7Z_GUID(ICryptoGetTextPassword2, 0x05, 0x00, 0x11); + +#undef DEFINE_7Z_GUID + +#endif // LIB7Z_GUID_H diff --git a/src/libs/installer/lib7z_list.h b/src/libs/installer/lib7z_list.h new file mode 100644 index 000000000..6e2646025 --- /dev/null +++ b/src/libs/installer/lib7z_list.h @@ -0,0 +1,62 @@ +/************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ +#ifndef LIB7Z_LIST_H +#define LIB7Z_LIST_H + +#include "installer_global.h" + +#include <QDateTime> +#include <QFile> +#include <QPoint> + +namespace Lib7z +{ + struct INSTALLER_EXPORT File + { + public: + QString path; + QDateTime utcTime; + QPoint archiveIndex; + bool isDirectory = false; + quint64 compressedSize = 0; + quint64 uncompressedSize = 0; + QFile::Permissions permissions = 0; + }; + INSTALLER_EXPORT bool operator==(const File &lhs, const File &rhs); + + QVector<File> INSTALLER_EXPORT listArchive(QFileDevice *archive); + +} // namespace Lib7z + +#endif // LIB7Z_LIST_H diff --git a/src/libs/installer/licenseoperation.cpp b/src/libs/installer/licenseoperation.cpp index 25637b5a3..059798338 100644 --- a/src/libs/installer/licenseoperation.cpp +++ b/src/libs/installer/licenseoperation.cpp @@ -37,7 +37,8 @@ using namespace QInstaller; -LicenseOperation::LicenseOperation() +LicenseOperation::LicenseOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("License")); } @@ -55,26 +56,25 @@ bool LicenseOperation::performOperation() return false; } - PackageManagerCore *const core = value(QLatin1String("installer")).value<PackageManagerCore*>(); + PackageManagerCore *const core = packageManager(); if (!core) { setError( UserDefinedError ); setErrorString(tr("Needed installer object in %1 operation is empty.").arg(name())); return false; } - QString targetDir = QString::fromLatin1("%1/%2").arg(core->value(scTargetDir), - QLatin1String("Licenses")); + QString targetDir = QString::fromLatin1("%1%2%3").arg(core->value(scTargetDir), + QDir::separator(), QLatin1String("Licenses")); QDir dir; dir.mkpath(targetDir); setArguments(QStringList(targetDir)); - for (QVariantMap::const_iterator it = licenses.begin(); it != licenses.end(); ++it) { - QFile file(targetDir + QDir::separator() + it.key()); + for (QVariantMap::const_iterator it = licenses.constBegin(); it != licenses.constEnd(); ++it) { + QFile file(targetDir + QLatin1Char('/') + it.key()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { setError(UserDefinedError); - setErrorString(tr("Can not write license file: %1.").arg(targetDir + QDir::separator() - + it.key())); + setErrorString(tr("Can not write license file \"%1\".").arg(QDir::toNativeSeparators(file.fileName()))); return false; } @@ -87,7 +87,7 @@ bool LicenseOperation::performOperation() bool LicenseOperation::undoOperation() { - QVariantMap licenses = value(QLatin1String("licenses")).toMap(); + const QVariantMap licenses = value(QLatin1String("licenses")).toMap(); if (licenses.isEmpty()) { setError(UserDefinedError); setErrorString(tr("No license files found to delete.")); @@ -108,8 +108,3 @@ bool LicenseOperation::testOperation() { return true; } - -Operation *LicenseOperation::clone() const -{ - return new LicenseOperation(); -} diff --git a/src/libs/installer/licenseoperation.h b/src/libs/installer/licenseoperation.h index 37820fbe5..49b885565 100644 --- a/src/libs/installer/licenseoperation.h +++ b/src/libs/installer/licenseoperation.h @@ -37,13 +37,12 @@ class INSTALLER_EXPORT LicenseOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::LicenseOperation) public: - LicenseOperation(); + explicit LicenseOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation* clone() const; }; } // namespace QInstaller diff --git a/src/libs/installer/linereplaceoperation.cpp b/src/libs/installer/linereplaceoperation.cpp index 87e1f2bf7..e67335e55 100644 --- a/src/libs/installer/linereplaceoperation.cpp +++ b/src/libs/installer/linereplaceoperation.cpp @@ -34,7 +34,8 @@ using namespace QInstaller; -LineReplaceOperation::LineReplaceOperation() +LineReplaceOperation::LineReplaceOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("LineReplace")); } @@ -45,18 +46,14 @@ void LineReplaceOperation::backup() bool LineReplaceOperation::performOperation() { - const QStringList args = arguments(); - // Arguments: // 1. filename // 2. startsWith Search-String // 3. Replace-Line-String - if (args.count() != 3) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("exactly 3"), QLatin1String(""))); + if (!checkArgumentCount(3)) return false; - } + + const QStringList args = arguments(); const QString fileName = args.at(0); const QString searchString = args.at(1); const QString replaceString = args.at(2); @@ -64,7 +61,8 @@ bool LineReplaceOperation::performOperation() QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { setError(UserDefinedError); - setErrorString(tr("Failed to open '%1' for reading.").arg(fileName)); + setErrorString(tr("Cannot open file \"%1\" for reading: %2").arg( + QDir::toNativeSeparators(fileName), file.errorString())); return false; } @@ -81,7 +79,8 @@ bool LineReplaceOperation::performOperation() if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { setError(UserDefinedError); - setErrorString(tr("Failed to open '%1' for writing.").arg(fileName)); + setErrorString(tr("Cannot open file \"%1\" for writing: %2").arg( + QDir::toNativeSeparators(fileName), file.errorString())); return false; } @@ -101,8 +100,3 @@ bool LineReplaceOperation::testOperation() { return true; } - -Operation *LineReplaceOperation::clone() const -{ - return new LineReplaceOperation(); -} diff --git a/src/libs/installer/linereplaceoperation.h b/src/libs/installer/linereplaceoperation.h index 6370a535c..d15e11fef 100644 --- a/src/libs/installer/linereplaceoperation.h +++ b/src/libs/installer/linereplaceoperation.h @@ -37,13 +37,12 @@ class INSTALLER_EXPORT LineReplaceOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::LineReplaceOperation) public: - LineReplaceOperation(); + explicit LineReplaceOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; }; } // namespace diff --git a/src/libs/installer/link.cpp b/src/libs/installer/link.cpp index ac4935a8b..ef91828ce 100644 --- a/src/libs/installer/link.cpp +++ b/src/libs/installer/link.cpp @@ -88,8 +88,7 @@ public: OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0); if (m_dirHandle == INVALID_HANDLE_VALUE) { - qWarning() << QString::fromLatin1("Could not open: '%1'; error: %2\n").arg(path) - .arg(QInstaller::windowsErrorString(GetLastError())); + qWarning() << "Cannot open" << path << ":" << QInstaller::windowsErrorString(GetLastError()); } } @@ -138,14 +137,12 @@ QString readWindowsSymLink(const QString &path) Link createJunction(const QString &linkPath, const QString &targetPath) { if (!QDir().mkpath(linkPath)) { - qWarning() << QString::fromLatin1("Could not create the mount directory: %1").arg( - linkPath); + qWarning() << "Cannot create the mount directory" << linkPath; return Link(linkPath); } FileHandleWrapper dirHandle(linkPath); if (dirHandle.handle() == INVALID_HANDLE_VALUE) { - qWarning() << QString::fromLatin1("Could not open: '%1'; error: %2\n").arg(linkPath) - .arg(QInstaller::windowsErrorString(GetLastError())); + qWarning() << "Cannot open" << linkPath << ":" << QInstaller::windowsErrorString(GetLastError()); return Link(linkPath); } @@ -175,8 +172,8 @@ Link createJunction(const QString &linkPath, const QString &targetPath) if (!::DeviceIoControl(dirHandle.handle(), FSCTL_SET_REPARSE_POINT, reparseStructData, reparseStructData->ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE, 0, 0, &bytesReturned, 0)) { - qWarning() << QString::fromLatin1("Could not set the reparse point: for '%1' to %2; error: %3" - ).arg(linkPath, targetPath).arg(QInstaller::windowsErrorString(GetLastError())); + qWarning() << "Cannot set the reparse point for" << linkPath << "to" << targetPath + << ":" << QInstaller::windowsErrorString(GetLastError()); } return Link(linkPath); } @@ -197,8 +194,7 @@ bool removeJunction(const QString &path) REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, 0, 0, &bytesReturned, 0)) { - qWarning() << QString::fromLatin1("Could not remove the reparse point: '%1'; error: %3" - ).arg(path).arg(QInstaller::windowsErrorString(GetLastError())); + qWarning() << "Cannot remove the reparse point" << path << ":" << QInstaller::windowsErrorString(GetLastError()); return false; } } @@ -211,8 +207,7 @@ Link createLnSymlink(const QString &linkPath, const QString &targetPath) int linkedError = symlink(QFileInfo(targetPath).absoluteFilePath().toUtf8(), QFileInfo(linkPath).absoluteFilePath().toUtf8()); if (linkedError != 0) { - qWarning() << QString::fromLatin1("Could not create a symlink: from '%1' to %2; error: %3" - ).arg(linkPath, targetPath).arg(linkedError); + qWarning() << "Cannot create a symlink from" << linkPath << "to" << targetPath << ":" << linkedError; } @@ -239,8 +234,7 @@ Link Link::create(const QString &link, const QString &targetPath) if (!linkPathExists) linkPathExists = QDir().mkpath(linkPath); if (!linkPathExists) { - qWarning() << QString::fromLatin1("Could not create the needed directories: %1").arg( - link); + qWarning() << "Cannot create the needed directories" << link; return Link(link); } @@ -248,8 +242,8 @@ Link Link::create(const QString &link, const QString &targetPath) if (QFileInfo(targetPath).isDir()) return createJunction(link, targetPath); - qWarning() << QString::fromLatin1("At the moment the %1 can not create anything else as "\ - "junctions for directories under windows").arg(QLatin1String(Q_FUNC_INFO)); + qWarning() << "At the moment the" << Q_FUNC_INFO << "can not create anything else as " + << "junctions for directories under windows"; return Link(link); #else return createLnSymlink(link, targetPath); diff --git a/src/libs/installer/messageboxhandler.cpp b/src/libs/installer/messageboxhandler.cpp index f99a9ebd3..fcdee4a5f 100644 --- a/src/libs/installer/messageboxhandler.cpp +++ b/src/libs/installer/messageboxhandler.cpp @@ -401,8 +401,8 @@ QMessageBox::StandardButton MessageBoxHandler::showMessageBox(MessageType messag messageTypeHash.insert(warningType, QLatin1String("warning")); }; - qDebug() << QString::fromLatin1("created %1 message box %2: '%3', %4").arg(messageTypeHash - .value(messageType),identifier, title, text); + qDebug().nospace() << "Created " << messageTypeHash.value(messageType).toUtf8().constData() + << " message box " << identifier << ": " << title << ", " << text; if (qobject_cast<QApplication*> (qApp) == 0) return defaultButton; diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp index b88f71137..0c674e313 100644 --- a/src/libs/installer/metadatajob.cpp +++ b/src/libs/installer/metadatajob.cpp @@ -34,6 +34,7 @@ #include "proxycredentialsdialog.h" #include "serverauthenticationdialog.h" #include "settings.h" +#include "testrepository.h" #include <QTemporaryDir> @@ -48,17 +49,19 @@ static QUrl resolveUrl(const FileTaskResult &result, const QString &url) } MetadataJob::MetadataJob(QObject *parent) - : KDJob(parent) + : Job(parent) , m_core(0) + , m_addCompressedPackages(false) { setCapabilities(Cancelable); - connect(&m_xmlTask, SIGNAL(finished()), this, SLOT(xmlTaskFinished())); - connect(&m_metadataTask, SIGNAL(finished()), this, SLOT(metadataTaskFinished())); - connect(&m_metadataTask, SIGNAL(progressValueChanged(int)), this, SLOT(progressChanged(int))); + connect(&m_xmlTask, &QFutureWatcherBase::finished, this, &MetadataJob::xmlTaskFinished); + connect(&m_metadataTask, &QFutureWatcherBase::finished, this, &MetadataJob::metadataTaskFinished); + connect(&m_metadataTask, &QFutureWatcherBase::progressValueChanged, this, &MetadataJob::progressChanged); } MetadataJob::~MetadataJob() { + resetCompressedFetch(); reset(); } @@ -72,46 +75,183 @@ Repository MetadataJob::repositoryForDirectory(const QString &directory) const void MetadataJob::doStart() { - reset(); if (!m_core) { - emitFinishedWithError(KDJob::Canceled, tr("Missing package manager core engine.")); + emitFinishedWithError(Job::Canceled, tr("Missing package manager core engine.")); return; // We can't do anything here without core, so avoid tons of !m_core checks. } - - emit infoMessage(this, tr("Preparing meta information download...")); - const bool onlineInstaller = m_core->isInstaller() && !m_core->isOfflineOnly(); - if (onlineInstaller || (m_core->isUpdater() || m_core->isPackageManager())) { - QList<FileTaskItem> items; - const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance(); - foreach (const Repository &repo, m_core->settings().repositories()) { - if (repo.isEnabled() && productKeyCheck->isValidRepository(repo)) { - QAuthenticator authenticator; - authenticator.setUser(repo.username()); - authenticator.setPassword(repo.password()); - - QString url = repo.url().toString() + QLatin1String("/Updates.xml?"); - if (!m_core->value(QLatin1String("UrlQueryString")).isEmpty()) - url += m_core->value(QLatin1String("UrlQueryString")) + QLatin1Char('&'); - - // also append a random string to avoid proxy caches - FileTaskItem item(url.append(QString::number(qrand() * qrand()))); - item.insert(TaskRole::UserRole, QVariant::fromValue(repo)); - item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator)); - items.append(item); + const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance(); + if (!m_addCompressedPackages) { + reset(); + emit infoMessage(this, tr("Preparing meta information download...")); + const bool onlineInstaller = m_core->isInstaller() && !m_core->isOfflineOnly(); + + if (onlineInstaller || m_core->isMaintainer()) { + QList<FileTaskItem> items; + foreach (const Repository &repo, m_core->settings().repositories()) { + if (repo.isEnabled() && + productKeyCheck->isValidRepository(repo)) { + QAuthenticator authenticator; + authenticator.setUser(repo.username()); + authenticator.setPassword(repo.password()); + + if (!repo.isCompressed()) { + QString url = repo.url().toString() + QLatin1String("/Updates.xml?"); + if (!m_core->value(scUrlQueryString).isEmpty()) + url += m_core->value(scUrlQueryString) + QLatin1Char('&'); + + // also append a random string to avoid proxy caches + FileTaskItem item(url.append(QString::number(qrand() * qrand()))); + item.insert(TaskRole::UserRole, QVariant::fromValue(repo)); + item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator)); + items.append(item); + } + else { + qDebug() << "Trying to parse compressed repo as normal repository."\ + "Check repository syntax."; + } + } } + if (items.count() > 0) { + startXMLTask(items); + } else { + emitFinished(); + } + } else { + emitFinished(); } - DownloadFileTask *const xmlTask = new DownloadFileTask(items); - xmlTask->setProxyFactory(m_core->proxyFactory()); - m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask)); } else { - emitFinished(); + resetCompressedFetch(); + + bool repositoriesFound = false; + foreach (const Repository &repo, m_core->settings().temporaryRepositories()) { + if (repo.isCompressed() && repo.isEnabled() && + productKeyCheck->isValidRepository(repo)) { + repositoriesFound = true; + startUnzipRepositoryTask(repo); + + //Set the repository disabled so we don't handle it many times + Repository replacement = repo; + replacement.setEnabled(false); + Settings &s = m_core->settings(); + QSet<Repository> temporaries = s.temporaryRepositories(); + if (temporaries.contains(repo)) { + temporaries.remove(repo); + temporaries.insert(replacement); + s.setTemporaryRepositories(temporaries, true); + } + } + } + if (!repositoriesFound) { + emitFinished(); + } + else { + setProgressTotalAmount(0); + emit infoMessage(this, tr("Unpacking compressed repositories. This may take a while...")); + } } } +void MetadataJob::startXMLTask(const QList<FileTaskItem> items) +{ + DownloadFileTask *const xmlTask = new DownloadFileTask(items); + xmlTask->setProxyFactory(m_core->proxyFactory()); + m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask)); +} + void MetadataJob::doCancel() { reset(); - emitFinishedWithError(KDJob::Canceled, tr("Meta data download canceled.")); + emitFinishedWithError(Job::Canceled, tr("Meta data download canceled.")); +} + +void MetadataJob::startUnzipRepositoryTask(const Repository &repo) +{ + QTemporaryDir tempRepoDir(QDir::tempPath() + QLatin1String("/compressedRepo-XXXXXX")); + if (!tempRepoDir.isValid()) { + qDebug() << "Cannot create unique temporary directory."; + return; + } + tempRepoDir.setAutoRemove(false); + m_tempDirDeleter.add(tempRepoDir.path()); + QString url = repo.url().toLocalFile(); + UnzipArchiveTask *task = new UnzipArchiveTask(url, tempRepoDir.path()); + QFutureWatcher<void> *watcher = new QFutureWatcher<void>(); + m_unzipRepositoryTasks.insert(watcher, qobject_cast<QObject*> (task)); + connect(watcher, &QFutureWatcherBase::finished, this, + &MetadataJob::unzipRepositoryTaskFinished); + connect(watcher, &QFutureWatcherBase::progressValueChanged, this, + &MetadataJob::progressChanged); + watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task)); +} + +void MetadataJob::unzipRepositoryTaskFinished() +{ + QFutureWatcher<void> *watcher = static_cast<QFutureWatcher<void> *>(sender()); + int error = Job::NoError; + QString errorString; + try { + watcher->waitForFinished(); // trigger possible exceptions + + QHashIterator<QFutureWatcher<void> *, QObject*> i(m_unzipRepositoryTasks); + while (i.hasNext()) { + + i.next(); + if (i.key() == watcher) { + UnzipArchiveTask *task = qobject_cast<UnzipArchiveTask*> (i.value()); + QString url = task->target(); + + QUrl targetUrl = targetUrl.fromLocalFile(url); + Repository repo(targetUrl, false, true); + url = repo.url().toString() + QLatin1String("/Updates.xml"); + TestRepository testJob(m_core); + testJob.setRepository(repo); + testJob.start(); + testJob.waitForFinished(); + error = testJob.error(); + errorString = testJob.errorString(); + if (error == Job::NoError) { + FileTaskItem item(url); + item.insert(TaskRole::UserRole, QVariant::fromValue(repo)); + m_unzipRepositoryitems.append(item); + } else { + //Repository is not valid, remove it + Repository repository; + repository.setUrl(QUrl(task->archive())); + Settings &s = m_core->settings(); + QSet<Repository> temporaries = s.temporaryRepositories(); + foreach (Repository repository, temporaries) { + if (repository.url().toLocalFile() == task->archive()) { + temporaries.remove(repository); + } + } + s.setTemporaryRepositories(temporaries, false); + } + } + } + delete m_unzipRepositoryTasks.value(watcher); + m_unzipRepositoryTasks.remove(watcher); + delete watcher; + + //One can specify many zipped repository items at once. As the repositories are + //unzipped one by one, we collect here all items before parsing xml files from those. + if (m_unzipRepositoryitems.count() > 0 && m_unzipRepositoryTasks.isEmpty()) { + startXMLTask(m_unzipRepositoryitems); + } else { + if (error != Job::NoError) { + emitFinishedWithError(QInstaller::DownloadError, errorString); + } + } + + } catch (const UnzipArchiveException &e) { + reset(); + emitFinishedWithError(QInstaller::ExtractionError, e.message()); + } catch (const QUnhandledException &e) { + reset(); + emitFinishedWithError(QInstaller::DownloadError, QLatin1String(e.what())); + } catch (...) { + reset(); + emitFinishedWithError(QInstaller::DownloadError, tr("Unknown exception during extracting.")); + } } void MetadataJob::xmlTaskFinished() @@ -124,7 +264,7 @@ void MetadataJob::xmlTaskFinished() if (e.type() == AuthenticationRequiredException::Type::Proxy) { const QNetworkProxy proxy = e.proxy(); ProxyCredentialsDialog proxyCredentials(proxy); - qDebug() << e.message(); + qDebug().noquote() << e.message(); if (proxyCredentials.exec() == QDialog::Accepted) { qDebug() << "Retrying with new credentials ..."; @@ -139,7 +279,7 @@ void MetadataJob::xmlTaskFinished() emitFinishedWithError(QInstaller::DownloadError, tr("Missing proxy credentials.")); } } else if (e.type() == AuthenticationRequiredException::Type::Server) { - qDebug() << e.message(); + qDebug().noquote() << e.message(); ServerAuthenticationDialog dlg(e.message(), e.taskItem()); if (dlg.exec() == QDialog::Accepted) { Repository original = e.taskItem().value(TaskRole::UserRole) @@ -160,7 +300,7 @@ void MetadataJob::xmlTaskFinished() if (s.updateDefaultRepositories(update) == Settings::UpdatesApplied || s.updateUserRepositories(update) == Settings::UpdatesApplied) { - if (m_core->isUpdater() || m_core->isPackageManager()) + if (m_core->isMaintainer()) m_core->writeMaintenanceConfigFiles(); } } @@ -181,7 +321,7 @@ void MetadataJob::xmlTaskFinished() emitFinishedWithError(QInstaller::DownloadError, tr("Unknown exception during download.")); } - if (error() != KDJob::NoError) + if (error() != Job::NoError) return; if (status == XmlDownloadSuccess) { @@ -189,6 +329,7 @@ void MetadataJob::xmlTaskFinished() DownloadFileTask *const metadataTask = new DownloadFileTask(m_packages); metadataTask->setProxyFactory(m_core->proxyFactory()); m_metadataTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, metadataTask)); + setProgressTotalAmount(100); emit infoMessage(this, tr("Retrieving meta information from remote repository...")); } else if (status == XmlDownloadRetry) { QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); @@ -204,17 +345,14 @@ void MetadataJob::unzipTaskFinished() try { watcher->waitForFinished(); // trigger possible exceptions } catch (const UnzipArchiveException &e) { - reset(); emitFinishedWithError(QInstaller::ExtractionError, e.message()); } catch (const QUnhandledException &e) { - reset(); emitFinishedWithError(QInstaller::DownloadError, QLatin1String(e.what())); } catch (...) { - reset(); emitFinishedWithError(QInstaller::DownloadError, tr("Unknown exception during extracting.")); } - if (error() != KDJob::NoError) + if (error() != Job::NoError) return; delete m_unzipTasks.value(watcher); @@ -232,6 +370,11 @@ void MetadataJob::progressChanged(int progress) setProcessedAmount(progress); } +void MetadataJob::setProgressTotalAmount(int maximum) +{ + setTotalAmount(maximum); +} + void MetadataJob::metadataTaskFinished() { try { @@ -246,7 +389,7 @@ void MetadataJob::metadataTaskFinished() QFutureWatcher<void> *watcher = new QFutureWatcher<void>(); m_unzipTasks.insert(watcher, qobject_cast<QObject*> (task)); - connect(watcher, SIGNAL(finished()), this, SLOT(unzipTaskFinished())); + connect(watcher, &QFutureWatcherBase::finished, this, &MetadataJob::unzipTaskFinished); watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task)); } } else { @@ -272,13 +415,24 @@ void MetadataJob::reset() m_packages.clear(); m_metadata.clear(); - setError(KDJob::NoError); + setError(Job::NoError); setErrorString(QString()); setCapabilities(Cancelable); try { m_xmlTask.cancel(); m_metadataTask.cancel(); + } catch (...) {} + m_tempDirDeleter.releaseAndDeleteAll(); +} + +void MetadataJob::resetCompressedFetch() +{ + setError(Job::NoError); + setErrorString(QString()); + m_unzipRepositoryitems.clear(); + + try { foreach (QFutureWatcher<void> *const watcher, m_unzipTasks.keys()) { watcher->cancel(); watcher->deleteLater(); @@ -286,20 +440,32 @@ void MetadataJob::reset() foreach (QObject *const object, m_unzipTasks) object->deleteLater(); m_unzipTasks.clear(); + + foreach (QFutureWatcher<void> *const watcher, m_unzipRepositoryTasks.keys()) { + watcher->cancel(); + watcher->deleteLater(); + } + foreach (QObject *const object, m_unzipRepositoryTasks) + object->deleteLater(); + m_unzipRepositoryTasks.clear(); } catch (...) {} - m_tempDirDeleter.releaseAndDeleteAll(); } MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &results) { foreach (const FileTaskResult &result, results) { - if (error() != KDJob::NoError) + if (error() != Job::NoError) return XmlDownloadFailure; + //If repository is not found, target might be empty. Do not continue parsing the + //repository and do not prevent further repositories usage. + if (result.target().isEmpty()) { + continue; + } Metadata metadata; QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX")); if (!tmp.isValid()) { - qDebug() << "Could not create unique temporary directory."; + qDebug() << "Cannot create unique temporary directory."; return XmlDownloadFailure; } @@ -309,21 +475,22 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re QFile file(result.target()); if (!file.rename(metadata.directory + QLatin1String("/Updates.xml"))) { - qDebug() << "Could not rename target to Updates.xml. Error:" << file.errorString(); + qDebug() << "Cannot rename target to Updates.xml:" << file.errorString(); return XmlDownloadFailure; } if (!file.open(QIODevice::ReadOnly)) { - qDebug() << "Could not open Updates.xml for reading. Error:" << file.errorString(); + qDebug() << "Cannot open Updates.xml for reading:" << file.errorString(); return XmlDownloadFailure; } QString error; QDomDocument doc; if (!doc.setContent(&file, &error)) { - qDebug() << QString::fromLatin1("Could not fetch a valid version of Updates.xml from " - "repository: %1. Error: %2").arg(metadata.repository.displayname(), error); - return XmlDownloadFailure; + qDebug().nospace() << "Cannot fetch a valid version of Updates.xml from repository " + << metadata.repository.displayname() << ": " << error; + //If there are other repositories, try to use those + continue; } file.close(); @@ -346,7 +513,7 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re for (int j = 0; j < c2.count(); ++j) { if (c2.at(j).toElement().tagName() == scName) packageName = c2.at(j).toElement().text(); - else if (c2.at(j).toElement().tagName() == scRemoteVersion) + else if (c2.at(j).toElement().tagName() == scVersion) packageVersion = (online ? c2.at(j).toElement().text() : QString()); else if ((c2.at(j).toElement().tagName() == QLatin1String("SHA1")) && testCheckSum) packageHash = c2.at(j).toElement().text(); @@ -408,12 +575,12 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re if (ProductKeyCheck::instance()->isValidRepository(newRepository)) { // store the new repository and the one old it replaces repositoryUpdates.insertMulti(action, qMakePair(newRepository, oldRepository)); - qDebug() << "Replace repository:" << oldRepository.displayname() << "with:" + qDebug() << "Replace repository" << oldRepository.displayname() << "with" << newRepository.displayname(); } } else { qDebug() << "Invalid additional repositories action set in Updates.xml fetched " - "from:" << metadata.repository.displayname() << "Line:" << el.lineNumber(); + "from" << metadata.repository.displayname() << "line:" << el.lineNumber(); } } } @@ -441,7 +608,7 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re return XmlDownloadRetry; } } else if (s.updateDefaultRepositories(repositoryUpdates) == Settings::UpdatesApplied) { - if (m_core->isUpdater() || m_core->isPackageManager()) + if (m_core->isMaintainer()) m_core->writeMaintenanceConfigFiles(); QFile::remove(result.target()); return XmlDownloadRetry; diff --git a/src/libs/installer/metadatajob.h b/src/libs/installer/metadatajob.h index bf1c8f54a..c6d5ad353 100644 --- a/src/libs/installer/metadatajob.h +++ b/src/libs/installer/metadatajob.h @@ -31,7 +31,7 @@ #include "downloadfiletask.h" #include "fileutils.h" -#include "kdjob.h" +#include "job.h" #include "repository.h" #include <QFutureWatcher> @@ -46,7 +46,7 @@ struct Metadata Repository repository; }; -class INSTALLER_EXPORT MetadataJob : public KDJob +class INSTALLER_EXPORT MetadataJob : public Job { Q_OBJECT Q_DISABLE_COPY(MetadataJob) @@ -64,6 +64,7 @@ public: QList<Metadata> metadata() const { return m_metadata.values(); } Repository repositoryForDirectory(const QString &directory) const; void setPackageManagerCore(PackageManagerCore *core) { m_core = core; } + void addCompressedPackages(bool addCompressPackage) { m_addCompressedPackages = addCompressPackage;} private slots: void doStart(); @@ -73,9 +74,14 @@ private slots: void unzipTaskFinished(); void metadataTaskFinished(); void progressChanged(int progress); + void setProgressTotalAmount(int maximum); + void unzipRepositoryTaskFinished(); + void startXMLTask(const QList<FileTaskItem> items); private: + void startUnzipRepositoryTask(const Repository &repo); void reset(); + void resetCompressedFetch(); Status parseUpdatesXml(const QList<FileTaskResult> &results); private: @@ -87,6 +93,9 @@ private: QFutureWatcher<FileTaskResult> m_xmlTask; QFutureWatcher<FileTaskResult> m_metadataTask; QHash<QFutureWatcher<void> *, QObject*> m_unzipTasks; + QHash<QFutureWatcher<void> *, QObject*> m_unzipRepositoryTasks; + bool m_addCompressedPackages; + QList<FileTaskItem> m_unzipRepositoryitems; }; } // namespace QInstaller diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h index 34e23cd78..9160f4cc9 100644 --- a/src/libs/installer/metadatajob_p.h +++ b/src/libs/installer/metadatajob_p.h @@ -29,9 +29,13 @@ #ifndef METADATAJOB_P_H #define METADATAJOB_P_H +#include "lib7z_extract.h" #include "lib7z_facade.h" #include "metadatajob.h" +#include <QDir> +#include <QFile> + namespace QInstaller{ class UnzipArchiveException : public QException @@ -60,7 +64,8 @@ public: UnzipArchiveTask(const QString &arcive, const QString &target) : m_archive(arcive), m_targetDir(target) {} - + QString target() { return m_targetDir; } + QString archive() { return m_archive; } void doTask(QFutureInterface<void> &fi) { fi.reportStarted(); @@ -77,14 +82,14 @@ public: Lib7z::extractArchive(&archive, m_targetDir); } catch (const Lib7z::SevenZipException& e) { fi.reportException(UnzipArchiveException(MetadataJob::tr("Error while extracting " - "'%1': %2").arg(m_archive, e.message()))); + "archive \"%1\": %2").arg(QDir::toNativeSeparators(m_archive), e.message()))); } catch (...) { fi.reportException(UnzipArchiveException(MetadataJob::tr("Unknown exception " - "caught while extracting %1.").arg(m_archive))); + "caught while extracting archive \"%1\".").arg(QDir::toNativeSeparators(m_archive)))); } } else { - fi.reportException(UnzipArchiveException(MetadataJob::tr("Could not open %1 for " - "reading. Error: %2").arg(m_archive, archive.errorString()))); + fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for " + "reading: %2").arg(QDir::toNativeSeparators(m_archive), archive.errorString()))); } fi.reportFinished(); diff --git a/src/libs/installer/minimumprogressoperation.cpp b/src/libs/installer/minimumprogressoperation.cpp index 92762d3b1..5ae571a59 100644 --- a/src/libs/installer/minimumprogressoperation.cpp +++ b/src/libs/installer/minimumprogressoperation.cpp @@ -30,7 +30,8 @@ using namespace QInstaller; -MinimumProgressOperation::MinimumProgressOperation() +MinimumProgressOperation::MinimumProgressOperation(PackageManagerCore *core) + : UpdateOperation(core) { // This shouldn't be callable by script, but we need a name for the binary format setName(QLatin1String("MinimumProgress")); @@ -56,9 +57,3 @@ bool MinimumProgressOperation::testOperation() { return true; } - -Operation *MinimumProgressOperation::clone() const -{ - return new MinimumProgressOperation(); -} - diff --git a/src/libs/installer/minimumprogressoperation.h b/src/libs/installer/minimumprogressoperation.h index bfea8de72..4cdafca70 100644 --- a/src/libs/installer/minimumprogressoperation.h +++ b/src/libs/installer/minimumprogressoperation.h @@ -40,13 +40,12 @@ class MinimumProgressOperation : public QObject, public Operation Q_OBJECT public: - MinimumProgressOperation(); + explicit MinimumProgressOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; signals: void progressChanged(double progress); diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index 8f492c06a..7b6842720 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.cpp @@ -41,6 +41,7 @@ #include "qprocesswrapper.h" #include "qsettingswrapper.h" #include "remoteclient.h" +#include "remotefileengine.h" #include "settings.h" #include "utils.h" #include "installercalculator.h" @@ -56,12 +57,16 @@ #include <QtCore/QRegExp> #include <QtCore/QSettings> #include <QtCore/QTemporaryFile> +#include <QtCore/QTextCodec> +#include <QtCore/QTextDecoder> +#include <QtCore/QTextEncoder> +#include <QtCore/QTextStream> #include <QDesktopServices> #include <QFileDialog> -#include "kdsysinfo.h" -#include "kdupdaterupdateoperationfactory.h" +#include "sysinfo.h" +#include "updateoperationfactory.h" #ifdef Q_OS_WIN # include "qt_windows.h" @@ -423,7 +428,7 @@ void PackageManagerCore::writeMaintenanceTool() gainAdminRights(); gainedAdminRights = true; } - d->m_updaterApplication.packagesInfo()->writeToDisk(); + d->m_localPackageHub->writeToDisk(); if (gainedAdminRights) dropAdminRights(); d->m_needToWriteMaintenanceTool = false; @@ -634,11 +639,11 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize) DownloadArchivesJob archivesJob(this); archivesJob.setAutoDelete(false); archivesJob.setArchivesToDownload(archivesToDownload); - connect(this, SIGNAL(installationInterrupted()), &archivesJob, SLOT(cancel())); - connect(&archivesJob, SIGNAL(outputTextChanged(QString)), ProgressCoordinator::instance(), - SLOT(emitLabelAndDetailTextChanged(QString))); - connect(&archivesJob, SIGNAL(downloadStatusChanged(QString)), ProgressCoordinator::instance(), - SIGNAL(downloadStatusChanged(QString))); + connect(this, &PackageManagerCore::installationInterrupted, &archivesJob, &Job::cancel); + connect(&archivesJob, &DownloadArchivesJob::outputTextChanged, + ProgressCoordinator::instance(), &ProgressCoordinator::emitLabelAndDetailTextChanged); + connect(&archivesJob, &DownloadArchivesJob::downloadStatusChanged, + ProgressCoordinator::instance(), &ProgressCoordinator::downloadStatusChanged); ProgressCoordinator::instance()->registerPartProgress(&archivesJob, SIGNAL(progressChanged(double)), partProgressSize); @@ -646,13 +651,13 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize) archivesJob.start(); archivesJob.waitForFinished(); - if (archivesJob.error() == KDJob::Canceled) + if (archivesJob.error() == Job::Canceled) interrupt(); - else if (archivesJob.error() != KDJob::NoError) + else if (archivesJob.error() != Job::NoError) throw Error(archivesJob.errorString()); if (d->statusCanceledOrFailed()) - throw Error(tr("Installation canceled by user")); + throw Error(tr("Installation canceled by user.")); ProgressCoordinator::instance()->emitDownloadStatus(tr("All downloads finished.")); @@ -701,7 +706,6 @@ void PackageManagerCore::rollBackInstallation() } } - KDUpdater::PackagesInfo &packages = *d->m_updaterApplication.packagesInfo(); while (!d->m_performedOperationsCurrentSession.isEmpty()) { try { Operation *const operation = d->m_performedOperationsCurrentSession.takeLast(); @@ -728,14 +732,14 @@ void PackageManagerCore::rollBackInstallation() component = d->componentsToReplace().value(componentName).second; if (component) { component->setUninstalled(); - packages.removePackage(component->name()); + d->m_localPackageHub->removePackage(component->name()); } } - packages.writeToDisk(); + d->m_localPackageHub->writeToDisk(); if (isInstaller()) { - if (packages.packageInfoCount() == 0) { - QFile file(packages.fileName()); + if (d->m_localPackageHub->packageInfoCount() == 0) { + QFile file(d->m_localPackageHub->fileName()); file.remove(); } } @@ -745,7 +749,7 @@ void PackageManagerCore::rollBackInstallation() } catch (const Error &e) { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("ElevationError"), tr("Authentication Error"), tr("Some components " - "could not be removed completely because admin rights could not be acquired: %1.") + "could not be removed completely because administrative rights could not be acquired: %1.") .arg(e.message())); } catch (...) { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("unknown"), @@ -781,6 +785,32 @@ bool PackageManagerCore::fileExists(const QString &filePath) const return QFileInfo(filePath).exists(); } +/*! + 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 + terminators are translated to the local encoding. + + \note If the file does not exist or an error occurs while reading the file, an + empty string is returned. + + \sa {installer::readFile}{installer.readFile} + + */ +QString PackageManagerCore::readFile(const QString &filePath, const QString &codecName) const +{ + QFile f(filePath); + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) + return QString(); + + QTextCodec *codec = QTextCodec::codecForName(qPrintable(codecName)); + if (!codec) + return QString(); + + QTextStream stream(&f); + stream.setCodec(codec); + return stream.readAll(); +} + // -- QInstaller /*! @@ -820,6 +850,8 @@ PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationB d->initialize(QHash<QString, QString>()); + RemoteClient::instance().setAuthorizationFallbackDisabled(settings().disableAuthorizationFallback()); + // // Sanity check to detect a broken installations with missing operations. // Every installed package should have at least one MinimalProgress operation. @@ -849,6 +881,40 @@ PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationB } } +class VerboseWriterAdminOutput : public VerboseWriterOutput +{ +public: + VerboseWriterAdminOutput(PackageManagerCore *core) : m_core(core) {} + + virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data) + { + bool gainedAdminRights = false; + + if (!RemoteClient::instance().isActive()) { + m_core->gainAdminRights(); + gainedAdminRights = true; + } + + RemoteFileEngine file; + file.setFileName(fileName); + if (file.open(openMode)) { + file.write(data.constData(), data.size()); + file.close(); + if (gainedAdminRights) + m_core->dropAdminRights(); + return true; + } + + if (gainedAdminRights) + m_core->dropAdminRights(); + + return false; + } + +private: + PackageManagerCore *m_core; +}; + /*! Destroys the instance. */ @@ -862,8 +928,19 @@ PackageManagerCore::~PackageManagerCore() } delete d; + try { + PlainVerboseWriterOutput plainOutput; + if (!VerboseWriter::instance()->flush(&plainOutput)) { + VerboseWriterAdminOutput adminOutput(this); + VerboseWriter::instance()->flush(&adminOutput); + } + } catch (...) { + // Intentionally left blank; don't permit exceptions from VerboseWriter + // to escape destructor. + } + RemoteClient::instance().setActive(false); - RemoteClient::instance().shutdown(); + RemoteClient::instance().destroy(); QMutexLocker _(globalVirtualComponentsFontMutex()); delete sVirtualComponentsFont; @@ -962,7 +1039,7 @@ bool PackageManagerCore::fetchLocalPackagesTree() d->setStatus(Running); if (!isPackageManager()) { - d->setStatus(Failure, tr("Application not running in Package Manager mode!")); + d->setStatus(Failure, tr("Application not running in Package Manager mode.")); return false; } @@ -984,7 +1061,7 @@ bool PackageManagerCore::fetchLocalPackagesTree() component->loadDataFromPackage(installedPackages.value(key)); const QString &name = component->name(); if (components.contains(name)) { - qCritical("Could not register component! Component with identifier %s already registered.", + qCritical("Cannot register component! Component with identifier %s already registered.", qPrintable(name)); continue; } @@ -1021,7 +1098,7 @@ void PackageManagerCore::networkSettingsChanged() d->m_repoFetched = false; d->m_updateSourcesAdded = false; - if (d->isUpdater() || d->isPackageManager()) { + if (isMaintainer() ) { bool gainedAdminRights = false; QTemporaryFile tempAdminFile(d->targetDir() + QStringLiteral("/XXXXXX")); if (!tempAdminFile.open() || !tempAdminFile.isWritable()) { @@ -1032,6 +1109,7 @@ void PackageManagerCore::networkSettingsChanged() if (gainedAdminRights) dropAdminRights(); } + KDUpdater::FileDownloaderFactory::instance().setProxyFactory(proxyFactory()); emit coreNetworkSettingsChanged(); @@ -1071,15 +1149,44 @@ PackagesList PackageManagerCore::remotePackages() } /*! + Checks for compressed packages to install. Returns \c true if newer versions exist + and they can be installed. +*/ +bool PackageManagerCore::fetchCompressedPackagesTree() +{ + const LocalPackagesHash installedPackages = d->localInstalledPackages(); + if (!isInstaller() && status() == Failure) + return false; + + if (!d->fetchMetaInformationFromCompressedRepositories()) + return false; + + if (!d->addUpdateResourcesFromRepositories(true, true)) { + return false; + } + + PackagesList packages; + const PackagesList &compPackages = d->compressedPackages(); + if (compPackages.isEmpty()) + return false; + packages.append(compPackages); + const PackagesList &rPackages = d->remotePackages(); + packages.append(rPackages); + + return fetchPackagesTree(packages, installedPackages); +} + + +/*! Checks for packages to install. Returns \c true if newer versions exist - and they can be installed and sets the status of the update to \c Success. + and they can be installed. */ bool PackageManagerCore::fetchRemotePackagesTree() { d->setStatus(Running); if (isUninstaller()) { - d->setStatus(Failure, tr("Application running in Uninstaller mode!")); + d->setStatus(Failure, tr("Application running in Uninstaller mode.")); return false; } @@ -1095,6 +1202,9 @@ bool PackageManagerCore::fetchRemotePackagesTree() if (!d->fetchMetaInformationFromRepositories()) return false; + if (!d->fetchMetaInformationFromCompressedRepositories()) + return false; + if (!d->addUpdateResourcesFromRepositories(true)) return false; @@ -1102,6 +1212,11 @@ bool PackageManagerCore::fetchRemotePackagesTree() if (packages.isEmpty()) return false; + return fetchPackagesTree(packages, installedPackages); +} + +bool PackageManagerCore::fetchPackagesTree(const PackagesList &packages, const LocalPackagesHash installedPackages) { + bool success = false; if (!isUpdater()) { success = fetchAllPackages(packages, installedPackages); @@ -1115,7 +1230,7 @@ bool PackageManagerCore::fetchRemotePackagesTree() } const LocalPackage localPackage = installedPackages.value(name); - const QString updateVersion = update->data(scRemoteVersion).toString(); + const QString updateVersion = update->data(scVersion).toString(); if (KDUpdater::compareVersion(updateVersion, localPackage.version) <= 0) break; // remote version equals or is less than the installed maintenance tool @@ -1284,12 +1399,12 @@ void PackageManagerCore::addUserRepositories(const QStringList &repositories) \sa {installer::setTemporaryRepositories}{installer.setTemporaryRepositories} \sa addUserRepositories() */ -void PackageManagerCore::setTemporaryRepositories(const QStringList &repositories, bool replace) +void PackageManagerCore::setTemporaryRepositories(const QStringList &repositories, bool replace, + bool compressed) { QSet<Repository> repositorySet; foreach (const QString &repository, repositories) - repositorySet.insert(Repository::fromUserInput(repository)); - + repositorySet.insert(Repository::fromUserInput(repository, compressed)); settings().setTemporaryRepositories(repositorySet, replace); } @@ -1513,6 +1628,52 @@ QList<Component*> PackageManagerCore::orderedComponentsToInstall() const return d->installerCalculator()->orderedComponentsToInstall(); } +bool PackageManagerCore::calculateComponents(QString *displayString) +{ + QString htmlOutput; + QString lastInstallReason; + if (!calculateComponentsToUninstall() || + !calculateComponentsToInstall()) { + htmlOutput.append(QString::fromLatin1("<h2><font color=\"red\">%1</font></h2><ul>") + .arg(tr("Cannot resolve all dependencies."))); + //if we have a missing dependency or a recursion we can display it + if (!componentsToInstallError().isEmpty()) { + htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg( + componentsToInstallError())); + } + htmlOutput.append(QLatin1String("</ul>")); + if (displayString) + *displayString = htmlOutput; + return false; + } + + // In case of updater mode we don't uninstall components. + if (!isUpdater()) { + QList<Component*> componentsToRemove = componentsToUninstall(); + if (!componentsToRemove.isEmpty()) { + htmlOutput.append(QString::fromLatin1("<h3>%1</h3><ul>").arg(tr("Components about to " + "be removed."))); + foreach (Component *component, componentsToRemove) + htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(component->name())); + htmlOutput.append(QLatin1String("</ul>")); + } + } + + foreach (Component *component, orderedComponentsToInstall()) { + const QString reason = installReason(component); + if (lastInstallReason != reason) { + if (!lastInstallReason.isEmpty()) // means we had to close the previous list + htmlOutput.append(QLatin1String("</ul>")); + htmlOutput.append(QString::fromLatin1("<h3>%1</h3><ul>").arg(reason)); + lastInstallReason = reason; + } + htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(component->name())); + } + if (displayString) + *displayString = htmlOutput; + return true; +} + /*! Calculates a list of components to uninstall based on the current run mode. The aboutCalculateComponentsToUninstall() signal is emitted @@ -1616,8 +1777,8 @@ ComponentModel *PackageManagerCore::defaultComponentModel() const d->m_defaultModel = componentModel(const_cast<PackageManagerCore*> (this), QLatin1String("AllComponentsModel")); } - connect(this, SIGNAL(finishAllComponentsReset(QList<QInstaller::Component*>)), d->m_defaultModel, - SLOT(setRootComponents(QList<QInstaller::Component*>))); + connect(this, &PackageManagerCore::finishAllComponentsReset, d->m_defaultModel, + &ComponentModel::setRootComponents); return d->m_defaultModel; } @@ -1631,11 +1792,70 @@ ComponentModel *PackageManagerCore::updaterComponentModel() const d->m_updaterModel = componentModel(const_cast<PackageManagerCore*> (this), QLatin1String("UpdaterComponentsModel")); } - connect(this, SIGNAL(finishUpdaterComponentsReset(QList<QInstaller::Component*>)), d->m_updaterModel, - SLOT(setRootComponents(QList<QInstaller::Component*>))); + connect(this, &PackageManagerCore::finishUpdaterComponentsReset, d->m_updaterModel, + &ComponentModel::setRootComponents); return d->m_updaterModel; } +void PackageManagerCore::updateComponentsSilently() +{ + //Check if there are processes running in the install + QStringList excludeFiles; + excludeFiles.append(maintenanceToolName()); + + QStringList runningProcesses = d->runningInstallerProcesses(excludeFiles); + if (!runningProcesses.isEmpty()) { + qDebug() << "Unable to update components. Please stop these processes: " + << runningProcesses << " and try again."; + return; + } + + autoAcceptMessageBoxes(); + + //Prevent infinite loop if installation for some reason fails. + setMessageBoxAutomaticAnswer(QLatin1String("installationErrorWithRetry"), QMessageBox::Cancel); + + fetchRemotePackagesTree(); + + const QList<QInstaller::Component*> componentList = components( + ComponentType::Root | ComponentType::Descendants); + + if (componentList.count() == 0) { + qDebug() << "No updates available."; + } else { + // Check if essential components are available (essential components are disabled). + // If essential components are found, update first essential updates, + // restart installer and install rest of the updates. + bool essentialUpdatesFound = false; + foreach (Component *component, componentList) { + if (component->value(scEssential, scFalse).toLower() == scTrue) + essentialUpdatesFound = true; + } + if (!essentialUpdatesFound) { + //Mark all components to be updated + foreach (Component *comp, componentList) { + comp->setCheckState(Qt::Checked); + } + } + QString htmlOutput; + bool componentsOk = calculateComponents(&htmlOutput); + if (componentsOk) { + if (runPackageUpdater()) { + writeMaintenanceTool(); + if (essentialUpdatesFound) { + qDebug() << "Essential components updated successfully."; + } + else { + qDebug() << "Components updated successfully."; + } + } + } + else { + qDebug() << htmlOutput; + } + } +} + /*! Returns the settings for the package manager. */ @@ -1700,20 +1920,21 @@ bool PackageManagerCore::killProcess(const QString &absoluteFilePath) const processPath = QDir::cleanPath(processPath.replace(QLatin1Char('\\'), QLatin1Char('/'))); if (processPath == normalizedPath) { - qDebug() << QString::fromLatin1("try to kill process: %1(%2)").arg(process.name).arg(process.id); + qDebug().nospace() << "try to kill process " << process.name << " (" << process.id << ")"; //to keep the ui responsible use QtConcurrent::run QFutureWatcher<bool> futureWatcher; const QFuture<bool> future = QtConcurrent::run(KDUpdater::killProcess, process, 30000); QEventLoop loop; - loop.connect(&futureWatcher, SIGNAL(finished()), SLOT(quit()), Qt::QueuedConnection); + connect(&futureWatcher, &QFutureWatcher<bool>::finished, + &loop, &QEventLoop::quit, Qt::QueuedConnection); futureWatcher.setFuture(future); if (!future.isFinished()) loop.exec(); - qDebug() << QString::fromLatin1("\"%1\" killed!").arg(process.name); + qDebug() << process.name << "killed!"; return future.result(); } } @@ -1757,6 +1978,12 @@ bool PackageManagerCore::localInstallerBinaryUsed() \a stdIn is sent as standard input to the application. + \a stdInCodec is the name of the codec to use for converting the input string + into bytes to write to the standard input of the application. + + \a stdOutCodec is the name of the codec to use for converting data written by the + application to standard output into a string. + Returns an empty array if the program could not be executed, otherwise the output of command as the first item, and the return code as the second. @@ -1766,7 +1993,7 @@ bool PackageManagerCore::localInstallerBinaryUsed() \sa executeDetached() */ QList<QVariant> PackageManagerCore::execute(const QString &program, const QStringList &arguments, - const QString &stdIn) const + const QString &stdIn, const QString &stdInCodec, const QString &stdOutCodec) const { QProcessWrapper process; @@ -1783,13 +2010,23 @@ QList<QVariant> PackageManagerCore::execute(const QString &program, const QStrin return QList< QVariant >(); if (!adjustedStdIn.isNull()) { - process.write(adjustedStdIn.toLatin1()); + QTextCodec *codec = QTextCodec::codecForName(qPrintable(stdInCodec)); + if (!codec) + return QList<QVariant>(); + + QTextEncoder encoder(codec); + process.write(encoder.fromUnicode(adjustedStdIn)); process.closeWriteChannel(); } process.waitForFinished(-1); - return QList<QVariant>() << QString::fromLatin1(process.readAllStandardOutput()) << process.exitCode(); + QTextCodec *codec = QTextCodec::codecForName(qPrintable(stdOutCodec)); + if (!codec) + return QList<QVariant>(); + return QList<QVariant>() + << QTextDecoder(codec).toUnicode(process.readAllStandardOutput()) + << process.exitCode(); } /*! @@ -1864,14 +2101,7 @@ QString PackageManagerCore::environmentVariable(const QString &name) const */ bool PackageManagerCore::operationExists(const QString &name) { - static QSet<QString> existingOperations; - if (existingOperations.contains(name)) - return true; - QScopedPointer<Operation> op(KDUpdater::UpdateOperationFactory::instance().create(name)); - if (!op.data()) - return false; - existingOperations.insert(name); - return true; + return KDUpdater::UpdateOperationFactory::instance().containsProduct(name); } /*! @@ -1883,7 +2113,7 @@ bool PackageManagerCore::operationExists(const QString &name) */ bool PackageManagerCore::performOperation(const QString &name, const QStringList &arguments) { - QScopedPointer<Operation> op(KDUpdater::UpdateOperationFactory::instance().create(name)); + QScopedPointer<Operation> op(KDUpdater::UpdateOperationFactory::instance().create(name, this)); if (!op.data()) return false; @@ -2260,6 +2490,14 @@ bool PackageManagerCore::isPackageManager() const } /*! + Returns \c true if it is a package manager or an updater. +*/ +bool PackageManagerCore::isMaintainer() const +{ + return isPackageManager() || isUpdater(); +} + +/*! Runs the installer. Returns \c true on success, \c false otherwise. \sa {installer::runInstaller}{installer.runInstaller} @@ -2308,7 +2546,7 @@ bool PackageManagerCore::run() return d->runInstaller(); else if (isUninstaller()) return d->runUninstaller(); - else if (isPackageManager() || isUpdater()) + else if (isMaintainer()) return d->runPackageUpdater(); return false; } @@ -2327,7 +2565,7 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo // check if we already added the component to the available components list const QString name = data.package->data(scName).toString(); if (data.components->contains(name)) { - qCritical("Could not register component! Component with identifier %s already registered.", + qCritical("Cannot register component! Component with identifier %s already registered.", qPrintable(name)); return false; } @@ -2413,7 +2651,7 @@ void PackageManagerCore::storeReplacedComponents(QHash<QString, Component *> &co // installer binary or the installed component list, just ignore it. This // can happen when in installer mode and probably package manager mode too. if (isUpdater()) - qWarning() << componentName << "- Does not exist in the repositories anymore."; + qDebug() << componentName << "- Does not exist in the repositories anymore."; continue; } if (!componentToReplace && !d->componentsToReplace().contains(componentName)) { @@ -2529,7 +2767,7 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const continue; // Update for not installed package found, skip it. const LocalPackage &localPackage = locals.value(name); - const QString updateVersion = update->data(scRemoteVersion).toString(); + const QString updateVersion = update->data(scVersion).toString(); if (KDUpdater::compareVersion(updateVersion, localPackage.version) <= 0) continue; @@ -2650,7 +2888,7 @@ void PackageManagerCore::updateDisplayVersions(const QString &displayKey) } visited.clear(); const QString displayVersionRemote = findDisplayVersion(key, componentsHash, - scRemoteVersion, visited); + scVersion, visited); if (displayVersionRemote.isEmpty()) componentsHash.value(key)->setValue(displayKey, tr("invalid")); else @@ -2698,3 +2936,13 @@ ComponentModel *PackageManagerCore::componentModel(PackageManagerCore *core, con return model; } + +QStringList PackageManagerCore::filesForDelayedDeletion() const +{ + return d->m_filesForDelayedDeletion; +} + +void PackageManagerCore::addFilesForDelayedDeletion(const QStringList &files) +{ + d->m_filesForDelayedDeletion.append(files); +} diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h index dc24bb46c..2a235bd44 100644 --- a/src/libs/installer/packagemanagercore.h +++ b/src/libs/installer/packagemanagercore.h @@ -122,6 +122,7 @@ public: PackagesList remotePackages(); bool fetchRemotePackagesTree(); + bool fetchCompressedPackagesTree(); bool run(); void reset(const QHash<QString, QString> ¶ms); @@ -133,7 +134,9 @@ public: Q_INVOKABLE bool localInstallerBinaryUsed(); Q_INVOKABLE QList<QVariant> execute(const QString &program, - const QStringList &arguments = QStringList(), const QString &stdIn = QString()) const; + const QStringList &arguments = QStringList(), const QString &stdIn = QString(), + const QString &stdInCodec = QLatin1String("latin1"), + const QString &stdOutCodec = QLatin1String("latin1")) const; Q_INVOKABLE bool executeDetached(const QString &program, const QStringList &arguments = QStringList(), const QString &workingDirectory = QString()) const; @@ -174,8 +177,8 @@ public: void setTestChecksum(bool test); Q_INVOKABLE void addUserRepositories(const QStringList &repositories); - Q_INVOKABLE void setTemporaryRepositories(const QStringList &repositories, bool replace = false); - + Q_INVOKABLE void setTemporaryRepositories(const QStringList &repositories, + bool replace = false, bool compressed = false); Q_INVOKABLE void autoAcceptMessageBoxes(); Q_INVOKABLE void autoRejectMessageBoxes(); Q_INVOKABLE void setMessageBoxAutomaticAnswer(const QString &identifier, int button); @@ -184,6 +187,7 @@ public: Q_INVOKABLE bool isFileExtensionRegistered(const QString &extension) const; Q_INVOKABLE bool fileExists(const QString &filePath) const; + Q_INVOKABLE QString readFile(const QString &filePath, const QString &codecName) const; public: ScriptEngine *componentScriptEngine() const; @@ -199,6 +203,7 @@ public: Q_INVOKABLE bool calculateComponentsToInstall() const; QList<Component*> orderedComponentsToInstall() const; + bool calculateComponents(QString *displayString); Q_INVOKABLE bool calculateComponentsToUninstall() const; QList<Component*> componentsToUninstall() const; @@ -210,6 +215,7 @@ public: ComponentModel *defaultComponentModel() const; ComponentModel *updaterComponentModel() const; + void updateComponentsSilently(); // convenience Q_INVOKABLE bool isInstaller() const; @@ -224,6 +230,8 @@ public: Q_INVOKABLE void setPackageManager(); Q_INVOKABLE bool isPackageManager() const; + bool isMaintainer() const; + bool isVerbose() const; void setVerbose(bool on); @@ -254,6 +262,9 @@ public: void setNeedsHardRestart(bool needsHardRestart = true); bool finishedWithSuccess() const; + QStringList filesForDelayedDeletion() const; + void addFilesForDelayedDeletion(const QStringList &files); + public Q_SLOTS: bool runInstaller(); bool runUninstaller(); @@ -279,6 +290,7 @@ Q_SIGNALS: void finishButtonClicked(); void metaJobProgress(int progress); + void metaJobTotalProgress(int progress); void metaJobInfoMessage(const QString &message); void startAllComponentsReset(); @@ -327,6 +339,8 @@ private: ComponentModel *componentModel(PackageManagerCore *core, const QString &objectName) const; QList<Component *> componentsMarkedForInstallation() const; + bool fetchPackagesTree(const PackagesList &packages, const LocalPackagesHash installedPackages); + private: PackageManagerCorePrivate *const d; friend class PackageManagerCorePrivate; diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index c0723d72c..fb802ef8b 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -49,13 +49,13 @@ #include "componentchecker.h" #include "globals.h" -#include "kdselfrestarter.h" -#include "kdupdaterfiledownloaderfactory.h" -#include "kdupdaterupdatesourcesinfo.h" -#include "kdupdaterupdateoperationfactory.h" +#include "selfrestarter.h" +#include "filedownloaderfactory.h" +#include "updateoperationfactory.h" #include <productkeycheck.h> +#include <QSettings> #include <QtConcurrentRun> #include <QtCore/QCoreApplication> #include <QtCore/QDir> @@ -92,9 +92,9 @@ public: { if (!m_operation) return; - qDebug() << QString::fromLatin1("%1 %2 operation: %3").arg(state, m_operation->value( + qDebug().noquote() << QString::fromLatin1("%1 %2 operation: %3").arg(state, m_operation->value( QLatin1String("component")).toString(), m_operation->name()); - qDebug() << QString::fromLatin1("\t- arguments: %1").arg(m_operation->arguments() + qDebug().noquote() << QString::fromLatin1("\t- arguments: %1").arg(m_operation->arguments() .join(QLatin1String(", "))); } ~OperationTracer() { @@ -151,6 +151,7 @@ static void deferredRename(const QString &oldName, const QString &newName, bool .fileName())); QTextStream batch(&f); + batch.setCodec("UTF-16"); batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n"; batch << "Set tmp = WScript.CreateObject(\"WScript.Shell\")\n"; batch << QString::fromLatin1("file = \"%1\"\n").arg(arguments[2]); @@ -161,8 +162,16 @@ static void deferredRename(const QString &oldName, const QString &newName, bool batch << " WScript.Sleep(1000)\n"; batch << "wend\n"; batch << QString::fromLatin1("fso.MoveFile \"%1\", file\n").arg(arguments[1]); - if (restart) - batch << QString::fromLatin1("tmp.exec \"%1 --updater\"\n").arg(arguments[2]); + if (restart) { + //Restart with same command line arguments as first executable + QStringList commandLineArguments = QCoreApplication::arguments(); + batch << QString::fromLatin1("tmp.exec \"%1 --updater").arg(arguments[2]); + //Skip the first argument as that is executable itself + for (int i = 1; i < commandLineArguments.count(); i++) { + batch << QString::fromLatin1(" %1").arg(commandLineArguments.at(i)); + } + batch << QString::fromLatin1("\"\n"); + } batch << "fso.DeleteFile(WScript.ScriptFullName)\n"; } @@ -171,7 +180,7 @@ static void deferredRename(const QString &oldName, const QString &newName, bool #else QFile::remove(newName); QFile::rename(oldName, newName); - KDSelfRestarter::setRestartOnQuit(restart); + SelfRestarter::setRestartOnQuit(restart); #endif } @@ -180,7 +189,8 @@ static void deferredRename(const QString &oldName, const QString &newName, bool PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) : m_updateFinder(0) - , m_updaterApplication(new DummyConfigurationInterface) + , m_compressedFinder(0) + , m_localPackageHub(std::make_shared<LocalPackageHub>()) , m_core(core) , m_updates(false) , m_repoFetched(false) @@ -201,7 +211,8 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker, const QList<OperationBlob> &performedOperations) : m_updateFinder(0) - , m_updaterApplication(new DummyConfigurationInterface) + , m_compressedFinder(0) + , m_localPackageHub(std::make_shared<LocalPackageHub>()) , m_status(PackageManagerCore::Unfinished) , m_needsHardRestart(false) , m_testChecksum(false) @@ -227,24 +238,27 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q { foreach (const OperationBlob &operation, performedOperations) { QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance() - .create(operation.name)); + .create(operation.name, core)); if (op.isNull()) { - qWarning() << QString::fromLatin1("Failed to load unknown operation %1") - .arg(operation.name); + qWarning() << "Failed to load unknown operation" << operation.name; continue; } if (!op->fromXml(operation.xml)) { - qWarning() << "Failed to load XML for operation:" << operation.name; + qWarning() << "Failed to load XML for operation" << operation.name; continue; } m_performedOperationsOld.append(op.take()); } - connect(this, SIGNAL(installationStarted()), m_core, SIGNAL(installationStarted())); - connect(this, SIGNAL(installationFinished()), m_core, SIGNAL(installationFinished())); - connect(this, SIGNAL(uninstallationStarted()), m_core, SIGNAL(uninstallationStarted())); - connect(this, SIGNAL(uninstallationFinished()), m_core, SIGNAL(uninstallationFinished())); + connect(this, &PackageManagerCorePrivate::installationStarted, + m_core, &PackageManagerCore::installationStarted); + connect(this, &PackageManagerCorePrivate::installationFinished, + m_core, &PackageManagerCore::installationFinished); + connect(this, &PackageManagerCorePrivate::uninstallationStarted, + m_core, &PackageManagerCore::uninstallationStarted); + connect(this, &PackageManagerCorePrivate::uninstallationFinished, + m_core, &PackageManagerCore::uninstallationFinished); } PackageManagerCorePrivate::~PackageManagerCorePrivate() @@ -306,7 +320,8 @@ bool PackageManagerCorePrivate::performOperationThreaded(Operation *operation, O const QFuture<bool> future = QtConcurrent::run(runOperation, operation, type); QEventLoop loop; - loop.connect(&futureWatcher, SIGNAL(finished()), SLOT(quit()), Qt::QueuedConnection); + QObject::connect(&futureWatcher, &decltype(futureWatcher)::finished, &loop, &QEventLoop::quit, + Qt::QueuedConnection); futureWatcher.setFuture(future); if (!future.isFinished()) @@ -338,7 +353,7 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c return false; // append all components to their respective parents QHash<QString, Component*>::const_iterator it; - for (it = components.begin(); it != components.end(); ++it) { + for (it = components.constBegin(); it != components.constEnd(); ++it) { QString id = it.key(); QInstaller::Component *component = it.value(); while (!id.isEmpty() && component->parentComponent() == 0) { @@ -390,7 +405,7 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c foreach (QInstaller::Component *component, components) { const QStringList warnings = ComponentChecker::checkComponent(component); foreach (const QString &warning, warnings) - qCWarning(lcComponentChecker) << warning; + qCWarning(lcComponentChecker).noquote() << warning; } } catch (const Error &error) { clearAllComponentLists(); @@ -408,7 +423,7 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c void PackageManagerCorePrivate::cleanUpComponentEnvironment() { // clean up registered (downloaded) data - if (m_core->isUpdater() || m_core->isPackageManager()) + if (m_core->isMaintainer()) BinaryFormatEngineHandler::instance()->clear(); // there could be still some references to already deleted components, @@ -543,45 +558,38 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> ¶ms readMaintenanceConfigFiles(QCoreApplication::applicationDirPath()); #endif } + processFilesForDelayedDeletion(); + m_data.setDynamicPredefinedVariables(); + + disconnect(this, &PackageManagerCorePrivate::installationStarted, + ProgressCoordinator::instance(), &ProgressCoordinator::reset); + connect(this, &PackageManagerCorePrivate::installationStarted, + ProgressCoordinator::instance(), &ProgressCoordinator::reset); + disconnect(this, &PackageManagerCorePrivate::uninstallationStarted, + ProgressCoordinator::instance(), &ProgressCoordinator::reset); + connect(this, &PackageManagerCorePrivate::uninstallationStarted, + ProgressCoordinator::instance(), &ProgressCoordinator::reset); - foreach (Operation *currentOperation, m_performedOperationsOld) - currentOperation->setValue(QLatin1String("installer"), QVariant::fromValue(m_core)); - - disconnect(this, SIGNAL(installationStarted()), ProgressCoordinator::instance(), SLOT(reset())); - connect(this, SIGNAL(installationStarted()), ProgressCoordinator::instance(), SLOT(reset())); - disconnect(this, SIGNAL(uninstallationStarted()), ProgressCoordinator::instance(), SLOT(reset())); - connect(this, SIGNAL(uninstallationStarted()), ProgressCoordinator::instance(), SLOT(reset())); - - m_updaterApplication.updateSourcesInfo()->setFileName(QString()); - KDUpdater::PackagesInfo &packagesInfo = *m_updaterApplication.packagesInfo(); - packagesInfo.setFileName(componentsXmlPath()); + if (!isInstaller()) + m_localPackageHub->setFileName(componentsXmlPath()); - // Note: force overwriting the application name and version in case we run as installer. Both will be - // set to wrong initial values if we install into an already existing installation. This can happen - // if the components.xml path has not been changed, but name or version of the new installer. - if (isInstaller() || packagesInfo.applicationName().isEmpty()) { + if (isInstaller() || m_localPackageHub->applicationName().isEmpty()) { // TODO: this seems to be wrong, we should ask for ProductName defaulting to applicationName... - packagesInfo.setApplicationName(m_data.settings().applicationName()); + m_localPackageHub->setApplicationName(m_data.settings().applicationName()); } - if (isInstaller() || packagesInfo.applicationVersion().isEmpty()) { - packagesInfo.setApplicationVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION))); - } + if (isInstaller() || m_localPackageHub->applicationVersion().isEmpty()) + m_localPackageHub->setApplicationVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION))); - if (isInstaller()) { - // TODO: this seems to be wrong, we should ask for ProductName defaulting to applicationName... - m_updaterApplication.addUpdateSource(m_data.settings().applicationName(), - m_data.settings().applicationName(), QString(), QUrl(QLatin1String("resource://metadata/")), 0); - m_updaterApplication.updateSourcesInfo()->setModified(false); - } + if (isInstaller()) + m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 0)); m_metadataJob.disconnect(); m_metadataJob.setAutoDelete(false); m_metadataJob.setPackageManagerCore(m_core); - connect(&m_metadataJob, SIGNAL(infoMessage(KDJob*, QString)), this, - SLOT(infoMessage(KDJob*, QString))); - connect(&m_metadataJob, SIGNAL(progress(KDJob *, quint64, quint64)), this, - SLOT(infoProgress(KDJob *, quint64, quint64))); + connect(&m_metadataJob, &Job::infoMessage, this, &PackageManagerCorePrivate::infoMessage); + connect(&m_metadataJob, &Job::progress, this, &PackageManagerCorePrivate::infoProgress); + connect(&m_metadataJob, &Job::totalProgress, this, &PackageManagerCorePrivate::totalProgress); KDUpdater::FileDownloaderFactory::instance().setProxyFactory(m_core->proxyFactory()); } @@ -651,7 +659,7 @@ QByteArray PackageManagerCorePrivate::replaceVariables(const QByteArray &ba) con */ Operation *PackageManagerCorePrivate::createOwnedOperation(const QString &type) { - m_ownedOperations.append(KDUpdater::UpdateOperationFactory::instance().create(type)); + m_ownedOperations.append(KDUpdater::UpdateOperationFactory::instance().create(type, m_core)); return m_ownedOperations.last(); } @@ -732,24 +740,30 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles() // write current state (variables) to the maintenance tool ini file const QString iniPath = targetDir() + QLatin1Char('/') + m_data.settings().maintenanceToolIniFile(); - QVariantHash variables; + QVariantHash variables; // Do not change to QVariantMap! Breaks existing .ini files, + // cause the variant types do not match while restoring the variables from the file. QSettingsWrapper cfg(iniPath, QSettingsWrapper::IniFormat); foreach (const QString &key, m_data.keys()) { - if (key != scRunProgramDescription && key != scRunProgram && key != scRunProgramArguments) - variables.insert(key, m_data.value(key)); + if (key == scRunProgramDescription || key == scRunProgram || key == scRunProgramArguments) + continue; + QVariant value = m_data.value(key); + if (value.canConvert(QVariant::String)) + value = replacePath(value.toString(), targetDir(), QLatin1String(scRelocatable)); + variables.insert(key, value); } cfg.setValue(QLatin1String("Variables"), variables); - QVariantList repos; + QVariantList repos; // Do not change either! foreach (const Repository &repo, m_data.settings().defaultRepositories()) repos.append(QVariant().fromValue(repo)); cfg.setValue(QLatin1String("DefaultRepositories"), repos); - cfg.sync(); + cfg.setValue(QLatin1String("FilesForDelayedDeletion"), m_filesForDelayedDeletion); + cfg.sync(); if (cfg.status() != QSettingsWrapper::NoError) { const QString reason = cfg.status() == QSettingsWrapper::AccessError ? tr("Access error") : tr("Format error"); - throw Error(tr("Could not write installer configuration to %1: %2").arg(iniPath, reason)); + throw Error(tr("Cannot write installer configuration to %1: %2").arg(iniPath, reason)); } QFile file(targetDir() + QLatin1Char('/') + QLatin1String("network.xml")); @@ -794,17 +808,22 @@ void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &target { QSettingsWrapper cfg(targetDir + QLatin1Char('/') + m_data.settings().maintenanceToolIniFile(), QSettingsWrapper::IniFormat); - const QVariantHash vars = cfg.value(QLatin1String("Variables")).toHash(); - for (QHash<QString, QVariant>::ConstIterator it = vars.constBegin(); it != vars.constEnd(); ++it) - m_data.setValue(it.key(), it.value().toString()); - + const QVariantHash v = cfg.value(QLatin1String("Variables")).toHash(); // Do not change to + // QVariantMap! Breaks reading from existing .ini files, cause the variant types do not match. + for (QVariantHash::const_iterator it = v.constBegin(); it != v.constEnd(); ++it) { + m_data.setValue(it.key(), replacePath(it.value().toString(), QLatin1String(scRelocatable), + targetDir)); + } QSet<Repository> repos; - const QVariantList variants = cfg.value(QLatin1String("DefaultRepositories")).toList(); + const QVariantList variants = cfg.value(QLatin1String("DefaultRepositories")) + .toList(); // Do not change either! foreach (const QVariant &variant, variants) repos.insert(variant.value<Repository>()); if (!repos.isEmpty()) m_data.settings().setDefaultRepositories(repos); + m_filesForDelayedDeletion = cfg.value(QLatin1String("FilesForDelayedDeletion")).toStringList(); + QFile file(targetDir + QLatin1String("/network.xml")); if (!file.open(QIODevice::ReadOnly)) return; @@ -1003,13 +1022,13 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q { QFile dummy(resourcePath.filePath(QLatin1String("installer.dat"))); if (dummy.exists() && !dummy.remove()) { - throw Error(tr("Could not remove data file '%1': %2").arg(dummy.fileName(), + throw Error(tr("Cannot remove data file \"%1\": %2").arg(dummy.fileName(), dummy.errorString())); } } if (!dataOut.rename(resourcePath.filePath(QLatin1String("installer.dat")))) { - throw Error(tr("Could not write maintenance tool data to %1: %2").arg(out.fileName(), + throw Error(tr("Cannot write maintenance tool data to %1: %2").arg(out.fileName(), out.errorString())); } dataOut.setAutoRemove(false); @@ -1028,13 +1047,13 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q { QFile dummy(maintenanceToolRenamedName); if (dummy.exists() && !dummy.remove()) { - throw Error(tr("Could not remove data file '%1': %2").arg(dummy.fileName(), + throw Error(tr("Cannot remove data file \"%1\": %2").arg(dummy.fileName(), dummy.errorString())); } } if (!out.copy(maintenanceToolRenamedName)) { - throw Error(tr("Could not write maintenance tool to %1: %2").arg(maintenanceToolRenamedName, + throw Error(tr("Cannot write maintenance tool to \"%1\": %2").arg(maintenanceToolRenamedName, out.errorString())); } @@ -1066,8 +1085,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinaryData(QFileDevice *outp file.remove(); // clear all possible leftovers m_core->setValue(QString::fromLatin1("DefaultResourceReplacement"), QString()); } else { - qWarning() << QString::fromLatin1("Could not replace default resource with '%1'.") - .arg(newDefaultResource); + qWarning() << "Cannot replace default resource with" << QDir::toNativeSeparators(newDefaultResource); } } @@ -1080,9 +1098,6 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinaryData(QFileDevice *outp const qint64 operationsStart = output->pos(); QInstaller::appendInt64(output, performedOperations.count()); foreach (Operation *operation, performedOperations) { - // the installer can't be put into XML, remove it first - operation->clearValue(QLatin1String("installer")); - QInstaller::appendString(output, operation->name()); QInstaller::appendString(output, operation->toXml().toString()); @@ -1258,17 +1273,17 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper if (!replacementBinary.remove()) { // Is there anything more sensible we can do with this error? I think not. It's not serious // enough for throwing / aborting the process. - qDebug() << QString::fromLatin1("Could not remove installer base binary '%1' after updating " - "the maintenance tool: %2").arg(installerBaseBinary, replacementBinary.errorString()); + qDebug() << "Cannot remove installer base binary" << installerBaseBinary + << "after updating the maintenance tool:" << replacementBinary.errorString(); } else { - qDebug() << QString::fromLatin1("Removed installer base binary '%1' after updating the " - "maintenance tool.").arg(installerBaseBinary); + qDebug() << "Removed installer base binary" << installerBaseBinary + << "after updating the maintenance tool."; } m_installerBaseBinaryUnreplaced.clear(); } else if (!installerBaseBinary.isEmpty() && !QFileInfo(installerBaseBinary).exists()) { - qWarning() << QString::fromLatin1("The current maintenance tool could not be " - "updated. '%1' does not exist. Please fix the 'setInstallerBaseBinary(<temp_installer_base_" - "binary_path>)' call in your script.").arg(installerBaseBinary); + qWarning() << "The current maintenance tool could not be updated." << installerBaseBinary + << "does not exist. Please fix the \"setInstallerBaseBinary(<temp_installer_base_" + "binary_path>)\" call in your script."; } QFile input; @@ -1278,9 +1293,9 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper try { if (isInstaller()) { if (QFile::exists(dataFile)) { - qWarning() << QString::fromLatin1("Found binary data file '%1' but " + qWarning() << "Found binary data file" << dataFile << "but " "deliberately not used. Running as installer requires to read the " - "resources from the application binary.").arg(dataFile); + "resources from the application binary."; } throw Error(); } @@ -1329,12 +1344,12 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper QFile dummy(dataFile + QLatin1String(".new")); if (dummy.exists() && !dummy.remove()) { - throw Error(tr("Could not remove data file '%1': %2").arg(dummy.fileName(), + throw Error(tr("Cannot remove data file \"%1\": %2").arg(dummy.fileName(), dummy.errorString())); } if (!file.rename(dataFile + QLatin1String(".new"))) { - throw Error(tr("Could not write maintenance tool binary data to %1: %2") + throw Error(tr("Cannot write maintenance tool binary data to %1: %2") .arg(file.fileName(), file.errorString())); } file.setAutoRemove(false); @@ -1394,7 +1409,7 @@ QString PackageManagerCorePrivate::registerPath() } QString path = QLatin1String("HKEY_CURRENT_USER"); - if (m_data.value(QLatin1String("AllUsers"), scFalse).toString() == scTrue) + if (m_data.value(scAllUsers, scFalse).toString() == scTrue) path = QLatin1String("HKEY_LOCAL_MACHINE"); return path + QLatin1String("\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\") @@ -1486,14 +1501,13 @@ bool PackageManagerCorePrivate::runInstaller() componentsInstallPartProgressSize = double(1); // Force an update on the components xml as the install dir might have changed. - KDUpdater::PackagesInfo &info = *m_updaterApplication.packagesInfo(); - info.setFileName(componentsXmlPath()); + m_localPackageHub->setFileName(componentsXmlPath()); // Clear the packages as we might install into an already existing installation folder. - info.clearPackageInfoList(); + m_localPackageHub->clearPackageInfos(); // also update the application name, might be set from a script as well - info.setApplicationName(m_data.value(QLatin1String("ProductName"), + m_localPackageHub->setApplicationName(m_data.value(QLatin1String("ProductName"), m_data.settings().applicationName()).toString()); - info.setApplicationVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION))); + m_localPackageHub->setApplicationVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION))); const int progressOperationCount = countProgressOperations(componentsToInstall) // add one more operation as we support progress @@ -1521,7 +1535,6 @@ bool PackageManagerCorePrivate::runInstaller() binaryFile = resourcePath.filePath(QLatin1String("installer.dat")); #endif createRepo->setValue(QLatin1String("uninstall-only"), true); - createRepo->setValue(QLatin1String("installer"), QVariant::fromValue(m_core)); createRepo->setArguments(QStringList() << binaryFile << target + QLatin1String("/repository")); @@ -1862,7 +1875,7 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr bool ignoreError = false; bool ok = performOperationThreaded(operation); while (!ok && !ignoreError && m_core->status() != PackageManagerCore::Canceled) { - qDebug() << QString::fromLatin1("Operation '%1' with arguments: '%2' failed: %3") + qDebug() << QString::fromLatin1("Operation \"%1\" with arguments \"%2\" failed: %3") .arg(operation->name(), operation->arguments().join(QLatin1String("; ")), operation->errorString()); const QMessageBox::StandardButton button = @@ -1900,7 +1913,7 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr if (!component->stopProcessForUpdateRequests().isEmpty()) { Operation *stopProcessForUpdatesOp = KDUpdater::UpdateOperationFactory::instance() - .create(QLatin1String("FakeStopProcessForUpdate")); + .create(QLatin1String("FakeStopProcessForUpdate"), m_core); const QStringList arguments(component->stopProcessForUpdateRequests().join(QLatin1String(","))); stopProcessForUpdatesOp->setArguments(arguments); addPerformed(stopProcessForUpdatesOp); @@ -1908,12 +1921,18 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr } // now mark the component as installed - KDUpdater::PackagesInfo &packages = *m_updaterApplication.packagesInfo(); - packages.installPackage(component->name(), component->value(scVersion), component->value(scDisplayName), - component->value(scDescription), component->dependencies(), component->forcedInstallation(), - component->isVirtual(), component->value(scUncompressedSize).toULongLong(), - component->value(scInheritVersion)); - packages.writeToDisk(); + m_localPackageHub->addPackage(component->name(), + component->value(scVersion), + component->value(scDisplayName), + component->value(scDescription), + component->dependencies(), + component->autoDependencies(), + component->forcedInstallation(), + component->isVirtual(), + component->value(scUncompressedSize).toULongLong(), + component->value(scInheritVersion), + component->isCheckable()); + m_localPackageHub->writeToDisk(); component->setInstalled(); component->markAsPerformedInstallation(); @@ -2018,7 +2037,13 @@ void PackageManagerCorePrivate::registerMaintenanceTool() const quint64 limit = std::numeric_limits<quint32>::max(); // maximum 32 bit value if (estimatedSizeKB <= limit) settings.setValue(QLatin1String("EstimatedSize"), static_cast<quint32>(estimatedSizeKB)); - settings.setValue(QLatin1String("NoModify"), 0); + + const bool supportsModify = m_core->value(scSupportsModify, scTrue) == scTrue; + if (supportsModify) + settings.setValue(QLatin1String("NoModify"), 0); + else + settings.setValue(QLatin1String("NoModify"), 1); + settings.setValue(QLatin1String("NoRepair"), 1); #endif } @@ -2034,7 +2059,6 @@ void PackageManagerCorePrivate::unregisterMaintenanceTool() void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations, double progressSize, bool adminRightsGained, bool deleteOperation) { - KDUpdater::PackagesInfo &packages = *m_updaterApplication.packagesInfo(); try { foreach (Operation *undoOperation, undoOperations) { if (statusCanceledOrFailed()) @@ -2070,7 +2094,7 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera component = componentsToReplace().value(componentName).second; if (component) { component->setUninstalled(); - packages.removePackage(component->name()); + m_localPackageHub->removePackage(component->name()); } } @@ -2081,13 +2105,13 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera delete undoOperation; } } catch (const Error &error) { - packages.writeToDisk(); + m_localPackageHub->writeToDisk(); throw Error(error.message()); } catch (...) { - packages.writeToDisk(); + m_localPackageHub->writeToDisk(); throw Error(tr("Unknown error")); } - packages.writeToDisk(); + m_localPackageHub->writeToDisk(); } PackagesList PackageManagerCorePrivate::remotePackages() @@ -2098,12 +2122,14 @@ PackagesList PackageManagerCorePrivate::remotePackages() m_updates = false; delete m_updateFinder; - m_updateFinder = new KDUpdater::UpdateFinder(&m_updaterApplication); + m_updateFinder = new KDUpdater::UpdateFinder; m_updateFinder->setAutoDelete(false); + m_updateFinder->setPackageSources(m_packageSources); + m_updateFinder->setLocalPackageHub(m_localPackageHub); m_updateFinder->run(); if (m_updateFinder->updates().isEmpty()) { - setStatus(PackageManagerCore::Failure, tr("Could not retrieve remote tree: %1.") + setStatus(PackageManagerCore::Failure, tr("Cannot retrieve remote tree %1.") .arg(m_updateFinder->errorString())); return PackagesList(); } @@ -2112,6 +2138,29 @@ PackagesList PackageManagerCorePrivate::remotePackages() return m_updateFinder->updates(); } +PackagesList PackageManagerCorePrivate::compressedPackages() +{ + if (m_compressedUpdates && m_compressedFinder) + return m_compressedFinder->updates(); + m_compressedUpdates = false; + delete m_compressedFinder; + + m_compressedFinder = new KDUpdater::UpdateFinder; + m_compressedFinder->setAutoDelete(false); + m_compressedFinder->addCompressedPackage(true); + m_compressedFinder->setPackageSources(m_compressedPackageSources); + + m_compressedFinder->setLocalPackageHub(m_localPackageHub); + m_compressedFinder->run(); + if (m_compressedFinder->updates().isEmpty()) { + setStatus(PackageManagerCore::Failure, tr("Cannot retrieve remote tree %1.") + .arg(m_compressedFinder->errorString())); + return PackagesList(); + } + m_compressedUpdates = true; + return m_compressedFinder->updates(); +} + /*! Returns a hash containing the installed package name and it's associated package information. If the application is running in installer mode or the local components file could not be parsed, the @@ -2119,27 +2168,32 @@ PackagesList PackageManagerCorePrivate::remotePackages() */ LocalPackagesHash PackageManagerCorePrivate::localInstalledPackages() { + if (isInstaller()) + return LocalPackagesHash(); + LocalPackagesHash installedPackages; + if (m_localPackageHub->error() != LocalPackageHub::NoError) { + if (m_localPackageHub->fileName().isEmpty()) + m_localPackageHub->setFileName(componentsXmlPath()); + else + m_localPackageHub->refresh(); - if (!isInstaller()) { - KDUpdater::PackagesInfo &packagesInfo = *m_updaterApplication.packagesInfo(); - if (!packagesInfo.isValid()) { - packagesInfo.setFileName(componentsXmlPath()); - if (packagesInfo.applicationName().isEmpty()) - packagesInfo.setApplicationName(m_data.settings().applicationName()); - if (packagesInfo.applicationVersion().isEmpty()) - packagesInfo.setApplicationVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION))); - } + if (m_localPackageHub->applicationName().isEmpty()) + m_localPackageHub->setApplicationName(m_data.settings().applicationName()); + if (m_localPackageHub->applicationVersion().isEmpty()) + m_localPackageHub->setApplicationVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION))); + } - if (packagesInfo.error() != KDUpdater::PackagesInfo::NoError) - setStatus(PackageManagerCore::Failure, tr("Failure to read packages from: %1.").arg(componentsXmlPath())); + if (m_localPackageHub->error() != LocalPackageHub::NoError) { + setStatus(PackageManagerCore::Failure, tr("Failure to read packages from %1.") + .arg(componentsXmlPath())); + } - foreach (const LocalPackage &package, packagesInfo.packageInfos()) { - if (statusCanceledOrFailed()) - break; - installedPackages.insert(package.name, package); - } - } + foreach (const LocalPackage &package, m_localPackageHub->packageInfos()) { + if (statusCanceledOrFailed()) + break; + installedPackages.insert(package.name, package); + } return installedPackages; } @@ -2157,12 +2211,12 @@ bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories() m_metadataJob.start(); m_metadataJob.waitForFinished(); } catch (Error &error) { - setStatus(PackageManagerCore::Failure, tr("Could not retrieve meta information: %1") + setStatus(PackageManagerCore::Failure, tr("Cannot retrieve meta information: %1") .arg(error.message())); return m_repoFetched; } - if (m_metadataJob.error() != KDJob::NoError) { + if (m_metadataJob.error() != Job::NoError) { switch (m_metadataJob.error()) { case QInstaller::UserIgnoreError: break; // we can simply ignore this error, the user knows about it @@ -2176,9 +2230,46 @@ bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories() return m_repoFetched; } -bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChecksum) +bool PackageManagerCorePrivate::fetchMetaInformationFromCompressedRepositories() +{ + bool compressedRepoFetched = false; + + m_compressedUpdates = false; + m_updateSourcesAdded = false; + + try { + //Tell MetadataJob that only compressed packages needed to be fetched and not all. + //We cannot do this in general fetch meta method as the compressed packages might be + //installed after components tree is generated + m_metadataJob.addCompressedPackages(true); + m_metadataJob.start(); + m_metadataJob.waitForFinished(); + m_metadataJob.addCompressedPackages(false); + } catch (Error &error) { + setStatus(PackageManagerCore::Failure, tr("Cannot retrieve meta information: %1") + .arg(error.message())); + return compressedRepoFetched; + } + + if (m_metadataJob.error() != Job::NoError) { + switch (m_metadataJob.error()) { + case QInstaller::UserIgnoreError: + break; // we can simply ignore this error, the user knows about it + default: + //Do not change core status here, we can recover if there is invalid + //compressed repository + setStatus(m_core->status(), m_metadataJob.errorString()); + return compressedRepoFetched; + } + } + + compressedRepoFetched = true; + return compressedRepoFetched; +} + +bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository) { - if (m_updateSourcesAdded) + if (!compressedRepository && m_updateSourcesAdded) return m_updateSourcesAdded; const QList<Metadata> metadata = m_metadataJob.metadata(); @@ -2186,21 +2277,21 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe m_updateSourcesAdded = true; return m_updateSourcesAdded; } - - // forces an refresh / clear on all update sources - m_updaterApplication.updateSourcesInfo()->refresh(); - if (isInstaller()) { - m_updaterApplication.addUpdateSource(m_data.settings().applicationName(), - m_data.settings().applicationName(), QString(), - QUrl(QLatin1String("resource://metadata/")), 0); - m_updaterApplication.updateSourcesInfo()->setModified(false); + if (compressedRepository) { + m_compressedPackageSources.clear(); + } + else { + m_packageSources.clear(); + m_updates = false; + m_updateSourcesAdded = false; + if (isInstaller()) + m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 0)); } - m_updates = false; - m_updateSourcesAdded = false; - - const QString &appName = m_data.settings().applicationName(); foreach (const Metadata &data, metadata) { + if (compressedRepository && !data.repository.isCompressed()) { + continue; + } if (statusCanceledOrFailed()) return false; @@ -2214,7 +2305,7 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe QInstaller::openForRead(&updatesFile); } catch(const Error &e) { qDebug() << "Error opening Updates.xml:" << e.message(); - setStatus(PackageManagerCore::Failure, tr("Could not add temporary update source information.")); + setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information.")); return false; } @@ -2223,9 +2314,9 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe QString error; QDomDocument doc; if (!doc.setContent(&updatesFile, &error, &line, &column)) { - qDebug() << QString::fromLatin1("Parse error in file %4: %1 at line %2 col %3").arg(error, - QString::number(line), QString::number(column), updatesFile.fileName()); - setStatus(PackageManagerCore::Failure, tr("Could not add temporary update source information.")); + qDebug().nospace() << "Parse error in file" << updatesFile.fileName() + << ": " << error << " at line " << line << " col " << column; + setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information.")); return false; } @@ -2233,14 +2324,16 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe if (!checksum.isNull()) m_core->setTestChecksum(checksum.toElement().text().toLower() == scTrue); } - m_updaterApplication.addUpdateSource(appName, appName, QString(), - QUrl::fromLocalFile(data.directory), 1); + if (compressedRepository) + m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data.directory), 1)); + else + m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data.directory), 1)); + ProductKeyCheck::instance()->addPackagesFromXml(data.directory + QLatin1String("/Updates.xml")); } - m_updaterApplication.updateSourcesInfo()->setModified(false); - - if (m_updaterApplication.updateSourcesInfo()->updateSourceInfoCount() == 0) { - setStatus(PackageManagerCore::Failure, tr("Could not find any update source information.")); + if ((compressedRepository && m_compressedPackageSources.count() == 0 ) || + (!compressedRepository && m_packageSources.count() == 0)) { + setStatus(PackageManagerCore::Failure, tr("Cannot find any update source information.")); return false; } @@ -2292,25 +2385,20 @@ OperationList PackageManagerCorePrivate::sortOperationsBasedOnComponentDependenc const QString componentName = operation->value(QLatin1String("component")).toString(); if (componentName.isEmpty()) sortedOperations.append(operation); - else { - OperationList componentOperationList = componentOperationHash.value(componentName); - componentOperationList.append(operation); - componentOperationHash.insert(operation->value(QLatin1String("component")).toString(), - componentOperationList); - } + else + componentOperationHash[componentName].append(operation); } - const QString empty; const QRegExp dash(QLatin1String("-.*")); Graph<QString> componentGraph; // create the complete component graph foreach (const Component* node, m_core->components(PackageManagerCore::ComponentType::All)) { componentGraph.addNode(node->name()); - componentGraph.addEdges(node->name(), node->dependencies().replaceInStrings(dash, empty)); + componentGraph.addEdges(node->name(), node->dependencies().replaceInStrings(dash, QString())); } const QStringList resolvedComponents = componentGraph.sort(); if (componentGraph.hasCycle()) { - throw Error(tr("Dependency cycle between components detected: '%1' and '%2'.") + throw Error(tr("Dependency cycle between components \"%1\" and \"%2\" detected.") .arg(componentGraph.cycle().first, componentGraph.cycle().second)); } foreach (const QString &componentName, resolvedComponents) @@ -2326,4 +2414,44 @@ void PackageManagerCorePrivate::handleMethodInvocationRequest(const QString &inv QMetaObject::invokeMethod(obj, qPrintable(invokableMethodName)); } +void PackageManagerCorePrivate::processFilesForDelayedDeletion() +{ + if (m_filesForDelayedDeletion.isEmpty()) + return; + + const QStringList filesForDelayedDeletion = std::move(m_filesForDelayedDeletion); + foreach (const QString &i, filesForDelayedDeletion) { + QFile file(i); //TODO: this should happen asnyc and report errors, I guess + if (file.exists() && !file.remove()) { + qWarning("Cannot delete file %s: %s", qPrintable(i), + qPrintable(file.errorString())); + m_filesForDelayedDeletion << i; // try again next time + } + } +} + +void PackageManagerCorePrivate::findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result) +{ + QString executable; + QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Executable | QDir::Files | QDir::System, QDirIterator::Subdirectories ); + + while (it.hasNext()) { + executable = it.next(); + foreach (QString exclude, excludeFiles) { + if (QDir::toNativeSeparators(executable.toLower()) + != QDir::toNativeSeparators(exclude.toLower())) { + result->append(executable); + } + } + } +} + +QStringList PackageManagerCorePrivate::runningInstallerProcesses(const QStringList &excludeFiles) +{ + QStringList resultFiles; + findExecutablesRecursive(QCoreApplication::applicationDirPath(), excludeFiles, &resultFiles); + return checkRunningProcessesFromList(resultFiles); +} + + } // namespace QInstaller diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h index 663bb19bd..21ab3fc40 100644 --- a/src/libs/installer/packagemanagercore_p.h +++ b/src/libs/installer/packagemanagercore_p.h @@ -33,15 +33,15 @@ #include "packagemanagercore.h" #include "packagemanagercoredata.h" #include "packagemanagerproxyfactory.h" +#include "packagesource.h" #include "qinstallerglobal.h" -#include "kdsysinfo.h" -#include "kdupdaterapplication.h" -#include "kdupdaterupdatefinder.h" +#include "sysinfo.h" +#include "updatefinder.h" #include <QObject> -class KDJob; +class Job; QT_FORWARD_DECLARE_CLASS(QFile) QT_FORWARD_DECLARE_CLASS(QFileDevice) @@ -60,27 +60,6 @@ class InstallerCalculator; class UninstallerCalculator; class RemoteFileEngineHandler; -/* - The default configuration interface implementation does call QSettings to save files for later deletion, - though according to QSettings there should nothing be written if QSettings is not setup properly (which - we do not in our case). Still, caused by a broken QSettings implementation at least on Linux we write an - empty config file which resulted in QTIFW-196. To workaround the issue we now use this empty dummy class. -*/ -class DummyConfigurationInterface : public KDUpdater::ConfigurationInterface -{ -public: - QVariant value(const QString &key) const - { - Q_UNUSED(key) - return QVariant(); - } - void setValue(const QString &key, const QVariant &value) - { - if (value.isNull()) - qDebug() << "DummyConfigurationInterface called with key:" << key << "and value:" << value; - } -}; - class PackageManagerCorePrivate : public QObject { Q_OBJECT @@ -191,7 +170,11 @@ signals: public: UpdateFinder *m_updateFinder; - Application m_updaterApplication; + UpdateFinder *m_compressedFinder; + QSet<PackageSource> m_packageSources; + QSet<PackageSource> m_compressedPackageSources; + std::shared_ptr<LocalPackageHub> m_localPackageHub; + QStringList m_filesForDelayedDeletion; int m_status; QString m_error; @@ -219,13 +202,17 @@ public: bool m_dependsOnLocalInstallerBinary; private slots: - void infoMessage(KDJob *, const QString &message) { + void infoMessage(Job *, const QString &message) { emit m_core->metaJobInfoMessage(message); } - void infoProgress(KDJob *, quint64 progress, quint64) { + void infoProgress(Job *, quint64 progress, quint64) { emit m_core->metaJobProgress(progress); } + void totalProgress(quint64 total) { + emit m_core->metaJobTotalProgress(total); + } + void handleMethodInvocationRequest(const QString &invokableMethodName); private: @@ -241,15 +228,21 @@ private: bool adminRightsGained, bool deleteOperation); PackagesList remotePackages(); + PackagesList compressedPackages(); LocalPackagesHash localInstalledPackages(); bool fetchMetaInformationFromRepositories(); - bool addUpdateResourcesFromRepositories(bool parseChecksum); + bool fetchMetaInformationFromCompressedRepositories(); + bool addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository = false); + void processFilesForDelayedDeletion(); + void findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result); + QStringList runningInstallerProcesses(const QStringList &exludeFiles); private: PackageManagerCore *m_core; MetadataJob m_metadataJob; bool m_updates; + bool m_compressedUpdates; bool m_repoFetched; bool m_updateSourcesAdded; qint64 m_magicBinaryMarker; diff --git a/src/libs/installer/packagemanagercoredata.cpp b/src/libs/installer/packagemanagercoredata.cpp index 2a92a468e..3b35794cf 100644 --- a/src/libs/installer/packagemanagercoredata.cpp +++ b/src/libs/installer/packagemanagercoredata.cpp @@ -47,27 +47,14 @@ namespace QInstaller PackageManagerCoreData::PackageManagerCoreData(const QHash<QString, QString> &variables) { m_variables = variables; + setDynamicPredefinedVariables(); // Set some common variables that may used e.g. as placeholder in some of the settings variables or // in a script or... - m_variables.insert(QLatin1String("rootDir"), QDir::rootPath()); - m_variables.insert(QLatin1String("homeDir"), QDir::homePath()); - m_variables.insert(QLatin1String("RootDir"), QDir::rootPath()); - m_variables.insert(QLatin1String("HomeDir"), QDir::homePath()); m_variables.insert(scTargetConfigurationFile, QLatin1String("components.xml")); m_variables.insert(QLatin1String("InstallerDirPath"), QCoreApplication::applicationDirPath()); m_variables.insert(QLatin1String("InstallerFilePath"), QCoreApplication::applicationFilePath()); - QString dir = QLatin1String("/opt"); -#ifdef Q_OS_WIN - TCHAR buffer[MAX_PATH + 1] = { 0 }; - SHGetFolderPath(0, CSIDL_PROGRAM_FILES, 0, 0, buffer); - dir = QString::fromWCharArray(buffer); -#elif defined (Q_OS_OSX) - dir = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).value(0); -#endif - m_variables.insert(QLatin1String("ApplicationsDir"), dir); - #ifdef Q_OS_WIN m_variables.insert(QLatin1String("os"), QLatin1String("win")); #elif defined(Q_OS_OSX) @@ -78,26 +65,6 @@ PackageManagerCoreData::PackageManagerCoreData(const QHash<QString, QString> &va // TODO: add more platforms as needed... #endif -#ifdef Q_OS_WIN - QSettingsWrapper user(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\" - "CurrentVersion\\Explorer\\User Shell Folders"), QSettingsWrapper::NativeFormat); - QSettingsWrapper system(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\" - "CurrentVersion\\Explorer\\Shell Folders"), QSettingsWrapper::NativeFormat); - - const QString programs = user.value(QLatin1String("Programs"), QString()).toString(); - const QString allPrograms = system.value(QLatin1String("Common Programs"), QString()).toString(); - - QString desktop; - if (m_variables.value(QLatin1String("AllUsers")) == scTrue) { - desktop = system.value(QLatin1String("Desktop")).toString(); - } else { - desktop = user.value(QLatin1String("Desktop")).toString(); - } - m_variables.insert(QLatin1String("DesktopDir"), replaceWindowsEnvironmentVariables(desktop)); - m_variables.insert(QLatin1String("UserStartMenuProgramsPath"), replaceWindowsEnvironmentVariables(programs)); - m_variables.insert(QLatin1String("AllUsersStartMenuProgramsPath"), replaceWindowsEnvironmentVariables(allPrograms)); -#endif - m_settings = Settings::fromFileAndPrefix(QLatin1String(":/metadata/installer-config/config.xml"), QLatin1String(":/metadata/installer-config/"), Settings::RelaxedParseMode); @@ -127,12 +94,81 @@ void PackageManagerCoreData::clear() m_settings = Settings(); } +/*! + Set some common variables that may be used e.g. as placeholder in some of the settings + variables or in a script or... +*/ +void PackageManagerCoreData::setDynamicPredefinedVariables() +{ + m_variables.insert(QLatin1String("rootDir"), QDir::rootPath()); + m_variables.insert(QLatin1String("homeDir"), QDir::homePath()); + m_variables.insert(QLatin1String("RootDir"), QDir::rootPath()); + m_variables.insert(QLatin1String("HomeDir"), QDir::homePath()); + + QString dir = QLatin1String("/opt"); +#ifdef Q_OS_WIN + TCHAR buffer[MAX_PATH + 1] = { 0 }; + SHGetFolderPath(0, CSIDL_PROGRAM_FILES, 0, 0, buffer); + dir = QString::fromWCharArray(buffer); +#elif defined (Q_OS_OSX) + dir = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).value(0); +#endif + m_variables.insert(QLatin1String("ApplicationsDir"), dir); + + QString dirX86 = dir; + QString dirX64 = dir; +#ifdef Q_OS_WIN + QSettingsWrapper current(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion") + , QSettingsWrapper::NativeFormat); + BOOL onWow64Or64bit = TRUE; +#ifndef Q_OS_WIN64 + IsWow64Process(GetCurrentProcess(), &onWow64Or64bit); +#endif + QString programfilesX86; + QString programfilesX64; + if (onWow64Or64bit == TRUE) { + programfilesX86 = current.value(QLatin1String("ProgramFilesDir (x86)"), QString()).toString(); + programfilesX64 = current.value(QLatin1String("ProgramW6432Dir"), QString()).toString(); + } else { + programfilesX86 = current.value(QLatin1String("ProgramFilesDir"), QString()).toString(); + programfilesX64 = programfilesX86; + } + dirX86 = replaceWindowsEnvironmentVariables(programfilesX86); + dirX64 = replaceWindowsEnvironmentVariables(programfilesX64); +#endif + m_variables.insert(QLatin1String("ApplicationsDirX86"), dirX86); + m_variables.insert(QLatin1String("ApplicationsDirX64"), dirX64); + +#ifdef Q_OS_WIN + QSettingsWrapper user(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\" + "CurrentVersion\\Explorer\\User Shell Folders"), QSettingsWrapper::NativeFormat); + QSettingsWrapper system(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\" + "CurrentVersion\\Explorer\\Shell Folders"), QSettingsWrapper::NativeFormat); + + const QString programs = user.value(QLatin1String("Programs"), QString()).toString(); + const QString allPrograms = system.value(QLatin1String("Common Programs"), QString()) + .toString(); + + QString desktop; + if (m_variables.value(QLatin1String("AllUsers")) == scTrue) { + desktop = system.value(QLatin1String("Desktop")).toString(); + } else { + desktop = user.value(QLatin1String("Desktop")).toString(); + } + m_variables.insert(QLatin1String("DesktopDir"), replaceWindowsEnvironmentVariables(desktop)); + m_variables.insert(QLatin1String("UserStartMenuProgramsPath"), + replaceWindowsEnvironmentVariables(programs)); + m_variables.insert(QLatin1String("AllUsersStartMenuProgramsPath"), + replaceWindowsEnvironmentVariables(allPrograms)); +#endif +} + Settings &PackageManagerCoreData::settings() const { return m_settings; } -QList<QString> PackageManagerCoreData::keys() const +QStringList PackageManagerCoreData::keys() const { return m_variables.keys(); } @@ -144,7 +180,7 @@ bool PackageManagerCoreData::contains(const QString &key) const bool PackageManagerCoreData::setValue(const QString &key, const QString &normalizedValue) { - if (m_variables.value(key) == normalizedValue) + if (m_variables.contains(key) && m_variables.value(key) == normalizedValue) return false; m_variables.insert(key, normalizedValue); return true; diff --git a/src/libs/installer/packagemanagercoredata.h b/src/libs/installer/packagemanagercoredata.h index 59b0c9808..ec0d4c302 100644 --- a/src/libs/installer/packagemanagercoredata.h +++ b/src/libs/installer/packagemanagercoredata.h @@ -40,9 +40,10 @@ public: explicit PackageManagerCoreData(const QHash<QString, QString> &variables); void clear(); + void setDynamicPredefinedVariables(); Settings &settings() const; - QList<QString> keys() const; + QStringList keys() const; bool contains(const QString &key) const; bool setValue(const QString &key, const QString &normalizedValue); diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp index 31d733599..5ee3d14fc 100644 --- a/src/libs/installer/packagemanagergui.cpp +++ b/src/libs/installer/packagemanagergui.cpp @@ -40,7 +40,7 @@ #include "scriptengine.h" #include "productkeycheck.h" -#include "kdsysinfo.h" +#include "sysinfo.h" #include <QApplication> @@ -49,7 +49,9 @@ #include <QtCore/QProcess> #include <QtCore/QTimer> +#include <QAbstractItemView> #include <QCheckBox> +#include <QComboBox> #include <QDesktopServices> #include <QFileDialog> #include <QGridLayout> @@ -63,10 +65,12 @@ #include <QProgressBar> #include <QPushButton> #include <QRadioButton> +#include <QStringListModel> #include <QTextBrowser> #include <QTreeView> #include <QVBoxLayout> #include <QShowEvent> +#include <QFileDialog> #ifdef Q_OS_WIN # include <qt_windows.h> @@ -105,7 +109,6 @@ public: setLayout(new QVBoxLayout); layout()->addWidget(widget); layout()->setContentsMargins(0, 0, 0, 0); - layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); addPageAndProperties(packageManagerCore()->controlScriptEngine()); addPageAndProperties(packageManagerCore()->componentScriptEngine()); @@ -202,6 +205,7 @@ public: , m_modified(false) , m_autoSwitchPage(true) , m_showSettingsButton(false) + , m_silent(false) { m_wizardButtonTypes.insert(QWizard::BackButton, QLatin1String("QWizard::BackButton")); m_wizardButtonTypes.insert(QWizard::NextButton, QLatin1String("QWizard::NextButton")); @@ -225,6 +229,7 @@ public: bool m_modified; bool m_autoSwitchPage; bool m_showSettingsButton; + bool m_silent; QHash<int, QWizardPage*> m_defaultPages; QHash<int, QString> m_defaultButtonText; @@ -310,48 +315,66 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent) if (!m_core->settings().wizardStyle().isEmpty()) setWizardStyle(getStyle(m_core->settings().wizardStyle())); + // set custom stylesheet + const QString styleSheetFile = m_core->settings().styleSheet(); + if (!styleSheetFile.isEmpty()) { + QFile sheet(styleSheetFile); + if (sheet.exists()) { + if (sheet.open(QIODevice::ReadOnly)) + setStyleSheet(QString::fromLatin1(sheet.readAll())); + else + qWarning() << "The specified style sheet file can not be opened."; + } else { + qWarning() << "A style sheet file is specified, but it does not exist."; + } + } + setOption(QWizard::NoBackButtonOnStartPage); setOption(QWizard::NoBackButtonOnLastPage); - connect(this, SIGNAL(rejected()), m_core, SLOT(setCanceled())); - connect(this, SIGNAL(interrupted()), m_core, SLOT(interrupt())); + connect(this, &QDialog::rejected, m_core, &PackageManagerCore::setCanceled); + connect(this, &PackageManagerGui::interrupted, m_core, &PackageManagerCore::interrupt); // both queued to show the finished page once everything is done - connect(m_core, SIGNAL(installationFinished()), this, SLOT(showFinishedPage()), + connect(m_core, &PackageManagerCore::installationFinished, + this, &PackageManagerGui::showFinishedPage, Qt::QueuedConnection); - connect(m_core, SIGNAL(uninstallationFinished()), this, SLOT(showFinishedPage()), + connect(m_core, &PackageManagerCore::uninstallationFinished, + this, &PackageManagerGui::showFinishedPage, Qt::QueuedConnection); - connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(currentPageChanged(int))); - connect(this, SIGNAL(currentIdChanged(int)), m_core, SIGNAL(currentPageChanged(int))); - connect(button(QWizard::FinishButton), SIGNAL(clicked()), this, SIGNAL(finishButtonClicked())); - connect(button(QWizard::FinishButton), SIGNAL(clicked()), m_core, SIGNAL(finishButtonClicked())); + connect(this, &QWizard::currentIdChanged, this, &PackageManagerGui::currentPageChanged); + connect(this, &QWizard::currentIdChanged, m_core, &PackageManagerCore::currentPageChanged); + connect(button(QWizard::FinishButton), &QAbstractButton::clicked, + this, &PackageManagerGui::finishButtonClicked); + connect(button(QWizard::FinishButton), &QAbstractButton::clicked, + m_core, &PackageManagerCore::finishButtonClicked); // make sure the QUiLoader's retranslateUi is executed first, then the script - connect(this, SIGNAL(languageChanged()), m_core, SLOT(languageChanged()), Qt::QueuedConnection); - connect(this, SIGNAL(languageChanged()), this, SLOT(onLanguageChanged()), Qt::QueuedConnection); + connect(this, &PackageManagerGui::languageChanged, + m_core, &PackageManagerCore::languageChanged, Qt::QueuedConnection); + connect(this, &PackageManagerGui::languageChanged, + this, &PackageManagerGui::onLanguageChanged, Qt::QueuedConnection); connect(m_core, - SIGNAL(wizardPageInsertionRequested(QWidget*,QInstaller::PackageManagerCore::WizardPage)), - this, SLOT(wizardPageInsertionRequested(QWidget*,QInstaller::PackageManagerCore::WizardPage))); - connect(m_core, SIGNAL(wizardPageRemovalRequested(QWidget*)), this, - SLOT(wizardPageRemovalRequested(QWidget*))); - connect(m_core, - SIGNAL(wizardWidgetInsertionRequested(QWidget*,QInstaller::PackageManagerCore::WizardPage)), - this, SLOT(wizardWidgetInsertionRequested(QWidget*,QInstaller::PackageManagerCore::WizardPage))); - connect(m_core, SIGNAL(wizardWidgetRemovalRequested(QWidget*)), this, - SLOT(wizardWidgetRemovalRequested(QWidget*))); - connect(m_core, SIGNAL(wizardPageVisibilityChangeRequested(bool,int)), this, - SLOT(wizardPageVisibilityChangeRequested(bool,int)), Qt::QueuedConnection); + &PackageManagerCore::wizardPageInsertionRequested, + this, &PackageManagerGui::wizardPageInsertionRequested); + connect(m_core, &PackageManagerCore::wizardPageRemovalRequested, + this, &PackageManagerGui::wizardPageRemovalRequested); + connect(m_core, &PackageManagerCore::wizardWidgetInsertionRequested, + this, &PackageManagerGui::wizardWidgetInsertionRequested); + connect(m_core, &PackageManagerCore::wizardWidgetRemovalRequested, + this, &PackageManagerGui::wizardWidgetRemovalRequested); + connect(m_core, &PackageManagerCore::wizardPageVisibilityChangeRequested, + this, &PackageManagerGui::wizardPageVisibilityChangeRequested, Qt::QueuedConnection); - connect(m_core, - SIGNAL(setValidatorForCustomPageRequested(QInstaller::Component*,QString,QString)), this, - SLOT(setValidatorForCustomPageRequested(QInstaller::Component*,QString,QString))); + connect(m_core, &PackageManagerCore::setValidatorForCustomPageRequested, + this, &PackageManagerGui::setValidatorForCustomPageRequested); - connect(m_core, SIGNAL(setAutomatedPageSwitchEnabled(bool)), this, - SLOT(setAutomatedPageSwitchEnabled(bool))); + connect(m_core, &PackageManagerCore::setAutomatedPageSwitchEnabled, + this, &PackageManagerGui::setAutomatedPageSwitchEnabled); - connect(this, SIGNAL(customButtonClicked(int)), this, SLOT(customButtonClicked(int))); + connect(this, &QWizard::customButtonClicked, this, &PackageManagerGui::customButtonClicked); for (int i = QWizard::BackButton; i < QWizard::CustomButton1; ++i) d->m_defaultButtonText.insert(i, buttonText(QWizard::WizardButton(i))); @@ -395,6 +418,43 @@ QWizard::WizardStyle PackageManagerGui::getStyle(const QString &name) } /*! + Hides the GUI when \a silent is \c true. +*/ +void PackageManagerGui::setSilent(bool silent) +{ + d->m_silent = silent; + setVisible(!silent); +} + +/*! + Returns the current silent state. +*/ +bool PackageManagerGui::isSilent() const +{ + return d->m_silent; +} + +/*! + Updates the model of \a object (which must be a QComboBox or + QAbstractItemView) such that it contains the given \a items. +*/ +void PackageManagerGui::setTextItems(QObject *object, const QStringList &items) +{ + if (QComboBox *comboBox = qobject_cast<QComboBox*>(object)) { + comboBox->setModel(new QStringListModel(items)); + return; + } + + if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(object)) { + view->setModel(new QStringListModel(items)); + return; + } + + qDebug() << "Cannot set text items on object of type" + << object->metaObject()->className() << "."; +} + +/*! Enables automatic page switching when \a request is \c true. */ void PackageManagerGui::setAutomatedPageSwitchEnabled(bool request) @@ -445,7 +505,7 @@ void PackageManagerGui::clickButton(int wb, int delay) wb = QWizard::CancelButton; if (QAbstractButton *b = button(static_cast<QWizard::WizardButton>(wb))) - QTimer::singleShot(delay, b, SLOT(click())); + QTimer::singleShot(delay, b, &QAbstractButton::click); else qWarning() << "Button with type: " << d->buttonType(wb) << "not found!"; } @@ -738,13 +798,13 @@ void PackageManagerGui::cancelButtonClicked() question = tr("Do you want to quit the installer application?"); if (m_core->isUninstaller()) question = tr("Do you want to quit the uninstaller application?"); - if (m_core->isUpdater() || m_core->isPackageManager()) + if (m_core->isMaintainer()) question = tr("Do you want to quit the maintenance application?"); } const QMessageBox::StandardButton button = MessageBoxHandler::question(MessageBoxHandler::currentBestSuitParent(), - QLatin1String("cancelInstallation"), tr("Question"), question, + QLatin1String("cancelInstallation"), tr("%1 Question").arg(m_core->value(scTitle)), question, QMessageBox::Yes | QMessageBox::No); if (button == QMessageBox::Yes) { @@ -784,7 +844,6 @@ void PackageManagerGui::setModified(bool value) */ void PackageManagerGui::showFinishedPage() { - qDebug() << "SHOW FINISHED PAGE"; if (d->m_autoSwitchPage) next(); else @@ -1023,7 +1082,17 @@ QPixmap PackageManagerPage::watermarkPixmap() const */ QPixmap PackageManagerPage::bannerPixmap() const { - return QPixmap(m_core->value(QLatin1String("BannerPixmap"))); + QPixmap banner(m_core->value(QLatin1String("BannerPixmap"))); + + if (!banner.isNull()) { + int width; + if (m_core->settings().containsValue(QLatin1String("WizardDefaultWidth")) ) + width = m_core->settings().wizardDefaultWidth(); + else + width = size().width(); + banner = banner.scaledToWidth(width, Qt::SmoothTransformation); + } + return banner; } /*! @@ -1149,7 +1218,7 @@ int PackageManagerPage::nextId() const core->calculateComponentsToInstall(); foreach (Component* component, core->orderedComponentsToInstall()) { - if ((core->isPackageManager() || core->isUpdater()) && component->isInstalled()) + if (core->isMaintainer() && component->isInstalled()) continue; // package manager or updater, hide as long as the component is installed // The component is about to be installed and provides a license, so the page needs to @@ -1210,20 +1279,22 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core) m_packageManager->setObjectName(QLatin1String("PackageManagerRadioButton")); boxLayout->addWidget(m_packageManager); m_packageManager->setChecked(core->isPackageManager()); - connect(m_packageManager, SIGNAL(toggled(bool)), this, SLOT(setPackageManager(bool))); + connect(m_packageManager, &QAbstractButton::toggled, this, &IntroductionPage::setPackageManager); m_updateComponents = new QRadioButton(tr("Update components"), this); m_updateComponents->setObjectName(QLatin1String("UpdaterRadioButton")); boxLayout->addWidget(m_updateComponents); m_updateComponents->setChecked(core->isUpdater()); - connect(m_updateComponents, SIGNAL(toggled(bool)), this, SLOT(setUpdater(bool))); + connect(m_updateComponents, &QAbstractButton::toggled, this, &IntroductionPage::setUpdater); m_removeAllComponents = new QRadioButton(tr("Remove all components"), this); m_removeAllComponents->setObjectName(QLatin1String("UninstallerRadioButton")); boxLayout->addWidget(m_removeAllComponents); m_removeAllComponents->setChecked(core->isUninstaller()); - connect(m_removeAllComponents, SIGNAL(toggled(bool)), this, SLOT(setUninstaller(bool))); - connect(m_removeAllComponents, SIGNAL(toggled(bool)), core, SLOT(setCompleteUninstallation(bool))); + connect(m_removeAllComponents, &QAbstractButton::toggled, + this, &IntroductionPage::setUninstaller); + connect(m_removeAllComponents, &QAbstractButton::toggled, + core, &PackageManagerCore::setCompleteUninstallation); boxLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); @@ -1251,16 +1322,19 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core) core->setCompleteUninstallation(core->isUninstaller()); - connect(core, SIGNAL(metaJobProgress(int)), this, SLOT(onProgressChanged(int))); - connect(core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString))); - connect(core, SIGNAL(coreNetworkSettingsChanged()), this, SLOT(onCoreNetworkSettingsChanged())); + connect(core, &PackageManagerCore::metaJobProgress, this, &IntroductionPage::onProgressChanged); + connect(core, &PackageManagerCore::metaJobTotalProgress, this, &IntroductionPage::setTotalProgress); + connect(core, &PackageManagerCore::metaJobInfoMessage, this, &IntroductionPage::setMessage); + connect(core, &PackageManagerCore::coreNetworkSettingsChanged, + this, &IntroductionPage::onCoreNetworkSettingsChanged); m_updateComponents->setEnabled(ProductKeyCheck::instance()->hasValidKey()); #ifdef Q_OS_WIN if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) { m_taskButton = new QWinTaskbarButton(this); - connect(core, SIGNAL(metaJobProgress(int)), m_taskButton->progress(), SLOT(setValue(int))); + connect(core, &PackageManagerCore::metaJobProgress, + m_taskButton->progress(), &QWinTaskbarProgress::setValue); } else { m_taskButton = 0; } @@ -1276,7 +1350,7 @@ int IntroductionPage::nextId() const if (packageManagerCore()->isUninstaller()) return PackageManagerCore::ReadyForInstallation; - if (packageManagerCore()->isUpdater() || packageManagerCore()->isPackageManager()) + if (packageManagerCore()->isMaintainer()) return PackageManagerCore::ComponentSelection; return PackageManagerPage::nextId(); @@ -1301,8 +1375,7 @@ bool IntroductionPage::validatePage() } gui()->setSettingsButtonEnabled(false); - const bool maintenance = core->isUpdater() || core->isPackageManager(); - if (maintenance) { + if (core->isMaintainer()) { showAll(); setMaintenanceToolsEnabled(false); } else { @@ -1363,7 +1436,7 @@ bool IntroductionPage::validatePage() setComplete(true); } - if (maintenance) { + if (core->isMaintainer()) { showMaintenanceTools(); setMaintenanceToolsEnabled(true); } else { @@ -1440,11 +1513,19 @@ void IntroductionPage::setMessage(const QString &msg) */ void IntroductionPage::onProgressChanged(int progress) { - m_progressBar->setRange(0, 100); m_progressBar->setValue(progress); } /*! + Sets total \a progress value to progress bar. +*/ +void IntroductionPage::setTotalProgress(int totalProgress) +{ + if (m_progressBar) + m_progressBar->setRange(0, totalProgress); +} + +/*! Displays the error message \a error on the page. */ void IntroductionPage::setErrorMessage(const QString &error) @@ -1545,7 +1626,7 @@ void IntroductionPage::entering() m_progressBar->setValue(0); m_progressBar->setRange(0, 0); PackageManagerCore *core = packageManagerCore(); - if (core->isUninstaller() || core->isUpdater() || core->isPackageManager()) { + if (core->isUninstaller() || core->isMaintainer()) { showMaintenanceTools(); setMaintenanceToolsEnabled(true); } @@ -1635,19 +1716,19 @@ LicenseAgreementPage::LicenseAgreementPage(PackageManagerCore *core) m_licenseListWidget = new QListWidget(this); m_licenseListWidget->setObjectName(QLatin1String("LicenseListWidget")); - m_licenseListWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); - connect(m_licenseListWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), - this, SLOT(currentItemChanged(QListWidgetItem*))); + m_licenseListWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + connect(m_licenseListWidget, &QListWidget::currentItemChanged, + this, &LicenseAgreementPage::currentItemChanged); m_textBrowser = new QTextBrowser(this); m_textBrowser->setReadOnly(true); m_textBrowser->setOpenLinks(false); m_textBrowser->setOpenExternalLinks(true); m_textBrowser->setObjectName(QLatin1String("LicenseTextBrowser")); - m_textBrowser->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); - connect(m_textBrowser, SIGNAL(anchorClicked(QUrl)), this, SLOT(openLicenseUrl(QUrl))); + m_textBrowser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + connect(m_textBrowser, &QTextBrowser::anchorClicked, this, &LicenseAgreementPage::openLicenseUrl); - QHBoxLayout *licenseBoxLayout = new QHBoxLayout(); + QVBoxLayout *licenseBoxLayout = new QVBoxLayout(); licenseBoxLayout->addWidget(m_licenseListWidget); licenseBoxLayout->addWidget(m_textBrowser); @@ -1684,8 +1765,8 @@ LicenseAgreementPage::LicenseAgreementPage(PackageManagerCore *core) gridLayout->addWidget(m_rejectLabel, 1, 1); layout->addLayout(gridLayout); - connect(m_acceptRadioButton, SIGNAL(toggled(bool)), this, SIGNAL(completeChanged())); - connect(m_rejectRadioButton, SIGNAL(toggled(bool)), this, SIGNAL(completeChanged())); + connect(m_acceptRadioButton, &QAbstractButton::toggled, this, &QWizardPage::completeChanged); + connect(m_rejectRadioButton, &QAbstractButton::toggled, this, &QWizardPage::completeChanged); m_rejectRadioButton->setChecked(true); } @@ -1758,7 +1839,7 @@ void LicenseAgreementPage::updateUi() acceptButtonText = tr("I accept the licenses."); rejectButtonText = tr("I do not accept the licenses."); } - + m_licenseListWidget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); setColoredSubTitle(subTitleText); m_acceptLabel->setText(acceptButtonText); @@ -1781,6 +1862,7 @@ public: , m_allModel(m_core->defaultComponentModel()) , m_updaterModel(m_core->updaterComponentModel()) , m_currentModel(m_allModel) + , m_compressedButtonVisible(false) { m_treeView->setObjectName(QLatin1String("ComponentsTreeView")); @@ -1796,23 +1878,28 @@ public: m_descriptionLabel->setWordWrap(true); m_descriptionLabel->setObjectName(QLatin1String("ComponentDescriptionLabel")); - QVBoxLayout *vlayout = new QVBoxLayout; - vlayout->addWidget(m_descriptionLabel); + m_vlayout = new QVBoxLayout; + m_vlayout->setObjectName(QLatin1String("VerticalLayout")); + m_vlayout->addWidget(m_descriptionLabel); m_sizeLabel = new QLabel(q); m_sizeLabel->setWordWrap(true); - vlayout->addWidget(m_sizeLabel); + m_vlayout->addWidget(m_sizeLabel); m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel")); - vlayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding, +#ifdef INSTALLCOMPRESSED + allowCompressedRepositoryInstall(); +#endif + m_vlayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); - hlayout->addLayout(vlayout, 2); + hlayout->addLayout(m_vlayout, 2); QVBoxLayout *layout = new QVBoxLayout(q); layout->addLayout(hlayout, 1); m_checkDefault = new QPushButton; - connect(m_checkDefault, SIGNAL(clicked()), this, SLOT(selectDefault())); + connect(m_checkDefault, &QAbstractButton::clicked, + this, &ComponentSelectionPage::Private::selectDefault); if (m_core->isInstaller()) { m_checkDefault->setObjectName(QLatin1String("SelectDefaultComponentsButton")); m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+A", @@ -1830,7 +1917,8 @@ public: m_checkAll = new QPushButton; hlayout->addWidget(m_checkAll); - connect(m_checkAll, SIGNAL(clicked()), this, SLOT(selectAll())); + connect(m_checkAll, &QAbstractButton::clicked, + this, &ComponentSelectionPage::Private::selectAll); m_checkAll->setObjectName(QLatin1String("SelectAllComponentsButton")); m_checkAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+S", "select all components"))); @@ -1838,7 +1926,8 @@ public: m_uncheckAll = new QPushButton; hlayout->addWidget(m_uncheckAll); - connect(m_uncheckAll, SIGNAL(clicked()), this, SLOT(deselectAll())); + connect(m_uncheckAll, &QAbstractButton::clicked, + this, &ComponentSelectionPage::Private::deselectAll); m_uncheckAll->setObjectName(QLatin1String("DeselectAllComponentsButton")); m_uncheckAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+D", "deselect all components"))); @@ -1849,22 +1938,56 @@ public: layout->addLayout(hlayout); } + void allowCompressedRepositoryInstall() + { + if (m_compressedButtonVisible) { + return; + } + + connect(m_core, SIGNAL(metaJobProgress(int)), this, SLOT(onProgressChanged(int))); + connect(m_core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString))); + + m_bspLabel = new QLabel(ComponentSelectionPage::tr("To install new "\ + "compressed repository, browse the repositories from your computer"),q); + m_bspLabel->setWordWrap(true); + m_bspLabel->setObjectName(QLatin1String("CompressedButtonLabel")); + + m_vlayout->addSpacing(50); + m_vlayout->addWidget(m_bspLabel); + + m_progressBar = new QProgressBar(); + m_progressBar->setRange(0, 0); + m_progressBar->hide(); + m_vlayout->addWidget(m_progressBar); + m_progressBar->setObjectName(QLatin1String("CompressedInstallProgressBar")); + + m_installCompressButton = new QPushButton; + connect(m_installCompressButton, &QAbstractButton::clicked, + this, &ComponentSelectionPage::Private::selectCompressedPackage); + m_installCompressButton->setObjectName(QLatin1String("InstallCompressedPackageButton")); + m_installCompressButton->setText(ComponentSelectionPage::tr("&Browse QBSP files")); + m_vlayout->addWidget(m_installCompressButton); + m_compressedButtonVisible = true; + } + void updateTreeView() { m_checkDefault->setVisible(m_core->isInstaller() || m_core->isPackageManager()); if (m_treeView->selectionModel()) { - disconnect(m_treeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(currentSelectedChanged(QModelIndex))); + disconnect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &ComponentSelectionPage::Private::currentSelectedChanged); } m_currentModel = m_core->isUpdater() ? m_updaterModel : m_allModel; m_treeView->setModel(m_currentModel); m_treeView->setExpanded(m_currentModel->index(0, 0), true); - const bool installActionColumnVisible = false; + const bool installActionColumnVisible = m_core->settings().installActionColumnVisible(); if (!installActionColumnVisible) m_treeView->hideColumn(ComponentModelHelper::ActionColumn); + m_treeView->header()->setSectionResizeMode( + ComponentModelHelper::NameColumn, QHeaderView::ResizeToContents); if (m_core->isInstaller()) { m_treeView->setHeaderHidden(true); for (int i = ComponentModelHelper::InstalledVersionColumn; i < m_currentModel->columnCount(); ++i) @@ -1895,8 +2018,8 @@ public: hasChildren = m_currentModel->hasChildren(m_currentModel->index(row, 0)); m_treeView->setRootIsDecorated(hasChildren); - connect(m_treeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(currentSelectedChanged(QModelIndex))); + connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &ComponentSelectionPage::Private::currentSelectedChanged); m_treeView->setCurrentIndex(m_currentModel->index(0, 0)); } @@ -1932,6 +2055,59 @@ public slots: m_currentModel->setCheckedState(ComponentModel::AllUnchecked); } + void selectCompressedPackage() + { + QString defaultDownloadDirectory = + QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + QStringList fileNames = QFileDialog::getOpenFileNames(NULL, + ComponentSelectionPage::tr("Open File"),defaultDownloadDirectory, + QLatin1String("QBSP or 7z Files (*.qbsp *.7z)")); + + QSet<Repository> set; + foreach (QString fileName, fileNames) { + Repository repository = Repository::fromUserInput(fileName, true); + repository.setEnabled(true); + set.insert(repository); + } + if (set.count() > 0) { + m_progressBar->show(); + m_installCompressButton->hide(); + QPushButton *const b = qobject_cast<QPushButton *>(q->gui()->button(QWizard::NextButton)); + b->setEnabled(false); + m_core->settings().addTemporaryRepositories(set, false); + if (!m_core->fetchCompressedPackagesTree()) { + setMessage(m_core->error()); + } + else { + updateTreeView(); + setMessage(ComponentSelectionPage::tr("To install new "\ + "compressed repository, browse the repositories from your computer")); + } + + m_progressBar->hide(); + m_installCompressButton->show(); + b->setEnabled(true); + } + } + + /*! + Updates the value of \a progress on the progress bar. + */ + void onProgressChanged(int progress) + { + m_progressBar->setValue(progress); + } + + /*! + Displays the message \a msg on the page. + */ + void setMessage(const QString &msg) + { + QWizardPage *page = q->gui()->currentPage(); + if (m_bspLabel && page && page->objectName() == QLatin1String("ComponentSelectionPage")) + m_bspLabel->setText(msg); + } + void selectDefault() { m_currentModel->setCheckedState(ComponentModel::DefaultChecked); @@ -1969,6 +2145,11 @@ public: QPushButton *m_checkAll; QPushButton *m_uncheckAll; QPushButton *m_checkDefault; + QPushButton *m_installCompressButton; + QLabel *m_bspLabel; + QProgressBar *m_progressBar; + QVBoxLayout *m_vlayout; + bool m_compressedButtonVisible; }; @@ -2012,7 +2193,7 @@ void ComponentSelectionPage::entering() QT_TR_NOOP("Please select the components you want to update."), QT_TR_NOOP("Please select the components you want to install."), QT_TR_NOOP("Please select the components you want to uninstall."), - QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them.") + QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.") }; int index = 0; @@ -2087,6 +2268,11 @@ void ComponentSelectionPage::deselectComponent(const QString &id) d->m_currentModel->setData(idx, Qt::Unchecked, Qt::CheckStateRole); } +void ComponentSelectionPage::allowCompressedRepositoryInstall() +{ + d->allowCompressedRepositoryInstall(); +} + void ComponentSelectionPage::setModified(bool modified) { setComplete(modified); @@ -2143,19 +2329,24 @@ TargetDirectoryPage::TargetDirectoryPage(PackageManagerCore *core) QLabel *msgLabel = new QLabel(this); msgLabel->setWordWrap(true); msgLabel->setObjectName(QLatin1String("MessageLabel")); - msgLabel->setText(tr("Please specify the folder where %1 will be installed.").arg(productName())); + msgLabel->setText(tr("Please specify the directory where %1 will be installed.").arg(productName())); layout->addWidget(msgLabel); QHBoxLayout *hlayout = new QHBoxLayout; + m_textChangeTimer.setSingleShot(true); + m_textChangeTimer.setInterval(200); + connect(&m_textChangeTimer, &QTimer::timeout, this, &QWizardPage::completeChanged); + m_lineEdit = new QLineEdit(this); m_lineEdit->setObjectName(QLatin1String("TargetDirectoryLineEdit")); - connect(m_lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(completeChanged())); + connect(m_lineEdit, &QLineEdit::textChanged, + &m_textChangeTimer, static_cast<void (QTimer::*)()>(&QTimer::start)); hlayout->addWidget(m_lineEdit); QPushButton *browseButton = new QPushButton(this); browseButton->setObjectName(QLatin1String("BrowseDirectoryButton")); - connect(browseButton, SIGNAL(clicked()), this, SLOT(dirRequested())); + connect(browseButton, &QAbstractButton::clicked, this, &TargetDirectoryPage::dirRequested); browseButton->setShortcut(QKeySequence(tr("Alt+R", "browse file system to choose a file"))); browseButton->setText(tr("B&rowse...")); hlayout->addWidget(browseButton); @@ -2226,6 +2417,11 @@ void TargetDirectoryPage::initializePage() */ bool TargetDirectoryPage::validatePage() { + m_textChangeTimer.stop(); + + if (!isComplete()) + return false; + if (!isVisible()) return true; @@ -2251,14 +2447,14 @@ bool TargetDirectoryPage::validatePage() QFileInfo fi2(targetDir + QDir::separator() + fileName); if (fi2.exists()) { - return failWithError(QLatin1String("TargetDirectoryInUse"), tr("The folder you selected already " + return failWithError(QLatin1String("TargetDirectoryInUse"), tr("The directory you selected already " "exists and contains an installation. Choose a different target for installation.")); } return askQuestion(QLatin1String("OverwriteTargetDirectory"), - tr("You have selected an existing, non-empty folder for installation.\nNote that it will be " + tr("You have selected an existing, non-empty directory for installation.\nNote that it will be " "completely wiped on uninstallation of this application.\nIt is not advisable to install into " - "this folder as installation might fail.\nDo you want to continue?")); + "this directory as installation might fail.\nDo you want to continue?")); } else if (fi.isFile() || fi.isSymLink()) { return failWithError(QLatin1String("WrongTargetDirectory"), tr("You have selected an existing file " "or symlink, please choose a different target for installation.")); @@ -2313,7 +2509,7 @@ bool TargetDirectoryPage::isComplete() const QString TargetDirectoryPage::targetDirWarning() const { if (targetDir().isEmpty()) - return tr("The installation path cannot be empty, please specify a valid folder."); + return tr("The installation path cannot be empty, please specify a valid directory."); QDir target(targetDir()); if (target.isRelative()) @@ -2376,7 +2572,7 @@ QString TargetDirectoryPage::targetDirWarning() const } if (nativeTargetDir.endsWith(QLatin1Char('.'))) - return tr("The installation path must not end with '.', please specify a valid folder."); + return tr("The installation path must not end with '.', please specify a valid directory."); QString ambiguousChars = QLatin1String("[\"~<>|?*!@#$%^&:,; ]" "|(\\\\CON)(\\\\|$)|(\\\\PRN)(\\\\|$)|(\\\\AUX)(\\\\|$)|(\\\\NUL)(\\\\|$)|(\\\\COM\\d)(\\\\|$)|(\\\\LPT\\d)(\\\\|$)"); @@ -2391,8 +2587,8 @@ QString TargetDirectoryPage::targetDirWarning() const // check if there are not allowed characters in the target path QRegularExpressionMatch match = ambCharRegEx.match(nativeTargetDir); if (match.hasMatch()) { - return tr("The installation path must not contain '%1', " - "please specify a valid folder.").arg(match.captured(0)); + return tr("The installation path must not contain \"%1\", " + "please specify a valid directory.").arg(match.captured(0)); } return QString(); @@ -2437,7 +2633,7 @@ StartMenuDirectoryPage::StartMenuDirectoryPage(PackageManagerCore *core) setObjectName(QLatin1String("StartMenuDirectoryPage")); setColoredTitle(tr("Start Menu shortcuts")); setColoredSubTitle(tr("Select the Start Menu in which you would like to create the program's " - "shortcuts. You can also enter a name to create a new folder.")); + "shortcuts. You can also enter a name to create a new directory.")); m_lineEdit = new QLineEdit(this); m_lineEdit->setText(core->value(scStartMenuDir, productName())); @@ -2445,7 +2641,7 @@ StartMenuDirectoryPage::StartMenuDirectoryPage(PackageManagerCore *core) startMenuPath = core->value(QLatin1String("UserStartMenuProgramsPath")); QStringList dirs = QDir(startMenuPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot); - if (core->value(QLatin1String("AllUsers")) == scTrue) { + if (core->value(scAllUsers, scFalse) == scTrue) { startMenuPath = core->value(QLatin1String("AllUsersStartMenuProgramsPath")); dirs += QDir(startMenuPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot); } @@ -2461,8 +2657,8 @@ StartMenuDirectoryPage::StartMenuDirectoryPage(PackageManagerCore *core) setLayout(layout); - connect(m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, - SLOT(currentItemChanged(QListWidgetItem*))); + connect(m_listWidget, &QListWidget::currentItemChanged, this, + &StartMenuDirectoryPage::currentItemChanged); } /*! @@ -2568,7 +2764,7 @@ void ReadyForInstallationPage::entering() .absolutePath()))); setComplete(true); return; - } else if (packageManagerCore()->isPackageManager() || packageManagerCore()->isUpdater()) { + } else if (packageManagerCore()->isMaintainer()) { setButtonText(QWizard::CommitButton, tr("U&pdate")); setColoredTitle(tr("Ready to Update Packages")); m_msgLabel->setText(tr("Setup is now ready to begin updating your installation.")); @@ -2581,7 +2777,7 @@ void ReadyForInstallationPage::entering() } QString htmlOutput; - bool componentsOk = calculateComponents(&htmlOutput); + bool componentsOk = packageManagerCore()->calculateComponents(&htmlOutput); m_taskDetailsBrowser->setHtml(htmlOutput); m_taskDetailsBrowser->setVisible(!componentsOk || isVerbose()); setComplete(componentsOk); @@ -2594,19 +2790,19 @@ void ReadyForInstallationPage::entering() // at the moment there is no better way to check this if (targetVolume.size() == 0 && installVolumeAvailableSize == 0) { - qDebug() << QString::fromLatin1("Could not determine available space on device. Volume " - "descriptor: %1, Mount path: %2. Continue silently.").arg(targetVolume - .volumeDescriptor(), targetVolume.mountPath()); + qDebug().nospace() << "Cannot determine available space on device. " + "Volume descriptor: " << targetVolume.volumeDescriptor() + << ", Mount path: " << targetVolume.mountPath() << ". Continue silently."; return; // TODO: Shouldn't this also disable the "Next" button? } const bool tempOnSameVolume = (targetVolume == tempVolume); if (tempOnSameVolume) { - qDebug() << "Tmp and install folder are on the same volume. Volume mount point:" + qDebug() << "Tmp and install directories are on the same volume. Volume mount point:" << targetVolume.mountPath() << "Free space available:" << humanReadableSize(installVolumeAvailableSize); } else { - qDebug() << "Tmp is on a different volume than the install folder. Tmp volume mount point:" + qDebug() << "Tmp is on a different volume than the installation directory. Tmp volume mount point:" << tempVolume.mountPath() << "Free space available:" << humanReadableSize(tempVolumeAvailableSize) << "Install volume mount point:" << targetVolume.mountPath() << "Free space available:" @@ -2638,7 +2834,7 @@ void ReadyForInstallationPage::entering() if (tempOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) { m_msgLabel->setText(tr("Not enough disk space to store temporary files and the " - "installation! Available space: %1, at least required %2.") + "installation. %1 are available, while %2 are at least required.") .arg(humanReadableSize(installVolumeAvailableSize), humanReadableSize(required + tempRequired))); setComplete(false); @@ -2646,16 +2842,16 @@ void ReadyForInstallationPage::entering() } if (installVolumeAvailableSize < required) { - m_msgLabel->setText(tr("Not enough disk space to store all selected components! Available " - "space: %1, at least required: %2.").arg(humanReadableSize(installVolumeAvailableSize), + m_msgLabel->setText(tr("Not enough disk space to store all selected components! %1 are available " + "while %2 are at least required.").arg(humanReadableSize(installVolumeAvailableSize), humanReadableSize(required))); setComplete(false); return; } if (tempVolumeAvailableSize < tempRequired) { - m_msgLabel->setText(tr("Not enough disk space to store temporary files! Available space: " - "%1, at least required: %2.").arg(humanReadableSize(tempVolumeAvailableSize), + m_msgLabel->setText(tr("Not enough disk space to store temporary files! %1 are available " + "while %2 are at least required.").arg(humanReadableSize(tempVolumeAvailableSize), humanReadableSize(tempRequired))); setComplete(false); return; @@ -2678,51 +2874,7 @@ void ReadyForInstallationPage::entering() .arg(humanReadableSize(packageManagerCore()->requiredDiskSpace())))); } -bool ReadyForInstallationPage::calculateComponents(QString *displayString) -{ - QString htmlOutput; - QString lastInstallReason; - if (!packageManagerCore()->calculateComponentsToUninstall() || - !packageManagerCore()->calculateComponentsToInstall()) { - htmlOutput.append(QString::fromLatin1("<h2><font color=\"red\">%1</font></h2><ul>") - .arg(tr("Cannot resolve all dependencies."))); - //if we have a missing dependency or a recursion we can display it - if (!packageManagerCore()->componentsToInstallError().isEmpty()) { - htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg( - packageManagerCore()->componentsToInstallError())); - } - htmlOutput.append(QLatin1String("</ul>")); - if (displayString) - *displayString = htmlOutput; - return false; - } - // In case of updater mode we don't uninstall components. - if (!packageManagerCore()->isUpdater()) { - QList<Component*> componentsToRemove = packageManagerCore()->componentsToUninstall(); - if (!componentsToRemove.isEmpty()) { - htmlOutput.append(QString::fromLatin1("<h3>%1</h3><ul>").arg(tr("Components about to " - "be removed."))); - foreach (Component *component, componentsToRemove) - htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(component->name())); - htmlOutput.append(QLatin1String("</ul>")); - } - } - - foreach (Component *component, packageManagerCore()->orderedComponentsToInstall()) { - const QString installReason = packageManagerCore()->installReason(component); - if (lastInstallReason != installReason) { - if (!lastInstallReason.isEmpty()) // means we had to close the previous list - htmlOutput.append(QLatin1String("</ul>")); - htmlOutput.append(QString::fromLatin1("<h3>%1</h3><ul>").arg(installReason)); - lastInstallReason = installReason; - } - htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(component->name())); - } - if (displayString) - *displayString = htmlOutput; - return true; -} /*! Called when end users leave the page and the PackageManagerGui:currentPageChanged() @@ -2769,22 +2921,27 @@ PerformInstallationPage::PerformInstallationPage(PackageManagerCore *core) m_performInstallationForm->setupUi(this); - connect(ProgressCoordinator::instance(), SIGNAL(detailTextChanged(QString)), - m_performInstallationForm, SLOT(appendProgressDetails(QString))); - connect(ProgressCoordinator::instance(), SIGNAL(detailTextResetNeeded()), - m_performInstallationForm, SLOT(clearDetailsBrowser())); - connect(m_performInstallationForm, SIGNAL(showDetailsChanged()), this, - SLOT(toggleDetailsWereChanged())); + connect(ProgressCoordinator::instance(), &ProgressCoordinator::detailTextChanged, + m_performInstallationForm, &PerformInstallationForm::appendProgressDetails); + connect(ProgressCoordinator::instance(), &ProgressCoordinator::detailTextResetNeeded, + m_performInstallationForm, &PerformInstallationForm::clearDetailsBrowser); + connect(m_performInstallationForm, &PerformInstallationForm::showDetailsChanged, + this, &PerformInstallationPage::toggleDetailsWereChanged); - connect(core, SIGNAL(installationStarted()), this, SLOT(installationStarted())); - connect(core, SIGNAL(installationFinished()), this, SLOT(installationFinished())); + connect(core, &PackageManagerCore::installationStarted, + this, &PerformInstallationPage::installationStarted); + connect(core, &PackageManagerCore::installationFinished, + this, &PerformInstallationPage::installationFinished); - connect(core, SIGNAL(uninstallationStarted()), this, SLOT(uninstallationStarted())); - connect(core, SIGNAL(uninstallationFinished()), this, SLOT(uninstallationFinished())); + connect(core, &PackageManagerCore::uninstallationStarted, + this, &PerformInstallationPage::uninstallationStarted); + connect(core, &PackageManagerCore::uninstallationFinished, + this, &PerformInstallationPage::uninstallationFinished); - connect(core, SIGNAL(titleMessageChanged(QString)), this, SLOT(setTitleMessage(QString))); - connect(this, SIGNAL(setAutomatedPageSwitchEnabled(bool)), core, - SIGNAL(setAutomatedPageSwitchEnabled(bool))); + connect(core, &PackageManagerCore::titleMessageChanged, + this, &PerformInstallationPage::setTitleMessage); + connect(this, &PerformInstallationPage::setAutomatedPageSwitchEnabled, + core, &PackageManagerCore::setAutomatedPageSwitchEnabled); m_performInstallationForm->setDetailsWidgetVisible(true); @@ -2823,7 +2980,7 @@ void PerformInstallationPage::entering() setColoredTitle(tr("Uninstalling %1").arg(productName())); QTimer::singleShot(30, packageManagerCore(), SLOT(runUninstaller())); - } else if (packageManagerCore()->isPackageManager() || packageManagerCore()->isUpdater()) { + } else if (packageManagerCore()->isMaintainer()) { setButtonText(QWizard::CommitButton, tr("&Update")); setColoredTitle(tr("Updating components of %1").arg(productName())); @@ -2924,12 +3081,6 @@ FinishedPage::FinishedPage(PackageManagerCore *core) m_msgLabel->setWordWrap(true); m_msgLabel->setObjectName(QLatin1String("MessageLabel")); -#ifdef Q_OS_OSX - m_msgLabel->setText(tr("Click Done to exit the %1 Wizard.").arg(productName())); -#else - m_msgLabel->setText(tr("Click Finish to exit the %1 Wizard.").arg(productName())); -#endif - m_runItCheckBox = new QCheckBox(this); m_runItCheckBox->setObjectName(QLatin1String("RunItCheckBox")); m_runItCheckBox->setChecked(true); @@ -2948,12 +3099,16 @@ FinishedPage::FinishedPage(PackageManagerCore *core) */ void FinishedPage::entering() { + m_msgLabel->setText(tr("Click %1 to exit the %2 Wizard.") + .arg(gui()->defaultButtonText(QWizard::FinishButton).remove(QLatin1Char('&'))) + .arg(productName())); + if (m_commitButton) { - disconnect(m_commitButton, SIGNAL(clicked()), this, SLOT(handleFinishClicked())); + disconnect(m_commitButton, &QAbstractButton::clicked, this, &FinishedPage::handleFinishClicked); m_commitButton = 0; } - if (packageManagerCore()->isUpdater() || packageManagerCore()->isPackageManager()) { + if (packageManagerCore()->isMaintainer()) { #ifdef Q_OS_OSX gui()->setOption(QWizard::NoCancelButton, false); #endif @@ -2962,13 +3117,13 @@ void FinishedPage::entering() cancel->setEnabled(true); cancel->setVisible(true); // we don't use the usual FinishButton so we need to connect the misused CancelButton - connect(cancel, SIGNAL(clicked()), gui(), SIGNAL(finishButtonClicked())); - connect(cancel, SIGNAL(clicked()), packageManagerCore(), SIGNAL(finishButtonClicked())); + connect(cancel, &QAbstractButton::clicked, gui(), &PackageManagerGui::finishButtonClicked); + connect(cancel, &QAbstractButton::clicked, packageManagerCore(), &PackageManagerCore::finishButtonClicked); // for the moment we don't want the rejected signal connected - disconnect(gui(), SIGNAL(rejected()), packageManagerCore(), SLOT(setCanceled())); + disconnect(gui(), &QDialog::rejected, packageManagerCore(), &PackageManagerCore::setCanceled); - connect(gui()->button(QWizard::CommitButton), SIGNAL(clicked()), this, - SLOT(cleanupChangedConnects())); + connect(gui()->button(QWizard::CommitButton), &QAbstractButton::clicked, + this, &FinishedPage::cleanupChangedConnects); } setButtonText(QWizard::CommitButton, tr("Restart")); setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::FinishButton)); @@ -2987,8 +3142,8 @@ void FinishedPage::entering() gui()->updateButtonLayout(); if (m_commitButton) { - disconnect(m_commitButton, SIGNAL(clicked()), this, SLOT(handleFinishClicked())); - connect(m_commitButton, SIGNAL(clicked()), this, SLOT(handleFinishClicked())); + disconnect(m_commitButton, &QAbstractButton::clicked, this, &FinishedPage::handleFinishClicked); + connect(m_commitButton, &QAbstractButton::clicked, this, &FinishedPage::handleFinishClicked); } if (packageManagerCore()->status() == PackageManagerCore::Success) { @@ -3055,12 +3210,12 @@ void FinishedPage::cleanupChangedConnects() { if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton)) { // remove the workaround connect from entering page - disconnect(cancel, SIGNAL(clicked()), gui(), SIGNAL(finishButtonClicked())); - disconnect(cancel, SIGNAL(clicked()), packageManagerCore(), SIGNAL(finishButtonClicked())); - connect(gui(), SIGNAL(rejected()), packageManagerCore(), SLOT(setCanceled())); + disconnect(cancel, &QAbstractButton::clicked, gui(), &PackageManagerGui::finishButtonClicked); + disconnect(cancel, &QAbstractButton::clicked, packageManagerCore(), &PackageManagerCore::finishButtonClicked); + connect(gui(), &QDialog::rejected, packageManagerCore(), &PackageManagerCore::setCanceled); - disconnect(gui()->button(QWizard::CommitButton), SIGNAL(clicked()), this, - SLOT(cleanupChangedConnects())); + disconnect(gui()->button(QWizard::CommitButton), &QAbstractButton::clicked, + this, &FinishedPage::cleanupChangedConnects); } } diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h index 94dd0a9d4..238e22a62 100644 --- a/src/libs/installer/packagemanagergui.h +++ b/src/libs/installer/packagemanagergui.h @@ -33,6 +33,7 @@ #include <QtCore/QEvent> #include <QtCore/QMetaType> +#include <QtCore/QTimer> #include <QWizard> #include <QWizardPage> @@ -87,6 +88,11 @@ public: void updateButtonLayout(); static QWizard::WizardStyle getStyle(const QString &name); + void setSilent(bool silent); + bool isSilent() const; + + void setTextItems(QObject *object, const QStringList &items); + Q_SIGNALS: void interrupted(); void languageChanged(); @@ -218,6 +224,7 @@ public Q_SLOTS: void onCoreNetworkSettingsChanged(); void setMessage(const QString &msg); void onProgressChanged(int progress); + void setTotalProgress(int totalProgress); void setErrorMessage(const QString &error); Q_SIGNALS: @@ -303,6 +310,7 @@ public: Q_INVOKABLE void selectDefault(); Q_INVOKABLE void selectComponent(const QString &id); Q_INVOKABLE void deselectComponent(const QString &id); + Q_INVOKABLE void allowCompressedRepositoryInstall(); protected: void entering(); @@ -347,6 +355,7 @@ private: private: QLineEdit *m_lineEdit; QLabel *m_warningLabel; + QTimer m_textChangeTimer; }; @@ -389,9 +398,6 @@ protected: void leaving(); private: - bool calculateComponents(QString *displayString); - -private: QLabel *m_msgLabel; QTextBrowser* m_taskDetailsBrowser; }; diff --git a/src/libs/installer/packagemanagerpagefactory.cpp b/src/libs/installer/packagemanagerpagefactory.cpp index f891bfbb3..808dec263 100644 --- a/src/libs/installer/packagemanagerpagefactory.cpp +++ b/src/libs/installer/packagemanagerpagefactory.cpp @@ -36,9 +36,4 @@ PackageManagerPageFactory &PackageManagerPageFactory::instance() return factory; } -PackageManagerPage *PackageManagerPageFactory::create(int id, PackageManagerCore *core) const -{ - return KDGenericFactory<PackageManagerPage, int, PackageManagerCore*>::createWithArg(id, core); -} - } diff --git a/src/libs/installer/packagemanagerpagefactory.h b/src/libs/installer/packagemanagerpagefactory.h index 87a08fd34..4cb7cf5d4 100644 --- a/src/libs/installer/packagemanagerpagefactory.h +++ b/src/libs/installer/packagemanagerpagefactory.h @@ -29,16 +29,16 @@ #ifndef PACKAGEMANAGERPAGEFACTORY_H #define PACKAGEMANAGERPAGEFACTORY_H -#include <kdgenericfactory.h> -#include <packagemanagergui.h> +#include "genericfactory.h" +#include "qinstallerglobal.h" namespace QInstaller { class PackageManagerCore; class PackageManagerPage; -class INSTALLER_EXPORT PackageManagerPageFactory : public KDGenericFactory<PackageManagerPage, - int, QInstaller::PackageManagerCore*> +class INSTALLER_EXPORT PackageManagerPageFactory : public GenericFactory<PackageManagerPage, int, + PackageManagerCore*> { Q_DISABLE_COPY(PackageManagerPageFactory) @@ -46,12 +46,11 @@ public: static PackageManagerPageFactory &instance(); template<typename T> void registerPackageManagerPage(int id) { - registerProductWithArg<T>(id); + registerProduct<T>(id); } - PackageManagerPage *create(int id, QInstaller::PackageManagerCore *core) const; private: - PackageManagerPageFactory() {} + PackageManagerPageFactory() = default; }; } // namespace QInstaller diff --git a/src/libs/installer/packagemanagerproxyfactory.cpp b/src/libs/installer/packagemanagerproxyfactory.cpp index 734e1ba34..98aef7d9a 100644 --- a/src/libs/installer/packagemanagerproxyfactory.cpp +++ b/src/libs/installer/packagemanagerproxyfactory.cpp @@ -115,7 +115,7 @@ void PackageManagerProxyFactory::setProxyCredentials(const QNetworkProxy &proxy, auto p = std::find_if(m_proxyCredentials.begin(), m_proxyCredentials.end(), FindProxyCredential(proxy.hostName(), proxy.port())); - if (p == m_proxyCredentials.constEnd()) { + if (p == m_proxyCredentials.end()) { ProxyCredential proxyCredential; proxyCredential.host = proxy.hostName(); proxyCredential.port = proxy.port(); diff --git a/src/libs/installer/packagemanagerproxyfactory.h b/src/libs/installer/packagemanagerproxyfactory.h index f2d64ae96..31f1ffeba 100644 --- a/src/libs/installer/packagemanagerproxyfactory.h +++ b/src/libs/installer/packagemanagerproxyfactory.h @@ -29,7 +29,7 @@ #ifndef PACKAGEMANAGERPROXYFACTORY_H #define PACKAGEMANAGERPROXYFACTORY_H -#include "kdupdaterfiledownloaderfactory.h" +#include "filedownloaderfactory.h" namespace QInstaller { diff --git a/src/libs/installer/packagesource.cpp b/src/libs/installer/packagesource.cpp new file mode 100644 index 000000000..f60c5c8e1 --- /dev/null +++ b/src/libs/installer/packagesource.cpp @@ -0,0 +1,91 @@ +/************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "packagesource.h" + +namespace QInstaller { + +/*! + \inmodule QtInstallerFramework + \class QInstaller::PackageSource + \brief The PackageSource class specifies a single package source. + + An package source represents a link to an repository that contains packages applicable by + the installer or package maintenance application. This structure describes a single package + source in terms of url and priority. While different repositories can host the same packages, + packages coming from a higher priority source take precedence over lower priority packages + during applicable package computation. +*/ + +/*! + \fn PackageSource::PackageSource() + + Constructs an empty package source info object. The object's priority is set to -1. The url is + initialized using a \l{default-constructed value}. +*/ + +/*! + \fn PackageSource::PackageSource(const QUrl &u, int p) + + Constructs an package source info object. The object's url is set to \a u, while the priority + is set to \a p. +*/ + +/*! + \variable PackageSource::url + \brief The URL of the package source. +*/ + +/*! + \variable PackageSource::priority + \brief The priority of the package source. +*/ + +/*! + Returns the hash value for the \a key, using \a seed to seed the calculation. +*/ +uint qHash(const PackageSource &key, uint seed) +{ + return qHash(key.url, seed) ^ key.priority; +} + +/*! + Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false. +*/ +bool operator==(const PackageSource &lhs, const PackageSource &rhs) +{ + return lhs.url == rhs.url && lhs.priority == rhs.priority; +} + +} // namespace QInstaller diff --git a/src/libs/installer/packagesource.h b/src/libs/installer/packagesource.h new file mode 100644 index 000000000..6bc565c15 --- /dev/null +++ b/src/libs/installer/packagesource.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef PACKAGESOURCE_H +#define PACKAGESOURCE_H + +#include "installer_global.h" + +#include <QUrl> + +namespace QInstaller { + +struct INSTALLER_EXPORT PackageSource +{ + PackageSource() + : priority(-1) + {} + PackageSource(const QUrl &u, int p) + : url(u) + , priority(p) + {} + + QUrl url; + int priority; +}; + +INSTALLER_EXPORT uint qHash(const PackageSource &key, uint seed); +INSTALLER_EXPORT bool operator==(const PackageSource &lhs, const PackageSource &rhs); + +} // namespace QInstaller + +#endif // PACKAGESOURCE_H diff --git a/src/libs/installer/performinstallationform.cpp b/src/libs/installer/performinstallationform.cpp index bf5a74985..b66cbb5bd 100644 --- a/src/libs/installer/performinstallationform.cpp +++ b/src/libs/installer/performinstallationform.cpp @@ -117,13 +117,13 @@ void PerformInstallationForm::setupUi(QWidget *widget) m_downloadStatus->setObjectName(QLatin1String("DownloadStatus")); m_downloadStatus->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); topLayout->addWidget(m_downloadStatus); - connect(ProgressCoordinator::instance(), SIGNAL(downloadStatusChanged(QString)), this, - SLOT(onDownloadStatusChanged(QString))); + connect(ProgressCoordinator::instance(), &ProgressCoordinator::downloadStatusChanged, this, + &PerformInstallationForm::onDownloadStatusChanged); m_detailsButton = new QPushButton(tr("&Show Details"), widget); m_detailsButton->setObjectName(QLatin1String("DetailsButton")); m_detailsButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); - connect(m_detailsButton, SIGNAL(clicked()), this, SLOT(toggleDetails())); + connect(m_detailsButton, &QAbstractButton::clicked, this, &PerformInstallationForm::toggleDetails); topLayout->addWidget(m_detailsButton); QVBoxLayout *bottomLayout = new QVBoxLayout(); @@ -142,7 +142,8 @@ void PerformInstallationForm::setupUi(QWidget *widget) baseLayout->addLayout(bottomLayout); m_updateTimer = new QTimer(widget); - connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(updateProgress())); //updateProgress includes label + connect(m_updateTimer, &QTimer::timeout, + this, &PerformInstallationForm::updateProgress); //updateProgress includes label m_updateTimer->setInterval(30); m_progressBar->setRange(0, 100); @@ -176,7 +177,7 @@ void PerformInstallationForm::updateProgress() m_progressBar->setValue(progressPercentage); #ifdef Q_OS_WIN if (m_taskButton) { - if (!m_taskButton->window()) + if (!m_taskButton->window() && QApplication::activeWindow()) m_taskButton->setWindow(QApplication::activeWindow()->windowHandle()); m_taskButton->progress()->setValue(progressPercentage); } diff --git a/src/libs/installer/productkeycheck.cpp b/src/libs/installer/productkeycheck.cpp index 4a8ce4677..c1dfe83d6 100644 --- a/src/libs/installer/productkeycheck.cpp +++ b/src/libs/installer/productkeycheck.cpp @@ -101,3 +101,8 @@ QList<int> ProductKeyCheck::registeredPages() const { return QList<int>(); } + +bool ProductKeyCheck::hasValidLicense() const +{ + return true; +} diff --git a/src/libs/installer/productkeycheck.h b/src/libs/installer/productkeycheck.h index 221fdc7ef..b7e8c6d52 100644 --- a/src/libs/installer/productkeycheck.h +++ b/src/libs/installer/productkeycheck.h @@ -66,6 +66,7 @@ public: bool isValidPackage(const QString &packageName) const; QList<int> registeredPages() const; + bool hasValidLicense() const; private: ProductKeyCheck(); diff --git a/src/libs/installer/progresscoordinator.cpp b/src/libs/installer/progresscoordinator.cpp index f94142de6..1a3f4e33f 100644 --- a/src/libs/installer/progresscoordinator.cpp +++ b/src/libs/installer/progresscoordinator.cpp @@ -101,7 +101,7 @@ void ProgressCoordinator::registerPartProgress(QObject *sender, const char *sign void ProgressCoordinator::partProgressChanged(double fraction) { if (fraction < 0 || fraction > 1) { - qWarning() << "The fraction is outside from possible value:" << QString::number(fraction); + qWarning() << "The fraction is outside from possible value:" << fraction; return; } diff --git a/src/libs/installer/proxycredentialsdialog.h b/src/libs/installer/proxycredentialsdialog.h index eaac3cacf..99dfd942c 100644 --- a/src/libs/installer/proxycredentialsdialog.h +++ b/src/libs/installer/proxycredentialsdialog.h @@ -30,9 +30,7 @@ #include <QDialog> -QT_BEGIN_NAMESPACE -class QNetworkProxy; -QT_END_NAMESPACE +QT_FORWARD_DECLARE_CLASS(QNetworkProxy) namespace QInstaller { diff --git a/src/libs/installer/qinstallerglobal.h b/src/libs/installer/qinstallerglobal.h index 2dc1f92b3..884044db9 100644 --- a/src/libs/installer/qinstallerglobal.h +++ b/src/libs/installer/qinstallerglobal.h @@ -31,9 +31,9 @@ #include <installer_global.h> -#include <kdupdaterupdate.h> -#include <kdupdaterupdateoperation.h> -#include <kdupdaterpackagesinfo.h> +#include "update.h" +#include "updateoperation.h" +#include "localpackagehub.h" namespace QInstaller { @@ -55,8 +55,7 @@ typedef QList<QInstaller::Operation*> OperationList; typedef KDUpdater::Update Package; typedef QList<QInstaller::Package*> PackagesList; -typedef KDUpdater::PackageInfo LocalPackage; -typedef QHash<QString, LocalPackage> LocalPackagesHash; +typedef QHash<QString, KDUpdater::LocalPackage> LocalPackagesHash; } // namespace QInstaller diff --git a/src/libs/installer/qprocesswrapper.cpp b/src/libs/installer/qprocesswrapper.cpp index 9396a3b56..1bf60ead2 100644 --- a/src/libs/installer/qprocesswrapper.cpp +++ b/src/libs/installer/qprocesswrapper.cpp @@ -43,20 +43,17 @@ QProcessWrapper::QProcessWrapper(QObject *parent) qRegisterMetaType<QProcess::ProcessState>(); m_timer.start(250); - connect(&m_timer, SIGNAL(timeout()), this, SLOT(processSignals())); - connect(&process, SIGNAL(bytesWritten(qint64)), SIGNAL(bytesWritten(qint64))); - connect(&process, SIGNAL(aboutToClose()), SIGNAL(aboutToClose())); - connect(&process, SIGNAL(readChannelFinished()), SIGNAL(readChannelFinished())); + connect(&m_timer, &QTimer::timeout, this, &QProcessWrapper::processSignals); + connect(&process, &QIODevice::bytesWritten, this, &QProcessWrapper::bytesWritten); + connect(&process, &QIODevice::aboutToClose, this, &QProcessWrapper::aboutToClose); + connect(&process, &QIODevice::readChannelFinished, this, &QProcessWrapper::readChannelFinished); connect(&process, SIGNAL(error(QProcess::ProcessError)), SIGNAL(error(QProcess::ProcessError))); - connect(&process, SIGNAL(readyReadStandardOutput()), SIGNAL(readyReadStandardOutput())); - connect(&process, SIGNAL(readyReadStandardError()), SIGNAL(readyReadStandardError())); - connect(&process, SIGNAL(finished(int)), SIGNAL(finished(int))); - connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), - SIGNAL(finished(int,QProcess::ExitStatus))); - connect(&process, SIGNAL(readyRead()), SIGNAL(readyRead())); - connect(&process, SIGNAL(started()), SIGNAL(started())); - connect(&process, SIGNAL(stateChanged(QProcess::ProcessState)), - SIGNAL(stateChanged(QProcess::ProcessState))); + connect(&process, &QProcess::readyReadStandardOutput, this, &QProcessWrapper::readyReadStandardOutput); + connect(&process, &QProcess::readyReadStandardError, this, &QProcessWrapper::readyReadStandardError); + connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), SIGNAL(finished(int,QProcess::ExitStatus))); + connect(&process, &QIODevice::readyRead, this, &QProcessWrapper::readyRead); + connect(&process, &QProcess::started, this, &QProcessWrapper::started); + connect(&process, &QProcess::stateChanged, this, &QProcessWrapper::stateChanged); } QProcessWrapper::~QProcessWrapper() @@ -97,7 +94,6 @@ void QProcessWrapper::processSignals() emit stateChanged(static_cast<QProcess::ProcessState> (receivedSignals.takeFirst() .toInt())); } else if (name == QLatin1String(Protocol::QProcessSignalFinished)) { - emit finished(receivedSignals.first().toInt()); emit finished(receivedSignals.takeFirst().toInt(), static_cast<QProcess::ExitStatus> (receivedSignals.takeFirst().toInt())); } diff --git a/src/libs/installer/qprocesswrapper.h b/src/libs/installer/qprocesswrapper.h index 95c7fadb7..e76217eda 100644 --- a/src/libs/installer/qprocesswrapper.h +++ b/src/libs/installer/qprocesswrapper.h @@ -117,7 +117,6 @@ Q_SIGNALS: void error(QProcess::ProcessError); void readyReadStandardOutput(); void readyReadStandardError(); - void finished(int exitCode); void finished(int exitCode, QProcess::ExitStatus exitStatus); void readyRead(); void started(); diff --git a/src/libs/installer/qtpatch.cpp b/src/libs/installer/qtpatch.cpp index 6bd8ab853..23ac372fd 100644 --- a/src/libs/installer/qtpatch.cpp +++ b/src/libs/installer/qtpatch.cpp @@ -85,8 +85,8 @@ QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteA if (process.exitStatus() == QProcess::CrashExit) { qWarning() << qmake.absoluteFilePath() << args << "crashed with exit code" << process.exitCode() - << "standard output: " << output - << "error output: " << process.readAllStandardError(); + << "standard output:" << output + << "error output:" << process.readAllStandardError(); return qmakeValueHash; } qmakeValueHash = readQmakeOutput(output); @@ -120,7 +120,7 @@ bool QtPatch::patchBinaryFile(const QString &fileName, openFileForPatching(&file); if (!file.isOpen()) { qDebug() << "qpatch: warning: file" << qPrintable(fileName) << "cannot open."; - qDebug() << qPrintable(file.errorString()); + qDebug().noquote() << file.errorString(); return false; } @@ -168,8 +168,7 @@ bool QtPatch::patchTextFile(const QString &fileName, QFile file(fileName); if (!file.open(QFile::ReadOnly)) { - qDebug() << QString::fromLatin1("qpatch: warning: Open the file '%1' stopped: %2").arg( - fileName, file.errorString()); + qDebug() << "Cannot open file" << fileName << "for patching:" << file.errorString(); return false; } @@ -183,7 +182,7 @@ bool QtPatch::patchTextFile(const QString &fileName, } if (!file.open(QFile::WriteOnly | QFile::Truncate)) { - qDebug() << QString::fromLatin1("qpatch: error: file '%1' not writable").arg(fileName); + qDebug() << "File" << fileName << "not writable."; return false; } @@ -203,7 +202,6 @@ bool QtPatch::openFileForPatching(QFile *file) } return file->openMode() == QFile::ReadWrite; } - qDebug() << QString::fromLatin1("qpatch: error: File '%1 is open, so it cannot be opened again.").arg( - file->fileName()); + qDebug() << "File" << file->fileName() << "is open, so it cannot be opened again."; return false; } diff --git a/src/libs/installer/registerfiletypeoperation.cpp b/src/libs/installer/registerfiletypeoperation.cpp index a1f55a4b5..2cfd8f9c4 100644 --- a/src/libs/installer/registerfiletypeoperation.cpp +++ b/src/libs/installer/registerfiletypeoperation.cpp @@ -28,6 +28,7 @@ #include "registerfiletypeoperation.h" +#include "constants.h" #include "packagemanagercore.h" #include "qsettingswrapper.h" @@ -73,7 +74,9 @@ static QVariantHash readHive(QSettingsWrapper *const settings, const QString &hi // -- RegisterFileTypeOperation -RegisterFileTypeOperation::RegisterFileTypeOperation() +RegisterFileTypeOperation::RegisterFileTypeOperation(PackageManagerCore *core) + : UpdateOperation(core) + , m_optionalArgumentsRead(false) { setName(QLatin1String("RegisterFileType")); } @@ -85,37 +88,29 @@ void RegisterFileTypeOperation::backup() bool RegisterFileTypeOperation::performOperation() { #ifdef Q_OS_WIN - QStringList args = arguments(); - QString progId = takeProgIdArgument(args); - - if (args.count() < 2 || args.count() > 5) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("2 to 5"), QLatin1String(""))); + ensureOptionalArgumentsRead(); + if (!checkArgumentCount(2, 5, tr("<extension> <command> [description [contentType [icon]]]"))) return false; - } + QStringList args = arguments(); bool allUsers = false; - PackageManagerCore *const core = value(QLatin1String("installer")).value<PackageManagerCore*>(); - if (core && core->value(QLatin1String("AllUsers")) == QLatin1String("true")) + PackageManagerCore *const core = packageManager(); + if (core && core->value(scAllUsers) == scTrue) allUsers = true; QSettingsWrapper settings(QLatin1String(allUsers ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER") , QSettingsWrapper::NativeFormat); - const QString extension = args.at(0); - if (progId.isEmpty()) - progId = QString::fromLatin1("%1_auto_file").arg(extension); - const QString classesProgId = QString::fromLatin1("Software/Classes/") + progId; - const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(extension); - const QString classesApplications = QString::fromLatin1("Software/Classes/Applications/") + progId; + const QString classesProgId = QString::fromLatin1("Software/Classes/") + m_progId; + const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(args.at(0)); + const QString classesApplications = QString::fromLatin1("Software/Classes/Applications/") + m_progId; // backup old value setValue(QLatin1String("oldType"), readHive(&settings, classesFileType)); // register new values - settings.setValue(QString::fromLatin1("%1/Default").arg(classesFileType), progId); - settings.setValue(QString::fromLatin1("%1/OpenWithProgIds/%2").arg(classesFileType, progId), QString()); + settings.setValue(QString::fromLatin1("%1/Default").arg(classesFileType), m_progId); + settings.setValue(QString::fromLatin1("%1/OpenWithProgIds/%2").arg(classesFileType, m_progId), QString()); settings.setValue(QString::fromLatin1("%1/shell/Open/Command/Default").arg(classesProgId), args.at(1)); settings.setValue(QString::fromLatin1("%1/shell/Open/Command/Default").arg(classesApplications), args.at(1)); @@ -151,28 +146,23 @@ bool RegisterFileTypeOperation::performOperation() bool RegisterFileTypeOperation::undoOperation() { #ifdef Q_OS_WIN + ensureOptionalArgumentsRead(); QStringList args = arguments(); - QString progId = takeProgIdArgument(args); - if (args.count() < 2 || args.count() > 5) { - setErrorString(tr("Register File Type: Invalid arguments")); + if (!checkArgumentCount(2, 5, tr("Register File Type: Invalid arguments"))) return false; - } bool allUsers = false; - PackageManagerCore *const core = value(QLatin1String("installer")).value<PackageManagerCore*>(); - if (core && core->value(QLatin1String("AllUsers")) == QLatin1String("true")) + PackageManagerCore *const core = packageManager(); + if (core && core->value(scAllUsers) == scTrue) allUsers = true; QSettingsWrapper settings(QLatin1String(allUsers ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER") , QSettingsWrapper::NativeFormat); - const QString extension = args.at(0); - if (progId.isEmpty()) - progId = QString::fromLatin1("%1_auto_file").arg(extension); - const QString classesProgId = QString::fromLatin1("Software/Classes/") + progId; - const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(extension); - const QString classesApplications = QString::fromLatin1("Software/Classes/Applications/") + progId; + const QString classesProgId = QString::fromLatin1("Software/Classes/") + m_progId; + const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(args.at(0)); + const QString classesApplications = QString::fromLatin1("Software/Classes/Applications/") + m_progId; // Quoting MSDN here: When uninstalling an application, the ProgIDs and most other registry information // associated with that application should be deleted as part of the uninstallation.However, applications @@ -194,7 +184,7 @@ bool RegisterFileTypeOperation::undoOperation() settings.endGroup(); } else { // some changes happened, remove the only save value we know about - settings.remove(QString::fromLatin1("%1/OpenWithProgIds/%2").arg(classesFileType, progId)); + settings.remove(QString::fromLatin1("%1/OpenWithProgIds/%2").arg(classesFileType, m_progId)); } // remove ProgId and Applications entry @@ -216,7 +206,21 @@ bool RegisterFileTypeOperation::testOperation() return true; } -Operation *RegisterFileTypeOperation::clone() const +void RegisterFileTypeOperation::ensureOptionalArgumentsRead() { - return new RegisterFileTypeOperation(); +#ifdef Q_OS_WIN + if (m_optionalArgumentsRead) + return; + + m_optionalArgumentsRead = true; + + QStringList args = arguments(); + + m_progId = takeProgIdArgument(args); + + if (m_progId.isEmpty() && args.count() > 0) + m_progId = QString::fromLatin1("%1_auto_file").arg(args.at(0)); + + setArguments(args); +#endif } diff --git a/src/libs/installer/registerfiletypeoperation.h b/src/libs/installer/registerfiletypeoperation.h index de82dedd5..8850ab44b 100644 --- a/src/libs/installer/registerfiletypeoperation.h +++ b/src/libs/installer/registerfiletypeoperation.h @@ -38,13 +38,18 @@ class INSTALLER_EXPORT RegisterFileTypeOperation : public QObject, public Operat Q_OBJECT public: - RegisterFileTypeOperation(); + explicit RegisterFileTypeOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; + +private: + void ensureOptionalArgumentsRead(); + + bool m_optionalArgumentsRead; + QString m_progId; }; } diff --git a/src/libs/installer/remoteclient.cpp b/src/libs/installer/remoteclient.cpp index 666cd813a..c6f5e535f 100644 --- a/src/libs/installer/remoteclient.cpp +++ b/src/libs/installer/remoteclient.cpp @@ -31,6 +31,8 @@ namespace QInstaller { +RemoteClient *RemoteClient::s_instance = 0; + RemoteClient::RemoteClient() : d_ptr(new RemoteClientPrivate(this)) { @@ -42,8 +44,9 @@ RemoteClient::~RemoteClient() RemoteClient &RemoteClient::instance() { - static RemoteClient instance; - return instance; + if (!s_instance) + s_instance = new RemoteClient; + return *s_instance; } QString RemoteClient::socketName() const @@ -69,6 +72,12 @@ void RemoteClient::init(const QString &socketName, const QString &key, Protocol: d->init(socketName, key, mode, startAs); } +void RemoteClient::setAuthorizationFallbackDisabled(bool disabled) +{ + Q_D(RemoteClient); + d->setAuthorizationFallbackDisabled(disabled); +} + void RemoteClient::shutdown() { Q_D(RemoteClient); @@ -76,6 +85,12 @@ void RemoteClient::shutdown() d_ptr.reset(new RemoteClientPrivate(this)); } +void RemoteClient::destroy() +{ + delete s_instance; + s_instance = 0; +} + bool RemoteClient::isActive() const { Q_D(const RemoteClient); diff --git a/src/libs/installer/remoteclient.h b/src/libs/installer/remoteclient.h index 4d435098c..c2090bf98 100644 --- a/src/libs/installer/remoteclient.h +++ b/src/libs/installer/remoteclient.h @@ -47,8 +47,10 @@ public: static RemoteClient &instance(); void init(const QString &socketName, const QString &key, Protocol::Mode mode, Protocol::StartAs startAs); + void setAuthorizationFallbackDisabled(bool disabled); void shutdown(); + void destroy(); QString socketName() const; QString authorizationKey() const; @@ -61,6 +63,7 @@ private: ~RemoteClient(); private: + static RemoteClient *s_instance; QScopedPointer<RemoteClientPrivate> d_ptr; }; diff --git a/src/libs/installer/remoteclient_p.h b/src/libs/installer/remoteclient_p.h index 0afd9dc16..d1e873d43 100644 --- a/src/libs/installer/remoteclient_p.h +++ b/src/libs/installer/remoteclient_p.h @@ -59,6 +59,7 @@ public: , m_active(false) , m_key(QLatin1String(Protocol::DefaultAuthorizationKey)) , m_mode(Protocol::Mode::Debug) + , m_authorizationFallbackDisabled(false) { m_thread.setObjectName(QLatin1String("KeepAlive")); } @@ -101,6 +102,11 @@ public: } } + void setAuthorizationFallbackDisabled(bool disabled) + { + m_authorizationFallbackDisabled = disabled; + } + void maybeStartServer() { if (m_mode == Protocol::Mode::Debug) m_serverStarted = true; // we expect the server to be started by the developer @@ -118,6 +124,16 @@ public: started = AdminAuthorization::execute(0, m_serverCommand, m_serverArguments); 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; + } // something went wrong with authorizing, either user pressed cancel or entered // wrong password const QString fallback = m_serverCommand + QLatin1String(" ") + m_serverArguments @@ -126,11 +142,11 @@ public: const QMessageBox::Button res = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("AuthorizationError"), - QCoreApplication::translate("RemoteClient", "Could not get authorization."), - QCoreApplication::translate("RemoteClient", "Could not get authorization that " + 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 root " - "and then clicking OK.").arg(fallback), + "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) @@ -179,6 +195,7 @@ private: QString m_key; QThread m_thread; Protocol::Mode m_mode; + bool m_authorizationFallbackDisabled; }; } // namespace QInstaller diff --git a/src/libs/installer/remoteobject.cpp b/src/libs/installer/remoteobject.cpp index 69cc66e65..49c111610 100644 --- a/src/libs/installer/remoteobject.cpp +++ b/src/libs/installer/remoteobject.cpp @@ -30,7 +30,6 @@ #include "protocol.h" #include "remoteclient.h" -#include "localsocket.h" #include <QCoreApplication> #include <QElapsedTimer> @@ -52,7 +51,8 @@ RemoteObject::~RemoteObject() { if (m_socket) { if (QThread::currentThread() == m_socket->thread()) { - writeData(QLatin1String(Protocol::Destroy), m_type, dummy, dummy); + if (m_type != QLatin1String("RemoteClientPrivate")) + writeData(QLatin1String(Protocol::Destroy), m_type, dummy, dummy); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Socket running in a different Thread than this object."); } @@ -68,7 +68,7 @@ bool RemoteObject::authorize() if (m_socket) delete m_socket; - m_socket = new LocalSocket; + m_socket = new QLocalSocket; m_socket->connectToServer(RemoteClient::instance().socketName()); if (m_socket->waitForConnected()) { diff --git a/src/libs/installer/remoteobject.h b/src/libs/installer/remoteobject.h index 832e3ccf3..ecd86c984 100644 --- a/src/libs/installer/remoteobject.h +++ b/src/libs/installer/remoteobject.h @@ -93,7 +93,7 @@ public: QByteArray data; while (!receivePacket(m_socket, &command, &data)) { if (!m_socket->waitForReadyRead(-1)) { - throw Error(tr("Could not read all data after sending command: %1. " + throw Error(tr("Cannot read all data after sending command: %1. " "Bytes expected: %2, Bytes received: %3. Error: %4").arg(name).arg(0) .arg(m_socket->bytesAvailable()).arg(m_socket->errorString())); } diff --git a/src/libs/installer/remoteserver.cpp b/src/libs/installer/remoteserver.cpp index 3cd3d0248..9d178ca06 100644 --- a/src/libs/installer/remoteserver.cpp +++ b/src/libs/installer/remoteserver.cpp @@ -75,13 +75,13 @@ void RemoteServer::start() d->m_localServer = new LocalServer(d->m_socketName, d->m_key); d->m_localServer->moveToThread(&d->m_thread); - connect(&d->m_thread, SIGNAL(finished()), d->m_localServer, SLOT(deleteLater())); - connect(d->m_localServer, SIGNAL(newIncomingConnection()), this, SLOT(restartWatchdog())); - connect(d->m_localServer, SIGNAL(shutdownRequested()), this, SLOT(deleteLater())); + connect(&d->m_thread, &QThread::finished, d->m_localServer, &QObject::deleteLater); + connect(d->m_localServer, &LocalServer::newIncomingConnection, this, &RemoteServer::restartWatchdog); + connect(d->m_localServer, &LocalServer::shutdownRequested, this, &QObject::deleteLater); d->m_thread.start(); if (d->m_mode == Protocol::Mode::Production) { - connect(d->m_watchdog.data(), SIGNAL(timeout()), this, SLOT(deleteLater())); + connect(d->m_watchdog.data(), &QTimer::timeout, this, &QObject::deleteLater); d->m_watchdog->start(); } } diff --git a/src/libs/installer/remoteserver_p.h b/src/libs/installer/remoteserver_p.h index 2660f1219..e39350f2f 100644 --- a/src/libs/installer/remoteserver_p.h +++ b/src/libs/installer/remoteserver_p.h @@ -79,9 +79,9 @@ private: if (m_shutdown) return; - QThread *const thread = new RemoteServerConnection(socketDescriptor, m_key, this); - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - connect(thread, SIGNAL(shutdownRequested()), this, SLOT(shutdown())); + RemoteServerConnection *thread = new RemoteServerConnection(socketDescriptor, m_key, this); + connect(thread, &QThread::finished, thread, &QObject::deleteLater); + connect(thread, &RemoteServerConnection::shutdownRequested, this, &LocalServer::shutdown); thread->start(); emit newIncomingConnection(); } diff --git a/src/libs/installer/remoteserverconnection.cpp b/src/libs/installer/remoteserverconnection.cpp index 5236657e2..61bacc0c5 100644 --- a/src/libs/installer/remoteserverconnection.cpp +++ b/src/libs/installer/remoteserverconnection.cpp @@ -33,7 +33,6 @@ #include "remoteserverconnection_p.h" #include "utils.h" #include "permissionsettings.h" -#include "localsocket.h" #include <QCoreApplication> #include <QDataStream> @@ -66,7 +65,7 @@ private: void RemoteServerConnection::run() { - LocalSocket socket; + QLocalSocket socket; socket.setSocketDescriptor(m_socketDescriptor); QScopedPointer<PermissionSettings> settings; @@ -144,11 +143,11 @@ void RemoteServerConnection::run() stream >> type; if (type == QLatin1String(Protocol::QSettings)) { settings.reset(); - } else if (command == QLatin1String(Protocol::QProcess)) { + } else if (type == QLatin1String(Protocol::QProcess)) { m_signalReceiver->m_receivedSignals.clear(); m_process->deleteLater(); m_process = 0; - } else if (command == QLatin1String(Protocol::QAbstractFileEngine)) { + } else if (type == QLatin1String(Protocol::QAbstractFileEngine)) { delete m_engine; m_engine = 0; } diff --git a/src/libs/installer/remoteserverconnection_p.h b/src/libs/installer/remoteserverconnection_p.h index d8334d566..dad5f4133 100644 --- a/src/libs/installer/remoteserverconnection_p.h +++ b/src/libs/installer/remoteserverconnection_p.h @@ -46,19 +46,21 @@ private: explicit QProcessSignalReceiver(QProcess *process) : QObject(process) { - connect(process, SIGNAL(bytesWritten(qint64)), SLOT(onBytesWritten(qint64))); - connect(process, SIGNAL(aboutToClose()), SLOT(onAboutToClose())); - connect(process, SIGNAL(readChannelFinished()), SLOT(onReadChannelFinished())); + connect(process, &QIODevice::bytesWritten, this, &QProcessSignalReceiver::onBytesWritten); + connect(process, &QIODevice::aboutToClose, this, &QProcessSignalReceiver::onAboutToClose); + connect(process, &QIODevice::readChannelFinished, this, &QProcessSignalReceiver::onReadChannelFinished); connect(process, SIGNAL(error(QProcess::ProcessError)), SLOT(onError(QProcess::ProcessError))); - connect(process, SIGNAL(readyReadStandardOutput()), SLOT(onReadyReadStandardOutput())); - connect(process, SIGNAL(readyReadStandardError()), SLOT(onReadyReadStandardError())); - connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), - SLOT(onFinished(int, QProcess::ExitStatus))); - connect(process, SIGNAL(readyRead()), SLOT(onReadyRead())); - connect(process, SIGNAL(started()), SLOT(onStarted())); - connect(process, SIGNAL(stateChanged(QProcess::ProcessState)), - SLOT(onStateChanged(QProcess::ProcessState))); + connect(process, &QProcess::readyReadStandardOutput, + this, &QProcessSignalReceiver::onReadyReadStandardOutput); + connect(process, &QProcess::readyReadStandardError, + this, &QProcessSignalReceiver::onReadyReadStandardError); + connect(process, SIGNAL(finished(int,QProcess::ExitStatus)), + SLOT(onFinished(int,QProcess::ExitStatus))); + connect(process, &QIODevice::readyRead, this, &QProcessSignalReceiver::onReadyRead); + connect(process, &QProcess::started, this, &QProcessSignalReceiver::onStarted); + connect(process, &QProcess::stateChanged, + this, &QProcessSignalReceiver::onStateChanged); } private Q_SLOTS: diff --git a/src/libs/installer/replaceoperation.cpp b/src/libs/installer/replaceoperation.cpp index 946711feb..8cc1e0315 100644 --- a/src/libs/installer/replaceoperation.cpp +++ b/src/libs/installer/replaceoperation.cpp @@ -34,7 +34,8 @@ using namespace QInstaller; -ReplaceOperation::ReplaceOperation() +ReplaceOperation::ReplaceOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("Replace")); } @@ -45,18 +46,14 @@ void ReplaceOperation::backup() bool ReplaceOperation::performOperation() { - const QStringList args = arguments(); - // Arguments: // 1. filename // 2. Source-String // 3. Replace-String - if (args.count() != 3) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("exactly 3"), QLatin1String(""))); + if (!checkArgumentCount(3)) return false; - } + + const QStringList args = arguments(); const QString fileName = args.at(0); const QString before = args.at(1); const QString after = args.at(2); @@ -64,7 +61,8 @@ bool ReplaceOperation::performOperation() QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { setError(UserDefinedError); - setErrorString(tr("Failed to open %1 for reading").arg(fileName)); + setErrorString(tr("Cannot open file \"%1\" for reading: %2").arg( + QDir::toNativeSeparators(fileName), file.errorString())); return false; } @@ -74,7 +72,8 @@ bool ReplaceOperation::performOperation() if (!file.open(QIODevice::WriteOnly)) { setError(UserDefinedError); - setErrorString(tr("Failed to open %1 for writing").arg(fileName)); + setErrorString(tr("Cannot open file \"%1\" for writing: %2").arg( + QDir::toNativeSeparators(fileName), file.errorString())); return false; } @@ -95,8 +94,3 @@ bool ReplaceOperation::testOperation() { return true; } - -Operation *ReplaceOperation::clone() const -{ - return new ReplaceOperation(); -} diff --git a/src/libs/installer/replaceoperation.h b/src/libs/installer/replaceoperation.h index 0ad614697..0d2783ab6 100644 --- a/src/libs/installer/replaceoperation.h +++ b/src/libs/installer/replaceoperation.h @@ -37,13 +37,12 @@ class INSTALLER_EXPORT ReplaceOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::ReplaceOperation) public: - ReplaceOperation(); + explicit ReplaceOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; }; } // namespace QInstaller diff --git a/src/libs/installer/repository.cpp b/src/libs/installer/repository.cpp index 0bfa23a96..eb45573a9 100644 --- a/src/libs/installer/repository.cpp +++ b/src/libs/installer/repository.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "repository.h" -#include "kdupdaterfiledownloaderfactory.h" +#include "filedownloaderfactory.h" #include <QDataStream> #include <QFileInfo> @@ -41,6 +41,7 @@ namespace QInstaller { Repository::Repository() : m_default(false) , m_enabled(false) + , m_compressed(false) { registerMetaType(); } @@ -55,28 +56,31 @@ Repository::Repository(const Repository &other) , m_username(other.m_username) , m_password(other.m_password) , m_displayname(other.m_displayname) + , m_compressed(other.m_compressed) { registerMetaType(); } /*! - Constructs a new repository by setting its address to \a url and its default state. + Constructs a new repository by setting its address to \a url + and its default and \a compressed states. */ -Repository::Repository(const QUrl &url, bool isDefault) +Repository::Repository(const QUrl &url, bool isDefault, bool compressed) : m_url(url) , m_default(isDefault) , m_enabled(true) + , m_compressed(compressed) { registerMetaType(); } /*! - Constructs a new repository by setting its address to \a repositoryUrl as string and its - default state. + Constructs a new repository by setting its address to \a repositoryUrl as + string and its \a compressed state. Note: user and password can be inside the \a repositoryUrl string: http://user:password@repository.url */ -Repository Repository::fromUserInput(const QString &repositoryUrl) +Repository Repository::fromUserInput(const QString &repositoryUrl, bool compressed) { QUrl url = QUrl::fromUserInput(repositoryUrl); const QStringList supportedSchemes = KDUpdater::FileDownloaderFactory::supportedSchemes(); @@ -88,7 +92,7 @@ Repository Repository::fromUserInput(const QString &repositoryUrl) url.setUserName(QString()); url.setPassword(QString()); - Repository repository(url, false); + Repository repository(url, false, compressed); repository.setUsername(userName); repository.setPassword(password); return repository; @@ -195,6 +199,22 @@ void Repository::setDisplayName(const QString &displayname) } /*! + Returns true if repository is compressed +*/ +bool Repository::isCompressed() const +{ + return m_compressed; +} + +/*! + Sets this repository to \a compressed state to know weather the repository + needs to be uncompressed before use. +*/ +void Repository::setCompressed(bool compressed) +{ + m_compressed = compressed; +} +/*! Compares the values of this repository to \a other and returns true if they are equal (same server, default state, enabled state as well as username and password). \sa operator!=() */ @@ -227,6 +247,7 @@ const Repository &Repository::operator=(const Repository &other) m_username = other.m_username; m_password = other.m_password; m_displayname = other.m_displayname; + m_compressed = other.m_compressed; return *this; } @@ -239,7 +260,7 @@ void Repository::registerMetaType() QDataStream &operator>>(QDataStream &istream, Repository &repository) { - QByteArray url, username, password, displayname; + QByteArray url, username, password, displayname, compressed; istream >> url >> repository.m_default >> repository.m_enabled >> username >> password >> displayname; repository.setUrl(QUrl::fromEncoded(QByteArray::fromBase64(url))); repository.setUsername(QString::fromUtf8(QByteArray::fromBase64(username))); diff --git a/src/libs/installer/repository.h b/src/libs/installer/repository.h index 89b18f612..b73e7bd4c 100644 --- a/src/libs/installer/repository.h +++ b/src/libs/installer/repository.h @@ -41,10 +41,10 @@ class INSTALLER_EXPORT Repository public: explicit Repository(); Repository(const Repository &other); - explicit Repository(const QUrl &url, bool isDefault); + explicit Repository(const QUrl &url, bool isDefault, bool compressed = false); static void registerMetaType(); - static Repository fromUserInput(const QString &repositoryUrl); + static Repository fromUserInput(const QString &repositoryUrl, bool compressed = false); bool isValid() const; bool isDefault() const; @@ -64,6 +64,8 @@ public: QString displayname() const; void setDisplayName(const QString &displayname); + bool isCompressed() const; + void setCompressed(bool compressed); bool operator==(const Repository &other) const; bool operator!=(const Repository &other) const; @@ -80,6 +82,7 @@ private: QString m_username; QString m_password; QString m_displayname; + bool m_compressed; }; inline uint qHash(const Repository &repository) diff --git a/src/libs/installer/resources/install.png b/src/libs/installer/resources/install.png Binary files differnew file mode 100644 index 000000000..8e3309c9f --- /dev/null +++ b/src/libs/installer/resources/install.png diff --git a/src/libs/installer/resources/installer.qrc b/src/libs/installer/resources/installer.qrc index 77bf00f65..48a7c65bd 100644 --- a/src/libs/installer/resources/installer.qrc +++ b/src/libs/installer/resources/installer.qrc @@ -3,5 +3,9 @@ <file>installer.png</file> <file>installer.ico</file> <file>installer.icns</file> + <file>install.png</file> + <file>uninstall.png</file> + <file>keepinstalled.png</file> + <file>keepuninstalled.png</file> </qresource> </RCC> diff --git a/src/libs/installer/resources/keepinstalled.png b/src/libs/installer/resources/keepinstalled.png Binary files differnew file mode 100644 index 000000000..7f8489e28 --- /dev/null +++ b/src/libs/installer/resources/keepinstalled.png diff --git a/src/libs/installer/resources/keepuninstalled.png b/src/libs/installer/resources/keepuninstalled.png Binary files differnew file mode 100644 index 000000000..2753092cc --- /dev/null +++ b/src/libs/installer/resources/keepuninstalled.png diff --git a/src/libs/installer/resources/uninstall.png b/src/libs/installer/resources/uninstall.png Binary files differnew file mode 100644 index 000000000..5646770da --- /dev/null +++ b/src/libs/installer/resources/uninstall.png diff --git a/src/libs/installer/scriptengine.cpp b/src/libs/installer/scriptengine.cpp index d099ba218..59f71a52c 100644 --- a/src/libs/installer/scriptengine.cpp +++ b/src/libs/installer/scriptengine.cpp @@ -198,6 +198,21 @@ QList<QJSValue> GuiProxy::findChildren(QObject *parent, const QString &objectNam return children; } +/*! + Hides the GUI when \a silent is \c true. +*/ +void GuiProxy::setSilent(bool silent) +{ + if (m_gui) + m_gui->setSilent(silent); +} + +void GuiProxy::setTextItems(QObject *object, const QStringList &items) +{ + if (m_gui) + m_gui->setTextItems(object, items); +} + void GuiProxy::cancelButtonClicked() { if (m_gui) @@ -260,7 +275,7 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) : setGuiQObject(core->guiObject()); QQmlEngine::setObjectOwnership(core, QQmlEngine::CppOwnership); global.setProperty(QLatin1String("installer"), m_engine.newQObject(core)); - connect(core, SIGNAL(guiObjectChanged(QObject*)), this, SLOT(setGuiQObject(QObject*))); + connect(core, &PackageManagerCore::guiObjectChanged, this, &ScriptEngine::setGuiQObject); } else { global.setProperty(QLatin1String("installer"), m_engine.newQObject(new QObject)); } @@ -366,7 +381,7 @@ QJSValue ScriptEngine::loadInContext(const QString &context, const QString &file { QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { - throw Error(tr("Could not open the requested script file at %1: %2.") + throw Error(tr("Cannot open script file at %1: %2") .arg(fileName, file.errorString())); } @@ -384,9 +399,10 @@ QJSValue ScriptEngine::loadInContext(const QString &context, const QString &file QJSValue scriptContext = evaluate(scriptContent, fileName); scriptContext.setProperty(QLatin1String("Uuid"), QUuid::createUuid().toString()); if (scriptContext.isError()) { - throw Error(tr("Exception while loading the component script '%1'. (%2)").arg( - QFileInfo(file).absoluteFilePath(), scriptContext.toString().isEmpty() ? - QString::fromLatin1("Unknown error.") : scriptContext.toString())); + 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())); } return scriptContext; } diff --git a/src/libs/installer/scriptengine_p.h b/src/libs/installer/scriptengine_p.h index 2d76912d4..7af5cbab8 100644 --- a/src/libs/installer/scriptengine_p.h +++ b/src/libs/installer/scriptengine_p.h @@ -49,7 +49,7 @@ public: ConsoleProxy() {} public slots : - void log(const QString &log) { qDebug() << log; } + void log(const QString &log) { qDebug().noquote() << log; } }; class InstallerProxy : public QObject @@ -153,6 +153,10 @@ public: Q_INVOKABLE QJSValue findChild(QObject *parent, const QString &objectName); Q_INVOKABLE QList<QJSValue> findChildren(QObject *parent, const QString &objectName); + Q_INVOKABLE void setSilent(bool silent); + + Q_INVOKABLE void setTextItems(QObject *object, const QStringList &items); + signals: void interrupted(); void languageChanged(); diff --git a/src/libs/installer/selfrestartoperation.cpp b/src/libs/installer/selfrestartoperation.cpp index 1d7c16f23..586e5bb0b 100644 --- a/src/libs/installer/selfrestartoperation.cpp +++ b/src/libs/installer/selfrestartoperation.cpp @@ -29,30 +29,31 @@ #include "selfrestartoperation.h" #include "packagemanagercore.h" -#include <kdselfrestarter.h> +#include "selfrestarter.h" using namespace QInstaller; -SelfRestartOperation::SelfRestartOperation() +SelfRestartOperation::SelfRestartOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("SelfRestart")); } void SelfRestartOperation::backup() { - setValue(QLatin1String("PreviousSelfRestart"), KDSelfRestarter::restartOnQuit()); + setValue(QLatin1String("PreviousSelfRestart"), SelfRestarter::restartOnQuit()); } bool SelfRestartOperation::performOperation() { - PackageManagerCore *const core = value(QLatin1String("installer")).value<PackageManagerCore*>(); + PackageManagerCore *const core = packageManager(); if (!core) { setError(UserDefinedError); - setErrorString(tr("Installer object needed in '%1' operation is empty.").arg(name())); + setErrorString(tr("Installer object needed in operation %1 is empty.").arg(name())); return false; } - if (!core->isUpdater() && !core->isPackageManager()) { + if (!core->isMaintainer()) { setError(UserDefinedError); setErrorString(tr("Self Restart: Only valid within updater or packagemanager mode.")); return false; @@ -63,13 +64,13 @@ bool SelfRestartOperation::performOperation() setErrorString(tr("Self Restart: Invalid arguments")); return false; } - KDSelfRestarter::setRestartOnQuit(true); - return KDSelfRestarter::restartOnQuit(); + SelfRestarter::setRestartOnQuit(true); + return SelfRestarter::restartOnQuit(); } bool SelfRestartOperation::undoOperation() { - KDSelfRestarter::setRestartOnQuit(value(QLatin1String("PreviousSelfRestart")).toBool()); + SelfRestarter::setRestartOnQuit(value(QLatin1String("PreviousSelfRestart")).toBool()); return true; } @@ -77,8 +78,3 @@ bool SelfRestartOperation::testOperation() { return true; } - -Operation *SelfRestartOperation::clone() const -{ - return new SelfRestartOperation(); -} diff --git a/src/libs/installer/selfrestartoperation.h b/src/libs/installer/selfrestartoperation.h index 54a05f3d1..7adccd183 100644 --- a/src/libs/installer/selfrestartoperation.h +++ b/src/libs/installer/selfrestartoperation.h @@ -37,13 +37,12 @@ class INSTALLER_EXPORT SelfRestartOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::SelfRestartOperation) public: - SelfRestartOperation(); + explicit SelfRestartOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; }; } diff --git a/src/libs/installer/settings.cpp b/src/libs/installer/settings.cpp index 95f32afbf..79d4bcddc 100644 --- a/src/libs/installer/settings.cpp +++ b/src/libs/installer/settings.cpp @@ -33,6 +33,8 @@ #include <QtCore/QFileInfo> #include <QtCore/QStringList> +#include <QtGui/QFontMetrics> +#include <QtWidgets/QApplication> #include <QRegularExpression> #include <QXmlStreamReader> @@ -56,6 +58,7 @@ static const QLatin1String scRemoteRepositories("RemoteRepositories"); static const QLatin1String scDependsOnLocalInstallerBinary("DependsOnLocalInstallerBinary"); static const QLatin1String scTranslations("Translations"); static const QLatin1String scCreateLocalRepository("CreateLocalRepository"); +static const QLatin1String scInstallActionColumnVisible("InstallActionColumnVisible"); static const QLatin1String scFtpProxy("FtpProxy"); static const QLatin1String scHttpProxy("HttpProxy"); @@ -79,9 +82,10 @@ static void raiseError(QXmlStreamReader &reader, const QString &error, Settings: } else { QFile *xmlFile = qobject_cast<QFile*>(reader.device()); if (xmlFile) { - qWarning() << QString::fromLatin1("Ignoring following settings reader error in %1, line %2, " - "column %3: %4").arg(xmlFile->fileName()).arg(reader.lineNumber()).arg(reader.columnNumber()) - .arg(error); + qWarning().noquote().nospace() + << "Ignoring following settings reader error in " << xmlFile->fileName() + << ", line " << reader.lineNumber() << ", column " << reader.columnNumber() + << ": " << error; } else { qWarning("Ignoring following settings reader error: %s", qPrintable(error)); } @@ -97,7 +101,7 @@ static QStringList readArgumentAttributes(QXmlStreamReader &reader, Settings::Pa switch (token) { case QXmlStreamReader::StartElement: { if (!reader.attributes().isEmpty()) { - raiseError(reader, QString::fromLatin1("Unexpected attribute for element '%1'.") + raiseError(reader, QString::fromLatin1("Unexpected attribute for element \"%1\".") .arg(reader.name().toString()), parseMode); return arguments; } else { @@ -105,7 +109,7 @@ static QStringList readArgumentAttributes(QXmlStreamReader &reader, Settings::Pa (lc) ? arguments.append(reader.readElementText().toLower()) : arguments.append(reader.readElementText()); } else { - raiseError(reader, QString::fromLatin1("Unexpected element '%1'.").arg(reader.name() + raiseError(reader, QString::fromLatin1("Unexpected element \"%1\".").arg(reader.name() .toString()), parseMode); return arguments; } @@ -147,23 +151,23 @@ static QSet<Repository> readRepositories(QXmlStreamReader &reader, bool isDefaul } else if (reader.name() == QLatin1String("Enabled")) { repo.setEnabled(bool(reader.readElementText().toInt())); } else { - raiseError(reader, QString::fromLatin1("Unexpected element '%1'.").arg(reader.name() + raiseError(reader, QString::fromLatin1("Unexpected element \"%1\".").arg(reader.name() .toString()), parseMode); } if (!reader.attributes().isEmpty()) { - raiseError(reader, QString::fromLatin1("Unexpected attribute for element '%1'.") + raiseError(reader, QString::fromLatin1("Unexpected attribute for element \"%1\".") .arg(reader.name().toString()), parseMode); } } set.insert(repo); } else { - raiseError(reader, QString::fromLatin1("Unexpected element '%1'.").arg(reader.name().toString()), + raiseError(reader, QString::fromLatin1("Unexpected element \"%1\".").arg(reader.name().toString()), parseMode); } if (!reader.attributes().isEmpty()) { - raiseError(reader, QString::fromLatin1("Unexpected attribute for element '%1'.").arg(reader + raiseError(reader, QString::fromLatin1("Unexpected attribute for element \"%1\".").arg(reader .name().toString()), parseMode); } } @@ -230,12 +234,12 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix, file.setFileName(overrideConfig.fileName()); if (!file.open(QIODevice::ReadOnly)) - throw Error(tr("Could not open settings file %1 for reading: %2").arg(path, file.errorString())); + throw Error(tr("Cannot open settings file %1 for reading: %2").arg(path, file.errorString())); QXmlStreamReader reader(&file); if (reader.readNextStartElement()) { if (reader.name() != QLatin1String("Installer")) { - reader.raiseError(QString::fromLatin1("Unexpected element '%1' as root element.").arg(reader + reader.raiseError(QString::fromLatin1("Unexpected element \"%1\" as root element.").arg(reader .name().toString())); } } @@ -247,26 +251,27 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix, << scStartMenuDir << scMaintenanceToolName << scMaintenanceToolIniFile << scRemoveTargetDir << scRunProgram << scRunProgramArguments << scRunProgramDescription << scDependsOnLocalInstallerBinary - << scAllowSpaceInPath << scAllowNonAsciiCharacters << scWizardStyle << scTitleColor + << scAllowSpaceInPath << scAllowNonAsciiCharacters << scDisableAuthorizationFallback + << scWizardStyle << scStyleSheet << scTitleColor << scWizardDefaultWidth << scWizardDefaultHeight << scRepositorySettingsPageVisible << scTargetConfigurationFile - << scRemoteRepositories << scTranslations << QLatin1String(scControlScript) - << scCreateLocalRepository; + << scRemoteRepositories << scTranslations << scUrlQueryString << QLatin1String(scControlScript) + << scCreateLocalRepository << scInstallActionColumnVisible << scSupportsModify; Settings s; s.d->m_data.insert(scPrefix, prefix); while (reader.readNextStartElement()) { const QString name = reader.name().toString(); if (!elementList.contains(name)) - raiseError(reader, QString::fromLatin1("Unexpected element '%1'.").arg(name), parseMode); + raiseError(reader, QString::fromLatin1("Unexpected element \"%1\".").arg(name), parseMode); if (!reader.attributes().isEmpty()) { - raiseError(reader, QString::fromLatin1("Unexpected attribute for element '%1'.").arg(name), + raiseError(reader, QString::fromLatin1("Unexpected attribute for element \"%1\".").arg(name), parseMode); } if (s.d->m_data.contains(name)) - reader.raiseError(QString::fromLatin1("Element '%1' has been defined before.").arg(name)); + reader.raiseError(QString::fromLatin1("Element \"%1\" has been defined before.").arg(name)); if (name == scTranslations) { s.setTranslations(readArgumentAttributes(reader, parseMode, QLatin1String("Translation"), true)); @@ -318,6 +323,8 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix, s.d->m_data.insert(scRepositorySettingsPageVisible, true); if (!s.d->m_data.contains(scCreateLocalRepository)) s.d->m_data.insert(scCreateLocalRepository, false); + if (!s.d->m_data.contains(scInstallActionColumnVisible)) + s.d->m_data.insert(scInstallActionColumnVisible, false); return s; } @@ -372,19 +379,41 @@ QString Settings::wizardStyle() const return d->m_data.value(scWizardStyle).toString(); } +QString Settings::styleSheet() const +{ + return d->absolutePathFromKey(scStyleSheet); +} + QString Settings::titleColor() const { return d->m_data.value(scTitleColor).toString(); } +static int lengthToInt(const QVariant &variant) +{ + QString length = variant.toString().trimmed(); + if (length.endsWith(QLatin1String("em"), Qt::CaseInsensitive)) { + length.chop(2); + return qRound(length.toDouble() * QApplication::fontMetrics().height()); + } + if (length.endsWith(QLatin1String("ex"), Qt::CaseInsensitive)) { + length.chop(2); + return qRound(length.toDouble() * QApplication::fontMetrics().xHeight()); + } + if (length.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) { + length.chop(2); + } + return length.toInt(); +} + int Settings::wizardDefaultWidth() const { - return d->m_data.value(scWizardDefaultWidth).toInt(); + return lengthToInt(d->m_data.value(scWizardDefaultWidth)); } int Settings::wizardDefaultHeight() const { - return d->m_data.value(scWizardDefaultHeight).toInt(); + return lengthToInt(d->m_data.value(scWizardDefaultHeight)); } QString Settings::installerApplicationIcon() const @@ -472,6 +501,11 @@ bool Settings::createLocalRepository() const return d->m_data.value(scCreateLocalRepository).toBool(); } +bool Settings::installActionColumnVisible() const +{ + return d->m_data.value(scInstallActionColumnVisible, false).toBool(); +} + bool Settings::allowSpaceInPath() const { return d->m_data.value(scAllowSpaceInPath, true).toBool(); @@ -482,6 +516,11 @@ bool Settings::allowNonAsciiCharacters() const return d->m_data.value(scAllowNonAsciiCharacters, false).toBool(); } +bool Settings::disableAuthorizationFallback() const +{ + return d->m_data.value(scDisableAuthorizationFallback, false).toBool(); +} + bool Settings::dependsOnLocalInstallerBinary() const { return d->m_data.value(scDependsOnLocalInstallerBinary).toBool(); @@ -699,3 +738,8 @@ QString Settings::controlScript() const { return d->m_data.value(QLatin1String(scControlScript)).toString(); } + +bool Settings::supportsModify() const +{ + return d->m_data.value(scSupportsModify, true).toBool(); +} diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h index 0fa938167..3dc1c99c3 100644 --- a/src/libs/installer/settings.h +++ b/src/libs/installer/settings.h @@ -83,6 +83,7 @@ public: QString installerWindowIcon() const; QString systemIconSuffix() const; QString wizardStyle() const; + QString styleSheet() const; QString titleColor() const; int wizardDefaultWidth() const; int wizardDefaultHeight() const; @@ -106,6 +107,7 @@ public: QString configurationFileName() const; bool createLocalRepository() const; + bool installActionColumnVisible() const; bool dependsOnLocalInstallerBinary() const; bool hasReplacementRepos() const; @@ -127,6 +129,7 @@ public: bool allowSpaceInPath() const; bool allowNonAsciiCharacters() const; + bool disableAuthorizationFallback() const; bool containsValue(const QString &key) const; QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; @@ -149,6 +152,8 @@ public: QString controlScript() const; + bool supportsModify() const; + private: class Private; QSharedDataPointer<Private> d; diff --git a/src/libs/installer/settingsoperation.cpp b/src/libs/installer/settingsoperation.cpp index 53a5e8c8a..e6d88b71e 100644 --- a/src/libs/installer/settingsoperation.cpp +++ b/src/libs/installer/settingsoperation.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "settingsoperation.h" #include "packagemanagercore.h" -#include "kdupdaterupdateoperations.h" +#include "updateoperations.h" #include "qsettingswrapper.h" #include <QDir> @@ -35,7 +35,8 @@ using namespace QInstaller; -SettingsOperation::SettingsOperation() +SettingsOperation::SettingsOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("Settings")); } @@ -63,7 +64,7 @@ bool SettingsOperation::checkArguments() if (!missingArguments.isEmpty()) { setError(InvalidArguments); - setErrorString(tr("Missing argument(s) '%1' calling '%2' with arguments '%3'.").arg( + setErrorString(tr("Missing argument(s) \"%1\" calling %2 with arguments \"%3\".").arg( missingArguments.join(QLatin1String("; ")), name(), arguments().join(QLatin1String("; ")))); return false; } @@ -73,7 +74,7 @@ bool SettingsOperation::checkArguments() if (!possibleMethodValues.contains(method)) { setError(InvalidArguments); - setErrorString(tr("Current method argument calling '%1' with arguments '%2' is not " + setErrorString(tr("Current method argument calling \"%1\" with arguments \"%2\" is not " "supported. Please use set, remove, add_array_value or remove_array_value.").arg(name(), arguments().join(QLatin1String("; ")))); return false; @@ -177,14 +178,14 @@ bool SettingsOperation::undoOperation() if (cleanUp) { QFile settingsFile(path); if (!settingsFile.remove()) - qWarning() << settingsFile.errorString(); + qWarning().noquote() << settingsFile.errorString(); if (!value(QLatin1String("createddir")).toString().isEmpty()) { KDUpdater::MkdirOperation mkDirOperation; mkDirOperation.setArguments(QStringList() << QFileInfo(path).absolutePath()); mkDirOperation.setValue(QLatin1String("createddir"), value(QLatin1String("createddir"))); if (!mkDirOperation.undoOperation()) { - qWarning() << mkDirOperation.errorString(); + qWarning().noquote() << mkDirOperation.errorString(); } } } @@ -195,9 +196,3 @@ bool SettingsOperation::testOperation() { return true; } - -Operation *SettingsOperation::clone() const -{ - return new SettingsOperation(); -} - diff --git a/src/libs/installer/settingsoperation.h b/src/libs/installer/settingsoperation.h index 14b0eb169..0b94ea015 100644 --- a/src/libs/installer/settingsoperation.h +++ b/src/libs/installer/settingsoperation.h @@ -37,13 +37,12 @@ class INSTALLER_EXPORT SettingsOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::SettingsOperation) public: - SettingsOperation(); + explicit SettingsOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; private: bool checkArguments(); diff --git a/src/libs/installer/simplemovefileoperation.cpp b/src/libs/installer/simplemovefileoperation.cpp index 5f6ea4ff3..d5cbde2ee 100644 --- a/src/libs/installer/simplemovefileoperation.cpp +++ b/src/libs/installer/simplemovefileoperation.cpp @@ -28,11 +28,13 @@ #include "simplemovefileoperation.h" +#include <QDir> #include <QtCore/QFileInfo> namespace QInstaller { -SimpleMoveFileOperation::SimpleMoveFileOperation() +SimpleMoveFileOperation::SimpleMoveFileOperation(PackageManagerCore *core) + : UpdateOperation(core) { setName(QLatin1String("SimpleMoveFile")); } @@ -43,21 +45,17 @@ void SimpleMoveFileOperation::backup() bool SimpleMoveFileOperation::performOperation() { - const QStringList args = arguments(); - if (args.count() != 2) { - setError(InvalidArguments); - setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.") - .arg(name()).arg(arguments().count()).arg(tr("exactly 2"), QLatin1String(""))); + if (!checkArgumentCount(2)) return false; - } + const QStringList args = arguments(); const QString source = args.at(0); const QString target = args.at(1); if (source.isEmpty() || target.isEmpty()) { setError(UserDefinedError); - setErrorString(tr("None of the arguments can be empty: source '%1', target '%2'.") - .arg(source, target)); + setErrorString(tr("None of the arguments can be empty: source \"%1\", target \"%2\".") + .arg(QDir::toNativeSeparators(source), QDir::toNativeSeparators(target))); return false; } @@ -67,8 +65,8 @@ bool SimpleMoveFileOperation::performOperation() if (file.exists()) { if (!file.remove()) { setError(UserDefinedError); - setErrorString(tr("Cannot move source '%1' to target '%2', because target exists and is " - "not removable.").arg(source, target)); + setErrorString(tr("Cannot move file from \"%1\" to \"%2\", because the target path exists and is " + "not removable.").arg(QDir::toNativeSeparators(source), QDir::toNativeSeparators(target))); return false; } } @@ -76,12 +74,14 @@ bool SimpleMoveFileOperation::performOperation() file.setFileName(source); if (!file.rename(target)) { setError(UserDefinedError); - setErrorString(tr("Cannot move source '%1' to target '%2': %3").arg(source, target, - file.errorString())); + setErrorString(tr("Cannot move file \"%1\" to \"%2\": %3").arg( + QDir::toNativeSeparators(source), QDir::toNativeSeparators(target), + file.errorString())); return false; } - emit outputTextChanged(tr("Move '%1' to '%2'.").arg(source, target)); + emit outputTextChanged(tr("Moving file \"%1\" to \"%2\".").arg(QDir::toNativeSeparators(source), + QDir::toNativeSeparators(target))); return true; } @@ -91,7 +91,8 @@ bool SimpleMoveFileOperation::undoOperation() const QString target = arguments().at(1); QFile(target).rename(source); - emit outputTextChanged(tr("Move '%1' to '%2'.").arg(target, source)); + emit outputTextChanged(tr("Moving file \"%1\" to \"%2\".").arg(QDir::toNativeSeparators(target), + QDir::toNativeSeparators(source))); return true; } @@ -101,9 +102,4 @@ bool SimpleMoveFileOperation::testOperation() return true; } -Operation *SimpleMoveFileOperation::clone() const -{ - return new SimpleMoveFileOperation(); -} - } // namespace QInstaller diff --git a/src/libs/installer/simplemovefileoperation.h b/src/libs/installer/simplemovefileoperation.h index 9b2ea2603..26cb5c462 100644 --- a/src/libs/installer/simplemovefileoperation.h +++ b/src/libs/installer/simplemovefileoperation.h @@ -40,13 +40,12 @@ class INSTALLER_EXPORT SimpleMoveFileOperation : public QObject, public Operatio Q_OBJECT public: - SimpleMoveFileOperation(); + explicit SimpleMoveFileOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); - Operation *clone() const; Q_SIGNALS: void outputTextChanged(const QString &progress); diff --git a/src/libs/installer/sysinfo_win.cpp b/src/libs/installer/sysinfo_win.cpp index de1318295..69c1744ca 100644 --- a/src/libs/installer/sysinfo_win.cpp +++ b/src/libs/installer/sysinfo_win.cpp @@ -26,7 +26,7 @@ ** **************************************************************************/ -#include "kdsysinfo.h" +#include "sysinfo.h" #include "link.h" #ifdef Q_CC_MINGW diff --git a/src/libs/installer/testrepository.cpp b/src/libs/installer/testrepository.cpp index a3e8ce9c3..4ee2d41ae 100644 --- a/src/libs/installer/testrepository.cpp +++ b/src/libs/installer/testrepository.cpp @@ -27,26 +27,29 @@ **************************************************************************/ #include "testrepository.h" -#include <kdupdaterfiledownloader.h> -#include <kdupdaterfiledownloaderfactory.h> +#include "packagemanagercore.h" +#include "packagemanagerproxyfactory.h" +#include "proxycredentialsdialog.h" +#include "serverauthenticationdialog.h" -#include <QtCore/QFile> +#include <QFile> -using namespace QInstaller; +namespace QInstaller { -TestRepository::TestRepository(QObject *parent) - : KDJob(parent) - , m_downloader(0) +TestRepository::TestRepository(PackageManagerCore *parent) + : Job(parent) + , m_core(parent) { - setTimeout(10000); setAutoDelete(false); setCapabilities(Cancelable); + + connect(&m_timer, &QTimer::timeout, this, &TestRepository::onTimeout); + connect(&m_xmlTask, &QFutureWatcherBase::finished, this, &TestRepository::downloadCompleted); } TestRepository::~TestRepository() { - if (m_downloader) - m_downloader->deleteLater(); + reset(); } Repository TestRepository::repository() const @@ -56,100 +59,127 @@ Repository TestRepository::repository() const void TestRepository::setRepository(const Repository &repository) { - cancel(); - - setError(NoError); - setErrorString(QString()); + reset(); m_repository = repository; } void TestRepository::doStart() { - if (m_downloader) - m_downloader->deleteLater(); + reset(); + if (!m_core) { + emitFinishedWithError(Job::Canceled, tr("Missing package manager core engine.")); + return; // We can't do anything here without core, so avoid tons of !m_core checks. + } const QUrl url = m_repository.url(); if (url.isEmpty()) { - emitFinishedWithError(InvalidUrl, tr("Empty repository URL.")); - return; - } - - m_downloader = KDUpdater::FileDownloaderFactory::instance().create(url.scheme(), this); - if (!m_downloader) { - emitFinishedWithError(InvalidUrl, tr("URL scheme not supported: %1 (%2).") - .arg(url.scheme(), url.toString())); + emitFinishedWithError(QInstaller::InvalidUrl, tr("Empty repository URL.")); return; } QAuthenticator auth; auth.setUser(m_repository.username()); auth.setPassword(m_repository.password()); - m_downloader->setAuthenticator(auth); - - connect(m_downloader, SIGNAL(downloadCompleted()), this, SLOT(downloadCompleted())); - connect(m_downloader, SIGNAL(downloadAborted(QString)), this, SLOT(downloadAborted(QString)), - Qt::QueuedConnection); - connect(m_downloader, SIGNAL(authenticatorChanged(QAuthenticator)), this, - SLOT(onAuthenticatorChanged(QAuthenticator))); - m_downloader->setAutoRemoveDownloadedFile(true); - m_downloader->setUrl(QUrl(url.toString() + QString::fromLatin1("/Updates.xml"))); + FileTaskItem item(m_repository.url().toString() + QLatin1String("/Updates.xml?") + + QString::number(qrand() * qrand())); + item.insert(TaskRole::Authenticator, QVariant::fromValue(auth)); - m_downloader->download(); + m_timer.start(10000); + DownloadFileTask *const xmlTask = new DownloadFileTask(item); + if (m_core) + xmlTask->setProxyFactory(m_core->proxyFactory()); + m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask)); } void TestRepository::doCancel() { - if (m_downloader) { - QString errorString = m_downloader->errorString(); - if (errorString.isEmpty()) - errorString = tr("Got a timeout while testing: '%1'").arg(m_repository.displayname()); - // at the moment the download sends downloadCompleted() if we cancel it, so just - disconnect(m_downloader, 0, this, 0); - m_downloader->cancelDownload(); - emitFinishedWithError(KDJob::Canceled, errorString); - } + reset(); + emitFinishedWithError(Job::Canceled, tr("Download canceled.")); +} + +void TestRepository::onTimeout() +{ + reset(); + emitFinishedWithError(Job::Canceled, tr("Timeout while testing repository \"%1\".") + .arg(m_repository.displayname())); } void TestRepository::downloadCompleted() { - QString errorMsg; - int error = DownloadError; + if (error() != Job::NoError) + return; - if (m_downloader->isDownloaded()) { - QFile file(m_downloader->downloadedFileName()); - if (file.exists() && file.open(QIODevice::ReadOnly)) { + try { + m_xmlTask.waitForFinished(); + + m_timer.stop(); + QFile file(m_xmlTask.future().results().value(0).target()); + if (file.open(QIODevice::ReadOnly)) { QDomDocument doc; QString errorMsg; if (!doc.setContent(&file, &errorMsg)) { - error = InvalidUpdatesXml; - errorMsg = tr("Could not parse Updates.xml! Error: %1.").arg(errorMsg); + emitFinishedWithError(QInstaller::InvalidUpdatesXml, + tr("Cannot parse Updates.xml: %1").arg(errorMsg)); } else { - error = NoError; + emitFinishedWithError(Job::NoError, QString(/*Success*/)); // OPK } } else { - errorMsg = tr("Updates.xml could not be opened for reading!"); + emitFinishedWithError(QInstaller::DownloadError, + tr("Cannot open Updates.xml for reading: %1").arg(file.errorString())); } - } else { - errorMsg = tr("Updates.xml could not be found on server!"); + } catch (const AuthenticationRequiredException &e) { + m_timer.stop(); + if (e.type() == AuthenticationRequiredException::Type::Server) { + ServerAuthenticationDialog dlg(e.message(), e.taskItem()); + if (dlg.exec() == QDialog::Accepted) { + m_repository.setUsername(dlg.user()); + m_repository.setPassword(dlg.password()); + QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); + } else { + QMetaObject::invokeMethod(this, "doCancel", Qt::QueuedConnection); + } + return; + } else if (e.type() == AuthenticationRequiredException::Type::Proxy) { + const QNetworkProxy proxy = e.proxy(); + ProxyCredentialsDialog proxyCredentials(proxy); + if (proxyCredentials.exec() == QDialog::Accepted) { + PackageManagerProxyFactory *factory = m_core->proxyFactory(); + factory->setProxyCredentials(proxy, proxyCredentials.userName(), + proxyCredentials.password()); + m_core->setProxyFactory(factory); + } + QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); + return; + } else { + emitFinishedWithError(QInstaller::DownloadError, tr("Authentication failed.")); + } + } catch (const TaskException &e) { + m_timer.stop(); + emitFinishedWithError(QInstaller::DownloadError, e.message()); + } catch (const QUnhandledException &e) { + m_timer.stop(); + emitFinishedWithError(QInstaller::DownloadError, QLatin1String(e.what())); + } catch (...) { + m_timer.stop(); + emitFinishedWithError(QInstaller::DownloadError, + tr("Unknown error while testing repository \"%1\".").arg(m_repository.displayname())); } +} - if (error > NoError) - emitFinishedWithError(error, errorMsg); - else - emitFinished(); - m_downloader->deleteLater(); - m_downloader = 0; -} +// -- private -void TestRepository::downloadAborted(const QString &reason) +void TestRepository::reset() { - emitFinishedWithError(DownloadError, reason); -} + m_timer.stop(); + setError(NoError); + setErrorString(QString()); -void TestRepository::onAuthenticatorChanged(const QAuthenticator &authenticator) -{ - m_repository.setUsername(authenticator.user()); - m_repository.setPassword(authenticator.password()); + try { + if (m_xmlTask.isRunning()) + m_xmlTask.cancel(); + } catch (...) {} } + +} // namespace QInstaller diff --git a/src/libs/installer/testrepository.h b/src/libs/installer/testrepository.h index 43165560d..80d964692 100644 --- a/src/libs/installer/testrepository.h +++ b/src/libs/installer/testrepository.h @@ -28,55 +28,47 @@ #ifndef TESTREPOSITORY_H #define TESTREPOSITORY_H -#include "qinstallerglobal.h" +#include "downloadfiletask.h" +#include "job.h" +#include "repository.h" -#include <repository.h> -#include <settings.h> - -#include <kdjob.h> - -QT_BEGIN_NAMESPACE -class QAuthenticator; -class QLocale; -class QVariant; -QT_END_NAMESPACE - -namespace KDUpdater { - class FileDownloader; -} +#include <QFutureWatcher> +#include <QTimer> namespace QInstaller { - class PackageManagerCore; -} -namespace QInstaller { +class PackageManagerCore; -class INSTALLER_EXPORT TestRepository : public KDJob +class INSTALLER_EXPORT TestRepository : public Job { Q_OBJECT + Q_DISABLE_COPY(TestRepository) public: - - explicit TestRepository(QObject *parent = 0); + explicit TestRepository(PackageManagerCore *parent = 0); ~TestRepository(); - QInstaller::Repository repository() const; - void setRepository(const QInstaller::Repository &repository); + Repository repository() const; + void setRepository(const Repository &repository); -private: +private slots: void doStart(); void doCancel(); -private Q_SLOTS: + void onTimeout(); void downloadCompleted(); - void downloadAborted(const QString &reason); - void onAuthenticatorChanged(const QAuthenticator &authenticator); private: - QInstaller::Repository m_repository; - KDUpdater::FileDownloader *m_downloader; + void reset(); + +private: + PackageManagerCore *m_core; + + QTimer m_timer; + Repository m_repository; + QFutureWatcher<FileTaskResult> m_xmlTask; }; -} //namespace QInstaller +} // namespace QInstaller #endif // TESTREPOSITORY_H diff --git a/src/libs/installer/unziptask.cpp b/src/libs/installer/unziptask.cpp index 4b78b6d6e..f1f0dd0a1 100644 --- a/src/libs/installer/unziptask.cpp +++ b/src/libs/installer/unziptask.cpp @@ -25,7 +25,9 @@ ** $QT_END_LICENSE$ ** **************************************************************************/ + #include "unziptask.h" +#include "lib7z_facade.h" #ifdef Q_OS_UNIX # include "StdAfx.h" @@ -34,21 +36,15 @@ // TODO: include once we switch from lib7z_fascade.h //#include "Common/MyInitGuid.h" -#include "7zip/IPassword.h" +#include "7zip/Archive/IArchive.h" #include "7zip/Common/FileStreams.h" #include "7zip/UI/Common/OpenArchive.h" #include "Windows/FileDir.h" #include "Windows/PropVariant.h" -#include "7zCrc.h" - #include <QDir> -void registerArc7z(); -void registerCodecLZMA(); -void registerCodecLZMA2(); - namespace QInstaller { class ArchiveExtractCallback : public IArchiveExtractCallback, public CMyUnknownImp @@ -99,11 +95,11 @@ public: return E_FAIL; bool isDir = false; - if (IsArchiveItemFolder(m_arc.Archive, m_currentIndex, isDir) != S_OK) + if (Archive_IsItem_Folder(m_arc.Archive, m_currentIndex, isDir) != S_OK) return E_FAIL; bool isEncrypted = false; - if (GetArchiveItemBoolProp(m_arc.Archive, m_currentIndex, kpidEncrypted, isEncrypted) != S_OK) + if (Archive_GetItemBoolProp(m_arc.Archive, m_currentIndex, kpidEncrypted, isEncrypted) != S_OK) return E_FAIL; if (isDir || isEncrypted) @@ -149,7 +145,7 @@ public: default: // fall through and bail case NArchive::NExtract::NOperationResult::kCRCError: case NArchive::NExtract::NOperationResult::kDataError: - case NArchive::NExtract::NOperationResult::kUnSupportedMethod: + case NArchive::NExtract::NOperationResult::kUnsupportedMethod: m_outFileStream->Close(); m_outFileStreamComPtr.Release(); return E_FAIL; @@ -157,7 +153,7 @@ public: UInt32 attributes; if (GetAttributes(&attributes)) - NWindows::NFile::NDirectory::MySetFileAttributes((wchar_t*)(m_currentTarget.utf16()), attributes); + NWindows::NFile::NDir::SetFileAttrib((wchar_t*)(m_currentTarget.utf16()), attributes); FILETIME accessTime, creationTime, modificationTime; const bool writeAccessTime = GetTime(kpidATime, &accessTime); @@ -234,13 +230,7 @@ UnzipTask::UnzipTask(const QString &source, const QString &target) : m_source(source) , m_target(target) { - { - CrcGenerateTable(); - - registerArc7z(); - registerCodecLZMA(); - registerCodecLZMA2(); - } + Lib7z::initSevenZ(); } void UnzipTask::doTask(QFutureInterface<QString> &fi) @@ -251,19 +241,29 @@ void UnzipTask::doTask(QFutureInterface<QString> &fi) if (codecs.Load() != S_OK) return; - CIntVector formatIndices; - if (!codecs.FindFormatForArchiveType(L"", formatIndices)) - return; - CInFileStream *fileStream = new CInFileStream; + COpenOptions op; + op.codecs = &codecs; + + CObjectVector<COpenType> types; + op.types = &types; // Empty, because we use a stream. + + CIntVector excluded; + op.excludedFormats = &excluded; + + const CMyComPtr<CInFileStream> fileStream = new CInFileStream; fileStream->Open((wchar_t*) (m_source.utf16())); + op.stream = fileStream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). + + CObjectVector<CProperty> properties; + op.props = &properties; CArchiveLink archiveLink; - if (archiveLink.Open2(&codecs, formatIndices, false, fileStream, UString(), 0) != S_OK) + if (archiveLink.Open2(op, nullptr) != S_OK) return; UINT32 count = 0; - for (int i = 0; i < archiveLink.Arcs.Size(); ++i) { + for (unsigned i = 0; i < archiveLink.Arcs.Size(); ++i) { const CArc& arc = archiveLink.Arcs[i]; UInt32 numItems = 0; if (arc.Archive->GetNumberOfItems(&numItems) != S_OK) @@ -272,7 +272,7 @@ void UnzipTask::doTask(QFutureInterface<QString> &fi) } fi.setExpectedResultCount(count); - for (int i = 0; i < archiveLink.Arcs.Size(); ++i) { + for (unsigned i = 0; i < archiveLink.Arcs.Size(); ++i) { if (fi.isCanceled()) break; if (fi.isPaused()) diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp index d76eab696..a11ac774e 100644 --- a/src/libs/installer/utils.cpp +++ b/src/libs/installer/utils.cpp @@ -214,24 +214,40 @@ QInstaller::VerboseWriter::VerboseWriter() QInstaller::VerboseWriter::~VerboseWriter() { + if (preFileBuffer.isOpen()) { + PlainVerboseWriterOutput output; + (void)flush(&output); + } +} + +bool QInstaller::VerboseWriter::flush(VerboseWriterOutput *output) +{ stream.flush(); if (logFileName.isEmpty()) // binarycreator - return; + return true; + if (!preFileBuffer.isOpen()) + return true; //if the installer installed nothing - there is no target directory - where the logfile can be saved if (!QFileInfo(logFileName).absoluteDir().exists()) - return; - - QFile output(logFileName); - if (output.open(QIODevice::ReadWrite | QIODevice::Append | QIODevice::Text)) { - QString logInfo; - logInfo += QLatin1String("************************************* Invoked: "); - logInfo += currentDateTimeAsString; - logInfo += QLatin1String("\n"); - output.write(logInfo.toLocal8Bit()); - output.write(preFileBuffer.data()); - output.close(); + return true; + + QString logInfo; + logInfo += QLatin1String("************************************* Invoked: "); + logInfo += currentDateTimeAsString; + logInfo += QLatin1String("\n"); + + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + buffer.write(logInfo.toLocal8Bit()); + buffer.write(preFileBuffer.data()); + buffer.close(); + + if (output->write(logFileName, QIODevice::ReadWrite | QIODevice::Append | QIODevice::Text, buffer.data())) { + preFileBuffer.close(); + stream.setDevice(0); + return true; } - stream.setDevice(0); + return false; } void QInstaller::VerboseWriter::setFileName(const QString &fileName) @@ -252,6 +268,20 @@ void QInstaller::VerboseWriter::appendLine(const QString &msg) stream << msg << endl; } +QInstaller::VerboseWriterOutput::~VerboseWriterOutput() +{ +} + +bool QInstaller::PlainVerboseWriterOutput::write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data) +{ + QFile output(fileName); + if (output.open(openMode)) { + output.write(data); + return true; + } + return false; +} + #ifdef Q_OS_WIN // taken from qcoreapplication_p.h template<typename Char> diff --git a/src/libs/installer/utils.h b/src/libs/installer/utils.h index cfaeaa67d..7cd237dcc 100644 --- a/src/libs/installer/utils.h +++ b/src/libs/installer/utils.h @@ -67,6 +67,20 @@ namespace QInstaller { INSTALLER_EXPORT std::ostream& operator<<(std::ostream &os, const QString &string); + class INSTALLER_EXPORT VerboseWriterOutput + { + public: + virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data) = 0; + + protected: + ~VerboseWriterOutput(); + }; + + class INSTALLER_EXPORT PlainVerboseWriterOutput : public VerboseWriterOutput + { + public: + virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data); + }; class INSTALLER_EXPORT VerboseWriter { @@ -76,6 +90,8 @@ namespace QInstaller { static VerboseWriter *instance(); + bool flush(VerboseWriterOutput *output); + void appendLine(const QString &msg); void setFileName(const QString &fileName); |