diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 0000000..bc0df43 --- /dev/null +++ b/.arcconfig @@ -0,0 +1,4 @@ +{ + "phabricator.uri" : "https://phabricator.kde.org/" +} + diff --git a/CMakeLists.txt b/CMakeLists.txt index 282763d..87f3cdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,90 +1,92 @@ # vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab: cmake_minimum_required (VERSION 2.8.12) project (PlasmaVault) -set(PROJECT_VERSION "5.10.90") +set(PROJECT_VERSION "5.12.80") set(PROJECT_VERSION_MAJOR 5) set (PLASMAVAULT_VERSION "0.1") set (REQUIRED_QT_VERSION 5.7.0) # We don't build in-source if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") message ( FATAL_ERROR "plasmavault requires an out of source build. Please create a separate build directory and run 'cmake path_to_plasma [options]' there." ) endif () # Extra CMake stuff include (FeatureSummary) find_package (ECM 5.17.0 NO_MODULE) set_package_properties (ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary (WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include (KDEInstallDirs) include (KDECMakeSettings) include (KDECompilerSettings) include (GenerateExportHeader) include (ECMGenerateHeaders) # Qt set (CMAKE_AUTOMOC ON) find_package ( Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED COMPONENTS Core DBus Quick Widgets ) # KDE Frameworks set (KF5_DEP_VERSION "5.17.0") # handled by release scripts find_package ( KF5 ${KF5_DEP_VERSION} REQUIRED COMPONENTS Activities Config ConfigWidgets CoreAddons DBusAddons I18n KIO Plasma WidgetsAddons NetworkManagerQt ) find_package (KF5SysGuard REQUIRED) include_directories ( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/common ) # Basic includes include (CPack) include (ECMPackageConfigHelpers) include (ECMSetupVersion) +add_definitions (-DQT_NO_URL_CAST_FROM_STRING) +add_definitions (-DQT_NO_CAST_TO_ASCII) add_definitions (-DTRANSLATION_DOMAIN=\"plasmavault-kde\") add_definitions (-fexceptions) if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ki18n_install (po) endif () # libPlasmaVault add_subdirectory (kded) add_subdirectory (plasma) add_subdirectory (icons) # Write out the features feature_summary (WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/kded/CMakeLists.txt b/kded/CMakeLists.txt index 198ff77..2822303 100644 --- a/kded/CMakeLists.txt +++ b/kded/CMakeLists.txt @@ -1,90 +1,92 @@ include_directories ( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/engine ) set ( kded_plasmavault_SRCS service.cpp engine/vault.cpp engine/backend_p.cpp engine/fusebackend_p.cpp engine/types.cpp engine/commandresult.cpp engine/backends/encfs/encfsbackend.cpp engine/backends/cryfs/cryfsbackend.cpp ui/dialogdsl.cpp ui/activitieslinkingwidget.cpp ui/backendchooserwidget.cpp ui/cryfscypherchooserwidget.cpp ui/directorypairchooserwidget.cpp ui/directorychooserwidget.cpp ui/namechooserwidget.cpp ui/noticewidget.cpp ui/passwordchooserwidget.cpp ui/offlineonlywidget.cpp ui/vaultcreationwizard.cpp ui/vaultconfigurationwizard.cpp + ui/mountdialog.cpp ../common/vaultinfo.cpp ) ki18n_wrap_ui ( kded_plasmavault_SRCS ui/activitieslinkingwidget.ui ui/backendchooserwidget.ui ui/cryfscypherchooserwidget.ui ui/directorypairchooserwidget.ui ui/directorychooserwidget.ui ui/namechooserwidget.ui ui/noticewidget.ui ui/passwordchooserwidget.ui ui/offlineonlywidget.ui ui/vaultcreationwizard.ui ui/vaultconfigurationwizard.ui + ui/mountdialog.ui ) add_library ( kded_plasmavault MODULE ${kded_plasmavault_SRCS} ) set_target_properties ( kded_plasmavault PROPERTIES OUTPUT_NAME plasmavault ) kcoreaddons_desktop_to_json ( kded_plasmavault plasmavault.desktop ) target_link_libraries ( kded_plasmavault Qt5::Core Qt5::DBus Qt5::Widgets KF5::Activities KF5::ConfigCore KF5::ConfigWidgets KF5::CoreAddons KF5::DBusAddons KF5::I18n KF5::KIOCore KF5::KIOWidgets KF5::SysGuard KF5::WidgetsAddons KF5::ProcessCore KF5::NetworkManagerQt ) install ( TARGETS kded_plasmavault DESTINATION ${PLUGIN_INSTALL_DIR}/kf5/kded) diff --git a/kded/engine/vault.cpp b/kded/engine/vault.cpp index b5faa14..9065d79 100644 --- a/kded/engine/vault.cpp +++ b/kded/engine/vault.cpp @@ -1,663 +1,665 @@ /* * Copyright 2017 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "vault.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "backend_p.h" #include "asynqt/basic/all.h" #include "asynqt/wrappers/process.h" #include "asynqt/operations/listen.h" #include "asynqt/operations/cast.h" #include #define CFG_NAME "name" #define CFG_LAST_STATUS "lastStatus" +#define CFG_LAST_ERROR "lastError" #define CFG_MOUNT_POINT "mountPoint" #define CFG_BACKEND "backend" #define CFG_ACTIVITIES "activities" #define CFG_OFFLINEONLY "offlineOnly" namespace PlasmaVault { class Vault::Private { public: Vault * const q; KSharedConfigPtr config; Device device; QTimer savingDelay; struct Data { QString name; MountPoint mountPoint; VaultInfo::Status status; QString message; QStringList activities; bool isOfflineOnly; QString backendName; Backend::Ptr backend; }; using ExpectedData = AsynQt::Expected; ExpectedData data; void updateMessage(const QString &message) { if (!data) return; data->message = message; emit q->messageChanged(message); } void updateStatus() { if (data) { // Checking the status, and whether we should update it const auto oldStatus = data->status; const auto newStatus = isOpened() ? VaultInfo::Opened : isInitialized() ? VaultInfo::Closed : VaultInfo::NotInitialized; if (oldStatus == newStatus) return; data->status = newStatus; emit q->statusChanged(data->status); if (newStatus == VaultInfo::Closed || newStatus == VaultInfo::Opened) { emit q->isOpenedChanged(newStatus == VaultInfo::Opened); } if (oldStatus == VaultInfo::NotInitialized || newStatus == VaultInfo::NotInitialized) { emit q->isInitializedChanged(newStatus); } if (oldStatus == VaultInfo::Creating || oldStatus == VaultInfo::Opening || oldStatus == VaultInfo::Closing || oldStatus == VaultInfo::Destroying) { emit q->isBusyChanged(false); } // Saving the data for the current mount KConfigGroup generalConfig(config, "EncryptedDevices"); generalConfig.writeEntry(device.data(), true); KConfigGroup vaultConfig(config, device.data()); vaultConfig.writeEntry(CFG_LAST_STATUS, (int)data->status); vaultConfig.writeEntry(CFG_MOUNT_POINT, data->mountPoint.data()); vaultConfig.writeEntry(CFG_NAME, data->name); vaultConfig.writeEntry(CFG_BACKEND, data->backend->name()); vaultConfig.writeEntry(CFG_ACTIVITIES, data->activities); vaultConfig.writeEntry(CFG_OFFLINEONLY, data->isOfflineOnly); org::kde::KDirNotify::emitFilesAdded( QUrl::fromLocalFile(data->mountPoint.data())); - config->sync(); - } else { emit q->isOpenedChanged(false); emit q->isInitializedChanged(false); emit q->isBusyChanged(false); KConfigGroup generalConfig(config, "EncryptedDevices"); generalConfig.writeEntry(device.data(), false); KConfigGroup vaultConfig(config, device.data()); vaultConfig.writeEntry(CFG_LAST_STATUS, (int)VaultInfo::Error); + vaultConfig.writeEntry(CFG_LAST_ERROR, + data.error().message() + " (code: " + + QString::number(data.error().code()) + ")"); // vaultConfig.deleteEntry(CFG_MOUNT_POINT); // vaultConfig.deleteEntry(CFG_NAME); // vaultConfig.deleteEntry(CFG_BACKEND); // vaultConfig.deleteEntry(CFG_ACTIVITIES); // vaultConfig.deleteEntry(CFG_OFFLINEONLY); emit q->statusChanged(VaultInfo::Error); } config->sync(); } ExpectedData errorData(Error::Code error, const QString &message) const { qWarning() << "error: " << message; return ExpectedData::error(error, message); } ExpectedData loadVault(const Device &device, const QString &name = QString(), const MountPoint &mountPoint = MountPoint(), const Payload &payload = Payload()) const { if (!config->hasGroup(device.data())) { return errorData(Error::DeviceError, i18n("Unknown device")); } Data vaultData; const QString backendName = payload[KEY_BACKEND].toString(); const QStringList activities = payload[KEY_ACTIVITIES].toStringList(); const bool isOfflineOnly = payload[KEY_OFFLINEONLY].toBool(); // status should never be in this state, if we got an error, // d->data should not be valid vaultData.status = VaultInfo::Error; // Reading the mount data from the config const KConfigGroup vaultConfig(config, device.data()); vaultData.name = vaultConfig.readEntry(CFG_NAME, name); vaultData.mountPoint = MountPoint(vaultConfig.readEntry(CFG_MOUNT_POINT, mountPoint.data())); vaultData.backendName = vaultConfig.readEntry(CFG_BACKEND, backendName); vaultData.activities = vaultConfig.readEntry(CFG_ACTIVITIES, activities); vaultData.isOfflineOnly = vaultConfig.readEntry(CFG_OFFLINEONLY, isOfflineOnly); const QDir mountPointDir(vaultData.mountPoint); return // If the backend is not known, we need to fail !Backend::availableBackends().contains(vaultData.backendName) ? errorData(Error::BackendError, i18n("Configured backend does not exist: %1", vaultData.backendName)) : // If the mount point is empty, we can not do anything vaultData.mountPoint.isEmpty() ? errorData(Error::MountPointError, i18n("Mount point is not specified")) : // Lets try to create the mount point !mountPointDir.exists() && !QDir().mkpath(vaultData.mountPoint) ? errorData(Error::MountPointError, - i18n("Can not create the mount point")) : + i18n("Cannot create the mount point")) : // Instantiate the backend if possible !(vaultData.backend = Backend::instance(vaultData.backendName)) ? errorData(Error::BackendError, - i18n("Configured backend can not be instantiated: %1", vaultData.backendName)) : + i18n("Configured backend cannot be instantiated: %1", vaultData.backendName)) : // otherwise ExpectedData::success(vaultData); } Private(Vault *parent, const Device &device) : q(parent) , config(KSharedConfig::openConfig(PLASMAVAULT_CONFIG_FILE)) , device(device) , data(loadVault(device)) { updateStatus(); } template T followFuture(VaultInfo::Status whileNotFinished, const T &future) { using namespace AsynQt::operators; emit q->isBusyChanged(true); data->status = whileNotFinished; return future | onSuccess([this] { updateStatus(); }); } bool isInitialized() const { return data && data->backend->isInitialized(device); } bool isOpened() const { return data && data->backend->isOpened(data->mountPoint); } }; Vault::Vault(const Device &device, QObject *parent) : QObject(parent) , d(new Private(this, device)) { d->savingDelay.setInterval(300); d->savingDelay.setSingleShot(true); connect(&d->savingDelay, &QTimer::timeout, this, [&] { qDebug() << "Saving vault info:" << d->data->name << d->data->mountPoint << d->data->activities << d->data->isOfflineOnly ; KConfigGroup vaultConfig(d->config, d->device.data()); vaultConfig.writeEntry(CFG_MOUNT_POINT, d->data->mountPoint.data()); vaultConfig.writeEntry(CFG_NAME, d->data->name); vaultConfig.writeEntry(CFG_ACTIVITIES, d->data->activities); vaultConfig.writeEntry(CFG_OFFLINEONLY, d->data->isOfflineOnly); d->config->sync(); emit infoChanged(); }); } Vault::~Vault() { close(); } void Vault::saveConfiguration() { d->savingDelay.start(); } FutureResult<> Vault::create(const QString &name, const MountPoint &mountPoint, const Payload &payload) { using namespace AsynQt::operators; return // If the backend is already known, and the device is initialized, // we do not want to do it again d->data && d->data->backend->isInitialized(d->device) ? errorResult(Error::DeviceError, - i18n("This device is already registered. Can not recreate it.")) : + i18n("This device is already registered. Cannot recreate it.")) : // Mount not open, check the error messages !(d->data = d->loadVault(d->device, name, mountPoint, payload)) ? errorResult(Error::BackendError, i18n("Unknown error, unable to create the backend.")) : // otherwise d->followFuture(VaultInfo::Creating, d->data->backend->initialize(name, d->device, mountPoint, payload)) | onSuccess([mountPoint] { // If we have successfully created the vault, // lets try to set its icon QFile dotDir(mountPoint + "/.directory"); if (dotDir.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&dotDir); out << "[Desktop Entry]\nIcon=folder-decrypted\n"; } }); } FutureResult<> Vault::open(const Payload &payload) { return // We can not mount something that has not been registered // with us before !d->data ? errorResult(Error::BackendError, - i18n("Can not open an unknown vault.")) : + i18n("Cannot open an unknown vault.")) : // otherwise d->followFuture(VaultInfo::Opening, d->data->backend->open(d->device, d->data->mountPoint, payload)); } FutureResult<> Vault::close() { using namespace AsynQt::operators; return // We can not mount something that has not been registered // with us before !d->data ? errorResult(Error::BackendError, - i18n("The vault is unknown, can not close it.")) : + i18n("The vault is unknown, cannot close it.")) : // otherwise d->followFuture(VaultInfo::Closing, d->data->backend->close(d->device, d->data->mountPoint)) | onSuccess([this] (const Result<> &result) { if (!isOpened() || result) { d->updateMessage(QString()); } else { // We want to check whether there is an application // that is accessing the vault AsynQt::Process::getOutput("lsof", { "-t", mountPoint() }) | cast() | onError([this] { d->updateMessage(i18n("Unable to close the vault, an application is using it")); }) | onSuccess([this] (const QString &result) { // based on ksolidnotify.cpp QStringList blockApps; const auto &pidList = result.split(QRegExp(QStringLiteral("\\s+")), QString::SkipEmptyParts); if (pidList.size() == 0) { d->updateMessage(i18n("Unable to close the vault, an application is using it")); close(); } else { KSysGuard::Processes procs; for (const QString &pidStr: pidList) { int pid = pidStr.toInt(); if (!pid) { continue; } procs.updateOrAddProcess(pid); KSysGuard::Process *proc = procs.getProcess(pid); if (!blockApps.contains(proc->name())) { blockApps << proc->name(); } } blockApps.removeDuplicates(); d->updateMessage(i18n("Unable to close the vault, it is used by %1", blockApps.join(", "))); } }); } }); } // FutureResult<> Vault::configure() // { // return close(); // } FutureResult<> Vault::forceClose() { using namespace AsynQt::operators; AsynQt::await( AsynQt::Process::getOutput("lsof", { "-t", mountPoint() }) | cast() | onError([this] { d->updateMessage(i18n("Failed to fetch the list of applications using this vault")); }) | onSuccess([this] (const QString &result) { // based on ksolidnotify.cpp QStringList blockApps; const auto &pidList = result.split(QRegExp(QStringLiteral("\\s+")), QString::SkipEmptyParts); KSysGuard::Processes procs; for (const QString &pidStr: pidList) { int pid = pidStr.toInt(); if (!pid) { continue; } procs.sendSignal(pid, SIGKILL); } })); return close(); } FutureResult<> Vault::destroy(const Payload &payload) { return // We can not mount something that has not been registered // with us before !d->data ? errorResult(Error::BackendError, - i18n("The vault is unknown, can not destroy it.")) : + i18n("The vault is unknown, cannot destroy it.")) : // otherwise d->followFuture(VaultInfo::Destroying, d->data->backend->destroy(d->device, d->data->mountPoint, payload)); } VaultInfo::Status Vault::status() const { return d->data->status; } bool Vault::isValid() const { return d->data; } Device Vault::device() const { return d->device; } QList Vault::availableDevices() { const auto config = KSharedConfig::openConfig(PLASMAVAULT_CONFIG_FILE); const KConfigGroup general(config, "EncryptedDevices"); QList results; for (const auto& item: general.keyList()) { results << Device(item); } return results; } QStringList Vault::statusMessage() { for (const auto& backendName: Backend::availableBackends()) { auto backend = Backend::instance(backendName); backend->validateBackend(); } return {}; } QString Vault::message() const { if (!d->data) { return d->data.error().message(); } else { return d->data->message; } } bool Vault::isInitialized() const { return d->isInitialized(); } bool Vault::isOpened() const { return d->isOpened(); } MountPoint Vault::mountPoint() const { return d->data->mountPoint; } void Vault::setMountPoint(const MountPoint &mountPoint) { if (d->data->mountPoint != mountPoint.data()) { QDir().rmpath(d->data->mountPoint.data()); QDir().mkpath(mountPoint.data()); d->data->mountPoint = mountPoint; saveConfiguration(); } } QStringList Vault::activities() const { return d->data->activities; } void Vault::setActivities(const QStringList &activities) { d->data->activities = activities; emit activitiesChanged(activities); saveConfiguration(); } bool Vault::isOfflineOnly() const { return d->data->isOfflineOnly; } void Vault::setIsOfflineOnly(bool isOfflineOnly) { d->data->isOfflineOnly = isOfflineOnly; emit isOfflineOnlyChanged(isOfflineOnly); saveConfiguration(); } QString Vault::name() const { return d->data->name; } void Vault::setName(const QString &name) { d->data->name = name; emit nameChanged(name); saveConfiguration(); } QString Vault::backend() const { return d->data->backendName; } bool Vault::isBusy() const { if (!d->data) { return false; } switch (status()) { case VaultInfo::Creating: case VaultInfo::Opening: case VaultInfo::Closing: case VaultInfo::Destroying: return true; default: return false; } } VaultInfo Vault::info() const { VaultInfo vaultInfo; vaultInfo.device = device(); vaultInfo.name = name(); vaultInfo.status = status(); vaultInfo.message = message(); vaultInfo.activities = activities(); vaultInfo.isOfflineOnly = isOfflineOnly(); return vaultInfo; } } // namespace PlasmaVault diff --git a/kded/plasmavault.desktop b/kded/plasmavault.desktop index 0981ea5..d876c24 100644 --- a/kded/plasmavault.desktop +++ b/kded/plasmavault.desktop @@ -1,68 +1,79 @@ [Desktop Entry] Name=Plasma Vault module Name[ar]=وحدة خزنة بلازما Name[ca]=Mòdul de Plasma per a Voltes Name[ca@valencia]=Mòdul de Plasma per a Voltes +Name[cs]=Modul sejfu pro Plasma Name[da]=Sikkerhedsboksmodul til Plasma +Name[de]=Modul für Plasma-Vaults Name[el]=Άρθρωμα Plasma για τα θησαυροφυλάκια Name[en_GB]=Plasma Vault module Name[es]=Módulo de cajas fuertes para Plasma Name[eu]=Plasma kutxa-gotor modulua +Name[fi]=Plasman holvimoduuli Name[fr]=Module Plasma Vault Name[gl]=Módulo de caixa forte de Plasma Name[hu]=Plasma Vault modul Name[it]=Modulo del caveau di Plasma +Name[ko]=Plasma Vault 모듈 Name[nl]=Plasma module voor kluis +Name[nn]=Modul for Plasma-datakvelv Name[pa]=ਪਲਾਜ਼ਮਾ ਵਾਲਟ ਮੋਡੀਊਲ -Name[pl]=Moduł krypty Plazmy +Name[pl]=Moduł sejfu Plazmy Name[pt]=Módulo do Plasma Vault Name[pt_BR]=Módulo de cofre do Plasma Name[sk]=Modul Plasma Vault Name[sl]=Modul shrambe za Plasmo Name[sr]=Модул плазма трезора Name[sr@ijekavian]=Модул плазма трезора Name[sr@ijekavianlatin]=Modul plasma trezora Name[sr@latin]=Modul plasma trezora Name[sv]=Plasma valvmodul Name[tr]=Plazma Kasası modülü Name[uk]=Модуль сховищ Плазми Name[x-test]=xxPlasma Vault modulexx Name[zh_CN]=Plasma 保险库模块 Comment=Provides encrypted vaults Comment[ar]=توفّر خزنات معمّاة Comment[ca]=Proporciona voltes encriptades Comment[ca@valencia]=Proporciona voltes encriptades +Comment[cs]=Poskytuje šifrované sejfy +Comment[da]=Giver mulighed for krypterede sikkerhedsbokse +Comment[de]=Stellt verschlüsselte Vaults zur Verfügung Comment[el]=Παρέχει κρυπτογραφημένα θησαυροφυλάκια Comment[en_GB]=Provides encrypted vaults Comment[es]=Proporciona cajas fuertes cifradas Comment[eu]=Zifratutako kutxa gotorrak hornitzen ditu +Comment[fi]=Tarjoaa salatut holvit Comment[fr]=Fournit des coffres-forts chiffrés Comment[gl]=Fornece caixas fortes cifradas. Comment[hu]=Titkosított tárak Comment[it]=Caveau criptati forniti +Comment[ko]=암호화된 비밀 공간 제공 Comment[nl]=Levert versleutelde kluizen +Comment[nn]=Tilbyr krypterte datakvelv Comment[pa]=ਇੰਕ੍ਰਿਪਟ ਕੀਤੇ ਵਾਲਟ ਦਿੰਦਾ ਹੈ -Comment[pl]=Zapewnia zaszyfrowane krypty +Comment[pl]=Zapewnia zaszyfrowane sejfy Comment[pt]=Fornece 'cofres' encriptados Comment[pt_BR]=Fornece cofres criptografados Comment[sk]=Poskytuje šifrovanú klenbu Comment[sl]=Ponuja šifrirane shrambe Comment[sr]=Даје шифроване трезоре Comment[sr@ijekavian]=Даје шифроване трезоре Comment[sr@ijekavianlatin]=Daje šifrovane trezore Comment[sr@latin]=Daje šifrovane trezore Comment[sv]=Tillhandahåller krypterade valv Comment[tr]=Şifreli kasalar sağlar Comment[uk]=Надає доступ до шифрованих сховищ Comment[x-test]=xxProvides encrypted vaultsxx Comment[zh_CN]=提供加密的保险库 Type=Service X-KDE-ServiceTypes=KDEDModule X-KDE-ModuleType=Library X-KDE-Library=plasmavault X-KDE-DBus-ModuleName=plasmavault X-KDE-Kded-autoload=true X-KDE-Kded-load-on-demand=true X-KDE-Kded-phase=1 OnlyShowIn=KDE; diff --git a/kded/service.cpp b/kded/service.cpp index a684481..871e5d8 100644 --- a/kded/service.cpp +++ b/kded/service.cpp @@ -1,337 +1,340 @@ /* * Copyright 2017 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "service.h" #include #include #include #include #include #include #include "engine/vault.h" #include "engine/commandresult.h" #include "ui/vaultcreationwizard.h" #include "ui/vaultconfigurationwizard.h" +#include "ui/mountdialog.h" + +#include #include K_PLUGIN_FACTORY_WITH_JSON(PlasmaVaultServiceFactory, "plasmavault.json", registerPlugin();) using namespace PlasmaVault; +using AsynQt::Expected; + class PlasmaVaultService::Private { public: QHash knownVaults; KActivities::Consumer kamd; - QVector devicesInhibittingNetworking; - struct { - bool networkingEnabled; - } savedNetworkingStatus; + struct NetworkingState { + bool wasNetworkingEnabled; + QVector devicesInhibittingNetworking; + }; + // Ideally, this would be std::optional... lovely C++17 + Expected savedNetworkingState = + Expected::error(0); + + void saveNetworkingState() + { + // Ignore the request if we already have a saved state + if (savedNetworkingState) { + return; + } + + savedNetworkingState = Expected::success( + NetworkingState { + NetworkManager::isNetworkingEnabled() || true, + {} + }); + } + + void restoreNetworkingState() + { + // Ignore the request if we do not have a state saved + // or if there are more devices inhibitting networking + if (!savedNetworkingState || !savedNetworkingState->devicesInhibittingNetworking.isEmpty()) { + return; + } + + NetworkManager::setNetworkingEnabled(savedNetworkingState->wasNetworkingEnabled); + } + + Vault* vaultFor(const QString &device_) const { const Device device(device_); if (!knownVaults.contains(device)) { return nullptr; } return knownVaults[device]; } }; PlasmaVaultService::PlasmaVaultService(QObject * parent, const QVariantList&) : KDEDModule(parent) , d(new Private()) { connect(this, &KDEDModule::moduleRegistered, this, &PlasmaVaultService::slotRegistered); connect(&d->kamd, &KActivities::Consumer::currentActivityChanged, this, &PlasmaVaultService::onCurrentActivityChanged); init(); } PlasmaVaultService::~PlasmaVaultService() { } void PlasmaVaultService::init() { for (const Device &device: Vault::availableDevices()) { registerVault(new Vault(device, this)); } } PlasmaVault::VaultInfoList PlasmaVaultService::availableDevices() const { PlasmaVault::VaultInfoList result; for (const auto &vault: d->knownVaults.values()) { const auto vaultData = vault->info(); result << vaultData; } return result; } void PlasmaVaultService::requestNewVault() { const auto dialog = new VaultCreationWizard(); connect(dialog, &VaultCreationWizard::createdVault, this, &PlasmaVaultService::registerVault); dialog->show(); } void PlasmaVaultService::slotRegistered(const QDBusObjectPath &path) { if (path.path() == QLatin1String("/modules/plasmavault")) { emit registered(); } } void PlasmaVaultService::registerVault(Vault *vault) { if (!vault->isValid()) { qWarning() << "Warning: Trying to register an invalid vault: " << vault->device(); return; } if (d->knownVaults.contains(vault->device())) { qWarning() << "Warning: This one is already registered: " << vault->device(); return; } vault->setParent(this); d->knownVaults[vault->device()] = vault; connect(vault, &Vault::statusChanged, this, &PlasmaVaultService::onVaultStatusChanged); connect(vault, &Vault::messageChanged, this, &PlasmaVaultService::onVaultMessageChanged); connect(vault, &Vault::infoChanged, this, &PlasmaVaultService::onVaultInfoChanged); emit vaultAdded(vault->info()); } void PlasmaVaultService::onVaultStatusChanged(VaultInfo::Status status) { - Q_UNUSED(status); - const auto vault = qobject_cast(sender()); + if (vault->isOfflineOnly()) { + d->saveNetworkingState(); + auto& devicesInhibittingNetworking = d->savedNetworkingState->devicesInhibittingNetworking; + + // We need to check whether this vault + // should be added or removed from the + // inhibitors list + const bool alreadyInhibiting = + devicesInhibittingNetworking.contains(vault->device()); + + if (status == VaultInfo::Opened && !alreadyInhibiting) { + devicesInhibittingNetworking << vault->device(); + } + + if (status != VaultInfo::Opened && alreadyInhibiting) { + devicesInhibittingNetworking.removeAll(vault->device()); + } + + // qDebug() << "Vaults inhibitting networking: " << devicesInhibittingNetworking; + + // Now, let's handle the networking part + if (!devicesInhibittingNetworking.isEmpty()) { + NetworkManager::setNetworkingEnabled(false); + } + + d->restoreNetworkingState(); + } + emit vaultChanged(vault->info()); } void PlasmaVaultService::onVaultInfoChanged() { const auto vault = qobject_cast(sender()); emit vaultChanged(vault->info()); } void PlasmaVaultService::onVaultMessageChanged(const QString &message) { Q_UNUSED(message); const auto vault = qobject_cast(sender()); emit vaultChanged(vault->info()); } -template -class PasswordMountDialog: protected KPasswordDialog { //_ -public: - PasswordMountDialog(Vault *vault, Function function) - : m_vault(vault) - , m_function(function) - { - } - - void show() - { - KPasswordDialog::show(); - } - -private: - bool checkPassword() override - { - auto future = m_vault->open({ { KEY_PASSWORD, password() } }); - - const auto result = AsynQt::await(future); - - if (result) { - m_function(); - return true; - - } else { - showErrorMessage(result.error().message()); - return false; - } - } - - void hideEvent(QHideEvent *) override - { - deleteLater(); - } - - Vault *m_vault; - Function m_function; -}; - -template -void showPasswordMountDialog(Vault *vault, Function &&function) +void showPasswordMountDialog(Vault *vault, const std::function &function) { - auto dialog = new PasswordMountDialog( - vault, std::forward(function)); - dialog->show(); + auto dialog = new MountDialog(vault, function); + dialog->open(); } //^ void PlasmaVaultService::openVault(const QString &device) { if (auto vault = d->vaultFor(device)) { if (vault->isOpened()) return; - if (vault->isOfflineOnly()) { - if (d->devicesInhibittingNetworking.isEmpty()) { - d->savedNetworkingStatus.networkingEnabled = - NetworkManager::isNetworkingEnabled(); - NetworkManager::setNetworkingEnabled(false); - } - d->devicesInhibittingNetworking << device; - } - showPasswordMountDialog(vault, [this, vault] { emit vaultChanged(vault->info()); }); } } void PlasmaVaultService::closeVault(const QString &device) { if (auto vault = d->vaultFor(device)) { if (!vault->isOpened()) return; vault->close(); - - if (!d->devicesInhibittingNetworking.isEmpty()) { - d->devicesInhibittingNetworking.removeAll(device); - if (d->devicesInhibittingNetworking.isEmpty()) { - NetworkManager::setNetworkingEnabled(d->savedNetworkingStatus.networkingEnabled); - } - } } } void PlasmaVaultService::configureVault(const QString &device) { if (auto vault = d->vaultFor(device)) { const auto dialog = new VaultConfigurationWizard(vault); // connect(dialog, &VaultConfigurationWizard::configurationChanged, // this, &PlasmaVaultService::registerVault); dialog->show(); } } void PlasmaVaultService::forceCloseVault(const QString &device) { if (auto vault = d->vaultFor(device)) { if (!vault->isOpened()) return; vault->forceClose(); } } void PlasmaVaultService::openVaultInFileManager(const QString &device) { if (auto vault = d->vaultFor(device)) { if (vault->isOpened()) { new KRun(QUrl::fromLocalFile((QString)vault->mountPoint()), 0); } else { showPasswordMountDialog(vault, [this, vault] { emit vaultChanged(vault->info()); new KRun(QUrl::fromLocalFile((QString)vault->mountPoint()), 0); }); } } } void PlasmaVaultService::onCurrentActivityChanged( const QString ¤tActivity) { for (auto* vault: d->knownVaults.values()) { const auto vaultActivities = vault->activities(); if (!vaultActivities.isEmpty() && !vaultActivities.contains(currentActivity)) { vault->close(); } } } #include "service.moc" diff --git a/kded/ui/backendchooserwidget.cpp b/kded/ui/backendchooserwidget.cpp index a5ca899..3c1ec4f 100644 --- a/kded/ui/backendchooserwidget.cpp +++ b/kded/ui/backendchooserwidget.cpp @@ -1,128 +1,155 @@ /* * Copyright 2017 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "backendchooserwidget.h" #include "ui_backendchooserwidget.h" #include "vault.h" #include "engine/backend_p.h" class BackendChooserWidget::Private { public: Private(BackendChooserWidget *parent) : q(parent) { } Ui::BackendChooserWidget ui; bool vaultNameValid = false; bool backendValid = false; + QByteArray bestsBackend; + int bestBackendPrio = -1; void setVaultNameValid(bool valid) { vaultNameValid = valid; q->setIsValid(vaultNameValid && backendValid); } void setBackendValid(bool valid) { backendValid = valid; q->setIsValid(vaultNameValid && backendValid); } BackendChooserWidget * const q; }; BackendChooserWidget::BackendChooserWidget() : DialogDsl::DialogModule(false) , d(new Private(this)) { d->ui.setupUi(this); d->ui.textStatus->hide(); + d->ui.page2Layout->setRowStretch(1, 10); connect(d->ui.editVaultName, &QLineEdit::textChanged, this, [&] (const QString &vaultName) { d->setVaultNameValid(!vaultName.isEmpty()); }); connect(d->ui.comboBackend, static_cast(&QComboBox::activated), this, &BackendChooserWidget::checkCurrentBackend); -} + connect(d->ui.pickBackendButton, SIGNAL(clicked()), this, SLOT(showBackendSelector())); +} BackendChooserWidget::~BackendChooserWidget() { } - void BackendChooserWidget::checkCurrentBackend() { const auto backendId = d->ui.comboBackend->currentData().toString(); const auto backend = PlasmaVault::Backend::instance(backendId); bool backendValid = false; d->setBackendValid(false); if (!backend) { d->ui.textStatus->setHtml(i18n("The specified backend is not available")); d->ui.textStatus->show(); } else { d->ui.textStatus->hide(); const auto result = AsynQt::await(backend->validateBackend()); if (!result) { d->ui.textStatus->setHtml(result.error().message()); d->ui.textStatus->show(); } else { backendValid = true; } } d->setBackendValid(backendValid); } - -void BackendChooserWidget::addItem(const QByteArray &id, const QString &title) +void BackendChooserWidget::showBackendSelector() { - d->ui.comboBackend->addItem(title, id); + d->ui.vaultEncryptionConfig->setCurrentWidget(d->ui.page2); checkCurrentBackend(); } +void BackendChooserWidget::addItem(const QByteArray &id, const QString &title, int priority) +{ + d->ui.comboBackend->addItem(title, id); + + if (priority > d->bestBackendPrio) { + const auto backend = PlasmaVault::Backend::instance(id); + Q_ASSERT(backend); // backend and UI out of sync. Its an assert since they both are part of the same .so + if (backend && AsynQt::await(backend->validateBackend())) { + d->bestBackendPrio = priority; + d->bestsBackend = id; + d->ui.backendName->setText(title); + d->setBackendValid(true); + } + } +} PlasmaVault::Vault::Payload BackendChooserWidget::fields() const { + QByteArray backend = d->bestsBackend; + if (d->ui.vaultEncryptionConfig->currentWidget() == d->ui.page2) + backend = d->ui.comboBackend->currentData().toByteArray(); + Q_ASSERT(!backend.isEmpty()); return { - { KEY_BACKEND, d->ui.comboBackend->currentData() }, + { KEY_BACKEND, backend}, { KEY_NAME, d->ui.editVaultName->text() } }; } +void BackendChooserWidget::checkBackendAvailable() +{ + if (d->bestsBackend.isEmpty()) { // in case there are no backends found at all + showBackendSelector(); // show the more helpful selector + } +} diff --git a/kded/ui/backendchooserwidget.h b/kded/ui/backendchooserwidget.h index 4afa5b2..d375943 100644 --- a/kded/ui/backendchooserwidget.h +++ b/kded/ui/backendchooserwidget.h @@ -1,47 +1,49 @@ /* * Copyright 2017 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PLASMAVAULT_KDED_UI_BACKEND_CHOOSER_WIDGET_H #define PLASMAVAULT_KDED_UI_BACKEND_CHOOSER_WIDGET_H #include "dialogdsl.h" class BackendChooserWidget: public DialogDsl::DialogModule { Q_OBJECT public: BackendChooserWidget(); ~BackendChooserWidget(); - void addItem(const QByteArray &id, const QString &title); + void addItem(const QByteArray &id, const QString &title, int priority); PlasmaVault::Vault::Payload fields() const override; - QByteArray backendId(); + + void checkBackendAvailable(); private Q_SLOTS: void checkCurrentBackend(); + void showBackendSelector(); private: class Private; QScopedPointer d; }; #endif // include guard diff --git a/kded/ui/backendchooserwidget.ui b/kded/ui/backendchooserwidget.ui index 9dcc62a..0558560 100644 --- a/kded/ui/backendchooserwidget.ui +++ b/kded/ui/backendchooserwidget.ui @@ -1,118 +1,184 @@ BackendChooserWidget 0 0 - 653 - 443 + 381 + 301 - - + + Vaul&t name: editVaultName - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + - + Qt::Vertical QSizePolicy::Fixed 20 8 - - - - Choose the encryption system you want to use for this vault: + + + + 0 - - - - - - - - - 200 - 0 - + + + + 0 - - - - - - Qt::Horizontal + + 0 - - - 40 - 20 - + + 0 - - - - - - - - QFrame::NoFrame - + + 0 + + + + + + + Backend: + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Change + + + + + + + + + Qt::Vertical + + + + 20 + 283 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Choose the encryption system you want to use for this vault: + + + + + + + + + + 200 + 0 + + + + + + + + Qt::Horizontal + + + + 204 + 20 + + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + + + + + - - - - Qt::Vertical - - - - 20 - 40 - - - - diff --git a/kded/ui/cryfscypherchooserwidget.ui b/kded/ui/cryfscypherchooserwidget.ui index 1068fcf..a0001b1 100644 --- a/kded/ui/cryfscypherchooserwidget.ui +++ b/kded/ui/cryfscypherchooserwidget.ui @@ -1,28 +1,28 @@ CryfsCypherChooserWidget 0 0 652 62 - Choose the used cypher: + Choose the used cipher: diff --git a/kded/ui/directorychooserwidget.cpp b/kded/ui/directorychooserwidget.cpp index fd8e6e4..e965639 100644 --- a/kded/ui/directorychooserwidget.cpp +++ b/kded/ui/directorychooserwidget.cpp @@ -1,102 +1,102 @@ /* * Copyright 2017 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "directorychooserwidget.h" #include "ui_directorychooserwidget.h" #include "vault.h" class DirectoryChooserWidget::Private { public: Ui::DirectoryChooserWidget ui; DirectoryChooserWidget::Flags flags; bool mountPointValid = false; DirectoryChooserWidget *const q; Private(DirectoryChooserWidget *parent) : q(parent) { } void setMountPointValid(bool valid) { if (mountPointValid == valid) return; mountPointValid = valid; q->setIsValid(valid); } bool isDirectoryValid(const QUrl &url) const { if (url.isEmpty()) return false; QDir directory(url.toString()); // TODO: Support alternative flags once they are needed if (!directory.exists() || directory.entryList().isEmpty()) return true; return false; } }; DirectoryChooserWidget::DirectoryChooserWidget( DirectoryChooserWidget::Flags flags) : DialogDsl::DialogModule(false), d(new Private(this)) { d->ui.setupUi(this); d->flags = flags; connect(d->ui.editMountPoint, &KUrlRequester::textEdited, - this, [&] (const QString &url) { - d->setMountPointValid(d->isDirectoryValid(url)); + this, [&] () { + d->setMountPointValid(d->isDirectoryValid(d->ui.editMountPoint->url())); }); } DirectoryChooserWidget::~DirectoryChooserWidget() { } PlasmaVault::Vault::Payload DirectoryChooserWidget::fields() const { return { { KEY_MOUNT_POINT, d->ui.editMountPoint->url().toLocalFile() } }; } void DirectoryChooserWidget::init( const PlasmaVault::Vault::Payload &payload) { const auto mountPoint = payload[KEY_MOUNT_POINT].toString(); d->ui.editMountPoint->setText(mountPoint); } diff --git a/kded/ui/directorypairchooserwidget.cpp b/kded/ui/directorypairchooserwidget.cpp index 85d3c0d..ac3ae3d 100644 --- a/kded/ui/directorypairchooserwidget.cpp +++ b/kded/ui/directorypairchooserwidget.cpp @@ -1,128 +1,142 @@ /* * Copyright 2017 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "directorypairchooserwidget.h" #include "ui_directorypairchooserwidget.h" #include "vault.h" +#include + class DirectoryPairChooserWidget::Private { public: Ui::DirectoryPairChooserWidget ui; - DirectoryPairChooserWidget::Flags flags; + const DirectoryPairChooserWidget::Flags flags; bool mountPointValid = false; bool encryptedLocationValid = false; DirectoryPairChooserWidget *const q; - Private(DirectoryPairChooserWidget *parent) - : q(parent) + Private(DirectoryPairChooserWidget *parent, DirectoryPairChooserWidget::Flags flags) + : flags(flags), q(parent) { + if (flags & DirectoryPairChooserWidget::SkipDevicePicker) + encryptedLocationValid = true; } void setEncryptedLocationValid(bool valid) { if (encryptedLocationValid == valid) return; encryptedLocationValid = valid; // We only change the global valid state if // the mount point was already valid if (mountPointValid) { q->setIsValid(valid); } } void setMountPointValid(bool valid) { if (mountPointValid == valid) return; mountPointValid = valid; // We only change the global valid state if // the enc location was already valid if (encryptedLocationValid) { q->setIsValid(valid); } } bool isDirectoryValid(const QUrl &url) const { if (url.isEmpty()) return false; + // TODO check the vaults database to see if another vault already uses this dir QDir directory(url.toString()); // TODO: Support alternative flags once they are needed if (!directory.exists() || directory.entryList().isEmpty()) return true; return false; } }; - - DirectoryPairChooserWidget::DirectoryPairChooserWidget( DirectoryPairChooserWidget::Flags flags) - : DialogDsl::DialogModule(false), d(new Private(this)) + : DialogDsl::DialogModule(false), d(new Private(this, flags)) { d->ui.setupUi(this); - d->flags = flags; - - connect(d->ui.editDevice, &KUrlRequester::textEdited, - this, [&] (const QString &url) { - d->setEncryptedLocationValid(d->isDirectoryValid(url)); - }); + if (flags & DirectoryPairChooserWidget::SkipDevicePicker) { + d->ui.editDevice->setVisible(false); + d->ui.labelDevice->setVisible(false); + } else { + connect(d->ui.editDevice, &KUrlRequester::textEdited, + this, [&] () { + d->setEncryptedLocationValid(d->isDirectoryValid(d->ui.editDevice->url())); + }); + } connect(d->ui.editMountPoint, &KUrlRequester::textEdited, - this, [&] (const QString &url) { - d->setMountPointValid(d->isDirectoryValid(url)); + this, [&] () { + d->setMountPointValid(d->isDirectoryValid(d->ui.editMountPoint->url())); }); - } - DirectoryPairChooserWidget::~DirectoryPairChooserWidget() { } - PlasmaVault::Vault::Payload DirectoryPairChooserWidget::fields() const { return { { KEY_DEVICE, d->ui.editDevice->url().toLocalFile() }, { KEY_MOUNT_POINT, d->ui.editMountPoint->url().toLocalFile() } }; } void DirectoryPairChooserWidget::init( const PlasmaVault::Vault::Payload &payload) { + const QString basePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + + QStringLiteral("/plasma-vault"); + const auto name = payload[KEY_NAME].toString(); - d->ui.editDevice->setText("~/.vaults/" + name + ".enc"); - d->ui.editMountPoint->setText("~/Vaults/" + name); -} + Q_ASSERT(!name.isEmpty()); + QString path = QString("%1/%2.enc").arg(basePath).arg(name); + int index = 1; + while (QDir(path).exists()) { + path = QString("%1/%2_%3.enc").arg(basePath).arg(name).arg(index++); + } + d->ui.editDevice->setText(path); + d->ui.editMountPoint->setText(QDir::homePath() + QStringLiteral("/Vaults/") + name); + d->setEncryptedLocationValid(d->isDirectoryValid(d->ui.editDevice->url())); + d->setMountPointValid(d->isDirectoryValid(d->ui.editMountPoint->url())); +} diff --git a/kded/ui/directorypairchooserwidget.h b/kded/ui/directorypairchooserwidget.h index f2ff2cc..1dfaaa6 100644 --- a/kded/ui/directorypairchooserwidget.h +++ b/kded/ui/directorypairchooserwidget.h @@ -1,57 +1,55 @@ /* * Copyright 2017 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PLASMAVAULT_KDED_UI_DIRECTORY_PAIR_CHOOSER_WIDGET_H #define PLASMAVAULT_KDED_UI_DIRECTORY_PAIR_CHOOSER_WIDGET_H #include "dialogdsl.h" class DirectoryPairChooserWidget: public DialogDsl::DialogModule { Q_OBJECT public: enum Flags { - RequireNothing = 0, - RequireEmptyEncryptedLocation = 1, - RequireEmptyMountPoint = 2, - RequireEmptyDirectories = RequireEmptyEncryptedLocation | RequireEmptyMountPoint + NoFlags = 0, + SkipDevicePicker = 1 }; DirectoryPairChooserWidget(Flags flags); ~DirectoryPairChooserWidget(); PlasmaVault::Vault::Payload fields() const override; void init(const PlasmaVault::Vault::Payload &payload) override; private: class Private; QScopedPointer d; }; -inline DialogDsl::ModuleFactory directoryPairChooser(DirectoryPairChooserWidget::Flags flags) +inline DialogDsl::ModuleFactory directoryPairChooser(DirectoryPairChooserWidget::Flags flags = DirectoryPairChooserWidget::NoFlags) { return [=] { return new DirectoryPairChooserWidget(flags); }; } #endif // include guard diff --git a/kded/ui/mountdialog.cpp b/kded/ui/mountdialog.cpp new file mode 100644 index 0000000..76515bc --- /dev/null +++ b/kded/ui/mountdialog.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2017 by Kees vd Broek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "mountdialog.h" +#include "engine/vault.h" + +#include +#include +#include + + +MountDialog::MountDialog(PlasmaVault::Vault *vault, const std::function &function) + : m_vault(vault), + m_function(function) +{ + m_ui.setupUi(this); + m_ui.errorLabel->setVisible(false); + m_ui.vaultName->setText(vault->name()); + + QStyleOption option; + option.initFrom(this); + const int iconSize = style()->pixelMetric(QStyle::PM_MessageBoxIconSize, &option, this); + m_ui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-password")).pixmap(iconSize)); +} + +void MountDialog::accept() +{ + setCursor(Qt::WaitCursor); + setEnabled(false); + + m_ui.password->lineEdit()->setCursor(Qt::WaitCursor); + QString pwd = m_ui.password->password(); + auto future = m_vault->open({ { KEY_PASSWORD, pwd } }); + const auto result = AsynQt::await(future); + + unsetCursor(); + setEnabled(true); + m_ui.password->lineEdit()->unsetCursor(); + + if (result) { + m_function(); + QDialog::accept(); + } else { + qDebug() << "We've got an error" << result.error().message(); + // m_ui.errorLabel->setText(i18n("Failed to open: %1").arg(result.error().message())); + m_ui.errorLabel->setText(i18n("Failed to open: %1", result.error().message())); + m_ui.errorLabel->setVisible(true); + } +} diff --git a/kded/ui/backendchooserwidget.h b/kded/ui/mountdialog.h similarity index 60% copy from kded/ui/backendchooserwidget.h copy to kded/ui/mountdialog.h index 4afa5b2..00d4b4d 100644 --- a/kded/ui/backendchooserwidget.h +++ b/kded/ui/mountdialog.h @@ -1,47 +1,46 @@ /* - * Copyright 2017 by Ivan Cukic + * Copyright 2017 by Kees vd Broek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef MOUNTDIALOG_H +#define MOUNTDIALOG_H -#ifndef PLASMAVAULT_KDED_UI_BACKEND_CHOOSER_WIDGET_H -#define PLASMAVAULT_KDED_UI_BACKEND_CHOOSER_WIDGET_H +#include +#include -#include "dialogdsl.h" +#include "ui_mountdialog.h" -class BackendChooserWidget: public DialogDsl::DialogModule { - Q_OBJECT +namespace PlasmaVault { + class Vault; +} +class MountDialog : public QDialog +{ public: - BackendChooserWidget(); - ~BackendChooserWidget(); + MountDialog(PlasmaVault::Vault *vault, const std::function &function); - void addItem(const QByteArray &id, const QString &title); - - PlasmaVault::Vault::Payload fields() const override; - QByteArray backendId(); - -private Q_SLOTS: - void checkCurrentBackend(); +protected: + void accept() override; private: - class Private; - QScopedPointer d; + PlasmaVault::Vault *m_vault; + std::function m_function; + Ui_MountDialog m_ui; }; -#endif // include guard - +#endif diff --git a/kded/ui/mountdialog.ui b/kded/ui/mountdialog.ui new file mode 100644 index 0000000..efcd57f --- /dev/null +++ b/kded/ui/mountdialog.ui @@ -0,0 +1,187 @@ + + + MountDialog + + + + 0 + 0 + 488 + 198 + + + + Plasma Vault + + + + + + + + + 16 + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Please enter the password to open this vault: + + + + + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + Password: + + + password + + + + + + + QLineEdit::Password + + + + + + + + 64 + 64 + + + + + + + 0 + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + false + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + false + + + + + + + + KPasswordLineEdit + QWidget +
kpasswordlineedit.h
+
+
+ + + + buttonBox + accepted() + MountDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + MountDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/kded/ui/vaultconfigurationwizard.cpp b/kded/ui/vaultconfigurationwizard.cpp index 2408e71..3dd34d8 100644 --- a/kded/ui/vaultconfigurationwizard.cpp +++ b/kded/ui/vaultconfigurationwizard.cpp @@ -1,188 +1,189 @@ /* * Copyright 2017 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "vaultconfigurationwizard.h" #include "ui_vaultconfigurationwizard.h" #include #include #include #include +#include #include "dialogdsl.h" #include "vault.h" using namespace DialogDsl; using namespace DialogDsl::operators; #include "backendchooserwidget.h" #include "activitieslinkingwidget.h" #include "cryfscypherchooserwidget.h" #include "directorychooserwidget.h" #include "noticewidget.h" #include "namechooserwidget.h" #include "passwordchooserwidget.h" #include "offlineonlywidget.h" using PlasmaVault::Vault; class VaultConfigurationWizard::Private { public: VaultConfigurationWizard *const q; Vault *vault; Ui::VaultConfigurationWizard ui; QStackedLayout *layout; steps currentSteps; QVector currentModuleDialogs; steps defaultSteps { i18n("General") / step { nameChooser(), directoryChooser(DirectoryChooserWidget::RequireEmptyMountPoint) }, i18n("Advanced") / step { activitiesChooser(), offlineOnlyChooser() } /* i18n("Dismantle") / step { notice( "dismantle-message", i18n("Note that Plasma Vault will not delete any of the files,\n\ the dismantling process only removes the vault from Plasma.\n\ You will need to remove the files manually."), NoticeWidget::ShowAlways) } */ }; Logic logic { { "encfs" / i18n("EncFS"), defaultSteps }, { "cryfs" / i18n("CryFS"), defaultSteps } }; Private(Vault *vault, VaultConfigurationWizard *parent) : q(parent) , vault(vault) { ui.setupUi(parent); ui.message->hide(); layout = new QStackedLayout(); layout->setContentsMargins(0, 0, 0, 0); ui.container->setLayout(layout); auto tabs = new QTabWidget(); layout->addWidget(tabs); // Loading the backends auto modules = logic[Key(vault->backend().toLatin1())]; Vault::Payload payload { { KEY_NAME, QVariant(vault->name()) }, { KEY_MOUNT_POINT, QVariant(vault->mountPoint()) }, { KEY_ACTIVITIES, QVariant(vault->activities()) }, { KEY_OFFLINEONLY, QVariant(vault->isOfflineOnly()) } }; for (const auto& module: modules) { DialogModule *stepWidget = new CompoundDialogModule(module); stepWidget->init(payload); tabs->addTab(stepWidget, module.title()); currentModuleDialogs << stepWidget; } } void setVaultOpened(bool vaultOpened) { bool configurationEnabled = !vaultOpened; ui.buttons->button(QDialogButtonBox::Ok)->setEnabled(configurationEnabled); ui.frameUnlockVault->setVisible(!configurationEnabled); ui.container->setEnabled(configurationEnabled); } void saveConfiguration() { Vault::Payload collectedPayload; qDebug() << "Getting the data"; for (const auto* module: currentModuleDialogs) { qDebug() << "Data: " << module->fields(); collectedPayload.unite(module->fields()); } const auto name = collectedPayload[KEY_NAME].toString(); const PlasmaVault::MountPoint mountPoint(collectedPayload[KEY_MOUNT_POINT].toString()); const auto activities = collectedPayload[KEY_ACTIVITIES].toStringList(); const auto isOfflineOnly = collectedPayload[KEY_OFFLINEONLY].toBool(); if (name.isEmpty() || mountPoint.isEmpty()) return; vault->setName(name); vault->setMountPoint(mountPoint); vault->setActivities(activities); vault->setIsOfflineOnly(isOfflineOnly); } }; VaultConfigurationWizard::VaultConfigurationWizard(Vault *vault, QWidget *parent) : QDialog(parent) , d(new Private(vault, this)) { setWindowTitle(i18n("Configure")); d->setVaultOpened(vault->isOpened()); connect(d->ui.buttonCloseVault, &QPushButton::clicked, this, [=] () { vault->close(); }); connect(vault, &Vault::isOpenedChanged, this, [=] (bool isOpened) { d->setVaultOpened(isOpened); }); connect(d->ui.buttons, &QDialogButtonBox::accepted, this, [=] { d->saveConfiguration(); }); } VaultConfigurationWizard::~VaultConfigurationWizard() { } diff --git a/kded/ui/vaultconfigurationwizard.ui b/kded/ui/vaultconfigurationwizard.ui index 0f5fc29..56148b2 100644 --- a/kded/ui/vaultconfigurationwizard.ui +++ b/kded/ui/vaultconfigurationwizard.ui @@ -1,120 +1,123 @@ VaultConfigurationWizard 0 0 646 529 Dialog + + + QFrame::StyledPanel QFrame::Raised The vault configuration can only be changed while it is closed. Qt::Horizontal 40 20 Close the vault false Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok KMessageWidget QFrame
kmessagewidget.h
buttons accepted() VaultConfigurationWizard accept() 254 522 157 274 buttons rejected() VaultConfigurationWizard reject() 322 522 286 274
diff --git a/kded/ui/vaultcreationwizard.cpp b/kded/ui/vaultcreationwizard.cpp index cc7088d..8c4742c 100644 --- a/kded/ui/vaultcreationwizard.cpp +++ b/kded/ui/vaultcreationwizard.cpp @@ -1,306 +1,313 @@ /* * Copyright 2017 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "vaultcreationwizard.h" #include "ui_vaultcreationwizard.h" #include #include #include #include #include "dialogdsl.h" #include "vault.h" using namespace DialogDsl; using namespace DialogDsl::operators; #include "backendchooserwidget.h" #include "activitieslinkingwidget.h" #include "cryfscypherchooserwidget.h" #include "directorypairchooserwidget.h" #include "noticewidget.h" #include "passwordchooserwidget.h" #include "offlineonlywidget.h" class VaultCreationWizard::Private { public: VaultCreationWizard *const q; Ui::VaultCreationWizard ui; QPushButton *buttonPrevious; QPushButton *buttonNext; QPushButton *buttonCreate; QStackedLayout *layout; inline void buttonNextSetEnabled(bool enabled) { buttonNext->setEnabled(enabled); buttonCreate->setEnabled(enabled); } QVector currentStepModules; steps currentSteps; BackendChooserWidget *firstStepModule = nullptr; DialogDsl::DialogModule *currentModule = nullptr; Logic logic { { "encfs" / i18n("EncFS"), { step { notice("encfs-message", i18n("Security notice:\n\ According to a security audit by Taylor Hornby (Defuse Security),\n\ the current implementation of Encfs is vulnerable or potentially vulnerable\n\ to multiple types of attacks.\n\ For example, an attacker with read/write access\n\ to encrypted data might lower the decryption complexity\n\ for subsequently encrypted data without this being noticed by a legitimate user,\n\ or might use timing analysis to deduce information.\n\

\n\ This means that you should not synchronize\n\ the encrypted data to a cloud storage service,\n\ or use it in other circumstances where the attacker\n\ can frequently access the encrypted data.\n\

\n\ See defuse.ca/audits/encfs.htm for more information.")) }, step { passwordChooser() }, - step { directoryPairChooser(DirectoryPairChooserWidget::RequireEmptyDirectories) }, + step { directoryPairChooser(DirectoryPairChooserWidget::SkipDevicePicker) }, step { activitiesChooser(), offlineOnlyChooser() } } }, { "cryfs" / i18n("CryFS"), { step { notice("cryfs-message", i18n("Security notice:\n\ CryFS encrypts your files, so you can safely store them anywhere.\n\ It works well together with cloud services like Dropbox, iCloud, OneDrive and others.\n\

\n\ Unlike some other file-system overlay solutions,\n\ it does not expose the directory structure,\n\ the number of files nor the file sizes\n\ through the encrypted data format.\n\

\n\ One important thing to note is that,\n\ while CryFS is considered safe,\n\ there is no independent security audit\n\ which confirms this.")) }, step { passwordChooser() }, - step { directoryPairChooser(DirectoryPairChooserWidget::RequireEmptyDirectories) }, + step { directoryPairChooser() }, step { cryfsCypherChooser(), activitiesChooser(), offlineOnlyChooser() } } } }; + // to suggest the highest priority to the user as a starting value + QMap priorities = { + { "encfs", 2 }, + { "cryfs", 1 } + }; + template QPushButton *addDialogButton(const QString &icon, const QString &title, ClickHandler clickHandler) { auto button = new QPushButton(QIcon::fromTheme(icon), title); ui.buttons->addButton(button, QDialogButtonBox::ActionRole); QObject::connect(button, &QPushButton::clicked, q, clickHandler); return button; } Private(VaultCreationWizard *parent) : q(parent) { ui.setupUi(parent); ui.message->hide(); layout = new QStackedLayout(); layout->setContentsMargins(0, 0, 0, 0); ui.container->setLayout(layout); // The dialog buttons do not have previous/next by default // so we need to create them buttonPrevious = addDialogButton("go-previous", i18n("Previous"), [this] { previousStep(); }); buttonNext = addDialogButton("go-next", i18n("Next"), [this] { nextStep(); }); buttonCreate = addDialogButton("dialog-ok-apply", i18n("Create"), [this] { createVault(); }); // The 'Create' button should be hidden by default buttonCreate->hide(); buttonPrevious->setEnabled(false); buttonNextSetEnabled(false); // Loading the fist page of the wizard firstStepModule = new BackendChooserWidget(); setCurrentModule(firstStepModule); layout->addWidget(firstStepModule); // Loading the backends to the combo box for (const auto& key: logic.keys()) { - firstStepModule->addItem(key, key.translation()); + firstStepModule->addItem(key, key.translation(), priorities.value(key)); } + firstStepModule->checkBackendAvailable(); } void setCurrentModule(DialogDsl::DialogModule *module) { // If there is a current module already, disconnect it if (currentModule) { currentModule->aboutToBeHidden(); currentModule->disconnect(); } // The current module needs to be changed currentModule = module; currentModule->aboutToBeShown(); QObject::connect( currentModule, &DialogModule::isValidChanged, q, [&] (bool valid) { buttonNextSetEnabled(valid); }); // Lets update the button states // 1. next/create button is enabled only if the current // module is in the valid state buttonNextSetEnabled(currentModule->isValid()); // 2. previous button is enabled only if we are not on // the first page buttonPrevious->setEnabled(currentStepModules.size() > 0); // 3. If we have loaded the last page, we want to show the // 'Create' button instead of 'Next' if (!currentSteps.isEmpty() && currentStepModules.size() == currentSteps.size()) { buttonNext->hide(); buttonCreate->show(); } else { buttonNext->show(); buttonCreate->hide(); } // Calling to initialize the module -- we are passing all the // previously collected data to it auto collectedPayload = firstStepModule->fields(); for (const auto* module: currentStepModules) { collectedPayload.unite(module->fields()); } currentModule->init(collectedPayload); } void previousStep() { if (currentStepModules.isEmpty()) return; // We want to kill the current module, and move to the previous one currentStepModules.takeLast(); currentModule->deleteLater();; if (currentStepModules.size()) { setCurrentModule(currentStepModules.last()); } else { setCurrentModule(firstStepModule); } if (!currentModule->shouldBeShown()) { previousStep(); } } void nextStep() { // If the step modules are empty, this means that we // have just started - the user chose the backend // and we need to load the vault creation steps if (currentStepModules.isEmpty()) { const auto &fields = firstStepModule->fields(); currentSteps = logic[fields[KEY_BACKEND].toByteArray()]; } // Loading the modulws that we need to show now auto subModules = currentSteps[currentStepModules.size()]; // If there is only one module on the current page, // lets not complicate things by creating the compound module DialogModule *stepWidget = (subModules.size() == 1) ? subModules.first()() : new CompoundDialogModule(subModules); // Adding the widget to the list and the layout currentStepModules << stepWidget; layout->addWidget(stepWidget); layout->setCurrentWidget(stepWidget); // Set the newly added module to be the current setCurrentModule(stepWidget); if (!currentModule->shouldBeShown()) { nextStep(); } } void createVault() { auto collectedPayload = firstStepModule->fields(); for (const auto* module: currentStepModules) { collectedPayload.unite(module->fields()); } const auto name = collectedPayload[KEY_NAME].toString(); const PlasmaVault::Device device(collectedPayload[KEY_DEVICE].toString()); const PlasmaVault::MountPoint mountPoint(collectedPayload[KEY_MOUNT_POINT].toString()); auto vault = new PlasmaVault::Vault(device, q); auto future = vault->create(name, mountPoint, collectedPayload); auto result = AsynQt::await(future); if (result) { emit q->createdVault(vault); q->QDialog::accept(); } else { ui.message->setText(result.error().message()); ui.message->setMessageType(KMessageWidget::Error); ui.message->show(); delete vault; } } }; VaultCreationWizard::VaultCreationWizard(QWidget *parent) : QDialog(parent) , d(new Private(this)) { - setWindowTitle(i18n("Create a New Vault")); + setWindowTitle(i18nc("@title:window", "Create a New Vault")); } VaultCreationWizard::~VaultCreationWizard() { } diff --git a/kded/ui/vaultcreationwizard.ui b/kded/ui/vaultcreationwizard.ui index 14f0539..419c954 100644 --- a/kded/ui/vaultcreationwizard.ui +++ b/kded/ui/vaultcreationwizard.ui @@ -1,81 +1,84 @@ VaultCreationWizard 0 0 646 529 Dialog + + + false Qt::Horizontal QDialogButtonBox::Cancel KMessageWidget QFrame
kmessagewidget.h
buttons accepted() VaultCreationWizard accept() 248 254 157 274 buttons rejected() VaultCreationWizard reject() 316 260 286 274
diff --git a/plasma/package/contents/ui/ActionItem.qml b/plasma/package/contents/ui/ActionItem.qml index 7cf3d01..3693170 100644 --- a/plasma/package/contents/ui/ActionItem.qml +++ b/plasma/package/contents/ui/ActionItem.qml @@ -1,77 +1,77 @@ /* * Copyright 2017 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras MouseArea { id: vaultItem property alias icon: actionIcon.source property alias text: actionText.text signal activated() hoverEnabled: true height: units.iconSizes.medium onContainsMouseChanged: { vaultItem.ListView.view.currentIndex = (containsMouse ? index : -1) } PlasmaCore.IconItem { id: actionIcon anchors { left: parent.left verticalCenter: parent.verticalCenter margins: units.smallSpacing } width: units.iconSizes.medium height: units.iconSizes.medium } PlasmaComponents.Label { id: actionText anchors { left: actionIcon.right + right: parent.right verticalCenter: parent.verticalCenter margins: units.smallSpacing } - width: parent.width height: undefined elide: Text.ElideRight } MouseArea { anchors.fill: parent onClicked: { vaultItem.activated(); } } } diff --git a/plasma/package/contents/ui/main.qml b/plasma/package/contents/ui/main.qml index 67efa46..969ecc8 100644 --- a/plasma/package/contents/ui/main.qml +++ b/plasma/package/contents/ui/main.qml @@ -1,104 +1,123 @@ /* * Copyright 2017 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras Item { + // For quick mockups in plasmoidviewer + // property var vaultsModel: ListModel { + // ListElement { + // name: "TestVault" + // icon: "folder-decrypted" + // device: "" + // mountPoint: "" + // isBusy: false + // isOpened: true + // activities: [] + // status: 1 // VaultInfo::Opened + // message: "" + // } + // } + property var vaultsModel: plasmoid.nativeInterface.vaultsModel property var vaultsModelActions: plasmoid.nativeInterface.vaultsModel.source() property var expandedItem: null Binding { target: plasmoid property: "busy" value: vaultsModelActions.isBusy } Binding { target: plasmoid property: "icon" value: { return vaultsModelActions.hasError ? "plasmavault_error" : "plasmavault"; } } Plasmoid.fullRepresentation: ColumnLayout { anchors { fill: parent } - Layout.minimumWidth: 200 - Layout.minimumHeight: 200 + Layout.minimumWidth: 300 * units.devicePixelRatio + Layout.minimumHeight: 200 * units.devicePixelRatio // PlasmaExtras.Heading { // text: i18nd("plasmavault-kde", "Encrypted vaults") // } ListView { id: vaultsList model: vaultsModel currentIndex: -1 Layout.fillWidth: true Layout.fillHeight: true highlight: PlasmaComponents.Highlight { id: highlight } delegate: VaultItem { icon: model.icon name: model.name message: model.message isOpened: model.isOpened device: model.device isOfflineOnly: model.isOfflineOnly width: parent.width onItemExpanded: { if (expandedItem != null) { expandedItem.collapse(); } expandedItem = item; } } + visible: count > 0 + interactive: false } PlasmaComponents.Button { id: buttonCreateNewVault - text: i18nd("plasmavault-kde", "Create a New Vault") + text: i18nd("plasmavault-kde", "Create a New Vault...") + iconSource: "list-add" onClicked: vaultsModelActions.requestNewVault() + Layout.alignment: vaultsList.visible ? Qt.AlignLeft : Qt.AlignCenter } } } diff --git a/plasma/package/metadata.desktop b/plasma/package/metadata.desktop index 6b26cd1..0957f8d 100644 --- a/plasma/package/metadata.desktop +++ b/plasma/package/metadata.desktop @@ -1,79 +1,93 @@ [Desktop Entry] Name=Vaults Name[ar]=الخزنات Name[ca]=Voltes Name[ca@valencia]=Voltes +Name[cs]=Sejfy +Name[da]=Sikkerhedsbokse +Name[de]=Vaults Name[el]=Θησαυροφυλάκια Name[en_GB]=Vaults Name[es]=Cajas fuertes Name[eu]=Kutxa gotorrak +Name[fi]=Holvit Name[fr]=Coffres-forts Name[gl]=Caixas fortes Name[hu]=Tárak Name[it]=Caveau +Name[ko]=비밀 공간 Name[nl]=Kluizen +Name[nn]=Datakvelv Name[pa]=ਵਾਲਟ -Name[pl]=Krypty +Name[pl]=Sejfy Name[pt]=Cofres Name[pt_BR]=Cofres +Name[ru]=Зашифрованные папки Name[sk]=Klenby Name[sl]=Shrambe Name[sr]=Трезори Name[sr@ijekavian]=Трезори Name[sr@ijekavianlatin]=Trezori Name[sr@latin]=Trezori Name[sv]=Valv Name[tr]=Kasalar Name[uk]=Сховища Name[x-test]=xxVaultsxx Name[zh_CN]=保险库 Comment=Create encrypted vaults Comment[ar]=أنشئ خزنات معمّاة Comment[ca]=Crea voltes encriptades Comment[ca@valencia]=Crea voltes encriptades +Comment[cs]=Vytvářejte šifrované sejfy +Comment[da]=Opret krypterede sikkerhedsbokse +Comment[de]=Verschlüsselte Vaults erstellen Comment[el]=Δημιουργία κρυπτογραφημένων θησαυροφυλακίων Comment[en_GB]=Create encrypted vaults Comment[es]=Crear cajas fuertes cifradas Comment[eu]=Sortu zifratutako kutxa gotorrak +Comment[fi]=Luo salattuja holveja Comment[fr]=Créer des coffres-forts chiffrés Comment[gl]=Crear caixas fortes cifradas. Comment[hu]=Titkosított tárak létrehozása Comment[it]=Crea caveau criptati +Comment[ko]=암호화된 비밀 공간 생성 Comment[nl]=Versleutelde kluizen aanmaken +Comment[nn]=Lag krypterte datakvelv Comment[pa]=ਇੰਕ੍ਰਿਪਟ ਕੀਤੇ ਵਾਲਟ ਬਣਾਓ -Comment[pl]=Tworzy zaszyfrowane krypty +Comment[pl]=Tworzy zaszyfrowane sejfy Comment[pt]=Criar cofres encriptados Comment[pt_BR]=Cria cofres criptografados +Comment[ru]=Создание зашифрованных папок Comment[sk]=Vytvoriť šifrované klenby Comment[sl]=Ustvari šifrirane shrambe Comment[sr]=Правите шифроване трезоре Comment[sr@ijekavian]=Правите шифроване трезоре Comment[sr@ijekavianlatin]=Pravite šifrovane trezore Comment[sr@latin]=Pravite šifrovane trezore Comment[sv]=Skapa krypterade valv Comment[tr]=Şifrelenmiş kasa oluştur Comment[uk]=Створення шифрованих сховищ Comment[x-test]=xxCreate encrypted vaultsxx Comment[zh_CN]=创建加密的保险库 Icon=plasmavault Type=Service ServiceTypes=Plasma/Applet X-KDE-Library=plasma_applet_vault X-KDE-PluginInfo-Author=Ivan Čukić X-KDE-PluginInfo-Email=ivan.cukic at kde.org X-KDE-PluginInfo-Name=org.kde.plasma.vault X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category=Utilities X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-Plasma-NotificationArea=true X-Plasma-NotificationAreaCategory=SystemServices X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml