Warning, file /network/falkon/src/lib/plugins/ocssupport.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* ============================================================
0002 * Falkon - Qt web browser
0003 * Copyright (C) 2019 David Rosca <nowrep@gmail.com>
0004 *
0005 * This program is free software: you can redistribute it and/or modify
0006 * it under the terms of the GNU General Public License as published by
0007 * the Free Software Foundation, either version 3 of the License, or
0008 * (at your option) any later version.
0009 *
0010 * This program is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 * GNU General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU General Public License
0016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017 * ============================================================ */
0018 #include "ocssupport.h"
0019 #include "pluginproxy.h"
0020 #include "datapaths.h"
0021 #include "networkmanager.h"
0022 #include "desktopfile.h"
0023 #include "desktopnotificationsfactory.h"
0024 #include "mainapplication.h"
0025 
0026 #include <QDir>
0027 #include <QBuffer>
0028 #include <QUrlQuery>
0029 #include <QNetworkReply>
0030 
0031 #include <KZip>
0032 
0033 Q_GLOBAL_STATIC(OcsSupport, qz_ocs_support)
0034 
0035 static DesktopFile readMetaData(const KArchiveDirectory *directory)
0036 {
0037     const KArchiveEntry *entry = directory->entry(QSL("metadata.desktop"));
0038     if (!entry || !entry->isFile()) {
0039         qWarning() << "No metadata.desktop found";
0040         return {};
0041     }
0042     const QString tempDir = DataPaths::path(DataPaths::Temp);
0043     static_cast<const KArchiveFile*>(entry)->copyTo(tempDir);
0044     return DesktopFile(tempDir + QL1S("/metadata.desktop"));
0045 }
0046 
0047 OcsSupport::OcsSupport(QObject *parent)
0048     : QObject(parent)
0049 {
0050 }
0051 
0052 bool OcsSupport::handleUrl(const QUrl &url)
0053 {
0054     if (url.host() != QL1S("install")) {
0055         return false;
0056     }
0057 
0058     QUrl fileUrl;
0059     QString fileType;
0060     QString fileName;
0061 
0062     const auto items = QUrlQuery(url).queryItems(QUrl::FullyDecoded);
0063     for (const auto &item : items) {
0064         if (item.first == QL1S("url")) {
0065             fileUrl = QUrl(item.second);
0066         } else if (item.first == QL1S("type")) {
0067             fileType = item.second;
0068         } else if (item.first == QL1S("filename")) {
0069             fileName = item.second;
0070         }
0071     }
0072 
0073     if (!fileType.startsWith(QL1S("falkon_"))) {
0074         return false;
0075     }
0076 
0077     if (fileType != QL1S("falkon_themes") && fileType != QL1S("falkon_extensions")) {
0078         qWarning() << "Unsupported type" << fileType;
0079         return false;
0080     }
0081 
0082     if (!fileUrl.isValid()) {
0083         qWarning() << "Invalid url" << fileUrl << url;
0084         return false;
0085     }
0086 
0087     qInfo() << "Downloading" << fileUrl;
0088 
0089     QNetworkReply *reply = mApp->networkManager()->get(QNetworkRequest(fileUrl));
0090     connect(reply, &QNetworkReply::finished, this, [=]() {
0091         reply->deleteLater();
0092         if (reply->error() != QNetworkReply::NoError) {
0093             qWarning() << "Error downloading" << fileUrl << reply->error() << reply->errorString();
0094             return;
0095         }
0096         QBuffer buf;
0097         buf.setData(reply->readAll());
0098         KZip zip(&buf);
0099         if (!zip.open(QIODevice::ReadOnly)) {
0100             qWarning() << "Failed to open archive";
0101             return;
0102         }
0103         QString notifyMessage;
0104         if (fileType == QL1S("falkon_themes")) {
0105             installTheme(zip.directory());
0106         } else if (fileType == QL1S("falkon_extensions")) {
0107             installExtension(zip.directory());
0108         }
0109     });
0110 
0111     return true;
0112 }
0113 
0114 // static
0115 OcsSupport *OcsSupport::instance()
0116 {
0117     return qz_ocs_support();
0118 }
0119 
0120 void OcsSupport::installTheme(const KArchiveDirectory *directory)
0121 {
0122     auto showError = []() {
0123         mApp->desktopNotifications()->showNotification(tr("Installation failed"), tr("Failed to install theme"));
0124     };
0125 
0126     if (directory->entries().size() != 1) {
0127         qWarning() << "Invalid archive format";
0128         showError();
0129         return;
0130     }
0131 
0132     const QString name = directory->entries().at(0);
0133     const KArchiveEntry *entry = directory->entry(name);
0134     if (!entry || !entry->isDirectory()) {
0135         qWarning() << "Invalid archive format";
0136         showError();
0137         return;
0138     }
0139 
0140     const DesktopFile metaData = readMetaData(static_cast<const KArchiveDirectory*>(entry));
0141 
0142     const QString targetDir = DataPaths::path(DataPaths::Config) + QL1S("/themes");
0143     QDir().mkpath(targetDir);
0144 
0145     if (QFileInfo::exists(targetDir + QL1C('/') + name)) {
0146         qWarning() << "Theme" << name << "already exists";
0147         mApp->desktopNotifications()->showNotification(tr("Installation failed"), tr("Theme is already installed"));
0148         return;
0149     }
0150 
0151     if (!directory->copyTo(targetDir)) {
0152         qWarning() << "Failed to copy theme to" << targetDir;
0153         showError();
0154         return;
0155     }
0156 
0157     qInfo() << "Theme installed to" << targetDir;
0158 
0159     mApp->desktopNotifications()->showNotification(tr("Theme installed"), tr("'%1' was successfully installed").arg(metaData.name()));
0160 }
0161 
0162 void OcsSupport::installExtension(const KArchiveDirectory *directory)
0163 {
0164     auto showError = []() {
0165         mApp->desktopNotifications()->showNotification(tr("Installation failed"), tr("Failed to install extension"));
0166     };
0167 
0168     if (directory->entries().size() != 1) {
0169         qWarning() << "Invalid archive format";
0170         showError();
0171         return;
0172     }
0173 
0174     const QString name = directory->entries().at(0);
0175     const KArchiveEntry *entry = directory->entry(name);
0176     if (!entry || !entry->isDirectory()) {
0177         qWarning() << "Invalid archive format";
0178         showError();
0179         return;
0180     }
0181 
0182     const DesktopFile metaData = readMetaData(static_cast<const KArchiveDirectory*>(entry));
0183     const QString extensionType = metaData.value(QSL("X-Falkon-Type")).toString();
0184 
0185     QString type;
0186     if (extensionType == QL1S("Extension/Python")) {
0187         type = QSL("python");
0188     } else if (extensionType == QL1S("Extension/Qml")) {
0189         type = QSL("qml");
0190     }
0191 
0192     if (type.isEmpty()) {
0193         qWarning() << "Unsupported extension type" << extensionType;
0194         showError();
0195         return;
0196     }
0197 
0198     const QString targetDir = DataPaths::path(DataPaths::Config) + QL1S("/plugins/");
0199     QDir().mkpath(targetDir);
0200 
0201     if (QFileInfo::exists(targetDir + QL1S("/") + name)) {
0202         qWarning() << "Extension" << name << "already exists";
0203         mApp->desktopNotifications()->showNotification(tr("Installation failed"), tr("Extension is already installed"));
0204         return;
0205     }
0206 
0207     if (!directory->copyTo(targetDir)) {
0208         qWarning() << "Failed to copy extension to" << targetDir;
0209         showError();
0210         return;
0211     }
0212 
0213     qInfo() << "Extension installed to" << targetDir;
0214 
0215     const QString fullId = QSL("%1:%2/%3").arg(type, targetDir, name);
0216     if (!mApp->plugins()->addPlugin(fullId)) {
0217         qWarning() << "Failed to add plugin" << fullId;
0218         showError();
0219         return;
0220     }
0221 
0222     mApp->desktopNotifications()->showNotification(tr("Extension installed"), tr("'%1' was successfully installed").arg(metaData.name()));
0223 }