mirror of https://github.com/cutefishos/settings
feat(vpn): list, switch, import and add VPNs
parent
d0fb8d9144
commit
ad0bfb784f
@ -0,0 +1,511 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick.Layouts
|
||||
import Cutefish.Settings 1.0
|
||||
import FishUI 1.0 as FishUI
|
||||
import Cutefish.Network 1.0 as NM
|
||||
|
||||
// Sets up a VPN from what the provider or the network's administrator hands out: the
|
||||
// server and how to sign in. A password left empty is asked for when connecting.
|
||||
FishUI.Window {
|
||||
id: control
|
||||
|
||||
required property NM.Handler handler
|
||||
|
||||
windowButtonsTopMargin: Metrics.dialogWindowButtonsMargin
|
||||
windowButtonsRightMargin: Metrics.dialogWindowButtonsMargin
|
||||
header.height: 44
|
||||
contentTopMargin: 0
|
||||
|
||||
width: contentWidth
|
||||
height: contentHeight
|
||||
minimumWidth: contentWidth
|
||||
minimumHeight: contentHeight
|
||||
maximumWidth: contentWidth
|
||||
maximumHeight: contentHeight
|
||||
modality: Qt.WindowModal
|
||||
|
||||
property int contentWidth: 440
|
||||
property int contentHeight: _mainLayout.implicitHeight + 32
|
||||
|
||||
title: qsTr("Add VPN")
|
||||
visible: false
|
||||
minimizeButtonVisible: false
|
||||
|
||||
background.color: FishUI.Theme.secondBackgroundColor
|
||||
// The background item reaches a grown window a frame or two after its buffer does.
|
||||
color: background.color
|
||||
flags: Qt.Dialog | Qt.FramelessWindowHint
|
||||
|
||||
// Read again each time the dialog opens, so a plugin installed meanwhile shows up.
|
||||
property var kinds: []
|
||||
readonly property var kindInfo: kinds[_kindBox.currentIndex] ?? ({})
|
||||
readonly property string kind: kindInfo.kind ?? ""
|
||||
readonly property bool installed: kindInfo.installed ?? false
|
||||
readonly property bool wireGuard: kind === "wireguard"
|
||||
readonly property bool certificates: kind === "openvpn" && _authenticationBox.currentValue === "certificates"
|
||||
readonly property bool usesUserName: installed && !wireGuard && !certificates
|
||||
|
||||
property bool adding: false
|
||||
property string error
|
||||
|
||||
readonly property bool canAdd: installed && !adding && filled(_serverTextField)
|
||||
&& (wireGuard ? filled(_privateKeyTextField) && filled(_addressTextField)
|
||||
&& filled(_publicKeyTextField)
|
||||
: certificates ? filled(_certificateField.field) && filled(_keyField.field)
|
||||
: filled(_userNameTextField))
|
||||
|
||||
function filled(field) {
|
||||
return field.text.trim().length > 0
|
||||
}
|
||||
|
||||
function add() {
|
||||
if (!canAdd)
|
||||
return
|
||||
|
||||
adding = true
|
||||
error = ""
|
||||
handler.addVpn({
|
||||
kind: kind,
|
||||
name: _nameTextField.text,
|
||||
server: _serverTextField.text,
|
||||
userName: _userNameTextField.text,
|
||||
password: _passwordTextField.text,
|
||||
authentication: _authenticationBox.currentValue,
|
||||
caFile: _caField.field.text,
|
||||
certFile: _certificateField.field.text,
|
||||
keyFile: _keyField.field.text,
|
||||
keyPassword: _keyPasswordTextField.text,
|
||||
presharedKey: _presharedKeyTextField.text,
|
||||
privateKey: _privateKeyTextField.text,
|
||||
publicKey: _publicKeyTextField.text,
|
||||
address: _addressTextField.text,
|
||||
allowedIps: _allowedIpsTextField.text,
|
||||
dns: _dnsTextField.text
|
||||
})
|
||||
}
|
||||
|
||||
function chooseFile(field, title) {
|
||||
_fileDialog.field = field
|
||||
_fileDialog.title = title
|
||||
_fileDialog.open()
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
kinds = handler.vpnKinds().map(function(item) {
|
||||
item.text = item.installed ? item.label : qsTr("%1 (Not Installed)").arg(item.label)
|
||||
return item
|
||||
})
|
||||
_kindBox.currentIndex = 0
|
||||
_serverTextField.forceActiveFocus()
|
||||
return
|
||||
}
|
||||
|
||||
for (const field of [_nameTextField, _serverTextField, _userNameTextField, _passwordTextField,
|
||||
_presharedKeyTextField, _caField.field, _certificateField.field, _keyField.field,
|
||||
_keyPasswordTextField, _privateKeyTextField, _publicKeyTextField, _addressTextField,
|
||||
_allowedIpsTextField, _dnsTextField])
|
||||
field.clear()
|
||||
_kindBox.currentIndex = 0
|
||||
_authenticationBox.currentIndex = 0
|
||||
adding = false
|
||||
error = ""
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: control.handler
|
||||
|
||||
function onVpnAdded() {
|
||||
if (!control.adding)
|
||||
return
|
||||
control.adding = false
|
||||
control.close()
|
||||
}
|
||||
|
||||
function onVpnAddFailed(message) {
|
||||
if (!control.adding)
|
||||
return
|
||||
control.adding = false
|
||||
control.error = message
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Escape"
|
||||
onActivated: control.close()
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: _fileDialog
|
||||
|
||||
property Item field
|
||||
|
||||
onAccepted: field.text = decodeURIComponent(selectedFile.toString().replace("file://", ""))
|
||||
}
|
||||
|
||||
component FileField: RowLayout {
|
||||
property alias field: _fileTextField
|
||||
property alias placeholderText: _fileTextField.placeholderText
|
||||
|
||||
signal choose()
|
||||
signal accepted()
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: Metrics.controlSpacing
|
||||
|
||||
FishUI.TextField {
|
||||
id: _fileTextField
|
||||
Layout.fillWidth: true
|
||||
onAccepted: parent.accepted()
|
||||
}
|
||||
|
||||
FishUI.Button {
|
||||
Layout.fillHeight: true
|
||||
text: qsTr("Choose…")
|
||||
onClicked: parent.choose()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: _mainLayout
|
||||
// Not filled to the window: the window height follows this layout's implicit
|
||||
// height a polish later, and a filled layout would be spread over the old height.
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Metrics.dialogPadding
|
||||
spacing: Metrics.dialogSpacing
|
||||
|
||||
FishUI.Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: Metrics.xxl
|
||||
text: control.title
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 14
|
||||
font.weight: Font.DemiBold
|
||||
color: FishUI.Theme.textColor
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: _form
|
||||
Layout.fillWidth: true
|
||||
columns: 2
|
||||
columnSpacing: Metrics.dialogSpacing
|
||||
rowSpacing: Metrics.formRowSpacing
|
||||
|
||||
// Hidden rows leave the grid, so the fields would move as they appear.
|
||||
readonly property real labelWidth: Math.max(_kindLabel.implicitWidth,
|
||||
_nameLabel.implicitWidth,
|
||||
_serverLabel.implicitWidth,
|
||||
_authenticationLabel.implicitWidth,
|
||||
_userNameLabel.implicitWidth,
|
||||
_passwordLabel.implicitWidth,
|
||||
_presharedKeyLabel.implicitWidth,
|
||||
_certificateLabel.implicitWidth,
|
||||
_keyLabel.implicitWidth,
|
||||
_keyPasswordLabel.implicitWidth,
|
||||
_caLabel.implicitWidth,
|
||||
_publicKeyLabel.implicitWidth,
|
||||
_privateKeyLabel.implicitWidth,
|
||||
_addressLabel.implicitWidth,
|
||||
_allowedIpsLabel.implicitWidth,
|
||||
_dnsLabel.implicitWidth)
|
||||
|
||||
FishUI.Label {
|
||||
id: _kindLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
text: qsTr("Type")
|
||||
}
|
||||
|
||||
FishUI.ComboBox {
|
||||
id: _kindBox
|
||||
Layout.fillWidth: true
|
||||
textRole: "text"
|
||||
valueRole: "kind"
|
||||
model: control.kinds
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillWidth: true
|
||||
visible: !control.installed && control.kind.length > 0
|
||||
text: qsTr("Install %1 to set up this kind of VPN.").arg(control.kindInfo.package ?? "")
|
||||
textFormat: Text.PlainText
|
||||
color: FishUI.Theme.disabledTextColor
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _nameLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.installed
|
||||
text: qsTr("Name")
|
||||
}
|
||||
|
||||
FishUI.TextField {
|
||||
id: _nameTextField
|
||||
Layout.fillWidth: true
|
||||
visible: control.installed
|
||||
placeholderText: qsTr("Optional")
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _serverLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.installed
|
||||
text: qsTr("Server")
|
||||
}
|
||||
|
||||
FishUI.TextField {
|
||||
id: _serverTextField
|
||||
Layout.fillWidth: true
|
||||
visible: control.installed
|
||||
placeholderText: qsTr("Required")
|
||||
inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase | Qt.ImhUrlCharactersOnly
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _authenticationLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.installed && control.kind === "openvpn"
|
||||
text: qsTr("Sign In With")
|
||||
}
|
||||
|
||||
FishUI.ComboBox {
|
||||
id: _authenticationBox
|
||||
Layout.fillWidth: true
|
||||
visible: control.installed && control.kind === "openvpn"
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
model: [
|
||||
{ text: qsTr("User Name and Password"), value: "password" },
|
||||
{ text: qsTr("Certificate"), value: "certificates" }
|
||||
]
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _userNameLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.usesUserName
|
||||
text: qsTr("User Name")
|
||||
}
|
||||
|
||||
FishUI.TextField {
|
||||
id: _userNameTextField
|
||||
Layout.fillWidth: true
|
||||
visible: control.usesUserName
|
||||
placeholderText: qsTr("Required")
|
||||
inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _passwordLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.usesUserName
|
||||
text: qsTr("Password")
|
||||
}
|
||||
|
||||
FishUI.TextField {
|
||||
id: _passwordTextField
|
||||
Layout.fillWidth: true
|
||||
visible: control.usesUserName
|
||||
placeholderText: qsTr("Ask when connecting")
|
||||
echoMode: TextInput.Password
|
||||
inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _presharedKeyLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.installed && control.kind === "l2tp"
|
||||
text: qsTr("Pre-shared Key")
|
||||
}
|
||||
|
||||
FishUI.TextField {
|
||||
id: _presharedKeyTextField
|
||||
Layout.fillWidth: true
|
||||
visible: control.installed && control.kind === "l2tp"
|
||||
placeholderText: qsTr("Optional")
|
||||
echoMode: TextInput.Password
|
||||
inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _certificateLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.certificates
|
||||
text: qsTr("Certificate")
|
||||
}
|
||||
|
||||
FileField {
|
||||
id: _certificateField
|
||||
visible: control.certificates
|
||||
placeholderText: qsTr("Required")
|
||||
onChoose: control.chooseFile(field, qsTr("Choose Your Certificate"))
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _keyLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.certificates
|
||||
text: qsTr("Private Key")
|
||||
}
|
||||
|
||||
FileField {
|
||||
id: _keyField
|
||||
visible: control.certificates
|
||||
placeholderText: qsTr("Required")
|
||||
onChoose: control.chooseFile(field, qsTr("Choose Your Private Key"))
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _keyPasswordLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.certificates
|
||||
text: qsTr("Key Password")
|
||||
}
|
||||
|
||||
FishUI.TextField {
|
||||
id: _keyPasswordTextField
|
||||
Layout.fillWidth: true
|
||||
visible: control.certificates
|
||||
placeholderText: qsTr("Optional")
|
||||
echoMode: TextInput.Password
|
||||
inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _caLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.installed && control.kind === "openvpn"
|
||||
text: qsTr("CA Certificate")
|
||||
}
|
||||
|
||||
FileField {
|
||||
id: _caField
|
||||
visible: control.installed && control.kind === "openvpn"
|
||||
placeholderText: qsTr("Optional")
|
||||
onChoose: control.chooseFile(field, qsTr("Choose the CA Certificate"))
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _publicKeyLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.wireGuard
|
||||
text: qsTr("Server Public Key")
|
||||
}
|
||||
|
||||
FishUI.TextField {
|
||||
id: _publicKeyTextField
|
||||
Layout.fillWidth: true
|
||||
visible: control.wireGuard
|
||||
placeholderText: qsTr("Required")
|
||||
inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _privateKeyLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.wireGuard
|
||||
text: qsTr("Private Key")
|
||||
}
|
||||
|
||||
FishUI.TextField {
|
||||
id: _privateKeyTextField
|
||||
Layout.fillWidth: true
|
||||
visible: control.wireGuard
|
||||
placeholderText: qsTr("Required")
|
||||
echoMode: TextInput.Password
|
||||
inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _addressLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.wireGuard
|
||||
text: qsTr("Address")
|
||||
}
|
||||
|
||||
FishUI.TextField {
|
||||
id: _addressTextField
|
||||
Layout.fillWidth: true
|
||||
visible: control.wireGuard
|
||||
placeholderText: "10.0.0.2/24"
|
||||
inputMethodHints: Qt.ImhNoPredictiveText
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _allowedIpsLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.wireGuard
|
||||
text: qsTr("Allowed IPs")
|
||||
}
|
||||
|
||||
FishUI.TextField {
|
||||
id: _allowedIpsTextField
|
||||
Layout.fillWidth: true
|
||||
visible: control.wireGuard
|
||||
placeholderText: qsTr("All traffic")
|
||||
inputMethodHints: Qt.ImhNoPredictiveText
|
||||
onAccepted: control.add()
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
id: _dnsLabel
|
||||
Layout.preferredWidth: _form.labelWidth
|
||||
visible: control.wireGuard
|
||||
text: qsTr("DNS")
|
||||
}
|
||||
|
||||
FishUI.TextField {
|
||||
id: _dnsTextField
|
||||
Layout.fillWidth: true
|
||||
visible: control.wireGuard
|
||||
placeholderText: qsTr("Optional")
|
||||
inputMethodHints: Qt.ImhNoPredictiveText
|
||||
onAccepted: control.add()
|
||||
}
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
Layout.fillWidth: true
|
||||
visible: control.error.length > 0
|
||||
text: control.error
|
||||
textFormat: Text.PlainText
|
||||
color: FishUI.Theme.redColor
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Metrics.dialogButtonSpacing
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
FishUI.ConfirmDialogButton {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: control.close()
|
||||
}
|
||||
|
||||
FishUI.ConfirmDialogButton {
|
||||
enabled: control.canAdd
|
||||
primary: true
|
||||
text: qsTr("Add")
|
||||
onClicked: control.add()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,139 @@
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Cutefish.Settings 1.0
|
||||
import FishUI 1.0 as FishUI
|
||||
import Cutefish.Network 1.0 as NM
|
||||
import "../"
|
||||
|
||||
// One VPN's type and state, and the way to remove it.
|
||||
FishUI.Window {
|
||||
id: control
|
||||
title: model.name
|
||||
|
||||
parentWindow: rootWindow
|
||||
x: parentWindow ? parentWindow.x + Math.round((parentWindow.width - width) / 2) : 0
|
||||
y: parentWindow ? parentWindow.y + Math.round((parentWindow.height - height) / 2) : 0
|
||||
width: 400
|
||||
property int contentHeight: detailsLayout.implicitHeight + header.height + 32
|
||||
height: contentHeight
|
||||
minimumWidth: width
|
||||
maximumWidth: width
|
||||
minimumHeight: contentHeight
|
||||
maximumHeight: contentHeight
|
||||
flags: Qt.Dialog | Qt.FramelessWindowHint
|
||||
modality: Qt.WindowModal
|
||||
visible: false
|
||||
minimizeButtonVisible: false
|
||||
header.height: 44
|
||||
contentTopMargin: header.height
|
||||
// The page colour, so the cards stand out as they do on a page.
|
||||
background.color: FishUI.Theme.backgroundColor
|
||||
// The background item reaches a grown window a frame or two after its buffer does.
|
||||
color: background.color
|
||||
|
||||
headerItem: FishUI.Label {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Metrics.dialogPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - 64
|
||||
text: control.title
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 14
|
||||
font.weight: Font.DemiBold
|
||||
color: FishUI.Theme.textColor
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
// Created per click, so each one goes away with its window.
|
||||
onClosing: {
|
||||
if (model.vpnUsesUserName)
|
||||
handler.setVpnUserName(model.connectionPath, userNameField.text)
|
||||
destroy()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (model.vpnUsesUserName && model.vpnUserName.length === 0)
|
||||
Qt.callLater(userNameField.forceActiveFocus)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: detailsLayout
|
||||
// Not filled to the window: the window height follows this layout's implicit
|
||||
// height a polish later, and a filled layout would be spread over the old height.
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Metrics.dialogPadding
|
||||
spacing: Metrics.dialogSpacing
|
||||
|
||||
ListCard {
|
||||
visible: model.vpnUsesUserName
|
||||
|
||||
ListRow {
|
||||
interactive: false
|
||||
text: qsTr("User Name")
|
||||
|
||||
FishUI.TextField {
|
||||
id: userNameField
|
||||
Layout.preferredWidth: 200
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
text: model.vpnUserName
|
||||
placeholderText: qsTr("Required")
|
||||
inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
|
||||
onAccepted: control.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListCard {
|
||||
InfoRow {
|
||||
text: qsTr("Type")
|
||||
value: model.vpnType
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
text: qsTr("Status")
|
||||
value: model.connectionState === NM.Enums.Activated ? qsTr("Connected")
|
||||
: model.connectionState === NM.Enums.Activating ? qsTr("Connecting…")
|
||||
: qsTr("Not connected")
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
visible: model.connectionState === NM.Enums.Activated && model.ipAddress.length > 0
|
||||
text: qsTr("IPv4 Address")
|
||||
value: model.ipAddress
|
||||
}
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
Layout.fillWidth: true
|
||||
visible: model.statusMessage.length > 0
|
||||
text: model.statusMessage
|
||||
textFormat: Text.PlainText
|
||||
color: FishUI.Theme.disabledTextColor
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Metrics.dialogButtonSpacing
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
FishUI.ConfirmDialogButton {
|
||||
text: qsTr("Close")
|
||||
onClicked: control.close()
|
||||
}
|
||||
|
||||
FishUI.ConfirmDialogButton {
|
||||
text: qsTr("Remove VPN")
|
||||
primary: true
|
||||
destructive: true
|
||||
onClicked: {
|
||||
handler.removeConnection(model.connectionPath)
|
||||
control.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,100 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import FishUI 1.0 as FishUI
|
||||
import Cutefish.Settings 1.0
|
||||
import Cutefish.Network 1.0 as NM
|
||||
import "../"
|
||||
|
||||
// One VPN: its switch connects and disconnects it; below its name, its type, or why it
|
||||
// can't connect.
|
||||
ListRow {
|
||||
id: control
|
||||
|
||||
readonly property bool busy: model.connectionState === NM.Enums.Activating
|
||||
|| model.connectionState === NM.Enums.Deactivating
|
||||
readonly property bool up: model.connectionState === NM.Enums.Activated
|
||||
|| model.connectionState === NM.Enums.Activating
|
||||
// Signing in takes a user name the imported file didn't have.
|
||||
readonly property bool needsUserName: model.vpnUsesUserName && model.vpnUserName.length === 0
|
||||
|
||||
Accessible.name: model.name
|
||||
onClicked: openDetails()
|
||||
|
||||
leading: FishUI.Icon {
|
||||
width: 18
|
||||
height: width
|
||||
name: "network-vpn"
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: Metrics.textSpacing
|
||||
|
||||
FishUI.Label {
|
||||
id: primaryLabel
|
||||
Layout.fillWidth: true
|
||||
text: model.name
|
||||
textFormat: Text.PlainText
|
||||
color: control.enabled ? FishUI.Theme.textColor : FishUI.Theme.disabledTextColor
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
FishUI.Label {
|
||||
Layout.fillWidth: true
|
||||
text: model.statusMessage.length > 0 ? model.statusMessage
|
||||
: model.connectionState === NM.Enums.Activating ? qsTr("Connecting…")
|
||||
: control.needsUserName ? qsTr("Add your user name to connect.")
|
||||
: model.vpnType
|
||||
textFormat: Text.PlainText
|
||||
color: model.statusMessage.length > 0 && model.canActivate ? FishUI.Theme.redColor
|
||||
: FishUI.Theme.disabledTextColor
|
||||
font.pixelSize: Math.max(1, primaryLabel.font.pixelSize - 1)
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
|
||||
FishUI.BusyIndicator {
|
||||
width: 18
|
||||
height: width
|
||||
visible: control.busy
|
||||
running: visible
|
||||
}
|
||||
|
||||
FishUI.Switch {
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
rightPadding: 0
|
||||
rightInset: 0
|
||||
enabled: model.canActivate || control.up
|
||||
checked: control.up
|
||||
onToggled: {
|
||||
if (checked && control.needsUserName)
|
||||
control.openDetails()
|
||||
else if (checked)
|
||||
handler.activateConnection(model.connectionPath, "", "")
|
||||
else
|
||||
handler.deactivateConnection(model.connectionPath, "")
|
||||
// The switch shows what NetworkManager reports, not what was asked for.
|
||||
checked = Qt.binding(function() { return control.up })
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
width: 20
|
||||
Layout.rightMargin: Metrics.trailingGlyphMargin(iconName, width)
|
||||
iconName: "info"
|
||||
iconColor: FishUI.Theme.highlightColor
|
||||
onClicked: control.openDetails()
|
||||
}
|
||||
|
||||
function openDetails() {
|
||||
var component = Qt.createComponent("VpnDetailsDialog.qml")
|
||||
if (component.status === Component.Ready) {
|
||||
var dialog = component.createObject(rootWindow, { "parentWindow": rootWindow })
|
||||
dialog.show()
|
||||
dialog.raise()
|
||||
dialog.requestActivate()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,76 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* nm-l2tp-service - L2TP VPN integration with NetworkManager
|
||||
*
|
||||
* Dan Williams <dcbw@redhat.com>
|
||||
*
|
||||
* Copyright 2008, 2014 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef NM_L2TP_SERVICE_DEFINES_H
|
||||
#define NM_L2TP_SERVICE_DEFINES_H
|
||||
|
||||
#define NM_DBUS_SERVICE_L2TP "org.freedesktop.NetworkManager.l2tp"
|
||||
|
||||
/* For the NM <-> VPN plugin service */
|
||||
#define NM_DBUS_INTERFACE_L2TP "org.freedesktop.NetworkManager.l2tp"
|
||||
#define NM_DBUS_PATH_L2TP "/org/freedesktop/NetworkManager/l2tp"
|
||||
|
||||
/* For the VPN plugin service <-> PPP plugin */
|
||||
#define NM_DBUS_INTERFACE_L2TP_PPP "org.freedesktop.NetworkManager.l2tp.ppp"
|
||||
#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_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_REFUSE_EAP "refuse-eap"
|
||||
#define NM_L2TP_KEY_REFUSE_PAP "refuse-pap"
|
||||
#define NM_L2TP_KEY_REFUSE_CHAP "refuse-chap"
|
||||
#define NM_L2TP_KEY_REFUSE_MSCHAP "refuse-mschap"
|
||||
#define NM_L2TP_KEY_REFUSE_MSCHAPV2 "refuse-mschapv2"
|
||||
#define NM_L2TP_KEY_REQUIRE_MPPE "require-mppe"
|
||||
#define NM_L2TP_KEY_REQUIRE_MPPE_40 "require-mppe-40"
|
||||
#define NM_L2TP_KEY_REQUIRE_MPPE_128 "require-mppe-128"
|
||||
#define NM_L2TP_KEY_MPPE_STATEFUL "mppe-stateful"
|
||||
#define NM_L2TP_KEY_NOBSDCOMP "nobsdcomp"
|
||||
#define NM_L2TP_KEY_NODEFLATE "nodeflate"
|
||||
#define NM_L2TP_KEY_NO_VJ_COMP "no-vj-comp"
|
||||
#define NM_L2TP_KEY_NO_PCOMP "nopcomp"
|
||||
#define NM_L2TP_KEY_NO_ACCOMP "noaccomp"
|
||||
#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_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 */
|
||||
@ -1,111 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* nm-openvpn-service - openvpn integration with NetworkManager
|
||||
*
|
||||
* Copyright (C) 2005 - 2008 Tim Niemueller <tim@niemueller.de>
|
||||
* Copyright (C) 2005 - 2008 Dan Williams <dcbw@redhat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NM_OPENVPN_SERVICE_H
|
||||
#define NM_OPENVPN_SERVICE_H
|
||||
|
||||
#define NM_DBUS_SERVICE_OPENVPN "org.freedesktop.NetworkManager.openvpn"
|
||||
#define NM_DBUS_INTERFACE_OPENVPN "org.freedesktop.NetworkManager.openvpn"
|
||||
#define NM_DBUS_PATH_OPENVPN "/org/freedesktop/NetworkManager/openvpn"
|
||||
|
||||
#define NM_OPENVPN_KEY_AUTH "auth"
|
||||
#define NM_OPENVPN_KEY_CA "ca"
|
||||
#define NM_OPENVPN_KEY_CERT "cert"
|
||||
#define NM_OPENVPN_KEY_CIPHER "cipher"
|
||||
#define NM_OPENVPN_KEY_KEYSIZE "keysize"
|
||||
#define NM_OPENVPN_KEY_COMP_LZO "comp-lzo"
|
||||
#define NM_OPENVPN_KEY_CONNECTION_TYPE "connection-type"
|
||||
#define NM_OPENVPN_KEY_FLOAT "float"
|
||||
#define NM_OPENVPN_KEY_FRAGMENT_SIZE "fragment-size"
|
||||
#define NM_OPENVPN_KEY_KEY "key"
|
||||
#define NM_OPENVPN_KEY_LOCAL_IP "local-ip" /* ??? */
|
||||
#define NM_OPENVPN_KEY_MSSFIX "mssfix"
|
||||
#define NM_OPENVPN_KEY_NS_CERT_TYPE "ns-cert-type"
|
||||
#define NM_OPENVPN_KEY_PING "ping"
|
||||
#define NM_OPENVPN_KEY_PING_EXIT "ping-exit"
|
||||
#define NM_OPENVPN_KEY_PING_RESTART "ping-restart"
|
||||
#define NM_OPENVPN_KEY_PORT "port"
|
||||
#define NM_OPENVPN_KEY_PROTO_TCP "proto-tcp"
|
||||
#define NM_OPENVPN_KEY_PROXY_TYPE "proxy-type"
|
||||
#define NM_OPENVPN_KEY_PROXY_SERVER "proxy-server"
|
||||
#define NM_OPENVPN_KEY_PROXY_PORT "proxy-port"
|
||||
#define NM_OPENVPN_KEY_PROXY_RETRY "proxy-retry"
|
||||
#define NM_OPENVPN_KEY_HTTP_PROXY_USERNAME "http-proxy-username"
|
||||
#define NM_OPENVPN_KEY_REMOTE "remote"
|
||||
#define NM_OPENVPN_KEY_REMOTE_RANDOM "remote-random"
|
||||
#define NM_OPENVPN_KEY_REMOTE_IP "remote-ip"
|
||||
#define NM_OPENVPN_KEY_STATIC_KEY "static-key"
|
||||
#define NM_OPENVPN_KEY_STATIC_KEY_DIRECTION "static-key-direction"
|
||||
#define NM_OPENVPN_KEY_TA "ta"
|
||||
#define NM_OPENVPN_KEY_TA_DIR "ta-dir"
|
||||
#define NM_OPENVPN_KEY_TUNNEL_MTU "tunnel-mtu"
|
||||
#define NM_OPENVPN_KEY_USERNAME "username"
|
||||
#define NM_OPENVPN_KEY_TAP_DEV "tap-dev"
|
||||
#define NM_OPENVPN_KEY_DEV "dev"
|
||||
#define NM_OPENVPN_KEY_DEV_TYPE "dev-type"
|
||||
#define NM_OPENVPN_KEY_TUN_IPV6 "tun-ipv6"
|
||||
#define NM_OPENVPN_KEY_TLS_CIPHER "tls-cipher"
|
||||
#define NM_OPENVPN_KEY_TLS_CRYPT "tls-crypt"
|
||||
#define NM_OPENVPN_KEY_TLS_REMOTE "tls-remote"
|
||||
#define NM_OPENVPN_KEY_VERIFY_X509_NAME "verify-x509-name"
|
||||
#define NM_OPENVPN_KEY_REMOTE_CERT_TLS "remote-cert-tls"
|
||||
#define NM_OPENVPN_KEY_MAX_ROUTES "max-routes"
|
||||
|
||||
#define NM_OPENVPN_KEY_PASSWORD "password"
|
||||
#define NM_OPENVPN_KEY_CERTPASS "cert-pass"
|
||||
#define NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD "http-proxy-password"
|
||||
/* Internal auth-dialog -> service token indicating that no secrets are
|
||||
* required for the connection.
|
||||
*/
|
||||
#define NM_OPENVPN_KEY_NOSECRET "no-secret"
|
||||
|
||||
#define NM_OPENVPN_KEY_RENEG_SECONDS "reneg-seconds"
|
||||
|
||||
#define NM_OPENVPN_AUTH_NONE "none"
|
||||
#define NM_OPENVPN_AUTH_RSA_MD4 "RSA-MD4"
|
||||
#define NM_OPENVPN_AUTH_MD5 "MD5"
|
||||
#define NM_OPENVPN_AUTH_SHA1 "SHA1"
|
||||
#define NM_OPENVPN_AUTH_SHA224 "SHA224"
|
||||
#define NM_OPENVPN_AUTH_SHA256 "SHA256"
|
||||
#define NM_OPENVPN_AUTH_SHA384 "SHA384"
|
||||
#define NM_OPENVPN_AUTH_SHA512 "SHA512"
|
||||
#define NM_OPENVPN_AUTH_RIPEMD160 "RIPEMD160"
|
||||
|
||||
#define NM_OPENVPN_CONTYPE_TLS "tls"
|
||||
#define NM_OPENVPN_CONTYPE_STATIC_KEY "static-key"
|
||||
#define NM_OPENVPN_CONTYPE_PASSWORD "password"
|
||||
#define NM_OPENVPN_CONTYPE_PASSWORD_TLS "password-tls"
|
||||
|
||||
/* arguments of "--remote-cert-tls" */
|
||||
#define NM_OPENVPN_REM_CERT_TLS_CLIENT "client"
|
||||
#define NM_OPENVPN_REM_CERT_TLS_SERVER "server"
|
||||
|
||||
/* arguments of "--ns-cert-type" */
|
||||
#define NM_OPENVPN_NS_CERT_TYPE_CLIENT "client"
|
||||
#define NM_OPENVPN_NS_CERT_TYPE_SERVER "server"
|
||||
|
||||
/* possible types for verify-x509-name */
|
||||
#define NM_OPENVPN_VERIFY_X509_NAME_TYPE_SUBJECT "subject"
|
||||
#define NM_OPENVPN_VERIFY_X509_NAME_TYPE_NAME "name"
|
||||
#define NM_OPENVPN_VERIFY_X509_NAME_TYPE_NAME_PREFIX "name-prefix"
|
||||
|
||||
#endif /* NM_OPENVPN_SERVICE_H */
|
||||
@ -1,56 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* nm-pptp-service - PPTP VPN integration with NetworkManager
|
||||
*
|
||||
* Dan Williams <dcbw@redhat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* (C) Copyright 2008 Red Hat, Inc. //krazy:exclude=copyright
|
||||
*/
|
||||
|
||||
#ifndef NM_PPTP_SERVICE_H
|
||||
#define NM_PPTP_SERVICE_H
|
||||
|
||||
#define NM_DBUS_SERVICE_PPTP_PPP "org.freedesktop.NetworkManager.pptp-ppp"
|
||||
#define NM_DBUS_PATH_PPTP_PPP "/org/freedesktop/NetworkManager/pptp/ppp"
|
||||
#define NM_DBUS_INTERFACE_PPTP_PPP "org.freedesktop.NetworkManager.pptp.ppp"
|
||||
|
||||
|
||||
/* For the NM <-> VPN plugin service */
|
||||
#define NM_DBUS_SERVICE_PPTP "org.freedesktop.NetworkManager.pptp"
|
||||
#define NM_DBUS_INTERFACE_PPTP "org.freedesktop.NetworkManager.pptp"
|
||||
#define NM_DBUS_PATH_PPTP "/org/freedesktop/NetworkManager/pptp"
|
||||
|
||||
#define NM_PPTP_KEY_GATEWAY "gateway"
|
||||
#define NM_PPTP_KEY_USER "user"
|
||||
#define NM_PPTP_KEY_PASSWORD "password"
|
||||
#define NM_PPTP_KEY_DOMAIN "domain"
|
||||
#define NM_PPTP_KEY_REFUSE_EAP "refuse-eap"
|
||||
#define NM_PPTP_KEY_REFUSE_PAP "refuse-pap"
|
||||
#define NM_PPTP_KEY_REFUSE_CHAP "refuse-chap"
|
||||
#define NM_PPTP_KEY_REFUSE_MSCHAP "refuse-mschap"
|
||||
#define NM_PPTP_KEY_REFUSE_MSCHAPV2 "refuse-mschapv2"
|
||||
#define NM_PPTP_KEY_REQUIRE_MPPE "require-mppe"
|
||||
#define NM_PPTP_KEY_REQUIRE_MPPE_40 "require-mppe-40"
|
||||
#define NM_PPTP_KEY_REQUIRE_MPPE_128 "require-mppe-128"
|
||||
#define NM_PPTP_KEY_MPPE_STATEFUL "mppe-stateful"
|
||||
#define NM_PPTP_KEY_NOBSDCOMP "nobsdcomp"
|
||||
#define NM_PPTP_KEY_NODEFLATE "nodeflate"
|
||||
#define NM_PPTP_KEY_NO_VJ_COMP "no-vj-comp"
|
||||
#define NM_PPTP_KEY_LCP_ECHO_FAILURE "lcp-echo-failure"
|
||||
#define NM_PPTP_KEY_LCP_ECHO_INTERVAL "lcp-echo-interval"
|
||||
|
||||
|
||||
#endif /* NM_PPTP_PLUGIN_H */
|
||||
@ -1,11 +0,0 @@
|
||||
#include "vpn.h"
|
||||
|
||||
VPN::VPN(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void VPN::activateVPNConnection(const QString &connection, const QString &device,
|
||||
const QString &specificObject)
|
||||
{
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
#ifndef VPN_H
|
||||
#define VPN_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class VPN : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VPN(QObject *parent = nullptr);
|
||||
|
||||
Q_INVOKABLE void activateVPNConnection(const QString &connection, const QString &device, const QString &specificParameter);
|
||||
};
|
||||
|
||||
#endif // VPN_H
|
||||
Loading…
Reference in New Issue