add system vpn

Signed-off-by: xiaohui.xie <xiexiaohui15@h-partners.com>
This commit is contained in:
xiaohui.xie 2024-07-09 14:25:18 +08:00
parent 6d48d4dfb3
commit d020d021ec
52 changed files with 4337 additions and 34 deletions

View File

@ -4,7 +4,7 @@
{
"name": "default",
"signingConfig": "release",
"compileSdkVersion": 11,
"compileSdkVersion": 12,
"compatibleSdkVersion": 10
}
]

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Copyright (c) 2021-2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@ -38,6 +38,14 @@ export default class MainAbility extends Ability {
let url = 'pages/settingList';
if (this.funcAbilityWant?.parameters?.router && this.funcAbilityWant.parameters.router === 'volumeControl') {
url = 'pages/volumeControl';
} else if (this.funcAbilityWant?.uri === "wifi") {
url = 'pages/wifi';
} else if (this.funcAbilityWant?.uri === "bluetooth") {
url = 'pages/bluetooth';
} else if (this.funcAbilityWant?.uri === "volumeControl") {
url = 'pages/volumeControl';
} else if (this.funcAbilityWant?.uri === "locationServices") {
url = 'pages/locationServices';
}
windowStage.setUIContent(this.context, url, null);
GlobalContext.getContext().setObject(GlobalContext.globalKeySettingsAbilityContext, this.context);

View File

@ -0,0 +1,250 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import common from '@ohos.app.ability.common';
import HashMap from '@ohos.util.HashMap';
import { util } from '@kit.ArkTS';
import { IpsecVpnConfig } from './VpnConfig';
import { VpnConfigModel } from './VpnConfigModel';
import { VpnTypeModel } from '../../model/vpnImpl/VpnTypeModel';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
const MODULE_TAG: string = 'setting_vpn:SwanCtlModel:';
//param
const KEY_VPN_ADDRESS: string = 'vpn_address_value';
const KEY_VPN_USERNAME: string = 'vpn_username_value';
const KEY_VPN_IPSEC_IDENTIFIER: string = 'vpn_ipsec_identifier_value';
const KEY_VPN_PASSWORD: string = 'vpn_password_value';
const KEY_VPN_IPSEC_SHAREDKEY: string = 'vpn_ipsec_sharedKey_value';
//file path
const SWANCTL_IKE2_IPSEC_MSCHAPV2_PATH: string = 'vpn/ike2_ipsec_mschapv2/swanctl.conf';
const STRONGSWAN_IKE2_IPSEC_MSCHAPV2_PATH: string = 'vpn/ike2_ipsec_mschapv2/strongswan.conf';
const SWANCTL_IKE2_IPSEC_RSA_PATH: string = 'vpn/ike2_ipsec_rsa/swanctl.conf';
const STRONGSWAN_IKE2_IPSEC_RSA_PATH: string = 'vpn/ike2_ipsec_rsa/strongswan.conf';
const SWANCTL_IKE2_IPSEC_PSK_PATH: string = 'vpn/ike2_ipsec_psk/swanctl.conf';
const STRONGSWAN_IKE2_IPSEC_PSK_PATH: string = 'vpn/ike2_ipsec_psk/strongswan.conf';
const SWANCTL_IPSEC_XAUTH_PSK_PATH: string = 'vpn/ipsec_xauth_psk/swanctl.conf';
const STRONGSWAN_IPSEC_XAUTH_PSK_PATH: string = 'vpn/ipsec_xauth_psk/strongswan.conf';
const SWANCTL_IPSEC_XAUTH_RSA_PATH: string = 'vpn/ipsec_xauth_rsa/swanctl.conf';
const STRONGSWAN_IPSEC_XAUTH_RSA_PATH: string = 'vpn/ipsec_xauth_rsa/strongswan.conf';
const STRONGSWAN_L2TP_IPSEC_RSA_PATH: string = 'vpn/l2tp_ipsec_rsa/strongswan.conf';
const STRONGSWAN_L2TP_IPSEC_PSK_PATH: string = 'vpn/l2tp_ipsec_psk/strongswan.conf';
const CLIENT_L2TP_IPSEC_PSK_PATH: string = 'vpn/l2tp_ipsec_psk/options.l2tpd.client.conf';
const CLIENT_L2TP_IPSEC_RSA_PATH: string = 'vpn/l2tp_ipsec_rsa/options.l2tpd.client.conf';
const XL2TPD_L2TP_IPSEC_PSK_PATH: string = 'vpn/l2tp_ipsec_psk/xl2tpd.conf';
const XL2TPD_L2TP_IPSEC_RSA_PATH: string = 'vpn/l2tp_ipsec_rsa/xl2tpd.conf';
const IPSECCONF_L2TP_IPSEC_PSK_PATH: string = 'vpn/l2tp_ipsec_psk/ipsec.conf';
const IPSECCONF_L2TP_IPSEC_RSA_PATH: string = 'vpn/l2tp_ipsec_rsa/ipsec.conf';
const IPSEC_SECRETS_L2TP_IPSEC_PSK_PATH: string = 'vpn/l2tp_ipsec_psk/ipsec.secrets.conf';
const IPSEC_SECRETS_L2TP_IPSEC_RSA_PATH: string = 'vpn/l2tp_ipsec_rsa/ipsec.secrets.conf';
const SWANCTL_IPSEC_HYBRID_RSA_PATH: string = 'vpn/ipsec_hybrid_rsa/swanctl.conf';
const STRONGSWAN_IPSEC_HYBRID_RSA_PATH: string = 'vpn/ipsec_hybrid_rsa/strongswan.conf';
export class SwanCtlModel {
private context: common.UIAbilityContext = undefined;
private swanCtlMap = new HashMap();
private strongSwanMap = new HashMap();
private l2tpdClientMap = new HashMap();
private xl2tpdMap = new HashMap();
private ipsecConfMap = new HashMap();
private ipsecSecretsMap = new HashMap();
private static instance: SwanCtlModel;
public static getInstance(): SwanCtlModel {
if (!this.instance) {
this.instance = new SwanCtlModel();
}
return this.instance;
}
public async init(context: common.UIAbilityContext): Promise<void> {
this.context = context;
this.readTemplate();
}
public buildConfig(ipsec: IpsecVpnConfig): IpsecVpnConfig {
let helper: util.Base64Helper = new util.Base64Helper();
if (ipsec.vpnType !== VpnTypeModel.TYPE_L2TP_IPSEC_PSK && ipsec.vpnType !== VpnTypeModel.TYPE_L2TP_IPSEC_RSA) {
ipsec.swanctlConfig = helper.encodeToStringSync(this.ipsec2swanCtl(ipsec));
}
ipsec.strongSwanConfig = helper.encodeToStringSync(this.ipsec2strongSwan(ipsec));
if (ipsec.vpnType === VpnTypeModel.TYPE_L2TP_IPSEC_PSK || ipsec.vpnType === VpnTypeModel.TYPE_L2TP_IPSEC_RSA) {
ipsec.optionsL2tpdClient = helper.encodeToStringSync(this.ipsec2L2tpdClient(ipsec));
ipsec.xl2tpdConfig = helper.encodeToStringSync(this.ipsec2Xl2tpd(ipsec));
ipsec.ipsecConfig = helper.encodeToStringSync(this.ipsec2IpsecConf(ipsec));
ipsec.ipsecSecrets = helper.encodeToStringSync(this.ipsec2IpsecSecrets(ipsec));
}
return ipsec;
}
private ipsec2swanCtl(ipsec: IpsecVpnConfig): Uint8Array {
if (this.swanCtlMap.isEmpty()) {
this.readTemplate();
}
let template = this.swanCtlMap.get(ipsec.vpnType);
let vpnTemplate = String(template);
vpnTemplate = this.replaceConfigParam(vpnTemplate, KEY_VPN_ADDRESS, VpnConfigModel.getInstance().getAddress(ipsec));
vpnTemplate = this.replaceConfigParam(vpnTemplate, KEY_VPN_IPSEC_IDENTIFIER, ipsec.ipsecIdentifier);
vpnTemplate = this.replaceConfigParam(vpnTemplate, KEY_VPN_IPSEC_SHAREDKEY, ipsec.ipsecPreSharedKey);
vpnTemplate = this.replaceConfigParam(vpnTemplate, KEY_VPN_USERNAME, ipsec.userName);
vpnTemplate = this.replaceConfigParam(vpnTemplate, KEY_VPN_PASSWORD, ipsec.password);
let swanCtl = this.stringToUint8Array(vpnTemplate);
return swanCtl;
}
private ipsec2strongSwan(ipsec: IpsecVpnConfig): Uint8Array {
if (this.strongSwanMap.isEmpty()) {
this.readTemplate();
}
let template = this.strongSwanMap.get(ipsec.vpnType);
let strongSwan = this.stringToUint8Array(String(template));
return strongSwan;
}
private ipsec2Xl2tpd(ipsec: IpsecVpnConfig): Uint8Array {
if (this.xl2tpdMap.isEmpty()) {
this.readTemplate();
}
let template = this.xl2tpdMap.get(ipsec.vpnType);
let vpnTemplate = String(template);
vpnTemplate = this.replaceConfigParam(vpnTemplate, KEY_VPN_ADDRESS, VpnConfigModel.getInstance().getAddress(ipsec));
let xl2tpd = this.stringToUint8Array(vpnTemplate);
return xl2tpd;
}
private ipsec2L2tpdClient(ipsec: IpsecVpnConfig): Uint8Array {
if (this.l2tpdClientMap.isEmpty()) {
this.readTemplate();
}
let template = this.l2tpdClientMap.get(ipsec.vpnType);
let vpnTemplate = String(template);
vpnTemplate = this.replaceConfigParam(vpnTemplate, KEY_VPN_USERNAME, ipsec.userName);
vpnTemplate = this.replaceConfigParam(vpnTemplate, KEY_VPN_PASSWORD, ipsec.password);
let xl2tpd = this.stringToUint8Array(vpnTemplate);
return xl2tpd;
}
private ipsec2IpsecConf(ipsec: IpsecVpnConfig): Uint8Array {
if (this.ipsecConfMap.isEmpty()) {
this.readTemplate();
}
let template = this.ipsecConfMap.get(ipsec.vpnType);
let vpnTemplate = String(template);
vpnTemplate = this.replaceConfigParam(vpnTemplate, KEY_VPN_ADDRESS, VpnConfigModel.getInstance().getAddress(ipsec));
let ipsecConf = this.stringToUint8Array(vpnTemplate);
return ipsecConf;
}
private ipsec2IpsecSecrets(ipsec: IpsecVpnConfig): Uint8Array {
if (this.ipsecSecretsMap.isEmpty()) {
this.readTemplate();
}
let template = this.ipsecSecretsMap.get(ipsec.vpnType);
let vpnTemplate = String(template);
vpnTemplate = this.replaceConfigParam(vpnTemplate, KEY_VPN_IPSEC_SHAREDKEY, ipsec.ipsecPreSharedKey);
let ipsecSecrets = this.stringToUint8Array(vpnTemplate);
return ipsecSecrets;
}
private readTemplate(): void {
//swanctl.config
this.swanCtlMap.set(VpnTypeModel.TYPE_IKEV2_IPSEC_MSCHAPv2, SWANCTL_IKE2_IPSEC_MSCHAPV2_PATH);
this.swanCtlMap.set(VpnTypeModel.TYPE_IKEV2_IPSEC_RSA, SWANCTL_IKE2_IPSEC_RSA_PATH);
this.swanCtlMap.set(VpnTypeModel.TYPE_IKEV2_IPSEC_PSK, SWANCTL_IKE2_IPSEC_PSK_PATH);
this.swanCtlMap.set(VpnTypeModel.TYPE_IPSEC_XAUTH_PSK, SWANCTL_IPSEC_XAUTH_PSK_PATH);
this.swanCtlMap.set(VpnTypeModel.TYPE_IPSEC_XAUTH_RSA, SWANCTL_IPSEC_XAUTH_RSA_PATH);
this.swanCtlMap.set(VpnTypeModel.TYPE_IPSEC_HYBRID_RSA, SWANCTL_IPSEC_HYBRID_RSA_PATH);
this.swanCtlMap = this.readRawFile(this.swanCtlMap);
//strongSwan.config
this.strongSwanMap.set(VpnTypeModel.TYPE_IKEV2_IPSEC_MSCHAPv2, STRONGSWAN_IKE2_IPSEC_MSCHAPV2_PATH);
this.strongSwanMap.set(VpnTypeModel.TYPE_IKEV2_IPSEC_RSA, STRONGSWAN_IKE2_IPSEC_RSA_PATH);
this.strongSwanMap.set(VpnTypeModel.TYPE_IKEV2_IPSEC_PSK, STRONGSWAN_IKE2_IPSEC_PSK_PATH);
this.strongSwanMap.set(VpnTypeModel.TYPE_IPSEC_XAUTH_PSK, STRONGSWAN_IPSEC_XAUTH_PSK_PATH);
this.strongSwanMap.set(VpnTypeModel.TYPE_IPSEC_XAUTH_RSA, STRONGSWAN_IPSEC_XAUTH_RSA_PATH);
this.strongSwanMap.set(VpnTypeModel.TYPE_IPSEC_HYBRID_RSA, STRONGSWAN_IPSEC_HYBRID_RSA_PATH);
this.strongSwanMap.set(VpnTypeModel.TYPE_L2TP_IPSEC_PSK, STRONGSWAN_L2TP_IPSEC_PSK_PATH);
this.strongSwanMap.set(VpnTypeModel.TYPE_L2TP_IPSEC_RSA, STRONGSWAN_L2TP_IPSEC_RSA_PATH);
this.strongSwanMap = this.readRawFile(this.strongSwanMap);
//xl2tpd.conf
this.xl2tpdMap.set(VpnTypeModel.TYPE_L2TP_IPSEC_PSK, XL2TPD_L2TP_IPSEC_PSK_PATH);
this.xl2tpdMap.set(VpnTypeModel.TYPE_L2TP_IPSEC_RSA, XL2TPD_L2TP_IPSEC_RSA_PATH);
this.xl2tpdMap = this.readRawFile(this.xl2tpdMap);
//options.l2tpd.client.conf
this.l2tpdClientMap.set(VpnTypeModel.TYPE_L2TP_IPSEC_PSK, CLIENT_L2TP_IPSEC_PSK_PATH);
this.l2tpdClientMap.set(VpnTypeModel.TYPE_L2TP_IPSEC_RSA, CLIENT_L2TP_IPSEC_RSA_PATH);
this.l2tpdClientMap = this.readRawFile(this.l2tpdClientMap);
//ipsec.conf
this.ipsecConfMap.set(VpnTypeModel.TYPE_L2TP_IPSEC_PSK, IPSECCONF_L2TP_IPSEC_PSK_PATH);
this.ipsecConfMap.set(VpnTypeModel.TYPE_L2TP_IPSEC_RSA, IPSECCONF_L2TP_IPSEC_RSA_PATH);
this.ipsecConfMap = this.readRawFile(this.ipsecConfMap);
//ipsec.secrets.conf
this.ipsecSecretsMap.set(VpnTypeModel.TYPE_L2TP_IPSEC_PSK, IPSEC_SECRETS_L2TP_IPSEC_PSK_PATH);
this.ipsecSecretsMap.set(VpnTypeModel.TYPE_L2TP_IPSEC_RSA, IPSEC_SECRETS_L2TP_IPSEC_RSA_PATH);
this.ipsecSecretsMap = this.readRawFile(this.ipsecSecretsMap);
}
private readRawFile(pathMap): HashMap<number, string> {
try {
pathMap.forEach((path, key) => {
this.context.resourceManager.getRawFileContent(path as string, (error, value) => {
if (error !== null && error !== undefined) {
LogUtil.log(MODULE_TAG + 'readRawFile faile, error:' + error);
} else {
pathMap.set(key, this.uint8ArrayToString(value));
}
});
});
} catch (error) {
LogUtil.error(MODULE_TAG + 'callback getRawfileContent failed, error:' + error);
}
return pathMap;
}
private replaceConfigParam(vpnTemplate: string, key: string, value: string): string {
if (!value || !key) {
LogUtil.warn(MODULE_TAG + 'replaceConfigParam failed, params is null or undefined');
return vpnTemplate;
}
while (vpnTemplate.indexOf(key) !== -1) {
vpnTemplate = vpnTemplate.replace(key, value.trim());
}
return vpnTemplate;
}
private uint8ArrayToString(fileData): string {
let dataString = '';
for (let i = 0; i < fileData.length; i++) {
dataString += String.fromCharCode(fileData[i]);
}
return dataString;
}
private stringToUint8Array(str: string): Uint8Array {
let arr: number[] = [];
for (let i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
return new Uint8Array(arr);
}
}

View File

@ -0,0 +1,94 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import vpn from '@ohos.net.vpn';
import { VpnTypeModel } from './VpnTypeModel';
/**
* extend system VpnConig
*/
export default class VpnConfig implements vpn.SysVpnConfig {
// vpnConfig
addresses: Array<vpn.LinkAddress> = [];
routes?: Array<vpn.RouteInfo>;
dnsAddresses?: Array<string>;
searchDomains?: Array<string>;
isLegacy: boolean = true;
// sysVpnConfig
vpnId: string = '';
vpnName: string = '';
vpnType: vpn.SysVpnType = VpnTypeModel.TYPE_IKEV2_IPSEC_MSCHAPv2;
userName?: string;
password?: string;
saveLogin: boolean = false;
userId?: number;
forwardingRoutes?: string;
}
export class OpenVpnConfig extends VpnConfig implements vpn.OpenVpnConfig {
ovpnConfigFilePath?: string // openVpn file name
ovpnConfigContent?: string // openVpn config
ovpnConfig?: string // openVpn config base64
ovpnAuthType: number = 0 // openvpn auth type
ovpnProtocolFileRaw?: string // Protocol
ovpnProtocol: number = 0 // 0:tcp 1:udp
ovpnAddressPortFileRaw?: string // port
ovpnPort?: string // openvpn port
askpass: string // private key password
ovpnCaCertFilePath?: string // openVpn CA FilePath
ovpnUserCertFilePath?: string // openVpn USER FilePath
ovpnPrivateKeyFilePath?: string // openVpn private key FilePath
ovpnCaCertFileRaw?: string // ca raw data
ovpnCaCert?: string // CA data
ovpnUserCertFileRaw?: string //
ovpnUserCert?: string //
ovpnPrivateKeyFileRaw?: string // private key raw data
ovpnPrivateKey?: string // private key data
ovpnUserPassFileRaw?: string // userpass raw d
ovpnProxyHostFileRaw?: string // host raw data
ovpnProxyHost?: string //ovpn host
ovpnProxyPort?: string //ovpn port
ovpnProxyUserPassFileRaw?: string // userpass data
ovpnProxyUser?: string //ovpn user
ovpnProxyPass?: string //ovpn pass
}
export class IpsecVpnConfig extends VpnConfig implements vpn.IpsecVpnConfig {
ipsecIdentifier?: string // ipsec identifier
ipsecPreSharedKey?: string // ipsec pre sharedKey
l2tpSharedKey?: string // L2TP secret
ipsecPublicUserCertConfig?: string // public userCert config
ipsecPublicUserCertFilePath?: string // public userCert FilePath
ipsecPrivateUserCertConfig?: string // private userCert config
ipsecPrivateUserCertFilePath?: string // private userCert FilePath
ipsecCaCertConfig?: string // ca config
ipsecCaCertFilePath?: string // ca FilePath
ipsecPublicServerCertConfig?: string // public serverCert config
ipsecPublicServerCertFilePath?: string // public serverCert FilePath
ipsecPrivateServerCertConfig?: string // private serverCert config
ipsecPrivateServerCertFilePath?: string // private serverCert FilePath
swanctlConfig?: string // swanctl config base64
strongSwanConfig?: string // strongswan config base64
optionsL2tpdClient?: string // optionsL2tpd Client base64
xl2tpdConfig?: string // xl2tpd config base64
ipsecConfig?: string // swanctl config base64
ipsecSecrets?: string // swanctl config base64
}
export class VpnListItem {
vpnName: string //vpnName
vpnId: string //UUID
}

View File

@ -0,0 +1,336 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import util from '@ohos.util';
import { promptAction } from '@kit.ArkUI';
import VpnConfig, { IpsecVpnConfig, OpenVpnConfig } from './VpnConfig';
import VpnConstant from './VpnConstant';
import { VpnTypeModel } from './VpnTypeModel';
import { SwanCtlModel } from './SwanCtlModel';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
const MODULE_TAG: string = 'setting_vpn:VpnConnectModel:';
const OVPN_PROTOCOL_TCP: number = 0;
const OVPN_PROTOCOL_UDP: number = 1;
/**
* system vpn config model
*/
export class VpnConfigModel {
private static instance: VpnConfigModel;
private isNeedUpdateVpnList: boolean = false;
public static getInstance(): VpnConfigModel {
if (!this.instance) {
this.instance = new VpnConfigModel();
}
return this.instance;
}
isUpdateVpnList(): boolean {
return this.isNeedUpdateVpnList;
}
setNeedUpdateVpnList(isUpdate: boolean): void {
this.isNeedUpdateVpnList = isUpdate;
}
setAddress(vpnConfig: VpnConfig, vpnAddress: string): void {
if (vpnConfig === undefined || vpnConfig === null) {
LogUtil.error(MODULE_TAG + `setAddress failed, invalid config`);
return;
}
vpnConfig.addresses = [{
address: { address: vpnAddress },
prefixLength: 1,
}]
}
getAddress(vpnConfig: VpnConfig): string | undefined {
if (vpnConfig === undefined || vpnConfig === null) {
LogUtil.error(MODULE_TAG + `setRoutes failed, invalid config`);
return undefined;
}
if (vpnConfig.addresses?.length > 0) {
return vpnConfig.addresses[0].address?.address;
}
LogUtil.error(MODULE_TAG + `getAddress invalid vpnConfig`);
return undefined;
}
stringToUint8Array(str: string): Uint8Array {
let arr: number[] = [];
for (let i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
return new Uint8Array(arr);
}
uint8ArrayToString(data: Uint8Array): string {
let resultStr: string = '';
const charArray = data.map(value => Number(value));
charArray.forEach((value) => {
resultStr += String.fromCharCode(value);
});
return resultStr;
}
showToast(msg: string | Resource): void {
if (!msg) {
LogUtil.error(MODULE_TAG + `showToast failed, invalid msg`);
return;
}
promptAction.showToast({
message: msg,
duration: 2000,
});
}
inflateConfigFromFileData(config: OpenVpnConfig, data: string[]): void {
config.ovpnConfigFilePath = data[0];
let content = data[1];
config.ovpnConfigContent = content;
let regex = /proto\s+(tcp|udp)/;
let match = regex.exec(content);
if (match && match.length >= 1) {
config.ovpnProtocolFileRaw = match[0];
config.ovpnProtocol = match[1] === 'udp' ? OVPN_PROTOCOL_UDP : OVPN_PROTOCOL_TCP;
}
regex = /remote\s+([^\s]+)\s+(\d+)/;
match = regex.exec(content);
if (match && match.length >= 2) {
config.ovpnAddressPortFileRaw = match[0];
this.setAddress(config, match[1]);
config.ovpnPort = match[2];
}
regex = /<ca>([\s\S]*?)<\/ca>/
match = regex.exec(content);
if (match) {
config.ovpnCaCertFileRaw = match[0];
}
regex = /<cert>([\s\S]*?)<\/cert>/
match = regex.exec(content);
if (match && match.length >= 1) {
config.ovpnUserCertFileRaw = match[0];
}
regex = /<key>([\s\S]*?)<\/key>/
match = regex.exec(content);
if (match && match.length >= 1) {
config.ovpnPrivateKeyFileRaw = match[0];
}
regex = /<auth-user-pass>\s*([\s\S]+?)\s*(\r?\n|\r)\s*([\s\S]+?)\s*<\/auth-user-pass>/
match = regex.exec(content);
if (match && match.length >= 4) {
config.ovpnUserPassFileRaw = match[0];
config.userName = match[1];
config.password = match[3];
}
regex = /http-proxy\s+([^\s]+)\s+(\d+)/;
match = regex.exec(content);
if (match && match.length >= 3) {
config.ovpnProxyHostFileRaw = match[0];
config.ovpnProxyHost = match[1];
config.ovpnProxyPort = match[2];
}
regex = /<http-proxy-user-pass>\s*([\s\S]+?)\s*(\r?\n|\r)\s*([\s\S]+?)\s*<\/http-proxy-user-pass>/;
match = regex.exec(content);
if (match && match.length >= 4) {
config.ovpnProxyUserPassFileRaw = match[0];
config.ovpnProxyUser = match[1];
config.ovpnProxyPass = match[3];
}
}
prepareAddSysVpnConfig(config: VpnConfig): void {
switch (config?.vpnType) {
case VpnTypeModel.TYPE_OPENVPN:
this.prepareOvpnConfig(config as OpenVpnConfig);
break;
default:
this.prepareIpsecConfig(config as IpsecVpnConfig);
break;
}
}
prepareIpsecConfig(config: IpsecVpnConfig): void {
if (config === undefined || config === null) {
LogUtil.error(MODULE_TAG + "prepareIpsecConfig faild, invalid param.")
return;
}
SwanCtlModel.getInstance().buildConfig(config);
}
prepareOvpnConfig(config: OpenVpnConfig): void {
if (config === undefined || config === null) {
LogUtil.error(MODULE_TAG + "prepareOvpnConfig faild, invalid param.")
return;
}
let that = new util.Base64Helper();
let content = '';
if (config.ovpnConfig) {
let data = that.decodeSync(config.ovpnConfig);
content = this.uint8ArrayToString(data);
} else {
content = config.ovpnConfigContent ?? 'client\ndev tun';
}
let protocolReplace: string = `proto ${config.ovpnProtocol === OVPN_PROTOCOL_TCP ? 'tcp' : 'udp'}`;
if (protocolReplace === config.ovpnProtocolFileRaw) {
content = content.replace(config.ovpnProtocolFileRaw, '\n' + protocolReplace);
} else {
content += '\n';
content += protocolReplace;
}
let vpnAddress: string = this.getAddress(config);
if (vpnAddress && config.ovpnPort) {
let addressPortReplace: string = `remote ${vpnAddress} ${config.ovpnPort}`;
if (addressPortReplace === config.ovpnAddressPortFileRaw) {
content = content.replace(config.ovpnAddressPortFileRaw, '\n' + addressPortReplace);
} else {
content += '\n';
content += addressPortReplace;
content += '\nlog \/data\/service\/el1\/public\/netmanager\/config.log\ndev-node \/dev\/tun' +
'\nresolv-retry infinite\nnobind\npersist-key\npersist-tun\nverb 7\n';
}
}
if (config.ovpnCaCert) {
let caCert: string = config.ovpnCaCert;
if (config.ovpnCaCertFileRaw) {
if (config.ovpnCaCertFileRaw !== caCert) {
content = content.replace(config.ovpnCaCertFileRaw, '\n' + caCert);
}
} else {
content += '\n';
content += caCert;
}
}
if (config.ovpnAuthType === VpnConstant.OVPN_AUTH_TYPE_PWD) {
config.ovpnUserCert = undefined;
if (config.ovpnUserCertFileRaw) {
content.replace(config.ovpnUserCertFileRaw, '');
}
config.ovpnUserCertFileRaw = undefined;
config.ovpnUserCertFilePath = undefined;
}
if (config.ovpnUserCert) {
let userCert: string = config.ovpnUserCert;
if (config.ovpnUserCertFileRaw) {
if (config.ovpnUserCertFileRaw !== userCert) {
content = content.replace(config.ovpnUserCertFileRaw, '\n' + userCert);
}
} else {
content += '\n';
content += userCert;
}
}
if (config.ovpnAuthType === VpnConstant.OVPN_AUTH_TYPE_PWD) {
config.ovpnPrivateKey = undefined;
if (config.ovpnPrivateKeyFileRaw) {
content.replace(config.ovpnPrivateKeyFileRaw, '');
}
config.ovpnPrivateKeyFileRaw = undefined;
config.ovpnPrivateKeyFilePath = undefined;
}
if (config.ovpnPrivateKey) {
let privateKey: string = config.ovpnPrivateKey;
if (config.ovpnPrivateKeyFileRaw) {
if (config.ovpnPrivateKeyFileRaw !== privateKey) {
content = content.replace(config.ovpnPrivateKeyFileRaw, '\n' + privateKey);
}
} else {
content += '\n';
content += privateKey;
}
}
if (config.ovpnAuthType === VpnConstant.OVPN_AUTH_TYPE_TLS) {
config.userName = undefined;
config.password = undefined;
if (config.ovpnUserPassFileRaw) {
content = content.replace(config.ovpnUserPassFileRaw, '');
}
}
if (config.userName) {
let userPass: string = '<auth-user-pass>';
userPass += '\n';
userPass += config.userName;
userPass += '\n';
userPass += config.password;
userPass += '\n';
userPass += '</auth-user-pass>'
if (config.userName) {
if (config.ovpnUserPassFileRaw) {
if (config.ovpnUserPassFileRaw !== userPass) {
content = content.replace(config.ovpnUserPassFileRaw, '\n' + userPass);
}
} else {
content += '\n';
content += userPass;
}
}
}
if (config.ovpnProxyHost) {
let proxyHostPort: string = `http-proxy ${config.ovpnProxyHost} ${config.ovpnProxyPort ?? ''}`;
if (config.ovpnProxyHostFileRaw) {
if (config.ovpnProxyHostFileRaw !== proxyHostPort) {
content = content.replace(config.ovpnProxyHostFileRaw, '\n' + proxyHostPort);
}
} else {
content += '\n';
content += proxyHostPort;
}
}
if (config.ovpnProxyUser) {
let proxyUserPass: string = '<http-proxy-user-pass>';
proxyUserPass += '\n';
proxyUserPass += config.ovpnProxyUser;
proxyUserPass += '\n';
proxyUserPass += config.ovpnProxyPass;
proxyUserPass += '\n';
proxyUserPass += '</http-proxy-user-pass>'
if (config.ovpnProxyUserPassFileRaw) {
if (config.ovpnProxyUserPassFileRaw !== proxyUserPass) {
content = content.replace(config.ovpnProxyUserPassFileRaw, '\n' + proxyUserPass);
}
} else {
content += '\n';
content += proxyUserPass;
}
}
let regex = /^\s*$\n/gm;
content = content.replace(regex, '');
config.ovpnConfig = that.encodeToStringSync(this.stringToUint8Array(content));
}
}

View File

@ -0,0 +1,198 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import vpn from '@ohos.net.vpn';
import { BusinessError } from '@kit.BasicServicesKit';
import type common from '@ohos.app.ability.common';
import VpnConfig from './VpnConfig';
import VpnConstant from './VpnConstant';
import { VpnConfigModel } from '../../model/vpnImpl/VpnConfigModel';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
const MODULE_TAG: string = 'setting_vpn:VpnConnectModel:';
AppStorage.setOrCreate(VpnConstant.STORAGE_KEY_CONNECT_STATE, VpnConstant.VPN_STATE_NONE);
/**
* app management service class
*/
export class VpnConnectModel {
private static instance: VpnConnectModel;
private connection: vpn.VpnConnection | undefined = undefined;
private timeoutId: number = undefined;
private connectedVpnId: string = undefined;
private connectState: number = VpnConstant.VPN_STATE_NONE;
private replaceConnectVpnConfig: VpnConfig = undefined;
public static getInstance(): VpnConnectModel {
if (!this.instance) {
this.instance = new VpnConnectModel();
}
return this.instance;
}
setReplaceConnectVpn(vpnConfig: VpnConfig): void {
this.replaceConnectVpnConfig = vpnConfig;
}
setConnectState(state: number, id?: string): void {
if (id) {
this.connectedVpnId = id;
}
LogUtil.info(MODULE_TAG + `setConnectState: ${this.connectState} -> ${state} id:${this.connectedVpnId}`);
this.connectState = state;
AppStorage.setOrCreate(VpnConstant.STORAGE_KEY_CONNECT_STATE, state);
}
getConnectedVpnId(): string {
return this.connectedVpnId;
}
isConnecting(vpnId: string): boolean {
return (vpnId === this.connectedVpnId) &&
(this.connectState === VpnConstant.VPN_STATE_CONNECTING);
}
isConnectedOrConnecting(vpnId: string): boolean {
let connectState = this.connectState;
return (vpnId === this.connectedVpnId) &&
((connectState === VpnConstant.VPN_STATE_CONNECTING) ||
(connectState === VpnConstant.VPN_STATE_CONNECTED));
}
onConnectStateChange(isConnected: boolean): void {
LogUtil.info(MODULE_TAG + `onConnectStateChange isConnected=` + isConnected);
this.removeTimeout();
if (isConnected) {
this.setConnectState(VpnConstant.VPN_STATE_CONNECTED);
return;
}
if (this.replaceConnectVpnConfig) {
let config = this.replaceConnectVpnConfig;
this.replaceConnectVpnConfig = undefined;
this.setUp(config);
return;
}
if (this.connectState === VpnConstant.VPN_STATE_CONNECTING) {
this.setConnectState(VpnConstant.VPN_STATE_CONNECT_FAILED);
return;
}
if (this.connectState === VpnConstant.VPN_STATE_DISCONNECTING) {
this.setConnectState(VpnConstant.VPN_STATE_DISCONNECTED);
}
}
init(context: common.UIAbilityContext): void {
this.connection = vpn.createVpnConnection(context);
try {
LogUtil.info(MODULE_TAG + `vpn on start`);
vpn.on('connect', (data) => {
this.onConnectStateChange(data?.isConnected);
})
} catch (error) {
LogUtil.error(MODULE_TAG + `vpn on error = ${JSON.stringify(error)}`);
}
this.getConnectedVpn();
}
release(): void {
try {
LogUtil.info(MODULE_TAG + `vpnConnection on subscribe off start`);
vpn.off('connect', (data) => {
LogUtil.info(MODULE_TAG + `vpnConnection off data = ${data}`);
})
} catch (error) {
LogUtil.error(MODULE_TAG + `vpnConnection off error = ${JSON.stringify(error)}`);
}
}
removeTimeout(): void {
if (this.timeoutId !== undefined) {
LogUtil.info(MODULE_TAG + `removeTimeout timeoutId = ${this.timeoutId}`);
clearTimeout(this.timeoutId);
}
}
async setUp(vpnConfig: VpnConfig): Promise<void> {
if (vpnConfig === undefined || vpnConfig === null) {
LogUtil.info(MODULE_TAG + `setUp failed, invalid param.`);
return;
}
LogUtil.info(MODULE_TAG + `setUp start`);
this.setConnectState(VpnConstant.VPN_STATE_CONNECTING, vpnConfig.vpnId);
this.removeTimeout();
this.timeoutId = setTimeout(() => {
LogUtil.info(MODULE_TAG + `setUp timeout vpnId=` + vpnConfig.vpnId);
this.setConnectState(VpnConstant.VPN_STATE_DISCONNECTING, vpnConfig.vpnId);
this.destroy((error: string) => {
if (error) {
LogUtil.error(MODULE_TAG + `vpn destroy failed, error:` + error);
}
this.setConnectState(VpnConstant.VPN_STATE_CONNECT_FAILED, vpnConfig.vpnId);
});
}, VpnConstant.VPN_CONNECT_TIME_OUT_DURATION);
try {
await this.connection.setUp(vpnConfig);
} catch (err) {
LogUtil.error(MODULE_TAG + `setUp error = ${JSON.stringify(err)}`);
this.removeTimeout();
VpnConfigModel.getInstance().showToast($r('app.string.vpn_error_operation_failed') + ' error:' + err);
this.setConnectState(VpnConstant.VPN_STATE_CONNECT_FAILED, vpnConfig.vpnId);
// destroy connection
this.destroy((error: string) => {
if (error) {
LogUtil.info(MODULE_TAG + `vpn destroy failed, error:` + error);
}
});
}
}
getConnectedVpn(): void {
try {
LogUtil.info(MODULE_TAG + `getConnectedVpn start`);
vpn.getConnectedSysVpnConfig().then((data) => {
if (data && data.addresses && data.vpnId) {
this.setConnectState(VpnConstant.VPN_STATE_CONNECTED, data.vpnId);
} else {
this.setConnectState(VpnConstant.VPN_STATE_NONE);
}
});
} catch (error) {
LogUtil.error(MODULE_TAG + `getConnectedVpn error: ${JSON.stringify(error)}`);
this.setConnectState(VpnConstant.VPN_STATE_NONE);
}
}
isHapAvailable(): boolean {
return this.timeoutId !== undefined;
}
destroy(callback): void {
this.connection?.destroy((error: BusinessError) => {
if (error) {
LogUtil.info(MODULE_TAG + `destroy error = ${JSON.stringify(error)}`);
}
callback(error?.message);
});
}
uint8ArrayToString(u8a: Uint8Array): string {
let dataStr = "";
for (let i = 0; i < u8a.length; i++) {
dataStr += String.fromCharCode(u8a[i])
}
return dataStr;
}
}

View File

@ -0,0 +1,39 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export default class VpnConstant {
static readonly VPN_NAME_MAX_LENGTH: number = 30;
static readonly VPN_USER_NAME_MAX_LENGTH: number = 30;
static readonly VPN_PASSWORD_MAX_LENGTH: number = 30;
static readonly INPUT_MAX_LENGTH: number = 100;
static readonly STORAGE_KEY_CONNECT_STATE: string = 'vpnConnectState';
static readonly VPN_STATE_NONE: number = 0;
static readonly VPN_STATE_CONNECTING: number = 1; // connecting
static readonly VPN_STATE_CONNECTED: number = 2; // connect success
static readonly VPN_STATE_DISCONNECTING: number = 3; // not connect
static readonly VPN_STATE_DISCONNECTED: number = 4; // not connect
static readonly VPN_STATE_CONNECT_FAILED: number = 5; // connect failed
static readonly VPN_CONNECT_TIME_OUT_DURATION: number = 60000;
static readonly REGEX_IP: RegExp = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
static readonly REGEX_PORT: RegExp = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
static readonly OVPN_AUTH_TYPE_TLS: number = 0;
static readonly OVPN_AUTH_TYPE_PWD: number = 1;
static readonly OVPN_AUTH_TYPE_TLS_PWD: number = 2;
}

View File

@ -0,0 +1,100 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import parameter from '@ohos.systemParameterEnhance';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
const MODULE_TAG: string = 'setting_vpn:VpnTypeModel:';
/**
* support vpn type & displayname
*/
export class VpnTypeModel {
static readonly TYPE_IKEV2_IPSEC_MSCHAPv2: number = 1; // vpn.SysVpnType.IKEV2_IPSEC_MSCHAPv2;
static readonly TYPE_IKEV2_IPSEC_PSK: number = 2; // vpn.SysVpnType.IKEV2_IPSEC_PSK;
static readonly TYPE_IKEV2_IPSEC_RSA: number = 3; // vpn.SysVpnType.IKEV2_IPSEC_RSA;
static readonly TYPE_L2TP_IPSEC_PSK: number = 4; // vpn.SysVpnType.L2TP_IPSEC_PSK;
static readonly TYPE_L2TP_IPSEC_RSA: number = 5; // vpn.SysVpnType.L2TP_IPSEC_RSA;
static readonly TYPE_IPSEC_XAUTH_PSK: number = 6; // vpn.SysVpnType.IPSEC_XAUTH_PSK;
static readonly TYPE_IPSEC_XAUTH_RSA: number = 7; // vpn.SysVpnType.IPSEC_XAUTH_RSA;
static readonly TYPE_IPSEC_HYBRID_RSA: number = 8; // vpn.SysVpnType.IPSEC_HYBRID_RSA;
static readonly TYPE_OPENVPN: number = 9; // vpn.SysVpnType.OPENVPN;
private supportVpnTypes: number[] = [];
private static instance: VpnTypeModel;
public static getInstance(): VpnTypeModel {
if (!this.instance) {
this.instance = new VpnTypeModel();
}
return this.instance;
}
constructor() {
let supportStr: string = parameter.getSync("const.product.supportVpn", "");
supportStr.split(',').forEach((vpnTypeStr) => {
let vpnType: number = Number(vpnTypeStr);
switch (vpnType) {
case VpnTypeModel.TYPE_IKEV2_IPSEC_MSCHAPv2:
case VpnTypeModel.TYPE_IKEV2_IPSEC_PSK:
case VpnTypeModel.TYPE_IKEV2_IPSEC_RSA:
case VpnTypeModel.TYPE_L2TP_IPSEC_PSK:
case VpnTypeModel.TYPE_L2TP_IPSEC_RSA:
case VpnTypeModel.TYPE_IPSEC_XAUTH_PSK:
case VpnTypeModel.TYPE_IPSEC_XAUTH_RSA:
case VpnTypeModel.TYPE_IPSEC_HYBRID_RSA:
case VpnTypeModel.TYPE_OPENVPN:
this.supportVpnTypes.push(vpnType);
break;
default :
LogUtil.info(MODULE_TAG + supportStr + ` has unknown vpnType:` + vpnType);
break;
}
})
LogUtil.info(MODULE_TAG + `supportVpn ${this.supportVpnTypes}`);
}
isSupportVpn(): boolean {
return this.supportVpnTypes.length > 0;
}
getSupportVpnTypes(): number[] {
return this.supportVpnTypes;
}
getSupportVpnTypeStrs(): string[] {
let types: string[] = [];
this.supportVpnTypes.forEach(type => {
types.push(this.getVpnTypeStr(type))
});
return types;
}
getVpnTypeStr(vpnType: number): string {
switch (vpnType) {
case VpnTypeModel.TYPE_IKEV2_IPSEC_MSCHAPv2: return 'IKEv2/IPSec MSCHAPv2';
case VpnTypeModel.TYPE_IKEV2_IPSEC_PSK: return 'IKEv2/IPSec PSK';
case VpnTypeModel.TYPE_IKEV2_IPSEC_RSA: return 'IKEv2/IPSec RSA';
case VpnTypeModel.TYPE_L2TP_IPSEC_PSK: return 'L2TP/IPSec PSK';
case VpnTypeModel.TYPE_L2TP_IPSEC_RSA: return 'L2TP/IPSec RSA';
case VpnTypeModel.TYPE_IPSEC_XAUTH_PSK: return 'IPSec Xauth PSK';
case VpnTypeModel.TYPE_IPSEC_XAUTH_RSA: return 'IPSec Xauth RSA';
case VpnTypeModel.TYPE_IPSEC_HYBRID_RSA: return 'IPSec Hybrid RSA';
case VpnTypeModel.TYPE_OPENVPN: return 'OpenVpn';
default :
LogUtil.warn(MODULE_TAG + 'getVpnTypeStr unknown vpnType:' + vpnType);
return '';
}
}
}

View File

@ -16,12 +16,15 @@
import display from '@ohos.display';
import deviceInfo from '@ohos.deviceInfo';
import NfcModel from '../model/moreConnectionsImpl/NfcModel';
import parameter from '@ohos.systemparameter';
import LogUtil from '../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
import ConfigData from '../../../../../../common/utils/src/main/ets/default/baseUtil/ConfigData';
import HeadComponent from '../../../../../../common/component/src/main/ets/default/headComponent';
import {
SubEntryComponent,
SubEntryComponentWithEndText
} from '../../../../../../common/component/src/main/ets/default/subEntryComponent';
import { VpnTypeModel } from '../model/vpnImpl/VpnTypeModel';
const TAG = ConfigData.TAG + 'MoreConnections: ';
const deviceTypeInfo = deviceInfo.deviceType;
@ -38,6 +41,7 @@ struct MoreConnections {
private maxScreenHeight: number = 0;
private nfcImageWidth: number = 0;
private nfcImageHeight: number = 0;
private nfcSupport: string = 'false';
nfcStatusChange() {
AppStorage.SetOrCreate("nfcStatusInfo", this.nfcStatus ? $r("app.string.enabled") : $r("app.string.disabled"))
@ -48,13 +52,43 @@ struct MoreConnections {
GridContainer({ gutter: ConfigData.GRID_CONTAINER_GUTTER_24, margin: ConfigData.GRID_CONTAINER_MARGIN_24 }) {
Column() {
HeadComponent({ headName: $r('app.string.moreConnectionsTab'), isActive: true });
Column() {
List() {
// NFC
if (this.nfcSupport === 'true') {
ListItem() {
SubEntryComponentWithEndText({
targetPage: 'pages/nfc',
title: $r('app.string.NFC'),
endText: $nfcStatusInfo
})
.margin({ top: $r("app.float.distance_8") })
}
}
// vpn
if (VpnTypeModel.getInstance().isSupportVpn()) {
ListItem() {
SubEntryComponent({
targetPage: 'pages/vpn/vpnList',
title: $r("app.string.VPN"),
})
}
}
}
.width(ConfigData.WH_100_100)
.divider({
strokeWidth: $r('app.float.divider_wh'),
color: $r('sys.color.ohos_id_color_list_separator'),
startMargin: $r('app.float.wh_value_15'),
endMargin: $r('app.float.wh_value_15') })
}
.borderRadius($r("app.float.radius_16"))
.backgroundColor($r("app.color.white_bg_color"))
.width(ConfigData.WH_100_100)
.margin({ top: $r("app.float.distance_12") })
.padding({
top: $r("app.float.distance_4"),
bottom: $r("app.float.distance_4")
})
}
.useSizeType({
sm: { span: 4, offset: 0 },
@ -71,6 +105,15 @@ struct MoreConnections {
}
aboutToAppear() {
this.nfcSupport = parameter.getSync('const.SystemCapability.Communication.NFC.Core', 'false');
LogUtil.info(TAG + `nfcSupport ${this.nfcSupport}`);
if (this.nfcSupport === 'true') {
this.nfcInit();
}
}
nfcInit(){
LogUtil.info(TAG + 'aboutToAppear in');
try {
@ -106,8 +149,7 @@ struct MoreConnections {
this.nfcImageWidth = (this.maxScreenHeight / 2) * 0.8;
this.nfcImageHeight = (this.maxScreenHeight / 2) * 0.8;
}
}
else {
} else {
if (this.maxScreenHeight > this.maxScreenWidth / 2) {
this.nfcImageWidth = (this.maxScreenWidth / 2) * 0.7;
this.nfcImageHeight = (this.maxScreenWidth / 2) * 0.7;

View File

@ -25,6 +25,7 @@ import ConfigData from '../../../../../../common/utils/src/main/ets/default/base
import ResourceUtil from '../../../../../../common/search/src/main/ets/default/common/ResourceUtil';
import {SettingItemComponent} from '../../../../../../common/component/src/main/ets/default/settingItemComponent';
import GlobalResourceManager from '../../../../../../common/utils/src/main/ets/default/baseUtil/GlobalResourceManager';
import { VpnTypeModel } from '../model/vpnImpl/VpnTypeModel';
const deviceTypeInfo = deviceInfo.deviceType;
@ -120,22 +121,6 @@ struct SettingList {
}
LogUtil.info('CCCC settings SettingList aboutToAppear end');
}
onPageShow() {
LogUtil.info('CCCC settings SettingList onPageShow enter');
FeatureAbility.getWant().then((want) => {
if (want.uri === "wifi") {
Router.replace({ uri: "pages/wifi" });
} else if (want.uri === "bluetooth") {
Router.replace({ uri: "pages/bluetooth" });
} else if (want.uri === "volumeControl") {
Router.replace({ uri: "pages/volumeControl" });
} else if (want.uri === "locationServices") {
Router.replace({ uri: "pages/locationServices" });
}
})
LogUtil.info('CCCC settings SettingList onPageShow end');
}
}
@Component
@ -272,13 +257,13 @@ struct EntryComponent {
})
}
if (this.info === 'true') {
ListItem(){
if (this.info === 'true' || VpnTypeModel.getInstance().isSupportVpn()) {
ListItem() {
// moreConnections
SettingItemComponent({
targetPage: "pages/moreConnections",
settingTitle: $r("app.string.moreConnectionsTab"),
settingEndText:$endTextEmpty,
settingEndText: $endTextEmpty,
settingIcon: "/res/image/ic_settings_more_connections.svg",
})
}

View File

@ -0,0 +1,224 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import deviceInfo from '@ohos.deviceInfo';
import fileIo from '@ohos.file.fs';
import picker from '@ohos.file.picker';
import util from '@ohos.util';
import { BusinessError } from '@ohos.base';
import { SelectDialog } from '@ohos.arkui.advanced.Dialog';
import VpnConfig, { OpenVpnConfig } from '../../model/vpnImpl/VpnConfig';
import { VpnTypeModel } from '../../model/vpnImpl/VpnTypeModel';
import VpnConstant from '../../model/vpnImpl/VpnConstant';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
export const SELECTER_VPNTYPE: number = 1;
export const SELECTER_OVPN_PROTOCOL: number = 2;
export const SELECTER_OVPN_AUTH: number = 3;
export const deviceTypeInfo = deviceInfo.deviceType;
export const INPUT_TYPE_IP: number = 1;
export const INPUT_TYPE_PORT: number = 2;
const MODULE_TAG: string = 'setting_vpn:custom_component:';
@Component
export struct TextWithInput {
title: Resource | string = '';
inputType: number | undefined = undefined;
inputPlaceholder: Resource | string = '';
maxLength: number = VpnConstant.INPUT_MAX_LENGTH;
@Prop inputText: string = '';
@State fontColor: ResourceColor = $r('app.color.font_color_182431');
onChange: (value: string) => void = () => {};
build() {
Column() {
Text(this.title)
.fontSize($r('app.float.font_16'))
.fontColor($r('app.color.font_color_182431'))
.fontWeight(FontWeight.Medium)
.textAlign(TextAlign.Start)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.textAlign(TextAlign.Start)
.padding({ left: 8 })
.margin({ top: 10 });
TextInput({ placeholder: this.inputPlaceholder, text: this.inputText }).onChange((value: string) => {
if (this.inputType === INPUT_TYPE_IP) {
if (VpnConstant.REGEX_IP.test(value)) {
this.fontColor = $r('app.color.font_color_182431');
} else {
this.fontColor = $r('sys.color.ohos_id_color_warning');
}
}
if (this.inputType === INPUT_TYPE_PORT) {
if (VpnConstant.REGEX_PORT.test(value)) {
this.fontColor = $r('app.color.font_color_182431');
} else {
this.fontColor = $r('sys.color.ohos_id_color_warning');
}
}
this.onChange(value);
}).padding({ left: 5 })
.fontSize($r('app.float.font_16'))
.showUnderline(true)
.height(45)
.margin({ top: 5 })
.fontColor(this.fontColor)
.maxLength(this.maxLength);
}.alignItems(HorizontalAlign.Start)
}
}
@Component
export struct Selector {
title: string | Resource = '';
@Link vpnConfig: VpnConfig;
type: number = 0;
sheetTitles: string[] = [];
sheetInfos: SheetInfo[] = [];
selectDialogController: CustomDialogController | undefined = undefined;
build() {
Row() {
Text(this.title)
.fontSize($r('app.float.font_16'))
.fontColor($r('app.color.font_color_182431'))
.fontWeight(FontWeight.Medium)
.textAlign(TextAlign.Start)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.textAlign(TextAlign.Start)
Blank()
Text(this.sheetTitles[this.getSelectIndex(this.vpnConfig, this.type)])
.padding({ right: 4 })
Image($r('app.media.ic_settings_arrow'))
.width($r('app.float.wh_value_12'))
.height($r('app.float.wh_value_24'))
.fillColor($r('sys.color.ohos_id_color_primary'))
.opacity($r('app.float.opacity_0_2'))
}
.borderRadius($r('app.float.radius_16'))
.backgroundColor('#ffffff')
.width('100%')
.height($r('app.float.wh_value_56'))
.borderRadius($r('app.float.radius_16'))
.padding({ left: 8, right: 8 })
.onClick(() => {
this.showSelectDialog();
})
}
getSelectIndex(vpnConfig: VpnConfig, selecter: number): number {
if (selecter === SELECTER_VPNTYPE) {
return VpnTypeModel.getInstance().getSupportVpnTypes().indexOf(vpnConfig.vpnType);
} else if (selecter === SELECTER_OVPN_PROTOCOL) {
let openvpn : OpenVpnConfig = vpnConfig as OpenVpnConfig;
return openvpn.ovpnProtocol;
} else if (selecter === SELECTER_OVPN_AUTH) {
let openvpn : OpenVpnConfig = vpnConfig as OpenVpnConfig;
return openvpn.ovpnAuthType;
} else {
return 0;
}
}
showSelectDialog() {
this.selectDialogController = new CustomDialogController({
builder: SelectDialog({
title: this.title,
selectedIndex: this.getSelectIndex(this.vpnConfig, this.type),
confirm: {
value: $r('app.string.cancel'),
action: () => {
this.selectDialogController?.close();
},
},
radioContent: this.sheetInfos
}),
autoCancel: true,
alignment: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? DialogAlignment.Bottom : DialogAlignment.Center,
offset: ({ dx: 0, dy: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? '-24dp' : 0 })
});
this.selectDialogController?.open();
}
}
@Component
export struct SelectFile {
title: ResourceStr = '';
@Prop vpnConfig: VpnConfig;
@State fileName: string | undefined = undefined;
realFilePath: string | undefined = undefined;
callback?: (result: string[]) => void;
build() {
Row() {
Text(this.title)
.fontSize($r('app.float.font_16'))
.fontColor($r('app.color.font_color_182431'))
.fontWeight(FontWeight.Medium)
.textAlign(TextAlign.Start)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.textAlign(TextAlign.Start)
Blank()
Text(this.fileName ?? $r('app.string.vpn_edit_ovpn_click_select'))
.padding({ right: 4 })
Image($r('sys.media.ohos_ic_public_arrow_right')).width($r('app.float.wh_value_12'))
.height($r('app.float.wh_value_24'))
.fillColor($r('sys.color.ohos_id_color_primary'))
.opacity($r('app.float.opacity_0_2'))
}
.borderRadius($r('app.float.radius_16'))
.backgroundColor('#ffffff')
.width('100%')
.height($r('app.float.wh_value_56'))
.borderRadius($r('app.float.radius_16'))
.padding({ left: 8, right: 8 })
.onClick(() => {
this.pickFile((result: string[]) => {
if (result) {
this.fileName = result[0];
this.callback!(result);
}
})
})
}
pickFile(callback: (result: string[]) => void): void {
try {
let selectOption = new picker.DocumentSelectOptions();
let documentPicker = new picker.DocumentViewPicker();
documentPicker.select(selectOption).then((result: Array<string>) => {
if (result.length > 0) {
let uri = result[0];
let fileName = uri.substring(uri.lastIndexOf('/') + 1);
let file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY);
let fileStat = fileIo.statSync(file.fd);
let buf = new ArrayBuffer(fileStat.size);
fileIo.readSync(file.fd, buf);
fileIo.close(file.fd)
let unit8 = new Uint8Array(buf);
let content = new util.TextDecoder().decodeWithStream(unit8);
callback([fileName, content]);
}
}).catch((err: BusinessError) => {
LogUtil.error(MODULE_TAG + 'DocumentViewPicker select failed with err: ' + JSON.stringify(err));
});
} catch (error) {
let err: BusinessError = error as BusinessError;
LogUtil.error(MODULE_TAG + 'DocumentViewPicker failed with err: ' + JSON.stringify(err));
}
}
}

View File

@ -0,0 +1,301 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { util } from '@kit.ArkTS';
import { TextWithInput, SelectFile, INPUT_TYPE_IP } from './customComponent';
import { IpsecVpnConfig } from '../../model/vpnImpl/VpnConfig';
import { VpnTypeModel } from '../../model/vpnImpl/VpnTypeModel';
import { VpnConfigModel } from '../../model/vpnImpl/VpnConfigModel';
import VpnConstant from '../../model/vpnImpl/VpnConstant';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
@Component
export default struct IpsecVpnEdit {
onSaveBtnEnableChange: Function = (enabled: boolean) => {};
@State @Watch('onVpnConfigChange') vpnConfig: IpsecVpnConfig = new IpsecVpnConfig();
@State ipsecIdentifierVisibility: Visibility = Visibility.None;
@State l2tpSecretVisibility: Visibility = Visibility.None;
@State ipsecSecretVisibility: Visibility = Visibility.None;
@State userCertVisibility: Visibility = Visibility.None;
@State caServerCertVisibility: Visibility = Visibility.None;
@State advanceChecked: boolean = false;
@State vpnAddress: string = '';
@State dnsAddress: string = '';
vpnType: number = 0;
base64Util = new util.Base64Helper();
aboutToAppear() {
this.vpnAddress = VpnConfigModel.getInstance().getAddress(this.vpnConfig) ?? '';
let dnsAddresses = this.vpnConfig.dnsAddresses;
if (dnsAddresses) {
this.dnsAddress = dnsAddresses[0];
}
this.updateUI();
}
updateUI() {
this.updateIpsecIdentifierVisibility();
this.updateL2tpSecretVisibility();
this.updateIpsecSecretVisibility();
this.updateUserCertVisibility();
this.updateCaServerCertVisibility();
}
onVpnConfigChange() {
if (this.vpnType !== this.vpnConfig.vpnType) {
LogUtil.info('onVpnConfigChange ' + this.vpnType + '->' + this.vpnConfig.vpnType);
this.vpnType = this.vpnConfig.vpnType;
this.updateUI();
}
}
onNonNullableParamChange(): void {
let isNonNullParamReady = true;
if (!VpnConstant.REGEX_IP.test(this.vpnAddress)) {
isNonNullParamReady = false;
}
if (this.dnsAddress.length >0 && !VpnConstant.REGEX_IP.test(this.dnsAddress)) {
isNonNullParamReady = false;
}
if (this.vpnConfig.vpnType !== VpnTypeModel.TYPE_L2TP_IPSEC_PSK) {
if (this.ipsecIdentifierVisibility === Visibility.Visible && !this.vpnConfig.ipsecIdentifier) {
isNonNullParamReady = false;
}
if (this.l2tpSecretVisibility === Visibility.Visible && !this.vpnConfig.l2tpSharedKey) {
isNonNullParamReady = false;
}
}
if (this.ipsecSecretVisibility === Visibility.Visible && !this.vpnConfig.ipsecPreSharedKey) {
isNonNullParamReady = false;
}
this.onSaveBtnEnableChange(isNonNullParamReady);
}
updateIpsecIdentifierVisibility(): void {
switch (this.vpnConfig.vpnType) {
case VpnTypeModel.TYPE_IKEV2_IPSEC_MSCHAPv2:
case VpnTypeModel.TYPE_IKEV2_IPSEC_PSK:
case VpnTypeModel.TYPE_IKEV2_IPSEC_RSA:
case VpnTypeModel.TYPE_L2TP_IPSEC_PSK:
case VpnTypeModel.TYPE_IPSEC_XAUTH_PSK:
this.ipsecIdentifierVisibility = Visibility.Visible;
break;
default:
this.ipsecIdentifierVisibility = Visibility.None;
break;
}
}
updateL2tpSecretVisibility(): void {
switch (this.vpnConfig.vpnType) {
case VpnTypeModel.TYPE_L2TP_IPSEC_PSK:
case VpnTypeModel.TYPE_L2TP_IPSEC_RSA:
this.l2tpSecretVisibility = Visibility.Visible;
break;
default:
this.l2tpSecretVisibility = Visibility.None;
break;
}
}
updateIpsecSecretVisibility(): void {
switch (this.vpnConfig.vpnType) {
case VpnTypeModel.TYPE_IKEV2_IPSEC_PSK:
case VpnTypeModel.TYPE_L2TP_IPSEC_PSK:
case VpnTypeModel.TYPE_IPSEC_XAUTH_PSK:
this.ipsecSecretVisibility = Visibility.Visible;
break;
default:
this.ipsecSecretVisibility = Visibility.None;
break;
}
}
updateUserCertVisibility(): void {
switch (this.vpnConfig.vpnType) {
case VpnTypeModel.TYPE_IKEV2_IPSEC_RSA:
case VpnTypeModel.TYPE_L2TP_IPSEC_RSA:
case VpnTypeModel.TYPE_IPSEC_XAUTH_RSA:
this.userCertVisibility = Visibility.Visible;
break;
default:
this.userCertVisibility = Visibility.None;
break;
}
}
updateCaServerCertVisibility(): void {
switch (this.vpnConfig.vpnType) {
case VpnTypeModel.TYPE_IKEV2_IPSEC_MSCHAPv2:
case VpnTypeModel.TYPE_IKEV2_IPSEC_RSA:
case VpnTypeModel.TYPE_L2TP_IPSEC_RSA:
case VpnTypeModel.TYPE_IPSEC_XAUTH_RSA:
case VpnTypeModel.TYPE_IPSEC_HYBRID_RSA:
this.caServerCertVisibility = Visibility.Visible;
break;
default:
this.caServerCertVisibility = Visibility.None;
break;
}
}
build() {
Column() {
TextWithInput({
title: $r('app.string.vpn_edit_serveraddress'),
inputPlaceholder: '',
inputText: this.vpnAddress,
inputType: INPUT_TYPE_IP,
onChange: (value: string) => {
this.vpnAddress = value.trim();
VpnConfigModel.getInstance().setAddress(this.vpnConfig, this.vpnAddress);
this.onNonNullableParamChange();
}
})
TextWithInput({
title: $r('app.string.vpn_edit_ipsecidentifier'),
inputPlaceholder: $r('app.string.vpn_edit_unuse'),
inputText: this.vpnConfig.ipsecIdentifier,
onChange: (value: string) => {
this.vpnConfig.ipsecIdentifier = value.trim();
this.onNonNullableParamChange();
}
}).visibility(this.ipsecIdentifierVisibility)
TextWithInput({
title: $r('app.string.vpn_edit_l2tpsecret'),
inputPlaceholder: $r('app.string.vpn_edit_unuse'),
inputText: this.vpnConfig.l2tpSharedKey,
onChange: (value: string) => {
this.vpnConfig.l2tpSharedKey = value.trim();
this.onNonNullableParamChange();
}
}).visibility(this.l2tpSecretVisibility)
TextWithInput({
title: $r('app.string.vpn_edit_ipsecsecret'),
inputPlaceholder: $r('app.string.vpn_edit_unuse'),
inputText: this.vpnConfig.ipsecPreSharedKey,
onChange: (value: string) => {
this.vpnConfig.ipsecPreSharedKey = value.trim();
this.onNonNullableParamChange();
}
}).visibility(this.ipsecSecretVisibility)
SelectFile({
title: $r('app.string.vpn_edit_usercert'),
fileName: this.vpnConfig.ipsecPublicUserCertFilePath,
vpnConfig: this.vpnConfig,
callback: (result: string[]) => {
this.vpnConfig.ipsecPublicUserCertFilePath = result[0];
this.vpnConfig.
ipsecPublicUserCertConfig = this.base64Util.encodeToStringSync(
VpnConfigModel.getInstance().stringToUint8Array(result[1]));
this.onNonNullableParamChange();
}
}).margin({ top: 15 })
.visibility(this.userCertVisibility)
SelectFile({
title: $r('app.string.vpn_edit_user_private_cert'),
fileName: this.vpnConfig.ipsecPrivateUserCertFilePath,
vpnConfig: this.vpnConfig,
callback: (result: string[]) => {
this.vpnConfig.ipsecPrivateUserCertFilePath = result[0];
this.vpnConfig.
ipsecPrivateUserCertConfig = this.base64Util.encodeToStringSync(
VpnConfigModel.getInstance().stringToUint8Array(result[1]));
this.onNonNullableParamChange();
}
}).margin({ top: 15 })
.visibility(this.userCertVisibility)
SelectFile({
title: $r('app.string.vpn_edit_cacert'),
fileName: this.vpnConfig.ipsecCaCertFilePath,
vpnConfig: this.vpnConfig,
callback: (result: string[]) => {
this.vpnConfig.ipsecCaCertFilePath = result[0];
this.vpnConfig.ipsecCaCertConfig = this.base64Util.encodeToStringSync(
VpnConfigModel.getInstance().stringToUint8Array(result[1]));
this.onNonNullableParamChange();
}
}).margin({ top: 10 })
.visibility(this.caServerCertVisibility)
SelectFile({
title: $r('app.string.vpn_edit_servercert'),
fileName: this.vpnConfig.ipsecPublicServerCertFilePath,
vpnConfig: this.vpnConfig,
callback: (result: string[]) => {
this.vpnConfig.ipsecPublicServerCertFilePath = result[0];
this.vpnConfig.ipsecPublicServerCertConfig = this.base64Util.encodeToStringSync(
VpnConfigModel.getInstance().stringToUint8Array(result[1]));
this.onNonNullableParamChange();
}
}).margin({ top: 10 })
.visibility(this.caServerCertVisibility)
SelectFile({
title: $r('app.string.vpn_edit_server_private_cert'),
fileName: this.vpnConfig.ipsecPrivateServerCertFilePath,
vpnConfig: this.vpnConfig,
callback: (result: string[]) => {
this.vpnConfig.ipsecPrivateServerCertFilePath = result[0];
this.vpnConfig.ipsecPrivateServerCertConfig = this.base64Util.encodeToStringSync(
VpnConfigModel.getInstance().stringToUint8Array(result[1]));
this.onNonNullableParamChange();
}
}).margin({ top: 10, bottom: 10 })
.visibility(this.caServerCertVisibility)
Row({ space: 10 }) {
Checkbox().shape(CheckBoxShape.ROUNDED_SQUARE).onChange((value) => {
this.advanceChecked = value;
})
Text($r('app.string.vpn_edit_advanced'))
}.width('100%').margin({ left: 4 })
Column({ space: 10 }) {
TextWithInput({
title: $r('app.string.vpn_edit_searchdomains'),
inputPlaceholder: $r('app.string.vpn_edit_unuse'),
inputText: this.vpnConfig.searchDomains?.[0],
onChange: (value: string) => {
let searchDomain = value.trim();
if (searchDomain) {
this.vpnConfig.searchDomains = [searchDomain];
}
}
})
TextWithInput({
title: $r('app.string.vpn_edit_dnsaddresses'),
inputPlaceholder: $r('app.string.vpn_edit_unuse'),
inputText: this.dnsAddress,
inputType: INPUT_TYPE_IP,
onChange: (value: string) => {
this.dnsAddress = value.trim();
if (this.dnsAddress && this.dnsAddress.length > 0) {
this.vpnConfig.dnsAddresses = [this.dnsAddress];
}
this.onNonNullableParamChange();
}
})
TextWithInput({
title: $r('app.string.vpn_edit_router'),
inputPlaceholder: $r('app.string.vpn_edit_unuse'),
inputText: this.vpnConfig.forwardingRoutes,
onChange: (value: string) => {
this.vpnConfig.forwardingRoutes = value.trim();
}
})
}.width('100%')
.alignItems(HorizontalAlign.Start)
.visibility(this.advanceChecked ? Visibility.Visible : Visibility.None)
}
}
}

View File

@ -0,0 +1,274 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ResourceManager from '@ohos.resourceManager';
import {
TextWithInput,
Selector,
SelectFile,
SELECTER_OVPN_PROTOCOL,
SELECTER_OVPN_AUTH,
INPUT_TYPE_IP,
INPUT_TYPE_PORT
} from './customComponent';
import { OpenVpnConfig } from '../../model/vpnImpl/VpnConfig';
import { VpnConfigModel } from '../../model/vpnImpl/VpnConfigModel';
import VpnConstant from '../../model/vpnImpl/VpnConstant';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
import { GlobalContext } from '../../../../../../../common/utils/src/main/ets/default/baseUtil/GlobalContext';
@Component
export default struct OpenVpnEdit {
onSaveBtnEnableChange: Function = (enabled: boolean) => {};
@State vpnConfig: OpenVpnConfig = new OpenVpnConfig();
@State vpnAddress: string = '';
@State vpnPort: string = '';
@State ovpnAdvanceChecked: boolean = false;
@State ovpnProxyChecked: boolean = false;
@State ovpnProxyHost: string = '';
@State ovpnProxyPort: string = '';
ovpnProtocolSheetTitles: string[] = [];
ovpnProtocolSheets: SheetInfo[] = [];
ovpnAuthSheetTitles: string[] = [];
ovpnAuthSheets: SheetInfo[] = [];
aboutToAppear() {
try {
let context = GlobalContext.getContext().getObject(GlobalContext.globalKeySettingsAbilityContext) as Context;
let resMgr: ResourceManager.ResourceManager = context.resourceManager;
this.vpnConfig.ovpnProtocol = this.vpnConfig.ovpnProtocol ?? 0;
this.ovpnProtocolSheetTitles = resMgr.getStringArrayValueSync($r('app.strarray.ovpn_protocol').id);
this.ovpnProtocolSheets = this.ovpnProtocolSheetTitles.map((title, index) => {
let sheetInfo: SheetInfo = {
title: title,
action: () => {
this.vpnConfig.ovpnProtocol = index;
}
}
return sheetInfo;
})
this.vpnConfig.ovpnAuthType = this.vpnConfig.ovpnAuthType ?? 0;
this.ovpnAuthSheetTitles = resMgr.getStringArrayValueSync($r('app.strarray.ovpn_auth_type').id);
this.ovpnAuthSheets = this.ovpnAuthSheetTitles.map((title, index) => {
let sheetInfo: SheetInfo = {
title: title,
action: () => {
this.vpnConfig.ovpnAuthType = index;
}
}
return sheetInfo;
})
} catch (error) {
LogUtil.error(`getStringArrayValueSync failed, error: ${JSON.stringify(error)}.`);
}
this.updateUI();
}
updateUI() {
this.vpnAddress = VpnConfigModel.getInstance().getAddress(this.vpnConfig) ?? '';
this.vpnPort = this.vpnConfig.ovpnPort ?? '';
}
onNonNullableParamChange(): void {
let isNonNullParamReady = true;
if (!VpnConstant.REGEX_IP.test(this.vpnAddress) || !VpnConstant.REGEX_PORT.test(this.vpnPort)) {
isNonNullParamReady = false;
}
if (this.ovpnProxyHost.length > 0 && !VpnConstant.REGEX_IP.test(this.ovpnProxyHost)) {
isNonNullParamReady = false;
}
if (this.ovpnProxyPort.length > 0 && !VpnConstant.REGEX_PORT.test(this.ovpnProxyPort)) {
isNonNullParamReady = false;
}
this.onSaveBtnEnableChange(isNonNullParamReady);
}
build() {
Column() {
SelectFile({
title: $r('app.string.vpn_edit_ovpn_configfile'),
fileName: this.vpnConfig.ovpnConfigFilePath,
vpnConfig: this.vpnConfig,
callback: (result: string[]) => {
VpnConfigModel.getInstance().inflateConfigFromFileData(this.vpnConfig, result);
this.updateUI();
}
});
TextWithInput({
title: $r('app.string.vpn_edit_ovpn_serveraddress'),
inputPlaceholder: '',
inputText: this.vpnAddress,
inputType: INPUT_TYPE_IP,
onChange: (value: string) => {
this.vpnAddress = value.trim();
VpnConfigModel.getInstance().setAddress(this.vpnConfig, this.vpnAddress);
this.onNonNullableParamChange();
}
}).margin({ top: 5 })
TextWithInput({
title: $r('app.string.vpn_edit_ovpn_port'),
inputPlaceholder: '',
inputText: this.vpnPort,
inputType: INPUT_TYPE_PORT,
onChange: (value: string) => {
this.vpnPort = value.trim()
this.vpnConfig.ovpnPort = this.vpnPort;
this.onNonNullableParamChange();
}
})
Selector({
title: $r('app.string.vpn_edit_ovpn_protocol'),
vpnConfig: this.vpnConfig,
type: SELECTER_OVPN_PROTOCOL,
sheetTitles: this.ovpnProtocolSheetTitles,
sheetInfos: this.ovpnProtocolSheets
}).margin({ top: 10, bottom: 10 });
Row({ space: 10 }) {
Checkbox().shape(CheckBoxShape.ROUNDED_SQUARE).onChange((value) => {
this.ovpnAdvanceChecked = value;
})
Text($r('app.string.vpn_edit_advanced'))
}.width('100%').margin({ left: 4, bottom: 10 })
Column({ space: 10 }) {
Selector({
title: $r('app.string.vpn_edit_auth_type'),
vpnConfig: this.vpnConfig,
type: SELECTER_OVPN_AUTH,
sheetTitles: this.ovpnAuthSheetTitles,
sheetInfos: this.ovpnAuthSheets
})
SelectFile({
title: $r('app.string.vpn_edit_ovpn_ca_cert_file'),
fileName: this.vpnConfig.ovpnCaCertFilePath,
vpnConfig: this.vpnConfig,
callback: (result: string[]) => {
this.vpnConfig.ovpnCaCertFilePath = result[0];
this.vpnConfig.ovpnCaCert = '<ca>\n' + result[1] + '\n</ca>';
}
});
SelectFile({
title: $r('app.string.vpn_edit_ovpn_user_cert_file'),
fileName: this.vpnConfig.ovpnUserCertFilePath,
vpnConfig: this.vpnConfig,
callback: (result: string[]) => {
this.vpnConfig.ovpnUserCertFilePath = result[0];
this.vpnConfig.ovpnUserCert = '<cert>\n' + result[1] + '\n</cert>';
}
}).visibility(this.vpnConfig.ovpnAuthType !== VpnConstant.OVPN_AUTH_TYPE_PWD ?
Visibility.Visible : Visibility.None);
SelectFile({
title: $r('app.string.vpn_edit_ovpn_private_key'),
fileName: this.vpnConfig.ovpnPrivateKeyFilePath,
vpnConfig: this.vpnConfig,
callback: (result: string[]) => {
this.vpnConfig.ovpnPrivateKeyFilePath = result[0];
this.vpnConfig.ovpnPrivateKey = '<key>\n' + result[1] + '\n</key>';
}
}).visibility(this.vpnConfig.ovpnAuthType !== VpnConstant.OVPN_AUTH_TYPE_PWD ?
Visibility.Visible : Visibility.None);
TextWithInput({
title: $r('app.string.vpn_edit_ovpn_private_key_psd'),
inputPlaceholder: '',
inputText: this.vpnConfig.askpass,
onChange: (value: string) => {
this.vpnConfig.askpass = value.trim();
}
}).visibility(this.vpnConfig.ovpnAuthType !== VpnConstant.OVPN_AUTH_TYPE_PWD ?
Visibility.Visible : Visibility.None);
TextWithInput({
title: $r('app.string.vpn_edit_ovpn_username'),
inputPlaceholder: '',
inputText: this.vpnConfig.userName,
maxLength: VpnConstant.VPN_USER_NAME_MAX_LENGTH,
onChange: (value: string) => {
this.vpnConfig.userName = value.trim();
}
}).visibility(this.vpnConfig.ovpnAuthType !== VpnConstant.OVPN_AUTH_TYPE_TLS ?
Visibility.Visible : Visibility.None);
TextWithInput({
title: $r('app.string.vpn_edit_ovpn_password'),
inputPlaceholder: '',
inputText: this.vpnConfig.password,
maxLength: VpnConstant.VPN_PASSWORD_MAX_LENGTH,
onChange: (value: string) => {
this.vpnConfig.password = value.trim();
}
}).visibility(this.vpnConfig.ovpnAuthType !== VpnConstant.OVPN_AUTH_TYPE_TLS ?
Visibility.Visible : Visibility.None);
Row({ space: 10 }) {
Checkbox().shape(CheckBoxShape.ROUNDED_SQUARE).onChange((value) => {
this.ovpnProxyChecked = value;
})
Text($r('app.string.vpn_edit_proxy_advanced'))
}.width('100%').margin({ left: 4 })
Column({ space: 10 }) {
TextWithInput({
title: $r('app.string.vpn_edit_ovpn_proxy_host'),
inputPlaceholder: $r('app.string.vpn_edit_unuse'),
inputText: this.ovpnProxyHost,
inputType: INPUT_TYPE_IP,
onChange: (value: string) => {
this.ovpnProxyHost = value.trim();
this.vpnConfig.ovpnProxyHost = this.ovpnProxyHost;
this.onNonNullableParamChange();
}
})
TextWithInput({
title: $r('app.string.vpn_edit_ovpn_proxy_port'),
inputPlaceholder: $r('app.string.vpn_edit_unuse'),
inputText: this.ovpnProxyPort,
inputType: INPUT_TYPE_PORT,
onChange: (value: string) => {
this.ovpnProxyPort = value.trim();
this.vpnConfig.ovpnProxyPort = this.ovpnProxyPort;
this.onNonNullableParamChange();
}
})
TextWithInput({
title: $r('app.string.vpn_edit_ovpn_proxy_username'),
inputPlaceholder: $r('app.string.vpn_edit_unuse'),
inputText: this.vpnConfig.ovpnProxyUser,
onChange: (value: string) => {
this.vpnConfig.ovpnProxyUser = value.trim();
}
})
TextWithInput({
title: $r('app.string.vpn_edit_ovpn_proxy_password'),
inputPlaceholder: $r('app.string.vpn_edit_unuse'),
inputText: this.vpnConfig.ovpnProxyPass,
onChange: (value: string) => {
this.vpnConfig.ovpnProxyPass = value.trim();
}
})
}.visibility(this.ovpnProxyChecked ? Visibility.Visible : Visibility.None)
}.visibility(this.ovpnAdvanceChecked ? Visibility.Visible : Visibility.None)
}
}
}

View File

@ -0,0 +1,230 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BusinessError } from '@ohos.base';
import deviceInfo from '@ohos.deviceInfo';
import router from '@ohos.router';
import util from '@ohos.util';
import vpn from '@ohos.net.vpn';
import { VpnConnectModel } from '../../model/vpnImpl/VpnConnectModel';
import VpnConfig, { VpnListItem } from '../../model/vpnImpl/VpnConfig';
import { SwanCtlModel } from '../../model/vpnImpl/SwanCtlModel';
import VpnConstant from '../../model/vpnImpl/VpnConstant';
import { VpnConfigModel } from '../../model/vpnImpl/VpnConfigModel';
import { ResourceUtils } from '../../model/accessibilityImpl/resourceUtils';
import HeadComponent from '../../../../../../../common/component/src/main/ets/default/headComponent';
import ConfigData from '../../../../../../../common/utils/src/main/ets/default/baseUtil/ConfigData';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
const deviceTypeInfo = deviceInfo.deviceType;
const MODULE_TAG: string = 'setting_vpn:VpnConnect:';
@Entry
@Component
export struct VpnConnect {
@StorageLink('connectState') connectState: number = VpnConstant.VPN_STATE_NONE;
@State vpnConfig: VpnConfig = new VpnConfig();
@State vpnUserName: string = '';
@State vpnPassword: string = '';
@State isSetupBtnEnabled: boolean = false;
async aboutToAppear() {
let state: number | undefined = AppStorage.get(VpnConstant.STORAGE_KEY_CONNECT_STATE);
if (state) {
this.connectState = state;
}
let vpnItem: VpnListItem = router.getParams() as VpnListItem;
if (vpnItem && vpnItem.vpnId) {
LogUtil.info(MODULE_TAG + `vpnid = ${JSON.stringify(vpnItem.vpnId)}`)
try {
let data: vpn.SysVpnConfig = await vpn.getSysVpnConfig(vpnItem.vpnId);
if (data) {
this.vpnConfig = data as VpnConfig;
if (this.vpnConfig.saveLogin) {
this.vpnUserName = this.vpnConfig.userName ?? '';
this.vpnPassword = this.vpnConfig.password ?? '';
}
this.setConnectBtnEnabled();
} else {
VpnConfigModel.getInstance().showToast($r('app.string.vpn_error_operation_failed'));
}
} catch (err) {
let message = (err as BusinessError).message;
VpnConfigModel.getInstance().showToast(
ResourceUtils.getStringSync($r('app.string.vpn_error_operation_failed')) + ': ' + message);
}
} else {
LogUtil.error(MODULE_TAG + `getParams is error`)
}
}
build() {
Column() {
HeadComponent({
headName: $r('app.string.vpn_connect_title', this.vpnConfig.vpnName),
isActive: true
}).padding({ right: 35 })
Column() {
Column() {
Text($r('app.string.vpn_connect_username'))
.width('100%')
TextInput({ text: this.vpnUserName })
.padding({ left: 1 })
.fontSize($r('app.float.font_16'))
.maxLength(VpnConstant.VPN_USER_NAME_MAX_LENGTH)
.backgroundColor('#00000000')
.height(40)
.onChange((value) => {
value = value.replace(/\s+/g, '');
this.vpnUserName = value.trim();
this.setConnectBtnEnabled();
})
Divider()
.color($r('sys.color.ohos_id_color_list_separator'))
Text($r('app.string.vpn_connect_password'))
.width('100%')
.margin({ top: 15 })
TextInput({ text: this.vpnPassword })
.padding({ left: 1 })
.type(InputType.Password)
.maxLength(VpnConstant.VPN_PASSWORD_MAX_LENGTH)
.fontSize($r('app.float.font_16'))
.backgroundColor('#00000000')
.height(40)
.onChange((value) => {
value = value.replace(/\s+/g, '');
this.vpnPassword = value.trim();
this.setConnectBtnEnabled();
})
Divider()
.color($r('sys.color.ohos_id_color_list_separator'))
Row({ space: 8 }) {
Checkbox().select(this.vpnConfig.saveLogin ?? false).onChange((value) => {
this.vpnConfig.saveLogin = value;
}).shape(CheckBoxShape.ROUNDED_SQUARE)
Text($r('app.string.vpn_connect_save'))
}.width('100%').margin({ top: 15 })
}
Blank().layoutWeight(1)
Row() {
Button($r('app.string.cancel'))
.fontColor('#0A59F7')
.fontSize(16)
.height(45)
.fontWeight(FontWeight.Bold)
.padding({ top: 10, left: 60, right: 60, bottom: 10 })
.borderRadius($r('app.float.radius_20'))
.backgroundColor('#E6E8E9')
.layoutWeight(1)
.onClick(() => {
router.back();
})
Blank().width(30)
Button($r('app.string.vpn_connect_confirm'))
.fontColor(this.isSetupBtnEnabled ? '#0A59F7' : '#92B3F3')
.fontSize(16)
.height(45)
.fontWeight(FontWeight.Bold)
.padding({ top: 10, left: 60, right: 60, bottom: 10 })
.borderRadius($r('app.float.radius_20'))
.backgroundColor('#E6E8E9')
.layoutWeight(1)
.enabled(this.isSetupBtnEnabled)
.onClick(() => {
this.onSetupBtnClick();
})
}.margin({ bottom: 65 })
}
.padding($r('app.float.distance_4'))
}
.width(ConfigData.WH_100_100)
.height(ConfigData.WH_100_100)
.padding({ left: ConfigData.GRID_CONTAINER_MARGIN_24, right: ConfigData.GRID_CONTAINER_MARGIN_24, bottom: 20 })
.backgroundColor($r('sys.color.ohos_id_color_sub_background'))
}
async onSetupBtnClick() {
LogUtil.info(MODULE_TAG + `onSetupBtnClick current connect = ${JSON.stringify(this.connectState)}`);
if (this.connectState === VpnConstant.VPN_STATE_CONNECTED ||
this.connectState === VpnConstant.VPN_STATE_CONNECTING) {
this.showVpnChangeDialog();
return;
}
if ((!this.vpnConfig.userName || !this.vpnConfig.password) ||
this.vpnUserName !== this.vpnConfig.userName || this.vpnPassword !== this.vpnConfig.password) {
this.vpnConfig.userName = this.vpnUserName;
this.vpnConfig.password = this.vpnPassword;
// name and pwd changed, need to rebuild config
this.vpnConfig = SwanCtlModel.getInstance().buildConfig(this.vpnConfig);
if (!this.vpnConfig.vpnId || this.vpnConfig.vpnId === '') {
this.vpnConfig.vpnId = util.generateRandomUUID();
}
try {
await vpn.addSysVpnConfig(this.vpnConfig);
VpnConnectModel.getInstance().setUp(this.vpnConfig);
} catch (err) {
let message = (err as BusinessError).message;
VpnConfigModel.getInstance().showToast(ResourceUtils.getStringSync($r('app.string.vpn_error_operation_failed')) + ': ' + message);
}
router.back();
return;
}
VpnConnectModel.getInstance().setUp(this.vpnConfig);
router.back();
}
setConnectBtnEnabled() {
if (this.vpnUserName === '' || this.vpnPassword === '') {
this.isSetupBtnEnabled = false;
} else {
this.isSetupBtnEnabled = true;
}
}
showVpnChangeDialog(): void {
AlertDialog.show({
title: $r('app.string.vpn_change_alert_title'),
message: $r('app.string.vpn_change_alert_content'),
primaryButton: {
value: $r('app.string.cancel'),
action: () => {
LogUtil.info('dialog cancel callbacks');
}
},
secondaryButton: {
value: $r('app.string.vpn_change_alert_confirm'),
action: () => {
VpnConnectModel.getInstance().setConnectState(VpnConstant.VPN_STATE_DISCONNECTING);
VpnConnectModel.getInstance().setReplaceConnectVpn(this.vpnConfig);
VpnConnectModel.getInstance().destroy((error: string) => {
if (error) {
LogUtil.info(MODULE_TAG + `vpn destroy failed, error:` + error);
}
});
router.back();
}
},
alignment: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ?
DialogAlignment.Bottom : DialogAlignment.Center,
offset: ({ dx: 0, dy: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? '-24dp' : 0 })
})
}
}

View File

@ -0,0 +1,263 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BusinessError } from '@ohos.base';
import router from '@ohos.router';
import vpn from '@ohos.net.vpn';
import util from '@ohos.util';
import window from '@ohos.window';
import OpenVpnEdit from './openVpnEdit';
import IpsecVpnEdit from './ipsecVpnEdit';
import { TextWithInput, Selector, SELECTER_VPNTYPE, deviceTypeInfo } from './customComponent';
import { VpnTypeModel } from '../../model/vpnImpl/VpnTypeModel';
import { VpnConnectModel } from '../../model/vpnImpl/VpnConnectModel';
import VpnConstant from '../../model/vpnImpl/VpnConstant';
import { VpnConfigModel } from '../../model/vpnImpl/VpnConfigModel';
import { ResourceUtils } from '../../model/accessibilityImpl/resourceUtils';
import VpnConfig, { OpenVpnConfig, IpsecVpnConfig } from '../../model/vpnImpl/VpnConfig';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
import ConfigData from '../../../../../../../common/utils/src/main/ets/default/baseUtil/ConfigData';
import HeadComponent from '../../../../../../../common/component/src/main/ets/default/headComponent';
const MODULE_TAG: string = 'setting_vpn:VpnEdit:';
@Entry
@Component
export struct VpnEdit {
@State vpnConfig: VpnConfig = new VpnConfig();
@State isSaveBtnEnabled: boolean = false;
@State isDelBtnVisibility: Visibility = Visibility.None;
protocolSheets: SheetInfo[] = [];
aboutToAppear() {
window.getLastWindow(getContext(this), (err, w) => {
w.setWindowSystemBarProperties({
statusBarColor: '#ffffff'
})
})
let param = router.getParams();
if (param) {
this.vpnConfig = param as VpnConfig;
}
this.isDelBtnVisibility = this.vpnConfig.vpnName ? Visibility.Visible : Visibility.None;
this.protocolSheets = VpnTypeModel.getInstance().getSupportVpnTypeStrs().map((str, index) => {
let s: SheetInfo = {
title: str,
action: () => {
this.vpnConfig.vpnType = (index < VpnTypeModel.getInstance().getSupportVpnTypes().length) ?
VpnTypeModel.getInstance().getSupportVpnTypes()[index] : 0;
}
}
return s;
})
}
onSaveBtnEnableChange: Function = (enabled: boolean) => {
this.isSaveBtnEnabled = enabled && this.vpnConfig.vpnName.length > 0;
}
async onSaveClick() {
if (!this.vpnConfig.vpnName || this.vpnConfig.vpnName.length < 1) {
VpnConfigModel.getInstance().showToast($r('app.string.vpn_error_invalid_param'));
return;
}
// destroy connect when vpn connected
if (VpnConnectModel.getInstance().isConnectedOrConnecting(this.vpnConfig.vpnId)) {
VpnConfigModel.getInstance().showToast($r('app.string.vpn_error_disconnect_first'))
return;
}
VpnConfigModel.getInstance().prepareAddSysVpnConfig(this.vpnConfig);
if (!this.vpnConfig.vpnId || this.vpnConfig.vpnId === '') {
this.vpnConfig.vpnId = util.generateRandomUUID();
}
try {
await vpn.addSysVpnConfig(this.vpnConfig);
VpnConfigModel.getInstance().setNeedUpdateVpnList(true);
this.backToListPage();
} catch (err) {
let message = (err as BusinessError).message;
VpnConfigModel.getInstance().showToast(
ResourceUtils.getStringSync($r('app.string.vpn_error_operation_failed')) + ': ' + message);
}
}
backToListPage() {
router.back();
}
async onDeleteClick() {
if (!this.vpnConfig) {
LogUtil.error(MODULE_TAG + 'onDeleteClick failed, vpn config is error');
return;
}
if (!this.vpnConfig.vpnId || this.vpnConfig.vpnId === '') {
LogUtil.error(MODULE_TAG + 'onDeleteClick failed, vpnId is error');
return;
}
try {
await vpn.deleteSysVpnConfig(this.vpnConfig.vpnId);
VpnConfigModel.getInstance().setNeedUpdateVpnList(true);
this.backToListPage();
}catch (err){
let message = (err as BusinessError).message;
VpnConfigModel.getInstance().showToast(
ResourceUtils.getStringSync($r('app.string.vpn_error_operation_failed')) + ': ' + message);
}
}
showDeleteDialog(): void {
AlertDialog.show({
message: $r('app.string.vpn_line_remove_alert_title', this.vpnConfig.vpnName),
primaryButton: {
value: $r('app.string.cancel'),
action: () => {
LogUtil.info('dialog cancel callbacks');
}
},
secondaryButton: {
fontColor: $r('sys.color.ohos_id_color_warning'),
value: $r('app.string.vpn_line_remove_alert_confirm'),
action: () => {
this.onDeleteClick();
}
},
alignment: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ?
DialogAlignment.Bottom : DialogAlignment.Center,
offset: ({ dx: 0, dy: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? '-24dp' : 0 })
})
}
build() {
Column() {
GridContainer({ gutter: ConfigData.GRID_CONTAINER_GUTTER_24, margin: ConfigData.GRID_CONTAINER_MARGIN_24 }) {
RelativeContainer() {
HeadComponent({ headName: $r('app.string.vpn_edit_title'), isActive: true }).alignRules({
top: { anchor: '__container__', align: VerticalAlign.Top },
left: { anchor: '__container__', align: HorizontalAlign.Start }
}).id('head')
Scroll() {
Column({ space: 10 }) {
TextWithInput({
title: $r('app.string.vpn_edit_alias'),
inputPlaceholder: '',
inputText: this.vpnConfig.vpnName,
maxLength: VpnConstant.VPN_NAME_MAX_LENGTH,
onChange: (value: string) => {
this.vpnConfig.vpnName = value.trim();
this.onSaveBtnEnableChange(this.vpnConfig.vpnName.length > 0 ? true : false);
}
})
Text($r('app.string.vpn_line_disconnect_operation_remove'))
.fontColor($r('sys.color.ohos_id_color_badge_red'))
.fontWeight(FontWeight.Bold)
.width(ConfigData.WH_100_100)
.textAlign(TextAlign.Center)
.onClick(() => {
if (VpnConnectModel.getInstance().isConnectedOrConnecting(this.vpnConfig.vpnId)) {
VpnConfigModel.getInstance().showToast($r('app.string.vpn_error_disconnect_first'))
} else {
this.showDeleteDialog();
}
})
.borderRadius($r('app.float.radius_16'))
.backgroundColor('#ffffff')
.width('100%')
.height($r('app.float.wh_value_52'))
.borderRadius($r('app.float.radius_16'))
.padding({ left: 8, right: 8 })
.visibility(this.isDelBtnVisibility)
Selector({
title: $r('app.string.vpn_edit_type'),
vpnConfig: this.vpnConfig,
type: SELECTER_VPNTYPE,
sheetTitles: VpnTypeModel.getInstance().getSupportVpnTypeStrs(),
sheetInfos: this.protocolSheets,
}).enabled(this.isDelBtnVisibility !== Visibility.Visible)
if (this.vpnConfig.vpnType === VpnTypeModel.TYPE_OPENVPN) {
OpenVpnEdit({
vpnConfig: this.vpnConfig as OpenVpnConfig,
onSaveBtnEnableChange: this.onSaveBtnEnableChange,
})
} else {
IpsecVpnEdit({
vpnConfig: this.vpnConfig as IpsecVpnConfig,
onSaveBtnEnableChange: this.onSaveBtnEnableChange,
})
}
Blank().layoutWeight(1)
}.alignItems(HorizontalAlign.Start).width(ConfigData.WH_100_100)
}.alignRules({
top: { anchor: 'head', align: VerticalAlign.Bottom },
left: { anchor: '__container__', align: HorizontalAlign.Start },
right: { anchor: '__container__', align: HorizontalAlign.End },
bottom: { anchor: 'bottom', align: VerticalAlign.Top }
}).id('content')
Row() {
Button($r('app.string.cancel'))
.fontColor('#0A59F7')
.fontSize(16)
.height(45)
.layoutWeight(1)
.fontWeight(FontWeight.Bold)
.padding({ top: 10, left: 60, right: 60, bottom: 10 })
.borderRadius($r('app.float.radius_20'))
.backgroundColor('#E6E8E9')
.onClick(() => {
this.backToListPage();
})
Blank().width(30)
Button($r('app.string.vpn_edit_save'))
.fontColor(this.isSaveBtnEnabled ? '#0A59F7' : '#92B3F3')
.fontSize(16)
.height(45)
.layoutWeight(1)
.fontWeight(FontWeight.Bold)
.padding({ top: 10, left: 60, right: 60, bottom: 10 })
.borderRadius($r('app.float.radius_20'))
.backgroundColor('#E6E8E9')
.enabled(this.isSaveBtnEnabled)
.background()
.onClick(() => {
this.onSaveClick();
})
}.margin(15).alignRules({
bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
left: { anchor: '__container__', align: HorizontalAlign.Start }
}).id('bottom')
}.width(ConfigData.WH_100_100).height(ConfigData.WH_100_100)
.useSizeType({
sm: { span: 4, offset: 0 },
md: { span: 6, offset: 1 },
lg: { span: 8, offset: 2 }
})
}
.width(ConfigData.WH_100_100)
.height(ConfigData.WH_100_100)
}
.backgroundColor($r('sys.color.ohos_id_color_sub_background'))
.width(ConfigData.WH_100_100)
.height(ConfigData.WH_100_100)
}
}

View File

@ -0,0 +1,235 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BusinessError } from '@ohos.base';
import deviceInfo from '@ohos.deviceInfo';
import vpn from '@ohos.net.vpn';
import router from '@ohos.router';
import { VpnConnectModel } from '../../model/vpnImpl/VpnConnectModel';
import VpnConfig, { VpnListItem } from '../../model/vpnImpl/VpnConfig';
import { VpnConfigModel } from '../../model/vpnImpl/VpnConfigModel';
import VpnConstant from '../../model/vpnImpl/VpnConstant';
import { ResourceUtils } from '../../model/accessibilityImpl/resourceUtils';
import ComponentConfig from '../../../../../../../common/component/src/main/ets/default/ComponentConfig';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
const MODULE_TAG: string = 'setting_vpn:VpnLine:';
const deviceTypeInfo = deviceInfo.deviceType;
/**
* item custom component
*/
@Entry
@Component
export default struct VpnLine {
@StorageLink(VpnConstant.STORAGE_KEY_CONNECT_STATE) @Watch('onConnectStateChange') connectState: number =
VpnConstant.VPN_STATE_NONE;
@State connectStateLabel: string | Resource = '';
@State vpnListItem: VpnListItem | undefined = undefined;
@State titleFontColor: Resource = $r('sys.color.ohos_id_color_text_primary');
@State isTouched: boolean = false;
@State heights: Resource = $r('app.float.wh_value_72');
@State fontSize: Resource = $r('sys.float.ohos_id_text_size_body1');
@State valueFontSize: Resource = $r('sys.float.ohos_id_text_size_body2');
private isEnabled: boolean = true;
@State editPopupVisibility: boolean = false
showDisconnectDialog(): void {
AlertDialog.show({
message: $r('app.string.vpn_line_disconnect_alert_title', this.vpnListItem!.vpnName),
primaryButton: {
value: $r('app.string.cancel'),
action: () => {
LogUtil.info('dialog cancel callbacks');
}
},
secondaryButton: {
fontColor: $r('sys.color.ohos_id_color_warning'),
value: $r('app.string.vpn_line_disconnect_alert_confirm'),
action: () => {
VpnConnectModel.getInstance().setConnectState(VpnConstant.VPN_STATE_DISCONNECTING);
VpnConnectModel.getInstance().destroy((error: string) => {
if (error) {
LogUtil.info(MODULE_TAG + `vpn destroy failed, error:` + error);
}
});
}
},
alignment: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ?
DialogAlignment.Bottom : DialogAlignment.Center,
offset: ({ dx: 0, dy: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? '-24dp' : 0 })
})
}
aboutToAppear(): void {
let state: number | undefined = AppStorage.get(VpnConstant.STORAGE_KEY_CONNECT_STATE);
if (state) {
this.connectState = state;
if (this.connectState === VpnConstant.VPN_STATE_CONNECTED &&
this.vpnListItem!.vpnId === VpnConnectModel.getInstance().getConnectedVpnId()) {
this.connectStateLabel = $r('app.string.vpn_state_connec_success');
}
}
}
async onEditBtnClick() {
if (VpnConnectModel.getInstance().isConnecting(this.vpnListItem!.vpnId)) {
this.showDisconnectDialog();
return;
}
try {
let data: vpn.SysVpnConfig = await vpn.getSysVpnConfig(this.vpnListItem?.vpnId);
if (data) {
router.pushUrl({
url: 'pages/vpn/vpnEdit',
params: data as VpnConfig
});
}
} catch (err) {
let message = (err as BusinessError).message;
VpnConfigModel.getInstance().showToast(ResourceUtils.getStringSync($r('app.string.vpn_error_operation_failed')) + ': ' + message);
}
}
onListItemClick() {
if (VpnConnectModel.getInstance().isConnectedOrConnecting(this.vpnListItem!.vpnId)) {
this.showDisconnectDialog();
} else {
router.pushUrl({
url: 'pages/vpn/vpnConnect',
params: this.vpnListItem
});
}
}
onConnectStateChange(): void {
if (this.vpnListItem!.vpnId === VpnConnectModel.getInstance().getConnectedVpnId()) {
LogUtil.info(MODULE_TAG + 'onConnectStateChange state=' + this.connectState);
switch (this.connectState) {
case VpnConstant.VPN_STATE_CONNECTING:
this.connectStateLabel = $r('app.string.vpn_state_connect_ing');
return;
case VpnConstant.VPN_STATE_CONNECTED:
this.connectStateLabel = $r('app.string.vpn_state_connec_success');
return;
case VpnConstant.VPN_STATE_DISCONNECTING:
this.connectStateLabel = $r('app.string.vpn_state_disconnecting');
return;
case VpnConstant.VPN_STATE_DISCONNECTED:
this.connectStateLabel = $r('app.string.vpn_state_disconnected');
return;
case VpnConstant.VPN_STATE_CONNECT_FAILED:
this.connectStateLabel = $r('app.string.vpn_state_connec_failed');
return;
default:
this.connectStateLabel = '';
return;
}
}
this.connectStateLabel = '';
}
build() {
Row() {
Row() {
Image($r('app.media.ic_vpn'))
.width(30)
.height(16)
.margin({
right: $r('app.float.wh_value_16')
})
Column() {
Text(this.vpnListItem?.vpnName ?? '')
.fontColor(this.isEnabled ? this.titleFontColor : $r('sys.color.ohos_id_color_primary'))
.fontSize(this.fontSize)
.fontWeight(FontWeight.Medium)
.align(Alignment.Start)
.maxLines(ComponentConfig.MAX_LINES_1)
.width(ComponentConfig.WH_100_100)
.ellipsisMode(EllipsisMode.END)
.padding({ right: 50 })
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(this.connectStateLabel)
.fontColor($r('sys.color.ohos_id_color_mask_light'))
.fontSize(this.valueFontSize)
.textAlign(TextAlign.Start)
.fontWeight(FontWeight.Regular)
.maxLines(ComponentConfig.MAX_LINES_1)
.width(ComponentConfig.WH_100_100)
.ellipsisMode(EllipsisMode.END)
.padding({ right: 50 })
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ top: 8 })
.visibility(this.connectStateLabel.toString().length > 0 ? Visibility.Visible : Visibility.None)
}
.alignItems(HorizontalAlign.Start)
.width('80%')
Row() {
Image($r('app.media.ic_public_detail'))
.width(24)
.height(24)
}
.width($r('app.float.wh_value_50'))
.height(ComponentConfig.WH_100_100)
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)
.margin({
right: $r('app.float.wh_value_16')
})
.onClick(() => {
this.onEditBtnClick();
})
}
.flexShrink(0)
.alignItems(VerticalAlign.Center)
.align(Alignment.Start)
Blank()
}
.width(ComponentConfig.WH_100_100)
.padding({
left: $r('sys.float.ohos_id_card_margin_end'),
right: $r('sys.float.ohos_id_card_margin_end'),
top: $r('sys.float.ohos_id_elements_margin_vertical_m'),
bottom: $r('sys.float.ohos_id_elements_margin_vertical_m')
})
.alignItems(VerticalAlign.Center)
.height('62vp')
.borderRadius($r('app.float.radius_16'))
.linearGradient(this.isTouched ? {
angle: 90,
direction: GradientDirection.Right,
colors: [[$r('app.color.DCEAF9'), 0.0], [$r('app.color.FAFAFA'), 1.0]]
} : {
angle: 90,
direction: GradientDirection.Right,
colors: [[$r('sys.color.ohos_id_color_foreground_contrary'), 1],
[$r('sys.color.ohos_id_color_foreground_contrary'), 1]]
})
.onTouch((event?: TouchEvent | undefined) => {
if (event?.type === TouchType.Down) {
this.isTouched = true;
}
if (event?.type === TouchType.Up) {
this.isTouched = false;
}
})
.onClick(() => {
this.onListItemClick();
})
}
}

View File

@ -0,0 +1,190 @@
/**
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BusinessError } from '@ohos.base';
import common from '@ohos.app.ability.common';
import router from '@ohos.router';
import vpn from '@ohos.net.vpn';
import VpnLine from './vpnLine';
import VpnConfig, { VpnListItem } from '../../model/vpnImpl/VpnConfig';
import { VpnConnectModel } from '../../model/vpnImpl/VpnConnectModel';
import { SwanCtlModel } from '../../model/vpnImpl/SwanCtlModel';
import { VpnConfigModel } from '../../model/vpnImpl/VpnConfigModel';
import { ResourceUtils } from '../../model/accessibilityImpl/resourceUtils';
import ConfigData from '../../../../../../../common/utils/src/main/ets/default/baseUtil/ConfigData';
import { BaseData } from '../../../../../../../common/utils/src/main/ets/default/bean/BaseData';
import HeadComponent from '../../../../../../../common/component/src/main/ets/default/headComponent';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
const MODULE_TAG: string = 'setting_vpn:VPNList:';
/**
* VPN list
*/
@Entry
@Component
struct VPNList {
@State vpnList: VpnListItem[] = [];
@State isLoading: boolean = false;
build() {
Column() {
GridContainer({ gutter: ConfigData.GRID_CONTAINER_GUTTER_24, margin: ConfigData.GRID_CONTAINER_MARGIN_24 }) {
Column() {
HeadComponent({ headName: $r('app.string.VPN'), isActive: true })
if (this.isLoading) {
Column() {
LoadingProgress()
.color(Color.White)
.width(80)
}.width('100%').height('100%').justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
} else {
if (this.vpnList!.length > 0 ) {
Column() {
List() {
ForEach(this.vpnList, (item: VpnListItem, index: number) => {
ListItem() {
VpnLine({
vpnListItem: item,
fontSize: $r('sys.float.ohos_id_text_size_body1'),
valueFontSize: $r('sys.float.ohos_id_text_size_body2'),
})
}.padding(3)
}, (item: BaseData) => JSON.stringify(item));
}
.margin({ top: $r('app.float.distance_8') })
.align(Alignment.Top)
.borderRadius($r('app.float.radius_16'))
.backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
.divider({
strokeWidth: $r('app.float.divider_wh'),
color: $r('sys.color.ohos_id_color_list_separator'),
startMargin: $r('app.float.wh_value_48'),
endMargin: $r('app.float.wh_value_8')
})
}
.width(ConfigData.WH_100_100)
.height(ConfigData.WH_100_100)
.layoutWeight(1)
Column({ space: 5 }) {
Image($r('app.media.ic_public_add'))
.width(20)
.height(20)
Text($r('app.string.vpn_list_add'))
.fontColor('#333333')
.fontSize(14)
.fontWeight(FontWeight.Medium)
}
.margin({ bottom: 25, top: 25 })
.onClick(() => {
this.onAddBtnClick();
})
} else {
Column() {
Image($r('app.media.empty'))
.width(200)
.height(200)
Text($r('app.string.vpn_list_empty'))
.fontSize(15)
.fontColor('#999999')
}
.justifyContent(FlexAlign.Center)
.width(ConfigData.WH_100_100)
.height(ConfigData.WH_100_100)
.layoutWeight(1)
Column({ space: 5 }) {
Button($r('app.string.vpn_list_add'))
.fontColor('#0A59F7')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.height(45)
.padding({ top: 10, left: 60, right: 60, bottom: 10 })
.borderRadius($r('app.float.radius_24'))
.backgroundColor('#E6E8E9')
.onClick(() => {
this.onAddBtnClick();
})
}.margin({ bottom: 25, top: 25 })
}
}
}
.useSizeType({
sm: { span: 4, offset: 0 },
md: { span: 6, offset: 1 },
lg: { span: 8, offset: 2 }
})
}
.width(ConfigData.WH_100_100)
.height(ConfigData.WH_100_100)
}
.backgroundColor($r('sys.color.ohos_id_color_sub_background'))
.width(ConfigData.WH_100_100)
.height(ConfigData.WH_100_100)
}
onAddBtnClick() {
router.pushUrl({
url: 'pages/vpn/vpnEdit',
});
}
async updateVpnList() {
this.isLoading = true;
try {
let data: vpn.SysVpnConfig[] = await vpn.getSysVpnConfigList();
if (data) {
let list = data as VpnListItem[];
let currentVpnId = VpnConnectModel.getInstance().getConnectedVpnId();
list.sort((item1, item2) => {
if (currentVpnId! && item1.vpnId === currentVpnId) {
return -1;
} else if (currentVpnId! && item2.vpnId === currentVpnId) {
return 1;
} else {
return item1.vpnName.localeCompare(item2.vpnName, 'zh');
}
});
this.vpnList = list;
} else {
this.vpnList = [];
}
} catch (err) {
let message = (err as BusinessError).message;
VpnConfigModel.getInstance().showToast(ResourceUtils.getStringSync($r('app.string.vpn_error_operation_failed')) + ': ' + message);
}
this.isLoading = false;
}
aboutToAppear(): void {
LogUtil.info(MODULE_TAG + 'aboutToAppear');
VpnConfigModel.getInstance().setNeedUpdateVpnList(true);
VpnConnectModel.getInstance().init(getContext() as common.UIAbilityContext);
SwanCtlModel.getInstance().init(getContext() as common.UIAbilityContext);
}
onPageShow(): void {
if (VpnConfigModel.getInstance().isUpdateVpnList()) {
this.updateVpnList();
VpnConfigModel.getInstance().setNeedUpdateVpnList(false);
}
}
aboutToDisappear(): void {
LogUtil.info(MODULE_TAG + 'aboutToDisappear');
VpnConnectModel.getInstance().release();
}
}

View File

@ -2,7 +2,7 @@
"module": {
"name": "phone",
"type": "entry",
"srcEntrance": "./ets/Application/AbilityStage.ts",
"srcEntry": "./ets/Application/AbilityStage.ts",
"description": "$string:mainability_description",
"mainElement": "com.ohos.settings.MainAbility",
"deviceTypes": [
@ -22,7 +22,7 @@
"abilities": [
{
"name": "com.ohos.settings.MainAbility",
"srcEntrance": "./ets/MainAbility/MainAbility.ts",
"srcEntry": "./ets/MainAbility/MainAbility.ts",
"description": "$string:mainability_description",
"icon": "$media:app_icon",
"label": "$string:entry_MainAbility",
@ -43,7 +43,7 @@
},
{
"name": "com.ohos.settings.AppInfoAbility",
"srcEntrance": "./ets/MainAbility/AppInfoAbility.ts",
"srcEntry": "./ets/MainAbility/AppInfoAbility.ts",
"description": "$string:applicationInfo",
"icon": "$media:app_icon",
"label": "$string:applicationInfo",
@ -227,6 +227,9 @@
{
"name": "ohos.permission.sec.ACCESS_UDID",
"reason": "$string:GET_ACCESS_UDID"
},
{
"name": "ohos.permission.MANAGE_VPN"
}
]
}

View File

@ -83,6 +83,10 @@
{
"name": "white",
"value": "#FFFFFF"
},
{
"name": "FA2A2D",
"value": "#FA2A2D"
}
]
}

View File

@ -0,0 +1,29 @@
{
"strarray": [
{
"name": "ovpn_auth_type",
"value": [
{
"value": "证书(TLS)"
},
{
"value": "密码"
},
{
"value": "证书和密码(TLS)"
}
]
},
{
"name": "ovpn_protocol",
"value": [
{
"value": "TCP"
},
{
"value": "UDP"
}
]
}
]
}

View File

@ -1491,6 +1491,282 @@
{
"name": "GET_ACCESS_UDID",
"value": "获取UDID的权限"
},
{
"name": "vpn_change_alert_title",
"value": "要替换现有VPN吗?"
},
{
"name": "vpn_change_alert_content",
"value": "您已连接到VPN,如果您要连接到其他VPN,则系统替换现有VPN."
},
{
"name": "vpn_change_alert_confirm",
"value": "替换"
},
{
"name": "vpn_connect_title",
"value": "连接到%s"
},
{
"name": "vpn_connect_username",
"value": "用户名"
},
{
"name": "vpn_connect_password",
"value": "密码"
},
{
"name": "vpn_connect_save",
"value": "保存账户信息"
},
{
"name": "vpn_connect_confirm",
"value": "连接"
},
{
"name": "vpn_edit_title",
"value": "编辑VPN网络"
},
{
"name": "vpn_edit_alias",
"value": "名称"
},
{
"name": "vpn_edit_type",
"value": "类型"
},
{
"name": "vpn_edit_ovpn_configfile",
"value": "OpenVPN 配置文件"
},
{
"name": "vpn_edit_ovpn_ca_cert_file",
"value": "CA 证书"
},
{
"name": "vpn_edit_ovpn_user_cert_file",
"value": "用户证书"
},
{
"name": "vpn_edit_ovpn_private_key",
"value": "私钥"
},
{
"name": "vpn_edit_ovpn_private_key_psd",
"value": "私钥密码"
},
{
"name": "vpn_edit_ovpn_username",
"value": "用户名"
},
{
"name": "vpn_edit_ovpn_password",
"value": "密码"
},
{
"name": "vpn_edit_ovpn_click_select",
"value": "点击选择"
},
{
"name": "vpn_edit_ovpn_protocol",
"value": "OpenVPN 协议"
},
{
"name": "vpn_edit_auth_type",
"value": "OpenVPN 认证类型"
},
{
"name": "vpn_edit_serveraddress",
"value": "服务器地址"
},
{
"name": "vpn_edit_ovpn_serveraddress",
"value": "OpenVPN 服务器地址"
},
{
"name": "vpn_edit_ovpn_port",
"value": "OpenVPN 服务器端口"
},
{
"name": "vpn_edit_ipsecidentifier",
"value": "IPSec 标识符"
},
{
"name": "vpn_edit_l2tpsecret",
"value": "L2TP 密钥"
},
{
"name": "vpn_edit_ipsecsecret",
"value": "IPSec 预共享密钥"
},
{
"name": "vpn_edit_usercert",
"value": "IPSec 用户证书公钥"
},
{
"name": "vpn_edit_user_private_cert",
"value": "IPSec 用户证书私钥"
},
{
"name": "vpn_edit_cacert",
"value": "IPSec CA 证书"
},
{
"name": "vpn_edit_servercert",
"value": "IPSec 服务器证书公钥"
},
{
"name": "vpn_edit_server_private_cert",
"value": "IPSec 服务器秘钥证书私钥"
},
{
"name": "vpn_edit_unuse",
"value": "未使用"
},
{
"name": "vpn_edit_advanced",
"value": "显示高级选项"
},
{
"name": "vpn_edit_proxy_advanced",
"value": "显示代理"
},
{
"name": "vpn_edit_searchdomains",
"value": "DNS 搜索域"
},
{
"name": "vpn_edit_dnsaddresses",
"value": "DNS 服务器(例如 8.8.8.8)"
},
{
"name": "vpn_edit_router",
"value": "转发路线(例如 10.0.0.0/8)"
},
{
"name": "vpn_edit_ovpn_proxy_host",
"value": "OpenVPN 代理主机"
},
{
"name": "vpn_edit_ovpn_proxy_port",
"value": "OpenVPN 代理端口"
},
{
"name": "vpn_edit_ovpn_proxy_username",
"value": "OpenVPN 代理用户名"
},
{
"name": "vpn_edit_ovpn_proxy_password",
"value": "OpenVPN 代理密码"
},
{
"name": "vpn_edit_ovpn_useauth",
"value": "使用基本身份验证"
},
{
"name": "vpn_edit_save",
"value": "保存"
},
{
"name": "vpn_edit_cert_not_specified",
"value": "(未指定)"
},
{
"name": "vpn_edit_cert_not_verify_server",
"value": "(不验证服务器)"
},
{
"name": "vpn_edit_cert_from_server",
"value": "(来自服务器)"
},
{
"name": "vpn_line_remove_alert_title",
"value": "是否删除%s网络?"
},
{
"name": "vpn_line_remove_alert_confirm",
"value": "删除"
},
{
"name": "vpn_line_disconnect_alert_title",
"value": "是否断开%s网络?"
},
{
"name": "vpn_line_disconnect_alert_confirm",
"value": "断开"
},
{
"name": "vpn_line_disconnect_operation_edit",
"value": "编辑VPN网络"
},
{
"name": "vpn_line_disconnect_operation_remove",
"value": "删除VPN网络"
},
{
"name": "vpn_state_connect_ing",
"value": "连接中..."
},
{
"name": "vpn_state_connec_success",
"value": "连接成功"
},
{
"name": "vpn_state_connec_failed",
"value": "连接失败"
},
{
"name": "vpn_state_disconnecting",
"value": "断开中..."
},
{
"name": "vpn_state_disconnected",
"value": "已断开"
},
{
"name": "vpn_list_empty",
"value": "没有VPN"
},
{
"name": "vpn_list_add",
"value": "添加VPN网络"
}, {
"name": "ovpn_auth_type_cert",
"value": "证书( TLS )"
},
{
"name": "ovpn_auth_type_pass",
"value": "密码"
},
{
"name": "ovpn_auth_type_certpass",
"value": "证书和密码( TLS )"
},
{
"name": "VPN",
"value": "VPN"
},
{
"name": "vpn_error_invalid_param",
"value": "参数错误"
},
{
"name": "vpn_error_operation_failed",
"value": "API调用失败"
},
{
"name": "vpn_error_system_internal",
"value": "系统内部错误"
},
{
"name": "vpn_error_disconnect_first",
"value": "请先断开VPN再操作"
}
]
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1710986847033" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4597" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M79.205516 455.117464a455.106029 452.591568 90 1 0 905.183136 0 455.106029 452.591568 90 1 0-905.183136 0Z" fill="#EEEEEE" p-id="4598"></path><path d="M791.719262 692.116443l24.002896-24.13162a16.907189 16.907189 0 0 1 24.002896-0.004022L903.732776 732.342512l-48.005791 48.271284-64.007723-64.361712a17.134742 17.134742 0 0 1 0-24.135641z" fill="#D8D8D8" p-id="4599"></path><path d="M722.156558 273.075052l0.318574-0.489239a5.711581 5.711581 0 0 0-1.154831 3.453117V341.340956c0 3.140232 2.531527 5.688825 5.654692 5.688826h85.09345c0.739547 0 1.45065-0.142221 2.099177-0.403907l0.494927 0.403907v261.685966c0 15.706847-12.663325 28.444127-28.284839 28.444127h-492.197171c-15.615826 0-28.28484-12.73728-28.28484-28.444127V301.519179c0-15.706847 12.669014-28.444127 28.290529-28.444127h427.970332z" fill="#FDFDFD" p-id="4600"></path><path d="M322.471066 364.096258m8.533238 0l265.804676 0q8.533238 0 8.533238 8.533238l0 0q0 8.533238-8.533238 8.533238l-265.804676 0q-8.533238 0-8.533238-8.533238l0 0q0-8.533238 8.533238-8.533238Z" fill="#D8D8D8" p-id="4601"></path><path d="M322.471066 438.050988m8.533238 0l186.599161 0q8.533238 0 8.533238 8.533238l0 0q0 8.533238-8.533238 8.533238l-186.599161 0q-8.533238 0-8.533238-8.533238l0 0q0-8.533238 8.533238-8.533238Z" fill="#D8D8D8" p-id="4602"></path><path d="M322.471066 512.005717m8.533238 0l186.599161 0q8.533238 0 8.533238 8.533238l0 0q0 8.533238-8.533238 8.533238l-186.599161 0q-8.533238 0-8.533238-8.533238l0 0q0-8.533238 8.533238-8.533238Z" fill="#D8D8D8" p-id="4603"></path><path d="M226.295784 25.611149a19.910889 19.802801 90 1 0 39.605602 0 19.910889 19.802801 90 1 0-39.605602 0Z" fill="#EEEEEE" p-id="4604"></path><path d="M995.703726 347.029782a11.377651 11.315074 90 1 0 22.630147 0 11.377651 11.315074 90 1 0-22.630147 0Z" fill="#EEEEEE" p-id="4605"></path><path d="M715.65992 278.763877v68.265905c0 3.140232 2.531527 5.688825 5.660381 5.688825h90.5149a5.700203 5.700203 0 1 0 3.396229-10.239886l-90.520589-68.265904a5.631937 5.631937 0 0 0-7.918845 1.137765c-0.739547 0.984167-1.132076 2.184509-1.132076 3.413295z" fill="#D8D8D8" p-id="4606"></path><path d="M585.539417 566.049558a113.776507 113.150736 90 1 0 226.301473 0 113.776507 113.150736 90 1 0-226.301473 0Z" fill="#EEEEEE" p-id="4607"></path><path d="M759.714183 659.937932l24.006843-24.137686 31.999643 32.181685-24.006843 24.137686zM42.432948 267.386227c4.113021 0 7.549071 14.739747 8.322752 34.303617 19.444405 0.77368 34.104508 4.221108 34.104508 8.362573s-14.660103 7.588893-34.110197 8.368262C49.98202 337.984549 46.545969 352.718607 42.438637 352.718607c-4.11871 0-7.55476-14.739747-8.322751-34.303617C14.660103 317.646999 0 314.199571 0 310.052417s14.660103-7.588893 34.110197-8.368262C34.872499 282.120284 38.314239 267.386227 42.432948 267.386227zM995.703726 153.609719c2.742014 0 5.028922 9.824601 5.546604 22.869078 12.970522 0.511994 22.738235 2.815969 22.738235 5.575049s-9.773402 5.063055-22.738235 5.575049c-0.511994 13.044477-2.804591 22.869078-5.546604 22.869078-2.747703 0-5.03461-9.824601-5.552294-22.869078-12.964833-0.511994-22.738235-2.815969-22.738235-5.575049s9.773402-5.063055 22.738235-5.575049c0.511994-13.044477 2.804591-22.869078 5.552294-22.869078z" fill="#D8D8D8" p-id="4608"></path><path d="M851.440803 0.011435c4.181287 0 7.657159 17.248519 8.356885 39.946931 22.573259 0.705414 39.730756 4.204042 39.730756 8.408084 0 4.209731-17.157497 7.70267-39.730756 8.402395-0.699726 22.698413-4.175598 39.952621-8.362573 39.952621-4.181287 0-7.657159-17.248519-8.356885-39.946932-22.573259-0.705414-39.730756-4.204042-39.730756-8.408084 0-4.209731 17.157497-7.70267 39.730756-8.402395 0.699726-22.698413 4.175598-39.952621 8.362573-39.95262z" fill="#EEEEEE" p-id="4609"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Public/ic_public_add</title>
<defs>
<path d="M12.75,21.25 C12.75,21.6642136 12.4142136,22 12,22 C11.5857864,22 11.25,21.6642136 11.25,21.25 L11.25,12.75 L2.75,12.75 C2.33578644,12.75 2,12.4142136 2,12 C2,11.5857864 2.33578644,11.25 2.75,11.25 L11.25,11.25 L11.25,2.75 C11.25,2.33578644 11.5857864,2 12,2 C12.4142136,2 12.75,2.33578644 12.75,2.75 L12.75,21.25 Z M21.25,11.25 C21.6642136,11.25 22,11.5857864 22,12 C22,12.4142136 21.6642136,12.75 21.25,12.75 L13.75,12.75 L13.75,11.25 L21.25,11.25 Z" id="path-1"></path>
</defs>
<g id="Public/ic_public_add" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="形状结合" fill="#000000" xlink:href="#path-1"></use>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>ic_public_detail</title>
<g id="ic_public_detail" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组" transform="translate(1.000000, 1.000000)" fill="#000000" fill-rule="nonzero">
<path d="M11,0 C17.0751322,0 22,4.92486775 22,11 C22,17.0751322 17.0751322,22 11,22 C4.92486775,22 0,17.0751322 0,11 C0,4.92486775 4.92486775,0 11,0 Z M11,1.5 C5.75329488,1.5 1.5,5.75329488 1.5,11 C1.5,16.2467051 5.75329488,20.5 11,20.5 C16.2467051,20.5 20.5,16.2467051 20.5,11 C20.5,5.75329488 16.2467051,1.5 11,1.5 Z M11,8.75 C11.4142136,8.75 11.75,9.0857864 11.75,9.5 L11.75,16.25 C11.75,16.6642136 11.4142136,17 11,17 C10.5857864,17 10.25,16.6642136 10.25,16.25 L10.25,9.5 C10.25,9.0857864 10.5857864,8.75 11,8.75 Z M11,5 C11.6903559,5 12.25,5.55964406 12.25,6.25 C12.25,6.94035594 11.6903559,7.5 11,7.5 C10.3096441,7.5 9.75,6.94035594 9.75,6.25 C9.75,5.55964406 10.3096441,5 11,5 Z" id="path-1"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,35 @@
<svg xmlns="http://www.w3.org/2000/svg" width="29.502" height="15.56" viewBox="0 0 29.502 15.56">
<defs>
<style>
.cls-1, .cls-2 {
fill: none;
}
.cls-2 {
stroke: #b2b2b2;
stroke-linecap: round;
stroke-width: 5px;
}
.cls-3 {
fill: #b2b2b2;
}
.cls-4, .cls-5 {
stroke: none;
}
.cls-5 {
fill: #b2b2b2;
}
</style>
</defs>
<g id="组_1" data-name="组 1" transform="translate(-35 -10.64)">
<g id="路径_14" data-name="路径 14" class="cls-1" transform="translate(35 10.64)">
<path class="cls-4" d="M7.78,0A7.78,7.78,0,1,1,0,7.78,7.78,7.78,0,0,1,7.78,0Z"/>
<path class="cls-5" d="M 7.780176162719727 4.999996185302734 C 6.247176170349121 4.999996185302734 4.999996185302734 6.247176170349121 4.999996185302734 7.780176162719727 C 4.999996185302734 9.313176155090332 6.247176170349121 10.56035614013672 7.780176162719727 10.56035614013672 C 9.313176155090332 10.56035614013672 10.56035614013672 9.313176155090332 10.56035614013672 7.780176162719727 C 10.56035614013672 6.247176170349121 9.313176155090332 4.999996185302734 7.780176162719727 4.999996185302734 M 7.780176162719727 -3.814697265625e-06 C 12.07704639434814 -3.814697265625e-06 15.56035614013672 3.483305931091309 15.56035614013672 7.780176162719727 C 15.56035614013672 12.07704639434814 12.07704639434814 15.56035614013672 7.780176162719727 15.56035614013672 C 3.483305931091309 15.56035614013672 -3.814697265625e-06 12.07704639434814 -3.814697265625e-06 7.780176162719727 C -3.814697265625e-06 3.483305931091309 3.483305931091309 -3.814697265625e-06 7.780176162719727 -3.814697265625e-06 Z"/>
</g>
<line id="直线_7" data-name="直线 7" class="cls-2" x2="11.875" transform="translate(50.127 18.625)"/>
<path id="路径_15" data-name="路径 15" class="cls-3" d="M0,0H5V2A2,2,0,0,1,3,4H2A2,2,0,0,1,0,2Z" transform="translate(57 22)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -43,6 +43,9 @@
"pages/accessibilityShortKey",
"pages/accessibilityColorCorrection",
"pages/accessibilityScreenTouch",
"pages/abilityDialog/accessibilityWindowShortKeyDialog"
"pages/abilityDialog/accessibilityWindowShortKeyDialog",
"pages/vpn/vpnList",
"pages/vpn/vpnEdit",
"pages/vpn/vpnConnect"
]
}

View File

@ -0,0 +1,29 @@
{
"strarray": [
{
"name": "ovpn_auth_type",
"value": [
{
"value": "Certificate(TLS)"
},
{
"value": "Password"
},
{
"value": "Certificate and Password(TLS)"
}
]
},
{
"name": "ovpn_protocol",
"value": [
{
"value": "TCP"
},
{
"value": "UDP"
}
]
}
]
}

View File

@ -1487,6 +1487,272 @@
{
"name": "GET_ACCESS_UDID",
"value": "Permission to obtain a UDID"
},
{
"name": "vpn_change_alert_title",
"value": "Replace the existing VPN?"
},
{
"name": "vpn_change_alert_content",
"value": "You have connected to the VPN. If you want to connect to another VPN, the system will replace the existing VPN."
},
{
"name": "vpn_change_alert_confirm",
"value": "Replace"
},
{
"name": "vpn_connect_title",
"value": "Connection to %s"
},
{
"name": "vpn_connect_username",
"value": "Username"
},
{
"name": "vpn_connect_password",
"value": "Password"
},
{
"name": "vpn_connect_save",
"value": "Save Account Information"
},
{
"name": "vpn_connect_confirm",
"value": "Connect"
},
{
"name": "vpn_edit_title",
"value": "Edit VPN Network"
},
{
"name": "vpn_edit_alias",
"value": "Alias"
},
{
"name": "vpn_edit_type",
"value": "Protocol Type"
},
{
"name": "vpn_edit_ovpn_configfile",
"value": "OpenVPN Config File"
},
{
"name": "vpn_edit_ovpn_click_select",
"value": "Click Select"
},
{
"name": "vpn_edit_ovpn_ca_cert_file",
"value": "CA Certificate"
},
{
"name": "vpn_edit_ovpn_user_cert_file",
"value": "User Certificate"
},
{
"name": "vpn_edit_ovpn_private_key",
"value": "Private key"
},
{
"name": "vpn_edit_ovpn_private_key_psd",
"value": "Private key password"
},
{
"name": "vpn_edit_ovpn_protocol",
"value": "OpenVPN Protocol"
},
{
"name": "vpn_edit_auth_type",
"value": "OpenVPN Auth Type"
},
{
"name": "vpn_edit_serveraddress",
"value": "Server Address"
},
{
"name": "vpn_edit_ovpn_serveraddress",
"value": "OpenVpn Server Address"
},
{
"name": "vpn_edit_ovpn_port",
"value": "OpenVpn Server Port"
},
{
"name": "vpn_edit_ipsecidentifier",
"value": "IPSec Identifier"
},
{
"name": "vpn_edit_l2tpsecret",
"value": "L2TP Secret"
},
{
"name": "vpn_edit_ipsecsecret",
"value": "IPSec Secret"
},
{
"name": "vpn_edit_usercert",
"value": "IPSec User Certificate Public Key"
},
{
"name": "vpn_edit_user_private_cert",
"value": "IPSec User Certificate Private Key"
},
{
"name": "vpn_edit_cacert",
"value": "IPSec CA Certificate"
},
{
"name": "vpn_edit_servercert",
"value": "IPSec Server Certificate Public Key"
},
{
"name": "vpn_edit_server_private_cert",
"value": "IPSec Server Certificate Private Key"
},
{
"name": "vpn_edit_unuse",
"value": "not used"
},
{
"name": "vpn_edit_advanced",
"value": "Show Advanced Options"
},
{
"name": "vpn_edit_proxy_advanced",
"value": "Show Proxy Advanced Options"
},
{
"name": "vpn_edit_searchdomains",
"value": "DNS Search Domain"
},
{
"name": "vpn_edit_dnsaddresses",
"value": "DNS Server (e.g. 8.8.8.8)"
},
{
"name": "vpn_edit_router",
"value": "Forwarding Route (e.g. 10.0.0.0/8)"
},
{
"name": "vpn_edit_ovpn_proxy_host",
"value": "OpenVpn Proxy Host"
},
{
"name": "vpn_edit_ovpn_proxy_port",
"value": "OpenVpn Proxy Port"
},
{
"name": "vpn_edit_ovpn_proxy_username",
"value": "OpenVpn Proxy Username"
},
{
"name": "vpn_edit_ovpn_proxy_password",
"value": "OpenVpn Proxy Password"
},
{
"name": "vpn_edit_ovpn_useauth",
"value": "Using Basic Authentication"
},
{
"name": "vpn_edit_save",
"value": "Save"
},
{
"name": "vpn_edit_cert_not_specified",
"value": "(unspecified)"
},
{
"name": "vpn_edit_cert_not_verify_server",
"value": "(Do not verify server)"
},
{
"name": "vpn_edit_cert_from_server",
"value": "(From server)"
},
{
"name": "vpn_line_remove_alert_title",
"value": "Delete the %s vpn?"
},
{
"name": "vpn_line_remove_alert_confirm",
"value": "Delete"
},
{
"name": "vpn_line_disconnect_alert_title",
"value": "Disconnect the %s VPN?"
},
{
"name": "vpn_line_disconnect_alert_confirm",
"value": "Disconnect"
},
{
"name": "vpn_line_disconnect_operation_edit",
"value": "Edit VPN Network"
},
{
"name": "vpn_line_disconnect_operation_remove",
"value": "Delete VPN Network"
},
{
"name": "vpn_state_connect_ing",
"value": "Connecting..."
},
{
"name": "vpn_state_connec_success",
"value": "Connection successful"
},
{
"name": "vpn_state_connec_failed",
"value": "Connection failed"
},
{
"name": "vpn_state_disconnecting",
"value": "Disconnecting..."
},
{
"name": "vpn_state_disconnected",
"value": "DisConnected"
},
{
"name": "vpn_list_empty",
"value": "No VPN Network"
},
{
"name": "vpn_list_add",
"value": "Add VPN Network"
}, {
"name": "ovpn_auth_type_cert",
"value": "Certificate(TLS)"
},
{
"name": "ovpn_auth_type_pass",
"value": "Password"
},
{
"name": "ovpn_auth_type_certpass",
"value": "Certificate and Password(TLS)"
},
{
"name": "VPN",
"value": "VPN"
},
{
"name": "vpn_error_invalid_param",
"value": "Invalid parameter value"
},
{
"name": "vpn_error_operation_failed",
"value": "Operation failed"
},
{
"name": "vpn_error_system_internal",
"value": "System internal error"
},
{
"name": "vpn_error_disconnect_first",
"value": "Disconnect the VPN before operating"
}
]
}

View File

@ -0,0 +1,35 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes des md4 sha1 sha2 fips-prf pem pkcs1 curve25519 gmp x509 curl revocation hmac kdf vici kernel-netlink socket-default eap-identity eap-mschapv2 updown
}
charon {
filelog {
charon {
# path to the log file, specify this as section name in versions prior to 5.7.0
path = /data/service/el1/public/netmanager/charon.log
# add a timestamp prefix
time_format = %b %e %T
# prepend connection name, simplifies grepping
ike_name = yes
# overwrite existing files
append = no
# increase default loglevel for all daemon subsystems
default = 4
# flush each line to disk
flush_line = yes
}
stderr {
# more detailed loglevel for a specific subsystem, overriding the
# default loglevel.
ike = 4
knl = 4
}
}
}

View File

@ -0,0 +1,31 @@
connections {
home {
remote_addrs = vpn_address_value
vips = 0.0.0.0
local {
auth = eap-mschapv2
eap_id = vpn_username_value
}
remote {
auth = pubkey
}
children {
home {
remote_ts=0.0.0.0/0
esp_proposals = aes128gcm128-x25519
}
}
version = 2
proposals = aes128-sha256-x25519
}
}
secrets {
eap-carol {
id = ipsec_identifier_value
secret = password_value
}
eap-dave {
id = vpn_username_value
secret = vpn_password_value
}
}

View File

@ -0,0 +1,39 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = random openssl
}
charon-systemd {
load = random nonce aes sha1 sha2 hmac kdf curve25519 kernel-netlink socket-default updown vici kernel-libipsec
}
charon {
plugins {
kernel-libipsec {
allow_peer_ts = yes
}
}
filelog {
charon {
# path to the log file, specify this as section name in versions prior to 5.7.0
path = /data/service/el1/public/netmanager/charon.log
# add a timestamp prefix
time_format = %b %e %T
# prepend connection name, simplifies grepping
ike_name = yes
# overwrite existing files
append = no
# increase default loglevel for all daemon subsystems
default = 4
# flush each line to disk
flush_line = yes
}
stderr {
# more detailed loglevel for a specific subsystem, overriding the
# default loglevel.
ike = 4
knl = 4
}
}
}

View File

@ -0,0 +1,30 @@
connections {
home {
remote_addrs = vpn_address_value
vips = 0.0.0.0
local {
auth = psk
}
remote {
auth = psk
id = vpn_ipsec_identifier_value
}
children {
home {
remote_ts=0.0.0.0/0
esp_proposals = aes128gcm128-x25519
}
}
version = 2
proposals = aes128-sha256-x25519
}
}
secrets {
ike-moon {
id = vpn_ipsec_identifier_value
secret = vpn_ipsec_sharedKey_value
}
}

View File

@ -0,0 +1,35 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes des md4 sha1 sha2 fips-prf pem pkcs1 curve25519 gmp x509 curl revocation hmac kdf vici kernel-netlink socket-default eap-identity eap-mschapv2 updown
}
charon {
filelog {
charon {
# path to the log file, specify this as section name in versions prior to 5.7.0
path = /data/service/el1/public/netmanager/charon.log
# add a timestamp prefix
time_format = %b %e %T
# prepend connection name, simplifies grepping
ike_name = yes
# overwrite existing files
append = no
# increase default loglevel for all daemon subsystems
default = 4
# flush each line to disk
flush_line = yes
}
stderr {
# more detailed loglevel for a specific subsystem, overriding the
# default loglevel.
ike = 4
knl = 4
}
}
}

View File

@ -0,0 +1,22 @@
connections {
home {
remote_addrs = vpn_address_value
vips = 0.0.0.0
local {
auth = pubkey
certs = /data/service/el1/public/netmanager/client.cert.pem
id = vpn_ipsec_identifier_value
}
remote {
auth = pubkey
}
children {
home {
remote_ts=0.0.0.0/0
esp_proposals = aes128gcm128-x25519
}
}
version = 2
proposals = aes128-sha256-x25519
}
}

View File

@ -0,0 +1,35 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes des md4 sha1 sha2 fips-prf pem pkcs1 curve25519 gmp x509 curl revocation hmac kdf vici kernel-netlink socket-default eap-identity eap-mschapv2 updown
}
charon {
filelog {
charon {
# path to the log file, specify this as section name in versions prior to 5.7.0
path = /data/service/el1/public/netmanager/charon.log
# add a timestamp prefix
time_format = %b %e %T
# prepend connection name, simplifies grepping
ike_name = yes
# overwrite existing files
append = no
# increase default loglevel for all daemon subsystems
default = 4
# flush each line to disk
flush_line = yes
}
stderr {
# more detailed loglevel for a specific subsystem, overriding the
# default loglevel.
ike = 4
knl = 4
}
}
}

View File

@ -0,0 +1,28 @@
connections {
home {
remote_addrs = vpn_address_value
vips = 0.0.0.0
local {
auth = xauth
xauth_id = vpn_username_value
}
remote {
auth = pubkey
}
children {
home {
remote_ts=0.0.0.0/0
esp_proposals = aes256-sha2_384
}
}
version = 1
proposals = aes256-sha2_384-modp1024
}
}
secrets {
xauth {
id = vpn_username_value
secret = vpn_password_value
}
}

View File

@ -0,0 +1,43 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes des md4 sha1 sha2 fips-prf pem pkcs1 curve25519 gmp x509 curl revocation hmac kdf vici kernel-netlink socket-default eap-identity eap-mschapv2 updown
}
charon {
i_dont_care_about_security_and_use_aggressive_mode_psk = yes
plugins {
kernel-netlink {
install_routes_xfrmi = yes
}
}
filelog {
charon {
# path to the log file, specify this as section name in versions prior to 5.7.0
path = /data/service/el1/public/netmanager/charon.log
# add a timestamp prefix
time_format = %b %e %T
# prepend connection name, simplifies grepping
ike_name = yes
# overwrite existing files
append = no
# increase default loglevel for all daemon subsystems
default = 4
# flush each line to disk
flush_line = yes
}
stderr {
# more detailed loglevel for a specific subsystem, overriding the
# default loglevel.
ike = 2
knl = 3
}
}
}

View File

@ -0,0 +1,34 @@
connections {
home {
remote_addrs = vpn_address_value
vips = 0.0.0.0
local {
auth = psk
}
local-xauth {
auth = xauth
xauth_id = vpn_username_value
}
remote {
auth = psk
}
children {
home {
remote_ts=0.0.0.0/0
esp_proposals = aes256-sha2_384
}
}
version = 1
proposals = aes256-sha2_384-modp1024
aggressive=yes
}
}
secrets {
ike-moon {
secret = vpn_ipsec_sharedKey_value
}
xauth{
id = vpn_username_value
secret = vpn_password_value
}
}

View File

@ -0,0 +1,37 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes des md4 sha1 sha2 fips-prf pem pkcs1 curve25519 gmp x509 curl revocation hmac kdf vici kernel-netlink socket-default eap-identity eap-mschapv2 updown
}
charon {
i_dont_care_about_security_and_use_aggressive_mode_psk = yes
filelog {
charon {
# path to the log file, specify this as section name in versions prior to 5.7.0
path = /data/service/el1/public/netmanager/charon.log
# add a timestamp prefix
time_format = %b %e %T
# prepend connection name, simplifies grepping
ike_name = yes
# overwrite existing files
append = no
# increase default loglevel for all daemon subsystems
default = 4
# flush each line to disk
flush_line = yes
}
stderr {
# more detailed loglevel for a specific subsystem, overriding the
# default loglevel.
ike = 4
knl = 4
}
}
}

View File

@ -0,0 +1,31 @@
connections {
home {
remote_addrs = vpn_address_value
vips = 0.0.0.0
local {
auth = pubkey
certs = /data/service/el1/public/netmanager/client.cert.pem
id = vpn_username_value
}
local-xauth {
auth = xauth
}
remote {
auth = pubkey
}
children {
home {
remote_ts=0.0.0.0/0
esp_proposals = aes256-sha2_384
}
}
version = 1
proposals = aes256-sha2_384-modp1024
}
}
secrets {
xauth-carol {
id = vpn_username_value
secret = vpn_password_value
}
}

View File

@ -0,0 +1,23 @@
# ipsec.conf - strongSwan IPsec configuration file
config setup
conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev1
authby=secret
ike=aes128-sha1-modp1024,3des-sha1-modp1024!
esp=aes128-sha1-modp1024,3des-sha1-modp1024!
conn home
keyexchange=ikev1
left=%defaultroute
auto=add
authby=secret
type=transport
leftprotoport=17/1701
rightprotoport=17/1701
right=vpn_address_value

View File

@ -0,0 +1,2 @@
# ipsec.secrets.conf - strongSwan IPsec secrets file
: PSK vpn_ipsec_sharedKey_value

View File

@ -0,0 +1,16 @@
ipcp-accept-local
ipcp-accept-remote
refuse-eap
require-mschap-v2
noccp
noauth
logfile /data/service/el1/public/netmanager/xl2tpd.log
idle 1800
mtu 1410
mru 1410
defaultroute
usepeerdns
debug
connect-delay 5000
name vpn_username_value
password vpn_password_value

View File

@ -0,0 +1,39 @@
# strongswan.conf - strongSwan configuration file
#
# Refer to the strongswan.conf(5) manpage for details
#
# Configuration changes should be made in the included files
charon {
load_modular = yes
plugins {
include /system/etc/strongswan/strongswan.d/charon/*.conf
kernel-libipsec {
load = no
}
}
filelog {
charon {
# path to the log file, specify this as section name in versions prior to 5.7.0
path = /data/service/el1/public/netmanager/charon.log
# add a timestamp prefix
time_format = %b %e %T
# prepend connection name, simplifies grepping
ike_name = yes
# overwrite existing files
append = no
# increase default loglevel for all daemon subsystems
default = 4
# flush each line to disk
flush_line = yes
}
stderr {
# more detailed loglevel for a specific subsystem, overriding the
# default loglevel.
ike = 4
knl = 4
}
}
}
include /system/etc/strongswan/strongswan.d/*.conf

View File

@ -0,0 +1,6 @@
[lac myVPN]
; set this to the ip address of your vpn server
lns = vpn_address_value
ppp debug = yes
pppoptfile = /data/service/el1/public/netmanager/options.l2tpd.client.conf
length bit = yes

View File

@ -0,0 +1,23 @@
# ipsec.conf - strongSwan IPsec configuration file
config setup
conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev1
authby=secret
ike=aes128-sha1-modp1024,3des-sha1-modp1024!
esp=aes128-sha1-modp1024,3des-sha1-modp1024!
conn home
keyexchange=ikev1
left=%defaultroute
auto=add
authby=secret
type=transport
leftprotoport=17/1701
rightprotoport=17/1701
right= vpn_address_value

View File

@ -0,0 +1,2 @@
# ipsec.secrets.conf - strongSwan IPsec secrets file
: PSK vpn_ipsec_sharedKey_value

View File

@ -0,0 +1,16 @@
ipcp-accept-local
ipcp-accept-remote
refuse-eap
require-mschap-v2
noccp
noauth
logfile /data/service/el1/public/netmanager/xl2tpd.log
idle 1800
mtu 1410
mru 1410
defaultroute
usepeerdns
debug
connect-delay 5000
name USERNAME_VALUE
password PASSWORD_VALUE

View File

@ -0,0 +1,40 @@
# strongswan.conf - strongSwan configuration file
#
# Refer to the strongswan.conf(5) manpage for details
#
# Configuration changes should be made in the included files
charon {
load_modular = yes
plugins {
include /system/etc/strongswan/strongswan.d/charon/*.conf
kernel-libipsec {
load = no
}
}
filelog {
charon {
# path to the log file, specify this as section name in versions prior to 5.7.0
path = /data/service/el1/public/netmanager/charon.log
# add a timestamp prefix
time_format = %b %e %T
# prepend connection name, simplifies grepping
ike_name = yes
# overwrite existing files
append = no
# increase default loglevel for all daemon subsystems
default = 4
# flush each line to disk
flush_line = yes
}
stderr {
# more detailed loglevel for a specific subsystem, overriding the
# default loglevel.
ike = 4
knl = 4
}
}
}
include /system/etc/strongswan/strongswan.d/*.conf

View File

@ -0,0 +1,6 @@
[lac myVPN]
; set this to the ip address of your vpn server
lns = vpn_address_value
ppp debug = yes
pppoptfile = /data/service/el1/public/netmanager/options.l2tpd.client.conf
length bit = yes

View File

@ -0,0 +1,29 @@
{
"strarray": [
{
"name": "ovpn_auth_type",
"value": [
{
"value": "证书(TLS)"
},
{
"value": "密码"
},
{
"value": "证书和密码(TLS)"
}
]
},
{
"name": "ovpn_protocol",
"value": [
{
"value": "TCP"
},
{
"value": "UDP"
}
]
}
]
}

View File

@ -1491,6 +1491,270 @@
{
"name": "GET_ACCESS_UDID",
"value": "获取UDID的权限"
},
{
"name": "vpn_change_alert_title",
"value": "要替换现有VPN吗?"
},
{
"name": "vpn_change_alert_content",
"value": "您已连接到VPN,如果您要连接到其他VPN,则系统替换现有VPN."
},
{
"name": "vpn_change_alert_confirm",
"value": "替换"
},
{
"name": "vpn_connect_title",
"value": "连接到%s"
},
{
"name": "vpn_connect_username",
"value": "用户名"
},
{
"name": "vpn_connect_password",
"value": "密码"
},
{
"name": "vpn_connect_save",
"value": "保存账户信息"
},
{
"name": "vpn_connect_confirm",
"value": "连接"
},
{
"name": "vpn_edit_title",
"value": "编辑VPN网络"
},
{
"name": "vpn_edit_alias",
"value": "名称"
},
{
"name": "vpn_edit_type",
"value": "类型"
},
{
"name": "vpn_edit_ovpn_configfile",
"value": "OpenVPN 配置文件"
},
{
"name": "vpn_edit_ovpn_ca_cert_file",
"value": "CA 证书"
},
{
"name": "vpn_edit_ovpn_user_cert_file",
"value": "用户证书"
},
{
"name": "vpn_edit_ovpn_private_key",
"value": "私钥"
},
{
"name": "vpn_edit_ovpn_private_key_psd",
"value": "私钥密码"
},
{
"name": "vpn_edit_ovpn_click_select",
"value": "点击选择"
},
{
"name": "vpn_edit_ovpn_protocol",
"value": "OpenVPN 协议"
},
{
"name": "vpn_edit_auth_type",
"value": "OpenVPN 认证类型"
},
{
"name": "vpn_edit_serveraddress",
"value": "服务器地址"
},
{
"name": "vpn_edit_ovpn_serveraddress",
"value": "OpenVPN 服务器地址"
},
{
"name": "vpn_edit_ovpn_port",
"value": "OpenVPN 服务器端口"
},
{
"name": "vpn_edit_ipsecidentifier",
"value": "IPSec 标识符"
},
{
"name": "vpn_edit_l2tpsecret",
"value": "L2TP 密钥"
},
{
"name": "vpn_edit_ipsecsecret",
"value": "IPSec 预共享密钥"
},
{
"name": "vpn_edit_usercert",
"value": "IPSec 用户证书公钥"
},
{
"name": "vpn_edit_user_private_cert",
"value": "IPSec 用户证书私钥"
},
{
"name": "vpn_edit_cacert",
"value": "IPSec CA 证书"
},
{
"name": "vpn_edit_servercert",
"value": "IPSec 服务器证书公钥"
},
{
"name": "vpn_edit_server_private_cert",
"value": "IPSec 服务器证书私钥"
},
{
"name": "vpn_edit_unuse",
"value": "未使用"
},
{
"name": "vpn_edit_advanced",
"value": "显示高级选项"
},
{
"name": "vpn_edit_proxy_advanced",
"value": "显示代理"
},
{
"name": "vpn_edit_searchdomains",
"value": "DNS 搜索域"
},
{
"name": "vpn_edit_dnsaddresses",
"value": "DNS 服务器(例如 8.8.8.8)"
},
{
"name": "vpn_edit_router",
"value": "转发路线(例如 10.0.0.0/8)"
},
{
"name": "vpn_edit_ovpn_proxy_host",
"value": "OpenVPN 代理主机"
},
{
"name": "vpn_edit_ovpn_proxy_port",
"value": "OpenVPN 代理端口"
},
{
"name": "vpn_edit_ovpn_proxy_username",
"value": "OpenVPN 代理用户名"
},
{
"name": "vpn_edit_ovpn_proxy_password",
"value": "OpenVPN 代理密码"
},
{
"name": "vpn_edit_ovpn_useauth",
"value": "使用基本身份验证"
},
{
"name": "vpn_edit_save",
"value": "保存"
},
{
"name": "vpn_edit_cert_not_specified",
"value": "(未指定)"
},
{
"name": "vpn_edit_cert_not_verify_server",
"value": "(不验证服务器)"
},
{
"name": "vpn_edit_cert_from_server",
"value": "(来自服务器)"
},
{
"name": "vpn_line_remove_alert_title",
"value": "是否删除%s网络?"
},
{
"name": "vpn_line_remove_alert_confirm",
"value": "删除"
},
{
"name": "vpn_line_disconnect_alert_title",
"value": "是否断开%s网络?"
},
{
"name": "vpn_line_disconnect_alert_confirm",
"value": "断开"
},
{
"name": "vpn_line_disconnect_operation_edit",
"value": "编辑VPN网络"
},
{
"name": "vpn_line_disconnect_operation_remove",
"value": "删除VPN网络"
},
{
"name": "vpn_state_connect_ing",
"value": "连接中..."
},
{
"name": "vpn_state_connec_success",
"value": "连接成功"
},
{
"name": "vpn_state_connec_failed",
"value": "连接失败"
},
{
"name": "vpn_state_disconnecting",
"value": "断开中..."
},
{
"name": "vpn_state_disconnected",
"value": "已断开"
},
{
"name": "vpn_list_empty",
"value": "没有VPN"
},
{
"name": "vpn_list_add",
"value": "添加VPN网络"
},
{
"name": "ovpn_auth_type_cert",
"value": "证书( TLS )"
},
{
"name": "ovpn_auth_type_pass",
"value": "密码"
},
{
"name": "ovpn_auth_type_certpass",
"value": "证书和密码( TLS )"
},
{
"name": "VPN",
"value": "VPN"
},
{
"name": "vpn_error_invalid_param",
"value": "参数错误"
},
{
"name": "vpn_error_operation_failed",
"value": "API调用失败"
},
{
"name": "vpn_error_system_internal",
"value": "系统内部错误"
},
{
"name": "vpn_error_disconnect_first",
"value": "请先断开VPN再操作"
}
]
}