1 /*
2 This file is part of KDE.
3
4 SPDX-FileCopyrightText: 2009 Eckhart Wörner <ewoerner@kde.org>
5 SPDX-FileCopyrightText: 2009 Frederik Gladhorn <gladhorn@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
8 */
9
10 #include "providermanager.h"
11
12 #include "attica_debug.h"
13 #include "atticautils.h"
14
15 #include <QAuthenticator>
16 #include <QCoreApplication>
17 #include <QDebug>
18 #include <QFile>
19 #include <QNetworkProxy>
20 #include <QPluginLoader>
21 #include <QProcess>
22 #include <QSet>
23 #include <QSignalMapper>
24 #include <QTimer>
25 #include <QXmlStreamReader>
26
27 #include "platformdependent.h"
28 #include "qtplatformdependent_p.h"
29 #include <QLibraryInfo>
30
31 using namespace Attica;
32
33 class ProviderManager::Private
34 {
35 public:
36 PlatformDependent *m_internals;
37 QHash<QUrl, Provider> m_providers;
38 QHash<QUrl, QUrl> m_providerTargets;
39 QHash<QString, QNetworkReply *> m_downloads;
40 bool m_authenticationSuppressed;
41
Private()42 Private()
43 : m_internals(nullptr)
44 , m_authenticationSuppressed(false)
45 {
46 }
~Private()47 ~Private()
48 {
49 // do not delete m_internals: it is the root component of a plugin!
50 }
51 };
52
loadPlatformDependent(const ProviderFlags & flags)53 PlatformDependent *ProviderManager::loadPlatformDependent(const ProviderFlags &flags)
54 {
55 if (flags & ProviderManager::DisablePlugins) {
56 return new QtPlatformDependent;
57 }
58
59 QPluginLoader loader(QStringLiteral("attica_kde"));
60 PlatformDependent *ret = qobject_cast<PlatformDependent *>(loader.instance());
61
62 return ret ? ret : new QtPlatformDependent;
63 }
64
ProviderManager(const ProviderFlags & flags)65 ProviderManager::ProviderManager(const ProviderFlags &flags)
66 : d(new Private)
67 {
68 d->m_internals = loadPlatformDependent(flags);
69 connect(d->m_internals->nam(), &QNetworkAccessManager::authenticationRequired, this, &ProviderManager::authenticate);
70 }
71
loadDefaultProviders()72 void ProviderManager::loadDefaultProviders()
73 {
74 QTimer::singleShot(0, this, &ProviderManager::slotLoadDefaultProvidersInternal);
75 }
76
setAuthenticationSuppressed(bool suppressed)77 void ProviderManager::setAuthenticationSuppressed(bool suppressed)
78 {
79 d->m_authenticationSuppressed = suppressed;
80 }
81
clear()82 void ProviderManager::clear()
83 {
84 d->m_providerTargets.clear();
85 d->m_providers.clear();
86 }
87
slotLoadDefaultProvidersInternal()88 void ProviderManager::slotLoadDefaultProvidersInternal()
89 {
90 const auto providerFiles = d->m_internals->getDefaultProviderFiles();
91 for (const QUrl &url : providerFiles) {
92 addProviderFile(url);
93 }
94 if (d->m_downloads.isEmpty()) {
95 Q_EMIT defaultProvidersLoaded();
96 }
97 }
98
defaultProviderFiles()99 QList<QUrl> ProviderManager::defaultProviderFiles()
100 {
101 return d->m_internals->getDefaultProviderFiles();
102 }
103
~ProviderManager()104 ProviderManager::~ProviderManager()
105 {
106 delete d;
107 }
108
addProviderFileToDefaultProviders(const QUrl & url)109 void ProviderManager::addProviderFileToDefaultProviders(const QUrl &url)
110 {
111 d->m_internals->addDefaultProviderFile(url);
112 addProviderFile(url);
113 }
114
removeProviderFileFromDefaultProviders(const QUrl & url)115 void ProviderManager::removeProviderFileFromDefaultProviders(const QUrl &url)
116 {
117 d->m_internals->removeDefaultProviderFile(url);
118 }
119
addProviderFile(const QUrl & url)120 void ProviderManager::addProviderFile(const QUrl &url)
121 {
122 if (url.isLocalFile()) {
123 QFile file(url.toLocalFile());
124 if (!file.open(QIODevice::ReadOnly)) {
125 qWarning() << "ProviderManager::addProviderFile: could not open provider file: " << url.toString();
126 return;
127 }
128 parseProviderFile(QLatin1String(file.readAll()), url);
129 } else {
130 if (!d->m_downloads.contains(url.toString())) {
131 QNetworkRequest req(url);
132 req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
133 QNetworkReply *reply = d->m_internals->get(req);
134 qCDebug(ATTICA) << "executing" << Utils::toString(reply->operation()) << "for" << reply->url();
135 connect(reply, &QNetworkReply::finished, this, [this, url]() {
136 fileFinished(url.toString());
137 });
138 d->m_downloads.insert(url.toString(), reply);
139 }
140 }
141 }
142
fileFinished(const QString & url)143 void ProviderManager::fileFinished(const QString &url)
144 {
145 QNetworkReply *reply = d->m_downloads.take(url);
146 if (reply) {
147 if (reply->error()) {
148 Q_EMIT failedToLoad(QUrl(url), reply->error());
149 } else {
150 parseProviderFile(QLatin1String(reply->readAll()), QUrl(url));
151 }
152 reply->deleteLater();
153 } else {
154 Q_EMIT failedToLoad(QUrl(url), QNetworkReply::UnknownNetworkError);
155 }
156 }
157
addProviderFromXml(const QString & providerXml)158 void ProviderManager::addProviderFromXml(const QString &providerXml)
159 {
160 parseProviderFile(providerXml, QUrl());
161 }
162
parseProviderFile(const QString & xmlString,const QUrl & url)163 void ProviderManager::parseProviderFile(const QString &xmlString, const QUrl &url)
164 {
165 QXmlStreamReader xml(xmlString);
166 while (!xml.atEnd() && xml.readNext()) {
167 if (xml.isStartElement() && xml.name() == QLatin1String("provider")) {
168 QUrl baseUrl;
169 QString name;
170 QUrl icon;
171 QString person;
172 QString friendV;
173 QString message;
174 QString achievement;
175 QString activity;
176 QString content;
177 QString fan;
178 QString forum;
179 QString knowledgebase;
180 QString event;
181 QString comment;
182 QString registerUrl;
183
184 while (!xml.atEnd() && xml.readNext()) {
185 if (xml.isStartElement()) {
186 if (xml.name() == QLatin1String("location")) {
187 baseUrl = QUrl(xml.readElementText());
188 } else if (xml.name() == QLatin1String("name")) {
189 name = xml.readElementText();
190 } else if (xml.name() == QLatin1String("icon")) {
191 icon = QUrl(xml.readElementText());
192 } else if (xml.name() == QLatin1String("person")) {
193 person = xml.attributes().value(QLatin1String("ocsversion")).toString();
194 } else if (xml.name() == QLatin1String("friend")) {
195 friendV = xml.attributes().value(QLatin1String("ocsversion")).toString();
196 } else if (xml.name() == QLatin1String("message")) {
197 message = xml.attributes().value(QLatin1String("ocsversion")).toString();
198 } else if (xml.name() == QLatin1String("achievement")) {
199 achievement = xml.attributes().value(QLatin1String("ocsversion")).toString();
200 } else if (xml.name() == QLatin1String("activity")) {
201 activity = xml.attributes().value(QLatin1String("ocsversion")).toString();
202 } else if (xml.name() == QLatin1String("content")) {
203 content = xml.attributes().value(QLatin1String("ocsversion")).toString();
204 } else if (xml.name() == QLatin1String("fan")) {
205 fan = xml.attributes().value(QLatin1String("ocsversion")).toString();
206 } else if (xml.name() == QLatin1String("forum")) {
207 forum = xml.attributes().value(QLatin1String("ocsversion")).toString();
208 } else if (xml.name() == QLatin1String("knowledgebase")) {
209 knowledgebase = xml.attributes().value(QLatin1String("ocsversion")).toString();
210 } else if (xml.name() == QLatin1String("event")) {
211 event = xml.attributes().value(QLatin1String("ocsversion")).toString();
212 } else if (xml.name() == QLatin1String("comment")) {
213 comment = xml.attributes().value(QLatin1String("ocsversion")).toString();
214 } else if (xml.name() == QLatin1String("register")) {
215 registerUrl = xml.readElementText();
216 }
217 } else if (xml.isEndElement() && xml.name() == QLatin1String("provider")) {
218 break;
219 }
220 }
221 if (!baseUrl.isEmpty()) {
222 // qCDebug(ATTICA) << "Adding provider" << baseUrl;
223 d->m_providers.insert(baseUrl,
224 Provider(d->m_internals,
225 baseUrl,
226 name,
227 icon,
228 person,
229 friendV,
230 message,
231 achievement,
232 activity,
233 content,
234 fan,
235 forum,
236 knowledgebase,
237 event,
238 comment,
239 registerUrl));
240 d->m_providerTargets[url] = baseUrl;
241 Q_EMIT providerAdded(d->m_providers.value(baseUrl));
242 }
243 }
244 }
245
246 if (xml.error() != QXmlStreamReader::NoError) {
247 qCDebug(ATTICA) << "error:" << xml.errorString() << "in" << url;
248 }
249
250 if (d->m_downloads.isEmpty()) {
251 Q_EMIT defaultProvidersLoaded();
252 }
253 }
254
providerFor(const QUrl & url) const255 Provider ProviderManager::providerFor(const QUrl &url) const
256 {
257 return providerByUrl(d->m_providerTargets.value(url));
258 }
259
providerByUrl(const QUrl & url) const260 Provider ProviderManager::providerByUrl(const QUrl &url) const
261 {
262 return d->m_providers.value(url);
263 }
264
providers() const265 QList<Provider> ProviderManager::providers() const
266 {
267 return d->m_providers.values();
268 }
269
270 #if ATTICA_BUILD_DEPRECATED_SINCE(5, 23)
contains(const QString & provider) const271 bool ProviderManager::contains(const QString &provider) const
272 {
273 return d->m_providers.contains(QUrl(provider));
274 }
275 #endif
276
providerFiles() const277 QList<QUrl> ProviderManager::providerFiles() const
278 {
279 return d->m_providerTargets.keys();
280 }
281
authenticate(QNetworkReply * reply,QAuthenticator * auth)282 void ProviderManager::authenticate(QNetworkReply *reply, QAuthenticator *auth)
283 {
284 QUrl baseUrl;
285 const QList<QUrl> urls = d->m_providers.keys();
286 for (const QUrl &url : urls) {
287 if (url.isParentOf(reply->url())) {
288 baseUrl = url;
289 break;
290 }
291 }
292
293 // qCDebug(ATTICA) << "ProviderManager::authenticate" << baseUrl;
294
295 QString user;
296 QString password;
297 if (auth->user().isEmpty() && auth->password().isEmpty()) {
298 if (d->m_internals->hasCredentials(baseUrl)) {
299 if (d->m_internals->loadCredentials(baseUrl, user, password)) {
300 // qCDebug(ATTICA) << "ProviderManager::authenticate: loading authentication";
301 auth->setUser(user);
302 auth->setPassword(password);
303 return;
304 }
305 }
306 }
307
308 if (!d->m_authenticationSuppressed && d->m_internals->askForCredentials(baseUrl, user, password)) {
309 // qCDebug(ATTICA) << "ProviderManager::authenticate: asking internals for new credentials";
310 // auth->setUser(user);
311 // auth->setPassword(password);
312 return;
313 }
314
315 qWarning() << "ProviderManager::authenticate: No authentication credentials provided, aborting." << reply->url().toString();
316 Q_EMIT authenticationCredentialsMissing(d->m_providers.value(baseUrl));
317 reply->abort();
318 }
319
proxyAuthenticationRequired(const QNetworkProxy & proxy,QAuthenticator * authenticator)320 void ProviderManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
321 {
322 Q_UNUSED(proxy)
323 Q_UNUSED(authenticator)
324 }
325
initNetworkAccesssManager()326 void ProviderManager::initNetworkAccesssManager()
327 {
328 connect(d->m_internals->nam(), &QNetworkAccessManager::authenticationRequired, this, &ProviderManager::authenticate);
329 connect(d->m_internals->nam(), &QNetworkAccessManager::proxyAuthenticationRequired, this, &ProviderManager::proxyAuthenticationRequired);
330 }
331