File indexing completed on 2024-04-28 09:29:22
0001 /* 0002 * SPDX-FileCopyrightText: 2017, 2018, 2019 Ivan Cukic <ivan.cukic (at) kde.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "service.h" 0008 0009 #include <QDBusObjectPath> 0010 #include <QMessageBox> 0011 #include <QProcess> 0012 0013 #include <PlasmaActivities/Consumer> 0014 #include <KApplicationTrader> 0015 #include <KIO/ApplicationLauncherJob> 0016 #include <KLocalizedString> 0017 #include <KPasswordDialog> 0018 #include <KPluginFactory> 0019 #include <KService> 0020 0021 #include "engine/commandresult.h" 0022 #include "engine/vault.h" 0023 0024 #include "ui/mountdialog.h" 0025 #include "ui/vaultconfigurationdialog.h" 0026 #include "ui/vaultcreationwizard.h" 0027 #include "ui/vaultimportingwizard.h" 0028 0029 #include <functional> 0030 0031 #include <asynqt/operations/listen.h> 0032 0033 #include <config-plasma-vault.h> 0034 #if HAVE_NETWORKMANAGER 0035 #include <NetworkManagerQt/Manager> 0036 #else 0037 namespace NetworkManager 0038 { 0039 bool isNetworkingEnabled() 0040 { 0041 return true; 0042 } 0043 0044 void setNetworkingEnabled(bool enabled) 0045 { 0046 Q_UNUSED(enabled); 0047 } 0048 } 0049 #endif 0050 0051 K_PLUGIN_FACTORY_WITH_JSON(PlasmaVaultServiceFactory, "plasmavault.json", registerPlugin<PlasmaVaultService>();) 0052 0053 using namespace PlasmaVault; 0054 0055 using AsynQt::Expected; 0056 0057 class PlasmaVaultService::Private 0058 { 0059 public: 0060 QHash<Device, Vault *> knownVaults; 0061 QSet<Device> openVaults; 0062 KActivities::Consumer kamd; 0063 0064 struct NetworkingState { 0065 bool wasNetworkingEnabled; 0066 QList<QString> devicesInhibittingNetworking; 0067 }; 0068 // Ideally, this would be std::optional... lovely C++17 0069 Expected<NetworkingState, int> savedNetworkingState = Expected<NetworkingState, int>::error(0); 0070 0071 void saveNetworkingState() 0072 { 0073 // Ignore the request if we already have a saved state 0074 if (savedNetworkingState) { 0075 return; 0076 } 0077 0078 savedNetworkingState = Expected<NetworkingState, int>::success(NetworkingState{NetworkManager::isNetworkingEnabled() || true, {}}); 0079 } 0080 0081 void restoreNetworkingState() 0082 { 0083 // Ignore the request if we do not have a state saved 0084 // or if there are more devices inhibitting networking 0085 if (!savedNetworkingState || !savedNetworkingState->devicesInhibittingNetworking.isEmpty()) { 0086 return; 0087 } 0088 0089 NetworkManager::setNetworkingEnabled(savedNetworkingState->wasNetworkingEnabled); 0090 } 0091 0092 Vault *vaultFor(const QString &device_) const 0093 { 0094 const Device device(device_); 0095 0096 if (!knownVaults.contains(device)) { 0097 return nullptr; 0098 } 0099 0100 return knownVaults[device]; 0101 } 0102 }; 0103 0104 PlasmaVaultService::PlasmaVaultService(QObject *parent, const QVariantList &) 0105 : KDEDModule(parent) 0106 , d(new Private()) 0107 { 0108 connect(this, &KDEDModule::moduleRegistered, this, &PlasmaVaultService::slotRegistered); 0109 0110 // Close vaults that don't belong to the current activity 0111 connect(&d->kamd, &KActivities::Consumer::currentActivityChanged, this, &PlasmaVaultService::onCurrentActivityChanged); 0112 0113 // When an activity is deleted, remove it from all the vaults 0114 connect(&d->kamd, &KActivities::Consumer::activityRemoved, this, &PlasmaVaultService::onActivityRemoved); 0115 0116 // When activities are loaded, remove activities that no longer exist 0117 // the vaults 0118 connect(&d->kamd, &KActivities::Consumer::activitiesChanged, this, &PlasmaVaultService::onActivitiesChanged); 0119 0120 for (const Device &device : Vault::availableDevices()) { 0121 registerVault(new Vault(device, this)); 0122 } 0123 0124 onActivitiesChanged(d->kamd.activities()); 0125 } 0126 0127 PlasmaVaultService::~PlasmaVaultService() 0128 { 0129 } 0130 0131 PlasmaVault::VaultInfoList PlasmaVaultService::availableDevices() const 0132 { 0133 PlasmaVault::VaultInfoList result; 0134 for (const auto &vault : d->knownVaults.values()) { 0135 result << vault->info(); 0136 } 0137 return result; 0138 } 0139 0140 void PlasmaVaultService::requestNewVault() 0141 { 0142 const auto dialog = new VaultCreationWizard(); 0143 0144 connect(dialog, &VaultCreationWizard::createdVault, this, &PlasmaVaultService::registerVault); 0145 0146 dialog->show(); 0147 } 0148 0149 void PlasmaVaultService::requestImportVault() 0150 { 0151 const auto dialog = new VaultImportingWizard(); 0152 0153 connect(dialog, &VaultImportingWizard::importedVault, this, &PlasmaVaultService::registerVault); 0154 0155 dialog->show(); 0156 } 0157 0158 void PlasmaVaultService::slotRegistered(const QDBusObjectPath &path) 0159 { 0160 if (path.path() == QLatin1String("/modules/plasmavault")) { 0161 Q_EMIT registered(); 0162 } 0163 } 0164 0165 void PlasmaVaultService::registerVault(Vault *vault) 0166 { 0167 if (!vault->isValid()) { 0168 qWarning() << "Warning: Trying to register an invalid vault: " << vault->device().data(); 0169 return; 0170 } 0171 0172 if (d->knownVaults.contains(vault->device())) { 0173 qWarning() << "Warning: This one is already registered: " << vault->device().data(); 0174 return; 0175 } 0176 0177 vault->setParent(this); 0178 0179 d->knownVaults[vault->device()] = vault; 0180 0181 connect(vault, &Vault::statusChanged, this, &PlasmaVaultService::onVaultStatusChanged); 0182 connect(vault, &Vault::messageChanged, this, &PlasmaVaultService::onVaultMessageChanged); 0183 connect(vault, &Vault::infoChanged, this, &PlasmaVaultService::onVaultInfoChanged); 0184 0185 Q_EMIT vaultAdded(vault->info()); 0186 0187 if (vault->status() == VaultInfo::Opened) { 0188 d->openVaults << vault->device(); 0189 } 0190 } 0191 0192 void PlasmaVaultService::forgetVault(Vault *vault) 0193 { 0194 // Can not be open 0195 // d->openVaults.remove(vault.device()); 0196 // and therefore can not inhibit networking 0197 // ... d->savedNetworkingState ... 0198 0199 Q_EMIT vaultRemoved(vault->device().data()); 0200 0201 d->knownVaults.remove(vault->device()); 0202 vault->deleteLater(); 0203 } 0204 0205 void PlasmaVaultService::onVaultStatusChanged(VaultInfo::Status status) 0206 { 0207 const auto vault = static_cast<Vault *>(sender()); 0208 0209 if (status == VaultInfo::Dismantled) { 0210 forgetVault(vault); 0211 0212 } else if (status == VaultInfo::Opened) { 0213 d->openVaults << vault->device(); 0214 if (d->openVaults.size() == 1) { 0215 Q_EMIT hasOpenVaultsChanged(true); 0216 } 0217 0218 } else { 0219 d->openVaults.remove(vault->device()); 0220 if (d->openVaults.isEmpty()) { 0221 Q_EMIT hasOpenVaultsChanged(false); 0222 } 0223 } 0224 0225 if (vault->isOfflineOnly()) { 0226 d->saveNetworkingState(); 0227 auto &devicesInhibittingNetworking = d->savedNetworkingState->devicesInhibittingNetworking; 0228 0229 // We need to check whether this vault 0230 // should be added or removed from the 0231 // inhibitors list 0232 const bool alreadyInhibiting = devicesInhibittingNetworking.contains(vault->device().data()); 0233 0234 if (status == VaultInfo::Opened && !alreadyInhibiting) { 0235 auto deviceOpeningHandle = "{opening}" + vault->device().data(); 0236 devicesInhibittingNetworking.removeAll(deviceOpeningHandle); 0237 devicesInhibittingNetworking << vault->device().data(); 0238 } 0239 0240 if (status != VaultInfo::Opened && alreadyInhibiting) { 0241 devicesInhibittingNetworking.removeAll(vault->device().data()); 0242 } 0243 0244 // Now, let's handle the networking part 0245 if (!devicesInhibittingNetworking.isEmpty()) { 0246 NetworkManager::setNetworkingEnabled(false); 0247 } 0248 0249 d->restoreNetworkingState(); 0250 } 0251 0252 Q_EMIT vaultChanged(vault->info()); 0253 } 0254 0255 void PlasmaVaultService::onVaultInfoChanged() 0256 { 0257 const auto vault = static_cast<Vault *>(sender()); 0258 Q_EMIT vaultChanged(vault->info()); 0259 } 0260 0261 void PlasmaVaultService::onVaultMessageChanged(const QString &message) 0262 { 0263 Q_UNUSED(message); 0264 const auto vault = static_cast<Vault *>(sender()); 0265 Q_EMIT vaultChanged(vault->info()); 0266 } 0267 0268 template<typename OnAccepted, typename OnRejected> 0269 void showPasswordMountDialog(Vault *vault, OnAccepted onAccepted, OnRejected onRejected) 0270 { 0271 auto dialog = new MountDialog(vault); 0272 0273 QObject::connect(dialog, &QDialog::accepted, vault, onAccepted); 0274 QObject::connect(dialog, &QDialog::rejected, vault, onRejected); 0275 0276 dialog->open(); 0277 } 0278 //^ 0279 0280 void PlasmaVaultService::openVault(const QString &device) 0281 { 0282 if (auto vault = d->vaultFor(device)) { 0283 if (vault->isOpened()) 0284 return; 0285 0286 if (vault->isOfflineOnly()) { 0287 d->saveNetworkingState(); 0288 0289 auto &devicesInhibittingNetworking = d->savedNetworkingState->devicesInhibittingNetworking; 0290 auto deviceOpeningHandle = "{opening}" + vault->device().data(); 0291 0292 // We need to check whether this vault 0293 // should be added or removed from the 0294 // inhibitors list 0295 const bool alreadyInhibiting = devicesInhibittingNetworking.contains(deviceOpeningHandle); 0296 0297 if (!alreadyInhibiting) { 0298 devicesInhibittingNetworking << deviceOpeningHandle; 0299 } 0300 0301 NetworkManager::setNetworkingEnabled(false); 0302 } 0303 0304 auto stopInhibiting = [this, vault] { 0305 if (d->savedNetworkingState) { 0306 auto &devicesInhibittingNetworking = d->savedNetworkingState->devicesInhibittingNetworking; 0307 auto deviceOpeningHandle = "{opening}" + vault->device().data(); 0308 devicesInhibittingNetworking.removeAll(deviceOpeningHandle); 0309 } 0310 }; 0311 0312 showPasswordMountDialog( 0313 vault, 0314 [this, vault, stopInhibiting] { 0315 Q_EMIT vaultChanged(vault->info()); 0316 stopInhibiting(); 0317 }, 0318 [this, vault, stopInhibiting] { 0319 stopInhibiting(); 0320 if (vault->status() != VaultInfo::Opened) { 0321 d->restoreNetworkingState(); 0322 } 0323 }); 0324 } 0325 } 0326 0327 void PlasmaVaultService::closeVault(const QString &device) 0328 { 0329 if (auto vault = d->vaultFor(device)) { 0330 if (!vault->isOpened()) 0331 return; 0332 0333 vault->close(); 0334 } 0335 } 0336 0337 void PlasmaVaultService::configureVault(const QString &device) 0338 { 0339 if (auto vault = d->vaultFor(device)) { 0340 const auto dialog = new VaultConfigurationDialog(vault); 0341 0342 dialog->show(); 0343 } 0344 } 0345 0346 void PlasmaVaultService::forceCloseVault(const QString &device) 0347 { 0348 if (auto vault = d->vaultFor(device)) { 0349 if (!vault->isOpened()) 0350 return; 0351 0352 vault->forceClose(); 0353 } 0354 } 0355 0356 void PlasmaVaultService::openVaultInFileManager(const QString &device) 0357 { 0358 auto openFileManager = [this](const auto &vault) { 0359 KService::Ptr service = KApplicationTrader::preferredService(QStringLiteral("inode/directory")); 0360 0361 // A hack to always open a new dolphin window 0362 // BUG: 445542 0363 // https://bugs.kde.org/show_bug.cgi?id=445542 0364 if (service->desktopEntryName() == QStringLiteral("org.kde.dolphin")) { 0365 service->setExec(service->exec() + QStringLiteral(" --new-window")); 0366 } 0367 0368 auto *job = new KIO::ApplicationLauncherJob(service, this); 0369 job->setUrls({QUrl::fromLocalFile((QString)vault->mountPoint().data())}); 0370 job->start(); 0371 }; 0372 0373 if (auto vault = d->vaultFor(device)) { 0374 if (vault->isOpened()) { 0375 openFileManager(vault); 0376 0377 } else { 0378 showPasswordMountDialog( 0379 vault, 0380 [this, vault, openFileManager] { 0381 Q_EMIT vaultChanged(vault->info()); 0382 openFileManager(vault); 0383 }, 0384 [this, vault] { 0385 if (vault->status() != VaultInfo::Opened && d->savedNetworkingState) { 0386 auto &devicesInhibittingNetworking = d->savedNetworkingState->devicesInhibittingNetworking; 0387 devicesInhibittingNetworking.removeAll(vault->device().data()); 0388 d->restoreNetworkingState(); 0389 } 0390 }); 0391 } 0392 } 0393 } 0394 0395 bool PlasmaVaultService::hasOpenVaults() const 0396 { 0397 return !d->openVaults.isEmpty(); 0398 } 0399 0400 void PlasmaVaultService::closeAllVaults() 0401 { 0402 for (const auto &device : d->openVaults) { 0403 closeVault(device.data()); 0404 } 0405 } 0406 0407 void PlasmaVaultService::forceCloseAllVaults() 0408 { 0409 for (const auto &device : d->openVaults) { 0410 forceCloseVault(device.data()); 0411 } 0412 } 0413 0414 void PlasmaVaultService::deleteVault(const QString &device, const QString &name) 0415 { 0416 if (!d->knownVaults.contains(Device(device))) { 0417 qWarning() << "The specified vault does not exist: " << device; 0418 return; 0419 } 0420 0421 auto vault = d->knownVaults[Device(device)]; 0422 0423 if (vault->status() == VaultInfo::Opened) { 0424 qWarning() << "Can not delete an open vault: " << device; 0425 return; 0426 } 0427 0428 if (vault->name() != name) { 0429 qWarning() << "Name is not correct: " << device; 0430 return; 0431 } 0432 0433 AsynQt::onFinished(vault->dismantle({}), [](const auto &future) { 0434 const auto &result = future.result(); 0435 if (result) 0436 return; 0437 0438 const auto &error = result.error(); 0439 if (error.code() != Error::OperationCancelled) { 0440 QMessageBox::critical(nullptr, i18n("Error deleting vault"), error.message()); 0441 } 0442 }); 0443 } 0444 0445 void PlasmaVaultService::updateStatus() 0446 { 0447 for (const auto &device : d->knownVaults.keys()) { 0448 auto vault = d->knownVaults[device]; 0449 vault->updateStatus(); 0450 } 0451 } 0452 0453 void PlasmaVaultService::onActivitiesChanged(const QStringList &knownActivities) 0454 { 0455 if (knownActivities == QStringList{"00000000-0000-0000-0000-000000000000"}) 0456 return; 0457 qDebug() << "Known activities:" << knownActivities; 0458 0459 for (auto *vault : d->knownVaults.values()) { 0460 auto vaultActivities = vault->activities(); 0461 const auto removedBegin = std::remove_if(vaultActivities.begin(), vaultActivities.end(), [&knownActivities](const QString &vaultActivity) { 0462 return !knownActivities.contains(vaultActivity); 0463 }); 0464 if (removedBegin != vaultActivities.end()) { 0465 vaultActivities.erase(removedBegin, vaultActivities.end()); 0466 vault->setActivities(vaultActivities); 0467 vault->saveConfiguration(); 0468 } 0469 } 0470 } 0471 0472 void PlasmaVaultService::onCurrentActivityChanged(const QString ¤tActivity) 0473 { 0474 for (auto *vault : d->knownVaults.values()) { 0475 const auto vaultActivities = vault->activities(); 0476 if (!vaultActivities.isEmpty() && !vaultActivities.contains(currentActivity)) { 0477 vault->close(); 0478 } 0479 } 0480 } 0481 0482 void PlasmaVaultService::onActivityRemoved(const QString &removedActivity) 0483 { 0484 for (auto *vault : d->knownVaults.values()) { 0485 auto vaultActivities = vault->activities(); 0486 if (vaultActivities.removeAll(removedActivity) > 0) { 0487 vault->setActivities(vaultActivities); 0488 vault->saveConfiguration(); 0489 Q_EMIT vaultChanged(vault->info()); 0490 } 0491 } 0492 } 0493 0494 #include "service.moc" 0495 0496 #include "moc_service.cpp"