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