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;
5using System.Diagnostics;
6using System.IO;
7using System.IO.Compression;
8using System.Net;
10using System.Reflection;
11using System.Runtime.ExceptionServices;
13using System.Text;
14using System.Threading;
15using System.Threading.Tasks;
16using System.Web;
17using System.Xml;
18using Waher.Content;
31using Waher.Events;
70using Waher.Script;
79using Waher.Security;
127
129{
133 [Singleton]
134 [ModuleDependency(typeof(SemanticModule))]
136 {
140 public const string NamespaceSynchronizationIeeeV1 = "urn:ieee:iot:synchronization:1.0";
141
145 public const string NamespaceSynchronizationNeuroFoundationV1 = "urn:nf:iot:synchronization:1.0";
146
150 public const string NamespaceDnsOverXmpp = "urn:xmpp:dox:0";
151
155 public const string NamespaceJwt = "urn:xmpp:jwt:0";
156
160 public const int DB_Generation = 1;
161
167 public static string NamespaceSynchronization(NamespaceSet Version)
168 {
169 switch (Version)
170 {
171 case NamespaceSet.XsfV0:
173 default:
174 case NamespaceSet.NeuroFoundationV1: return NamespaceSynchronizationNeuroFoundationV1;
175 }
176 }
177
183 public static NamespaceSet GetVersion(string Namespace)
184 {
185 switch (Namespace)
186 {
188 default:
197 return NamespaceSet.NeuroFoundationV1;
198
208 return NamespaceSet.IeeeV1;
209
211 return NamespaceSet.XsfV0;
212 }
213 }
214
219 private const string Ip2LocalizationPackageName = "IP2LOCATION-LITE-DB5.CSV";
220
221 private static XmppServerModule instance = null;
222 private static Calibration calibration;
223 private readonly static Stopwatch clock = CreateWatch();
224 private static readonly byte[] iotBrokerPackagePublicKey = System.Convert.FromBase64String("BRb026TgJ5L1t6T5jWL23m0BQHg1kUNY308V8ixlqmNN8nrPDzB3tTygDylyzjmDCFgWSf7OyUWA");
225 private static readonly Edwards448 ed448 = new Edwards448(); // TODO: Allow custom algorithm.
226 private static IPersistentDictionary salts = null;
227 internal const string AutoInstallDelayParameterName = "Autoinstall.DelayMin";
228 internal const string AutoInstallContentOnlyParameterName = "Autoinstall.ContentOnly";
229 internal const string AutoInstallTimeParameterName = "Autoinstall.Time";
230
231 private readonly Dictionary<CaseInsensitiveString, WebNode> webNodes = new Dictionary<CaseInsensitiveString, WebNode>();
232 private readonly SortedDictionary<string, LinkedList<IAdminCommand>> adminCommands = new SortedDictionary<string, LinkedList<IAdminCommand>>();
233 private readonly Dictionary<CaseInsensitiveString, RoomInfo> mucRooms = new Dictionary<CaseInsensitiveString, RoomInfo>();
234 private readonly Dictionary<CaseInsensitiveString, SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>> presenceByNickAndRoom = new Dictionary<CaseInsensitiveString, SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>>();
235 private HttpAuthenticationScheme[] defaultAuthenticationSchemesAdmin;
236 private HttpAuthenticationScheme[] defaultAuthenticationSchemesXmpp;
237 private Dictionary<string, bool> vulnerableResources = null;
238 private Cache<string, IP4Localization> ip4LocalizationCache;
239 private Cache<string, Variables> chatSessions = null;
240 private Cache<string, IConsolidator> consolidators;
241 private Cache<string, string> ssoTokens;
242 private EventStatisticsSink eventStatistics = null;
243 private PersistenceLayer persistenceLayer = null;
244 private XmppServer xmppServer = null;
245 private SmtpServer smtpServer = null;
246 private FtpServer ftpServer = null;
247 private Socks5Component socks5Component = null;
248 private EventLogComponent eventLogComponent = null;
249 private ProvisioningComponent provisioningComponent = null;
250 private PubSubComponent pubSubComponent = null;
251 private MultiUserChatComponent mucComponent = null;
252 private ServiceRegistrationComponent serviceRegistrationComponent = null;
253 private LegalComponent legalComponent = null;
254 private EDalerComponent eDalerComponent = null;
255 private GeoSpatialComponent geoComponent = null;
256 private ConcentratorServer concentratorServer = null;
257 private ServiceRegistrationClient serviceRegistrationClient;
258 private HttpServer httpServer = null;
259 private HttpxServer httpxServer = null;
260 private HttpFileUploadSettings httpFileUploadSettings = null;
261 private HttpFileUploadComponent httpFileUploadComponent;
262 private XmppFileUploadResource httpFileUploadResource;
263 private HttpFolderResource httpEncryptedFileUploadResource;
264 private HttpFolderResource httpPubSubFileUploadResource;
265 private HttpFolderResource httpInternalTransferUploadResource;
266 private CreateApiKey createApiKey = null;
267 private UpdateApiKey updateApiKey = null;
268 private CreateAccount createAccount = null;
269 private UpdateAccount updateAccount = null;
270 private DeleteAccount deleteAccount = null;
271 private UpdatePubSubNode updatePubSubNode = null;
272 private DeletePubSubNode deletePubSubNode = null;
273 private LoadMoreItems loadMoreItems = null;
274 private Feedback feedback = null;
275 private RequestAccount requestAccount = null;
276 private RequestApiKey requestApiKey = null;
277 private SearchEvents searchEvents = null;
278 private SendOperatorMessage sendMessage = null;
279 private SetContractState setContractState = null;
280 private PublisherAvatar publisherAvatar = null;
281 private BoshWebClientResource webClient1 = null;
282 private BoshWebClientResource webClient2 = null;
283 private WebSocketClientResource webSocketClient = null;
284 private WebHostMetaDataXml webHostMetaDataXml = null;
285 private WebHostMetaDataJson webHostMetaDataJson = null;
286 private ConnectionsSource connectionsSource = null;
287 private LegalIdentityStateChanged legalIdentityStateChanged = null;
288 private QR qr = null;
289 private UploadPackage uploadPackage = null;
290 private UploadSignature uploadSignature = null;
291 private DeletePackage deletePackage = null;
292 private InstallPackage installPackage = null;
293 private UninstallPackage uninstallPackage = null;
294 private HttpFolderResource packages = null;
295 private DnsOverHttpsResource dnsOverHttps = null;
296 private ValidateLegalId validateLegalId = null;
297 private ValidateContract validateContract = null;
298 private MultiFactorAuthentication mfa = null;
299 private QuickLogin quickLogin = null;
300 private RemoteLogin remoteLogin = null;
301 private WhatsMyEndpointResource whatsMyEndpointResource = null;
302 private AddNote addNote = null;
303 private PublicTokenView publicTokenView = null;
304 private PublicTokenHistoryView publicTokenHistoryView = null;
305 private PublicNodes publicNodes = null;
306 private Vault vault = null;
307 private KillMachine killMachine = null;
308 private HttpResource chatFile = null;
309 private XmppOverHttp xmppOverHttp = null;
310 private HttpReverseProxyResource httpProxy = null;
311 private Buckets performanceStatistics = null;
312 private Timer sampleTimer = null;
313
314 private static string appData = string.Empty;
315 private static string appDataDrive = null;
316 internal static DateTime autoUpdateTP = DateTime.MinValue;
317
318 public XmppServerModule()
319 {
320 }
321
322 public async Task Start()
323 {
324 try
325 {
326 Log.Informational("XMPP Server starting.");
327 instance = this;
328
329 if (Types.TryGetModuleParameter("AppData", out string s))
330 {
331 appData = s;
332
333 if (!string.IsNullOrEmpty(appData))
334 appDataDrive = Path.GetPathRoot(appData);
335 }
336
337 if (Expression.TryGetConstant("OsTime", null, out IElement OsTime) &&
338 OsTime.AssociatedObjectValue is TimeSpan TimeSinceOsStart)
339 {
340 double LastOsStartTime = await RuntimeSettings.GetAsync("OS.StartTime", 0.0);
341 double CurrentOsStartTime = TimeSinceOsStart.TotalSeconds;
342
343 if (CurrentOsStartTime < LastOsStartTime)
344 {
345 string Unit = "seconds";
346 double d = CurrentOsStartTime;
347 byte NrDec = 0;
348
349 if (d >= 60)
350 {
351 d /= 60;
352 Unit = "minutes";
353 NrDec = 1;
354
355 if (d >= 60)
356 {
357 d /= 60;
358 Unit = "hours";
359
360 if (d >= 24)
361 {
362 d /= 24;
363 Unit = "days";
364
365 if (d >= 7)
366 {
367 d /= 7;
368 Unit = "weeks";
369 }
370 }
371 }
372 }
373
374 Log.Alert("Operating System restarted " +
375 CommonTypes.Encode(d, NrDec) + " " + Unit + " ago.");
376 }
377
378 await RuntimeSettings.SetAsync("OS.StartTime", CurrentOsStartTime);
379 }
380
381 this.eventStatistics = new EventStatisticsSink("Event Statistics");
382 Log.Register(this.eventStatistics);
383
384 DateTime Now = DateTime.Now;
385 DateTime TP = new DateTime(Now.Year, Now.Month, Now.Day, Now.Hour, Now.Minute, 0);
386 this.performanceStatistics = new Buckets(TP, new Duration(false, 0, 0, 0, 0, 1, 0), false, true, true);
387
388 TP = new DateTime(Now.Year, Now.Month, Now.Day, Now.Hour, Now.Minute, Now.Second);
389 int UntilNext = (int)((TP.AddSeconds(1) - Now).TotalMilliseconds + 0.5);
390
391 if (UntilNext < 0)
392 UntilNext += 1000;
393
394 this.sampleTimer = new Timer(this.SampleTimerEventHandler, null, UntilNext, 1000);
395
396 this.persistenceLayer = new PersistenceLayer();
397 Gateway.OAuthEnvironment.Register(this.persistenceLayer);
399
400 salts = await Database.GetDictionary("Salts");
401
402 this.ip4LocalizationCache = new Cache<string, IP4Localization>(int.MaxValue, TimeSpan.FromDays(30), TimeSpan.FromDays(1));
403 LoginAuditor.AnnotateEndpoint += this.LoginAuditor_AnnotateEndpoint;
404
405 List<string> Ip4DnsBlackLists = new List<string>();
406 List<string> Ip6DnsBlackLists = new List<string>();
407
408 try
409 {
410 XmlDocument Doc = XML.LoadFromFile(Path.Combine(appData, "DNSBL.xml"));
411
412 XSL.Validate("DNSBL", Doc, "BlackLists", "http://waher.se/Schema/DNSBL.xsd",
413 XSL.LoadSchema(typeof(XmppServerModule).Namespace + ".Schema.DNSBL.xsd"));
414
415 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
416 {
417 if (N is XmlElement E && E.LocalName == "BlackList")
418 {
419 s = XML.Attribute(E, "ip4DomainName");
420 if (!string.IsNullOrEmpty(s))
421 Ip4DnsBlackLists.Add(s);
422
423 s = XML.Attribute(E, "ip6DomainName");
424 if (!string.IsNullOrEmpty(s))
425 Ip6DnsBlackLists.Add(s);
426 }
427 }
428 }
429 catch (Exception ex)
430 {
431 Log.Exception(ex);
432 }
433
434 List<SpfExpression> SpfExpressions = new List<SpfExpression>();
435
436 try
437 {
438 XmlDocument Doc = XML.LoadFromFile(Path.Combine(appData, "SPF.xml"));
439
440 XSL.Validate("SPF", Doc, "SpfRecords", "http://waher.se/Schema/SPF.xsd",
441 XSL.LoadSchema(typeof(XmppServerModule).Namespace + ".Schema.SPF.xsd"));
442
443 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
444 {
445 if (N is XmlElement E && E.LocalName == "SpfRecord")
446 {
447 string Domain = XML.Attribute(E, "domain");
448 string Spf = XML.Attribute(E, "spf");
449 bool IncludeSubdomains = XML.Attribute(E, "includeSubdomains", false);
450
451 SpfExpressions.Add(new SpfExpression(Domain, IncludeSubdomains, Spf));
452 }
453 }
454 }
455 catch (Exception ex)
456 {
457 Log.Exception(ex);
458 }
459
460 try
461 {
463 XmlDocument Doc = XML.LoadFromFile(Path.Combine(appData, "StopWords.xml"));
464
465 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
466 {
467 if (N is XmlElement E && E.LocalName == "Language")
468 {
469 foreach (XmlNode N2 in E.ChildNodes)
470 {
471 if (N2 is XmlElement E2 && E2.LocalName == "Stopword")
472 await FullTextSearchModule.Tokenize(new object[] { E2.InnerText }, StopWords);
473 }
474 }
475 }
476
477 string[] StopWords2 = new string[StopWords.TokenCounts.Count];
478 StopWords.TokenCounts.Keys.CopyTo(StopWords2, 0);
479
480 Search.RegisterStopWords(StopWords2);
481 }
482 catch (Exception ex)
483 {
484 Log.Exception(ex);
485 }
486
487 IoTBroker.Legal.Identity.Iso3166.Load();
488 IoTBroker.Legal.Identity.PersonalNumberSchemes.Load();
489 IoTBroker.Legal.Identity.PhoneCountryCodes.Load();
490
491 Encoding.RegisterProvider(new CodePages());
492
493 #region Upgrading internal database
494
495 long DbGen = await RuntimeSettings.GetAsync("DB.Generation.Broker", 0);
496
497 while (DbGen < DB_Generation)
498 {
499 DbGen++;
500
501 Log.Notice("Upgrading to DB Generation " + DbGen.ToString() + ".");
502
503 switch (DbGen)
504 {
505 case 1: // Encrypting sensitive fields.
506 foreach (Type T in Types.GetTypesImplementingInterface(typeof(IEncryptedProperties)))
507 {
508 try
509 {
510 MethodInfo MI = TypeSource.FindMethod.MakeGenericMethod(T);
511 object[] FindParameters = new object[] { 0, int.MaxValue, new FilterAnd(), Array.Empty<string>() };
512
513 object Obj = MI.Invoke(null, FindParameters);
514 Obj = await ScriptNode.WaitPossibleTask(Obj);
515
516 if (Obj is IEnumerable Objects)
517 {
518 await Database.StartBulk();
519 try
520 {
521 foreach (object Object in Objects)
522 await Database.Update(Object);
523 }
524 finally
525 {
526 await Database.EndBulk();
527 }
528 }
529 }
530 catch (Exception ex)
531 {
532 Log.Exception(ex, T.FullName);
533 }
534 }
535 break;
536 }
537
538 await RuntimeSettings.SetAsync("DB.Generation.Broker", DbGen);
539 }
540
541 #endregion
542
543 #region SMTP
544
545 this.smtpServer = new SmtpServer(Gateway.Domain, Gateway.GetConfigPorts("SMTP"), 10 * 1024 * 1024,
546 Gateway.Certificate, !(Gateway.Certificate is null), this.persistenceLayer, Ip4DnsBlackLists.ToArray(),
547 Ip6DnsBlackLists.ToArray(), SpfExpressions.ToArray());
548
549 Types.SetModuleParameter("SMTP_SERVER", this.smtpServer);
550
551 this.smtpServer.ExternalSniffers.Add(new XmlFileSniffer(appData + "SMTP" + Path.DirectorySeparatorChar +
552 "SMTP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml", appData + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
553 7, BinaryPresentationMethod.ByteCount));
554
555 this.smtpServer.SmtpSnifferPath = appData + "SMTP" + Path.DirectorySeparatorChar +
556 "%ENDPOINT%" + Path.DirectorySeparatorChar + "SMTP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml";
557
558 if (!(RelayConfiguration.Instance is null))
560
561 #endregion
562
563 #region FTP
564
565 Dictionary<int, KeyValuePair<ClientCertificates, bool>> PortSpecificMTlsSettings = new Dictionary<int, KeyValuePair<ClientCertificates, bool>>();
566 int[] FtpPorts = Gateway.GetConfigPorts("FTP");
567 int[] FtpsPorts = Gateway.GetConfigPorts("FTPS");
568 int[] FtpDataPorts = Gateway.GetConfigPorts("FTP.PassiveData");
569
570 foreach (int Port in FtpPorts.Join(FtpsPorts).Join(FtpDataPorts))
571 {
572 Gateway.HttpServer.GetMTlsSettings(Port, out ClientCertificates ClientCertificate, out bool TrustClientCertificates);
573 PortSpecificMTlsSettings[Port] = new KeyValuePair<ClientCertificates, bool>(ClientCertificate, TrustClientCertificates);
574 }
575
576 this.ftpServer = new FtpServer(Gateway.Domain, FtpPorts, FtpsPorts,
577 FtpDataPorts, Gateway.Certificate, !(Gateway.Certificate is null),
578 ClientCertificates.NotUsed, false, PortSpecificMTlsSettings, true,
579 this.persistenceLayer);
580
581 Types.SetModuleParameter("FTP_SERVER", this.ftpServer);
582
583 this.ftpServer.ExternalSniffers.Add(new XmlFileSniffer(appData + "FTP" + Path.DirectorySeparatorChar +
584 "FTP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml", appData + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
585 7, BinaryPresentationMethod.ByteCount));
586
587 #endregion
588
589 this.httpServer = Types.TryGetModuleParameter<HttpServer>("HTTP");
590
591 this.xmppServer = await XmppServer.Create(Gateway.Domain, Gateway.AlternativeDomains,
592 Gateway.GetConfigPorts("XMPP.C2S"),
593 Gateway.GetConfigPorts("XMPP.S2S"),
594 Gateway.Certificate, !(Gateway.Certificate is null), this.persistenceLayer, this.smtpServer, this.httpServer);
595
596 this.xmppServer.GetParentConnection += (sender, e) =>
597 {
598 e.Client = Gateway.XmppClient;
599 return Task.CompletedTask;
600 };
601
602 Types.SetModuleParameter("XMPP_SERVER", this.xmppServer);
603
604 this.xmppServer.ClientConnectionAdded += this.XmppServer_ClientConnectionAdded;
605 this.xmppServer.ClientConnectionRemoved += this.XmppServer_ClientConnectionRemoved;
606 this.xmppServer.ClientConnectionUpdated += this.XmppServer_ClientConnectionUpdated;
607 this.xmppServer.ServerConnectionAdded += this.XmppServer_ServerConnectionAdded;
608 this.xmppServer.ServerConnectionRemoved += this.XmppServer_ServerConnectionRemoved;
609 this.xmppServer.ServerConnectionUpdated += this.XmppServer_ServerConnectionUpdated;
610
611 Gateway.ConcentratorServer.SensorServer.AssignAuthority += this.SensorServer_AssignAuthority;
612 Gateway.XmppClient.RegisterMessageHandler("Delivered", ContractsClient.NamespaceOnboarding, TransferIdDelivered, true);
613
614 ContractsClient.GetLocalSchema += this.ContractsClient_GetLocalSchema;
615 ContractsClient.ValidateLocalSignature += this.ContractsClient_ValidateLocalSignature;
616 ContractsClient.GetLocalPublicKey += this.ContractsClient_GetLocalPublicKey;
617
618 #region Neuro-Foundation V1 handlers
619
620 this.xmppServer.RegisterIqGetHandler("req", NamespaceSynchronizationNeuroFoundationV1, ClockSynchronization.RequestHandler, true);
621 this.xmppServer.RegisterIqGetHandler("sourceReq", NamespaceSynchronizationNeuroFoundationV1, ClockSynchronization.ClockSourceReq, false);
622
623 #endregion
624
625 #region IEEE V1 handlers
626
627 this.xmppServer.RegisterIqGetHandler("req", NamespaceSynchronizationIeeeV1, ClockSynchronization.RequestHandler, true);
628 this.xmppServer.RegisterIqGetHandler("sourceReq", NamespaceSynchronizationIeeeV1, ClockSynchronization.ClockSourceReq, false);
629
630 #endregion
631
632 #region XSF handlers
633
634 #endregion
635
636 this.xmppServer.RegisterIqGetHandler("dns", NamespaceDnsOverXmpp, this.DnsRequest, true);
637
638 this.xmppServer.RegisterIqGetHandler("jwt", NamespaceJwt, this.JwtRequest, true);
639
640 this.persistenceLayer.Server = this.xmppServer;
641
642 this.pubSubComponent = await PubSubComponent.Create(this.xmppServer, "pubsub", "Publish/Subscribe service");
643 this.mucComponent = new MultiUserChatComponent(this.xmppServer, "muc", "Multi-User Chat service");
644 this.eventLogComponent = new EventLogComponent(this.xmppServer, "log", "Event Log");
645 this.serviceRegistrationComponent = new ServiceRegistrationComponent(this.xmppServer, "services", "Service Registry");
646 this.geoComponent = new GeoSpatialComponent(this.xmppServer, "geo", "Geo-Spatial information");
647 this.provisioningComponent = new ProvisioningComponent(this.xmppServer, "provisioning", "Thing Registry and Provisioning Server", this.geoComponent);
648 this.legalComponent = new LegalComponent(this.xmppServer, "legal", "Smart Contracts", this.httpServer, Path.Combine(Gateway.AppDataFolder, "Attachments"), null, this.pubSubComponent, this.geoComponent);
649 this.eDalerComponent = new EDalerComponent(this.xmppServer, "edaler", "eDaler", this.legalComponent);
650 this.legalComponent.EDaler = this.eDalerComponent;
651
652 await this.geoComponent.Load();
653
654 int[] Socks5Ports = Gateway.GetConfigPorts("SOCKS5");
655
656 if (Socks5Ports.Length > 0)
657 this.socks5Component = new Socks5Component(this.xmppServer, "socks5", Socks5Ports[0]);
658
659 string FileName = Path.Combine(Gateway.AppDataFolder, "HoneyPotResources.txt");
660 if (File.Exists(FileName))
661 {
662 try
663 {
664 s = File.ReadAllText(FileName);
665 string[] Resources = s.Split(CommonTypes.CRLF, StringSplitOptions.RemoveEmptyEntries);
666 Dictionary<string, bool> VulnerableResources = new Dictionary<string, bool>(StringComparer.InvariantCultureIgnoreCase);
667
668 foreach (string Resource in Resources)
669 {
670 if (Resource.StartsWith("/"))
671 VulnerableResources[Resource[1..]] = true;
672 }
673
674 this.vulnerableResources = VulnerableResources;
675
676 Gateway.Root.FileNotFound += this.CheckForHoneyPotResources;
677 }
678 catch (Exception ex)
679 {
680 Log.Exception(ex);
681 }
682 }
683
684 WebServices.Agent.Account.DomainInfo TempResource = new WebServices.Agent.Account.DomainInfo();
685 this.defaultAuthenticationSchemesAdmin = TempResource.CreateAuthenticationSchemes(Users.Source);
686 this.defaultAuthenticationSchemesXmpp = TempResource.AuthenticationSchemes;
687
688 if (!(this.httpServer is null))
689 {
690 this.httpServer.Register(new HttpConfigurableFileResource("/.well-known/security.txt",
691 Path.Combine(Gateway.RootFolder, ".well-known", "security.txt"), PlainTextCodec.DefaultContentType, true));
692 this.httpServer.Register(new HttpConfigurableFileResource("/firebase-messaging-sw.js",
693 Path.Combine(Gateway.RootFolder, "firebase-messaging-sw.js"), JavaScriptCodec.DefaultContentType, true));
694
695 this.httpServer.Register(this.webClient1 = new BoshWebClientResource(this.xmppServer, this.httpServer, "/webclient"));
696 this.httpServer.Register(this.webClient2 = new BoshWebClientResource(this.xmppServer, this.httpServer, "/http-bind"));
697 this.httpServer.Register(this.webSocketClient = new WebSocketClientResource(this.xmppServer, "/xmpp-websocket"));
698 this.httpServer.Register(this.webHostMetaDataXml = new WebHostMetaDataXml());
699 this.httpServer.Register(this.webHostMetaDataJson = new WebHostMetaDataJson());
700 this.httpServer.Register(this.packages = new HttpFolderResource("/Packages", PackagesFolder, false, false, false, false, HostDomainOptions.SameForAllDomains, new PackageUrlValidator()));
701 this.httpServer.Register(this.createApiKey = new CreateApiKey());
702 this.httpServer.Register(this.updateApiKey = new UpdateApiKey());
703 this.httpServer.Register(this.createAccount = new CreateAccount());
704 this.httpServer.Register(this.updateAccount = new UpdateAccount());
705 this.httpServer.Register(this.deleteAccount = new DeleteAccount());
706 this.httpServer.Register(this.updatePubSubNode = new UpdatePubSubNode());
707 this.httpServer.Register(this.deletePubSubNode = new DeletePubSubNode());
708 this.httpServer.Register(this.loadMoreItems = new LoadMoreItems());
709 this.httpServer.Register(this.feedback = new Feedback());
710 this.httpServer.Register(this.requestAccount = new RequestAccount());
711 this.httpServer.Register(this.requestApiKey = new RequestApiKey());
712 this.httpServer.Register(this.searchEvents = new SearchEvents());
713 this.httpServer.Register(this.sendMessage = new SendOperatorMessage());
714 this.httpServer.Register(this.setContractState = new SetContractState());
715 this.httpServer.Register(this.publisherAvatar = new PublisherAvatar());
716 this.httpServer.Register(this.legalIdentityStateChanged = new LegalIdentityStateChanged());
717 this.httpServer.Register(this.qr = new QR());
718 this.httpServer.Register(this.uploadPackage = new UploadPackage());
719 this.httpServer.Register(this.uploadSignature = new UploadSignature());
720 this.httpServer.Register(this.deletePackage = new DeletePackage());
721 this.httpServer.Register(this.installPackage = new InstallPackage());
722 this.httpServer.Register(this.uninstallPackage = new UninstallPackage());
723 this.httpServer.Register(this.dnsOverHttps = new DnsOverHttpsResource());
724 this.httpServer.Register(this.validateLegalId = new ValidateLegalId());
725 this.httpServer.Register(this.validateContract = new ValidateContract());
726 this.httpServer.Register(this.mfa = new MultiFactorAuthentication());
727 this.httpServer.Register(this.quickLogin = new QuickLogin());
728 this.httpServer.Register(this.remoteLogin = new RemoteLogin());
729 this.httpServer.Register(this.whatsMyEndpointResource = new WhatsMyEndpointResource("/WMEP"));
730 this.httpServer.Register(this.addNote = new AddNote());
731 this.httpServer.Register(this.publicTokenView = new PublicTokenView());
732 this.httpServer.Register(this.publicTokenHistoryView = new PublicTokenHistoryView());
733 this.httpServer.Register(this.publicNodes = new PublicNodes());
734 this.httpServer.Register(this.vault = new Vault());
735 this.httpServer.Register(this.killMachine = new KillMachine());
736 this.httpServer.Register(this.httpProxy = new HttpReverseProxyResource("/HttpProxy",
737 TimeSpan.FromSeconds(30), false,
739 this.chatFile = this.httpServer.Register("/ChatFile", this.ChatFileDownload, false, true);
740 this.httpxServer = new HttpxServer(this.xmppServer, this.httpServer, 8192);
741
743 this.httpServer.Register(this.xmppOverHttp = new XmppOverHttp("/HTTPX"));
744
745 if (!(this.httpServer.Sniffers is null))
746 {
747 this.xmppServer.C2sSniffers.Add(new XmlFileSniffer(appData + "XMPP_C2S" + Path.DirectorySeparatorChar +
748 "XMPP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml", appData + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
749 7, BinaryPresentationMethod.ByteCount));
750
751 this.xmppServer.ClientSnifferPath = appData + "XMPP_C2S" + Path.DirectorySeparatorChar +
752 "%ENDPOINT%" + Path.DirectorySeparatorChar + "XMPP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml";
753
754 this.xmppServer.S2sSniffers.Add(new XmlFileSniffer(appData + "XMPP_S2S" + Path.DirectorySeparatorChar +
755 "XMPP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml", appData + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
756 7, BinaryPresentationMethod.ByteCount));
757
758 this.xmppServer.DomainSnifferPath = appData + "XMPP_S2S" + Path.DirectorySeparatorChar +
759 "%DOMAIN%" + Path.DirectorySeparatorChar + "XMPP Server Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml";
760 }
761
762 bool Encrypted = true;
763 int[] Ports = this.httpServer.OpenHttpsPorts;
764 if (Ports.Length == 0)
765 {
766 Encrypted = false;
767 Ports = this.httpServer.OpenHttpPorts;
768 }
769
770 if (Ports.Length > 0)
771 {
772 StringBuilder Url = new StringBuilder();
773 int Port = Ports[0];
774
775 Url.Append("http");
776 if (Encrypted)
777 Url.Append('s');
778 Url.Append("://");
779 Url.Append(Gateway.Domain);
780
781 if (Encrypted)
782 {
784 {
785 Url.Append(':');
786 Url.Append(Port.ToString());
787 }
788 }
789 else
790 {
792 {
793 Url.Append(':');
794 Url.Append(Port.ToString());
795 }
796 }
797
798 string Root = Url.ToString();
799
800 Url.Append("/HttpUpload");
801
802 this.httpFileUploadSettings = new HttpFileUploadSettings()
803 {
804 MaxFileSize = 1024 * 1024 * 20,
805 MaxFilesPerMinute = 10,
806 MaxBytesPerMinute = 1024 * 1024 * 50,
807 FileLifetime = TimeSpan.FromMinutes(10),
808 FileFolder = Path.Combine(appData, "HttpUpload"),
809 HttpFolder = Url.ToString(),
810 BackupFolder = await Export.GetFullExportFolderAsync(),
811 KeyFolder = await Export.GetFullKeyExportFolderAsync(),
812 EncryptedStorageFolder = Path.Combine(appData, "EncryptedStorage"),
813 EncryptedStorageRoot = Root + "/EncryptedStorage",
814 InternalTransferFolder = Path.Combine(appData, "InternalTransfer"),
815 InternalTransferRoot = Root + "/InternalTransfer",
816 PubSubStorageFolder = Path.Combine(appData, "PubSubStorage"),
817 PubSubStorageRoot = Root + "/PubSubStorage"
818 };
819
820 this.httpFileUploadComponent = new HttpFileUploadComponent(this.xmppServer, "upload", this.httpFileUploadSettings);
821 this.httpFileUploadResource = new XmppFileUploadResource("/HttpUpload", this.httpFileUploadComponent, Encrypted);
822 this.httpEncryptedFileUploadResource = new HttpFolderResource("/EncryptedStorage", this.httpFileUploadSettings.EncryptedStorageFolder, false, false, true, false);
823 this.httpPubSubFileUploadResource = new HttpFolderResource("/PubSubStorage", this.httpFileUploadSettings.PubSubStorageFolder, false, false, true, false);
824 this.httpInternalTransferUploadResource = new HttpFolderResource("/InternalTransfer", this.httpFileUploadSettings.InternalTransferFolder, false, false, false, false, new InternalTransferAccess());
825 this.httpServer.Register(this.httpFileUploadResource);
826 this.httpServer.Register(this.httpEncryptedFileUploadResource);
827 this.httpServer.Register(this.httpPubSubFileUploadResource);
828 this.httpServer.Register(this.httpInternalTransferUploadResource);
829
830 Export.OnExportFolderUpdated += this.Export_OnExportFolderUpdated;
831 Export.OnExportKeyFolderUpdated += this.Export_OnExportKeyFolderUpdated;
832 }
833
834 HttpReverseProxyResource[] ReverseProxies = this.httpServer.GetRegisteredResources<HttpReverseProxyResource>();
835
836 foreach (HttpReverseProxyResource ProxyResource in ReverseProxies)
837 {
838 if (ProxyResource.UserSessions)
839 {
840 this.ssoTokens ??= new Cache<string, string>(int.MaxValue, TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(5));
841
843 ProxyResource.BeforeForwardRequest += this.ProxyResource_AddSsoInformationEncrypted;
844 else
845 ProxyResource.BeforeForwardRequest += this.ProxyResource_AddSsoInformationUnencrypted;
846 }
847 }
848 }
849
850 await this.DeleteOldItems();
851
852 Gateway.OnNewCertificate += this.Gateway_OnNewCertificate;
853 Gateway.ScheduleEvent(this.Service_NewDay, DateTime.Today.AddDays(1), null);
854
855 if (Types.TryGetModuleParameter("Concentrator", out this.concentratorServer))
856 {
857 await this.concentratorServer.Register(this.connectionsSource = new ConnectionsSource());
858 await this.concentratorServer.Register(new GatewayConfigSource());
859 await this.concentratorServer.Register(new ProgramDataSource());
860 }
861
865
866 foreach (PubSubNode Node in await Database.Find<PubSubNode>(new FilterAnd(
867 new FilterFieldEqualTo("Service", string.Empty), new FilterFieldEqualTo("IsRoot", true),
868 new FilterFieldEqualTo("PublishOnWeb", true))))
869 {
870 this.WebNodeStatus(Node.Name, Node.PublishOnWeb);
871 }
872
873 await this.CheckRegistration();
874
875 if (!(Gateway.ProvisioningClient is null))
876 Gateway.ProvisioningClient.ManagePresenceSubscriptionRequests = false;
877
878 if (!(Gateway.SoftwareUpdateClient is null))
879 {
880 Gateway.SoftwareUpdateClient.OnSoftwareUpdated += this.SoftwareUpdateClient_OnSoftwareUpdated;
881 Gateway.SoftwareUpdateClient.OnSoftwareValidation += this.SoftwareUpdateClient_OnSoftwareValidation;
882 Gateway.SoftwareUpdateClient.OnSoftwareDownloaded += this.SoftwareUpdateClient_OnSoftwareDownloaded;
883 Gateway.SoftwareUpdateClient.OnSoftwareDeleted += this.SoftwareUpdateClient_OnSoftwareDeleted;
884 Gateway.SoftwareUpdateClient.OnDownloadedSoftwareDeleted += this.SoftwareUpdateClient_OnDownloadedSoftwareDeleted;
885 }
886
887 Gateway.XmppClient.OnStateChanged += this.XmppClient_OnStateChanged;
888 Gateway.XmppClient.OnChatMessage += this.XmppClient_OnChatMessage;
889
890 if (!(Gateway.MucClient is null))
891 {
892 Gateway.XmppClient.OnGroupChatMessage += this.XmppClient_OnGroupChatMessage;
893 Gateway.XmppClient.OnValidateSender += this.XmppClient_OnValidateSender;
894
895 Gateway.MucClient.RoomInvitationReceived += this.MucClient_RoomInvitationReceived;
896 Gateway.MucClient.DirectInvitationReceived += this.MucClient_DirectInvitationReceived;
897 Gateway.MucClient.OccupantPresence += this.MucClient_OccupantPresence;
898 Gateway.MucClient.PrivateMessageReceived += this.MucClient_PrivateMessageReceived;
899 Gateway.MucClient.RoomDestroyed += this.MucClient_RoomDestroyed;
900
901 await this.LoadPermanentRooms();
902 }
903
904 await Gateway.XmppClient.SetPresence(Networking.XMPP.Availability.Chat);
905
906 TP = await RuntimeSettings.GetAsync(AutoInstallTimeParameterName, DateTime.MinValue);
907 if (TP > DateTime.MinValue)
908 autoUpdateTP = Gateway.ScheduleEvent(this.UpdateSoftware, TP, null);
909
910 StringBuilder sb = new StringBuilder();
911 SortedDictionary<string, bool> AlphabeticalOrder = new SortedDictionary<string, bool>();
912
913 foreach (Assembly A in Types.Assemblies)
914 AlphabeticalOrder[A.FullName] = true;
915
916 foreach (string Name in AlphabeticalOrder.Keys)
917 sb.AppendLine(Name);
918
919 string H = Hashes.ComputeSHA256HashString(Encoding.UTF8.GetBytes(sb.ToString()));
920 string H2 = await RuntimeSettings.GetAsync("BrokerVersion", string.Empty);
921 bool Updated = H != H2;
922
923 if (Gateway.HasDomain)
924 {
925 Now = DateTime.Now;
926
927 sb.Clear();
928
929 sb.Append("XMPP server at [`");
930 sb.Append(Gateway.Domain);
931 sb.Append("`](http://");
932 sb.Append(Gateway.Domain);
933 sb.Append("/) ");
934
935 if (Updated)
936 sb.Append("updated and ");
937
938 sb.Append("restarted at ");
939 sb.Append(Now.ToShortDateString());
940 sb.Append(", ");
941 sb.Append(Now.ToLongTimeString());
942 sb.Append('.');
943
944 if (Updated)
945 sb.Append(" [Release Notes](https://lab.tagroot.io/ReleaseNotes)");
946
947 await Gateway.SendNotification(sb.ToString());
948 }
949
950 if (Updated)
951 await RuntimeSettings.SetAsync("BrokerVersion", H);
952
953 await LegalComponent.CheckLegalIdentityReferences();
954 await PaiwiseProcessor.QueueUnprocessedPayments(this.eDalerComponent);
955 await ComponentSynchronization.QueueUnprocessedMessages();
956 await MarketplaceProcessor.LoadActiveItems(this.eDalerComponent);
957 await StateMachineProcessor.ModuleStarted(this.legalComponent, this.eDalerComponent);
958
959 if (Gateway.HasDomain && DomainConfiguration.Instance.UseEncryption) // i.e. accessible from the Internet using domain name.
960 Gateway.HttpxProxy.PostResource = this.xmppOverHttp;
961 /*{
962 IPAddress MyExternalIp = null;
963 IPAddress MyLocalIp = null;
964
965 try
966 {
967 WebPoster Poster = new WebPoster();
968
969 ContentResponse Response = await InternetContent.PostAsync(new Uri("https://" + XmppConfiguration.Instance.Host + "/WMEP"), null);
970 if (!Response.HasError && Response.Decoded is string PlainText)
971 {
972 int i = PlainText.LastIndexOf(':');
973 if (i > 0)
974 {
975 PlainText = PlainText.Substring(0, i);
976 if (IPAddress.TryParse(PlainText, out IPAddress Address))
977 {
978 if (InternetGatewayRegistrator.IsPublicAddress(Address))
979 {
980 Gateway.HttpxProxy.PostResource = "https://" + MyExternalIp.ToString() + "/HTTPX";
981
982 using (TcpClient Client = new TcpClient())
983 {
984 await Client.ConnectAsync(XmppConfiguration.Instance.Host, XmppConfiguration.Instance.Port);
985 if (Client.Client.LocalEndPoint is IPEndPoint Endpoint)
986 {
987 MyLocalIp = Endpoint.Address;
988 MyExternalIp = Address;
989 }
990
991 Client.Close();
992 }
993 }
994 }
995 }
996 }
997 }
998 catch (Exception)
999 {
1000 // Unable to get public IP from parent neuron.
1001 }
1002 }*/
1003
1004 Gateway.ScheduleEvent(this.ImportIpLocalizationDatabase, DateTime.Now.AddMinutes(15), null);
1005
1006 Gateway.OnTerminate += this.Gateway_OnTerminate;
1007
1008 await CheckFullTestSearchIndices();
1009
1010 Log.Informational("XMPP Server started.");
1011 }
1012 catch (Exception ex)
1013 {
1014 Log.Exception(ex);
1015 }
1016 }
1017
1021 public HttpAuthenticationScheme[] DefaultAuthenticationSchemesAdmin => this.defaultAuthenticationSchemesAdmin;
1022
1026 public HttpAuthenticationScheme[] DefaultAuthenticationSchemesXmpp => this.defaultAuthenticationSchemesXmpp;
1027
1031 public static ISerializerContext FullSerialization => Ledger.Provider as Persistence.NeuroLedger.NeuroLedgerProvider;
1032
1036 public static ISerializerContext NormalizedSerialization => Database.Provider as FilesProvider;
1037
1038 private static async Task CheckFullTestSearchIndices()
1039 {
1040 bool ReindexIdentities = await Search.SetFullTextSearchIndexCollection(
1041 "FTS_Identities", "LegalIdentities");
1042
1043 if (await Search.AddFullTextSearch("LegalIdentities",
1044 new PropertyDefinition(typeof(PropertiesTokenizer).FullName, "Properties")))
1045 {
1046 ReindexIdentities = true;
1047 }
1048
1049 bool ReindexContracts = await Search.SetFullTextSearchIndexCollection(
1050 "FTS_Contracts", "Contracts");
1051
1052 if (await Search.AddFullTextSearch("Contracts",
1053 new PropertyDefinition(typeof(RolesTokenizer).FullName, "Roles"),
1054 new PropertyDefinition(typeof(ParametersTokenizer).FullName, "Parameters"),
1055 new PropertyDefinition(typeof(HumanReadableTextsTokenizer).FullName, "ForHumans")))
1056 {
1057 ReindexContracts = true;
1058 }
1059
1060 bool ReindexTokens = await Search.SetFullTextSearchIndexCollection(
1061 "FTS_Tokens", "NeuroFeatureTokens");
1062
1063 if (await Search.AddFullTextSearch("NeuroFeatureTokens",
1064 new PropertyDefinition(typeof(NeuroFeatureTokenizer).FullName, "FriendlyName"),
1065 new PropertyDefinition(typeof(NeuroFeatureTokenizer).FullName, "Category"),
1066 new PropertyDefinition(typeof(MarkdownTokenizer).FullName, "Description")))
1067 {
1068 ReindexTokens = true;
1069 }
1070
1071 bool ReindexAccounts = await Search.SetFullTextSearchIndexCollection(
1072 "FTS_Accounts", "BrokerAccounts");
1073
1074 if (await Search.AddFullTextSearch("BrokerAccounts",
1075 new PropertyDefinition(typeof(AccountTokenizer).FullName, "UserName"),
1076 new PropertyDefinition(typeof(AccountTokenizer).FullName, "EMail"),
1077 new PropertyDefinition(typeof(AccountTokenizer).FullName, "PhoneNr"),
1078 new PropertyDefinition(typeof(AccountTokenizer).FullName, "FirstName"),
1079 new PropertyDefinition(typeof(AccountTokenizer).FullName, "MiddleNames"),
1080 new PropertyDefinition(typeof(AccountTokenizer).FullName, "LastNames"),
1081 new PropertyDefinition(typeof(AccountTokenizer).FullName, "PersonalNumber"),
1082 new PropertyDefinition(typeof(AccountTokenizer).FullName, "Country"),
1083 new PropertyDefinition(typeof(AccountTokenizer).FullName, "OrgNumber"),
1084 new PropertyDefinition(typeof(AccountTokenizer).FullName, "OrgName"),
1085 new PropertyDefinition(typeof(AccountTokenizer).FullName, "OrgRole"),
1086 new PropertyDefinition(typeof(AccountTokenizer).FullName, "OrgDepartment"),
1087 new PropertyDefinition(typeof(AccountTokenizer).FullName, "OrgCountry")))
1088 {
1089 ReindexAccounts = true;
1090 }
1091
1092 if (ReindexIdentities)
1093 await ReindexFullTextSearchIndex("FTS_Identities", "LegalIdentities");
1094
1095 if (ReindexContracts)
1096 await ReindexFullTextSearchIndex("FTS_Contracts", "Contracts");
1097
1098 if (ReindexTokens)
1099 await ReindexFullTextSearchIndex("FTS_Tokens", "NeuroFeatureTokens");
1100
1101 if (ReindexAccounts)
1102 await ReindexFullTextSearchIndex("FTS_Accounts", "BrokerAccounts");
1103 }
1104
1105 private static async Task ReindexFullTextSearchIndex(string IndexName, string CollectionName)
1106 {
1107 long NrObjects = await Search.ReindexCollection(IndexName);
1108
1109 Log.Notice("Full Text Search Index regenerated.", IndexName,
1110 new KeyValuePair<string, object>("Collection", CollectionName),
1111 new KeyValuePair<string, object>("Index", IndexName),
1112 new KeyValuePair<string, object>("NrObjects", NrObjects));
1113 }
1114
1115 private async Task SensorServer_AssignAuthority(object Sender, Networking.XMPP.Events.AuthorityEventArgs e)
1116 {
1117 if (await IsAdmin(e.BareJid))
1118 e.Authority = new MaximumAuthority(e.BareJid);
1119 else if (!string.IsNullOrEmpty(Gateway.ProvisioningClient?.OwnerJid) &&
1120 Gateway.ProvisioningClient.OwnerJid == e.BareJid)
1121 {
1122 e.Authority = new MaximumAuthority(e.BareJid);
1123 }
1124 else
1125 e.Authority = new NoAuthority(e.BareJid);
1126 }
1127
1128 private Task Gateway_OnTerminate(object Sender, EventArgs e)
1129 {
1130 System.Timers.Timer CheckTimer = new System.Timers.Timer(80000);
1131 CheckTimer.Elapsed += this.CheckRunningTasks;
1132
1133 System.Timers.Timer CloseTimer = new System.Timers.Timer(90000);
1134 CloseTimer.Elapsed += this.CloseProcess;
1135
1136 System.Timers.Timer KillTimer = new System.Timers.Timer(110000);
1137 CloseTimer.Elapsed += this.KillProcess;
1138
1139 return Task.CompletedTask;
1140 }
1141
1142 private void CheckRunningTasks(object Sender, System.Timers.ElapsedEventArgs e)
1143 {
1145 {
1147 StringBuilder sb = new StringBuilder();
1148
1149 sb.AppendLine("Active asynchronous processors stopping the service shutdown:");
1150
1151 foreach (IAsyncProcessor AsyncProcessor in Processors)
1152 {
1153 sb.AppendLine();
1154 sb.Append("* ");
1155 sb.Append(AsyncProcessor.Name);
1156 }
1157
1158 Log.Alert(sb.ToString());
1159
1161 }
1162 }
1163
1164 private void CloseProcess(object Sender, System.Timers.ElapsedEventArgs e)
1165 {
1166 Process.GetCurrentProcess().Close();
1167 }
1168
1169 private void KillProcess(object Sender, System.Timers.ElapsedEventArgs e)
1170 {
1171 Process.GetCurrentProcess().Kill();
1172 }
1173
1174 private async Task CheckForHoneyPotResources(object Sender, FileNotFoundEventArgs e)
1175 {
1177 if (ex is null)
1178 return; // Already processed.
1179
1180 try
1181 {
1182 string s = e.Request.SubPath;
1183 if (s.StartsWith("/"))
1184 s = s[1..];
1185
1186 if (!(this.vulnerableResources?.ContainsKey(s) ?? false))
1187 return;
1188
1189 e.Exception = null;
1190
1191 bool Blocked = await Gateway.LoginAuditor.ProcessLoginFailure(e.Request.RemoteEndPoint, "HTTP", DateTime.Now,
1192 "Scanning for vulnerable web resources.");
1193
1194 Log.Warning("Honey-pot resource requested.", "/" + s, e.Request.RemoteEndPoint, "HoneyPot",
1195 new KeyValuePair<string, object>("Blocked", Blocked));
1196 e.Request.Session?.Add("HoneyPotBlocked", Blocked);
1197
1198 await Task.Delay(60000); // Wait one minute before returning response. (After 2 minutes, the request is removed from the server.)
1199
1200 string ContentType = ex.ContentType;
1201 byte[] Content = ex.Content;
1202
1203 if (Content is null)
1204 {
1205 object ContentObject = await ex.GetContentObjectAsync();
1206
1207 if (ContentObject is null)
1208 {
1209 Content = Encoding.UTF8.GetBytes(ex.Message);
1210 ContentType = "text/plain; charset=utf-8";
1211 }
1212 else
1213 {
1214 ContentResponse P = await InternetContent.EncodeAsync(ContentObject, Encoding.UTF8);
1215 if (P.HasError)
1216 {
1217 Log.Exception(P.Error);
1218 return;
1219 }
1220
1221 Content = P.Encoded;
1223 }
1224 }
1225
1226 string Html = await Gateway.GetCustomErrorHtml(e.Request, "HoneyPot.md", ContentType, Content);
1227 if (!string.IsNullOrEmpty(Html))
1228 {
1229 e.Response.StatusCode = ex.StatusCode;
1230 e.Response.StatusMessage = ex.Message;
1231
1232 if (!(ex.HeaderFields is null))
1233 {
1234 foreach (KeyValuePair<string, string> P in ex.HeaderFields)
1235 {
1236 if (string.Compare(P.Key, "Content-Type", true) != 0)
1237 e.Response.SetHeader(P.Key, P.Value);
1238 }
1239 }
1240
1241 e.Response.ContentType = "text/html; charset=utf-8";
1242 await e.Response.Write(true, Encoding.UTF8.GetBytes(Html));
1243 await e.Response.SendResponse();
1244 }
1245 else
1246 await e.Response.SendResponse(ex);
1247
1248 await e.Response.DisposeAsync();
1249 }
1250 catch (Exception ex2)
1251 {
1252 Log.Exception(ex2);
1253 }
1254 }
1255
1256 private bool subscribingSWUpdates = false;
1257 private bool subscribedToPackages = false;
1258
1259 private async Task XmppClient_OnStateChanged(object Sender, Networking.XMPP.XmppState NewState)
1260 {
1261 if (NewState == Networking.XMPP.XmppState.Connected)
1262 {
1263 if (!(Gateway.SoftwareUpdateClient is null) &&
1264 !this.subscribingSWUpdates &&
1265 !this.xmppServer.IsServerDomain(Gateway.XmppClient.Domain, true))
1266 {
1267 this.subscribingSWUpdates = true;
1268 try
1269 {
1270 DateTime LastUpdate = await RuntimeSettings.GetAsync("SW.Update.Last", DateTime.MinValue);
1271
1272 if ((DateTime.Now - LastUpdate).TotalDays >= 7 || !this.subscribedToPackages)
1273 {
1274 await Gateway.SoftwareUpdateClient.SubscribeAsync("*");
1275 this.subscribedToPackages = true;
1276
1277 foreach (Networking.XMPP.Software.Package Package in await Gateway.SoftwareUpdateClient.GetPackagesAsync())
1278 await this.CheckSoftwarePackage(Package, (P, MessageId) => Task.FromResult<string>(MessageId));
1279
1280 await RuntimeSettings.SetAsync("SW.Update.Last", DateTime.Now);
1281 }
1282 }
1283 catch (Exception ex)
1284 {
1285 Log.Exception(ex);
1286 }
1287 finally
1288 {
1289 this.subscribingSWUpdates = false;
1290 }
1291 }
1292
1293 await this.RejoinRooms();
1294 }
1295 }
1296
1297 private Task Gateway_OnNewCertificate(object Sender, IoTGateway.Events.CertificateEventArgs e)
1298 {
1299 this.smtpServer?.UpdateCertificate(e.Certificate);
1300 this.xmppServer?.UpdateCertificate(e.Certificate);
1301 this.ftpServer?.UpdateCertificate(e.Certificate);
1302
1303 return Task.CompletedTask;
1304 }
1305
1306 internal void WebNodeStatus(CaseInsensitiveString NodeName, bool Visible)
1307 {
1309
1310 if (Visible)
1311 {
1312 lock (this.webNodes)
1313 {
1314 if (this.webNodes.ContainsKey(NodeName))
1315 return;
1316
1317 WebNode = new WebNode(NodeName);
1318 this.webNodes[NodeName] = WebNode;
1319 }
1320
1321 try
1322 {
1323 this.httpServer.Register(WebNode);
1324 }
1325 catch (Exception ex)
1326 {
1327 Log.Exception(ex);
1328
1329 lock (this.webNodes)
1330 {
1331 this.webNodes.Remove(NodeName);
1332 }
1333 }
1334 }
1335 else
1336 {
1337 lock (this.webNodes)
1338 {
1339 if (!this.webNodes.TryGetValue(NodeName, out WebNode))
1340 return;
1341
1342 this.webNodes.Remove(NodeName);
1343 }
1344
1345 this.httpServer.Unregister(WebNode);
1346 }
1347 }
1348
1349 public async Task DoImportIpLocalizationDatabase()
1350 {
1351 string s = Path.Combine(PackagesFolder, Ip2LocalizationPackageName);
1352 await RuntimeSettings.SetAsync(s, DateTime.MinValue);
1353 this.ImportIpLocalizationDatabase(null);
1354 }
1355
1356 private static bool importingIp = false;
1357
1358 private async void ImportIpLocalizationDatabase(object State)
1359 {
1360 if (!importingIp)
1361 {
1362 importingIp = true;
1363 try
1364 {
1365 string s = Path.Combine(PackagesFolder, Ip2LocalizationPackageName);
1366
1367 if (File.Exists(s))
1368 {
1369 DateTime TP = File.GetLastWriteTimeUtc(s);
1370 DateTime Last = await RuntimeSettings.GetAsync(s, DateTime.MinValue);
1371
1372 if (TP > Last)
1373 {
1374 StringBuilder sb = new StringBuilder("Samples:=[");
1375 DateTime Start = DateTime.Now;
1376 long Count = 0;
1377 long Skipped = 0;
1378 bool First = true;
1379
1380 Log.Informational("Starting import of IP localization database.");
1381 await Gateway.SendNotification("Starting import of IP localization database.");
1382
1383 await Database.Clear("IP4Localization");
1384
1385 try
1386 {
1387 await FindIpAddress("127.0.0.1"); // Makes sure indices are created.
1388 }
1389 catch (Exception ex)
1390 {
1391 Log.Exception(ex);
1392 }
1393
1394 await Database.StartBulk();
1395 try
1396 {
1397 using FileStream fs = File.OpenRead(s);
1398 using StreamReader r = new StreamReader(fs);
1399
1400 while (!r.EndOfStream)
1401 {
1402 string Row = await r.ReadLineAsync();
1403 string[][] Records = CSV.Parse(Row);
1404
1405 foreach (string[] Record in Records)
1406 {
1407 if (Record.Length >= 8 &&
1408 uint.TryParse(Record[0], out uint IpRangeFrom) &&
1409 uint.TryParse(Record[1], out uint IpRangeTo) &&
1410 CommonTypes.TryParse(Record[6], out double Latitude) &&
1411 CommonTypes.TryParse(Record[7], out double Longitude))
1412 {
1413 Count++;
1414
1416 {
1417 RangeStart = IpRangeFrom,
1418 RangeEnd = IpRangeTo,
1419 CountryCode = Record[2],
1420 Country = Record[3],
1421 Region = Record[4],
1422 City = Record[5],
1423 Latitude = Latitude,
1424 Longitude = Longitude
1425 };
1426
1427 await Database.Insert(Rec);
1428
1429 if (Count % 10000 == 0)
1430 {
1431 await Database.EndBulk();
1432 await Database.StartBulk();
1433
1434 if (First)
1435 First = false;
1436 else
1437 sb.AppendLine(",");
1438
1439 sb.Append('[');
1440 sb.Append(Count.ToString());
1441 sb.Append(',');
1442 sb.Append(CommonTypes.Encode((DateTime.Now - Start).TotalSeconds));
1443 sb.Append(']');
1444 }
1445 }
1446 else
1447 Skipped++;
1448 }
1449 }
1450 }
1451 finally
1452 {
1453 await Database.EndBulk();
1454 }
1455
1456 if (!First)
1457 sb.AppendLine(",");
1458
1459 sb.Append('[');
1460 sb.Append(Count.ToString());
1461 sb.Append(',');
1462 sb.Append(CommonTypes.Encode((DateTime.Now - Start).TotalSeconds));
1463 sb.AppendLine("]];");
1464
1465 sb.AppendLine("Objects:=Samples[0,];");
1466 sb.AppendLine("Seconds:=Samples[1,];");
1467 sb.AppendLine("ObjOverTime:=plot2dcurve(Seconds,Objects);");
1468 sb.AppendLine("ObjOverTime.Title:=\"Objects imported over time\";");
1469 sb.AppendLine("ObjOverTime.LabelX:=\"Seconds\";");
1470 sb.AppendLine("ObjOverTime.LabelY:=\"#Objects\";");
1471
1472 sb.AppendLine("NrSamples:=count(Objects);");
1473 sb.AppendLine("DeltaSeconds:=Seconds[1..(NrSamples-1)]-Seconds[0..(NrSamples-2)];");
1474 sb.AppendLine("DeltaObjects:=Objects[1..(NrSamples-1)]-Objects[0..(NrSamples-2)];");
1475 sb.AppendLine("ObjectsPerSecond:=DeltaObjects./DeltaSeconds;");
1476 sb.AppendLine("SpeedOverTime:=plot2dcurve(Seconds[1..(NrSamples-1)],ObjectsPerSecond);");
1477 sb.AppendLine("SpeedOverTime.Title:=\"Objects/s imported over time\";");
1478 sb.AppendLine("SpeedOverTime.LabelX:=\"Seconds\";");
1479 sb.AppendLine("SpeedOverTime.LabelY:=\"#Objects/s\";");
1480
1481 File.WriteAllText(appData + "ImportTimes.script", sb.ToString());
1482
1483 Log.Informational("Import of IP localization database completed.",
1484 new KeyValuePair<string, object>("NrImported", Count),
1485 new KeyValuePair<string, object>("NrSkipped", Skipped));
1486
1487 await RuntimeSettings.SetAsync(s, TP);
1488
1489 await Gateway.SendNotification("Import of IP localization database completed. " +
1490 Count.ToString() + " records imported. " + Skipped.ToString() + " " +
1491 (Skipped == 1 ? "record" : "records") + " skipped.");
1492
1493 try
1494 {
1495 Expression Exp = new Expression(sb.ToString());
1497
1498 await Exp.EvaluateAsync(Variables);
1499
1500 if (Variables.TryGetVariable("ObjOverTime", out Variable v) && v.ValueObject is Graph ObjOverTime)
1501 await Gateway.SendNotification(ObjOverTime);
1502
1503 if (Variables.TryGetVariable("SpeedOverTime", out v) && v.ValueObject is Graph SpeedOverTime)
1504 await Gateway.SendNotification(SpeedOverTime);
1505 }
1506 catch (Exception ex)
1507 {
1508 Log.Exception(ex);
1509 }
1510 }
1511 }
1512 }
1513 catch (Exception ex)
1514 {
1515 Log.Error("Unable to import IP localization database.\r\n\r\n" + ex.Message, Ip2LocalizationPackageName);
1516 }
1517 finally
1518 {
1519 importingIp = false;
1520 }
1521 }
1522 }
1523
1524 private async Task LoginAuditor_AnnotateEndpoint(object Sender, AnnotateEndpointEventArgs e)
1525 {
1526 if (this.ip4LocalizationCache is null)
1527 return;
1528
1529 if (!this.ip4LocalizationCache.TryGetValue(e.RemoteEndPoint, out IP4Localization Location))
1530 {
1531 Location = await FindIpAddress(e.RemoteEndPoint);
1532 this.ip4LocalizationCache?.Add(e.RemoteEndPoint, Location);
1533 }
1534
1535 if (!(Location is null))
1536 {
1537 e.AddTag("City", Location.City);
1538 e.AddTag("Region", Location.Region);
1539 e.AddTag("Country", Location.Country);
1540 e.AddTag("Code", Location.CountryCode);
1541
1542 string s = "flag-" + Location.CountryCode?.ToLower();
1544 e.AddTag("Flag", ":" + s + ":");
1545
1546 e.AddTag("Latitude", Location.Latitude);
1547 e.AddTag("Longitude", Location.Longitude);
1548 e.AddTag("Acknowledgement", "This site or product includes IP2Location LITE data available from http://www.ip2location.com.");
1549 }
1550 }
1551
1557 public static Task<IP4Localization> FindIpAddress(string RemoteEndPoint)
1558 {
1559 RemoteEndPoint = RemoteEndPoint.RemovePortNumber();
1560
1561 if (!IPAddress.TryParse(RemoteEndPoint, out IPAddress Addr))
1562 return Task.FromResult<IP4Localization>(null);
1563
1564 return FindIpAddress(Addr);
1565 }
1566
1572 public static async Task<IP4Localization> FindIpAddress(IPAddress Addr)
1573 {
1574 if (Addr.AddressFamily != AddressFamily.InterNetwork)
1575 return null;
1576
1577 byte[] Bytes = Addr.GetAddressBytes();
1578
1579 uint Value = Bytes[0];
1580 Value <<= 8;
1581 Value |= Bytes[1];
1582 Value <<= 8;
1583 Value |= Bytes[2];
1584 Value <<= 8;
1585 Value |= Bytes[3];
1586
1587 try
1588 {
1589 foreach (IP4Localization Loc in await Database.Find<IP4Localization>(0, 1, new FilterFieldLesserOrEqualTo("RangeStart", Value), "-RangeStart"))
1590 {
1591 if (Value >= Loc.RangeStart && Value <= Loc.RangeEnd)
1592 return Loc;
1593 }
1594 }
1595 catch (Exception ex)
1596 {
1597 Log.Exception(ex);
1598 }
1599
1600 return null;
1601 }
1602
1608 public async static Task AppendRemoteEndPointToTable(StringBuilder Markdown, string RemoteEndPoint)
1609 {
1610 Markdown.Append("| Remote Endpoint: | ");
1611 Markdown.Append(RemoteEndPoint);
1612 Markdown.AppendLine(" |");
1613
1614 foreach (KeyValuePair<string, object> P in await LoginAuditor.Annotate(RemoteEndPoint))
1615 {
1616 Markdown.Append("| ");
1617 Markdown.Append(MarkdownDocument.Encode(P.Key));
1618 Markdown.Append(" | ");
1619 Markdown.Append(MarkdownDocument.Encode(P.Value?.ToString() ?? string.Empty));
1620 Markdown.AppendLine(" |");
1621 }
1622 }
1623
1624 private async void Service_NewDay(object P)
1625 {
1626 try
1627 {
1628 if (!(this.httpServer is null))
1629 {
1631
1633 {
1634 Start = Stat.LastStat,
1635 Timestamp = Stat.CurrentStat,
1636 NrBytesRx = Stat.NrBytesRx,
1637 NrBytesTx = Stat.NrBytesTx,
1638 NrCalls = Stat.NrCalls,
1639 CallsPerMethod = this.Convert(Stat.CallsPerMethod),
1640 CallsPerUserAgent = this.Convert(Stat.CallsPerUserAgent),
1641 CallsPerFrom = this.Convert(Stat.CallsPerFrom),
1642 CallsPerResource = this.Convert(Stat.CallsPerResource)
1643 };
1644
1646 }
1647
1648 if (!(this.xmppServer is null))
1649 {
1651
1653 {
1654 Start = Stat.LastStat,
1655 Timestamp = Stat.CurrentStat,
1656 NrBytesRx = Stat.NrBytesRx,
1657 NrBytesTx = Stat.NrBytesTx,
1658 NrStanzas = Stat.NrStanzas,
1659 StanzasPerStanzaType = this.Convert(Stat.StanzasPerStanzaType),
1660 StanzasPerFromDomain = this.Convert(Stat.StanzasPerFromDomain),
1661 StanzasPerToDomain = this.Convert(Stat.StanzasPerToDomain),
1662 StanzasPerFromBareJid = this.Convert(Stat.StanzasPerFromBareJid),
1663 StanzasPerToBareJid = this.Convert(Stat.StanzasPerToBareJid),
1664 StanzasPerNamespace = this.Convert(Stat.StanzasPerNamespace),
1665 StanzasPerFqn = this.Convert(Stat.StanzasPerFqn)
1666 };
1667
1669 }
1670
1671 if (!(this.eventStatistics is null))
1672 {
1673 EventStatistics Stat = this.eventStatistics.GetStatisticsSinceLast();
1674
1675 EventStatistic EventStatistic = new Sources.Reports.Events.Commands.EventStatistic()
1676 {
1677 Start = Stat.LastStat,
1678 Timestamp = Stat.CurrentStat,
1679 PerType = this.Convert(Stat.PerType),
1680 PerLevel = this.Convert(Stat.PerLevel),
1681 PerEventId = this.Convert(Stat.PerEventId),
1682 PerActor = this.Convert(Stat.PerActor),
1683 PerModule = this.Convert(Stat.PerModule),
1684 PerFacility = this.Convert(Stat.PerFacility),
1685 PerStackTrace = this.Convert(Stat.PerStackTrace)
1686 };
1687
1689 }
1690
1691 await this.DeleteOldItems();
1692 }
1693 catch (Exception ex)
1694 {
1695 Log.Exception(ex);
1696 }
1697 finally
1698 {
1699 Gateway.ScheduleEvent(this.Service_NewDay, DateTime.Today.AddDays(1), null);
1700 }
1701 }
1702
1703 private async Task DeleteOldItems()
1704 {
1705 try
1706 {
1707 DateTime TimeLimit = DateTime.Now.AddDays(-30); // TODO: Make configurable.
1708 int Nr, Nr2;
1709
1710 if (!(this.persistenceLayer is null))
1711 {
1712 try
1713 {
1714 Nr = await this.persistenceLayer.DeleteOfflineMessages(TimeLimit);
1715 if (Nr > 0)
1716 Log.Informational(Nr.ToString() + " offline messages deleted.");
1717 }
1718 catch (Exception ex)
1719 {
1720 Log.Error("Unable to delete old offline messages.\r\n\r\n" + ex.Message,
1721 string.Empty, string.Empty, string.Empty, EventLevel.Medium,
1722 string.Empty, string.Empty, ex.StackTrace);
1723 }
1724 }
1725
1726 if (!(this.xmppServer is null))
1727 {
1728 try
1729 {
1730 Nr = await this.xmppServer.DeleteOldMailContent(TimeLimit);
1731 if (Nr > 0)
1732 Log.Informational("Mail content from " + Nr.ToString() + " old messages deleted.");
1733 }
1734 catch (Exception ex)
1735 {
1736 Log.Error("Unable to delete old e-mail messages.\r\n\r\n" + ex.Message,
1737 string.Empty, string.Empty, string.Empty, EventLevel.Medium,
1738 string.Empty, string.Empty, ex.StackTrace);
1739 }
1740 }
1741
1742 if (!(this.pubSubComponent is null))
1743 {
1744 try
1745 {
1746 (Nr, Nr2) = await this.pubSubComponent.DeleteExpiredNodes();
1747 if (Nr > 0)
1748 Log.Informational(Nr.ToString() + " pubsub nodes (and " + Nr2.ToString() + " items) deleted.");
1749 }
1750 catch (Exception ex)
1751 {
1752 Log.Error("Unable to delete expired pubsub nodes.\r\n\r\n" + ex.Message,
1753 string.Empty, string.Empty, string.Empty, EventLevel.Medium,
1754 string.Empty, string.Empty, ex.StackTrace);
1755 }
1756 }
1757
1758 try
1759 {
1760 (int NrTokens, int NrEvents, int NrTags) = await NeuroFeatures.NeuroFeaturesProcessor.DeleteExpiredTokens(this.eDalerComponent);
1761 if (NrTokens > 0)
1762 Log.Informational(NrTokens.ToString() + " tokens, " + NrTags.ToString() + " tags and " + NrEvents.ToString() + " events deleted.");
1763 }
1764 catch (Exception ex)
1765 {
1766 Log.Error("Unable to delete expired tokens.\r\n\r\n" + ex.Message,
1767 string.Empty, string.Empty, string.Empty, EventLevel.Medium,
1768 string.Empty, string.Empty, ex.StackTrace);
1769 }
1770
1771 try
1772 {
1773 (int NrMachines, int NrEventHandlers, int NrCurrentStates, int NrSamples) =
1774 await StateMachineProcessor.DeleteExpiredMachines();
1775 if (NrMachines > 0)
1776 Log.Informational(NrMachines.ToString() + " state machines, " + NrEventHandlers.ToString() + " event handlers and " + NrSamples.ToString() + " samples deleted.");
1777 }
1778 catch (Exception ex)
1779 {
1780 Log.Error("Unable to delete expired state machines.\r\n\r\n" + ex.Message,
1781 string.Empty, string.Empty, string.Empty, EventLevel.Medium,
1782 string.Empty, string.Empty, ex.StackTrace);
1783 }
1784
1785 try
1786 {
1787 int NrAttachments = await AttachmentCache.DeleteOldAttachments();
1788 if (NrAttachments > 0)
1789 Log.Informational(NrAttachments.ToString() + " cached attachments deleted.");
1790 }
1791 catch (Exception ex)
1792 {
1793 Log.Error("Unable to delete old cached attachments.\r\n\r\n" + ex.Message,
1794 string.Empty, string.Empty, string.Empty, EventLevel.Medium,
1795 string.Empty, string.Empty, ex.StackTrace);
1796 }
1797
1798 try
1799 {
1800 int Count = await (this.legalComponent?.DeleteOldPreviewApplications(14) ?? Task.FromResult(0)); // TODO: Make configurable.
1801
1802 if (Count > 0)
1803 Log.Informational(Count.ToString() + " old preview files deleted.");
1804 }
1805 catch (Exception ex)
1806 {
1807 Log.Error("Unable to delete old preview files.\r\n\r\n" + ex.Message,
1808 string.Empty, string.Empty, string.Empty, EventLevel.Medium,
1809 string.Empty, string.Empty, ex.StackTrace);
1810 }
1811
1812 Gateway.DeleteOldFiles(this.httpFileUploadSettings.InternalTransferFolder, 7);
1813 }
1814 catch (Exception ex)
1815 {
1816 Log.Exception(ex);
1817 }
1818 }
1819
1820 private Sources.Reports.Events.Commands.Statistic[] Convert(Dictionary<string, Waher.Events.Statistics.Statistic> Statistics)
1821 {
1822 List<Sources.Reports.Events.Commands.Statistic> Result = new List<Sources.Reports.Events.Commands.Statistic>();
1823
1824 foreach (KeyValuePair<string, Waher.Events.Statistics.Statistic> P in Statistics)
1825 {
1826 Result.Add(new Sources.Reports.Events.Commands.Statistic()
1827 {
1828 Name = P.Key,
1829 Count = P.Value.Count,
1830 First = P.Value.First,
1831 Last = P.Value.Last
1832 });
1833 }
1834
1835 return Result.ToArray();
1836 }
1837
1838 public async Task Stop()
1839 {
1840 Log.Informational("XMPP Server shutting down.");
1841
1842 await PaiwiseProcessor.StopProcessingPayments();
1843 await StateMachineProcessor.ModuleStopped();
1844
1845 Gateway.OnNewCertificate -= this.Gateway_OnNewCertificate;
1846 Gateway.OnTerminate -= this.Gateway_OnTerminate;
1847
1848 if (!(Gateway.SoftwareUpdateClient is null))
1849 {
1850 Gateway.SoftwareUpdateClient.OnSoftwareUpdated -= this.SoftwareUpdateClient_OnSoftwareUpdated;
1851 Gateway.SoftwareUpdateClient.OnSoftwareValidation -= this.SoftwareUpdateClient_OnSoftwareValidation;
1852 Gateway.SoftwareUpdateClient.OnSoftwareDownloaded -= this.SoftwareUpdateClient_OnSoftwareDownloaded;
1853 Gateway.SoftwareUpdateClient.OnSoftwareDeleted -= this.SoftwareUpdateClient_OnSoftwareDeleted;
1854 Gateway.SoftwareUpdateClient.OnDownloadedSoftwareDeleted -= this.SoftwareUpdateClient_OnDownloadedSoftwareDeleted;
1855 }
1856
1857
1858 if (!(Gateway.MucClient is null))
1859 {
1860 Gateway.XmppClient.OnGroupChatMessage -= this.XmppClient_OnGroupChatMessage;
1861 Gateway.XmppClient.OnValidateSender -= this.XmppClient_OnValidateSender;
1862
1863 Gateway.MucClient.RoomInvitationReceived -= this.MucClient_RoomInvitationReceived;
1864 Gateway.MucClient.DirectInvitationReceived -= this.MucClient_DirectInvitationReceived;
1865 Gateway.MucClient.OccupantPresence -= this.MucClient_OccupantPresence;
1866 Gateway.MucClient.PrivateMessageReceived -= this.MucClient_PrivateMessageReceived;
1867 Gateway.MucClient.RoomDestroyed -= this.MucClient_RoomDestroyed;
1868 }
1869
1870 this.sampleTimer?.Dispose();
1871 this.sampleTimer = null;
1872
1873 await this.LeaveAllRooms();
1874
1875 LoginAuditor.AnnotateEndpoint -= this.LoginAuditor_AnnotateEndpoint;
1876 this.ip4LocalizationCache?.Dispose();
1877 this.ip4LocalizationCache = null;
1878
1879 if (!(this.eventStatistics is null))
1880 {
1881 Log.Unregister(this.eventStatistics);
1882 await this.eventStatistics.DisposeAsync();
1883 this.eventStatistics = null;
1884 }
1885
1886 instance = null;
1887
1888 if (!(this.vulnerableResources is null))
1889 {
1890 this.vulnerableResources = null;
1891
1892 if (!(Gateway.Root is null))
1893 Gateway.Root.FileNotFound -= this.CheckForHoneyPotResources;
1894 }
1895
1896 if (!(this.httpServer is null))
1897 {
1898 Export.OnExportFolderUpdated -= this.Export_OnExportFolderUpdated;
1899 Export.OnExportKeyFolderUpdated -= this.Export_OnExportKeyFolderUpdated;
1900
1901 this.httpServer.Unregister(this.webClient1);
1902 this.httpServer.Unregister(this.webClient2);
1903 this.httpServer.Unregister(this.webSocketClient);
1904 this.httpServer.Unregister(this.webHostMetaDataXml);
1905 this.httpServer.Unregister(this.webHostMetaDataJson);
1906 this.httpServer.Unregister(this.packages);
1907 this.httpServer.Unregister(this.createApiKey);
1908 this.httpServer.Unregister(this.updateApiKey);
1909 this.httpServer.Unregister(this.createAccount);
1910 this.httpServer.Unregister(this.updateAccount);
1911 this.httpServer.Unregister(this.deleteAccount);
1912 this.httpServer.Unregister(this.updatePubSubNode);
1913 this.httpServer.Unregister(this.deletePubSubNode);
1914 this.httpServer.Unregister(this.loadMoreItems);
1915 this.httpServer.Unregister(this.feedback);
1916 this.httpServer.Unregister(this.requestAccount);
1917 this.httpServer.Unregister(this.requestApiKey);
1918 this.httpServer.Unregister(this.searchEvents);
1919 this.httpServer.Unregister(this.sendMessage);
1920 this.httpServer.Unregister(this.setContractState);
1921 this.httpServer.Unregister(this.publisherAvatar);
1922 this.httpServer.Unregister(this.legalIdentityStateChanged);
1923 this.httpServer.Unregister(this.qr);
1924 this.httpServer.Unregister(this.uploadPackage);
1925 this.httpServer.Unregister(this.uploadSignature);
1926 this.httpServer.Unregister(this.deletePackage);
1927 this.httpServer.Unregister(this.installPackage);
1928 this.httpServer.Unregister(this.uninstallPackage);
1929 this.httpServer.Unregister(this.dnsOverHttps);
1930 this.httpServer.Unregister(this.validateLegalId);
1931 this.httpServer.Unregister(this.validateContract);
1932 this.httpServer.Unregister(this.mfa);
1933 this.httpServer.Unregister(this.quickLogin);
1934 this.httpServer.Unregister(this.remoteLogin);
1935 this.httpServer.Unregister(this.whatsMyEndpointResource);
1936 this.httpServer.Unregister(this.addNote);
1937 this.httpServer.Unregister(this.publicTokenView);
1938 this.httpServer.Unregister(this.publicTokenHistoryView);
1939 this.httpServer.Unregister(this.publicNodes);
1940 this.httpServer.Unregister(this.vault);
1941 this.httpServer.Unregister(this.killMachine);
1942 this.httpServer.Unregister(this.httpProxy);
1943 this.httpServer.Unregister(this.chatFile);
1944 this.httpServer.Unregister(this.xmppOverHttp);
1945
1946 this.httpxServer?.Dispose();
1947 this.httpxServer = null;
1948
1949 if (!(this.httpFileUploadResource is null))
1950 this.httpServer.Unregister(this.httpFileUploadResource);
1951
1952 if (!(this.httpEncryptedFileUploadResource is null))
1953 this.httpServer.Unregister(this.httpEncryptedFileUploadResource);
1954
1955 if (!(this.httpPubSubFileUploadResource is null))
1956 this.httpServer.Unregister(this.httpPubSubFileUploadResource);
1957
1958 if (!(this.httpInternalTransferUploadResource is null))
1959 this.httpServer.Unregister(this.httpInternalTransferUploadResource);
1960
1961 if (!(this.webNodes is null))
1962 {
1963 WebNode[] Nodes;
1964
1965 lock (this.webNodes)
1966 {
1967 Nodes = new WebNode[this.webNodes.Count];
1968 this.webNodes.Values.CopyTo(Nodes, 0);
1969 this.webNodes.Clear();
1970 }
1971
1972 foreach (WebNode WebNode in Nodes)
1973 this.httpServer.Unregister(WebNode);
1974 }
1975
1976 this.webClient1 = null;
1977 this.webClient2 = null;
1978 this.webSocketClient = null;
1979 this.webHostMetaDataXml = null;
1980 this.webHostMetaDataJson = null;
1981 this.createApiKey = null;
1982 this.updateApiKey = null;
1983 this.createAccount = null;
1984 this.updateAccount = null;
1985 this.deleteAccount = null;
1986 this.updatePubSubNode = null;
1987 this.deletePubSubNode = null;
1988 this.loadMoreItems = null;
1989 this.feedback = null;
1990 this.requestAccount = null;
1991 this.requestApiKey = null;
1992 this.searchEvents = null;
1993 this.sendMessage = null;
1994 this.setContractState = null;
1995 this.publisherAvatar = null;
1996 this.legalIdentityStateChanged = null;
1997 this.qr = null;
1998 this.uploadPackage = null;
1999 this.uploadSignature = null;
2000 this.deletePackage = null;
2001 this.installPackage = null;
2002 this.uninstallPackage = null;
2003 this.dnsOverHttps = null;
2004 this.validateLegalId = null;
2005 this.validateContract = null;
2006 this.mfa = null;
2007 this.quickLogin?.Dispose();
2008 this.quickLogin = null;
2009 this.remoteLogin = null;
2010 this.whatsMyEndpointResource = null;
2011 this.addNote = null;
2012 this.publicTokenView = null;
2013 this.publicTokenHistoryView = null;
2014 this.publicNodes = null;
2015 this.vault = null;
2016 this.killMachine = null;
2017 this.httpProxy = null;
2018 this.chatFile = null;
2019 this.xmppOverHttp = null;
2020 this.httpFileUploadResource = null;
2021 this.httpEncryptedFileUploadResource = null;
2022 this.httpPubSubFileUploadResource = null;
2023 this.httpInternalTransferUploadResource = null;
2024
2025 this.httpServer = null;
2026 }
2027
2028 this.httpFileUploadComponent?.Dispose();
2029 this.httpFileUploadComponent = null;
2030
2031 this.socks5Component?.Dispose();
2032 this.socks5Component = null;
2033
2034 this.serviceRegistrationComponent?.Dispose();
2035 this.serviceRegistrationComponent = null;
2036
2037 this.pubSubComponent?.Dispose();
2038 this.pubSubComponent = null;
2039
2040 this.mucComponent?.Dispose();
2041 this.mucComponent = null;
2042
2043 this.provisioningComponent?.Dispose();
2044 this.provisioningComponent = null;
2045
2046 this.legalComponent?.Dispose();
2047 this.legalComponent = null;
2048
2049 this.eDalerComponent?.Dispose();
2050 this.eDalerComponent = null;
2051
2052 this.geoComponent?.Dispose();
2053 this.geoComponent = null;
2054
2055 this.eventLogComponent?.Dispose();
2056 this.eventLogComponent = null;
2057
2058 if (!(this.xmppServer is null))
2059 {
2060 this.xmppServer.ClientConnectionAdded -= this.XmppServer_ClientConnectionAdded;
2061 this.xmppServer.ClientConnectionRemoved -= this.XmppServer_ClientConnectionRemoved;
2062 this.xmppServer.ClientConnectionUpdated -= this.XmppServer_ClientConnectionUpdated;
2063 this.xmppServer.ServerConnectionAdded -= this.XmppServer_ServerConnectionAdded;
2064 this.xmppServer.ServerConnectionRemoved -= this.XmppServer_ServerConnectionRemoved;
2065 this.xmppServer.ServerConnectionUpdated -= this.XmppServer_ServerConnectionUpdated;
2066
2067 this.xmppServer.Dispose();
2068 this.xmppServer = null;
2069 }
2070
2071 this.smtpServer?.Dispose();
2072 this.smtpServer = null;
2073
2074 this.ftpServer?.Dispose();
2075 this.ftpServer = null;
2076
2077 this.chatSessions?.Dispose();
2078 this.chatSessions = null;
2079
2080 this.consolidators?.Dispose();
2081 this.consolidators = null;
2082
2083 this.ssoTokens?.Dispose();
2084 this.ssoTokens = null;
2085
2086 salts?.Dispose();
2087 salts = null;
2088
2089 Log.Informational("XMPP Server shut down.");
2090 }
2091
2092 public static XmppServerModule Instance => instance;
2093 public static SmtpServer MailServer => instance?.smtpServer;
2094 public static FtpServer FileServer => instance?.ftpServer;
2095 public static Socks5Component Socks5 => instance?.socks5Component;
2096 public static ProvisioningComponent Provisioning => instance?.provisioningComponent;
2097 public static LegalComponent Legal => instance?.legalComponent;
2098 public static EDalerComponent EDaler => instance?.eDalerComponent;
2099 public static GeoSpatialComponent Geo => instance?.geoComponent;
2100 public static PubSubComponent PubSub => instance?.pubSubComponent;
2101 public static MultiUserChatComponent Muc => instance?.mucComponent;
2102 public static ServiceRegistrationComponent ServiceRegistry => instance?.serviceRegistrationComponent;
2103 public static EventLogComponent EventLog => instance?.eventLogComponent;
2104 public static PersistenceLayer PersistenceLayer => instance?.persistenceLayer;
2105
2106 public static XmppServer Server
2107 {
2108 get
2109 {
2110 if (!(instance is null))
2111 return instance.xmppServer;
2112
2113 if (Types.TryGetModuleParameter("XMPP_SERVER", out XmppServer Server) &&
2114 !Server.Disposed)
2115 {
2116 return Server;
2117 }
2118
2119 return null;
2120 }
2121 }
2122
2123 public static string PackagesFolder
2124 {
2125 get => Gateway.SoftwareUpdateClient?.PackageFolder ?? Path.Combine(Gateway.AppDataFolder, "Packages");
2126 }
2127
2128 public static string DownloadsFolder
2129 {
2130 get => Path.Combine(Gateway.RootFolder, "Downloads");
2131 }
2132
2133 public static string InternalTransfersFolder
2134 {
2135 get => instance?.httpFileUploadSettings.InternalTransferFolder;
2136 }
2137
2138 public static ApiKey GetApiKey(string Key)
2139 {
2140 ApiKey Result = GetApiKeyAsync(Key).Result
2141 ?? throw new NotFoundException();
2142
2143 return Result;
2144 }
2145
2146 public static async Task<ApiKey> GetApiKeyAsync(string Key)
2147 {
2148 if (string.IsNullOrEmpty(Key))
2149 {
2150 return new ApiKey()
2151 {
2152 // TODO: Add statistics.
2153 };
2154 }
2155 else
2156 {
2157 foreach (ApiKey ApiKey in await Database.Find<ApiKey>(new FilterFieldEqualTo("Key", Key)))
2158 return ApiKey;
2159
2160 return null;
2161 }
2162 }
2163
2164 public static IAccount GetAccount(CaseInsensitiveString UserName)
2165 {
2166 IAccount Result = GetAccountAsync(UserName).Result
2167 ?? throw new NotFoundException();
2168
2169 return Result;
2170 }
2171
2172 public static Task<IAccount> GetAccountAsync(CaseInsensitiveString UserName)
2173 {
2174 if (instance is null)
2175 return GetAccountAsyncNoInit(UserName);
2176 else
2177 return ((IXmppServerPersistenceLayer)instance.persistenceLayer).GetAccount(UserName);
2178 }
2179
2180 private static async Task<IAccount> GetAccountAsyncNoInit(CaseInsensitiveString UserName)
2181 {
2182 foreach (Account Account in await Database.Find<Account>(new FilterFieldEqualTo("UserName", UserName)))
2183 return Account;
2184
2185 return null;
2186 }
2187
2188 public static Task<IRosterItem> GetRosterItemAsync(CaseInsensitiveString UserName, CaseInsensitiveString Jid)
2189 {
2190 if (instance is null)
2191 return GetRosterItemAsyncNoInit(UserName, Jid);
2192 else
2193 return instance.persistenceLayer.GetRosterItem(UserName, Jid);
2194 }
2195
2196 private static async Task<IRosterItem> GetRosterItemAsyncNoInit(CaseInsensitiveString UserName, CaseInsensitiveString Jid)
2197 {
2198 foreach (RosterItem Item in await Database.Find<RosterItem>(new FilterAnd(
2199 new FilterFieldEqualTo("UserName", UserName),
2200 new FilterFieldEqualTo("BareJid", Jid))))
2201 {
2202 return Item;
2203 }
2204
2205 return null;
2206 }
2207
2208 public static Task<IEnumerable<IRosterItem>> GetRosterAsync(CaseInsensitiveString UserName)
2209 {
2210 if (instance is null)
2211 return GetRosterAsyncNoInit(UserName);
2212 else
2213 return instance.persistenceLayer.GetRoster(UserName);
2214 }
2215
2216 private static async Task<IEnumerable<IRosterItem>> GetRosterAsyncNoInit(CaseInsensitiveString UserName)
2217 {
2218 return await Database.Find<RosterItem>(new FilterAnd(
2219 new FilterFieldEqualTo("UserName", UserName)));
2220 }
2221
2222 private static Stopwatch CreateWatch()
2223 {
2224 Stopwatch Watch = new Stopwatch();
2225 Watch.Start();
2226
2227 calibration = Calibrate(Watch);
2228
2229 return Watch;
2230 }
2231
2235 public static void Calibrate()
2236 {
2237 calibration = Calibrate(clock);
2238 }
2239
2240 private static Calibration Calibrate(Stopwatch Clock)
2241 {
2242 DateTime TP = DateTime.Now;
2243 long Ticks = TP.Ticks;
2244 long HF = 0;
2245 int i;
2246
2247 for (i = 0; i < 3; i++) // An extra round, to avoid JIT effects.
2248 {
2249 while ((TP = DateTime.UtcNow).Ticks == Ticks)
2250 ;
2251
2252 HF = Clock.ElapsedTicks;
2253 Ticks = TP.Ticks;
2254 }
2255
2256 return new Calibration()
2257 {
2258 Reference = TP,
2259 ReferenceHfTick = HF,
2260 TicksTo100Ns = 1e7 / Stopwatch.Frequency
2261 };
2262 }
2263
2267 public static DateTimeHF Now
2268 {
2269 get
2270 {
2271 DateTime NowRef = DateTime.UtcNow;
2272 long Ticks = clock.ElapsedTicks;
2273 Calibration Calibration = calibration;
2274
2275 Ticks -= Calibration.ReferenceHfTick;
2276
2277 long Ns100 = (long)(Ticks * Calibration.TicksTo100Ns + 0.5);
2278 long Milliseconds = Ns100 / 10000;
2279
2280 DateTime Now = Calibration.Reference.AddMilliseconds(Milliseconds);
2281
2282 if ((Now - NowRef).TotalSeconds >= 1)
2283 {
2284 Calibrate();
2285 return XmppServerModule.Now;
2286 }
2287 else
2288 {
2289 Ns100 %= 10000;
2290
2291 return new DateTimeHF(Now, (int)(Ns100 / 10), (int)(Ns100 % 10), Ticks);
2292 }
2293 }
2294 }
2295
2296 private async Task DnsRequest(object Sender, IqEventArgs e)
2297 {
2298 try
2299 {
2300 byte[] ReqBin = System.Convert.FromBase64String(e.Query.InnerText);
2301
2302 DnsMessage Msg = new DnsMessage(ReqBin);
2303
2304 foreach (Question Question in Msg.Questions)
2305 {
2307 if (DnsResponse is null)
2308 await e.IqErrorBadRequest(e.To, "Unable to resolve query.", "en");
2309 else
2310 {
2311 byte[] RespBin = (byte[])DnsResponse.Raw.Clone();
2312
2313 RespBin[0] = ReqBin[0]; // Use same ID as in request.
2314 RespBin[1] = ReqBin[1];
2315
2316 StringBuilder Xml = new StringBuilder();
2317
2318 Xml.Append("<dns xmlns='");
2319 Xml.Append(NamespaceDnsOverXmpp);
2320 Xml.Append("'>");
2321 Xml.Append(System.Convert.ToBase64String(RespBin));
2322 Xml.Append("</dns>");
2323
2324 await e.IqResult(Xml.ToString(), e.To);
2325 }
2326 return;
2327 }
2328
2329 await e.IqErrorBadRequest(e.To, "No question in request.", "en");
2330 }
2331 catch (Exception ex)
2332 {
2333 await e.IqError(ex, e.To);
2334 }
2335 }
2336
2337 public static PubSubNode GetPubSubNode(CaseInsensitiveString NodeName)
2338 {
2339 PubSubNode Result = GetPubSubNodeAsync(NodeName).Result
2340 ?? throw new NotFoundException();
2341
2342 return Result;
2343 }
2344
2345 public static Task<PubSubNode> GetPubSubNodeAsync(CaseInsensitiveString NodeName)
2346 {
2347 if (instance is null)
2348 return GetPubSubNodeAsyncNoInit(NodeName);
2349 else
2350 return instance.pubSubComponent.GetNodeAsync(CaseInsensitiveString.Empty, NodeName, null, XmppAddress.Empty, null);
2351 }
2352
2353 private static async Task<PubSubNode> GetPubSubNodeAsyncNoInit(CaseInsensitiveString NodeName)
2354 {
2355 foreach (PubSubNode PubSubNode in await Database.Find<PubSubNode>(new FilterAnd(
2356 new FilterFieldEqualTo("Service", string.Empty), new FilterFieldEqualTo("Name", NodeName))))
2357 {
2358 return PubSubNode;
2359 }
2360
2361 return null;
2362 }
2363
2364 public static IClientConnection[] GetClientConnections()
2365 {
2366 return instance.xmppServer.GetClientConnections();
2367 }
2368
2369 public static IClientConnection GetClientConnection(string FullJid)
2370 {
2371 if (instance.xmppServer.TryGetClientConnection(FullJid, out IClientConnection Result))
2372 return Result;
2373 else
2374 throw new NotFoundException("Client resource not connected: " + FullJid);
2375 }
2376
2377 public static S2sEndpointStatistics[] GetServerConnectionStatistics()
2378 {
2379 return instance.xmppServer.GetServerConnectionStatistics();
2380 }
2381
2382 public static IS2SEndpoint GetS2sConnection(string Domain)
2383 {
2384 if (instance.xmppServer.TryGetS2sEndpoint(Domain, out IS2SEndpoint Result))
2385 return Result;
2386 else
2387 throw new NotFoundException("Domain not connected: " + Domain);
2388 }
2389
2390 public static Task<bool> IsAdmin(CaseInsensitiveString Jid)
2391 {
2392 return IsAdmin(Jid, null);
2393 }
2394
2395 public static async Task<bool> IsAdmin(CaseInsensitiveString Jid, IUser User)
2396 {
2397 Jid = Networking.XMPP.XmppClient.GetBareJID(Jid);
2398 if (!(Gateway.XmppClient is null) && string.Compare(Gateway.XmppClient.BareJID, Jid, true) == 0)
2399 return true;
2400
2402
2403 foreach (CaseInsensitiveString s in Admins)
2404 {
2405 if (string.Compare(s, Jid, true) == 0)
2406 return true;
2407 }
2408
2409 User ??= await Users.GetUser(Jid, false);
2410 if (User is null)
2411 return false;
2412
2413 return User.HasPrivilege("Admin");
2414 }
2415
2416 public static string DateTimesToHTML(DateTime Created, DateTime Updated)
2417 {
2418 StringBuilder Result = new StringBuilder();
2419
2420 Created = Created.ToLocalTime();
2421
2422 if (Updated != DateTime.MinValue)
2423 Updated = Updated.ToLocalTime();
2424
2425 Result.Append("<span class=\"ContentItemTimestamp\">");
2426 Result.Append(Created.ToShortDateString());
2427 Result.Append(", ");
2428 Result.Append(Created.ToLongTimeString());
2429 Result.Append("</span>");
2430
2431 if (Updated != Created && Updated != DateTime.MinValue)
2432 {
2433 Result.Append("<span class=\"ContentItemUpdated\">");
2434 Result.Append(Updated.ToShortDateString());
2435 Result.Append(", ");
2436 Result.Append(Updated.ToLongTimeString());
2437 Result.Append("</span>");
2438 }
2439
2440 return Result.ToString();
2441 }
2442
2443 private async Task CheckRegistration()
2444 {
2445 try
2446 {
2447 if (!(Gateway.XmppClient is null) && Gateway.XmppClient.State == Networking.XMPP.XmppState.Connected)
2448 {
2449 this.serviceRegistrationClient ??= new ServiceRegistrationClient(Gateway.XmppClient, "services.tagroot.io");
2450 await this.serviceRegistrationClient.CheckRegistration();
2451
2452 if (Gateway.HasDomain)
2453 {
2454 string VCardDomain = await RuntimeSettings.GetAsync("VCard.Domain", string.Empty);
2455 if (!Gateway.IsDomain(VCardDomain, true))
2456 {
2457 // XEP-0054 - vcard-temp: http://xmpp.org/extensions/xep-0054.html
2458 // XEP-0153 - vCard-Based Avatars: http://xmpp.org/extensions/xep-0153.html
2459
2460 StringBuilder Xml = new StringBuilder();
2461 string BareJid = Gateway.XmppClient.BareJID;
2462 byte[] Avatar = Resources.LoadResource(typeof(XmppServerModule).Namespace + ".Graphics.XMPP_logo.png");
2463
2464 Xml.Append("<vCard xmlns='vcard-temp'>");
2465 Xml.Append("<FN>XMPP Broker on ");
2466 Xml.Append(XML.Encode(Gateway.Domain));
2467 Xml.Append("</FN>");
2468 Xml.Append("<URL>");
2469 Xml.Append(Gateway.GetUrl("/"));
2470 Xml.Append("</URL>");
2471 Xml.Append("<JABBERID>");
2472 Xml.Append(XML.Encode(BareJid));
2473 Xml.Append("</JABBERID>");
2474 Xml.Append("<PHOTO><TYPE>image/png</TYPE><BINVAL>");
2475 Xml.Append(System.Convert.ToBase64String(Avatar, Base64FormattingOptions.None));
2476 Xml.Append("</BINVAL></PHOTO>");
2477 Xml.Append("</vCard>");
2478
2479 await Gateway.AvatarClient.UpdateLocalAvatarAsync("image/png", Avatar, 96, 96, true);
2480 await Gateway.XmppClient.IqSetAsync(BareJid, Xml.ToString());
2481 await RuntimeSettings.SetAsync("VCard.Domain", Gateway.Domain);
2482 }
2483 }
2484 return;
2485 }
2486 }
2487 catch (Exception ex)
2488 {
2489 Log.Exception(ex);
2490 }
2491
2492 Gateway.ScheduleEvent((P) => Task.Run(() => this.CheckRegistration()), DateTime.Now.AddMinutes(15), null);
2493 }
2494
2495 private Task XmppServer_ClientConnectionAdded(object Sender, Networking.XMPP.Server.ClientConnectionEventArgs e)
2496 {
2497 return this.connectionsSource?.ClientConnectionAdded(Sender, e) ?? Task.CompletedTask;
2498 }
2499
2500 private Task XmppServer_ClientConnectionRemoved(object Sender, Networking.XMPP.Server.ClientConnectionEventArgs e)
2501 {
2502 return this.connectionsSource?.ClientConnectionRemoved(Sender, e) ?? Task.CompletedTask;
2503 }
2504
2505 private Task XmppServer_ClientConnectionUpdated(object Sender, Networking.XMPP.Server.ClientConnectionEventArgs e)
2506 {
2507 return this.connectionsSource?.ClientConnectionUpdated(Sender, e) ?? Task.CompletedTask;
2508 }
2509
2510 private Task XmppServer_ServerConnectionAdded(object Sender, Networking.XMPP.Server.ServerConnectionEventArgs e)
2511 {
2512 return this.connectionsSource?.ServerConnectionAdded(Sender, e) ?? Task.CompletedTask;
2513 }
2514
2515 private Task XmppServer_ServerConnectionRemoved(object Sender, Networking.XMPP.Server.ServerConnectionEventArgs e)
2516 {
2517 return this.connectionsSource?.ServerConnectionRemoved(Sender, e) ?? Task.CompletedTask;
2518 }
2519
2520 private Task XmppServer_ServerConnectionUpdated(object Sender, Networking.XMPP.Server.ServerConnectionEventArgs e)
2521 {
2522 return this.connectionsSource?.ServerConnectionUpdated(Sender, e) ?? Task.CompletedTask;
2523 }
2524
2525 internal async Task<bool> CheckSoftwarePackage(Networking.XMPP.Software.Package RemotePackage, ResponseCallbackHandler ResponseCallback)
2526 {
2527 try
2528 {
2529 Package LocalPackage = await ProvisioningComponent.GetPackage(RemotePackage.FileName);
2530
2531 if (LocalPackage is null ||
2532 LocalPackage.Published < RemotePackage.Published ||
2533 !CheckPackageSize(LocalPackage.FileName, RemotePackage.Bytes))
2534 {
2535 string MessageId = await ResponseCallback("Downloading `" + RemotePackage.FileName + "`", string.Empty);
2537 DateTime Last = DateTime.UtcNow;
2538 long BytesDownloaded = 0;
2539
2540 Destination.OnWrite += async (Sender, e) =>
2541 {
2542 DateTime TP = DateTime.UtcNow;
2543
2544 BytesDownloaded = e.Total;
2545
2546 if (TP.Second != Last.Second)
2547 {
2548 Last = TP;
2549 await ResponseCallback("Downloading `" + RemotePackage.FileName + "`: " + BytesDownloaded.ToString() + " bytes downloaded.", MessageId);
2550 }
2551 };
2552
2553 string FileName = await Gateway.SoftwareUpdateClient.DownloadPackageAsync(RemotePackage, Destination);
2554
2555 await this.PackageDownloaded(RemotePackage, Gateway.SoftwareUpdateClient.ComponentAddress, MessageId, ResponseCallback);
2556
2557 return true;
2558 }
2559 }
2560 catch (Exception ex)
2561 {
2562 await ReturnError(ex, ResponseCallback);
2563 }
2564
2565 return false;
2566 }
2567
2568 private static bool CheckPackageSize(string FileName, long ExpectedSize)
2569 {
2570 string FullPath = Path.Combine(PackagesFolder, FileName);
2571
2572 if (!File.Exists(FullPath))
2573 return false;
2574
2575 System.IO.FileInfo Info = new System.IO.FileInfo(FullPath);
2576
2577 return Info.Length == ExpectedSize;
2578 }
2579
2580 private Task SoftwareUpdateClient_OnSoftwareUpdated(object Sender, Networking.XMPP.Software.PackageUpdatedEventArgs e)
2581 {
2582 Log.Informational("New software package available.", PackageTags(e.Package));
2583 e.Download = true;
2584
2585 return Task.CompletedTask;
2586 }
2587
2588 private static KeyValuePair<string, object>[] PackageTags(Networking.XMPP.Software.Package Package)
2589 {
2590 return new KeyValuePair<string, object>[]
2591 {
2592 new KeyValuePair<string, object>("FileName", Package.FileName),
2593 new KeyValuePair<string, object>("Bytes", Package.Bytes),
2594 new KeyValuePair<string, object>("Published", Package.Published),
2595 new KeyValuePair<string, object>("Supersedes", Package.Supersedes),
2596 new KeyValuePair<string, object>("Created", Package.Created),
2597 new KeyValuePair<string, object>("Signature", System.Convert.ToBase64String(Package.Signature ?? Array.Empty<byte>())),
2598 new KeyValuePair<string, object>("URL", Package.Url)
2599 };
2600 }
2601
2602 private async Task SoftwareUpdateClient_OnSoftwareValidation(object Sender, Networking.XMPP.Software.PackageFileEventArgs e)
2603 {
2604 string FileName = Path.GetFileName(e.LocalFileName);
2605
2606 if (string.Compare(FileName, BrokerPackage.FileName, true) == 0)
2607 {
2608 if (!ValidateIoTBrokerPackage(e.LocalFileName, e.Package.Signature))
2609 throw new Exception("Invalid signature.");
2610 }
2611 else
2612 {
2613 Package Package = await ProvisioningComponent.GetPackage(FileName);
2614
2615 if (!(Package is null) &&
2616 Package.Installed > DateTime.MinValue &&
2617 !(Package.PublicKey is null))
2618 {
2619 if (!ValidatePackage(e.LocalFileName, Package.PublicKey, e.Package.Signature))
2620 throw new Exception("Invalid signature.");
2621 }
2622 }
2623 }
2624
2625 internal static bool ValidateIoTBrokerPackage(string FileName, byte[] Signature)
2626 {
2627 Log.Notice("Validating package.", FileName);
2628
2629 using FileStream f = File.OpenRead(FileName);
2630 bool Result = ValidateIoTBrokerPackage(f, Signature);
2631
2632 if (Result)
2633 Log.Informational("Package valid.", FileName);
2634 else
2635 Log.Error("Package not valid.", FileName);
2636
2637 return Result;
2638 }
2639
2640 internal static bool ValidatePackage(string FileName, byte[] PublicKey, byte[] Signature)
2641 {
2642 Log.Notice("Validating package.", FileName);
2643
2644 using FileStream f = File.OpenRead(FileName);
2645 bool Result = ValidatePackage(f, PublicKey, Signature);
2646
2647 if (Result)
2648 Log.Informational("Package valid.", FileName);
2649 else
2650 Log.Error("Package not valid.", FileName);
2651
2652 return Result;
2653 }
2654
2655 internal static bool ValidateIoTBrokerPackage(Stream Data, byte[] Signature)
2656 {
2657 return ValidatePackage(Data, iotBrokerPackagePublicKey, Signature);
2658 }
2659
2660 internal static bool ValidatePackage(Stream Data, byte[] PublicKey, byte[] Signature)
2661 {
2662 return ed448.Verify(Data, PublicKey, Signature);
2663 }
2664
2665 private Task SoftwareUpdateClient_OnSoftwareDownloaded(object Sender, Networking.XMPP.Software.PackageFileEventArgs e)
2666 {
2667 return this.PackageDownloaded(e.Package, e.From, string.Empty,
2668 (Markdown, MessageId) => Task.FromResult(MessageId));
2669 }
2670
2671 private async Task PackageDownloaded(Networking.XMPP.Software.Package DownloadedPackage,
2672 string RemoteEndPoint, string MessageId, ResponseCallbackHandler ResponseCallback)
2673 {
2674 Log.Notice("New software package downloaded.", PackageTags(DownloadedPackage));
2675
2676 await ResponseCallback("Software package downloaded: `" + DownloadedPackage.FileName + "`", MessageId);
2677
2678 DateTime Now = DateTime.UtcNow;
2679 Package Package = await ProvisioningComponent.GetPackage(DownloadedPackage.FileName);
2680
2681 if (Package is null)
2682 {
2683 Package = new Package()
2684 {
2685 FileName = DownloadedPackage.FileName,
2686 Signature = DownloadedPackage.Signature,
2687 RemoteEndPoint = RemoteEndPoint,
2688 Published = Now,
2689 Supersedes = DateTime.MinValue,
2690 Created = Now,
2691 Bytes = DownloadedPackage.Bytes,
2692 AesKey = null,
2693 Installed = DateTime.MinValue,
2694 PublicKey = null,
2695 Downloadable = false
2696 };
2697
2698 await Database.Insert(Package);
2699 }
2700 else
2701 {
2702 Package.Signature = DownloadedPackage.Signature;
2703 Package.RemoteEndPoint = RemoteEndPoint;
2704 Package.Supersedes = Package.Published;
2705 Package.Published = Now;
2706 Package.Bytes = DownloadedPackage.Bytes;
2707
2708 if (!(Package.AesKey is null))
2709 Package.ContentOnly = IsContentPackage(Package);
2710
2711 await Database.Update(Package);
2712 }
2713
2714 string SignatureFile = Path.Combine(Gateway.SoftwareUpdateClient.PackageFolder,
2715 Path.ChangeExtension(Package.FileName, "signature"));
2716
2717 StringBuilder sb = new StringBuilder();
2718
2719 sb.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
2720 sb.AppendLine("<Signatures xmlns=\"http://waher.se/Schema/Signatures.xsd\">");
2721 sb.Append("\t<Signature fileName=\"");
2722 sb.Append(XML.Encode(Package.FileName));
2723 sb.Append("\">");
2724 sb.Append(System.Convert.ToBase64String(Package.Signature));
2725 sb.AppendLine("</Signature>");
2726 sb.AppendLine("</Signatures>");
2727
2728 File.WriteAllText(SignatureFile, sb.ToString(), Encoding.UTF8);
2729
2730 if (!(this.provisioningComponent is null))
2731 await this.provisioningComponent.NewPackage(Package);
2732
2733 if (string.Compare(DownloadedPackage.FileName, BrokerPackage.FileName, true) == 0 ||
2734 Package.Installed > DateTime.MinValue)
2735 {
2736 await this.NewSoftwareAvailable(Package, RemoteEndPoint);
2737 }
2738 else if (string.Compare(DownloadedPackage.FileName, Ip2LocalizationPackageName, true) == 0)
2739 Gateway.ScheduleEvent(this.ImportIpLocalizationDatabase, DateTime.Now.AddMinutes(5), null);
2740
2741 // TODO: PlantUML package
2742 // TODO: GraphViz package
2743 }
2744
2745 internal async Task NewSoftwareAvailable(Package DownloadedPackage, string RemoteEndPoint)
2746 {
2747 StringBuilder Markdown = new StringBuilder();
2748 DateTime Now = DateTime.Now;
2749 long DelayMinutes;
2750
2751 if (DownloadedPackage.ContentOnly)
2752 {
2753 bool InstallPackage = await RuntimeSettings.GetAsync(AutoInstallContentOnlyParameterName, false);
2754
2755 if (InstallPackage)
2756 DelayMinutes = 1;
2757 else
2758 DelayMinutes = await RuntimeSettings.GetAsync(AutoInstallDelayParameterName, 0);
2759 }
2760 else
2761 DelayMinutes = await RuntimeSettings.GetAsync(AutoInstallDelayParameterName, 0);
2762
2763 Markdown.AppendLine("New version of installed software has been downloaded");
2764 Markdown.AppendLine("==========================================================");
2765 Markdown.AppendLine();
2766 Markdown.AppendLine("| Package ||");
2767 Markdown.AppendLine("|:------|:-------|");
2768 Markdown.AppendLine("| Filename: | `" + DownloadedPackage.FileName + "` |");
2769 Markdown.AppendLine("| Size: | " + Export.FormatBytes(DownloadedPackage.Bytes) + " |");
2770 Markdown.AppendLine("| From: | `" + RemoteEndPoint + "` |");
2771 Markdown.AppendLine("| Published: | " + MarkdownDocument.Encode(DownloadedPackage.Published.ToString()) + " |");
2772
2773 if (DownloadedPackage.Supersedes != DateTime.MinValue)
2774 Markdown.AppendLine("| Supersedes: | " + MarkdownDocument.Encode(DownloadedPackage.Supersedes.ToString()) + " |");
2775
2776 Markdown.AppendLine("| Signature: | " + System.Convert.ToBase64String(DownloadedPackage.Signature) + " |");
2777 Markdown.AppendLine("| Download: | [" + MarkdownDocument.Encode(DownloadedPackage.FileName) + "](" + DownloadedPackage.Url + ") |");
2778 Markdown.Append("| Date | ");
2779 Markdown.Append(MarkdownDocument.Encode(Now.ToShortDateString()));
2780 Markdown.AppendLine(" |");
2781 Markdown.Append("| Time | ");
2782 Markdown.Append(MarkdownDocument.Encode(Now.ToLongTimeString()));
2783 Markdown.AppendLine(" |");
2784 Markdown.AppendLine();
2785
2786 if (string.Compare(DownloadedPackage.FileName, BrokerPackage.FileName, true) == 0)
2787 {
2788 Markdown.AppendLine("See [Release Notes](https://lab.tagroot.io/ReleaseNotes) for information about what is new.");
2789 Markdown.AppendLine();
2790 }
2791
2792 if (DelayMinutes > 0)
2793 {
2794 if (DownloadedPackage.ContentOnly)
2795 {
2796 try
2797 {
2798 Log.Notice("Updating software.", DownloadedPackage.FileName);
2799
2800 await this.UpdateSoftware(DownloadedPackage, false);
2801
2802 Log.Notice("Software successfully updated.", DownloadedPackage.FileName);
2803
2804 Markdown.AppendLine("The new software has been automatically installed.");
2805 }
2806 catch (Exception ex)
2807 {
2808 Log.Exception(ex, DownloadedPackage.FileName);
2809
2810 Markdown.AppendLine("Unable to install the new software. The following error was reported: ");
2811 Markdown.AppendLine();
2812 Markdown.AppendLine("```");
2813 Markdown.AppendLine(ex.Message.Trim());
2814 Markdown.AppendLine("```");
2815 }
2816 }
2817 else
2818 {
2819 if (autoUpdateTP > DateTime.MinValue)
2820 {
2821 Gateway.CancelScheduledEvent(autoUpdateTP);
2822 autoUpdateTP = DateTime.MinValue;
2823 }
2824
2825 DateTime TP = Now.AddMinutes(DelayMinutes);
2826 autoUpdateTP = Gateway.ScheduleEvent(this.UpdateSoftware, TP, null);
2827
2828 await RuntimeSettings.SetAsync(AutoInstallTimeParameterName, autoUpdateTP);
2829
2830 Markdown.Append("The new software will be automatically installed");
2831
2832 if (DelayMinutes == 1)
2833 Markdown.Append(" in 1 minute");
2834 else if (DelayMinutes <= 60)
2835 Markdown.Append(" in " + DelayMinutes.ToString() + " minutes");
2836 else
2837 {
2838 Markdown.Append(" at ");
2839
2840 if (TP.Date == Now.Date)
2841 Markdown.Append(TP.ToShortTimeString());
2842 else
2843 {
2844 Markdown.Append(TP.ToShortDateString());
2845 Markdown.Append(", ");
2846 Markdown.Append(TP.ToShortTimeString());
2847 }
2848 }
2849
2850 Markdown.AppendLine(", unless cancelled from the chat interface.");
2851 }
2852 }
2853 else
2854 Markdown.AppendLine("You can update the server with the new package from the chat interface.");
2855
2856 await Gateway.SendNotification(Markdown.ToString());
2857 }
2858
2865 {
2866 try
2867 {
2868 string FileName = Path.Combine(PackagesFolder, Package.FileName);
2869 if (!File.Exists(FileName))
2870 return false;
2871
2872 StringBuilder sb = new StringBuilder();
2873
2874 sb.Append(Package.FileName);
2875 sb.Append(":");
2876
2877 if (Package.AesKey is null)
2878 return false;
2879 else
2880 sb.Append(Hashes.BinaryToString(Package.AesKey));
2881
2882 sb.Append(":Waher.Utility.Install");
2883
2884 SHAKE256 H = new SHAKE256(384);
2885 byte[] Digest = H.ComputeVariable(Encoding.UTF8.GetBytes(sb.ToString()));
2886 byte[] AesKey = new byte[32];
2887 byte[] IV = new byte[16];
2888 Aes Aes = null;
2889 FileStream fs = null;
2890 ICryptoTransform AesTransform = null;
2891 CryptoStream Decrypted = null;
2892 GZipStream Decompressed = null;
2893
2894 Buffer.BlockCopy(Digest, 0, AesKey, 0, 32);
2895 Buffer.BlockCopy(Digest, 32, IV, 0, 16);
2896
2897 try
2898 {
2899 Aes = Aes.Create();
2900 Aes.BlockSize = 128;
2901 Aes.KeySize = 256;
2902 Aes.Mode = CipherMode.CBC;
2903 Aes.Padding = PaddingMode.Zeros;
2904
2905 fs = File.OpenRead(FileName);
2906 AesTransform = Aes.CreateDecryptor(AesKey, IV);
2907 Decrypted = new CryptoStream(fs, AesTransform, CryptoStreamMode.Read);
2908 Decompressed = new GZipStream(Decrypted, CompressionMode.Decompress);
2909
2910 byte b = ReadByte(Decompressed);
2911 byte[] Bin;
2912
2913 if (b > 0)
2914 {
2915 Bin = new byte[b];
2916 Decompressed.ReadAll(Bin, 0, b);
2917 }
2918
2919 Bin = ReadBin(Decompressed);
2920 if (Encoding.ASCII.GetString(Bin) != "IoTGatewayPackage")
2921 throw new Exception("Invalid package file.");
2922
2923 while ((b = ReadByte(Decompressed)) != 0)
2924 {
2925 string RelativeName = Encoding.UTF8.GetString(ReadBin(Decompressed)); // RelativeName
2926 ReadVarLenUInt(Decompressed); // FileAttributes
2927 ReadVarLenUInt(Decompressed); // CreationTimeUtc
2928 ReadVarLenUInt(Decompressed); // LastAccessTimeUtc
2929 ReadVarLenUInt(Decompressed); // LastWriteTimeUtc
2930 ulong Bytes = ReadVarLenUInt(Decompressed);
2931
2932 switch (b)
2933 {
2934 case 1: // Program file in installation folder, not assembly file
2935 SkipBytes(Decompressed, Bytes);
2936 break;
2937
2938 case 2: // Assembly file
2939 case 5: // External program file
2940 case 6: // External program folder
2941 return false;
2942
2943 case 3: // Content file (copy if newer)
2944 case 4: // Content file (always copy)
2945 SkipBytes(Decompressed, Bytes);
2946 break;
2947
2948 default:
2949 throw new Exception("Invalid package file.");
2950 }
2951 }
2952 }
2953 finally
2954 {
2955 Decompressed?.Dispose();
2956 Decrypted?.Dispose();
2957 AesTransform?.Dispose();
2958 Aes?.Dispose();
2959 fs?.Dispose();
2960 }
2961
2962 return true;
2963 }
2964 catch (Exception ex)
2965 {
2966 Log.Exception(ex);
2967 return false;
2968 }
2969 }
2970
2971 private static byte[] ReadBin(Stream Input)
2972 {
2973 ulong Len = ReadVarLenUInt(Input);
2974 if (Len > int.MaxValue)
2975 throw new Exception("Invalid package.");
2976
2977 int c = (int)Len;
2978 byte[] Result = new byte[c];
2979
2980 Input.ReadAll(Result, 0, c);
2981
2982 return Result;
2983 }
2984
2985 private static ulong ReadVarLenUInt(Stream Input)
2986 {
2987 ulong Len = 0;
2988 int Offset = 0;
2989 byte b;
2990
2991 do
2992 {
2993 b = ReadByte(Input);
2994
2995 Len |= ((ulong)(b & 127)) << Offset;
2996 Offset += 7;
2997 }
2998 while ((b & 0x80) != 0);
2999
3000 return Len;
3001 }
3002
3003 private static byte ReadByte(Stream Input)
3004 {
3005 int i = Input.ReadByte();
3006 if (i < 0)
3007 throw new EndOfStreamException("Reading past end-of-file.");
3008
3009 return (byte)i;
3010 }
3011
3012 private static void SkipBytes(Stream Input, ulong Bytes)
3013 {
3014 uint c = 65536;
3015 if (Bytes < c)
3016 c = (uint)Bytes;
3017
3018 byte[] Buffer = new byte[c];
3019
3020 while (Bytes > 0)
3021 {
3022 Input.ReadAll(Buffer, 0, (int)c);
3023 Bytes -= c;
3024 if (c > Bytes)
3025 c = (uint)Bytes;
3026 }
3027 }
3028
3029 internal async void UpdateSoftware(object P)
3030 {
3031 try
3032 {
3033 Package Package = P as Package;
3034 await this.UpdateSoftware(Package, true);
3035 }
3036 catch (Exception ex)
3037 {
3038 Log.Exception(ex);
3039 }
3040 }
3041
3042 internal Task UpdateSoftware(Package Package, bool Backup)
3043 {
3045 {
3046 To = string.Empty,
3047 Folder = PackagesFolder
3048 };
3049
3050 return this.UpdateSoftware(Package, State, Backup);
3051 }
3052
3053 internal Task UpdateSoftware(Package Package, ChatState State, bool Backup)
3054 {
3055 return this.UpdateSoftware(Package, State, Backup, (Msg, MessageId) => Task.FromResult<string>(MessageId));
3056 }
3057
3058 internal async Task UpdateSoftware(Package Package, ChatState State, bool Backup, ResponseCallbackHandler ResponseCallback)
3059 {
3060 string MessageId = string.Empty;
3061
3062 if (Backup && (Package is null || !Package.ContentOnly))
3063 {
3064 MessageId = await ResponseCallback("Performing backup.", MessageId);
3065 await Gateway.DoBackup();
3066 }
3067
3068 StringBuilder sb = new StringBuilder();
3069 Package[] Packages = await ProvisioningComponent.GetPackages();
3070 bool RestartRequired;
3071 string ServerFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + FileSystem.ExecutableExtension);
3072 if (!File.Exists(ServerFileName))
3073 ServerFileName = Path.ChangeExtension(ServerFileName, "dll");
3074
3075 sb.Append("Install -d \"");
3076 sb.Append(Gateway.AppDataFolder[..^1]);
3077 sb.Append("\" -s \"");
3078 sb.Append(ServerFileName);
3079 sb.Append('"');
3080
3081 string BrokerPackageFileName = Path.Combine(PackagesFolder, BrokerPackage.FileName);
3082 bool UpdateBroker = false;
3083
3084 if (Package is null || !Package.ContentOnly)
3085 {
3086 autoUpdateTP = DateTime.MinValue;
3087 await RuntimeSettings.SetAsync(AutoInstallTimeParameterName, DateTime.MinValue);
3088
3089 if (!File.Exists(BrokerPackageFileName))
3090 {
3091 await ResponseCallback("`" + BrokerPackage.FileName + "` not found.", string.Empty);
3092 return;
3093 }
3094
3095 UpdateBroker = true;
3096
3097 List<Package> ToInstall = new List<Package>();
3098
3099 foreach (Package Package2 in Packages)
3100 {
3101 if (Package2.Installed == DateTime.MinValue)
3102 continue;
3103
3104 if (Package2.FileName == BrokerPackage.FileName)
3105 continue;
3106
3107 if (Package2.ContentOnly)
3108 continue;
3109
3110 if (Package2.Published > Package2.Installed)
3111 {
3112 ToInstall.Add(Package2);
3113
3114 Package2.Installed = Package2.Published;
3115 await Database.Update(Package2);
3116 }
3117 else if (Package2 == Package)
3118 ToInstall.Add(Package2);
3119 }
3120
3121 Packages = ToInstall.ToArray();
3122 RestartRequired = true;
3123 }
3124 else
3125 {
3126 if (!File.Exists(Path.Combine(PackagesFolder, Package.FileName)))
3127 {
3128 await ResponseCallback("`" + Package.FileName + "` not found.", string.Empty);
3129 return;
3130 }
3131
3133 {
3134 Package.Installed = Package.Published;
3135 await Database.Update(Package);
3136 }
3137
3138 Packages = new Package[] { Package };
3139 RestartRequired = false;
3140
3141 sb.Append(" -co");
3142 }
3143
3144 foreach (Package Package2 in Packages)
3145 {
3146 string PackageFileName = Path.Combine(PackagesFolder, Package2.FileName);
3147
3148 if (File.Exists(PackageFileName))
3149 {
3150 sb.Append(" -p \"");
3151 sb.Append(PackageFileName.Replace("\"", "\\\""));
3152
3153 if (!(Package2.AesKey is null))
3154 {
3155 sb.Append("\" -k \"");
3156 sb.Append(Hashes.BinaryToString(Package2.AesKey));
3157 }
3158
3159 sb.Append('"');
3160 }
3161 }
3162
3163 if (UpdateBroker)
3164 {
3165 sb.Append(" -p \"");
3166 sb.Append(BrokerPackageFileName);
3167 sb.Append("\" -k \"");
3168 sb.Append(BrokerPackage.Key);
3169 sb.Append('"');
3170
3171 if (Types.TryGetModuleParameter("SERVICE_NAME", out string ServiceName) &&
3172 !string.IsNullOrEmpty(ServiceName))
3173 {
3174 sb.Append(" -sn \"");
3175 sb.Append(ServiceName);
3176 sb.Append('"');
3177 }
3178 }
3179
3180 sb.Append(" -n \"");
3181 sb.Append(Gateway.InstanceName);
3182 sb.Append("\" -w 120000 -v -i > install.log");
3183
3184 MessageId = await ResponseCallback("Starting update process...", MessageId);
3185
3186 if (await this.ExecuteCommand(State.To, sb.ToString(), State, true, ResponseCallback))
3187 {
3188 if (RestartRequired)
3189 {
3190 await ResponseCallback("Stopping service... Update procedure will continue when service has been stopped. Service will then restart using the updated version.", MessageId);
3191
3192 Gateway.ScheduleEvent((P) => Gateway.Terminate(), DateTime.Now.AddSeconds(1), null);
3193 }
3194 else
3195 {
3196 await ResponseCallback("Package installed. No restart required.", MessageId);
3198 }
3199 }
3200 }
3201
3202 internal async Task UninstallSoftware(string PackageFileName, byte[] AesKey)
3203 {
3204 string s = Path.Combine(PackagesFolder, PackageFileName);
3205 if (!File.Exists(s))
3206 return;
3207
3208 string s2 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + FileSystem.ExecutableExtension);
3209 if (!File.Exists(s2))
3210 s2 = s2[..^3] + "dll";
3211
3212 StringBuilder sb = new StringBuilder();
3213
3214 sb.Append("Install -p \"");
3215 sb.Append(s);
3216 sb.Append("\" -d \"");
3217 sb.Append(Gateway.AppDataFolder[..^1]);
3218 sb.Append("\" -s \"");
3219 sb.Append(s2);
3220
3221 if (!(AesKey is null))
3222 {
3223 sb.Append("\" -k \"");
3224 sb.Append(Hashes.BinaryToString(AesKey));
3225 }
3226
3227 sb.Append('"');
3228
3229 Package Package = await ProvisioningComponent.GetPackage(PackageFileName);
3230 bool RestartRequired = Package is null || !Package.ContentOnly;
3231
3232 if (!RestartRequired)
3233 sb.Append(" -co");
3234
3235 sb.Append(" -n \"");
3236 sb.Append(Gateway.InstanceName);
3237 sb.Append("\" -w 120000 -v -u > install.log");
3238
3240 {
3241 To = string.Empty,
3242 Folder = PackagesFolder
3243 };
3244
3245 await Gateway.DoBackup();
3246
3247 if (await this.ExecuteCommand(string.Empty, sb.ToString(), State, true,
3248 (Msg, MessageId) => Task.FromResult<string>(MessageId)) && RestartRequired)
3249 {
3250 Gateway.ScheduleEvent((P) => Gateway.Terminate(), DateTime.Now.AddSeconds(1), null);
3251 }
3252 }
3253
3254 private Task SoftwareUpdateClient_OnSoftwareDeleted(object Sender, Networking.XMPP.Software.PackageDeletedEventArgs e)
3255 {
3256 Log.Informational("Software package deleted on server.", PackageTags(e.Package));
3257 e.Delete = true;
3258
3259 return Task.CompletedTask;
3260 }
3261
3262 private async Task SoftwareUpdateClient_OnDownloadedSoftwareDeleted(object Sender, Networking.XMPP.Software.PackageFileEventArgs e)
3263 {
3264 Log.Notice("Local copy of software package deleted.", PackageTags(e.Package));
3265
3266 Package OldPackage = await ProvisioningComponent.GetPackage(e.Package.FileName);
3267
3268 if (!(OldPackage is null))
3269 {
3270 await Database.Delete(OldPackage);
3271
3272 if (!(this.provisioningComponent is null))
3273 await this.provisioningComponent.PackageDeleted(OldPackage);
3274 }
3275 }
3276
3277 private async Task<(string, string, bool)> GetPlainText(Networking.XMPP.Events.MessageEventArgs e, DateTime OfflineThreshold)
3278 {
3279 string s = e.Body?.Trim();
3280
3281 string ReplaceObjectId = string.Empty;
3282 bool IsMarkdown = false;
3283
3284 foreach (XmlNode N in e.Message.ChildNodes)
3285 {
3286 if (N is XmlElement E)
3287 {
3288 switch (E.LocalName)
3289 {
3290 case "content":
3291 if (E.NamespaceURI == "urn:xmpp:content" &&
3292 string.Compare(XML.Attribute(E, "type"), MarkdownCodec.ContentType, true) == 0)
3293 {
3294 if (string.IsNullOrEmpty(s))
3295 {
3296 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(E.InnerText.Trim());
3297 s = await Doc.GeneratePlainText();
3298 }
3299
3300 IsMarkdown = true;
3301 }
3302 break;
3303
3304 case "delay":
3305 if (E.NamespaceURI == XmppServer.DelayedDeliveryNamespace &&
3306 E.HasAttribute("stamp") &&
3307 XML.TryParse(E.GetAttribute("stamp"), out DateTime Timestamp) &&
3308 Timestamp.ToUniversalTime() < OfflineThreshold.ToUniversalTime())
3309 {
3310 return (string.Empty, string.Empty, false);
3311 }
3312 break;
3313
3314 case "replace":
3315 if (E.NamespaceURI == "urn:xmpp:message-correct:0")
3316 ReplaceObjectId = XML.Attribute(E, "id");
3317 break;
3318 }
3319 }
3320 }
3321
3322 return (s, ReplaceObjectId, IsMarkdown);
3323
3324 }
3325
3326 private string GetMarkdown(Networking.XMPP.Events.MessageEventArgs e, DateTime OfflineThreshold, bool EncodeIfText, out string ReplaceObjectId, out bool IsMarkdown)
3327 {
3328 string s = e.Body?.Trim();
3329
3330 ReplaceObjectId = string.Empty;
3331 IsMarkdown = false;
3332
3333 foreach (XmlNode N in e.Message.ChildNodes)
3334 {
3335 if (N is XmlElement E)
3336 {
3337 switch (E.LocalName)
3338 {
3339 case "content":
3340 if (E.NamespaceURI == "urn:xmpp:content" &&
3341 string.Compare(XML.Attribute(E, "type"), MarkdownCodec.ContentType, true) == 0)
3342 {
3343 s = E.InnerText.Trim();
3344 IsMarkdown = true;
3345 }
3346 break;
3347
3348 case "delay":
3349 if (E.NamespaceURI == XmppServer.DelayedDeliveryNamespace &&
3350 E.HasAttribute("stamp") &&
3351 XML.TryParse(E.GetAttribute("stamp"), out DateTime Timestamp) &&
3352 Timestamp.ToUniversalTime() < OfflineThreshold.ToUniversalTime())
3353 {
3354 return string.Empty;
3355 }
3356 break;
3357
3358 case "replace":
3359 if (E.NamespaceURI == "urn:xmpp:message-correct:0")
3360 ReplaceObjectId = XML.Attribute(E, "id");
3361 break;
3362 }
3363 }
3364 }
3365
3366 return EncodeIfText ? MarkdownDocument.Encode(s) : s;
3367 }
3368
3369 private async Task XmppClient_OnChatMessage(object Sender, Networking.XMPP.Events.MessageEventArgs e)
3370 {
3371 (string Message, string _, bool IsMarkdown) = await this.GetPlainText(e, DateTime.UtcNow.AddMinutes(-1));
3372
3373 Task _ = Task.Run(async () => await this.ProcessChatMessage(Message, e.From, e.ThreadID, async (Response, MessageId) =>
3374 {
3375 KeyValuePair<string, string> P = await this.PrepareAdminResponse(Response, MessageId, IsMarkdown);
3376 string Xml = P.Key;
3377 MessageId = P.Value;
3378
3379 if (Xml is null)
3380 Xml = "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
3381 else
3382 Xml += "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
3383
3384 await Gateway.XmppClient.SendMessage(Networking.XMPP.QoSLevel.Unacknowledged, Networking.XMPP.MessageType.Chat,
3385 MessageId, e.From, Xml, string.Empty, string.Empty, string.Empty, e.ThreadID, string.Empty, null, null);
3386
3387 return MessageId;
3388 }, Gateway.XmppClient));
3389 }
3390
3391 private Task XmppClient_OnValidateSender(object Sender, Networking.XMPP.Events.ValidateSenderEventArgs e)
3392 {
3393 if (e.MessageStanza is null || !e.Rejected)
3394 return Task.CompletedTask;
3395
3396 switch (e.MessageStanza.Type)
3397 {
3398 case Networking.XMPP.MessageType.GroupChat:
3399 bool InRoom;
3400
3401 lock (this.mucRooms)
3402 {
3403 InRoom = this.mucRooms.ContainsKey(e.FromBareJID);
3404 }
3405
3406 if (InRoom)
3407 e.Accept();
3408 else
3409 e.Reject();
3410
3411 break;
3412
3413 case Networking.XMPP.MessageType.Normal:
3414 case Networking.XMPP.MessageType.Chat:
3415 foreach (XmlNode N in e.MessageStanza.Message.ChildNodes)
3416 {
3417 if (!(N is XmlElement E))
3418 continue;
3419
3420 switch (E.NamespaceURI)
3421 {
3422 case Networking.XMPP.MUC.MultiUserChatClient.NamespaceMuc:
3423 case Networking.XMPP.MUC.MultiUserChatClient.NamespaceMucAdmin:
3424 case Networking.XMPP.MUC.MultiUserChatClient.NamespaceMucOwner:
3425 case Networking.XMPP.MUC.MultiUserChatClient.NamespaceMucUser:
3426 lock (this.mucRooms)
3427 {
3428 InRoom = this.mucRooms.ContainsKey(e.FromBareJID);
3429 }
3430
3431 if (InRoom)
3432 e.Accept();
3433 else
3434 e.Reject();
3435 return Task.CompletedTask;
3436 }
3437 }
3438 break;
3439 }
3440
3441 return Task.CompletedTask;
3442 }
3443
3444 private async Task MucClient_DirectInvitationReceived(object Sender, Networking.XMPP.MUC.DirectInvitationMessageEventArgs e)
3445 {
3446 if (await IsAdmin(e.From))
3447 {
3448 StringBuilder sb = new StringBuilder();
3449 string NickName = Gateway.Domain;
3450
3451 if (string.IsNullOrEmpty(NickName))
3453
3454 sb.Append("Accepting an invitation from `");
3455 sb.Append(e.FromBareJID);
3456 sb.Append("` to join the room `");
3457 sb.Append(e.RoomId);
3458 sb.Append('@');
3459 sb.Append(e.Domain);
3460 sb.Append("` using nick-name `");
3461 sb.Append(NickName);
3462 sb.Append("`.");
3463
3464 if (!string.IsNullOrEmpty(e.Reason))
3465 {
3466 sb.AppendLine();
3467 sb.Append(e.Reason);
3468 }
3469
3470 await Gateway.SendNotification(sb.ToString());
3471
3472 await Enter.Execute(e.RoomId, e.Domain, NickName, e.Password, true, true, (Markdown, MessageId) =>
3473 {
3474 if (string.IsNullOrEmpty(MessageId))
3475 MessageId = Guid.NewGuid().ToString();
3476
3477 Gateway.SendNotification(Markdown, MessageId);
3478 return Task.FromResult<string>(MessageId);
3479 });
3480 }
3481 }
3482
3483 private Task MucClient_RoomInvitationReceived(object Sender, Networking.XMPP.MUC.RoomInvitationMessageEventArgs e)
3484 {
3485 e.Decline("Only accepting direct invitations.", "en");
3486 return Task.CompletedTask;
3487 }
3488
3489 internal async Task EnterRoom(string RoomId, string Domain, string NickName, string Password, bool Permanent)
3490 {
3491 RoomInfo Info = new RoomInfo(RoomId, Domain, NickName, Password, Permanent);
3492 string Jid = RoomId + "@" + Domain;
3493 bool Changed;
3494
3495 lock (this.mucRooms)
3496 {
3497 Changed = (!this.mucRooms.TryGetValue(Jid, out RoomInfo PrevInfo)) || (PrevInfo.Permanent ^ Permanent);
3498 this.mucRooms[Jid] = Info;
3499 }
3500
3501 if (Changed)
3502 await this.UpdatePermamentRooms();
3503 }
3504
3505 internal bool TryGetNickName(string RoomId, string Domain, out string NickName)
3506 {
3507 string Jid = RoomId + "@" + Domain;
3508
3509 lock (this.mucRooms)
3510 {
3511 if (this.mucRooms.TryGetValue(Jid, out RoomInfo RoomInfo))
3512 {
3513 NickName = RoomInfo.NickName;
3514 return true;
3515 }
3516 else
3517 {
3518 NickName = string.Empty;
3519 return false;
3520 }
3521 }
3522 }
3523
3524 internal bool TryGetDomainAndNickName(string RoomId, out string Domain, out string NickName)
3525 {
3526 RoomInfo Result = null;
3527
3528 lock (this.mucRooms)
3529 {
3530 foreach (RoomInfo RoomInfo in this.mucRooms.Values)
3531 {
3532 if (string.Compare(RoomInfo.RoomId, RoomId, true) == 0)
3533 {
3534 if (Result is null)
3535 Result = RoomInfo;
3536 else
3537 {
3538 Domain = NickName = null;
3539 return false;
3540 }
3541 }
3542 }
3543 }
3544
3545 Domain = Result?.Domain;
3546 NickName = Result?.NickName;
3547
3548 return !(Result is null);
3549 }
3550
3551 internal async Task LeaveRoom(string RoomId, string Domain)
3552 {
3553 string Jid = RoomId + "@" + Domain;
3554 bool Changed;
3555
3556 lock (this.mucRooms)
3557 {
3558 Changed = this.mucRooms.TryGetValue(Jid, out RoomInfo Info) && Info.Permanent;
3559 this.mucRooms.Remove(Jid);
3560 }
3561
3562 if (Changed)
3563 await this.UpdatePermamentRooms();
3564 }
3565
3566 private async Task UpdatePermamentRooms()
3567 {
3568 StringBuilder sb = new StringBuilder();
3569
3570 lock (this.mucRooms)
3571 {
3572 foreach (RoomInfo Rec in this.mucRooms.Values)
3573 {
3574 if (Rec.Permanent)
3575 {
3576 sb.Append(Rec.RoomId);
3577 sb.Append(" | ");
3578 sb.Append(Rec.Domain);
3579 sb.Append(" | ");
3580 sb.Append(Rec.NickName);
3581
3582 if (!string.IsNullOrEmpty(Rec.Password))
3583 {
3584 sb.Append(" | ");
3585 sb.Append(Rec.Password);
3586 }
3587
3588 sb.AppendLine();
3589 }
3590 }
3591 }
3592
3593 await RuntimeSettings.SetAsync("MUC.Rooms", sb.ToString().Trim());
3594 }
3595
3596 private async Task LoadPermanentRooms()
3597 {
3598 string s = await RuntimeSettings.GetAsync("MUC.Rooms", string.Empty);
3599 string[] Rows = s.Split(CommonTypes.CRLF, StringSplitOptions.RemoveEmptyEntries);
3600
3601 lock (this.adminCommands)
3602 {
3603 foreach (string Row in Rows)
3604 {
3605 string[] Parts = Row.Split(new string[] { " | " }, StringSplitOptions.None);
3606
3607 switch (Parts.Length)
3608 {
3609 case 3:
3610 this.mucRooms[Parts[0] + "@" + Parts[1]] = new RoomInfo(Parts[0], Parts[1], Parts[2], true);
3611 break;
3612
3613 case 4:
3614 this.mucRooms[Parts[0] + "@" + Parts[1]] = new RoomInfo(Parts[0], Parts[1], Parts[2], Parts[3], true);
3615 break;
3616 }
3617 }
3618 }
3619
3620 if (Gateway.XmppClient.State == Networking.XMPP.XmppState.Connected)
3621 await this.RejoinRooms();
3622
3623 Gateway.ScheduleEvent((P) => this.CheckRoomsAlive(), DateTime.Now.AddMinutes(1), null);
3624 }
3625
3626 internal RoomInfo[] GetRooms()
3627 {
3628 RoomInfo[] Infos;
3629
3630 lock (this.mucRooms)
3631 {
3632 Infos = new RoomInfo[this.mucRooms.Count];
3633 this.mucRooms.Values.CopyTo(Infos, 0);
3634 }
3635
3636 return Infos;
3637 }
3638
3639 private async Task RejoinRooms()
3640 {
3641 foreach (RoomInfo Info in this.GetRooms())
3642 await this.RejoinRoom(Info);
3643 }
3644
3645 private Task RejoinRoom(RoomInfo Info)
3646 {
3647 return Gateway.MucClient?.EnterRoom(Info.RoomId, Info.Domain, Info.NickName, Info.Password, (Sender, e) =>
3648 {
3649 if (e.Ok)
3650 Gateway.MucClient.SetPresence(Info.RoomId, Info.Domain, Info.NickName, Networking.XMPP.Availability.Chat, null, null);
3651 else
3652 Gateway.ScheduleEvent((P) => this.RejoinRoom(Info), DateTime.Now.AddMinutes(1), null);
3653
3654 return Task.CompletedTask;
3655
3656 }, null) ?? Task.CompletedTask;
3657 }
3658
3659 internal void CheckRoomsAlive()
3660 {
3661 try
3662 {
3663 if (Gateway.XmppClient.State == Networking.XMPP.XmppState.Connected)
3664 {
3665 RoomInfo[] Infos = this.GetRooms();
3666
3667 foreach (RoomInfo Info in Infos)
3668 {
3669 Gateway.MucClient.SelfPing(Info.RoomId, Info.Domain, Info.NickName, (Sender, e) =>
3670 {
3671 if (!e.Ok)
3672 {
3673 if (e.StanzaError is Networking.XMPP.StanzaErrors.NotAcceptableException) // Need to reconnect with room.
3674 {
3675 RoomInfo Info2 = (RoomInfo)e.State;
3676 Gateway.MucClient.EnterRoom(Info2.RoomId, Info2.Domain, Info2.NickName, Info2.Password, async (sender2, e2) =>
3677 {
3678 if (e2.Ok)
3679 await Gateway.MucClient.SetPresence(Info2.RoomId, Info2.Domain, Info2.NickName, Networking.XMPP.Availability.Chat, null, null);
3680 else
3681 {
3682 RoomInfo Info3 = (RoomInfo)e2.State;
3683
3684 if (e2.StanzaError is Networking.XMPP.StanzaErrors.ForbiddenException) // Blocked
3685 {
3686 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3687 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; forbidden entry (blocked?).");
3688 }
3689 else if (e2.StanzaError is Networking.XMPP.StanzaErrors.NotAuthorizedException) // Password changed, too many failed attempts
3690 {
3691 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3692 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; password changed.");
3693 }
3694 else if (e2.StanzaError is Networking.XMPP.StanzaErrors.RegistrationRequiredException) // Not registered in room
3695 {
3696 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3697 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; no longer registered in room.");
3698 }
3699 else if (e2.StanzaError is Networking.XMPP.StanzaErrors.ItemNotFoundException) // Room not found/locked
3700 {
3701 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3702 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; room not found, or is locked for other purposes.");
3703 }
3704 else if (e2.StanzaError is Networking.XMPP.StanzaErrors.ServiceUnavailableException) // Room full
3705 {
3706 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3707 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; room is full.");
3708 }
3709 else if (e2.StanzaError is Networking.XMPP.StanzaErrors.ConflictException) // Nick-name conflict
3710 {
3711 await this.LeaveRoom(Info3.RoomId, Info3.Domain);
3712 await Gateway.SendNotification("Left room `" + Info3.RoomId + "@" + Info3.Domain + "`; nick-name conflict.");
3713 }
3714 }
3715 }, Info2);
3716 }
3717 }
3718
3719 return Task.CompletedTask;
3720 }, Info);
3721 }
3722 }
3723 }
3724 catch (Exception ex)
3725 {
3726 Log.Exception(ex);
3727 }
3728 finally
3729 {
3730 Gateway.ScheduleEvent((P) => this.CheckRoomsAlive(), DateTime.Now.AddMinutes(1), null);
3731 }
3732 }
3733
3734 private async Task LeaveAllRooms()
3735 {
3736 RoomInfo[] Rooms = this.GetRooms();
3737 int i, c = Rooms.Length;
3738 TaskCompletionSource<bool>[] TaskCompletionSources = new TaskCompletionSource<bool>[c];
3739 Task[] Tasks = new Task[c];
3740
3741 for (i = 0; i < c; i++)
3742 {
3743 RoomInfo Info = Rooms[i];
3744 TaskCompletionSources[i] = new TaskCompletionSource<bool>();
3745 Tasks[i] = TaskCompletionSources[i].Task;
3746
3747 await Gateway.MucClient.SetPresence(Info.RoomId, Info.Domain, Info.NickName, Networking.XMPP.Availability.Offline,
3748 (_, e) =>
3749 {
3750 ((TaskCompletionSource<bool>)e.State)?.TrySetResult(true);
3751 return Task.CompletedTask;
3752 }, TaskCompletionSources[i]);
3753 }
3754
3755 _ = Task.Delay(5000).ContinueWith((_) =>
3756 {
3757 foreach (TaskCompletionSource<bool> T in TaskCompletionSources)
3758 T.TrySetResult(false);
3759 return Task.CompletedTask;
3760 });
3761
3762 await Task.WhenAll(Tasks);
3763 }
3764
3765 private async Task XmppClient_OnGroupChatMessage(object Sender, Networking.XMPP.Events.MessageEventArgs e)
3766 {
3767 XmppAddress Addr = new XmppAddress(e.From);
3768 string RoomId = Addr.Account;
3769 string Domain = Addr.Domain;
3770 string NickName = Addr.Resource;
3771 (string Message, string _, bool IsMarkdown) = await this.GetPlainText(e, DateTime.UtcNow.AddMinutes(-1));
3772
3773 Task _ = Task.Run(async () => await this.ProcessChatMessage(Message, this.GetOccupantRealJid(e), e.ThreadID, async (Response, MessageId) =>
3774 {
3775 KeyValuePair<string, string> P = await this.PrepareAdminResponse(Response, MessageId, IsMarkdown);
3776 string Xml = P.Key;
3777 MessageId = P.Value;
3778
3779 if (Xml is null)
3780 Xml = "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
3781 else
3782 Xml += "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
3783
3784 await Gateway.MucClient.SendCustomPrivateMessage(MessageId, RoomId, Domain, NickName, Xml, string.Empty, e.ThreadID, string.Empty);
3785
3786 return MessageId;
3787 }, Gateway.XmppClient));
3788 }
3789
3790 internal void RegisterConsolidator(string ThreadId, IConsolidator Consolidator)
3791 {
3792 if (this.consolidators is null)
3793 {
3794 this.consolidators = new Cache<string, IConsolidator>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromSeconds(30));
3795 this.consolidators.Removed += this.Consolidators_Removed;
3796 }
3797
3798 this.consolidators[ThreadId] = Consolidator;
3799 }
3800
3801 private Task Consolidators_Removed(object Sender, CacheItemEventArgs<string, IConsolidator> e)
3802 {
3803 return e.Value.DisposeAsync();
3804 }
3805
3806 private async Task MucClient_PrivateMessageReceived(object Sender, Networking.XMPP.MUC.RoomOccupantMessageEventArgs e)
3807 {
3808 if (!string.IsNullOrEmpty(e.ThreadID) &&
3809 !(this.consolidators is null) &&
3810 this.consolidators.TryGetValue(e.ThreadID, out IConsolidator Consolidator))
3811 {
3812 string Response = this.GetMarkdown(e, DateTime.UtcNow.AddMinutes(-1), false, out string ReplaceObjectId, out bool IsMarkdown);
3813 MarkdownDocument Doc = null;
3814
3816 {
3817 if (!string.IsNullOrEmpty(e.Body))
3818 Response = e.Body;
3819 else
3820 Doc = await MarkdownDocument.CreateAsync(Response);
3821 }
3822
3823 if (Doc is null)
3824 {
3825 if (!string.IsNullOrEmpty(ReplaceObjectId))
3826 await Consolidator.Update(e.NickName, Response, ReplaceObjectId);
3827 else
3828 await Consolidator.Add(e.NickName, Response, e.Id);
3829 }
3830 else
3831 {
3832 if (!string.IsNullOrEmpty(ReplaceObjectId))
3833 await Consolidator.Update(e.NickName, Doc, ReplaceObjectId);
3834 else
3835 await Consolidator.Add(e.NickName, Doc, e.Id);
3836 }
3837 }
3838 else
3839 {
3840 (string Message, string _, bool IsMarkdown) = await this.GetPlainText(e, DateTime.UtcNow.AddMinutes(-1));
3841
3842 Task _ = Task.Run(async () => await this.ProcessChatMessage(Message, this.GetOccupantRealJid(e), e.ThreadID, async (Response, MessageId) =>
3843 {
3844 KeyValuePair<string, string> P = await this.PrepareAdminResponse(Response, MessageId, IsMarkdown);
3845 string Xml = P.Key;
3846 MessageId = P.Value;
3847
3848 if (Xml is null)
3849 Xml = "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
3850 else
3851 Xml += "<muteDiff xmlns='http://waher.se/Schema/Editing.xsd'/>";
3852
3853 await Gateway.MucClient.SendCustomPrivateMessage(MessageId, e.RoomId, e.Domain, e.NickName, Xml, string.Empty, e.ThreadID, string.Empty);
3854
3855 return MessageId;
3856 }, Gateway.XmppClient));
3857 }
3858 }
3859
3860 private async Task<KeyValuePair<string, string>> PrepareAdminResponse(string Response, string MessageId, bool IncomingWasMarkdown)
3861 {
3862 string Xml;
3863
3864 if (IncomingWasMarkdown)
3865 Xml = await Gateway.GetMultiFormatChatMessageXml(Response, false, false);
3866 else
3867 Xml = await Gateway.GetMultiFormatChatMessageXml(Response, true, true);
3868
3869 if (string.IsNullOrEmpty(MessageId))
3870 MessageId = Guid.NewGuid().ToString();
3871 else
3872 Xml += "<replace id='" + MessageId + "' xmlns='urn:xmpp:message-correct:0'/>";
3873
3874 return new KeyValuePair<string, string>(Xml, MessageId);
3875 }
3876
3877 private Task MucClient_RoomDestroyed(object Sender, Networking.XMPP.MUC.UserPresenceEventArgs e)
3878 {
3879 lock (this.presenceByNickAndRoom)
3880 {
3881 this.presenceByNickAndRoom.Remove(e.RoomJid);
3882 }
3883
3884 return Task.CompletedTask;
3885 }
3886
3887 private Task MucClient_OccupantPresence(object Sender, Networking.XMPP.MUC.UserPresenceEventArgs e)
3888 {
3889 lock (this.presenceByNickAndRoom)
3890 {
3891 if (!this.presenceByNickAndRoom.TryGetValue(e.RoomJid, out SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs> ByNick))
3892 {
3893 if (e.Availability == Networking.XMPP.Availability.Offline)
3894 ByNick = null;
3895 else
3896 {
3897 ByNick = new SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>();
3898 this.presenceByNickAndRoom[e.RoomJid] = ByNick;
3899 }
3900 }
3901
3902 if (!(ByNick is null))
3903 {
3904 if (e.Availability == Networking.XMPP.Availability.Offline)
3905 {
3906 if (ByNick.Remove(e.NickName) && ByNick.Count == 0)
3907 this.presenceByNickAndRoom.Remove(e.RoomId);
3908 }
3909 else
3910 ByNick[e.NickName] = e;
3911 }
3912 }
3913
3914 return Task.CompletedTask;
3915 }
3916
3917 internal int GetNrOccupants(string RoomJid, string RoomId, string Domain, bool ExcludeSelf, bool ExcludeOffline)
3918 {
3919 string OwnNick;
3920
3921 if (ExcludeSelf)
3922 {
3923 if (!this.TryGetNickName(RoomId, Domain, out OwnNick))
3924 OwnNick = null;
3925 }
3926 else
3927 OwnNick = null;
3928
3929 lock (this.presenceByNickAndRoom)
3930 {
3931 if (this.presenceByNickAndRoom.TryGetValue(RoomJid, out SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs> ByNick))
3932 {
3933 if (!ExcludeSelf && !ExcludeOffline)
3934 return ByNick.Count;
3935
3936 int c = 0;
3937
3938 foreach (Networking.XMPP.MUC.UserPresenceEventArgs e in ByNick.Values)
3939 {
3940 if (ExcludeOffline && !e.IsOnline)
3941 continue;
3942
3943 if (ExcludeSelf && !(OwnNick is null) && Networking.XMPP.XmppClient.GetResource(e.From) == OwnNick)
3944 continue;
3945
3946 c++;
3947 }
3948
3949 return c;
3950 }
3951 else
3952 return 0;
3953 }
3954 }
3955
3956 internal KeyValuePair<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>[] GetOccupants(string RoomJid)
3957 {
3958 lock (this.presenceByNickAndRoom)
3959 {
3960 if (!this.presenceByNickAndRoom.TryGetValue(RoomJid, out SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs> ByNick))
3961 return null;
3962
3963 KeyValuePair<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>[] Result =
3964 new KeyValuePair<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs>[ByNick.Count];
3965
3966 ByNick.CopyTo(Result, 0);
3967
3968 return Result;
3969 }
3970 }
3971
3972 private string GetOccupantRealJid(Networking.XMPP.Events.MessageEventArgs e)
3973 {
3974 foreach (XmlNode N in e.Message.ChildNodes)
3975 {
3976 if (N is XmlElement E && E.LocalName == "addresses" && E.NamespaceURI == XmppServer.ExtendedAddressingNamespace)
3977 {
3978 foreach (XmlNode N2 in E.ChildNodes)
3979 {
3980 if (N2 is XmlElement E2 &&
3981 E2.LocalName == "address" &&
3982 E2.NamespaceURI == E.NamespaceURI &&
3983 E2.HasAttribute("type") &&
3984 E2.GetAttribute("type") == "ofrom")
3985 {
3986 return XML.Attribute(E2, "jid");
3987 }
3988 }
3989 }
3990 }
3991
3992 string OccupantJid = e.From;
3993 XmppAddress Addr = new XmppAddress(OccupantJid);
3994 CaseInsensitiveString RoomJid = Addr.BareJid;
3995 CaseInsensitiveString NickName = Addr.Resource;
3996
3997 lock (this.presenceByNickAndRoom)
3998 {
3999 if (this.presenceByNickAndRoom.TryGetValue(RoomJid, out SortedDictionary<CaseInsensitiveString, Networking.XMPP.MUC.UserPresenceEventArgs> ByNick) &&
4000 ByNick.TryGetValue(NickName, out Networking.XMPP.MUC.UserPresenceEventArgs e2))
4001 {
4002 return string.IsNullOrEmpty(e2.FullJid) ? OccupantJid : e2.FullJid;
4003 }
4004 }
4005
4006 return OccupantJid;
4007 }
4008
4009 internal Task ProcessChatMessage(string s, string From, string ThreadId, ResponseCallbackHandler ResponseCallback, ICommunicationLayer Channel)
4010 {
4011 return this.ProcessChatMessage(s, From, null, ThreadId, ResponseCallback, Channel);
4012 }
4013
4014 internal async Task ProcessChatMessage(string s, string From, IUser User, string ThreadId, ResponseCallbackHandler ResponseCallback, ICommunicationLayer Channel)
4015 {
4016 try
4017 {
4018 if (string.IsNullOrEmpty(s))
4019 {
4020 Channel?.Information("(Ignored, since empty)");
4021 return;
4022 }
4023
4024 if (From == Gateway.XmppClient.FullJID)
4025 {
4026 if (string.IsNullOrEmpty(ThreadId) ||
4027 this.consolidators is null ||
4028 !this.consolidators.ContainsKey(ThreadId))
4029 {
4030 string Msg = "Supressing query from self. No outstanding consolidation query registered: " + s;
4031
4032 Channel?.Warning(Msg);
4033
4034 Log.Warning(Msg, Gateway.Domain, From);
4035 return;
4036 }
4037 }
4038 else if (!await IsAdmin(From, User))
4039 {
4040 string Msg = "Ignoring incoming chat message (sender is not an administrator): " + s;
4041
4042 Channel?.Warning(Msg);
4043
4044 Log.Warning(Msg, Gateway.Domain, From);
4045 return;
4046 }
4047
4048 Channel?.Information("Chat command string accepted.");
4049
4050 if (this.chatSessions is null)
4051 {
4052 this.chatSessions = new Cache<string, Variables>(1000, TimeSpan.MaxValue, TimeSpan.FromMinutes(15), true);
4053 this.chatSessions.Removed += this.ChatSessions_Removed;
4054 }
4055
4056 if (!this.chatSessions.TryGetValue(From, out Variables Session))
4057 {
4059 this.chatSessions[From] = Session;
4060 }
4061
4062 if (!Session.TryGetVariable(" State ", out Variable v) || !(v.ValueObject is ChatState State))
4063 {
4064 State = new ChatState(Session)
4065 {
4066 To = From,
4067 Folder = PackagesFolder
4068 };
4069 Session[" State "] = State;
4070 }
4071
4072 StringBuilder sb = new StringBuilder();
4073 List<string> ArgumentsList = new List<string>();
4074 LinkedList<IAdminCommand> Commands;
4076 object Details;
4077 string[] Arguments;
4078 string CommandName = null;
4079 int c = 0;
4080 bool InQuote = false;
4081 bool Escape = false;
4082
4083 foreach (char ch in s)
4084 {
4085 if (char.IsWhiteSpace(ch) && !InQuote)
4086 {
4087 if (c > 0)
4088 {
4089 if (CommandName is null)
4090 CommandName = sb.ToString().ToLower();
4091 else
4092 ArgumentsList.Add(sb.ToString());
4093
4094 sb.Clear();
4095 c = 0;
4096 }
4097 }
4098 else if (Escape)
4099 {
4100 sb.Append(ch);
4101 c++;
4102 Escape = false;
4103 }
4104 else if (ch == '\\')
4105 Escape = true;
4106 else if (ch == '"')
4107 InQuote = !InQuote;
4108 else
4109 {
4110 sb.Append(ch);
4111 c++;
4112 }
4113 }
4114
4115 if (Escape)
4116 {
4117 sb.Append('\\');
4118 c++;
4119 }
4120
4121 if (c > 0)
4122 {
4123 if (CommandName is null)
4124 CommandName = sb.ToString().ToLower();
4125 else
4126 ArgumentsList.Add(sb.ToString());
4127 }
4128
4129 if (CommandName is null)
4130 {
4131 Channel?.Information("(Ignored, no command)");
4132 return;
4133 }
4134
4135 Arguments = ArgumentsList.ToArray();
4136
4137 lock (this.adminCommands)
4138 {
4139 if (this.adminCommands.Count == 0)
4140 {
4141 foreach (Type T in Types.GetTypesImplementingInterface(typeof(IAdminCommand)))
4142 {
4143 try
4144 {
4145 TypeInfo TI = T.GetTypeInfo();
4146
4147 if (TI.IsAbstract || TI.IsInterface || TI.IsGenericTypeDefinition)
4148 continue;
4149
4151
4152 string s2 = Command.Name.ToLower();
4153 if (!this.adminCommands.TryGetValue(s2, out Commands))
4154 {
4155 Commands = new LinkedList<IAdminCommand>();
4156 this.adminCommands[s2] = Commands;
4157 }
4158
4159 Commands.AddLast(Command);
4160
4161 string[] Aliases = Command.Aliases;
4162
4163 if (!(Aliases is null))
4164 {
4165 foreach (string Alias in Aliases)
4166 {
4167 s2 = Alias.ToLower();
4168 if (!this.adminCommands.TryGetValue(s2, out Commands))
4169 {
4170 Commands = new LinkedList<IAdminCommand>();
4171 this.adminCommands[s2] = Commands;
4172 }
4173
4174 Commands.AddLast(Command);
4175 }
4176 }
4177 }
4178 catch (Exception ex)
4179 {
4180 Log.Exception(ex, T.FullName);
4181 }
4182 }
4183 }
4184
4185 Commands = null;
4186 Command = null;
4187 Details = null;
4188
4189 if (this.adminCommands.TryGetValue(CommandName, out Commands))
4190 {
4191 foreach (IAdminCommand Alternative in Commands)
4192 {
4193 if (Alternative.AppliesTo(s, Arguments, out Details))
4194 {
4195 Command = Alternative;
4196 break;
4197 }
4198 }
4199 }
4200 }
4201
4202 if (Command is null)
4203 {
4204 if (Arguments.Length == 0 && !(Commands is null))
4205 await this.ProcessChatMessage("help " + s, From, User, ThreadId, ResponseCallback, Channel);
4206 else if (State.CommandMode)
4207 {
4208 foreach (string Row in s.Split(CommonTypes.CRLF, StringSplitOptions.RemoveEmptyEntries))
4209 await this.ExecuteCommand(From, Row.Trim(), State, false, ResponseCallback);
4210 }
4211 else
4212 {
4213 StringBuilder PrintOutput = new StringBuilder();
4214 StringWriter PrintWriter = new StringWriter(PrintOutput);
4215 Session.ConsoleOut = PrintWriter;
4216 Expression Exp = new Expression(s);
4217 IElement Result;
4218 AdminScriptRec Rec = new AdminScriptRec()
4219 {
4220 ResponseCallback = ResponseCallback
4221 };
4222
4223 Task Preview(object Sender, PreviewEventArgs e)
4224 {
4225 return this.ReturnScriptResult(e.Preview, Rec);
4226 }
4227 ;
4228
4229 Session.OnPreview += Preview;
4230 try
4231 {
4232 Result = await Exp.Root.EvaluateAsync(Session);
4233 }
4235 {
4236 Result = ex.ReturnValue;
4237 //ScriptReturnValueException.Reuse(ex);
4238 }
4239 catch (ScriptBreakLoopException ex)
4240 {
4241 Result = ex.LoopValue ?? ObjectValue.Null;
4242 //ScriptBreakLoopException.Reuse(ex);
4243 }
4245 {
4246 Result = ex.LoopValue ?? ObjectValue.Null;
4247 //ScriptContinueLoopException.Reuse(ex);
4248 }
4249 catch (Exception ex)
4250 {
4251 Result = new ObjectValue(ex);
4252 }
4253 finally
4254 {
4255 Session.OnPreview -= Preview;
4256 }
4257
4258 Session["Ans"] = Result;
4259
4260 string Printed = PrintOutput.ToString().Trim();
4261 PrintOutput.Clear();
4262
4263 if (!string.IsNullOrEmpty(Printed))
4264 await SendPrinted(Printed, string.Empty, ResponseCallback);
4265
4266 await this.ReturnScriptResult(Result, Rec);
4267 }
4268 }
4269 else
4270 await Command.Execute(State, ArgumentsList.ToArray(), s, Details, ResponseCallback);
4271 }
4272 catch (Exception ex)
4273 {
4274 await ReturnError(ex, ResponseCallback);
4275 }
4276 }
4277
4278 private static async Task ReturnError(Exception ex, ResponseCallbackHandler ResponseCallback)
4279 {
4280 try
4281 {
4282 ex = Log.UnnestException(ex);
4283 StringBuilder Markdown = new StringBuilder();
4284
4285 Markdown.AppendLine("An error occurred when processing request:");
4286 Markdown.AppendLine();
4287 Markdown.AppendLine("<font class=\"error\">");
4288 Markdown.AppendLine();
4289 Markdown.AppendLine(MarkdownDocument.Encode(ex.Message));
4290 Markdown.AppendLine();
4291 Markdown.AppendLine("```");
4292 Markdown.AppendLine(Log.CleanStackTrace(ex.StackTrace));
4293 Markdown.AppendLine("```");
4294 Markdown.AppendLine();
4295 Markdown.AppendLine("</font>");
4296
4297 await ResponseCallback(Markdown.ToString(), string.Empty);
4298 }
4299 catch (Exception ex2)
4300 {
4301 Log.Exception(ex2);
4302 }
4303 }
4304
4305 private class AdminScriptRec
4306 {
4307 public string MessageId = string.Empty;
4308 public ResponseCallbackHandler ResponseCallback;
4309 }
4310
4311 private async Task ReturnScriptResult(IElement Result, AdminScriptRec Rec)
4312 {
4313 StringBuilder sb = new StringBuilder();
4314
4316 Result = ToMatrix.ToMatrix();
4317
4318 if (Result is Graph G)
4319 {
4320 sb.AppendLine("```Graph");
4321 G.ToXml(sb);
4322 sb.AppendLine();
4323 sb.AppendLine("```");
4324
4325 Rec.MessageId = await Rec.ResponseCallback(sb.ToString(), Rec.MessageId);
4326 }
4327 else if (Result.AssociatedObjectValue is SKImage Img)
4328 {
4329 SKData Data = Img.Encode(SKEncodedImageFormat.Png, 100);
4330 byte[] Bin = Data.ToArray();
4331 Data.Dispose();
4332
4333 sb.Append("![Image result](data:image/png;base64,");
4334 sb.Append(System.Convert.ToBase64String(Bin, 0, Bin.Length));
4335 sb.Append(')');
4336
4337 Rec.MessageId = await Rec.ResponseCallback(sb.ToString(), Rec.MessageId);
4338 }
4339 else if (Result.AssociatedObjectValue is Exception ex)
4340 {
4341 ex = Log.UnnestException(ex);
4342
4343 if (ex is AggregateException ex2)
4344 {
4345 sb.Clear();
4346
4347 foreach (Exception ex3 in ex2.InnerExceptions)
4348 {
4349 sb.AppendLine(ex3.Message);
4350 sb.AppendLine();
4351 }
4352
4353 Rec.MessageId = await SendErrorMessage(MarkdownDocument.Encode(sb.ToString()), Rec.MessageId, Rec.ResponseCallback);
4354 }
4355 else
4356 Rec.MessageId = await SendErrorMessage(MarkdownDocument.Encode(ex.Message), Rec.MessageId, Rec.ResponseCallback);
4357 }
4358 else if (Result.AssociatedObjectValue is ObjectMatrix M && !(M.ColumnNames is null))
4359 {
4360 sb.Clear();
4361
4362 foreach (string s3 in M.ColumnNames)
4363 {
4364 sb.Append("| ");
4365 sb.Append(MarkdownDocument.Encode(s3));
4366 sb.Append(' ');
4367 }
4368
4369 sb.AppendLine("|");
4370
4371 foreach (string _ in M.ColumnNames)
4372 sb.Append("|---");
4373
4374 sb.AppendLine("|");
4375
4376 int x, y;
4377
4378 for (y = 0; y < M.Rows; y++)
4379 {
4380 for (x = 0; x < M.Columns; x++)
4381 {
4382 sb.Append("| ");
4383
4384 object Item = M.GetElement(x, y).AssociatedObjectValue;
4385 if (!(Item is null))
4386 {
4387 if (Item is string s3)
4388 s3 = MarkdownDocument.Encode(s3);
4389 else if (Item is MarkdownElement Element)
4390 s3 = Element.ToString();
4391 else
4393
4394 s3 = s3.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "<br/>");
4395 sb.Append(s3);
4396 sb.Append(' ');
4397 }
4398 }
4399
4400 sb.AppendLine("|");
4401 }
4402
4403 Rec.MessageId = await Rec.ResponseCallback(sb.ToString(), Rec.MessageId);
4404 }
4405 else
4406 Rec.MessageId = await SendResult(Expression.ToExpressionString(Result), Rec.MessageId, Rec.ResponseCallback);
4407 }
4408
4409 internal bool TryGetAdminCommand(string CommandName, out LinkedList<IAdminCommand> Command)
4410 {
4411 lock (this.adminCommands)
4412 {
4413 return this.adminCommands.TryGetValue(CommandName, out Command);
4414 }
4415 }
4416
4417 internal string[] AdminCommands
4418 {
4419 get
4420 {
4421 string[] Result;
4422
4423 lock (this.adminCommands)
4424 {
4425 Result = new string[this.adminCommands.Count];
4426 this.adminCommands.Keys.CopyTo(Result, 0);
4427 }
4428
4429 return Result;
4430 }
4431 }
4432
4433 internal static async Task SendPrinted(string Msg, string MessageId, ResponseCallbackHandler ResponseCallback)
4434 {
4436
4437 foreach (string Row in Msg.Replace("\r\n", "\n").Replace('\r', '\n').Split('\n'))
4438 await ResponseCallback("<font style=\"color:" + PrintColor + "\">" + Row + "</font>", MessageId);
4439 }
4440
4441 internal static Task<string> SendResult(string Msg, string MessageId, ResponseCallbackHandler ResponseCallback)
4442 {
4443 StringBuilder sb = new StringBuilder();
4444
4445 if (Msg.IndexOfAny(CommonTypes.CRLF) < 0)
4446 {
4447 sb.Append("<font class=\"result\">`");
4448 sb.Append(Msg);
4449 sb.Append("`</font>");
4450 }
4451 else
4452 {
4453 sb.AppendLine("<font class=\"result\">");
4454 sb.AppendLine();
4455 sb.AppendLine("```");
4456
4457
4458 Msg = Msg.Replace("\r\n", "\n").Replace("\r", "\n");
4459 foreach (string Row in Msg.Split('\n'))
4460 sb.AppendLine(Row);
4461
4462 sb.AppendLine("```");
4463 sb.AppendLine();
4464 sb.AppendLine("</font>");
4465 }
4466
4467 return ResponseCallback(sb.ToString(), MessageId);
4468 }
4469
4470 internal static Task<string> SendErrorMessage(string Msg, string MessageId, ResponseCallbackHandler ResponseCallback)
4471 {
4472 StringBuilder sb = new StringBuilder();
4473
4474 sb.AppendLine("<font class=\"error\">");
4475 sb.AppendLine();
4476 sb.AppendLine(Msg);
4477 sb.AppendLine();
4478 sb.AppendLine("</font>");
4479
4480 return ResponseCallback(sb.ToString(), MessageId);
4481 }
4482
4483 internal async Task<bool> ExecuteCommand(string To, string Command, ChatState State, bool Shell, ResponseCallbackHandler ResponseCallback)
4484 {
4485 if (string.IsNullOrEmpty(Command))
4486 return false;
4487
4488 int AppDataLen = Gateway.AppDataFolder.Length;
4489 bool Exists;
4490 string s;
4491
4492 try
4493 {
4494 s = Path.GetFullPath(Path.Combine(State.Folder, Command));
4495 Exists = File.Exists(s);
4496 }
4497 catch (Exception)
4498 {
4499 Exists = false;
4500 s = null;
4501 }
4502
4503 if (Exists)
4504 {
4505 if (s.Length < AppDataLen || string.Compare(Gateway.AppDataFolder, s[..AppDataLen], true) != 0)
4506 {
4507 await SendErrorMessage("Access to files outside of the application data folder not permitted.", string.Empty, ResponseCallback);
4508 return false;
4509 }
4510
4511 if (!string.IsNullOrEmpty(To))
4512 await ResponseCallback(this.DownloadableFile(s, State, To), string.Empty);
4513 return true;
4514 }
4515
4516 int i = Command.IndexOfAny(new char[] { ' ', '\t' });
4517
4518 if (i < 0)
4519 {
4520 s = Command;
4521 i = Command.Length;
4522 }
4523 else
4524 s = Command[..i];
4525
4526 switch (s.ToLower())
4527 {
4528 case "analyzeclock":
4529 case "csp":
4530 case "exstat":
4531 case "extract":
4532 case "install":
4533 case "regex":
4534 case "sign":
4535 case "transform":
4536
4537 if (Shell && string.Compare(s, "install", true) == 0)
4538 s = Path.Combine(Path.GetDirectoryName(typeof(Gateway).Assembly.Location), "InstallUtility", "Waher.Utility." + s + FileSystem.ExecutableExtension);
4539 else
4540 s = Path.Combine(Path.GetDirectoryName(typeof(Gateway).Assembly.Location), "Waher.Utility." + s + FileSystem.ExecutableExtension);
4541
4542 if (File.Exists(s))
4543 {
4544 Command = "\"" + s + "\"" + Command[i..];
4545 break;
4546 }
4547
4548 s = s[..^3] + "dll";
4549 if (File.Exists(s))
4550 {
4551 Command = "dotnet \"" + s + "\"" + Command[i..];
4552 break;
4553 }
4554
4555 await SendErrorMessage("File not found: `" + s[..^3] + "exe`. Unable to execute command.", string.Empty, ResponseCallback);
4556 return false;
4557
4558 // Windows commands
4559 case "bcdedit":
4560 case "call":
4561 case "cmd":
4562 case "del":
4563 case "diskpart":
4564 case "doskey":
4565 case "erase":
4566 case "format":
4567 case "fsutil":
4568 case "label":
4569 case "md":
4570 case "mkdir":
4571 case "mode":
4572 case "move":
4573 case "openfiles":
4574 case "rd":
4575 case "ren":
4576 case "rename":
4577 case "replace":
4578 case "rmdir":
4579 case "robocopy":
4580 case "sc":
4581 case "schtasks":
4582 case "start":
4583 case "taskkill":
4584 case "xcopy":
4585 case "wmic":
4586 case "telnet":
4587 case "icacls":
4588 case "runas":
4589 case "notepad":
4590 case "cl":
4591 case "python":
4592 case "perl":
4593
4594 // macOS commands
4595 // Linux commands
4596 case "bash":
4597 case "zh":
4598 case "zsh":
4599 case "source":
4600 case "rm":
4601 case "diskutil":
4602 case "fdisk":
4603 case "alias":
4604 case "erasedisk":
4605 case "mkfs":
4606 case "e2label":
4607 case "stty":
4608 case "setterm":
4609 case "mv":
4610 case "lsof":
4611 case "rsync":
4612 case "launchctl":
4613 case "systemctl":
4614 case "crontab":
4615 case "launchd":
4616 case "open":
4617 case "xdg-open":
4618 case "kill":
4619 case "dd":
4620 case "chmod":
4621 case "chown":
4622 case "sudo":
4623 case "su":
4624 case "killall":
4625 case "ssh":
4626 case "wget":
4627 case "curl":
4628 case "tar":
4629 case "unzip":
4630 case "zip":
4631 case "nano":
4632 case "vi":
4633 case "vim":
4634 case "emacs":
4635 case "script":
4636 case "screen":
4637 case "tmux":
4638 case "nohup":
4639
4640 await SendErrorMessage("Command not allowed.", string.Empty, ResponseCallback);
4641 // TODO: Secure with provisioning on a command level.
4642 return false;
4643
4644 case "cd.":
4645 return ChangeDirectory(State, ".", ResponseCallback);
4646
4647 case "cd..":
4648 return ChangeDirectory(State, "..", ResponseCallback);
4649
4650 case "cd":
4651 case "chdir":
4652 return ChangeDirectory(State, Command[s.Length..].Trim(), ResponseCallback);
4653
4654 case "dir":
4655 case "ls":
4656 try
4657 {
4658 string Pattern = Command[s.Length..].Trim();
4659 if (string.IsNullOrEmpty(Pattern))
4660 Pattern = "*.*";
4661
4662 DirectoryInfo Folder = new DirectoryInfo(State.Folder);
4663 DirectoryInfo[] Folders;
4664 System.IO.FileInfo[] Files;
4665 StringBuilder Markdown = new StringBuilder();
4666 Dictionary<string, long> Sizes = new Dictionary<string, long>();
4667 Dictionary<string, DateTime> DateTimes = new Dictionary<string, DateTime>();
4668 string MaxSizeFileName = null;
4669 string MaxDateTimeFileName = null;
4670 DateTime MaxDateTime = DateTime.MinValue;
4671 long MaxSize = int.MinValue;
4672 DateTime TP;
4673 bool IsMax;
4674
4675 Markdown.AppendLine("| Name | Size | Date | Time |");
4676 Markdown.AppendLine("|:-----|-----:|:----:|:----:|");
4677
4678 Folders = Folder.GetDirectories(Pattern, SearchOption.TopDirectoryOnly);
4679
4680 foreach (DirectoryInfo DirInfo in Folders)
4681 {
4682 try
4683 {
4684 DateTimes[DirInfo.Name] = DirInfo.LastWriteTime;
4685 if (DirInfo.LastWriteTime > MaxDateTime)
4686 {
4687 MaxDateTime = DirInfo.LastWriteTime;
4688 MaxDateTimeFileName = DirInfo.Name;
4689 }
4690 }
4691 catch (Exception)
4692 {
4693 // Ignore.
4694 }
4695 }
4696
4697 foreach (DirectoryInfo DirInfo in Folders)
4698 {
4699 Markdown.Append("| ");
4700 Markdown.Append(MarkdownDocument.Encode(Path.GetFileName(DirInfo.Name)));
4701 Markdown.Append(" | \\<DIR\\> | ");
4702
4703 if (DateTimes.TryGetValue(DirInfo.Name, out TP))
4704 {
4705 if (IsMax = (DirInfo.Name == MaxDateTimeFileName))
4706 Markdown.Append("**");
4707
4708 Markdown.Append(TP.ToShortDateString());
4709
4710 if (IsMax)
4711 Markdown.Append("**");
4712
4713 Markdown.Append(" | ");
4714
4715 if (IsMax)
4716 Markdown.Append("**");
4717
4718 Markdown.Append(TP.ToLongTimeString());
4719
4720 if (IsMax)
4721 Markdown.Append("**");
4722
4723 Markdown.AppendLine(" |");
4724 }
4725 else
4726 Markdown.AppendLine("N/A ||");
4727 }
4728
4729 Files = Folder.GetFiles(Pattern, SearchOption.TopDirectoryOnly);
4730 DateTimes.Clear();
4731 MaxDateTime = DateTime.MinValue;
4732
4733 foreach (System.IO.FileInfo FileInfo in Files)
4734 {
4735 try
4736 {
4737 Sizes[FileInfo.Name] = FileInfo.Length;
4738 if (FileInfo.Length > MaxSize)
4739 {
4740 MaxSize = FileInfo.Length;
4741 MaxSizeFileName = FileInfo.Name;
4742 }
4743 }
4744 catch (Exception)
4745 {
4746 // Ignore.
4747 }
4748
4749 try
4750 {
4751 DateTimes[FileInfo.Name] = FileInfo.LastWriteTime;
4752 if (FileInfo.LastWriteTime > MaxDateTime)
4753 {
4754 MaxDateTime = FileInfo.LastWriteTime;
4755 MaxDateTimeFileName = FileInfo.Name;
4756 }
4757 }
4758 catch (Exception)
4759 {
4760 // Ignore.
4761 }
4762 }
4763
4764 foreach (System.IO.FileInfo FileInfo in Files)
4765 {
4766 Markdown.Append("| ");
4767 Markdown.Append(this.DownloadableFile(FileInfo.Name, State, To));
4768 Markdown.Append(" | ");
4769
4770 if (Sizes.TryGetValue(FileInfo.Name, out long Size))
4771 {
4772 if (IsMax = (FileInfo.Name == MaxSizeFileName))
4773 Markdown.Append("**");
4774
4775 Markdown.Append(Export.FormatBytes(Size));
4776
4777 if (IsMax)
4778 Markdown.Append("**");
4779 }
4780 else
4781 Markdown.Append("N/A");
4782
4783 Markdown.Append(" | ");
4784
4785 if (DateTimes.TryGetValue(FileInfo.Name, out TP))
4786 {
4787 if (IsMax = (FileInfo.Name == MaxDateTimeFileName))
4788 Markdown.Append("**");
4789
4790 Markdown.Append(TP.ToShortDateString());
4791
4792 if (IsMax)
4793 Markdown.Append("**");
4794
4795 Markdown.Append(" | ");
4796
4797 if (IsMax)
4798 Markdown.Append("**");
4799
4800 Markdown.Append(TP.ToLongTimeString());
4801
4802 if (IsMax)
4803 Markdown.Append("**");
4804
4805 Markdown.AppendLine(" |");
4806 }
4807 else
4808 Markdown.AppendLine("N/A ||");
4809 }
4810
4811 await ResponseCallback(Markdown.ToString(), string.Empty);
4812 return true;
4813 }
4814 catch (Exception)
4815 {
4816 // Run as normal command.
4817 }
4818 break;
4819
4820 default:
4821 if (s.StartsWith("cd.") || s.StartsWith("cd" + Path.DirectorySeparatorChar))
4822 return ChangeDirectory(State, Command[2..].Trim(), ResponseCallback);
4823
4824 if (File.Exists(s))
4825 {
4826 await SendErrorMessage("Executing files not allowed.", string.Empty, ResponseCallback);
4827 return false;
4828 }
4829
4830 // TODO: Secure with provisioning on a command level.
4831 break;
4832 }
4833
4834 ProcessStartInfo StartInfo;
4835
4836 switch (Environment.OSVersion.Platform)
4837 {
4838 case PlatformID.Win32S:
4839 case PlatformID.Win32Windows:
4840 case PlatformID.Win32NT:
4841 case PlatformID.WinCE:
4842 StartInfo = new ProcessStartInfo()
4843 {
4844 FileName = "cmd.exe",
4845 Arguments = "/S /C \" " + Command.Trim() + " \"",
4846 WorkingDirectory = State.Folder
4847 };
4848 break;
4849
4850 case PlatformID.Unix:
4851 case PlatformID.MacOSX:
4852 StartInfo = new ProcessStartInfo()
4853 {
4854 FileName = "/bin/zsh",
4855 Arguments = "-c \" " + Command.Trim() + " \"",
4856 WorkingDirectory = State.Folder
4857 };
4858 break;
4859
4860 default:
4861 await SendErrorMessage("Not supported on this operating system.", string.Empty, ResponseCallback);
4862 return false;
4863 }
4864
4865 if (Shell)
4866 {
4867 StartInfo.UseShellExecute = true;
4868 StartInfo.WindowStyle = ProcessWindowStyle.Normal;
4869 }
4870 else
4871 {
4872 StartInfo.CreateNoWindow = true;
4873 StartInfo.ErrorDialog = false;
4874 StartInfo.RedirectStandardInput = false;
4875 StartInfo.RedirectStandardError = true;
4876 StartInfo.RedirectStandardOutput = true;
4877 StartInfo.UseShellExecute = false;
4878 StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
4879 }
4880
4881 Log.Notice("Starting process.",
4882 new KeyValuePair<string, object>("FileName", StartInfo.FileName),
4883 new KeyValuePair<string, object>("Arguments", StartInfo.Arguments),
4884 new KeyValuePair<string, object>("WorkingDirectory", StartInfo.WorkingDirectory),
4885 new KeyValuePair<string, object>("UseShellExecute", StartInfo.UseShellExecute),
4886 new KeyValuePair<string, object>("WindowStyle", StartInfo.WindowStyle),
4887 new KeyValuePair<string, object>("CreateNoWindow", StartInfo.CreateNoWindow),
4888 new KeyValuePair<string, object>("ErrorDialog", StartInfo.ErrorDialog),
4889 new KeyValuePair<string, object>("RedirectStandardInput", StartInfo.RedirectStandardInput),
4890 new KeyValuePair<string, object>("RedirectStandardError", StartInfo.RedirectStandardError),
4891 new KeyValuePair<string, object>("RedirectStandardOutput", StartInfo.RedirectStandardOutput));
4892
4893 Process P = new Process()
4894 {
4895 StartInfo = StartInfo
4896 };
4897
4898 if (Shell)
4899 {
4900 P.Start();
4901 return true;
4902 }
4903
4904 Thread T = new Thread((e) =>
4905 {
4906 try
4907 {
4908 try
4909 {
4910 LinkedListNode<Process> ProcessNode = State.Processes.AddLast(P);
4911
4912 P.Start();
4913
4914 do
4915 {
4916 bool Output = false;
4917
4918 s = P.StandardOutput.ReadToEnd().Trim();
4919
4920 if (!string.IsNullOrEmpty(s))
4921 {
4922 StringBuilder Markdown = new StringBuilder();
4923
4924 Markdown.AppendLine("```");
4925 Markdown.AppendLine(s.Trim());
4926 Markdown.AppendLine("```");
4927
4928 ResponseCallback(Markdown.ToString(), string.Empty);
4929 Output = true;
4930 }
4931
4932 s = P.StandardError.ReadToEnd().Trim();
4933 if (!string.IsNullOrEmpty(s))
4934 {
4935 StringBuilder Markdown = new StringBuilder();
4936
4937 Markdown.AppendLine("<font class=\"error\">");
4938 Markdown.AppendLine();
4939 Markdown.AppendLine("```");
4940 Markdown.AppendLine(s.Trim());
4941 Markdown.AppendLine("```");
4942 Markdown.AppendLine();
4943 Markdown.AppendLine("</font>");
4944
4945 ResponseCallback(Markdown.ToString(), string.Empty);
4946 Output = true;
4947 }
4948
4949 if (!Output)
4950 ResponseCallback("The command executed with no response.", string.Empty);
4951 }
4952 while (!P.HasExited);
4953
4954 State.Processes.Remove(ProcessNode);
4955 }
4956 finally
4957 {
4958 P.Dispose();
4959 }
4960 }
4961 catch (Exception ex)
4962 {
4963 Log.Exception(ex);
4964 }
4965 })
4966 {
4967 IsBackground = true,
4968 Name = "CMD Thread",
4969 Priority = ThreadPriority.BelowNormal
4970 };
4971
4972 T.Start();
4973
4974 return true;
4975 }
4976
4977 internal string DownloadableFile(string s, ChatState State, string To)
4978 {
4979 if (!File.Exists(s))
4980 return MarkdownDocument.Encode(s);
4981
4984
4985 using (FileStream fs = File.Open(s, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
4986 {
4987 State.Files[Token] = FileInfo = new Admin.FileInfo()
4988 {
4989 Path = s,
4990 FileName = Path.GetFileName(s),
4991 ContentType = InternetContent.GetContentType(Path.GetExtension(s)),
4992 ETag = this.packages.ComputeETag(fs)
4993 };
4994 }
4995
4996 string Url = Gateway.GetUrl("/ChatFile/" + HttpUtility.UrlEncode(FileInfo.FileName) +
4997 "?jid=" + HttpUtility.UrlEncode(To) + "&t=" + Token);
4998
4999 return "<a href=\"" + XML.HtmlAttributeEncode(Url) + "\">" + MarkdownDocument.Encode(FileInfo.FileName) + "</a>";
5000 }
5001
5002 private static bool ChangeDirectory(ChatState State, string NewFolder, ResponseCallbackHandler ResponseCallback)
5003 {
5004 string s = Path.GetFullPath(Path.Combine(State.Folder, NewFolder)) + Path.DirectorySeparatorChar;
5005 int AppDataLen = Gateway.AppDataFolder.Length;
5006
5007 if (s.Length < AppDataLen || string.Compare(Gateway.AppDataFolder, s[..AppDataLen], true) != 0)
5008 {
5009 SendErrorMessage("Access to folders outside of the application data folder not permitted.", string.Empty, ResponseCallback);
5010 return false;
5011 }
5012 else if (!Directory.Exists(s))
5013 {
5014 SendErrorMessage("Folder does not exist", string.Empty, ResponseCallback);
5015 return false;
5016 }
5017 else
5018 {
5019 State.Folder = s;
5020 ResponseCallback("New folder is `" + s + "`", string.Empty);
5021 return true;
5022 }
5023 }
5024
5025 private async Task ChatFileDownload(HttpRequest Request, HttpResponse Response)
5026 {
5027 if (!Request.Header.TryGetQueryParameter("jid", out string Jid) ||
5028 !Request.Header.TryGetQueryParameter("t", out string Token))
5029 {
5030 throw new BadRequestException("Bad request.");
5031 }
5032
5033 if (this.chatSessions is null || !this.chatSessions.TryGetValue(HttpUtility.UrlDecode(Jid), out Variables Session))
5034 throw new NotFoundException("Session has expired.");
5035
5036 if (!Session.TryGetVariable(" State ", out Variable v) ||
5037 !(v.ValueObject is ChatState State) ||
5038 !State.Files.TryGetValue(Token, out Admin.FileInfo Rec))
5039 {
5040 throw new NotFoundException("Token not recognized.");
5041 }
5042
5043 if (Rec.FileName != HttpUtility.UrlDecode(Request.SubPath[1..]))
5044 throw new BadRequestException("Bad request.");
5045
5046 if (string.Compare(Path.GetExtension(Rec.FileName), ".xml", true) == 0)
5047 {
5048 XmlReaderSettings ReaderSettings = new XmlReaderSettings()
5049 {
5050 Async = true,
5051 CheckCharacters = false,
5052 ConformanceLevel = ConformanceLevel.Document,
5053 DtdProcessing = DtdProcessing.Ignore,
5054 IgnoreComments = false,
5055 IgnoreProcessingInstructions = false,
5056 IgnoreWhitespace = true,
5057 ValidationFlags = System.Xml.Schema.XmlSchemaValidationFlags.None
5058 };
5059 XmlWriterSettings WriterSettings = new XmlWriterSettings()
5060 {
5061 Async = true,
5062 CheckCharacters = false,
5063 CloseOutput = false,
5064 ConformanceLevel = ConformanceLevel.Document,
5065 Encoding = Encoding.UTF8,
5066 Indent = true,
5067 IndentChars = "\t",
5068 NamespaceHandling = NamespaceHandling.OmitDuplicates,
5069 NewLineChars = "\r\n",
5070 NewLineHandling = NewLineHandling.Replace,
5071 NewLineOnAttributes = false,
5072 OmitXmlDeclaration = false,
5073 WriteEndDocumentOnClose = true
5074 };
5075
5076 using FileStream fs = File.Open(Rec.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
5077 using XmlReader r = XmlReader.Create(fs, ReaderSettings);
5078 TemporaryFile TempFile = new TemporaryFile();
5079 try
5080 {
5081 using XmlWriter w = XmlWriter.Create(TempFile, WriterSettings);
5082
5083 while (await r.ReadAsync())
5084 {
5085 switch (r.NodeType)
5086 {
5087 case XmlNodeType.XmlDeclaration:
5088 // Managed by XmlWriter
5089 break;
5090
5091 case XmlNodeType.ProcessingInstruction:
5092 string Url = r.Value;
5093 string s;
5094 int i, j;
5095
5096 if ((i = Url.IndexOf(s = Gateway.RootFolder, StringComparison.CurrentCultureIgnoreCase)) >= 0 ||
5097 (i = Url.IndexOf(s = Gateway.AppDataFolder, StringComparison.CurrentCultureIgnoreCase)) >= 0)
5098 {
5099 Url = Url.Remove(i, s.Length - 1);
5100
5101 j = Url.IndexOf('"', i);
5102 if (j >= 0)
5103 Url = Url.Remove(i, j - i).Insert(i, Url[i..j].Replace(Path.DirectorySeparatorChar, '/'));
5104 }
5105
5106 await w.WriteProcessingInstructionAsync(r.Name, Url);
5107 break;
5108
5109 case XmlNodeType.Attribute:
5110 await w.WriteAttributeStringAsync(r.Prefix, r.LocalName, r.NamespaceURI, r.Value);
5111 break;
5112
5113 case XmlNodeType.CDATA:
5114 await w.WriteCDataAsync(r.Value);
5115 break;
5116
5117 case XmlNodeType.Comment:
5118 await w.WriteCommentAsync(r.Value);
5119 break;
5120
5121 case XmlNodeType.EntityReference:
5122 await w.WriteEntityRefAsync(r.Name);
5123 break;
5124
5125 case XmlNodeType.Text:
5126 case XmlNodeType.SignificantWhitespace:
5127 await w.WriteStringAsync(r.Value);
5128 break;
5129
5130 case XmlNodeType.Whitespace:
5131 // Ignore
5132 break;
5133
5134 case XmlNodeType.Element:
5135 await w.WriteStartElementAsync(r.Prefix, r.LocalName, r.NamespaceURI);
5136
5137 bool IsEmpty = r.IsEmptyElement;
5138
5139 if (r.HasAttributes && r.MoveToFirstAttribute())
5140 {
5141 await w.WriteAttributeStringAsync(r.Prefix, r.LocalName, r.NamespaceURI, r.Value);
5142
5143 while (r.MoveToNextAttribute())
5144 await w.WriteAttributeStringAsync(r.Prefix, r.LocalName, r.NamespaceURI, r.Value);
5145 }
5146
5147 if (IsEmpty)
5148 w.WriteEndElement();
5149 break;
5150
5151 case XmlNodeType.EndElement:
5152 await w.WriteEndElementAsync();
5153 break;
5154
5155 case XmlNodeType.Document:
5156 case XmlNodeType.DocumentFragment:
5157 case XmlNodeType.DocumentType:
5158 case XmlNodeType.EndEntity:
5159 case XmlNodeType.Entity:
5160 case XmlNodeType.Notation:
5161 break;
5162 }
5163 }
5164 }
5165 catch (XmlException)
5166 {
5167 // Ignore
5168 }
5169 catch (Exception ex)
5170 {
5171 TempFile.Dispose();
5172 ExceptionDispatchInfo.Capture(ex).Throw();
5173 }
5174
5175 Response.OnResponseSent += (Sender, e) =>
5176 {
5177 TempFile.Dispose();
5178 return Task.CompletedTask;
5179 };
5180
5181 DateTime LastWriteTime = File.GetLastWriteTimeUtc(Rec.Path);
5182 await HttpFolderResource.SendResponse(TempFile.FileName, Rec.ContentType,
5183 Rec.ETag, LastWriteTime, false, Response);
5184 return;
5185 }
5186
5187 await HttpFolderResource.SendResponse(Rec.Path, Rec.ContentType, Rec.ETag,
5188 File.GetLastWriteTimeUtc(Rec.Path), false, Response);
5189 }
5190
5191 private Task ChatSessions_Removed(object Sender, CacheItemEventArgs<string, Variables> e)
5192 {
5193 if (e.Value.TryGetVariable(" State ", out Variable v) &&
5194 v.ValueObject is ChatState State)
5195 {
5196 foreach (Process P in State.Processes)
5197 {
5198 try
5199 {
5200 P.Kill();
5201 }
5202 catch (Exception ex)
5203 {
5204 Log.Exception(ex);
5205 }
5206 }
5207
5208 State.Processes.Clear();
5209 }
5210
5211 return Task.CompletedTask;
5212 }
5213
5220 public static bool VerifyRecaptcha(object Posted, string RemoteEndPoint)
5221 {
5222 if (!(Posted is Dictionary<string, string> Form))
5223 return false;
5224
5225 if (!Form.TryGetValue("g-recaptcha-response", out string RecaptchaResponse))
5226 return false;
5227
5228 return Feedback.SiteVerify(RecaptchaResponse, RemoteEndPoint).Result;
5229 }
5230
5237 public static Task<bool> SendMailMessage(string To, string Subject, string Markdown)
5238 {
5239 return SendMailMessage(To, Subject, Markdown, Array.Empty<EmbeddedContent>());
5240 }
5241
5249 public static Task<bool> SendMailMessage(string To, string Subject, string Markdown, params EmbeddedContent[] Attachments)
5250 {
5251 string From = null;
5252
5253 if (RelayConfiguration.Instance.UseRelayServer)
5254 From = RelayConfiguration.Instance.Sender;
5255
5256 if (string.IsNullOrEmpty(From))
5258
5259 return SendMailMessage(From, To, Subject, Markdown, Attachments);
5260 }
5261
5269 public static Task<bool> SendMailMessage(string From, string To, string Subject, string Markdown)
5270 {
5271 return SendMailMessage(From, To, Subject, Markdown, Array.Empty<EmbeddedContent>());
5272 }
5273
5282 public static Task<bool> SendMailMessage(string From, string To, string Subject, string Markdown, params EmbeddedContent[] Attachments)
5283 {
5284 if (RelayConfiguration.Instance.UseRelayServer)
5285 {
5286 if (string.IsNullOrEmpty(From))
5287 From = RelayConfiguration.Instance.Sender;
5288
5289 return SendMailMessage(
5294 From,
5295 To,
5296 Subject,
5297 Markdown,
5298 Attachments);
5299 }
5300 else
5301 return instance?.xmppServer?.SendMailMessage(From, To, Subject, Markdown) ?? Task.FromResult(false);
5302 }
5303
5316 public static Task<bool> SendMailMessage(string SmtpHost, int SmtpPort, string UserName, string Password,
5317 string From, string To, string Subject, string Markdown)
5318 {
5319 return SendMailMessage(SmtpHost, SmtpPort, UserName, Password, From, To, Subject, Markdown, Array.Empty<EmbeddedContent>());
5320 }
5321
5335 public static async Task<bool> SendMailMessage(string SmtpHost, int SmtpPort, string UserName, string Password,
5336 string From, string To, string Subject, string Markdown, params EmbeddedContent[] Attachments)
5337 {
5338 string Styles = string.Empty;
5339
5340 if (!(Attachments is null))
5341 {
5342 List<EmbeddedContent> Attachments2 = null;
5343 bool Changed = false;
5344
5345 foreach (EmbeddedContent Attachment in Attachments)
5346 {
5347 if (!(Attachment.Decoded is null) &&
5349 {
5350 Styles += CssDocument.Css;
5351 Changed = true;
5352 continue;
5353 }
5354 else if (Attachment.Decoded is null &&
5355 !(Attachment.Raw is null) &&
5357 {
5359
5360 if (!Decoded.HasError && Decoded.Decoded is CssDocument CssDocument2)
5361 {
5362 Styles += CssDocument2.Css;
5363 Changed = true;
5364 continue;
5365 }
5366 }
5367
5368 Attachments2 ??= new List<EmbeddedContent>();
5369 Attachments2.Add(Attachment);
5370 }
5371
5372 if (Changed)
5373 Attachments = Attachments2?.ToArray();
5374 }
5375
5376 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(Markdown);
5377 StringBuilder sb = new StringBuilder();
5378
5379 sb.AppendLine("<html>");
5380 sb.AppendLine("<head>");
5381
5382 if (!string.IsNullOrEmpty(Styles))
5383 {
5384 sb.AppendLine("<style>");
5385 sb.AppendLine(Styles);
5386 sb.AppendLine("</style>");
5387 }
5388
5389 sb.AppendLine("</head>");
5390 sb.AppendLine("<body>");
5391 sb.AppendLine(HtmlDocument.GetBody(await Doc.GenerateHTML()));
5392 sb.AppendLine("</body>");
5393 sb.AppendLine("</html>");
5394
5395 string HTML = sb.ToString();
5396 string PlainText = await Doc.GeneratePlainText();
5397 EmbeddedContent[] Alternatives = new EmbeddedContent[]
5398 {
5399 new EmbeddedContent()
5400 {
5401 ContentType = "text/html; charset=utf-8",
5402 Raw = Encoding.UTF8.GetBytes(HTML)
5403 },
5404 new EmbeddedContent()
5405 {
5406 ContentType = "text/plain; charset=utf-8",
5407 Raw = Encoding.UTF8.GetBytes(PlainText)
5408 },
5409 new EmbeddedContent()
5410 {
5411 ContentType = "text/markdown; charset=utf-8",
5412 Raw = Encoding.UTF8.GetBytes(Markdown)
5413 }
5414 };
5415
5416 ContentResponse P = await InternetContent.EncodeAsync(new ContentAlternatives(Alternatives), Encoding.UTF8);
5417 P.AssertOk();
5418
5419 byte[] BodyBin = P.Encoded;
5420 string ContentType = P.ContentType;
5421
5422 if (!(Attachments is null) && Attachments.Length > 0)
5423 {
5424 EmbeddedContent[] Mixed = new EmbeddedContent[Attachments.Length + 1];
5425 Mixed[0] = new EmbeddedContent()
5426 {
5427 ContentType = ContentType,
5428 Raw = BodyBin
5429 };
5430 Array.Copy(Attachments, 0, Mixed, 1, Attachments.Length);
5431
5432 P = await InternetContent.EncodeAsync(new MixedContent(Mixed), Encoding.UTF8);
5433 P.AssertOk();
5434
5435 BodyBin = P.Encoded;
5436 ContentType = P.ContentType;
5437 }
5438
5439 KeyValuePair<string, string>[] Headers = new KeyValuePair<string, string>[]
5440 {
5441 new KeyValuePair<string, string>("MIME-VERSION", "1.0"),
5442 new KeyValuePair<string, string>("FROM", From),
5443 new KeyValuePair<string, string>("TO", To),
5444 new KeyValuePair<string, string>("SUBJECT", Subject),
5445 new KeyValuePair<string, string>("DATE", CommonTypes.EncodeRfc822(DateTime.Now)),
5446 new KeyValuePair<string, string>("IMPORTANCE", "normal"),
5447 new KeyValuePair<string, string>("X-PRIORITY", "3"),
5448 new KeyValuePair<string, string>("MESSAGE-ID", Guid.NewGuid().ToString()),
5449 new KeyValuePair<string, string>("CONTENT-TYPE", ContentType)
5450 };
5451
5452 int i = To.IndexOf('@');
5453 if (i < 0)
5454 throw new ArgumentException("Invalid mail address: " + To, nameof(To));
5455
5456 string Domain = To[(i + 1)..].Trim();
5457
5458 return await (instance?.smtpServer?.SendMessage(Domain, SmtpHost, SmtpPort, UserName, Password, From, To, Headers, BodyBin, DateTime.Now) ?? Task.FromResult(false));
5459 }
5460
5461 private static readonly PerformanceAlertMonitor cpuMonitor = new PerformanceAlertMonitor(75, 10 * 60, 90, 30 * 60,
5462 "CPU usage is above 75% for 10 minutes.",
5463 "CPU usage is below 75% for 10 minutes.",
5464 "CPU usage is above 90% for 30 minutes.",
5465 "CPU usage is below 90% for 30 minutes.");
5466 private static readonly PerformanceAlertMonitor freeMemoryMonitor = new PerformanceAlertMonitor(1000, 30, 200, 30,
5467 "Free Memory available is below 1000 MB for 30 seconds.",
5468 "Free Memory available is above 1000 MB for 30 seconds.",
5469 "Free Memory available is below 200 MB for 30 seconds.",
5470 "Free Memory available is above 200 MB for 30 seconds.");
5471 private static readonly PerformanceAlertMonitor freeHDMonitor = new PerformanceAlertMonitor(10, 30, 2, 30,
5472 "Free Hard Disk available is below 10 GB for 30 seconds.",
5473 "Free Hard Disk available is above 10 GB for 30 seconds.",
5474 "Free Hard Disk available is below 2 GB for 30 seconds.",
5475 "Free Hard Disk available is above 2 GB for 30 seconds.");
5476 private static readonly PerformanceAlertMonitor allocatedMemoryMonitor = new PerformanceAlertMonitor(1500, 30, 1900, 30,
5477 "Allocated Memory is above 1500 MB for 30 seconds.",
5478 "Allocated Memory is below 1500 MB for 30 seconds.",
5479 "Allocated Memory is above 1900 MB for 30 seconds.",
5480 "Allocated Memory is below 1900 MB for 30 seconds.");
5481 private MethodInfo getCounter2 = null;
5482 private MethodInfo getCounter3 = null;
5483 private MethodInfo getCpuValue = null;
5484 private MethodInfo getFreeMemoryValue = null;
5485 private object cpuCounter = null;
5486 private object freeMemoryCounter = null;
5487
5488 private async void SampleTimerEventHandler(object Sender)
5489 {
5490 try
5491 {
5492 if (this.getCounter2 is null)
5493 {
5494 Type T = Types.GetType("Waher.IoTGateway.Svc.PerformanceCounters");
5495 this.getCounter2 = T?.GetMethod("GetCounter", new Type[] { typeof(string), typeof(string) });
5496 this.getCounter3 = T?.GetMethod("GetCounter", new Type[] { typeof(string), typeof(string), typeof(string) });
5497
5498 this.cpuCounter = this.getCounter3?.Invoke(null, new object[] { "Processor", "_Total", "% Processor Time" });
5499 this.getCpuValue = this.cpuCounter?.GetType()?.GetMethod("NextValue", Array.Empty<Type>());
5500
5501 this.freeMemoryCounter = this.getCounter2?.Invoke(null, new object[] { "Memory", "Available MBytes" });
5502 this.freeMemoryCounter ??= this.getCounter2?.Invoke(null, new object[] { "Minne", "Tillgängliga megabyte" });
5503
5504 this.getFreeMemoryValue = this.freeMemoryCounter?.GetType()?.GetMethod("NextValue", Array.Empty<Type>());
5505 }
5506
5507 if (this.getCpuValue?.Invoke(this.cpuCounter, Types.NoParameters) is float CPU)
5508 {
5509 await this.performanceStatistics.Sample("CPU", CPU);
5510 cpuMonitor.Sample(CPU);
5511 }
5512
5513 if (this.getFreeMemoryValue?.Invoke(this.freeMemoryCounter, Types.NoParameters) is float FreeMemory)
5514 {
5515 await this.performanceStatistics.Sample("FreeMemory", FreeMemory);
5516 freeMemoryMonitor.Sample(FreeMemory);
5517 }
5518
5519 double AllocatedMemory = GC.GetTotalMemory(true) / 1048576.0;
5520 await this.performanceStatistics.Sample("AllocMemory", AllocatedMemory);
5521 allocatedMemoryMonitor.Sample(AllocatedMemory);
5522
5523 if (!string.IsNullOrEmpty(appDataDrive))
5524 {
5525 DriveInfo Drive = new DriveInfo(appDataDrive);
5526 double FreeHD = Drive.AvailableFreeSpace / 1073741824.0;
5527 await this.performanceStatistics.Sample("FreeHD", FreeHD);
5528 freeHDMonitor.Sample(FreeHD);
5529 }
5530 }
5531 catch (Exception ex)
5532 {
5533 Log.Exception(ex);
5534 }
5535 }
5536
5540 public Bucket CurrentCpuBucket
5541 {
5542 get
5543 {
5544 if (this.performanceStatistics.TryGetBucket("CPU", out Bucket Bucket))
5545 return Bucket;
5546 else
5547 return null;
5548 }
5549 }
5550
5554 public Bucket CurrentFreeMemoryBucket
5555 {
5556 get
5557 {
5558 if (this.performanceStatistics.TryGetBucket("FreeMemory", out Bucket Bucket))
5559 return Bucket;
5560 else
5561 return null;
5562 }
5563 }
5564
5568 public Bucket CurrentFreeHDBucket
5569 {
5570 get
5571 {
5572 if (this.performanceStatistics.TryGetBucket("FreeHD", out Bucket Bucket))
5573 return Bucket;
5574 else
5575 return null;
5576 }
5577 }
5578
5582 public Bucket CurrentAllocatedMemoryBucket
5583 {
5584 get
5585 {
5586 if (this.performanceStatistics.TryGetBucket("AllocMemory", out Bucket Bucket))
5587 return Bucket;
5588 else
5589 return null;
5590 }
5591 }
5592
5593 private async Task Export_OnExportKeyFolderUpdated(object Sender, EventArgs e)
5594 {
5595 try
5596 {
5597 this.httpFileUploadSettings.BackupFolder = await Export.GetFullExportFolderAsync();
5598 }
5599 catch (Exception ex)
5600 {
5601 Log.Exception(ex);
5602 }
5603 }
5604
5605 private async Task Export_OnExportFolderUpdated(object Sender, EventArgs e)
5606 {
5607 try
5608 {
5609 this.httpFileUploadSettings.KeyFolder = await Export.GetFullKeyExportFolderAsync();
5610 }
5611 catch (Exception ex)
5612 {
5613 Log.Exception(ex);
5614 }
5615 }
5616
5617 private async Task JwtRequest(object Sender, IqEventArgs e)
5618 {
5619 try
5620 {
5621 int Seconds = XML.Attribute(e.Query, "seconds", 0);
5622 if (Seconds <= 0)
5623 {
5624 await e.IqErrorBadRequest(e.To, "Number of seconds token is to be valid must be a positive integer.", "en");
5625 return;
5626 }
5627
5628 if (Seconds > 3600)
5629 Seconds = 3600;
5630
5631 if (!this.xmppServer.IsServerDomain(e.From.Domain, true))
5632 {
5633 await e.IqErrorForbidden(e.To, "Account must be on broker.", "en");
5634 return;
5635 }
5636
5637 int IssuedAt = (int)Math.Round(DateTime.UtcNow.Subtract(JSON.UnixEpoch).TotalSeconds);
5638 int Expires = IssuedAt + Seconds;
5639
5640 string Token = Gateway.JwtFactory.Create(
5641 new KeyValuePair<string, object>(JwtClaims.JwtId, System.Convert.ToBase64String(Gateway.NextBytes(32))),
5642 new KeyValuePair<string, object>(JwtClaims.Issuer, Gateway.Domain.Value),
5643 new KeyValuePair<string, object>(JwtClaims.Subject, e.From.Address.Value),
5644 new KeyValuePair<string, object>(JwtClaims.IssueTime, IssuedAt),
5645 new KeyValuePair<string, object>(JwtClaims.ExpirationTime, Expires));
5646
5647 StringBuilder Xml = new StringBuilder();
5648
5649 Xml.Append("<token xmlns='");
5650 Xml.Append(NamespaceJwt);
5651 Xml.Append("'>");
5652 Xml.Append(XML.Encode(Token));
5653 Xml.Append("</token>");
5654
5655 await e.IqResult(Xml.ToString(), e.To);
5656
5657 LoginAuditor.Success("Successful generation of JWT token.", e.From.BareJid, e.From.Address, "HTTP");
5658 }
5659 catch (Exception ex)
5660 {
5661 await e.IqError(ex, e.To);
5662 }
5663 }
5664
5670 internal string CreateJwtToken(params KeyValuePair<string, object>[] Claims)
5671 {
5672 return this.CreateJwtToken((IEnumerable<KeyValuePair<string, object>>)Claims);
5673 }
5674
5680 internal string CreateJwtToken(IEnumerable<KeyValuePair<string, object>> Claims)
5681 {
5682 return Gateway.JwtFactory.Create(Claims);
5683 }
5684
5691 internal bool ValidateJwtToken(string Token)
5692 {
5693 return this.ValidateJwtToken(Token, false);
5694 }
5695
5703 internal bool ValidateJwtToken(string Token, bool CheckIssuer)
5704 {
5705 return this.ValidateJwtToken(Token, CheckIssuer, out _);
5706 }
5707
5716 internal bool ValidateJwtToken(string Token, bool CheckIssuer, out JwtToken ParsedToken)
5717 {
5718 if (!JwtToken.TryParse(Token, out ParsedToken))
5719 return false;
5720
5721 return this.ValidateJwtToken(ParsedToken, CheckIssuer);
5722 }
5723
5730 internal bool ValidateJwtToken(JwtToken Token)
5731 {
5732 return this.ValidateJwtToken(Token, false);
5733 }
5734
5742 internal bool ValidateJwtToken(JwtToken Token, bool CheckIssuer)
5743 {
5744 if (CheckIssuer && !this.xmppServer.IsServerDomain(Token.Issuer, true))
5745 return false;
5746
5748 }
5749
5750 internal static Task<IClientConnection> JwtAuthenticate(HttpRequest Request)
5751 {
5752 return JwtAuthenticate(Request, Gateway.JwtFactory, instance.xmppServer);
5753 }
5754
5755 public static async Task<IClientConnection> JwtAuthenticate(HttpRequest Request,
5756 JwtFactory Factory, XmppServer Server)
5757 {
5758 string TokenStr = JwtAuthentication.GetAccessToken(Request);
5759 if (string.IsNullOrEmpty(TokenStr))
5760 {
5761 if (Request.Response?.TransferEncoding is HttpxResponse HttpxResponse)
5762 {
5763 IRecipient Recipient = await HttpxResponse.Server.TryGetRecipient(HttpxResponse.To, HttpxResponse.From);
5764 if (Recipient is IClientConnection ClientConnection)
5765 return ClientConnection;
5766 }
5767
5768 throw new UnauthorizedException("Unauthorized access prohibited.", new string[] { "Bearer realm=\"" + Gateway.Domain?.Value + "\"" });
5769 }
5770
5771 if (!JwtToken.TryParse(TokenStr, out JwtToken Token, out string ErrorReason))
5772 throw new UnauthorizedException(ErrorReason, new string[] { "Bearer realm=\"" + Gateway.Domain?.Value + "\"" });
5773
5774 if (!Factory.IsValid(Token, out Reason Reason))
5775 {
5776 LoginAuditor.Fail("Invalid JWT token.", Token.Subject ?? string.Empty, Request.RemoteEndPoint, "HTTP",
5777 new KeyValuePair<string, object>("Reason", Reason));
5778
5779 throw new ForbiddenException(Request, "Invalid JWT token.");
5780 }
5781
5782 if (!Server.TryGetClientConnection(Token.Subject, out IClientConnection Connection))
5783 {
5784 LoginAuditor.Fail("JWT token obsoleted, due to XMPP disconnect.", Token.Subject ?? string.Empty, Request.RemoteEndPoint, "HTTP");
5785
5786 throw new ForbiddenException(Request, "JWT token obsoleted, due to XMPP disconnect.");
5787 }
5788
5789 return Connection;
5790 }
5791
5792 private Task ProxyResource_AddSsoInformationEncrypted(object Sender, ProxyRequestEventArgs e)
5793 {
5794 return this.ProxyResource_AddSsoInformation(Sender, e, true);
5795 }
5796
5797 private Task ProxyResource_AddSsoInformationUnencrypted(object Sender, ProxyRequestEventArgs e)
5798 {
5799 return this.ProxyResource_AddSsoInformation(Sender, e, false);
5800 }
5801
5802 private Task ProxyResource_AddSsoInformation(object _, ProxyRequestEventArgs e, bool Encrypted)
5803 {
5804 // For a reference of JWT claims, see:
5805 // https://www.iana.org/assignments/jwt/jwt.xhtml#claims
5806
5808 if (Variables is null || e.Message.Headers.Contains("Authorization"))
5809 return Task.CompletedTask;
5810
5811 if (Variables.TryGetVariable("User", out Variable v) && v.ValueObject is IUserWithClaims User)
5812 return this.AddSsoInformation(User, e, Encrypted);
5813 else if (Variables.TryGetVariable(QuickLogin.UserVariableName, out v) && v.ValueObject is IUserWithClaims User2)
5814 return this.AddSsoInformation(User2, e, Encrypted);
5815 else
5816 return Task.CompletedTask;
5817 }
5818
5819 private async Task AddSsoInformation(IUserWithClaims User, ProxyRequestEventArgs e, bool Encrypted)
5820 {
5821 string EncryptedPrefix = Encrypted ? "e" : "u";
5822
5823 if (!this.ssoTokens.TryGetValue(EncryptedPrefix + User.UserName, out string JwtToken))
5824 {
5825 JwtToken = await User.CreateToken(Gateway.JwtFactory, Encrypted);
5826 if (string.IsNullOrEmpty(JwtToken))
5827 return;
5828
5829 this.ssoTokens[EncryptedPrefix + User.UserName] = JwtToken;
5830 }
5831
5832 e.Message.Headers.Add("Authorization", "Bearer " + JwtToken);
5833 }
5834
5842 {
5843 return Gateway.JwtFactory.IsValid(Token, out Reason);
5844 }
5845
5846 private static async Task TransferIdDelivered(object Sender, Networking.XMPP.Events.MessageEventArgs e)
5847 {
5848 string OnboardingNeuron = await LegalComponent.GetOnboardingNeuronDomainName();
5849
5850 if (e.From != OnboardingNeuron)
5851 return;
5852
5853 string Code = XML.Attribute(e.Content, "code");
5854 bool Deleted = XML.Attribute(e.Content, "deleted", false);
5855
5856 if (!Deleted)
5857 return;
5858
5859 await WebServices.Agent.Account.Transfer.TransferCodeDelivered(Code);
5860 }
5861
5862 private async Task ContractsClient_GetLocalSchema(object Sender, Networking.XMPP.Contracts.EventArguments.SchemaReferenceEventArgs e)
5863 {
5864 ValidationSchema Schema;
5865
5866 if (e.SchemaDigest is null)
5867 {
5868 Schema = await Database.FindFirstIgnoreRest<ValidationSchema>(
5869 new FilterFieldEqualTo("Namespace", e.Namespace),
5870 "-Created");
5871 }
5872 else
5873 {
5874 Schema = await Database.FindFirstIgnoreRest<ValidationSchema>(
5875 new FilterAnd(
5876 new FilterFieldEqualTo("Namespace", e.Namespace),
5877 new FilterFieldEqualTo("HashBase64", System.Convert.ToBase64String(e.SchemaDigest.Digest))));
5878 }
5879
5880 e.XmlSchema = Schema?.XmlSchema;
5881 }
5882
5883 private async Task ContractsClient_ValidateLocalSignature(object Sender, Networking.XMPP.Contracts.EventArguments.ValidateSignatureEventArgs e)
5884 {
5885 XmppAddress LegalId = new XmppAddress(e.LegalId);
5886
5887 if (this.legalComponent.IsComponentDomain(LegalId.Domain, true))
5888 {
5889 Legal.Identity.LegalIdentity Identity = await LegalComponent.GetLocalLegalIdentity(e.LegalId);
5890 if (!(Identity is null))
5891 {
5892 e.Valid = Identity.ValidateSignature(e.Data, e.Signature);
5893
5894 if (e.Valid.Value)
5895 {
5896 StringBuilder Xml = new StringBuilder();
5897 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this.legalComponent);
5898
5899 e.Identity = LegalIdentity.Parse(Xml.ToString());
5900 }
5901 }
5902 }
5903 }
5904
5905 private Task ContractsClient_GetLocalPublicKey(object Sender, Networking.XMPP.Contracts.EventArguments.PublicKeyEventArgs e)
5906 {
5907 if (this.legalComponent.IsComponentDomain(e.Address, true))
5908 {
5909 Tuple<Networking.XMPP.IE2eEndpoint, DateTime?, DateTime?> P =
5910 LegalComponent.GetPublicKey(e.Timestamp);
5911
5912 if (!(P.Item1 is null) && P.Item2.HasValue)
5913 e.ReturnKey(P.Item1, P.Item2.Value, P.Item3);
5914 }
5915
5916 return Task.CompletedTask;
5917 }
5918
5919 #region Encrypted files
5920
5927 public static Task<byte[]> LoadEncryptedFile(string FileName, byte[] Salt)
5928 {
5929 return LoadEncryptedFile(FileName, Salt, null);
5930 }
5931
5939 public static async Task<byte[]> LoadEncryptedFile(string FileName, byte[] Salt, long? Size)
5940 {
5941 using FileStream File = System.IO.File.OpenRead(FileName);
5942 Aes Aes = Aes.Create();
5943
5944 Aes.BlockSize = 128;
5945 Aes.KeySize = 256;
5946 Aes.Mode = CipherMode.CBC;
5947 Aes.Padding = PaddingMode.Zeros;
5948
5949 byte[] Key = new byte[32];
5950 byte[] IV = new byte[16];
5951
5952 Buffer.BlockCopy(Salt, 0, Key, 0, 32);
5953 Buffer.BlockCopy(Salt, 32, IV, 0, 16);
5954
5955 using ICryptoTransform Decryptor = Aes.CreateDecryptor(Key, IV);
5956 using CryptoStream DecryptedFile = new CryptoStream(File, Decryptor, CryptoStreamMode.Read);
5957
5958 if (Size.HasValue)
5959 {
5960 long c = Size.Value;
5961 if (c > int.MaxValue)
5962 throw new IOException("File too large.");
5963
5964 int c0 = (int)c;
5965 byte[] Bin = await DecryptedFile.ReadAllAsync(c0);
5966
5967 return Bin;
5968 }
5969 else
5970 {
5971 using MemoryStream ms = new MemoryStream();
5972 await DecryptedFile.CopyToAsync(ms);
5973 return ms.ToArray();
5974 }
5975 }
5976
5983 public static Task SaveEncryptedFile(string FileName, byte[] Salt, byte[] Data)
5984 {
5985 using MemoryStream ms = new MemoryStream(Data);
5986 return SaveEncryptedFile(FileName, Salt, ms);
5987 }
5988
5995 public static async Task SaveEncryptedFile(string FileName, byte[] Salt, Stream Data)
5996 {
5997 using FileStream File = System.IO.File.Create(FileName);
5998 Aes Aes = Aes.Create();
5999
6000 Aes.BlockSize = 128;
6001 Aes.KeySize = 256;
6002 Aes.Mode = CipherMode.CBC;
6003 Aes.Padding = PaddingMode.Zeros;
6004
6005 byte[] Key = new byte[32];
6006 byte[] IV = new byte[16];
6007
6008 Buffer.BlockCopy(Salt, 0, Key, 0, 32);
6009 Buffer.BlockCopy(Salt, 32, IV, 0, 16);
6010
6011 using ICryptoTransform Encryptor = Aes.CreateEncryptor(Key, IV);
6012 using CryptoStream EncryptedFile = new CryptoStream(File, Encryptor, CryptoStreamMode.Write);
6013
6014 Data.Position = 0;
6015 await Data.CopyToAsync(EncryptedFile);
6016 EncryptedFile.FlushFinalBlock();
6017 }
6018
6025 public static async Task<Stream> EncodeBlob(Stream Data)
6026 {
6027 long Len = Data.Length;
6028
6029 TemporaryStream Blob = new TemporaryStream();
6030
6031 do
6032 {
6033 byte b = (byte)(Len & 127);
6034 Len >>= 7;
6035
6036 if (Len > 0)
6037 b |= 0x80;
6038
6039 Blob.WriteByte(b);
6040 }
6041 while (Len > 0);
6042
6043 Data.Position = 0;
6044
6045 await Data.CopyToAsync(Blob);
6046
6047 Blob.Position = 0;
6048
6049 return Blob;
6050 }
6051
6058 public static async Task<Stream> DecodeBlob(Stream Data)
6059 {
6060 int b;
6061 long Len = 0;
6062 int Offset = 0;
6063
6064 do
6065 {
6066 b = Data.ReadByte();
6067 Len |= ((long)b & 127) << Offset;
6068 Offset += 7;
6069 }
6070 while ((b & 0x80) != 0);
6071
6072 TemporaryStream Blob = new TemporaryStream();
6073 byte[] Buf = new byte[65536];
6074 int i, c;
6075
6076 while (Len > 0)
6077 {
6078 c = (int)Math.Min(65536, Len);
6079 i = await Data.ReadAsync(Buf, 0, c);
6080 if (i <= 0)
6081 {
6082 await Blob.DisposeAsync();
6083 return null;
6084 }
6085
6086 await Blob.WriteAsync(Buf, 0, i);
6087 Len -= i;
6088 }
6089
6090 Blob.Position = 0;
6091 return Blob;
6092 }
6093
6094 internal static async Task<byte[]> CreateSalt(string Key, byte[] AdditionalData)
6095 {
6096 using Runtime.Threading.Semaphore Lock = await Semaphores.BeginWrite("Salt:" + Key);
6097
6098 salts ??= await Database.GetDictionary("Salts"); // For unit tests.
6099
6100 int c = AdditionalData?.Length ?? 0;
6101 byte[] Salt = Gateway.NextBytes(48);
6102
6103 byte[] Data = new byte[48 + c];
6104
6105 Buffer.BlockCopy(Salt, 0, Data, 0, 48);
6106 if (c > 0)
6107 Buffer.BlockCopy(AdditionalData, 0, Data, 48, c);
6108
6109 if (await salts.ContainsKeyAsync(Key))
6110 await salts.RemoveAsync(Key);
6111
6112 await salts.AddAsync(Key, Data);
6113
6114 return Salt;
6115 }
6116
6117 internal static async Task<KeyValuePair<byte[], byte[]>> GetSaltWithAdditionalData(string Key)
6118 {
6119 salts ??= await Database.GetDictionary("Salts"); // For unit tests.
6120
6121 KeyValuePair<bool, object> P = await salts.TryGetValueAsync(Key);
6122 int c;
6123
6124 if (!P.Key || !(P.Value is byte[] Data) || (c = Data.Length - 48) < 0)
6125 return new KeyValuePair<byte[], byte[]>(null, null);
6126
6127 byte[] Salt = new byte[48];
6128 byte[] AdditionalData = new byte[c];
6129
6130 Buffer.BlockCopy(Data, 0, Salt, 0, 48);
6131 if (c > 0)
6132 Buffer.BlockCopy(Data, 48, AdditionalData, 0, c);
6133
6134 return new KeyValuePair<byte[], byte[]>(Salt, AdditionalData);
6135 }
6136
6137 internal static async Task<bool> RemoveSalt(string Key)
6138 {
6139 salts ??= await Database.GetDictionary("Salts"); // For unit tests.
6140
6141 return await salts.RemoveAsync(Key);
6142 }
6143
6144 #endregion
6145 }
6146}
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:15
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
Definition: CommonTypes.cs:596
static readonly char[] CRLF
Contains the CR LF character sequence.
Definition: CommonTypes.cs:19
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:48
static string EncodeRfc822(DateTime Timestamp)
Encodes a date and time, according to RFC 822 §5.
Definition: CommonTypes.cs:669
Contains information about a response to a content request.
byte[] Encoded
Encoded object.
string ContentType
Internet Content-Type of encoded object.
bool HasError
If an error occurred.
object Decoded
Decoded object.
Exception Error
Error response.
void AssertOk()
Asserts response is OK.
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:14
const string DefaultContentType
Content-Type for CSS files.
Definition: CssCodec.cs:25
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 string GetContentType(string FileExtension)
Gets the content type of an item, given its file extension. It uses the TryGetContentType to see if a...
static Task< ContentResponse > EncodeAsync(object Object, Encoding Encoding, params string[] AcceptedContentTypes)
Encodes an object.
static Task< ContentResponse > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
Helps with common JSON-related tasks.
Definition: JSON.cs:16
static readonly DateTime UnixEpoch
Unix Date and Time epoch, starting at 1970-01-01T00:00:00Z
Definition: JSON.cs:20
Consolidates Markdown from multiple sources, sharing the same thread.
Definition: Consolidator.cs:20
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.
Represents mixed content, encoded with multipart/mixed
Definition: MixedContent.cs:7
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:231
Plain text encoder/decoder.
const string DefaultContentType
text/plain
Helps with common XML-related tasks.
Definition: XML.cs:21
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:1062
static XmlDocument LoadFromFile(string FileName)
Loads an XML document from a file.
Definition: XML.cs:1808
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:29
static bool TryParse(string s, out DateTime Value)
Tries to decode a string encoded DateTime.
Definition: XML.cs:892
static string HtmlAttributeEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe.
Definition: XML.cs:121
Static class managing loading of XSL resources stored as embedded resources or in content files.
Definition: XSL.cs:16
static XmlSchema LoadSchema(string ResourceName)
Loads an XML schema from an embedded resource.
Definition: XSL.cs:24
static void Validate(string ObjectID, XmlDocument Xml, params XmlSchema[] Schemas)
Validates an XML document given a set of XML schemas.
Definition: XSL.cs:134
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static string CleanStackTrace(string StackTrace)
Cleans a Stack Trace string, removing entries from the asynchronous execution model,...
Definition: Log.cs:194
static void Register(IEventSink EventSink)
Registers an event sink with the event log. Call Unregister(IEventSink) to unregister it,...
Definition: Log.cs:30
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:1657
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:576
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:692
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:344
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:828
static bool Unregister(IEventSink EventSink)
Unregisters an event sink from the event log.
Definition: Log.cs:47
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:460
static void Alert(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an alert event.
Definition: Log.cs:1237
virtual Task DisposeAsync()
IDisposableAsync.DisposeAsync()
Definition: LogObject.cs:1144
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.
Converts CSSX-files to CSS, by evaluating emebedded script and replacing it with results.
Definition: CssxToCss.cs:21
static string ColorToCss(SKColor Color)
Converts a color to its CSS representation.
Definition: CssxToCss.cs:194
Static class managing data export.
Definition: Export.cs:18
static async Task< string > GetFullExportFolderAsync()
Full path to export folder.
Definition: Export.cs:22
static async Task< string > GetFullKeyExportFolderAsync()
Full path to key folder.
Definition: Export.cs:35
static string FormatBytes(double Bytes)
Formats a file size using appropriate unit.
Definition: Export.cs:125
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:147
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:3087
static HttpServer HttpServer
HTTP Server
Definition: Gateway.cs:4118
static string InstanceName
Name of the current instance. Default instance=string.Empty
Definition: Gateway.cs:3127
static OAuth2Environment OAuthEnvironment
OAUTH2 environment reference.
Definition: Gateway.cs:4113
static Task Terminate()
Raises the OnTerminate event handler, letting the container executable know the application needs to ...
Definition: Gateway.cs:3233
static bool IsDomain(string DomainOrHost, bool IncludeAlternativeDomains)
If a domain or host name represents the gateway.
Definition: Gateway.cs:5174
static AvatarClient AvatarClient
XMPP Concentrator Server.
Definition: Gateway.cs:4058
static X509Certificate2 Certificate
Domain certificate.
Definition: Gateway.cs:3082
static void DeleteOldFiles(string Path, long KeepDays)
Deletes old files in a folder.
Definition: Gateway.cs:4549
static Task< string > GetMultiFormatChatMessageXml(string Markdown)
Gets XML for a multi-formatted chat message.
Definition: Gateway.cs:4959
static async Task DoBackup()
Performs a backup of the system.
Definition: Gateway.cs:4504
static LoginAuditor LoginAuditor
Current Login Auditor. Should be used by modules accepting user logins, to protect the system from un...
Definition: Gateway.cs:3860
static Task< int > ProcessNewServiceConfigurations()
Processes new Service Configuration Files. This method should be called after installation of new ser...
Definition: Gateway.cs:6085
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
Definition: Gateway.cs:4335
static string AppDataFolder
Application data folder.
Definition: Gateway.cs:3132
static int[] GetConfigPorts(string Protocol)
Gets the port numbers defined for a given protocol in the configuration file.
Definition: Gateway.cs:3198
static CaseInsensitiveString[] AlternativeDomains
Alternative domain names
Definition: Gateway.cs:3122
static Task SendNotification(Graph Graph)
Sends a graph as a notification message to configured notification recipients.
Definition: Gateway.cs:4677
static string GetUrl(string LocalResource)
Gets a URL for a resource.
Definition: Gateway.cs:5079
static MultiUserChatClient MucClient
XMPP Multi-User Chat Protocol (MUC) Client.
Definition: Gateway.cs:4088
static JwtFactory JwtFactory
JWT Factory, used to create and validate JWT tokens.
Definition: Gateway.cs:3186
static async Task< string > GetCustomErrorHtml(HttpRequest Request, string LocalFileName, string ContentType, byte[] Content)
Gets a custom error HTML document.
Definition: Gateway.cs:5656
static DateTime ScheduleEvent(Action< object > Callback, DateTime When, object State)
Schedules a one-time event.
Definition: Gateway.cs:4253
static CaseInsensitiveString[] GetNotificationAddresses()
Returns configured notification addresses.
Definition: Gateway.cs:4807
static XmppClient XmppClient
XMPP Client connection of gateway.
Definition: Gateway.cs:4038
static string RootFolder
Web root folder.
Definition: Gateway.cs:3142
static bool HasDomain
If a domain name is configured.
Definition: Gateway.cs:3093
static bool CancelScheduledEvent(DateTime When)
Cancels a scheduled event.
Definition: Gateway.cs:4275
static SoftwareUpdateClient SoftwareUpdateClient
XMPP Software Updates Client, if such a compoent is available on the XMPP broker.
Definition: Gateway.cs:4098
static ProvisioningClient ProvisioningClient
XMPP Provisioning Client.
Definition: Gateway.cs:4048
static HttpFolderResource Root
Root folder resource.
Definition: Gateway.cs:3152
Domain constant, contains the value of the gateway domain.
Definition: Domain.cs:13
static ThemeDefinition CurrentTheme
Current theme.
Definition: Theme.cs:90
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.
Tokenizes contents defined in a Markdown document.
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:120
Contains information about a DNS Question
Definition: Question.cs:9
DNS resolver, as defined in:
Definition: DnsResolver.cs:32
static Task< DnsResponse > TryQuery(string Name, QTYPE TYPE, QCLASS CLASS)
Tries to query a DNS name.
Definition: DnsResolver.cs:424
Implements a simple FTP Server, as defined in:
Definition: FtpServer.cs:38
CommunicationLayer ExternalSniffers
External Sniffers for FTP communication.
Definition: FtpServer.cs:419
void Dispose()
IDisposable.Dispose
Definition: FtpServer.cs:594
void UpdateCertificate(X509Certificate ServerCertificate)
Updates the server certificate
Definition: FtpServer.cs:514
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...
Base class for all HTTP authentication schemes, as defined in RFC-7235: https://datatracker....
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, bool LastModifiedUpdated, 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:22
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:182
string RemoteEndPoint
Remote end-point.
Definition: HttpRequest.cs:243
SessionVariables Session
Contains session states, if the resource requires sessions, or null otherwise.
Definition: HttpRequest.cs:212
string SubPath
Sub-path. If a resource is found handling the request, this property contains the trailing sub-path o...
Definition: HttpRequest.cs:194
HttpResponse Response
HTTP Response object, if one has been assigned to the request.
Definition: HttpRequest.cs:254
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:23
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...
Task Write(byte[] Data)
Returns binary data in the response.
void SetHeader(string FieldName, string Value)
Sets a custom header field value.
TransferEncoding TransferEncoding
Transfer encoding in response.
An HTTP Reverse proxy resource. Incoming requests are reverted to a another web server for processing...
Implements an HTTP server.
Definition: HttpServer.cs:41
int[] OpenHttpPorts
HTTP Ports successfully opened.
Definition: HttpServer.cs:768
CommunicationStatistics GetCommunicationStatisticsSinceLast()
Gets communication statistics since last call.
Definition: HttpServer.cs:2263
static SessionVariables CreateSessionVariables()
Creates a new collection of variables, that contains access to the global set of variables.
Definition: HttpServer.cs:2130
HttpResource Register(HttpResource Resource)
Registers a resource with the server.
Definition: HttpServer.cs:1568
const int DefaultHttpPort
Default HTTP Port (80).
Definition: HttpServer.cs:45
void GetMTlsSettings(int Port, out ClientCertificates ClientCertificates, out bool TrustClientCertificates)
Gets mTLS settings for a given port number.
Definition: HttpServer.cs:979
int[] OpenHttpsPorts
HTTPS Ports successfully opened.
Definition: HttpServer.cs:773
bool Unregister(HttpResource Resource)
Unregisters a resource from the server.
Definition: HttpServer.cs:1743
const int DefaultHttpsPort
Default HTTPS port (443).
Definition: HttpServer.cs:50
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:45
CommunicationLayer ExternalSniffers
External Sniffers for SMTP communication.
Definition: SmtpServer.cs:225
void UpdateCertificate(X509Certificate ServerCertificate)
Updates the server certificate
Definition: SmtpServer.cs:305
void Dispose()
IDisposable.Dispose
Definition: SmtpServer.cs:323
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.
Contains a reference to an attachment assigned to a legal object.
Definition: Attachment.cs:10
string ContentType
Internet Content Type of binary attachment.
Definition: Attachment.cs:48
Adds support for legal identities, smart contracts and signatures to an XMPP client.
const string NamespaceOnboarding
http://waher.se/schema/Onboarding/v1.xsd
Abstract base class of signatures
Definition: Signature.cs:10
Abstract base class for XMPP client connections
bool IsComponentDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the component domain, or optionally, an alternative component domain.
Definition: Component.cs:124
Information about a file upload.
Definition: FileInfo.cs:40
Implements HTTP File Upload support as an XMPP component: https://xmpp.org/extensions/xep-0363....
string InternalTransferFolder
Folder where files for internal transfer are stored.
Access scheme used for internal transfer of files between client and server.
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:139
Task IqErrorBadRequest(XmppAddress From, string ErrorText, string Language)
Returns a bad-request error.
Definition: IqEventArgs.cs:164
Task IqErrorForbidden(XmppAddress From, string ErrorText, string Language)
Returns a forbidden error.
Definition: IqEventArgs.cs:234
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:143
IClientConnection[] GetClientConnections()
Get active client connections
Definition: XmppServer.cs:826
void UpdateCertificate(X509Certificate ServerCertificate)
Updates the server certificate
Definition: XmppServer.cs:946
CommunicationLayer S2sSniffers
Sniffers for XMPP S2S communication.
Definition: XmppServer.cs:657
S2sEndpointStatistics[] GetServerConnectionStatistics()
Gets S2S connection statistics.
Definition: XmppServer.cs:882
bool TryGetClientConnection(string FullJID, out IClientConnection Connection)
Tries to get an active client connection.
Definition: XmppServer.cs:844
CommunicationLayer C2sSniffers
Sniffers for XMPP C2S communication.
Definition: XmppServer.cs:652
bool IsServerDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the server domain, or optionally, an alternative domain.
Definition: XmppServer.cs:898
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
Definition: XmppServer.cs:1691
bool TryGetS2sEndpoint(string RemoteDomain, out IS2SEndpoint Endpoint)
Tries to get a server-to-server connection state object.
Definition: XmppServer.cs:2468
void Dispose()
IDisposable.Dispose
Definition: XmppServer.cs:987
static Task< XmppServer > Create(CaseInsensitiveString Domain, CaseInsensitiveString[] AlternativeDomains, X509Certificate ServerCertificate, bool EncryptionRequired, IXmppServerPersistenceLayer PersistenceLayer)
Creates an instance of an XMPP server.
Definition: XmppServer.cs:323
Statistics.CommunicationStatistics GetCommunicationStatisticsSinceLast()
Gets communication statistics since last call.
Definition: XmppServer.cs:5649
const string DelayedDeliveryNamespace
urn:xmpp:delay (XEP-0203)
Definition: XmppServer.cs:193
async Task< bool > SendMailMessage(CaseInsensitiveString From, CaseInsensitiveString To, string Subject, string Markdown)
Sends a mail message
Definition: XmppServer.cs:6654
async Task< int > DeleteOldMailContent(DateTime OlderThan)
Deletes old mail content.
Definition: XmppServer.cs:6642
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...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static Task< IPersistentDictionary > GetDictionary(string Collection)
Gets a persistent dictionary containing objects in a collection.
Definition: Database.cs:2307
static Task EndBulk()
Ends bulk-processing of data. Must be called once for every call to StartBulk.
Definition: Database.cs:2259
static Task StartBulk()
Starts bulk-proccessing of data. Must be followed by a call to EndBulk.
Definition: Database.cs:2251
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:238
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
static async Task Clear(string CollectionName)
Clears a collection of all objects.
Definition: Database.cs:1965
Persists objects into binary files.
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:67
static Task< bool > AddFullTextSearch(string CollectionName, params PropertyDefinition[] Properties)
Adds properties for full-text-search indexation.
Definition: Search.cs:225
static void RegisterStopWords(params string[] StopWords)
Registers stop-words with the search-engine. Stop-words are ignored in searches.
Definition: Search.cs:169
static Task< bool > SetFullTextSearchIndexCollection(string IndexCollection, string CollectionName)
Defines the Full-text-search index collection name, for objects in a given collection.
Definition: Search.cs:214
static Task< long > ReindexCollection(string IndexCollectionName)
Reindexes the full-text-search index for a database collection.
Definition: Search.cs:285
Contains information about a tokenization process.
Provides a Serializer context for full serialization.
Implements an in-memory cache.
Definition: Cache.cs:17
bool ContainsKey(KeyType Key)
Checks if a key is available in the cache.
Definition: Cache.cs:404
void Dispose()
IDisposable.Dispose
Definition: Cache.cs:99
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Definition: Cache.cs:311
void Add(KeyType Key, ValueType Value)
Adds an item to the cache.
Definition: Cache.cs:446
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
Contains static methods
Definition: Files.cs:14
Static class managing loading of resources stored as embedded resources or in content files.
Definition: Resources.cs:13
static byte[] LoadResource(string ResourceName)
Loads a resource from an embedded resource.
Definition: Resources.cs:20
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
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:584
static Type GetType(string FullName)
Gets a type, given its full name.
Definition: Types.cs:42
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Definition: Types.cs:607
static Assembly[] Assemblies
Assemblies in the inventory.
Definition: Types.cs:948
static object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:572
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:1454
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:85
Processes work tasks, in an asynchronous manner.
string Name
Name of processor.
Maintains a record of active processors.
static IAsyncProcessor[] GetActiveProcessors()
Gets an array of all active processors.
static Task CloseAllProcessorsForTermination()
Closes all active processors for new items.
static bool HasActiveProcessors
If there are active processors.
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.
Statistical bucket
Definition: Bucket.cs:21
A collection of buckets
Definition: Buckets.cs:14
bool TryGetBucket(string Id, out Bucket Bucket)
Tries to get a bucket, given its ID.
Definition: Buckets.cs:284
async Task< SampleStatistic > Sample(string Counter, double Value)
Samples a value
Definition: Buckets.cs:199
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.
Class managing the contents of a temporary stream. When the class is disposed, any temporary file is ...
Manages a temporary stream. Contents is kept in-memory, if below a memory threshold,...
override void WriteByte(byte value)
Writes a byte to the current position in the stream and advances the position within the stream by on...
override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
Asynchronously writes a sequence of bytes to the current stream, advances the current position within...
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Definition: Semaphore.cs:19
Static class of application-wide semaphores that can be used to order access to editable objects.
Definition: Semaphores.cs:17
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
Definition: Semaphores.cs:91
Base class for all types of elements.
Definition: Element.cs:14
Class managing a script expression.
Definition: Expression.cs:41
ScriptNode Root
Root script node.
Definition: Expression.cs:4496
async Task< object > EvaluateAsync(Variables Variables)
Evaluates the expression, using the variables provided in the Variables collection....
Definition: Expression.cs:4461
static bool TryGetConstant(string Name, Variables Variables, out IElement ValueElement)
Tries to get a constant value, given its name.
Definition: Expression.cs:3382
static string ToExpressionString(object Value)
Converts an object to a string, that can be parsed as part of an expression.
Definition: Expression.cs:5052
Base class for graphs.
Definition: Graph.cs:88
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
static async Task< object > WaitPossibleTask(object Result)
Waits for any asynchronous process to terminate.
Definition: ScriptNode.cs:441
virtual Task< IElement > EvaluateAsync(Variables Variables)
Evaluates the node, using the variables provided in the Variables collection. This method should be ...
Definition: ScriptNode.cs:158
static readonly ObjectValue Null
Null value.
Definition: ObjectValue.cs:88
ToMatrix(ScriptNode Operand, bool NullCheck, int Start, int Length, Expression Expression)
To-Matrix operator.
Definition: ToMatrix.cs:22
Represents one record.
Definition: Record.cs:9
Data Source defined by a type definition
Definition: TypeSource.cs:22
static MethodInfo FindMethod
Generic object database Find method: Database.Find<T>(int, int, Filter, string[])
Definition: TypeSource.cs:102
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:126
virtual bool TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
Definition: Variables.cs:56
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:57
static string ComputeSHA256HashString(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Definition: Hashes.cs:449
static string BinaryToString(byte[] Data)
Converts an array of bytes to a string with their hexadecimal representations (in lower case).
Definition: Hashes.cs:63
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:66
bool IsValid(JwtToken Token)
Checks if a token is valid and signed by the factory.
Definition: JwtFactory.cs:279
string Create(params KeyValuePair< string, object >[] Claims)
Creates a new JWT token.
Definition: JwtFactory.cs:379
Contains information about a Java Web Token (JWT). JWT is defined in RFC 7519: https://tools....
Definition: JwtToken.cs:22
static bool TryParse(string Token, out JwtToken ParsedToken)
Tries to parse a JWT token.
Definition: JwtToken.cs:68
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 Task< KeyValuePair< string, object >[]> Annotate(string RemoteEndPoint, params KeyValuePair< string, object >[] Tags)
Annotates a remote endpoint.
static async void Success(string Message, string UserName, string RemoteEndPoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a successful login attempt.
async Task< bool > ProcessLoginFailure(string RemoteEndPoint, string Protocol, DateTime Timestamp, string Reason)
Processes a failed login attempt.
static void Fail(string Message, string UserName, string RemoteEndPoint, string Protocol)
Handles a failed login attempt.
byte[] ComputeVariable(byte[] N)
Computes the SPONGE function, as defined in section 4 of NIST FIPS 202.
Definition: Keccak1600.cs:408
Implements the SHA3 SHAKE256 extendable-output functions, as defined in section 6....
Definition: SHAKE256.cs:9
Contains information about a SPF string.
Corresponds to a user in the system.
Definition: User.cs:24
string UserName
User Name
Definition: User.cs:60
async Task< string > CreateToken(JwtFactory Factory, bool Encrypted, params KeyValuePair< string, object >[] AdditionalClaims)
Creates a JWT Token referencing the user object.
Definition: User.cs:330
bool HasPrivilege(string Privilege)
If the user has a given privilege.
Definition: User.cs:187
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
static IUserSource Source
User source.
Definition: Users.cs:37
readonly Dictionary< string, FileInfo > Files
Currently accesible files
Definition: ChatState.cs:39
readonly LinkedList< Process > Processes
Current processes related to session
Definition: ChatState.cs:44
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:41
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.
Tokenizes (for full-text-search) a broker 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...
Tokenizes (for full-text-search) a Neuro-Feature token.
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:21
byte[] PublicKey
Public key of issuer, used to create signature.
Definition: Package.cs:55
byte[] Signature
Cryptographic signature of package, as calculated by the issuer of the package.
Definition: Package.cs:49
DateTime Installed
When package was installed (if installed).
Definition: Package.cs:97
bool ContentOnly
If package only contains content (i.e. no assemblies and executable files).
Definition: Package.cs:115
CaseInsensitiveString FileName
Filename of package.
Definition: Package.cs:43
DateTime Published
When package was published.
Definition: Package.cs:79
byte[] AesKey
Symmetric cipher used to encrypt package file.
Definition: Package.cs:61
DateTime Supersedes
Timestamp of superceded package.
Definition: Package.cs:85
DateTime Created
When package record was created
Definition: Package.cs:91
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)
PubSub component, as defined in XEP-0060.
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
Monitors performance samples, and alerts the operator if performance levels degrate.
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.
static async Task RegisterReports()
Registers protocol reports.
Definition: EventsRoot.cs:42
static async Task RegisterReports()
Registers protocol reports.
static async Task RegisterReports()
Registers protocol reports.
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
DomainInfo()
Gets information about the domain.
Definition: DomainInfo.cs:17
HttpAuthenticationScheme[] CreateAuthenticationSchemes(IUserSource Users)
Creates a set of authentication schemes for the resource.
HttpAuthenticationScheme[] AuthenticationSchemes
Array of authentication schemes used for the resource.
static async Task< bool > SiteVerify(string RecaptchaResponse, string RemoteEndPoint)
Allows web pages and web services to verify that Google reCaptcha responses are valid.
Definition: Feedback.cs:166
Web Service, generating QR-codes based on URI input.
Definition: QR.cs:24
Access to secured vault storage via signed URLs.
Definition: Vault.cs:20
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
HttpAuthenticationScheme[] DefaultAuthenticationSchemesXmpp
Default HTTP Authentication schemes for XMPP-authenticated resources.
HttpAuthenticationScheme[] DefaultAuthenticationSchemesAdmin
Default HTTP Authentication schemes for administrative resources.
const string NamespaceDnsOverXmpp
urn:xmpp:dox:0
const int DB_Generation
Current Database generation. Used for upgrading the internal database.
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 async Task< byte[]> LoadEncryptedFile(string FileName, byte[] Salt, long? Size)
Loads an encrypted file.
static Task< bool > SendMailMessage(string To, string Subject, string Markdown, params EmbeddedContent[] Attachments)
Sends a mail message
static async Task SaveEncryptedFile(string FileName, byte[] Salt, Stream Data)
Saves an encrypted file.
static Task< IP4Localization > FindIpAddress(string RemoteEndPoint)
Finds locale information about an IP Address.
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 bool IsContentPackage(Package Package)
Checks if a Package is a Content-Only package.
static async Task< Stream > EncodeBlob(Stream Data)
Encodes a variable-length BLOB by prefixing it with its length. This permits the BLOB to be safely en...
static Task SaveEncryptedFile(string FileName, byte[] Salt, byte[] Data)
Saves an encrypted file.
static ISerializerContext NormalizedSerialization
Serialization context for normalized serialization.
static DateTimeHF Now
Current high-resolution date and time
static async Task AppendRemoteEndPointToTable(StringBuilder Markdown, string RemoteEndPoint)
Appends annotated information about a remote endpoint to a Markdown table.
static Task< bool > SendMailMessage(string From, string To, string Subject, string Markdown)
Sends a mail message
static Task< byte[]> LoadEncryptedFile(string FileName, byte[] Salt)
Loads an encrypted file.
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< Stream > DecodeBlob(Stream Data)
Decodes a variable-length BLOB encoded by the EncodeBlob method. The length prefix is read first,...
static async Task< IP4Localization > FindIpAddress(IPAddress Addr)
Finds locale information about an IP Address.
Origin of request has maximum authority.
Origin of request has no authority.
Definition: NoAuthority.cs:7
static HttpAuthenticationScheme[] GetAuthenticationSchemes()
Gets an array of authentication schemes available to authorize access to a web resource.
Definition: HttpModule.cs:108
Module for semantic things.
void Information(string Comment)
Called to inform the viewer of something.
void 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:11
Interface for XMPP Server persistence layers. The persistence layer should implement caching.
Interface for objects containing encrypted properties. Mark the properties that are encrypted with th...
Persistent dictionary that can contain more entries than possible in the internal memory.
Task< KeyValuePair< bool, object > > TryGetValueAsync(string key)
Gets the value associated with the specified key.
Task< bool > ContainsKeyAsync(string key)
Determines whether the System.Collections.Generic.IDictionary{string,object} contains an element with...
Task< bool > RemoveAsync(string key)
Removes the element with the specified key from the System.Collections.IDictionary object.
Task AddAsync(string key, object value)
Adds an element with the provided key and value to the System.Collections.Generic....
Interface for late-bound modules loaded at runtime.
Definition: IModule.cs:9
Interface for AsyncProcessor<T> instances.
Basic interface for all types of elements.
Definition: IElement.cs:21
object AssociatedObjectValue
Associated object value.
Definition: IElement.cs:34
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.
Definition: ImplTypes.g.cs:58
EventLevel
Event level.
Definition: EventLevel.cs:7
HostDomainOptions
Options on how to handle domain names provided in the Host header.
BinaryPresentationMethod
How binary data is to be presented.
ClientCertificates
Client Certificate Options
ContentType
DTLS Record content type.
Definition: Enumerations.cs:11
Reason
Reason a token is not valid.
Definition: JwtFactory.cs:15
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:14