system.dart 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import 'dart:io';
  2. import 'package:speed_safe/app/util/log.dart';
  3. import 'package:path/path.dart' as p;
  4. enum OS { windows, linux, macos }
  5. enum Architecture { x86_64, arm64 }
  6. const speedVersion = '0.7.6';
  7. const speedBuildNumber = 7;
  8. const speedFullVersion = '$speedVersion+$speedBuildNumber';
  9. const speedLastCommitHash = 'SELF_BUILD';
  10. const String singBoxUrl = 'https://github.com/SagerNet/sing-box';
  11. const String xrayCoreRepoUrl = 'https://github.com/xtls/xray-core';
  12. const String shadowsocksRustUrl =
  13. 'https://github.com/shadowsocks/shadowsocks-rust';
  14. const String hysteriaUrl = 'https://github.com/apernet/hysteria';
  15. const String singBoxRulesRepoUrl = 'https://github.com/lyc8503/sing-box-rules';
  16. const String geositeDBUrl =
  17. 'https://github.com/lyc8503/sing-box-rules/releases/latest/download/geosite.db';
  18. const String geoipDBUrl =
  19. 'https://github.com/lyc8503/sing-box-rules/releases/latest/download/geoip.db';
  20. const String v2rayRulesDatRepoUrl =
  21. 'https://github.com/Loyalsoldier/v2ray-rules-dat';
  22. const String geositeDatUrl =
  23. 'https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat';
  24. const String geoipDatUrl =
  25. 'https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat';
  26. const String sphiaRepoUrl = 'https://github.com/YukidouSatoru/sphia';
  27. final coreRepositories = {
  28. 'sing-box': singBoxUrl,
  29. 'xray-core': xrayCoreRepoUrl,
  30. 'shadowsocks-rust': shadowsocksRustUrl,
  31. 'hysteria': hysteriaUrl,
  32. 'sing-box-rules': singBoxRulesRepoUrl,
  33. 'v2ray-rules-dat': v2rayRulesDatRepoUrl,
  34. 'sphia': sphiaRepoUrl,
  35. // Add new core here
  36. };
  37. class SystemUtil{
  38. static late final OS os;
  39. static late final Architecture architecture;
  40. static late final bool isRoot;
  41. static void init() {
  42. os = determineOS();
  43. architecture = determineArchitecture();
  44. isRoot = determineIsRoot();
  45. }
  46. static OS determineOS() {
  47. if(Platform.isWindows) {
  48. return OS.windows;
  49. } else if (Platform.isLinux) {
  50. return OS.linux;
  51. } else if (Platform.isMacOS){
  52. return OS.macos;
  53. } else {
  54. logger.e('Unsupported OS');
  55. throw Exception('Unsupported OS');
  56. }
  57. }
  58. static Architecture determineArchitecture() {
  59. if (os == OS.windows) {
  60. final arch = Platform.environment['PROCESSOR_ARCHITECTURE'];
  61. // https://learn.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details
  62. if (arch == 'AMD64') {
  63. return Architecture.x86_64;
  64. } else if (arch == 'ARM64') {
  65. return Architecture.arm64;
  66. } else {
  67. logger.e('Unsupported Architecture');
  68. throw Exception('Unsupported Architecture');
  69. }
  70. } else {
  71. final result = Process.runSync('uname', ['-m']);
  72. if (result.exitCode == 0) {
  73. final arch = result.stdout.toString().trim();
  74. if (arch == 'x86_64') {
  75. return Architecture.x86_64;
  76. } else if (arch == 'aarch64' || arch == 'arm64') {
  77. return Architecture.arm64;
  78. } else {
  79. logger.e('Unsupported Architecture');
  80. throw Exception('Unsupported Architecture');
  81. }
  82. } else {
  83. logger.e('Unsupported Architecture');
  84. throw Exception('Unsupported Architecture');
  85. }
  86. }
  87. }
  88. static bool determineIsRoot() {
  89. if (os == OS.windows) {
  90. final result = Process.runSync('net', ['session']);
  91. if (result.exitCode == 0) {
  92. return true;
  93. } else {
  94. return false;
  95. }
  96. } else {
  97. final result = Process.runSync('id', ['-u']);
  98. if (result.exitCode == 0) {
  99. return result.stdout.toString().trim() == '0';
  100. } else {
  101. return false;
  102. }
  103. }
  104. }
  105. static void enableWindowsProxy(String listen, int httpPort) async {
  106. await runCommand('reg', [
  107. 'add',
  108. 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
  109. '/v',
  110. 'ProxyEnable',
  111. '/t',
  112. 'REG_DWORD',
  113. '/d',
  114. '1',
  115. '/f'
  116. ]);
  117. await runCommand('reg', [
  118. 'add',
  119. 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
  120. '/v',
  121. 'ProxyServer',
  122. '/d',
  123. '$listen:$httpPort',
  124. '/f'
  125. ]);
  126. await runCommand('reg', [
  127. 'add',
  128. 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
  129. '/v',
  130. 'ProxyOverride',
  131. '/d',
  132. '127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*',
  133. '/f'
  134. ]);
  135. }
  136. static void disableWindowsProxy() async {
  137. await runCommand('reg', [
  138. 'add',
  139. 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
  140. '/v',
  141. 'ProxyEnable',
  142. '/t',
  143. 'REG_DWORD',
  144. '/d',
  145. '0',
  146. '/f'
  147. ]);
  148. }
  149. static void enableMacOSProxy(String listen, int httpPort) async {
  150. await runCommand(
  151. 'networksetup', ['-setwebproxy', 'wi-fi', listen, httpPort.toString()]);
  152. await runCommand('networksetup',
  153. ['-setsecurewebproxy', 'wi-fi', listen, httpPort.toString()]);
  154. await runCommand('networksetup', ['-setwebproxystate', 'wi-fi', 'on']);
  155. await runCommand(
  156. 'networksetup', ['-setsecurewebproxystate', 'wi-fi', 'on']);
  157. }
  158. static void disableMacOSProxy() async {
  159. await runCommand('networksetup', ['-setwebproxystate', 'wi-fi', 'off']);
  160. await runCommand(
  161. 'networksetup', ['-setsecurewebproxystate', 'wi-fi', 'off']);
  162. }
  163. static Future<void> runCommand(
  164. String executable, List<String> arguments) async {
  165. final result = await Process.run(executable, arguments, runInShell: true);
  166. if (result.exitCode != 0) {
  167. logger.e('Failed to run command: $executable $arguments');
  168. throw Exception('Failed to run command: $executable $arguments');
  169. }
  170. }
  171. static String getCoreFileName(String coreName) {
  172. final ext = os == OS.windows ? '.exe' : '';
  173. switch (coreName) {
  174. case 'sing-box':
  175. return 'sing-box$ext';
  176. case 'xray-core':
  177. return 'xray$ext';
  178. case 'shadowsocks-rust':
  179. return 'sslocal$ext';
  180. case 'hysteria':
  181. final plat = os == OS.macos ? 'darwin' : os.name;
  182. final arch = architecture == Architecture.arm64 ? 'arm64' : 'amd64';
  183. return 'hysteria-$plat-$arch$ext';
  184. case 'sphia':
  185. return 'sphia$ext';
  186. case 'upgradeAgent':
  187. return 'upgradeAgent$ext';
  188. default:
  189. throw Exception('Unsupported core: $coreName');
  190. }
  191. }
  192. static void setFilePermission(String fileName) {
  193. if (os != OS.windows) {
  194. Process.runSync('chmod', ['+x', fileName]);
  195. }
  196. }
  197. static void createDirectory(String dirName) {
  198. final dir = Directory(dirName);
  199. try {
  200. if (!dir.existsSync()) {
  201. logger.i('Creating directory: $dirName');
  202. dir.createSync();
  203. }
  204. } catch (e) {
  205. logger.e('Error creating directory: $e');
  206. }
  207. }
  208. static bool fileExists(String fileName) {
  209. return File(p.join(binPath, fileName)).existsSync();
  210. }
  211. static void deleteFileIfExists(String filePath, String logMessage) {
  212. final file = File(filePath);
  213. if (file.existsSync()) {
  214. logger.i(logMessage);
  215. file.deleteSync();
  216. }
  217. }
  218. static bool coreExists(String coreName) {
  219. if (coreName == 'sing-box-rules') {
  220. return fileExists(p.join(binPath, 'geoip.db')) &&
  221. fileExists(p.join(binPath, 'geosite.db'));
  222. } else if (coreName == 'v2ray-rules-dat') {
  223. return fileExists(p.join(binPath, 'geoip.dat')) &&
  224. fileExists(p.join(binPath, 'geosite.dat'));
  225. } else {
  226. return fileExists(p.join(binPath, getCoreFileName(coreName)));
  227. }
  228. }
  229. static List<String> getCoreFileNames() {
  230. List<String> fileNames = [];
  231. coreRepositories.entries
  232. .toList()
  233. .sublist(0, coreRepositories.length - 3)
  234. .forEach((entry) {
  235. fileNames.add(getCoreFileName(entry.key));
  236. });
  237. return fileNames;
  238. }
  239. static void initPaths() {
  240. binPath = p.join(appPath, 'bin');
  241. configPath = p.join(appPath, 'config');
  242. logPath = p.join(appPath, 'log');
  243. tempPath = p.join(appPath, 'temp');
  244. }
  245. }
  246. late final String execPath;
  247. late final String appPath;
  248. late String binPath;
  249. late String configPath;
  250. late String logPath;
  251. late String tempPath;