forked from sbozzie/test-intel
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathIntelExtensions.cs
More file actions
415 lines (392 loc) · 18.3 KB
/
Copy pathIntelExtensions.cs
File metadata and controls
415 lines (392 loc) · 18.3 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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
namespace PleaseIgnore.IntelMap {
/// <summary>
/// Series of helper methods to assist with the construction of
/// classes within PleaseIgnore.IntelMap.
/// </summary>
/// <threadsafety static="true" instance="false" />
internal static class IntelExtensions {
/// <summary>The Unix time epoch</summary>
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
/// <summary>The 'unreserved' characters from RFC 3986</summary>
private const string Unreserved = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~";
/// <summary>Convert a number 0...15 to a upper-case hex digit</summary>
private const string HexUpperString = "0123456789ABCDEF";
/// <summary>Convert a number 0...15 to a lower-case hex digit</summary>
private const string HexLowerString = "0123456789abcdef";
/// <summary><see cref="Trace.Write(Object,String)" /> category</summary>
public const string WebTraceCategory = "PleaseIgnore.IntelMap";
/// <summary>Downtime in ticks from beginning of day</summary>
private const Int64 DowntimeTicks = 11L * TimeSpan.TicksPerHour;
/// <summary>Priority list of <see cref="IntelStatus" /></summary>
private static readonly IntelStatus[] StatusPriority = new IntelStatus[] {
IntelStatus.FatalError,
IntelStatus.AuthenticationError,
IntelStatus.InvalidPath,
IntelStatus.NetworkError,
IntelStatus.Starting,
IntelStatus.Active,
IntelStatus.Waiting
};
/// <summary>The base URL for requests on the intel map server</summary>
public const string BaseUrl = "http://test.intelmap.net/";
/// <summary>The URL for quering the channel list</summary>
public const string ChannelsUrl = BaseUrl + "intelchannels.pl";
/// <summary>The URL for reporting intel</summary>
public const string ReportUrl = BaseUrl + "report.pl";
/// <summary>
/// Gets the time and date of the most recent scheduled Tranquility
/// downtime.
/// </summary>
/// <value>
/// Instance of <see cref="DateTime"/> providing an estimate of when
/// the most recent Tranquility daily downtime begain.
/// </value>
public static DateTime LastDowntime {
get {
var nowTicks = DateTime.UtcNow.Ticks;
var eveTicks = nowTicks - DowntimeTicks;
return new DateTime(
eveTicks - eveTicks % TimeSpan.TicksPerDay + DowntimeTicks,
DateTimeKind.Utc);
}
}
/// <summary>
/// Gets the time and date of the next scheduled Tranquility
/// downtime.
/// </summary>
/// <value>
/// Instance of <see cref="DateTime"/> providing an estimate of when
/// the next Tranquility daily downtime should begain.
/// </value>
public static DateTime NextDowntime {
get {
return LastDowntime + new TimeSpan(TimeSpan.TicksPerDay);
}
}
/// <summary>Runs an action against each member of a collection.</summary>
/// <typeparam name="T">The type of the elements of
/// <paramref name="source" />.</typeparam>
/// <param name="source">An <see cref="IEnumerable{T}" /> to process.</param>
/// <param name="action">A function to execute on each member of
/// <paramref name="source" />.</param>
/// <returns>The parameter <paramref name="source" />.</returns>
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> action) {
Contract.Requires<ArgumentNullException>(source != null, "source");
Contract.Requires<ArgumentNullException>(action != null, "action");
foreach (var item in source) {
action(item);
}
return source;
}
/// <summary>
/// Converts the value of the current <see cref="DateTime" />
/// object to a standard Unix timestamp.
/// </summary>
/// <param name="timestamp">The <see cref="DateTime" /> object to
/// convert.</param>
/// <returns>
/// The representation of <paramref name="timestamp" /> as a Unix
/// timestamp, specifically the number of seconds elapsed (ignoring
/// leap seconds) since midnight, 1 Jan 1970 GMT.
/// </returns>
[Pure]
public static double ToUnixTime(this DateTime timestamp) {
Contract.Ensures(!double.IsInfinity(Contract.Result<double>()));
Contract.Ensures(!double.IsNaN(Contract.Result<double>()));
return Math.Floor((timestamp.ToUniversalTime() - Epoch).TotalSeconds);
}
/// <summary>
/// Picks the highest priority status out of an array of
/// <see cref="IntelStatus" />.
/// </summary>
/// <param name="array">An array of <see cref="IntelStatus" />
/// values.</param>
/// <returns>The highest priority status from
/// <paramref name="array" />.</returns>
[Pure]
public static IntelStatus Combine(params IntelStatus[] array) {
Contract.Requires(array != null);
Contract.Requires(array.Length > 0);
foreach (var status in StatusPriority) {
if (array.Any(x => x == status)) {
return status;
}
}
return array[0];
}
/// <summary>
/// Tests if a value of the <see cref="IntelStatus" />
/// enumeration refers to a "running" state.
/// </summary>
/// <param name="status">Value of <see cref="IntelStatus" /> to
/// test.</param>
/// <returns>
/// <see langword="true" /> if the value of <paramref name="status" />
/// refers to a normal operating state; otherwise,
/// <see langword="false" /> if it's in a stopped state.
/// </returns>
/// <remarks>
/// A "running" state is any state that is either reporting intel
/// or can automatically transition to a reporting state without direct
/// programmatic manipulation.
/// </remarks>
[Pure]
public static bool IsRunning(this IntelStatus status) {
Contract.Ensures(Contract.Result<bool>()
== ((status == IntelStatus.Active)
|| (status == IntelStatus.InvalidPath)
|| (status == IntelStatus.Starting)
|| (status == IntelStatus.Waiting)
|| (status == IntelStatus.NetworkError)
|| (status == IntelStatus.AuthenticationError)));
switch (status) {
case IntelStatus.Active:
case IntelStatus.InvalidPath:
case IntelStatus.Starting:
case IntelStatus.Waiting:
case IntelStatus.NetworkError:
case IntelStatus.AuthenticationError:
return true;
default:
return false;
}
}
/// <summary>
/// Tests if a value of the <see cref="IntelStatus" />
/// enumeration refers to an "error" state.
/// </summary>
/// <param name="status">Value of <see cref="IntelStatus" />
/// to test.</param>
/// <returns>
/// <see langword="true" /> if the value of <paramref name="status" />
/// refers to an error state; otherwise, <see langword="false" />.
/// </returns>
/// <remarks>
/// Error states include automatically recoverable errors (e.g.
/// <see cref="IntelStatus.NetworkError"/>) in additional to
/// unrecoverable states (e.g. <see cref="IntelStatus.FatalError"/>).
/// </remarks>
[Pure]
public static bool IsError(this IntelStatus status) {
Contract.Ensures(Contract.Result<bool>()
== ((status == IntelStatus.InvalidPath)
|| (status == IntelStatus.NetworkError)
|| (status == IntelStatus.AuthenticationError)
|| (status == IntelStatus.InvalidPath)));
switch (status) {
case IntelStatus.NetworkError:
case IntelStatus.AuthenticationError:
case IntelStatus.FatalError:
case IntelStatus.InvalidPath:
return true;
default:
return false;
}
}
/// <summary>Submits a standard POST to an HTTP(S)
/// server.</summary>
/// <param name="webRequest">The instance of <see cref="WebRequest" />
/// to use when submitting the HTTP POST.</param>
/// <param name="payload">The "application/x-www-form-urlencoded"
/// encoded payload to send as the POST content.</param>
/// <returns>
/// The instance of <see cref="WebResponse" /> providing the server's
/// response to the POST.
/// </returns>
public static WebResponse Post(this WebRequest webRequest, byte[] payload) {
Contract.Requires<ArgumentNullException>(webRequest != null, "webRequest");
Contract.Requires<ArgumentNullException>(payload != null, "payload");
Contract.Ensures(Contract.Result<WebResponse>() != null);
return Post(webRequest, payload, 0, payload.Length);
}
/// <summary>Submits a standard POST to an HTTP(S) server.</summary>
/// <param name="webRequest">The instance of <see cref="WebRequest" />
/// to use when submitting the HTTP POST.</param>
/// <param name="payload">The "application/x-www-form-urlencoded"
/// encoded payload to send as the POST content.</param>
/// <param name="offset">The zero-based byte offset in
/// <paramref name="payload" /> at which to begin copying bytes to the
/// server.</param>
/// <param name="count">The number of bytes to be sent to the
/// server.</param>
/// <returns>
/// The instance of <see cref="WebResponse" /> providing the server's
/// response to the POST.
/// </returns>
public static WebResponse Post(this WebRequest webRequest, byte[] payload, int offset, int count) {
Contract.Requires<ArgumentNullException>(webRequest != null, "webRequest");
Contract.Requires<ArgumentNullException>(payload != null, "payload");
Contract.Requires<ArgumentOutOfRangeException>(offset >= 0, "offset");
Contract.Requires<ArgumentOutOfRangeException>(count >= 0, "count");
Contract.Requires<ArgumentException>(offset + count <= payload.Length);
Contract.Ensures(Contract.Result<WebResponse>() != null);
try {
Trace.WriteLine("<< " + Encoding.UTF8.GetString(payload, offset, count),
WebTraceCategory);
webRequest.Method = "POST";
webRequest.ContentLength = count;
webRequest.ContentType = "application/x-www-form-urlencoded";
using (var stream = webRequest.GetRequestStream()) {
stream.Write(payload, offset, count);
}
var response = webRequest.GetResponse();
Contract.Assert(response != null);
return response;
} catch (Exception e) {
Trace.WriteLine("!! " + e.Message, WebTraceCategory);
throw;
}
}
/// <summary>
/// Submits a standard POST to an HTTP(S) server after encoding
/// a set of name-value pairs.
/// </summary>
/// <param name="webRequest">The instance of <see cref="WebRequest" />
/// to use when submitting the HTTP POST.</param>
/// <param name="variables">A list of name-value pairs to send to the
/// server.</param>
/// <returns>
/// The instance of <see cref="WebResponse" /> providing the server's
/// response to the POST.
/// </returns>
/// <remarks>
/// The name-value pairs provided by <paramref name="variables" />
/// will be encoded as per the method described in the HTML
/// specification (part 17.13.4) after being converted to
/// strings by calling <see cref="Object.ToString()" />.
/// </remarks>
public static WebResponse Post(this WebRequest webRequest,
IEnumerable<KeyValuePair<string, string>> variables) {
Contract.Requires<ArgumentNullException>(webRequest != null, "webRequest");
Contract.Requires<ArgumentNullException>(variables != null, "variables");
Contract.Requires<ArgumentException>(Contract.ForAll(variables,
x => !String.IsNullOrEmpty(x.Key)));
Contract.Ensures(Contract.Result<WebResponse>() != null);
// Compute an upper bounds on the payload length
var maxLength = variables.Sum(x => 2 + 9 * x.Key.Length
+ 9 * (x.Value ?? String.Empty).Length);
Contract.Assert(maxLength >= 0);
// Build up the POST payload
using (var stream = new MemoryStream(maxLength)) {
bool first = true;
foreach (var keypair in variables) {
// Write a '&' between each variable
if (!first) {
stream.WriteByte((byte)'&');
} else {
first = false;
}
// Write the variable name
WriteUriEncoded(stream, keypair.Key);
// Write the '=' between the name and value
stream.WriteByte((byte)'=');
// Write the variable value
WriteUriEncoded(stream, keypair.Value);
}
return Post(webRequest, stream.ToArray());
}
}
/// <summary>
/// Reads the entirety of the response body of a
/// <see cref="WebResponse" /> and then disposes the instances.
/// </summary>
/// <param name="webResponse">The instance of <see cref="WebResponse" />
/// to read out.</param>
/// <returns>The response payload parsed as a string.</returns>
public static string ReadContent(this WebResponse webResponse) {
Contract.Requires<ArgumentNullException>(webResponse != null, "webResponse");
Contract.Ensures(Contract.Result<string>() != null);
try {
var stream = webResponse.GetResponseStream();
using (var reader = new StreamReader(stream)) {
var responseData = reader.ReadToEnd();
Trace.WriteLine(">> " + responseData, WebTraceCategory);
return responseData;
}
} catch (Exception e) {
Trace.WriteLine("!! " + e.Message, WebTraceCategory);
throw;
} finally {
webResponse.Close();
}
}
/// <summary>
/// Writes the HTML form url encoded form a string to a
/// <see cref="Stream" />.
/// </summary>
/// <param name="stream">The instance of <see cref="Stream" /> to write
/// the data string to.</param>
/// <param name="dataString">The <see cref="String" /> to be
/// encoded.</param>
public static void WriteUriEncoded(this Stream stream, string dataString) {
Contract.Requires<ArgumentNullException>(stream != null, "stream");
if (!String.IsNullOrEmpty(dataString)) {
var bytes = Encoding.UTF8.GetBytes(dataString);
foreach (var current in bytes) {
if (current == ' ') {
// Escape space as '+'
stream.WriteByte((byte)'+');
} else if (Unreserved.Contains((char)current)) {
// Characters that should not be escaped
stream.WriteByte(current);
} else {
// Everything else
stream.WriteByte((byte)'%');
stream.WriteByte((byte)HexUpperString[current / 16]);
stream.WriteByte((byte)HexUpperString[current % 16]);
}
}
}
}
/// <summary>
/// Converts a byte array into a hex string using lower-case
/// characters for A-F.
/// </summary>
/// <param name="array">Byte array to convert to a hex string.</param>
/// <returns>The hex string representation of
/// <paramref name="array" />.</returns>
[Pure]
public static string ToLowerHexString(this byte[] array) {
Contract.Requires<ArgumentNullException>(array != null, "array");
Contract.Ensures(Contract.Result<string>() != null);
Contract.Ensures(Contract.Result<string>().Length == array.Length * 2);
if (array.Length == 0) {
return String.Empty;
} else {
StringBuilder builder = new StringBuilder(array.Length * 2);
foreach (var current in array) {
builder.Append(HexLowerString[(current / 16) % 16]);
builder.Append(HexLowerString[(current % 16)]);
}
return builder.ToString();
}
}
/// <summary>Parse an integer found in a Regular Expression
/// match.</summary>
/// <param name="capture">Regular expression <see cref="Capture" />
/// to parse.</param>
/// <returns>
/// The integer represented by the string matched by
/// <paramref name="capture" />.
/// </returns>
/// <remarks>
/// <see cref="ToInt32" /> decodes <paramref name="capture" />
/// according to the invariant culture.
/// </remarks>
[Pure]
public static int ToInt32(this Capture capture) {
Contract.Requires<ArgumentNullException>(capture != null, "capture");
return int.Parse(capture.Value, CultureInfo.InvariantCulture);
}
}
}