2using System.Collections.Generic;
7using System.Threading.Tasks;
28 private readonly
string componentAddress;
29 private readonly
string packageFolder;
30 private readonly Random rnd =
new Random();
74 #region Neuro-Foundation V1
94 #region Neuro-Foundation V1
132 StringBuilder Xml =
new StringBuilder();
134 Xml.Append(
"<getPackageInfo xmlns='");
136 Xml.Append(
"' fileName='");
140 return this.
client.
SendIqGet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
142 XmlElement E = e.FirstElement;
145 if (e.Ok && !(E is
null) && E.LocalName ==
"packageInfo")
151 await Callback.Raise(
this, e2);
164 TaskCompletionSource<Package> Result =
new TaskCompletionSource<Package>();
169 Result.TrySetResult(e.Package);
171 Result.TrySetException(e.StanzaError ??
new Exception(
"Unable to get package information."));
173 return Task.CompletedTask;
177 return await Result.Task;
187 StringBuilder Xml =
new StringBuilder();
189 Xml.Append(
"<getPackages xmlns='");
193 return this.
client.
SendIqGet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
195 XmlElement E = e.FirstElement;
198 if (e.Ok && !(E is
null) && E.LocalName ==
"packages")
200 List<Package> Packages = new List<Package>();
202 foreach (XmlNode N in E.ChildNodes)
204 if (N is XmlElement E2 && E2.LocalName ==
"packageInfo")
205 Packages.Add(Package.Parse(E2));
208 PackagesInfo = Packages.ToArray();
214 await Callback.Raise(
this, e2);
226 TaskCompletionSource<Package[]> Result =
new TaskCompletionSource<Package[]>();
228 await this.GetPackagesInformation((Sender, e) =>
231 Result.TrySetResult(e.Packages);
233 Result.TrySetException(e.StanzaError ??
new Exception(
"Unable to get packages."));
235 return Task.CompletedTask;
239 return await Result.Task;
249 public Task
Subscribe(
string FileName, EventHandlerAsync<IqResultEventArgs> Callback,
object State)
251 StringBuilder Xml =
new StringBuilder();
253 Xml.Append(
"<subscribe xmlns='");
254 Xml.Append(NamespaceSoftwareUpdatesCurrent);
255 Xml.Append(
"' fileName='");
259 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), Callback, State);
270 TaskCompletionSource<bool> Result =
new TaskCompletionSource<bool>();
272 await this.Subscribe(FileName, (Sender, e) =>
275 Result.TrySetResult(
true);
277 Result.TrySetException(e.StanzaError ??
new Exception(
"Unable to subscribe to software updates for " + FileName +
"."));
279 return Task.CompletedTask;
293 public Task
Unsubscribe(
string FileName, EventHandlerAsync<IqResultEventArgs> Callback,
object State)
295 StringBuilder Xml =
new StringBuilder();
297 Xml.Append(
"<unsubscribe xmlns='");
298 Xml.Append(NamespaceSoftwareUpdatesCurrent);
299 Xml.Append(
"' fileName='");
303 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), Callback, State);
314 TaskCompletionSource<bool> Result =
new TaskCompletionSource<bool>();
316 await this.Unsubscribe(FileName, (Sender, e) =>
319 Result.TrySetResult(
true);
322 Result.TrySetException(e.StanzaError ?? new Exception(
"Unable to unsubscribe from software updates for " + FileName +
"."));
325 return Task.CompletedTask;
337 public Task
GetSubscriptions(EventHandlerAsync<SubscriptionsEventArgs> Callback,
object State)
339 StringBuilder Xml =
new StringBuilder();
341 Xml.Append(
"<getSubscriptions xmlns='");
342 Xml.Append(NamespaceSoftwareUpdatesCurrent);
345 return this.client.SendIqGet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
347 XmlElement E = e.FirstElement;
348 string[] FileNames =
null;
350 if (e.Ok && !(E is
null) && E.LocalName ==
"subscriptions")
352 List<string> Subscriptions = new List<string>();
354 foreach (XmlNode N in E.ChildNodes)
356 if (N is XmlElement E2 && E2.LocalName ==
"subscription")
357 Subscriptions.Add(E2.InnerText);
360 FileNames = Subscriptions.ToArray();
366 await Callback.Raise(
this, e2);
377 TaskCompletionSource<string[]> Result =
new TaskCompletionSource<string[]>();
379 await this.GetSubscriptions((Sender, e) =>
382 Result.TrySetResult(e.FileNames);
384 Result.TrySetException(e.StanzaError ??
new Exception(
"Unable to get list of current subscriptions."));
386 return Task.CompletedTask;
390 return await Result.Task;
393 private async Task PackageNotificationHandler(
object Sender,
MessageEventArgs e)
395 if (
string.Compare(e.
From,
this.componentAddress,
true) != 0)
397 await this.client.Warning(
"Discarding package notification. Expected source: " + this.componentAddress +
". Actual source: " + e.
From);
401 Package PackageInfo = Package.Parse(e.
Content);
402 PackageUpdatedEventArgs e2 =
new PackageUpdatedEventArgs(PackageInfo, e);
404 await this.OnSoftwareUpdated.Raise(
this, e2,
false);
408 Task _ = Task.Run(() => this.Download(PackageInfo, e));
416 string FileName = await this.DownloadPackageAsync(PackageInfo);
417 PackageFileEventArgs e3 =
new PackageFileEventArgs(PackageInfo, FileName, e);
419 if (!await this.OnSoftwareValidation.Raise(
this, e3,
false))
421 File.Delete(FileName);
422 Log.
Warning(
"Package with invalid signature downloaded and deleted.", FileName);
426 await this.OnSoftwareDownloaded.Raise(
this, e3);
442 string FileName = Path.Combine(this.packageFolder, PackageInfo.
FileName);
443 int MaxRetryDelayMinutes = 5;
447 HttpStatusCode StatusCode;
451 using (HttpClient WebClient =
new HttpClient())
453 using (HttpResponseMessage Response = await WebClient.GetAsync(PackageInfo.
Url, HttpCompletionOption.ResponseHeadersRead))
455 if (Response.IsSuccessStatusCode)
457 using (Stream Input = await Response.Content.ReadAsStreamAsync())
459 using (Stream Output = File.Create(FileName))
461 await Input.CopyToAsync(Output);
468 StatusCode = Response.StatusCode;
474 StatusCode = HttpStatusCode.InternalServerError;
477 if ((
int)StatusCode < 500)
479 throw new IOException(
"Unable to download new package from server. HTTP Status Code returned: " +
480 StatusCode.ToString() +
" (" + ((
int)StatusCode).ToString() +
")");
487 MsDelay = this.rnd.Next(1000, MaxRetryDelayMinutes * 60000);
489 MaxRetryDelayMinutes <<= 1;
490 if (MaxRetryDelayMinutes > 1440)
491 MaxRetryDelayMinutes = 1440;
494 await Task.Delay(MsDelay);
502 public event EventHandlerAsync<PackageUpdatedEventArgs> OnSoftwareUpdated =
null;
514 public event EventHandlerAsync<PackageFileEventArgs> OnSoftwareValidation =
null;
519 public event EventHandlerAsync<PackageFileEventArgs> OnSoftwareDownloaded =
null;
521 private async Task PackageDeletedNotificationHandler(
object Sender,
MessageEventArgs e)
523 if (
string.Compare(e.
From,
this.componentAddress,
true) != 0)
528 await this.OnSoftwareDeleted.Raise(
this, e2,
false);
534 string FileName = Path.Combine(this.packageFolder, PackageInfo.
FileName);
536 if (File.Exists(FileName))
538 File.Delete(FileName);
540 await this.OnDownloadedSoftwareDeleted.Raise(
this,
new PackageFileEventArgs(PackageInfo, FileName, e));
554 public event EventHandlerAsync<PackageDeletedEventArgs> OnSoftwareDeleted =
null;
559 public event EventHandlerAsync<PackageFileEventArgs> OnDownloadedSoftwareDeleted =
null;
Helps with common XML-related tasks.
static string Encode(string s)
Encodes a string for use in XML.
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 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.
async Task SubscribeAsync(string FileName)
Subscribes to updates to a given software package.
async Task< string > DownloadPackageAsync(Package PackageInfo)
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.
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
XmppClient Client
XMPP Client.