diff --git a/kded/secretagent.cpp b/kded/secretagent.cpp index def38d4b..897bed97 100644 --- a/kded/secretagent.cpp +++ b/kded/secretagent.cpp @@ -1,650 +1,652 @@ /* Copyright 2013 Jan Grulich Copyright 2013 Lukas Tinkl Copyright 2013 by Daniel Nicoletti This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "passworddialog.h" #include "secretagent.h" #include "debug.h" #include "configuration.h" #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include SecretAgent::SecretAgent(QObject* parent) : NetworkManager::SecretAgent("org.kde.plasma.networkmanagement", parent) , m_openWalletFailed(false) , m_wallet(nullptr) , m_dialog(nullptr) { connect(NetworkManager::notifier(), &NetworkManager::Notifier::serviceDisappeared, this, &SecretAgent::killDialogs); // We have to import secrets previously stored in plaintext files importSecretsFromPlainTextFiles(); } SecretAgent::~SecretAgent() { } NMVariantMapMap SecretAgent::GetSecrets(const NMVariantMapMap &connection, const QDBusObjectPath &connection_path, const QString &setting_name, const QStringList &hints, uint flags) { qCDebug(PLASMA_NM) << Q_FUNC_INFO; qCDebug(PLASMA_NM) << "Path:" << connection_path.path(); qCDebug(PLASMA_NM) << "Setting name:" << setting_name; qCDebug(PLASMA_NM) << "Hints:" << hints; qCDebug(PLASMA_NM) << "Flags:" << flags; const QString callId = connection_path.path() % setting_name; for (const SecretsRequest &request : m_calls) { if (request == callId) { qCWarning(PLASMA_NM) << "GetSecrets was called again! This should not happen, cancelling first call" << connection_path.path() << setting_name; CancelGetSecrets(connection_path, setting_name); break; } } setDelayedReply(true); SecretsRequest request(SecretsRequest::GetSecrets); request.callId = callId; request.connection = connection; request.connection_path = connection_path; request.flags = static_cast(flags); request.hints = hints; request.setting_name = setting_name; request.message = message(); m_calls << request; processNext(); return NMVariantMapMap(); } void SecretAgent::SaveSecrets(const NMVariantMapMap &connection, const QDBusObjectPath &connection_path) { qCDebug(PLASMA_NM) << Q_FUNC_INFO; qCDebug(PLASMA_NM) << "Path:" << connection_path.path(); // qCDebug(PLASMA_NM) << "Setting:" << connection; setDelayedReply(true); SecretsRequest::Type type; if (hasSecrets(connection)) { type = SecretsRequest::SaveSecrets; } else { type = SecretsRequest::DeleteSecrets; } SecretsRequest request(type); request.connection = connection; request.connection_path = connection_path; request.message = message(); m_calls << request; processNext(); } void SecretAgent::DeleteSecrets(const NMVariantMapMap &connection, const QDBusObjectPath &connection_path) { qCDebug(PLASMA_NM) << Q_FUNC_INFO; qCDebug(PLASMA_NM) << "Path:" << connection_path.path(); // qCDebug(PLASMA_NM) << "Setting:" << connection; setDelayedReply(true); SecretsRequest request(SecretsRequest::DeleteSecrets); request.connection = connection; request.connection_path = connection_path; request.message = message(); m_calls << request; processNext(); } void SecretAgent::CancelGetSecrets(const QDBusObjectPath &connection_path, const QString &setting_name) { qCDebug(PLASMA_NM) << Q_FUNC_INFO; qCDebug(PLASMA_NM) << "Path:" << connection_path.path(); qCDebug(PLASMA_NM) << "Setting name:" << setting_name; QString callId = connection_path.path() % setting_name; for (int i = 0; i < m_calls.size(); ++i) { SecretsRequest request = m_calls.at(i); if (request.type == SecretsRequest::GetSecrets && callId == request.callId) { if (m_dialog == request.dialog) { m_dialog = nullptr; } delete request.dialog; sendError(SecretAgent::AgentCanceled, QLatin1String("Agent canceled the password dialog"), request.message); m_calls.removeAt(i); break; } } processNext(); } void SecretAgent::dialogAccepted() { for (int i = 0; i < m_calls.size(); ++i) { SecretsRequest request = m_calls[i]; if (request.type == SecretsRequest::GetSecrets && request.dialog == m_dialog) { NMStringMap tmpOpenconnectSecrets; NMVariantMapMap connection = request.dialog->secrets(); if (connection.contains(QLatin1String("vpn"))) { if (connection.value(QStringLiteral("vpn")).contains(QLatin1String("tmp-secrets"))) { QVariantMap vpnSetting = connection.value(QLatin1String("vpn")); tmpOpenconnectSecrets = qdbus_cast(vpnSetting.take(QLatin1String("tmp-secrets"))); connection.insert(QLatin1String("vpn"), vpnSetting); } } sendSecrets(connection, request.message); NetworkManager::ConnectionSettings::Ptr connectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(connection)); NetworkManager::ConnectionSettings::Ptr completeConnectionSettings; NetworkManager::Connection::Ptr con = NetworkManager::findConnectionByUuid(connectionSettings->uuid()); if (con) { completeConnectionSettings = con->settings(); } else { completeConnectionSettings = connectionSettings; } if (request.saveSecretsWithoutReply && completeConnectionSettings->connectionType() != NetworkManager::ConnectionSettings::Vpn) { bool requestOffline = true; if (completeConnectionSettings->connectionType() == NetworkManager::ConnectionSettings::Gsm) { NetworkManager::GsmSetting::Ptr gsmSetting = completeConnectionSettings->setting(NetworkManager::Setting::Gsm).staticCast(); if (gsmSetting) { if (gsmSetting->passwordFlags().testFlag(NetworkManager::Setting::NotSaved) || gsmSetting->passwordFlags().testFlag(NetworkManager::Setting::NotRequired)) { requestOffline = false; } else if (gsmSetting->pinFlags().testFlag(NetworkManager::Setting::NotSaved) || gsmSetting->pinFlags().testFlag(NetworkManager::Setting::NotRequired)) { requestOffline = false; } } } else if (completeConnectionSettings->connectionType() == NetworkManager::ConnectionSettings::Wireless) { NetworkManager::WirelessSecuritySetting::Ptr wirelessSecuritySetting = completeConnectionSettings->setting(NetworkManager::Setting::WirelessSecurity).staticCast(); if (wirelessSecuritySetting && wirelessSecuritySetting->keyMgmt() == NetworkManager::WirelessSecuritySetting::WpaEap) { NetworkManager::Security8021xSetting::Ptr security8021xSetting = completeConnectionSettings->setting(NetworkManager::Setting::Security8021x).staticCast(); if (security8021xSetting) { if (security8021xSetting->eapMethods().contains(NetworkManager::Security8021xSetting::EapMethodFast) || security8021xSetting->eapMethods().contains(NetworkManager::Security8021xSetting::EapMethodTtls) || security8021xSetting->eapMethods().contains(NetworkManager::Security8021xSetting::EapMethodPeap)) { if (security8021xSetting->passwordFlags().testFlag(NetworkManager::Setting::NotSaved) || security8021xSetting->passwordFlags().testFlag(NetworkManager::Setting::NotRequired)) { requestOffline = false; } } } } } if (requestOffline) { SecretsRequest requestOffline(SecretsRequest::SaveSecrets); requestOffline.connection = connection; requestOffline.connection_path = request.connection_path; requestOffline.saveSecretsWithoutReply = true; m_calls << requestOffline; } } else if (request.saveSecretsWithoutReply && completeConnectionSettings->connectionType() == NetworkManager::ConnectionSettings::Vpn && !tmpOpenconnectSecrets.isEmpty()) { NetworkManager::VpnSetting::Ptr vpnSetting = completeConnectionSettings->setting(NetworkManager::Setting::Vpn).staticCast(); if (vpnSetting) { NMStringMap data = vpnSetting->data(); NMStringMap secrets = vpnSetting->secrets(); // Load secrets from auth dialog which are returned back to NM if (connection.value(QLatin1String("vpn")).contains(QLatin1String("secrets"))) { secrets.unite(qdbus_cast(connection.value(QLatin1String("vpn")).value(QLatin1String("secrets")))); } // Load temporary secrets from auth dialog which are not returned to NM for (const QString &key : tmpOpenconnectSecrets.keys()) { if (secrets.contains(QLatin1Literal("save_passwords")) && secrets.value(QLatin1Literal("save_passwords")) == QLatin1String("yes")) { data.insert(key + QLatin1String("-flags"), QString::number(NetworkManager::Setting::AgentOwned)); } else { data.insert(key + QLatin1String("-flags"), QString::number(NetworkManager::Setting::NotSaved)); } secrets.insert(key, tmpOpenconnectSecrets.value(key)); } vpnSetting->setData(data); vpnSetting->setSecrets(secrets); if (!con) { con = NetworkManager::findConnection(request.connection_path.path()); } if (con) { con->update(completeConnectionSettings->toMap()); } } } m_calls.removeAt(i); break; } } m_dialog->deleteLater(); m_dialog = nullptr; processNext(); } void SecretAgent::dialogRejected() { for (int i = 0; i < m_calls.size(); ++i) { SecretsRequest request = m_calls[i]; if (request.type == SecretsRequest::GetSecrets && request.dialog == m_dialog) { sendError(SecretAgent::UserCanceled, QLatin1String("User canceled the password dialog"), request.message); m_calls.removeAt(i); break; } } m_dialog->deleteLater(); m_dialog = nullptr; processNext(); } void SecretAgent::killDialogs() { int i = 0; while (i < m_calls.size()) { SecretsRequest request = m_calls[i]; if (request.type == SecretsRequest::GetSecrets) { delete request.dialog; m_calls.removeAt(i); } ++i; } } void SecretAgent::walletOpened(bool success) { if (!success) { m_openWalletFailed = true; m_wallet->deleteLater(); m_wallet = nullptr; } else { m_openWalletFailed = false; } processNext(); } void SecretAgent::walletClosed() { if (m_wallet) { m_wallet->deleteLater(); } m_wallet = nullptr; } void SecretAgent::processNext() { int i = 0; while (i < m_calls.size()) { SecretsRequest &request = m_calls[i]; switch (request.type) { case SecretsRequest::GetSecrets: if (processGetSecrets(request)) { m_calls.removeAt(i); continue; } break; case SecretsRequest::SaveSecrets: if (processSaveSecrets(request)) { m_calls.removeAt(i); continue; } break; case SecretsRequest::DeleteSecrets: if (processDeleteSecrets(request)) { m_calls.removeAt(i); continue; } break; } ++i; } } bool SecretAgent::processGetSecrets(SecretsRequest &request) const { if (m_dialog) { return false; } NetworkManager::ConnectionSettings::Ptr connectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(request.connection)); NetworkManager::Setting::Ptr setting = connectionSettings->setting(request.setting_name); const bool requestNew = request.flags & RequestNew; const bool userRequested = request.flags & UserRequested; const bool allowInteraction = request.flags & AllowInteraction; const bool isVpn = (setting->type() == NetworkManager::Setting::Vpn); if (isVpn) { NetworkManager::VpnSetting::Ptr vpnSetting = connectionSettings->setting(NetworkManager::Setting::Vpn).dynamicCast(); if (vpnSetting->serviceType() == QLatin1String("org.freedesktop.NetworkManager.ssh") && vpnSetting->data()["auth-type"] == QLatin1String("ssh-agent")) { QString authSock = qgetenv("SSH_AUTH_SOCK"); qCDebug(PLASMA_NM) << Q_FUNC_INFO << "Sending SSH auth socket" << authSock; if (authSock.isEmpty()) { sendError(SecretAgent::NoSecrets, QLatin1String("SSH_AUTH_SOCK not present"), request.message); } else { NMStringMap secrets; secrets.insert(QLatin1String("ssh-auth-sock"), authSock); QVariantMap secretData; secretData.insert(QLatin1String("secrets"), QVariant::fromValue(secrets)); request.connection[request.setting_name] = secretData; sendSecrets(request.connection, request.message); } return true; } } NMStringMap secretsMap; if (!requestNew && useWallet()) { if (m_wallet->isOpen()) { if (m_wallet->hasFolder("Network Management") && m_wallet->setFolder("Network Management")) { QString key = QLatin1Char('{') % connectionSettings->uuid() % QLatin1Char('}') % QLatin1Char(';') % request.setting_name; m_wallet->readMap(key, secretsMap); } } else { qCDebug(PLASMA_NM) << Q_FUNC_INFO << "Waiting for the wallet to open"; return false; } } if (!secretsMap.isEmpty()) { setting->secretsFromStringMap(secretsMap); if (!isVpn && setting->needSecrets(requestNew).isEmpty()) { // Enough secrets were retrieved from storage request.connection[request.setting_name] = setting->secretsToMap(); sendSecrets(request.connection, request.message); return true; } } if (!Configuration::showPasswordDialog()) { sendError(SecretAgent::NoSecrets, "Cannot authenticate", request.message); + emit secretsError(request.connection_path.path(), i18n("Authentication to %1 failed. Wrong password?", request.connection.value("connection").value("id").toString())); return true; } else if (requestNew || (allowInteraction && !setting->needSecrets(requestNew).isEmpty()) || (allowInteraction && userRequested) || (isVpn && allowInteraction)) { m_dialog = new PasswordDialog(connectionSettings, request.flags, request.setting_name); connect(m_dialog, &PasswordDialog::accepted, this, &SecretAgent::dialogAccepted); connect(m_dialog, &PasswordDialog::rejected, this, &SecretAgent::dialogRejected); if (m_dialog->hasError()) { sendError(m_dialog->error(), m_dialog->errorMessage(), request.message); delete m_dialog; m_dialog = nullptr; return true; } else { request.dialog = m_dialog; request.saveSecretsWithoutReply = !connectionSettings->permissions().isEmpty(); m_dialog->show(); KWindowSystem::setState(m_dialog->winId(), NET::KeepAbove); KWindowSystem::forceActiveWindow(m_dialog->winId()); return false; } } else if (isVpn && userRequested) { // just return what we have NMVariantMapMap result; NetworkManager::VpnSetting::Ptr vpnSetting; vpnSetting = connectionSettings->setting(NetworkManager::Setting::Vpn).dynamicCast(); //FIXME workaround when NM is asking for secrets which should be system-stored, if we send an empty map it // won't ask for additional secrets with AllowInteraction flag which would display the authentication dialog if (vpnSetting->secretsToMap().isEmpty()) { // Insert an empty secrets map as it was before I fixed it in NetworkManagerQt to make sure NM will ask again // with flags we need QVariantMap secretsMap; secretsMap.insert(QLatin1String("secrets"), QVariant::fromValue(NMStringMap())); result.insert("vpn", secretsMap); } else { result.insert("vpn", vpnSetting->secretsToMap()); } sendSecrets(result, request.message); return true; } else if (setting->needSecrets().isEmpty()) { NMVariantMapMap result; result.insert(setting->name(), setting->secretsToMap()); sendSecrets(result, request.message); return true; } else { sendError(SecretAgent::InternalError, QLatin1String("Plasma-nm did not know how to handle the request"), request.message); return true; } } bool SecretAgent::processSaveSecrets(SecretsRequest &request) const { if (useWallet()) { if (m_wallet->isOpen()) { NetworkManager::ConnectionSettings connectionSettings(request.connection); if (!m_wallet->hasFolder("Network Management")) { m_wallet->createFolder("Network Management"); } if (m_wallet->setFolder("Network Management")) { for (const NetworkManager::Setting::Ptr &setting : connectionSettings.settings()) { NMStringMap secretsMap = setting->secretsToStringMap(); if (!secretsMap.isEmpty()) { QString entryName = QLatin1Char('{') % connectionSettings.uuid() % QLatin1Char('}') % QLatin1Char(';') % setting->name(); m_wallet->writeMap(entryName, secretsMap); } } } else if (!request.saveSecretsWithoutReply) { sendError(SecretAgent::InternalError, QLatin1String("Could not store secrets in the wallet."), request.message); return true; } } else { qCDebug(PLASMA_NM) << Q_FUNC_INFO << "Waiting for the wallet to open"; return false; } } if (!request.saveSecretsWithoutReply) { QDBusMessage reply = request.message.createReply(); if (!QDBusConnection::systemBus().send(reply)) { qCWarning(PLASMA_NM) << "Failed put save secrets reply into the queue"; } } return true; } bool SecretAgent::processDeleteSecrets(SecretsRequest &request) const { if (useWallet()) { if (m_wallet->isOpen()) { if (m_wallet->hasFolder("Network Management") && m_wallet->setFolder("Network Management")) { NetworkManager::ConnectionSettings connectionSettings(request.connection); for (const NetworkManager::Setting::Ptr &setting : connectionSettings.settings()) { QString entryName = QLatin1Char('{') % connectionSettings.uuid() % QLatin1Char('}') % QLatin1Char(';') % setting->name(); for (const QString &entry : m_wallet->entryList()) { if (entry.startsWith(entryName)) { m_wallet->removeEntry(entryName); } } } } } else { qCDebug(PLASMA_NM) << Q_FUNC_INFO << "Waiting for the wallet to open"; return false; } } QDBusMessage reply = request.message.createReply(); if (!QDBusConnection::systemBus().send(reply)) { qCWarning(PLASMA_NM) << "Failed put delete secrets reply into the queue"; } return true; } bool SecretAgent::useWallet() const { if (m_wallet) { return true; } /* If opening of KWallet failed before, we should not try to open it again and * we should return false instead */ if (m_openWalletFailed) { m_openWalletFailed = false; return false; } if (KWallet::Wallet::isEnabled()) { m_wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), 0, KWallet::Wallet::Asynchronous); if (m_wallet) { connect(m_wallet, &KWallet::Wallet::walletOpened, this, &SecretAgent::walletOpened); connect(m_wallet, &KWallet::Wallet::walletClosed, this, &SecretAgent::walletClosed); return true; } else { qCWarning(PLASMA_NM) << "Error opening kwallet."; } } else if (m_wallet) { m_wallet->deleteLater(); m_wallet = nullptr; } return false; } bool SecretAgent::hasSecrets(const NMVariantMapMap &connection) const { NetworkManager::ConnectionSettings connectionSettings(connection); for (const NetworkManager::Setting::Ptr &setting : connectionSettings.settings()) { if (!setting->secretsToMap().isEmpty()) { return true; } } return false; } void SecretAgent::sendSecrets(const NMVariantMapMap &secrets, const QDBusMessage &message) const { QDBusMessage reply; reply = message.createReply(QVariant::fromValue(secrets)); if (!QDBusConnection::systemBus().send(reply)) { qCWarning(PLASMA_NM) << "Failed put the secret into the queue"; } } void SecretAgent::importSecretsFromPlainTextFiles() { KConfig config(QLatin1String("plasma-networkmanagement"), KConfig::SimpleConfig); // No action is required when the list of secrets is empty if (!config.groupList().isEmpty()) { for (const QString &groupName : config.groupList()) { QString loadedUuid = groupName.split(';').first().remove('{').remove('}'); QString loadedSettingType = groupName.split(';').last(); NetworkManager::Connection::Ptr connection = NetworkManager::findConnectionByUuid(loadedUuid); if (connection) { NetworkManager::Setting::SecretFlags secretFlags = KWallet::Wallet::isEnabled() ? NetworkManager::Setting::AgentOwned : NetworkManager::Setting::None; QMap secrets = config.entryMap(groupName); NMVariantMapMap settings = connection->settings()->toMap(); for (const QString &setting : settings.keys()) { if (setting == QLatin1String("vpn")) { NetworkManager::VpnSetting::Ptr vpnSetting = connection->settings()->setting(NetworkManager::Setting::Vpn).staticCast(); if (vpnSetting) { // Add loaded secrets from the config file vpnSetting->secretsFromStringMap(secrets); NMStringMap vpnData = vpnSetting->data(); // Reset flags, we can't save secrets to our secret agent when KWallet is not enabled, because // we dropped support for plaintext files, therefore they need to be stored to NetworkManager for (const QString &key : vpnData.keys()) { if (key.endsWith(QLatin1String("-flags"))) { vpnData.insert(key, QString::number((int)secretFlags)); } } vpnSetting->setData(vpnData); settings.insert(setting, vpnSetting->toMap()); connection->update(settings); } } else { if (setting == loadedSettingType) { QVariantMap tmpSetting = settings.value(setting); // Reset flags, we can't save secrets to our secret agent when KWallet is not enabled, because // we dropped support for plaintext files, therefore they need to be stored to NetworkManager for (const QString &key : tmpSetting.keys()) { if (key.endsWith(QLatin1String("-flags"))) { tmpSetting.insert(key, (int)secretFlags); } } // Add loaded secrets from the config file QMap::const_iterator it = secrets.constBegin(); QMap::const_iterator end = secrets.constEnd(); for (; it != end; ++it) { tmpSetting.insert(it.key(), it.value()); } // Replace the old setting with the new one settings.insert(setting, tmpSetting); // Update the connection which re-saves secrets connection->update(settings); } } } } // Remove the group KConfigGroup group(&config, groupName); group.deleteGroup(); } } } diff --git a/kded/secretagent.h b/kded/secretagent.h index 7ed25060..8129f85d 100644 --- a/kded/secretagent.h +++ b/kded/secretagent.h @@ -1,125 +1,128 @@ /* Copyright 2013 Jan Grulich Copyright 2013 Lukas Tinkl Copyright 2013 by Daniel Nicoletti This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef PLASMA_NM_SECRET_AGENT_H #define PLASMA_NM_SECRET_AGENT_H #include namespace KWallet { class Wallet; } class PasswordDialog; class SecretsRequest { public: enum Type { GetSecrets, SaveSecrets, DeleteSecrets }; explicit SecretsRequest(Type _type) : type(_type), flags(NetworkManager::SecretAgent::None), saveSecretsWithoutReply(false), dialog(nullptr) {} inline bool operator==(const QString &other) const { return callId == other; } Type type; QString callId; NMVariantMapMap connection; QDBusObjectPath connection_path; QString setting_name; QStringList hints; NetworkManager::SecretAgent::GetSecretsFlags flags; /** * When a user connection is called on GetSecrets, * the secret agent is supposed to save the secrets * typed by user, when true proccessSaveSecrets * should skip the DBus reply. */ bool saveSecretsWithoutReply; QDBusMessage message; PasswordDialog *dialog; }; class Q_DECL_EXPORT SecretAgent : public NetworkManager::SecretAgent { Q_OBJECT public: explicit SecretAgent(QObject* parent = nullptr); ~SecretAgent() override; +Q_SIGNALS: + void secretsError(const QString &connectionPath, const QString &message) const; + public Q_SLOTS: NMVariantMapMap GetSecrets(const NMVariantMapMap&, const QDBusObjectPath&, const QString&, const QStringList&, uint) override; void SaveSecrets(const NMVariantMapMap &connection, const QDBusObjectPath &connection_path) override; void DeleteSecrets(const NMVariantMapMap &, const QDBusObjectPath &) override; void CancelGetSecrets(const QDBusObjectPath &, const QString &) override; private Q_SLOTS: void dialogAccepted(); void dialogRejected(); void killDialogs(); void walletOpened(bool success); void walletClosed(); private: void processNext(); /** * @brief processGetSecrets requests * @param request the request we are processing * @param ignoreWallet true if the code should avoid Wallet * normally if it failed to open * @return true if the item was processed */ bool processGetSecrets(SecretsRequest &request) const; bool processSaveSecrets(SecretsRequest &request) const; bool processDeleteSecrets(SecretsRequest &request) const; /** * @brief useWallet checks if the KWallet system is enabled * and tries to open it async. * @return return true if the method should use the wallet, * the caller MUST always check if the wallet is opened. */ bool useWallet() const; /** * @brief hasSecrets verifies if the desired connection has secrets to store * @param connection map with or without secrets * @return true if the connection has secrets, false otherwise */ bool hasSecrets(const NMVariantMapMap &connection) const; void sendSecrets(const NMVariantMapMap &secrets, const QDBusMessage &message) const; mutable bool m_openWalletFailed; mutable KWallet::Wallet *m_wallet; mutable PasswordDialog *m_dialog; QList m_calls; void importSecretsFromPlainTextFiles(); }; #endif // PLASMA_NM_SECRET_AGENT_H diff --git a/kded/service.cpp b/kded/service.cpp index 4b92724f..75494cb9 100644 --- a/kded/service.cpp +++ b/kded/service.cpp @@ -1,91 +1,92 @@ /* Copyright 2009 Dario Freddi Copyright 2009 Will Stephenson Copyright 2011-2012 Lamarque V. Souza Copyright 2013-2014 Jan Grulich 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 "secretagent.h" #include "notification.h" #include "monitor.h" #include "portalmonitor.h" #include #include #include #include #include K_PLUGIN_CLASS_WITH_JSON(NetworkManagementService, "networkmanagement.json") class NetworkManagementServicePrivate { public: SecretAgent *agent = nullptr; Notification *notification = nullptr; Monitor *monitor = nullptr; PortalMonitor *portalMonitor = nullptr; }; NetworkManagementService::NetworkManagementService(QObject * parent, const QVariantList&) : KDEDModule(parent), d_ptr(new NetworkManagementServicePrivate) { Q_D(NetworkManagementService); connect(this, &KDEDModule::moduleRegistered, this, &NetworkManagementService::slotRegistered); } NetworkManagementService::~NetworkManagementService() { delete d_ptr; } void NetworkManagementService::init() { Q_D(NetworkManagementService); if (!d->agent) { d->agent = new SecretAgent(this); + connect(d->agent, &SecretAgent::secretsError, this, &NetworkManagementService::secretsError); } if (!d->notification) { d->notification = new Notification(this); } if (!d->monitor) { d->monitor = new Monitor(this); } if (!d->portalMonitor) { d->portalMonitor = new PortalMonitor(this); } } void NetworkManagementService::slotRegistered(const QDBusObjectPath &path) { if (path.path() == QLatin1String("/modules/networkmanagement")) { Q_EMIT registered(); } } #include "service.moc" diff --git a/kded/service.h b/kded/service.h index a865e3c8..654ce8d7 100644 --- a/kded/service.h +++ b/kded/service.h @@ -1,56 +1,58 @@ /* Copyright 2009 Dario Freddi Copyright 2009 Will Stephenson Copyright 2012 Lamarque V. Souza Copyright 2013 Lukas Tinkl Copyright 2013-2014 Jan Grulich 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 PLASMANM_KDED_SERVICE_H #define PLASMANM_KDED_SERVICE_H #include #include class NetworkManagementServicePrivate; class Q_DECL_EXPORT NetworkManagementService : public KDEDModule { Q_CLASSINFO("D-Bus Interface", "org.kde.plasmanetworkmanagement") Q_OBJECT Q_DECLARE_PRIVATE(NetworkManagementService) public: NetworkManagementService(QObject * parent, const QVariantList&); ~NetworkManagementService() override; public Q_SLOTS: Q_SCRIPTABLE void init(); Q_SIGNALS: Q_SCRIPTABLE void registered(); + Q_SCRIPTABLE + void secretsError(const QString &connectionPath, const QString &message); private Q_SLOTS: void slotRegistered(const QDBusObjectPath &path); private: NetworkManagementServicePrivate * const d_ptr; }; #endif // PLASMANM_KDED_SERVICE_H diff --git a/libs/handler.cpp b/libs/handler.cpp index fd3bc43c..90b8999b 100644 --- a/libs/handler.cpp +++ b/libs/handler.cpp @@ -1,554 +1,567 @@ /* Copyright 2013-2014 Jan Grulich This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "handler.h" #include "connectioneditordialog.h" #include "uiutils.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if WITH_MODEMMANAGER_SUPPORT #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #define AGENT_SERVICE "org.kde.kded5" #define AGENT_PATH "/modules/networkmanagement" #define AGENT_IFACE "org.kde.plasmanetworkmanagement" Handler::Handler(QObject *parent) : QObject(parent) , m_tmpWirelessEnabled(NetworkManager::isWirelessEnabled()) , m_tmpWwanEnabled(NetworkManager::isWwanEnabled()) { initKdedModule(); QDBusConnection::sessionBus().connect(QStringLiteral(AGENT_SERVICE), QStringLiteral(AGENT_PATH), QStringLiteral(AGENT_IFACE), QStringLiteral("registered"), this, SLOT(initKdedModule())); + QDBusConnection::sessionBus().connect(QStringLiteral(AGENT_SERVICE), + QStringLiteral(AGENT_PATH), + QStringLiteral(AGENT_IFACE), + QStringLiteral("secretsError"), + this, SLOT(secretAgentError(QString, QString))); + // Interval (in ms) between attempts to scan for wifi networks m_wirelessScanRetryTimer.setInterval(2000); m_wirelessScanRetryTimer.setSingleShot(true); } Handler::~Handler() { } void Handler::activateConnection(const QString& connection, const QString& device, const QString& specificObject) { NetworkManager::Connection::Ptr con = NetworkManager::findConnection(connection); if (!con) { qCWarning(PLASMA_NM) << "Not possible to activate this connection"; return; } if (con->settings()->connectionType() == NetworkManager::ConnectionSettings::Vpn) { NetworkManager::VpnSetting::Ptr vpnSetting = con->settings()->setting(NetworkManager::Setting::Vpn).staticCast(); if (vpnSetting) { qCDebug(PLASMA_NM) << "Checking VPN" << con->name() << "type:" << vpnSetting->serviceType(); // get the list of supported VPN service types const KService::List services = KServiceTypeTrader::self()->query("PlasmaNetworkManagement/VpnUiPlugin", QString::fromLatin1("[X-NetworkManager-Services]=='%1'").arg(vpnSetting->serviceType())); if (services.isEmpty()) { qCWarning(PLASMA_NM) << "VPN" << vpnSetting->serviceType() << "not found, skipping"; KNotification *notification = new KNotification("MissingVpnPlugin", KNotification::CloseOnTimeout, this); notification->setComponentName("networkmanagement"); notification->setTitle(con->name()); notification->setText(i18n("Missing VPN plugin")); notification->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(KIconLoader::SizeHuge)); notification->sendEvent(); return; } } } #if WITH_MODEMMANAGER_SUPPORT if (con->settings()->connectionType() == NetworkManager::ConnectionSettings::Gsm) { NetworkManager::ModemDevice::Ptr nmModemDevice = NetworkManager::findNetworkInterface(device).objectCast(); if (nmModemDevice) { ModemManager::ModemDevice::Ptr mmModemDevice = ModemManager::findModemDevice(nmModemDevice->udi()); if (mmModemDevice) { ModemManager::Modem::Ptr modem = mmModemDevice->interface(ModemManager::ModemDevice::ModemInterface).objectCast(); NetworkManager::GsmSetting::Ptr gsmSetting = con->settings()->setting(NetworkManager::Setting::Gsm).staticCast(); if (gsmSetting && gsmSetting->pinFlags() == NetworkManager::Setting::NotSaved && modem && modem->unlockRequired() > MM_MODEM_LOCK_NONE) { QDBusInterface managerIface("org.kde.plasmanetworkmanagement", "/org/kde/plasmanetworkmanagement", "org.kde.plasmanetworkmanagement", QDBusConnection::sessionBus(), this); managerIface.call("unlockModem", mmModemDevice->uni()); connect(modem.data(), &ModemManager::Modem::unlockRequiredChanged, this, &Handler::unlockRequiredChanged); m_tmpConnectionPath = connection; m_tmpDevicePath = device; m_tmpSpecificPath = specificObject; return; } } } } #endif QDBusPendingReply reply = NetworkManager::activateConnection(connection, device, specificObject); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); watcher->setProperty("action", Handler::ActivateConnection); watcher->setProperty("connection", con->name()); connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished); } void Handler::addAndActivateConnection(const QString& device, const QString& specificObject, const QString& password) { NetworkManager::AccessPoint::Ptr ap; NetworkManager::WirelessDevice::Ptr wifiDev; for (const NetworkManager::Device::Ptr &dev : NetworkManager::networkInterfaces()) { if (dev->type() == NetworkManager::Device::Wifi) { wifiDev = dev.objectCast(); ap = wifiDev->findAccessPoint(specificObject); if (ap) { break; } } } if (!ap) { return; } NetworkManager::ConnectionSettings::Ptr settings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Wireless)); settings->setId(ap->ssid()); settings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); settings->setAutoconnect(true); settings->addToPermissions(KUser().loginName(), QString()); NetworkManager::WirelessSetting::Ptr wifiSetting = settings->setting(NetworkManager::Setting::Wireless).dynamicCast(); wifiSetting->setInitialized(true); wifiSetting = settings->setting(NetworkManager::Setting::Wireless).dynamicCast(); wifiSetting->setSsid(ap->ssid().toUtf8()); if (ap->mode() == NetworkManager::AccessPoint::Adhoc) { wifiSetting->setMode(NetworkManager::WirelessSetting::Adhoc); } NetworkManager::WirelessSecuritySetting::Ptr wifiSecurity = settings->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); NetworkManager::WirelessSecurityType securityType = NetworkManager::findBestWirelessSecurity(wifiDev->wirelessCapabilities(), true, (ap->mode() == NetworkManager::AccessPoint::Adhoc), ap->capabilities(), ap->wpaFlags(), ap->rsnFlags()); if (securityType != NetworkManager::NoneSecurity) { wifiSecurity->setInitialized(true); wifiSetting->setSecurity("802-11-wireless-security"); } if (securityType == NetworkManager::Leap || securityType == NetworkManager::DynamicWep || securityType == NetworkManager::Wpa2Eap || securityType == NetworkManager::WpaEap) { if (securityType == NetworkManager::DynamicWep || securityType == NetworkManager::Leap) { wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::Ieee8021x); if (securityType == NetworkManager::Leap) { wifiSecurity->setAuthAlg(NetworkManager::WirelessSecuritySetting::Leap); } } else { wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaEap); } m_tmpConnectionUuid = settings->uuid(); m_tmpDevicePath = device; m_tmpSpecificPath = specificObject; QPointer editor = new ConnectionEditorDialog(settings); editor->show(); KWindowSystem::setState(editor->winId(), NET::KeepAbove); KWindowSystem::forceActiveWindow(editor->winId()); connect(editor.data(), &ConnectionEditorDialog::accepted, [editor, this] () { addConnection(editor->setting()); }); connect(editor.data(), &ConnectionEditorDialog::finished, [editor] () { if (editor) { editor->deleteLater(); } }); editor->setModal(true); editor->show(); } else { if (securityType == NetworkManager::StaticWep) { wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::Wep); wifiSecurity->setWepKey0(password); if (KWallet::Wallet::isEnabled()) { wifiSecurity->setWepKeyFlags(NetworkManager::Setting::AgentOwned); } } else { if (ap->mode() == NetworkManager::AccessPoint::Adhoc) { wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaNone); } else { wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaPsk); } wifiSecurity->setPsk(password); if (KWallet::Wallet::isEnabled()) { wifiSecurity->setPskFlags(NetworkManager::Setting::AgentOwned); } } QDBusPendingReply reply = NetworkManager::addAndActivateConnection(settings->toMap(), device, specificObject); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); watcher->setProperty("action", Handler::AddAndActivateConnection); watcher->setProperty("connection", settings->name()); connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished); } settings.clear(); } void Handler::addConnection(const NMVariantMapMap& map) { QDBusPendingReply reply = NetworkManager::addConnection(map); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); watcher->setProperty("action", AddConnection); watcher->setProperty("connection", map.value("connection").value("id")); connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished); } void Handler::deactivateConnection(const QString& connection, const QString& device) { NetworkManager::Connection::Ptr con = NetworkManager::findConnection(connection); if (!con) { qCWarning(PLASMA_NM) << "Not possible to deactivate this connection"; return; } QDBusPendingReply<> reply; for (const NetworkManager::ActiveConnection::Ptr &active : NetworkManager::activeConnections()) { if (active->uuid() == con->uuid() && ((!active->devices().isEmpty() && active->devices().first() == device) || active->vpn())) { if (active->vpn()) { reply = NetworkManager::deactivateConnection(active->path()); } else { NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(active->devices().first()); if (device) { reply = device->disconnectInterface(); } } } } QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); watcher->setProperty("action", Handler::DeactivateConnection); connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished); } void Handler::disconnectAll() { for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) { device->disconnectInterface(); } } void Handler::enableAirplaneMode(bool enable) { if (enable) { m_tmpWirelessEnabled = NetworkManager::isWirelessEnabled(); m_tmpWwanEnabled = NetworkManager::isWwanEnabled(); enableBluetooth(false); enableWireless(false); enableWwan(false); } else { enableBluetooth(true); if (m_tmpWirelessEnabled) { enableWireless(true); } if (m_tmpWwanEnabled) { enableWwan(true); } } } void Handler::enableBluetooth(bool enable) { qDBusRegisterMetaType< QMap >(); QDBusMessage message = QDBusMessage::createMethodCall("org.bluez", "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); QDBusPendingReply > reply = QDBusConnection::systemBus().asyncCall(message); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); connect(watcher, &QDBusPendingCallWatcher::finished, [this, enable] (QDBusPendingCallWatcher *watcher) { QDBusPendingReply > reply = *watcher; if (reply.isValid()) { for (const QDBusObjectPath &path : reply.value().keys()) { const QString objPath = path.path(); qCDebug(PLASMA_NM) << "inspecting path" << objPath; const QStringList interfaces = reply.value().value(path).keys(); qCDebug(PLASMA_NM) << "interfaces:" << interfaces; if (interfaces.contains("org.bluez.Adapter1")) { // We need to check previous state first if (!enable) { QDBusMessage message = QDBusMessage::createMethodCall("org.bluez", objPath, "org.freedesktop.DBus.Properties", "Get"); QList arguments; arguments << QLatin1Literal("org.bluez.Adapter1"); arguments << QLatin1Literal("Powered"); message.setArguments(arguments); QDBusPendingReply getReply = QDBusConnection::systemBus().asyncCall(message); QDBusPendingCallWatcher *getWatcher = new QDBusPendingCallWatcher(getReply, this); connect(getWatcher, &QDBusPendingCallWatcher::finished, [this, objPath] (QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; if (reply.isValid()) { m_bluetoothAdapters.insert(objPath, reply.value().toBool()); QDBusMessage message = QDBusMessage::createMethodCall("org.bluez", objPath, "org.freedesktop.DBus.Properties", "Set"); QList arguments; arguments << QLatin1Literal("org.bluez.Adapter1"); arguments << QLatin1Literal("Powered"); arguments << QVariant::fromValue(QDBusVariant(QVariant(false))); message.setArguments(arguments); QDBusConnection::systemBus().asyncCall(message); } }); getWatcher->deleteLater(); } else if (enable && m_bluetoothAdapters.value(objPath)) { QDBusMessage message = QDBusMessage::createMethodCall("org.bluez", objPath, "org.freedesktop.DBus.Properties", "Set"); QList arguments; arguments << QLatin1Literal("org.bluez.Adapter1"); arguments << QLatin1Literal("Powered"); arguments << QVariant::fromValue(QDBusVariant(QVariant(enable))); message.setArguments(arguments); QDBusConnection::systemBus().asyncCall(message); } } } } }); watcher->deleteLater(); } void Handler::enableNetworking(bool enable) { NetworkManager::setNetworkingEnabled(enable); } void Handler::enableWireless(bool enable) { NetworkManager::setWirelessEnabled(enable); } void Handler::enableWwan(bool enable) { NetworkManager::setWwanEnabled(enable); } void Handler::removeConnection(const QString& connection) { NetworkManager::Connection::Ptr con = NetworkManager::findConnection(connection); if (!con || con->uuid().isEmpty()) { qCWarning(PLASMA_NM) << "Not possible to remove connection " << connection; return; } // Remove slave connections for (const NetworkManager::Connection::Ptr &connection : NetworkManager::listConnections()) { NetworkManager::ConnectionSettings::Ptr settings = connection->settings(); if (settings->master() == con->uuid()) { connection->remove(); } } QDBusPendingReply<> reply = con->remove(); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); watcher->setProperty("action", Handler::RemoveConnection); watcher->setProperty("connection", con->name()); connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished); } void Handler::updateConnection(const NetworkManager::Connection::Ptr& connection, const NMVariantMapMap& map) { QDBusPendingReply<> reply = connection->update(map); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); watcher->setProperty("action", UpdateConnection); watcher->setProperty("connection", connection->name()); connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished); } void Handler::requestScan(const QString &interface) { for (NetworkManager::Device::Ptr device : NetworkManager::networkInterfaces()) { if (device->type() == NetworkManager::Device::Wifi) { NetworkManager::WirelessDevice::Ptr wifiDevice = device.objectCast(); if (wifiDevice) { if (!interface.isEmpty() && interface != wifiDevice->interfaceName()) { continue; } qCDebug(PLASMA_NM) << "Requesting wifi scan on device" << wifiDevice->interfaceName(); QDBusPendingReply<> reply = wifiDevice->requestScan(); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); watcher->setProperty("action", Handler::RequestScan); watcher->setProperty("interface", wifiDevice->interfaceName()); connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished); } } } } void Handler::scanRequestFailed(const QString &interface) { if (m_wirelessScanRetryTimer.isActive()) { return; } qCDebug(PLASMA_NM) << "Trying soon a new scan on" << interface; emit wirelessScanTimerEnabled(false); auto retryAction = [this,interface]() { m_wirelessScanRetryTimer.disconnect(); requestScan(interface); }; connect(&m_wirelessScanRetryTimer, &QTimer::timeout, this, retryAction); m_wirelessScanRetryTimer.start(); } void Handler::initKdedModule() { QDBusMessage initMsg = QDBusMessage::createMethodCall(QStringLiteral(AGENT_SERVICE), QStringLiteral(AGENT_PATH), QStringLiteral(AGENT_IFACE), QStringLiteral("init")); QDBusConnection::sessionBus().send(initMsg); } +void Handler::secretAgentError(const QString &connectionPath, const QString &message) +{ + // If the password was wrong, forget it + removeConnection(connectionPath); + emit connectionActivationFailed(connectionPath, message); +} + void Handler::replyFinished(QDBusPendingCallWatcher * watcher) { QDBusPendingReply<> reply = *watcher; if (reply.isError() || !reply.isValid()) { KNotification *notification = nullptr; QString error = reply.error().message(); Handler::HandlerAction action = (Handler::HandlerAction)watcher->property("action").toUInt(); switch (action) { case Handler::ActivateConnection: notification = new KNotification("FailedToActivateConnection", KNotification::CloseOnTimeout, this); notification->setTitle(i18n("Failed to activate %1", watcher->property("connection").toString())); break; case Handler::AddAndActivateConnection: notification = new KNotification("FailedToAddConnection", KNotification::CloseOnTimeout, this); notification->setTitle(i18n("Failed to add %1", watcher->property("connection").toString())); break; case Handler::AddConnection: notification = new KNotification("FailedToAddConnection", KNotification::CloseOnTimeout, this); notification->setTitle(i18n("Failed to add connection %1", watcher->property("connection").toString())); break; case Handler::DeactivateConnection: notification = new KNotification("FailedToDeactivateConnection", KNotification::CloseOnTimeout, this); notification->setTitle(i18n("Failed to deactivate %1", watcher->property("connection").toString())); break; case Handler::RemoveConnection: notification = new KNotification("FailedToRemoveConnection", KNotification::CloseOnTimeout, this); notification->setTitle(i18n("Failed to remove %1", watcher->property("connection").toString())); break; case Handler::UpdateConnection: notification = new KNotification("FailedToUpdateConnection", KNotification::CloseOnTimeout, this); notification->setTitle(i18n("Failed to update connection %1", watcher->property("connection").toString())); break; case Handler::RequestScan: { const QString interface = watcher->property("interface").toString(); qCDebug(PLASMA_NM) << "Wireless scan on" << interface << "failed:" << error; scanRequestFailed(interface); break; } default: break; } if (notification) { notification->setComponentName("networkmanagement"); notification->setText(error); notification->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(KIconLoader::SizeHuge)); notification->sendEvent(); } } else { KNotification *notification = nullptr; Handler::HandlerAction action = (Handler::HandlerAction)watcher->property("action").toUInt(); switch (action) { case Handler::AddConnection: notification = new KNotification("ConnectionAdded", KNotification::CloseOnTimeout, this); notification->setText(i18n("Connection %1 has been added", watcher->property("connection").toString())); break; case Handler::RemoveConnection: notification = new KNotification("ConnectionRemoved", KNotification::CloseOnTimeout, this); notification->setText(i18n("Connection %1 has been removed", watcher->property("connection").toString())); break; case Handler::UpdateConnection: notification = new KNotification("ConnectionUpdated", KNotification::CloseOnTimeout, this); notification->setText(i18n("Connection %1 has been updated", watcher->property("connection").toString())); break; case Handler::RequestScan: qCDebug(PLASMA_NM) << "Wireless scan on" << watcher->property("interface").toString() << "succeeded"; emit wirelessScanTimerEnabled(true); break; default: break; } if (notification) { notification->setComponentName("networkmanagement"); notification->setTitle(watcher->property("connection").toString()); notification->setPixmap(QIcon::fromTheme("dialog-information").pixmap(KIconLoader::SizeHuge)); notification->sendEvent(); } } watcher->deleteLater(); } #if WITH_MODEMMANAGER_SUPPORT void Handler::unlockRequiredChanged(MMModemLock modemLock) { if (modemLock == MM_MODEM_LOCK_NONE) { activateConnection(m_tmpConnectionPath, m_tmpDevicePath, m_tmpSpecificPath); } } #endif diff --git a/libs/handler.h b/libs/handler.h index cffe40db..dea7bda7 100644 --- a/libs/handler.h +++ b/libs/handler.h @@ -1,131 +1,133 @@ /* Copyright 2013-2014 Jan Grulich This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef PLASMA_NM_HANDLER_H #define PLASMA_NM_HANDLER_H #include #include #include #include #if WITH_MODEMMANAGER_SUPPORT #include #endif class Q_DECL_EXPORT Handler : public QObject { Q_OBJECT public: enum HandlerAction { ActivateConnection, AddAndActivateConnection, AddConnection, DeactivateConnection, RemoveConnection, RequestScan, UpdateConnection }; explicit Handler(QObject* parent = nullptr); ~Handler() override; public Q_SLOTS: /** * Activates given connection * @connection - d-bus path of the connection you want to activate * @device - d-bus path of the device where the connection should be activated * @specificParameter - d-bus path of the specific object you want to use for this activation, i.e access point */ void activateConnection(const QString &connection, const QString &device, const QString &specificParameter); /** * Adds and activates a new wireless connection * @device - d-bus path of the wireless device where the connection should be activated * @specificParameter - d-bus path of the accesspoint you want to connect to * @password - pre-filled password which should be used for the new wireless connection * @autoConnect - boolean value whether this connection should be activated automatically when it's available * * Works automatically for wireless connections with WEP/WPA security, for wireless connections with WPA/WPA * it will open the connection editor for advanced configuration. * */ void addAndActivateConnection(const QString &device, const QString &specificParameter, const QString &password = QString()); /** * Adds a new connection * @map - NMVariantMapMap with connection settings */ void addConnection(const NMVariantMapMap &map); /** * Deactivates given connection * @connection - d-bus path of the connection you want to deactivate * @device - d-bus path of the connection where the connection is activated */ void deactivateConnection(const QString &connection, const QString &device); /** * Disconnects all connections */ void disconnectAll(); void enableAirplaneMode(bool enable); void enableNetworking(bool enable); void enableWireless(bool enable); void enableWwan(bool enable); /** * Removes given connection * @connection - d-bus path of the connection you want to edit */ void removeConnection(const QString & connection); /** * Updates given connection * @connection - connection which should be updated * @map - NMVariantMapMap with new connection settings */ void updateConnection(const NetworkManager::Connection::Ptr &connection, const NMVariantMapMap &map); void requestScan(const QString &interface = QString()); private Q_SLOTS: void initKdedModule(); + void secretAgentError(const QString &connectionPath, const QString &message); void replyFinished(QDBusPendingCallWatcher *watcher); #if WITH_MODEMMANAGER_SUPPORT void unlockRequiredChanged(MMModemLock modemLock); #endif Q_SIGNALS: void wirelessScanTimerEnabled(bool enable); + void connectionActivationFailed(const QString &connectionPath, const QString &message); private: bool m_tmpWirelessEnabled; bool m_tmpWwanEnabled; #if WITH_MODEMMANAGER_SUPPORT QString m_tmpConnectionPath; #endif QString m_tmpConnectionUuid; QString m_tmpDevicePath; QString m_tmpSpecificPath; QMap m_bluetoothAdapters; QTimer m_wirelessScanRetryTimer; void enableBluetooth(bool enable); void scanRequestFailed(const QString &interface); }; #endif // PLASMA_NM_HANDLER_H diff --git a/mobile/wifi/package/contents/ui/NetworkListView.qml b/mobile/wifi/package/contents/ui/NetworkListView.qml index bfbc7fbf..70cdaa24 100644 --- a/mobile/wifi/package/contents/ui/NetworkListView.qml +++ b/mobile/wifi/package/contents/ui/NetworkListView.qml @@ -1,166 +1,186 @@ /* * Copyright 2017-2018 Martin Kacej * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 or * (at your option) any later version. * * 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.6 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.2 as Controls import org.kde.plasma.networkmanagement 0.2 as PlasmaNM -import org.kde.kirigami 2.2 as Kirigami +import org.kde.kirigami 2.6 as Kirigami Kirigami.ScrollablePage { anchors.leftMargin: Kirigami.Units.largeSpacing * 2 - header: RowLayout { - id: layoutrow + header: ColumnLayout { width: parent.width - - Controls.Label { - anchors.left: parent.left - text: i18n("Wi-fi") + Kirigami.InlineMessage { + id: inlineError Layout.fillWidth: true - font.bold: true + showCloseButton: true + + visible: false + + type: Kirigami.MessageType.Warning + Connections { + target: handler + onConnectionActivationFailed: { + inlineError.text = message; + inlineError.visible = true; + } + } } - Controls.Switch { - id: wifiSwitchButton - checked: enabled && enabledConnections.wirelessEnabled - enabled: enabledConnections.wirelessHwEnabled - onClicked: { - handler.enableWireless(checked); + RowLayout { + id: layoutrow + Layout.fillWidth: true + + Controls.Label { + anchors.left: parent.left + text: i18n("Wi-fi") + Layout.fillWidth: true + font.bold: true + } + + Controls.Switch { + id: wifiSwitchButton + checked: enabled && enabledConnections.wirelessEnabled + enabled: enabledConnections.wirelessHwEnabled + onClicked: { + handler.enableWireless(checked); + } } } } ListView { id: view anchors.fill: parent clip: true width: parent.width currentIndex: -1 boundsBehavior: Flickable.StopAtBounds header: Column { width: parent.width Controls.Label { text: (mobileProxyModel.showSavedMode) ? i18n("Saved networks") : i18n("Available networks") } Rectangle { width: parent.width; height: 2; color: Kirigami.Theme.disabledTextColor} } model: mobileProxyModel delegate: ConnectionItemDelegate {} } actions.contextualActions: [ Kirigami.Action { iconName: "edit" text: i18n("Add custom connection") onTriggered: { applicationWindow().pageStack.push(connectionEditorDialogComponent) contextDrawer.close() } }, Kirigami.Action { iconName: "edit" text: i18n("Create Hotspot") onTriggered: { applicationWindow().pageStack.push(tetheringComponent) contextDrawer.close() } }, Kirigami.Action { iconName: "edit" text: i18n("Saved Connections") checkable: true checked: false onTriggered: { mobileProxyModel.showSavedMode = !mobileProxyModel.showSavedMode } } ] /* footer: Controls.Button { width: parent.width text: "ContextualActions" iconName: "edit" onClicked: bottomDrawer.open() } Kirigami.OverlayDrawer { id: bottomDrawer edge: Qt.BottomEdge contentItem: Item { implicitHeight: childrenRect.height + Kirigami.Units.gridUnit ColumnLayout{ anchors.centerIn: parent Controls.Button { text: "Add custom connection" onClicked: applicationWindow().pageStack.push(connectionEditorDialogComponent) } Controls.Button { text: "Create Hotspot" onClicked: showPassiveNotification("Open tethering") } Item { Layout.minimumHeight: Units.gridUnit * 4 } } } } */ Kirigami.OverlayDrawer { id: deleteConnectionDialog property var name property var dbusPath edge: Qt.BottomEdge contentItem: Item { implicitHeight: childrenRect.height + Kirigami.Units.gridUnit ColumnLayout { anchors.centerIn: parent Controls.Label { anchors.horizontalCenter: parent.horizontalCenter text: i18n("Delete connection ") + deleteConnectionDialog.name + " from device ?" } Controls.Button { text: i18n("Delete") anchors.horizontalCenter: parent.horizontalCenter onClicked: { handler.removeConnection(deleteConnectionDialog.dbusPath) deleteConnectionDialog.close() } } Controls.Button { text: i18n("Cancel") anchors.horizontalCenter: parent.horizontalCenter onClicked: deleteConnectionDialog.close() } Item { Layout.minimumHeight: Kirigami.Units.gridUnit * 4 } } onVisibleChanged: { if (!visible) { deleteConnectionDialog.name = "" deleteConnectionDialog.dbusPath = "" } } } } }