File indexing completed on 2024-05-12 08:59:04

0001 /*
0002  *  SPDX-FileCopyrightText: 2013 Alejandro Fiestas Olivares <afiestas@kde.org>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kioservices.h"
0008 #include "core.h"
0009 #include "getcredentialsjob.h"
0010 
0011 #include <QApplication>
0012 #include <QDebug>
0013 #include <QDirIterator>
0014 #include <QFile>
0015 #include <QStandardPaths>
0016 #include <QWidget>
0017 
0018 #include <KConfig>
0019 #include <KConfigGroup>
0020 #include <KDirNotify>
0021 #include <KIO/TransferJob>
0022 #include <KLocalizedString>
0023 #include <KPluginFactory>
0024 #include <KWallet>
0025 
0026 #include <Accounts/Manager>
0027 
0028 using namespace KWallet;
0029 
0030 K_PLUGIN_CLASS_WITH_JSON(KIOServices, "kio-webdav.json")
0031 
0032 KIOServices::KIOServices(QObject *parent, const QVariantList &args)
0033     : KAccounts::KAccountsDPlugin(parent, args)
0034 {
0035 }
0036 
0037 KIOServices::~KIOServices() = default;
0038 
0039 void KIOServices::onAccountCreated(const Accounts::AccountId accId, const Accounts::ServiceList &serviceList)
0040 {
0041     qDebug();
0042     for (const Accounts::Service &service : serviceList) {
0043         if (service.serviceType() != QLatin1String("dav-storage")) {
0044             qDebug() << "Ignoring: " << service.serviceType();
0045             continue;
0046         }
0047         if (isEnabled(accId, service.name())) {
0048             qDebug() << "Already configured: " << service.name();
0049             continue;
0050         }
0051 
0052         qDebug() << "Creating: " << service.name() << "Of type: " << service.serviceType();
0053         enableService(accId, service);
0054     }
0055 }
0056 
0057 void KIOServices::onAccountRemoved(const Accounts::AccountId accId)
0058 {
0059     qDebug();
0060     const QString accountId = QString::number(accId) + QStringLiteral("_");
0061 
0062     const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/remoteview/");
0063 
0064     QDirIterator i(path, QDir::NoDotAndDotDot | QDir::Files);
0065     while (i.hasNext()) {
0066         i.next();
0067         if (!i.fileName().startsWith(accountId)) {
0068             continue;
0069         }
0070 
0071         QString serviceName = i.fileName();
0072         qDebug() << "Removing: " << serviceName;
0073         serviceName = serviceName.mid(accountId.count(), serviceName.indexOf(QLatin1String(".desktop")) - accountId.count());
0074         qDebug() << "Removing N: " << serviceName;
0075         disableService(accId, serviceName);
0076     }
0077 }
0078 
0079 void KIOServices::onServiceEnabled(const Accounts::AccountId accId, const Accounts::Service &service)
0080 {
0081     if (service.serviceType() != QLatin1String("dav-storage")) {
0082         qDebug() << "Ignoring: " << service.serviceType();
0083         return;
0084     }
0085     if (isEnabled(accId, service.name())) {
0086         qDebug() << "Already configured: " << service.name();
0087         return;
0088     }
0089 
0090     enableService(accId, service);
0091 }
0092 
0093 void KIOServices::onServiceDisabled(const Accounts::AccountId accId, const Accounts::Service &service)
0094 {
0095     if (service.serviceType() != QLatin1String("dav-storage")) {
0096         qDebug() << "Ignoring: " << service.serviceType();
0097         return;
0098     }
0099     if (!isEnabled(accId, service.name())) {
0100         qDebug() << "Already not configured: " << service.name();
0101         return;
0102     }
0103 
0104     disableService(accId, service.name());
0105 }
0106 
0107 void KIOServices::enableService(const Accounts::AccountId accId, const Accounts::Service &service)
0108 {
0109     createNetAttach(accId, service);
0110 }
0111 
0112 void KIOServices::disableService(const Accounts::AccountId accId, const QString &serviceName)
0113 {
0114     removeNetAttach(QString::number(accId) + QStringLiteral("_") + serviceName);
0115 }
0116 
0117 bool KIOServices::isEnabled(const Accounts::AccountId accId, const QString &serviceName)
0118 {
0119     const QString uniqueId(QString::number(accId) + QStringLiteral("_") + serviceName);
0120 
0121     QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
0122     path += QStringLiteral("/remoteview/") + uniqueId + QStringLiteral(".desktop");
0123 
0124     return QFile::exists(path);
0125 }
0126 
0127 QCoro::Task<void> KIOServices::createNetAttach(const Accounts::AccountId accountId, const Accounts::Service &_service)
0128 {
0129     std::unique_ptr<Accounts::Account> account(KAccounts::accountsManager()->account(accountId));
0130     const Accounts::Service service = _service;
0131 
0132     KAccounts::GetCredentialsJob *job = new KAccounts::GetCredentialsJob(accountId, QString(), QString(), this);
0133     job->setServiceType(service.serviceType());
0134     job->start();
0135 
0136     co_await qCoro(job, &KJob::finished);
0137 
0138     if (job->error()) {
0139         co_return;
0140     }
0141 
0142     const QVariantMap credentials = job->credentialsData();
0143 
0144     const QString host = account->value(QStringLiteral("dav/host")).toString();
0145     const QString path = account->value(QStringLiteral("dav/storagePath")).toString();
0146 
0147     account->selectService(service);
0148     const QString username = credentials[QStringLiteral("UserName")].toString();
0149 
0150     WId windowId = 0;
0151     if (qApp->activeWindow()) {
0152         windowId = qApp->activeWindow()->winId();
0153     }
0154 
0155     auto wallet = Wallet::openWallet(Wallet::NetworkWallet(), windowId, Wallet::Asynchronous);
0156 
0157     bool opened = co_await qCoro(wallet, &KWallet::Wallet::walletOpened);
0158 
0159     if (!opened) {
0160         co_return;
0161     }
0162 
0163     QUrl url;
0164     url.setHost(host);
0165     url.setUserName(username);
0166     url.setScheme(QStringLiteral("webdavs"));
0167     url.setPath(path);
0168 
0169     const QString realm = co_await getRealm(url);
0170 
0171     const QString folderPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/remoteview/");
0172 
0173     QDir saveDir(folderPath);
0174     if (!saveDir.exists()) {
0175         if (!saveDir.mkpath(folderPath)) {
0176             qWarning() << "Directory" << folderPath << "for storage couldn't be created!";
0177         }
0178     }
0179     const QString desktopFilePath = folderPath + QString::number(accountId) + QLatin1Char('_') + service.name() + QStringLiteral(".desktop");
0180 
0181     qDebug() << "Creating knetAttach place";
0182     qDebug() << desktopFilePath;
0183     qDebug() << url.host();
0184     qDebug() << url.toString();
0185 
0186     KConfig _desktopFile(desktopFilePath, KConfig::SimpleConfig);
0187     KConfigGroup desktopFile(&_desktopFile, QStringLiteral("Desktop Entry"));
0188 
0189     const QString label = KAccounts::accountsManager()->provider(service.provider()).displayName() + QLatin1Char(' ') + service.displayName();
0190 
0191     desktopFile.writeEntry("Icon", service.iconName());
0192     desktopFile.writeEntry("Name", label);
0193     desktopFile.writeEntry("Type", "Link");
0194     desktopFile.writeEntry("URL", url.toString());
0195     //     desktopFile.writeEntry("Charset", url.fileEncoding());
0196     desktopFile.sync();
0197 
0198     QString walletUrl(url.scheme());
0199     walletUrl.append(QStringLiteral("-"));
0200     walletUrl.append(username);
0201     walletUrl.append(QStringLiteral("@"));
0202     walletUrl.append(url.host());
0203     walletUrl.append(QStringLiteral(":-1-")); // Overwrite the first option
0204 
0205     QMap<QString, QString> info;
0206     info[QStringLiteral("login")] = username;
0207     info[QStringLiteral("password")] = credentials[QStringLiteral("Secret")].toString();
0208 
0209     wallet->setFolder(QStringLiteral("Passwords"));
0210 
0211     if (!realm.isEmpty()) {
0212         wallet->writeMap(walletUrl + realm, info);
0213     }
0214     wallet->writeMap(walletUrl + QStringLiteral("webdav"), info);
0215     wallet->sync();
0216 
0217     org::kde::KDirNotify::emitFilesAdded(QUrl(QStringLiteral("remote:/")));
0218 }
0219 
0220 QCoro::Task<QString> KIOServices::getRealm(const QUrl &url)
0221 {
0222     KIO::TransferJob *job = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo);
0223     KIO::MetaData data;
0224     data.insert(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true"));
0225     job->setMetaData(data);
0226     job->setUiDelegate(nullptr);
0227     job->start();
0228 
0229     co_await qCoro(job, &KJob::finished);
0230 
0231     QRegularExpression rx(QStringLiteral("www-authenticate: Basic realm=\"([^\"]+)\""));
0232     Q_ASSERT(rx.isValid());
0233     QString headers = job->metaData().value(QStringLiteral("HTTP-Headers"));
0234 
0235     auto match = rx.match(headers);
0236 
0237     if (match.hasMatch()) {
0238         co_return match.captured(1);
0239     }
0240 
0241     co_return QString();
0242 }
0243 
0244 QCoro::Task<void> KIOServices::removeNetAttach(const QString &id)
0245 {
0246     WId windowId = 0;
0247     if (qApp->activeWindow()) {
0248         windowId = qApp->activeWindow()->winId();
0249     }
0250     std::unique_ptr<KWallet::Wallet> wallet(Wallet::openWallet(Wallet::NetworkWallet(), windowId, Wallet::Asynchronous));
0251 
0252     bool opened = co_await qCoro(wallet.get(), &KWallet::Wallet::walletOpened);
0253 
0254     if (!opened) {
0255         co_return;
0256     }
0257 
0258     QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
0259     path.append(QStringLiteral("/remoteview/") + id + QStringLiteral(".desktop"));
0260 
0261     KConfig _desktopFile(path, KConfig::SimpleConfig);
0262     KConfigGroup desktopFile(&_desktopFile, QStringLiteral("Desktop Entry"));
0263 
0264     const QUrl url(desktopFile.readEntry("URL", QUrl()));
0265     Q_ASSERT(!url.isEmpty());
0266 
0267     qDebug() << url.userName() << url.host() << url;
0268 
0269     QFile::remove(path);
0270     org::kde::KDirNotify::emitFilesRemoved(QList<QUrl>() << QUrl(QStringLiteral("remote:/") + id));
0271 
0272     QString walletUrl(QStringLiteral("webdav"));
0273     walletUrl.append(QStringLiteral("-"));
0274     walletUrl.append(url.userName());
0275     walletUrl.append(QStringLiteral("@"));
0276     walletUrl.append(url.host());
0277     walletUrl.append(QStringLiteral(":-1")); // Overwrite the first option
0278 
0279     wallet->setFolder(QStringLiteral("Passwords"));
0280     const QStringList entries = wallet->entryList();
0281     for (const QString &entry : entries) {
0282         if (!entry.startsWith(walletUrl)) {
0283             continue;
0284         }
0285         wallet->removeEntry(entry);
0286     }
0287 }
0288 
0289 #include "kioservices.moc"