Skip to content
Snippets Groups Projects
Forked from akiraohgaki / ocs-manager
92 commits behind the upstream repository.
itemhandler.cpp 15.13 KiB
#include "itemhandler.h"

#include <QUrlQuery>
#include <QJsonValue>
#include <QJsonArray>
#include <QFileInfo>
#include <QDateTime>

#ifdef QTIL_OS_UNIX
#include <QProcess>
#endif

#include "qtil_dir.h"
#include "qtil_file.h"
#include "qtil_networkresource.h"
#include "qtil_package.h"

#include "handlers/confighandler.h"

ItemHandler::ItemHandler(ConfigHandler *configHandler, QObject *parent)
    : QObject(parent), configHandler_(configHandler)
{}

QJsonObject ItemHandler::metadataSet() const
{
    return metadataSet_;
}

void ItemHandler::getItem(const QString &command, const QString &url, const QString &installType, const QString &filename,
                          const QString &providerKey, const QString &contentId)
{
    // Use URL as unique key for metadata, network resource, and installed item
    auto itemKey = url;

    QJsonObject metadata;
    metadata["command"] = command;
    metadata["url"] = url;
    metadata["install_type"] = installType;
    metadata["filename"] = filename;
    if (filename.isEmpty()) {
        metadata["filename"] = QUrl(url).fileName();
    }
    metadata["provider"] = providerKey;
    metadata["content_id"] = contentId;
    metadata["files"] = QJsonArray();
    metadata["installed_at"] = qint64();

    QJsonObject result;
    result["metadata"] = metadata;

    if (command == "install" && configHandler_->getUsrConfigInstalledItems().contains(itemKey)) {
        result["status"] = QString("error_downloadstart");
        result["message"] = tr("The item already installed");
        emit downloadStarted(result);
        return;
    }

    auto itemMetadataSet = metadataSet();

    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);

    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);
    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)
{
    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();
    }

    if ((scheme == "ocs" || scheme == "ocss")
            && (command == "download" || command == "install")
            && QUrl(url).isValid()
            && configHandler_->getAppConfigInstallTypes().contains(type)
            && !filename.isEmpty()) {
        getItem(command, url, type, filename, providerKey, contentId);
    }
    else {
        QJsonObject result;
        result["status"] = QString("error_downloadstart");
        result["message"] = tr("Invalid OCS-URL");
        emit downloadStarted(result);
    }
}

void ItemHandler::uninstall(const QString &itemKey)
{
    QJsonObject result;

    if (!configHandler_->getUsrConfigInstalledItems().contains(itemKey)) {
        result["status"] = QString("error_uninstallstart");
        result["message"] = tr("The item not installed");
        emit uninstallStarted(result);
        return;
    }
    result["status"] = QString("success_uninstallstart");
    result["message"] = tr("Uninstalling");
    emit uninstallStarted(result);

    auto installedItem = configHandler_->getUsrConfigInstalledItems()[itemKey].toObject();
    auto installType = installedItem["install_type"].toString();

    qtil::Dir destDir;
#ifdef QTIL_OS_UNIX
    destDir.setPath(configHandler_->getAppConfigInstallTypes()[installType].toObject()["destination"].toString());

    for (const auto &filename : installedItem["files"].toArray()) {
        QFileInfo fileInfo(destDir.path() + "/" + filename.toString());

        // plasmapkg: Installation process has should be saved plasmapkg into destination directory

        qtil::Package package(fileInfo.filePath());

        // 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()) {
            qtil::Dir(fileInfo.filePath()).remove();
        }
        else {
            qtil::File(fileInfo.filePath()).remove();
        }
    }
#else
    destDir.setPath(configHandler_->getAppConfigInstallTypes()[installType].toObject()["generic_destination"].toString());

    for (const auto &filename : installedItem["files"].toArray()) {
        QFileInfo fileInfo(destDir.path() + "/" + filename.toString());
        if (fileInfo.isDir()) {
            qtil::Dir(fileInfo.filePath()).remove();
        }
        else {
            qtil::File(fileInfo.filePath()).remove();
        }
    }
#endif

    configHandler_->removeUsrConfigInstalledItemsItem(itemKey);
    configHandler_->removeUsrConfigUpdateAvailableItemsItem(itemKey);
    result["status"] = QString("success_uninstall");
    result["message"] = tr("Uninstalled");
    emit uninstallFinished(result);
}

void ItemHandler::networkResourceFinished(qtil::NetworkResource *resource)
{
    auto itemKey = resource->id();

    auto itemMetadataSet = metadataSet();
    auto metadata = itemMetadataSet[itemKey].toObject();

    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);

    if (metadata["command"].toString() == "download") {
        saveDownloadedFile(resource);
    }
    else if (metadata["command"].toString() == "install") {
        installDownloadedFile(resource);
    }
}

void ItemHandler::setMetadataSet(const QJsonObject &metadataSet)
{
    metadataSet_ = metadataSet;
    emit metadataSetChanged();
}

void ItemHandler::saveDownloadedFile(qtil::NetworkResource *resource)
{
    auto itemKey = resource->id();

    auto itemMetadataSet = metadataSet();
    auto metadata = itemMetadataSet[itemKey].toObject();

    itemMetadataSet.remove(itemKey);
    setMetadataSet(itemMetadataSet);

    QJsonObject result;
    result["metadata"] = metadata;
    result["status"] = QString("success_savestart");
    result["message"] = tr("Saving");
    emit saveStarted(result);

    auto filename = metadata["filename"].toString();
    auto installType = metadata["install_type"].toString();

    qtil::Dir destDir(configHandler_->getAppConfigInstallTypes()[installType].toObject()["destination"].toString());
    destDir.make();
    qtil::File destFile(destDir.path() + "/" + filename);

    if (destFile.exists()) {
        auto filenamePrefix = QString::number(QDateTime::currentMSecsSinceEpoch()) + "_";
        destFile.setPath(destDir.path() + "/" + filenamePrefix + filename);
    }

    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();
}

void ItemHandler::installDownloadedFile(qtil::NetworkResource *resource)
{
    // Installation pre-process
    auto itemKey = resource->id();

    auto itemMetadataSet = metadataSet();
    auto metadata = itemMetadataSet[itemKey].toObject();

    itemMetadataSet.remove(itemKey);
    setMetadataSet(itemMetadataSet);

    QJsonObject result;
    result["metadata"] = metadata;
    result["status"] = QString("success_savestart");
    result["message"] = tr("Saving");
    emit saveStarted(result);

    auto filename = metadata["filename"].toString();
    auto installType = metadata["install_type"].toString();

    QString tempDirPrefix = "temp_" + filename;
    qtil::Dir tempDir(qtil::Dir::genericCachePath() + "/"
                       + configHandler_->getAppConfigApplication()["id"].toString() + "/"
                       + tempDirPrefix);
    tempDir.make();
    qtil::Dir tempDestDir(tempDir.path() + "/dest");
    tempDestDir.make();
    qtil::Package package(tempDir.path() + "/" + filename);

    if (!resource->saveData(package.path())) {
        result["status"] = QString("error_save");
        result["message"] = tr("Failed to save data");
        emit saveFinished(result);
        tempDir.remove();
        resource->deleteLater();
        return;
    }

    result["status"] = QString("success_save");
    result["message"] = tr("Saved");
    emit saveFinished(result);

    // Installation main-process
    result["status"] = QString("success_installstart");
    result["message"] = tr("Installing");
    emit installStarted(result);

    qtil::Dir destDir;
#ifdef QTIL_OS_UNIX
    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");
    }
    /*else if ((installType == "plasma_desktopthemes" || installType == "plasma5_desktopthemes")
             && package.installAsPlasmapkg("theme")) {
        package.installAsFile(tempDestDir.path() + "/" + filename);
        result["message"] = tr("The plasma desktop theme has been installed");
    }*/
    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());

    if (qtil::File(package.path()).copy(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;
    }
#endif

    destDir.make();

    QJsonArray installedFiles;
    auto filenamePrefix = QString::number(QDateTime::currentMSecsSinceEpoch()) + "_";

    for (const auto &fileInfo : tempDestDir.list()) {
        auto destFilename = fileInfo.fileName();
        if (QFileInfo::exists(destDir.path() + "/" + destFilename)) {
            destFilename = filenamePrefix + destFilename;
        }

        if (fileInfo.isDir()) {
            qtil::Dir(fileInfo.filePath()).move(destDir.path() + "/" + destFilename);
        }
        else {
            qtil::File(fileInfo.filePath()).move(destDir.path() + "/" + destFilename);
        }

        installedFiles.append(QJsonValue(destFilename));
    }

    // Installation post-process
    metadata.remove("command");
    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();
}