Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
Gateway.cs
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.IO;
5using System.Net;
6using System.Net.NetworkInformation;
7using System.Reflection;
8using System.Runtime.ExceptionServices;
9using System.Runtime.InteropServices;
10using System.Security.Cryptography;
11using System.Security.Cryptography.X509Certificates;
12using System.Text;
13using System.Text.RegularExpressions;
14using System.Threading;
15using System.Threading.Tasks;
16using System.Xml;
17using System.Xml.Schema;
18using Waher.Content;
33using Waher.Events;
80using Waher.Script;
83using Waher.Security;
88using Waher.Things;
91
92namespace Waher.IoTGateway
93{
99 public delegate Task<IDatabaseProvider> GetDatabaseProviderEventHandler(XmlElement Definition);
100
106 public delegate Task RegistrationEventHandler(MetaDataTag[] MetaData, RegistrationEventArgs e);
107
113 public delegate Task<MetaDataTag[]> GetRegistryMetaDataEventHandler(MetaDataTag[] MetaData);
114
120 public delegate Task<IDataSource[]> GetDataSources(params IDataSource[] DataSources);
121
125 public static class Gateway
126 {
130 public const string GatewayConfigLocalFileName = "Gateway.config";
131
135 public const string GatewayConfigLocalName = "GatewayConfiguration";
136
140 public const string GatewayConfigNamespace = "http://waher.se/Schema/GatewayConfiguration.xsd";
141
142 private const int MaxChunkSize = 4096;
143
144 private static readonly LinkedList<KeyValuePair<string, int>> ports = new LinkedList<KeyValuePair<string, int>>();
145 private static readonly Dictionary<int, EventHandlerAsync> serviceCommandByNr = new Dictionary<int, EventHandlerAsync>();
146 private static readonly Dictionary<EventHandlerAsync, int> serviceCommandNrByCallback = new Dictionary<EventHandlerAsync, int>();
147 private static readonly Dictionary<string, DateTime> lastUnauthorizedAccess = new Dictionary<string, DateTime>();
148 private static readonly DateTime startTime = DateTime.Now;
149 private static IDatabaseProvider internalProvider = null;
150 private static ThingRegistryClient thingRegistryClient = null;
151 private static ProvisioningClient provisioningClient = null;
152 private static XmppCredentials xmppCredentials = null;
153 private static XmppClient xmppClient = null;
154 private static Networking.XMPP.Avatar.AvatarClient avatarClient = null;
155 private static Networking.XMPP.InBandBytestreams.IbbClient ibbClient = null;
156 private static Socks5Proxy socksProxy = null;
157 private static ConcentratorServer concentratorServer = null;
158 private static SensorClient sensorClient = null;
159 private static ControlClient controlClient = null;
160 private static ConcentratorClient concentratorClient = null;
161 private static SynchronizationClient synchronizationClient = null;
162 private static PepClient pepClient = null;
163 private static MultiUserChatClient mucClient = null;
164 private static ContractsClient contractsClient = null;
165 private static SoftwareUpdateClient softwareUpdateClient = null;
166 private static MailClient mailClient = null;
167 private static X509Certificate2 certificate = null;
168 private static DateTime checkCertificate = DateTime.MinValue;
169 private static DateTime checkIp = DateTime.MinValue;
170 private static HttpServer webServer = null;
171 private static HttpFolderResource root = null;
172 private static HttpxProxy httpxProxy = null;
173 private static HttpxServer httpxServer = null;
174 private static CoapEndpoint coapEndpoint = null;
175 private static SystemConfiguration[] configurations;
176 private static LoginAuditor loginAuditor = null;
177 private static Scheduler scheduler = null;
178 private readonly static RandomNumberGenerator rnd = RandomNumberGenerator.Create();
179 private static AsyncMutex gatewayRunning = null;
180 private static AsyncMutex startingServer = null;
181 private static Emoji1LocalFiles emoji1_24x24 = null;
182 private static StreamWriter exceptionFile = null;
183 private static CaseInsensitiveString domain = null;
184 private static CaseInsensitiveString[] alternativeDomains = null;
185 private static CaseInsensitiveString ownerJid = null;
186 private static Dictionary<string, string> defaultPageByHostName = null;
187 private static string instance;
188 private static string appDataFolder;
189 private static string runtimeFolder;
190 private static string rootFolder;
191 private static string applicationName;
192 private static string exceptionFolder = null;
193 private static string exceptionFileName = null;
194 private static int nextServiceCommandNr = 128;
195 private static int beforeUninstallCommandNr = 0;
196 private static bool firstStart = true;
197 private static bool registered = false;
198 private static bool connected = false;
199 private static bool immediateReconnect;
200 private static bool consoleOutput;
201 private static bool loopbackIntefaceAvailable;
202 private static bool configuring = false;
203 private static bool exportExceptions = false;
204 private static bool stopped = false;
205
206 #region Life Cycle
207
211 public static DateTime StartTime => startTime;
212
218 public static Task<bool> Start(bool ConsoleOutput)
219 {
220 return Start(ConsoleOutput, true, string.Empty);
221 }
222
229 public static Task<bool> Start(bool ConsoleOutput, bool LoopbackIntefaceAvailable)
230 {
231 return Start(ConsoleOutput, LoopbackIntefaceAvailable, string.Empty);
232 }
233
241 public static async Task<bool> Start(bool ConsoleOutput, bool LoopbackIntefaceAvailable, string InstanceName)
242 {
243 bool FirstStart = firstStart;
244
245 firstStart = false;
246 instance = InstanceName;
247
248 string Suffix = string.IsNullOrEmpty(InstanceName) ? string.Empty : "." + InstanceName;
249 gatewayRunning = new AsyncMutex(false, "Waher.IoTGateway.Running" + Suffix);
250 if (!await gatewayRunning.WaitOne(1000))
251 return false; // Is running in another process.
252
253 startingServer = new AsyncMutex(false, "Waher.IoTGateway.Starting" + Suffix);
254 if (!await startingServer.WaitOne(1000))
255 {
256 await gatewayRunning.ReleaseMutex();
257 gatewayRunning.Dispose();
258 gatewayRunning = null;
259
260 startingServer.Dispose();
261 startingServer = null;
262 return false; // Being started in another process.
263 }
264
265 try
266 {
267 stopped = false;
268 consoleOutput = ConsoleOutput;
269 loopbackIntefaceAvailable = LoopbackIntefaceAvailable;
270
271 appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
272
273 if (!appDataFolder.EndsWith(new string(Path.DirectorySeparatorChar, 1)))
274 appDataFolder += Path.DirectorySeparatorChar;
275
276 appDataFolder += "IoT Gateway";
277
278 if (!string.IsNullOrEmpty(InstanceName))
279 appDataFolder += " " + InstanceName;
280
281 if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && !Directory.Exists(appDataFolder))
282 appDataFolder = appDataFolder.Replace("/usr/share", "/usr/local/share");
283
284 appDataFolder += Path.DirectorySeparatorChar;
285 rootFolder = appDataFolder + "Root" + Path.DirectorySeparatorChar;
286
287 Log.Register(new EventFilter("Alert Filter", new AlertNotifier("Alert Notifier"), EventType.Alert,
288 new CustomEventFilterDelegate((Event) => string.IsNullOrEmpty(Event.Facility))));
289
290 Log.Register(new XmlFileEventSink("XML File Event Sink",
291 appDataFolder + "Events" + Path.DirectorySeparatorChar + "Event Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml",
292 appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "EventXmlToHtml.xslt", 7));
293
294 if (FirstStart)
295 Assert.UnauthorizedAccess += Assert_UnauthorizedAccess;
296
297 Log.Informational("Server starting up.");
298
299 if (FirstStart)
300 {
301 Initialize();
302
303 beforeUninstallCommandNr = RegisterServiceCommand(BeforeUninstall);
304
305 if (!Directory.Exists(rootFolder))
306 {
307 string s = Path.Combine(runtimeFolder, "Root");
308 if (Directory.Exists(s))
309 {
310 CopyFolder(runtimeFolder, appDataFolder, "*.config", true);
311 CopyFolders(s, rootFolder, true);
312 CopyFolders(Path.Combine(runtimeFolder, "Graphics"), Path.Combine(appDataFolder, "Graphics"), true);
313 CopyFolders(Path.Combine(runtimeFolder, "Transforms"), Path.Combine(appDataFolder, "Transforms"), true);
314 }
315 }
316
317 string[] ManifestFiles = Directory.GetFiles(runtimeFolder, "*.manifest", SearchOption.TopDirectoryOnly);
318 Dictionary<string, CopyOptions> ContentOptions = new Dictionary<string, CopyOptions>();
319 int i;
320
321 for (i = 0; i < 2; i++)
322 {
323 foreach (string ManifestFile in ManifestFiles)
324 {
325 string FileName = Path.GetFileName(ManifestFile);
326 bool GatewayFile = FileName.StartsWith("Waher.IoTGateway", StringComparison.CurrentCultureIgnoreCase);
327
328 if ((i == 0 && GatewayFile) || (i == 1 && !GatewayFile))
329 {
330 CheckContentFiles(ManifestFile, ContentOptions);
331
332 if (ManifestFile.EndsWith("Waher.Utility.Install.manifest"))
333 CheckInstallUtilityFiles(ManifestFile);
334 }
335 }
336 }
337 }
338
339 Types.SetModuleParameter("AppData", appDataFolder);
340 Types.SetModuleParameter("Runtime", runtimeFolder);
341 Types.SetModuleParameter("Root", rootFolder);
342
343 scheduler = new Scheduler();
344
345 if (FirstStart)
346 {
347 Task T = Task.Run(() =>
348 {
349 GraphViz.Init(rootFolder);
350 XmlLayout.Init(rootFolder);
351 PlantUml.Init(rootFolder);
352 });
353 }
354
355 XmlDocument Config = new XmlDocument()
356 {
357 PreserveWhitespace = true
358 };
359
360 string GatewayConfigFileName = ConfigFilePath;
361 if (!File.Exists(GatewayConfigFileName))
362 GatewayConfigFileName = GatewayConfigLocalFileName;
363
364 Config.Load(GatewayConfigFileName);
366 XSL.LoadSchema(typeof(Gateway).Namespace + ".Schema.GatewayConfiguration.xsd", typeof(Gateway).Assembly));
367
368 IDatabaseProvider DatabaseProvider = null;
370 bool TrustClientCertificates = false;
371 Dictionary<int, KeyValuePair<ClientCertificates, bool>> PortSpecificMTlsSettings = null;
372
373 foreach (XmlNode N in Config.DocumentElement.ChildNodes)
374 {
375 if (N is XmlElement E)
376 {
377 switch (E.LocalName)
378 {
379 case "ApplicationName":
380 applicationName = E.InnerText;
381 break;
382
383 case "DefaultPage":
384 if (defaultPageByHostName is null)
385 defaultPageByHostName = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
386
387 defaultPageByHostName[XML.Attribute(E, "host")] = E.InnerText;
388 break;
389
390 case "MutualTls":
391 ClientCertificates = XML.Attribute(E, "clientCertificates", ClientCertificates.NotUsed);
392 TrustClientCertificates = XML.Attribute(E, "trustCertificates", false);
393
394 foreach (XmlNode N2 in E.ChildNodes)
395 {
396 if (N2.LocalName == "Port" && int.TryParse(N2.InnerText, out int PortNumber))
397 {
398 XmlElement E2 = (XmlElement)N2;
399 ClientCertificates ClientCertificatesPort = XML.Attribute(E2, "clientCertificates", ClientCertificates);
400 bool TrustClientCertificatesPort = XML.Attribute(E2, "trustCertificates", TrustClientCertificates);
401
402 if (PortSpecificMTlsSettings is null)
403 PortSpecificMTlsSettings = new Dictionary<int, KeyValuePair<ClientCertificates, bool>>();
404
405 PortSpecificMTlsSettings[PortNumber] = new KeyValuePair<ClientCertificates, bool>(ClientCertificatesPort, TrustClientCertificatesPort);
406 }
407 }
408 break;
409
410 case "ContentEncodings":
411 foreach (XmlNode N2 in E.ChildNodes)
412 {
413 if (N2.LocalName == "ContentEncoding")
414 {
415 XmlElement E2 = (XmlElement)N2;
416 string Method = XML.Attribute(E2, "method");
417 bool Dynamic = XML.Attribute(E2, "dynamic", true);
418 bool Static = XML.Attribute(E2, "static", true);
419
420 IContentEncoding Encoding = Types.FindBest<IContentEncoding, string>(Method);
421
422 if (Encoding is null)
423 Log.Error("Content-Encoding not found: " + Method, GatewayConfigLocalFileName);
424 else
425 Encoding.ConfigureSupport(Dynamic, Static);
426 }
427 }
428
430 break;
431
432 case "ExportExceptions":
433 exceptionFolder = Path.Combine(appDataFolder, XML.Attribute(E, "folder", "Exceptions"));
434
435 if (!Directory.Exists(exceptionFolder))
436 Directory.CreateDirectory(exceptionFolder);
437
438 DateTime Now = DateTime.Now;
439 string[] ExceptionFiles = Directory.GetFiles(exceptionFolder, "*.txt", SearchOption.TopDirectoryOnly);
440 foreach (string ExceptionFile in ExceptionFiles)
441 {
442 try
443 {
444 DateTime TP = File.GetLastWriteTime(ExceptionFile);
445 if ((Now - TP).TotalDays > 90)
446 File.Delete(ExceptionFile);
447 else
448 {
449 string XmlFile = Path.ChangeExtension(ExceptionFile, "xml");
450 if (!File.Exists(XmlFile))
451 {
452 Log.Informational("Processing " + ExceptionFile);
453 Analyze.Process(ExceptionFile, XmlFile);
454 }
455 }
456 }
457 catch (Exception ex)
458 {
459 Log.Exception(ex, ExceptionFile);
460 }
461 }
462
463 if (FirstStart)
464 {
465 int MaxTries = 1000;
466
467 do
468 {
469 exceptionFileName = Path.Combine(exceptionFolder, Now.Year.ToString("D4") + "-" + Now.Month.ToString("D2") + "-" + Now.Day.ToString("D2") +
470 " " + Now.Hour.ToString("D2") + "." + Now.Minute.ToString("D2") + "." + Now.Second.ToString("D2") + ".txt");
471
472 try
473 {
474 exceptionFile = File.CreateText(exceptionFileName);
475 }
476 catch (IOException)
477 {
478 exceptionFile = null;
479 Thread.Sleep(1000);
480 }
481 }
482 while (exceptionFile is null && --MaxTries > 0);
483
484 exportExceptions = !(exceptionFile is null);
485
486 if (exportExceptions)
487 {
488 exceptionFile.Write("Start of export: ");
489 exceptionFile.WriteLine(DateTime.Now.ToString());
490
491 AppDomain.CurrentDomain.FirstChanceException += (Sender, e) =>
492 {
493 if (!(exceptionFile is null))
494 {
495 lock (exceptionFile)
496 {
497 if (!exportExceptions || e.Exception.StackTrace.Contains("FirstChanceExceptionEventArgs"))
498 return;
499
500 exceptionFile.WriteLine(new string('-', 80));
501 exceptionFile.Write("Type: ");
502
503 if (!(e.Exception is null))
504 exceptionFile.WriteLine(e.Exception.GetType().FullName);
505 else
506 exceptionFile.WriteLine("null");
507
508 exceptionFile.Write("Time: ");
509 exceptionFile.WriteLine(DateTime.Now.ToString());
510
511 if (!(e.Exception is null))
512 {
513 LinkedList<Exception> Exceptions = new LinkedList<Exception>();
514 Exceptions.AddLast(e.Exception);
515
516 while (!(Exceptions.First is null))
517 {
518 Exception ex = Exceptions.First.Value;
519 Exceptions.RemoveFirst();
520
521 exceptionFile.WriteLine();
522
523 exceptionFile.WriteLine(ex.Message);
524 exceptionFile.WriteLine();
525 exceptionFile.WriteLine(Log.CleanStackTrace(ex.StackTrace));
526 exceptionFile.WriteLine();
527
528 if (ex is AggregateException ex2)
529 {
530 foreach (Exception ex3 in ex2.InnerExceptions)
531 Exceptions.AddLast(ex3);
532 }
533 else if (!(ex.InnerException is null))
534 Exceptions.AddLast(ex.InnerException);
535 }
536 }
537
538 exceptionFile.Flush();
539 }
540 }
541 };
542 }
543 }
544 break;
545
546 case "Database":
547 if (FirstStart || !Database.HasProvider)
548 {
549 if (!(DatabaseProvider is null))
550 throw new Exception("Database provider already initiated.");
551
552 if (!(GetDatabaseProvider is null))
553 DatabaseProvider = await GetDatabaseProvider(E);
554 else
555 DatabaseProvider = null;
556
557 if (DatabaseProvider is null)
558 throw new Exception("Database provider not defined. Make sure the GetDatabaseProvider event has an appropriate event handler.");
559
560 internalProvider = DatabaseProvider;
561 Database.Register(DatabaseProvider, false);
562 }
563 else
564 {
565 DatabaseProvider = Database.Provider;
566 await DatabaseProvider.Start();
567
569 await Ledger.Provider.Start();
570 }
571 break;
572
573 case "Ports":
574 foreach (XmlNode N2 in E.ChildNodes)
575 {
576 if (N2.LocalName == "Port")
577 {
578 XmlElement E2 = (XmlElement)N2;
579 string Protocol = XML.Attribute(E2, "protocol");
580 if (!string.IsNullOrEmpty(Protocol) && int.TryParse(E2.InnerText, out int Port2))
581 ports.AddLast(new KeyValuePair<string, int>(Protocol, Port2));
582 }
583 }
584 break;
585
586 case "LoginAuditor":
587 List<LoginInterval> LoginIntervals = new List<LoginInterval>();
588 Duration LastInterval = Duration.Zero;
589 bool LastMaxInterval = false;
590
591 foreach (XmlNode N2 in E.ChildNodes)
592 {
593 if (N2.LocalName == "Interval")
594 {
595 if (LastMaxInterval)
596 {
597 Log.Error("Only the last login auditor interval can be the empty 'eternal' interval.",
599 break;
600 }
601
602 XmlElement E2 = (XmlElement)N2;
603 int NrAttempts = XML.Attribute(E2, "nrAttempts", 0);
604 if (NrAttempts <= 0)
605 {
606 Log.Error("Number of attempts must be positive when defining an interval for the LoginAuditor",
608 continue;
609 }
610
611 if (!E2.HasAttribute("interval"))
612 {
613 LoginIntervals.Add(new LoginInterval(NrAttempts, TimeSpan.MaxValue));
614 LastMaxInterval = true;
615 }
616 else
617 {
618 Duration Interval = XML.Attribute(E2, "interval", Duration.Zero);
619 if (Interval <= Duration.Zero)
620 {
621 Log.Error("Login Auditor intervals must be positive", GatewayConfigLocalFileName);
622 continue;
623 }
624
625 if (Interval <= LastInterval)
626 {
627 Log.Error("Login Auditor intervals must be specified in an increasing order.",
629 continue;
630 }
631
632 LoginIntervals.Add(new LoginInterval(NrAttempts, Interval));
633 LastInterval = Interval;
634 }
635 }
636 }
637
638 if (LoginIntervals.Count == 0)
639 Log.Error("Login Auditor intervals not specified.", GatewayConfigLocalFileName);
640 else
641 loginAuditor = new LoginAuditor("Login Auditor", LoginIntervals.ToArray());
642 break;
643 }
644 }
645 }
646
647 if (DatabaseProvider is null)
648 throw new Exception("Database provider not defined in " + GatewayConfigLocalFileName + ".");
649
650 Database.CollectionRepaired += Database_CollectionRepaired;
651
652 await RepairIfInproperShutdown();
653
654 PersistedEventLog PersistedEventLog = new PersistedEventLog(90, new TimeSpan(4, 15, 0));
656 try
657 {
658 await PersistedEventLog.Queue(new Event(EventType.Informational, "Server starting up.", string.Empty, string.Empty, string.Empty, EventLevel.Minor, string.Empty, string.Empty, string.Empty));
659 }
660 catch (Exception ex)
661 {
662 Event Event = new Event(DateTime.Now, EventType.Critical, ex.Message, PersistedEventLog.ObjectID, string.Empty, string.Empty,
663 EventLevel.Major, string.Empty, ex.Source, Log.CleanStackTrace(ex.StackTrace));
664
666
667 Log.Event(Event);
668 }
669
670 if (loginAuditor is null)
671 {
672 loginAuditor = new LoginAuditor("Login Auditor",
673 new LoginInterval(5, TimeSpan.FromHours(1)), // Maximum 5 failed login attempts in an hour
674 new LoginInterval(2, TimeSpan.FromDays(1)), // Maximum 2x5 failed login attempts in a day
675 new LoginInterval(2, TimeSpan.FromDays(7)), // Maximum 2x2x5 failed login attempts in a week
676 new LoginInterval(2, TimeSpan.MaxValue)); // Maximum 2x2x2x5 failed login attempts in total, then blocked.
677 }
678
679 Log.Register(loginAuditor);
680
681 // Protecting Markdown resources:
683 MarkdownCodec.AllowRawEncoding(false, true);
685
686 // Protecting web-script resources:
688 WsCodec.AllowRawEncoding(false, true);
690
691 LinkedList<SystemConfiguration> NewConfigurations = null;
692 Dictionary<string, Type> SystemConfigurationTypes = new Dictionary<string, Type>();
693 Dictionary<string, SystemConfiguration> SystemConfigurations = new Dictionary<string, SystemConfiguration>();
694 bool Configured = true;
695 bool Simplify = (await ServiceRegistrationClient.GetRegistrationTime()).HasValue;
696
697 foreach (Type SystemConfigurationType in Types.GetTypesImplementingInterface(typeof(ISystemConfiguration)))
698 {
699 if (SystemConfigurationType.IsAbstract || SystemConfigurationType.IsInterface || SystemConfigurationType.IsGenericTypeDefinition)
700 continue;
701
702 SystemConfigurationTypes[SystemConfigurationType.FullName] = SystemConfigurationType;
703 }
704
706 {
707 string s = SystemConfiguration.GetType().FullName;
708
709 if (SystemConfigurations.ContainsKey(s))
710 await Database.Delete(SystemConfiguration); // No duplicates allowed by mistake
711 else
712 {
713 SystemConfigurations[s] = SystemConfiguration;
714 SystemConfigurationTypes.Remove(s);
715
717 {
719 {
722
723 if (NewConfigurations is null)
724 NewConfigurations = new LinkedList<SystemConfiguration>();
725
726 NewConfigurations.AddLast(SystemConfiguration);
727 continue;
728 }
729
730 if (Simplify && await SystemConfiguration.SimplifiedConfiguration())
731 {
734
735 if (NewConfigurations is null)
736 NewConfigurations = new LinkedList<SystemConfiguration>();
737
738 NewConfigurations.AddLast(SystemConfiguration);
739 continue;
740 }
741
742 Configured = false;
743 }
744 }
745 }
746
747 foreach (KeyValuePair<string, Type> P in SystemConfigurationTypes)
748 {
749 try
750 {
752 SystemConfiguration.Complete = false;
753 SystemConfiguration.Created = DateTime.Now;
754
756
757 SystemConfigurations[P.Key] = SystemConfiguration;
758
760 {
763
764 if (NewConfigurations is null)
765 NewConfigurations = new LinkedList<SystemConfiguration>();
766
767 NewConfigurations.AddLast(SystemConfiguration);
768 continue;
769 }
770
771 if (Simplify && await SystemConfiguration.SimplifiedConfiguration())
772 {
775
776 if (NewConfigurations is null)
777 NewConfigurations = new LinkedList<SystemConfiguration>();
778
779 NewConfigurations.AddLast(SystemConfiguration);
780 continue;
781 }
782
783 Configured = false;
784 }
785 catch (Exception ex)
786 {
787 Log.Exception(ex);
788 continue;
789 }
790 }
791
792 configurations = new SystemConfiguration[SystemConfigurations.Count];
793 SystemConfigurations.Values.CopyTo(configurations, 0);
794 Array.Sort(configurations, (c1, c2) => c1.Priority - c2.Priority);
795
796 ISystemConfiguration CurrentConfiguration = null;
797 LinkedList<HttpResource> SetupResources = null;
798
799 if (!Configured)
800 {
801 configuring = true;
802
803 if (loopbackIntefaceAvailable)
804 Log.Notice("System needs to be configured. This is done by navigating to the loopback interface using a browser on this machine.");
805 else
806 Log.Notice("System needs to be configured. This is done by navigating to the machine using a browser on another machine in the same network.");
807
808 webServer = new HttpServer(GetConfigPorts("HTTP"), null, null)
809 {
810 ResourceOverride = "/Starting.md",
811 ResourceOverrideFilter = "(?<!Login)[.]md(\\?[.]*)?$",
812 LoginAuditor = loginAuditor
813 };
814
815 webServer.Register("/Starting.md", StartingMd);
816 webServer.CustomError += WebServer_CustomError;
817
818 SetupResources = new LinkedList<HttpResource>();
819
820 SetupResources.AddLast(webServer.Register(new HttpFolderResource("/Graphics", Path.Combine(appDataFolder, "Graphics"), false, false, true, false, HostDomainOptions.SameForAllDomains))); // TODO: Add authentication mechanisms for PUT & DELETE.
821 SetupResources.AddLast(webServer.Register(new HttpFolderResource("/Transforms", Path.Combine(appDataFolder, "Transforms"), false, false, true, false, HostDomainOptions.SameForAllDomains))); // TODO: Add authentication mechanisms for PUT & DELETE.
822 SetupResources.AddLast(webServer.Register(new HttpFolderResource("/highlight", "Highlight", false, false, true, false, HostDomainOptions.SameForAllDomains))); // Syntax highlighting library, provided by http://highlightjs.org
823 SetupResources.AddLast(webServer.Register(root = new HttpFolderResource(string.Empty, rootFolder, false, false, true, true, HostDomainOptions.UseDomainSubfolders))); // TODO: Add authentication mechanisms for PUT & DELETE.
824 SetupResources.AddLast(webServer.Register("/", GoToDefaultPage));
825 SetupResources.AddLast(webServer.Register(new ClientEvents()));
826 SetupResources.AddLast(webServer.Register(new ClientEventsWebSocket()));
827 SetupResources.AddLast(webServer.Register(new Login()));
828 SetupResources.AddLast(webServer.Register(new Logout()));
829
830 emoji1_24x24 = new Emoji1LocalFiles(Emoji1SourceFileType.Svg, 24, 24, "/Graphics/Emoji1/svg/%FILENAME%",
831 Path.Combine(runtimeFolder, "Graphics", "Emoji1.zip"), Path.Combine(appDataFolder, "Graphics"));
832
833 root.AllowTypeConversion();
834
835 MarkdownToHtmlConverter.EmojiSource = emoji1_24x24;
836 MarkdownToHtmlConverter.RootFolder = rootFolder;
837 }
838
839 foreach (SystemConfiguration Configuration in configurations)
840 {
841 Configuration.SetStaticInstance(Configuration);
842
843 if (!(webServer is null))
844 await Configuration.InitSetup(webServer);
845 }
846
847 bool ReloadConfigurations;
848
849 do
850 {
851 ReloadConfigurations = false;
852
853 foreach (SystemConfiguration Configuration in configurations)
854 {
855 bool NeedsCleanup = false;
856
857 if (!Configuration.Complete)
858 {
859 CurrentConfiguration = Configuration;
860
861 if (!(webServer is null))
862 webServer.ResourceOverride = Configuration.Resource;
863
864 Configuration.SetStaticInstance(Configuration);
865
866 if (!(startingServer is null))
867 {
868 await startingServer.ReleaseMutex();
869 startingServer.Dispose();
870 startingServer = null;
871 }
872
873 await ClientEvents.PushEvent(ClientEvents.GetTabIDs(), "Reload", string.Empty);
874
875 if (!(webServer is null) && await Configuration.SetupConfiguration(webServer))
876 ReloadConfigurations = true;
877
878 NeedsCleanup = true;
879 }
880
881 DateTime StartConfig = DateTime.Now;
882
883 try
884 {
885 await Configuration.ConfigureSystem();
886 }
887 catch (Exception)
888 {
889 await RepairIfInproperShutdown();
890
891 try
892 {
893 await Configuration.ConfigureSystem();
894 }
895 catch (Exception ex)
896 {
897 Log.Exception(ex);
898 }
899 }
900
901 if (NeedsCleanup && !(webServer is null))
902 await Configuration.CleanupAfterConfiguration(webServer);
903
904 if (ReloadConfigurations)
905 {
906 Configured = true;
907
909 {
910 string s = SystemConfiguration.GetType().FullName;
911
912 if (!(webServer is null) && SystemConfigurations.TryGetValue(s, out SystemConfiguration OldConfiguration))
913 await OldConfiguration.UnregisterSetup(webServer);
914
915 SystemConfigurations[s] = SystemConfiguration;
917
918 if (!(webServer is null))
919 await SystemConfiguration.InitSetup(webServer);
920 }
921
922 foreach (SystemConfiguration SystemConfiguration in SystemConfigurations.Values)
923 {
925 {
926 Configured = false;
927 break;
928 }
929 }
930
931 configurations = new SystemConfiguration[SystemConfigurations.Count];
932 SystemConfigurations.Values.CopyTo(configurations, 0);
933 Array.Sort(configurations, (c1, c2) => c1.Priority - c2.Priority);
934
935 break;
936 }
937
938 if (DateTime.Now.Subtract(StartConfig).TotalSeconds > 2)
939 await ClientEvents.PushEvent(ClientEvents.GetTabIDs(), "Reload", string.Empty);
940 }
941 }
942 while (ReloadConfigurations);
943
944 configuring = false;
945 loginAuditor.Domain = DomainConfiguration.Instance.Domain;
946
947 if (!(webServer is null))
948 {
949 webServer.ResourceOverride = "/Starting.md";
950 await ClientEvents.PushEvent(ClientEvents.GetTabIDs(), "Reload", string.Empty);
951
952 if (!(SetupResources is null))
953 {
954 foreach (HttpResource Resource in SetupResources)
955 webServer.Unregister(Resource);
956 }
957
958 webServer.ConfigureMutualTls(ClientCertificates, TrustClientCertificates, PortSpecificMTlsSettings, true);
959 webServer.NetworkChanged();
960
961 webServer.AddHttpPorts(GetConfigPorts("HTTP"));
962
963 if (!(certificate is null))
964 {
965 webServer.AddHttpsPorts(GetConfigPorts("HTTPS"));
966 webServer.UpdateCertificate(certificate);
967 }
968 }
969 else
970 {
971 if (!(certificate is null))
972 {
973 webServer = new HttpServer(GetConfigPorts("HTTP"), GetConfigPorts("HTTPS"), certificate, true,
974 ClientCertificates, TrustClientCertificates, PortSpecificMTlsSettings, true);
975 }
976 else
977 webServer = new HttpServer(GetConfigPorts("HTTP"), null, null);
978
979 webServer.Register("/Starting.md", StartingMd);
980 webServer.ResourceOverride = "/Starting.md";
981 webServer.LoginAuditor = loginAuditor;
982
983 webServer.CustomError += WebServer_CustomError;
984
985 foreach (SystemConfiguration Configuration in configurations)
986 {
987 try
988 {
989 await Configuration.InitSetup(webServer);
990 }
991 catch (Exception)
992 {
993 await RepairIfInproperShutdown();
994
995 try
996 {
997 await Configuration.InitSetup(webServer);
998 }
999 catch (Exception ex)
1000 {
1001 Log.Exception(ex);
1002 }
1003 }
1004 }
1005 }
1006
1007 Types.SetModuleParameter("HTTP", webServer);
1008 Types.SetModuleParameter("X509", certificate);
1009 Types.SetModuleParameter("LoginAuditor", webServer.LoginAuditor);
1010
1011 await WriteWebServerOpenPorts();
1012 webServer.OnNetworkChanged += async (Sender, e) =>
1013 {
1014 try
1015 {
1016 await WriteWebServerOpenPorts();
1017 }
1018 catch (Exception ex)
1019 {
1020 Log.Exception(ex);
1021 }
1022 };
1023
1024 webServer.Register(new HttpFolderResource("/Graphics", Path.Combine(appDataFolder, "Graphics"), false, false, true, false, HostDomainOptions.SameForAllDomains)); // TODO: Add authentication mechanisms for PUT & DELETE.
1025 webServer.Register(new HttpFolderResource("/Transforms", Path.Combine(appDataFolder, "Transforms"), false, false, true, false, HostDomainOptions.SameForAllDomains)); // TODO: Add authentication mechanisms for PUT & DELETE.
1026 webServer.Register(new HttpFolderResource("/highlight", "Highlight", false, false, true, false, HostDomainOptions.SameForAllDomains)); // Syntax highlighting library, provided by http://highlightjs.org
1027 webServer.Register(root = new HttpFolderResource(string.Empty, rootFolder, false, false, true, true, HostDomainOptions.UseDomainSubfolders)); // TODO: Add authentication mechanisms for PUT & DELETE.
1028 webServer.Register(httpxProxy = new HttpxProxy("/HttpxProxy", xmppClient, MaxChunkSize));
1029 webServer.Register("/", GoToDefaultPage);
1030 webServer.Register(new HttpConfigurableFileResource("/robots.txt", Path.Combine(rootFolder, "robots.txt"), PlainTextCodec.DefaultContentType));
1031 webServer.Register(new HttpConfigurableFileResource("/favicon.ico", Path.Combine(rootFolder, "favicon.ico"), ImageCodec.ContentTypeIcon));
1032 webServer.Register(new ClientEvents());
1033 webServer.Register(new ClientEventsWebSocket());
1034 webServer.Register(new Login());
1035 webServer.Register(new Logout());
1036 webServer.Register(new ProposeContract());
1037
1038 if (emoji1_24x24 is null)
1039 {
1040 emoji1_24x24 = new Emoji1LocalFiles(Emoji1SourceFileType.Svg, 24, 24, "/Graphics/Emoji1/svg/%FILENAME%",
1041 Path.Combine(runtimeFolder, "Graphics", "Emoji1.zip"), Path.Combine(appDataFolder, "Graphics"));
1042
1043 MarkdownToHtmlConverter.EmojiSource = emoji1_24x24;
1044 MarkdownToHtmlConverter.RootFolder = rootFolder;
1045 }
1046
1047 root.AllowTypeConversion();
1048
1049 XmlElement DefaultHttpResponseHeaders = Config.DocumentElement["DefaultHttpResponseHeaders"];
1050 if (!(DefaultHttpResponseHeaders is null))
1051 {
1052 foreach (XmlNode N in DefaultHttpResponseHeaders.ChildNodes)
1053 {
1054 if (N is XmlElement E && E.LocalName == "DefaultHttpResponseHeader")
1055 {
1056 string HeaderKey = XML.Attribute(E, "key");
1057 string HeaderValue = XML.Attribute(E, "value");
1058
1059 root.AddDefaultResponseHeader(HeaderKey, HeaderValue);
1060 }
1061 }
1062 }
1063
1064 XmlElement FileFolders = Config.DocumentElement["FileFolders"];
1065 if (!(FileFolders is null))
1066 {
1067 foreach (XmlNode N in FileFolders.ChildNodes)
1068 {
1069 if (N is XmlElement E && E.LocalName == "FileFolder")
1070 {
1071 string WebFolder = XML.Attribute(E, "webFolder");
1072 string FolderPath = XML.Attribute(E, "folderPath");
1073
1074 HttpFolderResource FileFolder = new HttpFolderResource(WebFolder, FolderPath, false, false, true, true, HostDomainOptions.SameForAllDomains);
1075 webServer.Register(FileFolder);
1076
1077 foreach (XmlNode N2 in E.ChildNodes)
1078 {
1079 if (N2 is XmlElement E2 && E2.LocalName == "DefaultHttpResponseHeader")
1080 {
1081 string HeaderKey = XML.Attribute(E2, "key");
1082 string HeaderValue = XML.Attribute(E2, "value");
1083
1084 FileFolder.AddDefaultResponseHeader(HeaderKey, HeaderValue);
1085 }
1086 }
1087 }
1088 }
1089 }
1090
1091 XmlElement VanityResources = Config.DocumentElement["VanityResources"];
1092 if (!(VanityResources is null))
1093 {
1094 foreach (XmlNode N in VanityResources.ChildNodes)
1095 {
1096 if (N is XmlElement E && E.LocalName == "VanityResource")
1097 {
1098 string RegEx = XML.Attribute(E, "regex");
1099 string Url = XML.Attribute(E, "url");
1100
1101 try
1102 {
1103 webServer.RegisterVanityResource(RegEx, Url);
1104 }
1105 catch (Exception ex)
1106 {
1107 Log.Error("Unable to register vanity resource: " + ex.Message,
1108 new KeyValuePair<string, object>("RegEx", RegEx),
1109 new KeyValuePair<string, object>("Url", Url));
1110 }
1111 }
1112 }
1113 }
1114
1115 XmlElement Redirections = Config.DocumentElement["Redirections"];
1116 if (!(Redirections is null))
1117 {
1118 foreach (XmlNode N in Redirections.ChildNodes)
1119 {
1120 if (N is XmlElement E && E.LocalName == "Redirection")
1121 {
1122 string Resource = XML.Attribute(E, "resource");
1123 string Location = XML.Attribute(E, "location");
1124 bool IncludeSubPaths = XML.Attribute(E, "includeSubPaths", false);
1125 bool Permanent = XML.Attribute(E, "permanent", false);
1126
1127 try
1128 {
1129 webServer.Register(new HttpRedirectionResource(Resource, Location, IncludeSubPaths, Permanent));
1130 }
1131 catch (Exception ex)
1132 {
1133 Log.Error("Unable to register redirection: " + ex.Message,
1134 new KeyValuePair<string, object>("Resource", Resource),
1135 new KeyValuePair<string, object>("Location", Location),
1136 new KeyValuePair<string, object>("IncludeSubPaths", IncludeSubPaths),
1137 new KeyValuePair<string, object>("Permanent", Permanent));
1138 }
1139 }
1140 }
1141 }
1142
1143 XmlElement ReverseProxy = Config.DocumentElement["ReverseProxy"];
1144 if (!(ReverseProxy is null))
1145 {
1146 foreach (XmlNode N in ReverseProxy.ChildNodes)
1147 {
1148 if (N is XmlElement E && E.LocalName == "ProxyResource")
1149 {
1150 string LocalResource = XML.Attribute(E, "localResource");
1151 string RemoteDomain = XML.Attribute(E, "remoteDomain");
1152 string RemoteFolder = XML.Attribute(E, "remoteFolder");
1153 bool Encrypted = XML.Attribute(E, "encrypted", false);
1154 int RemotePort = XML.Attribute(E, "remotePort", Encrypted ? HttpServer.DefaultHttpsPort : HttpServer.DefaultHttpPort);
1155 bool UseSession = XML.Attribute(E, "useSession", false);
1156 int TimeoutMs = XML.Attribute(E, "timeoutMs", 10000);
1157
1158 try
1159 {
1160 webServer.Register(new HttpReverseProxyResource(LocalResource, RemoteDomain, RemotePort, RemoteFolder, Encrypted,
1161 TimeSpan.FromMilliseconds(TimeoutMs), UseSession));
1162 }
1163 catch (Exception ex)
1164 {
1165 Log.Error("Unable to register reverse proxy: " + ex.Message,
1166 new KeyValuePair<string, object>("LocalResource", LocalResource),
1167 new KeyValuePair<string, object>("RemoteDomain", RemoteDomain),
1168 new KeyValuePair<string, object>("Encrypted", Encrypted),
1169 new KeyValuePair<string, object>("RemotePort", RemotePort),
1170 new KeyValuePair<string, object>("RemoteFolder", RemoteFolder),
1171 new KeyValuePair<string, object>("UseSession", UseSession),
1172 new KeyValuePair<string, object>("TimeoutMs", TimeoutMs));
1173 }
1174 }
1175 }
1176 }
1177
1178 await LoadScriptResources();
1179
1180 httpxServer = new HttpxServer(xmppClient, webServer, MaxChunkSize);
1181 Types.SetModuleParameter("HTTPX", httpxProxy);
1182 Types.SetModuleParameter("HTTPXS", httpxServer);
1183
1184 httpxProxy.IbbClient = ibbClient;
1185 httpxServer.IbbClient = ibbClient;
1186
1187 httpxProxy.Socks5Proxy = socksProxy;
1188 httpxServer.Socks5Proxy = socksProxy;
1189
1190 if (xmppCredentials.Sniffer)
1191 {
1192 ISniffer Sniffer;
1193
1194 Sniffer = new XmlFileSniffer(appDataFolder + "HTTP" + Path.DirectorySeparatorChar +
1195 "HTTP Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml",
1196 appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
1197 7, BinaryPresentationMethod.ByteCount);
1198 webServer.Add(Sniffer);
1199 }
1200
1201 await ClientEvents.PushEvent(ClientEvents.GetTabIDs(), "Reload", string.Empty);
1202
1203 try
1204 {
1205 coapEndpoint = new CoapEndpoint();
1206 Types.SetModuleParameter("CoAP", coapEndpoint);
1207 }
1208 catch (Exception ex)
1209 {
1210 Log.Exception(ex);
1211 }
1212
1213 IDataSource[] Sources;
1214
1215 try
1216 {
1217 Sources = new IDataSource[] { new MeteringTopology() };
1218 }
1219 catch (Exception)
1220 {
1221 await RepairIfInproperShutdown();
1222
1223 try
1224 {
1225 Sources = new IDataSource[] { new MeteringTopology() };
1226 }
1227 catch (Exception ex)
1228 {
1229 Log.Exception(ex);
1230 Sources = new IDataSource[0];
1231 }
1232 }
1233
1234 Types.GetLoadedModules(); // Makes sure all modules are instantiated, allowing static constructors to add
1235 // appropriate data sources, if necessary.
1236
1237 if (!(GetDataSources is null))
1238 Sources = await GetDataSources(Sources);
1239
1240 concentratorServer = await ConcentratorServer.Create(xmppClient, thingRegistryClient, provisioningClient, Sources);
1241 avatarClient = new Networking.XMPP.Avatar.AvatarClient(xmppClient, pepClient);
1242
1243 Types.SetModuleParameter("Concentrator", concentratorServer);
1244 Types.SetModuleParameter("Sources", concentratorServer.DataSources);
1246 Types.SetModuleParameter("Sensor", concentratorServer.SensorServer);
1247 Types.SetModuleParameter("Control", concentratorServer.ControlServer);
1248 Types.SetModuleParameter("Registry", thingRegistryClient);
1249 Types.SetModuleParameter("Provisioning", provisioningClient);
1250 Types.SetModuleParameter("Avatar", avatarClient);
1251 Types.SetModuleParameter("Scheduler", scheduler);
1252
1253 if (FirstStart)
1254 {
1255 MeteringTopology.OnNewMomentaryValues += NewMomentaryValues;
1256 ProvisionedMeteringNode.QrCodeUrlRequested += ProvisionedMeteringNode_QrCodeUrlRequested;
1257 }
1258
1259 DeleteOldDataSourceEvents(null);
1260
1261 try
1262 {
1263 string BinaryFolder = AppDomain.CurrentDomain.BaseDirectory;
1264 string[] LanguageFiles = Directory.GetFiles(BinaryFolder, "*.lng", SearchOption.AllDirectories);
1265 string FileName;
1266
1267 if (LanguageFiles.Length > 0)
1268 {
1269 XmlSchema Schema = XSL.LoadSchema(Translator.SchemaResource, typeof(Translator).Assembly);
1270
1271 foreach (string LanguageFile in LanguageFiles)
1272 {
1273 string Xml = null;
1274
1275 try
1276 {
1277 FileName = LanguageFile;
1278 if (FileName.StartsWith(BinaryFolder))
1279 FileName = FileName.Substring(BinaryFolder.Length);
1280
1281 DateTime LastWriteTime = File.GetLastWriteTime(LanguageFile);
1282 DateTime LastImportedTime = await RuntimeSettings.GetAsync(FileName, DateTime.MinValue);
1283
1284 if (LastWriteTime > LastImportedTime)
1285 {
1286 Log.Informational("Importing language file.", FileName);
1287
1288 Xml = await Resources.ReadAllTextAsync(LanguageFile);
1289 XmlDocument Doc = new XmlDocument()
1290 {
1291 PreserveWhitespace = true
1292 };
1293 Doc.LoadXml(Xml);
1294
1296
1297 using (XmlReader r = new XmlNodeReader(Doc))
1298 {
1299 await Translator.ImportAsync(r);
1300 }
1301
1302 RuntimeSettings.Set(FileName, LastWriteTime);
1303 }
1304 }
1305 catch (XmlException ex)
1306 {
1307 ex = XML.AnnotateException(ex, Xml);
1308 Log.Exception(ex, LanguageFile);
1309 }
1310 catch (Exception ex)
1311 {
1312 Log.Exception(ex, LanguageFile);
1313 }
1314 }
1315 }
1316
1317 foreach (string UnhandledException in Directory.GetFiles(appDataFolder, "UnhandledException*.txt", SearchOption.TopDirectoryOnly))
1318 {
1319 try
1320 {
1321 string Msg = await Resources.ReadAllTextAsync(UnhandledException);
1322 File.Delete(UnhandledException);
1323
1324 StringBuilder sb = new StringBuilder();
1325
1326 sb.AppendLine("Unhandled Exception");
1327 sb.AppendLine("=======================");
1328 sb.AppendLine();
1329 sb.AppendLine("```");
1330 sb.AppendLine(Msg);
1331 sb.AppendLine("```");
1332
1333 Log.Emergency(sb.ToString());
1334 }
1335 catch (Exception ex)
1336 {
1337 Log.Emergency(ex, UnhandledException);
1338 }
1339 }
1340
1341 if (await Types.StartAllModules(int.MaxValue, new ModuleStartOrder()))
1342 {
1343 Log.Informational("Server started.");
1344 await ProcessServiceConfigurations(false);
1345 }
1346 else
1347 Log.Critical("Unable to start all modules.");
1348
1349 if (!(NewConfigurations is null))
1350 {
1351 foreach (SystemConfiguration Configuration in NewConfigurations)
1352 {
1353 StringBuilder sb = new StringBuilder();
1354
1355 sb.AppendLine("New System Configuration");
1356 sb.AppendLine("=============================");
1357 sb.AppendLine();
1358 sb.AppendLine("A new system configuration is available.");
1359 sb.AppendLine("It has been set to simplified configuration, to not stop processing.");
1360 sb.AppendLine("You should review the configuration however, as soon as possible.");
1361 sb.AppendLine();
1362 sb.Append("[Click here to review the new system configuration](http");
1363
1365 sb.Append('s');
1366
1367 sb.Append("://");
1369 sb.Append(Configuration.Resource);
1370 sb.AppendLine(").");
1371
1372 Log.Alert(sb.ToString());
1373 }
1374 }
1375 }
1376 catch (Exception ex)
1377 {
1378 Log.Exception(ex);
1379 }
1380 finally
1381 {
1382 if (!(webServer is null))
1383 {
1384 webServer.ResourceOverride = null;
1385 webServer.ResourceOverrideFilter = null;
1386 }
1387
1388 if (!(startingServer is null))
1389 {
1390 await startingServer.ReleaseMutex();
1391 startingServer.Dispose();
1392 startingServer = null;
1393 }
1394
1395 if (xmppClient.State != XmppState.Connected)
1396 await xmppClient.Connect();
1397 }
1398 }
1399 catch (Exception ex)
1400 {
1401 Log.Exception(ex);
1402
1403 if (!(startingServer is null))
1404 {
1405 await startingServer.ReleaseMutex();
1406 startingServer.Dispose();
1407 startingServer = null;
1408 }
1409
1410 if (!(gatewayRunning is null))
1411 {
1412 await gatewayRunning.ReleaseMutex();
1413 gatewayRunning.Dispose();
1414 gatewayRunning = null;
1415 }
1416
1417 ExceptionDispatchInfo.Capture(ex).Throw();
1418 }
1419
1420 return true;
1421 }
1422
1423 private static Task ProvisionedMeteringNode_QrCodeUrlRequested(object Sender, GetQrCodeUrlEventArgs e)
1424 {
1425 StringBuilder Link = new StringBuilder();
1426 Link.Append("https://");
1427
1428 if (string.IsNullOrEmpty(Domain))
1429 Link.Append(XmppClient.Domain);
1430 else
1431 Link.Append(Domain);
1432
1433 Link.Append("/QR/");
1434 Link.Append(WebUtility.UrlEncode(e.Text));
1435
1436 e.Url = Link.ToString();
1437
1438 return Task.CompletedTask;
1439 }
1440
1441 private static async Task StartingMd(HttpRequest Request, HttpResponse Response)
1442 {
1443 if (string.IsNullOrEmpty(webServer?.ResourceOverride))
1444 throw new TemporaryRedirectException("/");
1445
1446 string Markdown;
1447
1448 try
1449 {
1450 Markdown = await Resources.ReadAllTextAsync(Path.Combine(rootFolder, "Starting.md"));
1451 }
1452 catch (Exception)
1453 {
1454 StringBuilder sb = new StringBuilder();
1455
1456 sb.AppendLine("Title: Starting");
1457 sb.AppendLine("Description: The starting page will be displayed while the service is being started.");
1458 sb.AppendLine("Cache-Control: max-age=0, no-cache, no-store");
1459 sb.AppendLine("Refresh: 2");
1460 sb.AppendLine();
1461 sb.AppendLine("============================================================================================================================================");
1462 sb.AppendLine();
1463 sb.AppendLine("Starting Service");
1464 sb.AppendLine("====================");
1465 sb.AppendLine();
1466 sb.AppendLine("Please wait while the service is being started. This page will update automatically.");
1467
1468 Markdown = sb.ToString();
1469 }
1470
1471 Variables v = Request.Session ?? new Variables();
1472 MarkdownSettings Settings = new MarkdownSettings(emoji1_24x24, true, v);
1473 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(Markdown, Settings);
1474 string Html = await Doc.GenerateHTML();
1475
1476 Response.ContentType = "text/html; charset=utf-8";
1477 await Response.Write(System.Text.Encoding.UTF8.GetBytes(Html));
1478 await Response.SendResponse();
1479 }
1480
1481 private static Task GoToDefaultPage(HttpRequest Request, HttpResponse Response)
1482 {
1483 if (TryGetDefaultPage(Request, out string DefaultPage))
1484 throw new TemporaryRedirectException(DefaultPage);
1485 else
1486 throw new NotFoundException("No default page defined.");
1487 }
1488
1489 private class ModuleStartOrder : IComparer<IModule>
1490 {
1491 private readonly DependencyOrder dependencyOrder = new DependencyOrder();
1492
1493 public int Compare(IModule x, IModule y)
1494 {
1495 int c1 = this.ModuleCategory(x);
1496 int c2 = this.ModuleCategory(y);
1497
1498 int i = c1 - c2;
1499 if (i != 0)
1500 return i;
1501
1502 return this.dependencyOrder.Compare(x, y);
1503 }
1504
1505 private int ModuleCategory(IModule x)
1506 {
1507 if (x is Persistence.LifeCycle.DatabaseModule)
1508 return 1;
1509 else if (x is Runtime.Transactions.TransactionModule)
1510 return 2;
1511 else if (x is NetworkingModule)
1512 return int.MaxValue;
1513 else
1514 return 3;
1515 }
1516 }
1517
1518 private static async Task RepairIfInproperShutdown()
1519 {
1520 IDatabaseProvider DatabaseProvider = Database.Provider;
1521 Type ProviderType = DatabaseProvider.GetType();
1522 PropertyInfo AutoRepairReportFolder = ProviderType.GetProperty("AutoRepairReportFolder");
1523 AutoRepairReportFolder?.SetValue(DatabaseProvider, Path.Combine(AppDataFolder, "Backup"));
1524
1525 MethodInfo MI = ProviderType.GetMethod("RepairIfInproperShutdown", new Type[] { typeof(string) });
1526
1527 if (!(MI is null))
1528 {
1529 Task T = MI.Invoke(DatabaseProvider, new object[] { AppDataFolder + "Transforms" + Path.DirectorySeparatorChar + "DbStatXmlToHtml.xslt" }) as Task;
1530
1531 if (T is Task<string[]> StringArrayTask)
1532 DatabaseConfiguration.RepairedCollections = await StringArrayTask;
1533 else if (!(T is null))
1534 await T;
1535 }
1536 }
1537
1538 private static async Task WriteWebServerOpenPorts()
1539 {
1540 StringBuilder sb = new StringBuilder();
1541
1542 foreach (int Port in webServer.OpenPorts)
1543 sb.AppendLine(Port.ToString());
1544
1545 try
1546 {
1547 await Resources.WriteAllTextAsync(appDataFolder + "Ports.txt", sb.ToString());
1548 }
1549 catch (Exception ex)
1550 {
1551 Log.Exception(ex);
1552 }
1553 }
1554
1555 private static void Assert_UnauthorizedAccess(object Sender, UnauthorizedAccessEventArgs e)
1556 {
1557 DateTime Now = DateTime.Now;
1558 string Key = e.Trace.ToString();
1559
1560 lock (lastUnauthorizedAccess)
1561 {
1562 if (lastUnauthorizedAccess.TryGetValue(Key, out DateTime TP) && (Now - TP).TotalHours < 1)
1563 return;
1564
1565 lastUnauthorizedAccess[Key] = Now;
1566 }
1567
1568 StringBuilder Markdown = new StringBuilder();
1569
1570 Markdown.AppendLine("Unauthorized access detected and prevented.");
1571 Markdown.AppendLine("===============================================");
1572 Markdown.AppendLine();
1573 Markdown.AppendLine("| Details ||");
1574 Markdown.AppendLine("|:---|:---|");
1575 Markdown.Append("| Method | `");
1576 Markdown.Append(e.Method.Name);
1577 Markdown.AppendLine("` |");
1578 Markdown.Append("| Type | `");
1579 Markdown.Append(e.Type.FullName);
1580 Markdown.AppendLine("` |");
1581 Markdown.Append("| Assembly | `");
1582 Markdown.Append(e.Assembly.GetName().Name);
1583 Markdown.AppendLine("` |");
1584 Markdown.Append("| Date | ");
1585 Markdown.Append(MarkdownDocument.Encode(Now.ToShortDateString()));
1586 Markdown.AppendLine(" |");
1587 Markdown.Append("| Time | ");
1588 Markdown.Append(MarkdownDocument.Encode(Now.ToLongTimeString()));
1589 Markdown.AppendLine(" |");
1590 Markdown.AppendLine();
1591 Markdown.AppendLine("Stack Trace:");
1592 Markdown.AppendLine();
1593 Markdown.AppendLine("```");
1594 Markdown.AppendLine(Log.CleanStackTrace(e.Trace.ToString()));
1595 Markdown.AppendLine("```");
1596
1597 SendNotification(Markdown.ToString());
1598 }
1599
1600 private static void CheckContentFiles(string ManifestFileName, Dictionary<string, CopyOptions> ContentOptions)
1601 {
1602 try
1603 {
1604 XmlDocument Doc = new XmlDocument()
1605 {
1606 PreserveWhitespace = true
1607 };
1608 Doc.Load(ManifestFileName);
1609
1610 if (!(Doc.DocumentElement is null) &&
1611 Doc.DocumentElement.LocalName == "Module" &&
1612 Doc.DocumentElement.NamespaceURI == "http://waher.se/Schema/ModuleManifest.xsd")
1613 {
1614 CheckContentFiles(Doc.DocumentElement, runtimeFolder, runtimeFolder, appDataFolder, ContentOptions);
1615 }
1616 }
1617 catch (Exception ex)
1618 {
1619 Log.Exception(ex, ManifestFileName);
1620 }
1621 }
1622
1623 private enum CopyOptions
1624 {
1625 IfNewer,
1626 Always
1627 }
1628
1629 private static void CheckContentFiles(XmlElement Element, string RuntimeFolder, string RuntimeSubfolder, string AppDataSubFolder,
1630 Dictionary<string, CopyOptions> ContentOptions)
1631 {
1632 bool AppDataFolderChecked = false;
1633
1634 foreach (XmlNode N in Element.ChildNodes)
1635 {
1636 if (N is XmlElement E)
1637 {
1638 switch (E.LocalName)
1639 {
1640 case "Folder":
1641 string Name = XML.Attribute(E, "name");
1642 CheckContentFiles(E, RuntimeFolder, Path.Combine(RuntimeSubfolder, Name), Path.Combine(AppDataSubFolder, Name),
1643 ContentOptions);
1644 break;
1645
1646 case "Content":
1647 Name = XML.Attribute(E, "fileName");
1648 CopyOptions CopyOptions = XML.Attribute(E, "copy", CopyOptions.IfNewer);
1649
1650 string s = Path.Combine(RuntimeSubfolder, Name);
1651 if (!File.Exists(s))
1652 {
1653 s = Path.Combine(RuntimeFolder, Name);
1654 if (!File.Exists(s))
1655 break;
1656 }
1657
1658 if (!AppDataFolderChecked)
1659 {
1660 AppDataFolderChecked = true;
1661
1662 if (!Directory.Exists(AppDataSubFolder))
1663 Directory.CreateDirectory(AppDataSubFolder);
1664 }
1665
1666 string s2 = Path.Combine(AppDataSubFolder, Name);
1667
1668 if (CopyOptions == CopyOptions.Always || !File.Exists(s2))
1669 {
1670 File.Copy(s, s2, true);
1671 ContentOptions[s2] = CopyOptions;
1672 }
1673 else
1674 {
1675 DateTime TP = File.GetLastWriteTime(s);
1676 DateTime TP2 = File.GetLastWriteTime(s2);
1677
1678 if (TP > TP2 &&
1679 (!ContentOptions.TryGetValue(s2, out CopyOptions CopyOptions2) ||
1680 CopyOptions2 != CopyOptions.Always))
1681 {
1682 File.Copy(s, s2, true);
1683 ContentOptions[s2] = CopyOptions;
1684 }
1685 }
1686 break;
1687 }
1688 }
1689 }
1690 }
1691
1692 private static void CheckInstallUtilityFiles(string ManifestFileName)
1693 {
1694 try
1695 {
1696 XmlDocument Doc = new XmlDocument()
1697 {
1698 PreserveWhitespace = true
1699 };
1700 Doc.Load(ManifestFileName);
1701
1702 if (!(Doc.DocumentElement is null) &&
1703 Doc.DocumentElement.LocalName == "Module" &&
1704 Doc.DocumentElement.NamespaceURI == "http://waher.se/Schema/ModuleManifest.xsd")
1705 {
1706 string InstallUtilityFolder = Path.Combine(runtimeFolder, "InstallUtility");
1707 bool NoticeLogged = false;
1708
1709 if (!Directory.Exists(InstallUtilityFolder))
1710 Directory.CreateDirectory(InstallUtilityFolder);
1711
1712 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
1713 {
1714 if (N is XmlElement E)
1715 {
1716 switch (E.LocalName)
1717 {
1718 case "Assembly":
1719 string Name = XML.Attribute(E, "fileName");
1720 CopyOptions CopyOptions = XML.Attribute(E, "copy", CopyOptions.IfNewer);
1721
1722 string s = Path.Combine(runtimeFolder, Name);
1723 if (!File.Exists(s))
1724 break;
1725
1726 string s2 = Path.Combine(InstallUtilityFolder, Name);
1727
1728 if (CopyOptions == CopyOptions.IfNewer && File.Exists(s2))
1729 {
1730 DateTime TP = File.GetLastWriteTime(s);
1731 DateTime TP2 = File.GetLastWriteTime(s2);
1732
1733 if (TP <= TP2)
1734 break;
1735 }
1736
1737 if (!NoticeLogged)
1738 {
1739 NoticeLogged = true;
1740 Log.Notice("Copying Installation Utility executable files to InstallUtility subfolder.");
1741 }
1742
1743 File.Copy(s, s2, true);
1744 break;
1745 }
1746 }
1747 }
1748 }
1749 }
1750 catch (Exception ex)
1751 {
1752 Log.Exception(ex, ManifestFileName);
1753 }
1754 }
1755
1756 internal static bool ConsoleOutput => consoleOutput;
1757
1758 internal static async Task ConfigureXmpp(XmppConfiguration Configuration)
1759 {
1760 xmppCredentials = Configuration.GetCredentials();
1761 xmppClient = new XmppClient(xmppCredentials, "en", typeof(Gateway).Assembly);
1762 xmppClient.OnValidateSender += XmppClient_OnValidateSender;
1763 Types.SetModuleParameter("XMPP", xmppClient);
1764
1765 if (xmppCredentials.Sniffer)
1766 {
1767 ISniffer Sniffer;
1768
1769 if (consoleOutput)
1770 {
1771 Sniffer = new ConsoleOutSniffer(BinaryPresentationMethod.ByteCount, LineEnding.PadWithSpaces);
1772 xmppClient.Add(Sniffer);
1773 }
1774
1775 Sniffer = new XmlFileSniffer(appDataFolder + "XMPP" + Path.DirectorySeparatorChar +
1776 "XMPP Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml",
1777 appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
1778 7, BinaryPresentationMethod.ByteCount);
1779 xmppClient.Add(Sniffer);
1780 }
1781
1782 if (!string.IsNullOrEmpty(xmppCredentials.Events))
1783 {
1784 string s = xmppCredentials.Events;
1785 int i = s.IndexOf('.');
1786 if (i > 0)
1787 s = s.Substring(i + 1);
1788
1789 if (!IsDomain(s, true))
1790 {
1791 Log.Register(new EventFilter("XMPP Event Filter",
1792 new XmppEventSink("XMPP Event Sink", xmppClient, xmppCredentials.Events, false),
1793 EventType.Error));
1794 }
1795 }
1796
1797 if (!string.IsNullOrEmpty(xmppCredentials.ThingRegistry))
1798 {
1799 thingRegistryClient = new ThingRegistryClient(xmppClient, xmppCredentials.ThingRegistry);
1800 thingRegistryClient.Claimed += ThingRegistryClient_Claimed;
1801 thingRegistryClient.Disowned += ThingRegistryClient_Disowned;
1802 thingRegistryClient.Removed += ThingRegistryClient_Removed;
1803 }
1804
1805 if (!string.IsNullOrEmpty(xmppCredentials.Provisioning))
1806 provisioningClient = new ProvisioningClient(xmppClient, xmppCredentials.Provisioning);
1807 else
1808 provisioningClient = null;
1809
1810 scheduler.Add(DateTime.Now.AddMinutes(1), CheckConnection, null);
1811
1812 xmppClient.OnStateChanged += XmppClient_OnStateChanged;
1813
1814 ibbClient = new Networking.XMPP.InBandBytestreams.IbbClient(xmppClient, MaxChunkSize);
1815 Types.SetModuleParameter("IBB", ibbClient);
1816
1817 socksProxy = new Socks5Proxy(xmppClient);
1818 Types.SetModuleParameter("SOCKS5", socksProxy);
1819
1820 sensorClient = new SensorClient(xmppClient);
1821 controlClient = new ControlClient(xmppClient);
1822 concentratorClient = new ConcentratorClient(xmppClient);
1823 synchronizationClient = new SynchronizationClient(xmppClient);
1824 pepClient = new PepClient(xmppClient, XmppConfiguration.Instance.PubSub);
1825
1826 if (!string.IsNullOrEmpty(XmppConfiguration.Instance.LegalIdentities))
1827 {
1828 contractsClient = new ContractsClient(xmppClient, XmppConfiguration.Instance.LegalIdentities);
1829 contractsClient.SetKeySettingsInstance(string.Empty, true);
1830
1831 await contractsClient.LoadKeys(true);
1832 }
1833 else
1834 contractsClient = null;
1835
1836 if (!string.IsNullOrEmpty(XmppConfiguration.Instance.MultiUserChat))
1837 mucClient = new MultiUserChatClient(xmppClient, XmppConfiguration.Instance.MultiUserChat);
1838 else
1839 mucClient = null;
1840
1841 if (!string.IsNullOrEmpty(XmppConfiguration.Instance.SoftwareUpdates))
1842 {
1843 string PackagesFolder = Path.Combine(appDataFolder, "Packages");
1844 if (!Directory.Exists(PackagesFolder))
1845 Directory.CreateDirectory(PackagesFolder);
1846
1847 softwareUpdateClient = new SoftwareUpdateClient(xmppClient, XmppConfiguration.Instance.SoftwareUpdates, PackagesFolder);
1848 }
1849 else
1850 softwareUpdateClient = null;
1851
1852 mailClient = new MailClient(xmppClient);
1853 mailClient.MailReceived += MailClient_MailReceived;
1854 }
1855
1856 internal static async Task ConfigureDomain(DomainConfiguration Configuration)
1857 {
1858 int i, c = Configuration.AlternativeDomains?.Length ?? 0;
1859
1860 domain = Configuration.Domain;
1861 alternativeDomains = new CaseInsensitiveString[c];
1862
1863 for (i = 0; i < c; i++)
1864 alternativeDomains[i] = Configuration.AlternativeDomains[i];
1865
1866 if (Configuration.UseDomainName)
1867 {
1868 if (Configuration.DynamicDns)
1869 {
1870 await Configuration.CheckDynamicIp();
1871
1872 if (Configuration.DynDnsInterval > 0)
1873 {
1874 if (checkIp > DateTime.MinValue)
1875 {
1876 scheduler.Remove(checkIp);
1877 checkIp = DateTime.MinValue;
1878 }
1879
1880 checkIp = scheduler.Add(DateTime.Now.AddSeconds(Configuration.DynDnsInterval), CheckIp, Configuration);
1881 }
1882 }
1883
1884 if (Configuration.UseEncryption &&
1885 !(Configuration.Certificate is null) &&
1886 !(Configuration.PrivateKey is null))
1887 {
1888 await UpdateCertificate(Configuration);
1889
1890 if (checkCertificate > DateTime.MinValue)
1891 {
1892 scheduler.Remove(checkCertificate);
1893 checkCertificate = DateTime.MinValue;
1894 }
1895
1896 checkCertificate = scheduler.Add(DateTime.Now.AddHours(0.5 + NextDouble()), CheckCertificate, Configuration);
1897 }
1898 else
1899 certificate = null;
1900 }
1901 else
1902 certificate = null;
1903
1905 Users.Register(ComputeUserPasswordHash, "DIGEST-SHA3-256", LoginAuditor, domain, true);
1906
1907 await Privileges.LoadAll();
1908 await Roles.LoadAll();
1909 }
1910
1917 public static byte[] ComputeUserPasswordHash(string UserName, string Password)
1918 {
1919 SHA3_256 H = new SHA3_256();
1920 return H.ComputeVariable(System.Text.Encoding.UTF8.GetBytes(UserName + ":" + domain + ":" + Password));
1921 }
1922
1923 internal static async Task<bool> UpdateCertificate(DomainConfiguration Configuration)
1924 {
1925 try
1926 {
1927 if (!(Configuration.PFX is null))
1928 certificate = new X509Certificate2(Configuration.PFX, Configuration.Password);
1929 else
1930 {
1931 RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
1932 RSA.ImportCspBlob(Configuration.PrivateKey);
1933
1934 certificate = new X509Certificate2(Configuration.Certificate)
1935 {
1936 PrivateKey = RSA
1937 };
1938 }
1939
1940 try
1941 {
1942 webServer?.UpdateCertificate(Certificate);
1943
1944 await OnNewCertificate.Raise(typeof(Gateway), new Events.CertificateEventArgs(certificate));
1945 }
1946 catch (Exception ex)
1947 {
1948 Log.Exception(ex);
1949 }
1950
1951 return true;
1952 }
1953 catch (Exception ex)
1954 {
1955 Log.Exception(ex);
1956 }
1957
1958 return false;
1959 }
1960
1961 private static async void CheckCertificate(object P)
1962 {
1963 DomainConfiguration Configuration = (DomainConfiguration)P;
1964 DateTime Now = DateTime.Now;
1965
1966 try
1967 {
1968 if (Now.AddDays(50) >= certificate.NotAfter)
1969 {
1970 Log.Notice("Updating certificate");
1971
1972 if (await Configuration.CreateCertificate())
1973 {
1974 Log.Notice("Certificate created.");
1975 if (!await UpdateCertificate(Configuration))
1976 Log.Error("Unable to update gatetway with new certificate.");
1977 }
1978 else
1979 {
1980 int DaysLeft = (int)Math.Round((certificate.NotAfter - Now.Date).TotalDays);
1981
1982 if (DaysLeft < 2)
1983 Log.Emergency("Unable to generate new certificate.", domain);
1984 else if (DaysLeft < 5)
1985 Log.Alert("Unable to generate new certificate.", domain);
1986 else if (DaysLeft < 10)
1987 Log.Critical("Unable to generate new certificate.", domain);
1988 else if (DaysLeft < 20)
1989 Log.Error("Unable to generate new certificate.", domain);
1990 else
1991 Log.Warning("Unable to generate new certificate.", domain);
1992 }
1993 }
1994 }
1995 catch (Exception ex)
1996 {
1997 int DaysLeft = (int)Math.Round((certificate.NotAfter - Now.Date).TotalDays);
1998
1999 if (DaysLeft < 2)
2000 Log.Emergency(ex, domain);
2001 else if (DaysLeft < 5)
2002 Log.Alert(ex, domain);
2003 else
2004 Log.Exception(ex);
2005 }
2006 finally
2007 {
2008 checkCertificate = scheduler.Add(DateTime.Now.AddDays(0.5 + NextDouble()), CheckCertificate, Configuration);
2009 }
2010 }
2011
2015 public static event EventHandlerAsync<Events.CertificateEventArgs> OnNewCertificate = null;
2016
2017 private static async void CheckIp(object P)
2018 {
2019 DomainConfiguration Configuration = (DomainConfiguration)P;
2020
2021 try
2022 {
2023 await Configuration.CheckDynamicIp();
2024 }
2025 catch (Exception ex)
2026 {
2027 Log.Exception(ex);
2028 }
2029 finally
2030 {
2031 checkIp = scheduler.Add(DateTime.Now.AddSeconds(Configuration.DynDnsInterval), CheckIp, Configuration);
2032 }
2033 }
2034
2035 private static async void DeleteOldDataSourceEvents(object P)
2036 {
2037 try
2038 {
2039 await MeteringTopology.DeleteOldEvents(TimeSpan.FromDays(7));
2040 }
2041 catch (Exception ex)
2042 {
2043 Log.Exception(ex);
2044 }
2045 finally
2046 {
2047 ScheduleEvent(DeleteOldDataSourceEvents, DateTime.Today.AddDays(1).AddHours(4), null);
2048 }
2049 }
2050
2055
2059 public static event GetDataSources GetDataSources = null;
2060
2064 private static void Initialize()
2065 {
2066 string Folder = Assembly.GetExecutingAssembly().Location;
2067 if (string.IsNullOrEmpty(Folder))
2068 Folder = AppDomain.CurrentDomain.BaseDirectory;
2069
2070 runtimeFolder = Path.GetDirectoryName(Folder);
2071
2072 Directory.SetCurrentDirectory(runtimeFolder);
2073
2074 TypesLoader.Initialize(runtimeFolder, (FileName) =>
2075 {
2076 FileName = Path.GetFileName(FileName).ToLower();
2077 if (FileName.StartsWith("api-ms-win-") ||
2078 FileName.StartsWith("system.") ||
2079 FileName.StartsWith("microsoft.") ||
2080 FileName.StartsWith("windows.") ||
2081 FileName.StartsWith("waher.client.") ||
2082 FileName.StartsWith("waher.utility.") ||
2083 FileName.StartsWith("mscordaccore_x86_x86_") ||
2084 FileName.StartsWith("sos_x86_x86_"))
2085 {
2086 return false;
2087 }
2088
2089 switch (FileName)
2090 {
2091 case "clrcompression.dll":
2092 case "clretwrc.dll":
2093 case "clrgc.dll":
2094 case "clrjit.dll":
2095 case "coreclr.dll":
2096 case "dbgshim.dll":
2097 case "hostpolicy.dll":
2098 case "hostfxr.dll":
2099 case "libegl.dll":
2100 case "libglesv2.dll":
2101 case "libskiasharp.dll":
2102 case "libzstd.dll":
2103 case "mongocrypt.dll":
2104 case "mscordaccore.dll":
2105 case "mscordbi.dll":
2106 case "mscorlib.dll":
2107 case "mscorrc.debug.dll":
2108 case "mscorrc.dll":
2109 case "msquic.dll":
2110 case "netstandard.dll":
2111 case "snappy32.dll":
2112 case "snappy64.dll":
2113 case "snappier.dll":
2114 case "sni.dll":
2115 case "sos.dll":
2116 case "sos.netcore.dll":
2117 case "ucrtbase.dll":
2118 case "windowsbase.dll":
2119 case "waher.persistence.fileslw.dll":
2120 case "waher.persistence.serialization.dll":
2121 case "zstdsharp.dll":
2122 return false;
2123 }
2124
2125 return true;
2126 });
2127 }
2128
2129 private static bool CopyFile(string From, string To, bool OnlyIfNewer)
2130 {
2131 if (From == To)
2132 return false;
2133
2134 if (!File.Exists(From))
2135 return false;
2136
2137 if (OnlyIfNewer && File.Exists(To))
2138 {
2139 DateTime ToTP = File.GetLastWriteTimeUtc(To);
2140 DateTime FromTP = File.GetLastWriteTimeUtc(From);
2141
2142 if (ToTP >= FromTP)
2143 return false;
2144 }
2145
2146 File.Copy(From, To, true);
2147
2148 return true;
2149 }
2150
2151 private static void CopyFolder(string From, string To, string Mask, bool OnlyIfNewer)
2152 {
2153 if (Directory.Exists(From))
2154 {
2155 if (!Directory.Exists(To))
2156 Directory.CreateDirectory(To);
2157
2158 string[] Files = Directory.GetFiles(From, Mask, SearchOption.TopDirectoryOnly);
2159
2160 foreach (string File in Files)
2161 {
2162 string FileName = Path.GetFileName(File);
2163 CopyFile(File, Path.Combine(To, FileName), OnlyIfNewer);
2164 }
2165 }
2166 }
2167
2168 private static void CopyFolders(string From, string To, bool OnlyIfNewer)
2169 {
2170 if (Directory.Exists(From))
2171 {
2172 CopyFolder(From, To, "*.*", OnlyIfNewer);
2173
2174 string[] Folders = Directory.GetDirectories(From, "*.*", SearchOption.TopDirectoryOnly);
2175
2176 foreach (string Folder in Folders)
2177 {
2178 string FolderName = Path.GetFileName(Folder);
2179 CopyFolders(Folder, Path.Combine(To, FolderName), OnlyIfNewer);
2180 }
2181 }
2182 }
2183
2187 public static async Task Stop()
2188 {
2189 if (stopped)
2190 {
2191 Log.Notice("Request to stop Gateway, but Gateway already stopped.");
2192 return;
2193 }
2194
2195 Log.Informational("Server shutting down.");
2196
2197 bool StopInternalProvider = !(internalProvider is null) && Database.Provider != internalProvider;
2198
2199 stopped = true;
2200 try
2201 {
2202 scheduler?.Dispose();
2203 scheduler = null;
2204
2205 await Script.Threading.Functions.Background.TerminateTasks(10000);
2206 await Types.StopAllModules();
2207
2208 Database.CollectionRepaired -= Database_CollectionRepaired;
2209
2210 if (StopInternalProvider)
2211 await internalProvider.Stop();
2212
2213 if (!(startingServer is null))
2214 {
2215 await startingServer.ReleaseMutex();
2216 startingServer.Dispose();
2217 startingServer = null;
2218 }
2219
2220 if (!(gatewayRunning is null))
2221 {
2222 await gatewayRunning.ReleaseMutex();
2223 gatewayRunning.Dispose();
2224 gatewayRunning = null;
2225 }
2226
2227 if (!(configurations is null))
2228 {
2229 foreach (SystemConfiguration Configuration in configurations)
2230 {
2231 if (Configuration is IDisposable D)
2232 {
2233 try
2234 {
2235 D.Dispose();
2236 }
2237 catch (Exception ex)
2238 {
2239 Log.Exception(ex);
2240 }
2241 }
2242 }
2243
2244 configurations = null;
2245 }
2246
2247 ibbClient?.Dispose();
2248 ibbClient = null;
2249
2250 httpxProxy?.Dispose();
2251 httpxProxy = null;
2252
2253 httpxServer?.Dispose();
2254 httpxServer = null;
2255
2256 provisioningClient?.Dispose();
2257 provisioningClient = null;
2258
2259 thingRegistryClient?.Dispose();
2260 thingRegistryClient = null;
2261
2262 concentratorServer?.Dispose();
2263 concentratorServer = null;
2264
2265 avatarClient?.Dispose();
2266 avatarClient = null;
2267
2268 sensorClient?.Dispose();
2269 sensorClient = null;
2270
2271 controlClient?.Dispose();
2272 controlClient = null;
2273
2274 concentratorClient?.Dispose();
2275 concentratorClient = null;
2276
2277 synchronizationClient?.Dispose();
2278 synchronizationClient = null;
2279
2280 pepClient?.Dispose();
2281 pepClient = null;
2282
2283 mucClient?.Dispose();
2284 mucClient = null;
2285
2286 mailClient?.Dispose();
2287 mailClient = null;
2288
2289 contractsClient?.Dispose();
2290 contractsClient = null;
2291
2292 softwareUpdateClient?.Dispose();
2293 softwareUpdateClient = null;
2294
2295 if (!(xmppClient is null))
2296 {
2297 await xmppClient.OfflineAndDisposeAsync();
2298 xmppClient = null;
2299 }
2300
2301 coapEndpoint?.Dispose();
2302 coapEndpoint = null;
2303
2304 if (!(webServer is null))
2305 {
2306 foreach (ISniffer Sniffer in webServer.Sniffers)
2307 {
2308 webServer.Remove(Sniffer);
2309
2310 if (Sniffer is IDisposable Disposable)
2311 Disposable.Dispose();
2312 }
2313
2314 webServer.Dispose();
2315 webServer = null;
2316 }
2317
2318 root = null;
2319
2320 if (exportExceptions)
2321 {
2322 lock (exceptionFile)
2323 {
2324 exportExceptions = false;
2325
2326 exceptionFile.WriteLine(new string('-', 80));
2327 exceptionFile.Write("End of export: ");
2328 exceptionFile.WriteLine(DateTime.Now.ToString());
2329
2330 exceptionFile.Flush();
2331 exceptionFile.Close();
2332 }
2333
2334 exceptionFile = null;
2335 }
2336 }
2337 finally
2338 {
2339 Persistence.LifeCycle.DatabaseModule.Flush().Wait(60000);
2340
2341 if (StopInternalProvider)
2342 internalProvider.Flush().Wait(60000);
2343 }
2344 }
2345
2349 public static X509Certificate2 Certificate => certificate;
2350
2354 public static CaseInsensitiveString Domain => domain;
2355
2359 public static CaseInsensitiveString[] AlternativeDomains => alternativeDomains;
2360
2364 public static string InstanceName => instance;
2365
2369 public static string AppDataFolder => appDataFolder;
2370
2374 public static string RuntimeFolder => runtimeFolder;
2375
2379 public static string RootFolder => rootFolder;
2380
2384 public static HttpFolderResource Root => root;
2385
2389 public static string ApplicationName
2390 {
2391 get => applicationName;
2392 internal set => applicationName = value;
2393 }
2394
2398 public static Emoji1LocalFiles Emoji1_24x24 => emoji1_24x24;
2399
2403 public static bool Configuring => configuring;
2404
2408 public static IDatabaseProvider InternalDatabase => internalProvider;
2409
2413 public static string ConfigFilePath => Path.Combine(appDataFolder, GatewayConfigLocalFileName);
2414
2420 public static int[] GetConfigPorts(string Protocol)
2421 {
2422 List<int> Result = new List<int>();
2423
2424 foreach (KeyValuePair<string, int> P in ports)
2425 {
2426 if (P.Key == Protocol)
2427 Result.Add(P.Value);
2428 }
2429
2430 return Result.ToArray();
2431 }
2432
2437 public static string[] GetProtocols()
2438 {
2439 SortedDictionary<string, bool> Protocols = new SortedDictionary<string, bool>();
2440
2441 foreach (KeyValuePair<string, int> P in ports)
2442 Protocols[P.Key] = true;
2443
2444 string[] Result = new string[Protocols.Count];
2445 Protocols.Keys.CopyTo(Result, 0);
2446
2447 return Result;
2448 }
2449
2455 public static Task Terminate()
2456 {
2457 EventHandlerAsync h = OnTerminate ?? throw new InvalidOperationException("No OnTerminate event handler set.");
2458 return h.Raise(instance, EventArgs.Empty, false);
2459 }
2460
2465 public static event EventHandlerAsync OnTerminate = null;
2466
2473 public static bool TryGetDefaultPage(HttpRequest Request, out string DefaultPage)
2474 {
2475 return TryGetDefaultPage(Request.Header.Host?.Value ?? string.Empty, out DefaultPage);
2476 }
2477
2484 public static bool TryGetDefaultPage(string Host, out string DefaultPage)
2485 {
2486 if (defaultPageByHostName.TryGetValue(Host, out DefaultPage))
2487 return true;
2488
2489 if (Host.StartsWith("www.", StringComparison.CurrentCultureIgnoreCase) && defaultPageByHostName.TryGetValue(Host.Substring(4), out DefaultPage))
2490 return true;
2491
2492 if (defaultPageByHostName.TryGetValue(string.Empty, out DefaultPage))
2493 return true;
2494
2495 DefaultPage = string.Empty;
2496 return false;
2497 }
2498
2499 internal static void SetDefaultPages(params KeyValuePair<string, string>[] DefaultPages)
2500 {
2501 Dictionary<string, string> List = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
2502
2503 foreach (KeyValuePair<string, string> P in DefaultPages)
2504 List[P.Key] = P.Value;
2505
2506 defaultPageByHostName = List;
2507 }
2508
2509 #endregion
2510
2511 #region XMPP
2512
2513 private static Task XmppClient_OnValidateSender(object Sender, ValidateSenderEventArgs e)
2514 {
2515 RosterItem Item;
2516 string BareJid = e.FromBareJID.ToLower();
2517
2518 if (string.IsNullOrEmpty(BareJid) ||
2519 (!(xmppClient is null) &&
2520 (BareJid == xmppClient.Domain.ToLower() ||
2521 BareJid == xmppClient.BareJID.ToLower())))
2522 {
2523 e.Accept();
2524 }
2525 else if (BareJid.IndexOf('@') > 0 &&
2526 (xmppClient is null ||
2527 (Item = xmppClient.GetRosterItem(BareJid)) is null ||
2528 Item.State == SubscriptionState.None ||
2529 Item.State == SubscriptionState.Remove ||
2530 Item.State == SubscriptionState.Unknown))
2531 {
2532 foreach (XmlNode N in e.Stanza.ChildNodes)
2533 {
2534 if (N.LocalName == "query" && N.NamespaceURI == XmppClient.NamespaceServiceDiscoveryInfo)
2535 return Task.CompletedTask;
2536 }
2537
2538 e.Reject();
2539 }
2540
2541 return Task.CompletedTask;
2542 }
2543
2544 private static async void CheckConnection(object State)
2545 {
2546 try
2547 {
2548 if (!stopped && !NetworkingModule.Stopping)
2549 {
2550 scheduler.Add(DateTime.Now.AddMinutes(1), CheckConnection, null);
2551
2552 XmppState? State2 = xmppClient?.State;
2553 if (State2.HasValue &&
2554 (State2 == XmppState.Offline || State2 == XmppState.Error || State2 == XmppState.Authenticating) &&
2555 !(xmppClient is null))
2556 {
2557 try
2558 {
2559 await xmppClient.Reconnect();
2560 }
2561 catch (Exception ex)
2562 {
2563 Log.Exception(ex);
2564 }
2565 }
2566
2567 await CheckBackup();
2568
2569 await MinuteTick.Raise(null, EventArgs.Empty);
2570 }
2571 }
2572 catch (Exception ex)
2573 {
2574 Log.Exception(ex);
2575 }
2576 }
2577
2581 public static event EventHandlerAsync MinuteTick = null;
2582
2583 private static async Task XmppClient_OnStateChanged(object _, XmppState NewState)
2584 {
2585 switch (NewState)
2586 {
2587 case XmppState.Connected:
2588 connected = true;
2589
2590 MarkdownToHtmlConverter.BareJID = xmppClient.BareJID;
2591
2592 if (!registered && !(thingRegistryClient is null))
2593 {
2594 _ = Task.Run(async () =>
2595 {
2596 try
2597 {
2598 await Register();
2599 }
2600 catch (Exception ex)
2601 {
2602 Log.Exception(ex);
2603 }
2604 });
2605 }
2606
2607 if (!socksProxy.HasProxy)
2608 await socksProxy.StartSearch(null);
2609 break;
2610
2611 case XmppState.Offline:
2612 immediateReconnect = connected;
2613 connected = false;
2614
2615 if (immediateReconnect &&
2616 !(xmppClient is null) &&
2618 {
2619 await xmppClient.Reconnect();
2620 }
2621 break;
2622 }
2623 }
2624
2629 public static void CheckLocalLogin(HttpRequest Request)
2630 {
2631 Profiler Profiler = new Profiler();
2632 Profiler.Start();
2633
2634 ProfilerThread Thread = Profiler.CreateThread("Check Local Login", ProfilerThreadType.Sequential);
2635
2636 Thread.Start();
2637 try
2638 {
2639 Thread.NewState("Checks");
2640
2641 string RemoteEndpoint = Request.RemoteEndPoint;
2642 string From;
2643 int i;
2644 bool DoLog = false;
2645
2646 if (Request.Header.TryGetQueryParameter("debug", out string s) && CommonTypes.TryParse(s, out bool b))
2647 DoLog = b;
2648
2649 if (Request.Session is null)
2650 {
2651 if (DoLog)
2652 Log.Debug("No local login: No session.");
2653
2654 return;
2655 }
2656
2657 if (!Request.Session.TryGetVariable("from", out Variable v) || string.IsNullOrEmpty(From = v.ValueObject as string))
2658 From = "/";
2659
2660 if (!loopbackIntefaceAvailable && (XmppConfiguration.Instance is null || !XmppConfiguration.Instance.Complete || configuring))
2661 {
2662 LoginAuditor.Success("User logged in by default, since XMPP not configued and loopback interface not available.",
2663 string.Empty, Request.RemoteEndPoint, "Web");
2664
2665 Login.DoLogin(Request, From);
2666 return;
2667 }
2668
2669 if (DoLog)
2670 Log.Debug("Checking for local login from: " + RemoteEndpoint);
2671
2672 i = RemoteEndpoint.LastIndexOf(':');
2673 if (i < 0 || !int.TryParse(RemoteEndpoint.Substring(i + 1), out int Port))
2674 {
2675 if (DoLog)
2676 Log.Debug("Invalid port number: " + RemoteEndpoint);
2677
2678 return;
2679 }
2680
2681 if (!IPAddress.TryParse(RemoteEndpoint.Substring(0, i), out IPAddress Address))
2682 {
2683 if (DoLog)
2684 Log.Debug("Invalid IP Address: " + RemoteEndpoint);
2685
2686 return;
2687 }
2688
2689 if (!IsLocalCall(Address, Request, DoLog))
2690 {
2691 if (DoLog)
2692 Log.Debug("Request not local: " + RemoteEndpoint);
2693
2694 return;
2695 }
2696
2697#if !MONO
2698 if (XmppConfiguration.Instance is null || !XmppConfiguration.Instance.Complete || configuring)
2699#endif
2700 {
2701 LoginAuditor.Success("Local user logged in.", string.Empty, Request.RemoteEndPoint, "Web");
2702 Login.DoLogin(Request, From);
2703 return;
2704 }
2705
2706#if !MONO
2707 try
2708 {
2709 string FileName;
2710 string Arguments;
2711 bool WaitForExit;
2712
2713 switch (Environment.OSVersion.Platform)
2714 {
2715 case PlatformID.Win32S:
2716 case PlatformID.Win32Windows:
2717 case PlatformID.Win32NT:
2718 case PlatformID.WinCE:
2719 FileName = "netstat.exe";
2720 Arguments = "-a -n -o";
2721 WaitForExit = false;
2722
2723 Thread.NewState("netstat.exe");
2724 break;
2725
2726 case PlatformID.Unix:
2727 case PlatformID.MacOSX:
2728 FileName = "netstat";
2729 Arguments = "-anv -p tcp";
2730 WaitForExit = true;
2731
2732 Thread.NewState("netstat");
2733 break;
2734
2735 default:
2736 if (DoLog)
2737 Log.Debug("No local login: Unsupported operating system: " + Environment.OSVersion.Platform.ToString());
2738
2739 return;
2740 }
2741
2742 using (Process Proc = new Process())
2743 {
2744 ProcessStartInfo StartInfo = new ProcessStartInfo()
2745 {
2746 FileName = FileName,
2747 Arguments = Arguments,
2748 WindowStyle = ProcessWindowStyle.Hidden,
2749 UseShellExecute = false,
2750 RedirectStandardInput = true,
2751 RedirectStandardOutput = true,
2752 RedirectStandardError = true
2753 };
2754
2755 DateTime Start = DateTime.Now;
2756
2757 Proc.StartInfo = StartInfo;
2758 Proc.Start();
2759
2760 if (WaitForExit)
2761 {
2762 Proc.WaitForExit(5000);
2763 if (!Proc.HasExited)
2764 return;
2765 }
2766
2767 string Output = Proc.StandardOutput.ReadToEnd();
2768 DateTime Return = DateTime.Now;
2769
2770 Thread.Interval(Start, Return, "Shell");
2771
2772 if (DoLog)
2773 Log.Debug("Netstat output:\r\n\r\n" + Output);
2774
2775 if (Proc.ExitCode != 0)
2776 {
2777 Thread.Exception(new Exception("Exit code: " + Proc.ExitCode.ToString()));
2778
2779 if (DoLog)
2780 Log.Debug("Netstat exit code: " + Proc.ExitCode.ToString());
2781
2782 return;
2783 }
2784
2785 string[] Rows = Output.Split(CommonTypes.CRLF, StringSplitOptions.RemoveEmptyEntries);
2786
2787 foreach (string Row in Rows)
2788 {
2789 string[] Tokens = Regex.Split(Row, @"\s+");
2790
2791 switch (Environment.OSVersion.Platform)
2792 {
2793 case PlatformID.Win32S:
2794 case PlatformID.Win32Windows:
2795 case PlatformID.Win32NT:
2796 case PlatformID.WinCE:
2797 if (Tokens.Length < 6)
2798 break;
2799
2800 if (Tokens[1] != "TCP")
2801 break;
2802
2803 if (!SameEndpoint(Tokens[2], RemoteEndpoint))
2804 break;
2805
2806 if (Tokens[4] != "ESTABLISHED")
2807 break;
2808
2809 if (!int.TryParse(Tokens[5], out int PID))
2810 break;
2811
2812 Process P = Process.GetProcessById(PID);
2813 int CurrentSession = WTSGetActiveConsoleSessionId();
2814
2815 if (P.SessionId == CurrentSession)
2816 {
2817 LoginAuditor.Success("Local user logged in.", string.Empty, Request.RemoteEndPoint, "Web");
2818 Login.DoLogin(Request, From);
2819 return;
2820 }
2821 break;
2822
2823 case PlatformID.Unix:
2824 case PlatformID.MacOSX:
2825 if (Tokens.Length < 9)
2826 break;
2827
2828 if (Tokens[0] != "tcp4" && Tokens[0] != "tcp6")
2829 break;
2830
2831 if (!SameEndpoint(Tokens[4], RemoteEndpoint))
2832 break;
2833
2834 if (Tokens[5] != "ESTABLISHED")
2835 break;
2836
2837 if (!int.TryParse(Tokens[8], out PID))
2838 break;
2839
2840 P = Process.GetProcessById(PID);
2841 CurrentSession = Process.GetCurrentProcess().SessionId;
2842
2843 if (P.SessionId == CurrentSession)
2844 {
2845 LoginAuditor.Success("Local user logged in.", string.Empty, Request.RemoteEndPoint, "Web");
2846 Login.DoLogin(Request, From);
2847 return;
2848 }
2849 break;
2850
2851 default:
2852 if (DoLog)
2853 Log.Debug("No local login: Unsupported operating system: " + Environment.OSVersion.Platform.ToString());
2854
2855 return;
2856 }
2857 }
2858 }
2859 }
2860 catch (HttpException ex)
2861 {
2862 Thread.Exception(ex);
2863
2864 if (DoLog)
2865 Log.Exception(ex);
2866
2867 ExceptionDispatchInfo.Capture(ex).Throw();
2868 }
2869 catch (Exception ex)
2870 {
2871 Thread.Exception(ex);
2872
2873 if (DoLog)
2874 Log.Exception(ex);
2875
2876 return;
2877 }
2878#endif
2879 }
2880 finally
2881 {
2882 Thread.Stop();
2883 Profiler.Stop();
2884
2885 double TotalSeconds = Profiler.ElapsedSeconds;
2886
2887 if (TotalSeconds >= 1.0)
2888 {
2889 string Uml = Profiler.ExportPlantUml(TimeUnit.MilliSeconds);
2890
2891 Log.Debug("Long local login check.\r\n\r\n```uml\r\n" + Uml + "\r\n```");
2892 }
2893 }
2894 }
2895
2896 private static bool SameEndpoint(string EP1, string EP2)
2897 {
2898 if (string.Compare(EP1, EP2, true) == 0)
2899 return true;
2900
2901 switch (Environment.OSVersion.Platform)
2902 {
2903 case PlatformID.Unix:
2904 case PlatformID.MacOSX:
2905 int i = EP1.LastIndexOf('.');
2906 if (i < 0)
2907 break;
2908
2909 if (!int.TryParse(EP1.Substring(i + 1), out int Port1))
2910 break;
2911
2912 if (!IPAddress.TryParse(EP1.Substring(0, i), out IPAddress Addr1))
2913 break;
2914
2915 i = EP2.LastIndexOf(':');
2916 if (i < 0)
2917 break;
2918
2919 if (!int.TryParse(EP2.Substring(i + 1), out int Port2) || Port1 != Port2)
2920 break;
2921
2922 if (!IPAddress.TryParse(EP2.Substring(0, i), out IPAddress Addr2))
2923 break;
2924
2925 string s1 = Addr1.ToString();
2926 string s2 = Addr2.ToString();
2927
2928 if (string.Compare(s1, s2, true) == 0)
2929 return true;
2930
2931 break;
2932 }
2933
2934 return false;
2935 }
2936
2937 private static readonly IPAddress ipv6Local = IPAddress.Parse("[::1]");
2938 private static readonly IPAddress ipv4Local = IPAddress.Parse("127.0.0.1");
2939
2940 private static bool IsLocalCall(IPAddress Address, HttpRequest Request, bool DoLog)
2941 {
2942 if (Address.Equals(ipv4Local) || Address.Equals(ipv6Local))
2943 return true;
2944
2945 string s = Request.Header.Host?.Value ?? string.Empty;
2946 int i = s.IndexOf(':');
2947 if (i > 0)
2948 s = s.Substring(0, i);
2949
2950 if (string.Compare(s, "localhost", true) != 0)
2951 {
2952 if (!IPAddress.TryParse(s, out IPAddress IP) || !Address.Equals(IP))
2953 {
2954 if (DoLog)
2955 Log.Debug("Host is not localhost or an IP Address: " + (Request.Header.Host?.Value ?? string.Empty));
2956
2957 return false;
2958 }
2959 }
2960
2961 try
2962 {
2963 foreach (NetworkInterface Interface in NetworkInterface.GetAllNetworkInterfaces())
2964 {
2965 if (Interface.OperationalStatus != OperationalStatus.Up)
2966 continue;
2967
2968 IPInterfaceProperties Properties = Interface.GetIPProperties();
2969
2970 foreach (UnicastIPAddressInformation UnicastAddress in Properties.UnicastAddresses)
2971 {
2972 if (Address.Equals(UnicastAddress.Address))
2973 return true;
2974 }
2975 }
2976
2977 if (DoLog)
2978 Log.Debug("IP Address not found among network adapters: " + Address.ToString());
2979
2980 return false;
2981 }
2982 catch (Exception ex)
2983 {
2984 if (DoLog)
2985 Log.Debug(ex.Message);
2986
2987 return false;
2988 }
2989 }
2990
2991#if !MONO
2992 [DllImport("kernel32.dll")]
2993 static extern int WTSGetActiveConsoleSessionId();
2994#endif
2995
3002 {
3003 return new RequiredUserPrivileges(webServer, Privileges);
3004 }
3005
3012 public static RequiredUserPrivileges LoggedIn(string UserVariable, string[] Privileges)
3013 {
3014 return new RequiredUserPrivileges(UserVariable, webServer, Privileges);
3015 }
3016
3024 public static RequiredUserPrivileges LoggedIn(string UserVariable, string LoginPage, string[] Privileges)
3025 {
3026 return new RequiredUserPrivileges(UserVariable, LoginPage, webServer, Privileges);
3027 }
3028
3033 public static LoginAuditor LoginAuditor => loginAuditor;
3034
3042 {
3043 return AssertUserAuthenticated(Request.Session, Privilege);
3044 }
3045
3052 public static IUser AssertUserAuthenticated(Variables Session, string Privilege)
3053 {
3054 IUser User = null;
3055
3056 if (Session is null ||
3057 !Session.TryGetVariable("User", out Variable v) ||
3058 ((User = v.ValueObject as IUser) is null) ||
3060 {
3061 throw ForbiddenException.AccessDenied(string.Empty, User?.UserName, Privilege);
3062 }
3063
3064 return User;
3065 }
3066
3073 public static IUser AssertUserAuthenticated(HttpRequest Request, string[] Privileges)
3074 {
3075 return AssertUserAuthenticated(Request.Session, Privileges);
3076 }
3077
3084 public static IUser AssertUserAuthenticated(Variables Session, string[] Privileges)
3085 {
3086 if (Session is null ||
3087 !Session.TryGetVariable("User", out Variable v) ||
3088 (!(v.ValueObject is IUser User)))
3089 {
3090 throw ForbiddenException.AccessDenied(string.Empty, string.Empty, string.Empty);
3091 }
3092
3093 foreach (string Privilege in Privileges)
3094 {
3097 }
3098
3099 return User;
3100 }
3101
3102 #endregion
3103
3104 #region Thing Registry
3105
3106 private static Task ThingRegistryClient_Claimed(object Sender, ClaimedEventArgs e)
3107 {
3108 if (e.Node.IsEmpty)
3109 {
3110 ownerJid = e.JID;
3111 Log.Informational("Gateway has been claimed.", ownerJid, new KeyValuePair<string, object>("Public", e.IsPublic));
3112 }
3113
3114 return Task.CompletedTask;
3115 }
3116
3117 private static Task ThingRegistryClient_Disowned(object Sender, Networking.XMPP.Provisioning.Events.NodeEventArgs e)
3118 {
3119 if (e.Node.IsEmpty)
3120 {
3121 Log.Informational("Gateway has been disowned.", ownerJid);
3122 ownerJid = string.Empty;
3123 Task.Run(Register);
3124 }
3125
3126 return Task.CompletedTask;
3127 }
3128
3129 private static Task ThingRegistryClient_Removed(object Sender, Networking.XMPP.Provisioning.Events.NodeEventArgs e)
3130 {
3131 if (e.Node.IsEmpty)
3132 Log.Informational("Gateway has been removed from the public registry.", ownerJid);
3133
3134 return Task.CompletedTask;
3135 }
3136
3137 private static async Task Register()
3138 {
3139 string Key = Guid.NewGuid().ToString().Replace("-", string.Empty);
3140
3141 // For info on tag names, see: http://xmpp.org/extensions/xep-0347.html#tags
3142 MetaDataTag[] MetaData = new MetaDataTag[]
3143 {
3144 new MetaDataStringTag("KEY", Key),
3145 new MetaDataStringTag("CLASS", "Gateway"),
3146 new MetaDataStringTag("MAN", "waher.se"),
3147 new MetaDataStringTag("MODEL", "Waher.IoTGateway"),
3148 new MetaDataStringTag("PURL", "https://github.com/PeterWaher/IoTGateway#iotgateway"),
3149 new MetaDataNumericTag("V", 1.0)
3150 };
3151
3152 if (!(GetMetaData is null))
3153 MetaData = await GetMetaData(MetaData);
3154
3155 await thingRegistryClient.RegisterThing(MetaData, async (sender2, e2) =>
3156 {
3157 if (e2.Ok)
3158 {
3159 registered = true;
3160
3161 if (e2.IsClaimed)
3162 ownerJid = e2.OwnerJid;
3163 else
3164 ownerJid = string.Empty;
3165
3166 RegistrationEventHandler h = RegistrationSuccessful;
3167 if (!(h is null))
3168 await h(MetaData, e2);
3169 }
3170 }, null);
3171 }
3172
3176 public static event GetRegistryMetaDataEventHandler GetMetaData = null;
3177
3181 public static event RegistrationEventHandler RegistrationSuccessful = null;
3182
3187 {
3188 get { return xmppClient; }
3189 }
3190
3195 {
3196 get { return thingRegistryClient; }
3197 }
3198
3203 {
3204 get { return provisioningClient; }
3205 }
3206
3211 {
3212 get { return concentratorServer; }
3213 }
3214
3218 public static Networking.XMPP.Avatar.AvatarClient AvatarClient
3219 {
3220 get { return avatarClient; }
3221 }
3222
3227 {
3228 get { return sensorClient; }
3229 }
3230
3235 {
3236 get { return controlClient; }
3237 }
3238
3243 {
3244 get { return concentratorClient; }
3245 }
3246
3251 {
3252 get { return synchronizationClient; }
3253 }
3254
3258 public static PepClient PepClient
3259 {
3260 get { return pepClient; }
3261 }
3262
3266 public static MultiUserChatClient MucClient
3267 {
3268 get { return mucClient; }
3269 }
3270
3275 {
3276 get { return pepClient.PubSubClient; }
3277 }
3278
3283 {
3284 get { return softwareUpdateClient; }
3285 }
3286
3291 {
3292 get { return mailClient; }
3293 }
3294
3299 {
3300 get { return webServer; }
3301 }
3302
3307 {
3308 get { return httpxServer; }
3309 }
3310
3315 {
3316 get { return httpxProxy; }
3317 }
3318
3323 {
3324 get { return socksProxy; }
3325 }
3326
3331 {
3332 get { return coapEndpoint; }
3333 }
3334
3335
3336 // TODO: Teman: http://mmistakes.github.io/skinny-bones-jekyll/, http://jekyllrb.com/
3337
3338 #endregion
3339
3340 #region Service Commands
3341
3349 public static async Task<bool> ExecuteServiceCommand(int CommandNr)
3350 {
3352
3353 lock (serviceCommandByNr)
3354 {
3355 if (!serviceCommandByNr.TryGetValue(CommandNr, out h))
3356 h = null;
3357 }
3358
3359 if (h is null)
3360 {
3361 Log.Warning("Service command lacking command handler invoked.", CommandNr.ToString());
3362 return false;
3363 }
3364 else
3365 {
3366 try
3367 {
3368 await h(null, EventArgs.Empty);
3369 }
3370 catch (Exception ex)
3371 {
3372 Log.Exception(ex);
3373 }
3374
3375 return true;
3376 }
3377 }
3378
3384 public static int RegisterServiceCommand(EventHandlerAsync Callback)
3385 {
3386 int i;
3387
3388 lock (serviceCommandByNr)
3389 {
3390 if (serviceCommandNrByCallback.TryGetValue(Callback, out i))
3391 return i;
3392
3393 i = nextServiceCommandNr++;
3394
3395 serviceCommandNrByCallback[Callback] = i;
3396 serviceCommandByNr[i] = Callback;
3397 }
3398
3399 return i;
3400 }
3401
3407 public static bool UnregisterServiceCommand(EventHandlerAsync Callback)
3408 {
3409 lock (serviceCommandByNr)
3410 {
3411 if (serviceCommandNrByCallback.TryGetValue(Callback, out int i))
3412 {
3413 serviceCommandByNr.Remove(i);
3414 serviceCommandNrByCallback.Remove(Callback);
3415
3416 return true;
3417 }
3418 }
3419
3420 return false;
3421 }
3422
3426 public static int BeforeUninstallCommandNr
3427 {
3428 get { return beforeUninstallCommandNr; }
3429 }
3430
3434 public static event EventHandlerAsync OnBeforeUninstall = null;
3435
3436 private static Task BeforeUninstall(object Sender, EventArgs e)
3437 {
3438 return OnBeforeUninstall.Raise(Sender, e, false);
3439 }
3440
3441 #endregion
3442
3443 #region Scheduling
3444
3452 public static DateTime ScheduleEvent(ScheduledEventCallback Callback, DateTime When, object State)
3453 {
3454 return scheduler?.Add(When, Callback, State) ?? DateTime.MinValue;
3455 }
3456
3464 public static DateTime ScheduleEvent(ScheduledEventCallbackAsync Callback, DateTime When, object State)
3465 {
3466 return scheduler?.Add(When, Callback, State) ?? DateTime.MinValue;
3467 }
3468
3474 public static bool CancelScheduledEvent(DateTime When)
3475 {
3476 return scheduler?.Remove(When) ?? false;
3477 }
3478
3479 #endregion
3480
3481 #region Random number generation
3482
3487 public static double NextDouble()
3488 {
3489 byte[] b = new byte[8];
3490
3491 lock (rnd)
3492 {
3493 rnd.GetBytes(b);
3494 }
3495
3496 double d = BitConverter.ToUInt64(b, 0);
3497 d /= ulong.MaxValue;
3498
3499 return d;
3500 }
3501
3510 public static int NextInteger(int Max)
3511 {
3512 if (Max < 0)
3513 throw new ArgumentOutOfRangeException("Must be non-negative.", nameof(Max));
3514
3515 if (Max == 0)
3516 return 0;
3517
3518 int Result;
3519
3520 do
3521 {
3522 Result = (int)(NextDouble() * Max);
3523 }
3524 while (Result >= Max);
3525
3526 return Result;
3527 }
3528
3534 public static byte[] NextBytes(int NrBytes)
3535 {
3536 if (NrBytes < 0)
3537 throw new ArgumentException("Number of bytes must be non-negative.", nameof(NrBytes));
3538
3539 byte[] Result = new byte[NrBytes];
3540
3541 lock (rnd)
3542 {
3543 rnd.GetBytes(Result);
3544 }
3545
3546 return Result;
3547 }
3548
3549 #endregion
3550
3551 #region Momentary values
3552
3557 public static Task NewMomentaryValues(params Field[] Values)
3558 {
3559 concentratorServer?.SensorServer?.NewMomentaryValues(Values);
3560 return Task.CompletedTask;
3561 }
3562
3568 public static Task NewMomentaryValues(IThingReference Reference, params Field[] Values)
3569 {
3570 concentratorServer?.SensorServer?.NewMomentaryValues(Reference, Values);
3571 return Task.CompletedTask;
3572 }
3573
3578 public static Task NewMomentaryValues(IEnumerable<Field> Values)
3579 {
3580 concentratorServer?.SensorServer?.NewMomentaryValues(Values);
3581 return Task.CompletedTask;
3582 }
3583
3589 public static Task NewMomentaryValues(IThingReference Reference, IEnumerable<Field> Values)
3590 {
3591 concentratorServer?.SensorServer?.NewMomentaryValues(Reference, Values);
3592 return Task.CompletedTask;
3593 }
3594
3595 #endregion
3596
3597 #region Personal Eventing Protocol
3598
3604 {
3605 if (pepClient is null)
3606 throw new Exception("No PEP client available.");
3607
3608 return pepClient.Publish(PersonalEvent, null, null);
3609 }
3610
3616 public static void RegisterHandler(Type PersonalEventType, EventHandlerAsync<PersonalEventNotificationEventArgs> Handler)
3617 {
3618 pepClient?.RegisterHandler(PersonalEventType, Handler);
3619 }
3620
3627 public static bool UnregisterHandler(Type PersonalEventType, EventHandlerAsync<PersonalEventNotificationEventArgs> Handler)
3628 {
3629 if (pepClient is null)
3630 return false;
3631 else
3632 return pepClient.UnregisterHandler(PersonalEventType, Handler);
3633 }
3634
3639 public static event EventHandlerAsync<ItemNotificationEventArgs> PubSubItemNotification
3640 {
3641 add => pepClient.NonPepItemNotification += value;
3642 remove => pepClient.NonPepItemNotification -= value;
3643 }
3644
3645 #endregion
3646
3647 #region Backups
3648
3649 private static async Task<bool> CheckBackup()
3650 {
3651 bool Result = false;
3652
3653 try
3654 {
3655 if (await Export.GetAutomaticBackupsAsync())
3656 {
3657 DateTime Now = DateTime.Now;
3658 TimeSpan Timepoint = await Export.GetBackupTimeAsync();
3659 DateTime EstimatedTime = Now.Date + Timepoint;
3660 DateTime LastBackup = await Export.GetLastBackupAsync();
3661
3662 if ((Timepoint.Hours == Now.Hour && Timepoint.Minutes == Now.Minute) ||
3663 (lastBackupTimeCheck.HasValue && lastBackupTimeCheck.Value < EstimatedTime && Now >= EstimatedTime) ||
3664 LastBackup.AddDays(1) < EstimatedTime)
3665 {
3666 lastBackupTimeCheck = Now;
3667 await DoBackup();
3668
3669 Result = true;
3670 }
3671 }
3672 }
3673 catch (Exception ex)
3674 {
3675 Log.Exception(ex);
3676 }
3677
3678 return Result;
3679 }
3680
3684 public static async Task DoBackup()
3685 {
3686 DateTime Now = DateTime.Now;
3687
3688 await Export.SetLastBackupAsync(Now);
3689
3690 StartExport.ExportInfo ExportInfo = await StartExport.GetExporter("Encrypted", false, new string[0]);
3691
3692 ExportFormat.UpdateClientsFileUpdated(ExportInfo.LocalBackupFileName, -1, Now);
3693
3694 List<string> Folders = new List<string>();
3695
3696 foreach (Export.FolderCategory FolderCategory in Export.GetRegisteredFolders())
3697 Folders.AddRange(FolderCategory.Folders);
3698
3699 await StartExport.DoExport(ExportInfo, true, false, true, Folders.ToArray());
3700
3701 long KeepDays = await Export.GetKeepDaysAsync();
3702 long KeepMonths = await Export.GetKeepMonthsAsync();
3703 long KeepYears = await Export.GetKeepYearsAsync();
3704 string ExportFolder = await Export.GetFullExportFolderAsync();
3705 string KeyFolder = await Export.GetFullKeyExportFolderAsync();
3706
3707 DeleteOldFiles(ExportFolder, KeepDays, KeepMonths, KeepYears, Now);
3708 if (ExportFolder != KeyFolder)
3709 DeleteOldFiles(KeyFolder, KeepDays, KeepMonths, KeepYears, Now);
3710
3711 await OnAfterBackup.Raise(typeof(Gateway), EventArgs.Empty);
3712 }
3713
3714 private static DateTime? lastBackupTimeCheck = null;
3715
3720 public static event EventHandlerAsync OnAfterBackup = null;
3721
3722 private static void DeleteOldFiles(string Path, long KeepDays, long KeepMonths, long KeepYears, DateTime Now)
3723 {
3724 try
3725 {
3726 string[] Files = Directory.GetFiles(Path, "*.*", SearchOption.AllDirectories);
3727 DateTime CreationTime;
3728
3729 foreach (string FileName in Files)
3730 {
3731 try
3732 {
3733 CreationTime = File.GetCreationTime(FileName);
3734
3735 if (CreationTime.Day == 1)
3736 {
3737 if (CreationTime.Month == 1) // Yearly
3738 {
3739 if (Now.Year - CreationTime.Year <= KeepYears)
3740 continue;
3741 }
3742 else // Monthly
3743 {
3744 if (((Now.Year * 12 + Now.Month) - (CreationTime.Year * 12 + Now.Month)) <= KeepMonths)
3745 continue;
3746 }
3747 }
3748 else // Daily
3749 {
3750 if ((Now.Date - CreationTime.Date).TotalDays <= KeepDays)
3751 continue;
3752 }
3753
3754 File.Delete(FileName);
3755 Log.Informational("Backup file deleted.", FileName);
3756
3757 ExportFormat.UpdateClientsFileDeleted(System.IO.Path.GetFileName(FileName));
3758 }
3759 catch (Exception ex)
3760 {
3761 Log.Exception(ex, FileName);
3762 }
3763 }
3764 }
3765 catch (Exception ex)
3766 {
3767 Log.Exception(ex);
3768 }
3769 }
3770
3771 private static Task Database_CollectionRepaired(object Sender, CollectionRepairedEventArgs e)
3772 {
3773 StringBuilder Msg = new StringBuilder();
3774
3775 Msg.Append("Collection repaired: ");
3776 Msg.AppendLine(e.Collection);
3777
3778 if (!(e.Flagged is null))
3779 {
3780 foreach (FlagSource Source in e.Flagged)
3781 {
3782 Msg.AppendLine();
3783 Msg.Append("Reason: ");
3784 Msg.Append(MarkdownDocument.Encode(Source.Reason));
3785
3786 if (Source.Count > 1)
3787 {
3788 Msg.Append(" (");
3789 Msg.Append(Source.Count.ToString());
3790 Msg.Append(" times)");
3791 }
3792
3793 Msg.AppendLine();
3794 Msg.AppendLine();
3795 Msg.AppendLine("StackTrace:");
3796 Msg.AppendLine();
3797 Msg.AppendLine("```");
3798 Msg.AppendLine(Source.StackTrace);
3799 Msg.AppendLine("```");
3800 }
3801 }
3802
3803 Log.Alert(Msg.ToString(), e.Collection);
3804
3805 return Task.CompletedTask;
3806 }
3807
3808 #endregion
3809
3810 #region Notifications
3811
3812 private static Task MailClient_MailReceived(object Sender, MailEventArgs e)
3813 {
3814 return MailReceived.Raise(Sender, e);
3815 }
3816
3820 public static event EventHandlerAsync<MailEventArgs> MailReceived = null;
3821
3826 public static Task SendNotification(Graph Graph)
3827 {
3828 return SendNotification(Content.Markdown.Functions.ToMarkdown.GraphToMarkdown(Graph));
3829 }
3830
3835 public static Task SendNotification(PixelInformation Pixels)
3836 {
3837 return SendNotification(Content.Markdown.Functions.ToMarkdown.PixelsToMarkdown(Pixels));
3838 }
3839
3844 public static Task SendNotification(string Markdown)
3845 {
3846 return SendNotification(Markdown, string.Empty, false);
3847 }
3848
3854 public static Task SendNotification(string Markdown, string MessageId)
3855 {
3856 return SendNotification(Markdown, MessageId, false);
3857 }
3858
3864 public static Task SendNotificationUpdate(string Markdown, string MessageId)
3865 {
3866 return SendNotification(Markdown, MessageId, true);
3867 }
3868
3875 private static async Task SendNotification(string Markdown, string MessageId, bool Update)
3876 {
3877 try
3878 {
3879 CaseInsensitiveString[] Addresses = GetNotificationAddresses();
3880 (string Text, string Html) = await ConvertMarkdown(Markdown);
3881
3882 foreach (CaseInsensitiveString Admin in Addresses)
3883 await SendNotification(Admin, Markdown, Text, Html, MessageId, Update);
3884 }
3885 catch (Exception ex)
3886 {
3887 Log.Exception(ex);
3888 }
3889 }
3890
3891 private static Task<(string, string)> ConvertMarkdown(string Markdown)
3892 {
3893 return ConvertMarkdown(Markdown, true, true);
3894 }
3895
3896 private static async Task<(string, string)> ConvertMarkdown(string Markdown, bool TextVersion, bool HtmlVersion)
3897 {
3898 if (TextVersion || HtmlVersion)
3899 {
3900 MarkdownSettings Settings = new MarkdownSettings()
3901 {
3902 ParseMetaData = false
3903 };
3905 {
3906 XmlEntitiesOnly = true
3907 };
3908 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(Markdown, Settings);
3909 string Text = TextVersion ? await Doc.GeneratePlainText() : null;
3910 string Html = HtmlVersion ? HtmlDocument.GetBody(await Doc.GenerateHTML(HtmlSettings)) : null;
3911
3912 return (Text, Html);
3913 }
3914 else
3915 return (null, null);
3916 }
3917
3923 {
3925 }
3926
3927 private static async Task SendNotification(string To, string Markdown, string Text, string Html, string MessageId, bool Update)
3928 {
3929 if (!(XmppClient is null) && XmppClient.State == XmppState.Connected)
3930 {
3932 if (Item is null || (Item.State != SubscriptionState.To && Item.State != SubscriptionState.Both))
3933 {
3934 await xmppClient.RequestPresenceSubscription(To);
3935 ScheduleEvent(Resend, DateTime.Now.AddMinutes(15), new object[] { To, Markdown, Text, Html, MessageId, Update });
3936 }
3937 else
3938 await SendChatMessage(MessageType.Chat, Markdown, Text, Html, To, MessageId, string.Empty, Update);
3939 }
3940 else
3941 ScheduleEvent(Resend, DateTime.Now.AddSeconds(30), new object[] { To, Markdown, Text, Html, MessageId, Update });
3942 }
3943
3949 public static Task SendChatMessage(string Markdown, string To)
3950 {
3951 return SendChatMessage(Markdown, To, string.Empty);
3952 }
3953
3960 public static Task SendChatMessage(string Markdown, string To, string MessageId)
3961 {
3962 return SendChatMessage(Markdown, To, MessageId, string.Empty);
3963 }
3964
3972 public static async Task SendChatMessage(string Markdown, string To, string MessageId, string ThreadId)
3973 {
3974 (string Text, string Html) = await ConvertMarkdown(Markdown);
3975 await SendChatMessage(MessageType.Chat, Markdown, Text, Html, To, MessageId, ThreadId, false);
3976 }
3977
3984 public static Task SendChatMessageUpdate(string Markdown, string To, string MessageId)
3985 {
3986 return SendChatMessageUpdate(Markdown, To, MessageId, string.Empty);
3987 }
3988
3996 public static async Task SendChatMessageUpdate(string Markdown, string To, string MessageId, string ThreadId)
3997 {
3998 (string Text, string Html) = await ConvertMarkdown(Markdown);
3999 await SendChatMessage(MessageType.Chat, Markdown, Text, Html, To, MessageId, ThreadId, true);
4000 }
4001
4007 public static Task SendGroupChatMessage(string Markdown, string To)
4008 {
4009 return SendGroupChatMessage(Markdown, To, string.Empty);
4010 }
4011
4018 public static Task SendGroupChatMessage(string Markdown, string To, string MessageId)
4019 {
4020 return SendGroupChatMessage(Markdown, To, MessageId, string.Empty);
4021 }
4022
4030 public static async Task SendGroupChatMessage(string Markdown, string To, string MessageId, string ThreadId)
4031 {
4032 (string Text, string Html) = await ConvertMarkdown(Markdown);
4033 await SendChatMessage(MessageType.GroupChat, Markdown, Text, Html, To, MessageId, ThreadId, false);
4034 }
4035
4042 public static Task SendGroupChatMessageUpdate(string Markdown, string To, string MessageId)
4043 {
4044 return SendGroupChatMessageUpdate(Markdown, To, MessageId, string.Empty);
4045 }
4046
4054 public static async Task SendGroupChatMessageUpdate(string Markdown, string To, string MessageId, string ThreadId)
4055 {
4056 (string Text, string Html) = await ConvertMarkdown(Markdown);
4057 await SendChatMessage(MessageType.GroupChat, Markdown, Text, Html, To, MessageId, ThreadId, true);
4058 }
4059
4065 public static Task<string> GetMultiFormatChatMessageXml(string Markdown)
4066 {
4067 return GetMultiFormatChatMessageXml(Markdown, true, true);
4068 }
4069
4077 public static async Task<string> GetMultiFormatChatMessageXml(string Markdown, bool TextVersion, bool HtmlVersion)
4078 {
4079 (string Text, string Html) = await ConvertMarkdown(Markdown, TextVersion, HtmlVersion);
4080 return GetMultiFormatChatMessageXml(Text, Html, Markdown);
4081 }
4082
4090 public static string GetMultiFormatChatMessageXml(string Text, string Html, string Markdown)
4091 {
4092 StringBuilder Xml = new StringBuilder();
4093 AppendMultiFormatChatMessageXml(Xml, Text, Html, Markdown);
4094 return Xml.ToString();
4095 }
4096
4104 public static void AppendMultiFormatChatMessageXml(StringBuilder Xml, string Text, string Html, string Markdown)
4105 {
4106 if (string.IsNullOrEmpty(Text))
4107 Xml.Append("<body/>");
4108 else
4109 {
4110 Xml.Append("<body><![CDATA[");
4111 Xml.Append(Text.Replace("]]>", "]] >"));
4112 Xml.Append("]]></body>");
4113 }
4114
4115 if (!string.IsNullOrEmpty(Markdown))
4116 {
4117 Xml.Append("<content xmlns=\"urn:xmpp:content\" type=\"text/markdown\"><![CDATA[");
4118 Xml.Append(Markdown.Replace("]]>", "]] >"));
4119 Xml.Append("]]></content>");
4120 }
4121
4122 if (!string.IsNullOrEmpty(Html))
4123 {
4124 Xml.Append("<html xmlns='http://jabber.org/protocol/xhtml-im'>");
4125 Xml.Append("<body xmlns='http://www.w3.org/1999/xhtml'>");
4126
4127 HtmlDocument Doc = new HtmlDocument("<root>" + Html + "</root>");
4128 IEnumerable<HtmlNode> Children = (Doc.Body ?? Doc.Root).Children;
4129
4130 if (!(Children is null))
4131 {
4132 foreach (HtmlNode N in Children)
4133 N.Export(Xml);
4134 }
4135
4136 Xml.Append("</body></html>");
4137 }
4138 }
4139
4140 private static async Task SendChatMessage(MessageType Type, string Markdown, string Text, string Html, string To, string MessageId, string ThreadId, bool Update)
4141 {
4142 if (!(XmppClient is null) && XmppClient.State == XmppState.Connected)
4143 {
4144 StringBuilder Xml = new StringBuilder();
4145
4146 AppendMultiFormatChatMessageXml(Xml, Text, Html, Markdown);
4147
4148 if (Update && !string.IsNullOrEmpty(MessageId))
4149 {
4150 Xml.Append("<replace id='");
4151 Xml.Append(MessageId);
4152 Xml.Append("' xmlns='urn:xmpp:message-correct:0'/>");
4153
4154 MessageId = string.Empty;
4155 }
4156
4157 await xmppClient.SendMessage(QoSLevel.Unacknowledged, Type, MessageId, To, Xml.ToString(), string.Empty,
4158 string.Empty, string.Empty, ThreadId, string.Empty, null, null);
4159 }
4160 }
4161
4167 public static string GetUrl(string LocalResource)
4168 {
4169 return GetUrl(LocalResource, webServer);
4170 }
4171
4178 public static string GetUrl(string LocalResource, HttpServer Server)
4179 {
4180 StringBuilder sb = new StringBuilder();
4181 int DefaultPort;
4182 int[] Ports;
4183
4184 sb.Append("http");
4185
4186 if (certificate is null)
4187 {
4188 Ports = Server?.OpenHttpPorts ?? GetConfigPorts("HTTP");
4189 DefaultPort = 80;
4190 }
4191 else
4192 {
4193 sb.Append('s');
4194 Ports = Server?.OpenHttpsPorts ?? GetConfigPorts("HTTPS");
4195 DefaultPort = 443;
4196 }
4197
4198 sb.Append("://");
4200 sb.Append(domain);
4201 /*else if (httpxProxy?.ServerlessMessaging?.Network.State == PeerToPeerNetworkState.Ready)
4202 sb.Append(httpxProxy.ServerlessMessaging.Network.ExternalAddress.ToString());
4203
4204 TODO: P2P & Serverless messaging: Recognize HTTP request, and redirect to local HTTP Server, and return response.
4205 */
4206 else
4207 {
4208 IPAddress IP4 = null;
4209 IPAddress IP6 = null;
4210
4211 if (!(Server is null))
4212 {
4213 foreach (IPAddress Addr in Server.LocalIpAddresses)
4214 {
4215 if (IPAddress.IsLoopback(Addr))
4216 continue;
4217
4218 switch (Addr.AddressFamily)
4219 {
4220 case System.Net.Sockets.AddressFamily.InterNetwork:
4221 if (IP4 is null)
4222 IP4 = Addr;
4223 break;
4224
4225 case System.Net.Sockets.AddressFamily.InterNetworkV6:
4226 if (IP6 is null)
4227 IP6 = Addr;
4228 break;
4229 }
4230 }
4231 }
4232
4233 if (!(IP4 is null))
4234 sb.Append(IP4.ToString());
4235 else if (!(IP6 is null))
4236 sb.Append(IP6.ToString());
4237 else
4238 sb.Append(Dns.GetHostName());
4239 }
4240
4241 if (Array.IndexOf(Ports, DefaultPort) < 0 && Ports.Length > 0)
4242 {
4243 sb.Append(":");
4244 sb.Append(Ports[0].ToString());
4245 }
4246
4247 sb.Append(LocalResource);
4248
4249 return sb.ToString();
4250 }
4251
4258 public static bool IsDomain(string DomainOrHost, bool IncludeAlternativeDomains)
4259 {
4261 {
4262 if (DomainOrHost == domain)
4263 return true;
4264
4265 if (IncludeAlternativeDomains && !(alternativeDomains is null))
4266 {
4267 foreach (CaseInsensitiveString s in alternativeDomains)
4268 {
4269 if (s == DomainOrHost)
4270 return true;
4271 }
4272 }
4273 }
4274 else
4275 {
4276 if (DomainOrHost == "localhost" || string.IsNullOrEmpty(DomainOrHost))
4277 return true;
4278
4279 if (!(webServer is null))
4280 {
4281 foreach (IPAddress Addr in webServer.LocalIpAddresses)
4282 {
4283 if (Addr.ToString() == DomainOrHost)
4284 return true;
4285 }
4286 }
4287
4288 if (DomainOrHost == Dns.GetHostName())
4289 return true;
4290 }
4291
4292 return false;
4293 }
4294
4295 private static async Task Resend(object P)
4296 {
4297 object[] P2 = (object[])P;
4298 await SendNotification((string)P2[0], (string)P2[1], (string)P2[2], (string)P2[3], (string)P2[4], (bool)P2[5]);
4299 }
4300
4301 #endregion
4302
4303 #region Settings
4304
4308 internal static async Task SimplifiedConfiguration()
4309 {
4310 foreach (SystemConfiguration Configuration in configurations)
4311 {
4312 if (!Configuration.Complete)
4313 {
4314 bool Updated = await Configuration.EnvironmentConfiguration();
4315
4316 if (!Configuration.Complete && await Configuration.SimplifiedConfiguration())
4317 Updated = true;
4318
4319 if (Updated)
4320 {
4321 await Configuration.MakeCompleted();
4322 await Database.Update(Configuration);
4323 }
4324 }
4325 }
4326 }
4327
4334 public static WebMenuItem[] GetSettingsMenu(HttpRequest Request, string UserVariable)
4335 {
4336 List<WebMenuItem> Result = new List<WebMenuItem>();
4337 Variables Session = Request.Session;
4338 if (Session is null)
4339 return new WebMenuItem[0];
4340
4341 Language Language = ScriptExtensions.Constants.Language.GetLanguageAsync(Session).Result;
4342
4343 if (Session is null ||
4344 !Session.TryGetVariable(UserVariable, out Variable v) ||
4345 !(v.ValueObject is IUser User))
4346 {
4347 Result.Add(new WebMenuItem("Login", "/Login.md"));
4348 }
4349 else
4350 {
4351 if (!(configurations is null))
4352 {
4353 foreach (SystemConfiguration Configuration in configurations)
4354 {
4355 if (User.HasPrivilege("Settings." + Configuration.GetType().FullName))
4356 Result.Add(new WebMenuItem(Configuration.Title(Language).Result, Configuration.Resource));
4357 }
4358 }
4359
4360 if (!Session.TryGetVariable(" AutoLogin ", out v) || !(v.ValueObject is bool AutoLogin) || !AutoLogin)
4361 Result.Add(new WebMenuItem("Logout", "/Logout"));
4362 }
4363
4364 return Result.ToArray();
4365 }
4366
4367 #endregion
4368
4369 #region Smart Contracts
4370
4375 {
4376 get { return contractsClient; }
4377 set
4378 {
4379 if (contractsClient is null ||
4380 contractsClient == value ||
4381 (value is null && LegalIdentityConfiguration.Instance is null))
4382 {
4383 contractsClient = value;
4384 }
4385 else
4386 throw new InvalidOperationException("Not allowed to set a new Contracts Client class.");
4387 }
4388 }
4389
4393 public static string LatestApprovedLegalIdentityId
4394 {
4396 }
4397
4405 public static async Task RequestContractSignature(Contract Contract, string Role, string Purpose)
4406 {
4407 bool RoleFound = false;
4408
4409 if (Contract is null)
4410 throw new ArgumentException("Contract cannot be null.", nameof(Contract));
4411
4412 // TODO: Check contract server signature is valid.
4413
4414 foreach (Networking.XMPP.Contracts.Role R in Contract.Roles)
4415 {
4416 if (R.Name == Role)
4417 {
4418 RoleFound = true;
4419 break;
4420 }
4421 }
4422
4423 if (!RoleFound)
4424 throw new ArgumentException("Invalid role.", nameof(Role));
4425
4426 if (string.IsNullOrEmpty(Purpose) || Purpose.IndexOfAny(CommonTypes.CRLF) >= 0)
4427 throw new ArgumentException("Invalid purpose.", nameof(Purpose));
4428
4429 try
4430 {
4431 string Module = string.Empty;
4432 int Skip = 1;
4433
4434 while (true)
4435 {
4436 StackFrame Frame = new StackFrame(Skip);
4437 MethodBase Method = Frame.GetMethod();
4438 if (Method is null)
4439 break;
4440
4441 Type Type = Method.DeclaringType;
4442 Assembly Assembly = Type.Assembly;
4443 Module = Assembly.GetName().Name;
4444
4445 if (Type != typeof(Gateway) && !Module.StartsWith("System."))
4446 break;
4447
4448 Skip++;
4449 }
4450
4451 string Markdown;
4452 ContractSignatureRequest Request = await Database.FindFirstIgnoreRest<ContractSignatureRequest>(new FilterAnd(
4453 new FilterFieldEqualTo("ContractId", Contract.ContractId),
4454 new FilterFieldEqualTo("Role", Role),
4455 new FilterFieldEqualTo("Module", Module),
4456 new FilterFieldEqualTo("Provider", Contract.Provider),
4457 new FilterFieldEqualTo("Purpose", Purpose)));
4458
4459
4460 if (Request is null)
4461 {
4462 Request = new ContractSignatureRequest()
4463 {
4464 Received = DateTime.Now,
4465 Signed = null,
4466 ContractId = Contract.ContractId,
4467 Role = Role,
4468 Module = Module,
4469 Provider = Contract.Provider,
4470 Purpose = Purpose
4471 };
4472
4473 Request.SetContract(Contract);
4474
4475 await Database.Insert(Request);
4476
4477 Markdown = await Resources.ReadAllTextAsync(Path.Combine(rootFolder, "Settings", "SignatureRequest.md"));
4478
4479 int i = Markdown.IndexOf("~~~~~~");
4480 int c = Markdown.Length;
4481
4482 if (i >= 0)
4483 {
4484 i += 6;
4485 while (i < c && Markdown[i] == '~')
4486 i++;
4487
4488 Markdown = Markdown.Substring(i).TrimStart();
4489 }
4490
4491 i = Markdown.IndexOf("~~~~~~");
4492 if (i > 0)
4493 Markdown = Markdown.Substring(0, i).TrimEnd();
4494
4496 Variables["RequestId"] = Request.ObjectId;
4497 Variables["Request"] = Request;
4498
4499 MarkdownSettings Settings = new MarkdownSettings(emoji1_24x24, false, Variables);
4500 Markdown = await MarkdownDocument.Preprocess(Markdown, Settings);
4501
4502 StringBuilder sb = new StringBuilder(Markdown);
4503
4504 sb.AppendLine();
4505 sb.Append("Link: [`");
4506 sb.Append(Request.ContractId);
4507 sb.Append("](");
4508 sb.Append(GetUrl("/SignatureRequest.md?RequestId=" + Request.ObjectId));
4509 sb.AppendLine(")");
4510
4511 Markdown = sb.ToString();
4512 }
4513 else
4514 {
4515 Markdown = "**Reminder**: Smart Contract [" + Request.ContractId + "](" +
4516 GetUrl("/SignatureRequest.md?RequestId=" + Request.ObjectId) + ") is waiting for your signature.";
4517 }
4518
4519 await SendNotification(Markdown);
4520 }
4521 catch (Exception ex)
4522 {
4523 Log.Exception(ex);
4524 }
4525 }
4526
4536 public static Task<bool> PetitionLegalIdentity(string LegalId, string PetitionId, string Purpose,
4537 EventHandlerAsync<LegalIdentityPetitionResponseEventArgs> Callback, TimeSpan Timeout)
4538 {
4539 return LegalIdentityConfiguration.Instance.PetitionLegalIdentity(LegalId, PetitionId, Purpose, Callback, Timeout);
4540 }
4541
4552 public static Task<bool> PetitionLegalIdentity(string LegalId, string PetitionId, string Purpose, string Password,
4553 EventHandlerAsync<LegalIdentityPetitionResponseEventArgs> Callback, TimeSpan Timeout)
4554 {
4555 return LegalIdentityConfiguration.Instance.PetitionLegalIdentity(LegalId, PetitionId, Purpose, Password, Callback, Timeout);
4556 }
4557
4567 public static Task<bool> PetitionContract(string ContractId, string PetitionId, string Purpose,
4568 EventHandlerAsync<ContractPetitionResponseEventArgs> Callback, TimeSpan Timeout)
4569 {
4570 return LegalIdentityConfiguration.Instance.PetitionContract(ContractId, PetitionId, Purpose, Callback, Timeout);
4571 }
4572
4583 public static Task<bool> PetitionContract(string ContractId, string PetitionId, string Purpose, string Password,
4584 EventHandlerAsync<ContractPetitionResponseEventArgs> Callback, TimeSpan Timeout)
4585 {
4586 return LegalIdentityConfiguration.Instance.PetitionContract(ContractId, PetitionId, Purpose, Password, Callback, Timeout);
4587 }
4588
4589 #endregion
4590
4591 #region Finding Files
4592
4602 public static string[] FindFiles(Environment.SpecialFolder[] Folders, string Pattern, bool IncludeSubfolders, bool BreakOnFirst)
4603 {
4604 return FileSystem.FindFiles(Folders, Pattern, IncludeSubfolders, BreakOnFirst);
4605 }
4606
4616 public static string[] FindFiles(string[] Folders, string Pattern, bool IncludeSubfolders, bool BreakOnFirst)
4617 {
4618 return FileSystem.FindFiles(Folders, Pattern, IncludeSubfolders, BreakOnFirst);
4619 }
4620
4630 public static string[] FindFiles(string[] Folders, string Pattern, bool IncludeSubfolders, int MaxCount)
4631 {
4632 return FileSystem.FindFiles(Folders, Pattern, IncludeSubfolders, MaxCount);
4633 }
4634
4644 public static string[] FindFiles(string[] Folders, string Pattern, int SubfolderDepth, int MaxCount)
4645 {
4646 return FileSystem.FindFiles(Folders, Pattern, SubfolderDepth, MaxCount);
4647 }
4648
4655 public static string[] GetFolders(Environment.SpecialFolder[] Folders, params string[] AppendWith)
4656 {
4657 return FileSystem.GetFolders(Folders, AppendWith);
4658 }
4659
4669 public static string FindLatestFile(Environment.SpecialFolder[] Folders, string Pattern, bool IncludeSubfolders)
4670 {
4671 return FileSystem.FindLatestFile(Folders, Pattern, IncludeSubfolders);
4672 }
4673
4683 public static string FindLatestFile(string[] Folders, string Pattern, bool IncludeSubfolders)
4684 {
4685 return FileSystem.FindLatestFile(Folders, Pattern, IncludeSubfolders);
4686 }
4687
4697 public static string FindLatestFile(string[] Folders, string Pattern, int SubfolderDepth)
4698 {
4699 return FileSystem.FindLatestFile(Folders, Pattern, SubfolderDepth);
4700 }
4701
4702 #endregion
4703
4704 #region Custom Errors
4705
4706 private static readonly Dictionary<string, KeyValuePair<DateTime, MarkdownDocument>> defaultDocuments = new Dictionary<string, KeyValuePair<DateTime, MarkdownDocument>>();
4707
4708 private static async Task WebServer_CustomError(object Sender, CustomErrorEventArgs e)
4709 {
4710 HttpFieldAccept Accept = e.Request?.Header?.Accept;
4711 if (Accept is null || Accept.Value == "*/*")
4712 return;
4713
4715 {
4716 string Html = await GetCustomErrorHtml(e.Request, e.StatusCode.ToString() + ".md", e.ContentType, e.Content);
4717
4718 if (!string.IsNullOrEmpty(Html))
4719 e.SetContent("text/html; charset=utf-8", System.Text.Encoding.UTF8.GetBytes(Html));
4720 }
4721 }
4722
4731 public static async Task<string> GetCustomErrorHtml(HttpRequest Request, string LocalFileName, string ContentType, byte[] Content)
4732 {
4733 bool IsText;
4734 bool IsMarkdown;
4735 bool IsEmpty;
4736
4737 if (string.IsNullOrEmpty(ContentType))
4738 {
4739 IsText = IsMarkdown = false;
4740 IsEmpty = true;
4741 }
4742 else
4743 {
4744 IsText = ContentType.StartsWith(PlainTextCodec.DefaultContentType);
4745 IsMarkdown = ContentType.StartsWith(MarkdownCodec.ContentType);
4746 IsEmpty = false;
4747 }
4748
4749 if (IsEmpty || IsText || IsMarkdown)
4750 {
4751 MarkdownDocument Doc;
4752 DateTime TP;
4753
4754 lock (defaultDocuments)
4755 {
4756 if (defaultDocuments.TryGetValue(LocalFileName, out KeyValuePair<DateTime, MarkdownDocument> P))
4757 {
4758 TP = P.Key;
4759 Doc = P.Value;
4760 }
4761 else
4762 {
4763 TP = DateTime.MinValue;
4764 Doc = null;
4765 }
4766 }
4767
4768 string FullFileName = Path.Combine(appDataFolder, "Default", LocalFileName);
4769
4770 if (File.Exists(FullFileName))
4771 {
4772 DateTime TP2 = File.GetLastWriteTime(FullFileName);
4773 MarkdownSettings Settings;
4774 MarkdownDocument Detail;
4775 string Markdown;
4776
4777 if (Doc is null || TP2 > TP)
4778 {
4779 Markdown = await Resources.ReadAllTextAsync(FullFileName);
4780 Settings = new MarkdownSettings(emoji1_24x24, true)
4781 {
4782 RootFolder = rootFolder,
4783 Variables = Request.Session ?? HttpServer.CreateVariables()
4784 };
4785
4786 if (!Settings.Variables.ContainsVariable("Request"))
4787 Settings.Variables["Request"] = Request;
4788
4789 Doc = await MarkdownDocument.CreateAsync(Markdown, Settings, RootFolder, string.Empty, string.Empty);
4790
4791 lock (defaultDocuments)
4792 {
4793 defaultDocuments[LocalFileName] = new KeyValuePair<DateTime, MarkdownDocument>(TP2, Doc);
4794 }
4795 }
4796
4797 if (IsEmpty || Content is null)
4798 Detail = null;
4799 else
4800 {
4801 System.Text.Encoding Encoding = null;
4802 int i = ContentType.IndexOf(';');
4803
4804 if (i > 0)
4805 {
4806 KeyValuePair<string, string>[] Fields = CommonTypes.ParseFieldValues(ContentType.Substring(i + 1).TrimStart());
4807
4808 foreach (KeyValuePair<string, string> Field in Fields)
4809 {
4810 if (string.Compare(Field.Key, "CHARSET", true) == 0)
4811 Encoding = InternetContent.GetEncoding(Field.Value);
4812 }
4813 }
4814
4815 if (Encoding is null)
4816 Encoding = System.Text.Encoding.UTF8;
4817
4818 Markdown = CommonTypes.GetString(Content, Encoding);
4819 if (IsText)
4820 {
4821 MarkdownSettings Settings2 = new MarkdownSettings(null, false);
4822 Detail = await MarkdownDocument.CreateAsync("```\r\n" + Markdown + "\r\n```", Settings2);
4823 }
4824 else
4825 Detail = await MarkdownDocument.CreateAsync(Markdown, Doc.Settings);
4826 }
4827
4828 if (!(Doc.Tag is MultiReadSingleWriteObject DocSynchObj))
4829 {
4830 DocSynchObj = new MultiReadSingleWriteObject(Doc);
4831 Doc.Tag = DocSynchObj;
4832 }
4833
4834 if (await DocSynchObj.TryBeginWrite(30000))
4835 {
4836 try
4837 {
4838 Doc.Detail = Detail;
4839 return await Doc.GenerateHTML();
4840 }
4841 finally
4842 {
4843 await DocSynchObj.EndWrite();
4844 }
4845 }
4846 else
4847 throw new ServiceUnavailableException("Unable to generate custom HTML error document.");
4848 }
4849 else
4850 {
4851 if (!(Doc is null))
4852 {
4853 lock (defaultDocuments)
4854 {
4855 defaultDocuments.Remove(LocalFileName);
4856 }
4857 }
4858 }
4859 }
4860
4861 return null;
4862 }
4863
4864 #endregion
4865
4866 #region Sniffers & Events
4867
4878 public static string AddWebSniffer(string SnifferId, HttpRequest Request, ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
4879 {
4880 return AddWebSniffer(SnifferId, Request, BinaryPresentationMethod.ByteCount, ComLayer, UserVariable, Privileges);
4881 }
4882
4894 public static string AddWebSniffer(string SnifferId, HttpRequest Request, BinaryPresentationMethod BinaryPresentationMethod,
4895 ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
4896 {
4897 return AddWebSniffer(SnifferId, Request, TimeSpan.FromHours(1), BinaryPresentationMethod, ComLayer, UserVariable, Privileges);
4898 }
4899
4912 public static string AddWebSniffer(string SnifferId, HttpRequest Request, TimeSpan MaxLife,
4913 BinaryPresentationMethod BinaryPresentationMethod, ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
4914 {
4915 string Resource = Request.Header.ResourcePart;
4916 int i = Resource.IndexOfAny(new char[] { '?', '#' });
4917 if (i > 0)
4918 Resource = Resource.Substring(0, i);
4919
4920 return AddWebSniffer(SnifferId, Resource, MaxLife, BinaryPresentationMethod, ComLayer, UserVariable, Privileges);
4921 }
4922
4935 public static string AddWebSniffer(string SnifferId, string PageResource, TimeSpan MaxLife,
4936 BinaryPresentationMethod BinaryPresentationMethod, ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
4937 {
4938 bool Found = false;
4939
4940 foreach (ISniffer Sniffer in ComLayer)
4941 {
4942 if (Sniffer is WebSniffer WebSniffer && WebSniffer.SnifferId == SnifferId)
4943 {
4944 Found = true;
4945 break;
4946 }
4947 }
4948
4949 if (!Found)
4950 {
4951 WebSniffer Sniffer = new WebSniffer(SnifferId, PageResource, MaxLife, BinaryPresentationMethod, ComLayer, UserVariable, Privileges);
4952 ComLayer.Add(Sniffer);
4953 }
4954
4955 return "\r\n\r\n![Sniffer](/Sniffers/Sniffer.md)\r\n\r\n";
4956 }
4957
4967 public static void AddWebEventSink(string SinkId, HttpRequest Request, string UserVariable, params string[] Privileges)
4968 {
4969 AddWebEventSink(SinkId, Request, TimeSpan.FromHours(1), UserVariable, Privileges);
4970 }
4971
4982 public static void AddWebEventSink(string SinkId, HttpRequest Request, TimeSpan MaxLife, string UserVariable, params string[] Privileges)
4983 {
4984 string Resource = Request.Header.ResourcePart;
4985 int i = Resource.IndexOfAny(new char[] { '?', '#' });
4986 if (i > 0)
4987 Resource = Resource.Substring(0, i);
4988
4989 AddWebEventSink(SinkId, Resource, MaxLife, UserVariable, Privileges);
4990 }
4991
5002 public static void AddWebEventSink(string SinkId, string PageResource, TimeSpan MaxLife, string UserVariable, params string[] Privileges)
5003 {
5004 bool Found = false;
5005
5006 foreach (IEventSink Sink in Log.Sinks)
5007 {
5008 if (Sink is WebEventSink WebEventSink && WebEventSink.ObjectID == SinkId)
5009 {
5010 Found = true;
5011 break;
5012 }
5013 }
5014
5015 if (!Found)
5016 {
5017 WebEventSink Sink = new WebEventSink(SinkId, PageResource, MaxLife, UserVariable, Privileges);
5018 Log.Register(Sink);
5019 }
5020 }
5021
5022 #endregion
5023
5024 #region Script Resources
5025
5033 public static async Task<bool> AddScriptResource(string ResourceName, Expression Expression, string ReferenceFileName)
5034 {
5035 if (!await RemoveScriptResource(ResourceName, true))
5036 return false;
5037
5038 webServer.Register(new HttpScriptResource(ResourceName, Expression, ReferenceFileName, true));
5039
5040 await RuntimeSettings.SetAsync("Gateway.ScriptResource." + ResourceName, ReferenceFileName + " ||| " + Expression.Script);
5041
5042 return true;
5043 }
5044
5052 public static async Task<bool> AddScriptResource(string ResourceName, ScriptNode Expression, string ReferenceFileName)
5053 {
5054 if (!await RemoveScriptResource(ResourceName, true))
5055 return false;
5056
5057 webServer.Register(new HttpScriptResource(ResourceName, Expression, ReferenceFileName, true));
5058
5059 await RuntimeSettings.SetAsync("Gateway.ScriptResource." + ResourceName, ReferenceFileName + " ||| " + Expression.SubExpression);
5060
5061 return true;
5062 }
5063
5069 public static Task<bool> RemoveScriptResource(string ResourceName)
5070 {
5071 return RemoveScriptResource(ResourceName, false);
5072 }
5073
5080 private static async Task<bool> RemoveScriptResource(string ResourceName, bool ConsiderNonexistantRemoved)
5081 {
5082 if (!webServer.TryGetResource(ResourceName, false, out HttpResource Resource, out string SubPath))
5083 return false;
5084
5085 if (!string.IsNullOrEmpty(SubPath))
5086 return ConsiderNonexistantRemoved;
5087
5088 if (!(Resource is HttpScriptResource))
5089 return false;
5090
5091 webServer.Unregister(Resource);
5092
5093 await RuntimeSettings.DeleteAsync("Gateway.ScriptResource." + ResourceName);
5094
5095 return true;
5096 }
5097
5098 private static async Task LoadScriptResources()
5099 {
5100 Dictionary<string, object> Settings = await RuntimeSettings.GetWhereKeyLikeAsync("Gateway.ScriptResource.*", "*");
5101
5102 foreach (KeyValuePair<string, object> Setting in Settings)
5103 {
5104 if (!(Setting.Value is string Value))
5105 {
5106 Log.Error("Invalid Runtime setting found and ignored.",
5107 new KeyValuePair<string, object>("Key", Setting.Key),
5108 new KeyValuePair<string, object>("Value", Setting.Value));
5109
5110 continue;
5111 }
5112
5113 string ResourceName = Setting.Key.Substring(23);
5114 string ReferenceFileName;
5115 int i;
5116 Expression Exp;
5117
5118 i = Value.IndexOf(" ||| ");
5119 if (i < 0)
5120 ReferenceFileName = string.Empty;
5121 else
5122 {
5123 ReferenceFileName = Value.Substring(0, i);
5124 Value = Value.Substring(i + 5);
5125 }
5126
5127 try
5128 {
5129 Exp = new Expression(Value);
5130 webServer.Register(new HttpScriptResource(ResourceName, Exp, ReferenceFileName, true));
5131 }
5132 catch (Exception ex)
5133 {
5134 Log.Error("Invalid Runtime setting script. Resource could not be added.",
5135 new KeyValuePair<string, object>("Resource", ResourceName),
5136 new KeyValuePair<string, object>("Error", ex.Message),
5137 new KeyValuePair<string, object>("ReferenceFileName", ReferenceFileName),
5138 new KeyValuePair<string, object>("Script", Value));
5139
5140 continue;
5141 }
5142 }
5143 }
5144
5151 public static Task<int> ProcessNewServiceConfigurations()
5152 {
5153 return ProcessServiceConfigurations(true);
5154 }
5155
5156 private static async Task<int> ProcessServiceConfigurations(bool OnlyIfChanged)
5157 {
5158 string[] ConfigurationFiles = Directory.GetFiles(appDataFolder, "*.config", SearchOption.TopDirectoryOnly);
5159 int NrExecuted = 0;
5160
5161 foreach (string ConfigurationFile in ConfigurationFiles)
5162 {
5163 if (await ProcessServiceConfigurationFile(ConfigurationFile, OnlyIfChanged))
5164 NrExecuted++;
5165 }
5166
5167 return NrExecuted;
5168 }
5169
5170 private const string ServiceConfigurationRoot = "ServiceConfiguration";
5171 private const string ServiceConfigurationNamespace = "http://waher.se/Schema/ServiceConfiguration.xsd";
5172
5180 public static async Task<bool> ProcessServiceConfigurationFile(string ConfigurationFileName, bool OnlyIfChanged)
5181 {
5182 try
5183 {
5184 ConfigurationFileName = Path.GetFullPath(ConfigurationFileName);
5185
5186 string DirectoryName = Path.GetDirectoryName(ConfigurationFileName);
5187 if (!DirectoryName.EndsWith(new string(Path.DirectorySeparatorChar, 1)))
5188 DirectoryName += Path.DirectorySeparatorChar;
5189
5190 if (string.Compare(DirectoryName, appDataFolder, true) != 0)
5191 return false;
5192
5193 if (!File.Exists(ConfigurationFileName))
5194 return false;
5195
5196 XmlDocument Doc = new XmlDocument();
5197 Doc.Load(ConfigurationFileName);
5198
5199 if (Doc.DocumentElement.LocalName != ServiceConfigurationRoot || Doc.DocumentElement.NamespaceURI != ServiceConfigurationNamespace)
5200 return false;
5201
5202 XSL.Validate(Path.GetFileName(ConfigurationFileName), Doc, ServiceConfigurationRoot, ServiceConfigurationNamespace,
5203 XSL.LoadSchema(typeof(Gateway).Namespace + ".Schema.ServiceConfiguration.xsd", typeof(Gateway).Assembly));
5204
5205 bool ExecuteInitScript = await Content.Markdown.Functions.InitScriptFile.NeedsExecution(ConfigurationFileName);
5206
5207 if (OnlyIfChanged && !ExecuteInitScript)
5208 return false;
5209
5210 Log.Notice("Applying Service Configurations.", ConfigurationFileName);
5211
5212 webServer.UnregisterVanityResources(ConfigurationFileName);
5213
5214 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
5215 {
5216 if (!(N is XmlElement E))
5217 continue;
5218
5219 switch (E.LocalName)
5220 {
5221 case "VanityResources":
5222 foreach (XmlNode N2 in E.ChildNodes)
5223 {
5224 if (N2 is XmlElement E2 && E2.LocalName == "VanityResource")
5225 {
5226 string RegEx = XML.Attribute(E2, "regex");
5227 string Url = XML.Attribute(E2, "url");
5228
5229 try
5230 {
5231 webServer.RegisterVanityResource(RegEx, Url, ConfigurationFileName);
5232 }
5233 catch (Exception ex)
5234 {
5235 Log.Error("Unable to register vanity resource: " + ex.Message,
5236 new KeyValuePair<string, object>("RegEx", RegEx),
5237 new KeyValuePair<string, object>("Url", Url));
5238 }
5239 }
5240 }
5241 break;
5242
5243 case "StartupScript": // Always execute
5244 Expression Exp = new Expression(E.InnerText);
5246 await Exp.EvaluateAsync(v);
5247 break;
5248
5249 case "InitializationScript": // Execute, only if changed
5250 if (ExecuteInitScript)
5251 {
5252 Exp = new Expression(E.InnerText);
5254 await Exp.EvaluateAsync(v);
5255 }
5256 break;
5257 }
5258 }
5259
5260 return true;
5261 }
5262 catch (Exception ex)
5263 {
5264 Log.Exception(ex, ConfigurationFileName);
5265 return false;
5266 }
5267 }
5268
5269 #endregion
5270 }
5271}
Helps with parsing of commong data types.
Definition: CommonTypes.cs:13
static readonly char[] CRLF
Contains the CR LF character sequence.
Definition: CommonTypes.cs:17
static KeyValuePair< string, string >[] ParseFieldValues(string Value)
Parses a set of comma or semicolon-separated field values, optionaly delimited by ' or " characters.
Definition: CommonTypes.cs:472
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:46
static string GetString(byte[] Data, Encoding DefaultEncoding)
Gets a string from its binary representation, taking any Byte Order Mark (BOM) into account.
Provides emojis from Emoji One (http://emojione.com/) stored as local files.
HTML encoder/decoder.
Definition: HtmlCodec.cs:13
const string DefaultContentType
Default Content-Type for HTML: text/html
Definition: HtmlCodec.cs:24
HtmlElement Root
Root element.
Definition: HtmlDocument.cs:72
static string GetBody(string Html)
Extracts the contents of the BODY element in a HTML string.
Base class for all HTML nodes.
Definition: HtmlNode.cs:11
abstract void Export(XmlWriter Output)
Exports the HTML document to XML.
Image encoder/decoder.
Definition: ImageCodec.cs:14
const string ContentTypeIcon
image/x-icon
Definition: ImageCodec.cs:40
Static class managing encoding and decoding of internet content.
static Encoding GetEncoding(string CharacterSet)
Gets a character encoding from its name.
Class managing GraphViz integration into Markdown documents.
Definition: GraphViz.cs:47
static void Init(string ContentRootFolder)
Initializes the GraphViz-Markdown integration.
Definition: GraphViz.cs:74
Class managing 2D XML Layout integration into Markdown documents.
Definition: XmlLayout.cs:41
static void Init(string ContentRootFolder)
Initializes the Layout2D-Markdown integration.
Definition: XmlLayout.cs:64
static bool IsRawEncodingAllowedLocked
If the IsRawEncodingAllowed setting is locked.
const string ContentType
Markdown content type.
static void AllowRawEncoding(bool Allow, bool Lock)
If raw encoding of web script should be allowed.
Contains a markdown document. This markdown document class supports original markdown,...
MarkdownSettings Settings
Markdown settings.
static string Encode(string s)
Encodes all special characters in a string so that it can be included in a markdown document without ...
object Tag
Property can be used to tag document with client-specific information.
static async Task< string > Preprocess(string Markdown, MarkdownSettings Settings, params Type[] TransparentExceptionTypes)
Preprocesses markdown text.
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,...
Contains settings that the Markdown parser uses to customize its behavior.
Variables Variables
Collection of variables. Providing such a collection enables script execution inside markdown documen...
Class managing PlantUML integration into Markdown documents.
Definition: PlantUml.cs:51
static void Init(string ContentRootFolder)
Initializes the PlantUML-Markdown integration.
Definition: PlantUml.cs:72
Contains settings that the HTML export uses to customize HTML output.
Definition: HtmlSettings.cs:7
Web Script encoder/decoder.
Definition: WsCodec.cs:14
static bool IsRawEncodingAllowedLocked
If the IsRawEncodingAllowed setting is locked.
Definition: WsCodec.cs:40
static void AllowRawEncoding(bool Allow, bool Lock)
If raw encoding of web script should be allowed.
Definition: WsCodec.cs:23
const string ContentType
Markdown content type.
Definition: WsCodec.cs:52
Static class managing loading of resources stored as embedded resources or in content files.
Definition: Resources.cs:15
static async Task< string > ReadAllTextAsync(string FileName)
Reads a text file asynchronously.
Definition: Resources.cs:205
static Task WriteAllTextAsync(string FileName, string Text)
Creates a text file asynchronously.
Definition: Resources.cs:267
Static class helping modules to find files installed on the system.
Definition: FileSystem.cs:12
static string[] GetFolders(Environment.SpecialFolder[] Folders, params string[] AppendWith)
Gets the physical locations of special folders.
Definition: FileSystem.cs:126
static string[] FindFiles(Environment.SpecialFolder[] Folders, string Pattern, bool IncludeSubfolders, bool BreakOnFirst)
Finds files in a set of folders, and optionally, their subfolders. This method only finds files in fo...
Definition: FileSystem.cs:22
static string FindLatestFile(Environment.SpecialFolder[] Folders, string Pattern, bool IncludeSubfolders)
Finds the latest file matching a search pattern, by searching in a set of folders,...
Definition: FileSystem.cs:177
Plain text encoder/decoder.
const string DefaultContentType
text/plain
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
static XmlException AnnotateException(XmlException ex)
Creates a new XML Exception object, with reference to the source XML file, for information.
Definition: XML.cs:1588
Static class managing loading of XSL resources stored as embedded resources or in content files.
Definition: XSL.cs:15
static XmlSchema LoadSchema(string ResourceName)
Loads an XML schema from an embedded resource.
Definition: XSL.cs:23
static void Validate(string ObjectID, XmlDocument Xml, params XmlSchema[] Schemas)
Validates an XML document given a set of XML schemas.
Definition: XSL.cs:118
Class representing an event.
Definition: Event.cs:10
void Avoid(IEventSink EventSink)
If the event sink EventSink should be avoided when logging the event.
Definition: Event.cs:189
string Facility
Facility can be either a facility in the network sense or in the system sense.
Definition: Event.cs:151
Outputs sniffed data to an XML file.
Filters incoming events and passes remaining events to a secondary event sink.
Definition: EventFilter.cs:9
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static string CleanStackTrace(string StackTrace)
Cleans a Stack Trace string, removing entries from the asynchronous execution model,...
Definition: Log.cs:184
static IEventSink[] Sinks
Registered sinks.
Definition: Log.cs:122
static void Register(IEventSink EventSink)
Registers an event sink with the event log. Call Unregister(IEventSink) to unregister it,...
Definition: Log.cs:29
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
Definition: Log.cs:566
static void Critical(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a critical event.
Definition: Log.cs:1017
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Definition: Log.cs:682
static void Emergency(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an emergency event.
Definition: Log.cs:1437
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Definition: Log.cs:334
static void Notice(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a notice event.
Definition: Log.cs:450
static void Debug(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a debug event.
Definition: Log.cs:218
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:1227
static async void Event(Event Event)
Logs an event. It will be distributed to registered event sinks.
Definition: Log.cs:128
virtual string ObjectID
Object ID, used when logging events.
Definition: LogObject.cs:25
Creates an even sink that stores incoming (logged) events in the local object database,...
override async Task Queue(Event Event)
Queues an event to be output.
Event sink sending events to a destination over the XMPP network.
The ClientEvents class allows applications to push information asynchronously to web clients connecte...
Definition: ClientEvents.cs:51
static Task< int > PushEvent(string[] TabIDs, string Type, object Data)
Puses an event to a set of Tabs, given their Tab IDs.
static string[] GetTabIDs()
Gets all open Tab IDs.
Web-socket binding method for the ClientEvents class. It allows clients connect to the gateway using ...
Event sink that forwards events as notification messages to administrators.
Analyzes exceptions and extracts basic statistics.
Definition: Analyze.cs:14
static void Process(string ExceptionFileName, string OutputFileName)
Analyzes exceptions and extracts basic statistics.
Definition: Analyze.cs:23
Information about an exportable folder category
Definition: Export.cs:560
Static class managing data export.
Definition: Export.cs:19
static async Task< string > GetFullExportFolderAsync()
Full path to export folder.
Definition: Export.cs:23
static FolderCategory[] GetRegisteredFolders()
Gets registered exportable folders.
Definition: Export.cs:543
static async Task< string > GetFullKeyExportFolderAsync()
Full path to key folder.
Definition: Export.cs:36
static async Task SetLastBackupAsync(DateTime Value)
Set Timestamp of last backup.
Definition: Export.cs:506
static async Task< long > GetKeepMonthsAsync()
For how many months the monthly backups are kept.
Definition: Export.cs:430
static async Task< long > GetKeepYearsAsync()
For how many years the yearly backups are kept.
Definition: Export.cs:456
static async Task< DateTime > GetLastBackupAsync()
Get Timestamp of last backup.
Definition: Export.cs:495
static async Task< long > GetKeepDaysAsync()
For how many days backups are kept.
Definition: Export.cs:404
static async Task< bool > GetAutomaticBackupsAsync()
If automatic backups are activated
Definition: Export.cs:378
static async Task< TimeSpan > GetBackupTimeAsync()
Time of day to start performing backups.
Definition: Export.cs:482
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
static WebMenuItem[] GetSettingsMenu(HttpRequest Request, string UserVariable)
Gets the settings menu.
Definition: Gateway.cs:4334
static HttpxProxy HttpxProxy
HTTPX Proxy resource
Definition: Gateway.cs:3315
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:2354
static HttpServer HttpServer
HTTP Server
Definition: Gateway.cs:3299
static string InstanceName
Name of the current instance. Default instance=string.Empty
Definition: Gateway.cs:2364
static async Task< bool > ProcessServiceConfigurationFile(string ConfigurationFileName, bool OnlyIfChanged)
Processes a Service Configuration File. This method should be called for each service configuration f...
Definition: Gateway.cs:5180
static Task Terminate()
Raises the OnTerminate event handler, letting the container executable know the application needs to ...
Definition: Gateway.cs:2455
static Task NewMomentaryValues(IEnumerable< Field > Values)
Reports newly measured values.
Definition: Gateway.cs:3578
static void RegisterHandler(Type PersonalEventType, EventHandlerAsync< PersonalEventNotificationEventArgs > Handler)
Registers an event handler of a specific type of personal events.
Definition: Gateway.cs:3616
static string[] FindFiles(string[] Folders, string Pattern, bool IncludeSubfolders, int MaxCount)
Finds files in a set of folders, and optionally, their subfolders. This method only finds files in fo...
Definition: Gateway.cs:4630
static string GetMultiFormatChatMessageXml(string Text, string Html, string Markdown)
Gets XML for a multi-formatted chat message.
Definition: Gateway.cs:4090
static bool IsDomain(string DomainOrHost, bool IncludeAlternativeDomains)
If a domain or host name represents the gateway.
Definition: Gateway.cs:4258
static IUser AssertUserAuthenticated(HttpRequest Request, string Privilege)
Makes sure a request is being made from a session with a successful user login.
Definition: Gateway.cs:3041
static double NextDouble()
Generates a new floating-point value between 0 and 1, using a cryptographic random number generator.
Definition: Gateway.cs:3487
static string AddWebSniffer(string SnifferId, HttpRequest Request, ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
Creates a web sniffer, and adds it to a sniffable object.
Definition: Gateway.cs:4878
static string FindLatestFile(string[] Folders, string Pattern, int SubfolderDepth)
Finds the latest file matching a search pattern, by searching in a set of folders,...
Definition: Gateway.cs:4697
static Task SendNotification(PixelInformation Pixels)
Sends an image as a notification message to configured notification recipients.
Definition: Gateway.cs:3835
static X509Certificate2 Certificate
Domain certificate.
Definition: Gateway.cs:2349
static GetDatabaseProviderEventHandler GetDatabaseProvider
Event raised when the Gateway requires its database provider from the host.
Definition: Gateway.cs:2054
static Socks5Proxy Socks5Proxy
SOCKS5 Proxy
Definition: Gateway.cs:3323
static void AddWebEventSink(string SinkId, string PageResource, TimeSpan MaxLife, string UserVariable, params string[] Privileges)
Creates a web event sink, and registers it with Log.
Definition: Gateway.cs:5002
static Task SendChatMessage(string Markdown, string To, string MessageId)
Sends a chat message to a recipient.
Definition: Gateway.cs:3960
static string FindLatestFile(string[] Folders, string Pattern, bool IncludeSubfolders)
Finds the latest file matching a search pattern, by searching in a set of folders,...
Definition: Gateway.cs:4683
static string[] FindFiles(string[] Folders, string Pattern, int SubfolderDepth, int MaxCount)
Finds files in a set of folders, and optionally, their subfolders. This method only finds files in fo...
Definition: Gateway.cs:4644
static Task SendNotification(string Markdown, string MessageId)
Sends a notification message to configured notification recipients.
Definition: Gateway.cs:3854
static async Task SendChatMessageUpdate(string Markdown, string To, string MessageId, string ThreadId)
Sends a chat message update to a recipient.
Definition: Gateway.cs:3996
static EventHandlerAsync< Events.CertificateEventArgs > OnNewCertificate
Event raised when a new server certificate has been generated.
Definition: Gateway.cs:2015
static void AddWebEventSink(string SinkId, HttpRequest Request, TimeSpan MaxLife, string UserVariable, params string[] Privileges)
Creates a web event sink, and registers it with Log.
Definition: Gateway.cs:4982
static Task< bool > PetitionLegalIdentity(string LegalId, string PetitionId, string Purpose, EventHandlerAsync< LegalIdentityPetitionResponseEventArgs > Callback, TimeSpan Timeout)
Petitions information about a legal identity from its owner.
Definition: Gateway.cs:4536
static SensorClient SensorClient
XMPP Sensor Client.
Definition: Gateway.cs:3227
static string[] FindFiles(string[] Folders, string Pattern, bool IncludeSubfolders, bool BreakOnFirst)
Finds files in a set of folders, and optionally, their subfolders. This method only finds files in fo...
Definition: Gateway.cs:4616
static Task< bool > Start(bool ConsoleOutput, bool LoopbackIntefaceAvailable)
Starts the gateway.
Definition: Gateway.cs:229
static Task< bool > PetitionContract(string ContractId, string PetitionId, string Purpose, string Password, EventHandlerAsync< ContractPetitionResponseEventArgs > Callback, TimeSpan Timeout)
Petitions information about a smart contract from its owner.
Definition: Gateway.cs:4583
static IUser AssertUserAuthenticated(HttpRequest Request, string[] Privileges)
Makes sure a request is being made from a session with a successful user login.
Definition: Gateway.cs:3073
static async Task SendGroupChatMessageUpdate(string Markdown, string To, string MessageId, string ThreadId)
Sends a group chat message update to a recipient.
Definition: Gateway.cs:4054
static RequiredUserPrivileges LoggedIn(string[] Privileges)
Authentication mechanism that makes sure the call is made from a session with a valid authenticated u...
Definition: Gateway.cs:3001
static Task< string > GetMultiFormatChatMessageXml(string Markdown)
Gets XML for a multi-formatted chat message.
Definition: Gateway.cs:4065
static async Task DoBackup()
Performs a backup of the system.
Definition: Gateway.cs:3684
static string RuntimeFolder
Runtime folder.
Definition: Gateway.cs:2374
static string ConfigFilePath
Full path to Gateway.config file.
Definition: Gateway.cs:2413
static LoginAuditor LoginAuditor
Current Login Auditor. Should be used by modules accepting user logins, to protect the system from un...
Definition: Gateway.cs:3033
static Task< bool > Start(bool ConsoleOutput)
Starts the gateway.
Definition: Gateway.cs:218
static async Task Stop()
Stops the gateway.
Definition: Gateway.cs:2187
static async Task< string > GetMultiFormatChatMessageXml(string Markdown, bool TextVersion, bool HtmlVersion)
Gets XML for a multi-formatted chat message.
Definition: Gateway.cs:4077
static async Task SendChatMessage(string Markdown, string To, string MessageId, string ThreadId)
Sends a chat message to a recipient.
Definition: Gateway.cs:3972
static string AddWebSniffer(string SnifferId, HttpRequest Request, TimeSpan MaxLife, BinaryPresentationMethod BinaryPresentationMethod, ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
Creates a web sniffer, and adds it to a sniffable object.
Definition: Gateway.cs:4912
static bool TryGetDefaultPage(HttpRequest Request, out string DefaultPage)
Tries to get the default page of a host.
Definition: Gateway.cs:2473
static SynchronizationClient SynchronizationClient
XMPP Synchronization Client.
Definition: Gateway.cs:3251
static PepClient PepClient
XMPP Personal Eventing Protocol (PEP) Client.
Definition: Gateway.cs:3259
static Task NewMomentaryValues(IThingReference Reference, IEnumerable< Field > Values)
Reports newly measured values.
Definition: Gateway.cs:3589
static Task< int > ProcessNewServiceConfigurations()
Processes new Service Configuration Files. This method should be called after installation of new ser...
Definition: Gateway.cs:5151
const string GatewayConfigNamespace
http://waher.se/Schema/GatewayConfiguration.xsd
Definition: Gateway.cs:140
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
Definition: Gateway.cs:3534
static string AppDataFolder
Application data folder.
Definition: Gateway.cs:2369
static string[] GetProtocols()
Gets the protocol names defined in the configuration file.
Definition: Gateway.cs:2437
static int[] GetConfigPorts(string Protocol)
Gets the port numbers defined for a given protocol in the configuration file.
Definition: Gateway.cs:2420
static void AddWebEventSink(string SinkId, HttpRequest Request, string UserVariable, params string[] Privileges)
Creates a web event sink, and registers it with Log.
Definition: Gateway.cs:4967
static async Task< bool > AddScriptResource(string ResourceName, ScriptNode Expression, string ReferenceFileName)
Adds a script resource to the web server hosted by the gateway.
Definition: Gateway.cs:5052
static Task SendNotification(Graph Graph)
Sends a graph as a notification message to configured notification recipients.
Definition: Gateway.cs:3826
static IUser AssertUserAuthenticated(Variables Session, string Privilege)
Makes sure a request is being made from a session with a successful user login.
Definition: Gateway.cs:3052
static string GetUrl(string LocalResource)
Gets a URL for a resource.
Definition: Gateway.cs:4167
static async Task RequestContractSignature(Contract Contract, string Role, string Purpose)
Requests the operator to sign a smart contract.
Definition: Gateway.cs:4405
static Task< bool > PetitionContract(string ContractId, string PetitionId, string Purpose, EventHandlerAsync< ContractPetitionResponseEventArgs > Callback, TimeSpan Timeout)
Petitions information about a smart contract from its owner.
Definition: Gateway.cs:4567
static ContractsClient ContractsClient
XMPP Contracts Client, if such a compoent is available on the XMPP broker.
Definition: Gateway.cs:4375
static async Task< string > GetCustomErrorHtml(HttpRequest Request, string LocalFileName, string ContentType, byte[] Content)
Gets a custom error HTML document.
Definition: Gateway.cs:4731
static async Task< bool > AddScriptResource(string ResourceName, Expression Expression, string ReferenceFileName)
Adds a script resource to the web server hosted by the gateway.
Definition: Gateway.cs:5033
const string GatewayConfigLocalName
GatewayConfiguration
Definition: Gateway.cs:135
static Task SendGroupChatMessage(string Markdown, string To)
Sends a group chat message to a recipient.
Definition: Gateway.cs:4007
static Task SendGroupChatMessage(string Markdown, string To, string MessageId)
Sends a group chat message to a recipient.
Definition: Gateway.cs:4018
static byte[] ComputeUserPasswordHash(string UserName, string Password)
Computes a hash digest based on a user name and a password, and the current domain.
Definition: Gateway.cs:1917
static HttpxServer HttpxServer
HTTPX Server
Definition: Gateway.cs:3307
static Task SendNotificationUpdate(string Markdown, string MessageId)
Sends a notification message to configured notification recipients.
Definition: Gateway.cs:3864
static GetDataSources GetDataSources
Event raised when the Gateway requires a set of data sources to publish.
Definition: Gateway.cs:2059
static ThingRegistryClient ThingRegistryClient
XMPP Thing Registry Client.
Definition: Gateway.cs:3195
static string FindLatestFile(Environment.SpecialFolder[] Folders, string Pattern, bool IncludeSubfolders)
Finds the latest file matching a search pattern, by searching in a set of folders,...
Definition: Gateway.cs:4669
static DateTime ScheduleEvent(ScheduledEventCallback Callback, DateTime When, object State)
Schedules a one-time event.
Definition: Gateway.cs:3452
static Task SendChatMessage(string Markdown, string To)
Sends a chat message to a recipient.
Definition: Gateway.cs:3949
static Task< bool > RemoveScriptResource(string ResourceName)
Removes a script resource from the web server hosted by the gateway.
Definition: Gateway.cs:5069
static bool TryGetDefaultPage(string Host, out string DefaultPage)
Tries to get the default page of a host.
Definition: Gateway.cs:2484
static ConcentratorClient ConcentratorClient
XMPP Concentrator Client.
Definition: Gateway.cs:3243
static string[] FindFiles(Environment.SpecialFolder[] Folders, string Pattern, bool IncludeSubfolders, bool BreakOnFirst)
Finds files in a set of folders, and optionally, their subfolders. This method only finds files in fo...
Definition: Gateway.cs:4602
static bool UnregisterHandler(Type PersonalEventType, EventHandlerAsync< PersonalEventNotificationEventArgs > Handler)
Unregisters an event handler of a specific type of personal events.
Definition: Gateway.cs:3627
static int NextInteger(int Max)
Returns a non-negative random integer that is less than the specified maximum.
Definition: Gateway.cs:3510
static Task SendChatMessageUpdate(string Markdown, string To, string MessageId)
Sends a chat message update to a recipient.
Definition: Gateway.cs:3984
static async Task< bool > Start(bool ConsoleOutput, bool LoopbackIntefaceAvailable, string InstanceName)
Starts the gateway.
Definition: Gateway.cs:241
static DateTime StartTime
Timepoint of starting the gateway.
Definition: Gateway.cs:211
static string[] GetFolders(Environment.SpecialFolder[] Folders, params string[] AppendWith)
Gets the physical locations of special folders.
Definition: Gateway.cs:4655
static CaseInsensitiveString[] GetNotificationAddresses()
Returns configured notification addresses.
Definition: Gateway.cs:3922
static Task NewMomentaryValues(IThingReference Reference, params Field[] Values)
Reports newly measured values.
Definition: Gateway.cs:3568
static IUser AssertUserAuthenticated(Variables Session, string[] Privileges)
Makes sure a request is being made from a session with a successful user login.
Definition: Gateway.cs:3084
static int RegisterServiceCommand(EventHandlerAsync Callback)
Registers an administrative service command.
Definition: Gateway.cs:3384
static XmppClient XmppClient
XMPP Client connection of gateway.
Definition: Gateway.cs:3187
static CoapEndpoint CoapEndpoint
CoAP Endpoint
Definition: Gateway.cs:3331
static Task< bool > PetitionLegalIdentity(string LegalId, string PetitionId, string Purpose, string Password, EventHandlerAsync< LegalIdentityPetitionResponseEventArgs > Callback, TimeSpan Timeout)
Petitions information about a legal identity from its owner.
Definition: Gateway.cs:4552
static void AppendMultiFormatChatMessageXml(StringBuilder Xml, string Text, string Html, string Markdown)
Appends the XML for a multi-formatted chat message to a string being built.
Definition: Gateway.cs:4104
static string AddWebSniffer(string SnifferId, string PageResource, TimeSpan MaxLife, BinaryPresentationMethod BinaryPresentationMethod, ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
Creates a web sniffer, and adds it to a sniffable object.
Definition: Gateway.cs:4935
static bool CancelScheduledEvent(DateTime When)
Cancels a scheduled event.
Definition: Gateway.cs:3474
static bool UnregisterServiceCommand(EventHandlerAsync Callback)
Unregisters an administrative service command.
Definition: Gateway.cs:3407
static string GetUrl(string LocalResource, HttpServer Server)
Gets a URL for a resource.
Definition: Gateway.cs:4178
static Task SendNotification(string Markdown)
Sends a notification message to configured notification recipients.
Definition: Gateway.cs:3844
static RequiredUserPrivileges LoggedIn(string UserVariable, string LoginPage, string[] Privileges)
Authentication mechanism that makes sure the call is made from a session with a valid authenticated u...
Definition: Gateway.cs:3024
static DateTime ScheduleEvent(ScheduledEventCallbackAsync Callback, DateTime When, object State)
Schedules a one-time event.
Definition: Gateway.cs:3464
static Task SendGroupChatMessageUpdate(string Markdown, string To, string MessageId)
Sends a group chat message update to a recipient.
Definition: Gateway.cs:4042
static RequiredUserPrivileges LoggedIn(string UserVariable, string[] Privileges)
Authentication mechanism that makes sure the call is made from a session with a valid authenticated u...
Definition: Gateway.cs:3012
static async Task< bool > ExecuteServiceCommand(int CommandNr)
Executes a service command.
Definition: Gateway.cs:3349
static SoftwareUpdateClient SoftwareUpdateClient
XMPP Software Updates Client, if such a compoent is available on the XMPP broker.
Definition: Gateway.cs:3283
static ControlClient ControlClient
XMPP Control Client.
Definition: Gateway.cs:3235
static string AddWebSniffer(string SnifferId, HttpRequest Request, BinaryPresentationMethod BinaryPresentationMethod, ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
Creates a web sniffer, and adds it to a sniffable object.
Definition: Gateway.cs:4894
static ProvisioningClient ProvisioningClient
XMPP Provisioning Client.
Definition: Gateway.cs:3203
static MailClient MailClient
XMPP Mail Client, if support for mail-extensions is available on the XMPP broker.
Definition: Gateway.cs:3291
static async Task SendGroupChatMessage(string Markdown, string To, string MessageId, string ThreadId)
Sends a group chat message to a recipient.
Definition: Gateway.cs:4030
static Task PublishPersonalEvent(IPersonalEvent PersonalEvent)
Publishes a personal event on the XMPP network.
Definition: Gateway.cs:3603
const string GatewayConfigLocalFileName
Gateway.config
Definition: Gateway.cs:130
static void CheckLocalLogin(HttpRequest Request)
Checks if a web request comes from the local host in the current session. If so, the user is automati...
Definition: Gateway.cs:2629
static Task NewMomentaryValues(params Field[] Values)
Reports newly measured values.
Definition: Gateway.cs:3557
static DomainConfiguration Instance
Current instance of configuration.
string Password
Password for PFX file, if any.
int DynDnsInterval
Interval (in seconds) for checking if the IP address has changed.
string[] AlternativeDomains
Alternative domain names
byte[] PFX
PFX container for certificate and private key, if available.
bool UseDomainName
If the server uses a domain name.
bool DynamicDns
If the server uses a dynamic DNS service.
bool UseEncryption
If the server uses server-side encryption.
static NotificationConfiguration Instance
Current instance of configuration.
CaseInsensitiveString[] Addresses
Notification addresses.
Abstract base class for system configurations.
virtual Task InitSetup(HttpServer WebServer)
Initializes the setup object.
bool Complete
If the configuration is complete.
abstract Task< string > Title(Language Language)
Gets a title for the system configuration.
virtual Task< bool > SetupConfiguration(HttpServer WebServer)
Waits for the user to provide configuration.
abstract Task ConfigureSystem()
Is called during startup to configure the system.
abstract int Priority
Priority of the setting. Configurations are sorted in ascending order.
abstract string Resource
Resource to be redirected to, to perform the configuration.
virtual Task< bool > SimplifiedConfiguration()
Simplified configuration by configuring simple default values.
abstract Task< bool > EnvironmentConfiguration()
Environment configuration by configuring values available in environment variables.
virtual Task UnregisterSetup(HttpServer WebServer)
Unregisters the setup object.
virtual Task MakeCompleted()
Sets the configuration task as completed.
abstract void SetStaticInstance(ISystemConfiguration Configuration)
Sets the static instance of the configuration.
virtual Task CleanupAfterConfiguration(HttpServer WebServer)
Cleans up after configuration has been performed.
Represents an item in a web menu.
Definition: WebMenuItem.cs:11
string LegalIdentities
JID of legal identities component.
string SoftwareUpdates
JID of software updates component.
XmppCredentials GetCredentials()
Gets connection credentials.
string PubSub
JID of publish/subscribe component.
static XmppConfiguration Instance
Current instance of configuration.
string MultiUserChat
JID of Multi-User Chat service.
Abstract base class for export formats.
Definition: ExportFormat.cs:14
static void UpdateClientsFileUpdated(string FileName, long Length, DateTime Created)
Updates the status of a file on all pages viewing backup files
static void UpdateClientsFileDeleted(string FileName)
Removes a file from all pages viewing backup files
Represents a file-based resource that can have custom values depending on what domain the resource is...
Provides a resource that allows the caller to login to the gateway through a POST method call.
Definition: Login.cs:19
Logs the user out from the gateway.
Definition: Logout.cs:11
Authentication mechanism that makes sure the user has an established session with the IoT Gateway,...
Sending events to the corresponding web page(s).
Definition: WebEventSink.cs:15
Sending sniffer events to the corresponding web page(s).
Definition: WebSniffer.cs:20
CoAP client. CoAP is defined in RFC7252: https://tools.ietf.org/html/rfc7252
Definition: CoapEndpoint.cs:33
void Dispose()
IDisposable.Dispose
ISniffer[] Sniffers
Registered sniffers.
virtual void Add(ISniffer Sniffer)
ICommunicationLayer.Add
Event arguments for custom error content events.
void SetContent(string ContentType, byte[] Content)
Sets custom content to return.
HttpRequest Request
Current request object.
string ContentType
Content-Type of any content.
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
static ForbiddenException AccessDenied(string ObjectId, string ActorId, string MissingPrivilege)
Returns a ForbiddenException object, and logs a entry in the event log about the event.
Accept-Encoding HTTP Field header. (RFC 2616, §14.3)
static void ContentEncodingsReconfigured()
If Content-Encodings have been reconfigured.
Accept HTTP Field header. (RFC 2616, §14.1)
bool IsAcceptable(string Alternative)
Checks if an alternative is acceptable to the client sending a request.
Base class of all HTTP Exceptions.
string Value
HTTP Field Value
Definition: HttpField.cs:31
Publishes a folder with all its files and subfolders through HTTP GET, with optional support for PUT,...
void AllowTypeConversion(params string[] ContentTypes)
Enables content conversion on files in this folder, and its subfolders. If no content types are speci...
void AddDefaultResponseHeader(string Key, string Value)
Adds a default HTTP Response header that will be returned in responses for resources in the folder.
static void ProtectContentType(string ContentType)
Protects a content type, so that it cannot be retrieved in raw format by external parties through any...
An HTTP redirection resource. Incoming requests are redirected to another location.
HttpFieldHost Host
Host HTTP Field header. (RFC 2616, §14.23)
HttpFieldAccept Accept
Accept HTTP Field header. (RFC 2616, §14.1)
bool TryGetQueryParameter(string QueryParameter, out string Value)
Tries to get the value of an individual query parameter, if available.
string ResourcePart
Contains original resource part of request.
Represents an HTTP request.
Definition: HttpRequest.cs:18
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:134
string RemoteEndPoint
Remote end-point.
Definition: HttpRequest.cs:195
Variables Session
Contains session states, if the resource requires sessions, or null otherwise.
Definition: HttpRequest.cs:164
Base class for all HTTP resources.
Definition: HttpResource.cs:23
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:21
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
async Task Write(byte[] Data)
Returns binary data in the response.
An HTTP Reverse proxy resource. Incoming requests are reverted to a another web server for processing...
Publishes a web resource whose contents is produced by script.
Implements an HTTP server.
Definition: HttpServer.cs:36
void AddHttpsPorts(params int[] HttpsPorts)
Opens additional HTTPS ports, if not already open.
Definition: HttpServer.cs:539
int[] OpenHttpPorts
HTTP Ports successfully opened.
Definition: HttpServer.cs:693
void RegisterVanityResource(string RegexPattern, string MapTo)
Registers a vanity resource.
Definition: HttpServer.cs:1963
static Variables CreateVariables()
Creates a new collection of variables, that contains access to the global set of variables.
Definition: HttpServer.cs:1604
void ConfigureMutualTls(ClientCertificates ClientCertificates, bool TrustClientCertificates, bool LockSettings)
Configures Mutual-TLS capabilities of the server. Affects all connections, all resources.
Definition: HttpServer.cs:866
void UpdateCertificate(X509Certificate ServerCertificate)
Updates the server certificate
Definition: HttpServer.cs:854
ILoginAuditor LoginAuditor
Reference to login-auditor to help remove malicious users from the server.
Definition: HttpServer.cs:1270
IPAddress[] LocalIpAddresses
IP Addresses receiving requests on.
Definition: HttpServer.cs:715
HttpResource Register(HttpResource Resource)
Registers a resource with the server.
Definition: HttpServer.cs:1287
const int DefaultHttpPort
Default HTTP Port (80).
Definition: HttpServer.cs:40
bool TryGetResource(string ResourceName, out HttpResource Resource, out string SubPath)
Tries to get a resource from the server.
Definition: HttpServer.cs:1463
void Dispose()
IDisposable.Dispose
Definition: HttpServer.cs:645
int[] OpenPorts
Ports successfully opened.
Definition: HttpServer.cs:682
int[] OpenHttpsPorts
HTTPS Ports successfully opened.
Definition: HttpServer.cs:704
bool Unregister(HttpResource Resource)
Unregisters a resource from the server.
Definition: HttpServer.cs:1438
const int DefaultHttpsPort
Default HTTPS port (443).
Definition: HttpServer.cs:45
async void NetworkChanged()
Adapts the server to changes in the network. This method can be called automatically by calling the c...
Definition: HttpServer.cs:271
int UnregisterVanityResources(object Tag)
Unregisters vanity resources tagged with a specific object.
Definition: HttpServer.cs:1996
override void Add(ISniffer Sniffer)
ICommunicationLayer.Add
Definition: HttpServer.cs:1195
void AddHttpPorts(params int[] HttpPorts)
Opens additional HTTP ports, if not already open.
Definition: HttpServer.cs:377
The server has not found anything matching the Request-URI. No indication is given of whether the con...
The server is currently unable to handle the request due to a temporary overloading or maintenance of...
The requested resource resides temporarily under a different URI. Since the redirection MAY be altere...
Module that controls the life cycle of communication.
static bool Stopping
If the system is stopping.
Outputs sniffed data to the Console Output, serialized by ConsoleOut.
Outputs sniffed data to an XML file.
Provides help with managing avatars.
Definition: AvatarClient.cs:25
override void Dispose()
Disposes of the extension.
Definition: AvatarClient.cs:98
AvatarClient(XmppClient Client)
Provides help with managing avatars.
Definition: AvatarClient.cs:38
Implements an XMPP concentrator client interface.
override void Dispose()
Disposes of the extension.
Implements an XMPP concentrator server interface.
override void Dispose()
Disposes of the extension.
static Task< ConcentratorServer > Create(XmppClient Client, params IDataSource[] DataSources)
Creates an XMPP concentrator server interface.
Contains the definition of a contract
Definition: Contract.cs:22
string Provider
JID of the Trust Provider hosting the contract
Definition: Contract.cs:84
Role[] Roles
Roles defined in the smart contract.
Definition: Contract.cs:240
string ContractId
Contract identity
Definition: Contract.cs:65
Adds support for legal identities, smart contracts and signatures to an XMPP client.
Task< bool > LoadKeys(bool CreateIfNone)
Loads keys from the underlying persistence layer.
override void Dispose()
Disposes of the extension.
void SetKeySettingsInstance(string InstanceName, bool Locked)
Sets the key settings instance name.
Class defining a role
Definition: Role.cs:7
Implements an XMPP control client interface.
Event arguments for sender validation events.
string FromBareJID
Bare JID of resource sending the stanza.
void Reject()
Called from an event handler to reject the sender.
void Accept()
Called from an event handler to accept the sender.
Implements a Proxy resource that allows Web clients to fetch HTTP-based resources over HTTPX.
Definition: HttpxProxy.cs:19
void Dispose()
IDisposable.Dispose
Definition: HttpxProxy.cs:76
override void Dispose()
IDisposable.Dispose
Definition: HttpxServer.cs:93
Class sending and receiving binary streams over XMPP using XEP-0047: In-band Bytestreams: https://xmp...
Definition: IbbClient.cs:20
override void Dispose()
Disposes of the extension.
Definition: IbbClient.cs:49
IbbClient(XmppClient Client, int MaxBlockSize)
Class sending and receiving binary streams over XMPP using XEP-0047: In-band Bytestreams: https://xmp...
Definition: IbbClient.cs:37
Client managing communication with a Multi-User-Chat service. https://xmpp.org/extensions/xep-0045....
override void Dispose()
Disposes of the extension.
Client providing support for server mail-extension.
Definition: MailClient.cs:18
override void Dispose()
Disposes of the extension.
Definition: MailClient.cs:35
Event arguments for mail message events
Class managing a SOCKS5 proxy associated with the current XMPP server.
Definition: Socks5Proxy.cs:19
bool HasProxy
If a SOCKS5 proxy has been detected.
Definition: Socks5Proxy.cs:70
Task StartSearch(EventHandlerAsync Callback)
Starts the search of SOCKS5 proxies.
Definition: Socks5Proxy.cs:91
Client managing the Personal Eventing Protocol (XEP-0163). https://xmpp.org/extensions/xep-0163....
Definition: PepClient.cs:19
Task Publish(string Node, EventHandlerAsync< ItemResultEventArgs > Callback, object State)
Publishes an item on a node.
Definition: PepClient.cs:110
void RegisterHandler(Type PersonalEventType, EventHandlerAsync< PersonalEventNotificationEventArgs > Handler)
Registers an event handler of a specific type of personal events.
Definition: PepClient.cs:345
override void Dispose()
Disposes of the extension.
Definition: PepClient.cs:55
PubSubClient PubSubClient
PubSubClient used for the Personal Eventing Protocol. Use this client to perform administrative tasks...
Definition: PepClient.cs:95
bool UnregisterHandler(Type PersonalEventType, EventHandlerAsync< PersonalEventNotificationEventArgs > Handler)
Unregisters an event handler of a specific type of personal events.
Definition: PepClient.cs:380
Event argument base class for node information and JID events.
bool IsPublic
If the device is considered a public device, meaning it's available in searches in the thing registry...
Abstract base class for all meta-data tags.
Definition: MetaDataTag.cs:9
Implements an XMPP provisioning client interface.
override void Dispose()
Disposes of the extension.
Event arguments for Registration callbacks.
Implements an XMPP thing registry client interface.
Task RegisterThing(MetaDataTag[] MetaDataTags, EventHandlerAsync< RegistrationEventArgs > Callback, object State)
Registers a thing in the Thing Registry. Only things that does not have an owner can register with th...
override void Dispose()
Disposes of the extension.
Client managing communication with a Publish/Subscribe component. https://xmpp.org/extensions/xep-006...
Definition: PubSubClient.cs:20
Maintains information about an item in the roster.
Definition: RosterItem.cs:75
SubscriptionState State
roup Current subscription state.
Definition: RosterItem.cs:268
Implements an XMPP sensor client interface.
Definition: SensorClient.cs:21
override void Dispose()
Disposes of the extension.
Task NewMomentaryValues(params Field[] Values)
Reports newly measured values.
Implements an XMPP interface for remote software updates.
override void Dispose()
Disposes of the extension.
Implements the clock synchronization extesion as defined by the Neuro-Foundation (neuro-foundation....
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:59
XmppState State
Current state of connection.
Definition: XmppClient.cs:985
Task OfflineAndDisposeAsync()
Sends an offline presence, and then disposes the object by calling DisposeAsync.
Definition: XmppClient.cs:1119
Task SendMessage(MessageType Type, string To, string CustomXml, string Body, string Subject, string Language, string ThreadId, string ParentThreadId)
Sends a simple chat message
Definition: XmppClient.cs:5395
const string NamespaceServiceDiscoveryInfo
http://jabber.org/protocol/disco#info
Definition: XmppClient.cs:118
Task RequestPresenceSubscription(string BareJid)
Requests subscription of presence information from a contact.
Definition: XmppClient.cs:4919
async Task Reconnect()
Reconnects a client after an error or if it's offline. Reconnecting, instead of creating a completely...
Definition: XmppClient.cs:1269
Task Connect()
Connects the client.
Definition: XmppClient.cs:641
string Domain
Current Domain.
Definition: XmppClient.cs:3453
RosterItem GetRosterItem(string BareJID)
Gets a roster item.
Definition: XmppClient.cs:4522
Class containing credentials for an XMPP client connection.
string Events
JID of entity to whom events should be sent. Leave blank if events are not to be forwarded.
bool Sniffer
If a sniffer is to be used ('true' or 'false'). If 'true', network communication will be output to th...
string ThingRegistry
JID of Thing Registry to use. Leave blank if no thing registry is to be used.
string Provisioning
JID of Provisioning Server to use. Leave blank if no thing registry is to be used.
virtual void Dispose()
Disposes of the extension.
Represents a case-insensitive string.
static readonly CaseInsensitiveString Empty
Empty case-insensitive string
Event arguments for collection repaired events.
FlagSource[] Flagged
If the collection have been flagged as corrupt, and from what stack traces. Is null,...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static bool HasProvider
If a database provider is registered.
Definition: Database.cs:79
static void Register(IDatabaseProvider DatabaseProvider)
Registers a database provider for use from the static Database class, throughout the lifetime of the ...
Definition: Database.cs:31
static IDatabaseProvider Provider
Registered database provider.
Definition: Database.cs:57
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:717
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:247
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
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.
Source of code flagging a collection for repair.
Definition: FlagSource.cs:9
int Count
Number of times the collection has been flagged from this source.
Definition: FlagSource.cs:41
string StackTrace
Stack trace of source flagging the collection.
Definition: FlagSource.cs:35
string Reason
Reason for flagging collection.
Definition: FlagSource.cs:30
Static interface for ledger persistence. In order to work, a ledger provider has to be assigned to it...
Definition: Ledger.cs:14
static bool HasProvider
If a ledger provider is registered.
Definition: Ledger.cs:105
static ILedgerProvider Provider
Registered ledger provider.
Definition: Ledger.cs:83
Orders modules in dependency order.
Static class, loading and initializing assemblies dynamically.
Definition: TypesLoader.cs:21
static void Initialize()
Initializes the inventory engine, registering types and interfaces available in Types.
Definition: TypesLoader.cs:25
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static Task< bool > StartAllModules(int Timeout)
Starts all loaded modules.
Definition: Types.cs:457
static void SetModuleParameter(string Name, object Value)
Sets a module parameter. This parameter value will be accessible to modules when they are loaded.
Definition: Types.cs:560
static IModule[] GetLoadedModules()
Gets an array of loaded modules.
Definition: Types.cs:410
static object Instantiate(Type Type, params object[] Arguments)
Returns an instance of the type Type . If one needs to be created, it is. If the constructor requires...
Definition: Types.cs:1353
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:84
static Task StopAllModules()
Stops all modules.
Definition: Types.cs:327
Contains information about a language.
Definition: Language.cs:17
Language()
Contains information about a language.
Definition: Language.cs:31
Contains information about a namespace in a language.
Definition: Namespace.cs:17
Basic access point for runtime language localization.
Definition: Translator.cs:16
const string SchemaRoot
Expected root in XML files.
Definition: Translator.cs:30
static async Task ImportAsync(XmlReader Xml)
Imports language strings into the language database.
Definition: Translator.cs:236
const string SchemaResource
Resource name of embedded schema file.
Definition: Translator.cs:20
const string SchemaNamespace
Namespace of embedded schema file.
Definition: Translator.cs:25
Class that keeps track of events and timing.
Definition: Profiler.cs:67
void Stop()
Stops measuring time.
Definition: Profiler.cs:210
ProfilerThread CreateThread(string Name, ProfilerThreadType Type)
Creates a new profiler thread.
Definition: Profiler.cs:127
string ExportPlantUml(TimeUnit TimeUnit)
Exports events to PlantUML.
Definition: Profiler.cs:510
double ElapsedSeconds
Elapsed seconds since start.
Definition: Profiler.cs:224
void Start()
Starts measuring time.
Definition: Profiler.cs:200
Class that keeps track of events and timing for one thread.
void Exception(System.Exception Exception)
Exception occurred
void Interval(DateTime From, DateTime To, string Label)
Records an interval in the profiler thread.
void NewState(string State)
Thread changes state.
static async Task< DateTime?> GetRegistrationTime()
Gets the original registration time.
Static class managing persistent settings.
static bool Set(string Key, string Value)
Sets a string-valued setting.
static Task< Dictionary< string, object > > GetWhereKeyLikeAsync(string Key, string Wildcard)
Gets available settings, matching a search filter.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
static async Task< bool > DeleteAsync(string Key)
Deletes a runtime setting
static async Task< bool > SetAsync(string Key, string Value)
Sets a string-valued setting.
Represents an object that allows single concurrent writers but multiple concurrent readers....
Asynchronous mutex class.
Definition: AsyncMutex.cs:11
async Task ReleaseMutex()
Releases the mutex earlier aquired via a call to WaitOne.
Definition: AsyncMutex.cs:200
void Dispose()
IDisposable.Dispose
Definition: AsyncMutex.cs:110
Task< bool > WaitOne()
Waits for the Mutex to be free, and locks it.
Definition: AsyncMutex.cs:123
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
Definition: Scheduler.cs:26
bool Remove(DateTime When)
Removes an event scheduled for a given point in time.
Definition: Scheduler.cs:182
void Dispose()
IDisposable.Dispose
Definition: Scheduler.cs:46
DateTime Add(DateTime When, ScheduledEventCallback Callback, object State)
Adds an event.
Definition: Scheduler.cs:66
Class managing a script expression.
Definition: Expression.cs:39
string Script
Original script string.
Definition: Expression.cs:181
async Task< object > EvaluateAsync(Variables Variables)
Evaluates the expression, using the variables provided in the Variables collection....
Definition: Expression.cs:4275
Base class for graphs.
Definition: Graph.cs:79
Contains pixel information
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
Contains information about a variable.
Definition: Variable.cs:10
Collection of variables.
Definition: Variables.cs:25
virtual bool ContainsVariable(string Name)
If the collection contains a variable with a given name.
Definition: Variables.cs:76
virtual bool TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
Definition: Variables.cs:52
Event arguments for the Assert.UnauthorizedAccess event.
Assembly Assembly
Assembly in which the type is defined.
Class that monitors login events, and help applications determine malicious intent....
Definition: LoginAuditor.cs:26
static async void Success(string Message, string UserName, string RemoteEndpoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a successful login attempt.
Number of failing login attempts possible during given time period.
Login state information relating to a remote endpoint
byte[] ComputeVariable(byte[] N)
Computes the SPONGE function, as defined in section 4 of NIST FIPS 202.
Definition: Keccak1600.cs:412
Implements the SHA3-256 hash function, as defined in section 6.1 in the NIST FIPS 202: https://nvlpub...
Definition: SHA3_256.cs:13
Corresponds to a privilege in the system.
Definition: Privilege.cs:17
Maintains the collection of all privileges in the system.
Definition: Privileges.cs:14
static async Task LoadAll()
Loads all privileges
Definition: Privileges.cs:82
Maintains the collection of all roles in the system.
Definition: Roles.cs:14
static async Task LoadAll()
Loads all roles
Definition: Roles.cs:71
Corresponds to a user in the system.
Definition: User.cs:21
string UserName
User Name
Definition: User.cs:53
bool HasPrivilege(string Privilege)
If the user has a given privilege.
Definition: User.cs:129
Maintains the collection of all users in the system.
Definition: Users.cs:24
static bool HashMethodLocked
If the Hash Method has been registered and locked.
Definition: Users.cs:150
static void Register(HashComputationMethod HashComputationMethod, string HashMethodTypeName, LoginAuditor LoginAuditor, bool Lock)
Registers a Hash Digest Computation Method.
Definition: Users.cs:116
Event arguments for events that request an URL to a QR code.
Defines the Metering Topology data source. This data source contains a tree structure of persistent r...
static async Task< int > DeleteOldEvents(TimeSpan MaxAge)
Deletes old data source events.
const string SourceID
Source ID for the metering topology data source.
Base class for all sensor data fields.
Definition: Field.cs:20
bool IsEmpty
If the reference is an empty reference.
Interface for all event sinks.
Definition: IEventSink.cs:9
Interface for system configurations. The gateway will scan all module for system configuration classe...
Interface for content encodings in HTTP transfers.
void ConfigureSupport(bool Dynamic, bool Static)
Configures support for the algorithm.
Interface for observable classes implementing communication protocols.
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
Definition: ISniffer.cs:11
Interface for personal event objects.
Interface for database providers that can be plugged into the static Database class.
Task Flush()
Persists any pending changes.
Task Start()
Called when processing starts.
Task Stop()
Called when processing ends.
Task Start()
Called when processing starts.
Interface for late-bound modules loaded at runtime.
Definition: IModule.cs:9
Basic interface for a user.
Definition: IUser.cs:7
Interface for datasources that are published through the concentrator interface.
Definition: IDataSource.cs:14
Interface for thing references.
Emoji1SourceFileType
What source files to use when displaying emoji.
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.
delegate bool CustomEventFilterDelegate(Event Event)
Delegate for custom event filters.
delegate Task EventHandlerAsync(object Sender, EventArgs e)
Asynchronous version of EventArgs.
EventLevel
Event level.
Definition: EventLevel.cs:7
EventType
Type of event.
Definition: EventType.cs:7
delegate Task< MetaDataTag[]> GetRegistryMetaDataEventHandler(MetaDataTag[] MetaData)
Delegate for events requesting meta data for registration.
delegate Task< IDatabaseProvider > GetDatabaseProviderEventHandler(XmlElement Definition)
Delegate for callback methods used for the creation of database providers.
delegate Task< IDataSource[]> GetDataSources(params IDataSource[] DataSources)
Delegate for events requesting an array of data sources.
delegate Task RegistrationEventHandler(MetaDataTag[] MetaData, RegistrationEventArgs e)
Delegate for registration callback methods.
HostDomainOptions
Options on how to handle domain names provided in the Host header.
LineEnding
Type of line ending.
BinaryPresentationMethod
How binary data is to be presented.
QoSLevel
Quality of Service Level for asynchronous messages. Support for QoS Levels must be supported by the r...
Definition: QoSLevel.cs:8
SubscriptionState
State of a presence subscription.
Definition: RosterItem.cs:16
MessageType
Type of message received.
Definition: MessageType.cs:7
XmppState
State of XMPP connection.
Definition: XmppState.cs:7
ClientCertificates
Client Certificate Options
TimeUnit
Options for presenting time in reports.
Definition: Profiler.cs:16
ProfilerThreadType
Type of profiler thread.
delegate void ScheduledEventCallback(object State)
Callback method for scheduled events.
delegate Task ScheduledEventCallbackAsync(object State)
Callback method for asynchronous scheduled events.
Represents a duration value, as defined by the xsd:duration data type: http://www....
Definition: Duration.cs:13
static readonly Duration Zero
Zero value
Definition: Duration.cs:532