namespace ServiceLib.ViewModels; public class AddServerViewModel : MyReactiveObject { [Reactive] public ProfileItem SelectedSource { get; set; } [Reactive] public string? CoreType { get; set; } [Reactive] public string Cert { get; set; } [Reactive] public string CertTip { get; set; } [Reactive] public string CertSha { get; set; } public ReactiveCommand FetchCertCmd { get; } public ReactiveCommand FetchCertChainCmd { get; } public ReactiveCommand SaveCmd { get; } public AddServerViewModel(ProfileItem profileItem, Func>? updateView) { _config = AppManager.Instance.Config; _updateView = updateView; FetchCertCmd = ReactiveCommand.CreateFromTask(async () => { await FetchCert(); }); FetchCertChainCmd = ReactiveCommand.CreateFromTask(async () => { await FetchCertChain(); }); SaveCmd = ReactiveCommand.CreateFromTask(async () => { await SaveServerAsync(); }); this.WhenAnyValue(x => x.Cert) .Subscribe(_ => UpdateCertTip()); this.WhenAnyValue(x => x.CertSha) .Subscribe(_ => UpdateCertTip()); this.WhenAnyValue(x => x.Cert) .Subscribe(_ => UpdateCertSha()); if (profileItem.IndexId.IsNullOrEmpty()) { profileItem.Network = Global.DefaultNetwork; profileItem.HeaderType = Global.None; profileItem.RequestHost = ""; profileItem.StreamSecurity = ""; SelectedSource = profileItem; } else { SelectedSource = JsonUtils.DeepCopy(profileItem); } CoreType = SelectedSource?.CoreType?.ToString(); Cert = SelectedSource?.Cert?.ToString() ?? string.Empty; CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty; } private async Task SaveServerAsync() { if (SelectedSource.Remarks.IsNullOrEmpty()) { NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks); return; } if (SelectedSource.Address.IsNullOrEmpty()) { NoticeManager.Instance.Enqueue(ResUI.FillServerAddress); return; } var port = SelectedSource.Port.ToString(); if (port.IsNullOrEmpty() || !Utils.IsNumeric(port) || SelectedSource.Port <= 0 || SelectedSource.Port >= Global.MaxPort) { NoticeManager.Instance.Enqueue(ResUI.FillCorrectServerPort); return; } if (SelectedSource.ConfigType == EConfigType.Shadowsocks) { if (SelectedSource.Id.IsNullOrEmpty()) { NoticeManager.Instance.Enqueue(ResUI.FillPassword); return; } if (SelectedSource.Security.IsNullOrEmpty()) { NoticeManager.Instance.Enqueue(ResUI.PleaseSelectEncryption); return; } } if (SelectedSource.ConfigType is not EConfigType.SOCKS and not EConfigType.HTTP) { if (SelectedSource.Id.IsNullOrEmpty()) { NoticeManager.Instance.Enqueue(ResUI.FillUUID); return; } } SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType); SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert; SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha; if (await ConfigHandler.AddServer(_config, SelectedSource) == 0) { NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); _updateView?.Invoke(EViewAction.CloseWindow, null); } else { NoticeManager.Instance.Enqueue(ResUI.OperationFailed); } } private void UpdateCertTip(string? errorMessage = null) { CertTip = errorMessage.IsNullOrEmpty() ? ((Cert.IsNullOrEmpty() && CertSha.IsNullOrEmpty()) ? ResUI.CertNotSet : ResUI.CertSet) : errorMessage; } private void UpdateCertSha() { if (Cert.IsNullOrEmpty()) { return; } var certList = CertPemManager.ParsePemChain(Cert); if (certList.Count == 0) { return; } List shaList = new(); foreach (var cert in certList) { var sha = CertPemManager.GetCertSha256Thumbprint(cert); if (sha.IsNullOrEmpty()) { return; } shaList.Add(sha); } CertSha = string.Join('~', shaList); } private async Task FetchCert() { if (SelectedSource.StreamSecurity != Global.StreamSecurity) { return; } var domain = SelectedSource.Address; var serverName = SelectedSource.Sni; if (serverName.IsNullOrEmpty()) { serverName = SelectedSource.RequestHost; } if (serverName.IsNullOrEmpty()) { serverName = SelectedSource.Address; } if (!Utils.IsDomain(serverName)) { UpdateCertTip(ResUI.ServerNameMustBeValidDomain); return; } if (SelectedSource.Port > 0) { domain += $":{SelectedSource.Port}"; } (Cert, var certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName); UpdateCertTip(certError); } private async Task FetchCertChain() { if (SelectedSource.StreamSecurity != Global.StreamSecurity) { return; } var domain = SelectedSource.Address; var serverName = SelectedSource.Sni; if (serverName.IsNullOrEmpty()) { serverName = SelectedSource.RequestHost; } if (serverName.IsNullOrEmpty()) { serverName = SelectedSource.Address; } if (!Utils.IsDomain(serverName)) { UpdateCertTip(ResUI.ServerNameMustBeValidDomain); return; } if (SelectedSource.Port > 0) { domain += $":{SelectedSource.Port}"; } var (certs, certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName); Cert = CertPemManager.ConcatenatePemChain(certs); UpdateCertTip(certError); } }