File indexing completed on 2024-05-12 16:24:52
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"