diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,11 +2,11 @@ project(plasma-networkmanagement) -set(PROJECT_VERSION "5.17.80") +set(PROJECT_VERSION "5.18.80") set(PROJECT_VERSION_MAJOR 5) set(QT_MIN_VERSION "5.12.0") -set(KF5_MIN_VERSION "5.62.0") +set(KF5_MIN_VERSION "5.66.0") ################# set KDE specific information ################# @@ -55,14 +55,13 @@ ecm_find_qmlmodule(org.kde.prison 1.0) -if (BUILD_MOBILE) - find_package(KF5Kirigami2 ${KF5_MIN_VERSION} CONFIG) - set_package_properties(KF5Kirigami2 PROPERTIES - DESCRIPTION "A QtQuick based components set" - PURPOSE "Required at runtime by the mobile KCMs" - TYPE RUNTIME - ) -endif() +find_package(KF5Kirigami2 ${KF5_MIN_VERSION} CONFIG) +set_package_properties(KF5Kirigami2 PROPERTIES + DESCRIPTION "A QtQuick based components set" + PURPOSE "Required at runtime by the KCMs" + TYPE RUNTIME +) + # Required only for getting information about NetworkManager version in CMake find_package(NetworkManager 1.4.0) @@ -72,17 +71,17 @@ find_package(MobileBroadbandProviderInfo) set_package_properties(MobileBroadbandProviderInfo PROPERTIES DESCRIPTION "Database of mobile broadband service providers" - URL "http://live.gnome.org/NetworkManager/MobileBroadband/ServiceProviders" + URL "https://wiki.gnome.org/action/show/Projects/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/" + URL "https://download.kde.org/stable/qca-qt5/" TYPE REQUIRED) find_package(KF5Prison ${KF5_MIN_VERSION}) set_package_properties(KF5Prison PROPERTIES DESCRIPTION "Prison library" - URL "http://projects.kde.org/prison" + URL "https://commits.kde.org/prison" TYPE RUNTIME PURPOSE "Needed to create mobile barcodes for WiFi networks" ) diff --git a/applet/contents/config/config.qml b/applet/contents/config/config.qml deleted file mode 100644 --- a/applet/contents/config/config.qml +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright 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.0 - -import org.kde.plasma.configuration 2.0 - -ConfigModel { - id: configModel - - ConfigCategory { - name: i18n("General") - icon: "plasma" - source: "configGeneral.qml" - } -} diff --git a/applet/contents/config/main.xml b/applet/contents/config/main.xml deleted file mode 100644 --- a/applet/contents/config/main.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - true - - - - false - - - - diff --git a/applet/contents/ui/ConnectionItem.qml b/applet/contents/ui/ConnectionItem.qml --- a/applet/contents/ui/ConnectionItem.qml +++ b/applet/contents/ui/ConnectionItem.qml @@ -27,15 +27,17 @@ import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.networkmanagement 0.2 as PlasmaNM -PlasmaComponents.ListItem { +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 deactivated: ConnectionState === PlasmaNM.Enums.Deactivated + property int baseHeight: Uuid ? connectionNameLabel.implicitHeight + connectionStatusLabel.implicitHeight + units.smallSpacing * 2 + : stateChangeButton.implicitHeight + units.smallSpacing * 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 passwordIsStatic: (SecurityType == PlasmaNM.Enums.StaticWep || SecurityType == PlasmaNM.Enums.WpaPsk || + SecurityType == PlasmaNM.Enums.Wpa2Psk || SecurityType == PlasmaNM.Enums.SAE) + property bool predictableWirelessPassword: !Uuid && Type == PlasmaNM.Enums.Wireless && passwordIsStatic property bool showSpeed: plasmoid.expanded && ConnectionState == PlasmaNM.Enums.Activated && (Type == PlasmaNM.Enums.Wired || @@ -48,124 +50,135 @@ property real rxBytes: 0 property real txBytes: 0 - checked: connectionItem.containsMouse - enabled: true - height: expanded ? baseHeight + separator.height + expandableComponentLoader.height + (2 * Math.round(units.gridUnit / 3)) : baseHeight + height: expanded ? baseHeight + expandableComponentLoader.height + units.smallSpacing * (ConnectionState == PlasmaNM.Enums.Active ? 1 : Uuid ? 2 : 1) + : baseHeight ColumnLayout { + id: mainColumn anchors.fill: parent - RowLayout { + MouseArea { 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 - } + Layout.preferredHeight: mainRow.height + Layout.alignment: Qt.AlignTop + acceptedButtons: Qt.LeftButton | Qt.RightButton + hoverEnabled: true + + onEntered: { + connectionView.currentVisibleButtonIndex = index + connectionItem.checked = true } - ColumnLayout { - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft - spacing: 0 + onExited: { + connectionItem.checked = false + } - 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 + onPressed: { + if (mouse.button & Qt.LeftButton) { + changeExpanded() } - PlasmaComponents.Label { - id: connectionStatusLabel - Layout.fillWidth: true - height: paintedHeight - elide: Text.ElideRight - font.pointSize: theme.smallestFont.pointSize - opacity: 0.6 - text: itemText() + if (mouse.button & Qt.RightButton) { + contextMenu.visualParent = parent + contextMenu.prepare(); + contextMenu.open(mouse.x, mouse.y) } } - PlasmaComponents.BusyIndicator { - id: connectingIndicator - Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - Layout.preferredHeight: units.iconSizes.medium - Layout.preferredWidth: units.iconSizes.medium - running: plasmoid.expanded && !stateChangeButton.visible && ConnectionState == PlasmaNM.Enums.Activating - visible: running - opacity: visible - } + RowLayout { + id: mainRow + spacing: units.smallSpacing * 2 + height: baseHeight + width: mainColumn.width + anchors { + left: parent.left + right: parent.right + leftMargin: units.smallSpacing + // Identical margins around the button. + rightMargin: Math.round((baseHeight - stateChangeButton.height) / 2) + } + + PlasmaCore.SvgItem { + id: connectionSvgIcon + Layout.preferredHeight: Uuid ? units.iconSizes.medium : units.iconSizes.smallMedium + Layout.preferredWidth: Layout.preferredHeight + Layout.leftMargin: units.smallSpacing + elementId: ConnectionIcon + svg: PlasmaCore.Svg { + multipleImages: true + imagePath: "icons/network" + colorGroup: PlasmaCore.ColorScope.colorGroup + } + } + + // ColumnLayout with fillWidth in children, creates bind loop for width. + Column { + Layout.fillWidth: true + Layout.preferredHeight: connectionNameLabel.height + (connectionStatusLabel.visible ? connectionStatusLabel.height : 0) + spacing: 0 + + PlasmaComponents.Label { + id: connectionNameLabel + width: parent.width + elide: Text.ElideRight + height: undefined + font.weight: ConnectionState == PlasmaNM.Enums.Activated ? Font.DemiBold : Font.Normal + font.italic: ConnectionState == PlasmaNM.Enums.Activating ? true : false + text: ItemUniqueName + textFormat: Text.PlainText + } - 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") + PlasmaComponents.Label { + id: connectionStatusLabel + width: parent.width + elide: Text.ElideRight + height: undefined + font.pointSize: theme.smallestFont.pointSize + opacity: 0.6 + text: itemText() + visible: !!Uuid + } + } - Behavior on opacity { NumberAnimation { duration: units.shortDuration } } + PlasmaComponents.BusyIndicator { + id: connectingIndicator + Layout.preferredHeight: units.iconSizes.medium + Layout.preferredWidth: Layout.preferredHeight + running: plasmoid.expanded && !stateChangeButton.visible && ConnectionState == PlasmaNM.Enums.Activating + visible: running + opacity: visible + } - onClicked: changeState() - } + PlasmaComponents.Button { + id: stateChangeButton + opacity: connectionView.currentVisibleButtonIndex == index ? 1 : 0 + visible: opacity != 0 + text: (ConnectionState == PlasmaNM.Enums.Deactivated) ? i18n("Connect") : i18n("Disconnect") - MouseArea { - acceptedButtons: Qt.RightButton - Layout.alignment: Qt.AlignTop | Qt.AlignLeft - width: parent.width - height: parent.height - onPressed: { - contextMenu.visualParent = parent - contextMenu.prepare(); - contextMenu.open(mouse.x, mouse.y) + Behavior on opacity { NumberAnimation { duration: units.shortDuration } } + + onClicked: changeState() } } } - ColumnLayout { - Layout.fillHeight: true - 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 - } + Loader { + id: expandableComponentLoader + Layout.fillWidth: true + Layout.alignment: Qt.AlignBottom + Layout.bottomMargin: units.smallSpacing } } - PlasmaComponents.Menu { id: contextMenu property Component showQRComponent: null function prepare() { showQRMenuItem.visible = false; - if (Uuid && Type === PlasmaNM.Enums.Wireless && - (SecurityType === PlasmaNM.Enums.StaticWep || SecurityType === PlasmaNM.Enums.WpaPsk || SecurityType === PlasmaNM.Enums.Wpa2Psk)) { + if (Uuid && Type === PlasmaNM.Enums.Wireless && passwordIsStatic) { if (!showQRComponent) { showQRComponent = Qt.createComponent("ShowQR.qml", this); if (showQRComponent.status === Component.Error) { @@ -208,16 +221,15 @@ Component { id: detailsComponent - Item { - height: childrenRect.height + Column { + spacing: units.smallSpacing PlasmaComponents.TabBar { id: detailsTabBar anchors { left: parent.left right: parent.right - top: parent.top } height: visible ? implicitHeight : 0 visible: showSpeed @@ -242,10 +254,8 @@ DetailsText { anchors { left: parent.left - leftMargin: units.iconSizes.medium + leftMargin: units.iconSizes.smallMedium right: parent.right - top: detailsTabBar.visible ? detailsTabBar.bottom : parent.top - topMargin: Math.round(units.gridUnit / 3) } details: ConnectionDetails visible: detailsTabBar.currentTab == detailsTabButton @@ -255,8 +265,6 @@ anchors { left: parent.left right: parent.right - top: detailsTabBar.visible ? detailsTabBar.bottom : parent.top - topMargin: Math.round(units.gridUnit / 3) } rxBytes: RxBytes txBytes: TxBytes @@ -269,19 +277,15 @@ Component { id: passwordDialogComponent - Item { + ColumnLayout { 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 - } + Layout.leftMargin: units.iconSizes.smallMedium + units.smallSpacing * 2 + Layout.bottomMargin: units.smallSpacing + Layout.preferredWidth: units.gridUnit * 15 securityType: SecurityType onAccepted: { @@ -391,11 +395,8 @@ 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 (Uuid && ConnectionState == PlasmaNM.Enums.Deactivated) { + return LastUsed } else if (ConnectionState == PlasmaNM.Enums.Activated) { if (showSpeed) { var downloadColor = theme.highlightColor @@ -411,19 +412,10 @@ return i18n("Connected") } } + return "" } - onShowSpeedChanged: { - connectionModel.setDeviceStatisticsRefreshRateMs(DevicePath, showSpeed ? 2000 : 0) - } - - onActivatingChanged: { - if (ConnectionState == PlasmaNM.Enums.Activating) { - ListView.view.positionViewAtBeginning() - } - } - - onClicked: { + function changeExpanded() { if (visiblePasswordDialog) { appletProxyModel.dynamicSortFilter = true visiblePasswordDialog = false @@ -438,9 +430,36 @@ } } + onShowSpeedChanged: { + connectionModel.setDeviceStatisticsRefreshRateMs(DevicePath, showSpeed ? 2000 : 0) + } + + onActivatingChanged: { + if (ConnectionState == PlasmaNM.Enums.Activating) { + ListView.view.positionViewAtBeginning() + } + } + onContainsMouseChanged: { if (connectionItem.containsMouse) { connectionView.currentVisibleButtonIndex = index + devicesView.currentIndex = index + } else { + devicesView.currentIndex = -1 + } + } + + onDeactivatedChanged: { + /* Separator is part of section, which is visible only when available connections exist. Need to determine + if there is a connection in use, to show Separator. Otherwise need to hide it from the top of the list. + Connections in use are always on top, only need to check the first one. */ + if (appletProxyModel.data(appletProxyModel.index(0, 0), PlasmaNM.NetworkModel.SectionRole) !== "Available connections") { + if (connectionView.showSeparator != true) { + connectionView.showSeparator = true + } + return } + connectionView.showSeparator = false + return } } diff --git a/applet/contents/ui/DetailsText.qml b/applet/contents/ui/DetailsText.qml --- a/applet/contents/ui/DetailsText.qml +++ b/applet/contents/ui/DetailsText.qml @@ -22,11 +22,9 @@ import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons import org.kde.plasma.components 2.0 as PlasmaComponents -Item { +Column { property var details: [] - height: repeater.contentHeight - KQuickControlsAddons.Clipboard { id: clipboard } @@ -49,72 +47,60 @@ } } - Column { - anchors.fill: parent + Repeater { + id: repeater + + property int contentHeight: 0 + property int longestString: 0 - Repeater { - id: repeater + model: details.length / 2 - property int contentHeight: 0 - property int longestString: 0 + Item { + anchors { + left: parent.left + right: parent.right + } + height: Math.max(detailNameLabel.height, detailValueLabel.height) - model: details.length / 2 + PlasmaComponents.Label { + id: detailNameLabel - Item { anchors { left: parent.left - right: parent.right - topMargin: Math.round(units.gridUnit / 3) + leftMargin: repeater.longestString - paintedWidth + Math.round(units.gridUnit / 2) } - height: Math.max(detailNameLabel.height, detailValueLabel.height) - - PlasmaComponents.Label { - id: detailNameLabel - - anchors { - left: parent.left - leftMargin: repeater.longestString - paintedWidth + Math.round(units.gridUnit / 2) - verticalCenter: parent.verticalCenter - } - height: paintedHeight - font.pointSize: theme.smallestFont.pointSize - horizontalAlignment: Text.AlignRight - opacity: 0.6 - text: "" + details[index*2] + ":  " - - Component.onCompleted: { - if (paintedWidth > repeater.longestString) { - repeater.longestString = paintedWidth - } + height: paintedHeight + font.pointSize: theme.smallestFont.pointSize + horizontalAlignment: Text.AlignRight + text: details[index*2] + ": " + opacity: 0.6 + + Component.onCompleted: { + if (paintedWidth > repeater.longestString) { + repeater.longestString = paintedWidth } } + } - PlasmaComponents.Label { - id: detailValueLabel + PlasmaComponents.Label { + id: detailValueLabel - anchors { - left: detailNameLabel.right - right: parent.right - verticalCenter: parent.verticalCenter - } - height: paintedHeight - elide: Text.ElideRight - font.pointSize: theme.smallestFont.pointSize - opacity: 0.6 - text: details[(index*2)+1] - textFormat: Text.PlainText - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - onPressed: contextMenu.show(this, detailValueLabel.text, mouse.x, mouse.y) - } + anchors { + left: parent.left + right: parent.right + leftMargin: repeater.longestString + Math.round(units.gridUnit / 2) + } + height: paintedHeight + elide: Text.ElideRight + font.pointSize: theme.smallestFont.pointSize + text: details[(index*2)+1] + textFormat: Text.PlainText + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + onPressed: contextMenu.show(this, detailValueLabel.text, mouse.x, mouse.y) } - } - - // Count total height from added items, somehow contentRect.height doesn't work - onItemAdded: { - contentHeight = contentHeight + item.height } } } diff --git a/applet/contents/ui/Header.qml b/applet/contents/ui/Header.qml deleted file mode 100644 --- a/applet/contents/ui/Header.qml +++ /dev/null @@ -1,59 +0,0 @@ -/* - 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.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: header - - property alias text: headerLabel.text - - height: headerLabel.height + units.gridUnit; width: parent.width - sectionDelegate: true - - PlasmaNM.EnabledConnections { - id: enabledConnections - } - - PlasmaComponents.Label { - id: headerLabel - - anchors.centerIn: parent - height: paintedHeight - font.weight: Font.DemiBold - } - - Component.onCompleted: { - if (header.text === i18n("Available connections")) { - connectionView.availableConnectionsVisible = true - } - } - - Component.onDestruction: { - connectionView.availableConnectionsVisible = false - } - - onVisibleChanged: { - connectionView.availableConnectionsVisible = visible - } -} diff --git a/applet/contents/ui/ListItem.qml b/applet/contents/ui/ListItem.qml new file mode 100644 --- /dev/null +++ b/applet/contents/ui/ListItem.qml @@ -0,0 +1,90 @@ +/* + Copyright 2010 Marco Martin + Copyright 2016 Jan Grulich + Copyright 2020 George Vogiatzis + + 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.1 +import org.kde.plasma.core 2.0 as PlasmaCore + +/** + * Ignores the theme's listItem margins, and uses custom highlight(pressed) area. + * Could break some themes but the majority look fine. + * Also includes a separator to be used in sections. + */ +Item { + id: listItem + + signal clicked + + property alias containsMouse: itemMouse.containsMouse + property bool checked: false + property bool separator: false + property rect highlightRect: Qt.rect(0, 0, width, height) + + width: parent.width + + // Sections have spacing above but not below. Will use 2 of them below. + height: separator ? separatorLine.height + units.smallSpacing * 3 : parent.height + + PlasmaCore.Svg { + id: lineSvg + imagePath: "widgets/line" + } + PlasmaCore.SvgItem { + id: separatorLine + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: units.smallSpacing + } + elementId: "horizontal-line" + width: parent.width - units.gridUnit * 2 + svg: lineSvg + visible: separator + } + + PlasmaCore.FrameSvgItem { + id : background + imagePath: "widgets/listitem" + prefix: "normal" + anchors.fill: parent + visible: separator ? false : true + } + + PlasmaCore.FrameSvgItem { + id : pressed + imagePath: "widgets/listitem" + prefix: "pressed" + opacity: checked ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: units.shortDuration } } + + x: highlightRect.x + y: highlightRect.y + height: highlightRect.height + width: highlightRect.width + } + + MouseArea { + id: itemMouse + anchors.fill: parent + hoverEnabled: true + onClicked: listItem.clicked() + } +} diff --git a/applet/contents/ui/PasswordField.qml b/applet/contents/ui/PasswordField.qml --- a/applet/contents/ui/PasswordField.qml +++ b/applet/contents/ui/PasswordField.qml @@ -25,7 +25,6 @@ PlasmaComponents.TextField { property int securityType - width: units.gridUnit * 15 echoMode: TextInput.Password revealPasswordButtonShown: true placeholderText: i18n("Password...") diff --git a/applet/contents/ui/PopupDialog.qml b/applet/contents/ui/PopupDialog.qml --- a/applet/contents/ui/PopupDialog.qml +++ b/applet/contents/ui/PopupDialog.qml @@ -27,7 +27,8 @@ FocusScope { id: full - property var notificationInhibitorLock: undefined + + property alias toolbarValues: toolbar PlasmaNM.AvailableDevices { id: availableDevices @@ -58,20 +59,62 @@ id: scrollView Layout.fillWidth: true Layout.fillHeight: true + frameVisible: false + + PlasmaExtras.Heading { + id: disabledMessage + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + level: 3 + visible: connectionView.count === 0 && text != "" + enabled: false + text: { + if (toolbarValues.displayplaneModeMessage) { + return i18n("Airplane mode is enabled") + } + if (toolbarValues.displayWifiMessage) { + if (toolbarValues.displayWwanMessage) { + return i18n("Wireless and mobile networks are deactivated") + } + return i18n("Wireless is deactivated") + } + if (toolbarValues.displayWwanMessage) { + return i18n("Mobile network is deactivated") + } + return "" + } + } + + PlasmaExtras.Heading { + id: message + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + level: 3 + opacity: connectionView.count === 0 && !disabledMessage.visible ? 0.6 : 0 + // Check connectionView.count again, to avoid a small delay. + visible: opacity >= 0.6 && connectionView.count === 0 + Behavior on opacity { NumberAnimation { duration: 5000 } } + text: i18n("No available connections") + } ListView { id: connectionView - property bool availableConnectionsVisible: false property int currentVisibleButtonIndex: -1 + property bool showSeparator: false - anchors.fill: parent + spacing: units.smallSpacing clip: true model: appletProxyModel currentIndex: -1 boundsBehavior: Flickable.StopAtBounds - section.property: showSections ? "Section" : "" - section.delegate: Header { text: section } + section.property: showSeparator ? "Section" : "" + section.delegate: ListItem { separator: true } + highlight: PlasmaComponents.Highlight { } + highlightMoveDuration: 0 + highlightResizeDuration: 0 delegate: ConnectionItem { } } } @@ -81,33 +124,14 @@ target: plasmoid onExpandedChanged: { connectionView.currentVisibleButtonIndex = -1; - if (expanded) { - var service = notificationsEngine.serviceForSource("notifications"); - var operation = service.operationDescription("inhibit"); - operation.hint = "x-kde-appname"; - operation.value = "networkmanagement"; - var job = service.startOperationCall(operation); - job.finished.connect(function(job) { - if (expanded) { - notificationInhibitorLock = job.result; - } - }); - handler.requestScan() - } else { - notificationInhibitorLock = undefined; - toolbar.closeSearch() - } if (expanded) { + handler.requestScan(); full.connectionModel = networkModelComponent.createObject(full) } else { full.connectionModel.destroy() + toolbar.closeSearch(); } } } - - PlasmaCore.DataSource { - id: notificationsEngine - engine: "notifications" - } } diff --git a/applet/contents/ui/SwitchButton.qml b/applet/contents/ui/SwitchButton.qml --- a/applet/contents/ui/SwitchButton.qml +++ b/applet/contents/ui/SwitchButton.qml @@ -32,17 +32,14 @@ signal clicked - height: switchButtonIcon.height + units.gridUnit / 2 - width: switchButtonCheckbox.width + switchButtonIcon.width + units.gridUnit + height: Math.max(switchButtonCheckbox.height, switchButtonIcon.height) + width: switchButtonCheckbox.width + switchButtonIcon.width + units.smallSpacing * 3 PlasmaComponents.CheckBox { id: switchButtonCheckbox anchors { - bottomMargin: Math.round(units.gridUnit / 3) left: parent.left - leftMargin: units.gridUnit / 2 - topMargin: Math.round(units.gridUnit / 3) - verticalCenter: parent.verticalCenter + leftMargin: units.smallSpacing } onClicked: { @@ -54,10 +51,11 @@ id: switchButtonIcon anchors { left: switchButtonCheckbox.right - leftMargin: units.gridUnit / 2 - top: switchButtonCheckbox.top - bottom: switchButtonCheckbox.bottom + // Checkbox bug adds internally a smallSpacing on the right. Doubling the effective spacing here. + leftMargin: units.smallSpacing + verticalCenter: parent.verticalCenter } - width: height + width: units.iconSizes.smallMedium + height: units.iconSizes.smallMedium } } diff --git a/applet/contents/ui/Toolbar.qml b/applet/contents/ui/Toolbar.qml --- a/applet/contents/ui/Toolbar.qml +++ b/applet/contents/ui/Toolbar.qml @@ -21,20 +21,23 @@ import QtQuick 2.2 import QtQuick.Layouts 1.2 import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.components 3.0 as PlasmaComponents3 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.networkmanagement 0.2 as PlasmaNM import org.kde.kquickcontrolsaddons 2.0 -GridLayout { +ColumnLayout { id: toolbar + spacing: units.smallSpacing + + readonly property var displayWifiMessage: !wifiSwitchButton.checked && wifiSwitchButton.visible + readonly property var displayWwanMessage: !wwanSwitchButton.checked && wwanSwitchButton.visible + readonly property var displayplaneModeMessage: planeModeSwitchButton.checked && planeModeSwitchButton.visible function closeSearch() { searchToggleButton.checked = false } - rows: 2 - columns: 2 - PlasmaCore.Svg { id: lineSvg imagePath: "widgets/line" @@ -60,8 +63,12 @@ } } - Row { - Layout.fillWidth: true + PlasmaNM.Configuration { + id: configuration + } + + RowLayout { + spacing: units.smallSpacing SwitchButton { id: wifiSwitchButton @@ -119,46 +126,118 @@ initialized = true } } - } - Row { - Layout.column: 1 + Item { + Layout.fillWidth: true + } + + PlasmaComponents3.ToolButton { + id: hotspotButton + + icon { + height: units.iconSizes.small + width: units.iconSizes.small + name: "network-wireless-on" + } + checkable: true + text: i18n("Hotspot") + visible: handler.hotspotSupported + + onClicked: { + if (configuration.hotspotConnectionPath) { + checked = false + handler.stopHotspot() + } else { + checked = true + handler.createHotspot() + } + } + + PlasmaComponents3.ToolTip { + id: tooltip + } + + Connections { + target: handler + onHotspotCreated: { + hotspotButton.checked = true + tooltip.text = i18n("Disable Hotspot") + } + + onHotspotDisabled: { + hotspotButton.checked = false + tooltip.text = i18n("Create Hotspot") + } + } + + Component.onCompleted: { + checked = configuration.hotspotConnectionPath + tooltip.text = configuration.hotspotConnectionPath ? i18n("Disable Hotspot") : i18n("Create Hotspot") + } + } - PlasmaComponents.ToolButton { + PlasmaComponents3.ToolButton { id: searchToggleButton - iconSource: "search" - tooltip: i18ndc("plasma-nm", "button tooltip", "Search the connections") + icon { + height: units.iconSizes.small + width: units.iconSizes.small + name: "search" + } checkable: true + + PlasmaComponents3.ToolTip { + text: i18ndc("plasma-nm", "button tooltip", "Search the connections") + } } - PlasmaComponents.ToolButton { + PlasmaComponents3.ToolButton { id: openEditorButton - iconSource: "configure" - tooltip: i18n("Configure network connections...") + icon { + height: units.iconSizes.small + width: units.iconSizes.small + name: "configure" + } visible: mainWindow.kcmAuthorized + PlasmaComponents3.ToolTip { + text: i18n("Configure network connections...") + } + onClicked: { KCMShell.open(mainWindow.kcm) } } } - PlasmaComponents.TextField { + PlasmaCore.SvgItem { + Layout.fillWidth: true + elementId: "horizontal-line" + Layout.leftMargin: - units.smallSpacing * 1.5 + Layout.rightMargin: - units.smallSpacing * 1.5 + svg: lineSvg + } + + PlasmaComponents.TextField { id: searchTextField - Layout.row: 1 - Layout.columnSpan: 2 Layout.fillWidth: true Layout.leftMargin: units.smallSpacing Layout.rightMargin: units.smallSpacing + Layout.topMargin: units.smallSpacing Layout.bottomMargin: units.smallSpacing focus: true clearButtonShown: true placeholderText: i18ndc("plasma-nm", "text field placeholder text", "Search...") visible: searchToggleButton.checked - onVisibleChanged: if (!visible) text = "" + onVisibleChanged: { + if (visible) { + searchTextField.forceActiveFocus() + } else { + text = "" + } + } Keys.onEscapePressed: { //Check if the searchbar is actually visible before accepting the escape key. Otherwise, the escape key cannot dismiss the applet until one interacts with some other element. if (searchToggleButton.checked) { diff --git a/applet/contents/ui/TrafficMonitor.qml b/applet/contents/ui/TrafficMonitor.qml --- a/applet/contents/ui/TrafficMonitor.qml +++ b/applet/contents/ui/TrafficMonitor.qml @@ -18,7 +18,7 @@ License along with this library. If not, see . */ -import QtQuick 2.2 +import QtQuick 2.4 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 @@ -28,18 +28,22 @@ property real txBytes: 0 property alias interval: timer.interval - height: visible ? plotter.height + units.gridUnit : 0 + height: visible ? plotter.height + plotter.anchors.topMargin + units.smallSpacing : 0 Repeater { - model: 5 + id: labels + model: 6 + readonly property int labelHeight: theme.mSize(theme.smallestFont).height PlasmaComponents.Label { anchors { - left: parent.left + right: plotter.left top: parent.top - topMargin: Math.round(units.gridUnit / 3) + (index * plotter.height / 5) + rightMargin: units.smallSpacing + topMargin: Math.round(index * plotter.height / 5) } - height: paintedHeight + // Workaround to get paintedHeight. (Undefined or paintedheight does not work.) + height: labels.labelHeight font.pointSize: theme.smallestFont.pointSize lineHeight: 1.75 text: KCoreAddons.Format.formatByteSize(plotter.maxValue * (1 - index / 5)) + i18n("/s") @@ -54,12 +58,12 @@ 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 + leftMargin: speedMetrics.width + units.smallSpacing * 2 right: parent.right top: parent.top - topMargin: units.gridUnit + // Align plotter lines with labels. + topMargin: Math.round(labels.labelHeight / 2) } - width: units.gridUnit * 20 height: units.gridUnit * 8 horizontalGridLineCount: 5 @@ -95,4 +99,11 @@ } } } + + TextMetrics { + id: speedMetrics + font.pointSize: theme.smallestFont.pointSize + // Measure 888.8 KiB/s + text: KCoreAddons.Format.formatByteSize(910131) + i18n("/s") + } } diff --git a/applet/contents/ui/configGeneral.qml b/applet/contents/ui/configGeneral.qml deleted file mode 100644 --- a/applet/contents/ui/configGeneral.qml +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright 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.5 -import QtQuick.Controls 2.5 as QQC2 - -import org.kde.kirigami 2.5 as Kirigami - -Kirigami.FormLayout { - id: generalPage - - anchors.left: parent.left - anchors.right: parent.right - - signal configurationChanged - - property alias cfg_unlockModemOnDetection: unlockModem.checked - property alias cfg_manageVirtualConnections: manageVirtualConnections.checked - - QQC2.CheckBox { - id: unlockModem - text: i18n("Ask for PIN on modem detection") - onClicked: { - generalPage.configurationChanged() - } - } - - QQC2.CheckBox { - id: manageVirtualConnections - text: i18n("Show virtual connections") - onClicked: { - generalPage.configurationChanged() - } - } -} diff --git a/applet/contents/ui/main.qml b/applet/contents/ui/main.qml --- a/applet/contents/ui/main.qml +++ b/applet/contents/ui/main.qml @@ -27,7 +27,6 @@ Item { id: mainWindow - property bool showSections: true readonly property string kcm: "kcm_networkmanagement.desktop" readonly property bool kcmAuthorized: KCMShell.authorize(kcm).length == 1 @@ -47,17 +46,17 @@ function action_openKCM() { KCMShell.open(kcm) - - } + function action_showPortal() { Qt.openUrlExternally("http://networkcheck.kde.org") } Component.onCompleted: { if (kcmAuthorized) { plasmoid.setAction("openKCM", i18n("&Configure Network Connections..."), "preferences-system-network"); } + plasmoid.removeAction("configure"); plasmoid.setAction("showPortal", i18n("Open Network Login Page..."), "internet-services"); var action = plasmoid.action("showPortal"); @@ -84,10 +83,4 @@ onTriggered: handler.requestScan() } - - PlasmaNM.Configuration { - id: configuration - unlockModemOnDetection: plasmoid.configuration.unlockModemOnDetection - manageVirtualConnections: plasmoid.configuration.manageVirtualConnections - } } diff --git a/applet/metadata.desktop b/applet/metadata.desktop --- a/applet/metadata.desktop +++ b/applet/metadata.desktop @@ -24,6 +24,7 @@ Name[ja]=ネットワーク Name[ko]=네트워크 Name[lt]=Tinklai +Name[ml]=ശൃംഖലകൾ Name[nb]=Nettverk Name[nds]=Nettwarken Name[nl]=Netwerken @@ -73,6 +74,7 @@ Comment[ja]=ネットワークの状態と管理 Comment[ko]=네트워크 상태 및 제어 Comment[lt]=Tinklo būsena ir valdymas +Comment[ml]=ശൃംഖല നിലയും നിയന്ത്രണവും Comment[nb]=Nettverksstatus og styring Comment[nds]=Nettwarkstatus un -stüern Comment[nl]=Netwerkstatus en besturing diff --git a/kcm/CMakeLists.txt b/kcm/CMakeLists.txt --- a/kcm/CMakeLists.txt +++ b/kcm/CMakeLists.txt @@ -30,8 +30,9 @@ install( FILES kcm_networkmanagement.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) install( FILES + qml/AddConnectionDialog.qml + qml/ConfigurationDialog.qml qml/ConnectionItem.qml - qml/Dialog.qml qml/Header.qml qml/ListItem.qml qml/main.qml diff --git a/kcm/kcm.cpp b/kcm/kcm.cpp --- a/kcm/kcm.cpp +++ b/kcm/kcm.cpp @@ -77,13 +77,45 @@ kdeclarative.setupEngine(m_ui->connectionView->engine()); kdeclarative.setupContext(); + // Check if we can use AP mode to identify security type + bool useApMode = false; + bool foundInactive = false; + + NetworkManager::WirelessDevice::Ptr wifiDev; + + for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) { + if (device->type() == NetworkManager::Device::Wifi) { + wifiDev = device.objectCast(); + if (wifiDev) { + if (!wifiDev->isActive()) { + foundInactive = true; + } else { + // Prefer previous device if it was inactive + if (foundInactive) { + break; + } + } + + if (wifiDev->wirelessCapabilities().testFlag(NetworkManager::WirelessDevice::ApCap)) { + useApMode = true; + } + + // We prefer inactive wireless card with AP capabilities + if (foundInactive && useApMode) { + break; + } + } + } + } + m_ui->connectionView->setMinimumWidth(300); m_ui->connectionView->rootContext()->setContextProperty("alternateBaseColor", mainWidget->palette().color(QPalette::Active, QPalette::AlternateBase)); m_ui->connectionView->rootContext()->setContextProperty("backgroundColor", mainWidget->palette().color(QPalette::Active, QPalette::Window)); m_ui->connectionView->rootContext()->setContextProperty("baseColor", mainWidget->palette().color(QPalette::Active, QPalette::Base)); m_ui->connectionView->rootContext()->setContextProperty("highlightColor", mainWidget->palette().color(QPalette::Active, QPalette::Highlight)); m_ui->connectionView->rootContext()->setContextProperty("textColor", mainWidget->palette().color(QPalette::Active, QPalette::Text)); m_ui->connectionView->rootContext()->setContextProperty("connectionModified", false); + m_ui->connectionView->rootContext()->setContextProperty("useApMode", useApMode); m_ui->connectionView->setClearColor(Qt::transparent); m_ui->connectionView->setResizeMode(QQuickWidget::SizeRootObjectToView); m_ui->connectionView->setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kcm_networkmanagement/qml/main.qml")))); diff --git a/kcm/kcm_networkmanagement.desktop b/kcm/kcm_networkmanagement.desktop --- a/kcm/kcm_networkmanagement.desktop +++ b/kcm/kcm_networkmanagement.desktop @@ -26,11 +26,13 @@ Name[gl]=Conexións Name[he]=חיבורים Name[hu]=Kapcsolatok +Name[ia]=Connexiones Name[id]=Koneksi Name[it]=Connessioni Name[ja]=接続 Name[ko]=연결 Name[lt]=Ryšiai +Name[ml]=ബന്ധങ്ങള്‍ Name[nl]=Verbindingen Name[nn]=Tilkoplingar Name[pa]=ਕਨੈਕਸ਼ਨ @@ -69,11 +71,13 @@ Comment[gl]=Edite as súas conexións de rede Comment[he]=ערוך את חיבורי הרשת שלך Comment[hu]=A hálózati kapcsolatok szerkesztése +Comment[ia]=Modifica tu Connexiones de Rete Comment[id]=Edit Koneksi Jaringanmu Comment[it]=Modifica le tue connessioni di rete Comment[ja]=ネットワーク接続を編集 Comment[ko]=네트워크 연결 편집 Comment[lt]=Taisyti tinklo ryšius +Comment[ml]=നിങ്ങളുടെ നെറ്റ്‌വർക്ക് ബന്ധങ്ങള്‍ എഡിറ്റുചെയ്യുക Comment[nl]=Bewerk uw netwerkverbindingen Comment[nn]=Rediger nettverkstilkoplingar Comment[pa]=ਆਪਣੇ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨਾਂ ਨੂੰ ਸੋਧੋ @@ -114,6 +118,7 @@ X-KDE-Keywords[it]=rete,wifi,ethernet,mobile,banda larga,vpn,internet,traffico X-KDE-Keywords[ko]=network,wifi,ethernet,mobile,broadband,vpn,internet,traffic,네트워크,와이파이,무선랜,이더넷,모바일,광대역,인터넷,트래픽 X-KDE-Keywords[lt]=tinklas,belaidis,bevielis,wifi,laidinis,plačiajuostis,mobilusis,vpn,internetas,duomenų srautas +X-KDE-Keywords[ml]=ശൃംഖല,വൈഫൈ,ഈതര്‍നെറ്റ്,മൊബൈൽ,ബ്രോഡ്‌ബാൻഡ്,വിപിഎൻ,ഇന്റർനെറ്റ്,ട്രാഫിക് X-KDE-Keywords[nl]=netwerk,wifi,ethernet,mobiel,breedband,vpn,internet,verkeer X-KDE-Keywords[nn]=nettverk,wifi,trådlaus,Ethernet,mobilt,breiband,VPN,Internett,trafikk,tilkopling,samband X-KDE-Keywords[pa]=ਨੈੱਟਵਰਕ,ਵਾਈਫਾਈ,ਵਾਈ-ਫਾਈ,ਮੋਬਾਈਲ,ਬਰਾਂਡਬੈਂਡ,ਵੀਪੀਐਨ,ਇੰਟਰਨੈੱਟ,ਟਰੈਫਿਕ diff --git a/kcm/qml/Dialog.qml b/kcm/qml/AddConnectionDialog.qml rename from kcm/qml/Dialog.qml rename to kcm/qml/AddConnectionDialog.qml diff --git a/kcm/qml/ConfigurationDialog.qml b/kcm/qml/ConfigurationDialog.qml new file mode 100644 --- /dev/null +++ b/kcm/qml/ConfigurationDialog.qml @@ -0,0 +1,163 @@ +/* + Copyright 2019 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.5 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls 2.5 as QQC2 +import org.kde.kirigami 2.5 as Kirigami +import org.kde.plasma.networkmanagement 0.2 as PlasmaNM + +Dialog { + id: configurationDialog + standardButtons: Dialog.Ok | Dialog.Cancel + title: i18nc("@title:window", "Configuration") + + PlasmaNM.Configuration { + id: configuration + } + + contentItem: Item { + implicitHeight: 200 + implicitWidth: 400 + + Rectangle { + id: background + anchors.fill: parent + focus: true + color: baseColor + } + + Kirigami.FormLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: units.gridUnit + + Kirigami.Heading { + id: generalLabel + level: 2 + text: i18n("General") + } + + QQC2.CheckBox { + id: unlockModem + text: i18n("Ask for PIN on modem detection") + onClicked: configurationChanged() + Component.onCompleted: checked = configuration.unlockModemOnDetection + } + + QQC2.CheckBox { + id: manageVirtualConnections + text: i18n("Show virtual connections") + onClicked: configurationChanged() + Component.onCompleted: checked = configuration.manageVirtualConnections + } + + Kirigami.Heading { + id: hotspotLabel + level: 2 + text: i18n("Hotspot") + Component.onCompleted: visible = handler.hotspotSupported + } + + QQC2.TextField { + id: hotspotName + Kirigami.FormData.label: i18n("Hotspot name:") + onTextChanged: configurationChanged() + Component.onCompleted: { + text = configuration.hotspotName + visible = handler.hotspotSupported + } + } + + QQC2.TextField { + id: hotspotPassword + Kirigami.FormData.label: i18n("Hotspot password:") + validator: RegExpValidator { + regExp: if (useApMode) { + /^$|^(?:.{8,64}){1}$/ + } else { + /^$|^(?:.{5}|[0-9a-fA-F]{10}|.{13}|[0-9a-fA-F]{26}){1}$/ + } + } + + onAcceptableInputChanged: configurationChanged() + + Component.onCompleted: { + text = configuration.hotspotPassword + visible = handler.hotspotSupported + } + } + } + + Row { + id: buttonRow + anchors { + bottom: parent.bottom + right: parent.right + margins: units.smallSpacing + } + spacing: units.smallSpacing + + QQC2.Button { + id: okButton + enabled: false + text: i18n("Ok") + + onClicked: { + configurationDialog.accept() + } + } + + QQC2.Button { + id: cancelButton + text: i18n("Cancel") + + onClicked: { + configurationDialog.close() + } + } + } + } + + function configurationChanged() { + if (handler.hotspotSupported) { + okButton.enabled = hotspotPassword.acceptableInput && hotspotName.text + } else { + okButton.enabled = true + } + } + + onVisibleChanged: { + if (visible) { + unlockModem.checked = configuration.unlockModemOnDetection + manageVirtualConnections.checked = configuration.manageVirtualConnections + hotspotName.text = configuration.hotspotName + hotspotPassword.text = configuration.hotspotPassword + } + } + + onAccepted: { + configuration.unlockModemOnDetection = unlockModem.checked + configuration.manageVirtualConnections = manageVirtualConnections.checked + configuration.hotspotName = hotspotName.text + configuration.hotspotPassword = hotspotPassword.text + } +} + diff --git a/kcm/qml/ConnectionItem.qml b/kcm/qml/ConnectionItem.qml --- a/kcm/qml/ConnectionItem.qml +++ b/kcm/qml/ConnectionItem.qml @@ -19,8 +19,8 @@ */ import QtQuick 2.1 +import QtQuick.Controls 1.4 as QQC import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons -import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.networkmanagement 0.2 as PlasmaNM @@ -105,37 +105,36 @@ } } - PlasmaComponents.Menu { + QQC.Menu { id: connectionItemMenu - visualParent: mouseArea - PlasmaComponents.MenuItem { + QQC.MenuItem { text: ConnectionState == PlasmaNM.Enums.Deactivated ? i18n("Connect") : i18n("Disconnect") visible: ItemType == 1 - onClicked: { + onTriggered: { if (ConnectionState == PlasmaNM.Enums.Deactivated) { handler.activateConnection(ConnectionPath, DevicePath, SpecificPath); } else { handler.deactivateConnection(ConnectionPath, DevicePath); } } } - PlasmaComponents.MenuItem { - icon: "list-remove" + QQC.MenuItem { + iconName: "list-remove" text: i18n("Delete"); - onClicked: { + onTriggered: { aboutToRemoveConnection(Name, ConnectionPath) } } - PlasmaComponents.MenuItem { - icon: "document-export" + QQC.MenuItem { + iconName: "document-export" visible: KcmVpnConnectionExportable text: i18n("Export"); - onClicked: aboutToExportConnection(ConnectionPath) + onTriggered: aboutToExportConnection(ConnectionPath) } } @@ -149,7 +148,7 @@ if (mouse.button === Qt.LeftButton) { aboutToChangeConnection(KcmVpnConnectionExportable, Name, ConnectionPath) } else if (mouse.button == Qt.RightButton) { - connectionItemMenu.open(mouse.x, mouse.y) + connectionItemMenu.popup() } } } diff --git a/kcm/qml/main.qml b/kcm/qml/main.qml --- a/kcm/qml/main.qml +++ b/kcm/qml/main.qml @@ -20,10 +20,11 @@ import QtQuick 2.1 import QtQuick.Dialogs 1.1 -import QtQuick.Controls 1.2 as QtControls +import QtQuick.Controls 2.5 as QQC2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.plasma.networkmanagement 0.2 as PlasmaNM +import org.kde.kirigami 2.9 as Kirigami Item { id: root @@ -35,11 +36,13 @@ signal requestExportConnection(string connection) signal requestToChangeConnection(string name, string path) + Kirigami.Theme.colorSet: Kirigami.Theme.Window + Rectangle { id: background anchors.fill: parent focus: true - color: baseColor + color: Kirigami.Theme.backgroundColor } PlasmaNM.Handler { @@ -56,7 +59,11 @@ sourceModel: connectionModel } - QtControls.TextField { + PlasmaNM.Configuration { + id: configuration + } + + QQC2.TextField { id: searchField anchors { @@ -72,12 +79,14 @@ } } - QtControls.ScrollView { + QQC2.ScrollView { id: scrollView + Component.onCompleted: scrollView.background.visible = true // show frame + anchors { - bottom: buttonRow.top - bottomMargin: Math.round(units.gridUnit / 3) + bottom: rightButtonRow.top + bottomMargin: units.smallSpacing left: parent.left right: parent.right top: searchField.bottom @@ -90,8 +99,9 @@ property string currentConnectionName property string currentConnectionPath - anchors.fill: parent clip: true + focus: true + activeFocusOnTab: true model: editorProxyModel currentIndex: -1 boundsBehavior: Flickable.StopAtBounds @@ -127,53 +137,83 @@ } Row { - id: buttonRow + id: rightButtonRow anchors { bottom: parent.bottom right: parent.right - margins: Math.round(units.gridUnit / 3) + margins: units.smallSpacing } - spacing: Math.round(units.gridUnit / 2) + spacing: units.smallSpacing - QtControls.ToolButton { + QQC2.ToolButton { id: addConnectionButton - iconName: "list-add" - tooltip: i18n("Add new connection") + icon.name: "list-add" + + QQC2.ToolTip.text: i18n("Add new connection") + QQC2.ToolTip.visible: hovered onClicked: { addNewConnectionDialog.open() } } - QtControls.ToolButton { + QQC2.ToolButton { id: removeConnectionButton enabled: connectionView.currentConnectionPath && connectionView.currentConnectionPath.length - iconName: "list-remove" - tooltip: i18n("Remove selected connection") + icon.name: "list-remove" + + QQC2.ToolTip.text: i18n("Remove selected connection") + QQC2.ToolTip.visible: hovered onClicked: { deleteConfirmationDialog.connectionName = connectionView.currentConnectionName deleteConfirmationDialog.connectionPath = connectionView.currentConnectionPath deleteConfirmationDialog.open() } } - QtControls.ToolButton { + QQC2.ToolButton { id: exportConnectionButton enabled: connectionView.currentConnectionExportable - iconName: "document-export" - tooltip: i18n("Export selected connection") + icon.name: "document-export" + + QQC2.ToolTip.text: i18n("Export selected connection") + QQC2.ToolTip.visible: hovered onClicked: { root.requestExportConnection(connectionView.currentConnectionPath) } } } + Row { + id: leftButtonRow + + anchors { + bottom: parent.bottom + left: parent.left + margins: units.smallSpacing + } + spacing: units.smallSpacing + + QQC2.ToolButton { + id: configureButton + + icon.name: "configure" + + QQC2.ToolTip.text: i18n("Configuration") + QQC2.ToolTip.visible: hovered + + onClicked: { + configurationDialog.open() + } + } + } + MessageDialog { id: deleteConfirmationDialog @@ -194,14 +234,18 @@ } } - Dialog { + AddConnectionDialog { id: addNewConnectionDialog onRequestCreateConnection: { root.requestCreateConnection(type, vpnType, specificType, shared) } } + ConfigurationDialog { + id: configurationDialog + } + function deselectConnections() { connectionView.currentConnectionPath = "" } diff --git a/kded/modemmonitor.cpp b/kded/modemmonitor.cpp --- a/kded/modemmonitor.cpp +++ b/kded/modemmonitor.cpp @@ -45,7 +45,7 @@ class ModemMonitorPrivate { public: - QWeakPointer dialog; + QPointer dialog; }; ModemMonitor::ModemMonitor(QObject * parent) @@ -117,38 +117,38 @@ } if (lock == MM_MODEM_LOCK_SIM_PIN) { - d->dialog = new PinDialog(modem, PinDialog::SimPin); + d->dialog = QPointer(new PinDialog(modem, PinDialog::SimPin)); } else if (lock == MM_MODEM_LOCK_SIM_PIN2) { - d->dialog = new PinDialog(modem, PinDialog::SimPin2); + d->dialog = QPointer(new PinDialog(modem, PinDialog::SimPin2)); } else if (lock == MM_MODEM_LOCK_SIM_PUK) { - d->dialog = new PinDialog(modem, PinDialog::SimPuk); + d->dialog = QPointer(new PinDialog(modem, PinDialog::SimPuk)); } else if (lock == MM_MODEM_LOCK_SIM_PUK2 ) { - d->dialog = new PinDialog(modem, PinDialog::SimPuk); + d->dialog = QPointer(new PinDialog(modem, PinDialog::SimPuk)); } else if (lock == MM_MODEM_LOCK_PH_SP_PIN) { - d->dialog = new PinDialog(modem, PinDialog::ModemServiceProviderPin); + d->dialog = QPointer(new PinDialog(modem, PinDialog::ModemServiceProviderPin)); } else if (lock == MM_MODEM_LOCK_PH_SP_PUK) { - d->dialog = new PinDialog(modem, PinDialog::ModemServiceProviderPuk); + d->dialog = QPointer(new PinDialog(modem, PinDialog::ModemServiceProviderPuk)); } else if (lock == MM_MODEM_LOCK_PH_NET_PIN) { - d->dialog = new PinDialog(modem, PinDialog::ModemNetworkPin); + d->dialog = QPointer(new PinDialog(modem, PinDialog::ModemNetworkPin)); } else if (lock == MM_MODEM_LOCK_PH_NET_PUK) { - d->dialog = new PinDialog(modem, PinDialog::ModemNetworkPuk); + d->dialog = QPointer(new PinDialog(modem, PinDialog::ModemNetworkPuk)); } else if (lock == MM_MODEM_LOCK_PH_SIM_PIN) { - d->dialog = new PinDialog(modem, PinDialog::ModemPin); + d->dialog = QPointer(new PinDialog(modem, PinDialog::ModemPin)); } else if (lock == MM_MODEM_LOCK_PH_CORP_PIN) { - d->dialog = new PinDialog(modem, PinDialog::ModemCorporatePin); + d->dialog = QPointer(new PinDialog(modem, PinDialog::ModemCorporatePin)); } else if (lock == MM_MODEM_LOCK_PH_CORP_PUK) { - d->dialog = new PinDialog(modem, PinDialog::ModemCorporatePuk); + d->dialog = QPointer(new PinDialog(modem, PinDialog::ModemCorporatePuk)); } else if (lock == MM_MODEM_LOCK_PH_FSIM_PIN) { - d->dialog = new PinDialog(modem, PinDialog::ModemPhFsimPin); + d->dialog = QPointer(new PinDialog(modem, PinDialog::ModemPhFsimPin)); } else if (lock == MM_MODEM_LOCK_PH_FSIM_PUK) { - d->dialog = new PinDialog(modem, PinDialog::ModemPhFsimPuk); + d->dialog = QPointer(new PinDialog(modem, PinDialog::ModemPhFsimPuk)); } else if (lock == MM_MODEM_LOCK_PH_NETSUB_PIN) { - d->dialog = new PinDialog(modem, PinDialog::ModemNetworkSubsetPin); + d->dialog = QPointer(new PinDialog(modem, PinDialog::ModemNetworkSubsetPin)); } else if (lock == MM_MODEM_LOCK_PH_NETSUB_PUK) { - d->dialog = new PinDialog(modem, PinDialog::ModemNetworkSubsetPuk); + d->dialog = QPointer(new PinDialog(modem, PinDialog::ModemNetworkSubsetPuk)); } - if (d->dialog.toStrongRef().data()->exec() != QDialog::Accepted) { + if (d->dialog.data()->exec() != QDialog::Accepted) { goto OUT; } @@ -167,28 +167,28 @@ QDBusPendingCallWatcher *watcher = nullptr; - PinDialog::Type type = d->dialog.toStrongRef().data()->type(); + PinDialog::Type type = d->dialog.data()->type(); if (type == PinDialog::SimPin || type == PinDialog::SimPin2 || type == PinDialog::ModemServiceProviderPin || type == PinDialog::ModemNetworkPin || type == PinDialog::ModemPin || type == PinDialog::ModemCorporatePin || type == PinDialog::ModemPhFsimPin || type == PinDialog::ModemNetworkSubsetPin) { - QDBusPendingCall reply = sim->sendPin(d->dialog.toStrongRef().data()->pin()); + QDBusPendingCall reply = sim->sendPin(d->dialog.data()->pin()); watcher = new QDBusPendingCallWatcher(reply, sim.data()); } else if (type == PinDialog::SimPuk || type == PinDialog::SimPuk2 || type == PinDialog::ModemServiceProviderPuk || type == PinDialog::ModemNetworkPuk || type == PinDialog::ModemCorporatePuk || type == PinDialog::ModemPhFsimPuk || type == PinDialog::ModemNetworkSubsetPuk) { - QDBusPendingCall reply = sim->sendPuk(d->dialog.toStrongRef().data()->puk(), d->dialog.toStrongRef().data()->pin()); + QDBusPendingCall reply = sim->sendPuk(d->dialog.data()->puk(), d->dialog.data()->pin()); watcher = new QDBusPendingCallWatcher(reply, sim.data()); } connect(watcher, &QDBusPendingCallWatcher::finished, this, &ModemMonitor::onSendPinArrived); } OUT: if(d->dialog) { - d->dialog.toStrongRef().data()->deleteLater(); + d->dialog.data()->deleteLater(); } d->dialog.clear(); } diff --git a/kded/networkmanagement.desktop b/kded/networkmanagement.desktop --- a/kded/networkmanagement.desktop +++ b/kded/networkmanagement.desktop @@ -2,20 +2,29 @@ Name=Plasma Network Management module Name[ca]=Mòdul del Plasma per a la Gestió de la xarxa Name[cs]=Modul Plasma pro správu sítě +Name[da]=Plasma netværkshåndteringsmodul Name[de]=Plasma-Netzwerkverwaltungsmodul Name[en_GB]=Plasma Network Management module Name[es]=Modulo de gestión de redes de Plasma Name[et]=Plasma võrguhalduse moodul Name[eu]=Plasmako Sareak Kudeatzeko modulua +Name[fi]=Plasman verkonhallintamoduuli +Name[fr]=Module de gestion du réseau pour Plasma Name[gl]=Módulo de xestión da rede de Plasma +Name[hu]=Plasma hálózatkezelés modul +Name[ia]=Modulo de gestion de rete de Plasma Name[id]=Modul Pengelolaan Jaringan Plasma Name[it]=Modulo della gestione di rete di Plasma +Name[ko]=Plasma 네트워크 관리 모듈 Name[lt]=Plasma tinklo valdymo modulis +Name[ml]=പ്ലാസ്മ ശൃംഖല നടത്തിപ്പ്‌ മൊഡ്യൂൾ Name[nl]=Plasma module voor netwerkbeheer Name[nn]=Plasma-modul for nettverksstyring +Name[pa]=ਪਲਾਜ਼ਮਾ ਨੈੱਟਵਰਕ ਇੰਤਜ਼ਾਮ ਮੋਡੀਊਲ Name[pl]=Moduł Plazmy do zarządzania siecią Name[pt]=Módulo de Gestão de Rede do Plasma Name[pt_BR]=Módulo de Gerenciamento de Rede do Plasma +Name[ru]=Модуль для управления сетью Name[sk]=Modul Plasma správa siete Name[sv]=Plasma nätverkshanteringsmodul Name[uk]=Модуль керування мережею Плазми diff --git a/kded/networkmanagement.notifyrc b/kded/networkmanagement.notifyrc --- a/kded/networkmanagement.notifyrc +++ b/kded/networkmanagement.notifyrc @@ -25,6 +25,7 @@ Name[ja]=ネットワーク管理 Name[ko]=네트워크 관리 Name[lt]=Tinklo valdymas +Name[ml]=ശൃംഖല നടത്തിപ്പ്‌ Name[nb]=Nettverksstyring Name[nds]=Nettwarkpleeg Name[nl]=Netwerkbeheer @@ -72,6 +73,7 @@ Comment[ja]=ネットワーク管理 Comment[ko]=네트워크 관리 Comment[lt]=Tinklo valdymas +Comment[ml]=ശൃംഖല നടത്തിപ്പ്‌ Comment[nb]=Nettverksstyring Comment[nds]=Nettwarkpleeg Comment[nl]=Netwerkbeheer @@ -121,6 +123,7 @@ Name[ja]=接続が有効化されました Name[ko]=연결 활성화됨 Name[lt]=Ryšys aktyvuotas +Name[ml]=ബന്ധം സജീവമാക്കി Name[nb]=Tilkobling aktivert Name[nds]=Verbinnen anmaakt Name[nl]=Verbinding geactiveerd @@ -172,6 +175,7 @@ Name[ja]=接続が無効化されました Name[ko]=연결 비활성화됨 Name[lt]=Ryšys išjungtas +Name[ml]=ബന്ധം നിർജ്ജീവമാക്കി Name[nb]=Tilkobling deaktivert Name[nds]=Verbinnen utmaakt Name[nl]=Verbinding gedeactiveerd @@ -223,6 +227,7 @@ Name[ja]=接続が追加されました Name[ko]=연결 추가됨 Name[lt]=Ryšys pridėtas +Name[ml]=ബന്ധം ചേർത്തു Name[nb]=Tilkobling lagt til Name[nds]=Verbinnen toföögt Name[nl]=Verbinding toegevoegd @@ -273,6 +278,7 @@ Name[ja]=接続が削除されました Name[ko]=연결 삭제됨 Name[lt]=Ryšys pašalintas +Name[ml]=ബന്ധം നീക്കംചെയ്‌തു Name[nb]=Tilkobling fjernet Name[nds]=Verbinnen wegmaakt Name[nl]=Verbinding verwijderd @@ -323,6 +329,7 @@ Name[ja]=接続が更新されました Name[ko]=연결 업데이트됨 Name[lt]=Ryšys atnaujintas +Name[ml]=ബന്ധം പുതുക്കി Name[nb]=Tilkobling oppdatert Name[nds]=Verbinnen opfrischt Name[nl]=Verbinding bijgewerkt @@ -831,3 +838,32 @@ Urgency=Low IconName=dialog-password Action=Popup + +[Event/FailedToCreateHotspot] +Name=Failed to create hotspot +Name[ast]=Fallu al crear el puntu d'accesu +Name[ca]=Ha fallat en crear el punt d'accés +Name[da]=Kunne ikke aktivere internetdeling +Name[de]=Hotspot kann nicht erstellt werden +Name[es]=No se ha podido crear el punto de acceso +Name[et]=Pääsupunkti loomine nurjus +Name[eu]=Berogunea sortzea huts egin du +Name[fr]=Impossible de créer le point d'accès +Name[ia]=Il falleva a crear hotspot +Name[id]=Gagal menciptakan hotspot +Name[it]=Creazione hotspot non riuscita +Name[ko]=핫스팟을 만들 수 없음 +Name[lt]=Nepavyko sukurti viešosios prieigos taško +Name[nl]=Hotspot aanmaken is mislukt +Name[nn]=Klarte ikkje oppretta trådlaussone +Name[pa]=ਹਾਟਸਪਾਟ ਬਣਾਉਣ ਲਈ ਅਸਫ਼ਲ +Name[pt]=Não foi possível criar o ponto de acesso +Name[pt_BR]=Falha ao criar o ponto de acesso +Name[ru]=Не удалось создать точку доступа +Name[sv]=Misslyckade skapa accesspunkt +Name[uk]=Не вдалося створити хот-спот +Name[x-test]=xxFailed to create hotspotxx +Name[zh_TW]=無法建立熱點 +Urgency=Low +IconName=applications-internet +Action=Popup diff --git a/kded/notification.cpp b/kded/notification.cpp --- a/kded/notification.cpp +++ b/kded/notification.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include @@ -336,7 +335,7 @@ connect(notify, &KNotification::closed, this, &Notification::notificationClosed); notify->setProperty("uni", device->uni()); notify->setComponentName(QStringLiteral("networkmanagement")); - notify->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-warning")).pixmap(KIconLoader::SizeHuge)); + notify->setIconName(QStringLiteral("dialog-warning")); notify->setTitle(identifier); notify->setText(text); notify->sendEvent(); diff --git a/kded/portalmonitor.h b/kded/portalmonitor.h --- a/kded/portalmonitor.h +++ b/kded/portalmonitor.h @@ -24,7 +24,9 @@ #include +#include #include +#include class PortalMonitor : public QObject { @@ -36,6 +38,9 @@ private Q_SLOTS: void connectivityChanged(NetworkManager::Connectivity connectivity); void checkConnectivity(); + +private: + QPointer m_notification; }; #endif // PLASMA_NM_PORTAL_MONITOR_H diff --git a/kded/portalmonitor.cpp b/kded/portalmonitor.cpp --- a/kded/portalmonitor.cpp +++ b/kded/portalmonitor.cpp @@ -40,26 +40,45 @@ PortalMonitor::~PortalMonitor() { + if (m_notification) { + m_notification->close(); + } } void PortalMonitor::connectivityChanged(NetworkManager::Connectivity connectivity) { if (connectivity == NetworkManager::Portal) { + bool updateOnly = true; NetworkManager::ActiveConnection::Ptr primaryConnection = NetworkManager::primaryConnection(); - KNotification *notification = new KNotification(QStringLiteral("CaptivePortal"), KNotification::Persistent, this); - notification->setActions(QStringList{i18n("Log in")}); - notification->setComponentName(QStringLiteral("networkmanagement")); + + if (!m_notification) { + updateOnly = false; + m_notification = new KNotification(QStringLiteral("CaptivePortal"), KNotification::Persistent, this); + m_notification->setActions(QStringList{i18n("Log in")}); + m_notification->setComponentName(QStringLiteral("networkmanagement")); + m_notification->setText(i18n("You need to log in to this network")); + + connect(m_notification, &KNotification::action1Activated, this, [this] () { + QDesktopServices::openUrl(QUrl("http://networkcheck.kde.org")); + }); + } + if (primaryConnection) { - notification->setTitle(primaryConnection->id()); + m_notification->setTitle(primaryConnection->id()); } else { - notification->setTitle(i18n("Network authentication")); + m_notification->setTitle(i18n("Network authentication")); + } + + if (updateOnly) { + m_notification->update(); + } else { + m_notification->sendEvent(); + } + + } else { + if (m_notification) { + m_notification->close(); } - notification->setText(i18n("You need to log in to this network")); - notification->sendEvent(); - connect(notification, &KNotification::action1Activated, this, [notification] () { - QDesktopServices::openUrl(QUrl("http://networkcheck.kde.org")); - notification->close(); - }); } } diff --git a/kded/service.cpp b/kded/service.cpp --- a/kded/service.cpp +++ b/kded/service.cpp @@ -53,6 +53,9 @@ Q_D(NetworkManagementService); connect(this, &KDEDModule::moduleRegistered, this, &NetworkManagementService::slotRegistered); + + d->agent = new SecretAgent(this); + connect(d->agent, &SecretAgent::secretsError, this, &NetworkManagementService::secretsError); } NetworkManagementService::~NetworkManagementService() @@ -64,11 +67,6 @@ { Q_D(NetworkManagementService); - if (!d->agent) { - d->agent = new SecretAgent(this); - connect(d->agent, &SecretAgent::secretsError, this, &NetworkManagementService::secretsError); - } - if (!d->notification) { d->notification = new Notification(this); } diff --git a/libs/configuration.h b/libs/configuration.h --- a/libs/configuration.h +++ b/libs/configuration.h @@ -30,6 +30,9 @@ Q_PROPERTY(bool unlockModemOnDetection READ unlockModemOnDetection WRITE setUnlockModemOnDetection) Q_PROPERTY(bool manageVirtualConnections READ manageVirtualConnections WRITE setManageVirtualConnections) Q_PROPERTY(bool airplaneModeEnabled READ airplaneModeEnabled WRITE setAirplaneModeEnabled) + Q_PROPERTY(QString hotspotName READ hotspotName WRITE setHotspotName) + Q_PROPERTY(QString hotspotPassword READ hotspotPassword WRITE setHotspotPassword) + Q_PROPERTY(QString hotspotConnectionPath READ hotspotConnectionPath WRITE setHotspotConnectionPath) //Readonly constant property, as this value should only be set by the platform Q_PROPERTY(bool showPasswordDialog READ showPasswordDialog CONSTANT) @@ -44,6 +47,15 @@ static bool airplaneModeEnabled(); static void setAirplaneModeEnabled(bool enabled); + static QString hotspotName(); + static void setHotspotName(const QString &name); + + static QString hotspotPassword(); + static void setHotspotPassword(const QString &password); + + static QString hotspotConnectionPath(); + static void setHotspotConnectionPath(const QString &path); + static bool showPasswordDialog(); }; diff --git a/libs/configuration.cpp b/libs/configuration.cpp --- a/libs/configuration.cpp +++ b/libs/configuration.cpp @@ -22,12 +22,15 @@ #include #include +#include -Q_GLOBAL_STATIC_WITH_ARGS(KSharedConfigPtr, config, (KSharedConfig::openConfig(QLatin1String("plasma-nm")))) +static bool propManageVirtualConnectionsInitialized = false; +static bool propManageVirtualConnections = false; bool Configuration::unlockModemOnDetection() { - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { return grp.readEntry(QLatin1String("UnlockModemOnDetection"), true); @@ -38,30 +41,42 @@ void Configuration::setUnlockModemOnDetection(bool unlock) { - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { grp.writeEntry(QLatin1String("UnlockModemOnDetection"), unlock); } } bool Configuration::manageVirtualConnections() { - KConfigGroup grp(*config, QLatin1String("General")); + // Avoid reading from the config file over and over + if (propManageVirtualConnectionsInitialized) { + return propManageVirtualConnections; + } + + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { - return grp.readEntry(QLatin1String("ManageVirtualConnections"), false); + propManageVirtualConnections = grp.readEntry(QLatin1String("ManageVirtualConnections"), false); + propManageVirtualConnectionsInitialized = true; + + return propManageVirtualConnections; } return true; } void Configuration::setManageVirtualConnections(bool manage) { - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { grp.writeEntry(QLatin1String("ManageVirtualConnections"), manage); + propManageVirtualConnections = manage; } } @@ -72,7 +87,8 @@ const bool isWifiDisabled = !NetworkManager::isWirelessEnabled() || !NetworkManager::isWirelessHardwareEnabled(); const bool isWwanDisabled = !NetworkManager::isWwanEnabled() || !NetworkManager::isWwanHardwareEnabled(); - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { if (grp.readEntry(QLatin1String("AirplaneModeEnabled"), false)) { @@ -90,16 +106,87 @@ void Configuration::setAirplaneModeEnabled(bool enabled) { - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { grp.writeEntry(QLatin1String("AirplaneModeEnabled"), enabled); } } +QString Configuration::hotspotName() +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + KUser currentUser; + + const QString defaultName = currentUser.loginName() + QLatin1String("-hotspot"); + + if (grp.isValid()) { + return grp.readEntry(QLatin1String("HotspotName"), defaultName); + } + + return defaultName; +} + +void Configuration::setHotspotName(const QString &name) +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + + if (grp.isValid()) { + grp.writeEntry(QLatin1String("HotspotName"), name); + } +} + +QString Configuration::hotspotPassword() +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + + if (grp.isValid()) { + return grp.readEntry(QLatin1String("HotspotPassword"), QString()); + } + + return QString(); +} + +void Configuration::setHotspotPassword(const QString &password) +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + + if (grp.isValid()) { + grp.writeEntry(QLatin1String("HotspotPassword"), password); + } +} + +QString Configuration::hotspotConnectionPath() +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + + if (grp.isValid()) { + return grp.readEntry(QLatin1String("HotspotConnectionPath"), QString()); + } + + return QString(); +} + +void Configuration::setHotspotConnectionPath(const QString &path) +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + + if (grp.isValid()) { + grp.writeEntry(QLatin1String("HotspotConnectionPath"), path); + } +} + bool Configuration::showPasswordDialog() { - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { return grp.readEntry(QLatin1String("ShowPasswordDialog"), true); diff --git a/libs/declarative/enabledconnections.h b/libs/declarative/enabledconnections.h --- a/libs/declarative/enabledconnections.h +++ b/libs/declarative/enabledconnections.h @@ -47,6 +47,7 @@ * Indicates if the mobile broadband hardware is currently enabled, i.e. the state of the RF kill switch. */ Q_PROPERTY(bool wwanHwEnabled READ isWwanHwEnabled NOTIFY wwanHwEnabled) + Q_OBJECT public: explicit EnabledConnections(QObject* parent = nullptr); diff --git a/libs/declarative/enums.h b/libs/declarative/enums.h --- a/libs/declarative/enums.h +++ b/libs/declarative/enums.h @@ -69,7 +69,8 @@ WpaPsk, WpaEap, Wpa2Psk, - Wpa2Eap + Wpa2Eap, + SAE }; }; diff --git a/libs/editor/connectioneditorbase.cpp b/libs/editor/connectioneditorbase.cpp --- a/libs/editor/connectioneditorbase.cpp +++ b/libs/editor/connectioneditorbase.cpp @@ -53,9 +53,9 @@ #include #include #include +#include #include -#include #include #include #include @@ -145,6 +145,23 @@ if (securitySetting->keyMgmt() != NetworkManager::WirelessSecuritySetting::WirelessSecuritySetting::Unknown) { wirelessSetting->setSecurity("802-11-wireless-security"); } + + if (securitySetting->keyMgmt() == NetworkManager::WirelessSecuritySetting::SAE && + wirelessSetting->mode() == NetworkManager::WirelessSetting::Adhoc) { + // Ad-Hoc settings as specified by the supplicant + // Proto + QList protoVersions = securitySetting->proto(); + protoVersions << NetworkManager::WirelessSecuritySetting::Rsn; + securitySetting->setProto(protoVersions); + // Pairwise + QList pairwiseEncrypts = securitySetting->pairwise(); + pairwiseEncrypts << NetworkManager::WirelessSecuritySetting::Ccmp; + securitySetting->setPairwise(pairwiseEncrypts); + // Group + QList groupEncrypts = securitySetting->group(); + groupEncrypts << NetworkManager::WirelessSecuritySetting::Ccmp; + securitySetting->setGroup(groupEncrypts); + } } } return connectionSettings->toMap(); @@ -465,7 +482,7 @@ notification->setComponentName("networkmanagement"); notification->setTitle(i18n("Failed to get secrets for %1", watcher->property("connection").toString())); notification->setText(reply.error().message()); - notification->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(KIconLoader::SizeHuge)); + notification->setIconName(QStringLiteral("dialog-warning")); notification->sendEvent(); } diff --git a/libs/editor/settings/bondwidget.cpp b/libs/editor/settings/bondwidget.cpp --- a/libs/editor/settings/bondwidget.cpp +++ b/libs/editor/settings/bondwidget.cpp @@ -77,7 +77,11 @@ connect(m_ui->ifaceName, &KLineEdit::textChanged, this, &BondWidget::slotWidgetChanged); connect(m_ui->arpTargets, &KLineEdit::textChanged, this, &BondWidget::slotWidgetChanged); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->linkMonitoring, QOverload::of(&KComboBox::currentIndexChanged), this, &BondWidget::slotWidgetChanged); +#else + connect(m_ui->linkMonitoring, QOverload::of(&KComboBox::currentIndexChanged), this, &BondWidget::slotWidgetChanged); +#endif // Connect for setting check watchChangedSetting(); diff --git a/libs/editor/settings/connectionwidget.cpp b/libs/editor/settings/connectionwidget.cpp --- a/libs/editor/settings/connectionwidget.cpp +++ b/libs/editor/settings/connectionwidget.cpp @@ -70,12 +70,24 @@ connect(m_widget->allUsers, &QCheckBox::stateChanged, this, &ConnectionWidget::settingChanged); connect(m_widget->autoconnectVpn, &QCheckBox::stateChanged, this, &ConnectionWidget::settingChanged); connect(m_widget->pushButtonPermissions, &QPushButton::clicked, this, &ConnectionWidget::settingChanged); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_widget->firewallZone, QOverload::of(&QComboBox::currentIndexChanged), this, &ConnectionWidget::settingChanged); +#else + connect(m_widget->firewallZone, QOverload::of(&QComboBox::currentIndexChanged), this, &ConnectionWidget::settingChanged); +#endif connect(m_widget->firewallZone, &QComboBox::currentTextChanged, this, &ConnectionWidget::settingChanged); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_widget->vpnCombobox, QOverload::of(&QComboBox::currentIndexChanged), this, &ConnectionWidget::settingChanged); +#else + connect(m_widget->vpnCombobox, QOverload::of(&QComboBox::currentIndexChanged), this, &ConnectionWidget::settingChanged); +#endif connect(m_widget->vpnCombobox, &QComboBox::currentTextChanged, this, &ConnectionWidget::settingChanged); connect(m_widget->prioritySpin, QOverload::of(&QSpinBox::valueChanged), this, &ConnectionWidget::settingChanged); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_widget->metered, QOverload::of(&QComboBox::currentIndexChanged), this, &ConnectionWidget::settingChanged); +#else + connect(m_widget->metered, QOverload::of(&QComboBox::currentIndexChanged), this, &ConnectionWidget::settingChanged); +#endif connect(m_widget->pushButtonPermissions, &QPushButton::clicked, this, &ConnectionWidget::openAdvancedPermissions); } diff --git a/libs/editor/settings/ipv4widget.cpp b/libs/editor/settings/ipv4widget.cpp --- a/libs/editor/settings/ipv4widget.cpp +++ b/libs/editor/settings/ipv4widget.cpp @@ -107,7 +107,11 @@ loadConfig(setting); } +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->method, QOverload::of(&KComboBox::currentIndexChanged), this, &IPv4Widget::slotModeComboChanged); +#else + connect(m_ui->method, QOverload::of(&KComboBox::currentIndexChanged), this, &IPv4Widget::slotModeComboChanged); +#endif slotModeComboChanged(m_ui->method->currentIndex()); connect(m_ui->btnRoutes, &QPushButton::clicked, this, &IPv4Widget::slotRoutesDialog); @@ -118,7 +122,11 @@ // Connect for validity check connect(m_ui->dns, &KLineEdit::textChanged, this, &IPv4Widget::slotWidgetChanged); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->method, QOverload::of(&KComboBox::currentIndexChanged), this, &IPv4Widget::slotWidgetChanged); +#else + connect(m_ui->method, QOverload::of(&KComboBox::currentIndexChanged), this, &IPv4Widget::slotWidgetChanged); +#endif connect(&d->model, &QStandardItemModel::dataChanged, this, &IPv4Widget::slotWidgetChanged); connect(&d->model, &QStandardItemModel::rowsRemoved, this, &IPv4Widget::slotWidgetChanged); @@ -438,18 +446,18 @@ layout->addRow(new QLabel(i18n("You can find more information about these values here:
https://developer.gnome.org/NetworkManager/stable/nm-settings.html
"))); - auto sendHostname = new QCheckBox; + auto sendHostname = new QCheckBox(dlg); sendHostname->setChecked(m_tmpIpv4Setting.dhcpSendHostname()); layout->addRow(i18n("Send hostname:"), sendHostname); - auto dhcpHostname = new QLineEdit; + auto dhcpHostname = new QLineEdit(dlg); dhcpHostname->setText(m_tmpIpv4Setting.dhcpHostname()); dhcpHostname->setPlaceholderText(QHostInfo::localHostName()); layout->addRow(i18n("DHCP hostname:"), dhcpHostname); connect(sendHostname, &QCheckBox::toggled, dhcpHostname, &QLineEdit::setEnabled); - auto dadTimeout = new QSpinBox; + auto dadTimeout = new QSpinBox(dlg); dadTimeout->setSpecialValueText(i18n("Default")); dadTimeout->setSuffix(i18nc("Milliseconds", " ms")); dadTimeout->setMinimum(-1); @@ -462,7 +470,7 @@ layout->addWidget(box); connect(dlg, &QDialog::accepted, this, - [&] () { + [=] () { m_tmpIpv4Setting.setDhcpSendHostname(sendHostname->isChecked()); m_tmpIpv4Setting.setDhcpHostname(dhcpHostname->text()); m_tmpIpv4Setting.setDadTimeout(dadTimeout->value()); diff --git a/libs/editor/settings/ipv6widget.cpp b/libs/editor/settings/ipv6widget.cpp --- a/libs/editor/settings/ipv6widget.cpp +++ b/libs/editor/settings/ipv6widget.cpp @@ -93,7 +93,11 @@ loadConfig(setting); } +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->method, QOverload::of(&KComboBox::currentIndexChanged), this, &IPv6Widget::slotModeComboChanged); +#else + connect(m_ui->method, QOverload::of(&KComboBox::currentIndexChanged), this, &IPv6Widget::slotModeComboChanged); +#endif slotModeComboChanged(m_ui->method->currentIndex()); connect(m_ui->btnRoutes, &QPushButton::clicked, this, &IPv6Widget::slotRoutesDialog); @@ -103,7 +107,11 @@ // Connect for validity check connect(m_ui->dns, &KLineEdit::textChanged, this, &IPv6Widget::slotWidgetChanged); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->method, QOverload::of(&KComboBox::currentIndexChanged), this, &IPv6Widget::slotWidgetChanged); +#else + connect(m_ui->method, QOverload::of(&KComboBox::currentIndexChanged), this, &IPv6Widget::slotWidgetChanged); +#endif connect(&d->model, &QStandardItemModel::dataChanged, this, &IPv6Widget::slotWidgetChanged); connect(&d->model, &QStandardItemModel::rowsRemoved, this, &IPv6Widget::slotWidgetChanged); diff --git a/libs/editor/settings/security802-1x.cpp b/libs/editor/settings/security802-1x.cpp --- a/libs/editor/settings/security802-1x.cpp +++ b/libs/editor/settings/security802-1x.cpp @@ -76,7 +76,11 @@ watchChangedSetting(); // Connect for validity check +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->auth, QOverload::of(&KComboBox::currentIndexChanged), this, &Security8021x::slotWidgetChanged); +#else + connect(m_ui->auth, QOverload::of(&KComboBox::currentIndexChanged), this, &Security8021x::slotWidgetChanged); +#endif connect(m_ui->md5UserName, &KLineEdit::textChanged, this, &Security8021x::slotWidgetChanged); connect(m_ui->md5Password, &PasswordField::textChanged, this, &Security8021x::slotWidgetChanged); connect(m_ui->md5Password, &PasswordField::passwordOptionChanged, this, &Security8021x::slotWidgetChanged); @@ -525,7 +529,7 @@ editor->setItems(m_ui->leTlsSubjectMatch->text().remove(QLatin1Char(' ')).split(QLatin1Char(','), QString::SkipEmptyParts)); editor->setWindowTitle(i18n("Alternative Subject Matches")); - editor->setToolTip(i18n("This entry must be one of:
  • DNS: <name or ip address>
  • EMAIL: <email>
  • URI: <uri, e.g. http://www.kde.org>
")); + editor->setToolTip(i18n("This entry must be one of:
  • DNS: <name or ip address>
  • EMAIL: <email>
  • URI: <uri, e.g. https://www.kde.org>
")); editor->setValidator(altSubjectValidator); connect(editor.data(), &QDialog::accepted, diff --git a/libs/editor/settings/ui/wifisecurity.ui b/libs/editor/settings/ui/wifisecurity.ui --- a/libs/editor/settings/ui/wifisecurity.ui +++ b/libs/editor/settings/ui/wifisecurity.ui @@ -58,6 +58,11 @@ WPA/WPA2 Enterprise + + + WPA3 Personal + + @@ -96,7 +101,7 @@ 26 - + true @@ -203,7 +208,7 @@ - + true @@ -227,7 +232,7 @@ - + true diff --git a/libs/editor/settings/ui/wiredconnectionwidget.ui b/libs/editor/settings/ui/wiredconnectionwidget.ui --- a/libs/editor/settings/ui/wiredconnectionwidget.ui +++ b/libs/editor/settings/ui/wiredconnectionwidget.ui @@ -6,8 +6,8 @@ 0 0 - 401 - 217 + 437 + 232 @@ -27,16 +27,6 @@ - - - - Cloned MAC address: - - - clonedMacAddress - - - @@ -51,6 +41,16 @@ + + + + Cloned MAC address: + + + clonedMacAddress + + + @@ -124,95 +124,123 @@ Speed: + + + + + + Duplex: + - speed + duplex - - + + + + false + 0 0 - Request that the device use only the specified speed. In MBit/s, example 100 == 100Mbit/s - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - Mbit/s + Request that the device use the specified duplex mode. Either "half" or "full" - + 1 - - 9999 - - - 100 - + + + Half + + + + + Full + + - - - - Duplex: + + + + - - duplex + + Link negotiation: - - + + 0 0 - Request that the device use the specified duplex mode. Either "half" or "full" + Device link negotiation. If “Manual” is chosen, “Speed” and “Duplex” values will be forced without checking +the device compatibility. If unsure, leave here “Ignore” or pick “Automatic”. - Full + Ignore - Half + Automatic + + + + + Manual - - + + + + false + 0 0 - Allow auto-negotiation of port speed and duplex mode - - - Allow auto-negotiation - - - true + Request that the device use only the specified speed. - - false + + 1 + + + 10 Mb/s + + + + + 100 Mb/s + + + + + 1 Gb/s + + + + + 10 Gb/s + + @@ -239,74 +267,8 @@ clonedMacAddress btnRandomMacAddr mtu - speed duplex - - - autonegotiate - toggled(bool) - speedLabel - setHidden(bool) - - - 80 - 290 - - - 81 - 117 - - - - - autonegotiate - toggled(bool) - speed - setHidden(bool) - - - 80 - 290 - - - 245 - 118 - - - - - autonegotiate - toggled(bool) - duplexLabel - setHidden(bool) - - - 80 - 290 - - - 81 - 147 - - - - - autonegotiate - toggled(bool) - duplex - setHidden(bool) - - - 80 - 290 - - - 298 - 143 - - - - + diff --git a/libs/editor/settings/vlanwidget.cpp b/libs/editor/settings/vlanwidget.cpp --- a/libs/editor/settings/vlanwidget.cpp +++ b/libs/editor/settings/vlanwidget.cpp @@ -34,7 +34,11 @@ fillConnections(); connect(m_ui->ifaceName, &KLineEdit::textChanged, this, &VlanWidget::slotWidgetChanged); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->parent, QOverload::of(&KComboBox::currentIndexChanged), this, &VlanWidget::slotWidgetChanged); +#else + connect(m_ui->parent, QOverload::of(&KComboBox::currentIndexChanged), this, &VlanWidget::slotWidgetChanged); +#endif connect(m_ui->parent->lineEdit(), &QLineEdit::textChanged, this, &VlanWidget::slotWidgetChanged); // Connect for setting check diff --git a/libs/editor/settings/wificonnectionwidget.cpp b/libs/editor/settings/wificonnectionwidget.cpp --- a/libs/editor/settings/wificonnectionwidget.cpp +++ b/libs/editor/settings/wificonnectionwidget.cpp @@ -24,21 +24,28 @@ #include #include +#include #include "uiutils.h" WifiConnectionWidget::WifiConnectionWidget(const NetworkManager::Setting::Ptr &setting, QWidget* parent, Qt::WindowFlags f): SettingWidget(setting, parent, f), m_ui(new Ui::WifiConnectionWidget) { - qsrand(QTime::currentTime().msec()); - m_ui->setupUi(this); connect(m_ui->btnRandomMacAddr, &QPushButton::clicked, this, &WifiConnectionWidget::generateRandomClonedMac); connect(m_ui->SSIDCombo, &SsidComboBox::ssidChanged, this, QOverload<>::of(&WifiConnectionWidget::ssidChanged)); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->modeComboBox, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiConnectionWidget::modeChanged); +#else + connect(m_ui->modeComboBox, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiConnectionWidget::modeChanged); +#endif +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->band, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiConnectionWidget::bandChanged); +#else + connect(m_ui->band, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiConnectionWidget::bandChanged); +#endif // Connect for setting check watchChangedSetting(); @@ -125,9 +132,10 @@ void WifiConnectionWidget::generateRandomClonedMac() { QByteArray mac; + auto *generator = QRandomGenerator::global(); mac.resize(6); for (int i = 0; i < 6; i++) { - int random = qrand() % 255; + const int random = generator->bounded(255); mac[i] = random; } diff --git a/libs/editor/settings/wifisecurity.h b/libs/editor/settings/wifisecurity.h --- a/libs/editor/settings/wifisecurity.h +++ b/libs/editor/settings/wifisecurity.h @@ -40,7 +40,7 @@ public: // Keep this in sync with NetworkManager::WirelessSecurityType from // NetworkManagerQt. - enum SecurityTypeIndex { None = 0, WepHex, WepPassphrase, Leap, DynamicWep, WpaPsk, WpaEap }; + enum SecurityTypeIndex { None = 0, WepHex, WepPassphrase, Leap, DynamicWep, WpaPsk, WpaEap, SAE }; explicit WifiSecurity(const NetworkManager::Setting::Ptr &setting = NetworkManager::Setting::Ptr(), const NetworkManager::Security8021xSetting::Ptr &setting8021x = NetworkManager::Security8021xSetting::Ptr(), diff --git a/libs/editor/settings/wifisecurity.cpp b/libs/editor/settings/wifisecurity.cpp --- a/libs/editor/settings/wifisecurity.cpp +++ b/libs/editor/settings/wifisecurity.cpp @@ -44,8 +44,21 @@ m_ui->stackedWidget->insertWidget(3, m_8021xWidget); m_ui->stackedWidget->insertWidget(5, m_WPA2Widget); + // WPA3 Personal is available in NM 1.16+ + if (!NetworkManager::checkVersion(1, 16, 0)) { + m_ui->securityCombo->removeItem(7); + } + +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->securityCombo, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiSecurity::securityChanged); +#else + connect(m_ui->securityCombo, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiSecurity::securityChanged); +#endif +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->wepIndex, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiSecurity::setWepKey); +#else + connect(m_ui->wepIndex, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiSecurity::setWepKey); +#endif // Connect for setting check watchChangedSetting(); @@ -58,8 +71,16 @@ connect(m_ui->leapPassword, &PasswordField::passwordOptionChanged, this, &WifiSecurity::slotWidgetChanged); connect(m_ui->psk, &PasswordField::textChanged, this, &WifiSecurity::slotWidgetChanged); connect(m_ui->psk, &PasswordField::passwordOptionChanged, this, &WifiSecurity::slotWidgetChanged); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->wepIndex, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiSecurity::slotWidgetChanged); +#else + connect(m_ui->wepIndex, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiSecurity::slotWidgetChanged); +#endif +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->securityCombo, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiSecurity::slotWidgetChanged); +#else + connect(m_ui->securityCombo, QOverload::of(&KComboBox::currentIndexChanged), this, &WifiSecurity::slotWidgetChanged); +#endif connect(m_8021xWidget, &Security8021x::validChanged, this, &WifiSecurity::slotWidgetChanged); connect(m_WPA2Widget, &Security8021x::validChanged, this, &WifiSecurity::slotWidgetChanged); @@ -104,11 +125,13 @@ (!m_ui->leapPassword->text().isEmpty() || m_ui->leapPassword->passwordOption() == PasswordField::AlwaysAsk); } else if (securityIndex == WpaPsk) { // WPA return NetworkManager::wpaPskIsValid(m_ui->psk->text()) || - m_ui->psk->passwordOption() == PasswordField::AlwaysAsk;; + m_ui->psk->passwordOption() == PasswordField::AlwaysAsk; } else if (securityIndex == DynamicWep) { return m_8021xWidget->isValid(); } else if (securityIndex == WpaEap) { return m_WPA2Widget->isValid(); + } else if (securityIndex == SAE) { + return !m_ui->psk->text().isEmpty() || m_ui->psk->passwordOption() == PasswordField::AlwaysAsk; } return true; @@ -176,6 +199,16 @@ } else if (keyMgmt == NetworkManager::WirelessSecuritySetting::WpaEap) { m_ui->securityCombo->setCurrentIndex(WpaEap); // WPA2 Enterprise // done in the widget + } else if (keyMgmt == NetworkManager::WirelessSecuritySetting::SAE) { + m_ui->securityCombo->setCurrentIndex(SAE); // WPA3 + + if (wifiSecurity->pskFlags().testFlag(NetworkManager::Setting::None)) { + m_ui->psk->setPasswordOption(PasswordField::StoreForAllUsers); + } else if (wifiSecurity->pskFlags().testFlag(NetworkManager::Setting::AgentOwned)) { + m_ui->psk->setPasswordOption(PasswordField::StoreForUser); + } else { + m_ui->psk->setPasswordOption(PasswordField::AlwaysAsk); + } } if (keyMgmt != NetworkManager::WirelessSecuritySetting::Ieee8021x && @@ -212,7 +245,7 @@ if (!leapPassword.isEmpty()) { m_ui->leapPassword->setText(leapPassword); } - } else if (keyMgmt == NetworkManager::WirelessSecuritySetting::WpaPsk) { + } else if (keyMgmt == NetworkManager::WirelessSecuritySetting::WpaPsk || keyMgmt == NetworkManager::WirelessSecuritySetting::SAE) { const QString psk = wifiSecurity->psk(); if (!psk.isEmpty()) { m_ui->psk->setText(psk); @@ -290,6 +323,17 @@ } } else if (securityIndex == WpaEap) { // WPA2 Enterprise wifiSecurity.setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaEap); + } else if (securityIndex == SAE) { // WPA3 Personal + wifiSecurity.setKeyMgmt(NetworkManager::WirelessSecuritySetting::SAE); + wifiSecurity.setPsk(m_ui->psk->text()); + + if (m_ui->psk->passwordOption() == PasswordField::StoreForAllUsers) { + wifiSecurity.setPskFlags(NetworkManager::Setting::None); + } else if (m_ui->psk->passwordOption() == PasswordField::StoreForUser) { + wifiSecurity.setPskFlags(NetworkManager::Setting::AgentOwned); + } else { + wifiSecurity.setPskFlags(NetworkManager::Setting::NotSaved); + } } return wifiSecurity.toMap(); @@ -339,6 +383,9 @@ case NetworkManager::WirelessSecurityType::Wpa2Eap: m_ui->securityCombo->setCurrentIndex(WpaEap); break; + case NetworkManager::WirelessSecurityType::SAE: + m_ui->securityCombo->setCurrentIndex(SAE); + break; default: m_ui->securityCombo->setCurrentIndex(None); } @@ -369,12 +416,18 @@ void WifiSecurity::securityChanged(int index) { - if (index == 0) { + if (index == None) { m_ui->stackedWidget->setCurrentIndex(0); - } else if (index == 1 || index == 2) { + } else if (index == WepHex || index == WepPassphrase) { m_ui->stackedWidget->setCurrentIndex(1); - } else { - m_ui->stackedWidget->setCurrentIndex(index-1); + } else if (index == Leap) { + m_ui->stackedWidget->setCurrentIndex(2); + } else if (index == DynamicWep) { + m_ui->stackedWidget->setCurrentIndex(3); + } else if (index == WpaPsk || index == SAE) { + m_ui->stackedWidget->setCurrentIndex(4); + } else if (index == WpaEap) { + m_ui->stackedWidget->setCurrentIndex(5); } KAcceleratorManager::manage(m_ui->stackedWidget->currentWidget()); diff --git a/libs/editor/settings/wiredconnectionwidget.h b/libs/editor/settings/wiredconnectionwidget.h --- a/libs/editor/settings/wiredconnectionwidget.h +++ b/libs/editor/settings/wiredconnectionwidget.h @@ -35,6 +35,17 @@ Q_OBJECT public: + enum LinkNegotiation { + Ignore = 0, + Automatic, + Manual + }; + + enum Duplex { + Half = 0, + Full + }; + explicit WiredConnectionWidget(const NetworkManager::Setting::Ptr &setting, QWidget* parent = nullptr, Qt::WindowFlags f = {}); ~WiredConnectionWidget() override; diff --git a/libs/editor/settings/wiredconnectionwidget.cpp b/libs/editor/settings/wiredconnectionwidget.cpp --- a/libs/editor/settings/wiredconnectionwidget.cpp +++ b/libs/editor/settings/wiredconnectionwidget.cpp @@ -25,28 +25,30 @@ #include #include +#include + WiredConnectionWidget::WiredConnectionWidget(const NetworkManager::Setting::Ptr &setting, QWidget* parent, Qt::WindowFlags f): SettingWidget(setting, parent, f), m_widget(new Ui::WiredConnectionWidget) { - qsrand(QTime::currentTime().msec()); - m_widget->setupUi(this); - m_widget->speedLabel->setHidden(true); - m_widget->speed->setHidden(true); - m_widget->duplexLabel->setHidden(true); - m_widget->duplex->setHidden(true); connect(m_widget->btnRandomMacAddr, &QPushButton::clicked, this, &WiredConnectionWidget::generateRandomClonedMac); // Connect for setting check watchChangedSetting(); // Connect for validity check - connect(m_widget->autonegotiate, &QCheckBox::stateChanged, this, &WiredConnectionWidget::slotWidgetChanged); connect(m_widget->clonedMacAddress, &KLineEdit::textChanged, this, &WiredConnectionWidget::slotWidgetChanged); connect(m_widget->macAddress, &HwAddrComboBox::hwAddressChanged, this, &WiredConnectionWidget::slotWidgetChanged); - connect(m_widget->speed, QOverload::of(&QSpinBox::valueChanged), this, &WiredConnectionWidget::slotWidgetChanged); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + connect(m_widget->linkNegotiation, QOverload::of(&QComboBox::currentIndexChanged), this, [this] (int index) { +#else + connect(m_widget->linkNegotiation, QOverload::of(&QComboBox::currentIndexChanged), this, [this] (int index) { +#endif + m_widget->duplex->setEnabled(index == LinkNegotiation::Manual); + m_widget->speed->setEnabled(index == LinkNegotiation::Manual); + }); KAcceleratorManager::manage(this); @@ -74,19 +76,33 @@ m_widget->mtu->setValue(wiredSetting->mtu()); } - if (!wiredSetting->autoNegotiate()) { - m_widget->autonegotiate->setChecked(false); + if (wiredSetting->autoNegotiate()) { + m_widget->linkNegotiation->setCurrentIndex(LinkNegotiation::Automatic); + } else if (wiredSetting->speed() && wiredSetting->duplexType() != NetworkManager::WiredSetting::UnknownDuplexType) { + m_widget->linkNegotiation->setCurrentIndex(LinkNegotiation::Manual); + } - if (wiredSetting->speed()) { - m_widget->speed->setValue(wiredSetting->speed()); + if (wiredSetting->speed()) { + switch(wiredSetting->speed()) { + case 10: + m_widget->speed->setCurrentIndex(0); + break; + case 100: + m_widget->speed->setCurrentIndex(1); + break; + case 1000: + m_widget->speed->setCurrentIndex(2); + break; + case 10000: + m_widget->speed->setCurrentIndex(3); + break; } + } - // Default to "Full" duplex when duplex type is not set - if (wiredSetting->duplexType() == NetworkManager::WiredSetting::Full || wiredSetting->duplexType() == NetworkManager::WiredSetting::UnknownDuplexType) { - m_widget->duplex->setCurrentIndex(0); - } else { - m_widget->duplex->setCurrentIndex(1); - } + if (wiredSetting->duplexType() != NetworkManager::WiredSetting::Half) { + m_widget->duplex->setCurrentIndex(Duplex::Full); + } else { + m_widget->duplex->setCurrentIndex(Duplex::Half); } } @@ -104,30 +120,45 @@ wiredSetting.setMtu(m_widget->mtu->value()); } - if (m_widget->autonegotiate->isChecked()) { - wiredSetting.setAutoNegotiate(true); + if (m_widget->linkNegotiation->currentIndex() == LinkNegotiation::Automatic || m_widget->linkNegotiation->currentIndex() == LinkNegotiation::Ignore) { wiredSetting.setDuplexType(NetworkManager::WiredSetting::UnknownDuplexType); wiredSetting.setSpeed(0); } else { - wiredSetting.setAutoNegotiate(false); - wiredSetting.setSpeed(m_widget->speed->value()); + switch (m_widget->speed->currentIndex()) { + case 0: + wiredSetting.setSpeed(10); + break; + case 1: + wiredSetting.setSpeed(100); + break; + case 2: + wiredSetting.setSpeed(1000); + break; + case 3: + wiredSetting.setSpeed(10000); + break; + } - if (m_widget->duplex->currentIndex() == 0) { + if (m_widget->duplex->currentIndex() == Duplex::Full) { wiredSetting.setDuplexType(NetworkManager::WiredSetting::Full); } else { wiredSetting.setDuplexType(NetworkManager::WiredSetting::Half); } } + wiredSetting.setAutoNegotiate(m_widget->linkNegotiation->currentIndex() == LinkNegotiation::Automatic); + + return wiredSetting.toMap(); } void WiredConnectionWidget::generateRandomClonedMac() { + auto *generator = QRandomGenerator::global(); QByteArray mac; mac.resize(6); for (int i = 0; i < 6; i++) { - int random = qrand() % 255; + const int random = generator->bounded(255); mac[i] = random; } @@ -150,11 +181,5 @@ } } - if (!m_widget->autonegotiate->isChecked()) { - if (!m_widget->speed->value()) { - return false; - } - } - return true; } diff --git a/libs/editor/settings/wireguardpeerwidget.cpp b/libs/editor/settings/wireguardpeerwidget.cpp --- a/libs/editor/settings/wireguardpeerwidget.cpp +++ b/libs/editor/settings/wireguardpeerwidget.cpp @@ -186,10 +186,17 @@ int pos = 0; QLineEdit *widget = d->ui.allowedIPsLineEdit; QString ipString = widget->displayText(); - QStringList ipList = ipString.split(','); + QStringList rawIPList = ipString.split(','); + QStringList ipList; bool valid = QValidator::Acceptable == allowedIPsValidator.validate(ipString, pos); setBackground(widget, valid); + + ipList.reserve(rawIPList.size()); + for (const QString &ip : rawIPList) { + ipList.append(ip.trimmed()); + } + d->peerData[PNM_WG_PEER_KEY_ALLOWED_IPS] = ipList; if (valid != d->allowedIPsValid) { d->allowedIPsValid = valid; diff --git a/libs/editor/widgets/passwordfield.cpp b/libs/editor/widgets/passwordfield.cpp --- a/libs/editor/widgets/passwordfield.cpp +++ b/libs/editor/widgets/passwordfield.cpp @@ -65,7 +65,11 @@ m_currentPasswordOption = StoreForAllUsers; } +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_passwordOptionsMenu, QOverload::of(&QComboBox::currentIndexChanged), this, &PasswordField::changePasswordOption); +#else + connect(m_passwordOptionsMenu, QOverload::of(&QComboBox::currentIndexChanged), this, &PasswordField::changePasswordOption); +#endif // Disable by default m_passwordOptionsMenu->setVisible(false); diff --git a/libs/editor/widgets/settingwidget.cpp b/libs/editor/widgets/settingwidget.cpp --- a/libs/editor/widgets/settingwidget.cpp +++ b/libs/editor/widgets/settingwidget.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -69,7 +70,11 @@ // Connect all QComboBox widgets QList comboboxes = findChildren(); for (QComboBox *combobox : comboboxes) { +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(combobox, QOverload::of(&QComboBox::currentIndexChanged), this, &SettingWidget::settingChanged); +#else + connect(combobox, QOverload::of(&QComboBox::currentIndexChanged), this, &SettingWidget::settingChanged); +#endif connect(combobox, &QComboBox::currentTextChanged, this, &SettingWidget::settingChanged); } @@ -104,6 +109,12 @@ connect(tableview, &QTableView::clicked, this, &SettingWidget::settingChanged); } + // Connect all QGroupBox widgets + QList groupBoxes = findChildren(); + for (QGroupBox *box : groupBoxes) { + connect(box, &QGroupBox::toggled, this, &SettingWidget::settingChanged); + } + /********** OUR CUSTOM WIDGETS **********/ // Connect all PasswordField widgets QList passwordfields = findChildren(); diff --git a/libs/handler.h b/libs/handler.h --- a/libs/handler.h +++ b/libs/handler.h @@ -45,12 +45,17 @@ DeactivateConnection, RemoveConnection, RequestScan, - UpdateConnection + UpdateConnection, + CreateHotspot, }; explicit Handler(QObject* parent = nullptr); ~Handler() override; + Q_PROPERTY(bool hotspotSupported READ hotspotSupported NOTIFY hotspotSupportedChanged); +public: + bool hotspotSupported() const { return m_hotspotSupported; }; + public Q_SLOTS: /** * Activates given connection @@ -116,17 +121,26 @@ void updateConnection(const NetworkManager::Connection::Ptr &connection, const NMVariantMapMap &map); void requestScan(const QString &interface = QString()); + void createHotspot(); + void stopHotspot(); + private Q_SLOTS: void initKdedModule(); void secretAgentError(const QString &connectionPath, const QString &message); void replyFinished(QDBusPendingCallWatcher *watcher); + void hotspotCreated(QDBusPendingCallWatcher *watcher); + void primaryConnectionTypeChanged(NetworkManager::ConnectionSettings::ConnectionType type); #if WITH_MODEMMANAGER_SUPPORT void unlockRequiredChanged(MMModemLock modemLock); #endif Q_SIGNALS: void connectionActivationFailed(const QString &connectionPath, const QString &message); + void hotspotCreated(); + void hotspotDisabled(); + void hotspotSupportedChanged(bool hotspotSupported); private: + bool m_hotspotSupported; bool m_tmpWirelessEnabled; bool m_tmpWwanEnabled; #if WITH_MODEMMANAGER_SUPPORT @@ -141,6 +155,7 @@ void enableBluetooth(bool enable); void scanRequestFailed(const QString &interface); bool checkRequestScanRateLimit(const NetworkManager::WirelessDevice::Ptr &wifiDevice); + bool checkHotspotSupported(); void scheduleRequestScan(const QString &interface, int timeout); }; diff --git a/libs/handler.cpp b/libs/handler.cpp --- a/libs/handler.cpp +++ b/libs/handler.cpp @@ -20,6 +20,7 @@ #include "handler.h" #include "connectioneditordialog.h" +#include "configuration.h" #include "uiutils.h" #include "debug.h" @@ -57,7 +58,6 @@ #include #include #include -#include #include #define AGENT_SERVICE "org.kde.kded5" @@ -84,6 +84,20 @@ QStringLiteral(AGENT_IFACE), QStringLiteral("secretsError"), this, SLOT(secretAgentError(QString, QString))); + + + if (!Configuration::hotspotConnectionPath().isEmpty()) { + NetworkManager::ActiveConnection::Ptr hotspot = NetworkManager::findActiveConnection(Configuration::hotspotConnectionPath()); + if (!hotspot) { + Configuration::setHotspotConnectionPath(QString()); + } + } + + m_hotspotSupported = checkHotspotSupported(); + + if (NetworkManager::checkVersion(1, 16, 0)) { + connect(NetworkManager::notifier(), &NetworkManager::Notifier::primaryConnectionTypeChanged, this, &Handler::primaryConnectionTypeChanged); + } } Handler::~Handler() @@ -126,7 +140,7 @@ notification->setComponentName("networkmanagement"); notification->setTitle(con->name()); notification->setText(i18n("Missing VPN plugin")); - notification->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(KIconLoader::SizeHuge)); + notification->setIconName(QStringLiteral("dialog-warning")); notification->sendEvent(); return; } @@ -180,6 +194,9 @@ case NetworkManager::Wpa2Psk: ret += "T:WPA;"; break; + case NetworkManager::SAE: + ret += "T:SAE;"; + break; default: case NetworkManager::DynamicWep: case NetworkManager::WpaEap: @@ -203,6 +220,7 @@ break; case NetworkManager::WpaPsk: case NetworkManager::Wpa2Psk: + case NetworkManager::SAE: pass = secret["psk"].toString(); break; default: @@ -529,6 +547,110 @@ } } +void Handler::createHotspot() +{ + bool foundInactive = false; + bool useApMode = false; + NetworkManager::WirelessDevice::Ptr wifiDev; + + NetworkManager::ConnectionSettings::Ptr connectionSettings; + connectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Wireless)); + + NetworkManager::WirelessSetting::Ptr wifiSetting = connectionSettings->setting(NetworkManager::Setting::Wireless).dynamicCast(); + wifiSetting->setMode(NetworkManager::WirelessSetting::Adhoc); + wifiSetting->setSsid(Configuration::hotspotName().toUtf8()); + + for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) { + if (device->type() == NetworkManager::Device::Wifi) { + wifiDev = device.objectCast(); + if (wifiDev) { + if (!wifiDev->isActive()) { + foundInactive = true; + } else { + // Prefer previous device if it was inactive + if (foundInactive) { + break; + } + } + + if (wifiDev->wirelessCapabilities().testFlag(NetworkManager::WirelessDevice::ApCap)) { + useApMode = true; + } + + // We prefer inactive wireless card with AP capabilities + if (foundInactive && useApMode) { + break; + } + } + } + } + + if (!wifiDev) { + qCWarning(PLASMA_NM) << "Failed to create hotspot: missing wireless device"; + return; + } + + wifiSetting->setInitialized(true); + wifiSetting->setMode(useApMode ? NetworkManager::WirelessSetting::Ap :NetworkManager::WirelessSetting::Adhoc); + + if (!Configuration::hotspotPassword().isEmpty()) { + NetworkManager::WirelessSecuritySetting::Ptr wifiSecurity = connectionSettings->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + wifiSecurity->setInitialized(true); + + if (useApMode) { + // Use WPA2 + wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaPsk); + wifiSecurity->setPsk(Configuration::hotspotPassword()); + wifiSecurity->setPskFlags(NetworkManager::Setting::AgentOwned); + } else { + // Use WEP + wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::Wep); + wifiSecurity->setWepKeyType(NetworkManager::WirelessSecuritySetting::Passphrase); + wifiSecurity->setWepTxKeyindex(0); + wifiSecurity->setWepKey0(Configuration::hotspotPassword()); + wifiSecurity->setWepKeyFlags(NetworkManager::Setting::AgentOwned); + wifiSecurity->setAuthAlg(NetworkManager::WirelessSecuritySetting::Open); + } + } + + NetworkManager::Ipv4Setting::Ptr ipv4Setting = connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast(); + ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Shared); + ipv4Setting->setInitialized(true); + + connectionSettings->setId(Configuration::hotspotName()); + connectionSettings->setAutoconnect(false); + connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); + + const QVariantMap options = { {QLatin1String("persist"), QLatin1String("volatile")} }; + + QDBusPendingReply reply = NetworkManager::addAndActivateConnection2(connectionSettings->toMap(), wifiDev->uni(), QString(), options); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + watcher->setProperty("action", Handler::CreateHotspot); + watcher->setProperty("connection", Configuration::hotspotName()); + connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished); + connect(watcher, &QDBusPendingCallWatcher::finished, this, QOverload::of(&Handler::hotspotCreated)); +} + +void Handler::stopHotspot() +{ + const QString activeConnectionPath = Configuration::hotspotConnectionPath(); + + if (activeConnectionPath.isEmpty()) { + return; + } + + NetworkManager::ActiveConnection::Ptr hotspot = NetworkManager::findActiveConnection(activeConnectionPath); + + if (!hotspot) { + return; + } + + NetworkManager::deactivateConnection(activeConnectionPath); + Configuration::setHotspotConnectionPath(QString()); + + Q_EMIT hotspotDisabled(); +} + bool Handler::checkRequestScanRateLimit(const NetworkManager::WirelessDevice::Ptr &wifiDevice) { QDateTime now = QDateTime::currentDateTime(); @@ -548,6 +670,41 @@ return true; } +bool Handler::checkHotspotSupported() +{ + if (NetworkManager::checkVersion(1, 16, 0)) { + bool unusedWifiFound = false; + bool wifiFound = false; + + for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) { + if (device->type() == NetworkManager::Device::Wifi) { + wifiFound = true; + + NetworkManager::WirelessDevice::Ptr wifiDev = device.objectCast(); + if (wifiDev && !wifiDev->isActive()) { + unusedWifiFound = true; + } + } + } + + + if (!wifiFound) { + return false; + } + + if (unusedWifiFound) { + return true; + } + + // Check if the primary connection which is used for internet connectivity is not using WiFi + if (NetworkManager::primaryConnectionType() != NetworkManager::ConnectionSettings::Wireless) { + return true; + } + } + + return false; +} + void Handler::scheduleRequestScan(const QString &interface, int timeout) { QTimer *timer; @@ -634,14 +791,18 @@ scanRequestFailed(interface); break; } + case Handler::CreateHotspot: + notification = new KNotification("FailedToCreateHotspot", KNotification::CloseOnTimeout, this); + notification->setTitle(i18n("Failed to create hotspot %1", watcher->property("connection").toString())); + break; default: break; } if (notification) { notification->setComponentName("networkmanagement"); notification->setText(error); - notification->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(KIconLoader::SizeHuge)); + notification->setIconName(QStringLiteral("dialog-warning")); notification->sendEvent(); } } else { @@ -671,14 +832,51 @@ if (notification) { notification->setComponentName("networkmanagement"); notification->setTitle(watcher->property("connection").toString()); - notification->setPixmap(QIcon::fromTheme("dialog-information").pixmap(KIconLoader::SizeHuge)); + notification->setIconName(QStringLiteral("dialog-information")); notification->sendEvent(); } } watcher->deleteLater(); } +void Handler::hotspotCreated(QDBusPendingCallWatcher *watcher) +{ + QDBusPendingReply reply = *watcher; + + if (!reply.isError() && reply.isValid()) { + const QString activeConnectionPath = reply.argumentAt(1).value().path(); + + if (activeConnectionPath.isEmpty()) { + return; + } + + Configuration::setHotspotConnectionPath(activeConnectionPath); + + NetworkManager::ActiveConnection::Ptr hotspot = NetworkManager::findActiveConnection(activeConnectionPath); + + if (!hotspot) { + return; + } + + connect(hotspot.data(), &NetworkManager::ActiveConnection::stateChanged, [=] (NetworkManager::ActiveConnection::State state) { + if (state > NetworkManager::ActiveConnection::Activated) { + Configuration::setHotspotConnectionPath(QString()); + Q_EMIT hotspotDisabled(); + } + }); + + Q_EMIT hotspotCreated(); + } +} + +void Handler::primaryConnectionTypeChanged(NetworkManager::ConnectionSettings::ConnectionType type) +{ + Q_UNUSED(type) + m_hotspotSupported = checkHotspotSupported(); + Q_EMIT hotspotSupportedChanged(m_hotspotSupported); +} + #if WITH_MODEMMANAGER_SUPPORT void Handler::unlockRequiredChanged(MMModemLock modemLock) { diff --git a/libs/models/networkmodel.h b/libs/models/networkmodel.h --- a/libs/models/networkmodel.h +++ b/libs/models/networkmodel.h @@ -71,6 +71,7 @@ RxBytesRole, TxBytesRole }; + Q_ENUMS(ItemRole) int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; diff --git a/libs/models/networkmodel.cpp b/libs/models/networkmodel.cpp --- a/libs/models/networkmodel.cpp +++ b/libs/models/networkmodel.cpp @@ -20,6 +20,7 @@ #include "networkmodel.h" #include "networkmodelitem.h" +#include "configuration.h" #include "debug.h" #include "uiutils.h" @@ -447,6 +448,16 @@ { initializeSignals(network); + // Avoid duplicating entries in the model + if (!Configuration::hotspotConnectionPath().isEmpty()) { + NetworkManager::ActiveConnection::Ptr activeConnection = NetworkManager::findActiveConnection(Configuration::hotspotConnectionPath()); + + // If we are trying to add an AP which is the one created by our hotspot, then we can skip this and don't add it twice + if (activeConnection && activeConnection->specificObject() == network->referenceAccessPoint()->uni()) { + return; + } + } + // 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 diff --git a/libs/models/networkmodelitem.cpp b/libs/models/networkmodelitem.cpp --- a/libs/models/networkmodelitem.cpp +++ b/libs/models/networkmodelitem.cpp @@ -327,10 +327,10 @@ QString NetworkModelItem::sectionType() const { - if (m_connectionState == NetworkManager::ActiveConnection::Activated) { - return i18n("Active connections"); + if (m_connectionState == NetworkManager::ActiveConnection::Deactivated) { + return "Available connections"; } else { - return i18n("Available connections"); + return QString(); } } @@ -535,14 +535,26 @@ NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(m_devicePath); - // Get IPv[46]Address + // Get IPv[46]Address and related nameservers + IPv4 default gateway 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->ipV4Config().gateway().isEmpty()) { + QString addr = device->ipV4Config().gateway(); + if (!addr.isNull()) { + m_details << i18n("IPv4 Default Gateway") << addr; + } + } + if (!device->ipV4Config().nameservers().isEmpty()) { + QHostAddress addr = device->ipV4Config().nameservers().first(); + if (!addr.isNull()) { + m_details << i18n("IPv4 Nameserver") << addr.toString(); + } + } } if (device && device->ipV6Config().isValid() && m_connectionState == NetworkManager::ActiveConnection::Activated) { @@ -552,6 +564,12 @@ m_details << i18n("IPv6 Address") << addr.toString(); } } + if (!device->ipV6Config().nameservers().isEmpty()) { + QHostAddress addr = device->ipV6Config().nameservers().first(); + if (!addr.isNull()) { + m_details << i18n("IPv6 Nameserver") << addr.toString(); + } + } } if (m_type == NetworkManager::ConnectionSettings::Wired) { NetworkManager::WiredDevice::Ptr wiredDevice = device.objectCast(); @@ -567,9 +585,7 @@ if (m_mode == NetworkManager::WirelessSetting::Infrastructure) { m_details << i18n("Signal strength") << QStringLiteral("%1%").arg(m_signal); } - if (m_connectionState == NetworkManager::ActiveConnection::Activated) { - m_details << i18n("Security type") << UiUtils::labelFromWirelessSecurity(m_securityType); - } + 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()); diff --git a/libs/uiutils.cpp b/libs/uiutils.cpp --- a/libs/uiutils.cpp +++ b/libs/uiutils.cpp @@ -615,6 +615,9 @@ case NetworkManager::Wpa2Eap: tip = i18nc("@label WPA2-EAP security", "WPA2-EAP"); break; + case NetworkManager::SAE: + tip = i18nc("@label WPA3-SAE security", "WPA3-SAE"); + break; default: tip = i18nc("@label unknown security", "Unknown security type"); break; diff --git a/mobile/broadband/mobilebroadbandsettings.desktop b/mobile/broadband/mobilebroadbandsettings.desktop --- a/mobile/broadband/mobilebroadbandsettings.desktop +++ b/mobile/broadband/mobilebroadbandsettings.desktop @@ -13,12 +13,15 @@ Name[fi]=Mobiililaajakaista Name[fr]=Haut débit mobile Name[gl]=Banda larga móbil +Name[hu]=Mobil szélessáv +Name[ia]=Banda large mobile Name[id]=Mobile broadband Name[it]=Banda larga mobile Name[ko]=모바일 광대역 Name[lt]=Mobilusis plačiajuostis Name[nl]=Mobiel breedband Name[nn]=Mobilt breiband +Name[pa]=ਮੋਬਾਈਲ ਬਰਾਡਬੈਂਡ Name[pl]=Komórkowe szerokopasmowe Name[pt]=Banda larga móvel Name[pt_BR]=Banda larga móvel @@ -42,12 +45,14 @@ Comment[fi]=Mobiililaajakaista-asetukset Comment[fr]=Paramètre haut débit mobile Comment[gl]=Opción de banda larga móbil +Comment[hu]=Mobil szélessáv beállításai Comment[id]=Pengesetan mobile broadband Comment[it]=Impostazioni banda larga mobile Comment[ko]=모바일 광대역 설정 Comment[lt]=Mobiliojo plačiajuosčio nuostata Comment[nl]=Instelling voor mobiel breedband Comment[nn]=Innstilling for mobilt breiband +Comment[pa]=ਮੋਬਾਈਲ ਬਰਾਡਬੈਂਡ ਸੈਟਿੰਗ Comment[pl]=Ustawienia sieci komórkowej Comment[pt]=Configuração da banda larga móvel Comment[pt_BR]=Configurações da banda larga móvel @@ -85,12 +90,14 @@ X-KDE-Keywords[fi]=mobiili,data,verkko X-KDE-Keywords[fr]=mobile,données,réseau X-KDE-Keywords[gl]=móbil,datos,rede +X-KDE-Keywords[hu]=mobil,adat,hálózat X-KDE-Keywords[id]=mobile,data,jaringan X-KDE-Keywords[it]=mobile, dati,rete X-KDE-Keywords[ko]=mobile,data,network,모바일,데이터,네트워크 X-KDE-Keywords[lt]=mobilusis,duomenys,mobilieji,tinklas X-KDE-Keywords[nl]=mobiel,gegevens,netwerk X-KDE-Keywords[nn]=mobil,mobilt,data,nettverk +X-KDE-Keywords[pa]=ਮੋਬਾਈਲ,ਡਾਟਾ,ਨੈੱਟਵਰਕ X-KDE-Keywords[pl]=komórkowa,mobilna,dane,sieć X-KDE-Keywords[pt]=móvel,dados,rede X-KDE-Keywords[pt_BR]=móvel,dados,rede diff --git a/mobile/broadband/package/metadata.desktop b/mobile/broadband/package/metadata.desktop --- a/mobile/broadband/package/metadata.desktop +++ b/mobile/broadband/package/metadata.desktop @@ -13,12 +13,15 @@ Name[fi]=Mobiililaajakaista Name[fr]=Haut débit mobile Name[gl]=Banda larga móbil +Name[hu]=Mobil szélessáv +Name[ia]=Banda large mobile Name[id]=Mobile Broadband Name[it]=Banda larga mobile Name[ko]=모바일 광대역 Name[lt]=Mobilusis plačiajuostis Name[nl]=Mobiel breedband Name[nn]=Mobilt breiband +Name[pa]=ਮੋਬਾਈਲ ਬਰਾਡਬੈਂਡ Name[pl]=Komórkowe szerokopasmowe Name[pt]=Banda Larga Móvel Name[pt_BR]=Banda larga móvel @@ -42,12 +45,14 @@ Comment[fi]=Mobiililaajakaista-asetukset Comment[fr]=Paramètre haut débit mobile Comment[gl]=Opción de banda larga móbil +Comment[hu]=Mobil szélessáv beállításai Comment[id]=Pengesetan mobile broadband Comment[it]=Impostazioni banda larga mobile Comment[ko]=모바일 광대역 설정 Comment[lt]=Mobiliojo plačiajuosčio nuostata Comment[nl]=Instelling voor mobiel breedband Comment[nn]=Innstilling for mobilt breiband +Comment[pa]=ਮੋਬਾਈਲ ਬਰਾਡਬੈਂਡ ਸੈਟਿੰਗ Comment[pl]=Ustawienia sieci komórkowej Comment[pt]=Configuração da banda larga móvel Comment[pt_BR]=Configurações da banda larga móvel diff --git a/mobile/wifi/package/contents/ui/PasswordField.qml b/mobile/wifi/package/contents/ui/PasswordField.qml --- a/mobile/wifi/package/contents/ui/PasswordField.qml +++ b/mobile/wifi/package/contents/ui/PasswordField.qml @@ -19,12 +19,11 @@ */ import QtQuick 2.6 +import org.kde.kirigami 2.10 as Kirigami import org.kde.plasma.networkmanagement 0.2 as PlasmaNM -import QtQuick.Controls 2.2 as Controls -Controls.TextField { +Kirigami.PasswordField { property int securityType - echoMode: TextInput.Password placeholderText: i18n("Password...") validator: RegExpValidator { regExp: if (securityType == PlasmaNM.Enums.StaticWep) { diff --git a/mobile/wifi/package/metadata.desktop b/mobile/wifi/package/metadata.desktop --- a/mobile/wifi/package/metadata.desktop +++ b/mobile/wifi/package/metadata.desktop @@ -13,12 +13,15 @@ Name[fi]=Langaton verkko Name[fr]=Wi-Fi Name[gl]=Sen fíos +Name[hu]=Wi-Fi +Name[ia]=Wi-Fi Name[id]=Wi-Fi Name[it]=Wi-fi Name[ko]=Wi-Fi Name[lt]=Belaidis (Wi-Fi) Name[nl]=Wi-Fi Name[nn]=Wi-Fi +Name[pa]=Wi-Fi Name[pl]=Wi-Fi Name[pt]=Wi-Fi Name[pt_BR]=Wi-Fi @@ -43,12 +46,14 @@ Comment[fi]=Langattoman verkon asetukset Comment[fr]=Paramètre du réseau sans fil Comment[gl]=Opción de rede sen fíos +Comment[hu]=Vezeték nélküli hálózati beállítások Comment[id]=Pengesetan jaringan wireless Comment[it]=Impostazioni rete senza fili Comment[ko]=무선 네트워크 설정 Comment[lt]=Belaidžio tinklo nuostata Comment[nl]=Draadloos netwerk instelling Comment[nn]=Innstilling for trådlaust nettverk +Comment[pa]=ਬੇਤਾਕਰ ਨੈੱਟਵਰਕ ਸੈਟਿੰਗ Comment[pl]=Ustawienia sieci bezprzewodowej Comment[pt]=Configuração da rede sem-fios Comment[pt_BR]=Configurações da rede sem fio diff --git a/mobile/wifi/wifisettings.desktop b/mobile/wifi/wifisettings.desktop --- a/mobile/wifi/wifisettings.desktop +++ b/mobile/wifi/wifisettings.desktop @@ -13,12 +13,15 @@ Name[fi]=Langaton verkko Name[fr]=Wi-Fi Name[gl]=Sen fíos +Name[hu]=Wi-Fi +Name[ia]=Wi-Fi Name[id]=Wi-Fi Name[it]=Wi-fi Name[ko]=Wi-Fi Name[lt]=Belaidis (Wi-Fi) Name[nl]=Wi-Fi Name[nn]=Wi-Fi +Name[pa]=Wi-Fi Name[pl]=Wi-Fi Name[pt]=Wi-Fi Name[pt_BR]=Wi-Fi @@ -43,12 +46,14 @@ Comment[fi]=Langattoman verkon asetukset Comment[fr]=Paramètre du réseau sans fil Comment[gl]=Opción de rede sen fíos +Comment[hu]=Vezeték nélküli hálózati beállítások Comment[id]=Pengesetan jaringan wireless Comment[it]=Impostazioni rete senza fili Comment[ko]=무선 네트워크 설정 Comment[lt]=Belaidžio tinklo nuostata Comment[nl]=Draadloos netwerk instelling Comment[nn]=Innstilling for trådlaust nettverk +Comment[pa]=ਬੇਤਾਕਰ ਨੈੱਟਵਰਕ ਸੈਟਿੰਗ Comment[pl]=Ustawienia sieci bezprzewodowej Comment[pt]=Configuração da rede sem-fios Comment[pt_BR]=Configurações da rede sem fio @@ -87,12 +92,14 @@ X-KDE-Keywords[fi]=wifi,langaton,verkko X-KDE-Keywords[fr]=wifi,réseau X-KDE-Keywords[gl]=sen fíos,wifi,rede +X-KDE-Keywords[hu]=wifi,hálózat X-KDE-Keywords[id]=wifi,jaringan X-KDE-Keywords[it]=wifi,rete X-KDE-Keywords[ko]=wifi,network,네트워크,무선 X-KDE-Keywords[lt]=wifi,belaidis,bevielis,tinklas X-KDE-Keywords[nl]=wifi,netwerk X-KDE-Keywords[nn]=wifi,nettverk,trådlaust +X-KDE-Keywords[pa]=wifi,ਨੈੱਟਵਰਕ X-KDE-Keywords[pl]=wifi,sieć X-KDE-Keywords[pt]=wifi,rede X-KDE-Keywords[pt_BR]=wifi,sem fio,rede diff --git a/vpn/fortisslvpn/fortisslvpnwidget.cpp b/vpn/fortisslvpn/fortisslvpnwidget.cpp --- a/vpn/fortisslvpn/fortisslvpnwidget.cpp +++ b/vpn/fortisslvpn/fortisslvpnwidget.cpp @@ -50,6 +50,7 @@ d->ui.setupUi(this); d->ui.password->setPasswordOptionsEnabled(true); + d->ui.password->setPasswordNotRequiredEnabled(true); // Connect for setting check watchChangedSetting(); @@ -109,8 +110,10 @@ d->ui.password->setPasswordOption(PasswordField::StoreForAllUsers); } else if (passwordFlag == NetworkManager::Setting::AgentOwned) { d->ui.password->setPasswordOption(PasswordField::StoreForUser); - } else { + } else if (passwordFlag == NetworkManager::Setting::NotSaved) { d->ui.password->setPasswordOption(PasswordField::AlwaysAsk); + } else { + d->ui.password->setPasswordOption(PasswordField::NotRequired); } const QString caCert = data.value(NM_FORTISSLVPN_KEY_CA); @@ -188,8 +191,10 @@ data.insert(NM_FORTISSLVPN_KEY_PASSWORD"-flags", QString::number(NetworkManager::Setting::None)); } else if (d->ui.password->passwordOption() == PasswordField::StoreForUser) { data.insert(NM_FORTISSLVPN_KEY_PASSWORD"-flags", QString::number(NetworkManager::Setting::AgentOwned)); - } else { + } else if (d->ui.password->passwordOption() == PasswordField::AlwaysAsk) { data.insert(NM_FORTISSLVPN_KEY_PASSWORD"-flags", QString::number(NetworkManager::Setting::NotSaved)); + } else { + data.insert(NM_FORTISSLVPN_KEY_PASSWORD"-flags", QString::number(NetworkManager::Setting::NotRequired)); } if (!d->ui.caCert->url().isEmpty()) { @@ -211,6 +216,8 @@ if (d->advUi.otp->isChecked()) { data.insert(QLatin1String(NM_FORTISSLVPN_KEY_OTP"-flags"), QString::number(NetworkManager::Setting::NotSaved)); + } else { + data.insert(QLatin1String(NM_FORTISSLVPN_KEY_OTP"-flags"), QString::number(NetworkManager::Setting::None)); } if (!d->advUi.realm->text().isEmpty()) { diff --git a/vpn/l2tp/CMakeLists.txt b/vpn/l2tp/CMakeLists.txt --- a/vpn/l2tp/CMakeLists.txt +++ b/vpn/l2tp/CMakeLists.txt @@ -4,11 +4,11 @@ l2tp.cpp l2tpwidget.cpp l2tpauth.cpp - l2tpadvancedwidget.cpp + l2tpipsecwidget.cpp l2tppppwidget.cpp ) -ki18n_wrap_ui(l2tp_SRCS l2tp.ui l2tpadvanced.ui l2tpauth.ui l2tpppp.ui) +ki18n_wrap_ui(l2tp_SRCS l2tp.ui l2tpipsec.ui l2tpppp.ui) add_library(plasmanetworkmanagement_l2tpui ${l2tp_SRCS}) diff --git a/vpn/l2tp/l2tp.cpp b/vpn/l2tp/l2tp.cpp --- a/vpn/l2tp/l2tp.cpp +++ b/vpn/l2tp/l2tp.cpp @@ -22,7 +22,6 @@ #include -#include "nm-l2tp-service.h" #include "l2tp.h" #include "l2tpwidget.h" #include "l2tpauth.h" @@ -45,7 +44,7 @@ SettingWidget *L2tpUiPlugin::askUser(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) { - return new L2tpAuthDialog(setting, parent); + return new L2tpAuthWidget(setting, parent); } QString L2tpUiPlugin::suggestedFileName(const NetworkManager::ConnectionSettings::Ptr &connection) const diff --git a/vpn/l2tp/l2tp.ui b/vpn/l2tp/l2tp.ui --- a/vpn/l2tp/l2tp.ui +++ b/vpn/l2tp/l2tp.ui @@ -6,146 +6,276 @@ 0 0 - 450 - 388 + 495 + 422 - - - 6 - - - - - Gateway: - - - - - - - L2TP server IP or name. - - - true - - - - - - - User name: - - - - - - - Set the name used for authenticating the local system to the peer to <name>. - - - true - - - - - - - Password: - - - - - - - + + + + + + + Gateway: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + gateway + + + + + - Password passed to PPPD when prompted for it. + L2TP server IP or name. - + true + + + + Authentication type: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + cmbAuthType + + + + + + + + 0 + 0 + + + + Select an authentication mode. + + + + Password + + + + + Certificates (TLS) + + + + - - - - NT Domain: - + + + + + + + + Username: + + + false + + + username + + + + + + + <html><head/><body><p>Set the name used for authenticating the local system to the peer to &lt;name&gt;.</p></body></html> + + + true + + + + + + + Password: + + + password + + + + + + + + + true + + + <html><head/><body><p>Password passed to PPPD when prompted for it.</p></body></html> + + + true + + + + + + + + + NT Domain: + + + domain + + + + + + + <html><head/><body><p>Append the domain name &lt;domain&gt; to the local host name for</p><p>authentication purposes.</p></body></html> + + + + + + + Qt::Vertical + + + + 347 + 200 + + + + + + + + + + + + CA Certificate: + + + false + + + userCA + + + + + + + <html><head/><body><p>Certificate authority (CA) file in .pem, .der, .crt, .crt or .p12 formats.</p></body></html> + + + *.pem *.der *.crt *.cer *.p12 + + + + + + + User Certificate: + + + false + + + userCert + + + + + + + <html><head/><body><p>Certificate in .pem, .der, .crt, .cer or .p12 formats.</p></body></html> + + + *.pem *.der *.crt *.cer *.p12 + + + + + + + Private Key: + + + false + + + userKey + + + + + + + <html><head/><body><p>Private key in .pem, .der, .key, .pk8 or .p12 formats.</p></body></html> + + + *.pem *der *.key *.pk8 *.p12 + + + + + + + Private Key Password: + + + userKeyPassword + + + + + + + + + <html><head/><body><p>Password for private key or PKCS#12 certificate.</p></body></html> + + + true + + + + + + + - - - - Append the domain name <domain> to the local host name for -authentication purposes. - - - true - - - - - - - Qt::Horizontal - - - - - - - - - - - - - - CA Certificate: - - - - - - - Certificate: - - - - - - - Private Key: - - - - - - - Qt::Vertical - - - - 20 - 216 - - - - - - + + Qt::Horizontal - 188 - 22 + 40 + 20 @@ -166,36 +296,9 @@ - - - - false - - - - - - - false - - - - - - - false - - - - - - - Use Certificate - - - + KUrlRequester @@ -210,12 +313,34 @@ gateway + cmbAuthType username password domain + userCA + userCert + userKey + userKeyPassword btnIPSecSettings btnPPPSettings - + + + cmbAuthType + currentIndexChanged(int) + stackedWidget + setCurrentIndex(int) + + + 345 + 76 + + + 488 + 234 + + + + diff --git a/vpn/l2tp/l2tpadvanced.ui b/vpn/l2tp/l2tpadvanced.ui deleted file mode 100644 --- a/vpn/l2tp/l2tpadvanced.ui +++ /dev/null @@ -1,303 +0,0 @@ - - - L2tpAdvancedWidget - - - - 0 - 0 - 382 - 254 - - - - - 6 - - - - - Enable IPsec tunnel to L2TP host - - - - - - - false - - - Gateway ID: - - - - - - - false - - - - - - - false - - - Pre-shared Key: - - - - - - - false - - - true - - - - - - - false - - - Phase1 algorithms: - - - - - - - false - - - - - - - false - - - Phase2 algorithms: - - - - - - - false - - - - - - - false - - - Enforce UDP encapsulation - - - true - - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - PasswordField - QLineEdit -
passwordfield.h
-
-
- - - - cbEnableTunnelToHost - toggled(bool) - gatewayId - setEnabled(bool) - - - 159 - 14 - - - 209 - 65 - - - - - cbEnableTunnelToHost - toggled(bool) - presharedKey - setEnabled(bool) - - - 159 - 14 - - - 209 - 91 - - - - - cbEnableTunnelToHost - toggled(bool) - ike - setEnabled(bool) - - - 190 - 16 - - - 209 - 65 - - - - - cbEnableTunnelToHost - toggled(bool) - esp - setEnabled(bool) - - - 190 - 16 - - - 209 - 65 - - - - - cbEnableTunnelToHost - toggled(bool) - cbForceEncaps - setEnabled(bool) - - - 190 - 16 - - - 209 - 65 - - - - - cbEnableTunnelToHost - toggled(bool) - label_2 - setEnabled(bool) - - - 190 - 16 - - - 75 - 87 - - - - - cbEnableTunnelToHost - toggled(bool) - label - setEnabled(bool) - - - 190 - 16 - - - 86 - 49 - - - - - cbEnableTunnelToHost - toggled(bool) - label_3 - setEnabled(bool) - - - 190 - 16 - - - 64 - 125 - - - - - cbEnableTunnelToHost - toggled(bool) - label_4 - setEnabled(bool) - - - 190 - 16 - - - 64 - 163 - - - - - buttonBox - accepted() - L2tpAdvancedWidget - accept() - - - 190 - 119 - - - 190 - 68 - - - - - buttonBox - rejected() - L2tpAdvancedWidget - reject() - - - 190 - 119 - - - 190 - 68 - - - - -
diff --git a/vpn/l2tp/l2tpadvancedwidget.cpp b/vpn/l2tp/l2tpadvancedwidget.cpp deleted file mode 100644 --- a/vpn/l2tp/l2tpadvancedwidget.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - Copyright 2013 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 "l2tpadvancedwidget.h" -#include "ui_l2tpadvanced.h" -#include "nm-l2tp-service.h" - -#include -#include - -L2tpAdvancedWidget::L2tpAdvancedWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent) - : QDialog(parent) - , m_ui(new Ui::L2tpAdvancedWidget) -{ - m_ui->setupUi(this); - - setWindowTitle(i18n("L2TP IPsec Options")); - - KAcceleratorManager::manage(this); - - loadConfig(setting); -} - -L2tpAdvancedWidget::~L2tpAdvancedWidget() -{ - delete m_ui; -} - -void L2tpAdvancedWidget::loadConfig(const NetworkManager::VpnSetting::Ptr &setting) -{ - if (setting->data().value(NM_L2TP_KEY_IPSEC_ENABLE) == "yes") { - m_ui->cbEnableTunnelToHost->setChecked(true); - m_ui->gatewayId->setText(setting->data().value(NM_L2TP_KEY_IPSEC_GATEWAY_ID)); - m_ui->presharedKey->setText(setting->data().value(NM_L2TP_KEY_IPSEC_PSK)); - m_ui->ike->setText(setting->data().value(NM_L2TP_KEY_IPSEC_IKE)); - m_ui->esp->setText(setting->data().value(NM_L2TP_KEY_IPSEC_ESP)); - if (setting->data().value(NM_L2TP_KEY_IPSEC_FORCEENCAPS) == "yes" ) { - m_ui->cbForceEncaps->setChecked(true); - } else { - m_ui->cbForceEncaps->setChecked(false); - } - } else { - m_ui->cbEnableTunnelToHost->setChecked(false); - } -} - -NMStringMap L2tpAdvancedWidget::setting() const -{ - NMStringMap result; - - if (m_ui->cbEnableTunnelToHost->isChecked()) { - result.insert(NM_L2TP_KEY_IPSEC_ENABLE, "yes"); - - if (!m_ui->gatewayId->text().isEmpty()) { - result.insert(NM_L2TP_KEY_IPSEC_GATEWAY_ID, m_ui->gatewayId->text()); - } - - if (!m_ui->presharedKey->text().isEmpty()) { - result.insert(NM_L2TP_KEY_IPSEC_PSK, m_ui->presharedKey->text()); - } - - if (!m_ui->ike->text().isEmpty()) { - result.insert(NM_L2TP_KEY_IPSEC_IKE, m_ui->ike->text()); - } - - if (!m_ui->esp->text().isEmpty()) { - result.insert(NM_L2TP_KEY_IPSEC_ESP, m_ui->esp->text()); - } - - if (m_ui->cbForceEncaps->isChecked()) { - result.insert(NM_L2TP_KEY_IPSEC_FORCEENCAPS, "yes"); - } - } - - return result; -} diff --git a/vpn/l2tp/l2tpauth.h b/vpn/l2tp/l2tpauth.h --- a/vpn/l2tp/l2tpauth.h +++ b/vpn/l2tp/l2tpauth.h @@ -1,21 +1,22 @@ /* - Copyright 2013 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, + Copyright 2011 Ilia Kats + Copyright 2013 Lukáš Tinkl + + 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 - Lesser General Public License for more details. + 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 Lesser General Public - License along with this library. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #ifndef PLASMA_NM_L2TP_AUTH_H @@ -25,20 +26,20 @@ #include "settingwidget.h" -class L2tpAuthDialogPrivate; +class L2tpAuthWidgetPrivate; -class L2tpAuthDialog : public SettingWidget +class L2tpAuthWidget : public SettingWidget { Q_OBJECT - Q_DECLARE_PRIVATE(L2tpAuthDialog) + Q_DECLARE_PRIVATE(L2tpAuthWidget) public: - explicit L2tpAuthDialog(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = nullptr); - ~L2tpAuthDialog() override; + explicit L2tpAuthWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = nullptr); + ~L2tpAuthWidget() override; virtual void readSecrets(); QVariantMap setting() const override; private: - L2tpAuthDialogPrivate *const d_ptr; + L2tpAuthWidgetPrivate *const d_ptr; }; #endif // PLASMA_NM_L2TP_AUTH_H diff --git a/vpn/l2tp/l2tpauth.cpp b/vpn/l2tp/l2tpauth.cpp --- a/vpn/l2tp/l2tpauth.cpp +++ b/vpn/l2tp/l2tpauth.cpp @@ -1,92 +1,130 @@ /* - Copyright 2013 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, + Copyright 2011 Ilia Kats + Copyright 2013 Lukáš Tinkl + Copyright 2020 Douglas Kosovic + + 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 - Lesser General Public License for more details. + 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 Lesser General Public - License along with this library. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #include "l2tpauth.h" -#include "ui_l2tpauth.h" -#include "nm-l2tp-service.h" +#include "passwordfield.h" #include +#include +#include +#include + +#include -class L2tpAuthDialogPrivate +#include "nm-l2tp-service.h" + +class L2tpAuthWidgetPrivate { public: - Ui_L2tpAuth ui; NetworkManager::VpnSetting::Ptr setting; + QFormLayout *layout; }; -L2tpAuthDialog::L2tpAuthDialog(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) +L2tpAuthWidget::L2tpAuthWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) : SettingWidget(setting, parent) - , d_ptr(new L2tpAuthDialogPrivate) + , d_ptr(new L2tpAuthWidgetPrivate) { - Q_D(L2tpAuthDialog); - d->ui.setupUi(this); + Q_D(L2tpAuthWidget); d->setting = setting; - - KAcceleratorManager::manage(this); + d->layout = new QFormLayout(this); + setLayout(d->layout); readSecrets(); + + KAcceleratorManager::manage(this); } -L2tpAuthDialog::~L2tpAuthDialog() +L2tpAuthWidget::~L2tpAuthWidget() { delete d_ptr; } -void L2tpAuthDialog::readSecrets() +void L2tpAuthWidget::readSecrets() { - Q_D(L2tpAuthDialog); - const NMStringMap data = d->setting->data(); + Q_D(L2tpAuthWidget); const NMStringMap secrets = d->setting->secrets(); - - QString user = data.value(NM_L2TP_KEY_USER); - if (!user.isEmpty()) { - d->ui.leUserName->setText(user); + const NMStringMap dataMap = d->setting->data(); + const QString userAType = dataMap[NM_L2TP_KEY_USER_AUTH_TYPE]; + const QString machineAType = dataMap[NM_L2TP_KEY_MACHINE_AUTH_TYPE]; + QLabel *label; + PasswordField *lineEdit; + + NetworkManager::Setting::SecretFlags passType = (NetworkManager::Setting::SecretFlags)dataMap[NM_L2TP_KEY_PASSWORD"-flags"].toInt(); + NetworkManager::Setting::SecretFlags userCertType = (NetworkManager::Setting::SecretFlags)dataMap[NM_L2TP_KEY_USER_CERTPASS"-flags"].toInt(); + NetworkManager::Setting::SecretFlags machineCertType = (NetworkManager::Setting::SecretFlags)dataMap[NM_L2TP_KEY_MACHINE_CERTPASS"-flags"].toInt(); + + if ((userAType.isEmpty() || userAType == QLatin1String(NM_L2TP_AUTHTYPE_PASSWORD)) && !(passType.testFlag(NetworkManager::Setting::NotRequired))) { + label = new QLabel(this); + label->setText(i18n("User Password:")); + lineEdit = new PasswordField(this); + lineEdit->setPasswordModeEnabled(true); + lineEdit->setProperty("nm_secrets_key", QLatin1String(NM_L2TP_KEY_PASSWORD)); + lineEdit->setText(secrets.value(QLatin1String(NM_L2TP_KEY_PASSWORD))); + d->layout->addRow(label, lineEdit); + } else if (userAType == QLatin1String(NM_L2TP_AUTHTYPE_TLS) && !(userCertType.testFlag(NetworkManager::Setting::NotRequired))) { + label = new QLabel(this); + label->setText(i18n("User Certificate Password:")); + lineEdit = new PasswordField(this); + lineEdit->setPasswordModeEnabled(true); + lineEdit->setProperty("nm_secrets_key", QLatin1String(NM_L2TP_KEY_USER_CERTPASS)); + lineEdit->setText(secrets.value(QLatin1String(NM_L2TP_KEY_USER_CERTPASS))); + d->layout->addRow(label, lineEdit); } - bool haveUserPassword = true; - if (!((NetworkManager::Setting::SecretFlags)data.value(NM_L2TP_KEY_PASSWORD"-flags").toInt()).testFlag(NetworkManager::Setting::NotRequired)) { - d->ui.leUserPassword->setText(secrets.value(QLatin1String(NM_L2TP_KEY_PASSWORD))); - } else { - d->ui.userNameLabel->setVisible(false); - d->ui.leUserName->setVisible(false); - d->ui.userPasswordLabel->setVisible(false); - d->ui.leUserPassword->setVisible(false); - haveUserPassword = false; + + if (machineAType == QLatin1String(NM_L2TP_AUTHTYPE_TLS)) { + if (!(machineCertType.testFlag(NetworkManager::Setting::NotRequired))) { + label = new QLabel(this); + label->setText(i18n("Machine Certificate Password:")); + lineEdit = new PasswordField(this); + lineEdit->setPasswordModeEnabled(true); + lineEdit->setProperty("nm_secrets_key", QLatin1String(NM_L2TP_KEY_MACHINE_CERTPASS)); + lineEdit->setText(secrets.value(QLatin1String(NM_L2TP_KEY_MACHINE_CERTPASS))); + d->layout->addRow(label, lineEdit); + } } - if (haveUserPassword && d->ui.leUserPassword->text().isEmpty()) { - d->ui.leUserPassword->setFocus(Qt::OtherFocusReason); + for (int i = 0; i < d->layout->rowCount(); i++) { + PasswordField *le = qobject_cast(d->layout->itemAt(i, QFormLayout::FieldRole)->widget()); + if (le && le->text().isEmpty()) { + le->setFocus(Qt::OtherFocusReason); + break; + } } } -QVariantMap L2tpAuthDialog::setting() const +QVariantMap L2tpAuthWidget::setting() const { - Q_D(const L2tpAuthDialog); + Q_D(const L2tpAuthWidget); NMStringMap secrets; - QVariantMap result; - - if (!d->ui.leUserPassword->text().isEmpty()) { - secrets.insert(NM_L2TP_KEY_PASSWORD, d->ui.leUserPassword->text()); + QVariantMap secretData; + for (int i = 0; i < d->layout->rowCount(); i++) { + PasswordField *le = qobject_cast(d->layout->itemAt(i, QFormLayout::FieldRole)->widget()); + if (le && !le->text().isEmpty()) { + const QString key = le->property("nm_secrets_key").toString(); + secrets.insert(key, le->text()); + } } - result.insert("secrets", QVariant::fromValue(secrets)); - - return result; + secretData.insert("secrets", QVariant::fromValue(secrets)); + return secretData; } diff --git a/vpn/l2tp/l2tpauth.ui b/vpn/l2tp/l2tpauth.ui deleted file mode 100644 --- a/vpn/l2tp/l2tpauth.ui +++ /dev/null @@ -1,71 +0,0 @@ - - - L2tpAuth - - - - 0 - 0 - 378 - 133 - - - - - 6 - - - - - Username: - - - leUserName - - - - - - - false - - - Set the name used for authenticating the local system to the peer to <name>. - - - - - - - User Password: - - - false - - - leUserPassword - - - - - - - Password passed to PPPD when prompted for it. - - - true - - - - - - - - PasswordField - QLineEdit -
passwordfield.h
-
-
- - -
diff --git a/vpn/l2tp/l2tpipsec.ui b/vpn/l2tp/l2tpipsec.ui new file mode 100644 --- /dev/null +++ b/vpn/l2tp/l2tpipsec.ui @@ -0,0 +1,480 @@ + + + L2tpIpsecWidget + + + + 0 + 0 + 538 + 660 + + + + + + + true + + + Enable IPsec tunnel to L2TP host + + + true + + + true + + + + + + + 0 + 0 + + + + Machine Authentication + + + + + + + + Type: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + cmbAuthType + + + + + + + + 0 + 0 + + + + Select an authentication mode. + + + + Pre-shared Key (PSK) + + + + + Certificates (TLS) + + + + + + + + + + + + + + Pre-shared Key: + + + presharedKey + + + + + + + + + <html><head/><body><p>Pre-shared key (PSK) secret.</p></body></html> + + + true + + + + + + + + + + + + + <html><head/><body><p>Certificate authority (CA) file in .pem, .der, .crt, .crt or .p12 formats.</p></body></html> + + + *.pem *.der *.crt *.cer *.p12 + + + + + + + Machine Certificate: + + + false + + + machineCert + + + + + + + <html><head/><body><p>Certificate in .pem, .der or .p12 formats.</p></body></html> + + + *.pem *.der *.crt *.cer *.p12 + + + + + + + Private Key: + + + false + + + machineKey + + + + + + + <html><head/><body><p>Private key in .pem, .der, .key, .pk8 or .p12 formats.</p></body></html> + + + *.pem *der *.key *.pk8 *.p12 + + + + + + + Private Key Password: + + + machineKeyPassword + + + + + + + + + <html><head/><body><p>Password for private key or PKCS#12 certificate.</p></body></html> + + + true + + + + + + + + + CA Certificate: + + + false + + + machineCA + + + + + + + + + + + + + + Advanced + + + + + + Phase1 Algorithms: + + + ike + + + + + + + Phase2 Algorithms: + + + esp + + + + + + + <html><head/><body><p>Optional. A list of proposals for ESP - Quick Mode. The format is “enc-integ,enc-integ, …”.</p></body></html> + + + + + + + false + + + <html><head/><body><p>How long the keying channel of a connection should last before being renegotiated.</p></body></html> + + + hh:mm:ss + + + + + + + Phase1 Lifetime: + + + + + + + <html><head/><body><p>Optional. A list of proposals for IKE - Main Mode. The format is “enc-integ-group,enc-integ-group, …”.</p></body></html> + + + + + + + Phase2 Lifetime: + + + + + + + false + + + <html><head/><body><p>How long a particular instance of a connection (a set of encryption/authentication keys for user packets) should last, from successful negotiation to expiry.</p></body></html> + + + hh:mm:ss + + + + + + + <html><head/><body><p>Optional. How the IPsec server should be identified for authentication. Sometimes referred to as Peer ID or Gateway ID, also referred to as rightid by strongSwan, Libreswan, Openswan and FreeS/WAN. See strongSwan or Libreswan documentation for leftid/rightid syntax and identity parsing.</p></body></html> + + + + + + + <html><head/><body><p>IPComp compresses raw IP packets before they get encrypted. This saves some bandwidth, but uses more processing power.</p></body></html> + + + Use IP compression + + + + + + + <html><head/><body><p>Disable perfect forward security. Enable this option only if the server doesn’t support PFS.</p></body></html> + + + Disable PFS + + + + + + + <html><head/><body><p>Some firewalls block ESP traffic. Enforcing UDP encapsulation even if no NAT situation is detected might help in such cases.</p></body></html> + + + Enforce UDP encapsulation + + + false + + + + + + + Remote ID: + + + remoteId + + + + + + + + + + Qt::Vertical + + + + 510 + 0 + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + buttonBox + gbEnableTunnelToHost + + + + + KUrlRequester + QWidget +
kurlrequester.h
+
+ + PasswordField + QLineEdit +
passwordfield.h
+
+
+ + cmbAuthType + presharedKey + machineCA + machineCert + machineKey + machineKeyPassword + + + + + cmbAuthType + currentIndexChanged(int) + stackedWidget + setCurrentIndex(int) + + + 345 + 76 + + + 488 + 234 + + + + + buttonBox + accepted() + L2tpIpsecWidget + accept() + + + 190 + 119 + + + 190 + 68 + + + + + buttonBox + rejected() + L2tpIpsecWidget + reject() + + + 190 + 119 + + + 190 + 68 + + + + + cbIkelifetime + toggled(bool) + ikelifetime + setEnabled(bool) + + + 86 + 393 + + + 314 + 393 + + + + + cbSalifetime + toggled(bool) + salifetime + setEnabled(bool) + + + 86 + 418 + + + 314 + 418 + + + + +
diff --git a/vpn/l2tp/l2tpadvancedwidget.h b/vpn/l2tp/l2tpipsecwidget.h rename from vpn/l2tp/l2tpadvancedwidget.h rename to vpn/l2tp/l2tpipsecwidget.h --- a/vpn/l2tp/l2tpadvancedwidget.h +++ b/vpn/l2tp/l2tpipsecwidget.h @@ -1,5 +1,6 @@ /* Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -18,30 +19,44 @@ License along with this library. If not, see . */ -#ifndef PLASMA_NM_L2TP_ADVANCED_WIDGET_H -#define PLASMA_NM_L2TP_ADVANCED_WIDGET_H +#ifndef PLASMA_NM_L2TP_IPSEC_WIDGET_H +#define PLASMA_NM_L2TP_IPSEC_WIDGET_H #include #include namespace Ui { -class L2tpAdvancedWidget; +class L2tpIpsecWidget; } -class L2tpAdvancedWidget : public QDialog +class L2tpIpsecWidget : public QDialog { Q_OBJECT + + enum AuthType {PSK = 0, TLS}; + enum IpsecDaemonType {NoIpsecDaemon, Libreswan, Strongswan, Openswan, UnknownIpsecDaemon}; + public: - explicit L2tpAdvancedWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = nullptr); - ~L2tpAdvancedWidget() override; + explicit L2tpIpsecWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = nullptr); + ~L2tpIpsecWidget() override; NMStringMap setting() const; + NMStringMap secrets() const; + + static bool hasIpsecDaemon(); + +private Q_SLOTS: + void updateStartDirUrl(const QUrl &); + void setDefaultIkelifetime(bool isChecked); + void setDefaultSalifetime(bool isChecked); + void resizeStackedWidget(int currentIndex); private: void loadConfig(const NetworkManager::VpnSetting::Ptr &setting); - Ui::L2tpAdvancedWidget * m_ui; + Ui::L2tpIpsecWidget * m_ui; + static IpsecDaemonType m_ipsecDaemonType; }; -#endif // PLASMA_NM_L2TP_ADVANCED_WIDGET_H +#endif // PLASMA_NM_L2TP_IPSEC_WIDGET_H diff --git a/vpn/l2tp/l2tpipsecwidget.cpp b/vpn/l2tp/l2tpipsecwidget.cpp new file mode 100644 --- /dev/null +++ b/vpn/l2tp/l2tpipsecwidget.cpp @@ -0,0 +1,396 @@ +/* + Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic + + 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 +#include +#include "l2tpipsecwidget.h" +#include "ui_l2tpipsec.h" +#include "nm-l2tp-service.h" + +#include +#include + +#define DEFAULT_IPSEC_STRONGSWAN_IKELIFETIME_HOURS 3 +#define DEFAULT_IPSEC_STRONGSWAN_LIFETIME_HOURS 1 + +#define DEFAULT_IPSEC_LIBRESWAN_IKELIFETIME_HOURS 1 +#define DEFAULT_IPSEC_LIBRESWAN_SALIFETIME_HOURS 8 + +L2tpIpsecWidget::L2tpIpsecWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent) + : QDialog(parent) + , m_ui(new Ui::L2tpIpsecWidget) +{ + m_ui->setupUi(this); + m_ui->machineKeyPassword->setPasswordOptionsEnabled(true); + m_ui->machineKeyPassword->setPasswordNotRequiredEnabled(true); + + // use requesters' urlSelected signals to set other requester's startDirs to save clicking + // around the filesystem, also if it is a .p12 file, set the other URLs to that .p12 file. + QList requesters; + requesters << m_ui->machineCA << m_ui->machineCert << m_ui->machineKey; + for (const KUrlRequester *requester : requesters) { + connect(requester, &KUrlRequester::urlSelected, this, &L2tpIpsecWidget::updateStartDirUrl); + } + + connect(m_ui->cbIkelifetime, &QCheckBox::toggled, this, &L2tpIpsecWidget::setDefaultIkelifetime); + connect(m_ui->cbSalifetime, &QCheckBox::toggled, this, &L2tpIpsecWidget::setDefaultSalifetime); + connect(m_ui->cmbAuthType, QOverload::of(&QComboBox::currentIndexChanged), this, &L2tpIpsecWidget::resizeStackedWidget); + + setWindowTitle(i18n("L2TP IPsec Options")); + + KAcceleratorManager::manage(this); + + loadConfig(setting); + + resizeStackedWidget(m_ui->cmbAuthType->currentIndex()); +} + +L2tpIpsecWidget::~L2tpIpsecWidget() +{ + delete m_ui; +} + +void L2tpIpsecWidget::loadConfig(const NetworkManager::VpnSetting::Ptr &setting) +{ + const QString yesString = QLatin1String("yes"); + const QString noString = QLatin1String("no"); + + // General settings + const NMStringMap dataMap = setting->data(); + + if (!hasIpsecDaemon()) { + m_ui->gbEnableTunnelToHost->setChecked(false); + m_ui->gbEnableTunnelToHost->setDisabled(true); + } else if (dataMap[NM_L2TP_KEY_IPSEC_ENABLE] == yesString) { + m_ui->gbEnableTunnelToHost->setChecked(true); + + if (dataMap[NM_L2TP_KEY_MACHINE_AUTH_TYPE].isEmpty() + || dataMap[NM_L2TP_KEY_MACHINE_AUTH_TYPE] == QLatin1String(NM_L2TP_AUTHTYPE_PSK)) { + m_ui->cmbAuthType->setCurrentIndex(AuthType::PSK); + m_ui->stackedWidget->setCurrentIndex(AuthType::PSK); + + // *SWAN support Base64 encoded PSKs that are prefixed with "0s" + QString psk = dataMap[NM_L2TP_KEY_IPSEC_PSK]; + if (psk.length() > 2 && psk.startsWith(QLatin1String("0s"))) { + m_ui->presharedKey->setText(QByteArray::fromBase64(psk.mid(2).toUtf8())); + } else { + m_ui->presharedKey->setText(psk); + } + + } else { // NM_L2TP_AUTHTYPE_TLS + m_ui->cmbAuthType->setCurrentIndex(AuthType::TLS); + m_ui->stackedWidget->setCurrentIndex(AuthType::TLS); + + m_ui->machineCA->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_MACHINE_CA])); + m_ui->machineCert->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_MACHINE_CERT])); + m_ui->machineKey->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_MACHINE_KEY])); + + const NetworkManager::Setting::SecretFlags machineKeyPassType = static_cast(dataMap[NM_L2TP_KEY_MACHINE_CERTPASS"-flags"].toInt()); + if (machineKeyPassType.testFlag(NetworkManager::Setting::None)) { + m_ui->machineKeyPassword->setPasswordOption(PasswordField::StoreForAllUsers); + } else if (machineKeyPassType.testFlag(NetworkManager::Setting::AgentOwned)) { + m_ui->machineKeyPassword->setPasswordOption(PasswordField::StoreForUser); + } else if (machineKeyPassType.testFlag(NetworkManager::Setting::NotSaved)) { + m_ui->machineKeyPassword->setPasswordOption(PasswordField::AlwaysAsk); + } else if (machineKeyPassType.testFlag(NetworkManager::Setting::NotRequired)) { + m_ui->machineKeyPassword->setPasswordOption(PasswordField::NotRequired); + } + } + + if (!dataMap[NM_L2TP_KEY_IPSEC_GATEWAY_ID].isEmpty()) { + m_ui->remoteId->setText(dataMap[NM_L2TP_KEY_IPSEC_GATEWAY_ID]); + } else { + m_ui->remoteId->setText(dataMap[NM_L2TP_KEY_IPSEC_REMOTE_ID]); + } + m_ui->ike->setText(dataMap[NM_L2TP_KEY_IPSEC_IKE]); + m_ui->esp->setText(dataMap[NM_L2TP_KEY_IPSEC_ESP]); + + if (!dataMap[NM_L2TP_KEY_IPSEC_IKELIFETIME].isEmpty()) { + const int totalSeconds = dataMap[NM_L2TP_KEY_IPSEC_IKELIFETIME].toInt(); + const int hours = totalSeconds / 3600; + const int minutes = (totalSeconds % 3600) / 60; + const int seconds = totalSeconds % 60; + const QTime lifetime = QTime(hours, minutes, seconds); + + m_ui->ikelifetime->setTime(lifetime); + m_ui->ikelifetime->setEnabled(true); + m_ui->cbIkelifetime->setChecked(true); + } else { + setDefaultIkelifetime(false); + m_ui->ikelifetime->setEnabled(false); + m_ui->cbIkelifetime->setChecked(false); + } + + if (!dataMap[NM_L2TP_KEY_IPSEC_SALIFETIME].isEmpty()) { + const int totalSeconds = dataMap[NM_L2TP_KEY_IPSEC_SALIFETIME].toInt(); + const int hours = totalSeconds / 3600; + const int minutes = (totalSeconds % 3600) / 60; + const int seconds = totalSeconds % 60; + const QTime lifetime = QTime(hours, minutes, seconds); + + m_ui->salifetime->setTime(lifetime); + m_ui->salifetime->setEnabled(true); + m_ui->cbSalifetime->setChecked(true); + } else { + setDefaultSalifetime(false); + m_ui->salifetime->setEnabled(false); + m_ui->cbSalifetime->setChecked(false); + } + + m_ui->cbForceEncaps->setChecked(dataMap[NM_L2TP_KEY_IPSEC_FORCEENCAPS] == yesString); + m_ui->cbIPComp->setChecked(dataMap[NM_L2TP_KEY_IPSEC_IPCOMP] == yesString); + + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) { + m_ui->cbPFS->setChecked(dataMap[NM_L2TP_KEY_IPSEC_PFS] == noString); + } else { + m_ui->cbPFS->setChecked(false); + m_ui->cbPFS->setDisabled(true); + m_ui->cbPFS->setToolTip(""); + } + } else { + m_ui->gbEnableTunnelToHost->setChecked(false); + setDefaultIkelifetime(false); + setDefaultSalifetime(false); + if (m_ipsecDaemonType == IpsecDaemonType::Strongswan) { + m_ui->cbPFS->setDisabled(true); + m_ui->cbPFS->setToolTip(""); + } + } +} + +NMStringMap L2tpIpsecWidget::setting() const +{ + NMStringMap result; + const QString yesString = QLatin1String("yes"); + const QString noString = QLatin1String("no"); + + if (m_ui->gbEnableTunnelToHost->isChecked()) { + result.insert(NM_L2TP_KEY_IPSEC_ENABLE, yesString); + + if (m_ui->cmbAuthType->currentIndex() == AuthType::PSK) { + // NetworkManager-l2tp < 1.2.12 does not support Base64 PSK + // For backwards compatibilty don't use Base64 PSK for now. + if (!m_ui->presharedKey->text().isEmpty()) { + result.insert(NM_L2TP_KEY_IPSEC_PSK, m_ui->presharedKey->text()); + // *SWAN support Base64 encoded PSKs that are prefixed with "0s" + /* + QString psk = QLatin1String("0s"); + psk.append(m_ui->presharedKey->text().toUtf8().toBase64()); + result.insert(NM_L2TP_KEY_IPSEC_PSK, psk); + */ + } + } else { // AuthType::TLS + + result.insert(NM_L2TP_KEY_MACHINE_AUTH_TYPE, NM_L2TP_AUTHTYPE_TLS); + + result.insert(NM_L2TP_KEY_MACHINE_CA, m_ui->machineCA->url().toLocalFile()); + result.insert(NM_L2TP_KEY_MACHINE_CERT, m_ui->machineCert->url().toLocalFile()); + result.insert(NM_L2TP_KEY_MACHINE_KEY, m_ui->machineKey->url().toLocalFile()); + + switch (m_ui->machineKeyPassword->passwordOption()) { + case PasswordField::StoreForAllUsers: + result.insert(NM_L2TP_KEY_MACHINE_CERTPASS"-flags", + QString::number(NetworkManager::Setting::None)); + break; + case PasswordField::StoreForUser: + result.insert(NM_L2TP_KEY_MACHINE_CERTPASS"-flags", + QString::number(NetworkManager::Setting::AgentOwned)); + break; + case PasswordField::AlwaysAsk: + result.insert(NM_L2TP_KEY_MACHINE_CERTPASS"-flags", + QString::number(NetworkManager::Setting::NotSaved)); + break; + case PasswordField::NotRequired: + result.insert(NM_L2TP_KEY_MACHINE_CERTPASS"-flags", + QString::number(NetworkManager::Setting::NotRequired)); + break; + }; + } + + // NetworkManager-l2tp 1.2.12 recommends NM_L2TP_KEY_IPSEC_REMOTE_ID + // instead of deprecated NM_L2TP_KEY_IPSEC_GATEWAY_ID + // For backwards compatibilty use NM_L2TP_KEY_IPSEC_GATEWAY_ID for now. + if (!m_ui->remoteId->text().isEmpty()) { + result.insert(NM_L2TP_KEY_IPSEC_GATEWAY_ID, m_ui->remoteId->text()); + } + + if (!m_ui->ike->text().isEmpty()) { + result.insert(NM_L2TP_KEY_IPSEC_IKE, m_ui->ike->text()); + } + + if (!m_ui->esp->text().isEmpty()) { + result.insert(NM_L2TP_KEY_IPSEC_ESP, m_ui->esp->text()); + } + + if (m_ui->cbForceEncaps->isChecked()) { + result.insert(NM_L2TP_KEY_IPSEC_FORCEENCAPS, yesString); + } + + if (m_ui->cbIkelifetime->isChecked()) { + const int totalSeconds = m_ui->ikelifetime->time().hour() * 3600 + m_ui->ikelifetime->time().minute() * 60 + m_ui->ikelifetime->time().second(); + + result.insert(NM_L2TP_KEY_IPSEC_IKELIFETIME, QString::number(totalSeconds)); + } + + if (m_ui->cbSalifetime->isChecked()) { + const int totalSeconds = m_ui->salifetime->time().hour() * 3600 + m_ui->salifetime->time().minute() * 60 + m_ui->salifetime->time().second(); + + result.insert(NM_L2TP_KEY_IPSEC_SALIFETIME, QString::number(totalSeconds)); + } + + if (m_ui->cbIPComp->isChecked()) { + result.insert(NM_L2TP_KEY_IPSEC_IPCOMP, yesString); + } + + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) { + if (m_ui->cbPFS->isChecked()) { + result.insert(NM_L2TP_KEY_IPSEC_PFS, noString); + } + } + } + + return result; +} + +NMStringMap L2tpIpsecWidget::secrets() const +{ + NMStringMap result; + + if (m_ui->gbEnableTunnelToHost->isChecked()) { + if (m_ui->cmbAuthType->currentIndex() == AuthType::TLS) { + + // private key password + if (!m_ui->machineKeyPassword->text().isEmpty()) { + result.insert(NM_L2TP_KEY_MACHINE_CERTPASS, m_ui->machineKeyPassword->text()); + } + } + } + + return result; +} + +void L2tpIpsecWidget::updateStartDirUrl(const QUrl &url) +{ + QList requesters; + requesters << m_ui->machineCA << m_ui->machineCert << m_ui->machineKey; + const bool isP12 = url.toString().endsWith(QLatin1String(".p12")); + + for (KUrlRequester *requester : requesters) { + requester->setStartDir(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash)); + if (isP12) { + requester->setUrl(url); + } + } +} + +void L2tpIpsecWidget::setDefaultIkelifetime(bool isChecked) +{ + if (!isChecked) { + QTime lifetime; + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) { + lifetime = QTime(DEFAULT_IPSEC_LIBRESWAN_IKELIFETIME_HOURS, 0, 0); + } else { + lifetime = QTime(DEFAULT_IPSEC_STRONGSWAN_IKELIFETIME_HOURS, 0, 0); + } + m_ui->ikelifetime->setTime(lifetime); + } +} + +void L2tpIpsecWidget::setDefaultSalifetime(bool isChecked) +{ + if (!isChecked) { + QTime lifetime; + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) { + lifetime = QTime(DEFAULT_IPSEC_LIBRESWAN_SALIFETIME_HOURS, 0, 0); + } else { + lifetime = QTime(DEFAULT_IPSEC_STRONGSWAN_LIFETIME_HOURS, 0, 0); + } + m_ui->salifetime->setTime(lifetime); + } +} + +void L2tpIpsecWidget::resizeStackedWidget(int currentIndex) +{ + m_ui->stackedWidget->setCurrentIndex(currentIndex); + for (int i = 0; i < m_ui->stackedWidget->count(); ++i) { + QSizePolicy::Policy policy; + + if (i == currentIndex) { + policy = QSizePolicy::Preferred; + } else { + policy = QSizePolicy::Ignored; + } + m_ui->stackedWidget->widget(i)->setSizePolicy(QSizePolicy::Preferred, policy); + } + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + resize(width(), sizeHint().height()); +} + +L2tpIpsecWidget::IpsecDaemonType L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::NoIpsecDaemon; + +bool L2tpIpsecWidget::hasIpsecDaemon() +{ + // NetworkManager-l2tp currently only supports libreswan and strongswan + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan + || m_ipsecDaemonType == IpsecDaemonType::Strongswan) { + return true; + } + + QString ipsecBinary = QStandardPaths::findExecutable("ipsec", + QStringList() << "/sbin" << "/usr/sbin"); + + // On some Linux distributions, ipsec executable has been renamed strongswan + if (ipsecBinary.isEmpty()) { + ipsecBinary = QStandardPaths::findExecutable("strongswan", + QStringList() << "/sbin" << "/usr/sbin"); + } + + if (ipsecBinary.isEmpty()) { + m_ipsecDaemonType = IpsecDaemonType::NoIpsecDaemon; + return false; + } + + QProcess ipsecVersionProcess; + ipsecVersionProcess.setProgram(ipsecBinary); + ipsecVersionProcess.setArguments(QStringList() << "--version"); + ipsecVersionProcess.start(); + ipsecVersionProcess.waitForFinished(-1); + + if (ipsecVersionProcess.exitStatus() == QProcess::NormalExit) { + QString ipsecStdout = ipsecVersionProcess.readAllStandardOutput(); + + if (ipsecStdout.contains(" strongSwan ", Qt::CaseSensitive)) { + L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::Strongswan; + } else if (ipsecStdout.contains(" Libreswan ", Qt::CaseSensitive)) { + L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::Libreswan; + } else if (ipsecStdout.contains(" Openswan ", Qt::CaseSensitive)) { + L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::Openswan; + } else { + L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::UnknownIpsecDaemon; + } + } + + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan + || m_ipsecDaemonType == IpsecDaemonType::Strongswan) { + return true; + } + return false; +} diff --git a/vpn/l2tp/l2tppppwidget.h b/vpn/l2tp/l2tppppwidget.h --- a/vpn/l2tp/l2tppppwidget.h +++ b/vpn/l2tp/l2tppppwidget.h @@ -1,5 +1,6 @@ /* Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -34,14 +35,15 @@ { Q_OBJECT public: - explicit L2tpPPPWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = nullptr); + explicit L2tpPPPWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = nullptr, bool need_peer_eap = false); ~L2tpPPPWidget() override; NMStringMap setting() const; private: void loadConfig(const NetworkManager::VpnSetting::Ptr &setting); Ui::L2tpPppWidget * m_ui; + bool m_need_peer_eap; }; #endif // PLASMA_NM_L2TP_PPP_WIDGET_H diff --git a/vpn/l2tp/l2tppppwidget.cpp b/vpn/l2tp/l2tppppwidget.cpp --- a/vpn/l2tp/l2tppppwidget.cpp +++ b/vpn/l2tp/l2tppppwidget.cpp @@ -1,5 +1,6 @@ /* Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -25,9 +26,10 @@ #include #include -L2tpPPPWidget::L2tpPPPWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent) +L2tpPPPWidget::L2tpPPPWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent, bool need_peer_eap) : QDialog(parent) , m_ui(new Ui::L2tpPppWidget) + , m_need_peer_eap(need_peer_eap) { m_ui->setupUi(this); @@ -45,28 +47,34 @@ void L2tpPPPWidget::loadConfig(const NetworkManager::VpnSetting::Ptr &setting) { + const QString yesString = QLatin1String("yes"); + // General settings const NMStringMap dataMap = setting->data(); - // Authentication options - const QString yesString = QLatin1String("yes"); - bool refuse_pap = (dataMap[NM_L2TP_KEY_REFUSE_PAP] == yesString); - bool refuse_chap = (dataMap[NM_L2TP_KEY_REFUSE_CHAP] == yesString); - bool refuse_mschap = (dataMap[NM_L2TP_KEY_REFUSE_MSCHAP] == yesString); - bool refuse_mschapv2 = (dataMap[NM_L2TP_KEY_REFUSE_MSCHAPV2] == yesString); - bool refuse_eap = (dataMap[NM_L2TP_KEY_REFUSE_EAP] == yesString); - - QListWidgetItem * item = nullptr; - item = m_ui->listWidget->item(0); // PAP - item->setCheckState(refuse_pap ? Qt::Unchecked : Qt::Checked); - item = m_ui->listWidget->item(1); // CHAP - item->setCheckState(refuse_chap ? Qt::Unchecked : Qt::Checked); - item = m_ui->listWidget->item(2); // MSCHAP - item->setCheckState(refuse_mschap ? Qt::Unchecked : Qt::Checked); - item = m_ui->listWidget->item(3); // MSCHAPv2 - item->setCheckState(refuse_mschapv2 ? Qt::Unchecked : Qt::Checked); - item = m_ui->listWidget->item(4); // EAP - item->setCheckState(refuse_eap ? Qt::Unchecked : Qt::Checked); + if (m_need_peer_eap) { + m_ui->grp_authenfication->setVisible(false); + resize(width(), sizeHint().height()); + } else { + // Authentication options + const bool refuse_pap = (dataMap[NM_L2TP_KEY_REFUSE_PAP] == yesString); + const bool refuse_chap = (dataMap[NM_L2TP_KEY_REFUSE_CHAP] == yesString); + const bool refuse_mschap = (dataMap[NM_L2TP_KEY_REFUSE_MSCHAP] == yesString); + const bool refuse_mschapv2 = (dataMap[NM_L2TP_KEY_REFUSE_MSCHAPV2] == yesString); + const bool refuse_eap = (dataMap[NM_L2TP_KEY_REFUSE_EAP] == yesString); + + QListWidgetItem * item = nullptr; + item = m_ui->listWidget->item(0); // PAP + item->setCheckState(refuse_pap ? Qt::Unchecked : Qt::Checked); + item = m_ui->listWidget->item(1); // CHAP + item->setCheckState(refuse_chap ? Qt::Unchecked : Qt::Checked); + item = m_ui->listWidget->item(2); // MSCHAP + item->setCheckState(refuse_mschap ? Qt::Unchecked : Qt::Checked); + item = m_ui->listWidget->item(3); // MSCHAPv2 + item->setCheckState(refuse_mschapv2 ? Qt::Unchecked : Qt::Checked); + item = m_ui->listWidget->item(4); // EAP + item->setCheckState(refuse_eap ? Qt::Unchecked : Qt::Checked); + } // Cryptography and compression const bool mppe = (dataMap[NM_L2TP_KEY_REQUIRE_MPPE] == yesString); @@ -117,29 +125,30 @@ NMStringMap L2tpPPPWidget::setting() const { NMStringMap result; - - QListWidgetItem * item = nullptr; - item = m_ui->listWidget->item(0); // PAP const QString yesString = QLatin1String("yes"); - if (item->checkState() == Qt::Unchecked) { - result.insert(NM_L2TP_KEY_REFUSE_PAP, yesString); - } - item = m_ui->listWidget->item(1); // CHAP - if (item->checkState() == Qt::Unchecked) { - result.insert(NM_L2TP_KEY_REFUSE_CHAP, yesString); - } - item = m_ui->listWidget->item(2); // MSCHAP - if (item->checkState() == Qt::Unchecked) { - result.insert(NM_L2TP_KEY_REFUSE_MSCHAP, yesString); - } - item = m_ui->listWidget->item(3); // MSCHAPv2 - if (item->checkState() == Qt::Unchecked) { - result.insert(NM_L2TP_KEY_REFUSE_MSCHAPV2, yesString); - } - item = m_ui->listWidget->item(4); // EAP - if (item->checkState() == Qt::Unchecked) { - result.insert(NM_L2TP_KEY_REFUSE_EAP, yesString); + if (!m_need_peer_eap) { + QListWidgetItem * item = nullptr; + item = m_ui->listWidget->item(0); // PAP + if (item->checkState() == Qt::Unchecked) { + result.insert(NM_L2TP_KEY_REFUSE_PAP, yesString); + } + item = m_ui->listWidget->item(1); // CHAP + if (item->checkState() == Qt::Unchecked) { + result.insert(NM_L2TP_KEY_REFUSE_CHAP, yesString); + } + item = m_ui->listWidget->item(2); // MSCHAP + if (item->checkState() == Qt::Unchecked) { + result.insert(NM_L2TP_KEY_REFUSE_MSCHAP, yesString); + } + item = m_ui->listWidget->item(3); // MSCHAPv2 + if (item->checkState() == Qt::Unchecked) { + result.insert(NM_L2TP_KEY_REFUSE_MSCHAPV2, yesString); + } + item = m_ui->listWidget->item(4); // EAP + if (item->checkState() == Qt::Unchecked) { + result.insert(NM_L2TP_KEY_REFUSE_EAP, yesString); + } } // Cryptography and compression diff --git a/vpn/l2tp/l2tpwidget.h b/vpn/l2tp/l2tpwidget.h --- a/vpn/l2tp/l2tpwidget.h +++ b/vpn/l2tp/l2tpwidget.h @@ -1,5 +1,6 @@ /* Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -33,6 +34,9 @@ class L2tpWidget : public SettingWidget { Q_OBJECT + + enum AuthType {Password = 0, TLS}; + public: explicit L2tpWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget* parent = nullptr, Qt::WindowFlags f = {}); ~L2tpWidget() override; @@ -45,15 +49,14 @@ bool isValid() const override; private Q_SLOTS: - void userPasswordTypeChanged(int index); - void showAdvanced(); + void updateStartDirUrl(const QUrl &); + void showIpsec(); void showPpp(); - void certStateChanged(); private: Ui::L2tpWidget * m_ui; NetworkManager::VpnSetting::Ptr m_setting; - NetworkManager::VpnSetting::Ptr m_tmpAdvancedSetting; + NetworkManager::VpnSetting::Ptr m_tmpIpsecSetting; NetworkManager::VpnSetting::Ptr m_tmpPppSetting; }; diff --git a/vpn/l2tp/l2tpwidget.cpp b/vpn/l2tp/l2tpwidget.cpp --- a/vpn/l2tp/l2tpwidget.cpp +++ b/vpn/l2tp/l2tpwidget.cpp @@ -1,5 +1,6 @@ /* Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -19,7 +20,7 @@ */ #include "l2tpwidget.h" -#include "l2tpadvancedwidget.h" +#include "l2tpipsecwidget.h" #include "l2tppppwidget.h" #include "ui_l2tp.h" #include "nm-l2tp-service.h" @@ -39,16 +40,29 @@ m_ui->setupUi(this); m_ui->password->setPasswordOptionsEnabled(true); + m_ui->userKeyPassword->setPasswordOptionsEnabled(true); + m_ui->userKeyPassword->setPasswordNotRequiredEnabled(true); + + // use requesters' urlSelected signals to set other requester's startDirs to save clicking + // around the filesystem, also if it is a .p12 file, set the other URLs to that .p12 file. + QList requesters; + requesters << m_ui->userCA << m_ui->userCert << m_ui->userKey; + for (const KUrlRequester *requester : requesters) { + connect(requester, &KUrlRequester::urlSelected, this, &L2tpWidget::updateStartDirUrl); + } - connect(m_ui->btnIPSecSettings, &QPushButton::clicked, this, &L2tpWidget::showAdvanced); + if (L2tpIpsecWidget::hasIpsecDaemon()) { + connect(m_ui->btnIPSecSettings, &QPushButton::clicked, this, &L2tpWidget::showIpsec); + } else { + m_ui->btnIPSecSettings->setDisabled(true); + } connect(m_ui->btnPPPSettings, &QPushButton::clicked, this, &L2tpWidget::showPpp); // Connect for setting check watchChangedSetting(); // Connect for validity check connect(m_ui->gateway, &QLineEdit::textChanged, this, &L2tpWidget::slotWidgetChanged); - connect(m_ui->cbUseCertificate, &QCheckBox::stateChanged, this, &L2tpWidget::certStateChanged); KAcceleratorManager::manage(this); @@ -59,52 +73,53 @@ L2tpWidget::~L2tpWidget() { - m_tmpAdvancedSetting.clear(); + m_tmpIpsecSetting.clear(); m_tmpPppSetting.clear(); delete m_ui; } void L2tpWidget::loadConfig(const NetworkManager::Setting::Ptr &setting) { Q_UNUSED(setting); - const NMStringMap data = m_setting->data(); - - if (data.contains(NM_L2TP_KEY_GATEWAY)) { - m_ui->gateway->setText(data.value(NM_L2TP_KEY_GATEWAY)); - } - - if (data.contains(NM_L2TP_KEY_USER)) { - m_ui->username->setText(data.value(NM_L2TP_KEY_USER)); - } - - const NetworkManager::Setting::SecretFlags userPassType = static_cast(data.value(NM_L2TP_KEY_PASSWORD"-flags").toInt()); - if (userPassType.testFlag(NetworkManager::Setting::None)) { - m_ui->password->setPasswordOption(PasswordField::StoreForAllUsers); - } else if (userPassType.testFlag(NetworkManager::Setting::AgentOwned)) { - m_ui->password->setPasswordOption(PasswordField::StoreForUser); - } else { - m_ui->password->setPasswordOption(PasswordField::AlwaysAsk); - } + const NMStringMap dataMap = m_setting->data(); - if (data.contains(NM_L2TP_KEY_DOMAIN)) { - m_ui->domain->setText(data.value(NM_L2TP_KEY_DOMAIN)); - } + m_ui->gateway->setText(dataMap[NM_L2TP_KEY_GATEWAY]); - if (data.contains(NM_L2TP_KEY_CERT_CA)) { - m_ui->urCACertificate->setText(data.value(NM_L2TP_KEY_CERT_CA)); - } - - if (data.contains(NM_L2TP_KEY_CERT_PUB)) { - m_ui->urCertificate->setText(data.value(NM_L2TP_KEY_CERT_PUB)); - } + if (dataMap[NM_L2TP_KEY_USER_AUTH_TYPE].isEmpty() || dataMap[NM_L2TP_KEY_USER_AUTH_TYPE] == QLatin1String(NM_L2TP_AUTHTYPE_PASSWORD)) { + m_ui->cmbAuthType->setCurrentIndex(AuthType::Password); + m_ui->stackedWidget->setCurrentIndex(AuthType::Password); + m_ui->username->setText(dataMap[NM_L2TP_KEY_USER]); - if (data.contains(NM_L2TP_KEY_CERT_KEY)) { - m_ui->urPrivateKey->setText(data.value(NM_L2TP_KEY_CERT_KEY)); - } + const NetworkManager::Setting::SecretFlags userPassType = static_cast(dataMap[NM_L2TP_KEY_PASSWORD"-flags"].toInt()); + if (userPassType.testFlag(NetworkManager::Setting::None)) { + m_ui->password->setPasswordOption(PasswordField::StoreForAllUsers); + } else if (userPassType.testFlag(NetworkManager::Setting::AgentOwned)) { + m_ui->password->setPasswordOption(PasswordField::StoreForUser); + } else { + m_ui->password->setPasswordOption(PasswordField::AlwaysAsk); + } - if (data.value(NM_L2TP_KEY_USE_CERT) == QLatin1String("yes")) { - m_ui->cbUseCertificate->setChecked(true); + m_ui->domain->setText(dataMap[NM_L2TP_KEY_DOMAIN]); + + } else { // NM_L2TP_AUTHTYPE_TLS + m_ui->cmbAuthType->setCurrentIndex(AuthType::TLS); + m_ui->stackedWidget->setCurrentIndex(AuthType::TLS); + + m_ui->userCA->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_USER_CA])); + m_ui->userCert->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_USER_CERT])); + m_ui->userKey->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_USER_KEY])); + + const NetworkManager::Setting::SecretFlags userKeyPassType = static_cast(dataMap[NM_L2TP_KEY_USER_CERTPASS"-flags"].toInt()); + if (userKeyPassType.testFlag(NetworkManager::Setting::None)) { + m_ui->userKeyPassword->setPasswordOption(PasswordField::StoreForAllUsers); + } else if (userKeyPassType.testFlag(NetworkManager::Setting::AgentOwned)) { + m_ui->userKeyPassword->setPasswordOption(PasswordField::StoreForUser); + } else if (userKeyPassType.testFlag(NetworkManager::Setting::NotSaved)) { + m_ui->userKeyPassword->setPasswordOption(PasswordField::AlwaysAsk); + } else if (userKeyPassType.testFlag(NetworkManager::Setting::NotRequired)) { + m_ui->userKeyPassword->setPasswordOption(PasswordField::NotRequired); + } } loadSecrets(setting); @@ -115,10 +130,13 @@ NetworkManager::VpnSetting::Ptr vpnSetting = setting.staticCast(); if (vpnSetting) { + const QString authType = m_setting->data().value(NM_L2TP_KEY_USER_AUTH_TYPE); const NMStringMap secrets = vpnSetting->secrets(); - const QString userPassword = secrets.value(NM_L2TP_KEY_PASSWORD); - if (!userPassword.isEmpty()) { - m_ui->password->setText(userPassword); + + if (authType == QLatin1String(NM_L2TP_AUTHTYPE_TLS)) { + m_ui->userKeyPassword->setText(secrets.value(NM_L2TP_KEY_USER_CERTPASS)); + } else { // NM_L2TP_AUTHTYPE_PASSWORD + m_ui->password->setText(secrets.value(NM_L2TP_KEY_PASSWORD)); } } } @@ -128,53 +146,41 @@ NetworkManager::VpnSetting setting; setting.setServiceType(QLatin1String(NM_DBUS_SERVICE_L2TP)); NMStringMap data; - if (!m_tmpAdvancedSetting.isNull()) { - data = m_tmpAdvancedSetting->data(); + NMStringMap secrets; + + if (!m_tmpIpsecSetting.isNull()) { + data = m_tmpIpsecSetting->data(); + secrets = m_tmpIpsecSetting->secrets(); } else { // retrieve the settings if the dialog has not been opened - QScopedPointer adv(new L2tpAdvancedWidget(m_setting, nullptr)); - data = adv->setting(); + QScopedPointer ipsec(new L2tpIpsecWidget(m_setting, nullptr)); + data = ipsec->setting(); + secrets = ipsec->secrets(); } if (!m_tmpPppSetting.isNull()) { data.unite(m_tmpPppSetting->data()); } else { + const bool need_peer_eap = m_ui->cmbAuthType->currentIndex() != AuthType::Password; + // retrieve the settings if the dialog has not been opened - QScopedPointer ppp(new L2tpPPPWidget(m_setting, nullptr)); + QScopedPointer ppp(new L2tpPPPWidget(m_setting, nullptr, need_peer_eap)); data.unite(ppp->setting()); } - NMStringMap secrets; - if (!m_ui->gateway->text().isEmpty()) { data.insert(NM_L2TP_KEY_GATEWAY, m_ui->gateway->text()); } - if (m_ui->cbUseCertificate->isChecked()) { - data.insert(NM_L2TP_KEY_USE_CERT, "yes"); - - if (!m_ui->urCACertificate->text().isEmpty()) { - data.insert(NM_L2TP_KEY_CERT_CA, m_ui->urCACertificate->text()); - } - - if (!m_ui->urCertificate->text().isEmpty()) { - data.insert(NM_L2TP_KEY_CERT_PUB, m_ui->urCertificate->text()); - } - - if (!m_ui->urPrivateKey->text().isEmpty()) { - data.insert(NM_L2TP_KEY_CERT_KEY, m_ui->urPrivateKey->text()); - } - - data.insert(NM_L2TP_KEY_PASSWORD"-flags", QString::number(NetworkManager::Setting::NotRequired)); - - } else { - + if (m_ui->cmbAuthType->currentIndex() == AuthType::Password) { if (!m_ui->username->text().isEmpty()) { data.insert(NM_L2TP_KEY_USER, m_ui->username->text()); } - if (m_ui->password->isEnabled() && !m_ui->password->text().isEmpty()) { + if (!m_ui->password->text().isEmpty()) { secrets.insert(NM_L2TP_KEY_PASSWORD, m_ui->password->text()); + } else { + secrets.remove(NM_L2TP_KEY_PASSWORD); } switch (m_ui->password->passwordOption()) { @@ -191,95 +197,115 @@ if (!m_ui->domain->text().isEmpty()) { data.insert(NM_L2TP_KEY_DOMAIN, m_ui->domain->text()); } + + } else { // EnumAuthType::TLS + + data.insert(NM_L2TP_KEY_USER_AUTH_TYPE, NM_L2TP_AUTHTYPE_TLS); + + data.insert(NM_L2TP_KEY_USER_CA, m_ui->userCA->url().toLocalFile()); + data.insert(NM_L2TP_KEY_USER_CERT, m_ui->userCert->url().toLocalFile()); + data.insert(NM_L2TP_KEY_USER_KEY, m_ui->userKey->url().toLocalFile()); + + // key password + if (!m_ui->userKeyPassword->text().isEmpty()) { + secrets.insert(NM_L2TP_KEY_USER_CERTPASS, m_ui->userKeyPassword->text()); + } else { + secrets.remove(NM_L2TP_KEY_USER_CERTPASS); + } + + switch (m_ui->userKeyPassword->passwordOption()) { + case PasswordField::StoreForAllUsers: + data.insert(NM_L2TP_KEY_USER_CERTPASS"-flags", QString::number(NetworkManager::Setting::None)); + break; + case PasswordField::StoreForUser: + data.insert(NM_L2TP_KEY_USER_CERTPASS"-flags", QString::number(NetworkManager::Setting::AgentOwned)); + break; + case PasswordField::AlwaysAsk: + data.insert(NM_L2TP_KEY_USER_CERTPASS"-flags", QString::number(NetworkManager::Setting::NotSaved)); + break; + case PasswordField::NotRequired: + data.insert(NM_L2TP_KEY_USER_CERTPASS"-flags", QString::number(NetworkManager::Setting::NotRequired)); + break; + }; } setting.setData(data); setting.setSecrets(secrets); return setting.toMap(); } - -void L2tpWidget::userPasswordTypeChanged(int index) +void L2tpWidget::updateStartDirUrl(const QUrl &url) { - m_ui->password->setEnabled(index == SettingWidget::EnumPasswordStorageType::Store); + QList requesters; + requesters << m_ui->userCA << m_ui->userCert << m_ui->userKey; + bool isP12 = url.toString().endsWith(QLatin1String(".p12")); + + for (KUrlRequester *requester : requesters) { + requester->setStartDir(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash)); + if (isP12) { + requester->setUrl(url); + } + } } -void L2tpWidget::showAdvanced() +void L2tpWidget::showIpsec() { - QPointer adv; - if (m_tmpAdvancedSetting.isNull()) { - adv = new L2tpAdvancedWidget(m_setting, this); + QPointer ipsec; + if (m_tmpIpsecSetting.isNull()) { + ipsec = new L2tpIpsecWidget(m_setting, this); } else { - adv = new L2tpAdvancedWidget(m_tmpAdvancedSetting, this); + ipsec = new L2tpIpsecWidget(m_tmpIpsecSetting, this); } - connect(adv.data(), &L2tpAdvancedWidget::accepted, - [adv, this] () { - NMStringMap advData = adv->setting(); - if (!advData.isEmpty()) { - if (m_tmpAdvancedSetting.isNull()) { - m_tmpAdvancedSetting = NetworkManager::VpnSetting::Ptr(new NetworkManager::VpnSetting); + connect(ipsec.data(), &L2tpIpsecWidget::accepted, + [ipsec, this] () { + NMStringMap ipsecData = ipsec->setting(); + if (!ipsecData.isEmpty()) { + if (m_tmpIpsecSetting.isNull()) { + m_tmpIpsecSetting = NetworkManager::VpnSetting::Ptr(new NetworkManager::VpnSetting); } - m_tmpAdvancedSetting->setData(advData); + m_tmpIpsecSetting->setData(ipsecData); } }); - connect(adv.data(), &L2tpAdvancedWidget::finished, - [adv] () { - if (adv) { - adv->deleteLater(); + connect(ipsec.data(), &L2tpIpsecWidget::finished, + [ipsec] () { + if (ipsec) { + ipsec->deleteLater(); } }); - adv->setModal(true); - adv->show(); + ipsec->setModal(true); + ipsec->show(); } void L2tpWidget::showPpp() { - QPointer adv; + QPointer ipsec; + bool need_peer_eap = m_ui->cmbAuthType->currentIndex() != AuthType::Password; if (m_tmpPppSetting.isNull()) { - adv = new L2tpPPPWidget(m_setting, this); + ipsec = new L2tpPPPWidget(m_setting, this, need_peer_eap); } else { - adv = new L2tpPPPWidget(m_tmpPppSetting, this); + ipsec = new L2tpPPPWidget(m_tmpPppSetting, this, need_peer_eap); } - connect(adv.data(), &L2tpPPPWidget::accepted, - [adv, this] () { - NMStringMap advData = adv->setting(); - if (!advData.isEmpty()) { + connect(ipsec.data(), &L2tpPPPWidget::accepted, + [ipsec, this] () { + NMStringMap ipsecData = ipsec->setting(); + if (!ipsecData.isEmpty()) { if (m_tmpPppSetting.isNull()) { m_tmpPppSetting = NetworkManager::VpnSetting::Ptr(new NetworkManager::VpnSetting); } - m_tmpPppSetting->setData(advData); + m_tmpPppSetting->setData(ipsecData); } }); - connect(adv.data(), &L2tpPPPWidget::finished, - [adv] () { - if (adv) { - adv->deleteLater(); + connect(ipsec.data(), &L2tpPPPWidget::finished, + [ipsec] () { + if (ipsec) { + ipsec->deleteLater(); } }); - adv->setModal(true); - adv->show(); + ipsec->setModal(true); + ipsec->show(); } bool L2tpWidget::isValid() const { return !m_ui->gateway->text().isEmpty(); } - -void L2tpWidget::certStateChanged() -{ - if (m_ui->cbUseCertificate->isChecked()) { - m_ui->urCACertificate->setEnabled(true); - m_ui->urCertificate->setEnabled(true); - m_ui->urPrivateKey->setEnabled(true); - m_ui->username->setEnabled(false); - m_ui->password->setEnabled(false); - m_ui->domain->setEnabled(false); - } else { - m_ui->urCACertificate->setEnabled(false); - m_ui->urCertificate->setEnabled(false); - m_ui->urPrivateKey->setEnabled(false); - m_ui->username->setEnabled(true); - m_ui->password->setEnabled(true); - m_ui->domain->setEnabled(true); - } -} diff --git a/vpn/l2tp/nm-l2tp-service.h b/vpn/l2tp/nm-l2tp-service.h --- a/vpn/l2tp/nm-l2tp-service.h +++ b/vpn/l2tp/nm-l2tp-service.h @@ -1,22 +1,8 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +// SPDX-License-Identifier: GPL-2.0+ /* nm-l2tp-service - L2TP VPN integration with NetworkManager * * Dan Williams * - * 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) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Copyright 2008, 2014 Red Hat, Inc. */ @@ -34,15 +20,16 @@ #define NM_DBUS_PATH_L2TP_PPP "/org/freedesktop/NetworkManager/l2tp/ppp" #define NM_L2TP_KEY_GATEWAY "gateway" +#define NM_L2TP_KEY_USER_AUTH_TYPE "user-auth-type" #define NM_L2TP_KEY_USER "user" #define NM_L2TP_KEY_PASSWORD "password" -#define NM_L2TP_KEY_USE_CERT "use-cert" -#define NM_L2TP_KEY_CERT_PUB "cert-pub" -#define NM_L2TP_KEY_CERT_CA "cert-ca" -#define NM_L2TP_KEY_CERT_KEY "cert-key" +#define NM_L2TP_KEY_DOMAIN "domain" +#define NM_L2TP_KEY_USER_CA "user-ca" +#define NM_L2TP_KEY_USER_CERT "user-cert" +#define NM_L2TP_KEY_USER_KEY "user-key" +#define NM_L2TP_KEY_USER_CERTPASS "user-certpass" #define NM_L2TP_KEY_MTU "mtu" #define NM_L2TP_KEY_MRU "mru" -#define NM_L2TP_KEY_DOMAIN "domain" #define NM_L2TP_KEY_REFUSE_EAP "refuse-eap" #define NM_L2TP_KEY_REFUSE_PAP "refuse-pap" #define NM_L2TP_KEY_REFUSE_CHAP "refuse-chap" @@ -60,13 +47,30 @@ #define NM_L2TP_KEY_LCP_ECHO_FAILURE "lcp-echo-failure" #define NM_L2TP_KEY_LCP_ECHO_INTERVAL "lcp-echo-interval" #define NM_L2TP_KEY_UNIT_NUM "unit" - +#define NM_L2TP_KEY_MACHINE_AUTH_TYPE "machine-auth-type" +#define NM_L2TP_KEY_MACHINE_CA "machine-ca" +#define NM_L2TP_KEY_MACHINE_CERT "machine-cert" +#define NM_L2TP_KEY_MACHINE_KEY "machine-key" +#define NM_L2TP_KEY_MACHINE_CERTPASS "machine-certpass" #define NM_L2TP_KEY_IPSEC_ENABLE "ipsec-enabled" -#define NM_L2TP_KEY_IPSEC_GATEWAY_ID "ipsec-gateway-id" -#define NM_L2TP_KEY_IPSEC_GROUP_NAME "ipsec-group-name" +#define NM_L2TP_KEY_IPSEC_REMOTE_ID "ipsec-remote-id" +#define NM_L2TP_KEY_IPSEC_GATEWAY_ID "ipsec-gateway-id" /* deprecated, use ipsec-remote-id */ #define NM_L2TP_KEY_IPSEC_PSK "ipsec-psk" #define NM_L2TP_KEY_IPSEC_IKE "ipsec-ike" #define NM_L2TP_KEY_IPSEC_ESP "ipsec-esp" +#define NM_L2TP_KEY_IPSEC_IKELIFETIME "ipsec-ikelifetime" +#define NM_L2TP_KEY_IPSEC_SALIFETIME "ipsec-salifetime" #define NM_L2TP_KEY_IPSEC_FORCEENCAPS "ipsec-forceencaps" +#define NM_L2TP_KEY_IPSEC_IPCOMP "ipsec-ipcomp" +#define NM_L2TP_KEY_IPSEC_IKEV2 "ipsec-ikev2" +#define NM_L2TP_KEY_IPSEC_PFS "ipsec-pfs" + +/* Internal auth-dialog -> service token indicating that no secrets are required + * for the connection if X.509 private keys are used with no password protection. + */ +#define NM_L2TP_KEY_NOSECRET "no-secret" +#define NM_L2TP_AUTHTYPE_PASSWORD "password" +#define NM_L2TP_AUTHTYPE_TLS "tls" +#define NM_L2TP_AUTHTYPE_PSK "psk" #endif /* NM_L2TP_SERVICE_DEFINES_H */ diff --git a/vpn/openconnect/CMakeLists.txt b/vpn/openconnect/CMakeLists.txt --- a/vpn/openconnect/CMakeLists.txt +++ b/vpn/openconnect/CMakeLists.txt @@ -11,11 +11,11 @@ if (${OPENCONNECT_VERSION} VERSION_LESS "3.99") # macro_optional_find_package(OpenSSL) -# macro_log_feature(OpenSSL_FOUND "OpenSSL headers" "Encryption suite" "http://www.openssl.org" FALSE "" "Needed for OpenConnect support in Network Management") +# macro_log_feature(OpenSSL_FOUND "OpenSSL headers" "Encryption suite" "https://www.openssl.org" FALSE "" "Needed for OpenConnect support in Network Management") find_package(OpenSSL) set_package_properties(OpenSSL PROPERTIES DESCRIPTION "Encryption suite" - URL "http://www.openssl.org" + URL "https://www.openssl.org" TYPE OPTIONAL ) diff --git a/vpn/openconnect/openconnectauth.cpp b/vpn/openconnect/openconnectauth.cpp --- a/vpn/openconnect/openconnectauth.cpp +++ b/vpn/openconnect/openconnectauth.cpp @@ -122,7 +122,11 @@ d->cancelPipes[1] = -1; } +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(d->ui.cmbLogLevel, QOverload::of(&QComboBox::currentIndexChanged), this, &OpenconnectAuthWidget::logLevelChanged); +#else + connect(d->ui.cmbLogLevel, QOverload::of(&QComboBox::currentIndexChanged), this, &OpenconnectAuthWidget::logLevelChanged); +#endif connect(d->ui.viewServerLog, &QCheckBox::toggled, this, &OpenconnectAuthWidget::viewServerLogToggled); connect(d->ui.btnConnect, &QPushButton::clicked, this, &OpenconnectAuthWidget::connectHost); @@ -151,7 +155,11 @@ #endif // This might be set by readSecrets() so don't connect it until now +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(d->ui.cmbHosts, QOverload::of(&QComboBox::currentIndexChanged), this, &OpenconnectAuthWidget::connectHost); +#else + connect(d->ui.cmbHosts, QOverload::of(&QComboBox::currentIndexChanged), this, &OpenconnectAuthWidget::connectHost); +#endif KAcceleratorManager::manage(this); } @@ -568,7 +576,11 @@ #else if (sopt == AUTHGROUP_OPT(form)) { #endif +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(cmb, QOverload::of(&QComboBox::currentIndexChanged), this, &OpenconnectAuthWidget::formGroupChanged); +#else + connect(cmb, QOverload::of(&QComboBox::currentIndexChanged), this, &OpenconnectAuthWidget::formGroupChanged); +#endif } widget = qobject_cast(cmb); } diff --git a/vpn/openconnect/openconnectauthworkerthread.cpp b/vpn/openconnect/openconnectauthworkerthread.cpp --- a/vpn/openconnect/openconnectauthworkerthread.cpp +++ b/vpn/openconnect/openconnectauthworkerthread.cpp @@ -229,7 +229,6 @@ if (*m_userDecidedToQuit) { return; } - QString msg; - msg.vsprintf(fmt, argPtr); + const QString msg = QString::vasprintf(fmt, argPtr); Q_EMIT updateLog(msg, level); } diff --git a/vpn/openconnect/openconnectwidget.cpp b/vpn/openconnect/openconnectwidget.cpp --- a/vpn/openconnect/openconnectwidget.cpp +++ b/vpn/openconnect/openconnectwidget.cpp @@ -87,7 +87,11 @@ connect(d->tokenDlg, &QDialog::rejected, this, &OpenconnectSettingWidget::restoreTokens); connect(d->tokenDlg, &QDialog::accepted, this, &OpenconnectSettingWidget::saveTokens); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(d->tokenUi.cmbTokenMode, QOverload::of(&QComboBox::currentIndexChanged), this, QOverload::of((&OpenconnectSettingWidget::handleTokenSecret))); +#else + connect(d->tokenUi.cmbTokenMode, QOverload::of(&QComboBox::currentIndexChanged), this, QOverload::of((&OpenconnectSettingWidget::handleTokenSecret))); +#endif // Connect for setting check watchChangedSetting(); diff --git a/vpn/openconnect/plasmanetworkmanagement_openconnect_globalprotectui.desktop b/vpn/openconnect/plasmanetworkmanagement_openconnect_globalprotectui.desktop --- a/vpn/openconnect/plasmanetworkmanagement_openconnect_globalprotectui.desktop +++ b/vpn/openconnect/plasmanetworkmanagement_openconnect_globalprotectui.desktop @@ -18,14 +18,16 @@ Name[ast]=GlobalProtect de Palo Alto Networks (openconnect) Name[ca]=PAN de Global Protect (openconnect) Name[ca@valencia]=PAN de Global Protect (openconnect) +Name[da]=PAN Global Protect (openconnect) Name[de]=PAN Global Protect (openconnect) Name[en_GB]=PAN Global Protect (openconnect) Name[es]=PAN GlobalProtect (openconnect) Name[et]=PAN Global Protect (openconnect) Name[eu]=PAN Global Protect (openconnect) Name[fi]=PAN Global Protect (openconnect) Name[fr]=PAN Global Protect (openconnect) Name[gl]=PAN Global Protect (openconnect) +Name[hu]=PAN Global Protect (openconnect) Name[id]=PAN Global Protect (openconnect) Name[it]=PAN Global Protect (openconnect) Name[ko]=PAN Global Protect(openconnect) @@ -45,14 +47,16 @@ Comment=Compatible with PAN GlobalProtect SSL VPN Comment[ca]=Compatible amb la PAN VPN SSL de GlobalProtect Comment[ca@valencia]=Compatible amb la PAN VPN SSL de GlobalProtect +Comment[da]=Kompatibel med PAN GlobalProtect SSL VPN Comment[de]=Mit PAN-GlobalProtect-SSL-VPN kompatibel Comment[en_GB]=Compatible with PAN GlobalProtect SSL VPN Comment[es]=Compatible con VPN SSL de PAN GlobalProtect Comment[et]=Ühildub PAN GlobalProtect SSL VPN-iga Comment[eu]=PAN GlobalProtect SSL VPN-rekin bateragarria Comment[fi]=Yhteensopiva PAN GlobalProtect -SSL-VPN:n kanssa Comment[fr]=Compatible avec les VPN SSL GlobalProtect PAN Comment[gl]=Compatíbel con PAN GlobalProtect SSL VPN +Comment[hu]=Kompatibilis a PAN GlobalProtect SSL VPN-nel Comment[id]=Kompatibel dengan PAN GlobalProtect SSL VPN Comment[it]=Compatibile con PAN Global Protect VPN Comment[ko]=PAN GlobalProtect SSL VPN과 호환됨 diff --git a/vpn/openvpn/openvpnadvancedwidget.cpp b/vpn/openvpn/openvpnadvancedwidget.cpp --- a/vpn/openvpn/openvpnadvancedwidget.cpp +++ b/vpn/openvpn/openvpnadvancedwidget.cpp @@ -72,9 +72,21 @@ m_ui->proxyPassword->setPasswordOptionsEnabled(true); m_ui->proxyPassword->setPasswordNotRequiredEnabled(true); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->cbCertCheck, QOverload::of(&QComboBox::currentIndexChanged), this, &OpenVpnAdvancedWidget::certCheckTypeChanged); +#else + connect(m_ui->cbCertCheck, QOverload::of(&QComboBox::currentIndexChanged), this, &OpenVpnAdvancedWidget::certCheckTypeChanged); +#endif +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->cmbProxyType, QOverload::of(&QComboBox::currentIndexChanged), this, &OpenVpnAdvancedWidget::proxyTypeChanged); +#else + connect(m_ui->cmbProxyType, QOverload::of(&QComboBox::currentIndexChanged), this, &OpenVpnAdvancedWidget::proxyTypeChanged); +#endif +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(m_ui->cboTLSMode, QOverload::of(&QComboBox::currentIndexChanged), this, [this] (int index) { +#else + connect(m_ui->cboTLSMode, QOverload::of(&QComboBox::currentIndexChanged), this, [this] (int index) { +#endif if (index == 0) { m_ui->kurlTlsAuthKey->setDisabled(true); m_ui->cboDirection->setDisabled(true); diff --git a/vpn/ssh/plasmanetworkmanagement_sshui.desktop b/vpn/ssh/plasmanetworkmanagement_sshui.desktop --- a/vpn/ssh/plasmanetworkmanagement_sshui.desktop +++ b/vpn/ssh/plasmanetworkmanagement_sshui.desktop @@ -30,6 +30,7 @@ Name[gl]=SSH Name[he]=SSH Name[hu]=SSH +Name[ia]=SSH Name[id]=SSH Name[it]=SSH Name[ja]=SSH diff --git a/vpn/ssh/sshwidget.cpp b/vpn/ssh/sshwidget.cpp --- a/vpn/ssh/sshwidget.cpp +++ b/vpn/ssh/sshwidget.cpp @@ -53,7 +53,11 @@ d->ui.le_password->setPasswordOptionsEnabled(true); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(d->ui.cmb_authType, QOverload::of(&QComboBox::currentIndexChanged), this, &SshSettingWidget::authTypeChanged); +#else + connect(d->ui.cmb_authType, QOverload::of(&QComboBox::currentIndexChanged), this, &SshSettingWidget::authTypeChanged); +#endif connect(d->ui.btn_advancedOption, &QPushButton::clicked, this, &SshSettingWidget::doAdvancedDialog); d->advancedDlg = new QDialog(this);