mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-18 17:24:01 -04:00
603 lines
22 KiB
QML
603 lines
22 KiB
QML
import QtQuick
|
|
import qs.Common
|
|
import qs.Modals.Common
|
|
import qs.Services
|
|
import qs.DCommon.Widgets
|
|
import qs.Widgets
|
|
|
|
DModal {
|
|
id: root
|
|
|
|
layerNamespace: "dms:wifi-password"
|
|
keepPopoutsOpen: true
|
|
allowStacking: true
|
|
shouldBeVisible: false
|
|
modalWidth: Math.min(Theme.dialogMaxWidth, screenWidth - Theme.spacingXL * 2)
|
|
modalHeight: Math.min(contentFocusScope.implicitHeight, screenHeight - Theme.spacingXL * 2)
|
|
enableShadow: true
|
|
onBackgroundClicked: clearAndClose()
|
|
directContent: contentFocusScope
|
|
|
|
property bool disablePopupTransparency: true
|
|
property string wifiPasswordSSID: ""
|
|
property string wifiPasswordInput: ""
|
|
property string wifiUsernameInput: ""
|
|
property bool requiresEnterprise: false
|
|
property bool isHiddenNetwork: false
|
|
|
|
property string wifiAnonymousIdentityInput: ""
|
|
property string wifiDomainInput: ""
|
|
property string eapMethodValue: "peap"
|
|
property string phase2AuthValue: "mschapv2"
|
|
|
|
property bool isPromptMode: false
|
|
property string promptToken: ""
|
|
property string promptReason: ""
|
|
property var promptFields: []
|
|
property var promptHints: []
|
|
property string promptSetting: ""
|
|
|
|
property bool isVpnPrompt: false
|
|
property string connectionName: ""
|
|
property string vpnServiceType: ""
|
|
property string connectionType: ""
|
|
property var fieldsInfo: []
|
|
property var secretValues: ({})
|
|
|
|
readonly property bool isCertificateChangedPrompt: promptReason === "server-certificate-changed"
|
|
readonly property bool isCertificatePrompt: promptReason === "server-certificate" || isCertificateChangedPrompt
|
|
readonly property string serverCertificateFingerprint: promptHints.length > 0 ? promptHints[0] : ""
|
|
readonly property bool showUsernameField: requiresEnterprise && !isVpnPrompt && fieldsInfo.length === 0
|
|
readonly property bool showPasswordField: fieldsInfo.length === 0 && !isCertificatePrompt
|
|
readonly property bool showEapFields: requiresEnterprise && !isVpnPrompt && !isPromptMode
|
|
readonly property bool showPhase2Dropdown: eapMethodValue === "peap" || eapMethodValue === "ttls"
|
|
readonly property bool showAnonField: requiresEnterprise && !isVpnPrompt && !isPromptMode && eapMethodValue !== "pwd"
|
|
readonly property bool showDomainField: showAnonField
|
|
readonly property bool showSavePasswordCheckbox: (isVpnPrompt || fieldsInfo.length > 0) && promptReason !== "pkcs11" && !isCertificatePrompt
|
|
|
|
readonly property int certificateWarningHeight: certificateWarningColumn.implicitHeight + Theme.spacingM * 2
|
|
|
|
function focusFirstField() {
|
|
if (isCertificatePrompt) {
|
|
connectButton.forceActiveFocus();
|
|
return;
|
|
}
|
|
if (fieldsInfo.length > 0) {
|
|
if (dynamicFieldsRepeater.count > 0) {
|
|
const firstItem = dynamicFieldsRepeater.itemAt(0);
|
|
if (firstItem)
|
|
firstItem.forceActiveFocus();
|
|
}
|
|
return;
|
|
}
|
|
if (isHiddenNetwork) {
|
|
ssidInput.forceActiveFocus();
|
|
return;
|
|
}
|
|
if (requiresEnterprise && !isVpnPrompt) {
|
|
usernameInput.forceActiveFocus();
|
|
return;
|
|
}
|
|
passwordInput.forceActiveFocus();
|
|
}
|
|
|
|
function show(ssid) {
|
|
wifiPasswordSSID = ssid;
|
|
wifiPasswordInput = "";
|
|
wifiUsernameInput = "";
|
|
wifiAnonymousIdentityInput = "";
|
|
wifiDomainInput = "";
|
|
eapMethodValue = "peap";
|
|
phase2AuthValue = "mschapv2";
|
|
isPromptMode = false;
|
|
isHiddenNetwork = false;
|
|
promptToken = "";
|
|
promptReason = "";
|
|
promptFields = [];
|
|
promptHints = [];
|
|
promptSetting = "";
|
|
isVpnPrompt = false;
|
|
connectionName = "";
|
|
vpnServiceType = "";
|
|
connectionType = "";
|
|
fieldsInfo = [];
|
|
secretValues = {};
|
|
|
|
const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid);
|
|
requiresEnterprise = network?.enterprise || false;
|
|
|
|
open();
|
|
Qt.callLater(focusFirstField);
|
|
}
|
|
|
|
function showHidden() {
|
|
wifiPasswordSSID = "";
|
|
wifiPasswordInput = "";
|
|
wifiUsernameInput = "";
|
|
wifiAnonymousIdentityInput = "";
|
|
wifiDomainInput = "";
|
|
eapMethodValue = "peap";
|
|
phase2AuthValue = "mschapv2";
|
|
isPromptMode = false;
|
|
isHiddenNetwork = true;
|
|
promptToken = "";
|
|
promptReason = "";
|
|
promptFields = [];
|
|
promptHints = [];
|
|
promptSetting = "";
|
|
isVpnPrompt = false;
|
|
connectionName = "";
|
|
vpnServiceType = "";
|
|
connectionType = "";
|
|
fieldsInfo = [];
|
|
secretValues = {};
|
|
requiresEnterprise = false;
|
|
|
|
open();
|
|
Qt.callLater(focusFirstField);
|
|
}
|
|
|
|
function showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService, fInfo) {
|
|
isPromptMode = true;
|
|
promptToken = token;
|
|
promptReason = reason;
|
|
promptFields = fields || [];
|
|
promptHints = hints || [];
|
|
promptSetting = setting || "802-11-wireless-security";
|
|
connectionType = connType || "802-11-wireless";
|
|
connectionName = connName || ssid || "";
|
|
vpnServiceType = vpnService || "";
|
|
fieldsInfo = fInfo || [];
|
|
secretValues = {};
|
|
|
|
isVpnPrompt = (connectionType === "vpn" || connectionType === "wireguard");
|
|
wifiPasswordSSID = isVpnPrompt ? connectionName : ssid;
|
|
savePasswordCheckbox.checked = !isVpnPrompt;
|
|
|
|
requiresEnterprise = setting === "802-1x";
|
|
|
|
wifiPasswordInput = "";
|
|
wifiUsernameInput = "";
|
|
wifiAnonymousIdentityInput = "";
|
|
wifiDomainInput = "";
|
|
eapMethodValue = "peap";
|
|
phase2AuthValue = "mschapv2";
|
|
|
|
open();
|
|
Qt.callLater(() => {
|
|
if (reason === "wrong-password")
|
|
shakeSecretFields();
|
|
focusFirstField();
|
|
});
|
|
}
|
|
|
|
function hide() {
|
|
close();
|
|
}
|
|
|
|
function shakeSecretFields() {
|
|
if (fieldsInfo.length === 0) {
|
|
passwordInput.text = "";
|
|
passwordInput.shake();
|
|
return;
|
|
}
|
|
for (var i = 0; i < dynamicFieldsRepeater.count; i++) {
|
|
const item = dynamicFieldsRepeater.itemAt(i);
|
|
if (item?.morph)
|
|
item.shake();
|
|
}
|
|
}
|
|
|
|
function getFieldLabel(fieldName) {
|
|
switch (fieldName) {
|
|
case "username":
|
|
case "identity":
|
|
return I18n.tr("Username");
|
|
case "password":
|
|
return I18n.tr("Password");
|
|
case "cert-pass":
|
|
case "certpass":
|
|
return I18n.tr("Certificate Password");
|
|
case "private-key-password":
|
|
return I18n.tr("Private Key Password");
|
|
case "pin":
|
|
case "key_pass":
|
|
return I18n.tr("PIN", "noun, numeric personal identification number for a smart card");
|
|
case "psk":
|
|
return I18n.tr("Password");
|
|
case "anonymous-identity":
|
|
return I18n.tr("Anonymous Identity");
|
|
default:
|
|
return fieldName.charAt(0).toUpperCase() + fieldName.slice(1).replace(/-/g, " ");
|
|
}
|
|
}
|
|
|
|
function submitCredentialsAndClose() {
|
|
if (!connectButton.enabled)
|
|
return;
|
|
if (fieldsInfo.length > 0) {
|
|
NetworkService.submitCredentials(promptToken, secretValues, savePasswordCheckbox.checked);
|
|
hide();
|
|
secretValues = {};
|
|
return;
|
|
}
|
|
|
|
if (isPromptMode) {
|
|
const secrets = {};
|
|
if (isVpnPrompt) {
|
|
if (passwordInput.text)
|
|
secrets["password"] = passwordInput.text;
|
|
} else if (promptSetting === "802-11-wireless-security") {
|
|
secrets["psk"] = passwordInput.text;
|
|
} else if (promptSetting === "802-1x") {
|
|
if (usernameInput.text)
|
|
secrets["identity"] = usernameInput.text;
|
|
if (passwordInput.text)
|
|
secrets["password"] = passwordInput.text;
|
|
if (wifiAnonymousIdentityInput)
|
|
secrets["anonymous-identity"] = wifiAnonymousIdentityInput;
|
|
}
|
|
NetworkService.submitCredentials(promptToken, secrets, savePasswordCheckbox.checked);
|
|
} else {
|
|
const ssid = isHiddenNetwork ? ssidInput.text : wifiPasswordSSID;
|
|
const username = requiresEnterprise ? usernameInput.text : "";
|
|
const anonIdentity = showAnonField ? wifiAnonymousIdentityInput : "";
|
|
const domainMatch = showDomainField ? wifiDomainInput : "";
|
|
const eap = requiresEnterprise ? eapMethodValue : "";
|
|
const phase2 = requiresEnterprise && showPhase2Dropdown ? phase2AuthValue : "";
|
|
NetworkService.connectToWifi(ssid, passwordInput.text, username, anonIdentity, domainMatch, isHiddenNetwork, eap, phase2);
|
|
}
|
|
|
|
hide();
|
|
wifiPasswordInput = "";
|
|
wifiUsernameInput = "";
|
|
wifiAnonymousIdentityInput = "";
|
|
wifiDomainInput = "";
|
|
passwordInput.text = "";
|
|
if (requiresEnterprise)
|
|
usernameInput.text = "";
|
|
if (isHiddenNetwork)
|
|
ssidInput.text = "";
|
|
}
|
|
|
|
function clearAndClose() {
|
|
if (isPromptMode)
|
|
NetworkService.cancelCredentials(promptToken);
|
|
hide();
|
|
wifiPasswordInput = "";
|
|
wifiUsernameInput = "";
|
|
wifiAnonymousIdentityInput = "";
|
|
wifiDomainInput = "";
|
|
secretValues = {};
|
|
}
|
|
|
|
onShouldBeVisibleChanged: {
|
|
if (shouldBeVisible) {
|
|
Qt.callLater(focusFirstField);
|
|
return;
|
|
}
|
|
wifiPasswordInput = "";
|
|
wifiUsernameInput = "";
|
|
wifiAnonymousIdentityInput = "";
|
|
wifiDomainInput = "";
|
|
secretValues = {};
|
|
passwordInput.text = "";
|
|
usernameInput.text = "";
|
|
anonInput.text = "";
|
|
domainMatchInput.text = "";
|
|
ssidInput.text = "";
|
|
for (var i = 0; i < dynamicFieldsRepeater.count; i++) {
|
|
const item = dynamicFieldsRepeater.itemAt(i);
|
|
if (item)
|
|
item.text = "";
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: NetworkService
|
|
|
|
function onPasswordDialogShouldReopenChanged() {
|
|
if (!NetworkService.passwordDialogShouldReopen || NetworkService.connectingSSID === "")
|
|
return;
|
|
wifiPasswordSSID = NetworkService.connectingSSID;
|
|
wifiPasswordInput = "";
|
|
open();
|
|
NetworkService.passwordDialogShouldReopen = false;
|
|
}
|
|
}
|
|
|
|
DDialog {
|
|
id: contentFocusScope
|
|
|
|
anchors.fill: parent
|
|
focus: root.shouldBeVisible
|
|
acceptEnabled: connectButton.enabled
|
|
onAccepted: submitCredentialsAndClose()
|
|
onRejected: clearAndClose()
|
|
title: {
|
|
if (promptReason === "pkcs11")
|
|
return I18n.tr("Smartcard Authentication");
|
|
if (isCertificatePrompt)
|
|
return I18n.tr("Untrusted VPN certificate", "Title for VPN server certificate trust confirmation");
|
|
if (isVpnPrompt)
|
|
return I18n.tr("Connect to VPN");
|
|
if (isHiddenNetwork)
|
|
return I18n.tr("Connect to Hidden Network");
|
|
return I18n.tr("Connect to Wi-Fi");
|
|
}
|
|
supportingText: {
|
|
if (promptReason === "pkcs11")
|
|
return I18n.tr("Enter PIN for ") + wifiPasswordSSID;
|
|
if (isCertificatePrompt)
|
|
return wifiPasswordSSID;
|
|
if (fieldsInfo.length > 0)
|
|
return I18n.tr("Enter credentials for ") + wifiPasswordSSID;
|
|
if (isVpnPrompt)
|
|
return I18n.tr("Enter password for ") + wifiPasswordSSID;
|
|
if (isHiddenNetwork)
|
|
return I18n.tr("Enter network name and password");
|
|
return (requiresEnterprise ? I18n.tr("Enter credentials for ") : I18n.tr("Enter password for ")) + wifiPasswordSSID;
|
|
}
|
|
|
|
Rectangle {
|
|
id: certificateWarningBox
|
|
|
|
readonly property color warningTone: isCertificateChangedPrompt ? Theme.error : Theme.warning
|
|
|
|
width: parent.width
|
|
height: certificateWarningHeight
|
|
radius: Theme.cornerRadius
|
|
color: Theme.withAlpha(warningTone, 0.12)
|
|
border.color: Theme.withAlpha(warningTone, 0.5)
|
|
border.width: 1
|
|
visible: isCertificatePrompt
|
|
|
|
Column {
|
|
id: certificateWarningColumn
|
|
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.spacingM
|
|
spacing: Theme.spacingS
|
|
|
|
StyledText {
|
|
width: parent.width
|
|
text: isCertificateChangedPrompt ? I18n.tr("The server certificate has changed since it was last trusted. Only continue if you recognize the new fingerprint.", "Warning shown when a trusted VPN server certificate no longer matches") : I18n.tr("Only continue if you recognize this server certificate fingerprint.", "Warning shown before trusting an unverified VPN server certificate")
|
|
wrapMode: Text.Wrap
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceText
|
|
}
|
|
|
|
StyledText {
|
|
width: parent.width
|
|
text: serverCertificateFingerprint
|
|
wrapMode: Text.WrapAnywhere
|
|
font.family: SettingsData.monoFontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: certificateWarningBox.warningTone
|
|
}
|
|
}
|
|
}
|
|
|
|
DTextField {
|
|
id: ssidInput
|
|
visible: isHiddenNetwork
|
|
expressive: true
|
|
leftIconName: "wifi"
|
|
|
|
width: parent.width
|
|
labelText: I18n.tr("Network Name (SSID)")
|
|
enabled: root.shouldBeVisible
|
|
|
|
onAccepted: passwordInput.forceActiveFocus()
|
|
}
|
|
|
|
Repeater {
|
|
id: dynamicFieldsRepeater
|
|
model: fieldsInfo
|
|
|
|
delegate: DTextField {
|
|
id: fieldInput
|
|
required property var modelData
|
|
required property int index
|
|
expressive: true
|
|
morph: modelData.isSecret
|
|
leftIconName: modelData.isSecret ? "lock" : "person"
|
|
width: contentFocusScope.contentItem.width
|
|
showPasswordToggle: modelData.isSecret
|
|
isError: modelData.isSecret && isPromptMode && promptReason === "wrong-password" && text.length === 0
|
|
supportingText: isError ? I18n.tr("Incorrect password") : ""
|
|
echoMode: modelData.isSecret && !passwordVisible ? TextInput.Password : TextInput.Normal
|
|
labelText: getFieldLabel(modelData.name)
|
|
enabled: root.shouldBeVisible
|
|
|
|
onTextEdited: {
|
|
let updated = Object.assign({}, root.secretValues);
|
|
updated[modelData.name] = text;
|
|
root.secretValues = updated;
|
|
}
|
|
|
|
onAccepted: {
|
|
if (index < fieldsInfo.length - 1) {
|
|
const nextItem = dynamicFieldsRepeater.itemAt(index + 1);
|
|
if (nextItem)
|
|
nextItem.forceActiveFocus();
|
|
return;
|
|
}
|
|
submitCredentialsAndClose();
|
|
}
|
|
}
|
|
}
|
|
|
|
Row {
|
|
id: eapSelectorRow
|
|
|
|
visible: showEapFields
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
|
|
Column {
|
|
width: showPhase2Dropdown ? (parent.width - Theme.spacingM) / 2 : parent.width
|
|
spacing: Theme.spacingXS
|
|
|
|
StyledText {
|
|
text: I18n.tr("Authentication")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
}
|
|
|
|
DDropdown {
|
|
width: parent.width
|
|
dropdownWidth: parent.width
|
|
compactMode: true
|
|
options: ["PEAP", "TTLS", "PWD"]
|
|
currentValue: eapMethodValue.toUpperCase()
|
|
onValueChanged: value => {
|
|
eapMethodValue = value.toLowerCase();
|
|
phase2AuthValue = eapMethodValue === "ttls" ? "pap" : "mschapv2";
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
visible: showPhase2Dropdown
|
|
width: (parent.width - Theme.spacingM) / 2
|
|
spacing: Theme.spacingXS
|
|
|
|
StyledText {
|
|
text: I18n.tr("Inner authentication", "802.1X phase 2 authentication method")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
}
|
|
|
|
DDropdown {
|
|
width: parent.width
|
|
dropdownWidth: parent.width
|
|
compactMode: true
|
|
options: eapMethodValue === "ttls" ? ["PAP", "MSCHAPv2", "MSCHAP", "CHAP", "GTC", "MD5"] : ["MSCHAPv2", "GTC", "MD5"]
|
|
currentValue: phase2AuthValue === "mschapv2" ? "MSCHAPv2" : phase2AuthValue.toUpperCase()
|
|
onValueChanged: value => phase2AuthValue = value.toLowerCase()
|
|
}
|
|
}
|
|
}
|
|
|
|
DTextField {
|
|
id: usernameInput
|
|
visible: showUsernameField
|
|
expressive: true
|
|
leftIconName: "person"
|
|
|
|
width: parent.width
|
|
text: wifiUsernameInput
|
|
labelText: I18n.tr("Username", "text field label for network, vpn and account forms")
|
|
enabled: root.shouldBeVisible
|
|
|
|
onTextEdited: wifiUsernameInput = text
|
|
onAccepted: passwordInput.forceActiveFocus()
|
|
}
|
|
|
|
DTextField {
|
|
id: passwordInput
|
|
visible: showPasswordField
|
|
expressive: true
|
|
morph: true
|
|
leftIconName: "lock"
|
|
|
|
width: parent.width
|
|
text: wifiPasswordInput
|
|
showPasswordToggle: true
|
|
isError: isPromptMode && promptReason === "wrong-password" && text.length === 0
|
|
supportingText: isError ? I18n.tr("Incorrect password") : ""
|
|
echoMode: passwordVisible ? TextInput.Normal : TextInput.Password
|
|
labelText: promptReason === "pkcs11" ? I18n.tr("PIN", "noun, numeric personal identification number for a smart card") : I18n.tr("Password")
|
|
enabled: root.shouldBeVisible
|
|
|
|
onTextEdited: wifiPasswordInput = text
|
|
onAccepted: {
|
|
if (showAnonField) {
|
|
anonInput.forceActiveFocus();
|
|
return;
|
|
}
|
|
submitCredentialsAndClose();
|
|
}
|
|
}
|
|
|
|
DTextField {
|
|
id: anonInput
|
|
visible: showAnonField
|
|
expressive: true
|
|
leftIconName: "person_off"
|
|
|
|
width: parent.width
|
|
text: wifiAnonymousIdentityInput
|
|
labelText: I18n.tr("Anonymous Identity (optional)")
|
|
enabled: root.shouldBeVisible
|
|
|
|
onTextEdited: wifiAnonymousIdentityInput = text
|
|
onAccepted: domainMatchInput.forceActiveFocus()
|
|
}
|
|
|
|
DTextField {
|
|
id: domainMatchInput
|
|
visible: showDomainField
|
|
expressive: true
|
|
leftIconName: "domain"
|
|
|
|
width: parent.width
|
|
text: wifiDomainInput
|
|
labelText: I18n.tr("Domain (optional)")
|
|
enabled: root.shouldBeVisible
|
|
|
|
onTextEdited: wifiDomainInput = text
|
|
onAccepted: submitCredentialsAndClose()
|
|
}
|
|
|
|
DToggle {
|
|
id: savePasswordCheckbox
|
|
|
|
width: parent.width
|
|
text: I18n.tr("Save password")
|
|
visible: showSavePasswordCheckbox
|
|
checked: !isVpnPrompt
|
|
onToggled: checked => savePasswordCheckbox.checked = checked
|
|
}
|
|
|
|
actions: [
|
|
DButton {
|
|
maximumWidth: contentFocusScope.actionWidth
|
|
wrapText: true
|
|
text: I18n.tr("Cancel")
|
|
backgroundColor: "transparent"
|
|
textColor: Theme.primary
|
|
onClicked: clearAndClose()
|
|
},
|
|
DButton {
|
|
id: connectButton
|
|
maximumWidth: contentFocusScope.actionWidth
|
|
wrapText: true
|
|
|
|
text: isCertificatePrompt ? I18n.tr("Trust", "Button that approves a VPN server certificate fingerprint") : I18n.tr("Connect", "verb, connect to a network or device")
|
|
enabled: {
|
|
if (fieldsInfo.length > 0) {
|
|
for (var i = 0; i < fieldsInfo.length; i++) {
|
|
if (!fieldsInfo[i].isSecret)
|
|
continue;
|
|
const fieldName = fieldsInfo[i].name;
|
|
if (!secretValues[fieldName] || secretValues[fieldName].length === 0)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
if (isCertificatePrompt)
|
|
return serverCertificateFingerprint.length > 0;
|
|
if (isVpnPrompt)
|
|
return passwordInput.text.length > 0;
|
|
if (isHiddenNetwork)
|
|
return ssidInput.text.length > 0;
|
|
return requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0;
|
|
}
|
|
onClicked: submitCredentialsAndClose()
|
|
}
|
|
]
|
|
}
|
|
}
|