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