Skip to content
Snippets Groups Projects
itemhandler.cpp 15.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • akiraohgaki's avatar
    akiraohgaki committed
    #include "itemhandler.h"
    
    
    akiraohgaki's avatar
    akiraohgaki committed
    #include <QUrlQuery>
    
    akiraohgaki's avatar
    akiraohgaki committed
    #include <QJsonValue>
    #include <QJsonArray>
    #include <QFileInfo>
    #include <QDateTime>
    
    
    akiraohgaki's avatar
    akiraohgaki committed
    #ifdef QTIL_OS_UNIX
    
    akiraohgaki's avatar
    akiraohgaki committed
    #include <QProcess>
    #endif
    
    
    akiraohgaki's avatar
    akiraohgaki committed
    #include "qtil_dir.h"
    #include "qtil_file.h"
    #include "qtil_networkresource.h"
    #include "qtil_package.h"
    
    akiraohgaki's avatar
    akiraohgaki committed
    
    #include "handlers/confighandler.h"
    
    ItemHandler::ItemHandler(ConfigHandler *configHandler, QObject *parent)
        : QObject(parent), configHandler_(configHandler)
    {}
    
    QJsonObject ItemHandler::metadataSet() const
    {
        return metadataSet_;
    }
    
    
    akiraohgaki's avatar
    akiraohgaki committed
    void ItemHandler::getItem(const QString &command, const QString &url, const QString &installType, const QString &filename,
                              const QString &providerKey, const QString &contentId)
    
    akiraohgaki's avatar
    akiraohgaki committed
    {
        // Use URL as unique key for metadata, network resource, and installed item
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto itemKey = url;
    
    akiraohgaki's avatar
    akiraohgaki committed
    
        QJsonObject metadata;
    
    akiraohgaki's avatar
    akiraohgaki committed
        metadata["command"] = command;
    
    akiraohgaki's avatar
    akiraohgaki committed
        metadata["url"] = url;
        metadata["install_type"] = installType;
    
    akiraohgaki's avatar
    akiraohgaki committed
        metadata["filename"] = filename;
        if (filename.isEmpty()) {
            metadata["filename"] = QUrl(url).fileName();
        }
    
    akiraohgaki's avatar
    akiraohgaki committed
        metadata["provider"] = providerKey;
        metadata["content_id"] = contentId;
        metadata["files"] = QJsonArray();
        metadata["installed_at"] = qint64();
    
        QJsonObject result;
        result["metadata"] = metadata;
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        if (command == "install" && configHandler_->getUsrConfigInstalledItems().contains(itemKey)) {
            result["status"] = QString("error_downloadstart");
            result["message"] = tr("The item already installed");
            emit downloadStarted(result);
            return;
        }
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto itemMetadataSet = metadataSet();
    
    akiraohgaki's avatar
    akiraohgaki committed
    
        if (itemMetadataSet.contains(itemKey)) {
            result["status"] = QString("error_downloadstart");
            result["message"] = tr("The file already downloading");
            emit downloadStarted(result);
            return;
        }
    
        itemMetadataSet[itemKey] = metadata;
        setMetadataSet(itemMetadataSet);
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto *resource = new qtil::NetworkResource(itemKey, QUrl(url), true, this);
        connect(resource, &qtil::NetworkResource::downloadProgress, this, &ItemHandler::downloadProgress);
        connect(resource, &qtil::NetworkResource::finished, this, &ItemHandler::networkResourceFinished);
    
    akiraohgaki's avatar
    akiraohgaki committed
        resource->get();
    
        result["status"] = QString("success_downloadstart");
        result["message"] = tr("Downloading");
        emit downloadStarted(result);
    }
    
    
    void ItemHandler::getItemByOcsUrl(const QString &ocsUrl, const QString &providerKey, const QString &contentId)
    
    akiraohgaki's avatar
    akiraohgaki committed
    {
        QUrl ocsUrlObj(ocsUrl);
        QUrlQuery query(ocsUrlObj);
    
        QString scheme = "ocs";
        QString command = "download";
        QString url = "";
        QString type = "downloads";
        QString filename = "";
    
        if (!ocsUrlObj.scheme().isEmpty()) {
            scheme = ocsUrlObj.scheme();
        }
    
        if (!ocsUrlObj.host().isEmpty()) {
            command = ocsUrlObj.host();
        }
    
        if (query.hasQueryItem("url") && !query.queryItemValue("url").isEmpty()) {
            url = query.queryItemValue("url", QUrl::FullyDecoded);
        }
    
        if (query.hasQueryItem("type") && !query.queryItemValue("type").isEmpty()) {
            type = query.queryItemValue("type", QUrl::FullyDecoded);
        }
    
        if (query.hasQueryItem("filename") && !query.queryItemValue("filename").isEmpty()) {
            filename = QUrl(query.queryItemValue("filename", QUrl::FullyDecoded)).fileName();
        }
    
        if (!url.isEmpty() && filename.isEmpty()) {
            filename = QUrl(url).fileName();
        }
    
        // Still support xdg and xdgs schemes for backward compatibility
        if ((scheme == "ocs" || scheme == "ocss" || scheme == "xdg" || scheme == "xdgs")
                && (command == "download" || command == "install")
                && QUrl(url).isValid()
                && configHandler_->getAppConfigInstallTypes().contains(type)
                && !filename.isEmpty()) {
    
            getItem(command, url, type, filename, providerKey, contentId);
    
    akiraohgaki's avatar
    akiraohgaki committed
        }
        else {
            QJsonObject result;
            result["status"] = QString("error_downloadstart");
            result["message"] = tr("Invalid OCS-URL");
            emit downloadStarted(result);
        }
    }
    
    
    akiraohgaki's avatar
    akiraohgaki committed
    void ItemHandler::uninstall(const QString &itemKey)
    {
        QJsonObject result;
    
    akiraohgaki's avatar
    akiraohgaki committed
    
        if (!configHandler_->getUsrConfigInstalledItems().contains(itemKey)) {
            result["status"] = QString("error_uninstallstart");
            result["message"] = tr("The item not installed");
            emit uninstallStarted(result);
            return;
        }
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        result["status"] = QString("success_uninstallstart");
        result["message"] = tr("Uninstalling");
        emit uninstallStarted(result);
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto installedItem = configHandler_->getUsrConfigInstalledItems()[itemKey].toObject();
        auto installType = installedItem["install_type"].toString();
    
    akiraohgaki's avatar
    akiraohgaki committed
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        qtil::Dir destDir;
    #ifdef QTIL_OS_UNIX
    
    akiraohgaki's avatar
    akiraohgaki committed
        destDir.setPath(configHandler_->getAppConfigInstallTypes()[installType].toObject()["destination"].toString());
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        for (const auto &filename : installedItem["files"].toArray()) {
    
    akiraohgaki's avatar
    akiraohgaki committed
            QFileInfo fileInfo(destDir.path() + "/" + filename.toString());
    
            // plasmapkg: Installation process has should be saved plasmapkg into destination directory
    
    
    akiraohgaki's avatar
    akiraohgaki committed
            qtil::Package package(fileInfo.filePath());
    
    akiraohgaki's avatar
    akiraohgaki committed
    
            // Uninstall
            if (installType == "bin") {
                if (fileInfo.filePath().endsWith(".appimage", Qt::CaseInsensitive)) {
                    QProcess process;
                    process.start(fileInfo.filePath() + " --remove-appimage-desktop-integration");
                    process.waitForFinished();
                }
            }
            else if (installType == "plasma_plasmoids" || installType == "plasma4_plasmoids" || installType == "plasma5_plasmoids") {
                package.uninstallAsPlasmapkg("plasmoid");
            }
            else if (installType == "plasma_look_and_feel" || installType == "plasma5_look_and_feel") {
                package.uninstallAsPlasmapkg("lookandfeel");
            }
            else if (installType == "plasma_desktopthemes" || installType == "plasma5_desktopthemes") {
                package.uninstallAsPlasmapkg("theme");
            }
            else if (installType == "kwin_effects") {
                package.uninstallAsPlasmapkg("kwineffect");
            }
            else if (installType == "kwin_scripts") {
                package.uninstallAsPlasmapkg("kwinscript");
            }
            else if (installType == "kwin_tabbox") {
                package.uninstallAsPlasmapkg("windowswitcher");
            }
    
            // Remove file
            if (fileInfo.isDir()) {
    
    akiraohgaki's avatar
    akiraohgaki committed
                qtil::Dir(fileInfo.filePath()).remove();
    
    akiraohgaki's avatar
    akiraohgaki committed
            }
            else {
    
    akiraohgaki's avatar
    akiraohgaki committed
                qtil::File(fileInfo.filePath()).remove();
    
    akiraohgaki's avatar
    akiraohgaki committed
            }
        }
    #else
        destDir.setPath(configHandler_->getAppConfigInstallTypes()[installType].toObject()["generic_destination"].toString());
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        for (const auto &filename : installedItem["files"].toArray()) {
    
    akiraohgaki's avatar
    akiraohgaki committed
            QFileInfo fileInfo(destDir.path() + "/" + filename.toString());
            if (fileInfo.isDir()) {
    
    akiraohgaki's avatar
    akiraohgaki committed
                qtil::Dir(fileInfo.filePath()).remove();
    
    akiraohgaki's avatar
    akiraohgaki committed
            }
            else {
    
    akiraohgaki's avatar
    akiraohgaki committed
                qtil::File(fileInfo.filePath()).remove();
    
    akiraohgaki's avatar
    akiraohgaki committed
            }
        }
    #endif
    
        configHandler_->removeUsrConfigInstalledItemsItem(itemKey);
    
    akiraohgaki's avatar
    akiraohgaki committed
        configHandler_->removeUsrConfigUpdateAvailableItemsItem(itemKey);
    
    akiraohgaki's avatar
    akiraohgaki committed
    
        result["status"] = QString("success_uninstall");
        result["message"] = tr("Uninstalled");
        emit uninstallFinished(result);
    }
    
    
    akiraohgaki's avatar
    akiraohgaki committed
    void ItemHandler::networkResourceFinished(qtil::NetworkResource *resource)
    
    akiraohgaki's avatar
    akiraohgaki committed
    {
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto itemKey = resource->id();
    
    akiraohgaki's avatar
    akiraohgaki committed
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto itemMetadataSet = metadataSet();
        auto metadata = itemMetadataSet[itemKey].toObject();
    
    akiraohgaki's avatar
    akiraohgaki committed
    
        QJsonObject result;
        result["metadata"] = metadata;
    
        if (!resource->isFinishedWithNoError()) {
            itemMetadataSet.remove(itemKey);
            setMetadataSet(itemMetadataSet);
            result["status"] = QString("error_download");
            result["message"] = resource->reply()->errorString();
            emit downloadFinished(result);
            resource->deleteLater();
            return;
        }
    
        result["status"] = QString("success_download");
        result["message"] = tr("Downloaded");
        emit downloadFinished(result);
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        if (metadata["command"].toString() == "download") {
            saveDownloadedFile(resource);
        }
        else if (metadata["command"].toString() == "install") {
            installDownloadedFile(resource);
        }
    
    akiraohgaki's avatar
    akiraohgaki committed
    }
    
    void ItemHandler::setMetadataSet(const QJsonObject &metadataSet)
    {
        metadataSet_ = metadataSet;
        emit metadataSetChanged();
    }
    
    
    akiraohgaki's avatar
    akiraohgaki committed
    void ItemHandler::saveDownloadedFile(qtil::NetworkResource *resource)
    
    akiraohgaki's avatar
    akiraohgaki committed
    {
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto itemKey = resource->id();
    
    akiraohgaki's avatar
    akiraohgaki committed
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto itemMetadataSet = metadataSet();
        auto metadata = itemMetadataSet[itemKey].toObject();
    
    akiraohgaki's avatar
    akiraohgaki committed
    
        itemMetadataSet.remove(itemKey);
        setMetadataSet(itemMetadataSet);
    
        QJsonObject result;
        result["metadata"] = metadata;
        result["status"] = QString("success_savestart");
        result["message"] = tr("Saving");
        emit saveStarted(result);
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto filename = metadata["filename"].toString();
        auto installType = metadata["install_type"].toString();
    
    akiraohgaki's avatar
    akiraohgaki committed
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        qtil::Dir destDir(configHandler_->getAppConfigInstallTypes()[installType].toObject()["destination"].toString());
    
    akiraohgaki's avatar
    akiraohgaki committed
        destDir.make();
    
    akiraohgaki's avatar
    akiraohgaki committed
        qtil::File destFile(destDir.path() + "/" + filename);
    
    akiraohgaki's avatar
    akiraohgaki committed
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        if (destFile.exists()) {
            auto filenamePrefix = QString::number(QDateTime::currentMSecsSinceEpoch()) + "_";
            destFile.setPath(destDir.path() + "/" + filenamePrefix + filename);
        }
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        if (!resource->saveData(destFile.path())) {
            result["status"] = QString("error_save");
            result["message"] = tr("Failed to save data");
            emit saveFinished(result);
            resource->deleteLater();
            return;
        }
    
        result["status"] = QString("success_save");
        result["message"] = tr("Saved");
        emit saveFinished(result);
    
        resource->deleteLater();
    }
    
    
    akiraohgaki's avatar
    akiraohgaki committed
    void ItemHandler::installDownloadedFile(qtil::NetworkResource *resource)
    
    akiraohgaki's avatar
    akiraohgaki committed
    {
        // Installation pre-process
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto itemKey = resource->id();
    
    akiraohgaki's avatar
    akiraohgaki committed
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto itemMetadataSet = metadataSet();
        auto metadata = itemMetadataSet[itemKey].toObject();
    
    akiraohgaki's avatar
    akiraohgaki committed
    
        itemMetadataSet.remove(itemKey);
        setMetadataSet(itemMetadataSet);
    
        QJsonObject result;
        result["metadata"] = metadata;
    
    akiraohgaki's avatar
    akiraohgaki committed
        result["status"] = QString("success_savestart");
        result["message"] = tr("Saving");
        emit saveStarted(result);
    
    akiraohgaki's avatar
    akiraohgaki committed
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto filename = metadata["filename"].toString();
        auto installType = metadata["install_type"].toString();
    
    akiraohgaki's avatar
    akiraohgaki committed
    
    
        QString tempDirPrefix = "temp_" + filename;
    
    akiraohgaki's avatar
    akiraohgaki committed
        qtil::Dir tempDir(qtil::Dir::genericCachePath() + "/"
    
                           + configHandler_->getAppConfigApplication()["id"].toString() + "/"
                           + tempDirPrefix);
    
    akiraohgaki's avatar
    akiraohgaki committed
        tempDir.make();
    
    akiraohgaki's avatar
    akiraohgaki committed
        qtil::Dir tempDestDir(tempDir.path() + "/dest");
    
    akiraohgaki's avatar
    akiraohgaki committed
        tempDestDir.make();
    
    akiraohgaki's avatar
    akiraohgaki committed
        qtil::Package package(tempDir.path() + "/" + filename);
    
    akiraohgaki's avatar
    akiraohgaki committed
    
        if (!resource->saveData(package.path())) {
    
    akiraohgaki's avatar
    akiraohgaki committed
            result["status"] = QString("error_save");
    
    akiraohgaki's avatar
    akiraohgaki committed
            result["message"] = tr("Failed to save data");
    
    akiraohgaki's avatar
    akiraohgaki committed
            emit saveFinished(result);
    
    akiraohgaki's avatar
    akiraohgaki committed
            tempDir.remove();
            resource->deleteLater();
            return;
        }
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        result["status"] = QString("success_save");
        result["message"] = tr("Saved");
        emit saveFinished(result);
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        // Installation main-process
    
    akiraohgaki's avatar
    akiraohgaki committed
        result["status"] = QString("success_installstart");
        result["message"] = tr("Installing");
        emit installStarted(result);
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        qtil::Dir destDir;
    #ifdef QTIL_OS_UNIX
    
    akiraohgaki's avatar
    akiraohgaki committed
        destDir.setPath(configHandler_->getAppConfigInstallTypes()[installType].toObject()["destination"].toString());
    
        // plasmapkg: Need to save package to remove installed files later
    
        if (installType == "bin"
                && package.installAsProgram(tempDestDir.path() + "/" + filename)) {
            result["message"] = tr("The file has been installed as program");
        }
        else if ((installType == "plasma_plasmoids" || installType == "plasma4_plasmoids" || installType == "plasma5_plasmoids")
                 && package.installAsPlasmapkg("plasmoid")) {
            package.installAsFile(tempDestDir.path() + "/" + filename);
            result["message"] = tr("The plasmoid has been installed");
        }
        else if ((installType == "plasma_look_and_feel" || installType == "plasma5_look_and_feel")
                 && package.installAsPlasmapkg("lookandfeel")) {
            package.installAsFile(tempDestDir.path() + "/" + filename);
            result["message"] = tr("The plasma look and feel has been installed");
        }
    
    akiraohgaki's avatar
    akiraohgaki committed
        /*else if ((installType == "plasma_desktopthemes" || installType == "plasma5_desktopthemes")
    
    akiraohgaki's avatar
    akiraohgaki committed
                 && package.installAsPlasmapkg("theme")) {
            package.installAsFile(tempDestDir.path() + "/" + filename);
            result["message"] = tr("The plasma desktop theme has been installed");
    
    akiraohgaki's avatar
    akiraohgaki committed
        }*/
    
    akiraohgaki's avatar
    akiraohgaki committed
        else if (installType == "kwin_effects"
                 && package.installAsPlasmapkg("kwineffect")) {
            package.installAsFile(tempDestDir.path() + "/" + filename);
            result["message"] = tr("The KWin effect has been installed");
        }
        else if (installType == "kwin_scripts"
                 && package.installAsPlasmapkg("kwinscript")) {
            package.installAsFile(tempDestDir.path() + "/" + filename);
            result["message"] = tr("The KWin script has been installed");
        }
        else if (installType == "kwin_tabbox"
                 && package.installAsPlasmapkg("windowswitcher")) {
            package.installAsFile(tempDestDir.path() + "/" + filename);
            result["message"] = tr("The KWin window switcher has been installed");
        }
        else if (package.installAsArchive(tempDestDir.path())) {
            result["message"] = tr("The archive file has been extracted");
        }
        else if (package.installAsFile(tempDestDir.path() + "/" + filename)) {
            result["message"] = tr("The file has been installed");
        }
        else {
            result["status"] = QString("error_install");
            result["message"] = tr("Failed to installation");
            emit installFinished(result);
            tempDir.remove();
            resource->deleteLater();
            return;
        }
    #else
        destDir.setPath(configHandler_->getAppConfigInstallTypes()[installType].toObject()["generic_destination"].toString());
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        if (qtil::File(package.path()).copy(tempDestDir.path() + "/" + filename)) {
    
    akiraohgaki's avatar
    akiraohgaki committed
            result["message"] = tr("The file has been installed");
        }
        else {
            result["status"] = QString("error_install");
            result["message"] = tr("Failed to installation");
            emit installFinished(result);
            tempDir.remove();
            resource->deleteLater();
            return;
        }
    #endif
    
        destDir.make();
    
        QJsonArray installedFiles;
    
    akiraohgaki's avatar
    akiraohgaki committed
        auto filenamePrefix = QString::number(QDateTime::currentMSecsSinceEpoch()) + "_";
    
    
    akiraohgaki's avatar
    akiraohgaki committed
        for (const auto &fileInfo : tempDestDir.list()) {
    
    akiraohgaki's avatar
    akiraohgaki committed
            auto destFilename = fileInfo.fileName();
            if (QFileInfo::exists(destDir.path() + "/" + destFilename)) {
                destFilename = filenamePrefix + destFilename;
            }
    
    
    akiraohgaki's avatar
    akiraohgaki committed
            if (fileInfo.isDir()) {
    
    akiraohgaki's avatar
    akiraohgaki committed
                qtil::Dir(fileInfo.filePath()).move(destDir.path() + "/" + destFilename);
    
    akiraohgaki's avatar
    akiraohgaki committed
            }
            else {
    
    akiraohgaki's avatar
    akiraohgaki committed
                qtil::File(fileInfo.filePath()).move(destDir.path() + "/" + destFilename);
    
    akiraohgaki's avatar
    akiraohgaki committed
            }
    
    akiraohgaki's avatar
    akiraohgaki committed
    
            installedFiles.append(QJsonValue(destFilename));
    
    akiraohgaki's avatar
    akiraohgaki committed
        }
    
        // Installation post-process
    
    akiraohgaki's avatar
    akiraohgaki committed
        metadata.remove("command");
    
    akiraohgaki's avatar
    akiraohgaki committed
        metadata["files"] = installedFiles;
        metadata["installed_at"] = QDateTime::currentMSecsSinceEpoch();
        configHandler_->setUsrConfigInstalledItemsItem(itemKey, metadata);
    
        result["metadata"] = metadata;
        result["status"] = QString("success_install");
        emit installFinished(result);
    
        tempDir.remove();
        resource->deleteLater();
    }