5using System.Threading.Tasks;
29 private readonly
string componentAddress;
30 private readonly
string packageFolder;
31 private readonly Random rnd =
new Random();
75 #region Neuro-Foundation V1
93 #region Neuro-Foundation V1
133 StringBuilder Xml =
new StringBuilder();
135 Xml.Append(
"<getPackageInfo xmlns='");
137 Xml.Append(
"' fileName='");
141 return this.
client.
SendIqGet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
143 XmlElement E = e.FirstElement;
146 if (e.Ok && !(E is
null) && E.LocalName ==
"packageInfo")
152 await Callback.Raise(
this, e2);
165 TaskCompletionSource<Package> Result =
new TaskCompletionSource<Package>();
170 Result.TrySetResult(e.Package);
172 Result.TrySetException(e.StanzaError ??
new Exception(
"Unable to get package information."));
174 return Task.CompletedTask;
178 return await Result.Task;
188 StringBuilder Xml =
new StringBuilder();
190 Xml.Append(
"<getPackages xmlns='");
194 return this.
client.
SendIqGet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
196 XmlElement E = e.FirstElement;
199 if (e.Ok && !(E is
null) && E.LocalName ==
"packages")
201 List<Package> Packages = new List<Package>();
203 foreach (XmlNode N in E.ChildNodes)
205 if (N is XmlElement E2 && E2.LocalName ==
"packageInfo")
206 Packages.Add(Package.Parse(E2));
209 PackagesInfo = Packages.ToArray();
215 await Callback.Raise(
this, e2);
227 TaskCompletionSource<Package[]> Result =
new TaskCompletionSource<Package[]>();
229 await this.GetPackagesInformation((Sender, e) =>
232 Result.TrySetResult(e.Packages);
234 Result.TrySetException(e.StanzaError ??
new Exception(
"Unable to get packages."));
236 return Task.CompletedTask;
240 return await Result.Task;
250 public Task
Subscribe(
string FileName, EventHandlerAsync<IqResultEventArgs> Callback,
object State)
252 StringBuilder Xml =
new StringBuilder();
254 Xml.Append(
"<subscribe xmlns='");
255 Xml.Append(NamespaceSoftwareUpdatesCurrent);
256 Xml.Append(
"' fileName='");
260 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), Callback, State);
271 TaskCompletionSource<bool> Result =
new TaskCompletionSource<bool>();
273 await this.Subscribe(FileName, (Sender, e) =>
276 Result.TrySetResult(
true);
278 Result.TrySetException(e.StanzaError ??
new Exception(
"Unable to subscribe to software updates for " + FileName +
"."));
280 return Task.CompletedTask;
294 public Task
Unsubscribe(
string FileName, EventHandlerAsync<IqResultEventArgs> Callback,
object State)
296 StringBuilder Xml =
new StringBuilder();
298 Xml.Append(
"<unsubscribe xmlns='");
299 Xml.Append(NamespaceSoftwareUpdatesCurrent);
300 Xml.Append(
"' fileName='");
304 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), Callback, State);
315 TaskCompletionSource<bool> Result =
new TaskCompletionSource<bool>();
317 await this.Unsubscribe(FileName, (Sender, e) =>
320 Result.TrySetResult(
true);
323 Result.TrySetException(e.StanzaError ?? new Exception(
"Unable to unsubscribe from software updates for " + FileName +
"."));
326 return Task.CompletedTask;
338 public Task
GetSubscriptions(EventHandlerAsync<SubscriptionsEventArgs> Callback,
object State)
340 StringBuilder Xml =
new StringBuilder();
342 Xml.Append(
"<getSubscriptions xmlns='");
343 Xml.Append(NamespaceSoftwareUpdatesCurrent);
346 return this.client.SendIqGet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
348 XmlElement E = e.FirstElement;
349 string[] FileNames =
null;
351 if (e.Ok && !(E is
null) && E.LocalName ==
"subscriptions")
353 List<string> Subscriptions = new List<string>();
355 foreach (XmlNode N in E.ChildNodes)
357 if (N is XmlElement E2 && E2.LocalName ==
"subscription")
358 Subscriptions.Add(E2.InnerText);
361 FileNames = Subscriptions.ToArray();
367 await Callback.Raise(
this, e2);
378 TaskCompletionSource<string[]> Result =
new TaskCompletionSource<string[]>();
380 await this.GetSubscriptions((Sender, e) =>
383 Result.TrySetResult(e.FileNames);
385 Result.TrySetException(e.StanzaError ??
new Exception(
"Unable to get list of current subscriptions."));
387 return Task.CompletedTask;
391 return await Result.Task;
394 private async Task PackageNotificationHandler(
object Sender,
MessageEventArgs e)
396 if (
string.Compare(e.
From,
this.componentAddress,
true) != 0)
398 this.client.Warning(
"Discarding package notification. Expected source: " + this.componentAddress +
". Actual source: " + e.
From);
402 Package PackageInfo = Package.Parse(e.
Content);
403 PackageUpdatedEventArgs e2 =
new PackageUpdatedEventArgs(PackageInfo, e);
405 await this.OnSoftwareUpdated.Raise(
this, e2,
false);
409 Task
_ = Task.Run(() => this.Download(PackageInfo, e));
417 string FileName = await this.DownloadPackageAsync(PackageInfo);
418 PackageFileEventArgs e3 =
new PackageFileEventArgs(PackageInfo, FileName, e);
420 if (!await this.OnSoftwareValidation.Raise(
this, e3,
false))
422 File.Delete(FileName);
423 Log.
Warning(
"Package with invalid signature downloaded and deleted.", FileName);
427 await this.OnSoftwareDownloaded.Raise(
this, e3);
443 return this.DownloadPackageAsync(PackageInfo,
null);
455 return this.DownloadPackageAsync(PackageInfo, Destination, GetDefaultRemoteCertificateValidator(PackageInfo));
458 private static EventHandler<RemoteCertificateEventArgs> GetDefaultRemoteCertificateValidator(
Package PackageInfo)
460 if (PackageInfo.
Url.StartsWith(
"https://localhost:") ||
461 PackageInfo.
Url.StartsWith(
"https://localhost/"))
463 return TrustLocalhost;
483 EventHandler<RemoteCertificateEventArgs> RemoteCertificateValidator)
485 string FileName = Path.Combine(this.packageFolder, PackageInfo.
FileName);
486 int MaxRetryDelayMinutes = 5;
496 new Uri(PackageInfo.
Url),
null, RemoteCertificateValidator, 120000, Destination);
501 StatusCode = (int)ex.StatusCode;
503 StatusCode = (
int)
System.
Net.HttpStatusCode.InternalServerError;
505 ErrorMessage = Content.
Error.Message;
509 using Stream Output = File.Create(FileName);
511 Content.Encoded.Position = 0;
520 ErrorMessage = ex.Message;
524 StatusCode = (int)
System.
Net.HttpStatusCode.InternalServerError;
525 ErrorMessage = ex.Message;
528 if (StatusCode < 500)
531 null, PackageInfo.
FileName,
null,
null,
null,
null,
null,
532 new KeyValuePair<string, object>(
"Status Code", StatusCode),
533 new KeyValuePair<string, object>(
"Message", ErrorMessage));
540 MsDelay = this.rnd.Next(1000, MaxRetryDelayMinutes * 60000);
542 MaxRetryDelayMinutes <<= 1;
543 if (MaxRetryDelayMinutes > 1440)
544 MaxRetryDelayMinutes = 1440;
547 await Task.Delay(MsDelay);
555 public event EventHandlerAsync<PackageUpdatedEventArgs> OnSoftwareUpdated =
null;
567 public event EventHandlerAsync<PackageFileEventArgs> OnSoftwareValidation =
null;
572 public event EventHandlerAsync<PackageFileEventArgs> OnSoftwareDownloaded =
null;
574 private async Task PackageDeletedNotificationHandler(
object Sender,
MessageEventArgs e)
576 if (
string.Compare(e.
From,
this.componentAddress,
true) != 0)
581 await this.OnSoftwareDeleted.Raise(
this, e2,
false);
587 string FileName = Path.Combine(this.packageFolder, PackageInfo.
FileName);
589 if (File.Exists(FileName))
591 File.Delete(FileName);
593 await this.OnDownloadedSoftwareDeleted.Raise(
this,
new PackageFileEventArgs(PackageInfo, FileName, e));
607 public event EventHandlerAsync<PackageDeletedEventArgs> OnSoftwareDeleted =
null;
612 public event EventHandlerAsync<PackageFileEventArgs> OnDownloadedSoftwareDeleted =
null;
Contains information about a stream response to a content request.
TemporaryStream Encoded
Encoded object.
bool HasError
If an error occurred.
Exception Error
Error response.
Exception class for web exceptions.
HttpStatusCode StatusCode
HTTP Status Code of content.
Static class managing encoding and decoding of internet content.
static Task< ContentStreamResponse > GetTempStreamAsync(Uri Uri, params KeyValuePair< string, string >[] Headers)
Gets a (possibly big) resource, given its URI.
Remove certificate validation event arguments.
Helps with common XML-related tasks.
static string Encode(string s)
Encodes a string for use in XML.
Generic exception, with meta-data for logging.
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
Event arguments for message events.
string From
From where the message was received.
XmlElement Content
Content of the message. For messages that are processed by registered message handlers,...
Event arguments for software package deletion events.
bool Delete
If the downloaded package is to be deleted. Default=true.
Event arguments for software package events.
Event arguments for software package file events.
Information about a software package.
static Package Parse(XmlElement Xml)
Parses a package information element.
string FileName
Name of software package file.
string Url
URL to download software package.
Event arguments for Software packages events.
Implements an XMPP interface for remote software updates.
string PackageFolder
Folder of downloaded packages.
const string NamespaceSoftwareUpdatesCurrent
Current namespace for software updates.
const string Wildcard
Subscribing to a wildcard allows you to receive events when any software package is updated.
Task< string > DownloadPackageAsync(Package PackageInfo, TemporaryStream Destination)
Downloads a software package.
Task Subscribe(string FileName, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Subscribes to updates to a given software package.
static readonly string[] NamespacesSoftwareUpdates
Namespaces supported for software updates.
async Task< string[]> GetSubscriptionsAsync()
Gets current software update subscriptions.
async Task< Package[]> GetPackagesAsync()
Gets information about available software packages.
override void Dispose()
Disposes of the extension.
Task GetSubscriptions(EventHandlerAsync< SubscriptionsEventArgs > Callback, object State)
Gets current software update subscriptions.
async Task< Package > GetPackageInformationAsync(string FileName)
Gets information about a software package.
Task GetPackagesInformation(EventHandlerAsync< PackagesEventArgs > Callback, object State)
Gets information about available software packages.
SoftwareUpdateClient(XmppClient Client, string ComponentAddress, string PackageFolder)
Implements an XMPP interface for remote software updates.
const string NamespaceSoftwareUpdatesNeuroFoundationV1
urn:nf:iot:swu:1.0
string ComponentAddress
Component XMPP address.
Task GetPackageInformation(string FileName, EventHandlerAsync< PackageEventArgs > Callback, object State)
Gets information about a software package.
Task Unsubscribe(string FileName, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Unsubscribes to updates to a given software package.
Task< string > DownloadPackageAsync(Package PackageInfo)
Downloads a software package.
async Task SubscribeAsync(string FileName)
Subscribes to updates to a given software package.
async Task< string > DownloadPackageAsync(Package PackageInfo, TemporaryStream Destination, EventHandler< RemoteCertificateEventArgs > RemoteCertificateValidator)
Downloads a software package.
override string[] Extensions
Implemented extensions.
async Task UnsubscribeAsync(string FileName)
Unsubscribes to updates to a given software package.
const string NamespaceSoftwareUpdatesIeeeV1
urn:ieee:iot:swu:1.0
Event arguments for subscription list responses responsess.
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsClientFeature)
Unregisters a Message handler.
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsClientFeature)
Registers a Message handler.
Task< uint > SendIqGet(string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Get request.
Base class for XMPP Extensions.
XmppClient client
XMPP Client used by the extension.
void Exception(Exception Exception)
Called to inform the viewer of an exception state.
XmppClient Client
XMPP Client.
Manages a temporary stream. Contents is kept in-memory, if below a memory threshold,...
override async Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
Asynchronously reads the bytes from the current stream and writes them to another stream,...