diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs index 07e5aae0..54183cc0 100644 --- a/v2rayN/ServiceLib/Common/Utils.cs +++ b/v2rayN/ServiceLib/Common/Utils.cs @@ -38,6 +38,7 @@ namespace ServiceLib.Common { Logging.SaveLog(ex.Message, ex); } + return result; } @@ -58,6 +59,7 @@ namespace ServiceLib.Common { Logging.SaveLog(ex.Message, ex); } + return null; } @@ -92,6 +94,7 @@ namespace ServiceLib.Common { Logging.SaveLog(ex.Message, ex); } + return string.Empty; } @@ -116,6 +119,7 @@ namespace ServiceLib.Common { Logging.SaveLog(ex.Message, ex); } + return null; } @@ -137,6 +141,7 @@ namespace ServiceLib.Common { Logging.SaveLog(ex.Message, ex); } + return null; } @@ -156,6 +161,7 @@ namespace ServiceLib.Common { Logging.SaveLog("Base64Encode", ex); } + return string.Empty; } @@ -170,12 +176,12 @@ namespace ServiceLib.Common { if (plainText.IsNullOrEmpty()) return ""; plainText = plainText.Trim() - .Replace(Environment.NewLine, "") - .Replace("\n", "") - .Replace("\r", "") - .Replace('_', '/') - .Replace('-', '+') - .Replace(" ", ""); + .Replace(Environment.NewLine, "") + .Replace("\n", "") + .Replace("\r", "") + .Replace('_', '/') + .Replace('-', '+') + .Replace(" ", ""); if (plainText.Length % 4 > 0) { @@ -189,6 +195,7 @@ namespace ServiceLib.Common { Logging.SaveLog("Base64Decode", ex); } + return string.Empty; } @@ -251,14 +258,17 @@ namespace ServiceLib.Common unit = "TB"; return; } + result = GBs + ((MBs % factor) / (factor + 0.0)); unit = "GB"; return; } + result = MBs + ((KBs % factor) / (factor + 0.0)); unit = "MB"; return; } + result = KBs + ((amount % factor) / (factor + 0.0)); unit = "KB"; return; @@ -302,6 +312,7 @@ namespace ServiceLib.Common { continue; } + var key = Uri.UnescapeDataString(keyValue[0]); var val = Uri.UnescapeDataString(keyValue[1]); @@ -323,6 +334,7 @@ namespace ServiceLib.Common { sb.Append(b.ToString("x2")); } + return sb.ToString(); } @@ -337,6 +349,7 @@ namespace ServiceLib.Common { return url; } + try { Uri uri = new(url); @@ -368,6 +381,7 @@ namespace ServiceLib.Common { return text; } + return text.Replace(",", ",").Replace(Environment.NewLine, ","); } @@ -391,6 +405,7 @@ namespace ServiceLib.Common { return true; } + return text == "null"; } @@ -424,6 +439,7 @@ namespace ServiceLib.Common _ => false, }; } + return false; } @@ -448,6 +464,7 @@ namespace ServiceLib.Common if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31) return true; if (ipBytes[0] == 192 && ipBytes[1] == 168) return true; } + return false; } @@ -468,6 +485,7 @@ namespace ServiceLib.Common { Logging.SaveLog(ex.Message, ex); } + return false; } @@ -489,6 +507,7 @@ namespace ServiceLib.Common catch { } + return 59090; } @@ -512,7 +531,8 @@ namespace ServiceLib.Common { if (blFull) { - return $"{Global.AppName} - V{GetVersionInfo()} - {RuntimeInformation.ProcessArchitecture} - {File.GetLastWriteTime(GetExePath()):yyyy/MM/dd}"; + return + $"{Global.AppName} - V{GetVersionInfo()} - {RuntimeInformation.ProcessArchitecture} - {File.GetLastWriteTime(GetExePath()):yyyy/MM/dd}"; } else { @@ -523,6 +543,7 @@ namespace ServiceLib.Common { Logging.SaveLog(ex.Message, ex); } + return Global.AppName; } @@ -560,6 +581,7 @@ namespace ServiceLib.Common { Logging.SaveLog(ex.Message, ex); } + return string.Empty; } @@ -572,7 +594,11 @@ namespace ServiceLib.Common { try { - if (fileName.IsNullOrEmpty()) { return; } + if (fileName.IsNullOrEmpty()) + { + return; + } + Process.Start(new ProcessStartInfo(fileName, arguments) { UseShellExecute = true }); } catch (Exception ex) @@ -605,6 +631,7 @@ namespace ServiceLib.Common { Logging.SaveLog(ex.Message, ex); } + return systemHosts; } @@ -629,17 +656,20 @@ namespace ServiceLib.Common cmd = cmd.WithArguments(args); } } + var result = await cmd.ExecuteBufferedAsync(); if (result.IsSuccess) { return result.StandardOutput.ToString(); } + Logging.SaveLog(result.ToString() ?? ""); } catch (Exception ex) { Logging.SaveLog("GetCliWrapOutput", ex); } + return null; } @@ -654,6 +684,7 @@ namespace ServiceLib.Common { return startupPath; } + return Path.Combine(startupPath, fileName); } @@ -674,6 +705,7 @@ namespace ServiceLib.Common { Directory.CreateDirectory(tempPath); } + if (IsNullOrEmpty(filename)) { return tempPath; @@ -691,6 +723,7 @@ namespace ServiceLib.Common { Directory.CreateDirectory(tempPath); } + return Path.Combine(tempPath, filename); } @@ -701,6 +734,7 @@ namespace ServiceLib.Common { Directory.CreateDirectory(tempPath); } + if (Utils.IsNullOrEmpty(filename)) { return tempPath; @@ -718,6 +752,7 @@ namespace ServiceLib.Common { Directory.CreateDirectory(tempPath); } + if (coreType != null) { tempPath = Path.Combine(tempPath, coreType.ToLower().ToString()); @@ -726,6 +761,7 @@ namespace ServiceLib.Common Directory.CreateDirectory(tempPath); } } + if (IsNullOrEmpty(filename)) { return tempPath; @@ -743,6 +779,7 @@ namespace ServiceLib.Common { Directory.CreateDirectory(tempPath); } + if (Utils.IsNullOrEmpty(filename)) { return tempPath; @@ -760,6 +797,7 @@ namespace ServiceLib.Common { Directory.CreateDirectory(tempPath); } + if (Utils.IsNullOrEmpty(filename)) { return tempPath; @@ -820,11 +858,18 @@ namespace ServiceLib.Common public static async Task GetLinuxFontFamily(string lang) { - // var arg = new List() { "-c", $"fc-list :lang={lang} family" }; + // var arg = new List() { "-c", $"fc-list :lang={lang} family" }; var arg = new List() { "-c", $"fc-list : family" }; return await GetCliWrapOutput("/bin/bash", arg); } + public static string? GetHomePath() + { + return IsWindows() + ? Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%") + : Environment.GetEnvironmentVariable("HOME"); + } + #endregion Platform } } \ No newline at end of file diff --git a/v2rayN/ServiceLib/Common/WindowsUtils.cs b/v2rayN/ServiceLib/Common/WindowsUtils.cs new file mode 100644 index 00000000..a7b8fc5f --- /dev/null +++ b/v2rayN/ServiceLib/Common/WindowsUtils.cs @@ -0,0 +1,52 @@ +using Microsoft.Win32; + +namespace ServiceLib.Common +{ + internal static class WindowsUtils + { + public static string? RegReadValue(string path, string name, string def) + { + RegistryKey? regKey = null; + try + { + regKey = Registry.CurrentUser.OpenSubKey(path, false); + var value = regKey?.GetValue(name) as string; + return Utils.IsNullOrEmpty(value) ? def : value; + } + catch (Exception ex) + { + Logging.SaveLog(ex.Message, ex); + } + finally + { + regKey?.Close(); + } + return def; + } + + public static void RegWriteValue(string path, string name, object value) + { + RegistryKey? regKey = null; + try + { + regKey = Registry.CurrentUser.CreateSubKey(path); + if (Utils.IsNullOrEmpty(value.ToString())) + { + regKey?.DeleteValue(name, false); + } + else + { + regKey?.SetValue(name, value); + } + } + catch (Exception ex) + { + Logging.SaveLog(ex.Message, ex); + } + finally + { + regKey?.Close(); + } + } + } +} \ No newline at end of file diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 1c76cae3..e3c974cd 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -28,22 +28,25 @@ public const string CoreSpeedtestConfigFileName = "configSpeedtest.json"; public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json"; public const string ClashMixinConfigFileName = "Mixin.yaml"; - public const string V2raySampleClient = "ServiceLib.Sample.SampleClientConfig"; - public const string SingboxSampleClient = "ServiceLib.Sample.SingboxSampleClientConfig"; - public const string V2raySampleHttpRequestFileName = "ServiceLib.Sample.SampleHttpRequest"; - public const string V2raySampleHttpResponseFileName = "ServiceLib.Sample.SampleHttpResponse"; - public const string V2raySampleInbound = "ServiceLib.Sample.SampleInbound"; - public const string V2raySampleOutbound = "ServiceLib.Sample.SampleOutbound"; - public const string SingboxSampleOutbound = "ServiceLib.Sample.SingboxSampleOutbound"; - public const string CustomRoutingFileName = "ServiceLib.Sample.custom_routing_"; - public const string TunSingboxDNSFileName = "ServiceLib.Sample.tun_singbox_dns"; - public const string TunSingboxInboundFileName = "ServiceLib.Sample.tun_singbox_inbound"; - public const string TunSingboxRulesFileName = "ServiceLib.Sample.tun_singbox_rules"; - public const string DNSV2rayNormalFileName = "ServiceLib.Sample.dns_v2ray_normal"; - public const string DNSSingboxNormalFileName = "ServiceLib.Sample.dns_singbox_normal"; - public const string ClashMixinYaml = "ServiceLib.Sample.clash_mixin_yaml"; - public const string ClashTunYaml = "ServiceLib.Sample.clash_tun_yaml"; + public const string NamespaceSample = "ServiceLib.Sample."; + public const string V2raySampleClient = NamespaceSample + "SampleClientConfig"; + public const string SingboxSampleClient = NamespaceSample + "SingboxSampleClientConfig"; + public const string V2raySampleHttpRequestFileName = NamespaceSample + "SampleHttpRequest"; + public const string V2raySampleHttpResponseFileName = NamespaceSample + "SampleHttpResponse"; + public const string V2raySampleInbound = NamespaceSample + "SampleInbound"; + public const string V2raySampleOutbound = NamespaceSample + "SampleOutbound"; + public const string SingboxSampleOutbound = NamespaceSample + "SingboxSampleOutbound"; + public const string CustomRoutingFileName = NamespaceSample + "custom_routing_"; + public const string TunSingboxDNSFileName = NamespaceSample + "tun_singbox_dns"; + public const string TunSingboxInboundFileName = NamespaceSample + "tun_singbox_inbound"; + public const string TunSingboxRulesFileName = NamespaceSample + "tun_singbox_rules"; + public const string DNSV2rayNormalFileName = NamespaceSample + "dns_v2ray_normal"; + public const string DNSSingboxNormalFileName = NamespaceSample + "dns_singbox_normal"; + public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml"; + public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml"; + public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config"; + public const string DefaultSecurity = "auto"; public const string DefaultNetwork = "tcp"; public const string TcpHeaderHttp = "http"; diff --git a/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs b/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs new file mode 100644 index 00000000..acfd3e41 --- /dev/null +++ b/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs @@ -0,0 +1,148 @@ +using System.Security.Principal; +using System.Text.RegularExpressions; + +namespace ServiceLib.Handler +{ + public static class AutoStartupHandler + { + public static async Task UpdateTask(Config config) + { + if (Utils.IsWindows()) + { + await ClearTaskWindows(); + + if (config.GuiItem.AutoRun) + { + await SetTaskWindows(); + } + } + else if (Utils.IsLinux()) + { + await ClearTaskLinux(); + + if (config.GuiItem.AutoRun) + { + await SetTaskLinux(); + } + } + + return true; + } + + #region Windows + + private static async Task ClearTaskWindows() + { + var autoRunName = GetAutoRunNameWindows(); + WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, ""); + if (Utils.IsAdministrator()) + { + AutoStartTaskService(autoRunName, "", ""); + } + } + + private static async Task SetTaskWindows() + { + try + { + var autoRunName = GetAutoRunNameWindows(); + var exePath = Utils.GetExePath(); + if (Utils.IsAdministrator()) + { + AutoStartTaskService(autoRunName, exePath, ""); + } + else + { + WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, exePath.AppendQuotes()); + } + } + catch (Exception ex) + { + Logging.SaveLog(ex.Message, ex); + } + } + + /// + /// Auto Start via TaskService + /// + /// + /// + /// + /// + public static void AutoStartTaskService(string taskName, string fileName, string description) + { + if (Utils.IsNullOrEmpty(taskName)) + { + return; + } + + var logonUser = WindowsIdentity.GetCurrent().Name; + using var taskService = new Microsoft.Win32.TaskScheduler.TaskService(); + var tasks = taskService.RootFolder.GetTasks(new Regex(taskName)); + if (Utils.IsNullOrEmpty(fileName)) + { + foreach (var t in tasks) + { + taskService.RootFolder.DeleteTask(t.Name); + } + return; + } + + var task = taskService.NewTask(); + task.RegistrationInfo.Description = description; + task.Settings.DisallowStartIfOnBatteries = false; + task.Settings.StopIfGoingOnBatteries = false; + task.Settings.RunOnlyIfIdle = false; + task.Settings.IdleSettings.StopOnIdleEnd = false; + task.Settings.ExecutionTimeLimit = TimeSpan.Zero; + task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(10) }); + task.Principal.RunLevel = Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest; + task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName))); + + taskService.RootFolder.RegisterTaskDefinition(taskName, task); + } + + private static string GetAutoRunNameWindows() + { + return $"{Global.AutoRunName}_{Utils.GetMd5(Utils.StartupPath())}"; + } + + #endregion Windows + + #region Linux + + private static async Task ClearTaskLinux() + { + File.Delete(GetHomePathLinux()); + } + + private static async Task SetTaskLinux() + { + try + { + var linuxConfig = Utils.GetEmbedText(Global.LinuxAutostartConfig); + if (linuxConfig.IsNotEmpty()) + { + linuxConfig = linuxConfig.Replace("$ExecPath$", Utils.GetExePath()); + Logging.SaveLog(linuxConfig); + + var homePath = GetHomePathLinux(); + Directory.CreateDirectory(Path.GetDirectoryName(homePath)); + + await File.WriteAllTextAsync(homePath, linuxConfig); + } + } + catch (Exception ex) + { + Logging.SaveLog(ex.Message, ex); + } + } + + private static string GetHomePathLinux() + { + return Path.Combine(Utils.GetHomePath(), ".config", "autostart", $"{Global.AppName}.desktop"); + } + + #endregion Linux + } +} \ No newline at end of file diff --git a/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs b/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs index dbeeedb6..8ddd703f 100644 --- a/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs +++ b/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs @@ -11,24 +11,24 @@ namespace ServiceLib.Handler.SysProxy { if (type == 1) { - RegWriteValue(_regPath, "ProxyEnable", 0); - RegWriteValue(_regPath, "ProxyServer", string.Empty); - RegWriteValue(_regPath, "ProxyOverride", string.Empty); - RegWriteValue(_regPath, "AutoConfigURL", string.Empty); + WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0); + WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty); + WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty); + WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty); } if (type == 2) { - RegWriteValue(_regPath, "ProxyEnable", 1); - RegWriteValue(_regPath, "ProxyServer", strProxy ?? string.Empty); - RegWriteValue(_regPath, "ProxyOverride", exceptions ?? string.Empty); - RegWriteValue(_regPath, "AutoConfigURL", string.Empty); + WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 1); + WindowsUtils.RegWriteValue(_regPath, "ProxyServer", strProxy ?? string.Empty); + WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", exceptions ?? string.Empty); + WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty); } else if (type == 4) { - RegWriteValue(_regPath, "ProxyEnable", 0); - RegWriteValue(_regPath, "ProxyServer", string.Empty); - RegWriteValue(_regPath, "ProxyOverride", string.Empty); - RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty); + WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0); + WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty); + WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty); + WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty); } return true; } @@ -356,30 +356,5 @@ namespace ServiceLib.Handler.SysProxy ref int lpcEntries // Number of entries written to the buffer ); } - - private static void RegWriteValue(string path, string name, object value) - { - Microsoft.Win32.RegistryKey? regKey = null; - try - { - regKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(path); - if (string.IsNullOrEmpty(value.ToString())) - { - regKey?.DeleteValue(name, false); - } - else - { - regKey?.SetValue(name, value); - } - } - catch (Exception ex) - { - //Logging.SaveLog(ex.Message, ex); - } - finally - { - regKey?.Close(); - } - } } } \ No newline at end of file diff --git a/v2rayN/ServiceLib/Sample/linux_autostart_config b/v2rayN/ServiceLib/Sample/linux_autostart_config new file mode 100644 index 00000000..c2f2f4e5 --- /dev/null +++ b/v2rayN/ServiceLib/Sample/linux_autostart_config @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Exec=$ExecPath$ +Hidden=false +NoDisplay=false +X-GNOME-Autostart-enabled=true +Name[en_US]=v2rayN +Name=v2rayN +Comment[en_US]=v2rayN +Comment=v2rayN \ No newline at end of file diff --git a/v2rayN/ServiceLib/ServiceLib.csproj b/v2rayN/ServiceLib/ServiceLib.csproj index 9fefa90e..69baebff 100644 --- a/v2rayN/ServiceLib/ServiceLib.csproj +++ b/v2rayN/ServiceLib/ServiceLib.csproj @@ -6,7 +6,7 @@ enable 7.0.4 - + @@ -19,6 +19,8 @@ + + @@ -41,6 +43,7 @@ + diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index 54ae03ab..26a78152 100644 --- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -349,14 +349,9 @@ namespace ServiceLib.ViewModels if (await ConfigHandler.SaveConfig(_config) == 0) { - if (needReboot) - { - NoticeHandler.Instance.Enqueue(ResUI.NeedRebootTips); - } - else - { - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); - } + await AutoStartupHandler.UpdateTask(_config); + + NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess); _updateView?.Invoke(EViewAction.CloseWindow, null); } else diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index e0907647..8ac6dedc 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -377,7 +377,7 @@ -