Falkon Develop
Cross-platform Qt-based web browser
ocssupport.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2019 David Rosca <nowrep@gmail.com>
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17* ============================================================ */
18#include "ocssupport.h"
19#include "pluginproxy.h"
20#include "datapaths.h"
21#include "networkmanager.h"
22#include "desktopfile.h"
24#include "mainapplication.h"
25
26#include <QDir>
27#include <QBuffer>
28#include <QUrlQuery>
29#include <QNetworkReply>
30
31#include <KZip>
32
33Q_GLOBAL_STATIC(OcsSupport, qz_ocs_support)
34
35static DesktopFile readMetaData(const KArchiveDirectory *directory)
36{
37 const KArchiveEntry *entry = directory->entry(QSL("metadata.desktop"));
38 if (!entry || !entry->isFile()) {
39 qWarning() << "No metadata.desktop found";
40 return {};
41 }
42 const QString tempDir = DataPaths::path(DataPaths::Temp);
43 static_cast<const KArchiveFile*>(entry)->copyTo(tempDir);
44 return DesktopFile(tempDir + QL1S("/metadata.desktop"));
45}
46
47OcsSupport::OcsSupport(QObject *parent)
48 : QObject(parent)
49{
50}
51
52bool OcsSupport::handleUrl(const QUrl &url)
53{
54 if (url.host() != QL1S("install")) {
55 return false;
56 }
57
58 QUrl fileUrl;
59 QString fileType;
60 QString fileName;
61
62 const auto items = QUrlQuery(url).queryItems(QUrl::FullyDecoded);
63 for (const auto &item : items) {
64 if (item.first == QL1S("url")) {
65 fileUrl = QUrl(item.second);
66 } else if (item.first == QL1S("type")) {
67 fileType = item.second;
68 } else if (item.first == QL1S("filename")) {
69 fileName = item.second;
70 }
71 }
72
73 if (!fileType.startsWith(QL1S("falkon_"))) {
74 return false;
75 }
76
77 if (fileType != QL1S("falkon_themes") && fileType != QL1S("falkon_extensions")) {
78 qWarning() << "Unsupported type" << fileType;
79 return false;
80 }
81
82 if (!fileUrl.isValid()) {
83 qWarning() << "Invalid url" << fileUrl << url;
84 return false;
85 }
86
87 qInfo() << "Downloading" << fileUrl;
88
89 QNetworkReply *reply = mApp->networkManager()->get(QNetworkRequest(fileUrl));
90 connect(reply, &QNetworkReply::finished, this, [=]() {
91 reply->deleteLater();
92 if (reply->error() != QNetworkReply::NoError) {
93 qWarning() << "Error downloading" << fileUrl << reply->error() << reply->errorString();
94 return;
95 }
96 QBuffer buf;
97 buf.setData(reply->readAll());
98 KZip zip(&buf);
99 if (!zip.open(QIODevice::ReadOnly)) {
100 qWarning() << "Failed to open archive";
101 return;
102 }
103 QString notifyMessage;
104 if (fileType == QL1S("falkon_themes")) {
105 installTheme(zip.directory());
106 } else if (fileType == QL1S("falkon_extensions")) {
107 installExtension(zip.directory());
108 }
109 });
110
111 return true;
112}
113
114// static
116{
117 return qz_ocs_support();
118}
119
120void OcsSupport::installTheme(const KArchiveDirectory *directory)
121{
122 auto showError = []() {
123 mApp->desktopNotifications()->showNotification(tr("Installation failed"), tr("Failed to install theme"));
124 };
125
126 if (directory->entries().size() != 1) {
127 qWarning() << "Invalid archive format";
128 showError();
129 return;
130 }
131
132 const QString name = directory->entries().at(0);
133 const KArchiveEntry *entry = directory->entry(name);
134 if (!entry || !entry->isDirectory()) {
135 qWarning() << "Invalid archive format";
136 showError();
137 return;
138 }
139
140 const DesktopFile metaData = readMetaData(static_cast<const KArchiveDirectory*>(entry));
141
142 const QString targetDir = DataPaths::path(DataPaths::Config) + QL1S("/themes");
143 QDir().mkpath(targetDir);
144
145 if (QFileInfo::exists(targetDir + QL1C('/') + name)) {
146 qWarning() << "Theme" << name << "already exists";
147 mApp->desktopNotifications()->showNotification(tr("Installation failed"), tr("Theme is already installed"));
148 return;
149 }
150
151 if (!directory->copyTo(targetDir)) {
152 qWarning() << "Failed to copy theme to" << targetDir;
153 showError();
154 return;
155 }
156
157 qInfo() << "Theme installed to" << targetDir;
158
159 mApp->desktopNotifications()->showNotification(tr("Theme installed"), tr("'%1' was successfully installed").arg(metaData.name()));
160}
161
162void OcsSupport::installExtension(const KArchiveDirectory *directory)
163{
164 auto showError = []() {
165 mApp->desktopNotifications()->showNotification(tr("Installation failed"), tr("Failed to install extension"));
166 };
167
168 if (directory->entries().size() != 1) {
169 qWarning() << "Invalid archive format";
170 showError();
171 return;
172 }
173
174 const QString name = directory->entries().at(0);
175 const KArchiveEntry *entry = directory->entry(name);
176 if (!entry || !entry->isDirectory()) {
177 qWarning() << "Invalid archive format";
178 showError();
179 return;
180 }
181
182 const DesktopFile metaData = readMetaData(static_cast<const KArchiveDirectory*>(entry));
183 const QString extensionType = metaData.value(QSL("X-Falkon-Type")).toString();
184
185 QString type;
186 if (extensionType == QL1S("Extension/Python")) {
187 type = QSL("python");
188 } else if (extensionType == QL1S("Extension/Qml")) {
189 type = QSL("qml");
190 }
191
192 if (type.isEmpty()) {
193 qWarning() << "Unsupported extension type" << extensionType;
194 showError();
195 return;
196 }
197
198 const QString targetDir = DataPaths::path(DataPaths::Config) + QL1S("/plugins/");
199 QDir().mkpath(targetDir);
200
201 if (QFileInfo::exists(targetDir + QL1S("/") + name)) {
202 qWarning() << "Extension" << name << "already exists";
203 mApp->desktopNotifications()->showNotification(tr("Installation failed"), tr("Extension is already installed"));
204 return;
205 }
206
207 if (!directory->copyTo(targetDir)) {
208 qWarning() << "Failed to copy extension to" << targetDir;
209 showError();
210 return;
211 }
212
213 qInfo() << "Extension installed to" << targetDir;
214
215 const QString fullId = QSL("%1:%2/%3").arg(type, targetDir, name);
216 if (!mApp->plugins()->addPlugin(fullId)) {
217 qWarning() << "Failed to add plugin" << fullId;
218 showError();
219 return;
220 }
221
222 mApp->desktopNotifications()->showNotification(tr("Extension installed"), tr("'%1' was successfully installed").arg(metaData.name()));
223}
static QString path(Path type)
Definition: datapaths.cpp:66
QVariant value(const QString &key, bool localized=false) const
Definition: desktopfile.cpp:58
QString name() const
Definition: desktopfile.cpp:38
static OcsSupport * instance()
Definition: ocssupport.cpp:115
bool handleUrl(const QUrl &url)
Definition: ocssupport.cpp:52
OcsSupport(QObject *parent=nullptr)
Definition: ocssupport.cpp:47
#define mApp
#define QL1S(x)
Definition: qzcommon.h:44
#define QL1C(x)
Definition: qzcommon.h:48
#define QSL(x)
Definition: qzcommon.h:40