Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
XmppServerModule.cs
1using SkiaSharp;
2using System;
3using System.Collections.Generic;
4using System.Diagnostics;
5using System.IO;
6using System.IO.Compression;
7using System.Net;
8using System.Net.Sockets;
9using System.Reflection;
10using System.Runtime.ExceptionServices;
11using System.Security.Cryptography;
12using System.Text;
13using System.Threading;
14using System.Threading.Tasks;
15using System.Web;
16using System.Xml;
17using Waher.Content;
30using Waher.Events;
59using Waher.Script;
66using Waher.Security;
100using Waher.Things;
101
103{
107 [Singleton]
109 {
113 public const string NamespaceSynchronizationIeeeV1 = "urn:ieee:iot:synchronization:1.0";
114
118 public const string NamespaceSynchronizationNeuroFoundationV1 = "urn:nf:iot:synchronization:1.0";
119
123 public const string NamespaceDnsOverXmpp = "urn:xmpp:dox:0";
124
128 public const string NamespaceJwt = "urn:xmpp:jwt:0";
129
130
136 public static string NamespaceSynchronization(NamespaceSet Version)
137 {
138 switch (Version)
139 {
140 case NamespaceSet.XsfV0:
142 default:
143 case NamespaceSet.NeuroFoundationV1: return NamespaceSynchronizationNeuroFoundationV1;
144 }
145 }
146
152 public static NamespaceSet GetVersion(string Namespace)
153 {
154 switch (Namespace)
155 {
157 default:
166 return NamespaceSet.NeuroFoundationV1;
167
177 return NamespaceSet.IeeeV1;
178
180 return NamespaceSet.XsfV0;
181 }
182 }
183
188 private const string Ip2LocalizationPackageName = "IP2LOCATION-LITE-DB3.CSV";
189
190 private static XmppServerModule instance = null;
191 private static Calibration calibration;
192 private readonly static Stopwatch clock = CreateWatch();
193 private static readonly byte[] iotBrokerPackagePublicKey = System.Convert.FromBase64String("BRb026TgJ5L1t6T5jWL23m0BQHg1kUNY308V8ixlqmNN8nrPDzB3tTygDylyzjmDCFgWSf7OyUWA");
194 private static readonly Edwards448 ed448 = new Edwards448();
195 internal const string AutoInstallDelayParameterName = "Autoinstall.DelayMin";
196 internal const string AutoInstallContentOnlyParameterName = "Autoinstall.ContentOnly";
197 internal const string AutoInstallTimeParameterName = "Autoinstall.Time";
198
199 private readonly Dictionary<CaseInsensitiveString, WebNode> webNodes = new Dictionary<CaseInsensitiveString, WebNode>();
200 private readonly SortedDictionary<string, LinkedList<IAdminCommand>> adminCommands = new SortedDictionary<string, LinkedList<IAdminCommand>>();
201 private readonly Dictionary<CaseInsensitiveString, RoomInfo> mucRooms = new Dictionary<CaseInsensitiveString, RoomInfo>();
202 private readonly Dictionary<CaseInsensitiveString, SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>> presenceByNickAndRoom = new Dictionary<CaseInsensitiveString, SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>>();
203 private Dictionary<string, bool> vulnerableResources = null;
204 private Cache<string, IP4Localization> ip4LocalizationCache;
205 private Cache<string, Variables> chatSessions = null;
206 private Cache<string, IConsolidator> consolidators;
207 private Cache<string, string> ssoTokens;
208 private EventStatisticsSink eventStatistics = null;
209 private PersistenceLayer persistenceLayer = null;
210 private XmppServer xmppServer = null;
211 private SmtpServer smtpServer = null;
212 private Socks5Component socks5Component = null;
213 private EventLogComponent eventLogComponent = null;
214 private ProvisioningComponent provisioningComponent = null;
215 private PubSubComponent pubSubComponent = null;
216 private MultiUserChatComponent mucComponent = null;
217 private ServiceRegistrationComponent serviceRegistrationComponent = null;
218 private LegalComponent legalComponent = null;
219 private EDalerComponent eDalerComponent = null;
220 private ConcentratorServer concentratorServer = null;
221 private ServiceRegistrationClient serviceRegistrationClient;
222 private HttpServer httpServer = null;
223 private HttpxServer httpxServer = null;
224 private HttpFileUploadSettings httpFileUploadSettings = null;
225 private HttpFileUploadComponent httpFileUploadComponent;
226 private XmppFileUploadResource httpFileUploadResource;
227 private HttpFolderResource httpEncryptedFileUploadResource;
228 private HttpFolderResource httpPubSubFileUploadResource;
229 private CreateApiKey createApiKey = null;
230 private UpdateApiKey updateApiKey = null;
231 private CreateAccount createAccount = null;
232 private UpdateAccount updateAccount = null;
233 private DeleteAccount deleteAccount = null;
234 private UpdatePubSubNode updatePubSubNode = null;
235 private DeletePubSubNode deletePubSubNode = null;
236 private LoadMoreItems loadMoreItems = null;
237 private Feedback feedback = null;
238 private RequestAccount requestAccount = null;
239 private RequestApiKey requestApiKey = null;
240 private SearchEvents searchEvents = null;
241 private SendOperatorMessage sendMessage = null;
242 private SetContractState setContractState = null;
243 private PublisherAvatar publisherAvatar = null;
244 private BoshWebClientResource webClient1 = null;
245 private BoshWebClientResource webClient2 = null;
246 private WebSocketClientResource webSocketClient = null;
247 private WebHostMetaDataXml webHostMetaDataXml = null;
248 private WebHostMetaDataJson webHostMetaDataJson = null;
249 private ConnectionsSource connectionsSource = null;
250 private LegalIdentityStateChanged legalIdentityStateChanged = null;
251 private QR qr = null;
252 private UploadPackage uploadPackage = null;
253 private UploadSignature uploadSignature = null;
254 private DeletePackage deletePackage = null;
255 private InstallPackage installPackage = null;
256 private UninstallPackage uninstallPackage = null;
257 private HttpFolderResource packages = null;
258 private DnsOverHttpsResource dnsOverHttps = null;
259 private ValidateLegalId validateLegalId = null;
260 private ValidateContract validateContract = null;
261 private MultiFactorAuthentication mfa = null;
262 private QuickLogin quickLogin = null;
263 private WhatsMyEndpointResource whatsMyEndpointResource = null;
264 private AddNote addNote = null;
265 private HttpResource chatFile = null;
266 private XmppOverHttp xmppOverHttp = null;
267 private Buckets statistics = null;
268 private Timer sampleTimer = null;
269 private JwtFactory jwtFactory = null;
270
271 private static string appData = string.Empty;
272 internal static DateTime autoUpdateTP = DateTime.MinValue;
273
274 public XmppServerModule()
275 {
276 }
277
278 public async Task Start()
279 {
280 try
281 {
282 Log.Informational("XMPP Server starting.");
283 instance = this;
284
285 if (Types.TryGetModuleParameter("AppData", out object Obj) && (Obj is string s))
286 appData = s;
287
288 this.eventStatistics = new EventStatisticsSink("Event Statistics");
289 Log.Register(this.eventStatistics);
290
291 DateTime Now = DateTime.Now;
292 DateTime TP = new DateTime(Now.Year, Now.Month, Now.Day, Now.Hour, Now.Minute, 0);
293 this.statistics = new Buckets(TP, new Duration(false, 0, 0, 0, 0, 1, 0));
294
295 TP = new DateTime(Now.Year, Now.Month, Now.Day, Now.Hour, Now.Minute, Now.Second);
296 int UntilNext = (int)((TP.AddSeconds(1) - Now).TotalMilliseconds + 0.5);
297
298 if (UntilNext < 0)
299 UntilNext += 1000;
300
301 this.sampleTimer = new Timer(this.SampleTimerEventHandler, null, UntilNext, 1000);
302
303 this.persistenceLayer = new PersistenceLayer();
304
305 this.ip4LocalizationCache = new Cache<string, IP4Localization>(int.MaxValue, TimeSpan.FromDays(30), TimeSpan.FromDays(1));
306 LoginAuditor.AnnotateEndpoint += this.LoginAuditor_AnnotateEndpoint;
307
308 List<string> Ip4DnsBlackLists = new List<string>();
309 List<string> Ip6DnsBlackLists = new List<string>();
310
311 try
312 {
313 XmlDocument Doc = new XmlDocument();
314 Doc.Load(Path.Combine(appData, "DNSBL.xml"));
315
316 XSL.Validate("DNSBL", Doc, "BlackLists", "http://waher.se/Schema/DNSBL.xsd",
317 XSL.LoadSchema(typeof(XmppServerModule).Namespace + ".Schema.DNSBL.xsd"));
318
319 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
320 {
321 if (N is XmlElement E && E.LocalName == "BlackList")
322 {
323 s = XML.Attribute(E, "ip4DomainName");
324 if (!string.IsNullOrEmpty(s))
325 Ip4DnsBlackLists.Add(s);
326
327 s = XML.Attribute(E, "ip6DomainName");
328 if (!string.IsNullOrEmpty(s))
329 Ip6DnsBlackLists.Add(s);
330 }
331 }
332 }
333 catch (Exception ex)
334 {
335 Log.Exception(ex);
336 }
337
338 List<SpfExpression> SpfExpressions = new List<SpfExpression>();
339
340 try
341 {
342 XmlDocument Doc = new XmlDocument();
343 Doc.Load(Path.Combine(appData, "SPF.xml"));
344
345 XSL.Validate("SPF", Doc, "SpfRecords", "http://waher.se/Schema/SPF.xsd",
346 XSL.LoadSchema(typeof(XmppServerModule).Namespace + ".Schema.SPF.xsd"));
347
348 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
349 {
350 if (N is XmlElement E && E.LocalName == "SpfRecord")
351 {
352 string Domain = XML.Attribute(E, "domain");
353 string Spf = XML.Attribute(E, "spf");
354 bool IncludeSubdomains = XML.Attribute(E, "includeSubdomains", false);
355
356 SpfExpressions.Add(new SpfExpression(Domain, IncludeSubdomains, Spf));
357 }
358 }
359 }
360 catch (Exception ex)
361 {
362 Log.Exception(ex);
363 }
364
365 try
366 {
368 XmlDocument Doc = new XmlDocument();
369 Doc.Load(Path.Combine(appData, "StopWords.xml"));
370
371 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
372 {
373 if (N is XmlElement E && E.LocalName == "Language")
374 {
375 foreach (XmlNode N2 in E.ChildNodes)
376 {
377 if (N2 is XmlElement E2 && E2.LocalName == "Stopword")
378 await FullTextSearchModule.Tokenize(new object[] { E2.InnerText }, StopWords);
379 }
380 }
381 }
382
383 string[] StopWords2 = new string[StopWords.TokenCounts.Count];
384 StopWords.TokenCounts.Keys.CopyTo(StopWords2, 0);
385
386 Search.RegisterStopWords(StopWords2);
387 }
388 catch (Exception ex)
389 {
390 Log.Exception(ex);
391 }
392
393 IoTBroker.Legal.Identity.Iso3166.Load();
394 IoTBroker.Legal.Identity.PersonalNumberSchemes.Load();
395 IoTBroker.Legal.Identity.PhoneCountryCodes.Load();
396
397 Encoding.RegisterProvider(new CodePages());
398
399 this.smtpServer = new SmtpServer(Gateway.Domain, Gateway.GetConfigPorts("SMTP"), 10 * 1024 * 1024,
400 Gateway.Certificate, !(Gateway.Certificate is null), this.persistenceLayer, Ip4DnsBlackLists.ToArray(),
401 Ip6DnsBlackLists.ToArray(), SpfExpressions.ToArray());
402
403 Types.SetModuleParameter("SMTP_SERVER", this.smtpServer);
404
405 this.smtpServer.ExternalSniffers.Add(new XmlFileSniffer(appData + "SMTP" + Path.DirectorySeparatorChar +
406 "SMTP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml", appData + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
407 7, BinaryPresentationMethod.ByteCount));
408
409 this.smtpServer.SmtpSnifferPath = appData + "SMTP" + Path.DirectorySeparatorChar +
410 "%ENDPOINT%" + Path.DirectorySeparatorChar + "SMTP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml";
411
413
414 if (Types.TryGetModuleParameter("HTTP", out Obj))
415 this.httpServer = Obj as HttpServer;
416 else
417 this.httpServer = null;
418
419 this.xmppServer = await XmppServer.Create(Gateway.Domain, Gateway.AlternativeDomains,
420 Gateway.GetConfigPorts("XMPP.C2S"),
421 Gateway.GetConfigPorts("XMPP.S2S"),
422 Gateway.Certificate, !(Gateway.Certificate is null), this.persistenceLayer, this.smtpServer, this.httpServer);
423
424 Types.SetModuleParameter("XMPP_SERVER", this.xmppServer);
425
426 this.xmppServer.ClientConnectionAdded += this.XmppServer_ClientConnectionAdded;
427 this.xmppServer.ClientConnectionRemoved += this.XmppServer_ClientConnectionRemoved;
428 this.xmppServer.ClientConnectionUpdated += this.XmppServer_ClientConnectionUpdated;
429 this.xmppServer.ServerConnectionAdded += this.XmppServer_ServerConnectionAdded;
430 this.xmppServer.ServerConnectionRemoved += this.XmppServer_ServerConnectionRemoved;
431 this.xmppServer.ServerConnectionUpdated += this.XmppServer_ServerConnectionUpdated;
432
433 #region Neuro-Foundation V1 handlers
434
435 this.xmppServer.RegisterIqGetHandler("req", NamespaceSynchronizationNeuroFoundationV1, ClockSynchronization.RequestHandler, true);
436 this.xmppServer.RegisterIqGetHandler("sourceReq", NamespaceSynchronizationNeuroFoundationV1, ClockSynchronization.ClockSourceReq, false);
437
438 #endregion
439
440 #region IEEE V1 handlers
441
442 this.xmppServer.RegisterIqGetHandler("req", NamespaceSynchronizationIeeeV1, ClockSynchronization.RequestHandler, true);
443 this.xmppServer.RegisterIqGetHandler("sourceReq", NamespaceSynchronizationIeeeV1, ClockSynchronization.ClockSourceReq, false);
444
445 #endregion
446
447 #region XSF handlers
448
449 #endregion
450
451 this.xmppServer.RegisterIqGetHandler("dns", NamespaceDnsOverXmpp, this.DnsRequest, true);
452
453 this.xmppServer.RegisterIqGetHandler("jwt", NamespaceJwt, this.JwtRequest, true);
454 this.jwtFactory = JwtFactory.CreateHmacSha256();
455 Types.SetModuleParameter("JWT", this.jwtFactory);
456
457 this.persistenceLayer.Server = this.xmppServer;
458
459 this.provisioningComponent = new ProvisioningComponent(this.xmppServer, "provisioning", "Thing Registry and Provisioning Server");
460 this.pubSubComponent = await PubSubComponent.Create(this.xmppServer, "pubsub", "Publish/Subscribe service");
461 this.mucComponent = new MultiUserChatComponent(this.xmppServer, "muc", "Multi-User Chat service");
462 this.eventLogComponent = new EventLogComponent(this.xmppServer, "log", "Event Log");
463 this.serviceRegistrationComponent = new ServiceRegistrationComponent(this.xmppServer, "services", "Service Registry");
464 this.legalComponent = new LegalComponent(this.xmppServer, "legal", "Smart Contracts", this.httpServer, Path.Combine(Gateway.AppDataFolder, "Attachments"), null, this.pubSubComponent);
465 this.eDalerComponent = new EDalerComponent(this.xmppServer, "edaler", "eDaler", this.legalComponent);
466 this.legalComponent.EDaler = this.eDalerComponent;
467
468 int[] Socks5Ports = Gateway.GetConfigPorts("SOCKS5");
469
470 if (Socks5Ports.Length > 0)
471 this.socks5Component = new Socks5Component(this.xmppServer, "socks5", Socks5Ports[0]);
472
473 string FileName = Path.Combine(Gateway.AppDataFolder, "HoneyPotResources.txt");
474 if (File.Exists(FileName))
475 {
476 try
477 {
478 s = File.ReadAllText(FileName);
479 string[] Resources = s.Split(CommonTypes.CRLF, StringSplitOptions.RemoveEmptyEntries);
480 Dictionary<string, bool> VulnerableResources = new Dictionary<string, bool>(StringComparer.InvariantCultureIgnoreCase);
481
482 foreach (string Resource in Resources)
483 {
484 if (Resource.StartsWith("/"))
485 VulnerableResources[Resource.Substring(1)] = true;
486 }
487
488 this.vulnerableResources = VulnerableResources;
489
490 Gateway.Root.FileNotFound += this.Root_FileNotFound;
491 }
492 catch (Exception ex)
493 {
494 Log.Exception(ex);
495 }
496 }
497
498 if (!(this.httpServer is null))
499 {
500 this.httpServer.Register(new HttpConfigurableFileResource("/.well-known/security.txt",
501 Path.Combine(Gateway.RootFolder, ".well-known", "security.txt"), PlainTextCodec.DefaultContentType));
502 this.httpServer.Register(new HttpConfigurableFileResource("/firebase-messaging-sw.js",
503 Path.Combine(Gateway.RootFolder, "firebase-messaging-sw.js"), JavaScriptCodec.DefaultContentType));
504
505 this.httpServer.Register(this.webClient1 = new BoshWebClientResource(this.xmppServer, this.httpServer, "/webclient"));
506 this.httpServer.Register(this.webClient2 = new BoshWebClientResource(this.xmppServer, this.httpServer, "/http-bind"));
507 this.httpServer.Register(this.webSocketClient = new WebSocketClientResource(this.xmppServer, "/xmpp-websocket"));
508 this.httpServer.Register(this.webHostMetaDataXml = new WebHostMetaDataXml());
509 this.httpServer.Register(this.webHostMetaDataJson = new WebHostMetaDataJson());
510 this.httpServer.Register(this.packages = new HttpFolderResource("/Packages", PackagesFolder, false, false, true, false, HostDomainOptions.SameForAllDomains, new PackageUrlValidator()));
511 this.httpServer.Register(this.createApiKey = new CreateApiKey());
512 this.httpServer.Register(this.updateApiKey = new UpdateApiKey());
513 this.httpServer.Register(this.createAccount = new CreateAccount());
514 this.httpServer.Register(this.updateAccount = new UpdateAccount());
515 this.httpServer.Register(this.deleteAccount = new DeleteAccount());
516 this.httpServer.Register(this.updatePubSubNode = new UpdatePubSubNode());
517 this.httpServer.Register(this.deletePubSubNode = new DeletePubSubNode());
518 this.httpServer.Register(this.loadMoreItems = new LoadMoreItems());
519 this.httpServer.Register(this.feedback = new Feedback());
520 this.httpServer.Register(this.requestAccount = new RequestAccount());
521 this.httpServer.Register(this.requestApiKey = new RequestApiKey());
522 this.httpServer.Register(this.searchEvents = new SearchEvents());
523 this.httpServer.Register(this.sendMessage = new SendOperatorMessage());
524 this.httpServer.Register(this.setContractState = new SetContractState());
525 this.httpServer.Register(this.publisherAvatar = new PublisherAvatar());
526 this.httpServer.Register(this.legalIdentityStateChanged = new LegalIdentityStateChanged());
527 this.httpServer.Register(this.qr = new QR());
528 this.httpServer.Register(this.uploadPackage = new UploadPackage());
529 this.httpServer.Register(this.uploadSignature = new UploadSignature());
530 this.httpServer.Register(this.deletePackage = new DeletePackage());
531 this.httpServer.Register(this.installPackage = new InstallPackage());
532 this.httpServer.Register(this.uninstallPackage = new UninstallPackage());
533 this.httpServer.Register(this.dnsOverHttps = new DnsOverHttpsResource());
534 this.httpServer.Register(this.validateLegalId = new ValidateLegalId());
535 this.httpServer.Register(this.validateContract = new ValidateContract());
536 this.httpServer.Register(this.mfa = new MultiFactorAuthentication());
537 this.httpServer.Register(this.quickLogin = new QuickLogin());
538 this.httpServer.Register(this.whatsMyEndpointResource = new WhatsMyEndpointResource("/WMEP"));
539 this.httpServer.Register(this.addNote = new AddNote());
540 this.chatFile = this.httpServer.Register("/ChatFile", this.ChatFileDownload, false, true);
541 this.httpxServer = new HttpxServer(this.xmppServer, this.httpServer, 8192);
542
544 this.httpServer.Register(this.xmppOverHttp = new XmppOverHttp("/HTTPX"));
545
546 if (!(this.httpServer.Sniffers is null))
547 {
548 this.xmppServer.C2sSniffers.Add(new XmlFileSniffer(appData + "XMPP_C2S" + Path.DirectorySeparatorChar +
549 "XMPP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml", appData + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
550 7, BinaryPresentationMethod.ByteCount));
551
552 this.xmppServer.ClientSnifferPath = appData + "XMPP_C2S" + Path.DirectorySeparatorChar +
553 "%ENDPOINT%" + Path.DirectorySeparatorChar + "XMPP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml";
554
555 this.xmppServer.S2sSniffers.Add(new XmlFileSniffer(appData + "XMPP_S2S" + Path.DirectorySeparatorChar +
556 "XMPP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml", appData + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
557 7, BinaryPresentationMethod.ByteCount));
558
559 this.xmppServer.DomainSnifferPath = appData + "XMPP_S2S" + Path.DirectorySeparatorChar +
560 "%DOMAIN%" + Path.DirectorySeparatorChar + "XMPP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml";
561 }
562
563 bool Encrypted = true;
564 int[] Ports = this.httpServer.OpenHttpsPorts;
565 if (Ports.Length == 0)
566 {
567 Encrypted = false;
568 Ports = this.httpServer.OpenHttpPorts;
569 }
570
571 if (Ports.Length > 0)
572 {
573 StringBuilder Url = new StringBuilder();
574 int Port = Ports[0];
575
576 Url.Append("http");
577 if (Encrypted)
578 Url.Append('s');
579 Url.Append("://");
580 Url.Append(Gateway.Domain);
581
582 if (Encrypted)
583 {
585 {
586 Url.Append(':');
587 Url.Append(Port.ToString());
588 }
589 }
590 else
591 {
593 {
594 Url.Append(':');
595 Url.Append(Port.ToString());
596 }
597 }
598
599 string Root = Url.ToString();
600
601 Url.Append("/HttpUpload");
602
603 this.httpFileUploadSettings = new HttpFileUploadSettings()
604 {
605 MaxFileSize = 1024 * 1024 * 20,
606 MaxFilesPerMinute = 10,
607 MaxBytesPerMinute = 1024 * 1024 * 50,
608 FileLifetime = TimeSpan.FromMinutes(10),
609 FileFolder = Path.Combine(appData, "HttpUpload"),
610 HttpFolder = Url.ToString(),
611 BackupFolder = await Export.GetFullExportFolderAsync(),
612 KeyFolder = await Export.GetFullKeyExportFolderAsync(),
613 EncryptedStorageFolder = Path.Combine(appData, "EncryptedStorage"),
614 EncryptedStorageRoot = Root + "/EncryptedStorage",
615 PubSubStorageFolder = Path.Combine(appData, "PubSubStorage"),
616 PubSubStorageRoot = Root + "/PubSubStorage"
617 };
618
619 this.httpFileUploadComponent = new HttpFileUploadComponent(this.xmppServer, "upload", this.httpFileUploadSettings);
620 this.httpFileUploadResource = new XmppFileUploadResource("/HttpUpload", this.httpFileUploadComponent, Encrypted);
621 this.httpEncryptedFileUploadResource = new HttpFolderResource("/EncryptedStorage", this.httpFileUploadSettings.EncryptedStorageFolder, false, false, true, false);
622 this.httpPubSubFileUploadResource = new HttpFolderResource("/PubSubStorage", this.httpFileUploadSettings.PubSubStorageFolder, false, false, true, false);
623 this.httpServer.Register(this.httpFileUploadResource);
624 this.httpServer.Register(this.httpEncryptedFileUploadResource);
625 this.httpServer.Register(this.httpPubSubFileUploadResource);
626
627 Export.OnExportFolderUpdated += this.Export_OnExportFolderUpdated;
628 Export.OnExportKeyFolderUpdated += this.Export_OnExportKeyFolderUpdated;
629 }
630
631 HttpReverseProxyResource[] ReverseProxies = this.httpServer.GetRegisteredResources<HttpReverseProxyResource>();
632
633 foreach (HttpReverseProxyResource ProxyResource in ReverseProxies)
634 {
635 if (ProxyResource.UserSessions)
636 {
637 if (this.ssoTokens is null)
638 this.ssoTokens = new Cache<string, string>(int.MaxValue, TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(5));
639
641 ProxyResource.BeforeForwardRequest += this.ProxyResource_AddSsoInformationEncrypted;
642 else
643 ProxyResource.BeforeForwardRequest += this.ProxyResource_AddSsoInformationUnencrypted;
644 }
645 }
646 }
647
648 Gateway.OnNewCertificate += this.Gateway_OnNewCertificate;
649 Gateway.ScheduleEvent(this.Service_OnAfterBackup, DateTime.Today.AddDays(1), null);
650
651 if (Types.TryGetModuleParameter("Concentrator", out Obj) && !((this.concentratorServer = Obj as ConcentratorServer) is null))
652 {
653 await this.concentratorServer.Register(this.connectionsSource = new ConnectionsSource());
654 await this.concentratorServer.Register(new GatewayConfigSource());
655 await this.concentratorServer.Register(new PersistedDataSource());
656 await this.concentratorServer.Register(new ProgramDataSource());
657 await this.concentratorServer.Register(new ProtocolsSource(new INode[]
658 {
659 new HttpProtocol(this.httpServer),
660 new XmppProtocol(this.xmppServer)
661 }));
662 }
663
664 foreach (PubSubNode Node in await Database.Find<PubSubNode>(new FilterAnd(
665 new FilterFieldEqualTo("Service", string.Empty), new FilterFieldEqualTo("IsRoot", true),
666 new FilterFieldEqualTo("PublishOnWeb", true))))
667 {
668 this.WebNodeStatus(Node.Name, Node.PublishOnWeb);
669 }
670
671 await this.CheckRegistration();
672
673 if (!(Gateway.ProvisioningClient is null))
674 Gateway.ProvisioningClient.ManagePresenceSubscriptionRequests = false;
675
676 if (!(Gateway.SoftwareUpdateClient is null))
677 {
678 Gateway.SoftwareUpdateClient.OnSoftwareUpdated += this.SoftwareUpdateClient_OnSoftwareUpdated;
679 Gateway.SoftwareUpdateClient.OnSoftwareValidation += this.SoftwareUpdateClient_OnSoftwareValidation;
680 Gateway.SoftwareUpdateClient.OnSoftwareDownloaded += this.SoftwareUpdateClient_OnSoftwareDownloaded;
681 Gateway.SoftwareUpdateClient.OnSoftwareDeleted += this.SoftwareUpdateClient_OnSoftwareDeleted;
682 Gateway.SoftwareUpdateClient.OnDownloadedSoftwareDeleted += this.SoftwareUpdateClient_OnDownloadedSoftwareDeleted;
683 }
684
685 Gateway.XmppClient.OnStateChanged += this.XmppClient_OnStateChanged;
686 Gateway.XmppClient.OnChatMessage += this.XmppClient_OnChatMessage;
687
688 if (!(Gateway.MucClient is null))
689 {
690 Gateway.XmppClient.OnGroupChatMessage += this.XmppClient_OnGroupChatMessage;
691 Gateway.XmppClient.OnValidateSender += this.XmppClient_OnValidateSender;
692
693 Gateway.MucClient.RoomInvitationReceived += this.MucClient_RoomInvitationReceived;
694 Gateway.MucClient.DirectInvitationReceived += this.MucClient_DirectInvitationReceived;
695 Gateway.MucClient.OccupantPresence += this.MucClient_OccupantPresence;
696 Gateway.MucClient.PrivateMessageReceived += this.MucClient_PrivateMessageReceived;
697 Gateway.MucClient.RoomDestroyed += this.MucClient_RoomDestroyed;
698
699 await this.LoadPermanentRooms();
700 }
701
702 await Gateway.XmppClient.SetPresence(Networking.XMPP.Availability.Chat);
703
704 TP = await RuntimeSettings.GetAsync(AutoInstallTimeParameterName, DateTime.MinValue);
705 if (TP > DateTime.MinValue)
706 autoUpdateTP = Gateway.ScheduleEvent(this.UpdateSoftware, TP, null);
707
708 StringBuilder sb = new StringBuilder();
709 SortedDictionary<string, bool> AlphabeticalOrder = new SortedDictionary<string, bool>();
710
711 foreach (Assembly A in Types.Assemblies)
712 AlphabeticalOrder[A.FullName] = true;
713
714 foreach (string Name in AlphabeticalOrder.Keys)
715 sb.AppendLine(Name);
716
717 string H = Hashes.ComputeSHA256HashString(Encoding.UTF8.GetBytes(sb.ToString()));
718 string H2 = await RuntimeSettings.GetAsync("BrokerVersion", string.Empty);
719 bool Updated = H != H2;
720
721 if (Gateway.Domain != "example.com" &&
722 Gateway.Domain != "example2.com" &&
723 Gateway.Domain != "example3.com" &&
724 Gateway.Domain != "localhost" &&
726 {
727 Now = DateTime.Now;
728
729 sb.Clear();
730
731 sb.Append("XMPP server at [`");
732 sb.Append(Gateway.Domain);
733 sb.Append("`](http://");
734 sb.Append(Gateway.Domain);
735 sb.Append("/) ");
736
737 if (Updated)
738 sb.Append("updated and ");
739
740 sb.Append("restarted at ");
741 sb.Append(Now.ToShortDateString());
742 sb.Append(", ");
743 sb.Append(Now.ToLongTimeString());
744 sb.Append('.');
745
746 if (Updated)
747 sb.Append(" [Release Notes](https://lab.tagroot.io/ReleaseNotes)");
748
749 await Gateway.SendNotification(sb.ToString());
750 }
751
752 if (Updated)
753 await RuntimeSettings.SetAsync("BrokerVersion", H);
754
755 await LegalComponent.CheckLegalIdentityReferences();
756 await PaiwiseProcessor.QueueUnprocessedPayments(this.eDalerComponent);
757 await ComponentSynchronization.QueueUnprocessedMessages();
758 await MarketplaceProcessor.LoadActiveItems(this.eDalerComponent);
759 await StateMachineProcessor.ModuleStarted(this.legalComponent, this.eDalerComponent);
760
761 if (!string.IsNullOrEmpty(Gateway.Domain) && DomainConfiguration.Instance.UseEncryption) // i.e. accessible from the Internet using domain name.
762 Gateway.HttpxProxy.PostResource = this.xmppOverHttp;
763 /*{
764 IPAddress MyExternalIp = null;
765 IPAddress MyLocalIp = null;
766
767 try
768 {
769 WebPoster Poster = new WebPoster();
770
771 object Response = await InternetContent.PostAsync(new Uri("https://" + XmppConfiguration.Instance.Host + "/WMEP"), null);
772 if (Response is string PlainText)
773 {
774 int i = PlainText.LastIndexOf(':');
775 if (i > 0)
776 {
777 PlainText = PlainText.Substring(0, i);
778 if (IPAddress.TryParse(PlainText, out IPAddress Address))
779 {
780 if (InternetGatewayRegistrator.IsPublicAddress(Address))
781 {
782 Gateway.HttpxProxy.PostResource = "https://" + MyExternalIp.ToString() + "/HTTPX";
783
784 using (TcpClient Client = new TcpClient())
785 {
786 await Client.ConnectAsync(XmppConfiguration.Instance.Host, XmppConfiguration.Instance.Port);
787 if (Client.Client.LocalEndPoint is IPEndPoint Endpoint)
788 {
789 MyLocalIp = Endpoint.Address;
790 MyExternalIp = Address;
791 }
792
793 Client.Close();
794 }
795 }
796 }
797 }
798 }
799 }
800 catch (Exception)
801 {
802 // Unable to get public IP from parent neuron.
803 }
804 }*/
805
806 Gateway.ScheduleEvent(this.ImportIpLocalizationDatabase, DateTime.Now.AddMinutes(15), null);
807
808 Gateway.OnTerminate += this.Gateway_OnTerminate;
809
810 Log.Informational("XMPP Server started.");
811 }
812 catch (Exception ex)
813 {
814 Log.Exception(ex);
815 }
816 }
817
818 private Task Gateway_OnTerminate(object Sender, EventArgs e)
819 {
820 System.Timers.Timer Timer1 = new System.Timers.Timer(90000);
821 Timer1.Elapsed += this.CloseProcess;
822
823 System.Timers.Timer Timer2 = new System.Timers.Timer(110000);
824 Timer1.Elapsed += this.KillProcess;
825
826 return Task.CompletedTask;
827 }
828
829 private void CloseProcess(object Sender, System.Timers.ElapsedEventArgs e)
830 {
831 Process.GetCurrentProcess().Close();
832 }
833
834 private void KillProcess(object Sender, System.Timers.ElapsedEventArgs e)
835 {
836 Process.GetCurrentProcess().Kill();
837 }
838
839 private async Task Root_FileNotFound(object Sender, FileNotFoundEventArgs e)
840 {
841 try
842 {
844 string s = e.Request.SubPath;
845 if (s.StartsWith("/"))
846 s = s.Substring(1);
847
848 if (!(this.vulnerableResources?.ContainsKey(s) ?? false))
849 return;
850
851 e.Exception = null;
852
853 bool Blocked = await Gateway.LoginAuditor.ProcessLoginFailure(e.Request.RemoteEndPoint, "HTTP", DateTime.Now,
854 "Scanning for vulnerable web resources.");
855
856 Log.Warning("Honey-pot resource requested.", "/" + s, e.Request.RemoteEndPoint, "HoneyPot",
857 new KeyValuePair<string, object>("Blocked", Blocked));
858 e.Request.Session?.Add("HoneyPotBlocked", Blocked);
859
860 await Task.Delay(60000); // Wait one minute before returning response. (After 2 minutes, the request is removed from the server.)
861
862 string ContentType = ex.ContentType;
863 byte[] Content = ex.Content;
864
865 if (Content is null)
866 {
867 object ContentObject = await ex.GetContentObjectAsync();
868
869 if (ContentObject is null)
870 {
871 Content = Encoding.UTF8.GetBytes(ex.Message);
872 ContentType = "text/plain; charset=utf-8";
873 }
874 else
875 {
876 KeyValuePair<byte[], string> P = await InternetContent.EncodeAsync(ContentObject, Encoding.UTF8);
877 Content = P.Key;
878 ContentType = P.Value;
879 }
880 }
881
882 string Html = await Gateway.GetCustomErrorHtml(e.Request, "HoneyPot.md", ContentType, Content);
883 if (!string.IsNullOrEmpty(Html))
884 {
885 e.Response.StatusCode = ex.StatusCode;
886 e.Response.StatusMessage = ex.Message;
887
888 if (!(ex.HeaderFields is null))
889 {
890 foreach (KeyValuePair<string, string> P in ex.HeaderFields)
891 {
892 if (string.Compare(P.Key, "Content-Type", true) != 0)
893 e.Response.SetHeader(P.Key, P.Value);
894 }
895 }
896
897 e.Response.ContentType = "text/html; charset=utf-8";
898 await e.Response.Write(Encoding.UTF8.GetBytes(Html));
899 await e.Response.SendResponse();
900 }
901 else
902 await e.Response.SendResponse(ex);
903
904 await e.Response.DisposeAsync();
905 }
906 catch (Exception ex)
907 {
908 Log.Exception(ex);
909 }
910 }
911
912 private bool subscribingSWUpdates = false;
913 private bool subscribedToPackages = false;
914
915 private async Task XmppClient_OnStateChanged(object Sender, Networking.XMPP.XmppState NewState)
916 {
917 if (NewState == Networking.XMPP.XmppState.Connected)
918 {
919 if (!(Gateway.SoftwareUpdateClient is null) &&
920 !this.subscribingSWUpdates &&
921 string.Compare(Gateway.XmppClient.Domain, this.xmppServer?.Domain, true) != 0)
922 {
923 this.subscribingSWUpdates = true;
924 try
925 {
926 DateTime LastUpdate = await RuntimeSettings.GetAsync("SW.Update.Last", DateTime.MinValue);
927
928 if ((DateTime.Now - LastUpdate).TotalDays >= 7 || !this.subscribedToPackages)
929 {
930 await Gateway.SoftwareUpdateClient.SubscribeAsync("*");
931 this.subscribedToPackages = true;
932
933 foreach (Networking.XMPP.Software.Package Package in await Gateway.SoftwareUpdateClient.GetPackagesAsync())
934 await this.CheckSoftwarePackage(Package, (P, MessageId) => Task.FromResult<string>(MessageId));
935
936 await RuntimeSettings.SetAsync("SW.Update.Last", DateTime.Now);
937 }
938 }
939 catch (Exception ex)
940 {
941 Log.Exception(ex);
942 }
943 finally
944 {
945 this.subscribingSWUpdates = false;
946 }
947 }
948
949 await this.RejoinRooms();
950 }
951 }
952
953 private Task Gateway_OnNewCertificate(object Sender, IoTGateway.Events.CertificateEventArgs e)
954 {
955 this.smtpServer?.UpdateCertificate(e.Certificate);
956 this.xmppServer?.UpdateCertificate(e.Certificate);
957
958 return Task.CompletedTask;
959 }
960
961 internal void WebNodeStatus(CaseInsensitiveString NodeName, bool Visible)
962 {
964
965 if (Visible)
966 {
967 lock (this.webNodes)
968 {
969 if (this.webNodes.ContainsKey(NodeName))
970 return;
971
972 WebNode = new WebNode(NodeName);
973 this.webNodes[NodeName] = WebNode;
974 }
975
976 try
977 {
978 this.httpServer.Register(WebNode);
979 }
980 catch (Exception ex)
981 {
982 Log.Exception(ex);
983
984 lock (this.webNodes)
985 {
986 this.webNodes.Remove(NodeName);
987 }
988 }
989 }
990 else
991 {
992 lock (this.webNodes)
993 {
994 if (!this.webNodes.TryGetValue(NodeName, out WebNode))
995 return;
996
997 this.webNodes.Remove(NodeName);
998 }
999
1000 this.httpServer.Unregister(WebNode);
1001 }
1002 }
1003
1004 public async Task DoImportIpLocalizationDatabase()
1005 {
1006 string s = Path.Combine(PackagesFolder, Ip2LocalizationPackageName);
1007 await RuntimeSettings.SetAsync(s, DateTime.MinValue);
1008 this.ImportIpLocalizationDatabase(null);
1009 }
1010
1011 private static bool importingIp = false;
1012
1013 private async void ImportIpLocalizationDatabase(object State)
1014 {
1015 if (!importingIp)
1016 {
1017 importingIp = true;
1018 try
1019 {
1020 string s = Path.Combine(PackagesFolder, Ip2LocalizationPackageName);
1021
1022 if (File.Exists(s))
1023 {
1024 DateTime TP = File.GetLastWriteTime(s);
1025 DateTime Last = await RuntimeSettings.GetAsync(s, DateTime.MinValue);
1026
1027 if (TP > Last)
1028 {
1029 StringBuilder sb = new StringBuilder("Samples:=[");
1030 DateTime Start = DateTime.Now;
1031 long Count = 0;
1032 long Skipped = 0;
1033 bool First = true;
1034
1035 Log.Informational("Starting import of IP localization database.");
1036 await Gateway.SendNotification("Starting import of IP localization database.");
1037
1038 await Database.Clear("IP4Localization");
1039
1040 try
1041 {
1042 await FindIpAddress("127.0.0.1"); // Makes sure indices are created.
1043 }
1044 catch (Exception ex)
1045 {
1046 Log.Exception(ex);
1047 }
1048
1049 await Database.StartBulk();
1050 try
1051 {
1052 using (FileStream fs = File.OpenRead(s))
1053 {
1054 using (StreamReader r = new StreamReader(fs))
1055 {
1056 while (!r.EndOfStream)
1057 {
1058 string Row = await r.ReadLineAsync();
1059 string[][] Records = CSV.Parse(Row);
1060
1061 foreach (string[] Record in Records)
1062 {
1063 if (Record.Length >= 6 &&
1064 uint.TryParse(Record[0], out uint IpRangeFrom) &&
1065 uint.TryParse(Record[1], out uint IpRangeTo))
1066 {
1067 Count++;
1068
1070 {
1071 RangeStart = IpRangeFrom,
1072 RangeEnd = IpRangeTo,
1073 CountryCode = Record[2],
1074 Country = Record[3],
1075 Region = Record[4],
1076 City = Record[5]
1077 };
1078
1079 await Database.Insert(Rec);
1080
1081 if (Count % 10000 == 0)
1082 {
1083 await Database.EndBulk();
1084 await Database.StartBulk();
1085
1086 if (First)
1087 First = false;
1088 else
1089 sb.AppendLine(",");
1090
1091 sb.Append('[');
1092 sb.Append(Count.ToString());
1093 sb.Append(',');
1094 sb.Append(CommonTypes.Encode((DateTime.Now - Start).TotalSeconds));
1095 sb.Append(']');
1096 }
1097 }
1098 else
1099 Skipped++;
1100 }
1101 }
1102
1103 }
1104 }
1105 }
1106 finally
1107 {
1108 await Database.EndBulk();
1109 }
1110
1111 if (!First)
1112 sb.AppendLine(",");
1113
1114 sb.Append('[');
1115 sb.Append(Count.ToString());
1116 sb.Append(',');
1117 sb.Append(CommonTypes.Encode((DateTime.Now - Start).TotalSeconds));
1118 sb.AppendLine("]];");
1119
1120 sb.AppendLine("Objects:=Samples[0,];");
1121 sb.AppendLine("Seconds:=Samples[1,];");
1122 sb.AppendLine("ObjOverTime:=plot2dcurve(Seconds,Objects);");
1123 sb.AppendLine("ObjOverTime.Title:=\"Objects imported over time\";");
1124 sb.AppendLine("ObjOverTime.LabelX:=\"Seconds\";");
1125 sb.AppendLine("ObjOverTime.LabelY:=\"#Objects\";");
1126
1127 sb.AppendLine("NrSamples:=count(Objects);");
1128 sb.AppendLine("DeltaSeconds:=Seconds[1..(NrSamples-1)]-Seconds[0..(NrSamples-2)];");
1129 sb.AppendLine("DeltaObjects:=Objects[1..(NrSamples-1)]-Objects[0..(NrSamples-2)];");
1130 sb.AppendLine("ObjectsPerSecond:=DeltaObjects./DeltaSeconds;");
1131 sb.AppendLine("SpeedOverTime:=plot2dcurve(Seconds[1..(NrSamples-1)],ObjectsPerSecond);");
1132 sb.AppendLine("SpeedOverTime.Title:=\"Objects/s imported over time\";");
1133 sb.AppendLine("SpeedOverTime.LabelX:=\"Seconds\";");
1134 sb.AppendLine("SpeedOverTime.LabelY:=\"#Objects/s\";");
1135
1136 File.WriteAllText(appData + "ImportTimes.script", sb.ToString());
1137
1138 Log.Informational("Import of IP localization database completed.",
1139 new KeyValuePair<string, object>("NrImported", Count),
1140 new KeyValuePair<string, object>("NrSkipped", Skipped));
1141
1142 await RuntimeSettings.SetAsync(s, TP);
1143
1144 await Gateway.SendNotification("Import of IP localization database completed. " +
1145 Count.ToString() + " records imported. " + Skipped.ToString() + " " +
1146 (Skipped == 1 ? "record" : "records") + " skipped.");
1147
1148 try
1149 {
1150 Expression Exp = new Expression(sb.ToString());
1152
1153 await Exp.EvaluateAsync(Variables);
1154
1155 if (Variables.TryGetVariable("ObjOverTime", out Variable v) && v.ValueObject is Graph ObjOverTime)
1156 await Gateway.SendNotification(ObjOverTime);
1157
1158 if (Variables.TryGetVariable("SpeedOverTime", out v) && v.ValueObject is Graph SpeedOverTime)
1159 await Gateway.SendNotification(SpeedOverTime);
1160 }
1161 catch (Exception ex)
1162 {
1163 Log.Exception(ex);
1164 }
1165 }
1166 }
1167 }
1168 catch (Exception ex)
1169 {
1170 Log.Error("Unable to import IP localization database.\r\n\r\n" + ex.Message, Ip2LocalizationPackageName);
1171 }
1172 finally
1173 {
1174 importingIp = false;
1175 }
1176 }
1177 }
1178
1179 private async Task LoginAuditor_AnnotateEndpoint(object Sender, AnnotateEndpointEventArgs e)
1180 {
1181 if (this.ip4LocalizationCache is null)
1182 return;
1183
1184 if (!this.ip4LocalizationCache.TryGetValue(e.RemoteEndpoint, out IP4Localization Location))
1185 {
1186 Location = await FindIpAddress(e.RemoteEndpoint);
1187 this.ip4LocalizationCache?.Add(e.RemoteEndpoint, Location);
1188 }
1189
1190 if (!(Location is null))
1191 {
1192 e.AddTag("City", Location.City);
1193 e.AddTag("Region", Location.Region);
1194 e.AddTag("Country", Location.Country);
1195 e.AddTag("Code", Location.CountryCode);
1196
1197 string s = "flag-" + Location.CountryCode?.ToLower();
1198 if (EmojiUtilities.TryGetEmoji(s, out EmojiInfo _))
1199 e.AddTag("Flag", ":" + s + ":");
1200
1201 e.AddTag("Acknowledgement", "This site or product includes IP2Location LITE data available from http://www.ip2location.com.");
1202 }
1203 }
1204
1210 public static Task<IP4Localization> FindIpAddress(string RemoteEndpoint)
1211 {
1212 int i = RemoteEndpoint.LastIndexOf(':');
1213 if (i > 0 && int.TryParse(RemoteEndpoint.Substring(i + 1), out int _))
1214 RemoteEndpoint = RemoteEndpoint.Substring(0, i);
1215
1216 if (!IPAddress.TryParse(RemoteEndpoint, out IPAddress Addr))
1217 return Task.FromResult<IP4Localization>(null);
1218
1219 return FindIpAddress(Addr);
1220 }
1221
1227 public static async Task<IP4Localization> FindIpAddress(IPAddress Addr)
1228 {
1229 if (Addr.AddressFamily != AddressFamily.InterNetwork)
1230 return null;
1231
1232 byte[] Bytes = Addr.GetAddressBytes();
1233
1234 uint Value = Bytes[0];
1235 Value <<= 8;
1236 Value |= Bytes[1];
1237 Value <<= 8;
1238 Value |= Bytes[2];
1239 Value <<= 8;
1240 Value |= Bytes[3];
1241
1242 try
1243 {
1244 foreach (IP4Localization Loc in await Database.Find<IP4Localization>(0, 1, new FilterFieldLesserOrEqualTo("RangeStart", Value), "-RangeStart"))
1245 {
1246 if (Value >= Loc.RangeStart && Value <= Loc.RangeEnd)
1247 return Loc;
1248 }
1249 }
1250 catch (Exception ex)
1251 {
1252 Log.Exception(ex);
1253 }
1254
1255 return null;
1256 }
1257
1263 public async static Task AppendRemoteEndpointToTable(StringBuilder Markdown, string RemoteEndpoint)
1264 {
1265 Markdown.Append("| Remote Endpoint: | ");
1266 Markdown.Append(RemoteEndpoint);
1267 Markdown.AppendLine(" |");
1268
1269 foreach (KeyValuePair<string, object> P in await LoginAuditor.Annotate(RemoteEndpoint))
1270 {
1271 Markdown.Append("| ");
1272 Markdown.Append(MarkdownDocument.Encode(P.Key));
1273 Markdown.Append(" | ");
1274 Markdown.Append(MarkdownDocument.Encode(P.Value?.ToString() ?? string.Empty));
1275 Markdown.AppendLine(" |");
1276 }
1277 }
1278
1279 private async void Service_OnAfterBackup(object P)
1280 {
1281 try
1282 {
1283 if (!(this.httpServer is null))
1284 {
1286
1287 Sources.Protocols.Commands.HttpStatistic HttpStatistic = new Sources.Protocols.Commands.HttpStatistic()
1288 {
1289 Start = Stat.LastStat,
1290 Timestamp = Stat.CurrentStat,
1291 NrBytesRx = Stat.NrBytesRx,
1292 NrBytesTx = Stat.NrBytesTx,
1293 NrCalls = Stat.NrCalls,
1294 CallsPerMethod = this.Convert(Stat.CallsPerMethod),
1295 CallsPerUserAgent = this.Convert(Stat.CallsPerUserAgent),
1296 CallsPerFrom = this.Convert(Stat.CallsPerFrom),
1297 CallsPerResource = this.Convert(Stat.CallsPerResource)
1298 };
1299
1300 await Database.InsertLazy(HttpStatistic);
1301 }
1302
1303 if (!(this.xmppServer is null))
1304 {
1306
1307 Sources.Protocols.Commands.XmppStatistic XmppStatistic = new Sources.Protocols.Commands.XmppStatistic()
1308 {
1309 Start = Stat.LastStat,
1310 Timestamp = Stat.CurrentStat,
1311 NrBytesRx = Stat.NrBytesRx,
1312 NrBytesTx = Stat.NrBytesTx,
1313 NrStanzas = Stat.NrStanzas,
1314 StanzasPerStanzaType = this.Convert(Stat.StanzasPerStanzaType),
1315 StanzasPerFromDomain = this.Convert(Stat.StanzasPerFromDomain),
1316 StanzasPerToDomain = this.Convert(Stat.StanzasPerToDomain),
1317 StanzasPerFromBareJid = this.Convert(Stat.StanzasPerFromBareJid),
1318 StanzasPerToBareJid = this.Convert(Stat.StanzasPerToBareJid),
1319 StanzasPerNamespace = this.Convert(Stat.StanzasPerNamespace),
1320 StanzasPerFqn = this.Convert(Stat.StanzasPerFqn)
1321 };
1322
1323 await Database.InsertLazy(XmppStatistic);
1324 }
1325
1326 if (!(this.eventStatistics is null))
1327 {
1328 EventStatistics Stat = this.eventStatistics.GetStatisticsSinceLast();
1329
1330 Sources.PersistedData.Commands.EventStatistic EventStatistic = new Sources.PersistedData.Commands.EventStatistic()
1331 {
1332 Start = Stat.LastStat,
1333 Timestamp = Stat.CurrentStat,
1334 PerType = this.Convert(Stat.PerType),
1335 PerLevel = this.Convert(Stat.PerLevel),
1336 PerEventId = this.Convert(Stat.PerEventId),
1337 PerActor = this.Convert(Stat.PerActor),
1338 PerModule = this.Convert(Stat.PerModule),
1339 PerFacility = this.Convert(Stat.PerFacility),
1340 PerStackTrace = this.Convert(Stat.PerStackTrace)
1341 };
1342
1343 await Database.InsertLazy(EventStatistic);
1344 }
1345
1346 DateTime TimeLimit = DateTime.Now.AddDays(-30); // TODO: Make configurable.
1347 int Nr, Nr2;
1348
1349 if (!(this.persistenceLayer is null))
1350 {
1351 Nr = await this.persistenceLayer.DeleteOfflineMessages(TimeLimit);
1352 if (Nr > 0)
1353 Log.Informational(Nr.ToString() + " offline messages deleted.");
1354 }
1355
1356 if (!(this.xmppServer is null))
1357 {
1358 Nr = await this.xmppServer.DeleteOldMailContent(TimeLimit);
1359 if (Nr > 0)
1360 Log.Informational("Mail content from " + Nr.ToString() + " old messages deleted.");
1361 }
1362
1363 if (!(this.pubSubComponent is null))
1364 {
1365 (Nr, Nr2) = await this.pubSubComponent.DeleteExpiredNodes();
1366 if (Nr > 0)
1367 Log.Informational(Nr.ToString() + " pubsub nodes (and " + Nr2.ToString() + " items) deleted.");
1368 }
1369
1370 (int NrTokens, int NrEvents, int NrTags) = await NeuroFeatures.NeuroFeaturesProcessor.DeleteExpiredTokens();
1371 if (NrTokens > 0)
1372 Log.Informational(NrTokens.ToString() + " tokens, " + NrTags.ToString() + " tags and " + NrEvents.ToString() + " events deleted.");
1373
1374 (int NrMachines, int NrEventHandlers, int NrCurrentStates, int NrSamples) =
1375 await StateMachineProcessor.DeleteExpiredMachines();
1376 if (NrMachines > 0)
1377 Log.Informational(NrMachines.ToString() + " state machines, " + NrEventHandlers.ToString() + " event handlers and " + NrSamples.ToString() + " samples deleted.");
1378 }
1379 catch (Exception ex)
1380 {
1381 Log.Exception(ex);
1382 }
1383 finally
1384 {
1385 Gateway.ScheduleEvent(this.Service_OnAfterBackup, DateTime.Today.AddDays(1), null);
1386 }
1387 }
1388
1389 private Sources.PersistedData.Commands.Statistic[] Convert(Dictionary<string, Statistic> Statistics)
1390 {
1391 List<Sources.PersistedData.Commands.Statistic> Result = new List<Sources.PersistedData.Commands.Statistic>();
1392
1393 foreach (KeyValuePair<string, Statistic> P in Statistics)
1394 {
1395 Result.Add(new Sources.PersistedData.Commands.Statistic()
1396 {
1397 Name = P.Key,
1398 Count = P.Value.Count,
1399 First = P.Value.First,
1400 Last = P.Value.Last
1401 });
1402 }
1403
1404 return Result.ToArray();
1405 }
1406
1407 public async Task Stop()
1408 {
1409 Log.Informational("XMPP Server shutting down.");
1410
1411 await PaiwiseProcessor.StopProcessingPayments();
1412 await StateMachineProcessor.ModuleStopped();
1413
1414 Gateway.OnNewCertificate -= this.Gateway_OnNewCertificate;
1415 Gateway.OnTerminate -= this.Gateway_OnTerminate;
1416
1417 if (!(Gateway.SoftwareUpdateClient is null))
1418 {
1419 Gateway.SoftwareUpdateClient.OnSoftwareUpdated -= this.SoftwareUpdateClient_OnSoftwareUpdated;
1420 Gateway.SoftwareUpdateClient.OnSoftwareValidation -= this.SoftwareUpdateClient_OnSoftwareValidation;
1421 Gateway.SoftwareUpdateClient.OnSoftwareDownloaded -= this.SoftwareUpdateClient_OnSoftwareDownloaded;
1422 Gateway.SoftwareUpdateClient.OnSoftwareDeleted -= this.SoftwareUpdateClient_OnSoftwareDeleted;
1423 Gateway.SoftwareUpdateClient.OnDownloadedSoftwareDeleted -= this.SoftwareUpdateClient_OnDownloadedSoftwareDeleted;
1424 }
1425
1426
1427 if (!(Gateway.MucClient is null))
1428 {
1429 Gateway.XmppClient.OnGroupChatMessage -= this.XmppClient_OnGroupChatMessage;
1430 Gateway.XmppClient.OnValidateSender -= this.XmppClient_OnValidateSender;
1431
1432 Gateway.MucClient.RoomInvitationReceived -= this.MucClient_RoomInvitationReceived;
1433 Gateway.MucClient.DirectInvitationReceived -= this.MucClient_DirectInvitationReceived;
1434 Gateway.MucClient.OccupantPresence -= this.MucClient_OccupantPresence;
1435 Gateway.MucClient.PrivateMessageReceived -= this.MucClient_PrivateMessageReceived;
1436 Gateway.MucClient.RoomDestroyed -= this.MucClient_RoomDestroyed;
1437 }
1438
1439 this.sampleTimer?.Dispose();
1440 this.sampleTimer = null;
1441
1442 await this.LeaveAllRooms();
1443
1444 LoginAuditor.AnnotateEndpoint -= this.LoginAuditor_AnnotateEndpoint;
1445 this.ip4LocalizationCache?.Dispose();
1446 this.ip4LocalizationCache = null;
1447
1448 if (!(this.eventStatistics is null))
1449 {
1450 Log.Unregister(this.eventStatistics);
1451 this.eventStatistics.Dispose();
1452 this.eventStatistics = null;
1453 }
1454
1455 instance = null;
1456
1457 if (!(this.vulnerableResources is null))
1458 {
1459 this.vulnerableResources = null;
1460
1461 if (!(Gateway.Root is null))
1462 Gateway.Root.FileNotFound -= this.Root_FileNotFound;
1463 }
1464
1465 if (!(this.httpServer is null))
1466 {
1467 Export.OnExportFolderUpdated -= this.Export_OnExportFolderUpdated;
1468 Export.OnExportKeyFolderUpdated -= this.Export_OnExportKeyFolderUpdated;
1469
1470 this.httpServer.Unregister(this.webClient1);
1471 this.httpServer.Unregister(this.webClient2);
1472 this.httpServer.Unregister(this.webSocketClient);
1473 this.httpServer.Unregister(this.webHostMetaDataXml);
1474 this.httpServer.Unregister(this.webHostMetaDataJson);
1475 this.httpServer.Unregister(this.packages);
1476 this.httpServer.Unregister(this.createApiKey);
1477 this.httpServer.Unregister(this.updateApiKey);
1478 this.httpServer.Unregister(this.createAccount);
1479 this.httpServer.Unregister(this.updateAccount);
1480 this.httpServer.Unregister(this.deleteAccount);
1481 this.httpServer.Unregister(this.updatePubSubNode);
1482 this.httpServer.Unregister(this.deletePubSubNode);
1483 this.httpServer.Unregister(this.loadMoreItems);
1484 this.httpServer.Unregister(this.feedback);
1485 this.httpServer.Unregister(this.requestAccount);
1486 this.httpServer.Unregister(this.requestApiKey);
1487 this.httpServer.Unregister(this.searchEvents);
1488 this.httpServer.Unregister(this.sendMessage);
1489 this.httpServer.Unregister(this.setContractState);
1490 this.httpServer.Unregister(this.publisherAvatar);
1491 this.httpServer.Unregister(this.legalIdentityStateChanged);
1492 this.httpServer.Unregister(this.qr);
1493 this.httpServer.Unregister(this.uploadPackage);
1494 this.httpServer.Unregister(this.uploadSignature);
1495 this.httpServer.Unregister(this.deletePackage);
1496 this.httpServer.Unregister(this.installPackage);
1497 this.httpServer.Unregister(this.uninstallPackage);
1498 this.httpServer.Unregister(this.dnsOverHttps);
1499 this.httpServer.Unregister(this.validateLegalId);
1500 this.httpServer.Unregister(this.validateContract);
1501 this.httpServer.Unregister(this.mfa);
1502 this.httpServer.Unregister(this.quickLogin);
1503 this.httpServer.Unregister(this.whatsMyEndpointResource);
1504 this.httpServer.Unregister(this.addNote);
1505 this.httpServer.Unregister(this.chatFile);
1506 this.httpServer.Unregister(this.xmppOverHttp);
1507
1508 this.httpxServer?.Dispose();
1509 this.httpxServer = null;
1510
1511 if (!(this.httpFileUploadResource is null))
1512 this.httpServer.Unregister(this.httpFileUploadResource);
1513
1514 if (!(this.httpEncryptedFileUploadResource is null))
1515 this.httpServer.Unregister(this.httpEncryptedFileUploadResource);
1516
1517 if (!(this.httpPubSubFileUploadResource is null))
1518 this.httpServer.Unregister(this.httpPubSubFileUploadResource);
1519
1520 if (!(this.webNodes is null))
1521 {
1522 WebNode[] Nodes;
1523
1524 lock (this.webNodes)
1525 {
1526 Nodes = new WebNode[this.webNodes.Count];
1527 this.webNodes.Values.CopyTo(Nodes, 0);
1528 this.webNodes.Clear();
1529 }
1530
1531 foreach (WebNode WebNode in Nodes)
1532 this.httpServer.Unregister(WebNode);
1533 }
1534
1535 this.webClient1 = null;
1536 this.webClient2 = null;
1537 this.webSocketClient = null;
1538 this.webHostMetaDataXml = null;
1539 this.webHostMetaDataJson = null;
1540 this.createApiKey = null;
1541 this.updateApiKey = null;
1542 this.createAccount = null;
1543 this.updateAccount = null;
1544 this.deleteAccount = null;
1545 this.updatePubSubNode = null;
1546 this.deletePubSubNode = null;
1547 this.loadMoreItems = null;
1548 this.feedback = null;
1549 this.requestAccount = null;
1550 this.requestApiKey = null;
1551 this.searchEvents = null;
1552 this.sendMessage = null;
1553 this.setContractState = null;
1554 this.publisherAvatar = null;
1555 this.legalIdentityStateChanged = null;
1556 this.qr = null;
1557 this.uploadPackage = null;
1558 this.uploadSignature = null;
1559 this.deletePackage = null;
1560 this.installPackage = null;
1561 this.uninstallPackage = null;
1562 this.dnsOverHttps = null;
1563 this.validateLegalId = null;
1564 this.validateContract = null;
1565 this.mfa = null;
1566 this.quickLogin?.Dispose();
1567 this.quickLogin = null;
1568 this.whatsMyEndpointResource = null;
1569 this.addNote = null;
1570 this.chatFile = null;
1571 this.xmppOverHttp = null;
1572 this.httpFileUploadResource = null;
1573 this.httpEncryptedFileUploadResource = null;
1574 this.httpPubSubFileUploadResource = null;
1575
1576 this.httpServer = null;
1577 }
1578
1579 this.httpFileUploadComponent?.Dispose();
1580 this.httpFileUploadComponent = null;
1581
1582 this.socks5Component?.Dispose();
1583 this.socks5Component = null;
1584
1585 this.serviceRegistrationComponent?.Dispose();
1586 this.serviceRegistrationComponent = null;
1587
1588 this.pubSubComponent?.Dispose();
1589 this.pubSubComponent = null;
1590
1591 this.mucComponent?.Dispose();
1592 this.mucComponent = null;
1593
1594 this.provisioningComponent?.Dispose();
1595 this.provisioningComponent = null;
1596
1597 this.legalComponent?.Dispose();
1598 this.legalComponent = null;
1599
1600 this.eDalerComponent?.Dispose();
1601 this.eDalerComponent = null;
1602
1603 this.eventLogComponent?.Dispose();
1604 this.eventLogComponent = null;
1605
1606 if (!(this.xmppServer is null))
1607 {
1608 this.xmppServer.ClientConnectionAdded -= this.XmppServer_ClientConnectionAdded;
1609 this.xmppServer.ClientConnectionRemoved -= this.XmppServer_ClientConnectionRemoved;
1610 this.xmppServer.ClientConnectionUpdated -= this.XmppServer_ClientConnectionUpdated;
1611 this.xmppServer.ServerConnectionAdded -= this.XmppServer_ServerConnectionAdded;
1612 this.xmppServer.ServerConnectionRemoved -= this.XmppServer_ServerConnectionRemoved;
1613 this.xmppServer.ServerConnectionUpdated -= this.XmppServer_ServerConnectionUpdated;
1614
1615 this.xmppServer.Dispose();
1616 this.xmppServer = null;
1617 }
1618
1619 this.smtpServer?.Dispose();
1620 this.smtpServer = null;
1621
1622 this.chatSessions?.Dispose();
1623 this.chatSessions = null;
1624
1625 this.consolidators?.Dispose();
1626 this.consolidators = null;
1627
1628 this.ssoTokens?.Dispose();
1629 this.ssoTokens = null;
1630
1631 this.jwtFactory?.Dispose();
1632 this.jwtFactory = null;
1633
1634 Log.Informational("XMPP Server shut down.");
1635 }
1636
1637 public static XmppServerModule Instance => instance;
1638 public static XmppServer Server => instance?.xmppServer;
1639 public static SmtpServer MailServer => instance?.smtpServer;
1640 public static Socks5Component Socks5 => instance?.socks5Component;
1641 public static ProvisioningComponent Provisioning => instance?.provisioningComponent;
1642 public static LegalComponent Legal => instance?.legalComponent;
1643 public static EDalerComponent EDaler => instance?.eDalerComponent;
1644 public static PubSubComponent PubSub => instance?.pubSubComponent;
1645 public static MultiUserChatComponent Muc => instance?.mucComponent;
1646 public static ServiceRegistrationComponent ServiceRegistry => instance?.serviceRegistrationComponent;
1647 public static EventLogComponent EventLog => instance?.eventLogComponent;
1648 public static PersistenceLayer PersistenceLayer => instance?.persistenceLayer;
1649
1650 public static string PackagesFolder
1651 {
1652 get { return Gateway.SoftwareUpdateClient?.PackageFolder ?? Path.Combine(Gateway.AppDataFolder, "Packages"); }
1653 }
1654
1655 public static string DownloadsFolder
1656 {
1657 get { return Path.Combine(Gateway.RootFolder, "Downloads"); }
1658 }
1659
1660 public static ApiKey GetApiKey(string Key)
1661 {
1662 ApiKey Result = GetApiKeyAsync(Key).Result
1663 ?? throw new NotFoundException();
1664
1665 return Result;
1666 }
1667
1668 public static async Task<ApiKey> GetApiKeyAsync(string Key)
1669 {
1670 if (string.IsNullOrEmpty(Key))
1671 {
1672 return new ApiKey()
1673 {
1674 // TODO: Add statistics.
1675 };
1676 }
1677 else
1678 {
1679 foreach (ApiKey ApiKey in await Database.Find<ApiKey>(new FilterFieldEqualTo("Key", Key)))
1680 return ApiKey;
1681
1682 return null;
1683 }
1684 }
1685
1686 public static IAccount GetAccount(CaseInsensitiveString UserName)
1687 {
1688 IAccount Result = GetAccountAsync(UserName).Result
1689 ?? throw new NotFoundException();
1690
1691 return Result;
1692 }
1693
1694 public static Task<IAccount> GetAccountAsync(CaseInsensitiveString UserName)
1695 {
1696 if (instance is null)
1697 return GetAccountAsyncNoInit(UserName);
1698 else
1699 return ((IXmppServerPersistenceLayer)instance.persistenceLayer).GetAccount(UserName);
1700 }
1701
1702 private static async Task<IAccount> GetAccountAsyncNoInit(CaseInsensitiveString UserName)
1703 {
1704 foreach (Account Account in await Database.Find<Account>(new FilterFieldEqualTo("UserName", UserName)))
1705 return Account;
1706
1707 return null;
1708 }
1709
1710 public static Task<IRosterItem> GetRosterItemAsync(CaseInsensitiveString UserName, CaseInsensitiveString Jid)
1711 {
1712 if (instance is null)
1713 return GetRosterItemAsyncNoInit(UserName, Jid);
1714 else
1715 return instance.persistenceLayer.GetRosterItem(UserName, Jid);
1716 }
1717
1718 private static async Task<IRosterItem> GetRosterItemAsyncNoInit(CaseInsensitiveString UserName, CaseInsensitiveString Jid)
1719 {
1720 foreach (RosterItem Item in await Database.Find<RosterItem>(new FilterAnd(
1721 new FilterFieldEqualTo("UserName", UserName),
1722 new FilterFieldEqualTo("BareJid", Jid))))
1723 {
1724 return Item;
1725 }
1726
1727 return null;
1728 }
1729
1730 public static Task<IEnumerable<IRosterItem>> GetRosterAsync(CaseInsensitiveString UserName)
1731 {
1732 if (instance is null)
1733 return GetRosterAsyncNoInit(UserName);
1734 else
1735 return instance.persistenceLayer.GetRoster(UserName);
1736 }
1737
1738 private static async Task<IEnumerable<IRosterItem>> GetRosterAsyncNoInit(CaseInsensitiveString UserName)
1739 {
1740 return await Database.Find<RosterItem>(new FilterAnd(
1741 new FilterFieldEqualTo("UserName", UserName)));
1742 }
1743
1744 private static Stopwatch CreateWatch()
1745 {
1746 Stopwatch Watch = new Stopwatch();
1747 Watch.Start();
1748
1749 calibration = Calibrate(Watch);
1750
1751 return Watch;
1752 }
1753
1757 public static void Calibrate()
1758 {
1759 calibration = Calibrate(clock);
1760 }
1761
1762 private static Calibration Calibrate(Stopwatch Clock)
1763 {
1764 DateTime TP = DateTime.Now;
1765 long Ticks = TP.Ticks;
1766 long HF = 0;
1767 int i;
1768
1769 for (i = 0; i < 3; i++) // An extra round, to avoid JIT effects.
1770 {
1771 while ((TP = DateTime.UtcNow).Ticks == Ticks)
1772 ;
1773
1774 HF = Clock.ElapsedTicks;
1775 Ticks = TP.Ticks;
1776 }
1777
1778 return new Calibration()
1779 {
1780 Reference = TP,
1781 ReferenceHfTick = HF,
1782 TicksTo100Ns = 1e7 / Stopwatch.Frequency
1783 };
1784 }
1785
1789 public static DateTimeHF Now
1790 {
1791 get
1792 {
1793 DateTime NowRef = DateTime.UtcNow;
1794 long Ticks = clock.ElapsedTicks;
1795 Calibration Calibration = calibration;
1796
1797 Ticks -= Calibration.ReferenceHfTick;
1798
1799 long Ns100 = (long)(Ticks * Calibration.TicksTo100Ns + 0.5);
1800 long Milliseconds = Ns100 / 10000;
1801
1802 DateTime Now = Calibration.Reference.AddMilliseconds(Milliseconds);
1803
1804 if ((Now - NowRef).TotalSeconds >= 1)
1805 {
1806 Calibrate();
1807 return XmppServerModule.Now;
1808 }
1809 else
1810 {
1811 Ns100 %= 10000;
1812
1813 return new DateTimeHF(Now, (int)(Ns100 / 10), (int)(Ns100 % 10), Ticks);
1814 }
1815 }
1816 }
1817
1818 private async Task DnsRequest(object Sender, IqEventArgs e)
1819 {
1820 try
1821 {
1822 byte[] ReqBin = System.Convert.FromBase64String(e.Query.InnerText);
1823
1824 DnsMessage Msg = new DnsMessage(ReqBin);
1825
1826 foreach (Question Question in Msg.Questions)
1827 {
1829 if (DnsResponse is null)
1830 await e.IqErrorBadRequest(e.To, "Unable to resolve query.", "en");
1831 else
1832 {
1833 byte[] RespBin = (byte[])DnsResponse.Raw.Clone();
1834
1835 RespBin[0] = ReqBin[0]; // Use same ID as in request.
1836 RespBin[1] = ReqBin[1];
1837
1838 StringBuilder Xml = new StringBuilder();
1839
1840 Xml.Append("<dns xmlns='");
1841 Xml.Append(NamespaceDnsOverXmpp);
1842 Xml.Append("'>");
1843 Xml.Append(System.Convert.ToBase64String(RespBin));
1844 Xml.Append("</dns>");
1845
1846 await e.IqResult(Xml.ToString(), e.To);
1847 }
1848 return;
1849 }
1850
1851 await e.IqErrorBadRequest(e.To, "No question in request.", "en");
1852 }
1853 catch (Exception ex)
1854 {
1855 await e.IqError(ex, e.To);
1856 }
1857 }
1858
1859 public static PubSubNode GetPubSubNode(CaseInsensitiveString NodeName)
1860 {
1861 PubSubNode Result = GetPubSubNodeAsync(NodeName).Result
1862 ?? throw new NotFoundException();
1863
1864 return Result;
1865 }
1866
1867 public static Task<PubSubNode> GetPubSubNodeAsync(CaseInsensitiveString NodeName)
1868 {
1869 if (instance is null)
1870 return GetPubSubNodeAsyncNoInit(NodeName);
1871 else
1872 return instance.pubSubComponent.GetNodeAsync(CaseInsensitiveString.Empty, NodeName, null, XmppAddress.Empty, null);
1873 }
1874
1875 private static async Task<PubSubNode> GetPubSubNodeAsyncNoInit(CaseInsensitiveString NodeName)
1876 {
1877 foreach (PubSubNode PubSubNode in await Database.Find<PubSubNode>(new FilterAnd(
1878 new FilterFieldEqualTo("Service", string.Empty), new FilterFieldEqualTo("Name", NodeName))))
1879 {
1880 return PubSubNode;
1881 }
1882
1883 return null;
1884 }
1885
1886 public static IClientConnection[] GetClientConnections()
1887 {
1888 return instance.xmppServer.GetClientConnections();
1889 }
1890
1891 public static IClientConnection GetClientConnection(string FullJid)
1892 {
1893 if (instance.xmppServer.TryGetClientConnection(FullJid, out IClientConnection Result))
1894 return Result;
1895 else
1896 throw new NotFoundException("Client resource not connected: " + FullJid);
1897 }
1898
1899 public static S2sEndpointStatistics[] GetServerConnectionStatistics()
1900 {
1901 return instance.xmppServer.GetServerConnectionStatistics();
1902 }
1903
1904 public static IS2SEndpoint GetS2sConnection(string Domain)
1905 {
1906 if (instance.xmppServer.TryGetS2sEndpoint(Domain, out IS2SEndpoint Result))
1907 return Result;
1908 else
1909 throw new NotFoundException("Domain not connected: " + Domain);
1910 }
1911
1912 public static Task<bool> IsAdmin(CaseInsensitiveString Jid)
1913 {
1914 return IsAdmin(Jid, null);
1915 }
1916
1917 public static async Task<bool> IsAdmin(CaseInsensitiveString Jid, IUser User)
1918 {
1919 Jid = Networking.XMPP.XmppClient.GetBareJID(Jid);
1920 if (!(Gateway.XmppClient is null) && string.Compare(Gateway.XmppClient.BareJID, Jid, true) == 0)
1921 return true;
1922
1924
1925 foreach (CaseInsensitiveString s in Admins)
1926 {
1927 if (string.Compare(s, Jid, true) == 0)
1928 return true;
1929 }
1930
1931 if (User is null)
1932 User = await Users.GetUser(Jid, false);
1933
1934 if (User is null)
1935 return false;
1936
1937 return User.HasPrivilege("Admin");
1938 }
1939
1940 public static string DateTimesToHTML(DateTime Created, DateTime Updated)
1941 {
1942 StringBuilder Result = new StringBuilder();
1943
1944 Created = Created.ToLocalTime();
1945
1946 if (Updated != DateTime.MinValue)
1947 Updated = Updated.ToLocalTime();
1948
1949 Result.Append("<span class=\"ContentItemTimestamp\">");
1950 Result.Append(Created.ToShortDateString());
1951 Result.Append(", ");
1952 Result.Append(Created.ToLongTimeString());
1953 Result.Append("</span>");
1954
1955 if (Updated != Created && Updated != DateTime.MinValue)
1956 {
1957 Result.Append("<span class=\"ContentItemUpdated\">");
1958 Result.Append(Updated.ToShortDateString());
1959 Result.Append(", ");
1960 Result.Append(Updated.ToLongTimeString());
1961 Result.Append("</span>");
1962 }
1963
1964 return Result.ToString();
1965 }
1966
1967 private async Task CheckRegistration()
1968 {
1969 try
1970 {
1971 if (!(Gateway.XmppClient is null) && Gateway.XmppClient.State == Networking.XMPP.XmppState.Connected)
1972 {
1973 if (this.serviceRegistrationClient is null)
1974 this.serviceRegistrationClient = new ServiceRegistrationClient(Gateway.XmppClient, "services.tagroot.io");
1975
1976 await this.serviceRegistrationClient.CheckRegistration();
1977
1978 if (!string.IsNullOrEmpty(Gateway.Domain))
1979 {
1980 string VCardDomain = await RuntimeSettings.GetAsync("VCard.Domain", string.Empty);
1981 if (VCardDomain != Gateway.Domain)
1982 {
1983 // XEP-0054 - vcard-temp: http://xmpp.org/extensions/xep-0054.html
1984 // XEP-0153 - vCard-Based Avatars: http://xmpp.org/extensions/xep-0153.html
1985
1986 StringBuilder Xml = new StringBuilder();
1987 string BareJid = Gateway.XmppClient.BareJID;
1988 byte[] Avatar = Resources.LoadResource(typeof(XmppServerModule).Namespace + ".Graphics.XMPP_logo.png");
1989
1990 Xml.Append("<vCard xmlns='vcard-temp'>");
1991 Xml.Append("<FN>XMPP Broker on ");
1992 Xml.Append(XML.Encode(Gateway.Domain));
1993 Xml.Append("</FN>");
1994 Xml.Append("<URL>");
1995 Xml.Append(Gateway.GetUrl("/"));
1996 Xml.Append("</URL>");
1997 Xml.Append("<JABBERID>");
1998 Xml.Append(XML.Encode(BareJid));
1999 Xml.Append("</JABBERID>");
2000 Xml.Append("<PHOTO><TYPE>image/png</TYPE><BINVAL>");
2001 Xml.Append(System.Convert.ToBase64String(Avatar, Base64FormattingOptions.None));
2002 Xml.Append("</BINVAL></PHOTO>");
2003 Xml.Append("</vCard>");
2004
2005 await Gateway.AvatarClient.UpdateLocalAvatarAsync("image/png", Avatar, 96, 96, true);
2006 await Gateway.XmppClient.IqSetAsync(BareJid, Xml.ToString());
2007 await RuntimeSettings.SetAsync("VCard.Domain", Gateway.Domain);
2008 }
2009 }
2010 return;
2011 }
2012 }
2013 catch (Exception ex)
2014 {
2015 Log.Exception(ex);
2016 }
2017
2018 Gateway.ScheduleEvent((P) => Task.Run(() => this.CheckRegistration()), DateTime.Now.AddMinutes(15), null);
2019 }
2020
2021 private Task XmppServer_ClientConnectionAdded(object Sender, Networking.XMPP.Server.ClientConnectionEventArgs e)
2022 {
2023 return this.connectionsSource?.ClientConnectionAdded(Sender, e) ?? Task.CompletedTask;
2024 }
2025
2026 private Task XmppServer_ClientConnectionRemoved(object Sender, Networking.XMPP.Server.ClientConnectionEventArgs e)
2027 {
2028 return this.connectionsSource?.ClientConnectionRemoved(Sender, e) ?? Task.CompletedTask;
2029 }
2030
2031 private Task XmppServer_ClientConnectionUpdated(object Sender, Networking.XMPP.Server.ClientConnectionEventArgs e)
2032 {
2033 return this.connectionsSource?.ClientConnectionUpdated(Sender, e) ?? Task.CompletedTask;
2034 }
2035
2036 private Task XmppServer_ServerConnectionAdded(object Sender, Networking.XMPP.Server.ServerConnectionEventArgs e)
2037 {
2038 return this.connectionsSource?.ServerConnectionAdded(Sender, e) ?? Task.CompletedTask;
2039 }
2040
2041 private Task XmppServer_ServerConnectionRemoved(object Sender, Networking.XMPP.Server.ServerConnectionEventArgs e)
2042 {
2043 return this.connectionsSource?.ServerConnectionRemoved(Sender, e) ?? Task.CompletedTask;
2044 }
2045
2046 private Task XmppServer_ServerConnectionUpdated(object Sender, Networking.XMPP.Server.ServerConnectionEventArgs e)
2047 {
2048 return this.connectionsSource?.ServerConnectionUpdated(Sender, e) ?? Task.CompletedTask;
2049 }
2050
2051 internal async Task<bool> CheckSoftwarePackage(Networking.XMPP.Software.Package RemotePackage, ResponseCallbackHandler ResponseCallback)
2052 {
2053 try
2054 {
2055 Package LocalPackage = await ProvisioningComponent.GetPackage(RemotePackage.FileName);
2056
2057 if (LocalPackage is null || LocalPackage.Published < RemotePackage.Published)
2058 {
2059 string MessageId = await ResponseCallback("Downloading `" + RemotePackage.FileName + "`", string.Empty);
2060 string FileName = await Gateway.SoftwareUpdateClient.DownloadPackageAsync(RemotePackage);
2061
2062 await this.PackageDownloaded(RemotePackage, Gateway.SoftwareUpdateClient.ComponentAddress, MessageId, ResponseCallback);
2063
2064 return true;
2065 }
2066 }
2067 catch (Exception ex)
2068 {
2069 Log.Exception(ex);
2070 }
2071
2072 return false;
2073 }
2074
2075 private Task SoftwareUpdateClient_OnSoftwareUpdated(object Sender, Networking.XMPP.Software.PackageUpdatedEventArgs e)
2076 {
2077 Log.Informational("New software package available.", PackageTags(e.Package));
2078 e.Download = true;
2079
2080 return Task.CompletedTask;
2081 }
2082
2083 private static KeyValuePair<string, object>[] PackageTags(Networking.XMPP.Software.Package Package)
2084 {
2085 return new KeyValuePair<string, object>[]
2086 {
2087 new KeyValuePair<string, object>("FileName", Package.FileName),
2088 new KeyValuePair<string, object>("Bytes", Package.Bytes),
2089 new KeyValuePair<string, object>("Published", Package.Published),
2090 new KeyValuePair<string, object>("Supersedes", Package.Supersedes),
2091 new KeyValuePair<string, object>("Created", Package.Created),
2092 new KeyValuePair<string, object>("Signature", System.Convert.ToBase64String(Package.Signature ?? new byte[0])),
2093 new KeyValuePair<string, object>("URL", Package.Url)
2094 };
2095 }
2096
2097 private async Task SoftwareUpdateClient_OnSoftwareValidation(object Sender, Networking.XMPP.Software.PackageFileEventArgs e)
2098 {
2099 string FileName = Path.GetFileName(e.LocalFileName);
2100
2101 if (string.Compare(FileName, BrokerPackage.FileName, true) == 0)
2102 {
2103 if (!ValidateIoTBrokerPackage(e.LocalFileName, e.Package.Signature))
2104 throw new Exception("Invalid signature.");
2105 }
2106 else
2107 {
2108 Package Package = await ProvisioningComponent.GetPackage(FileName);
2109 if (!(Package is null) && Package.Installed > DateTime.MinValue && !(Package.PublicKey is null))
2110 {
2111 if (!ValidatePackage(e.LocalFileName, Package.PublicKey, e.Package.Signature))
2112 throw new Exception("Invalid signature.");
2113 }
2114 }
2115 }
2116
2117 internal static bool ValidateIoTBrokerPackage(string FileName, byte[] Signature)
2118 {
2119 Log.Notice("Validating package.", FileName);
2120
2121 using (FileStream f = File.OpenRead(FileName))
2122 {
2123 bool Result = ValidateIoTBrokerPackage(f, Signature);
2124
2125 if (Result)
2126 Log.Informational("Package valid.", FileName);
2127 else
2128 Log.Error("Package not valid.", FileName);
2129
2130 return Result;
2131 }
2132 }
2133
2134 internal static bool ValidatePackage(string FileName, byte[] PublicKey, byte[] Signature)
2135 {
2136 Log.Notice("Validating package.", FileName);
2137
2138 using (FileStream f = File.OpenRead(FileName))
2139 {
2140 bool Result = ValidatePackage(f, PublicKey, Signature);
2141
2142 if (Result)
2143 Log.Informational("Package valid.", FileName);
2144 else
2145 Log.Error("Package not valid.", FileName);
2146
2147 return Result;
2148 }
2149 }
2150
2151 internal static bool ValidateIoTBrokerPackage(Stream Data, byte[] Signature)
2152 {
2153 return ValidatePackage(Data, iotBrokerPackagePublicKey, Signature);
2154 }
2155
2156 internal static bool ValidatePackage(Stream Data, byte[] PublicKey, byte[] Signature)
2157 {
2158 return ed448.Verify(Data, PublicKey, Signature);
2159 }
2160
2161 private Task SoftwareUpdateClient_OnSoftwareDownloaded(object Sender, Networking.XMPP.Software.PackageFileEventArgs e)
2162 {
2163 return this.PackageDownloaded(e.Package, e.From, string.Empty, (Markdown, MessageId) => Task.FromResult(MessageId));
2164 }
2165
2166 private async Task PackageDownloaded(Networking.XMPP.Software.Package DownloadedPackage, string RemoteEndpoint, string MessageId,
2167 ResponseCallbackHandler ResponseCallback)
2168 {
2169 Log.Notice("New software package downloaded.", PackageTags(DownloadedPackage));
2170
2171 await ResponseCallback("Software package downloaded: `" + DownloadedPackage.FileName + "`", MessageId);
2172
2173 DateTime Now = DateTime.UtcNow;
2174 Package Package = await ProvisioningComponent.GetPackage(DownloadedPackage.FileName);
2175
2176 if (Package is null)
2177 {
2178 Package = new Package()
2179 {
2180 FileName = DownloadedPackage.FileName,
2181 Signature = DownloadedPackage.Signature,
2183 Published = Now,
2184 Supersedes = DateTime.MinValue,
2185 Created = Now,
2186 Bytes = DownloadedPackage.Bytes,
2187 AesKey = null,
2188 Installed = DateTime.MinValue,
2189 PublicKey = null,
2190 Downloadable = false
2191 };
2192
2193 await Database.Insert(Package);
2194 }
2195 else
2196 {
2197 Package.Signature = DownloadedPackage.Signature;
2198 Package.RemoteEndpoint = RemoteEndpoint;
2199 Package.Supersedes = Package.Published;
2200 Package.Published = Now;
2201 Package.Bytes = DownloadedPackage.Bytes;
2202
2203 if (!(Package.AesKey is null))
2204 Package.ContentOnly = IsContentPackage(Package);
2205
2206 await Database.Update(Package);
2207 }
2208
2209 if (!(this.provisioningComponent is null))
2210 await this.provisioningComponent.NewPackage(Package);
2211
2212 if (string.Compare(DownloadedPackage.FileName, BrokerPackage.FileName, true) == 0 ||
2213 Package.Installed > DateTime.MinValue)
2214 {
2215 await this.NewSoftwareAvailable(Package, RemoteEndpoint);
2216 }
2217 else if (string.Compare(DownloadedPackage.FileName, Ip2LocalizationPackageName, true) == 0)
2218 Gateway.ScheduleEvent(this.ImportIpLocalizationDatabase, DateTime.Now.AddMinutes(5), null);
2219
2220 // TODO: PlantUML package
2221 // TODO: GraphViz package
2222 }
2223
2224 internal async Task NewSoftwareAvailable(Package DownloadedPackage, string RemoteEndpoint)
2225 {
2226 StringBuilder Markdown = new StringBuilder();
2227 DateTime Now = DateTime.Now;
2228 long DelayMinutes;
2229
2230 if (DownloadedPackage.ContentOnly)
2231 {
2232 bool InstallPackage = await RuntimeSettings.GetAsync(AutoInstallContentOnlyParameterName, false);
2233
2234 if (InstallPackage)
2235 DelayMinutes = 1;
2236 else
2237 DelayMinutes = await RuntimeSettings.GetAsync(AutoInstallDelayParameterName, 0);
2238 }
2239 else
2240 DelayMinutes = await RuntimeSettings.GetAsync(AutoInstallDelayParameterName, 0);
2241
2242 Markdown.AppendLine("New version of installed software has been downloaded");
2243 Markdown.AppendLine("==========================================================");
2244 Markdown.AppendLine();
2245 Markdown.AppendLine("| Package ||");
2246 Markdown.AppendLine("|:------|:-------|");
2247 Markdown.AppendLine("| Filename: | `" + DownloadedPackage.FileName + "` |");
2248 Markdown.AppendLine("| Size: | " + Export.FormatBytes(DownloadedPackage.Bytes) + " |");
2249 Markdown.AppendLine("| From: | `" + RemoteEndpoint + "` |");
2250 Markdown.AppendLine("| Published: | " + MarkdownDocument.Encode(DownloadedPackage.Published.ToString()) + " |");
2251
2252 if (DownloadedPackage.Supersedes != DateTime.MinValue)
2253 Markdown.AppendLine("| Supersedes: | " + MarkdownDocument.Encode(DownloadedPackage.Supersedes.ToString()) + " |");
2254
2255 Markdown.AppendLine("| Signature: | " + System.Convert.ToBase64String(DownloadedPackage.Signature) + " |");
2256 Markdown.AppendLine("| Download: | [" + MarkdownDocument.Encode(DownloadedPackage.FileName) + "](" + DownloadedPackage.Url + ") |");
2257 Markdown.Append("| Date | ");
2258 Markdown.Append(MarkdownDocument.Encode(Now.ToShortDateString()));
2259 Markdown.AppendLine(" |");
2260 Markdown.Append("| Time | ");
2261 Markdown.Append(MarkdownDocument.Encode(Now.ToLongTimeString()));
2262 Markdown.AppendLine(" |");
2263 Markdown.AppendLine();
2264
2265 if (string.Compare(DownloadedPackage.FileName, BrokerPackage.FileName, true) == 0)
2266 {
2267 Markdown.AppendLine("See [Release Notes](https://lab.tagroot.io/ReleaseNotes) for information about what is new.");
2268 Markdown.AppendLine();
2269 }
2270
2271 if (DelayMinutes > 0)
2272 {
2273 if (DownloadedPackage.ContentOnly)
2274 {
2275 try
2276 {
2277 Log.Notice("Updating software.", DownloadedPackage.FileName);
2278
2279 await this.UpdateSoftware(DownloadedPackage, false);
2280
2281 Log.Notice("Software successfully updated.", DownloadedPackage.FileName);
2282
2283 Markdown.AppendLine("The new software has been automatically installed.");
2284 }
2285 catch (Exception ex)
2286 {
2287 Log.Exception(ex, DownloadedPackage.FileName);
2288
2289 Markdown.AppendLine("Unable to install the new software. The following error was reported: ");
2290 Markdown.AppendLine();
2291 Markdown.AppendLine("```");
2292 Markdown.AppendLine(ex.Message.Trim());
2293 Markdown.AppendLine("```");
2294 }
2295 }
2296 else
2297 {
2298 if (autoUpdateTP > DateTime.MinValue)
2299 {
2300 Gateway.CancelScheduledEvent(autoUpdateTP);
2301 autoUpdateTP = DateTime.MinValue;
2302 }
2303
2304 DateTime TP = Now.AddMinutes(DelayMinutes);
2305 autoUpdateTP = Gateway.ScheduleEvent(this.UpdateSoftware, TP, null);
2306
2307 await RuntimeSettings.SetAsync(AutoInstallTimeParameterName, autoUpdateTP);
2308
2309 Markdown.Append("The new software will be automatically installed");
2310
2311 if (DelayMinutes == 1)
2312 Markdown.Append(" in 1 minute");
2313 else if (DelayMinutes <= 60)
2314 Markdown.Append(" in " + DelayMinutes.ToString() + " minutes");
2315 else
2316 {
2317 Markdown.Append(" at ");
2318
2319 if (TP.Date == Now.Date)
2320 Markdown.Append(TP.ToShortTimeString());
2321 else
2322 {
2323 Markdown.Append(TP.ToShortDateString());
2324 Markdown.Append(", ");
2325 Markdown.Append(TP.ToShortTimeString());
2326 }
2327 }
2328
2329 Markdown.AppendLine(", unless cancelled from the chat interface.");
2330 }
2331 }
2332 else
2333 Markdown.AppendLine("You can update the server with the new package from the chat interface.");
2334
2335 await Gateway.SendNotification(Markdown.ToString());
2336 }
2337
2338 internal static bool IsContentPackage(Package Package)
2339 {
2340 try
2341 {
2342 string FileName = Path.Combine(PackagesFolder, Package.FileName);
2343 if (!File.Exists(FileName))
2344 return false;
2345
2346 StringBuilder sb = new StringBuilder();
2347
2348 sb.Append(Package.FileName);
2349 sb.Append(":");
2350
2351 if (!(Package.AesKey is null))
2352 sb.Append(Hashes.BinaryToString(Package.AesKey));
2353
2354 sb.Append(":Waher.Utility.Install");
2355
2356 SHAKE256 H = new SHAKE256(384);
2357 byte[] Digest = H.ComputeVariable(Encoding.UTF8.GetBytes(sb.ToString()));
2358 byte[] AesKey = new byte[32];
2359 byte[] IV = new byte[16];
2360 Aes Aes = null;
2361 FileStream fs = null;
2362 ICryptoTransform AesTransform = null;
2363 CryptoStream Decrypted = null;
2364 GZipStream Decompressed = null;
2365
2366 Array.Copy(Digest, 0, AesKey, 0, 32);
2367 Array.Copy(Digest, 32, IV, 0, 16);
2368
2369 try
2370 {
2371 Aes = Aes.Create();
2372 Aes.BlockSize = 128;
2373 Aes.KeySize = 256;
2374 Aes.Mode = CipherMode.CBC;
2375 Aes.Padding = PaddingMode.Zeros;
2376
2377 fs = File.OpenRead(FileName);
2378 AesTransform = Aes.CreateDecryptor(AesKey, IV);
2379 Decrypted = new CryptoStream(fs, AesTransform, CryptoStreamMode.Read);
2380 Decompressed = new GZipStream(Decrypted, CompressionMode.Decompress);
2381
2382 byte b = ReadByte(Decompressed);
2383 byte[] Bin;
2384
2385 if (b > 0)
2386 {
2387 Bin = new byte[b];
2388 Decompressed.ReadAll(Bin, 0, b);
2389 }
2390
2391 Bin = ReadBin(Decompressed);
2392 if (Encoding.ASCII.GetString(Bin) != "IoTGatewayPackage")
2393 throw new Exception("Invalid package file.");
2394
2395 while ((b = ReadByte(Decompressed)) != 0)
2396 {
2397 string RelativeName = Encoding.UTF8.GetString(ReadBin(Decompressed)); // RelativeName
2398 ReadVarLenUInt(Decompressed); // FileAttributes
2399 ReadVarLenUInt(Decompressed); // CreationTimeUtc
2400 ReadVarLenUInt(Decompressed); // LastAccessTimeUtc
2401 ReadVarLenUInt(Decompressed); // LastWriteTimeUtc
2402 ulong Bytes = ReadVarLenUInt(Decompressed);
2403
2404 switch (b)
2405 {
2406 case 1: // Program file in installation folder, not assembly file
2407 SkipBytes(Decompressed, Bytes);
2408 break;
2409
2410 case 2: // Assembly file
2411 case 5: // External program file
2412 case 6: // External program folder
2413 return false;
2414
2415 case 3: // Content file (copy if newer)
2416 case 4: // Content file (always copy)
2417 SkipBytes(Decompressed, Bytes);
2418 break;
2419
2420 default:
2421 throw new Exception("Invalid package file.");
2422 }
2423 }
2424 }
2425 finally
2426 {
2427 Decompressed?.Dispose();
2428 Decrypted?.Dispose();
2429 AesTransform?.Dispose();
2430 Aes?.Dispose();
2431 fs?.Dispose();
2432 }
2433
2434 return true;
2435 }
2436 catch (Exception ex)
2437 {
2438 Log.Exception(ex);
2439 return false;
2440 }
2441 }
2442
2443 private static byte[] ReadBin(Stream Input)
2444 {
2445 ulong Len = ReadVarLenUInt(Input);
2446 if (Len > int.MaxValue)
2447 throw new Exception("Invalid package.");
2448
2449 int c = (int)Len;
2450 byte[] Result = new byte[c];
2451
2452 Input.ReadAll(Result, 0, c);
2453
2454 return Result;
2455 }
2456
2457 private static ulong ReadVarLenUInt(Stream Input)
2458 {
2459 ulong Len = 0;
2460 int Offset = 0;
2461 byte b;
2462
2463 do
2464 {
2465 b = ReadByte(Input);
2466
2467 Len |= ((ulong)(b & 127)) << Offset;
2468 Offset += 7;
2469 }
2470 while ((b & 0x80) != 0);
2471
2472 return Len;
2473 }
2474
2475 private static byte ReadByte(Stream Input)
2476 {
2477 int i = Input.ReadByte();
2478 if (i < 0)
2479 throw new EndOfStreamException("Reading past end-of-file.");
2480
2481 return (byte)i;
2482 }
2483
2484 private static void SkipBytes(Stream Input, ulong Bytes)
2485 {
2486 int c = 65536;
2487 if (Bytes < (ulong)c)
2488 c = (int)Bytes;
2489
2490 byte[] Buffer = new byte[c];
2491
2492 Input.ReadAll(Buffer, 0, c);
2493 }
2494
2495 internal async void UpdateSoftware(object P)
2496 {
2497 try
2498 {
2499 Package Package = P as Package;
2500 await this.UpdateSoftware(Package, true);
2501 }
2502 catch (Exception ex)
2503 {
2504 Log.Exception(ex);
2505 }
2506 }
2507
2508 internal Task UpdateSoftware(Package Package, bool Backup)
2509 {
2511 {
2512 To = string.Empty,
2513 Folder = PackagesFolder
2514 };
2515
2516 return this.UpdateSoftware(Package, State, Backup);
2517 }
2518
2519 internal Task UpdateSoftware(Package Package, ChatState State, bool Backup)
2520 {
2521 return this.UpdateSoftware(Package, State, Backup, (Msg, MessageId) => Task.FromResult<string>(MessageId));
2522 }
2523
2524 internal async Task UpdateSoftware(Package Package, ChatState State, bool Backup, ResponseCallbackHandler ResponseCallback)
2525 {
2526 string MessageId = string.Empty;
2527
2528 if (Backup && (Package is null || !Package.ContentOnly))
2529 {
2530 MessageId = await ResponseCallback("Performing backup.", MessageId);
2531 await Gateway.DoBackup();
2532 }
2533
2534 StringBuilder sb = new StringBuilder();
2535 Package[] Packages = await ProvisioningComponent.GetPackages();
2536 bool RestartRequired;
2537 string ServerFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + FileSystem.ExecutableExtension);
2538 if (!File.Exists(ServerFileName))
2539 ServerFileName = Path.ChangeExtension(ServerFileName, "dll");
2540
2541 sb.Append("Install -d \"");
2542 sb.Append(Gateway.AppDataFolder.Substring(0, Gateway.AppDataFolder.Length - 1));
2543 sb.Append("\" -s \"");
2544 sb.Append(ServerFileName);
2545 sb.Append('"');
2546
2547 string BrokerPackageFileName = Path.Combine(PackagesFolder, BrokerPackage.FileName);
2548 bool UpdateBroker = false;
2549
2550 if (Package is null || !Package.ContentOnly)
2551 {
2552 autoUpdateTP = DateTime.MinValue;
2553 await RuntimeSettings.SetAsync(AutoInstallTimeParameterName, DateTime.MinValue);
2554
2555 if (!File.Exists(BrokerPackageFileName))
2556 {
2557 await ResponseCallback("`" + BrokerPackage.FileName + "` not found.", string.Empty);
2558 return;
2559 }
2560
2561 UpdateBroker = true;
2562
2563 List<Package> ToInstall = new List<Package>();
2564
2565 foreach (Package Package2 in Packages)
2566 {
2567 if (Package2.Installed == DateTime.MinValue)
2568 continue;
2569
2570 if (Package2.FileName == BrokerPackage.FileName)
2571 continue;
2572
2573 if (Package2.ContentOnly)
2574 continue;
2575
2576 if (Package2.Published > Package2.Installed)
2577 {
2578 ToInstall.Add(Package2);
2579
2580 Package2.Installed = Package2.Published;
2581 await Database.Update(Package2);
2582 }
2583 else if (Package2 == Package)
2584 ToInstall.Add(Package2);
2585 }
2586
2587 Packages = ToInstall.ToArray();
2588 RestartRequired = true;
2589 }
2590 else
2591 {
2592 if (!File.Exists(Path.Combine(PackagesFolder, Package.FileName)))
2593 {
2594 await ResponseCallback("`" + Package.FileName + "` not found.", string.Empty);
2595 return;
2596 }
2597
2599 {
2600 Package.Installed = Package.Published;
2601 await Database.Update(Package);
2602 }
2603
2604 Packages = new Package[] { Package };
2605 RestartRequired = false;
2606
2607 sb.Append(" -co");
2608 }
2609
2610 foreach (Package Package2 in Packages)
2611 {
2612 string PackageFileName = Path.Combine(PackagesFolder, Package2.FileName);
2613
2614 if (File.Exists(PackageFileName))
2615 {
2616 sb.Append(" -p \"");
2617 sb.Append(PackageFileName.Replace("\"", "\\\""));
2618
2619 if (!(Package2.AesKey is null))
2620 {
2621 sb.Append("\" -k \"");
2622 sb.Append(Hashes.BinaryToString(Package2.AesKey));
2623 }
2624
2625 sb.Append('"');
2626 }
2627 }
2628
2629 if (UpdateBroker)
2630 {
2631 sb.Append(" -p \"");
2632 sb.Append(BrokerPackageFileName);
2633 sb.Append("\" -k \"");
2634 sb.Append(BrokerPackage.Key);
2635 sb.Append('"');
2636 }
2637
2638 sb.Append(" -n \"");
2639 sb.Append(Gateway.InstanceName);
2640 sb.Append("\" -w 120000 -v -i > install.log");
2641
2642 MessageId = await ResponseCallback("Starting update process...", MessageId);
2643
2644 if (await this.ExecuteCommand(State.To, sb.ToString(), State, true, ResponseCallback))
2645 {
2646 if (RestartRequired)
2647 {
2648 await ResponseCallback("Stopping service... Update procedure will continue when service has been stopped. Service will then restart using the updated version.", MessageId);
2649
2650 Gateway.ScheduleEvent((P) => Gateway.Terminate(), DateTime.Now.AddSeconds(1), null);
2651 }
2652 else
2653 {
2654 await ResponseCallback("Package installed. No restart required.", MessageId);
2656 }
2657 }
2658 }
2659
2660 internal async Task UninstallSoftware(string PackageFileName, byte[] AesKey)
2661 {
2662 string s = Path.Combine(PackagesFolder, PackageFileName);
2663 if (!File.Exists(s))
2664 return;
2665
2666 string s2 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + FileSystem.ExecutableExtension);
2667 if (!File.Exists(s2))
2668 s2 = s2.Substring(0, s2.Length - 3) + "dll";
2669
2670 StringBuilder sb = new StringBuilder();
2671
2672 sb.Append("Install -p \"");
2673 sb.Append(s);
2674 sb.Append("\" -d \"");
2675 sb.Append(Gateway.AppDataFolder.Substring(0, Gateway.AppDataFolder.Length - 1));
2676 sb.Append("\" -s \"");
2677 sb.Append(s2);
2678
2679 if (!(AesKey is null))
2680 {
2681 sb.Append("\" -k \"");
2682 sb.Append(Hashes.BinaryToString(AesKey));
2683 }
2684
2685 sb.Append('"');
2686
2687 Package Package = await ProvisioningComponent.GetPackage(PackageFileName);
2688 bool RestartRequired = Package is null || !Package.ContentOnly;
2689
2690 if (!RestartRequired)
2691 sb.Append(" -co");
2692
2693 sb.Append(" -n \"");
2694 sb.Append(Gateway.InstanceName);
2695 sb.Append("\" -w 120000 -v -u > install.log");
2696
2698 {
2699 To = string.Empty,
2700 Folder = PackagesFolder
2701 };
2702
2703 await Gateway.DoBackup();
2704
2705 if (await this.ExecuteCommand(string.Empty, sb.ToString(), State, true,
2706 (Msg, MessageId) => Task.FromResult<string>(MessageId)) && RestartRequired)
2707 {
2708 Gateway.ScheduleEvent((P) => Gateway.Terminate(), DateTime.Now.AddSeconds(1), null);
2709 }
2710 }
2711
2712 private Task SoftwareUpdateClient_OnSoftwareDeleted(object Sender, Networking.XMPP.Software.PackageDeletedEventArgs e)
2713 {
2714 Log.Informational("Software package deleted on server.", PackageTags(e.Package));
2715 e.Delete = true;
2716
2717 return Task.CompletedTask;
2718 }
2719
2720 private async Task SoftwareUpdateClient_OnDownloadedSoftwareDeleted(object Sender, Networking.XMPP.Software.PackageFileEventArgs e)
2721 {
2722 Log.Notice("Local copy of software package deleted.", PackageTags(e.Package));
2723
2724 Package OldPackage = await ProvisioningComponent.GetPackage(e.Package.FileName);
2725
2726 if (!(OldPackage is null))
2727 {
2728 await Database.Delete(OldPackage);
2729
2730 if (!(this.provisioningComponent is null))
2731 await this.provisioningComponent.PackageDeleted(OldPackage);
2732 }
2733 }
2734
2735 private async Task<(string, string, bool)> GetPlainText(Networking.XMPP.Events.MessageEventArgs e, DateTime OfflineThreshold)
2736 {
2737 string s = e.Body?.Trim();
2738
2739 string ReplaceObjectId = string.Empty;
2740 bool IsMarkdown = false;
2741
2742 foreach (XmlNode N in e.Message.ChildNodes)
2743 {
2744 if (N is XmlElement E)
2745 {
2746 switch (E.LocalName)
2747 {
2748 case "content":
2749 if (E.NamespaceURI == "urn:xmpp:content" &&
2750 string.Compare(XML.Attribute(E, "type"), MarkdownCodec.ContentType, true) == 0)
2751 {
2752 if (string.IsNullOrEmpty(s))
2753 {
2754 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(E.InnerText.Trim());
2755 s = await Doc.GeneratePlainText();
2756 }
2757
2758 IsMarkdown = true;
2759 }
2760 break;
2761
2762 case "delay":
2763 if (E.NamespaceURI == XmppServer.DelayedDeliveryNamespace &&
2764 E.HasAttribute("stamp") &&
2765 XML.TryParse(E.GetAttribute("stamp"), out DateTime Timestamp) &&
2766 Timestamp.ToUniversalTime() < OfflineThreshold.ToUniversalTime())
2767 {
2768 return (string.Empty, string.Empty, false);
2769 }
2770 break;
2771
2772 case "replace":
2773 if (E.NamespaceURI == "urn:xmpp:message-correct:0")
2774 ReplaceObjectId = XML.Attribute(E, "id");
2775 break;
2776 }
2777 }
2778 }
2779
2780 return (s, ReplaceObjectId, IsMarkdown);
2781
2782 }
2783
2784 private string GetMarkdown(Networking.XMPP.Events.MessageEventArgs e, DateTime OfflineThreshold, bool EncodeIfText, out string ReplaceObjectId, out bool IsMarkdown)
2785 {
2786 string s = e.Body?.Trim();
2787
2788 ReplaceObjectId = string.Empty;
2789 IsMarkdown = false;
2790
2791 foreach (XmlNode N in e.Message.ChildNodes)
2792 {
2793 if (N is XmlElement E)
2794 {
2795 switch (E.LocalName)
2796 {
2797 case "content":
2798 if (E.NamespaceURI == "urn:xmpp:content" &&
2799 string.Compare(XML.Attribute(E, "type"), MarkdownCodec.ContentType, true) == 0)
2800 {
2801 s = E.InnerText.Trim();
2802 IsMarkdown = true;
2803 }
2804 break;
2805
2806 case "delay":
2807 if (E.NamespaceURI == XmppServer.DelayedDeliveryNamespace &&
2808 E.HasAttribute("stamp") &&
2809 XML.TryParse(E.GetAttribute("stamp"), out DateTime Timestamp) &&
2810 Timestamp.ToUniversalTime() < OfflineThreshold.ToUniversalTime())
2811 {
2812 return string.Empty;
2813 }
2814 break;
2815
2816 case "replace":
2817 if (E.NamespaceURI == "urn:xmpp:message-correct:0")
2818 ReplaceObjectId = XML.Attribute(E, "id");
2819 break;
2820 }
2821 }
2822 }
2823
2824 return EncodeIfText ? MarkdownDocument.Encode(s) : s;
2825 }
2826
2827 private async Task XmppClient_OnChatMessage(object Sender, Networking.XMPP.Events.MessageEventArgs e)
2828 {
2829 (string Message, string _, bool IsMarkdown) = await this.GetPlainText(e, DateTime.UtcNow.AddMinutes(-1));
2830
2831 Task _ = Task.Run(async () => await this.ProcessChatMessage(Message, e.From, e.ThreadID, async (Response, MessageId) =>
2832 {
2833 KeyValuePair<string, string> P = await this.PrepareAdminResponse(Response, MessageId, IsMarkdown);
2834 string Xml = P.Key;
2835 MessageId = P.Value;
2836
2837 if (Xml is null)
2838 Xml = "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
2839 else
2840 Xml += "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
2841
2842 await Gateway.XmppClient.SendMessage(Networking.XMPP.QoSLevel.Unacknowledged, Networking.XMPP.MessageType.Chat,
2843 MessageId, e.From, Xml, string.Empty, string.Empty, string.Empty, e.ThreadID, string.Empty, null, null);
2844
2845 return MessageId;
2846 }, Gateway.XmppClient));
2847 }
2848
2849 private Task XmppClient_OnValidateSender(object Sender, Networking.XMPP.Events.ValidateSenderEventArgs e)
2850 {
2851 if (e.MessageStanza is null || !e.Rejected)
2852 return Task.CompletedTask;
2853
2854 switch (e.MessageStanza.Type)
2855 {
2856 case Networking.XMPP.MessageType.GroupChat:
2857 bool InRoom;
2858
2859 lock (this.mucRooms)
2860 {
2861 InRoom = this.mucRooms.ContainsKey(e.FromBareJID);
2862 }
2863
2864 if (InRoom)
2865 e.Accept();
2866 else
2867 e.Reject();
2868
2869 break;
2870
2871 case Networking.XMPP.MessageType.Normal:
2872 case Networking.XMPP.MessageType.Chat:
2873 foreach (XmlNode N in e.MessageStanza.Message.ChildNodes)
2874 {
2875 if (!(N is XmlElement E))
2876 continue;
2877
2878 switch (E.NamespaceURI)
2879 {
2880 case Networking.XMPP.MUC.MultiUserChatClient.NamespaceMuc:
2881 case Networking.XMPP.MUC.MultiUserChatClient.NamespaceMucAdmin:
2882 case Networking.XMPP.MUC.MultiUserChatClient.NamespaceMucOwner:
2883 case Networking.XMPP.MUC.MultiUserChatClient.NamespaceMucUser:
2884 lock (this.mucRooms)
2885 {
2886 InRoom = this.mucRooms.ContainsKey(e.FromBareJID);
2887 }
2888
2889 if (InRoom)
2890 e.Accept();
2891 else
2892 e.Reject();
2893 return Task.CompletedTask;
2894 }
2895 }
2896 break;
2897 }
2898
2899 return Task.CompletedTask;
2900 }
2901
2902 private async Task MucClient_DirectInvitationReceived(object Sender, Networking.XMPP.MUC.DirectInvitationMessageEventArgs e)
2903 {
2904 if (await IsAdmin(e.From))
2905 {
2906 StringBuilder sb = new StringBuilder();
2907 string NickName = Gateway.Domain;
2908
2909 if (string.IsNullOrEmpty(NickName))
2911
2912 sb.Append("Accepting an invitation from `");
2913 sb.Append(e.FromBareJID);
2914 sb.Append("` to join the room `");
2915 sb.Append(e.RoomId);
2916 sb.Append('@');
2917 sb.Append(e.Domain);
2918 sb.Append("` using nick-name `");
2919 sb.Append(NickName);
2920 sb.Append("`.");
2921
2922 if (!string.IsNullOrEmpty(e.Reason))
2923 {
2924 sb.AppendLine();
2925 sb.Append(e.Reason);
2926 }
2927
2928 await Gateway.SendNotification(sb.ToString());
2929
2930 await Enter.Execute(e.RoomId, e.Domain, NickName, e.Password, true, true, (Markdown, MessageId) =>
2931 {
2932 if (string.IsNullOrEmpty(MessageId))
2933 MessageId = Guid.NewGuid().ToString();
2934
2935 Gateway.SendNotification(Markdown, MessageId);
2936 return Task.FromResult<string>(MessageId);
2937 });
2938 }
2939 }
2940
2941 private Task MucClient_RoomInvitationReceived(object Sender, Networking.XMPP.MUC.RoomInvitationMessageEventArgs e)
2942 {
2943 e.Decline("Only accepting direct invitations.", "en");
2944 return Task.CompletedTask;
2945 }
2946
2947 internal async Task EnterRoom(string RoomId, string Domain, string NickName, string Password, bool Permanent)
2948 {
2949 RoomInfo Info = new RoomInfo(RoomId, Domain, NickName, Password, Permanent);
2950 string Jid = RoomId + "@" + Domain;
2951 bool Changed;
2952
2953 lock (this.mucRooms)
2954 {
2955 Changed = (!this.mucRooms.TryGetValue(Jid, out RoomInfo PrevInfo)) || (PrevInfo.Permanent ^ Permanent);
2956 this.mucRooms[Jid] = Info;
2957 }
2958
2959 if (Changed)
2960 await this.UpdatePermamentRooms();
2961 }
2962
2963 internal bool TryGetNickName(string RoomId, string Domain, out string NickName)
2964 {
2965 string Jid = RoomId + "@" + Domain;
2966
2967 lock (this.mucRooms)
2968 {
2969 if (this.mucRooms.TryGetValue(Jid, out RoomInfo RoomInfo))
2970 {
2971 NickName = RoomInfo.NickName;
2972 return true;
2973 }
2974 else
2975 {
2976 NickName = string.Empty;
2977 return false;
2978 }
2979 }
2980 }
2981
2982 internal bool TryGetDomainAndNickName(string RoomId, out string Domain, out string NickName)
2983 {
2984 RoomInfo Result = null;
2985
2986 lock (this.mucRooms)
2987 {
2988 foreach (RoomInfo RoomInfo in this.mucRooms.Values)
2989 {
2990 if (string.Compare(RoomInfo.RoomId, RoomId, true) == 0)
2991 {
2992 if (Result is null)
2993 Result = RoomInfo;
2994 else
2995 {
2996 Domain = NickName = null;
2997 return false;
2998 }
2999 }
3000 }
3001 }
3002
3003 Domain = Result?.Domain;
3004 NickName = Result?.NickName;
3005
3006 return !(Result is null);
3007 }
3008
3009 internal async Task LeaveRoom(string RoomId, string Domain)
3010 {
3011 string Jid = RoomId + "@" + Domain;
3012 bool Changed;
3013
3014 lock (this.mucRooms)
3015 {
3016 Changed = this.mucRooms.TryGetValue(Jid, out RoomInfo Info) && Info.Permanent;
3017 this.mucRooms.Remove(Jid);
3018 }
3019
3020 if (Changed)
3021 await this.UpdatePermamentRooms();
3022 }
3023
3024 private async Task UpdatePermamentRooms()
3025 {
3026 StringBuilder sb = new StringBuilder();
3027
3028 lock (this.mucRooms)
3029 {
3030 foreach (RoomInfo Rec in this.mucRooms.Values)
3031 {
3032 if (Rec.Permanent)
3033 {
3034 sb.Append(Rec.RoomId);
3035 sb.Append(" | ");
3036 sb.Append(Rec.Domain);
3037 sb.Append(" | ");
3038 sb.Append(Rec.NickName);
3039
3040 if (!string.IsNullOrEmpty(Rec.Password))
3041 {
3042 sb.Append(" | ");
3043 sb.Append(Rec.Password);
3044 }
3045
3046 sb.AppendLine();
3047 }
3048 }
3049 }
3050
3051 await RuntimeSettings.SetAsync("MUC.Rooms", sb.ToString().Trim());
3052 }
3053
3054 private async Task LoadPermanentRooms()
3055 {
3056 string s = await RuntimeSettings.GetAsync("MUC.Rooms", string.Empty);
3057 string[] Rows = s.Split(CommonTypes.CRLF, StringSplitOptions.RemoveEmptyEntries);
3058
3059 lock (this.adminCommands)
3060 {
3061 foreach (string Row in Rows)
3062 {
3063 string[] Parts = Row.Split(new string[] { " | " }, StringSplitOptions.None);
3064
3065 switch (Parts.Length)
3066 {
3067 case 3:
3068 this.mucRooms[Parts[0] + "@" + Parts[1]] = new RoomInfo(Parts[0], Parts[1], Parts[2], true);
3069 break;
3070
3071 case 4:
3072 this.mucRooms[Parts[0] + "@" + Parts[1]] = new RoomInfo(Parts[0], Parts[1], Parts[2], Parts[3], true);
3073 break;
3074 }
3075 }
3076 }
3077
3078 if (Gateway.XmppClient.State == Networking.XMPP.XmppState.Connected)
3079 await this.RejoinRooms();
3080
3081 Gateway.ScheduleEvent((P) => this.CheckRoomsAlive(), DateTime.Now.AddMinutes(1), null);
3082 }
3083
3084 internal RoomInfo[] GetRooms()
3085 {
3086 RoomInfo[] Infos;
3087
3088 lock (this.mucRooms)
3089 {
3090 Infos = new RoomInfo[this.mucRooms.Count];
3091 this.mucRooms.Values.CopyTo(Infos, 0);
3092 }
3093
3094 return Infos;
3095 }
3096
3097 private async Task RejoinRooms()
3098 {
3099 foreach (RoomInfo Info in this.GetRooms())
3100 await this.RejoinRoom(Info);
3101 }
3102
3103 private Task RejoinRoom(RoomInfo Info)
3104 {
3105 return Gateway.MucClient?.EnterRoom(Info.RoomId, Info.Domain, Info.NickName, Info.Password, (Sender, e) =>
3106 {
3107 if (e.Ok)
3108 Gateway.MucClient.SetPresence(Info.RoomId, Info.Domain, Info.NickName, Networking.XMPP.Availability.Chat, null, null);
3109 else
3110 Gateway.ScheduleEvent((P) => this.RejoinRoom(Info), DateTime.Now.AddMinutes(1), null);
3111
3112 return Task.CompletedTask;
3113
3114 }, null) ?? Task.CompletedTask;
3115 }
3116
3117 internal void CheckRoomsAlive()
3118 {
3119 try
3120 {
3121 if (Gateway.XmppClient.State == Networking.XMPP.XmppState.Connected)
3122 {
3123 RoomInfo[] Infos = this.GetRooms();
3124
3125 foreach (RoomInfo Info in Infos)
3126 {
3127 Gateway.MucClient.SelfPing(Info.RoomId, Info.Domain, Info.NickName, (Sender, e) =>
3128 {
3129 if (!e.Ok)
3130 {
3131 if (e.StanzaError is Networking.XMPP.StanzaErrors.NotAcceptableException) // Need to reconnect with room.
3132 {
3133 RoomInfo Info2 = (RoomInfo)e.State;
3134 Gateway.MucClient.EnterRoom(Info2.RoomId, Info2.Domain, Info2.NickName, Info2.Password, async (sender2, e2) =>
3135 {
3136 if (e2.Ok)
3137 await Gateway.MucClient.SetPresence(Info2.RoomId, Info2.Domain, Info2.NickName, Networking.XMPP.Availability.Chat, null, null);
3138 else
3139 {
3140 RoomInfo Info3 = (RoomInfo)e2.State;
3141
3142 if (e2.StanzaError is Networking.XMPP.StanzaErrors.ForbiddenException) // Blocked
3143 {
3144 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3145 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; forbidden entry (blocked?).");
3146 }
3147 else if (e2.StanzaError is Networking.XMPP.StanzaErrors.NotAuthorizedException) // Password changed, too many failed attempts
3148 {
3149 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3150 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; password changed.");
3151 }
3152 else if (e2.StanzaError is Networking.XMPP.StanzaErrors.RegistrationRequiredException) // Not registered in room
3153 {
3154 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3155 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; no longer registered in room.");
3156 }
3157 else if (e2.StanzaError is Networking.XMPP.StanzaErrors.ItemNotFoundException) // Room not found/locked
3158 {
3159 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3160 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; room not found, or is locked for other purposes.");
3161 }
3162 else if (e2.StanzaError is Networking.XMPP.StanzaErrors.ServiceUnavailableException) // Room full
3163 {
3164 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3165 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; room is full.");
3166 }
3167 else if (e2.StanzaError is Networking.XMPP.StanzaErrors.ConflictException) // Nick-name conflict
3168 {
3169 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3170 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; nick-name conflict.");
3171 }
3172 }
3173 }, Info2);
3174 }
3175 }
3176
3177 return Task.CompletedTask;
3178 }, Info);
3179 }
3180 }
3181 }
3182 catch (Exception ex)
3183 {
3184 Log.Exception(ex);
3185 }
3186 finally
3187 {
3188 Gateway.ScheduleEvent((P) => this.CheckRoomsAlive(), DateTime.Now.AddMinutes(1), null);
3189 }
3190 }
3191
3192 private async Task LeaveAllRooms()
3193 {
3194 RoomInfo[] Rooms = this.GetRooms();
3195 int i, c = Rooms.Length;
3196 TaskCompletionSource<bool>[] TaskCompletionSources = new TaskCompletionSource<bool>[c];
3197 Task[] Tasks = new Task[c];
3198
3199 for (i = 0; i < c; i++)
3200 {
3201 RoomInfo Info = Rooms[i];
3202 TaskCompletionSources[i] = new TaskCompletionSource<bool>();
3203 Tasks[i] = TaskCompletionSources[i].Task;
3204
3205 await Gateway.MucClient.SetPresence(Info.RoomId, Info.Domain, Info.NickName, Networking.XMPP.Availability.Offline,
3206 (_, e) =>
3207 {
3208 ((TaskCompletionSource<bool>)e.State).TrySetResult(true);
3209 return Task.CompletedTask;
3210 }, TaskCompletionSources[i]);
3211 }
3212
3213 _ = Task.Delay(5000).ContinueWith((_) =>
3214 {
3215 foreach (TaskCompletionSource<bool> T in TaskCompletionSources)
3216 T.TrySetResult(false);
3217 return Task.CompletedTask;
3218 });
3219
3220 await Task.WhenAll(Tasks);
3221 }
3222
3223 private async Task XmppClient_OnGroupChatMessage(object Sender, Networking.XMPP.Events.MessageEventArgs e)
3224 {
3225 XmppAddress Addr = new XmppAddress(e.From);
3226 string RoomId = Addr.Account;
3227 string Domain = Addr.Domain;
3228 string NickName = Addr.Resource;
3229 (string Message, string _, bool IsMarkdown) = await this.GetPlainText(e, DateTime.UtcNow.AddMinutes(-1));
3230
3231 Task _ = Task.Run(async () => await this.ProcessChatMessage(Message, this.GetOccupantRealJid(e), e.ThreadID, async (Response, MessageId) =>
3232 {
3233 KeyValuePair<string, string> P = await this.PrepareAdminResponse(Response, MessageId, IsMarkdown);
3234 string Xml = P.Key;
3235 MessageId = P.Value;
3236
3237 if (Xml is null)
3238 Xml = "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
3239 else
3240 Xml += "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
3241
3242 await Gateway.MucClient.SendCustomPrivateMessage(MessageId, RoomId, Domain, NickName, Xml, string.Empty, e.ThreadID, string.Empty);
3243
3244 return MessageId;
3245 }, Gateway.XmppClient));
3246 }
3247
3248 internal void RegisterConsolidator(string ThreadId, IConsolidator Consolidator)
3249 {
3250 if (this.consolidators is null)
3251 {
3252 this.consolidators = new Cache<string, IConsolidator>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromSeconds(30));
3253 this.consolidators.Removed += this.Consolidators_Removed;
3254 }
3255
3256 this.consolidators[ThreadId] = Consolidator;
3257 }
3258
3259 private Task Consolidators_Removed(object Sender, CacheItemEventArgs<string, IConsolidator> e)
3260 {
3261 e.Value.Dispose();
3262 return Task.CompletedTask;
3263 }
3264
3265 private async Task MucClient_PrivateMessageReceived(object Sender, Networking.XMPP.MUC.RoomOccupantMessageEventArgs e)
3266 {
3267 if (!string.IsNullOrEmpty(e.ThreadID) &&
3268 !(this.consolidators is null) &&
3269 this.consolidators.TryGetValue(e.ThreadID, out IConsolidator Consolidator))
3270 {
3271 string Response = this.GetMarkdown(e, DateTime.UtcNow.AddMinutes(-1), false, out string ReplaceObjectId, out bool IsMarkdown);
3272 MarkdownDocument Doc = null;
3273
3275 {
3276 if (!string.IsNullOrEmpty(e.Body))
3277 Response = e.Body;
3278 else
3279 Doc = await MarkdownDocument.CreateAsync(Response);
3280 }
3281
3282 if (Doc is null)
3283 {
3284 if (!string.IsNullOrEmpty(ReplaceObjectId))
3285 await Consolidator.Update(e.NickName, Response, ReplaceObjectId);
3286 else
3287 await Consolidator.Add(e.NickName, Response, e.Id);
3288 }
3289 else
3290 {
3291 if (!string.IsNullOrEmpty(ReplaceObjectId))
3292 await Consolidator.Update(e.NickName, Doc, ReplaceObjectId);
3293 else
3294 await Consolidator.Add(e.NickName, Doc, e.Id);
3295 }
3296 }
3297 else
3298 {
3299 (string Message, string _, bool IsMarkdown) = await this.GetPlainText(e, DateTime.UtcNow.AddMinutes(-1));
3300
3301 Task _ = Task.Run(async () => await this.ProcessChatMessage(Message, this.GetOccupantRealJid(e), e.ThreadID, async (Response, MessageId) =>
3302 {
3303 KeyValuePair<string, string> P = await this.PrepareAdminResponse(Response, MessageId, IsMarkdown);
3304 string Xml = P.Key;
3305 MessageId = P.Value;
3306
3307 if (Xml is null)
3308 Xml = "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
3309 else
3310 Xml += "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
3311
3312 await Gateway.MucClient.SendCustomPrivateMessage(MessageId, e.RoomId, e.Domain, e.NickName, Xml, string.Empty, e.ThreadID, string.Empty);
3313
3314 return MessageId;
3315 }, Gateway.XmppClient));
3316 }
3317 }
3318
3319 private async Task<KeyValuePair<string, string>> PrepareAdminResponse(string Response, string MessageId, bool IncomingWasMarkdown)
3320 {
3321 string Xml;
3322
3323 if (IncomingWasMarkdown)
3324 Xml = await Gateway.GetMultiFormatChatMessageXml(Response, false, false);
3325 else
3326 Xml = await Gateway.GetMultiFormatChatMessageXml(Response, true, true);
3327
3328 if (string.IsNullOrEmpty(MessageId))
3329 MessageId = Guid.NewGuid().ToString();
3330 else
3331 Xml += "<replace id='" + MessageId + "' xmlns='urn:xmpp:message-correct:0'/>";
3332
3333 return new KeyValuePair<string, string>(Xml, MessageId);
3334 }
3335
3336 private Task MucClient_RoomDestroyed(object Sender, Networking.XMPP.MUC.UserPresenceEventArgs e)
3337 {
3338 lock (this.presenceByNickAndRoom)
3339 {
3340 this.presenceByNickAndRoom.Remove(e.RoomJid);
3341 }
3342
3343 return Task.CompletedTask;
3344 }
3345
3346 private Task MucClient_OccupantPresence(object Sender, Networking.XMPP.MUC.UserPresenceEventArgs e)
3347 {
3348 lock (this.presenceByNickAndRoom)
3349 {
3350 if (!this.presenceByNickAndRoom.TryGetValue(e.RoomJid, out SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs> ByNick))
3351 {
3352 if (e.Availability == Networking.XMPP.Availability.Offline)
3353 ByNick = null;
3354 else
3355 {
3356 ByNick = new SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>();
3357 this.presenceByNickAndRoom[e.RoomJid] = ByNick;
3358 }
3359 }
3360
3361 if (!(ByNick is null))
3362 {
3363 if (e.Availability == Networking.XMPP.Availability.Offline)
3364 {
3365 if (ByNick.Remove(e.NickName) && ByNick.Count == 0)
3366 this.presenceByNickAndRoom.Remove(e.RoomId);
3367 }
3368 else
3369 ByNick[e.NickName] = e;
3370 }
3371 }
3372
3373 return Task.CompletedTask;
3374 }
3375
3376 internal int GetNrOccupants(string RoomJid, string RoomId, string Domain, bool ExcludeSelf, bool ExcludeOffline)
3377 {
3378 string OwnNick;
3379
3380 if (ExcludeSelf)
3381 {
3382 if (!this.TryGetNickName(RoomId, Domain, out OwnNick))
3383 OwnNick = null;
3384 }
3385 else
3386 OwnNick = null;
3387
3388 lock (this.presenceByNickAndRoom)
3389 {
3390 if (this.presenceByNickAndRoom.TryGetValue(RoomJid, out SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs> ByNick))
3391 {
3392 if (!ExcludeSelf && !ExcludeOffline)
3393 return ByNick.Count;
3394
3395 int c = 0;
3396
3397 foreach (Networking.XMPP.MUC.UserPresenceEventArgs e in ByNick.Values)
3398 {
3399 if (ExcludeOffline && !e.IsOnline)
3400 continue;
3401
3402 if (ExcludeSelf && !(OwnNick is null) && Networking.XMPP.XmppClient.GetResource(e.From) == OwnNick)
3403 continue;
3404
3405 c++;
3406 }
3407
3408 return c;
3409 }
3410 else
3411 return 0;
3412 }
3413 }
3414
3415 internal KeyValuePair<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>[] GetOccupants(string RoomJid)
3416 {
3417 lock (this.presenceByNickAndRoom)
3418 {
3419 if (!this.presenceByNickAndRoom.TryGetValue(RoomJid, out SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs> ByNick))
3420 return null;
3421
3422 KeyValuePair<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>[] Result =
3423 new KeyValuePair<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>[ByNick.Count];
3424
3425 ByNick.CopyTo(Result, 0);
3426
3427 return Result;
3428 }
3429 }
3430
3431 private string GetOccupantRealJid(Networking.XMPP.Events.MessageEventArgs e)
3432 {
3433 foreach (XmlNode N in e.Message.ChildNodes)
3434 {
3435 if (N is XmlElement E && E.LocalName == "addresses" && E.NamespaceURI == XmppServer.ExtendedAddressingNamespace)
3436 {
3437 foreach (XmlNode N2 in E.ChildNodes)
3438 {
3439 if (N2 is XmlElement E2 &&
3440 E2.LocalName == "address" &&
3441 E2.NamespaceURI == E.NamespaceURI &&
3442 E2.HasAttribute("type") &&
3443 E2.GetAttribute("type") == "ofrom")
3444 {
3445 return XML.Attribute(E2, "jid");
3446 }
3447 }
3448 }
3449 }
3450
3451 string OccupantJid = e.From;
3452 XmppAddress Addr = new XmppAddress(OccupantJid);
3453 CaseInsensitiveString RoomJid = Addr.BareJid;
3454 CaseInsensitiveString NickName = Addr.Resource;
3455
3456 lock (this.presenceByNickAndRoom)
3457 {
3458 if (this.presenceByNickAndRoom.TryGetValue(RoomJid, out SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs> ByNick) &&
3459 ByNick.TryGetValue(NickName, out Networking.XMPP.MUC.UserPresenceEventArgs e2))
3460 {
3461 return string.IsNullOrEmpty(e2.FullJid) ? OccupantJid : e2.FullJid;
3462 }
3463 }
3464
3465 return OccupantJid;
3466 }
3467
3468 internal Task ProcessChatMessage(string s, string From, string ThreadId, ResponseCallbackHandler ResponseCallback, ICommunicationLayer Channel)
3469 {
3470 return this.ProcessChatMessage(s, From, null, ThreadId, ResponseCallback, Channel);
3471 }
3472
3473 internal async Task ProcessChatMessage(string s, string From, IUser User, string ThreadId, ResponseCallbackHandler ResponseCallback, ICommunicationLayer Channel)
3474 {
3475 StringBuilder Markdown;
3476
3477 try
3478 {
3479 if (string.IsNullOrEmpty(s))
3480 {
3481 if (!(Channel is null))
3482 await Channel.Information("(Ignored, since empty)");
3483
3484 return;
3485 }
3486
3487 if (From == Gateway.XmppClient.FullJID)
3488 {
3489 if (string.IsNullOrEmpty(ThreadId) ||
3490 this.consolidators is null ||
3491 !this.consolidators.ContainsKey(ThreadId))
3492 {
3493 string Msg = "Supressing query from self. No outstanding consolidation query registered: " + s;
3494
3495 if (!(Channel is null))
3496 await Channel.Warning(Msg);
3497
3498 Log.Warning(Msg, Gateway.Domain, From);
3499 return;
3500 }
3501 }
3502 else if (!await IsAdmin(From, User))
3503 {
3504 string Msg = "Ignoring incoming chat message (sender is not an administrator): " + s;
3505
3506 if (!(Channel is null))
3507 await Channel.Warning(Msg);
3508
3509 Log.Warning(Msg, Gateway.Domain, From);
3510 return;
3511 }
3512
3513 if (!(Channel is null))
3514 await Channel.Information("Chat command string accepted.");
3515
3516 if (this.chatSessions is null)
3517 {
3518 this.chatSessions = new Cache<string, Variables>(1000, TimeSpan.MaxValue, TimeSpan.FromMinutes(15), true);
3519 this.chatSessions.Removed += this.ChatSessions_Removed;
3520 }
3521
3522 if (!this.chatSessions.TryGetValue(From, out Variables Session))
3523 {
3524 Session = HttpServer.CreateVariables();
3525 this.chatSessions[From] = Session;
3526 }
3527
3528 if (!Session.TryGetVariable(" State ", out Variable v) || !(v.ValueObject is ChatState State))
3529 {
3530 State = new ChatState(Session)
3531 {
3532 To = From,
3533 Folder = PackagesFolder
3534 };
3535 Session[" State "] = State;
3536 }
3537
3538 StringBuilder sb = new StringBuilder();
3539 List<string> ArgumentsList = new List<string>();
3540 LinkedList<IAdminCommand> Commands;
3542 object Details;
3543 string[] Arguments;
3544 string CommandName = null;
3545 int c = 0;
3546 bool InQuote = false;
3547 bool Escape = false;
3548
3549 foreach (char ch in s)
3550 {
3551 if (char.IsWhiteSpace(ch) && !InQuote)
3552 {
3553 if (c > 0)
3554 {
3555 if (CommandName is null)
3556 CommandName = sb.ToString().ToLower();
3557 else
3558 ArgumentsList.Add(sb.ToString());
3559
3560 sb.Clear();
3561 c = 0;
3562 }
3563 }
3564 else if (Escape)
3565 {
3566 sb.Append(ch);
3567 c++;
3568 Escape = false;
3569 }
3570 else if (ch == '\\')
3571 Escape = true;
3572 else if (ch == '"')
3573 InQuote = !InQuote;
3574 else
3575 {
3576 sb.Append(ch);
3577 c++;
3578 }
3579 }
3580
3581 if (Escape)
3582 {
3583 sb.Append('\\');
3584 c++;
3585 }
3586
3587 if (c > 0)
3588 {
3589 if (CommandName is null)
3590 CommandName = sb.ToString().ToLower();
3591 else
3592 ArgumentsList.Add(sb.ToString());
3593 }
3594
3595 if (CommandName is null)
3596 {
3597 if (!(Channel is null))
3598 await Channel.Information("(Ignored, no command)");
3599
3600 return;
3601 }
3602
3603 Arguments = ArgumentsList.ToArray();
3604
3605 lock (this.adminCommands)
3606 {
3607 if (this.adminCommands.Count == 0)
3608 {
3609 foreach (Type T in Types.GetTypesImplementingInterface(typeof(IAdminCommand)))
3610 {
3611 try
3612 {
3613 TypeInfo TI = T.GetTypeInfo();
3614
3615 if (TI.IsAbstract || TI.IsInterface || TI.IsGenericTypeDefinition)
3616 continue;
3617
3619
3620 string s2 = Command.Name.ToLower();
3621 if (!this.adminCommands.TryGetValue(s2, out Commands))
3622 {
3623 Commands = new LinkedList<IAdminCommand>();
3624 this.adminCommands[s2] = Commands;
3625 }
3626
3627 Commands.AddLast(Command);
3628
3629 string[] Aliases = Command.Aliases;
3630
3631 if (!(Aliases is null))
3632 {
3633 foreach (string Alias in Aliases)
3634 {
3635 s2 = Alias.ToLower();
3636 if (!this.adminCommands.TryGetValue(s2, out Commands))
3637 {
3638 Commands = new LinkedList<IAdminCommand>();
3639 this.adminCommands[s2] = Commands;
3640 }
3641
3642 Commands.AddLast(Command);
3643 }
3644 }
3645 }
3646 catch (Exception ex)
3647 {
3648 Log.Exception(ex, T.FullName);
3649 }
3650 }
3651 }
3652
3653 Commands = null;
3654 Command = null;
3655 Details = null;
3656
3657 if (this.adminCommands.TryGetValue(CommandName, out Commands))
3658 {
3659 foreach (IAdminCommand Alternative in Commands)
3660 {
3661 if (Alternative.AppliesTo(s, Arguments, out Details))
3662 {
3663 Command = Alternative;
3664 break;
3665 }
3666 }
3667 }
3668 }
3669
3670 if (Command is null)
3671 {
3672 if (Arguments.Length == 0 && !(Commands is null))
3673 await this.ProcessChatMessage("help " + s, From, User, ThreadId, ResponseCallback, Channel);
3674 else if (State.CommandMode)
3675 {
3676 foreach (string Row in s.Split(CommonTypes.CRLF, StringSplitOptions.RemoveEmptyEntries))
3677 await this.ExecuteCommand(From, Row.Trim(), State, false, ResponseCallback);
3678 }
3679 else
3680 {
3681 StringBuilder PrintOutput = new StringBuilder();
3682 StringWriter PrintWriter = new StringWriter(PrintOutput);
3683 Session.ConsoleOut = PrintWriter;
3684 Expression Exp = new Expression(s);
3685 IElement Result;
3686 AdminScriptRec Rec = new AdminScriptRec()
3687 {
3688 ResponseCallback = ResponseCallback
3689 };
3690
3691 Task Preview(object Sender, PreviewEventArgs e)
3692 {
3693 return this.ReturnScriptResult(e.Preview, Rec);
3694 };
3695
3696 Session.OnPreview += Preview;
3697 try
3698 {
3699 Result = await Exp.Root.EvaluateAsync(Session);
3700 }
3702 {
3703 Result = ex.ReturnValue;
3704 }
3705 catch (Exception ex)
3706 {
3707 Result = new ObjectValue(ex);
3708 }
3709 finally
3710 {
3711 Session.OnPreview -= Preview;
3712 }
3713
3714 Session["Ans"] = Result;
3715
3716 string Printed = PrintOutput.ToString().Trim();
3717 PrintOutput.Clear();
3718
3719 if (!string.IsNullOrEmpty(Printed))
3720 await SendPrinted(Printed, string.Empty, ResponseCallback);
3721
3722 await this.ReturnScriptResult(Result, Rec);
3723 }
3724 }
3725 else
3726 await Command.Execute(State, ArgumentsList.ToArray(), s, Details, ResponseCallback);
3727 }
3728 catch (Exception ex)
3729 {
3730 try
3731 {
3732 ex = Log.UnnestException(ex);
3733
3734 Markdown = new StringBuilder();
3735
3736 Markdown.AppendLine("An error occurred when processing request:");
3737 Markdown.AppendLine();
3738 Markdown.AppendLine("<font class=\"error\">");
3739 Markdown.AppendLine();
3740 Markdown.AppendLine(MarkdownDocument.Encode(ex.Message));
3741 Markdown.AppendLine();
3742 Markdown.AppendLine("```");
3743 Markdown.AppendLine(Log.CleanStackTrace(ex.StackTrace));
3744 Markdown.AppendLine("```");
3745 Markdown.AppendLine();
3746 Markdown.AppendLine("</font>");
3747
3748 await ResponseCallback(Markdown.ToString(), string.Empty);
3749 }
3750 catch (Exception ex2)
3751 {
3752 Log.Exception(ex2);
3753 }
3754 }
3755 }
3756
3757 private class AdminScriptRec
3758 {
3759 public string MessageId = string.Empty;
3760 public ResponseCallbackHandler ResponseCallback;
3761 }
3762
3763 private async Task ReturnScriptResult(IElement Result, AdminScriptRec Rec)
3764 {
3765 StringBuilder sb = new StringBuilder();
3766
3768 Result = ToMatrix.ToMatrix();
3769
3770 if (Result is Graph G)
3771 {
3772 sb.AppendLine("```Graph");
3773 G.ToXml(sb);
3774 sb.AppendLine();
3775 sb.AppendLine("```");
3776
3777 Rec.MessageId = await Rec.ResponseCallback(sb.ToString(), Rec.MessageId);
3778 }
3779 else if (Result.AssociatedObjectValue is SKImage Img)
3780 {
3781 SKData Data = Img.Encode(SKEncodedImageFormat.Png, 100);
3782 byte[] Bin = Data.ToArray();
3783 Data.Dispose();
3784
3785 sb.Append("![Image result](data:image/png;base64,");
3786 sb.Append(System.Convert.ToBase64String(Bin, 0, Bin.Length));
3787 sb.Append(')');
3788
3789 Rec.MessageId = await Rec.ResponseCallback(sb.ToString(), Rec.MessageId);
3790 }
3791 else if (Result.AssociatedObjectValue is Exception ex)
3792 {
3793 ex = Log.UnnestException(ex);
3794
3795 if (ex is AggregateException ex2)
3796 {
3797 sb.Clear();
3798
3799 foreach (Exception ex3 in ex2.InnerExceptions)
3800 {
3801 sb.AppendLine(ex3.Message);
3802 sb.AppendLine();
3803 }
3804
3805 Rec.MessageId = await SendErrorMessage(MarkdownDocument.Encode(sb.ToString()), Rec.MessageId, Rec.ResponseCallback);
3806 }
3807 else
3808 Rec.MessageId = await SendErrorMessage(MarkdownDocument.Encode(ex.Message), Rec.MessageId, Rec.ResponseCallback);
3809 }
3810 else if (Result.AssociatedObjectValue is ObjectMatrix M && !(M.ColumnNames is null))
3811 {
3812 sb.Clear();
3813
3814 foreach (string s3 in M.ColumnNames)
3815 {
3816 sb.Append("| ");
3817 sb.Append(MarkdownDocument.Encode(s3));
3818 sb.Append(' ');
3819 }
3820
3821 sb.AppendLine("|");
3822
3823 foreach (string _ in M.ColumnNames)
3824 sb.Append("|---");
3825
3826 sb.AppendLine("|");
3827
3828 int x, y;
3829
3830 for (y = 0; y < M.Rows; y++)
3831 {
3832 for (x = 0; x < M.Columns; x++)
3833 {
3834 sb.Append("| ");
3835
3836 object Item = M.GetElement(x, y).AssociatedObjectValue;
3837 if (!(Item is null))
3838 {
3839 if (Item is string s3)
3840 s3 = MarkdownDocument.Encode(s3);
3841 else if (Item is MarkdownElement Element)
3842 s3 = Element.ToString();
3843 else
3845
3846 s3 = s3.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "<br/>");
3847 sb.Append(s3);
3848 sb.Append(' ');
3849 }
3850 }
3851
3852 sb.AppendLine("|");
3853 }
3854
3855 Rec.MessageId = await Rec.ResponseCallback(sb.ToString(), Rec.MessageId);
3856 }
3857 else
3858 Rec.MessageId = await SendResult(Expression.ToString(Result), Rec.MessageId, Rec.ResponseCallback);
3859 }
3860
3861 internal bool TryGetAdminCommand(string CommandName, out LinkedList<IAdminCommand> Command)
3862 {
3863 lock (this.adminCommands)
3864 {
3865 return this.adminCommands.TryGetValue(CommandName, out Command);
3866 }
3867 }
3868
3869 internal string[] AdminCommands
3870 {
3871 get
3872 {
3873 string[] Result;
3874
3875 lock (this.adminCommands)
3876 {
3877 Result = new string[this.adminCommands.Count];
3878 this.adminCommands.Keys.CopyTo(Result, 0);
3879 }
3880
3881 return Result;
3882 }
3883 }
3884
3885 internal static async Task SendPrinted(string Msg, string MessageId, ResponseCallbackHandler ResponseCallback)
3886 {
3887 string PrintColor = Theme.CurrentTheme.LinkColorUnvisited.ToString();
3888
3889 foreach (string Row in Msg.Replace("\r\n", "\n").Replace('\r', '\n').Split('\n'))
3890 await ResponseCallback("<font style=\"color:" + PrintColor + "\">" + Row + "</font>", MessageId);
3891 }
3892
3893 internal static Task<string> SendResult(string Msg, string MessageId, ResponseCallbackHandler ResponseCallback)
3894 {
3895 StringBuilder sb = new StringBuilder();
3896
3897 if (Msg.IndexOfAny(CommonTypes.CRLF) < 0)
3898 {
3899 sb.Append("<font class=\"result\">`");
3900 sb.Append(Msg);
3901 sb.Append("`</font>");
3902 }
3903 else
3904 {
3905 sb.AppendLine("<font class=\"result\">");
3906 sb.AppendLine();
3907 sb.AppendLine("```");
3908
3909
3910 Msg = Msg.Replace("\r\n", "\n").Replace("\r", "\n");
3911 foreach (string Row in Msg.Split('\n'))
3912 sb.AppendLine(Row);
3913
3914 sb.AppendLine("```");
3915 sb.AppendLine();
3916 sb.AppendLine("</font>");
3917 }
3918
3919 return ResponseCallback(sb.ToString(), MessageId);
3920 }
3921
3922 internal static Task<string> SendErrorMessage(string Msg, string MessageId, ResponseCallbackHandler ResponseCallback)
3923 {
3924 StringBuilder sb = new StringBuilder();
3925
3926 sb.AppendLine("<font class=\"error\">");
3927 sb.AppendLine();
3928 sb.AppendLine(Msg);
3929 sb.AppendLine();
3930 sb.AppendLine("</font>");
3931
3932 return ResponseCallback(sb.ToString(), MessageId);
3933 }
3934
3935 internal async Task<bool> ExecuteCommand(string To, string Command, ChatState State, bool Shell, ResponseCallbackHandler ResponseCallback)
3936 {
3937 if (string.IsNullOrEmpty(Command))
3938 return false;
3939
3940 int AppDataLen = Gateway.AppDataFolder.Length;
3941 bool Exists;
3942 string s;
3943
3944 try
3945 {
3946 s = Path.GetFullPath(Path.Combine(State.Folder, Command));
3947 Exists = File.Exists(s);
3948 }
3949 catch (Exception)
3950 {
3951 Exists = false;
3952 s = null;
3953 }
3954
3955 if (Exists)
3956 {
3957 if (s.Length < AppDataLen || string.Compare(Gateway.AppDataFolder, s.Substring(0, AppDataLen), true) != 0)
3958 {
3959 await SendErrorMessage("Access to files outside of the application data folder not permitted.", string.Empty, ResponseCallback);
3960 return false;
3961 }
3962
3963 if (!string.IsNullOrEmpty(To))
3964 await ResponseCallback(this.DownloadableFile(s, State, To), string.Empty);
3965 return true;
3966 }
3967
3968 int i = Command.IndexOfAny(new char[] { ' ', '\t' });
3969
3970 if (i < 0)
3971 {
3972 s = Command;
3973 i = Command.Length;
3974 }
3975 else
3976 s = Command.Substring(0, i);
3977
3978 switch (s.ToLower())
3979 {
3980 case "analyzeclock":
3981 case "csp":
3982 case "exstat":
3983 case "extract":
3984 case "install":
3985 case "regex":
3986 case "sign":
3987 case "transform":
3988
3989 if (Shell && string.Compare(s, "install", true) == 0)
3990 s = Path.Combine(Path.GetDirectoryName(typeof(Gateway).Assembly.Location), "InstallUtility", "Waher.Utility." + s + FileSystem.ExecutableExtension);
3991 else
3992 s = Path.Combine(Path.GetDirectoryName(typeof(Gateway).Assembly.Location), "Waher.Utility." + s + FileSystem.ExecutableExtension);
3993
3994 if (File.Exists(s))
3995 {
3996 Command = "\"" + s + "\"" + Command.Substring(i);
3997 break;
3998 }
3999
4000 s = s.Substring(0, s.Length - 3) + "dll";
4001 if (File.Exists(s))
4002 {
4003 Command = "dotnet \"" + s + "\"" + Command.Substring(i);
4004 break;
4005 }
4006
4007 await SendErrorMessage("File not found: `" + s.Substring(0, s.Length - 3) + "exe`. Unable to execute command.", string.Empty, ResponseCallback);
4008 return false;
4009
4010 // Windows commands
4011 case "bcdedit":
4012 case "call":
4013 case "cmd":
4014 case "del":
4015 case "diskpart":
4016 case "doskey":
4017 case "erase":
4018 case "format":
4019 case "fsutil":
4020 case "label":
4021 case "md":
4022 case "mkdir":
4023 case "mode":
4024 case "move":
4025 case "openfiles":
4026 case "rd":
4027 case "ren":
4028 case "rename":
4029 case "replace":
4030 case "rmdir":
4031 case "robocopy":
4032 case "sc":
4033 case "schtasks":
4034 case "start":
4035 case "taskkill":
4036 case "xcopy":
4037 case "wmic":
4038 case "telnet":
4039 case "icacls":
4040 case "runas":
4041 case "notepad":
4042 case "cl":
4043 case "python":
4044 case "perl":
4045
4046 // macOS commands
4047 // Linux commands
4048 case "bash":
4049 case "zh":
4050 case "zsh":
4051 case "source":
4052 case "rm":
4053 case "diskutil":
4054 case "fdisk":
4055 case "alias":
4056 case "erasedisk":
4057 case "mkfs":
4058 case "e2label":
4059 case "stty":
4060 case "setterm":
4061 case "mv":
4062 case "lsof":
4063 case "rsync":
4064 case "launchctl":
4065 case "systemctl":
4066 case "crontab":
4067 case "launchd":
4068 case "open":
4069 case "xdg-open":
4070 case "kill":
4071 case "dd":
4072 case "chmod":
4073 case "chown":
4074 case "sudo":
4075 case "su":
4076 case "killall":
4077 case "ssh":
4078 case "wget":
4079 case "curl":
4080 case "tar":
4081 case "unzip":
4082 case "zip":
4083 case "nano":
4084 case "vi":
4085 case "vim":
4086 case "emacs":
4087 case "script":
4088 case "screen":
4089 case "tmux":
4090 case "nohup":
4091
4092 await SendErrorMessage("Command not allowed.", string.Empty, ResponseCallback);
4093 // TODO: Secure with provisioning on a command level.
4094 return false;
4095
4096 case "cd.":
4097 return ChangeDirectory(State, ".", ResponseCallback);
4098
4099 case "cd..":
4100 return ChangeDirectory(State, "..", ResponseCallback);
4101
4102 case "cd":
4103 case "chdir":
4104 return ChangeDirectory(State, Command.Substring(s.Length).Trim(), ResponseCallback);
4105
4106 case "dir":
4107 case "ls":
4108 try
4109 {
4110 string Pattern = Command.Substring(s.Length).Trim();
4111 if (string.IsNullOrEmpty(Pattern))
4112 Pattern = "*.*";
4113
4114 DirectoryInfo Folder = new DirectoryInfo(State.Folder);
4115 DirectoryInfo[] Folders;
4116 System.IO.FileInfo[] Files;
4117 StringBuilder Markdown = new StringBuilder();
4118 Dictionary<string, long> Sizes = new Dictionary<string, long>();
4119 Dictionary<string, DateTime> DateTimes = new Dictionary<string, DateTime>();
4120 string MaxSizeFileName = null;
4121 string MaxDateTimeFileName = null;
4122 DateTime MaxDateTime = DateTime.MinValue;
4123 long MaxSize = int.MinValue;
4124 DateTime TP;
4125 bool IsMax;
4126
4127 Markdown.AppendLine("| Name | Size | Date | Time |");
4128 Markdown.AppendLine("|:-----|-----:|:----:|:----:|");
4129
4130 Folders = Folder.GetDirectories(Pattern, SearchOption.TopDirectoryOnly);
4131
4132 foreach (DirectoryInfo DirInfo in Folders)
4133 {
4134 try
4135 {
4136 DateTimes[DirInfo.Name] = DirInfo.LastWriteTime;
4137 if (DirInfo.LastWriteTime > MaxDateTime)
4138 {
4139 MaxDateTime = DirInfo.LastWriteTime;
4140 MaxDateTimeFileName = DirInfo.Name;
4141 }
4142 }
4143 catch (Exception)
4144 {
4145 // Ignore.
4146 }
4147 }
4148
4149 foreach (DirectoryInfo DirInfo in Folders)
4150 {
4151 Markdown.Append("| ");
4152 Markdown.Append(MarkdownDocument.Encode(Path.GetFileName(DirInfo.Name)));
4153 Markdown.Append(" | \\<DIR\\> | ");
4154
4155 if (DateTimes.TryGetValue(DirInfo.Name, out TP))
4156 {
4157 if (IsMax = (DirInfo.Name == MaxDateTimeFileName))
4158 Markdown.Append("**");
4159
4160 Markdown.Append(TP.ToShortDateString());
4161
4162 if (IsMax)
4163 Markdown.Append("**");
4164
4165 Markdown.Append(" | ");
4166
4167 if (IsMax)
4168 Markdown.Append("**");
4169
4170 Markdown.Append(TP.ToLongTimeString());
4171
4172 if (IsMax)
4173 Markdown.Append("**");
4174
4175 Markdown.AppendLine(" |");
4176 }
4177 else
4178 Markdown.AppendLine("N/A ||");
4179 }
4180
4181 Files = Folder.GetFiles(Pattern, SearchOption.TopDirectoryOnly);
4182 DateTimes.Clear();
4183 MaxDateTime = DateTime.MinValue;
4184
4185 foreach (System.IO.FileInfo FileInfo in Files)
4186 {
4187 try
4188 {
4189 Sizes[FileInfo.Name] = FileInfo.Length;
4190 if (FileInfo.Length > MaxSize)
4191 {
4192 MaxSize = FileInfo.Length;
4193 MaxSizeFileName = FileInfo.Name;
4194 }
4195 }
4196 catch (Exception)
4197 {
4198 // Ignore.
4199 }
4200
4201 try
4202 {
4203 DateTimes[FileInfo.Name] = FileInfo.LastWriteTime;
4204 if (FileInfo.LastWriteTime > MaxDateTime)
4205 {
4206 MaxDateTime = FileInfo.LastWriteTime;
4207 MaxDateTimeFileName = FileInfo.Name;
4208 }
4209 }
4210 catch (Exception)
4211 {
4212 // Ignore.
4213 }
4214 }
4215
4216 foreach (System.IO.FileInfo FileInfo in Files)
4217 {
4218 Markdown.Append("| ");
4219 Markdown.Append(this.DownloadableFile(FileInfo.Name, State, To));
4220 Markdown.Append(" | ");
4221
4222 if (Sizes.TryGetValue(FileInfo.Name, out long Size))
4223 {
4224 if (IsMax = (FileInfo.Name == MaxSizeFileName))
4225 Markdown.Append("**");
4226
4227 Markdown.Append(Export.FormatBytes(Size));
4228
4229 if (IsMax)
4230 Markdown.Append("**");
4231 }
4232 else
4233 Markdown.Append("N/A");
4234
4235 Markdown.Append(" | ");
4236
4237 if (DateTimes.TryGetValue(FileInfo.Name, out TP))
4238 {
4239 if (IsMax = (FileInfo.Name == MaxDateTimeFileName))
4240 Markdown.Append("**");
4241
4242 Markdown.Append(TP.ToShortDateString());
4243
4244 if (IsMax)
4245 Markdown.Append("**");
4246
4247 Markdown.Append(" | ");
4248
4249 if (IsMax)
4250 Markdown.Append("**");
4251
4252 Markdown.Append(TP.ToLongTimeString());
4253
4254 if (IsMax)
4255 Markdown.Append("**");
4256
4257 Markdown.AppendLine(" |");
4258 }
4259 else
4260 Markdown.AppendLine("N/A ||");
4261 }
4262
4263 await ResponseCallback(Markdown.ToString(), string.Empty);
4264 return true;
4265 }
4266 catch (Exception)
4267 {
4268 // Run as normal command.
4269 }
4270 break;
4271
4272 default:
4273 if (s.StartsWith("cd.") || s.StartsWith("cd" + Path.DirectorySeparatorChar))
4274 return ChangeDirectory(State, Command.Substring(2).Trim(), ResponseCallback);
4275
4276 if (File.Exists(s))
4277 {
4278 await SendErrorMessage("Executing files not allowed.", string.Empty, ResponseCallback);
4279 return false;
4280 }
4281
4282 // TODO: Secure with provisioning on a command level.
4283 break;
4284 }
4285
4286 ProcessStartInfo StartInfo;
4287
4288 switch (Environment.OSVersion.Platform)
4289 {
4290 case PlatformID.Win32S:
4291 case PlatformID.Win32Windows:
4292 case PlatformID.Win32NT:
4293 case PlatformID.WinCE:
4294 StartInfo = new ProcessStartInfo()
4295 {
4296 FileName = "cmd.exe",
4297 Arguments = "/S /C \" " + Command.Trim() + " \"",
4298 WorkingDirectory = State.Folder
4299 };
4300 break;
4301
4302 case PlatformID.Unix:
4303 case PlatformID.MacOSX:
4304 StartInfo = new ProcessStartInfo()
4305 {
4306 FileName = "/bin/zsh",
4307 Arguments = "-c \" " + Command.Trim() + " \"",
4308 WorkingDirectory = State.Folder
4309 };
4310 break;
4311
4312 default:
4313 await SendErrorMessage("Not supported on this operating system.", string.Empty, ResponseCallback);
4314 return false;
4315 }
4316
4317 if (Shell)
4318 {
4319 StartInfo.UseShellExecute = true;
4320 StartInfo.WindowStyle = ProcessWindowStyle.Normal;
4321 }
4322 else
4323 {
4324 StartInfo.CreateNoWindow = true;
4325 StartInfo.ErrorDialog = false;
4326 StartInfo.RedirectStandardInput = false;
4327 StartInfo.RedirectStandardError = true;
4328 StartInfo.RedirectStandardOutput = true;
4329 StartInfo.UseShellExecute = false;
4330 StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
4331 }
4332
4333 Process P = new Process()
4334 {
4335 StartInfo = StartInfo
4336 };
4337
4338 if (Shell)
4339 {
4340 P.Start();
4341 return true;
4342 }
4343
4344 Thread T = new Thread((e) =>
4345 {
4346 try
4347 {
4348 try
4349 {
4350 LinkedListNode<Process> ProcessNode = State.Processes.AddLast(P);
4351
4352 P.Start();
4353
4354 do
4355 {
4356 bool Output = false;
4357
4358 s = P.StandardOutput.ReadToEnd().Trim();
4359
4360 if (!string.IsNullOrEmpty(s))
4361 {
4362 StringBuilder Markdown = new StringBuilder();
4363
4364 Markdown.AppendLine("```");
4365 Markdown.AppendLine(s.Trim());
4366 Markdown.AppendLine("```");
4367
4368 ResponseCallback(Markdown.ToString(), string.Empty);
4369 Output = true;
4370 }
4371
4372 s = P.StandardError.ReadToEnd().Trim();
4373 if (!string.IsNullOrEmpty(s))
4374 {
4375 StringBuilder Markdown = new StringBuilder();
4376
4377 Markdown.AppendLine("<font class=\"error\">");
4378 Markdown.AppendLine();
4379 Markdown.AppendLine("```");
4380 Markdown.AppendLine(s.Trim());
4381 Markdown.AppendLine("```");
4382 Markdown.AppendLine();
4383 Markdown.AppendLine("</font>");
4384
4385 ResponseCallback(Markdown.ToString(), string.Empty);
4386 Output = true;
4387 }
4388
4389 if (!Output)
4390 ResponseCallback("The command executed with no response.", string.Empty);
4391 }
4392 while (!P.HasExited);
4393
4394 State.Processes.Remove(ProcessNode);
4395 }
4396 finally
4397 {
4398 P.Dispose();
4399 }
4400 }
4401 catch (Exception ex)
4402 {
4403 Log.Exception(ex);
4404 }
4405 })
4406 {
4407 IsBackground = true,
4408 Name = "CMD Thread",
4409 Priority = ThreadPriority.BelowNormal
4410 };
4411
4412 T.Start();
4413
4414 return true;
4415 }
4416
4417 internal string DownloadableFile(string s, ChatState State, string To)
4418 {
4419 if (!File.Exists(s))
4420 return MarkdownDocument.Encode(s);
4421
4424
4425 using (FileStream fs = File.Open(s, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
4426 {
4427 State.Files[Token] = FileInfo = new Admin.FileInfo()
4428 {
4429 Path = s,
4430 FileName = Path.GetFileName(s),
4431 ContentType = InternetContent.GetContentType(Path.GetExtension(s)),
4432 ETag = this.packages.ComputeETag(fs)
4433 };
4434 }
4435
4436 string Url = Gateway.GetUrl("/ChatFile/" + HttpUtility.UrlEncode(FileInfo.FileName) +
4437 "?jid=" + HttpUtility.UrlEncode(To) + "&t=" + Token);
4438
4439 return "<a href=\"" + XML.HtmlAttributeEncode(Url) + "\">" + MarkdownDocument.Encode(FileInfo.FileName) + "</a>";
4440 }
4441
4442 private static bool ChangeDirectory(ChatState State, string NewFolder, ResponseCallbackHandler ResponseCallback)
4443 {
4444 string s = Path.GetFullPath(Path.Combine(State.Folder, NewFolder)) + Path.DirectorySeparatorChar;
4445 int AppDataLen = Gateway.AppDataFolder.Length;
4446
4447 if (s.Length < AppDataLen || string.Compare(Gateway.AppDataFolder, s.Substring(0, AppDataLen), true) != 0)
4448 {
4449 SendErrorMessage("Access to folders outside of the application data folder not permitted.", string.Empty, ResponseCallback);
4450 return false;
4451 }
4452 else if (!Directory.Exists(s))
4453 {
4454 SendErrorMessage("Folder does not exist", string.Empty, ResponseCallback);
4455 return false;
4456 }
4457 else
4458 {
4459 State.Folder = s;
4460 ResponseCallback("New folder is `" + s + "`", string.Empty);
4461 return true;
4462 }
4463 }
4464
4465 private async Task ChatFileDownload(HttpRequest Request, HttpResponse Response)
4466 {
4467 if (!Request.Header.TryGetQueryParameter("jid", out string Jid) ||
4468 !Request.Header.TryGetQueryParameter("t", out string Token))
4469 {
4470 throw new BadRequestException("Bad request.");
4471 }
4472
4473 if (this.chatSessions is null || !this.chatSessions.TryGetValue(HttpUtility.UrlDecode(Jid), out Variables Session))
4474 throw new NotFoundException("Session has expired.");
4475
4476 if (!Session.TryGetVariable(" State ", out Variable v) ||
4477 !(v.ValueObject is ChatState State) ||
4478 !State.Files.TryGetValue(Token, out Admin.FileInfo Rec))
4479 {
4480 throw new NotFoundException("Token not recognized.");
4481 }
4482
4483 if (Rec.FileName != HttpUtility.UrlDecode(Request.SubPath.Substring(1)))
4484 throw new BadRequestException("Bad request.");
4485
4486 if (string.Compare(Path.GetExtension(Rec.FileName), ".xml", true) == 0)
4487 {
4488 XmlReaderSettings ReaderSettings = new XmlReaderSettings()
4489 {
4490 Async = true,
4491 CheckCharacters = false,
4492 ConformanceLevel = ConformanceLevel.Document,
4493 DtdProcessing = DtdProcessing.Ignore,
4494 IgnoreComments = false,
4495 IgnoreProcessingInstructions = false,
4496 IgnoreWhitespace = true,
4497 ValidationFlags = System.Xml.Schema.XmlSchemaValidationFlags.None
4498 };
4499 XmlWriterSettings WriterSettings = new XmlWriterSettings()
4500 {
4501 Async = true,
4502 CheckCharacters = false,
4503 CloseOutput = false,
4504 ConformanceLevel = ConformanceLevel.Document,
4505 Encoding = Encoding.UTF8,
4506 Indent = true,
4507 IndentChars = "\t",
4508 NamespaceHandling = NamespaceHandling.OmitDuplicates,
4509 NewLineChars = "\r\n",
4510 NewLineHandling = NewLineHandling.Replace,
4511 NewLineOnAttributes = false,
4512 OmitXmlDeclaration = false,
4513 WriteEndDocumentOnClose = true
4514 };
4515
4516 using (FileStream fs = File.Open(Rec.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
4517 {
4518 using (XmlReader r = XmlReader.Create(fs, ReaderSettings))
4519 {
4520 TemporaryFile TempFile = new TemporaryFile();
4521 try
4522 {
4523 using (XmlWriter w = XmlWriter.Create(TempFile, WriterSettings))
4524 {
4525 while (await r.ReadAsync())
4526 {
4527 switch (r.NodeType)
4528 {
4529 case XmlNodeType.XmlDeclaration:
4530 // Managed by XmlWriter
4531 break;
4532
4533 case XmlNodeType.ProcessingInstruction:
4534 string Url = r.Value;
4535 string s;
4536 int i, j;
4537
4538 if ((i = Url.IndexOf(s = Gateway.RootFolder, StringComparison.CurrentCultureIgnoreCase)) >= 0 ||
4539 (i = Url.IndexOf(s = Gateway.AppDataFolder, StringComparison.CurrentCultureIgnoreCase)) >= 0)
4540 {
4541 Url = Url.Remove(i, s.Length - 1);
4542
4543 j = Url.IndexOf('"', i);
4544 if (j >= 0)
4545 Url = Url.Remove(i, j - i).Insert(i, Url.Substring(i, j - i).Replace(Path.DirectorySeparatorChar, '/'));
4546 }
4547
4548 await w.WriteProcessingInstructionAsync(r.Name, Url);
4549 break;
4550
4551 case XmlNodeType.Attribute:
4552 await w.WriteAttributeStringAsync(r.Prefix, r.LocalName, r.NamespaceURI, r.Value);
4553 break;
4554
4555 case XmlNodeType.CDATA:
4556 await w.WriteCDataAsync(r.Value);
4557 break;
4558
4559 case XmlNodeType.Comment:
4560 await w.WriteCommentAsync(r.Value);
4561 break;
4562
4563 case XmlNodeType.EntityReference:
4564 await w.WriteEntityRefAsync(r.Name);
4565 break;
4566
4567 case XmlNodeType.Text:
4568 case XmlNodeType.SignificantWhitespace:
4569 await w.WriteStringAsync(r.Value);
4570 break;
4571
4572 case XmlNodeType.Whitespace:
4573 // Ignore
4574 break;
4575
4576 case XmlNodeType.Element:
4577 await w.WriteStartElementAsync(r.Prefix, r.LocalName, r.NamespaceURI);
4578
4579 bool IsEmpty = r.IsEmptyElement;
4580
4581 if (r.HasAttributes && r.MoveToFirstAttribute())
4582 {
4583 await w.WriteAttributeStringAsync(r.Prefix, r.LocalName, r.NamespaceURI, r.Value);
4584
4585 while (r.MoveToNextAttribute())
4586 await w.WriteAttributeStringAsync(r.Prefix, r.LocalName, r.NamespaceURI, r.Value);
4587 }
4588
4589 if (IsEmpty)
4590 w.WriteEndElement();
4591 break;
4592
4593 case XmlNodeType.EndElement:
4594 await w.WriteEndElementAsync();
4595 break;
4596
4597 case XmlNodeType.Document:
4598 case XmlNodeType.DocumentFragment:
4599 case XmlNodeType.DocumentType:
4600 case XmlNodeType.EndEntity:
4601 case XmlNodeType.Entity:
4602 case XmlNodeType.Notation:
4603 break;
4604 }
4605 }
4606 }
4607 }
4608 catch (XmlException)
4609 {
4610 // Ignore
4611 }
4612 catch (Exception ex)
4613 {
4614 TempFile.Dispose();
4615 ExceptionDispatchInfo.Capture(ex).Throw();
4616 }
4617
4618 Response.OnResponseSent += (Sender, e) =>
4619 {
4620 TempFile.Dispose();
4621 return Task.CompletedTask;
4622 };
4623
4624 DateTime LastWriteTime = File.GetLastWriteTime(Rec.Path);
4625 await HttpFolderResource.SendResponse(TempFile.FileName, Rec.ContentType, Rec.ETag, LastWriteTime, Response);
4626 return;
4627 }
4628 }
4629 }
4630
4631 await HttpFolderResource.SendResponse(Rec.Path, Rec.ContentType, Rec.ETag, File.GetLastWriteTime(Rec.Path), Response);
4632 }
4633
4634 private Task ChatSessions_Removed(object Sender, CacheItemEventArgs<string, Variables> e)
4635 {
4636 if (e.Value.TryGetVariable(" State ", out Variable v) &&
4637 v.ValueObject is ChatState State)
4638 {
4639 foreach (Process P in State.Processes)
4640 {
4641 try
4642 {
4643 P.Kill();
4644 }
4645 catch (Exception ex)
4646 {
4647 Log.Exception(ex);
4648 }
4649 }
4650
4651 State.Processes.Clear();
4652 }
4653
4654 return Task.CompletedTask;
4655 }
4656
4663 public static bool VerifyRecaptcha(object Posted, string RemoteEndpoint)
4664 {
4665 if (!(Posted is Dictionary<string, string> Form))
4666 return false;
4667
4668 if (!Form.TryGetValue("g-recaptcha-response", out string RecaptchaResponse))
4669 return false;
4670
4671 return Feedback.SiteVerify(RecaptchaResponse, RemoteEndpoint).Result;
4672 }
4673
4680 public static Task<bool> SendMailMessage(string To, string Subject, string Markdown)
4681 {
4682 return SendMailMessage(To, Subject, Markdown, new EmbeddedContent[0]);
4683 }
4684
4692 public static Task<bool> SendMailMessage(string To, string Subject, string Markdown, params EmbeddedContent[] Attachments)
4693 {
4694 string From = null;
4695
4696 if (RelayConfiguration.Instance.UseRelayServer)
4697 From = RelayConfiguration.Instance.Sender;
4698
4699 if (string.IsNullOrEmpty(From))
4701
4702 return SendMailMessage(From, To, Subject, Markdown, Attachments);
4703 }
4704
4712 public static Task<bool> SendMailMessage(string From, string To, string Subject, string Markdown)
4713 {
4714 return SendMailMessage(From, To, Subject, Markdown, new EmbeddedContent[0]);
4715 }
4716
4725 public static Task<bool> SendMailMessage(string From, string To, string Subject, string Markdown, params EmbeddedContent[] Attachments)
4726 {
4727 if (RelayConfiguration.Instance.UseRelayServer)
4728 {
4729 if (string.IsNullOrEmpty(From))
4730 From = RelayConfiguration.Instance.Sender;
4731
4732 return SendMailMessage(
4737 From,
4738 To,
4739 Subject,
4740 Markdown,
4741 Attachments);
4742 }
4743 else
4744 return instance?.xmppServer?.SendMailMessage(From, To, Subject, Markdown) ?? Task.FromResult(false);
4745 }
4746
4759 public static Task<bool> SendMailMessage(string SmtpHost, int SmtpPort, string UserName, string Password,
4760 string From, string To, string Subject, string Markdown)
4761 {
4762 return SendMailMessage(SmtpHost, SmtpPort, UserName, Password, From, To, Subject, Markdown, new EmbeddedContent[0]);
4763 }
4764
4778 public static async Task<bool> SendMailMessage(string SmtpHost, int SmtpPort, string UserName, string Password,
4779 string From, string To, string Subject, string Markdown, params EmbeddedContent[] Attachments)
4780 {
4781 string Styles = string.Empty;
4782
4783 if (!(Attachments is null))
4784 {
4785 List<EmbeddedContent> Attachments2 = null;
4786 bool Changed = false;
4787
4788 foreach (EmbeddedContent Attachment in Attachments)
4789 {
4790 if (!(Attachment.Decoded is null) && Attachment.Decoded is CssDocument CssDocument)
4791 {
4792 Styles += CssDocument.Css;
4793 Changed = true;
4794 continue;
4795 }
4796 else if (Attachment.Decoded is null && !(Attachment.Raw is null) && Attachment.ContentType.Contains(CssCodec.ContentType))
4797 {
4798 object Decoded = InternetContent.DecodeAsync(Attachment.ContentType, Attachment.Raw, null);
4799
4800 if (Decoded is CssDocument CssDocument2)
4801 {
4802 Styles += CssDocument2.Css;
4803 Changed = true;
4804 continue;
4805 }
4806 }
4807
4808 if (Attachments2 is null)
4809 Attachments2 = new List<EmbeddedContent>();
4810
4811 Attachments2.Add(Attachment);
4812 }
4813
4814 if (Changed)
4815 Attachments = Attachments2?.ToArray();
4816 }
4817
4818 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(Markdown);
4819 StringBuilder sb = new StringBuilder();
4820
4821 sb.AppendLine("<html>");
4822 sb.AppendLine("<head>");
4823
4824 if (!string.IsNullOrEmpty(Styles))
4825 {
4826 sb.AppendLine("<style>");
4827 sb.AppendLine(Styles);
4828 sb.AppendLine("</style>");
4829 }
4830
4831 sb.AppendLine("</head>");
4832 sb.AppendLine("<body>");
4833 sb.AppendLine(HtmlDocument.GetBody(await Doc.GenerateHTML()));
4834 sb.AppendLine("</body>");
4835 sb.AppendLine("</html>");
4836
4837 string HTML = sb.ToString();
4838 string PlainText = await Doc.GeneratePlainText();
4839 EmbeddedContent[] Alternatives = new EmbeddedContent[]
4840 {
4841 new EmbeddedContent()
4842 {
4843 ContentType = "text/html; charset=utf-8",
4844 Raw = Encoding.UTF8.GetBytes(HTML)
4845 },
4846 new EmbeddedContent()
4847 {
4848 ContentType = "text/plain; charset=utf-8",
4849 Raw = Encoding.UTF8.GetBytes(PlainText)
4850 },
4851 new EmbeddedContent()
4852 {
4853 ContentType = "text/markdown; charset=utf-8",
4854 Raw = Encoding.UTF8.GetBytes(Markdown)
4855 }
4856 };
4857
4858 KeyValuePair<byte[], string> P = await InternetContent.EncodeAsync(new ContentAlternatives(Alternatives), Encoding.UTF8);
4859 byte[] BodyBin = P.Key;
4860 string ContentType = P.Value;
4861
4862 if (!(Attachments is null) && Attachments.Length > 0)
4863 {
4864 EmbeddedContent[] Mixed = new EmbeddedContent[Attachments.Length + 1];
4865 Mixed[0] = new EmbeddedContent()
4866 {
4867 ContentType = ContentType,
4868 Raw = BodyBin
4869 };
4870 Array.Copy(Attachments, 0, Mixed, 1, Attachments.Length);
4871
4872 P = await InternetContent.EncodeAsync(new MixedContent(Mixed), Encoding.UTF8);
4873 BodyBin = P.Key;
4874 ContentType = P.Value;
4875 }
4876
4877 KeyValuePair<string, string>[] Headers = new KeyValuePair<string, string>[]
4878 {
4879 new KeyValuePair<string, string>("MIME-VERSION", "1.0"),
4880 new KeyValuePair<string, string>("FROM", From),
4881 new KeyValuePair<string, string>("TO", To),
4882 new KeyValuePair<string, string>("SUBJECT", Subject),
4883 new KeyValuePair<string, string>("DATE", CommonTypes.EncodeRfc822(DateTime.Now)),
4884 new KeyValuePair<string, string>("IMPORTANCE", "normal"),
4885 new KeyValuePair<string, string>("X-PRIORITY", "3"),
4886 new KeyValuePair<string, string>("MESSAGE-ID", Guid.NewGuid().ToString()),
4887 new KeyValuePair<string, string>("CONTENT-TYPE", ContentType)
4888 };
4889
4890 int i = To.IndexOf('@');
4891 if (i < 0)
4892 throw new ArgumentException("Invalid mail address: " + To, nameof(To));
4893
4894 string Domain = To.Substring(i + 1).Trim();
4895
4896 return await (instance?.smtpServer?.SendMessage(Domain, SmtpHost, SmtpPort, UserName, Password, From, To, Headers, BodyBin, DateTime.Now) ?? Task.FromResult(false));
4897 }
4898
4899 private MethodInfo getCounter2 = null;
4900 private MethodInfo getCounter3 = null;
4901 private MethodInfo getCpuValue = null;
4902 private MethodInfo getFreeMemoryValue = null;
4903 private object cpuCounter = null;
4904 private object freeMemoryCounter = null;
4905
4906 private async void SampleTimerEventHandler(object Sender)
4907 {
4908 try
4909 {
4910 if (this.getCounter2 is null)
4911 {
4912 Type T = Types.GetType("Waher.IoTGateway.Svc.PerformanceCounters");
4913 this.getCounter2 = T?.GetMethod("GetCounter", new Type[] { typeof(string), typeof(string) });
4914 this.getCounter3 = T?.GetMethod("GetCounter", new Type[] { typeof(string), typeof(string), typeof(string) });
4915
4916 this.cpuCounter = this.getCounter3?.Invoke(null, new object[] { "Processor", "_Total", "% Processor Time" });
4917 this.getCpuValue = this.cpuCounter?.GetType()?.GetMethod("NextValue", new Type[0]);
4918
4919 this.freeMemoryCounter = this.getCounter2?.Invoke(null, new object[] { "Memory", "Available MBytes" });
4920 if (this.freeMemoryCounter is null)
4921 this.freeMemoryCounter = this.getCounter2?.Invoke(null, new object[] { "Minne", "Tillgängliga megabyte" });
4922
4923 this.getFreeMemoryValue = this.freeMemoryCounter?.GetType()?.GetMethod("NextValue", new Type[0]);
4924 }
4925
4926 if (this.getCpuValue?.Invoke(this.cpuCounter, Types.NoParameters) is float CPU)
4927 await this.statistics.Sample("CPU", CPU);
4928
4929 if (this.getFreeMemoryValue?.Invoke(this.freeMemoryCounter, Types.NoParameters) is float FreeMemory)
4930 await this.statistics.Sample("FreeMemory", FreeMemory);
4931 }
4932 catch (Exception ex)
4933 {
4934 Log.Exception(ex);
4935 }
4936 }
4937
4941 public Bucket CurrentCpuBucket
4942 {
4943 get
4944 {
4945 if (this.statistics.TryGetBucket("CPU", out Bucket Bucket))
4946 return Bucket;
4947 else
4948 return null;
4949 }
4950 }
4951
4955 public Bucket CurrentFreeMemoryBucket
4956 {
4957 get
4958 {
4959 if (this.statistics.TryGetBucket("FreeMemory", out Bucket Bucket))
4960 return Bucket;
4961 else
4962 return null;
4963 }
4964 }
4965
4966 private async Task Export_OnExportKeyFolderUpdated(object Sender, EventArgs e)
4967 {
4968 try
4969 {
4970 this.httpFileUploadSettings.BackupFolder = await Export.GetFullExportFolderAsync();
4971 }
4972 catch (Exception ex)
4973 {
4974 Log.Exception(ex);
4975 }
4976 }
4977
4978 private async Task Export_OnExportFolderUpdated(object Sender, EventArgs e)
4979 {
4980 try
4981 {
4982 this.httpFileUploadSettings.KeyFolder = await Export.GetFullKeyExportFolderAsync();
4983 }
4984 catch (Exception ex)
4985 {
4986 Log.Exception(ex);
4987 }
4988 }
4989
4990 private async Task JwtRequest(object Sender, IqEventArgs e)
4991 {
4992 try
4993 {
4994 int Seconds = XML.Attribute(e.Query, "seconds", 0);
4995 if (Seconds <= 0)
4996 {
4997 await e.IqErrorBadRequest(e.To, "Number of seconds token is to be valid must be a positive integer.", "en");
4998 return;
4999 }
5000
5001 if (Seconds > 3600)
5002 Seconds = 3600;
5003
5004 int IssuedAt = (int)Math.Round(DateTime.UtcNow.Subtract(JSON.UnixEpoch).TotalSeconds);
5005 int Expires = IssuedAt + Seconds;
5006
5007 string Token = this.jwtFactory.Create(
5008 new KeyValuePair<string, object>(JwtClaims.JwtId, System.Convert.ToBase64String(Gateway.NextBytes(32))),
5009 new KeyValuePair<string, object>(JwtClaims.Issuer, Gateway.Domain.Value),
5010 new KeyValuePair<string, object>(JwtClaims.Subject, e.From.Address.Value),
5011 new KeyValuePair<string, object>(JwtClaims.IssueTime, IssuedAt),
5012 new KeyValuePair<string, object>(JwtClaims.ExpirationTime, Expires));
5013
5014 StringBuilder Xml = new StringBuilder();
5015
5016 Xml.Append("<token xmlns='");
5017 Xml.Append(NamespaceJwt);
5018 Xml.Append("'>");
5019 Xml.Append(XML.Encode(Token));
5020 Xml.Append("</token>");
5021
5022 await e.IqResult(Xml.ToString(), e.To);
5023
5024 LoginAuditor.Success("Successful generation of JWT token.", e.From.BareJid, e.From.Address, "HTTP");
5025 }
5026 catch (Exception ex)
5027 {
5028 await e.IqError(ex, e.To);
5029 }
5030 }
5031
5032 internal static Task<IClientConnection> JwtAuthenticate(HttpRequest Request)
5033 {
5034 return JwtAuthenticate(Request, instance.jwtFactory, instance.xmppServer);
5035 }
5036
5037 public static async Task<IClientConnection> JwtAuthenticate(HttpRequest Request,
5038 JwtFactory Factory, XmppServer Server)
5039 {
5040 string TokenStr = JwtAuthentication.GetAccessToken(Request);
5041 if (string.IsNullOrEmpty(TokenStr))
5042 {
5043 if (Request.Response?.TransferEncoding is HttpxResponse HttpxResponse)
5044 {
5045 (bool Found, IRecipient Recipient) = await HttpxResponse.Server.TryGetRecipient(HttpxResponse.To, HttpxResponse.From);
5046 if (Found && Recipient is IClientConnection ClientConnection)
5047 return ClientConnection;
5048 }
5049
5050 throw new UnauthorizedException("Unauthorized access prohibited.", new string[] { "Bearer realm=\"" + Gateway.Domain?.Value + "\"" });
5051 }
5052
5053 JwtToken Token = new JwtToken(TokenStr);
5054 if (!Factory.IsValid(Token, out Reason Reason))
5055 {
5056 LoginAuditor.Fail("Invalid JWT token.", Token.Subject ?? string.Empty, Request.RemoteEndPoint, "HTTP",
5057 new KeyValuePair<string, object>("Reason", Reason));
5058
5059 throw new ForbiddenException("Invalid JWT token.");
5060 }
5061
5062 if (!Server.TryGetClientConnection(Token.Subject, out IClientConnection Connection))
5063 {
5064 LoginAuditor.Fail("JWT token obsoleted, due to XMPP disconnect.", Token.Subject ?? string.Empty, Request.RemoteEndPoint, "HTTP");
5065
5066 throw new ForbiddenException("JWT token obsoleted, due to XMPP disconnect.");
5067 }
5068
5069 return Connection;
5070 }
5071
5072 private Task ProxyResource_AddSsoInformationEncrypted(object Sender, ProxyRequestEventArgs e)
5073 {
5074 return this.ProxyResource_AddSsoInformation(Sender, e, true);
5075 }
5076
5077 private Task ProxyResource_AddSsoInformationUnencrypted(object Sender, ProxyRequestEventArgs e)
5078 {
5079 return this.ProxyResource_AddSsoInformation(Sender, e, false);
5080 }
5081
5082 private Task ProxyResource_AddSsoInformation(object _, ProxyRequestEventArgs e, bool Encrypted)
5083 {
5084 // For a reference of JWT claims, see:
5085 // https://www.iana.org/assignments/jwt/jwt.xhtml#claims
5086
5088 if (Variables is null || e.Message.Headers.Contains("Authorization"))
5089 return Task.CompletedTask;
5090
5091 if (Variables.TryGetVariable("User", out Variable v) && v.ValueObject is IUserWithClaims User)
5092 return this.AddSsoInformation(User, e, Encrypted);
5093 else if (Variables.TryGetVariable("QuickLoginUser", out v) && v.ValueObject is IUserWithClaims User2)
5094 return this.AddSsoInformation(User2, e, Encrypted);
5095 else
5096 return Task.CompletedTask;
5097 }
5098
5099 private async Task AddSsoInformation(IUserWithClaims User, ProxyRequestEventArgs e, bool Encrypted)
5100 {
5101 string EncryptedPrefix = Encrypted ? "e" : "u";
5102
5103 if (!this.ssoTokens.TryGetValue(EncryptedPrefix + User.UserName, out string JwtToken))
5104 {
5105 JwtToken = await User.CreateToken(this.jwtFactory, Encrypted);
5106 if (string.IsNullOrEmpty(JwtToken))
5107 return;
5108
5109 this.ssoTokens[EncryptedPrefix + User.UserName] = JwtToken;
5110 }
5111
5112 e.Message.Headers.Add("Authorization", "Bearer " + JwtToken);
5113 }
5114
5122 {
5123 return this.jwtFactory.IsValid(Token, out Reason);
5124 }
5125
5126 }
5127}
Helps with common CSV-related tasks. (CSV=Comma Separated Values)
Definition: CSV.cs:21
static string[][] Parse(string Csv)
Parses a CSV string.
Definition: CSV.cs:29
Helps with parsing of commong data types.
Definition: CommonTypes.cs:13
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
Definition: CommonTypes.cs:594
static readonly char[] CRLF
Contains the CR LF character sequence.
Definition: CommonTypes.cs:17
static string EncodeRfc822(DateTime Timestamp)
Encodes a date and time, according to RFC 822 §5.
Definition: CommonTypes.cs:667
Contains information about an emoji.
Static class that provide methods for managing emojis.
static bool TryGetEmoji(string ShortName, out EmojiInfo Emoji)
Tries to get information about an emoji, given its short name.
CSS encoder/decoder.
Definition: CssCodec.cs:13
const string ContentType
Content-Type for CSS files.
Definition: CssCodec.cs:24
Encapsulates a CSS Document
Definition: CssDocument.cs:7
static string GetBody(string Html)
Extracts the contents of the BODY element in a HTML string.
const string DefaultContentType
application/javascript
Static class managing encoding and decoding of internet content.
static Task< KeyValuePair< byte[], string > > EncodeAsync(object Object, Encoding Encoding, params string[] AcceptedContentTypes)
Encodes an object.
static Task< object > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
static string GetContentType(string FileExtension)
Gets the content type of an item, given its file extension. It uses the TryGetContentType to see if a...
Helps with common JSON-related tasks.
Definition: JSON.cs:14
static readonly DateTime UnixEpoch
Unix Date and Time epoch, starting at 1970-01-01T00:00:00Z
Definition: JSON.cs:18
Consolidates Markdown from multiple sources, sharing the same thread.
Definition: Consolidator.cs:18
Task< bool > Update(string Source, MarkdownDocument Markdown, string Id)
Updates incoming markdown information.
Task< bool > Add(string Source, MarkdownDocument Markdown)
Adds incoming markdown information.
const string ContentType
Markdown content type.
Contains a markdown document. This markdown document class supports original markdown,...
static string Encode(string s)
Encodes all special characters in a string so that it can be included in a markdown document without ...
async Task< string > GeneratePlainText()
Generates Plain Text from the markdown text.
async Task< string > GenerateHTML()
Generates HTML from the markdown text.
static Task< MarkdownDocument > CreateAsync(string MarkdownText, params Type[] TransparentExceptionTypes)
Contains a markdown document. This markdown document class supports original markdown,...
Abstract base class for all markdown elements.
Represents alternative versions of the same content, encoded with multipart/alternative
Represents content embedded in other content.
string ContentType
Content-Type of embedded object.
object Decoded
Decoded body of embedded object. ContentType defines how TransferDecoded is transformed into Decoded.
byte[] Raw
Raw, untrasnformed body of embedded object.
Represents mixed content, encoded with multipart/mixed
Definition: MixedContent.cs:7
Static class managing loading of resources stored as embedded resources or in content files.
Definition: Resources.cs:15
static byte[] LoadResource(string ResourceName)
Loads a resource from an embedded resource.
Definition: Resources.cs:61
Static class helping modules to find files installed on the system.
Definition: FileSystem.cs:12
static string ExecutableExtension
Extension used by executable files on the platform.
Definition: FileSystem.cs:229
Plain text encoder/decoder.
const string DefaultContentType
text/plain
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:27
static bool TryParse(string s, out DateTime Value)
Tries to decode a string encoded DateTime.
Definition: XML.cs:744
static string HtmlAttributeEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe.
Definition: XML.cs:119
Static class managing loading of XSL resources stored as embedded resources or in content files.
Definition: XSL.cs:15
static XmlSchema LoadSchema(string ResourceName)
Loads an XML schema from an embedded resource.
Definition: XSL.cs:23
static void Validate(string ObjectID, XmlDocument Xml, params XmlSchema[] Schemas)
Validates an XML document given a set of XML schemas.
Definition: XSL.cs:118
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static string CleanStackTrace(string StackTrace)
Cleans a Stack Trace string, removing entries from the asynchronous execution model,...
Definition: Log.cs:184
static void Register(IEventSink EventSink)
Registers an event sink with the event log. Call Unregister(IEventSink) to unregister it,...
Definition: Log.cs:29
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.
Definition: Log.cs:1647
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.
Definition: Log.cs:566
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Definition: Log.cs:682
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Definition: Log.cs:334
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:818
static bool Unregister(IEventSink EventSink)
Unregisters an event sink from the event log.
Definition: Log.cs:46
static void Notice(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a notice event.
Definition: Log.cs:450
virtual void Dispose()
IDisposable.Dispose()
Definition: LogObject.cs:1134
Dictionary< string, Statistic > PerEventId
Number of events, per actor.
Dictionary< string, Statistic > PerFacility
Number of events, per actor.
Dictionary< string, Statistic > PerType
Number of events, per actor.
Dictionary< string, Statistic > PerActor
Number of events, per actor.
DateTime CurrentStat
Timestamp of current statistics.
Dictionary< string, Statistic > PerStackTrace
Number of events, per stack trace (Only of type Critical, Alert and Emergency).
DateTime LastStat
Timestamp of last statistics. If DateTime.MinValue, no statistics has been retrieved since restart of...
Dictionary< string, Statistic > PerLevel
Number of events, per actor.
Dictionary< string, Statistic > PerModule
Number of events, per actor.
Calculates statistics on incoming events.
EventStatistics GetStatisticsSinceLast()
Gets statistics of events logged since last call to GetStatisticsSinceLast.
Static class managing data export.
Definition: Export.cs:19
static async Task< string > GetFullExportFolderAsync()
Full path to export folder.
Definition: Export.cs:23
static async Task< string > GetFullKeyExportFolderAsync()
Full path to key folder.
Definition: Export.cs:36
static string FormatBytes(double Bytes)
Formats a file size using appropriate unit.
Definition: Export.cs:126
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:2354
static string InstanceName
Name of the current instance. Default instance=string.Empty
Definition: Gateway.cs:2364
static Task Terminate()
Raises the OnTerminate event handler, letting the container executable know the application needs to ...
Definition: Gateway.cs:2455
static X509Certificate2 Certificate
Domain certificate.
Definition: Gateway.cs:2349
static Task< string > GetMultiFormatChatMessageXml(string Markdown)
Gets XML for a multi-formatted chat message.
Definition: Gateway.cs:4065
static async Task DoBackup()
Performs a backup of the system.
Definition: Gateway.cs:3684
static LoginAuditor LoginAuditor
Current Login Auditor. Should be used by modules accepting user logins, to protect the system from un...
Definition: Gateway.cs:3033
static Task< int > ProcessNewServiceConfigurations()
Processes new Service Configuration Files. This method should be called after installation of new ser...
Definition: Gateway.cs:5151
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
Definition: Gateway.cs:3534
static string AppDataFolder
Application data folder.
Definition: Gateway.cs:2369
static int[] GetConfigPorts(string Protocol)
Gets the port numbers defined for a given protocol in the configuration file.
Definition: Gateway.cs:2420
static CaseInsensitiveString[] AlternativeDomains
Alternative domain names
Definition: Gateway.cs:2359
static Networking.XMPP.Avatar.AvatarClient AvatarClient
XMPP Concentrator Server.
Definition: Gateway.cs:3219
static Task SendNotification(Graph Graph)
Sends a graph as a notification message to configured notification recipients.
Definition: Gateway.cs:3826
static string GetUrl(string LocalResource)
Gets a URL for a resource.
Definition: Gateway.cs:4167
static MultiUserChatClient MucClient
XMPP Multi-User Chat Protocol (MUC) Client.
Definition: Gateway.cs:3267
static async Task< string > GetCustomErrorHtml(HttpRequest Request, string LocalFileName, string ContentType, byte[] Content)
Gets a custom error HTML document.
Definition: Gateway.cs:4731
static DateTime ScheduleEvent(ScheduledEventCallback Callback, DateTime When, object State)
Schedules a one-time event.
Definition: Gateway.cs:3452
static CaseInsensitiveString[] GetNotificationAddresses()
Returns configured notification addresses.
Definition: Gateway.cs:3922
static XmppClient XmppClient
XMPP Client connection of gateway.
Definition: Gateway.cs:3187
static string RootFolder
Web root folder.
Definition: Gateway.cs:2379
static bool CancelScheduledEvent(DateTime When)
Cancels a scheduled event.
Definition: Gateway.cs:3474
static SoftwareUpdateClient SoftwareUpdateClient
XMPP Software Updates Client, if such a compoent is available on the XMPP broker.
Definition: Gateway.cs:3283
static ProvisioningClient ProvisioningClient
XMPP Provisioning Client.
Definition: Gateway.cs:3203
static HttpFolderResource Root
Root folder resource.
Definition: Gateway.cs:2384
Domain constant, contains the value of the gateway domain.
Definition: Domain.cs:13
static ThemeDefinition CurrentTheme
Current theme.
Definition: Theme.cs:89
static DomainConfiguration Instance
Current instance of configuration.
bool UseEncryption
If the server uses server-side encryption.
SKColor LinkColorUnvisited
Color of unvisited links.
static XmppConfiguration Instance
Current instance of configuration.
Represents a file-based resource that can have custom values depending on what domain the resource is...
ISniffer[] Sniffers
Registered sniffers.
virtual void Add(ISniffer Sniffer)
ICommunicationLayer.Add
Question[] Questions
Question section
Definition: DnsMessage.cs:122
Contains information about a DNS Question
Definition: Question.cs:12
DNS resolver, as defined in:
Definition: DnsResolver.cs:32
static Task< DnsResponse > Query(string Name, QTYPE TYPE, QCLASS CLASS)
Resolves a DNS name.
Definition: DnsResolver.cs:329
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
Dictionary< string, Statistic > CallsPerFrom
Calls per From header value.
Dictionary< string, Statistic > CallsPerResource
Calls per resource.
DateTime CurrentStat
Timestamp of current statistics.
Dictionary< string, Statistic > CallsPerMethod
Calls per method.
DateTime LastStat
Timestamp of last statistics. If DateTime.MinValue, no statistics has been retrieved since restart of...
Dictionary< string, Statistic > CallsPerUserAgent
Calls per User Agent header value.
Event arguments for file not found events.
HttpResponse Response
Current response object.
HttpRequest Request
Current request object.
NotFoundException Exception
Exception that will be returned to client. Change, if a custom exception is to be returned....
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
KeyValuePair< string, string >[] HeaderFields
HTTP Header fields to include in the response.
async Task< object > GetContentObjectAsync()
Any content object to return. The object will be encoded before being sent.
string ContentType
The content type of Content, if provided.
byte[] Content
Any encoded content to return.
Publishes a folder with all its files and subfolders through HTTP GET, with optional support for PUT,...
static Task SendResponse(string FullPath, string ContentType, string ETag, DateTime LastModified, HttpResponse Response)
Sends a file-based response back to the client.
bool TryGetQueryParameter(string QueryParameter, out string Value)
Tries to get the value of an individual query parameter, if available.
Represents an HTTP request.
Definition: HttpRequest.cs:18
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:134
string RemoteEndPoint
Remote end-point.
Definition: HttpRequest.cs:195
Variables Session
Contains session states, if the resource requires sessions, or null otherwise.
Definition: HttpRequest.cs:164
string SubPath
Sub-path. If a resource is found handling the request, this property contains the trailing sub-path o...
Definition: HttpRequest.cs:146
HttpResponse Response
HTTP Response object, if one has been assigned to the request.
Definition: HttpRequest.cs:206
Base class for all HTTP resources.
Definition: HttpResource.cs:23
string ComputeETag(Stream fs)
Computes an ETag value for a resource.
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:21
async Task DisposeAsync()
Closes the connection and disposes of all resources.
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
void SetHeader(string FieldName, string Value)
Sets a custom header field value.
TransferEncoding TransferEncoding
Transfer encoding in response.
async Task Write(byte[] Data)
Returns binary data in the response.
An HTTP Reverse proxy resource. Incoming requests are reverted to a another web server for processing...
Implements an HTTP server.
Definition: HttpServer.cs:36
int[] OpenHttpPorts
HTTP Ports successfully opened.
Definition: HttpServer.cs:693
static Variables CreateVariables()
Creates a new collection of variables, that contains access to the global set of variables.
Definition: HttpServer.cs:1604
CommunicationStatistics GetCommunicationStatisticsSinceLast()
Gets communication statistics since last call.
Definition: HttpServer.cs:1755
HttpResource Register(HttpResource Resource)
Registers a resource with the server.
Definition: HttpServer.cs:1287
const int DefaultHttpPort
Default HTTP Port (80).
Definition: HttpServer.cs:40
int[] OpenHttpsPorts
HTTPS Ports successfully opened.
Definition: HttpServer.cs:704
bool Unregister(HttpResource Resource)
Unregisters a resource from the server.
Definition: HttpServer.cs:1438
const int DefaultHttpsPort
Default HTTPS port (443).
Definition: HttpServer.cs:45
The server has not found anything matching the Request-URI. No indication is given of whether the con...
Event arguments for proxy request events.
HttpRequest Request
Current request object.
HttpRequestMessage Message
Message being forwarded.
Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or ...
Implements a simple SMTP Server, as defined in:
Definition: SmtpServer.cs:44
CommunicationLayer ExternalSniffers
External Sniffers for SMTP communication.
Definition: SmtpServer.cs:224
void UpdateCertificate(X509Certificate ServerCertificate)
Updates the server certificate
Definition: SmtpServer.cs:300
void Dispose()
IDisposable.Dispose
Definition: SmtpServer.cs:325
Outputs sniffed data to an XML file.
Implements an XMPP concentrator server interface.
Task< bool > Register(IDataSource DataSource)
Registers a new data source with the concentrator.
Abstract base class for XMPP client connections
Information about a file upload.
Definition: FileInfo.cs:35
Implements HTTP File Upload support as an XMPP component: https://xmpp.org/extensions/xep-0363....
HTTP Resource managing HTTP Uploads, and access to uploaded files.
Event arguments for IQ queries.
Definition: IqEventArgs.cs:12
string Type
Type attribute in IQ stanza.
Definition: IqEventArgs.cs:83
XmppAddress From
From address attribute
Definition: IqEventArgs.cs:93
Task IqResult(string Xml, string From)
Returns a response to the current request.
Definition: IqEventArgs.cs:113
XmlElement Query
Query element, if found, null otherwise.
Definition: IqEventArgs.cs:70
XmppAddress To
To address attribute
Definition: IqEventArgs.cs:88
async Task IqError(string ErrorType, string Xml, XmppAddress From, string ErrorText, string Language)
Returns an error response to the current request.
Definition: IqEventArgs.cs:137
Task IqErrorBadRequest(XmppAddress From, string ErrorText, string Language)
Returns a bad-request error.
Definition: IqEventArgs.cs:159
Implements SOCKS5 Byte streams support as an XMPP component: https://xmpp.org/extensions/xep-0065....
override void Dispose()
IDisposable.Dispose
Dictionary< string, Statistic > StanzasPerFromDomain
Stanzas per domain of sender
Dictionary< string, Statistic > StanzasPerStanzaType
Stanzas per stanza and type.
DateTime LastStat
Timestamp of last statistics. If DateTime.MinValue, no statistics has been retrieved since restart of...
Dictionary< string, Statistic > StanzasPerToDomain
Stanzas per domain of receiver
Dictionary< string, Statistic > StanzasPerToBareJid
Stanzas per bare JID of receiver
Dictionary< string, Statistic > StanzasPerNamespace
Stanzas per namespace
Dictionary< string, Statistic > StanzasPerFqn
Stanzas per fully qualified name (namespace::localName)
Dictionary< string, Statistic > StanzasPerFromBareJid
Stanzas per bare JID of sender
Mainstains information about connectivity from a specific s2s endpoint.
Contains information about one XMPP address.
Definition: XmppAddress.cs:9
CaseInsensitiveString Resource
Resource part.
Definition: XmppAddress.cs:71
CaseInsensitiveString Domain
Domain
Definition: XmppAddress.cs:97
CaseInsensitiveString Address
XMPP Address
Definition: XmppAddress.cs:37
CaseInsensitiveString BareJid
Bare JID
Definition: XmppAddress.cs:45
static readonly XmppAddress Empty
Empty address.
Definition: XmppAddress.cs:31
CaseInsensitiveString Account
Account
Definition: XmppAddress.cs:124
const string ExtendedAddressingNamespace
http://jabber.org/protocol/address (XEP-0033)
Definition: XmppServer.cs:138
IClientConnection[] GetClientConnections()
Get active client connections
Definition: XmppServer.cs:789
void UpdateCertificate(X509Certificate ServerCertificate)
Updates the server certificate
Definition: XmppServer.cs:906
CommunicationLayer S2sSniffers
Sniffers for XMPP S2S communication.
Definition: XmppServer.cs:650
S2sEndpointStatistics[] GetServerConnectionStatistics()
Gets S2S connection statistics.
Definition: XmppServer.cs:845
bool TryGetClientConnection(string FullJID, out IClientConnection Connection)
Tries to get an active client connection.
Definition: XmppServer.cs:807
CommunicationLayer C2sSniffers
Sniffers for XMPP C2S communication.
Definition: XmppServer.cs:645
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
Definition: XmppServer.cs:1647
bool TryGetS2sEndpoint(string RemoteDomain, out IS2SEndpoint Endpoint)
Tries to get a server-to-server connection state object.
Definition: XmppServer.cs:2259
void Dispose()
IDisposable.Dispose
Definition: XmppServer.cs:947
static Task< XmppServer > Create(CaseInsensitiveString Domain, CaseInsensitiveString[] AlternativeDomains, X509Certificate ServerCertificate, bool EncryptionRequired, IXmppServerPersistenceLayer PersistenceLayer)
Creates an instance of an XMPP server.
Definition: XmppServer.cs:316
Statistics.CommunicationStatistics GetCommunicationStatisticsSinceLast()
Gets communication statistics since last call.
Definition: XmppServer.cs:5198
const string DelayedDeliveryNamespace
urn:xmpp:delay (XEP-0203)
Definition: XmppServer.cs:188
async Task< bool > SendMailMessage(CaseInsensitiveString From, CaseInsensitiveString To, string Subject, string Markdown)
Sends a mail message
Definition: XmppServer.cs:6204
async Task< int > DeleteOldMailContent(DateTime OlderThan)
Deletes old mail content.
Definition: XmppServer.cs:6187
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static readonly CaseInsensitiveString Empty
Empty case-insensitive string
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
Copies a specified number of characters from a specified position in this instance to a specified pos...
CaseInsensitiveString Remove(int startIndex)
Returns a new string in which all the characters in the current instance, beginning at a specified po...
CaseInsensitiveString Substring(int startIndex, int length)
Retrieves a substring from this instance. The substring starts at a specified character position and ...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static Task EndBulk()
Ends bulk-processing of data. Must be called once for every call to StartBulk.
Definition: Database.cs:1494
static async Task InsertLazy(object Object)
Inserts an object into the database, if unlocked. If locked, object will be inserted at next opportun...
Definition: Database.cs:156
static Task StartBulk()
Starts bulk-proccessing of data. Must be followed by a call to EndBulk.
Definition: Database.cs:1486
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:717
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:247
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
static async Task Clear(string CollectionName)
Clears a collection of all objects.
Definition: Database.cs:1206
This filter selects objects that conform to all child-filters provided.
Definition: FilterAnd.cs:10
This filter selects objects that have a named field equal to a given value.
This filter selects objects that have a named field lesser or equal to a given value.
Full-text search module, controlling the life-cycle of the full-text-search engine.
static async Task< TokenCount[]> Tokenize(IEnumerable< object > Objects)
Tokenizes a set of objects using available tokenizers. Tokenizers are classes with a default contruct...
Static class for access to Full-Text-Search
Definition: Search.cs:68
static void RegisterStopWords(params string[] StopWords)
Registers stop-words with the search-engine. Stop-words are ignored in searches.
Definition: Search.cs:197
Contains information about a tokenization process.
Implements an in-memory cache.
Definition: Cache.cs:15
bool ContainsKey(KeyType Key)
Checks if a key is available in the cache.
Definition: Cache.cs:296
void Dispose()
IDisposable.Dispose
Definition: Cache.cs:74
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Definition: Cache.cs:203
void Add(KeyType Key, ValueType Value)
Adds an item to the cache.
Definition: Cache.cs:338
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static void SetModuleParameter(string Name, object Value)
Sets a module parameter. This parameter value will be accessible to modules when they are loaded.
Definition: Types.cs:560
static Type GetType(string FullName)
Gets a type, given its full name.
Definition: Types.cs:41
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Definition: Types.cs:583
static Assembly[] Assemblies
Assemblies in the inventory.
Definition: Types.cs:884
static object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:548
static object Instantiate(Type Type, params object[] Arguments)
Returns an instance of the type Type . If one needs to be created, it is. If the constructor requires...
Definition: Types.cs:1353
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:84
async Task CheckRegistration(params Annotation[] Annotations)
Checks if the software needs to be registered.
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
static async Task< bool > SetAsync(string Key, string Value)
Sets a string-valued setting.
Class managing the contents of a temporary file. When the class is disposed, the temporary file is de...
override void Dispose(bool disposing)
Disposes of the object, and deletes the temporary file.
Base class for all types of elements.
Definition: Element.cs:13
Class managing a script expression.
Definition: Expression.cs:39
ScriptNode Root
Root script node.
Definition: Expression.cs:4299
async Task< object > EvaluateAsync(Variables Variables)
Evaluates the expression, using the variables provided in the Variables collection....
Definition: Expression.cs:4275
static string ToString(double Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4496
Base class for graphs.
Definition: Graph.cs:79
ToMatrix(ScriptNode Operand, bool NullCheck, int Start, int Length, Expression Expression)
To-Matrix operator.
Definition: ToMatrix.cs:22
Event arguments for preview events.
IElement Preview
Preview of result.
Contains information about a variable.
Definition: Variable.cs:10
Collection of variables.
Definition: Variables.cs:25
virtual Variable Add(string Name, object Value)
Adds a variable to the collection.
Definition: Variables.cs:122
virtual bool TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
Definition: Variables.cs:52
Edwards448 Elliptic Curve, as defined in RFC7748 and RFC8032: https://tools.ietf.org/html/rfc7748 htt...
Definition: Edwards448.cs:17
Contains methods for simple hash calculations.
Definition: Hashes.cs:59
static string ComputeSHA256HashString(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Definition: Hashes.cs:328
static string BinaryToString(byte[] Data)
Converts an array of bytes to a string with their hexadecimal representations (in lower case).
Definition: Hashes.cs:65
Use JWT tokens for authentication. The Bearer scheme defined in RFC 6750 is used: https://tools....
static string GetAccessToken(HttpRequest Request)
Gets the access token from an HTTP request.
Static class containing predefined JWT claim names.
Definition: JwtClaims.cs:10
const string Issuer
Issuer of the JWT
Definition: JwtClaims.cs:14
const string IssueTime
Time at which the JWT was issued; can be used to determine age of the JWT
Definition: JwtClaims.cs:39
const string JwtId
Unique identifier; can be used to prevent the JWT from being replayed (allows a token to be used only...
Definition: JwtClaims.cs:44
const string Subject
Subject of the JWT (the user)
Definition: JwtClaims.cs:19
const string ExpirationTime
Time after which the JWT expires
Definition: JwtClaims.cs:29
A factory that can create and validate JWT tokens.
Definition: JwtFactory.cs:53
bool IsValid(JwtToken Token)
Checks if a token is valid and signed by the factory.
Definition: JwtFactory.cs:176
static JwtFactory CreateHmacSha256()
Creates a JWT factory that can create and validate JWT tokens using the HMAC-SHA256 algorithm.
Definition: JwtFactory.cs:92
string Create(params KeyValuePair< string, object >[] Claims)
Creates a new JWT token.
Definition: JwtFactory.cs:248
void Dispose()
IDisposable.Dispose
Definition: JwtFactory.cs:160
Contains information about a Java Web Token (JWT). JWT is defined in RFC 7519: https://tools....
Definition: JwtToken.cs:21
Event arguments for endpoint annotation events.
void AddTag(string Key, object Value)
Adds a tag to the list of tags.
Class that monitors login events, and help applications determine malicious intent....
Definition: LoginAuditor.cs:26
static async void Success(string Message, string UserName, string RemoteEndpoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a successful login attempt.
static async void Fail(string Message, string UserName, string RemoteEndpoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a failed login attempt.
async Task< bool > ProcessLoginFailure(string RemoteEndpoint, string Protocol, DateTime Timestamp, string Reason)
Processes a failed login attempt.
static async Task< KeyValuePair< string, object >[]> Annotate(string RemoteEndpoint, params KeyValuePair< string, object >[] Tags)
Annotates a remote endpoint.
Login state information relating to a remote endpoint
byte[] ComputeVariable(byte[] N)
Computes the SPONGE function, as defined in section 4 of NIST FIPS 202.
Definition: Keccak1600.cs:412
Implements the SHA3 SHAKE256 extendable-output functions, as defined in section 6....
Definition: SHAKE256.cs:13
Contains information about a SPF string.
Corresponds to a user in the system.
Definition: User.cs:21
string UserName
User Name
Definition: User.cs:53
bool HasPrivilege(string Privilege)
If the user has a given privilege.
Definition: User.cs:129
async Task< string > CreateToken(JwtFactory Factory, bool Encrypted)
Creates a JWT Token referencing the user object.
Definition: User.cs:261
Maintains the collection of all users in the system.
Definition: Users.cs:24
static async Task< User > GetUser(string UserName, bool CreateIfNew)
Gets the User object corresponding to a User Name.
Definition: Users.cs:65
readonly Dictionary< string, FileInfo > Files
Currently accesible files
Definition: ChatState.cs:31
readonly LinkedList< Process > Processes
Current processes related to session
Definition: ChatState.cs:36
Information about a file accessible through admin command interface
Definition: FileInfo.cs:9
Asks the broker to enter a room.
Definition: Enter.cs:13
override async Task Execute(ChatState State, string[] Arguments, string OrgMessage, ResponseCallbackHandler ResponseCallback)
Executes the command.
Definition: Enter.cs:48
Multi-User Chat Room information
Definition: RoomInfo.cs:7
string NickName
Nick-name to use in room.
Definition: RoomInfo.cs:56
bool Permanent
If room association should be persisted.
Definition: RoomInfo.cs:66
string Password
Password to use to enter room.
Definition: RoomInfo.cs:61
Lists rooms currently entered by the neuron.
Definition: Rooms.cs:11
Consolidates responses from occupants in a MUC room.
Identity of the IoT Broker package.
Definition: BrokerPackage.cs:7
const string Key
Key for decrypting the contents of the package.
const string FileName
IoTBroker.package
Contains information about a broker account.
Definition: Account.cs:28
async Task< IRosterItem > GetRosterItem(CaseInsensitiveString UserName, CaseInsensitiveString Jid)
Gets a roster item for an account.
async Task DeleteOfflineMessages(IEnumerable< IOfflineMessage > Messages)
Deletes offline messages.
async Task< IEnumerable< IRosterItem > > GetRoster(CaseInsensitiveString UserName)
Gets the roster of an account.
Manages eDaler on accounts connected to the broker.
override void Dispose()
IDisposable.Dispose
Event log component, as defined in XEP-0337. https://xmpp.org/extensions/xep-0337....
override void Dispose()
IDisposable.Dispose
void Dispose()
IDisposable.Dispose
Definition: HttpxServer.cs:49
Marketplace processor, brokering sales of items via tenders and offers defined in smart contracts.
Multi-User-Chat (MUC) Component component, as defined in XEP-0045. https://xmpp.org/extensions/xep-00...
Paiwise processor, processing payment instructions defined in smart contracts.
const string NamespaceProvisioningOwnerIeeeV1
urn:ieee:iot:prov:o:1.0
const string NamespaceProvisioningTokenNeuroFoundationV1
urn:nf:iot:prov:t:1.0
const string NamespaceIoTDiscoveryNeuroFoundationV1
urn:nf:iot:disco:1.0
const string NamespaceProvisioningTokenIeeeV1
urn:ieee:iot:prov:t:1.0
const string NamespaceProvisioningDeviceIeeeV1
urn:ieee:iot:prov:d:1.0
const string NamespaceProvisioningOwnerNeuroFoundationV1
urn:nf:iot:prov:o:1.0
const string NamespaceProvisioningDeviceNeuroFoundationV1
urn:nf:iot:prov:d:1.0
Contains information about a software package.
Definition: Package.cs:18
byte[] PublicKey
Public key of issuer, used to create signature.
Definition: Package.cs:52
byte[] Signature
Cryptographic signature of package, as calculated by the issuer of the package.
Definition: Package.cs:46
DateTime Installed
When package was installed (if installed).
Definition: Package.cs:94
bool ContentOnly
If package only contains content (i.e. no assemblies and executable files).
Definition: Package.cs:112
CaseInsensitiveString FileName
Filename of package.
Definition: Package.cs:40
DateTime Published
When package was published.
Definition: Package.cs:76
byte[] AesKey
Symmetric cipher used to encrypt package file.
Definition: Package.cs:58
DateTime Supersedes
Timestamp of superceded package.
Definition: Package.cs:82
DateTime Created
When package record was created
Definition: Package.cs:88
PubSub component, as defined in XEP-0060. https://xmpp.org/extensions/xep-0060.html
async Task<(int, int)> DeleteExpiredNodes()
Deletes expired nodes
async Task< PubSubNode > GetNodeAsync(CaseInsensitiveString Service, CaseInsensitiveString NodeName, NodeAccessModel? AutoCreateAccess, XmppAddress From, CaseInsensitiveString Domain)
Gets a pubsub node.
override void Dispose()
IDisposable.Dispose
static async Task< PubSubComponent > Create(XmppServer Server, CaseInsensitiveString Subdomain, string Name)
Provisioning and registry service component.
Defines a node on which items can be published.
Definition: PubSubNode.cs:19
CaseInsensitiveString Name
Name of node.
Definition: PubSubNode.cs:113
bool PublishOnWeb
If the items published to the node should be available on the web or not.
Definition: PubSubNode.cs:654
bool TryGetBucket(string Id, out Bucket Bucket)
Tries to get a bucket, given its ID.
Definition: Buckets.cs:187
async Task Sample(string Counter, double Value)
Samples a value
Definition: Buckets.cs:130
Provides the user configuration options regarding use of SMTP Relay server to send mail.
static RelayConfiguration Instance
Current instance of configuration.
override Task ConfigureSystem()
Is called during startup to configure the system.
Root node of port numbers to use.
Definition: Ports.cs:11
Defines a proxy resource to act as a reverse proxy.
bool Encrypted
If forwarded requests are encrypted (HTTPS) or not (HTTP).
Data source mirroring the ProgramData folder for the broker.
Service helping services synchronize their clocks over the Internet.
Component that synchronizes content in the federated network, by sending synchronization messages and...
Date and Time value based on the intenal high-frequency timer.
Definition: DateTimeHF.cs:10
Web Host Meta Data in JSON format, as defined in XEP-0156 and RFC 6415: https://xmpp....
Web Host Meta Data in XML format, as defined in XEP-0156 and RFC 6415: https://xmpp....
Allows senders to send XMPP stanzas over HTTP POST.
Definition: XmppOverHttp.cs:20
Service Module hosting the XMPP broker and its components.
static Task< bool > SendMailMessage(string To, string Subject, string Markdown)
Sends a mail message
const string NamespaceSynchronizationNeuroFoundationV1
urn:nf:iot:synchronization:1.0
const string NamespaceSynchronizationIeeeV1
urn:ieee:iot:synchronization:1.0
const string NamespaceDnsOverXmpp
urn:xmpp:dox:0
async Task Stop()
Stops the module.
static void Calibrate()
Calibrates the internal clock with the high frequency timer.
async Task Start()
Starts the module.
static Task< bool > SendMailMessage(string To, string Subject, string Markdown, params EmbeddedContent[] Attachments)
Sends a mail message
static async Task AppendRemoteEndpointToTable(StringBuilder Markdown, string RemoteEndpoint)
Appends annotated information about a remote endpoint to a Markdown table.
static async Task< bool > SendMailMessage(string SmtpHost, int SmtpPort, string UserName, string Password, string From, string To, string Subject, string Markdown, params EmbeddedContent[] Attachments)
Sends a mail message
const string NamespaceJwt
urn:xmpp:jwt:0
static string NamespaceSynchronization(NamespaceSet Version)
Returns the namespace for IoT Clock Synchronization.
static NamespaceSet GetVersion(string Namespace)
Gets the namespace set version corresponding to a given a namespace.
bool AuthenticateJwtToken(JwtToken Token, out Reason Reason)
Validates a JWT token against the JWT factory defined for the XMPP Server.
static Task< bool > SendMailMessage(string SmtpHost, int SmtpPort, string UserName, string Password, string From, string To, string Subject, string Markdown)
Sends a mail message
static Task< IP4Localization > FindIpAddress(string RemoteEndpoint)
Finds locale information about an IP Address.
static DateTimeHF Now
Current high-resolution date and time
static Task< bool > SendMailMessage(string From, string To, string Subject, string Markdown)
Sends a mail message
static bool VerifyRecaptcha(object Posted, string RemoteEndpoint)
Method that can be used by web pages to verify Recaptcha responses.
static Task< bool > SendMailMessage(string From, string To, string Subject, string Markdown, params EmbeddedContent[] Attachments)
Sends a mail message
static async Task< IP4Localization > FindIpAddress(IPAddress Addr)
Finds locale information about an IP Address.
Task Information(string Comment)
Called to inform the viewer of something.
Task Warning(string Warning)
Called to inform the viewer of a warning state.
Interface for observable classes implementing communication protocols.
Interface for XMPP user accounts.
Definition: IAccount.cs:9
Interface for recipients of stanzas.
Definition: IRecipient.cs:9
Interface for XMPP S2S endpoints
Definition: IS2sEndpoint.cs:10
Interface for XMPP Server persistence layers. The persistence layer should implement caching.
Interface for late-bound modules loaded at runtime.
Definition: IModule.cs:9
Basic interface for all types of elements.
Definition: IElement.cs:20
object AssociatedObjectValue
Associated object value.
Definition: IElement.cs:33
Interface for objects that can be converted into matrices.
Definition: IToMatrix.cs:9
Basic interface for a user.
Definition: IUser.cs:7
A User that can participate in distributed operations, where the user is identified using a JWT token...
Basic interface for administration commands
bool AppliesTo(string CommandLine, string[] Arguments, out object Details)
If the command is applicable to the given command line.
Interface for nodes that are published through the concentrator interface.
Definition: INode.cs:49
HostDomainOptions
Options on how to handle domain names provided in the Host header.
BinaryPresentationMethod
How binary data is to be presented.
ContentType
DTLS Record content type.
Definition: Enumerations.cs:11
Reason
Reason a token is not valid.
Definition: JwtFactory.cs:12
delegate Task< string > ResponseCallbackHandler(string Markdown, string MessageId)
Delegate for response callback handler methods.
NamespaceSet
Namespace versions
Definition: NamespaceSet.cs:7
Represents a duration value, as defined by the xsd:duration data type: http://www....
Definition: Duration.cs:13