diff --git a/CMakeLists.txt b/CMakeLists.txt index a624851b..dbce2272 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,99 +1,99 @@ cmake_minimum_required(VERSION 3.0) project(plasma-networkmanagement) -set(QT_MIN_VERSION "5.9.0") -set(KF5_MIN_VERSION "5.46.0") - -set(PROJECT_VERSION "5.12.80") +set(PROJECT_VERSION "5.13.80") set(PROJECT_VERSION_MAJOR 5) +set(QT_MIN_VERSION "5.9.0") +set(KF5_MIN_VERSION "5.50.0") + ################# set KDE specific information ################# find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core DBus Quick QuickWidgets Widgets ) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED Config ConfigWidgets CoreAddons DBusAddons Declarative I18n IconThemes Kirigami2 KIO Notifications NetworkManagerQt Plasma Service WidgetsAddons WindowSystem Wallet ) find_package(KF5ModemManagerQt ${KF5_MIN_VERSION}) set_package_properties(KF5ModemManagerQt PROPERTIES TYPE OPTIONAL) # Required only for getting information about NetworkManager version in CMake -find_package(NetworkManager 1.0.0) +find_package(NetworkManager 1.4.0) set_package_properties(NetworkManager PROPERTIES TYPE REQUIRED) find_package(MobileBroadbandProviderInfo) set_package_properties(MobileBroadbandProviderInfo PROPERTIES DESCRIPTION "Database of mobile broadband service providers" URL "http://live.gnome.org/NetworkManager/MobileBroadband/ServiceProviders" TYPE OPTIONAL) find_package(Qca-qt5 2.1.0) set_package_properties(Qca-qt5 PROPERTIES DESCRIPTION "Support for encryption" URL "http://download.kde.org/stable/qca-qt5/" TYPE REQUIRED) if (DISABLE_MODEMMANAGER_SUPPORT) message(STATUS "Disabling ModemManager support") set(WITH_MODEMMANAGER_SUPPORT 0) else() if (KF5ModemManagerQt_FOUND) message(STATUS "Enabling ModemManager support") set(WITH_MODEMMANAGER_SUPPORT 1) else() message(STATUS "ModemManager or ModemManagerQt not found") set(WITH_MODEMMANAGER_SUPPORT 0) endif() endif() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0) add_definitions(-DQT_USE_FAST_CONCATENATION) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_FROM_BYTEARRAY) add_definitions(-DWITH_MODEMMANAGER_SUPPORT=${WITH_MODEMMANAGER_SUPPORT}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs ${CMAKE_CURRENT_SOURCE_DIR}/libs/editor/) add_subdirectory(applet) add_subdirectory(kded) add_subdirectory(kcm) add_subdirectory(libs) add_subdirectory(vpn) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/applet/contents/ui/ConnectionItem.qml b/applet/contents/ui/ConnectionItem.qml index e661338b..3e5af365 100644 --- a/applet/contents/ui/ConnectionItem.qml +++ b/applet/contents/ui/ConnectionItem.qml @@ -1,358 +1,353 @@ /* Copyright 2013-2017 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 . */ import QtQuick 2.2 import QtQuick.Layouts 1.2 import org.kde.kcoreaddons 1.0 as KCoreAddons import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.networkmanagement 0.2 as PlasmaNM PlasmaComponents.ListItem { id: connectionItem property bool activating: ConnectionState == PlasmaNM.Enums.Activating property int baseHeight: Math.max(units.iconSizes.medium, connectionNameLabel.height + connectionStatusLabel.height) + Math.round(units.gridUnit / 2) property bool expanded: visibleDetails || visiblePasswordDialog property bool predictableWirelessPassword: !Uuid && Type == PlasmaNM.Enums.Wireless && (SecurityType == PlasmaNM.Enums.StaticWep || SecurityType == PlasmaNM.Enums.WpaPsk || SecurityType == PlasmaNM.Enums.Wpa2Psk) - property bool showSpeed: ConnectionState == PlasmaNM.Enums.Activated && + property bool showSpeed: plasmoid.expanded && + ConnectionState == PlasmaNM.Enums.Activated && (Type == PlasmaNM.Enums.Wired || Type == PlasmaNM.Enums.Wireless || Type == PlasmaNM.Enums.Gsm || Type == PlasmaNM.Enums.Cdma) property bool visibleDetails: false property bool visiblePasswordDialog: false checked: connectionItem.containsMouse enabled: true height: expanded ? baseHeight + separator.height + expandableComponentLoader.height + (2 * Math.round(units.gridUnit / 3)) : baseHeight - PlasmaCore.DataSource { - id: dataSource - - property string downloadSource: "network/interfaces/" + DeviceName + "/receiver/data" - property string uploadSource: "network/interfaces/" + DeviceName + "/transmitter/data" - - connectedSources: showSpeed && plasmoid.expanded ? [downloadSource, uploadSource] : [] - engine: "systemmonitor" - interval: 2000 - } - ColumnLayout { anchors.fill: parent RowLayout { Layout.fillWidth: true spacing: Math.round(units.gridUnit / 2) PlasmaCore.SvgItem { id: connectionSvgIcon Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.preferredHeight: units.iconSizes.medium Layout.preferredWidth: units.iconSizes.medium elementId: ConnectionIcon svg: PlasmaCore.Svg { multipleImages: true imagePath: "icons/network" colorGroup: PlasmaCore.ColorScope.colorGroup } } ColumnLayout { Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft spacing: 0 PlasmaComponents.Label { id: connectionNameLabel Layout.fillWidth: true height: paintedHeight elide: Text.ElideRight font.weight: ConnectionState == PlasmaNM.Enums.Activated ? Font.DemiBold : Font.Normal font.italic: ConnectionState == PlasmaNM.Enums.Activating ? true : false text: ItemUniqueName textFormat: Text.PlainText } PlasmaComponents.Label { id: connectionStatusLabel Layout.fillWidth: true height: paintedHeight elide: Text.ElideRight font.pointSize: theme.smallestFont.pointSize opacity: 0.6 text: itemText() } } PlasmaComponents.BusyIndicator { id: connectingIndicator Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.fillHeight: true running: plasmoid.expanded && !stateChangeButton.visible && ConnectionState == PlasmaNM.Enums.Activating visible: running opacity: visible } PlasmaComponents.Button { id: stateChangeButton Layout.alignment: Qt.AlignVCenter | Qt.AlignRight opacity: connectionView.currentVisibleButtonIndex == index ? 1 : 0 visible: opacity != 0 text: (ConnectionState == PlasmaNM.Enums.Deactivated) ? i18n("Connect") : i18n("Disconnect") Behavior on opacity { NumberAnimation { duration: units.shortDuration } } onClicked: changeState() } } PlasmaCore.SvgItem { id: separator height: lineSvg.elementSize("horizontal-line").height Layout.fillWidth: true Layout.maximumHeight: height elementId: "horizontal-line" svg: PlasmaCore.Svg { id: lineSvg; imagePath: "widgets/line" } visible: connectionItem.expanded opacity: visible } Loader { id: expandableComponentLoader Layout.fillHeight: true Layout.fillWidth: true height: childrenRect.height } } Component { id: detailsComponent Item { height: childrenRect.height PlasmaComponents.TabBar { id: detailsTabBar anchors { left: parent.left right: parent.right top: parent.top } height: visible ? implicitHeight : 0 - visible: showSpeed && dataSource.data && dataSource.data[dataSource.downloadSource] && dataSource.data[dataSource.uploadSource] + visible: showSpeed PlasmaComponents.TabButton { id: speedTabButton text: i18n("Speed") } PlasmaComponents.TabButton { id: detailsTabButton text: i18n("Details") } Component.onCompleted: { if (!speedTabButton.visible) { currentTab = detailsTabButton } } } DetailsText { anchors { left: parent.left leftMargin: units.iconSizes.medium right: parent.right top: detailsTabBar.visible ? detailsTabBar.bottom : parent.top topMargin: Math.round(units.gridUnit / 3) } details: ConnectionDetails visible: detailsTabBar.currentTab == detailsTabButton } TrafficMonitor { anchors { left: parent.left right: parent.right top: detailsTabBar.visible ? detailsTabBar.bottom : parent.top topMargin: Math.round(units.gridUnit / 3) } - dataEngine: dataSource - deviceName: DeviceName + rxBytes: RxBytes + txBytes: TxBytes + interval: 2000 visible: detailsTabBar.currentTab == speedTabButton } } } Component { id: passwordDialogComponent Item { property alias password: passwordField.text property alias passwordInput: passwordField height: childrenRect.height PasswordField { id: passwordField anchors { left: parent.left right: parent.right top: parent.top } securityType: SecurityType onAccepted: { stateChangeButton.clicked() } onAcceptableInputChanged: { stateChangeButton.enabled = acceptableInput } Component.onCompleted: { stateChangeButton.enabled = false } Component.onDestruction: { stateChangeButton.enabled = true } } } } states: [ State { name: "collapsed" when: !(visibleDetails || visiblePasswordDialog) StateChangeScript { script: if (expandableComponentLoader.status == Loader.Ready) {expandableComponentLoader.sourceComponent = undefined} } }, State { name: "expandedDetails" when: visibleDetails StateChangeScript { script: createContent() } }, State { name: "expandedPasswordDialog" when: visiblePasswordDialog StateChangeScript { script: createContent() } PropertyChanges { target: stateChangeButton; opacity: 1 } } ] function createContent() { if (visibleDetails) { expandableComponentLoader.sourceComponent = detailsComponent } else if (visiblePasswordDialog) { expandableComponentLoader.sourceComponent = passwordDialogComponent expandableComponentLoader.item.passwordInput.forceActiveFocus() } } function changeState() { visibleDetails = false if (Uuid || !predictableWirelessPassword || visiblePasswordDialog) { if (ConnectionState == PlasmaNM.Enums.Deactivated) { if (!predictableWirelessPassword && !Uuid) { handler.addAndActivateConnection(DevicePath, SpecificPath) } else if (visiblePasswordDialog) { if (expandableComponentLoader.item.password != "") { handler.addAndActivateConnection(DevicePath, SpecificPath, expandableComponentLoader.item.password) visiblePasswordDialog = false } else { connectionItem.clicked() } } else { handler.activateConnection(ConnectionPath, DevicePath, SpecificPath) } } else { handler.deactivateConnection(ConnectionPath, DevicePath) } } else if (predictableWirelessPassword) { appletProxyModel.dynamicSortFilter = false visiblePasswordDialog = true } } function itemText() { if (ConnectionState == PlasmaNM.Enums.Activating) { if (Type == PlasmaNM.Enums.Vpn) return VpnState else return DeviceState } else if (ConnectionState == PlasmaNM.Enums.Deactivating) { if (Type == PlasmaNM.Enums.Vpn) return VpnState else return DeviceState } else if (ConnectionState == PlasmaNM.Enums.Deactivated) { var result = LastUsed if (SecurityType > PlasmaNM.Enums.NoneSecurity) result += ", " + SecurityTypeString return result } else if (ConnectionState == PlasmaNM.Enums.Activated) { - if (showSpeed && dataSource.data && dataSource.data[dataSource.downloadSource] && dataSource.data[dataSource.uploadSource]) { + if (showSpeed) { var downloadColor = theme.highlightColor // cycle upload color by 180 degrees var uploadColor = Qt.hsva((downloadColor.hsvHue + 0.5) % 1, downloadColor.hsvSaturation, downloadColor.hsvValue, downloadColor.a) - return i18n("Connected, %2/s, %4/s", + return i18n("Connected, %2, %4", downloadColor, - KCoreAddons.Format.formatByteSize(dataSource.data[dataSource.downloadSource].value * 1024 || 0), + KCoreAddons.Format.formatByteSize(RxBytes), uploadColor, - KCoreAddons.Format.formatByteSize(dataSource.data[dataSource.uploadSource].value * 1024 || 0)) + KCoreAddons.Format.formatByteSize(TxBytes)) } else { return i18n("Connected") } } } + onShowSpeedChanged: { + connectionModel.setDeviceStatisticsRefreshRateMs(DevicePath, showSpeed ? 2000 : 0) + } + onActivatingChanged: { if (ConnectionState == PlasmaNM.Enums.Activating) { ListView.view.positionViewAtBeginning() } } onClicked: { if (visiblePasswordDialog) { appletProxyModel.dynamicSortFilter = true visiblePasswordDialog = false } else { visibleDetails = !visibleDetails } if (visibleDetails || visiblePasswordDialog) { ListView.view.currentIndex = index } else { ListView.view.currentIndex = -1 } } onContainsMouseChanged: { if (connectionItem.containsMouse) { connectionView.currentVisibleButtonIndex = index } } } diff --git a/applet/contents/ui/TrafficMonitor.qml b/applet/contents/ui/TrafficMonitor.qml index e9ee4207..636b9bce 100644 --- a/applet/contents/ui/TrafficMonitor.qml +++ b/applet/contents/ui/TrafficMonitor.qml @@ -1,95 +1,98 @@ /* Copyright 2013-2017 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 . */ import QtQuick 2.2 import org.kde.kcoreaddons 1.0 as KCoreAddons import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons import org.kde.plasma.components 2.0 as PlasmaComponents Item { - property QtObject dataEngine: null - property string deviceName + property real rxBytes: 0 + property real txBytes: 0 + property alias interval: timer.interval height: visible ? plotter.height + units.gridUnit : 0 Repeater { model: 5 PlasmaComponents.Label { anchors { left: parent.left top: parent.top topMargin: Math.round(units.gridUnit / 3) + (index * plotter.height / 5) } height: paintedHeight font.pointSize: theme.smallestFont.pointSize lineHeight: 1.75 - text: KCoreAddons.Format.formatByteSize((plotter.maxValue * 1024) * (1 - index / 5)) + text: KCoreAddons.Format.formatByteSize(plotter.maxValue * (1 - index / 5)) + i18n("/s") } } KQuickControlsAddons.Plotter { id: plotter property variant downloadColor: theme.highlightColor property variant uploadColor: Qt.hsva((downloadColor.hsvHue + 0.5) % 1, downloadColor.hsvSaturation, downloadColor.hsvValue, downloadColor.a) // Joining two QList in QML/javascript doesn't seem to work so I'm getting maximum from both list separately readonly property int maxValue: Math.max(Math.max.apply(null, downloadPlotData.values), Math.max.apply(null, uploadPlotData.values)) anchors { left: parent.left leftMargin: units.gridUnit * 3 right: parent.right top: parent.top topMargin: units.gridUnit } width: units.gridUnit * 20 height: units.gridUnit * 8 horizontalGridLineCount: 5 dataSets: [ KQuickControlsAddons.PlotData { id: downloadPlotData label: i18n("Download") color: plotter.downloadColor }, KQuickControlsAddons.PlotData { id: uploadPlotData label: i18n("Upload") color: plotter.uploadColor } ] - Connections { - target: dataEngine; - onNewData: { - if (sourceName.indexOf("network/interfaces/" + deviceName) != 0) { - return; - } - var rx = dataEngine.data[dataEngine.downloadSource]; - var tx = dataEngine.data[dataEngine.uploadSource]; - if (rx === undefined || rx.value === undefined || - tx === undefined || tx.value === undefined) { - return; - } - - plotter.addSample([rx.value, tx.value]); + Timer { + id: timer + repeat: true + running: parent.visible + property real prevRxBytes + property real prevTxBytes + Component.onCompleted: { + prevRxBytes = rxBytes + prevTxBytes = txBytes + } + onTriggered: { + var rxSpeed = (rxBytes - prevRxBytes) * 1000 / interval + var txSpeed = (txBytes - prevTxBytes) * 1000 / interval + prevRxBytes = rxBytes + prevTxBytes = txBytes + plotter.addSample([rxSpeed, txSpeed]); } } } } diff --git a/kded/secretagent.cpp b/kded/secretagent.cpp index 7e12a2b8..702c4a5a 100644 --- a/kded/secretagent.cpp +++ b/kded/secretagent.cpp @@ -1,622 +1,645 @@ /* 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 #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; Q_FOREACH (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 Q_FOREACH (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 (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")) { Q_FOREACH (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); Q_FOREACH (const NetworkManager::Setting::Ptr &setting, connectionSettings.settings()) { QString entryName = QLatin1Char('{') % connectionSettings.uuid() % QLatin1Char('}') % QLatin1Char(';') % setting->name(); Q_FOREACH (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); Q_FOREACH (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()) { Q_FOREACH (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(); Q_FOREACH (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 Q_FOREACH (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 Q_FOREACH (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/libs/declarative/models/networkmodel.cpp b/libs/declarative/models/networkmodel.cpp index 4b5d9d3b..cce77fa7 100644 --- a/libs/declarative/models/networkmodel.cpp +++ b/libs/declarative/models/networkmodel.cpp @@ -1,1009 +1,1038 @@ /* Copyright 2013-2018 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 "networkmodel.h" #include "networkmodelitem.h" #include "debug.h" #include "uiutils.h" #if WITH_MODEMMANAGER_SUPPORT #include #include #endif #include #include NetworkModel::NetworkModel(QObject *parent) : QAbstractListModel(parent) { QLoggingCategory::setFilterRules(QStringLiteral("plasma-nm.debug = false")); initialize(); } NetworkModel::~NetworkModel() { } QVariant NetworkModel::data(const QModelIndex &index, int role) const { const int row = index.row(); if (row >= 0 && row < m_list.count()) { NetworkModelItem *item = m_list.itemAt(row); switch (role) { case ConnectionDetailsRole: return item->details(); case ConnectionIconRole: return item->icon(); case ConnectionPathRole: return item->connectionPath(); case ConnectionStateRole: return item->connectionState(); case DeviceName: return item->deviceName(); case DevicePathRole: return item->devicePath(); case DeviceStateRole: return item->deviceState(); case DuplicateRole: return item->duplicate(); case ItemUniqueNameRole: if (m_list.returnItems(NetworkItemsList::Name, item->name()).count() > 1) { return item->originalName(); } else { return item->name(); } case ItemTypeRole: return item->itemType(); case LastUsedRole: return UiUtils::formatLastUsedDateRelative(item->timestamp()); case LastUsedDateOnlyRole: return UiUtils::formatDateRelative(item->timestamp()); case NameRole: return item->name(); case SectionRole: return item->sectionType(); case SignalRole: return item->signal(); case SlaveRole: return item->slave(); case SsidRole: return item->ssid(); case SpecificPathRole: return item->specificPath(); case SecurityTypeRole: return item->securityType(); case SecurityTypeStringRole: return UiUtils::labelFromWirelessSecurity(item->securityType()); case TimeStampRole: return item->timestamp(); case TypeRole: return item->type(); case UniRole: return item->uni(); case UuidRole: return item->uuid(); case VpnState: return item->vpnState(); case VpnType: return item->vpnType(); + case RxBytesRole: + return item->rxBytes(); + case TxBytesRole: + return item->txBytes(); default: break; } } return QVariant(); } int NetworkModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return parent.isValid() ? 0 : m_list.count(); } QHash NetworkModel::roleNames() const { QHash roles = QAbstractListModel::roleNames(); roles[ConnectionDetailsRole] = "ConnectionDetails"; roles[ConnectionIconRole] = "ConnectionIcon"; roles[ConnectionPathRole] = "ConnectionPath"; roles[ConnectionStateRole] = "ConnectionState"; roles[DeviceName] = "DeviceName"; roles[DevicePathRole] = "DevicePath"; roles[DeviceStateRole] = "DeviceState"; roles[DuplicateRole] = "Duplicate"; roles[ItemUniqueNameRole] = "ItemUniqueName"; roles[ItemTypeRole] = "ItemType"; roles[LastUsedRole] = "LastUsed"; roles[LastUsedDateOnlyRole] = "LastUsedDateOnly"; roles[NameRole] = "Name"; roles[SectionRole] = "Section"; roles[SignalRole] = "Signal"; roles[SlaveRole] = "Slave"; roles[SsidRole] = "Ssid"; roles[SpecificPathRole] = "SpecificPath"; roles[SecurityTypeRole] = "SecurityType"; roles[SecurityTypeStringRole] = "SecurityTypeString"; roles[TimeStampRole] = "TimeStamp"; roles[TypeRole] = "Type"; roles[UniRole] = "Uni"; roles[UuidRole] = "Uuid"; roles[VpnState] = "VpnState"; roles[VpnType] = "VpnType"; + roles[RxBytesRole] = "RxBytes"; + roles[TxBytesRole] = "TxBytes"; return roles; } void NetworkModel::initialize() { // Initialize existing connections for (const NetworkManager::Connection::Ptr &connection : NetworkManager::listConnections()) { addConnection(connection); } // Initialize existing devices for (const NetworkManager::Device::Ptr &dev : NetworkManager::networkInterfaces()) { addDevice(dev); } // Initialize existing active connections for (const NetworkManager::ActiveConnection::Ptr &active : NetworkManager::activeConnections()) { addActiveConnection(active); } initializeSignals(); } void NetworkModel::initializeSignals() { connect(NetworkManager::notifier(), &NetworkManager::Notifier::activeConnectionAdded, this, &NetworkModel::activeConnectionAdded, Qt::UniqueConnection); connect(NetworkManager::notifier(), &NetworkManager::Notifier::activeConnectionRemoved, this, &NetworkModel::activeConnectionRemoved, Qt::UniqueConnection); connect(NetworkManager::settingsNotifier(), &NetworkManager::SettingsNotifier::connectionAdded, this, &NetworkModel::connectionAdded, Qt::UniqueConnection); connect(NetworkManager::settingsNotifier(), &NetworkManager::SettingsNotifier::connectionRemoved, this, &NetworkModel::connectionRemoved, Qt::UniqueConnection); connect(NetworkManager::notifier(), &NetworkManager::Notifier::deviceAdded, this, &NetworkModel::deviceAdded, Qt::UniqueConnection); connect(NetworkManager::notifier(), &NetworkManager::Notifier::deviceRemoved, this, &NetworkModel::deviceRemoved, Qt::UniqueConnection); connect(NetworkManager::notifier(), &NetworkManager::Notifier::statusChanged, this, &NetworkModel::statusChanged, Qt::UniqueConnection); } void NetworkModel::initializeSignals(const NetworkManager::ActiveConnection::Ptr &activeConnection) { if (activeConnection->vpn()) { NetworkManager::VpnConnection::Ptr vpnConnection = activeConnection.objectCast(); if (vpnConnection) { connect(vpnConnection.data(), &NetworkManager::VpnConnection::stateChanged, this, &NetworkModel::activeVpnConnectionStateChanged, Qt::UniqueConnection); } } else { connect(activeConnection.data(), &NetworkManager::ActiveConnection::stateChanged, this, &NetworkModel::activeConnectionStateChanged, Qt::UniqueConnection); } } void NetworkModel::initializeSignals(const NetworkManager::Connection::Ptr &connection) { connect(connection.data(), &NetworkManager::Connection::updated, this, &NetworkModel::connectionUpdated, Qt::UniqueConnection); } void NetworkModel::initializeSignals(const NetworkManager::Device::Ptr &device) { connect(device.data(), &NetworkManager::Device::availableConnectionAppeared, this, &NetworkModel::availableConnectionAppeared, Qt::UniqueConnection); connect(device.data(), &NetworkManager::Device::availableConnectionDisappeared, this, &NetworkModel::availableConnectionDisappeared, Qt::UniqueConnection); connect(device.data(), &NetworkManager::Device::ipV4ConfigChanged, this, &NetworkModel::ipConfigChanged, Qt::UniqueConnection); connect(device.data(), &NetworkManager::Device::ipV6ConfigChanged, this, &NetworkModel::ipConfigChanged, Qt::UniqueConnection); connect(device.data(), &NetworkManager::Device::ipInterfaceChanged, this, &NetworkModel::ipInterfaceChanged); connect(device.data(), &NetworkManager::Device::stateChanged, this, &NetworkModel::deviceStateChanged, Qt::UniqueConnection); + auto deviceStatistics = device->deviceStatistics(); + connect(deviceStatistics.data(), &NetworkManager::DeviceStatistics::rxBytesChanged, this, [this, device](qulonglong rxBytes) { + for (auto *item : m_list.returnItems(NetworkItemsList::Device, device->uni())) { + item->setRxBytes(rxBytes); + updateItem(item); + } + }); + connect(deviceStatistics.data(), &NetworkManager::DeviceStatistics::txBytesChanged, this, [this, device](qulonglong txBytes) { + for (auto *item : m_list.returnItems(NetworkItemsList::Device, device->uni())) { + item->setTxBytes(txBytes); + updateItem(item); + } + }); + if (device->type() == NetworkManager::Device::Wifi) { NetworkManager::WirelessDevice::Ptr wifiDev = device.objectCast(); connect(wifiDev.data(), &NetworkManager::WirelessDevice::networkAppeared, this, &NetworkModel::wirelessNetworkAppeared, Qt::UniqueConnection); connect(wifiDev.data(), &NetworkManager::WirelessDevice::networkDisappeared, this, &NetworkModel::wirelessNetworkDisappeared, Qt::UniqueConnection); } #if WITH_MODEMMANAGER_SUPPORT else if (device->type() == NetworkManager::Device::Modem) { ModemManager::ModemDevice::Ptr modem = ModemManager::findModemDevice(device->udi()); if (modem) { if (modem->hasInterface(ModemManager::ModemDevice::ModemInterface)) { ModemManager::Modem::Ptr modemNetwork = modem->interface(ModemManager::ModemDevice::ModemInterface).objectCast(); if (modemNetwork) { connect(modemNetwork.data(), &ModemManager::Modem::signalQualityChanged, this, &NetworkModel::gsmNetworkSignalQualityChanged, Qt::UniqueConnection); connect(modemNetwork.data(), &ModemManager::Modem::accessTechnologiesChanged, this, &NetworkModel::gsmNetworkAccessTechnologiesChanged, Qt::UniqueConnection); connect(modemNetwork.data(), &ModemManager::Modem::currentModesChanged, this, &NetworkModel::gsmNetworkCurrentModesChanged, Qt::UniqueConnection); } } } } #endif } void NetworkModel::initializeSignals(const NetworkManager::WirelessNetwork::Ptr &network) { connect(network.data(), &NetworkManager::WirelessNetwork::signalStrengthChanged, this, &NetworkModel::wirelessNetworkSignalChanged, Qt::UniqueConnection); connect(network.data(), &NetworkManager::WirelessNetwork::referenceAccessPointChanged, this, &NetworkModel::wirelessNetworkReferenceApChanged, Qt::UniqueConnection); } void NetworkModel::addActiveConnection(const NetworkManager::ActiveConnection::Ptr &activeConnection) { initializeSignals(activeConnection); NetworkManager::Device::Ptr device; NetworkManager::Connection::Ptr connection = activeConnection->connection(); // Not necessary to have device for VPN connections if (activeConnection && !activeConnection->vpn() && !activeConnection->devices().isEmpty()) { device = NetworkManager::findNetworkInterface(activeConnection->devices().first()); } // Check whether we have a base connection if (!m_list.contains(NetworkItemsList::Uuid, connection->uuid())) { // Active connection appeared before a base connection, so we have to add its base connection first addConnection(connection); } beginResetModel(); for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::NetworkItemsList::Uuid, connection->uuid())) { if (((device && device->uni() == item->devicePath()) || item->devicePath().isEmpty()) || item->type() == NetworkManager::ConnectionSettings::Vpn) { item->setActiveConnectionPath(activeConnection->path()); item->setConnectionState(activeConnection->state()); if (activeConnection->vpn()) { NetworkManager::VpnConnection::Ptr vpnConnection = activeConnection.objectCast(); NetworkManager::VpnConnection::State state = vpnConnection->state(); if (state == NetworkManager::VpnConnection::Prepare || state == NetworkManager::VpnConnection::NeedAuth || state == NetworkManager::VpnConnection::Connecting || state == NetworkManager::VpnConnection::GettingIpConfig) { item->setConnectionState(NetworkManager::ActiveConnection::Activating); } else if (state == NetworkManager::VpnConnection::Activated) { item->setConnectionState(NetworkManager::ActiveConnection::Activated); } else { item->setConnectionState(NetworkManager::ActiveConnection::Deactivated); } item->setVpnState(state); } item->invalidateDetails(); qCDebug(PLASMA_NM) << "Item " << item->name() << ": active connection state changed to " << item->connectionState(); } } endResetModel(); } void NetworkModel::addAvailableConnection(const QString &connection, const NetworkManager::Device::Ptr &device) { checkAndCreateDuplicate(connection, device); for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Connection, connection)) { // The item is already associated with another device if (!item->devicePath().isEmpty()) { continue; } if (device->ipInterfaceName().isEmpty()) { item->setDeviceName(device->interfaceName()); } else { item->setDeviceName(device->ipInterfaceName()); } item->setDevicePath(device->uni()); item->setDeviceState(device->state()); qCDebug(PLASMA_NM) << "Item " << item->name() << ": device changed to " << item->devicePath(); #if WITH_MODEMMANAGER_SUPPORT if (device->type() == NetworkManager::Device::Modem) { ModemManager::ModemDevice::Ptr modemDevice = ModemManager::findModemDevice(device->udi()); if (modemDevice) { ModemManager::Modem::Ptr modemInterface = modemDevice->interface(ModemManager::ModemDevice::ModemInterface).objectCast(); if (modemInterface) { item->setSignal(modemInterface->signalQuality().signal); qCDebug(PLASMA_NM) << "Item " << item->name() << ": signal changed to " << item->signal(); } } } #endif if (item->type() == NetworkManager::ConnectionSettings::Wireless && item->mode() == NetworkManager::WirelessSetting::Infrastructure) { // Find an accesspoint which could be removed, because it will be merged with a connection for (NetworkModelItem *secondItem : m_list.returnItems(NetworkItemsList::Ssid, item->ssid())) { if (secondItem->itemType() == NetworkModelItem::AvailableAccessPoint && secondItem->devicePath() == item->devicePath()) { const int row = m_list.indexOf(secondItem); qCDebug(PLASMA_NM) << "Access point " << secondItem->name() << ": merged to " << item->name() << " connection"; if (row >= 0) { beginRemoveRows(QModelIndex(), row, row); m_list.removeItem(secondItem); secondItem->deleteLater(); endRemoveRows(); } break; } } NetworkManager::WirelessDevice::Ptr wifiDevice = device.objectCast(); if (wifiDevice) { NetworkManager::WirelessNetwork::Ptr wifiNetwork = wifiDevice->findNetwork(item->ssid()); if (wifiNetwork) { updateFromWirelessNetwork(item, wifiNetwork, wifiDevice); } } } updateItem(item); break; } } void NetworkModel::addConnection(const NetworkManager::Connection::Ptr &connection) { // Can't add a connection without name or uuid if (connection->name().isEmpty() || connection->uuid().isEmpty()) { return; } initializeSignals(connection); NetworkManager::ConnectionSettings::Ptr settings = connection->settings(); NetworkManager::VpnSetting::Ptr vpnSetting; NetworkManager::WirelessSetting::Ptr wirelessSetting; if (settings->connectionType() == NetworkManager::ConnectionSettings::Vpn) { vpnSetting = settings->setting(NetworkManager::Setting::Vpn).dynamicCast(); } else if (settings->connectionType() == NetworkManager::ConnectionSettings::Wireless) { wirelessSetting = settings->setting(NetworkManager::Setting::Wireless).dynamicCast(); } // Check whether the connection is already in the model to avoid duplicates, but this shouldn't happen if (!m_list.contains(NetworkItemsList::Connection, connection->path())) { NetworkModelItem *item = new NetworkModelItem(); item->setConnectionPath(connection->path()); item->setName(settings->id()); item->setTimestamp(settings->timestamp()); item->setType(settings->connectionType()); item->setUuid(settings->uuid()); item->setSlave(settings->isSlave()); if (item->type() == NetworkManager::ConnectionSettings::Vpn) { item->setVpnType(vpnSetting->serviceType().section('.', -1)); } else if (item->type() == NetworkManager::ConnectionSettings::Wireless) { item->setMode(wirelessSetting->mode()); item->setSecurityType(NetworkManager::securityTypeFromConnectionSetting(settings)); item->setSsid(QString::fromUtf8(wirelessSetting->ssid())); } item->invalidateDetails(); const int index = m_list.count(); beginInsertRows(QModelIndex(), index, index); m_list.insertItem(item); endInsertRows(); qCDebug(PLASMA_NM) << "New connection " << item->name() << " added"; } } void NetworkModel::addDevice(const NetworkManager::Device::Ptr &device) { initializeSignals(device); if (device->type() == NetworkManager::Device::Wifi) { NetworkManager::WirelessDevice::Ptr wifiDev = device.objectCast(); for (const NetworkManager::WirelessNetwork::Ptr &wifiNetwork : wifiDev->networks()) { addWirelessNetwork(wifiNetwork, wifiDev); } } for (const NetworkManager::Connection::Ptr &connection : device->availableConnections()) { addAvailableConnection(connection->path(), device); } } void NetworkModel::addWirelessNetwork(const NetworkManager::WirelessNetwork::Ptr &network, const NetworkManager::WirelessDevice::Ptr &device) { initializeSignals(network); // BUG: 386342 // When creating a new hidden wireless network and attempting to connect to it, NM then later reports that AccessPoint appeared, but // it doesn't know its SSID from some reason, this also makes Wireless device to advertise a new available connection, which we later // attempt to merge with an AP, based on its SSID, but it doesn't find any, because we have AP with empty SSID. After this we get another // AccessPoint appeared signal, this time we know SSID, but we don't attempt any merging, because it's usually the other way around, thus // we need to attempt to merge it here with a connection we guess it's related to this new AP for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Type, NetworkManager::ConnectionSettings::Wireless)) { if (item->itemType() == NetworkModelItem::AvailableConnection) { NetworkManager::ConnectionSettings::Ptr connectionSettings = NetworkManager::findConnection(item->connectionPath())->settings(); if (connectionSettings && connectionSettings->connectionType() == NetworkManager::ConnectionSettings::Wireless) { NetworkManager::WirelessSetting::Ptr wirelessSetting = connectionSettings->setting(NetworkManager::Setting::Wireless).dynamicCast(); if (QString::fromUtf8(wirelessSetting->ssid()) == network->ssid()) { const QString bssid = NetworkManager::macAddressAsString(wirelessSetting->bssid()); const QString restrictedHw = NetworkManager::macAddressAsString(wirelessSetting->macAddress()); if ((bssid.isEmpty() || bssid == network->referenceAccessPoint()->hardwareAddress()) && (restrictedHw.isEmpty() || restrictedHw == device->hardwareAddress())) { updateFromWirelessNetwork(item, network, device); return; } } } } } NetworkManager::WirelessSetting::NetworkMode mode = NetworkManager::WirelessSetting::Infrastructure; NetworkManager::WirelessSecurityType securityType = NetworkManager::UnknownSecurity; NetworkManager::AccessPoint::Ptr ap = network->referenceAccessPoint(); if (ap && ap->capabilities().testFlag(NetworkManager::AccessPoint::Privacy)) { securityType = NetworkManager::findBestWirelessSecurity(device->wirelessCapabilities(), true, (device->mode() == NetworkManager::WirelessDevice::Adhoc), ap->capabilities(), ap->wpaFlags(), ap->rsnFlags()); if (network->referenceAccessPoint()->mode() == NetworkManager::AccessPoint::Infra) { mode = NetworkManager::WirelessSetting::Infrastructure; } else if (network->referenceAccessPoint()->mode() == NetworkManager::AccessPoint::Adhoc) { mode = NetworkManager::WirelessSetting::Adhoc; } else if (network->referenceAccessPoint()->mode() == NetworkManager::AccessPoint::ApMode) { mode = NetworkManager::WirelessSetting::Ap; } } NetworkModelItem *item = new NetworkModelItem(); if (device->ipInterfaceName().isEmpty()) { item->setDeviceName(device->interfaceName()); } else { item->setDeviceName(device->ipInterfaceName()); } item->setDevicePath(device->uni()); item->setMode(mode); item->setName(network->ssid()); item->setSignal(network->signalStrength()); item->setSpecificPath(network->referenceAccessPoint()->uni()); item->setSsid(network->ssid()); item->setType(NetworkManager::ConnectionSettings::Wireless); item->setSecurityType(securityType); item->invalidateDetails(); const int index = m_list.count(); beginInsertRows(QModelIndex(), index, index); m_list.insertItem(item); endInsertRows(); qCDebug(PLASMA_NM) << "New wireless network " << item->name() << " added"; } void NetworkModel::checkAndCreateDuplicate(const QString &connection, const NetworkManager::Device::Ptr &device) { bool createDuplicate = false; NetworkModelItem *originalItem = nullptr; for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Connection, connection)) { if (!item->duplicate()) { originalItem = item; } if (!item->duplicate() && item->itemType() == NetworkModelItem::AvailableConnection && (item->devicePath() != device->uni() && !item->devicePath().isEmpty())) { createDuplicate = true; } } if (createDuplicate) { NetworkModelItem *duplicatedItem = new NetworkModelItem(originalItem); duplicatedItem->invalidateDetails(); const int index = m_list.count(); beginInsertRows(QModelIndex(), index, index); m_list.insertItem(duplicatedItem); endInsertRows(); } } void NetworkModel::onItemUpdated() { NetworkModelItem *item = static_cast(sender()); if (item) { updateItem(item); } } +void NetworkModel::setDeviceStatisticsRefreshRateMs(const QString &devicePath, uint refreshRate) +{ + NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(devicePath); + + if (device) { + device->deviceStatistics()->setRefreshRateMs(refreshRate); + } +} + void NetworkModel::updateItem(NetworkModelItem*item) { const int row = m_list.indexOf(item); if (row >= 0) { item->invalidateDetails(); QModelIndex index = createIndex(row, 0); Q_EMIT dataChanged(index, index); } } void NetworkModel::accessPointSignalStrengthChanged(int signal) { NetworkManager::AccessPoint *apPtr = qobject_cast(sender()); if (apPtr) { for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Ssid, apPtr->ssid())) { if (item->specificPath() == apPtr->uni()) { item->setSignal(signal); updateItem(item); qCDebug(PLASMA_NM) << "AccessPoint " << item->name() << ": signal changed to " << item->signal(); } } } } void NetworkModel::activeConnectionAdded(const QString &activeConnection) { NetworkManager::ActiveConnection::Ptr activeCon = NetworkManager::findActiveConnection(activeConnection); if (activeCon) { addActiveConnection(activeCon); } } void NetworkModel::activeConnectionRemoved(const QString &activeConnection) { for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::ActiveConnection, activeConnection)) { item->setActiveConnectionPath(QString()); item->setConnectionState(NetworkManager::ActiveConnection::Deactivated); item->setVpnState(NetworkManager::VpnConnection::Disconnected); updateItem(item); qCDebug(PLASMA_NM) << "Item " << item->name() << ": active connection removed"; } } void NetworkModel::activeConnectionStateChanged(NetworkManager::ActiveConnection::State state) { NetworkManager::ActiveConnection *activePtr = qobject_cast(sender()); if (activePtr) { beginResetModel(); for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::ActiveConnection, activePtr->path())) { item->setConnectionState(state); item->invalidateDetails(); qCDebug(PLASMA_NM) << "Item " << item->name() << ": active connection changed to " << item->connectionState(); } endResetModel(); } } void NetworkModel::activeVpnConnectionStateChanged(NetworkManager::VpnConnection::State state, NetworkManager::VpnConnection::StateChangeReason reason) { Q_UNUSED(reason) NetworkManager::ActiveConnection *activePtr = qobject_cast(sender()); if (activePtr) { for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::ActiveConnection, activePtr->path())) { if (state == NetworkManager::VpnConnection::Prepare || state == NetworkManager::VpnConnection::NeedAuth || state == NetworkManager::VpnConnection::Connecting || state == NetworkManager::VpnConnection::GettingIpConfig) { item->setConnectionState(NetworkManager::ActiveConnection::Activating); } else if (state == NetworkManager::VpnConnection::Activated) { item->setConnectionState(NetworkManager::ActiveConnection::Activated); } else { item->setConnectionState(NetworkManager::ActiveConnection::Deactivated); } item->setVpnState(state); updateItem(item); qCDebug(PLASMA_NM) << "Item " << item->name() << ": active connection changed to " << item->connectionState(); } } } void NetworkModel::availableConnectionAppeared(const QString &connection) { NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(qobject_cast(sender())->uni()); if (device) { addAvailableConnection(connection, device); } } void NetworkModel::availableConnectionDisappeared(const QString &connection) { for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Connection, connection)) { bool available = false; const QString devicePath = item->devicePath(); const QString specificPath = item->specificPath(); // We have to check whether the connection is still available, because it might be // presented in the model for more devices and we don't want to remove it for all of them. // Check whether the device is still available NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(devicePath); if (device) { // Check whether the connection is still listed as available for (const NetworkManager::Connection::Ptr &connection : device->availableConnections()) { if (connection->path() == item->connectionPath()) { available = true; break; } } } if (!available) { item->setDeviceName(QString()); item->setDevicePath(QString()); item->setDeviceState(NetworkManager::Device::UnknownState); item->setSignal(0); item->setSpecificPath(QString()); qCDebug(PLASMA_NM) << "Item " << item->name() << " removed as available connection"; // Check whether the connection is still available as an access point, this happens // when we change its properties, like ssid, bssid, security etc. if (item->type() == NetworkManager::ConnectionSettings::Wireless && !specificPath.isEmpty()) { if (device && device->type() == NetworkManager::Device::Wifi) { NetworkManager::WirelessDevice::Ptr wifiDevice = device.objectCast(); if (wifiDevice) { NetworkManager::AccessPoint::Ptr ap = wifiDevice->findAccessPoint(specificPath); if (ap) { NetworkManager::WirelessNetwork::Ptr network = wifiDevice->findNetwork(ap->ssid()); if (network) { addWirelessNetwork(network, wifiDevice); } } } } } if (item->duplicate()) { const int row = m_list.indexOf(item); if (row >= 0) { qCDebug(PLASMA_NM) << "Duplicate item " << item->name() << " removed completely"; beginRemoveRows(QModelIndex(), row, row); m_list.removeItem(item); item->deleteLater(); endRemoveRows(); } } else { updateItem(item); } } available = false; } } void NetworkModel::connectionAdded(const QString &connection) { NetworkManager::Connection::Ptr newConnection = NetworkManager::findConnection(connection); if (newConnection) { addConnection(newConnection); } } void NetworkModel::connectionRemoved(const QString &connection) { bool remove = false; for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Connection, connection)) { // When the item type is wireless, we can remove only the connection and leave it as an available access point if (item->type() == NetworkManager::ConnectionSettings::Wireless && !item->devicePath().isEmpty()) { for (NetworkModelItem *secondItem : m_list.items()) { // Remove it entirely when there is another connection with the same configuration and for the same device // or it's a shared connection if ((item->mode() != NetworkManager::WirelessSetting::Infrastructure) || (item->connectionPath() != secondItem->connectionPath() && item->devicePath() == secondItem->devicePath() && item->mode() == secondItem->mode() && item->securityType() == secondItem->securityType() && item->ssid() == secondItem->ssid())) { remove = true; break; } } if (!remove) { item->setConnectionPath(QString()); item->setName(item->ssid()); item->setSlave(false); item->setTimestamp(QDateTime()); item->setUuid(QString()); updateItem(item); qCDebug(PLASMA_NM) << "Item " << item->name() << ": connection removed"; } } else { remove = true; } if (remove) { const int row = m_list.indexOf(item); if (row >= 0) { qCDebug(PLASMA_NM) << "Item " << item->name() << " removed completely"; beginRemoveRows(QModelIndex(), row, row); m_list.removeItem(item); item->deleteLater(); endRemoveRows(); } } remove = false; } } void NetworkModel::connectionUpdated() { NetworkManager::Connection *connectionPtr = qobject_cast(sender()); if (connectionPtr) { NetworkManager::ConnectionSettings::Ptr settings = connectionPtr->settings(); for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Connection, connectionPtr->path())) { item->setConnectionPath(connectionPtr->path()); item->setName(settings->id()); item->setTimestamp(settings->timestamp()); item->setType(settings->connectionType()); item->setUuid(settings->uuid()); if (item->type() == NetworkManager::ConnectionSettings::Wireless) { NetworkManager::WirelessSetting::Ptr wirelessSetting; wirelessSetting = settings->setting(NetworkManager::Setting::Wireless).dynamicCast(); item->setMode(wirelessSetting->mode()); item->setSecurityType(NetworkManager::securityTypeFromConnectionSetting(settings)); item->setSsid(QString::fromUtf8(wirelessSetting->ssid())); // TODO check whether BSSID has changed and update the wireless info } updateItem(item); qCDebug(PLASMA_NM) << "Item " << item->name() << ": connection updated"; } } } void NetworkModel::deviceAdded(const QString &device) { NetworkManager::Device::Ptr dev = NetworkManager::findNetworkInterface(device); if (dev) { addDevice(dev); } } void NetworkModel::deviceRemoved(const QString &device) { // Make all items unavailable for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Device, device)) { availableConnectionDisappeared(item->connectionPath()); } } void NetworkModel::deviceStateChanged(NetworkManager::Device::State state, NetworkManager::Device::State oldState, NetworkManager::Device::StateChangeReason reason) { Q_UNUSED(oldState); Q_UNUSED(reason); NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(qobject_cast(sender())->uni()); if (device) { beginResetModel(); for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Device, device->uni())) { item->setDeviceState(state); item->invalidateDetails(); // qCDebug(PLASMA_NM) << "Item " << item->name() << ": device state changed to " << item->deviceState(); } endResetModel(); } } #if WITH_MODEMMANAGER_SUPPORT void NetworkModel::gsmNetworkAccessTechnologiesChanged(QFlags accessTechnologies) { Q_UNUSED(accessTechnologies); ModemManager::Modem *gsmNetwork = qobject_cast(sender()); if (gsmNetwork) { for (const NetworkManager::Device::Ptr &dev : NetworkManager::networkInterfaces()) { if (dev->type() == NetworkManager::Device::Modem) { ModemManager::ModemDevice::Ptr modem = ModemManager::findModemDevice(dev->udi()); if (modem) { if (modem->hasInterface(ModemManager::ModemDevice::ModemInterface)) { ModemManager::Modem::Ptr modemNetwork = modem->interface(ModemManager::ModemDevice::ModemInterface).objectCast(); if (modemNetwork && modemNetwork->device() == gsmNetwork->device()) { // TODO store access technology internally? for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Device, dev->uni())) { updateItem(item); } } } } } } } } void NetworkModel::gsmNetworkCurrentModesChanged() { ModemManager::Modem *gsmNetwork = qobject_cast(sender()); if (gsmNetwork) { for (const NetworkManager::Device::Ptr &dev : NetworkManager::networkInterfaces()) { if (dev->type() == NetworkManager::Device::Modem) { ModemManager::ModemDevice::Ptr modem = ModemManager::findModemDevice(dev->udi()); if (modem) { if (modem->hasInterface(ModemManager::ModemDevice::ModemInterface)) { ModemManager::Modem::Ptr modemNetwork = modem->interface(ModemManager::ModemDevice::ModemInterface).objectCast(); if (modemNetwork && modemNetwork->device() == gsmNetwork->device()) { for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Device, dev->uni())) { updateItem(item); } } } } } } } } void NetworkModel::gsmNetworkSignalQualityChanged(const ModemManager::SignalQualityPair &signalQuality) { ModemManager::Modem *gsmNetwork = qobject_cast(sender()); if (gsmNetwork) { for (const NetworkManager::Device::Ptr &dev : NetworkManager::networkInterfaces()) { if (dev->type() == NetworkManager::Device::Modem) { ModemManager::ModemDevice::Ptr modem = ModemManager::findModemDevice(dev->udi()); if (modem) { if (modem->hasInterface(ModemManager::ModemDevice::ModemInterface)) { ModemManager::Modem::Ptr modemNetwork = modem->interface(ModemManager::ModemDevice::ModemInterface).objectCast(); if (modemNetwork && modemNetwork->device() == gsmNetwork->device()) { for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Device, dev->uni())) { item->setSignal(signalQuality.signal); updateItem(item); } } } } } } } } #endif void NetworkModel::ipConfigChanged() { NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(qobject_cast(sender())->uni()); if (device) { for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Device, device->uni())) { updateItem(item); // qCDebug(PLASMA_NM) << "Item " << item->name() << ": device ipconfig changed"; } } } void NetworkModel::ipInterfaceChanged() { NetworkManager::Device *device = qobject_cast(sender()); if (device) { for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Device, device->uni())) { if (device->ipInterfaceName().isEmpty()) { item->setDeviceName(device->interfaceName()); } else { item->setDeviceName(device->ipInterfaceName()); } } } } void NetworkModel::statusChanged(NetworkManager::Status status) { Q_UNUSED(status); qCDebug(PLASMA_NM) << "NetworkManager state changed to " << status; // This has probably effect only for VPN connections for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Type, NetworkManager::ConnectionSettings::Vpn)) { updateItem(item); } } void NetworkModel::wirelessNetworkAppeared(const QString &ssid) { NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(qobject_cast(sender())->uni()); if (device && device->type() == NetworkManager::Device::Wifi) { NetworkManager::WirelessDevice::Ptr wirelessDevice = device.objectCast(); NetworkManager::WirelessNetwork::Ptr network = wirelessDevice->findNetwork(ssid); addWirelessNetwork(network, wirelessDevice); } } void NetworkModel::wirelessNetworkDisappeared(const QString &ssid) { NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(qobject_cast(sender())->uni()); if (device) { for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Ssid, ssid, device->uni())) { // Remove the entire item, because it's only AP or it's a duplicated available connection if (item->itemType() == NetworkModelItem::AvailableAccessPoint || item->duplicate()) { const int row = m_list.indexOf(item); if (row >= 0) { qCDebug(PLASMA_NM) << "Wireless network " << item->name() << " removed completely"; beginRemoveRows(QModelIndex(), row, row); m_list.removeItem(item); item->deleteLater(); endRemoveRows(); } // Remove only AP and device from the item and leave it as an unavailable connection } else { if (item->mode() == NetworkManager::WirelessSetting::Infrastructure) { item->setDeviceName(QString()); item->setDevicePath(QString()); item->setSpecificPath(QString()); } item->setSignal(0); updateItem(item); qCDebug(PLASMA_NM) << "Item " << item->name() << ": wireless network removed"; } } } } void NetworkModel::wirelessNetworkReferenceApChanged(const QString &accessPoint) { NetworkManager::WirelessNetwork *networkPtr = qobject_cast(sender()); if (networkPtr) { for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Ssid, networkPtr->ssid(), networkPtr->device())) { NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(item->connectionPath()); if (connection) { NetworkManager::WirelessSetting::Ptr wirelessSetting = connection->settings()->setting(NetworkManager::Setting::Wireless).staticCast(); if (wirelessSetting) { if (wirelessSetting->bssid().isEmpty()) { item->setSpecificPath(accessPoint); updateItem(item); } } } } } } void NetworkModel::wirelessNetworkSignalChanged(int signal) { NetworkManager::WirelessNetwork *networkPtr = qobject_cast(sender()); if (networkPtr) { for (NetworkModelItem *item : m_list.returnItems(NetworkItemsList::Ssid, networkPtr->ssid(), networkPtr->device())) { if (item->specificPath() == networkPtr->referenceAccessPoint()->uni()) { item->setSignal(signal); updateItem(item); // qCDebug(PLASMA_NM) << "Wireless network " << item->name() << ": signal changed to " << item->signal(); } } } } NetworkManager::WirelessSecurityType NetworkModel::alternativeWirelessSecurity(const NetworkManager::WirelessSecurityType type) { if (type == NetworkManager::WpaPsk) { return NetworkManager::Wpa2Psk; } else if (type == NetworkManager::WpaEap) { return NetworkManager::Wpa2Eap; } else if (type == NetworkManager::Wpa2Psk) { return NetworkManager::WpaPsk; } else if (type == NetworkManager::Wpa2Eap) { return NetworkManager::WpaEap; } return type; } void NetworkModel::updateFromWirelessNetwork(NetworkModelItem *item, const NetworkManager::WirelessNetwork::Ptr &network, const NetworkManager::WirelessDevice::Ptr &device) { NetworkManager::WirelessSecurityType securityType = NetworkManager::UnknownSecurity; NetworkManager::AccessPoint::Ptr ap = network->referenceAccessPoint(); if (ap && ap->capabilities().testFlag(NetworkManager::AccessPoint::Privacy)) { securityType = NetworkManager::findBestWirelessSecurity(device->wirelessCapabilities(), true, (device->mode() == NetworkManager::WirelessDevice::Adhoc), ap->capabilities(), ap->wpaFlags(), ap->rsnFlags()); } // Check whether the connection is associated with some concrete AP NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(item->connectionPath()); if (connection) { NetworkManager::WirelessSetting::Ptr wirelessSetting = connection->settings()->setting(NetworkManager::Setting::Wireless).staticCast(); if (wirelessSetting) { if (!wirelessSetting->bssid().isEmpty()) { for (const NetworkManager::AccessPoint::Ptr ap : network->accessPoints()) { if (ap->hardwareAddress() == NetworkManager::macAddressAsString(wirelessSetting->bssid())) { item->setSignal(ap->signalStrength()); item->setSpecificPath(ap->uni()); // We need to watch this AP for signal changes connect(ap.data(), &NetworkManager::AccessPoint::signalStrengthChanged, this, &NetworkModel::accessPointSignalStrengthChanged, Qt::UniqueConnection); } } } else { item->setSignal(network->signalStrength()); item->setSpecificPath(network->referenceAccessPoint()->uni()); } } } item->setSecurityType(securityType); updateItem(item); } diff --git a/libs/declarative/models/networkmodel.h b/libs/declarative/models/networkmodel.h index 4b2a3306..110f2cf3 100644 --- a/libs/declarative/models/networkmodel.h +++ b/libs/declarative/models/networkmodel.h @@ -1,128 +1,131 @@ /* Copyright 2013-2018 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_NETWORK_MODEL_H #define PLASMA_NM_NETWORK_MODEL_H #include #include "networkitemslist.h" #include #include #include #include #if WITH_MODEMMANAGER_SUPPORT #include #endif class Q_DECL_EXPORT NetworkModel : public QAbstractListModel { Q_OBJECT public: explicit NetworkModel(QObject *parent = nullptr); ~NetworkModel() override; enum ItemRole { ConnectionDetailsRole = Qt::UserRole + 1, ConnectionIconRole, ConnectionPathRole, ConnectionStateRole, DeviceName, DevicePathRole, DeviceStateRole, DuplicateRole, ItemUniqueNameRole, ItemTypeRole, LastUsedRole, LastUsedDateOnlyRole, NameRole, SecurityTypeRole, SecurityTypeStringRole, SectionRole, SignalRole, SlaveRole, SsidRole, SpecificPathRole, TimeStampRole, TypeRole, UniRole, UuidRole, VpnState, - VpnType + VpnType, + RxBytesRole, + TxBytesRole }; int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; QHash roleNames() const override; public Q_SLOTS: void onItemUpdated(); + void setDeviceStatisticsRefreshRateMs(const QString &devicePath, uint refreshRate); private Q_SLOTS: void accessPointSignalStrengthChanged(int signal); void activeConnectionAdded(const QString &activeConnection); void activeConnectionRemoved(const QString &activeConnection); void activeConnectionStateChanged(NetworkManager::ActiveConnection::State state); void activeVpnConnectionStateChanged(NetworkManager::VpnConnection::State state,NetworkManager::VpnConnection::StateChangeReason reason); void availableConnectionAppeared(const QString &connection); void availableConnectionDisappeared(const QString &connection); void connectionAdded(const QString &connection); void connectionRemoved(const QString &connection); void connectionUpdated(); void deviceAdded(const QString &device); void deviceRemoved(const QString &device); void deviceStateChanged(NetworkManager::Device::State state, NetworkManager::Device::State oldState, NetworkManager::Device::StateChangeReason reason); #if WITH_MODEMMANAGER_SUPPORT void gsmNetworkAccessTechnologiesChanged(QFlags accessTechnologies); void gsmNetworkCurrentModesChanged(); void gsmNetworkSignalQualityChanged(const ModemManager::SignalQualityPair &signalQuality); #endif void ipConfigChanged(); void ipInterfaceChanged(); void statusChanged(NetworkManager::Status status); void wirelessNetworkAppeared(const QString &ssid); void wirelessNetworkDisappeared(const QString &ssid); void wirelessNetworkSignalChanged(int signal); void wirelessNetworkReferenceApChanged(const QString &accessPoint); void initialize(); private: NetworkItemsList m_list; void addActiveConnection(const NetworkManager::ActiveConnection::Ptr &activeConnection); void addAvailableConnection(const QString &connection, const NetworkManager::Device::Ptr &device); void addConnection(const NetworkManager::Connection::Ptr &connection); void addDevice(const NetworkManager::Device::Ptr &device); void addWirelessNetwork(const NetworkManager::WirelessNetwork::Ptr &network, const NetworkManager::WirelessDevice::Ptr &device); void checkAndCreateDuplicate(const QString &connection, const NetworkManager::Device::Ptr &device); void initializeSignals(); void initializeSignals(const NetworkManager::ActiveConnection::Ptr &activeConnection); void initializeSignals(const NetworkManager::Connection::Ptr &connection); void initializeSignals(const NetworkManager::Device::Ptr &device); void initializeSignals(const NetworkManager::WirelessNetwork::Ptr &network); void updateItem(NetworkModelItem *item); void updateFromWirelessNetwork(NetworkModelItem *item, const NetworkManager::WirelessNetwork::Ptr &network, const NetworkManager::WirelessDevice::Ptr &device); NetworkManager::WirelessSecurityType alternativeWirelessSecurity(const NetworkManager::WirelessSecurityType type); }; #endif // PLASMA_NM_NETWORK_MODEL_H diff --git a/libs/declarative/models/networkmodelitem.cpp b/libs/declarative/models/networkmodelitem.cpp index 8106644c..17c84108 100644 --- a/libs/declarative/models/networkmodelitem.cpp +++ b/libs/declarative/models/networkmodelitem.cpp @@ -1,573 +1,597 @@ /* Copyright 2013-2018 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 "networkmodelitem.h" #include "uiutils.h" #include #include #include #include #include #include #include #include #if NM_CHECK_VERSION (0, 9, 10) #include #endif #include #include #include #include #include #include #include #include #if WITH_MODEMMANAGER_SUPPORT #include #include #include #include #include #endif NetworkModelItem::NetworkModelItem(QObject *parent) : QObject(parent) , m_connectionState(NetworkManager::ActiveConnection::Deactivated) , m_deviceState(NetworkManager::Device::UnknownState) , m_detailsValid(false) , m_duplicate(false) , m_mode(NetworkManager::WirelessSetting::Infrastructure) , m_securityType(NetworkManager::NoneSecurity) , m_signal(0) , m_slave(false) , m_type(NetworkManager::ConnectionSettings::Unknown) , m_vpnState(NetworkManager::VpnConnection::Unknown) + , m_rxBytes(0) + , m_txBytes(0) { } NetworkModelItem::NetworkModelItem(const NetworkModelItem *item, QObject *parent) : QObject(parent) , m_connectionPath(item->connectionPath()) , m_connectionState(NetworkManager::ActiveConnection::Deactivated) , m_detailsValid(false) , m_duplicate(true) , m_mode(item->mode()) , m_name(item->name()) , m_securityType(item->securityType()) , m_slave(item->slave()) , m_ssid(item->ssid()) , m_timestamp(item->timestamp()) , m_type(item->type()) , m_uuid(item->uuid()) , m_vpnState(NetworkManager::VpnConnection::Unknown) + , m_rxBytes(0) + , m_txBytes(0) { } NetworkModelItem::~NetworkModelItem() { } QString NetworkModelItem::activeConnectionPath() const { return m_activeConnectionPath; } void NetworkModelItem::setActiveConnectionPath(const QString &path) { m_activeConnectionPath = path; } QString NetworkModelItem::connectionPath() const { return m_connectionPath; } void NetworkModelItem::setConnectionPath(const QString &path) { m_connectionPath = path; } NetworkManager::ActiveConnection::State NetworkModelItem::connectionState() const { return m_connectionState; } void NetworkModelItem::setConnectionState(NetworkManager::ActiveConnection::State state) { m_connectionState = state; } QStringList NetworkModelItem::details() const { if (!m_detailsValid) { updateDetails(); } return m_details; } QString NetworkModelItem::devicePath() const { return m_devicePath; } QString NetworkModelItem::deviceName() const { return m_deviceName; } void NetworkModelItem::setDeviceName(const QString &name) { m_deviceName = name; } void NetworkModelItem::setDevicePath(const QString &path) { m_devicePath = path; } QString NetworkModelItem::deviceState() const { return UiUtils::connectionStateToString(m_deviceState); } void NetworkModelItem::setDeviceState(const NetworkManager::Device::State state) { m_deviceState = state; } bool NetworkModelItem::duplicate() const { return m_duplicate; } QString NetworkModelItem::icon() const { switch (m_type) { case NetworkManager::ConnectionSettings::Adsl: return QStringLiteral("network-mobile-100"); break; case NetworkManager::ConnectionSettings::Bluetooth: if (connectionState() == NetworkManager::ActiveConnection::Activated) { return QStringLiteral("network-bluetooth-activated"); } else { return QStringLiteral("network-bluetooth"); } break; case NetworkManager::ConnectionSettings::Bond: break; case NetworkManager::ConnectionSettings::Bridge: break; case NetworkManager::ConnectionSettings::Cdma: case NetworkManager::ConnectionSettings::Gsm: if (m_signal == 0 ) { return QStringLiteral("network-mobile-0"); } else if (m_signal < 20) { return QStringLiteral("network-mobile-20"); } else if (m_signal < 40) { return QStringLiteral("network-mobile-40"); } else if (m_signal < 60) { return QStringLiteral("network-mobile-60"); } else if (m_signal < 80) { return QStringLiteral("network-mobile-80"); } else { return QStringLiteral("network-mobile-100"); } break; case NetworkManager::ConnectionSettings::Infiniband: break; case NetworkManager::ConnectionSettings::OLPCMesh: break; case NetworkManager::ConnectionSettings::Pppoe: return QStringLiteral("network-mobile-100"); break; case NetworkManager::ConnectionSettings::Vlan: break; case NetworkManager::ConnectionSettings::Vpn: return QStringLiteral("network-vpn"); break; case NetworkManager::ConnectionSettings::Wired: if (connectionState() == NetworkManager::ActiveConnection::Activated) { return QStringLiteral("network-wired-activated"); } else { return QStringLiteral("network-wired"); } break; case NetworkManager::ConnectionSettings::Wireless: if (m_signal == 0 ) { if (m_mode == NetworkManager::WirelessSetting::Adhoc || m_mode == NetworkManager::WirelessSetting::Ap) { return (m_securityType <= NetworkManager::NoneSecurity) ? QStringLiteral("network-wireless-100") : QStringLiteral("network-wireless-100-locked"); } return (m_securityType <= NetworkManager::NoneSecurity) ? QStringLiteral("network-wireless-0") : QStringLiteral("network-wireless-0-locked"); } else if (m_signal < 20) { return (m_securityType <= NetworkManager::NoneSecurity) ? QStringLiteral("network-wireless-20") : QStringLiteral("network-wireless-20-locked"); } else if (m_signal < 40) { return (m_securityType <= NetworkManager::NoneSecurity) ? QStringLiteral("network-wireless-40") : QStringLiteral("network-wireless-40-locked"); } else if (m_signal < 60) { return (m_securityType <= NetworkManager::NoneSecurity) ? QStringLiteral("network-wireless-60") : QStringLiteral("network-wireless-60-locked"); } else if (m_signal < 80) { return (m_securityType <= NetworkManager::NoneSecurity) ? QStringLiteral("network-wireless-80") : QStringLiteral("network-wireless-80-locked"); } else { return (m_securityType <= NetworkManager::NoneSecurity) ? QStringLiteral("network-wireless-100") : QStringLiteral("network-wireless-100-locked"); } break; default: break; } if (connectionState() == NetworkManager::ActiveConnection::Activated) { return QStringLiteral("network-wired-activated"); } else { return QStringLiteral("network-wired"); } } NetworkModelItem::ItemType NetworkModelItem::itemType() const { if (!m_devicePath.isEmpty() || m_type == NetworkManager::ConnectionSettings::Bond || m_type == NetworkManager::ConnectionSettings::Bridge || m_type == NetworkManager::ConnectionSettings::Vlan || #if NM_CHECK_VERSION(0, 9, 10) m_type == NetworkManager::ConnectionSettings::Team || #endif ((NetworkManager::status() == NetworkManager::Connected || NetworkManager::status() == NetworkManager::ConnectedLinkLocal || NetworkManager::status() == NetworkManager::ConnectedSiteOnly) && m_type == NetworkManager::ConnectionSettings::Vpn)) { if (m_connectionPath.isEmpty() && m_type == NetworkManager::ConnectionSettings::Wireless) { return NetworkModelItem::AvailableAccessPoint; } else { return NetworkModelItem::AvailableConnection; } } return NetworkModelItem::UnavailableConnection; } NetworkManager::WirelessSetting::NetworkMode NetworkModelItem::mode() const { return m_mode; } void NetworkModelItem::setMode(const NetworkManager::WirelessSetting::NetworkMode mode) { m_mode = mode; } QString NetworkModelItem::name() const { return m_name; } void NetworkModelItem::setName(const QString &name) { m_name = name; } QString NetworkModelItem::originalName() const { if (m_deviceName.isEmpty()) { return m_name; } return m_name % QLatin1String(" (") % m_deviceName % ')'; } QString NetworkModelItem::sectionType() const { if (m_connectionState == NetworkManager::ActiveConnection::Activated) { return i18n("Active connections"); } else { return i18n("Available connections"); } } NetworkManager::WirelessSecurityType NetworkModelItem::securityType() const { return m_securityType; } void NetworkModelItem::setSecurityType(NetworkManager::WirelessSecurityType type) { m_securityType = type; } int NetworkModelItem::signal() const { return m_signal; } void NetworkModelItem::setSignal(int signal) { m_signal = signal; } bool NetworkModelItem::slave() const { return m_slave; } void NetworkModelItem::setSlave(bool slave) { m_slave = slave; } QString NetworkModelItem::specificPath() const { return m_specificPath; } void NetworkModelItem::setSpecificPath(const QString &path) { m_specificPath = path; } QString NetworkModelItem::ssid() const { return m_ssid; } void NetworkModelItem::setSsid(const QString &ssid) { m_ssid = ssid; } NetworkManager::ConnectionSettings::ConnectionType NetworkModelItem::type() const { return m_type; } QDateTime NetworkModelItem::timestamp() const { return m_timestamp; } void NetworkModelItem::setTimestamp(const QDateTime &date) { m_timestamp = date; } void NetworkModelItem::setType(NetworkManager::ConnectionSettings::ConnectionType type) { m_type = type; } QString NetworkModelItem::uni() const { if (m_type == NetworkManager::ConnectionSettings::Wireless && m_uuid.isEmpty()) { return m_ssid + '%' + m_devicePath; } else { return m_connectionPath + '%' + m_devicePath; } } QString NetworkModelItem::uuid() const { return m_uuid; } void NetworkModelItem::setUuid(const QString &uuid) { m_uuid = uuid; } QString NetworkModelItem::vpnState() const { return UiUtils::vpnConnectionStateToString(m_vpnState); } void NetworkModelItem::setVpnState(NetworkManager::VpnConnection::State state) { m_vpnState = state; } QString NetworkModelItem::vpnType() const { return m_vpnType; } void NetworkModelItem::setVpnType(const QString &type) { m_vpnType = type; } +qulonglong NetworkModelItem::rxBytes() const +{ + return m_rxBytes; +} + +void NetworkModelItem::setRxBytes(qulonglong bytes) +{ + m_rxBytes = bytes; +} + +qulonglong NetworkModelItem::txBytes() const +{ + return m_txBytes; +} + +void NetworkModelItem::setTxBytes(qulonglong bytes) +{ + m_txBytes = bytes; +} + bool NetworkModelItem::operator==(const NetworkModelItem *item) const { if (!item->uuid().isEmpty() && !uuid().isEmpty()) { if (item->devicePath() == devicePath() && item->uuid() == uuid()) { return true; } } else if (item->type() == NetworkManager::ConnectionSettings::Wireless && type() == NetworkManager::ConnectionSettings::Wireless) { if (item->ssid() == ssid() && item->devicePath() == devicePath()) { return true; } } return false; } void NetworkModelItem::invalidateDetails() { m_detailsValid = false; } void NetworkModelItem::updateDetails() const { m_detailsValid = true; m_details.clear(); if (itemType() == NetworkModelItem::UnavailableConnection) { return; } NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(m_devicePath); // Get IPv[46]Address if (device && device->ipV4Config().isValid() && m_connectionState == NetworkManager::ActiveConnection::Activated) { if (!device->ipV4Config().addresses().isEmpty()) { QHostAddress addr = device->ipV4Config().addresses().first().ip(); if (!addr.isNull()) { m_details << i18n("IPv4 Address") << addr.toString(); } } } if (device && device->ipV6Config().isValid() && m_connectionState == NetworkManager::ActiveConnection::Activated) { if (!device->ipV6Config().addresses().isEmpty()) { QHostAddress addr = device->ipV6Config().addresses().first().ip(); if (!addr.isNull()) { m_details << i18n("IPv6 Address") << addr.toString(); } } } if (m_type == NetworkManager::ConnectionSettings::Wired) { NetworkManager::WiredDevice::Ptr wiredDevice = device.objectCast(); if (wiredDevice) { if (m_connectionState == NetworkManager::ActiveConnection::Activated) { m_details << i18n("Connection speed") << UiUtils::connectionSpeed(wiredDevice->bitRate()); } m_details << i18n("MAC Address") << wiredDevice->permanentHardwareAddress(); } } else if (m_type == NetworkManager::ConnectionSettings::Wireless) { NetworkManager::WirelessDevice::Ptr wirelessDevice = device.objectCast(); m_details << i18n("Access point (SSID)") << m_ssid; if (m_mode == NetworkManager::WirelessSetting::Infrastructure) { m_details << i18n("Signal strength") << QString("%1%").arg(m_signal); } if (m_connectionState == NetworkManager::ActiveConnection::Activated) { m_details << i18n("Security type") << UiUtils::labelFromWirelessSecurity(m_securityType); } if (wirelessDevice) { if (m_connectionState == NetworkManager::ActiveConnection::Activated) { m_details << i18n("Connection speed") << UiUtils::connectionSpeed(wirelessDevice->bitRate()); } m_details << i18n("MAC Address") << wirelessDevice->permanentHardwareAddress(); } } else if (m_type == NetworkManager::ConnectionSettings::Gsm || m_type == NetworkManager::ConnectionSettings::Cdma) { #if WITH_MODEMMANAGER_SUPPORT NetworkManager::ModemDevice::Ptr modemDevice = device.objectCast(); if (modemDevice) { ModemManager::ModemDevice::Ptr modem = ModemManager::findModemDevice(modemDevice->udi()); if (modem) { ModemManager::Modem::Ptr modemNetwork = modem->interface(ModemManager::ModemDevice::ModemInterface).objectCast(); if (m_type == NetworkManager::ConnectionSettings::Gsm) { ModemManager::Modem3gpp::Ptr gsmNet = modem->interface(ModemManager::ModemDevice::GsmInterface).objectCast(); if (gsmNet) { m_details << i18n("Operator") << gsmNet->operatorName(); } } else { ModemManager::ModemCdma::Ptr cdmaNet = modem->interface(ModemManager::ModemDevice::CdmaInterface).objectCast(); m_details << i18n("Network ID") << QString("%1").arg(cdmaNet->nid()); } if (modemNetwork) { m_details << i18n("Signal Quality") << QString("%1%").arg(modemNetwork->signalQuality().signal); m_details << i18n("Access Technology") << UiUtils::convertAccessTechnologyToString(modemNetwork->accessTechnologies()); } } } #endif } else if (m_type == NetworkManager::ConnectionSettings::Vpn) { m_details << i18n("VPN plugin") << m_vpnType; if (m_connectionState == NetworkManager::ActiveConnection::Activated) { NetworkManager::ActiveConnection::Ptr active = NetworkManager::findActiveConnection(m_activeConnectionPath); NetworkManager::VpnConnection::Ptr vpnConnection; if (active) { vpnConnection = NetworkManager::VpnConnection::Ptr(new NetworkManager::VpnConnection(active->path()), &QObject::deleteLater); } if (vpnConnection && !vpnConnection->banner().isEmpty()) { m_details << i18n("Banner") << vpnConnection->banner().simplified(); } } } else if (m_type == NetworkManager::ConnectionSettings::Bluetooth) { NetworkManager::BluetoothDevice::Ptr bluetoothDevice = device.objectCast(); if (bluetoothDevice) { m_details << i18n("Name") << bluetoothDevice->name(); if (bluetoothDevice->bluetoothCapabilities() == NetworkManager::BluetoothDevice::Pan) { m_details << i18n("Capabilities") << QStringLiteral("PAN"); } else if (bluetoothDevice->bluetoothCapabilities() == NetworkManager::BluetoothDevice::Dun) { m_details << i18n("Capabilities") << QStringLiteral("DUN"); } m_details << i18n("MAC Address") << bluetoothDevice->hardwareAddress(); } } else if (m_type == NetworkManager::ConnectionSettings::Infiniband) { NetworkManager::InfinibandDevice::Ptr infinibandDevice = device.objectCast(); m_details << i18n("Type") << i18n("Infiniband"); if (infinibandDevice) { m_details << i18n("MAC Address") << infinibandDevice->hwAddress(); } } else if (m_type == NetworkManager::ConnectionSettings::Bond) { NetworkManager::BondDevice::Ptr bondDevice = device.objectCast(); m_details << i18n("Type") << i18n("Bond"); if (bondDevice) { m_details << i18n("MAC Address") << bondDevice->hwAddress(); } } else if (m_type == NetworkManager::ConnectionSettings::Bridge) { NetworkManager::BridgeDevice::Ptr bridgeDevice = device.objectCast(); m_details << i18n("Type") << i18n("Bridge"); if (bridgeDevice) { m_details << i18n("MAC Address") << bridgeDevice->hwAddress(); } } else if (m_type == NetworkManager::ConnectionSettings::Vlan) { NetworkManager::VlanDevice::Ptr vlanDevice = device.objectCast(); m_details << i18n("Type") << i18n("Vlan"); if (vlanDevice) { m_details << i18n("Vlan ID") << QString("%1").arg(vlanDevice->vlanId()); m_details << i18n("MAC Address") << vlanDevice->hwAddress(); } } else if (m_type == NetworkManager::ConnectionSettings::Adsl) { m_details << i18n("Type") << i18n("Adsl"); } #if NM_CHECK_VERSION (0, 9, 10) else if (m_type == NetworkManager::ConnectionSettings::Team) { NetworkManager::TeamDevice::Ptr teamDevice = device.objectCast(); m_details << i18n("Type") << i18n("Team"); if (teamDevice) { m_details << i18n("MAC Address") << teamDevice->hwAddress(); } } #endif } diff --git a/libs/declarative/models/networkmodelitem.h b/libs/declarative/models/networkmodelitem.h index ff1496f9..0f4f4209 100644 --- a/libs/declarative/models/networkmodelitem.h +++ b/libs/declarative/models/networkmodelitem.h @@ -1,142 +1,150 @@ /* Copyright 2013-2018 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_MODEL_NETWORK_MODEL_ITEM_H #define PLASMA_NM_MODEL_NETWORK_MODEL_ITEM_H #include #include #include #include #include #include "networkmodel.h" class Q_DECL_EXPORT NetworkModelItem : public QObject { Q_OBJECT public: enum ItemType { UnavailableConnection, AvailableConnection, AvailableAccessPoint }; explicit NetworkModelItem(QObject *parent = nullptr); explicit NetworkModelItem(const NetworkModelItem *item, QObject *parent = nullptr); ~NetworkModelItem() override; QString activeConnectionPath() const; void setActiveConnectionPath(const QString &path); QString connectionPath() const; void setConnectionPath(const QString &path); NetworkManager::ActiveConnection::State connectionState() const; void setConnectionState(NetworkManager::ActiveConnection::State state); QStringList details() const; QString deviceName() const; void setDeviceName(const QString &name); QString devicePath() const; void setDevicePath(const QString &path); QString deviceState() const; void setDeviceState(const NetworkManager::Device::State state); bool duplicate() const; QString icon() const; ItemType itemType() const; NetworkManager::WirelessSetting::NetworkMode mode() const; void setMode(const NetworkManager::WirelessSetting::NetworkMode mode); QString name() const; void setName(const QString &name); QString originalName() const; QString sectionType() const; NetworkManager::WirelessSecurityType securityType() const; void setSecurityType(NetworkManager::WirelessSecurityType type); int signal() const; void setSignal(int signal); bool slave() const; void setSlave(bool slave); QString specificPath() const; void setSpecificPath(const QString &path); QString ssid() const; void setSsid(const QString &ssid); QDateTime timestamp() const; void setTimestamp(const QDateTime &date); NetworkManager::ConnectionSettings::ConnectionType type() const; void setType(NetworkManager::ConnectionSettings::ConnectionType type); QString uni() const; QString uuid() const; void setUuid(const QString &uuid); QString vpnState() const; void setVpnState(NetworkManager::VpnConnection::State state); QString vpnType() const; void setVpnType(const QString &type); + qulonglong rxBytes() const; + void setRxBytes(qulonglong bytes); + + qulonglong txBytes() const; + void setTxBytes(qulonglong bytes); + bool operator==(const NetworkModelItem *item) const; public Q_SLOTS: void invalidateDetails(); private: void updateDetails() const; QString m_activeConnectionPath; QString m_connectionPath; NetworkManager::ActiveConnection::State m_connectionState; QString m_devicePath; QString m_deviceName; NetworkManager::Device::State m_deviceState; mutable QStringList m_details; mutable bool m_detailsValid; bool m_duplicate; NetworkManager::WirelessSetting::NetworkMode m_mode; QString m_name; NetworkManager::WirelessSecurityType m_securityType; int m_signal; bool m_slave; QString m_specificPath; QString m_ssid; QDateTime m_timestamp; NetworkManager::ConnectionSettings::ConnectionType m_type; QString m_uuid; QString m_vpnType; NetworkManager::VpnConnection::State m_vpnState; + qulonglong m_rxBytes; + qulonglong m_txBytes; }; #endif // PLASMA_NM_MODEL_NETWORK_MODEL_ITEM_H diff --git a/vpn/openvpn/openvpn.cpp b/vpn/openvpn/openvpn.cpp index 7c46aa47..f15d8a97 100644 --- a/vpn/openvpn/openvpn.cpp +++ b/vpn/openvpn/openvpn.cpp @@ -1,881 +1,884 @@ /* Copyright 2008 Will Stephenson Copyright 2011-2012 Rajeesh K Nambiar Copyright 2011 Ilia Kats Copyright 2012-2016 Lamarque V. Souza 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 "openvpn.h" #include #include #include #include #include #include #include #include #include #include "openvpnwidget.h" #include "openvpnauth.h" #include #include "nm-openvpn-service.h" K_PLUGIN_FACTORY_WITH_JSON(OpenVpnUiPluginFactory, "plasmanetworkmanagement_openvpnui.json", registerPlugin();) #define AUTH_TAG "auth" #define AUTH_USER_PASS_TAG "auth-user-pass" #define CA_TAG "ca" #define CERT_TAG "cert" #define CIPHER_TAG "cipher" #define CLIENT_TAG "client" #define COMP_TAG "comp-lzo" #define DEV_TAG "dev" #define FRAGMENT_TAG "fragment" #define IFCONFIG_TAG "ifconfig" #define KEY_TAG "key" #define MSSFIX_TAG "mssfix" #define PKCS12_TAG "pkcs12" #define PORT_TAG "port" #define PROTO_TAG "proto" #define HTTP_PROXY_TAG "http-proxy" #define HTTP_PROXY_RETRY_TAG "http-proxy-retry" #define SOCKS_PROXY_TAG "socks-proxy" #define SOCKS_PROXY_RETRY_TAG "socks-proxy-retry" #define REMOTE_TAG "remote" #define RENEG_SEC_TAG "reneg-sec" #define RPORT_TAG "rport" #define SECRET_TAG "secret" #define TLS_AUTH_TAG "tls-auth" #define TLS_CLIENT_TAG "tls-client" #define TLS_REMOTE_TAG "tls-remote" #define TUNMTU_TAG "tun-mtu" #define KEY_DIRECTION_TAG "key-direction" #define BEGIN_KEY_CA_TAG "" #define END_KEY_CA_TAG "" #define BEGIN_KEY_CERT_TAG "" #define END_KEY_CERT_TAG "" #define BEGIN_KEY_KEY_TAG "" #define END_KEY_KEY_TAG "" #define BEGIN_KEY_SECRET_TAG "" #define END_KEY_SECRET_TAG "" #define BEGIN_TLS_AUTH_TAG "" #define END_TLS_AUTH_TAG "" #define PROC_TYPE_TAG "Proc-Type: 4,ENCRYPTED" #define PKCS8_TAG "-----BEGIN ENCRYPTED PRIVATE KEY-----" QString localCertPath() { return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/networkmanagement/certificates/"); } QString unQuote(QString &certVal, const QString &fileName) { /* Unquote according to openvpn rules * Unquoted filename is returned, and @certVal is modified * to the leftover string */ int nextSep; QString certFile = certVal.trimmed(); if (certFile.startsWith('"') || certFile.startsWith('\'')) { // Quoted certFile.remove(0,1); // Remove the starting quote nextSep = 0; while ((nextSep = certFile.indexOf(QRegExp("\"|'"), nextSep)) != -1) { if (nextSep > 0 && certFile.at(nextSep - 1) != '\\') { // Quote not escaped certVal = certFile.right(certFile.length() - nextSep - 1); // Leftover string certFile.truncate(nextSep); // Quoted string break; } } } else { nextSep = certFile.indexOf(QRegExp("\\s")); // First whitespace if (nextSep != -1) { certVal = certFile.right(certFile.length() - nextSep - 1); // Leftover certFile = certFile.left(nextSep); // value } else { certVal.clear(); } } certFile.replace("\\\\", "\\"); // Replace '\\' with '\' certFile.replace("\\ ", " "); // Replace escaped space with space if (QFileInfo(certFile).isRelative()) { certFile = QFileInfo(fileName).dir().absolutePath() + '/' + certFile; } return certFile; } bool isEncrypted(const QString &fileName) { bool encrypted = false; // TODO: if is_pkcs12(fileName) return true; // NOTE: will have to use SEC_PKCS12DecoderStart and friends from , which will // build a new dependency on nss-devel. See NetworkManager/libnm-util/crypto_nss.c+453 QFile inFile(fileName); if (!inFile.open(QFile::ReadOnly)) { return false; } QTextStream in(&inFile); while (!in.atEnd()) { QString line = in.readLine(); if (!line.isEmpty() && (line.startsWith(PROC_TYPE_TAG) || line.startsWith(PKCS8_TAG))) { encrypted = true; break; } } inFile.close(); return encrypted; } OpenVpnUiPlugin::OpenVpnUiPlugin(QObject * parent, const QVariantList &) : VpnUiPlugin(parent) { } OpenVpnUiPlugin::~OpenVpnUiPlugin() { } SettingWidget * OpenVpnUiPlugin::widget(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) { OpenVpnSettingWidget * wid = new OpenVpnSettingWidget(setting, parent); return wid; } SettingWidget * OpenVpnUiPlugin::askUser(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) { return new OpenVpnAuthWidget(setting, parent); } QString OpenVpnUiPlugin::suggestedFileName(const NetworkManager::ConnectionSettings::Ptr &connection) const { return connection->id() + "_openvpn.conf"; } QString OpenVpnUiPlugin::supportedFileExtensions() const { return "*.ovpn *.conf"; } NMVariantMapMap OpenVpnUiPlugin::importConnectionSettings(const QString &fileName) { NMVariantMapMap result; QFile impFile(fileName); if (!impFile.open(QFile::ReadOnly|QFile::Text)) { mError = VpnUiPlugin::Error; mErrorMessage = i18n("Could not open file"); return result; } bool copyCertificates; KMessageBox::ButtonCode buttonCode; if (KMessageBox::shouldBeShownYesNo(QLatin1String("copyCertificatesDialog"), buttonCode)) { copyCertificates = KMessageBox::questionYesNo(nullptr, i18n("Do you want to copy your certificates to %1?", localCertPath()), i18n("Copy certificates"), KStandardGuiItem::yes(), KStandardGuiItem::no(), QLatin1String("copyCertificatesDialog")) == KMessageBox::Yes; } else { copyCertificates = buttonCode == KMessageBox::Yes; } const QString connectionName = QFileInfo(fileName).completeBaseName(); NMStringMap dataMap; NMStringMap secretData; QVariantMap ipv4Data; QString proxy_type; QString proxy_user; QString proxy_passwd; bool have_client = false; bool have_remote = false; bool proxy_set = false; bool have_pass = false; bool have_sk = false; int key_direction = -1; QTextStream in(&impFile); while (!in.atEnd()) { QStringList key_value; QString line = in.readLine(); // Skip comments if (line.indexOf('#') >= 0) { line.truncate(line.indexOf('#')); } if (line.indexOf(';') >= 0) { line.truncate(line.indexOf(';')); } if (line.isEmpty()) { continue; } key_value.clear(); key_value << line.split(QRegExp("\\s+")); // Split at one or more whitespaces if (key_value[0] == CLIENT_TAG || key_value[0] == TLS_CLIENT_TAG) { have_client = true; continue; } if (key_value[0] == DEV_TAG) { if (key_value.count() == 2) { if (key_value[1].startsWith(QLatin1String("tun"))) { // ignore; default is tun } else if (key_value[1].startsWith(QLatin1String("tap"))) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_TAP_DEV), "yes"); } else { KMessageBox::information(nullptr, i18n("Unknown option: %1", line)); } } else { KMessageBox::information(nullptr, i18n("Invalid number of arguments (expected 1) in option: %1", line)); } continue; } if (key_value[0] == PROTO_TAG) { if (key_value.count() == 2) { /* Valid parameters are "udp", "tcp-client" and "tcp-server". * 'tcp' isn't technically valid, but it used to be accepted so * we'll handle it here anyway. */ if (key_value[1] == "udp") { // ignore; default is udp } else if (key_value[1] == "tcp-client" || key_value[1] == "tcp-server" || key_value[1] == "tcp") { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_PROTO_TCP), "yes"); } else { KMessageBox::information(nullptr, i18n("Unknown option: %1", line)); } } else { KMessageBox::information(nullptr, i18n("Invalid number of arguments (expected 1) in option: %1", line)); } continue; } if (key_value[0] == MSSFIX_TAG) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_MSSFIX), "yes"); continue; } if (key_value[0] == TUNMTU_TAG) { if (key_value.count() == 2) { if (key_value[1].toLong() >= 0 && key_value[1].toLong() < 0xFFFF ) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_TUNNEL_MTU), key_value[1]); } else { KMessageBox::information(nullptr, i18n("Invalid size (should be between 0 and 0xFFFF) in option: %1", line)); } } else { KMessageBox::information(nullptr, i18n("Invalid number of arguments (expected 1) in option: %1", line)); } continue; } if (key_value[0] == FRAGMENT_TAG) { if (key_value.count() == 2) { if (key_value[1].toLong() >= 0 && key_value[1].toLong() < 0xFFFF ) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_FRAGMENT_SIZE), key_value[1]); } else { KMessageBox::information(nullptr, i18n("Invalid size (should be between 0 and 0xFFFF) in option: %1", line)); } } else { KMessageBox::information(nullptr, i18n("Invalid number of arguments (expected 1) in option: %1", line)); } continue; } if (key_value[0] == COMP_TAG) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_COMP_LZO), "yes"); continue; } if (key_value[0] == RENEG_SEC_TAG) { if (key_value.count() == 2) { if (key_value[1].toLong() >= 0 && key_value[1].toLong() <= 604800 ) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_RENEG_SECONDS), key_value[1]); } else { KMessageBox::information(nullptr, i18n("Invalid size (should be between 0 and 604800) in option: %1", line)); } } else { KMessageBox::information(nullptr, i18n("Invalid number of arguments (expected 1) in option: %1", line)); } continue; } if (key_value[0] == HTTP_PROXY_RETRY_TAG || key_value[0] == SOCKS_PROXY_RETRY_TAG) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_PROXY_RETRY), "yes"); continue; } if (key_value[0] == HTTP_PROXY_TAG) { proxy_type = "http"; } if (key_value[0] == SOCKS_PROXY_TAG) { proxy_type = "socks"; } if (!proxy_type.isEmpty() && !proxy_set && key_value.count() >= 3) { bool success = true; if (proxy_type == "http" && key_value.count() >= 4) { // Parse the HTTP proxy file QFile httpProxyFile(QFileInfo(fileName).dir().absolutePath() + '/' + key_value[3]); if (httpProxyFile.open(QFile::ReadOnly|QFile::Text)) { QTextStream httpProxyIn(&httpProxyFile); while (!httpProxyIn.atEnd()) { QString httpProxyLine = httpProxyIn.readLine(); if (httpProxyLine.isEmpty()) { continue; } if (proxy_user.isEmpty()) { proxy_user = httpProxyLine; } if (proxy_passwd.isEmpty()) { proxy_passwd = httpProxyLine; break; } } if (proxy_user.isEmpty()||proxy_passwd.isEmpty()) { success = false; } } } if (success && !proxy_type.isEmpty() && key_value[2].toLong() > 0 // Port && key_value[2].toLong() < 65536) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_PROXY_TYPE), proxy_type); dataMap.insert(QLatin1String(NM_OPENVPN_KEY_PROXY_SERVER), key_value[1]); // Proxy server dataMap.insert(QLatin1String(NM_OPENVPN_KEY_PROXY_PORT), key_value[2]); // Port if (!proxy_user.isEmpty()) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_HTTP_PROXY_USERNAME), proxy_user); } if (!proxy_passwd.isEmpty()) { secretData.insert(QLatin1String(NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD), proxy_passwd); dataMap.insert(QLatin1String(NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD"-flags"), QString::number(NetworkManager::Setting::NotSaved)); } proxy_set = true; } if (!success) { KMessageBox::information(nullptr, i18n("Invalid proxy option: %1", line)); } continue; } if (key_value[0] == REMOTE_TAG) { if (key_value.count() >= 2 && key_value.count() <= 4) { QString remote = key_value[1]; if (remote.startsWith(QLatin1Char('\'')) || remote.startsWith(QLatin1Char('"'))) { remote.remove(0, 1); // Remove first quote remote.remove(remote.size() - 1, 1); // Remove last quote } dataMap.insert(QLatin1String(NM_OPENVPN_KEY_REMOTE), remote); have_remote = true; if (key_value.count() >= 3 && key_value[2].toLong() > 0 && key_value[2].toLong() < 65536) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_PORT), key_value[2]); if (key_value.count() == 4) { - // TODO + QString protocol = key_value[3]; + if(protocol.startsWith(QLatin1String("tcp"))) { + dataMap[QLatin1String(NM_OPENVPN_KEY_PROTO_TCP)] = QLatin1String("yes"); + } } } } } if (key_value[0] == PORT_TAG || key_value[0] == RPORT_TAG) { // Port specified in 'remote' always takes precedence if (!dataMap.contains(NM_OPENVPN_KEY_PORT)) { if (key_value.count() == 2 ) { if (key_value[1].toLong() > 0 && key_value[1].toLong() < 65536) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_PORT), key_value[1]); } else { KMessageBox::information(nullptr, i18n("Invalid port (should be between 1 and 65535) in option: %1", line)); } } else KMessageBox::information(nullptr, i18n("Invalid number of arguments (expected 1) in option: %1", line)); } continue; } if (key_value[0] == PKCS12_TAG && key_value.count() > 1) { key_value[1] = line.right(line.length() - line.indexOf(QRegExp("\\s"))); // Get whole string after key QString certFile = unQuote(key_value[1], fileName); dataMap.insert(QLatin1String(NM_OPENVPN_KEY_CA), certFile); dataMap.insert(QLatin1String(NM_OPENVPN_KEY_CERT), certFile); dataMap.insert(QLatin1String(NM_OPENVPN_KEY_KEY), certFile); continue; } if (key_value[0] == CA_TAG && key_value.count() > 1) { key_value[1] = line.right(line.length() - line.indexOf(QRegExp("\\s"))); // Get whole string after key if (key_value[1].trimmed() == QLatin1String("[inline]")) { // No data or file to copy for now, it will be available later when we reach tag. continue; } if (copyCertificates) { const QString absoluteFilePath = tryToCopyToCertificatesDirectory(connectionName, unQuote(key_value[1], fileName)); dataMap.insert(QLatin1String(NM_OPENVPN_KEY_CA), absoluteFilePath); } else { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_CA), unQuote(key_value[1], fileName)); } continue; } if (key_value[0] == CERT_TAG && key_value.count() > 1) { key_value[1] = line.right(line.length() - line.indexOf(QRegExp("\\s"))); // Get whole string after key if (key_value[1].trimmed() == QLatin1String("[inline]")) { // No data or file to copy for now, it will be available later when we reach tag. continue; } if (copyCertificates) { const QString absoluteFilePath = tryToCopyToCertificatesDirectory(connectionName, unQuote(key_value[1], fileName)); dataMap.insert(QLatin1String(NM_OPENVPN_KEY_CERT), absoluteFilePath); } else { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_CERT), unQuote(key_value[1], fileName)); } continue; } if (key_value[0] == KEY_TAG && key_value.count() > 1) { key_value[1] = line.right(line.length() - line.indexOf(QRegExp("\\s"))); // Get whole string after key if (key_value[1].trimmed() == QLatin1String("[inline]")) { // No data or file to copy for now, it will be available later when we reach tag. continue; } if (copyCertificates) { const QString absoluteFilePath = tryToCopyToCertificatesDirectory(connectionName, unQuote(key_value[1], fileName)); dataMap.insert(QLatin1String(NM_OPENVPN_KEY_KEY), absoluteFilePath); } else { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_KEY), unQuote(key_value[1], fileName)); } continue; } if (key_value[0] == SECRET_TAG && key_value.count() > 1) { key_value[1] = line.right(line.length() - line.indexOf(QRegExp("\\s"))); // Get whole string after key if (copyCertificates) { const QString absoluteFilePath = tryToCopyToCertificatesDirectory(connectionName, unQuote(key_value[1], fileName)); dataMap.insert(QLatin1String(NM_OPENVPN_KEY_STATIC_KEY), absoluteFilePath); } else { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_STATIC_KEY), unQuote(key_value[1], fileName)); } if (key_value.count() > 2) { key_value[2] = key_value[1]; if (!key_value[2].isEmpty() && (key_value[2].toLong() == 0 ||key_value[2].toLong() == 1)) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_STATIC_KEY_DIRECTION), key_value[2]); } } have_sk = true; continue; } if (key_value[0] == TLS_AUTH_TAG && key_value.count() >1) { key_value[1] = line.right(line.length() - line.indexOf(QRegExp("\\s"))); // Get whole string after key // We will copy inline certificate later when we reach tag. if (key_value[1].trimmed() != QLatin1String("[inline]")) { if (copyCertificates) { const QString absoluteFilePath = tryToCopyToCertificatesDirectory(connectionName, unQuote(key_value[1], fileName)); dataMap.insert(QLatin1String(NM_OPENVPN_KEY_TA), absoluteFilePath); } else { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_TA), unQuote(key_value[1], fileName)); } } if (key_value.count() > 2) { key_value[2] = key_value[1]; if (!key_value[2].isEmpty() && (key_value[2].toLong() == 0 ||key_value[2].toLong() == 1)) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_TA_DIR), key_value[2]); } } continue; } if (key_value[0] == CIPHER_TAG) { if (key_value.count() == 2) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_CIPHER), key_value[1]); } else { KMessageBox::information(nullptr, i18n("Invalid number of arguments (expected 1) in option: %1", line)); } continue; } if (key_value[0] == TLS_REMOTE_TAG) { if (!unQuote(key_value[1], fileName).isEmpty()) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_TLS_REMOTE), key_value[1]); } else { KMessageBox::information(nullptr, i18n("Unknown option: %1", line)); } continue; } if (key_value[0] == IFCONFIG_TAG) { if (key_value.count() == 3) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_LOCAL_IP), key_value[1]); dataMap.insert(QLatin1String(NM_OPENVPN_KEY_REMOTE_IP), key_value[2]); } else { KMessageBox::information(nullptr, i18n("Invalid number of arguments (expected 2) in option: %1", line)); } continue; } if (key_value[0] == AUTH_USER_PASS_TAG) { have_pass = true; } if (key_value[0] == AUTH_TAG) { if (key_value.count() == 2) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_AUTH), key_value[1]); } else { KMessageBox::information(nullptr, i18n("Invalid number of arguments (expected 1) in option: %1", line)); } continue; } if (key_value[0] == KEY_DIRECTION_TAG) { if (key_value.count() == 2) { key_direction = key_value[1].toInt(); } if (key_direction != 0 && key_direction != 1) { KMessageBox::information(nullptr, i18n("Invalid argument in option: %1", line)); key_direction = -1; } continue; } if (key_value[0] == BEGIN_KEY_CA_TAG) { const QString caAbsolutePath = saveFile(in, QLatin1String(END_KEY_CA_TAG), connectionName, "ca.crt"); if (!caAbsolutePath.isEmpty()) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_CA), caAbsolutePath); } continue; } else if (key_value[0] == BEGIN_KEY_CERT_TAG) { const QString certAbsolutePath = saveFile(in, QLatin1String(END_KEY_CERT_TAG), connectionName, "cert.crt"); if (!certAbsolutePath.isEmpty()) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_CERT), certAbsolutePath); } continue; } else if (key_value[0] == BEGIN_KEY_KEY_TAG) { const QString keyAbsolutePath = saveFile(in, QLatin1String(END_KEY_KEY_TAG), connectionName, "private.key"); if (!keyAbsolutePath.isEmpty()) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_KEY), keyAbsolutePath); } continue; } else if (key_value[0] == BEGIN_KEY_SECRET_TAG) { const QString secretAbsolutePath = saveFile(in, QLatin1String(END_KEY_SECRET_TAG), connectionName, "secret.key"); if (!secretAbsolutePath.isEmpty()) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_KEY), secretAbsolutePath); have_sk = true; if (key_direction > -1) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_STATIC_KEY_DIRECTION), QString().setNum(key_direction)); } } continue; } else if (key_value[0] == BEGIN_TLS_AUTH_TAG) { const QString tlsAuthAbsolutePath = saveFile(in, QLatin1String(END_TLS_AUTH_TAG), connectionName, "tls_auth.key"); if (!tlsAuthAbsolutePath.isEmpty()) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_TA), tlsAuthAbsolutePath); if (key_direction > -1) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_TA_DIR), QString().setNum(key_direction)); } } continue; } // Import X-NM-Routes if present if (key_value[0] == "X-NM-Routes") { QList list; for (int i = 1; i < key_value.count(); i++) { NetworkManager::IpRoute ipRoute; ipRoute.setIp(QHostAddress(key_value[1].split('/').first())); ipRoute.setPrefixLength(key_value[1].split('/').at(1).toInt()); list << ipRoute; } QList > dbusRoutes; Q_FOREACH (const NetworkManager::IpRoute &route, list) { QList dbusRoute; dbusRoute << htonl(route.ip().toIPv4Address()) << route.prefixLength() << htonl(route.nextHop().toIPv4Address()) << route.metric(); dbusRoutes << dbusRoute; } ipv4Data.insert("routes", QVariant::fromValue(dbusRoutes)); continue; } } if (!have_client && !have_sk) { mError = VpnUiPlugin::Error; mErrorMessage = i18n("File %1 is not a valid OpenVPN's client configuration file", fileName); return result; } else if (!have_remote) { mError = VpnUiPlugin::Error; mErrorMessage = i18n("File %1 is not a valid OpenVPN configuration (no remote).", fileName); return result; } else { QString conType; bool have_certs = false; bool have_ca = false; if (dataMap.contains(NM_OPENVPN_KEY_CA)) { have_ca = true; } if (have_ca && dataMap.contains(NM_OPENVPN_KEY_CERT) && dataMap.contains(NM_OPENVPN_KEY_KEY)) { have_certs = true; } // Determine connection type if (have_pass) { if (have_certs) { conType = NM_OPENVPN_CONTYPE_PASSWORD_TLS; } else if (have_ca) { conType = NM_OPENVPN_CONTYPE_PASSWORD; } } else if (have_certs) { conType = NM_OPENVPN_CONTYPE_TLS; } else if (have_sk) { conType = NM_OPENVPN_CONTYPE_STATIC_KEY; } if (conType.isEmpty()) { conType = NM_OPENVPN_CONTYPE_TLS; } dataMap.insert(QLatin1String(NM_OPENVPN_KEY_CONNECTION_TYPE), conType); // Default secret flags to be agent-owned if (have_pass) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_PASSWORD"-flags"), QString::number(NetworkManager::Setting::AgentOwned)); } if (have_certs) { if (dataMap.contains(NM_OPENVPN_KEY_KEY) && isEncrypted(dataMap[NM_OPENVPN_KEY_KEY])) { dataMap.insert(QLatin1String(NM_OPENVPN_KEY_CERTPASS"-flags"), QString::number(NetworkManager::Setting::AgentOwned)); } } } // Set the '...-type' and '...-flags' value also NetworkManager::VpnSetting setting; setting.setServiceType("org.freedesktop.NetworkManager.openvpn"); setting.setData(dataMap); setting.setSecrets(secretData); QVariantMap conn; conn.insert("id", connectionName); conn.insert("type", "vpn"); result.insert("connection", conn); result.insert("vpn", setting.toMap()); if (!ipv4Data.isEmpty()) { result.insert("ipv4", ipv4Data); } impFile.close(); return result; } QString OpenVpnUiPlugin::saveFile(QTextStream &in, const QString &endTag, const QString &connectionName, const QString &fileName) { const QString certificatesDirectory = localCertPath() + connectionName; const QString absoluteFilePath = certificatesDirectory + '/' + fileName; QFile outFile(absoluteFilePath); QDir().mkpath(certificatesDirectory); if (!outFile.open(QFile::WriteOnly | QFile::Text)) { KMessageBox::information(nullptr, i18n("Error saving file %1: %2", absoluteFilePath, outFile.errorString())); return QString(); } QTextStream out(&outFile); while (!in.atEnd()) { const QString line = in.readLine(); if (line.indexOf(endTag) >= 0) { break; } out << line << "\n"; } outFile.close(); return absoluteFilePath; } QString OpenVpnUiPlugin::tryToCopyToCertificatesDirectory(const QString &connectionName, const QString &sourceFilePath) { const QString certificatesDirectory = localCertPath(); const QString absoluteFilePath = certificatesDirectory + connectionName + '_' + QFileInfo(sourceFilePath).fileName(); QFile sourceFile(sourceFilePath); QDir().mkpath(certificatesDirectory); if (!sourceFile.copy(absoluteFilePath)) { KMessageBox::information(nullptr, i18n("Error copying certificate to %1: %2", absoluteFilePath, sourceFile.errorString())); return sourceFilePath; } return absoluteFilePath; } bool OpenVpnUiPlugin::exportConnectionSettings(const NetworkManager::ConnectionSettings::Ptr &connection, const QString &fileName) { QFile expFile(fileName); if (! expFile.open(QIODevice::WriteOnly | QIODevice::Text) ) { mError = VpnUiPlugin::Error; mErrorMessage = i18n("Could not open file for writing"); return false; } NMStringMap dataMap; NMStringMap secretData; NetworkManager::VpnSetting::Ptr vpnSetting = connection->setting(NetworkManager::Setting::Vpn).dynamicCast(); dataMap = vpnSetting->data(); secretData = vpnSetting->secrets(); QString line; QString cacert, user_cert, private_key; line = QString(CLIENT_TAG) + '\n'; expFile.write(line.toLatin1()); line = QString(REMOTE_TAG) + ' ' + dataMap[NM_OPENVPN_KEY_REMOTE] + (dataMap[NM_OPENVPN_KEY_PORT].isEmpty() ? "\n" : (' ' + dataMap[NM_OPENVPN_KEY_PORT]) + '\n'); expFile.write(line.toLatin1()); if (dataMap[NM_OPENVPN_KEY_CONNECTION_TYPE] == NM_OPENVPN_CONTYPE_TLS || dataMap[NM_OPENVPN_KEY_CONNECTION_TYPE] == NM_OPENVPN_CONTYPE_PASSWORD || dataMap[NM_OPENVPN_KEY_CONNECTION_TYPE] == NM_OPENVPN_CONTYPE_PASSWORD_TLS) { if (!dataMap[NM_OPENVPN_KEY_CA].isEmpty()) { cacert = dataMap[NM_OPENVPN_KEY_CA]; } } if (dataMap[NM_OPENVPN_KEY_CONNECTION_TYPE] == NM_OPENVPN_CONTYPE_TLS || dataMap[NM_OPENVPN_KEY_CONNECTION_TYPE] == NM_OPENVPN_CONTYPE_PASSWORD_TLS) { if (!dataMap[NM_OPENVPN_KEY_CERT].isEmpty()) { user_cert = dataMap[NM_OPENVPN_KEY_CERT]; } if (!dataMap[NM_OPENVPN_KEY_KEY].isEmpty()) { private_key = dataMap[NM_OPENVPN_KEY_KEY]; } } // Handle PKCS#12 (all certs are the same file) if (!cacert.isEmpty() && !user_cert.isEmpty() && !private_key.isEmpty() && cacert == user_cert && cacert == private_key) { line = QString("%1 \"%2\"\n").arg(PKCS12_TAG, cacert); expFile.write(line.toLatin1()); } else { if (!cacert.isEmpty()) { line = QString("%1 \"%2\"\n").arg(CA_TAG, cacert); expFile.write(line.toLatin1()); } if (!user_cert.isEmpty()) { line = QString("%1 \"%2\"\n").arg(CERT_TAG, user_cert); expFile.write(line.toLatin1()); } if (!private_key.isEmpty()) { line = QString("%1 \"%2\"\n").arg(KEY_TAG, private_key); expFile.write(line.toLatin1()); } } if (dataMap[NM_OPENVPN_KEY_CONNECTION_TYPE] == NM_OPENVPN_CONTYPE_TLS || dataMap[NM_OPENVPN_KEY_CONNECTION_TYPE] == NM_OPENVPN_CONTYPE_STATIC_KEY || dataMap[NM_OPENVPN_KEY_CONNECTION_TYPE] == NM_OPENVPN_CONTYPE_PASSWORD || dataMap[NM_OPENVPN_KEY_CONNECTION_TYPE] == NM_OPENVPN_CONTYPE_PASSWORD_TLS) { line = QString(AUTH_USER_PASS_TAG) + '\n'; expFile.write(line.toLatin1()); if (!dataMap[NM_OPENVPN_KEY_TLS_REMOTE].isEmpty()) { line = QString(TLS_REMOTE_TAG) + " \"" + dataMap[NM_OPENVPN_KEY_TLS_REMOTE] + "\"\n"; expFile.write(line.toLatin1()); } if (!dataMap[NM_OPENVPN_KEY_TA].isEmpty()) { line = QString(TLS_AUTH_TAG) + " \"" + dataMap[NM_OPENVPN_KEY_TA] + '\"' + (dataMap[NM_OPENVPN_KEY_TA_DIR].isEmpty() ? "\n" : (' ' + dataMap[NM_OPENVPN_KEY_TA_DIR]) + '\n'); expFile.write(line.toLatin1()); } } if (dataMap[NM_OPENVPN_KEY_CONNECTION_TYPE] == NM_OPENVPN_CONTYPE_STATIC_KEY) { line = QString(SECRET_TAG) + " \"" + dataMap[NM_OPENVPN_KEY_STATIC_KEY] + '\"' + (dataMap[NM_OPENVPN_KEY_STATIC_KEY_DIRECTION].isEmpty() ? "\n" : (' ' + dataMap[NM_OPENVPN_KEY_STATIC_KEY_DIRECTION]) + '\n'); expFile.write(line.toLatin1()); } if (dataMap.contains(NM_OPENVPN_KEY_RENEG_SECONDS) && !dataMap[NM_OPENVPN_KEY_RENEG_SECONDS].isEmpty()) { line = QString(RENEG_SEC_TAG) + ' ' + dataMap[NM_OPENVPN_KEY_RENEG_SECONDS] + '\n'; expFile.write(line.toLatin1()); } if (!dataMap[NM_OPENVPN_KEY_CIPHER].isEmpty()) { line = QString(CIPHER_TAG) + ' ' + dataMap[NM_OPENVPN_KEY_CIPHER] + '\n'; expFile.write(line.toLatin1()); } if (dataMap[NM_OPENVPN_KEY_COMP_LZO] == "yes") { line = QString(COMP_TAG) + " yes\n"; expFile.write(line.toLatin1()); } if (dataMap[NM_OPENVPN_KEY_MSSFIX] == "yes") { line = QString(MSSFIX_TAG) + '\n'; expFile.write(line.toLatin1()); } if (!dataMap[NM_OPENVPN_KEY_TUNNEL_MTU].isEmpty()) { line = QString(TUNMTU_TAG) + ' ' + dataMap[NM_OPENVPN_KEY_TUNNEL_MTU] + '\n'; expFile.write(line.toLatin1()); } if (!dataMap[NM_OPENVPN_KEY_FRAGMENT_SIZE].isEmpty()) { line = QString(FRAGMENT_TAG) + ' ' + dataMap[NM_OPENVPN_KEY_FRAGMENT_SIZE] + '\n'; expFile.write(line.toLatin1()); } line = QString(DEV_TAG) + (dataMap[NM_OPENVPN_KEY_TAP_DEV] == "yes" ? " tap\n" : " tun\n"); expFile.write(line.toLatin1()); line = QString(PROTO_TAG) + (dataMap[NM_OPENVPN_KEY_PROTO_TCP] == "yes" ? " tcp\n" : " udp\n"); expFile.write(line.toLatin1()); // Proxy stuff if (!dataMap[NM_OPENVPN_KEY_PROXY_TYPE].isEmpty()) { QString proxy_port = dataMap[NM_OPENVPN_KEY_PROXY_PORT]; if (dataMap[NM_OPENVPN_KEY_PROXY_TYPE] == "http" && !dataMap[NM_OPENVPN_KEY_PROXY_SERVER].isEmpty() && dataMap.contains(NM_OPENVPN_KEY_PROXY_PORT)) { if (proxy_port.toInt() == 0) { proxy_port = "8080"; } line = QString(HTTP_PROXY_TAG) + ' ' + dataMap[NM_OPENVPN_KEY_PROXY_SERVER] + ' ' + proxy_port + (dataMap[NM_OPENVPN_KEY_HTTP_PROXY_USERNAME].isEmpty() ? "\n" : (' ' + fileName + "-httpauthfile") + '\n'); expFile.write(line.toLatin1()); if (dataMap[NM_OPENVPN_KEY_PROXY_RETRY] == "yes") { line = QString(HTTP_PROXY_RETRY_TAG) + '\n'; expFile.write(line.toLatin1()); } // If there is a username, need to write an authfile if (!dataMap[NM_OPENVPN_KEY_HTTP_PROXY_USERNAME].isEmpty()) { QFile authFile(fileName + "-httpauthfile"); if (authFile.open(QFile::WriteOnly | QFile::Text)) { line = dataMap[NM_OPENVPN_KEY_HTTP_PROXY_USERNAME] + (dataMap[NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD].isEmpty()? "\n" : (dataMap[NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD] + '\n')); authFile.write(line.toLatin1()); authFile.close(); } } } else if (dataMap[NM_OPENVPN_KEY_PROXY_TYPE] == "socks" && !dataMap[NM_OPENVPN_KEY_PROXY_SERVER].isEmpty() && dataMap.contains(NM_OPENVPN_KEY_PROXY_PORT)) { if (proxy_port.toInt() == 0) { proxy_port = "1080"; } line = QString(SOCKS_PROXY_TAG) + dataMap[NM_OPENVPN_KEY_PROXY_SERVER] + ' ' + proxy_port + '\n'; expFile.write(line.toLatin1()); if (dataMap[NM_OPENVPN_KEY_PROXY_RETRY] == "yes") { line = QString(SOCKS_PROXY_RETRY_TAG) + '\n'; expFile.write(line.toLatin1()); } } } NetworkManager::Ipv4Setting::Ptr ipv4Setting = connection->setting(NetworkManager::Setting::Ipv4).dynamicCast(); // Export X-NM-Routes if (!ipv4Setting->routes().isEmpty()) { QString routes; Q_FOREACH (const NetworkManager::IpRoute &route, ipv4Setting->routes()) { routes += route.ip().toString() % QLatin1Char('/') % QString::number(route.prefixLength()) % QLatin1Char(' '); } if (!routes.isEmpty()) { routes = "X-NM-Routes " + routes.trimmed(); expFile.write(routes.toLatin1() + '\n'); } } // Add hard-coded stuff expFile.write("nobind\n" "auth-nocache\n" "script-security 2\n" "persist-key\n" "persist-tun\n" "user nobody\n" "group nobody\n"); expFile.close(); return true; } #include "openvpn.moc"