File indexing completed on 2023-09-24 07:57:29

0001 /*
0002     This file is part of KDE.
0003 
0004     SPDX-FileCopyrightText: 2009 Eckhart W├Ârner <ewoerner@kde.org>
0005     SPDX-FileCopyrightText: 2009 Frederik Gladhorn <gladhorn@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0008 */
0009 
0010 #include "providermanager.h"
0011 
0012 #include "attica_debug.h"
0013 #include "atticautils.h"
0014 
0015 #include <QAuthenticator>
0016 #include <QCoreApplication>
0017 #include <QDebug>
0018 #include <QFile>
0019 #include <QNetworkProxy>
0020 #include <QPluginLoader>
0021 #include <QSet>
0022 #include <QSignalMapper>
0023 #include <QTimer>
0024 #include <QXmlStreamReader>
0025 
0026 #include "platformdependent.h"
0027 #include "qtplatformdependent_p.h"
0028 #include <QLibraryInfo>
0029 
0030 using namespace Attica;
0031 
0032 class Q_DECL_HIDDEN ProviderManager::Private
0033 {
0034 public:
0035     PlatformDependent *m_internals;
0036     QHash<QUrl, Provider> m_providers;
0037     QHash<QUrl, QUrl> m_providerTargets;
0038     QHash<QString, QNetworkReply *> m_downloads;
0039     bool m_authenticationSuppressed;
0040 
0041     Private()
0042         : m_internals(nullptr)
0043         , m_authenticationSuppressed(false)
0044     {
0045     }
0046     ~Private()
0047     {
0048         // do not delete m_internals: it is the root component of a plugin!
0049     }
0050 };
0051 
0052 PlatformDependent *ProviderManager::loadPlatformDependent(const ProviderFlags &flags)
0053 {
0054     if (flags & ProviderManager::DisablePlugins) {
0055         return new QtPlatformDependent;
0056     }
0057 
0058     QPluginLoader loader(QStringLiteral("attica_kde"));
0059     PlatformDependent *ret = qobject_cast<PlatformDependent *>(loader.instance());
0060 
0061     return ret ? ret : new QtPlatformDependent;
0062 }
0063 
0064 ProviderManager::ProviderManager(const ProviderFlags &flags)
0065     : d(new Private)
0066 {
0067     d->m_internals = loadPlatformDependent(flags);
0068     connect(d->m_internals->nam(), &QNetworkAccessManager::authenticationRequired, this, &ProviderManager::authenticate);
0069 }
0070 
0071 void ProviderManager::loadDefaultProviders()
0072 {
0073     QTimer::singleShot(0, this, &ProviderManager::slotLoadDefaultProvidersInternal);
0074 }
0075 
0076 void ProviderManager::setAuthenticationSuppressed(bool suppressed)
0077 {
0078     d->m_authenticationSuppressed = suppressed;
0079 }
0080 
0081 void ProviderManager::clear()
0082 {
0083     d->m_providerTargets.clear();
0084     d->m_providers.clear();
0085 }
0086 
0087 void ProviderManager::slotLoadDefaultProvidersInternal()
0088 {
0089     const auto providerFiles = d->m_internals->getDefaultProviderFiles();
0090     for (const QUrl &url : providerFiles) {
0091         addProviderFile(url);
0092     }
0093     if (d->m_downloads.isEmpty()) {
0094         Q_EMIT defaultProvidersLoaded();
0095     }
0096 }
0097 
0098 QList<QUrl> ProviderManager::defaultProviderFiles()
0099 {
0100     return d->m_internals->getDefaultProviderFiles();
0101 }
0102 
0103 ProviderManager::~ProviderManager()
0104 {
0105     delete d;
0106 }
0107 
0108 void ProviderManager::addProviderFileToDefaultProviders(const QUrl &url)
0109 {
0110     d->m_internals->addDefaultProviderFile(url);
0111     addProviderFile(url);
0112 }
0113 
0114 void ProviderManager::removeProviderFileFromDefaultProviders(const QUrl &url)
0115 {
0116     d->m_internals->removeDefaultProviderFile(url);
0117 }
0118 
0119 void ProviderManager::addProviderFile(const QUrl &url)
0120 {
0121     if (url.isLocalFile()) {
0122         QFile file(url.toLocalFile());
0123         if (!file.open(QIODevice::ReadOnly)) {
0124             qWarning() << "ProviderManager::addProviderFile: could not open provider file: " << url.toString();
0125             return;
0126         }
0127         parseProviderFile(QLatin1String(file.readAll()), url);
0128     } else {
0129         if (!d->m_downloads.contains(url.toString())) {
0130             QNetworkRequest req(url);
0131             req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
0132             QNetworkReply *reply = d->m_internals->get(req);
0133             qCDebug(ATTICA) << "executing" << Utils::toString(reply->operation()) << "for" << reply->url();
0134             connect(reply, &QNetworkReply::finished, this, [this, url]() {
0135                 fileFinished(url.toString());
0136             });
0137             d->m_downloads.insert(url.toString(), reply);
0138         }
0139     }
0140 }
0141 
0142 void ProviderManager::fileFinished(const QString &url)
0143 {
0144     QNetworkReply *reply = d->m_downloads.take(url);
0145     if (reply) {
0146         if (reply->error()) {
0147             Q_EMIT failedToLoad(QUrl(url), reply->error());
0148         } else {
0149             parseProviderFile(QLatin1String(reply->readAll()), QUrl(url));
0150         }
0151         reply->deleteLater();
0152     } else {
0153         Q_EMIT failedToLoad(QUrl(url), QNetworkReply::UnknownNetworkError);
0154     }
0155 }
0156 
0157 void ProviderManager::addProviderFromXml(const QString &providerXml)
0158 {
0159     parseProviderFile(providerXml, QUrl());
0160 }
0161 
0162 void ProviderManager::parseProviderFile(const QString &xmlString, const QUrl &url)
0163 {
0164     QXmlStreamReader xml(xmlString);
0165     while (!xml.atEnd() && xml.readNext()) {
0166         if (xml.isStartElement() && xml.name() == QLatin1String("provider")) {
0167             QUrl baseUrl;
0168             QString name;
0169             QUrl icon;
0170             QString person;
0171             QString friendV;
0172             QString message;
0173             QString achievement;
0174             QString activity;
0175             QString content;
0176             QString fan;
0177             QString forum;
0178             QString knowledgebase;
0179             QString event;
0180             QString comment;
0181             QString registerUrl;
0182 
0183             while (!xml.atEnd() && xml.readNext()) {
0184                 if (xml.isStartElement()) {
0185                     if (xml.name() == QLatin1String("location")) {
0186                         baseUrl = QUrl(xml.readElementText());
0187                     } else if (xml.name() == QLatin1String("name")) {
0188                         name = xml.readElementText();
0189                     } else if (xml.name() == QLatin1String("icon")) {
0190                         icon = QUrl(xml.readElementText());
0191                     } else if (xml.name() == QLatin1String("person")) {
0192                         person = xml.attributes().value(QLatin1String("ocsversion")).toString();
0193                     } else if (xml.name() == QLatin1String("friend")) {
0194                         friendV = xml.attributes().value(QLatin1String("ocsversion")).toString();
0195                     } else if (xml.name() == QLatin1String("message")) {
0196                         message = xml.attributes().value(QLatin1String("ocsversion")).toString();
0197                     } else if (xml.name() == QLatin1String("achievement")) {
0198                         achievement = xml.attributes().value(QLatin1String("ocsversion")).toString();
0199                     } else if (xml.name() == QLatin1String("activity")) {
0200                         activity = xml.attributes().value(QLatin1String("ocsversion")).toString();
0201                     } else if (xml.name() == QLatin1String("content")) {
0202                         content = xml.attributes().value(QLatin1String("ocsversion")).toString();
0203                     } else if (xml.name() == QLatin1String("fan")) {
0204                         fan = xml.attributes().value(QLatin1String("ocsversion")).toString();
0205                     } else if (xml.name() == QLatin1String("forum")) {
0206                         forum = xml.attributes().value(QLatin1String("ocsversion")).toString();
0207                     } else if (xml.name() == QLatin1String("knowledgebase")) {
0208                         knowledgebase = xml.attributes().value(QLatin1String("ocsversion")).toString();
0209                     } else if (xml.name() == QLatin1String("event")) {
0210                         event = xml.attributes().value(QLatin1String("ocsversion")).toString();
0211                     } else if (xml.name() == QLatin1String("comment")) {
0212                         comment = xml.attributes().value(QLatin1String("ocsversion")).toString();
0213                     } else if (xml.name() == QLatin1String("register")) {
0214                         registerUrl = xml.readElementText();
0215                     }
0216                 } else if (xml.isEndElement() && xml.name() == QLatin1String("provider")) {
0217                     break;
0218                 }
0219             }
0220             if (!baseUrl.isEmpty()) {
0221                 // qCDebug(ATTICA) << "Adding provider" << baseUrl;
0222                 d->m_providers.insert(baseUrl,
0223                                       Provider(d->m_internals,
0224                                                baseUrl,
0225                                                name,
0226                                                icon,
0227                                                person,
0228                                                friendV,
0229                                                message,
0230                                                achievement,
0231                                                activity,
0232                                                content,
0233                                                fan,
0234                                                forum,
0235                                                knowledgebase,
0236                                                event,
0237                                                comment,
0238                                                registerUrl));
0239                 d->m_providerTargets[url] = baseUrl;
0240                 Q_EMIT providerAdded(d->m_providers.value(baseUrl));
0241             }
0242         }
0243     }
0244 
0245     if (xml.error() != QXmlStreamReader::NoError) {
0246         qCDebug(ATTICA) << "error:" << xml.errorString() << "in" << url;
0247     }
0248 
0249     if (d->m_downloads.isEmpty()) {
0250         Q_EMIT defaultProvidersLoaded();
0251     }
0252 }
0253 
0254 Provider ProviderManager::providerFor(const QUrl &url) const
0255 {
0256     return providerByUrl(d->m_providerTargets.value(url));
0257 }
0258 
0259 Provider ProviderManager::providerByUrl(const QUrl &url) const
0260 {
0261     return d->m_providers.value(url);
0262 }
0263 
0264 QList<Provider> ProviderManager::providers() const
0265 {
0266     return d->m_providers.values();
0267 }
0268 
0269 #if ATTICA_BUILD_DEPRECATED_SINCE(5, 23)
0270 bool ProviderManager::contains(const QString &provider) const
0271 {
0272     return d->m_providers.contains(QUrl(provider));
0273 }
0274 #endif
0275 
0276 QList<QUrl> ProviderManager::providerFiles() const
0277 {
0278     return d->m_providerTargets.keys();
0279 }
0280 
0281 void ProviderManager::authenticate(QNetworkReply *reply, QAuthenticator *auth)
0282 {
0283     QUrl baseUrl;
0284     const QList<QUrl> urls = d->m_providers.keys();
0285     for (const QUrl &url : urls) {
0286         if (url.isParentOf(reply->url())) {
0287             baseUrl = url;
0288             break;
0289         }
0290     }
0291 
0292     // qCDebug(ATTICA) << "ProviderManager::authenticate" << baseUrl;
0293 
0294     QString user;
0295     QString password;
0296     if (auth->user().isEmpty() && auth->password().isEmpty()) {
0297         if (d->m_internals->hasCredentials(baseUrl)) {
0298             if (d->m_internals->loadCredentials(baseUrl, user, password)) {
0299                 // qCDebug(ATTICA) << "ProviderManager::authenticate: loading authentication";
0300                 auth->setUser(user);
0301                 auth->setPassword(password);
0302                 return;
0303             }
0304         }
0305     }
0306 
0307     if (!d->m_authenticationSuppressed && d->m_internals->askForCredentials(baseUrl, user, password)) {
0308         // qCDebug(ATTICA) << "ProviderManager::authenticate: asking internals for new credentials";
0309         // auth->setUser(user);
0310         // auth->setPassword(password);
0311         return;
0312     }
0313 
0314     qWarning() << "ProviderManager::authenticate: No authentication credentials provided, aborting." << reply->url().toString();
0315     Q_EMIT authenticationCredentialsMissing(d->m_providers.value(baseUrl));
0316     reply->abort();
0317 }
0318 
0319 void ProviderManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
0320 {
0321     Q_UNUSED(proxy)
0322     Q_UNUSED(authenticator)
0323 }
0324 
0325 void ProviderManager::initNetworkAccesssManager()
0326 {
0327     connect(d->m_internals->nam(), &QNetworkAccessManager::authenticationRequired, this, &ProviderManager::authenticate);
0328     connect(d->m_internals->nam(), &QNetworkAccessManager::proxyAuthenticationRequired, this, &ProviderManager::proxyAuthenticationRequired);
0329 }
0330 
0331 #include "moc_providermanager.cpp"