CLI tool for running Playbooks
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

718 lines
32 KiB

1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
6 months ago
1 year ago
9 months ago
1 year ago
9 months ago
6 months ago
9 months ago
6 months ago
9 months ago
1 year ago
6 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
6 months ago
9 months ago
6 months ago
9 months ago
1 year ago
1 year ago
1 year ago
1 year ago
6 months ago
1 year ago
1 year ago
1 year ago
7 months ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
6 months ago
1 year ago
9 months ago
1 year ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
  1. using Newtonsoft.Json.Linq;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.IO.MemoryMappedFiles;
  7. using System.Linq;
  8. using System.Net;
  9. using System.Net.Http;
  10. using System.Runtime.InteropServices;
  11. using System.ServiceProcess;
  12. using System.Text;
  13. using System.Threading;
  14. using System.Threading.Tasks;
  15. using System.Windows;
  16. using System.Xml;
  17. using System.Xml.Serialization;
  18. using TrustedUninstaller.Shared.Actions;
  19. using TrustedUninstaller.Shared.Parser;
  20. using TrustedUninstaller.Shared.Tasks;
  21. namespace TrustedUninstaller.Shared
  22. {
  23. public static class AmeliorationUtil
  24. {
  25. private static readonly ConfigParser Parser = new ConfigParser();
  26. private static readonly HttpClient Client = new HttpClient();
  27. public static Playbook Playbook { set; get; } = new Playbook();
  28. public static bool UseKernelDriver = false;
  29. public static readonly List<string> ErrorDisplayList = new List<string>();
  30. public static int GetProgressMaximum(List<string> options)
  31. {
  32. return Parser.Tasks.Sum(task => task.Actions.Sum(action =>
  33. {
  34. var taskAction = (TaskAction)action;
  35. if ((!IsApplicableOption(taskAction.Option, options) || !IsApplicableArch(taskAction.Arch)) ||
  36. (taskAction.Builds != null && (
  37. !taskAction.Builds.Where(build => !build.StartsWith("!")).Any(build => IsApplicableWindowsVersion(build))
  38. ||
  39. taskAction.Builds.Where(build => build.StartsWith("!")).Any(build => !IsApplicableWindowsVersion(build)))) ||
  40. (taskAction.Options != null && (
  41. !taskAction.Options.Where(option => !option.StartsWith("!")).Any(option => IsApplicableOption(option, Playbook.Options))
  42. ||
  43. taskAction.Options.Where(option => option.StartsWith("!")).Any(option => !IsApplicableOption(option, Playbook.Options)))))
  44. {
  45. return 0;
  46. }
  47. return action.GetProgressWeight();
  48. }));
  49. }
  50. public static bool AddTasks(string configPath, string file)
  51. {
  52. try
  53. {
  54. //This allows for a proper detection of if any error occurred, and if so the CLI will relay an :AME-Fatal Error:
  55. //This is important, as we want the process to stop immediately if a YAML syntax error was detected.
  56. bool hadError = false;
  57. //Adds the config file to the parser's task list
  58. Parser.Add(Path.Combine(configPath, file));
  59. var currentTask = Parser.Tasks[Parser.Tasks.Count - 1];
  60. if ((!IsApplicableOption(currentTask.Option, Playbook.Options) || !IsApplicableArch(currentTask.Arch)) ||
  61. (currentTask.Builds != null && (
  62. !currentTask.Builds.Where(build => !build.StartsWith("!")).Any(build => IsApplicableWindowsVersion(build))
  63. ||
  64. currentTask.Builds.Where(build => build.StartsWith("!")).Any(build => !IsApplicableWindowsVersion(build)))) ||
  65. (currentTask.Options != null && (
  66. !currentTask.Options.Where(option => !option.StartsWith("!")).Any(option => IsApplicableOption(option, Playbook.Options))
  67. ||
  68. currentTask.Options.Where(option => option.StartsWith("!")).Any(option => !IsApplicableOption(option, Playbook.Options)))))
  69. {
  70. Parser.Tasks.Remove(currentTask);
  71. return true;
  72. }
  73. //Get the features of the last added task (the task that was just added from the config file)
  74. var features = currentTask.Features;
  75. //Each feature would reference a directory that has a YAML file, we take those directories and then run the
  76. //AddTasks function again, until we reach a file that doesn't reference any other YAML files, and add them
  77. //all to the parser's tasks list.
  78. if (features == null) return true;
  79. foreach (var feature in features)
  80. {
  81. var subResult = AddTasks(configPath, feature);
  82. // We could return false here, however we want to output ALL detected YAML errors,
  83. // which is why we continue here.
  84. if (!subResult) hadError = true;
  85. }
  86. return hadError ? false : true;
  87. }
  88. catch (Exception e)
  89. {
  90. Console.WriteLine($"Error adding tasks in {configPath + "\\" + file}:\r\n{e.Message}");
  91. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, $"Error adding tasks in {configPath + "\\" + file}.");
  92. return false;
  93. }
  94. }
  95. public static async Task<int> DoActions(UninstallTask task, UninstallTaskPrivilege privilege)
  96. {
  97. try
  98. {
  99. //If the privilege is admin and the program is running as TI, do not do the action.
  100. //if (privilege == UninstallTaskPrivilege.Admin && WinUtil.IsTrustedInstaller())
  101. //{
  102. // return 0;
  103. //}
  104. if (!WinUtil.IsTrustedInstaller())
  105. {
  106. Console.WriteLine("Relaunching as Trusted Installer!");
  107. var mmf = MemoryMappedFile.CreateNew("ImgA", 30000000);
  108. WinUtil.RelaunchAsTrustedInstaller();
  109. if (NativeProcess.Process == null)
  110. {
  111. ErrorLogger.WriteToErrorLog($"Could not launch TrustedInstaller process. Return output was null.",
  112. Environment.StackTrace, "Error while attempting to sync with TrustedInstaller process.");
  113. Console.WriteLine(":AME-Fatal Error: Could not launch TrustedInstaller process.");
  114. Environment.Exit(-1);
  115. }
  116. var delay = 20;
  117. while (!NativeProcess.Process.HasExited)
  118. {
  119. if (delay > 3500)
  120. {
  121. NativeProcess.Process.Kill();
  122. ErrorLogger.WriteToErrorLog($"Could not initialize memory data exchange. Timeframe exceeded.",
  123. Environment.StackTrace, "Error while attempting to sync with TrustedInstaller process.");
  124. Console.WriteLine(":AME-Fatal Error: Could not initialize memory data exchange.");
  125. Environment.Exit(-1);
  126. }
  127. Task.Delay(delay).Wait();
  128. // Kind of inefficient looping this, however it's likely to cause access errors otherwise
  129. using var stream = mmf.CreateViewStream();
  130. using BinaryReader binReader = new BinaryReader(stream);
  131. {
  132. var res = binReader.ReadBytes((int)stream.Length);
  133. var data = Encoding.UTF8.GetString(res);
  134. var end = data.IndexOf('\0');
  135. if (end == 0)
  136. {
  137. delay += 200;
  138. }
  139. else
  140. {
  141. break;
  142. }
  143. }
  144. }
  145. var offset = 0;
  146. var read = false;
  147. using (var stream = mmf.CreateViewStream())
  148. {
  149. while (!NativeProcess.Process.HasExited || read)
  150. {
  151. read = false;
  152. BinaryReader binReader = new BinaryReader(stream);
  153. binReader.BaseStream.Seek(offset, SeekOrigin.Begin);
  154. var res = binReader.ReadBytes((int)stream.Length - offset);
  155. var data = Encoding.UTF8.GetString(res);
  156. var end = data.IndexOf("\0");
  157. var content = data.Substring(0, end);
  158. offset += Encoding.UTF8.GetBytes(content).Length;
  159. var output = content.Split(new [] {Environment.NewLine}, StringSplitOptions.None);
  160. if (output.Length > 0) output = output.Take(output.Length - 1).ToArray();
  161. foreach (var line in output)
  162. {
  163. Console.WriteLine(line);
  164. read = true;
  165. // Introducing ANY delay here makes it lag behind, which isn't ideal
  166. //Task.Delay(5).Wait();
  167. }
  168. Task.Delay(20).Wait();
  169. }
  170. }
  171. mmf.Dispose();
  172. return 0; //Only returns after TI is done
  173. }
  174. //Goes through the list of tasks that are inside the parser class,
  175. //and runs the task using the RunTask method
  176. //Check the Actions folder inside the Shared folder for reference.
  177. foreach (ITaskAction action in task.Actions)
  178. {
  179. var taskAction = (TaskAction)action;
  180. if ((!IsApplicableOption(taskAction.Option, Playbook.Options) || !IsApplicableArch(taskAction.Arch)) ||
  181. (taskAction.Builds != null && (
  182. !taskAction.Builds.Where(build => !build.StartsWith("!")).Any(build => IsApplicableWindowsVersion(build))
  183. ||
  184. taskAction.Builds.Where(build => build.StartsWith("!")).Any(build => !IsApplicableWindowsVersion(build)))) ||
  185. (taskAction.Options != null && (
  186. !taskAction.Options.Where(option => !option.StartsWith("!")).Any(option => IsApplicableOption(option, Playbook.Options))
  187. ||
  188. taskAction.Options.Where(option => option.StartsWith("!")).Any(option => !IsApplicableOption(option, Playbook.Options)))))
  189. {
  190. continue;
  191. }
  192. int i = 0;
  193. //var actionType = action.GetType().ToString().Replace("TrustedUninstaller.Shared.Actions.", "");
  194. try
  195. {
  196. do
  197. {
  198. //Console.WriteLine($"Running {actionType}");
  199. Console.WriteLine();
  200. try
  201. {
  202. var actionTask = action.RunTask();
  203. if (actionTask == null)
  204. action.RunTaskOnMainThread();
  205. else await actionTask;
  206. action.ResetProgress();
  207. }
  208. catch (Exception e)
  209. {
  210. action.ResetProgress();
  211. if (e.InnerException != null)
  212. {
  213. ErrorLogger.WriteToErrorLog(e.InnerException.Message, e.InnerException.StackTrace, e.Message);
  214. }
  215. else
  216. {
  217. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, action.ErrorString());
  218. List<string> ExceptionBreakList = new List<string>() { "System.ArgumentException", "System.SecurityException", "System.UnauthorizedAccessException", "System.TimeoutException" };
  219. if (ExceptionBreakList.Any(x => x.Equals(e.GetType().ToString())))
  220. {
  221. i = 10;
  222. break;
  223. }
  224. }
  225. Thread.Sleep(300);
  226. }
  227. Console.WriteLine($"Status: {action.GetStatus()}");
  228. if (i > 0) Thread.Sleep(50);
  229. i++;
  230. } while (action.GetStatus() != UninstallTaskStatus.Completed && i < 10);
  231. }
  232. catch (Exception e)
  233. {
  234. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "Critical error while running action.");
  235. if (!((TaskAction)action).IgnoreErrors)
  236. Console.WriteLine($":AME-ERROR: Critical error while running action: " + e.Message);
  237. }
  238. if (i == 10)
  239. {
  240. var errorString = action.ErrorString();
  241. ErrorLogger.WriteToErrorLog(errorString, Environment.StackTrace, "Action failed to complete.");
  242. // AmeliorationUtil.ErrorDisplayList.Add(errorString) would NOT work here since this
  243. // might be a separate process, and thus has to be forwarded via the console
  244. if (!((TaskAction)action).IgnoreErrors)
  245. Console.WriteLine($":AME-ERROR: {errorString}");
  246. //Environment.Exit(-2);
  247. Console.WriteLine($"Action completed. Weight:{action.GetProgressWeight()}");
  248. continue;
  249. }
  250. Console.WriteLine($"Action completed. Weight:{action.GetProgressWeight()}");
  251. }
  252. Console.WriteLine("Task completed.");
  253. ProcessPrivilege.ResetTokens();
  254. }
  255. catch (Exception e)
  256. {
  257. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
  258. "Encountered an error while doing task actions.");
  259. }
  260. return 0;
  261. }
  262. public static Task<Playbook> DeserializePlaybook(string dir)
  263. {
  264. Playbook pb;
  265. XmlSerializer serializer = new XmlSerializer(typeof(Playbook));
  266. /*serializer.UnknownElement += delegate(object sender, XmlElementEventArgs args)
  267. {
  268. MessageBox.Show(args.Element.Name);
  269. };
  270. serializer.UnknownAttribute += delegate(object sender, XmlAttributeEventArgs args)
  271. {
  272. MessageBox.Show(args.Attr.Name);
  273. };*/
  274. using (XmlReader reader = XmlReader.Create($"{dir}\\playbook.conf"))
  275. {
  276. pb = (Playbook)serializer.Deserialize(reader);
  277. }
  278. var validateResult = pb.Validate();
  279. if (validateResult != null)
  280. throw new XmlException(validateResult);
  281. if (File.Exists($"{dir}\\options.txt"))
  282. {
  283. pb.Options = new List<string>();
  284. using (var reader = new StreamReader($"{dir}\\options.txt"))
  285. {
  286. while (!reader.EndOfStream)
  287. pb.Options.Add(reader.ReadLine());
  288. }
  289. }
  290. pb.Path = dir;
  291. return Task.FromResult(pb);
  292. }
  293. public static async Task<int> StartAmelioration()
  294. {
  295. //Needed after defender removal's reboot, the "current directory" will be set to System32
  296. //After the auto start up.
  297. Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
  298. if (Directory.Exists("Logs") && !WinUtil.IsTrustedInstaller())
  299. {
  300. if (File.Exists("Logs\\AdminOutput.txt"))
  301. {
  302. File.Delete("Logs\\AdminOutput.txt");
  303. }
  304. if (File.Exists("Logs\\TIOutput.txt"))
  305. {
  306. File.Delete("Logs\\TIOutput.txt");
  307. }
  308. }
  309. //Check if KPH is installed.
  310. ServiceController service = ServiceController.GetDevices()
  311. .FirstOrDefault(s => s.DisplayName == "KProcessHacker2");
  312. if (service == null)
  313. {
  314. //Installs KPH
  315. await WinUtil.RemoveProtectionAsync();
  316. }
  317. var langsFile = Path.Combine($"{Playbook.Path}\\Configuration", "langs.txt");
  318. //Download language packs that were selected by the user
  319. if (!File.Exists(langsFile))
  320. {
  321. File.Create(langsFile);
  322. }
  323. //var langsSelected = File.ReadLines(langsFile);
  324. //await DownloadLanguagesAsync(langsSelected);
  325. //Start adding tasks from the top level configuration folder.
  326. if (!AddTasks($"{Playbook.Path}\\Configuration", "custom.yml"))
  327. {
  328. Console.WriteLine($":AME-Fatal Error: Error adding tasks.");
  329. Environment.Exit(1);
  330. }
  331. if (!Parser.Tasks.Any())
  332. {
  333. Console.Error.WriteLine($"Couldn't find any tasks.");
  334. return -1;
  335. }
  336. //Sort the list based on the priority value.
  337. if (Parser.Tasks.Any(x => x.Priority != Parser.Tasks.First().Priority))
  338. Parser.Tasks.Sort(new TaskComparer());
  339. bool launched = false;
  340. foreach (var task in Parser.Tasks.Where(task => task.Actions.Count != 0))
  341. {
  342. try
  343. {
  344. //if (prevPriv == UninstallTaskPrivilege.TrustedInstaller && task.Privilege == UninstallTaskPrivilege.TrustedInstaller && !WinUtil.IsTrustedInstaller())
  345. if (!WinUtil.IsTrustedInstaller() && launched)
  346. {
  347. continue;
  348. }
  349. launched = true;
  350. await DoActions(task, task.Privilege);
  351. //prevPriv = task.Privilege;
  352. }
  353. catch (Exception ex)
  354. {
  355. ErrorLogger.WriteToErrorLog(ex.Message, ex.StackTrace, "Error during DoAction loop.");
  356. }
  357. }
  358. if (WinUtil.IsTrustedInstaller()) return 0;
  359. WinUtil.RegistryManager.UnhookUserHives();
  360. //Check if the kernel driver is installed.
  361. //service = ServiceController.GetDevices()
  362. //.FirstOrDefault(s => s.DisplayName == "KProcessHacker2");
  363. if (UseKernelDriver)
  364. {
  365. //Remove Process Hacker's kernel driver.
  366. await WinUtil.UninstallDriver();
  367. await AmeliorationUtil.SafeRunAction(new RegistryKeyAction()
  368. {
  369. KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\KProcessHacker2",
  370. });
  371. }
  372. Console.WriteLine();
  373. Console.WriteLine("Playbook finished.");
  374. return 0;
  375. }
  376. public static async Task DownloadLanguagesAsync(IEnumerable<string> langsSelected)
  377. {
  378. foreach (var lang in langsSelected)
  379. {
  380. var lowerLang = lang.ToLower();
  381. var arch = RuntimeInformation.OSArchitecture;
  382. var winVersion = Environment.OSVersion.Version.Build;
  383. var convertedArch = "";
  384. switch (arch)
  385. {
  386. case Architecture.X64:
  387. convertedArch = "amd64";
  388. break;
  389. case Architecture.Arm64:
  390. convertedArch = "arm64";
  391. break;
  392. case Architecture.X86:
  393. convertedArch = "x86";
  394. break;
  395. }
  396. var uuidOfWindowsVersion = "";
  397. var uuidResponse =
  398. await Client.GetAsync(
  399. $"https://api.uupdump.net/listid.php?search={winVersion}%20{convertedArch}&sortByDate=1");
  400. switch (uuidResponse.StatusCode)
  401. {
  402. //200 Status code
  403. case HttpStatusCode.OK:
  404. {
  405. var result = uuidResponse.Content.ReadAsStringAsync().Result;
  406. //Gets the UUID of the first build object in the response, we take the first since it's the newest.
  407. uuidOfWindowsVersion = (string)(JToken.Parse(result)["response"]?["builds"]?.Children().First()
  408. .Children().First().Last());
  409. break;
  410. }
  411. //400 Status code
  412. case HttpStatusCode.BadRequest:
  413. {
  414. var result = uuidResponse.Content.ReadAsStringAsync().Result;
  415. dynamic data = JObject.Parse(result);
  416. Console.WriteLine($"Bad request.\r\nError:{data["response"]["error"]}");
  417. break;
  418. }
  419. //429 Status code
  420. case (HttpStatusCode)429:
  421. {
  422. var result = uuidResponse.Content.ReadAsStringAsync().Result;
  423. dynamic data = JObject.Parse(result);
  424. Console.WriteLine($"Too many requests, try again later.\r\nError:{data["response"]["error"]}");
  425. break;
  426. }
  427. //500 Status code
  428. case HttpStatusCode.InternalServerError:
  429. {
  430. var result = uuidResponse.Content.ReadAsStringAsync().Result;
  431. dynamic data = JObject.Parse(result);
  432. Console.WriteLine($"Internal Server Error.\r\nError:{data["response"]["error"]}");
  433. break;
  434. }
  435. default:
  436. throw new ArgumentOutOfRangeException();
  437. }
  438. var responseString =
  439. await Client.GetAsync(
  440. $"https://api.uupdump.net/get.php?id={uuidOfWindowsVersion}&lang={lowerLang}");
  441. switch (responseString.StatusCode)
  442. {
  443. //200 Status code
  444. case HttpStatusCode.OK:
  445. {
  446. var result = responseString.Content.ReadAsStringAsync().Result;
  447. dynamic data = JObject.Parse(result);
  448. //Add different urls to different packages to a list
  449. var urls = new Dictionary<string, string>
  450. {
  451. {
  452. "basic", (string) data["response"]["files"][
  453. $"microsoft-windows-languagefeatures-basic-{lowerLang}-package-{convertedArch}.cab"]
  454. [
  455. "url"]
  456. },
  457. {
  458. "hw", (string) data["response"]["files"][
  459. $"microsoft-windows-languagefeatures-handwriting-{lowerLang}-package-{convertedArch}.cab"]
  460. [
  461. "url"]
  462. },
  463. {
  464. "ocr", (string) data["response"]["files"][
  465. $"microsoft-windows-languagefeatures-ocr-{lowerLang}-package-{convertedArch}.cab"][
  466. "url"]
  467. },
  468. {
  469. "speech", (string) data["response"]["files"][
  470. $"microsoft-windows-languagefeatures-speech-{lowerLang}-package-{convertedArch}.cab"]
  471. [
  472. "url"]
  473. },
  474. {
  475. "tts", (string) data["response"]["files"][
  476. $"microsoft-windows-languagefeatures-texttospeech-{lowerLang}-package-{convertedArch}.cab"]
  477. [
  478. "url"]
  479. }
  480. };
  481. var amePath = Path.Combine(Path.GetTempPath(), "AME\\");
  482. //Create the directory if it doesn't exist.
  483. var file = new FileInfo(amePath);
  484. file.Directory?.Create(); //Does nothing if the directory already exists
  485. //Final result being "temp\AME\Languages\file.cab"
  486. var downloadPath = Path.Combine(amePath, "Languages\\");
  487. file = new FileInfo(downloadPath);
  488. file.Directory?.Create();
  489. using (var webClient = new WebClient())
  490. {
  491. Console.WriteLine($"Downloading {lowerLang}.cab file, please wait..");
  492. foreach (var url in urls)
  493. {
  494. //Check if the file exists, if it does exist, skip it.
  495. if (File.Exists(Path.Combine(downloadPath, $"{url.Key}_{lowerLang}.cab")))
  496. {
  497. Console.WriteLine($"{url.Key}_{lowerLang} already exists, skipping.");
  498. continue;
  499. }
  500. //Output file format: featureName_languageCode.cab: speech_de-de.cab
  501. webClient.DownloadFile(url.Value, $@"{downloadPath}\{url.Key}_{lowerLang}.cab");
  502. }
  503. }
  504. break;
  505. }
  506. //400 Status code
  507. case HttpStatusCode.BadRequest:
  508. {
  509. var result = responseString.Content.ReadAsStringAsync().Result;
  510. dynamic data = JObject.Parse(result);
  511. Console.WriteLine($"Bad request.\r\nError:{data["response"]["error"]}");
  512. break;
  513. }
  514. //429 Status code
  515. case (HttpStatusCode)429:
  516. {
  517. var result = responseString.Content.ReadAsStringAsync().Result;
  518. dynamic data = JObject.Parse(result);
  519. Console.WriteLine($"Too many requests, try again later.\r\nError:{data["response"]["error"]}");
  520. break;
  521. }
  522. //500 Status code
  523. case HttpStatusCode.InternalServerError:
  524. {
  525. var result = responseString.Content.ReadAsStringAsync().Result;
  526. dynamic data = JObject.Parse(result);
  527. Console.WriteLine($"Internal Server Error.\r\nError:{data["response"]["error"]}");
  528. break;
  529. }
  530. default:
  531. throw new ArgumentOutOfRangeException();
  532. }
  533. }
  534. }
  535. private static bool IsApplicableWindowsVersion(string version)
  536. {
  537. bool negative = false;
  538. if (version.StartsWith("!"))
  539. {
  540. version = version.TrimStart('!');
  541. negative = true;
  542. }
  543. bool compareUpdateBuild = version.Contains(".");
  544. var currentBuild = decimal.Parse(compareUpdateBuild ? Globals.WinVer + "." + Globals.WinUpdateVer : Globals.WinVer.ToString());
  545. bool result = false;
  546. if (version.StartsWith(">="))
  547. {
  548. var parsed = decimal.Parse(version.Substring(2));
  549. if (currentBuild >= parsed)
  550. result = true;
  551. } else if (version.StartsWith("<="))
  552. {
  553. var parsed = decimal.Parse(version.Substring(2));
  554. if (currentBuild <= parsed)
  555. result = true;
  556. } else if (version.StartsWith(">"))
  557. {
  558. var parsed = decimal.Parse(version.Substring(1));
  559. if (currentBuild > parsed)
  560. result = true;
  561. } else if (version.StartsWith("<"))
  562. {
  563. var parsed = decimal.Parse(version.Substring(1));
  564. if (currentBuild < parsed)
  565. result = true;
  566. }
  567. else
  568. {
  569. var parsed = decimal.Parse(version);
  570. if (currentBuild == parsed)
  571. result = true;
  572. }
  573. return negative ? !result : result;
  574. }
  575. private static bool IsApplicableOption(string option, List<string> options)
  576. {
  577. if (String.IsNullOrEmpty(option))
  578. return true;
  579. if (option.Contains("&"))
  580. {
  581. if (option.Contains("!"))
  582. throw new ArgumentException("YAML options item must not contain both & and !", "options");
  583. return option.Split('&').All(splitOption => IsApplicableOption(splitOption, options));
  584. }
  585. bool negative = false;
  586. if (option.StartsWith("!"))
  587. {
  588. option = option.TrimStart('!');
  589. negative = true;
  590. }
  591. if (options == null)
  592. return negative ? true : false;
  593. var result = options.Contains(option, StringComparer.OrdinalIgnoreCase);
  594. return negative ? !result : result;
  595. }
  596. private static bool IsApplicableArch(string arch)
  597. {
  598. if (String.IsNullOrEmpty(arch))
  599. return true;
  600. bool negative = false;
  601. if (arch.StartsWith("!"))
  602. {
  603. arch = arch.TrimStart('!');
  604. negative = true;
  605. }
  606. var result = String.Equals(arch, RuntimeInformation.ProcessArchitecture.ToString(), StringComparison.OrdinalIgnoreCase);
  607. return negative ? !result : result;
  608. }
  609. public static async Task<bool> SafeRunAction(ITaskAction action)
  610. {
  611. try
  612. {
  613. return await action.RunTask();
  614. }
  615. catch (Exception e)
  616. {
  617. action.ResetProgress();
  618. if (e.InnerException != null)
  619. {
  620. ErrorLogger.WriteToErrorLog(e.InnerException.Message, e.InnerException.StackTrace, e.Message);
  621. }
  622. else
  623. {
  624. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, action.ErrorString());
  625. }
  626. }
  627. return false;
  628. }
  629. }
  630. }