-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathProgram.cs
More file actions
executable file
·138 lines (108 loc) · 4.42 KB
/
Program.cs
File metadata and controls
executable file
·138 lines (108 loc) · 4.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Xml;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace ModlinksShaVerifier
{
internal static class Program
{
private static readonly HttpClient _Client = new(
new HttpClientHandler
{
MaxConnectionsPerServer = 16,
}
);
private static async Task<bool> CheckLink(Manifest m, Link link)
{
using var sha = SHA256.Create();
try
{
await using Stream stream = await _Client.GetStreamAsync(link.URL);
var sw = new Stopwatch();
sw.Start();
string shasum = Convert.ToHexString(await sha.ComputeHashAsync(stream));
Console.WriteLine($"Took {sw.Elapsed.ToString()} to calc sum for {m.Name}");
if (shasum.Equals(link.SHA256, StringComparison.InvariantCultureIgnoreCase))
return true;
WriteError(
"Check",
$"Hash mismatch of {m.Name} in link {link.URL}. Expected value from modlinks: {link.SHA256}, Actual value: {shasum}"
);
}
catch (HttpRequestException e)
{
WriteError("Check", $"Request failed for {m.Name} - {link.URL}! {e.StatusCode}");
}
return false;
}
private static async Task<bool> CheckSingleSha(Manifest m)
{
Console.WriteLine($"Checking '{m.Name}'");
var res = await Task.WhenAll(m.Links.AsEnumerable().Select(x => CheckLink(m, x)));
return res.All(x => x);
}
private static async Task ReadManifests(string path, XmlSerializer s, Action<Manifest> f)
{
using var reader = XmlReader.Create(path, new XmlReaderSettings { Async = true });
while (await reader.ReadAsync())
{
if (reader.NodeType is not XmlNodeType.Element)
continue;
if (reader.Name is not nameof(Manifest))
continue;
f(s.Deserialize(reader) as Manifest ?? throw new XmlException("Unable to deserialize manifest!"));
}
}
internal static async Task<int> Main(string[] args)
{
var sw = new Stopwatch();
sw.Start();
if (args is not [var prevPath, var currPath])
{
await Console.Error.WriteLineAsync("Usage: ModlinksShaVerifier [CURRENT_FILE] [INCOMING_FILE]");
return 2;
}
if (!File.Exists(prevPath))
{
await Console.Error.WriteLineAsync($"Unable to access previous XML file {prevPath}! Does it exist?");
return 2;
}
if (!File.Exists(currPath))
{
await Console.Error.WriteLineAsync($"Unable to access new XML file {currPath}! Does it exist?");
return 2;
}
var serializer = new XmlSerializer(typeof(Manifest));
Dictionary<string, Links> prev = new();
await ReadManifests(
prevPath,
serializer,
m => prev.Add(m.Name, m.Links)
);
List<Task<bool>> checks = [];
await ReadManifests(
currPath,
serializer,
m =>
{
// Check the manifest if it either wasn't present before *or* its contents changed.
if (!prev.TryGetValue(m.Name, out Links? links) || links != m.Links)
checks.Add(CheckSingleSha(m));
}
);
var res = await Task.WhenAll(checks);
sw.Stop();
Console.WriteLine($"Checked {checks.Count} manifests in {sw.ElapsedMilliseconds}ms.");
// If they're not all correct, error.
return !res.All(x => x) ? 1 : 0;
}
private static void WriteError(string title, string message) =>
Console.WriteLine($"::error title={title}::{message}");
}
}