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;
3using System.Diagnostics;
4using System.IO;
5using System.Net;
6using System.Net.NetworkInformation;
7using System.Reflection;
8using System.Runtime.ExceptionServices;
11using System.Security.Cryptography.X509Certificates;
12using System.Text;
13using System.Text.RegularExpressions;
14using System.Threading.Tasks;
15using System.Xml;
16using System.Xml.Schema;
17using Waher.Content;
33using Waher.Events;
44using Waher.Groups;
52using Waher.Jobs;
85using Waher.Output;
89using Waher.Reports;
103using Waher.Script;
106using Waher.Security;
113using Waher.Things;
119
120namespace Waher.IoTGateway
121{
127 public delegate Task<IDatabaseProvider> GetDatabaseProviderEventHandler(XmlElement Definition);
128
134 public delegate Task RegistrationEventHandler(MetaDataTag[] MetaData, RegistrationEventArgs e);
135
141 public delegate Task<MetaDataTag[]> GetRegistryMetaDataEventHandler(MetaDataTag[] MetaData);
142
146 public static class Gateway
147 {
151 public const string GatewayConfigLocalFileName = "Gateway.config";
152
156 public const string WebApplicationFirewallLocalFileName = "WAF.xml";
157
161 public const string GatewayConfigLocalName = "GatewayConfiguration";
162
166 public const string GatewayConfigNamespace = "http://waher.se/Schema/GatewayConfiguration.xsd";
167
168 private const int MaxChunkSize = 4096;
169
170 private static readonly LinkedList<KeyValuePair<string, int>> ports = new LinkedList<KeyValuePair<string, int>>();
171 private static readonly Dictionary<int, EventHandlerAsync> serviceCommandByNr = new Dictionary<int, EventHandlerAsync>();
172 private static readonly Dictionary<EventHandlerAsync, int> serviceCommandNrByCallback = new Dictionary<EventHandlerAsync, int>();
173 private static readonly Dictionary<string, DateTime> lastUnauthorizedAccess = new Dictionary<string, DateTime>();
174 private static readonly DateTime startTime = DateTime.Now;
175 private static Cache<string, XmlFileSniffer> xmlFileSnifferCache = null;
176 private static byte[] emergencyMemory = new byte[1024 * 1024];
177 private static IDatabaseProvider internalProvider = null;
178 private static ThingRegistryClient thingRegistryClient = null;
179 private static ProvisioningClient provisioningClient = null;
180 private static XmppCredentials xmppCredentials = null;
181 private static XmppClient xmppClient = null;
182 private static AvatarClient avatarClient = null;
183 private static Networking.XMPP.InBandBytestreams.IbbClient ibbClient = null;
184 private static Socks5Proxy socksProxy = null;
185 private static ConcentratorServer concentratorServer = null;
186 private static SensorClient sensorClient = null;
187 private static ControlClient controlClient = null;
188 private static ConcentratorClient concentratorClient = null;
189 private static SynchronizationClient synchronizationClient = null;
190 private static PepClient pepClient = null;
191 private static MultiUserChatClient mucClient = null;
192 private static ContractsClient contractsClient = null;
193 private static SoftwareUpdateClient softwareUpdateClient = null;
194 private static GeoClient geoClient = null;
195 private static MailClient mailClient = null;
196 private static X509Certificate2 certificate = null;
197 private static DateTime checkCertificate = DateTime.MinValue;
198 private static DateTime checkIp = DateTime.MinValue;
199 private static OAuth2Environment oauthEnvironment = null;
200 private static HttpServer webServer = null;
201 private static HttpFolderResource root = null;
202 private static HttpxProxy httpxProxy = null;
203 private static HttpxServer httpxServer = null;
204 private static CoapEndpoint coapEndpoint = null;
205 private static SystemConfiguration[] configurations;
206 private static LoginAuditor loginAuditor = null;
207 private static Scheduler scheduler = null;
208 private readonly static RandomNumberGenerator rnd = RandomNumberGenerator.Create();
209 private static AsyncMutex gatewayRunning = null;
210 private static AsyncMutex startingServer = null;
211 private static Emoji1LocalFiles emoji1_24x24 = null;
212 private static StreamWriter exceptionFile = null;
213 private static CaseInsensitiveString domain = null;
214 private static CaseInsensitiveString[] alternativeDomains = null;
215 private static CaseInsensitiveString ownerJid = null;
216 private static Dictionary<string, string> defaultPageByHostName = null;
217 private static CommunicationLayer firstChanceExceptions = new CommunicationLayer(true);
218 private static IPersistentDictionary nonceValues;
219 private static JwtFactory jwtFactory = null;
220 private static XmlFileSnifferSet mcpSniffers = null;
221 private static DateTime wafTimestamp = DateTime.MinValue;
222 private static string instance;
223 private static string appDataFolder;
224 private static string runtimeFolder;
225 private static string rootFolder;
226 private static string reportsFolder;
227 private static string applicationName;
228 private static string exceptionFolder = null;
229 private static string exceptionFileName = null;
230 private static int nextServiceCommandNr = 128;
231 private static int beforeUninstallCommandNr = 0;
232 private static bool firstStart = true;
233 private static bool registered = false;
234 private static bool connected = false;
235 private static bool immediateReconnect;
236 private static bool consoleOutput;
237 private static bool loopbackIntefaceAvailable;
238 private static bool configuring = false;
239 private static bool exportExceptions = false;
240 private static bool stopped = false;
241
242 #region Life Cycle
243
247 public static DateTime StartTime => startTime;
248
254 public static Task<bool> Start(bool ConsoleOutput)
255 {
256 return Start(ConsoleOutput, true, string.Empty);
257 }
258
265 public static Task<bool> Start(bool ConsoleOutput, bool LoopbackIntefaceAvailable)
266 {
267 return Start(ConsoleOutput, LoopbackIntefaceAvailable, string.Empty);
268 }
269
277 public static async Task<bool> Start(bool ConsoleOutput, bool LoopbackIntefaceAvailable, string InstanceName)
278 {
279 bool FirstStart = firstStart;
280
281 firstStart = false;
282 instance = InstanceName;
283
284 string Suffix = string.IsNullOrEmpty(InstanceName) ? string.Empty : "." + InstanceName;
285 gatewayRunning = new AsyncMutex(false, "Waher.IoTGateway.Running" + Suffix);
286 if (!await gatewayRunning.WaitOne(1000))
287 return false; // Is running in another process.
288
289 startingServer = new AsyncMutex(false, "Waher.IoTGateway.Starting" + Suffix);
290 if (!await startingServer.WaitOne(1000))
291 {
292 await gatewayRunning.ReleaseMutex();
293 gatewayRunning.Dispose();
294 gatewayRunning = null;
295
296 startingServer.Dispose();
297 startingServer = null;
298 return false; // Being started in another process.
299 }
300
301 try
302 {
303 stopped = false;
304 consoleOutput = ConsoleOutput;
305 loopbackIntefaceAvailable = LoopbackIntefaceAvailable;
306
307 appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
308
309 if (!appDataFolder.EndsWith(new string(Path.DirectorySeparatorChar, 1)))
310 appDataFolder += Path.DirectorySeparatorChar;
311
312 appDataFolder += "IoT Gateway";
313
314 if (!string.IsNullOrEmpty(InstanceName))
315 appDataFolder += " " + InstanceName;
316
317 if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && !Directory.Exists(appDataFolder))
318 appDataFolder = appDataFolder.Replace("/usr/share", "/usr/local/share");
319 else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && !Directory.Exists(appDataFolder))
320 appDataFolder = appDataFolder.Replace("/usr/share", "/var/lib");
321
322 appDataFolder += Path.DirectorySeparatorChar;
323 rootFolder = appDataFolder + "Root" + Path.DirectorySeparatorChar;
324 reportsFolder = appDataFolder + "Reports" + Path.DirectorySeparatorChar;
325
326 Log.Register(new EventFilter("Alert Filter", new AlertNotifier("Alert Notifier"), EventType.Alert,
327 (Event) => string.IsNullOrEmpty(Event.Facility)));
328
329 Log.Register(new XmlFileEventSink("XML File Event Sink",
330 appDataFolder + "Events" + Path.DirectorySeparatorChar + "Event Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml",
331 appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "EventXmlToHtml.xslt", 7));
332
333 if (FirstStart)
334 Assert.UnauthorizedAccess += Assert_UnauthorizedAccess;
335
336 Log.Informational("Server starting up.");
337
338 if (FirstStart)
339 {
340 Initialize();
341
342 beforeUninstallCommandNr = RegisterServiceCommand(BeforeUninstall);
343 NextBytes(emergencyMemory);
344
345 if (!Directory.Exists(rootFolder))
346 {
347 string s = Path.Combine(runtimeFolder, "Root");
348 if (Directory.Exists(s))
349 {
350 CopyFolder(runtimeFolder, appDataFolder, "*.config", CopyOptions.IfNewer);
351 CopyFolders(s, rootFolder, CopyOptions.IfNewer);
352 CopyFolders(Path.Combine(runtimeFolder, "Graphics"), Path.Combine(appDataFolder, "Graphics"), CopyOptions.IfNewer);
353 CopyFolders(Path.Combine(runtimeFolder, "Transforms"), Path.Combine(appDataFolder, "Transforms"), CopyOptions.IfNewer);
354 }
355 }
356
357 string[] ManifestFiles = Directory.GetFiles(runtimeFolder, "*.manifest", SearchOption.TopDirectoryOnly);
358 Dictionary<string, CopyOptions> ContentOptions = new Dictionary<string, CopyOptions>();
359 int i;
360
361 for (i = 0; i < 2; i++)
362 {
363 foreach (string ManifestFile in ManifestFiles)
364 {
365 string FileName = Path.GetFileName(ManifestFile);
366 bool GatewayFile = FileName.StartsWith("Waher.IoTGateway", StringComparison.CurrentCultureIgnoreCase);
367
368 if ((i == 0 && GatewayFile) || (i == 1 && !GatewayFile))
369 {
370 CheckContentFiles(ManifestFile, ContentOptions);
371
372 if (ManifestFile.EndsWith("Waher.Utility.Install.manifest"))
373 CheckInstallUtilityFiles(ManifestFile);
374 }
375 }
376 }
377 }
378
379 Types.SetModuleParameter("AppData", appDataFolder);
380 Types.SetModuleParameter("Runtime", runtimeFolder);
381 Types.SetModuleParameter("Root", rootFolder);
382 Types.SetModuleParameter("Reports", reportsFolder);
383
384 scheduler = new Scheduler();
385
386 if (FirstStart)
387 {
388 Task T = Task.Run(() =>
389 {
390 GraphViz.Init(rootFolder);
391 XmlLayout.Init(rootFolder);
392 PlantUml.Init(rootFolder);
393 });
394 }
395
396
397 string GatewayConfigFileName = ConfigFilePath;
398 if (!File.Exists(GatewayConfigFileName))
399 GatewayConfigFileName = GatewayConfigLocalFileName;
400
401 XmlDocument Config = XML.LoadFromFile(GatewayConfigFileName, true);
402
404 XSL.LoadSchema(typeof(Gateway).Namespace + ".Schema.GatewayConfiguration.xsd", typeof(Gateway).Assembly));
405
406 IDatabaseProvider DatabaseProvider = null;
408 bool TrustClientCertificates = false;
409 Dictionary<int, KeyValuePair<ClientCertificates, bool>> PortSpecificMTlsSettings = null;
410 TimeSpan CleanupTime = new TimeSpan(4, 15, 0);
411 bool Http2Enabled = true;
412 int Http2InitialStreamWindowSize = 2500000;
413 int Http2InitialConnectionWindowSize = 5000000;
414 int Http2MaxFrameSize = 16384;
415 int Http2MaxConcurrentStreams = 100;
416 int Http2HeaderTableSize = 8192;
417 bool Http2NoRfc7540Priorities = false;
418 bool Http2Profiling = false;
419 bool HttpSniffersPerEndpoint = false;
420
421 // Bandwidth Delay Product, 100 MBit/s * 200 ms = 20 MBit = 2.5 MB window size
422 // to avoid congestion.
423
424 foreach (XmlNode N in Config.DocumentElement.ChildNodes)
425 {
426 if (N is XmlElement E)
427 {
428 switch (E.LocalName)
429 {
430 case "ApplicationName":
431 applicationName = E.InnerText;
432 break;
433
434 case "DefaultPage":
435 defaultPageByHostName ??= new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
436 defaultPageByHostName[XML.Attribute(E, "host")] = E.InnerText;
437 break;
438
439 case "MutualTls":
440 ClientCertificates = XML.Attribute(E, "clientCertificates", ClientCertificates.NotUsed);
441 TrustClientCertificates = XML.Attribute(E, "trustCertificates", false);
442
443 foreach (XmlNode N2 in E.ChildNodes)
444 {
445 if (N2.LocalName == "Port" && int.TryParse(N2.InnerText, out int PortNumber))
446 {
447 XmlElement E2 = (XmlElement)N2;
448 ClientCertificates ClientCertificatesPort = XML.Attribute(E2, "clientCertificates", ClientCertificates);
449 bool TrustClientCertificatesPort = XML.Attribute(E2, "trustCertificates", TrustClientCertificates);
450
451 PortSpecificMTlsSettings ??= new Dictionary<int, KeyValuePair<ClientCertificates, bool>>();
452 PortSpecificMTlsSettings[PortNumber] = new KeyValuePair<ClientCertificates, bool>(ClientCertificatesPort, TrustClientCertificatesPort);
453 }
454 }
455 break;
456
457 case "Http2Settings":
458 Http2Enabled = XML.Attribute(E, "enabled", Http2Enabled);
459 Http2InitialStreamWindowSize = XML.Attribute(E, "initialWindowSize", Http2InitialStreamWindowSize);
460 Http2InitialConnectionWindowSize = XML.Attribute(E, "initialConnectionWindowSize", Http2InitialConnectionWindowSize);
461 Http2MaxFrameSize = XML.Attribute(E, "maxFrameSize", Http2MaxFrameSize);
462 Http2MaxConcurrentStreams = XML.Attribute(E, "maxConcurrentStreams", Http2MaxConcurrentStreams);
463 Http2HeaderTableSize = XML.Attribute(E, "headerTableSize", Http2HeaderTableSize);
464 Http2NoRfc7540Priorities = XML.Attribute(E, "noRfc7540Priorities", Http2NoRfc7540Priorities);
465 Http2Profiling = XML.Attribute(E, "profiling", Http2Profiling);
466 HttpSniffersPerEndpoint = XML.Attribute(E, "sniffersPerEndpoint", false);
467 break;
468
469 case "ContentEncodings":
470 foreach (XmlNode N2 in E.ChildNodes)
471 {
472 if (N2.LocalName == "ContentEncoding")
473 {
474 XmlElement E2 = (XmlElement)N2;
475 string Method = XML.Attribute(E2, "method");
476 bool Dynamic = XML.Attribute(E2, "dynamic", true);
477 bool Static = XML.Attribute(E2, "static", true);
478
479 IContentEncoding Encoding = Types.FindBest<IContentEncoding, string>(Method);
480
481 if (Encoding is null)
482 Log.Error("Content-Encoding not found: " + Method, GatewayConfigLocalFileName);
483 else
484 Encoding.ConfigureSupport(Dynamic, Static);
485 }
486 }
487
489 break;
490
491 case "ExportExceptions":
492 exceptionFolder = Path.Combine(appDataFolder, XML.Attribute(E, "folder", "Exceptions"));
493
494 if (!Directory.Exists(exceptionFolder))
495 Directory.CreateDirectory(exceptionFolder);
496
497 DateTime UtcNow = DateTime.UtcNow;
498 string[] ExceptionFiles = Directory.GetFiles(exceptionFolder, "*.txt", SearchOption.TopDirectoryOnly);
499 foreach (string ExceptionFile in ExceptionFiles)
500 {
501 try
502 {
503 DateTime TP = File.GetLastWriteTimeUtc(ExceptionFile);
504 if ((UtcNow - TP).TotalDays > 90)
505 File.Delete(ExceptionFile);
506 else
507 {
508 string XmlFile = Path.ChangeExtension(ExceptionFile, "xml");
509 if (!File.Exists(XmlFile))
510 {
511 Log.Informational("Processing " + ExceptionFile);
512 Analyze.Process(ExceptionFile, XmlFile);
513 File.Delete(ExceptionFile);
514 }
515 }
516 }
517 catch (Exception ex)
518 {
519 Log.Exception(ex, ExceptionFile);
520 }
521 }
522
523 ExceptionFiles = Directory.GetFiles(exceptionFolder, "*.xml", SearchOption.TopDirectoryOnly);
524 foreach (string ExceptionFile in ExceptionFiles)
525 {
526 try
527 {
528 DateTime TP = File.GetLastWriteTimeUtc(ExceptionFile);
529 if ((UtcNow - TP).TotalDays > 90)
530 File.Delete(ExceptionFile);
531 }
532 catch (Exception ex)
533 {
534 Log.Exception(ex, ExceptionFile);
535 }
536 }
537
538 if (FirstStart)
539 {
540 int MaxTries = 1000;
541
542 do
543 {
544 UtcNow = DateTime.UtcNow;
545
546 exceptionFileName = Path.Combine(exceptionFolder, UtcNow.Year.ToString("D4") + "-" + UtcNow.Month.ToString("D2") + "-" + UtcNow.Day.ToString("D2") +
547 " " + UtcNow.Hour.ToString("D2") + "." + UtcNow.Minute.ToString("D2") + "." + UtcNow.Second.ToString("D2") + ".txt");
548
549 try
550 {
551 if (!File.Exists(exceptionFileName))
552 {
553 exceptionFile = File.CreateText(exceptionFileName);
554 }
555 else
556 await Task.Delay(1000);
557 }
558 catch (IOException)
559 {
560 exceptionFile = null;
561 await Task.Delay(1000);
562 }
563 }
564 while (exceptionFile is null && --MaxTries > 0);
565
566 exportExceptions = !(exceptionFile is null);
567
568 if (exportExceptions)
569 {
570 exceptionFile.Write("Start of export: ");
571 exceptionFile.WriteLine(DateTime.UtcNow.ToString());
572
573 AppDomain.CurrentDomain.FirstChanceException += (Sender, e) =>
574 {
575 if (!(exceptionFile is null))
576 {
577 lock (exceptionFile)
578 {
579 bool Emergency;
580
581 if (e.Exception is SystemException &&
582 (e.Exception is StackOverflowException ||
583 e.Exception is OutOfMemoryException ||
584 e.Exception is AccessViolationException))
585 {
586 emergencyMemory = null;
587 GC.GetTotalMemory(true);
588 Emergency = true;
589 }
590 else
591 Emergency = false;
592
593 string StackTrace = e.Exception.StackTrace;
594
595 if (!exportExceptions || StackTrace.Contains("FirstChanceExceptionEventArgs"))
596 return;
597
598 StringBuilder sb = new StringBuilder();
599
600 sb.AppendLine(new string('-', 80));
601 sb.Append("Type: ");
602
603 if (!(e.Exception is null))
604 sb.AppendLine(e.Exception.GetType().FullName);
605 else
606 sb.AppendLine("null");
607
608 sb.Append("Time: ");
609 sb.AppendLine(DateTime.UtcNow.ToString());
610
611 if (!(e.Exception is null))
612 {
613 if (Emergency)
614 {
615 sb.AppendLine();
616 sb.AppendLine(e.Exception.Message);
617 sb.AppendLine();
618 sb.AppendLine(StackTrace); // Avoid worsening the situation and conserve stack space.
619 sb.AppendLine();
620 }
621 else
622 {
623 LinkedList<Exception> Exceptions = new LinkedList<Exception>();
624 Exceptions.AddLast(e.Exception);
625
626 while (!(Exceptions.First is null))
627 {
628 Exception ex = Exceptions.First.Value;
629 Exceptions.RemoveFirst();
630
631 sb.AppendLine();
632 sb.AppendLine(ex.Message);
633 sb.AppendLine();
634 sb.AppendLine(Log.CleanStackTrace(ex.StackTrace));
635 sb.AppendLine();
636
637 if (ex is AggregateException ex2)
638 {
639 foreach (Exception ex3 in ex2.InnerExceptions)
640 Exceptions.AddLast(ex3);
641 }
642 else if (!(ex.InnerException is null))
643 Exceptions.AddLast(ex.InnerException);
644 }
645 }
646 }
647
648 exceptionFile.Write(sb.ToString());
649 exceptionFile.Flush();
650 }
651
652 if (firstChanceExceptions?.HasSniffers ?? false)
653 firstChanceExceptions.Exception(e.Exception);
654 }
655 };
656 }
657 }
658 break;
659
660 case "Database":
661 if (FirstStart || !Database.HasProvider)
662 {
663 if (!(DatabaseProvider is null))
664 throw new Exception("Database provider already initiated.");
665
666 if (!(GetDatabaseProvider is null))
667 DatabaseProvider = await GetDatabaseProvider(E);
668 else
669 DatabaseProvider = null;
670
671 if (DatabaseProvider is null)
672 throw new Exception("Database provider not defined. Make sure the GetDatabaseProvider event has an appropriate event handler.");
673
674 internalProvider = DatabaseProvider;
675 Database.Register(DatabaseProvider, false);
676 }
677 else
678 {
679 DatabaseProvider = Database.Provider;
680 await DatabaseProvider.Start();
681
683 await Ledger.Provider.Start();
684 }
685 break;
686
687 case "Ports":
688 foreach (XmlNode N2 in E.ChildNodes)
689 {
690 if (N2.LocalName == "Port")
691 {
692 XmlElement E2 = (XmlElement)N2;
693 string Protocol = XML.Attribute(E2, "protocol");
694 if (!string.IsNullOrEmpty(Protocol) && int.TryParse(E2.InnerText, out int Port2))
695 ports.AddLast(new KeyValuePair<string, int>(Protocol, Port2));
696 }
697 }
698 break;
699
700 case "LoginAuditor":
701
702 static LoginInterval[] ParseIntervals(XmlElement E)
703 {
704 List<LoginInterval> LoginIntervals = new List<LoginInterval>();
705 Duration LastInterval = Duration.Zero;
706 bool LastMaxInterval = false;
707
708 foreach (XmlNode N2 in E.ChildNodes)
709 {
710 if (N2 is XmlElement E2 && E2.LocalName == "Interval")
711 {
712 if (LastMaxInterval)
713 {
714 Log.Error("Only the last login auditor interval can be the empty 'eternal' interval.",
716 break;
717 }
718
719 int NrAttempts = XML.Attribute(E2, "nrAttempts", 0);
720 if (NrAttempts <= 0)
721 {
722 Log.Error("Number of attempts must be positive when defining an interval for the LoginAuditor",
724 continue;
725 }
726
727 if (!E2.HasAttribute("interval"))
728 {
729 LoginIntervals.Add(new LoginInterval(NrAttempts, TimeSpan.MaxValue));
730 LastMaxInterval = true;
731 }
732 else
733 {
734 Duration Interval = XML.Attribute(E2, "interval", Duration.Zero);
735 if (Interval <= Duration.Zero)
736 {
737 Log.Error("Login Auditor intervals must be positive", GatewayConfigLocalFileName);
738 continue;
739 }
740
741 if (Interval <= LastInterval)
742 {
743 Log.Error("Login Auditor intervals must be specified in an increasing order.",
745 continue;
746 }
747
748 LoginIntervals.Add(new LoginInterval(NrAttempts, Interval));
749 LastInterval = Interval;
750 }
751 }
752 }
753
754 return LoginIntervals.ToArray();
755 }
756
757 LoginInterval[] LoginIntervals = ParseIntervals(E);
758 List<RemoteEndpointIntervals> EndpointExceptions = new List<RemoteEndpointIntervals>();
759
760 foreach (XmlNode N2 in E.ChildNodes)
761 {
762 if (N2 is XmlElement E2 && E2.LocalName == "Exception")
763 {
764 string EndPoint = XML.Attribute(E2, "endpoint");
765 LoginInterval[] ExceptionIntervals = ParseIntervals(E2);
766
767 if (ExceptionIntervals.Length == 0)
768 Log.Error("Login Auditor exception intervals not specified for endpoint: " + EndPoint, GatewayConfigLocalFileName);
769 else
770 loginAuditor = new LoginAuditor("Login Auditor", LoginIntervals);
771
772 if (ExceptionIntervals.Length == 0)
773 Log.Error("Login Auditor intervals not specified.", GatewayConfigLocalFileName);
774 else
775 EndpointExceptions.Add(new RemoteEndpointIntervals(EndPoint, ExceptionIntervals));
776 }
777 }
778
779 if (LoginIntervals.Length == 0)
780 Log.Error("Login Auditor intervals not specified.", GatewayConfigLocalFileName);
781 else
782 loginAuditor = new LoginAuditor("Login Auditor", EndpointExceptions.ToArray(), LoginIntervals);
783
784 break;
785
786 case "EventSinks":
787
788 static IEventSink[] ParseSinks(XmlElement E, ref TimeSpan CleanupTime)
789 {
791
792 foreach (XmlNode N2 in E.ChildNodes)
793 {
794 if (!(N2 is XmlElement E2) || E2.NamespaceURI != E.NamespaceURI)
795 continue;
796
797 try
798 {
799 switch (E2.LocalName)
800 {
801 case "TextFileEventSink":
802 string SinkId = XML.Attribute(E2, "id");
803 string FileName = XML.Attribute(E2, "fileName");
804 int DeleteAfterDays = XML.Attribute(E2, "deleteAfterDays", 7);
805
806 Sinks.Add(new TextFileEventSink(SinkId, FileName, DeleteAfterDays));
807 break;
808
809 case "XmlFileEventSink":
810 SinkId = XML.Attribute(E2, "id");
811 FileName = XML.Attribute(E2, "fileName");
812 DeleteAfterDays = XML.Attribute(E2, "deleteAfterDays", 7);
813
814 string TransformFileName = XML.Attribute(E2, "transformFileName");
815 if (string.IsNullOrEmpty(TransformFileName))
816 TransformFileName = appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "EventXmlToHtml.xslt";
817
818 Sinks.Add(new XmlFileEventSink(SinkId, FileName, TransformFileName, DeleteAfterDays));
819 break;
820
821 case "MqttEventSink":
822 SinkId = XML.Attribute(E2, "id");
823 string Broker = XML.Attribute(E2, "broker");
824 int Port = XML.Attribute(E2, "port", 1883);
825 bool Tls = XML.Attribute(E2, "tls", false);
826 string UserName = XML.Attribute(E2, "userName");
827 string Password = XML.Attribute(E2, "password");
828 string Topic = XML.Attribute(E2, "topic");
829
831
832 if (string.IsNullOrEmpty(UserName))
833 {
834 MqttClient = new MqttClient(Broker, Port, certificate,
835 null, MqttQualityOfService.AtMostOnce, false, null);
836 }
837 else
838 {
839 MqttClient = new MqttClient(Broker, Port, Tls, UserName, Password,
840 null, MqttQualityOfService.AtMostOnce, false, null);
841 }
842
843 Sinks.Add(new MqttEventSink(SinkId, MqttClient, Topic, true));
844 break;
845
846 case "PipeEventSink":
847 SinkId = XML.Attribute(E2, "id");
848 string PipeName = XML.Attribute(E2, "pipeName");
849
850 Sinks.Add(new PipeEventSink(SinkId, PipeName));
851 break;
852
853 case "SocketEventSink":
854 SinkId = XML.Attribute(E2, "id");
855 string Host = XML.Attribute(E2, "host");
856 Port = XML.Attribute(E2, "port", 0);
857 Tls = XML.Attribute(E2, "tls", false);
858
859 Sinks.Add(new SocketEventSink(SinkId, Host, Port, Tls));
860 break;
861
862 case "SyslogEventSink":
863 SinkId = XML.Attribute(E2, "id");
864 string Name = XML.Attribute(E2, "name");
865 Host = XML.Attribute(E2, "host");
866 Port = XML.Attribute(E2, "port", 514);
867 Tls = XML.Attribute(E2, "tls", false);
868 SyslogEventSeparation Separation = XML.Attribute(E2, "separation", SyslogEventSeparation.OctetCounting);
869
870 if (Tls)
871 {
872 if (certificate is null)
873 {
874 Sinks.Add(new SyslogEventSink(Host, Port, true, Name, applicationName,
875 Separation, SinkId));
876 }
877 else
878 {
879 Sinks.Add(new SyslogEventSink(Host, Port, certificate, Name, applicationName,
880 Separation, SinkId));
881 }
882 }
883 else
884 {
885 Sinks.Add(new SyslogEventSink(Host, Port, false, Name, applicationName,
886 Separation, SinkId));
887 }
888 break;
889
890 case "WebHookEventSink":
891 SinkId = XML.Attribute(E2, "id");
892 string Url = XML.Attribute(E2, "url");
893
894 int MaxSecondsUsed = XML.Attribute(E2, "maxSecondsUsed", 0);
895 int MaxSecondsUnused = XML.Attribute(E2, "maxSecondsUnused", 0);
896 bool CollectOnType = XML.Attribute(E2, "collectOnType", false);
897 bool CollectOnLevel = XML.Attribute(E2, "collectOnLevel", false);
898 bool CollectOnEventId = XML.Attribute(E2, "collectOnEventId", false);
899 bool CollectOnObject = XML.Attribute(E2, "collectOnObject", false);
900 bool CollectOnActor = XML.Attribute(E2, "collectOnActor", false);
901 bool CollectOnFacility = XML.Attribute(E2, "collectOnFacility", false);
902 bool CollectOnModule = XML.Attribute(E2, "collectOnModule", false);
903
904 Sinks.Add(new WebHookEventSink(SinkId, Url, null, MaxSecondsUsed,
905 MaxSecondsUnused, CollectOnType, CollectOnLevel,
906 CollectOnEventId, CollectOnObject, CollectOnActor,
907 CollectOnFacility, CollectOnModule));
908 break;
909
910 case "XmppEventSink":
911 SinkId = XML.Attribute(E2, "id");
912 string Jid = XML.Attribute(E2, "jid");
913
914 Sinks.Add(new XmppEventSink(SinkId, xmppClient, Jid, false));
915 break;
916
917 case "EventFilter":
918 SinkId = XML.Attribute(E2, "id");
919 FromEventLevel Debug = XML.Attribute(E2, "debug", FromEventLevel.None);
920 FromEventLevel Informational = XML.Attribute(E2, "informational", FromEventLevel.None);
921 FromEventLevel Notice = XML.Attribute(E2, "notice", FromEventLevel.None);
922 FromEventLevel Warning = XML.Attribute(E2, "warning", FromEventLevel.None);
923 FromEventLevel Error = XML.Attribute(E2, "error", FromEventLevel.None);
924 FromEventLevel Critical = XML.Attribute(E2, "critical", FromEventLevel.None);
925 FromEventLevel Alert = XML.Attribute(E2, "alert", FromEventLevel.None);
926 FromEventLevel Emergency = XML.Attribute(E2, "emergency", FromEventLevel.None);
927 string EventIdsString = XML.Attribute(E2, "eventIds").Trim();
928 string[] EventIds;
929
930 if (string.IsNullOrEmpty(EventIdsString))
931 EventIds = null;
932 else
933 EventIds = EventIdsString.Split(',', StringSplitOptions.RemoveEmptyEntries);
934
935 IEventSink[] ChildSinks = ParseSinks(E2, ref CleanupTime);
936
937 switch (ChildSinks.Length)
938 {
939 case 0:
940 break;
941
942 case 1:
943 Sinks.Add(new EventFilter(SinkId, ChildSinks[0], Debug, Informational, Notice, Warning,
944 Error, Critical, Alert, Emergency, null, EventIds));
945 break;
946
947 default:
948 Sinks.Add(new EventFilter(SinkId, new EventSinks(SinkId, ChildSinks), Debug, Informational, Notice, Warning,
949 Error, Critical, Alert, Emergency, null, EventIds));
950 break;
951 }
952 break;
953
954 case "EventQueue":
955 SinkId = XML.Attribute(E2, "id");
956 string QueueName = XML.Attribute(E2, "name");
957 DeleteAfterDays = XML.Attribute(E2, "deleteAfterDays", 7);
958
959 Sinks.Add(new EventQueue(SinkId, QueueName, DeleteAfterDays, CleanupTime));
960 CleanupTime = CleanupTime.Add(TimeSpan.FromMinutes(2));
961 break;
962 }
963 }
964 catch (Exception ex)
965 {
967 }
968 }
969
970 return Sinks.ToArray();
971 }
972
973 foreach (IEventSink Sink in ParseSinks(E, ref CleanupTime))
974 Log.Register(Sink);
975
976 break;
977 }
978 }
979 }
980
981 if (DatabaseProvider is null)
982 throw new Exception("Database provider not defined in " + GatewayConfigLocalFileName + ".");
983
984 Database.CollectionRepaired += Database_CollectionRepaired;
985
986 await RepairIfInproperShutdown();
987
989 CleanupTime = CleanupTime.Add(TimeSpan.FromMinutes(2));
990
992 try
993 {
994 await PersistedEventLog.Queue(new Event(EventType.Informational, "Server starting up.", string.Empty, string.Empty, string.Empty, EventLevel.Minor, string.Empty, string.Empty, string.Empty));
995 }
996 catch (Exception ex)
997 {
998 Event Event = new Event(DateTime.UtcNow, EventType.Critical, ex.Message, PersistedEventLog.ObjectID, string.Empty, string.Empty,
999 EventLevel.Major, string.Empty, ex.Source, Log.CleanStackTrace(ex.StackTrace));
1000
1002
1003 Log.Event(Event);
1004 }
1005
1006 loginAuditor ??= new LoginAuditor("Login Auditor",
1007 new LoginInterval(5, TimeSpan.FromHours(1)), // Maximum 5 failed login attempts in an hour
1008 new LoginInterval(2, TimeSpan.FromDays(1)), // Maximum 2x5 failed login attempts in a day
1009 new LoginInterval(2, TimeSpan.FromDays(7)), // Maximum 2x2x5 failed login attempts in a week
1010 new LoginInterval(2, TimeSpan.MaxValue)); // Maximum 2x2x2x5 failed login attempts in total, then blocked.
1011
1012 Log.Register(loginAuditor);
1013
1014 nonceValues = await Database.GetDictionary("Nonces");
1015
1016 // Protecting Markdown resources:
1018 MarkdownCodec.AllowRawEncoding(false, true);
1023
1024 // Protecting web-script resources:
1026 WsCodec.AllowRawEncoding(false, true);
1029
1030 LinkedList<SystemConfiguration> NewConfigurations = null;
1031 Dictionary<string, Type> SystemConfigurationTypes = new Dictionary<string, Type>();
1032 Dictionary<string, SystemConfiguration> SystemConfigurations = new Dictionary<string, SystemConfiguration>();
1033 bool Configured = true;
1034 bool CheckDeferredConfigurations = false;
1035 bool Simplify = (await ServiceRegistrationClient.GetRegistrationTime()).HasValue;
1036
1037 foreach (Type SystemConfigurationType in Types.GetTypesImplementingInterface(typeof(ISystemConfiguration)))
1038 {
1039 if (SystemConfigurationType.IsAbstract || SystemConfigurationType.IsInterface || SystemConfigurationType.IsGenericTypeDefinition)
1040 continue;
1041
1042 SystemConfigurationTypes[SystemConfigurationType.FullName] = SystemConfigurationType;
1043 }
1044
1046 {
1047 string s = SystemConfiguration.GetType().FullName;
1048
1049 if (SystemConfigurations.ContainsKey(s))
1050 await Database.Delete(SystemConfiguration); // No duplicates allowed by mistake
1051 else
1052 {
1053 SystemConfigurations[s] = SystemConfiguration;
1054 SystemConfigurationTypes.Remove(s);
1055
1057 {
1059 {
1062
1063 NewConfigurations ??= new LinkedList<SystemConfiguration>();
1064 NewConfigurations.AddLast(SystemConfiguration);
1065 continue;
1066 }
1067
1068 if (Simplify && await SystemConfiguration.SimplifiedConfiguration())
1069 {
1072
1073 NewConfigurations ??= new LinkedList<SystemConfiguration>();
1074 NewConfigurations.AddLast(SystemConfiguration);
1075 continue;
1076 }
1077
1078 Configured = false;
1079 }
1080 }
1081 }
1082
1083 foreach (KeyValuePair<string, Type> P in SystemConfigurationTypes)
1084 {
1085 try
1086 {
1088 SystemConfiguration.Complete = false;
1089 SystemConfiguration.Created = DateTime.Now;
1090
1092
1093 SystemConfigurations[P.Key] = SystemConfiguration;
1094
1096 {
1099
1100 NewConfigurations ??= new LinkedList<SystemConfiguration>();
1101 NewConfigurations.AddLast(SystemConfiguration);
1102
1103 CheckDeferredConfigurations = true;
1104 continue;
1105 }
1106
1107 if (Simplify && await SystemConfiguration.SimplifiedConfiguration())
1108 {
1111
1112 NewConfigurations ??= new LinkedList<SystemConfiguration>();
1113 NewConfigurations.AddLast(SystemConfiguration);
1114 continue;
1115 }
1116
1117 Configured = false;
1118 }
1119 catch (Exception ex)
1120 {
1121 Log.Exception(ex);
1122 continue;
1123 }
1124 }
1125
1126 configurations = new SystemConfiguration[SystemConfigurations.Count];
1127 SystemConfigurations.Values.CopyTo(configurations, 0);
1128 Array.Sort(configurations, (c1, c2) => c1.Priority - c2.Priority);
1129
1130 ISystemConfiguration CurrentConfiguration = null;
1131 LinkedList<HttpResource> SetupResources = null;
1132
1133 if (!Configured)
1134 {
1135 configuring = true;
1136
1137 if (loopbackIntefaceAvailable)
1138 Log.Notice("System needs to be configured. This is done by navigating to the loopback interface using a browser on this machine.");
1139 else
1140 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.");
1141
1142 webServer = new HttpServer(GetConfigPorts("HTTP"), null, null)
1143 {
1144 ResourceOverride = "/Starting.md",
1145 ResourceOverrideFilter = "(?<!Login)[.]md(\\?[.]*)?$",
1146 LoginAuditor = loginAuditor
1147 };
1148
1149 webServer.Register("/Starting.md", StartingMd);
1150 webServer.CustomError += WebServer_CustomError;
1151
1152 SetupResources = new LinkedList<HttpResource>();
1153
1154 SetupResources.AddLast(webServer.Register(new HttpFolderResource("/Graphics", Path.Combine(appDataFolder, "Graphics"), false, false, true, false, HostDomainOptions.SameForAllDomains))); // TODO: Add authentication mechanisms for PUT & DELETE.
1155 SetupResources.AddLast(webServer.Register(new HttpFolderResource("/Transforms", Path.Combine(appDataFolder, "Transforms"), false, false, true, false, HostDomainOptions.SameForAllDomains))); // TODO: Add authentication mechanisms for PUT & DELETE.
1156 SetupResources.AddLast(webServer.Register(new HttpFolderResource("/highlight", "Highlight", false, false, true, false, HostDomainOptions.SameForAllDomains))); // Syntax highlighting library, provided by http://highlightjs.org
1157 SetupResources.AddLast(webServer.Register(root = new HttpFolderResource(string.Empty, rootFolder, false, false, true, true, HostDomainOptions.UseDomainSubfolders))); // TODO: Add authentication mechanisms for PUT & DELETE.
1158 SetupResources.AddLast(webServer.Register("/", GoToDefaultPage));
1159 SetupResources.AddLast(webServer.Register(new ClientEvents()));
1160 SetupResources.AddLast(webServer.Register(new ClientEventsWebSocket()));
1161 SetupResources.AddLast(webServer.Register(new Login()));
1162 SetupResources.AddLast(webServer.Register(new Logout()));
1163 SetupResources.AddLast(webServer.Register(new MasterJavascript(webServer)));
1164
1165 emoji1_24x24 = new Emoji1LocalFiles(Emoji1SourceFileType.Svg, 24, 24, "/Graphics/Emoji1/svg/%FILENAME%",
1166 Path.Combine(runtimeFolder, "Graphics", "Emoji1.zip"), Path.Combine(appDataFolder, "Graphics"));
1167
1168 root.AllowTypeConversion();
1169
1170 MarkdownSettings.SetDefaultEmojiSource(emoji1_24x24, true);
1171 MarkdownToHtmlConverter.EmojiSource = emoji1_24x24;
1172 MarkdownToHtmlConverter.RootFolder = rootFolder;
1173 }
1174
1175 foreach (SystemConfiguration Configuration in configurations)
1176 {
1177 Configuration.SetStaticInstance(Configuration);
1178
1179 if (!(webServer is null))
1180 await Configuration.InitSetup(webServer);
1181 }
1182
1183 bool ReloadConfigurations;
1184
1185 do
1186 {
1187 ReloadConfigurations = false;
1188
1189 foreach (SystemConfiguration Configuration in configurations)
1190 {
1191 bool NeedsCleanup = false;
1192
1193 if (!Configuration.Complete)
1194 {
1195 CurrentConfiguration = Configuration;
1196
1197 if (!(webServer is null))
1198 webServer.ResourceOverride = Configuration.Resource;
1199
1200 Configuration.SetStaticInstance(Configuration);
1201
1202 if (!(startingServer is null))
1203 {
1204 await startingServer.ReleaseMutex();
1205 startingServer.Dispose();
1206 startingServer = null;
1207 }
1208
1209 await ClientEvents.PushEvent(ClientEvents.GetTabIDs(), "Reload", string.Empty);
1210
1211 if (!(webServer is null) && await Configuration.SetupConfiguration(webServer))
1212 ReloadConfigurations = true;
1213
1214 NeedsCleanup = true;
1215 }
1216
1217 DateTime StartConfig = DateTime.UtcNow;
1218
1219 try
1220 {
1221 await Configuration.ConfigureSystem();
1222 }
1223 catch (Exception)
1224 {
1225 await RepairIfInproperShutdown();
1226
1227 try
1228 {
1229 await Configuration.ConfigureSystem();
1230 }
1231 catch (Exception ex)
1232 {
1233 Log.Exception(ex);
1234 }
1235 }
1236
1237 if (NeedsCleanup && !(webServer is null))
1238 await Configuration.CleanupAfterConfiguration(webServer);
1239
1240 if (ReloadConfigurations)
1241 {
1242 Configured = true;
1243
1245 {
1246 string s = SystemConfiguration.GetType().FullName;
1247
1248 if (!(webServer is null) && SystemConfigurations.TryGetValue(s, out SystemConfiguration OldConfiguration))
1249 await OldConfiguration.UnregisterSetup(webServer);
1250
1251 SystemConfigurations[s] = SystemConfiguration;
1253
1254 if (!(webServer is null))
1255 await SystemConfiguration.InitSetup(webServer);
1256 }
1257
1258 foreach (SystemConfiguration SystemConfiguration in SystemConfigurations.Values)
1259 {
1261 {
1262 Configured = false;
1263 break;
1264 }
1265 }
1266
1267 configurations = new SystemConfiguration[SystemConfigurations.Count];
1268 SystemConfigurations.Values.CopyTo(configurations, 0);
1269 Array.Sort(configurations, (c1, c2) => c1.Priority - c2.Priority);
1270
1271 break;
1272 }
1273
1274 if (DateTime.UtcNow.Subtract(StartConfig).TotalSeconds > 2)
1275 await ClientEvents.PushEvent(ClientEvents.GetTabIDs(), "Reload", string.Empty);
1276 }
1277 }
1278 while (ReloadConfigurations);
1279
1280 configuring = false;
1281 loginAuditor.Domain = DomainConfiguration.Instance.Domain;
1282
1283 if (!(webServer is null))
1284 {
1285 webServer.ResourceOverride = "/Starting.md";
1286 await ClientEvents.PushEvent(ClientEvents.GetTabIDs(), "Reload", string.Empty);
1287
1288 if (!(SetupResources is null))
1289 {
1290 foreach (HttpResource Resource in SetupResources)
1291 webServer.Unregister(Resource);
1292 }
1293
1294 webServer.ConfigureMutualTls(ClientCertificates, TrustClientCertificates, PortSpecificMTlsSettings, true);
1295 webServer.NetworkChanged();
1296
1297 webServer.AddHttpPorts(GetConfigPorts("HTTP"));
1298
1299 if (!(certificate is null))
1300 {
1301 webServer.AddHttpsPorts(GetConfigPorts("HTTPS"));
1302 webServer.UpdateCertificate(certificate);
1303 }
1304 }
1305 else
1306 {
1307 if (!(certificate is null))
1308 {
1309 webServer = new HttpServer(GetConfigPorts("HTTP"), GetConfigPorts("HTTPS"), certificate, true,
1310 ClientCertificates, TrustClientCertificates, PortSpecificMTlsSettings, true);
1311 }
1312 else
1313 webServer = new HttpServer(GetConfigPorts("HTTP"), null, null);
1314
1315 webServer.Register("/Starting.md", StartingMd);
1316 webServer.ResourceOverride = "/Starting.md";
1317 webServer.LoginAuditor = loginAuditor;
1318
1319 webServer.CustomError += WebServer_CustomError;
1320
1321 foreach (SystemConfiguration Configuration in configurations)
1322 {
1323 try
1324 {
1325 await Configuration.InitSetup(webServer);
1326 }
1327 catch (Exception)
1328 {
1329 await RepairIfInproperShutdown();
1330
1331 try
1332 {
1333 await Configuration.InitSetup(webServer);
1334 }
1335 catch (Exception ex)
1336 {
1337 Log.Exception(ex);
1338 }
1339 }
1340 }
1341 }
1342
1343 if (CheckDeferredConfigurations)
1344 {
1345 foreach (SystemConfiguration Configuration in configurations)
1346 {
1347 try
1348 {
1349 await Configuration.DeferredConfiguration(webServer);
1350 }
1351 catch (Exception ex)
1352 {
1353 Log.Exception(ex);
1354 }
1355 }
1356 }
1357
1358 webServer.SetHttp2ConnectionSettings(Http2Enabled, Http2InitialStreamWindowSize,
1359 Http2InitialConnectionWindowSize, Http2MaxFrameSize, Http2MaxConcurrentStreams,
1360 Http2HeaderTableSize, false, Http2NoRfc7540Priorities, Http2Profiling, true);
1361
1362 webServer.ConnectionProfiled += WebServer_ConnectionProfiled;
1363 webServer.OnTryGetLocalResourceFileName += (string Resource, string Host, out string FileName) =>
1364 TryGetLocalResourceFileName(Resource, Host, out FileName);
1365
1366 Types.SetModuleParameter("HTTP", webServer);
1367 Types.SetModuleParameter("X509", certificate);
1368 Types.SetModuleParameter("LoginAuditor", webServer.LoginAuditor);
1369
1370 InternetContent.LocalDomainCheck += InternetContent_LocalDomainCheck;
1371
1372 await WriteWebServerOpenPorts();
1373 webServer.OnNetworkChanged += async (Sender, e) =>
1374 {
1375 try
1376 {
1377 await WriteWebServerOpenPorts();
1378 }
1379 catch (Exception ex)
1380 {
1381 Log.Exception(ex);
1382 }
1383 };
1384
1385 await CheckWAF();
1386
1388
1389 if (HasDomain)
1390 {
1392 jwtFactory = JwtFactory.CreateHmacSha256("https://" + Domain);
1393 else
1394 jwtFactory = JwtFactory.CreateHmacSha256("http://" + Domain);
1395
1396 JwtFactory.ValidateAudience += (sender, e) =>
1397 {
1398 foreach (string Audience in e.Audience)
1399 {
1400 if (IsDomain(Audience, true))
1401 {
1402 e.Acceptable = true;
1403 return;
1404 }
1405 }
1406 };
1407 }
1408 else
1409 jwtFactory = JwtFactory.CreateHmacSha256(string.Empty);
1410
1411 oauthEnvironment = new OAuth2Environment
1412 {
1413 LoginMasterFileName = Path.Combine(rootFolder, "MasterOAuth.md")
1414 };
1415
1416 Types.SetModuleParameter("JWT", jwtFactory);
1418 Types.SetModuleParameter("OAUTH2", oauthEnvironment);
1419
1420 webServer.Register(new ProtectedResourceMetaData(oauthEnvironment));
1421 webServer.Register(new OAuthTokenResource(oauthEnvironment));
1422 webServer.Register(new OAuthDeviceAuthorizationResource(oauthEnvironment));
1423 webServer.Register(new OAuthAuthorizeResource(oauthEnvironment));
1424 webServer.Register(new OAuthIntrospectionResource(oauthEnvironment));
1425 webServer.Register(new AuthorizationServerMetaData(oauthEnvironment));
1426 webServer.Register(new OAuthRegistrationResource(oauthEnvironment));
1427 webServer.Register(new OAuthManagementResource(oauthEnvironment));
1428 webServer.Register(new HttpFolderResource("/Graphics", Path.Combine(appDataFolder, "Graphics"), false, false, true, false, HostDomainOptions.SameForAllDomains)); // TODO: Add authentication mechanisms for PUT & DELETE.
1429 webServer.Register(new HttpFolderResource("/Transforms", Path.Combine(appDataFolder, "Transforms"), false, false, true, false, HostDomainOptions.SameForAllDomains)); // TODO: Add authentication mechanisms for PUT & DELETE.
1430 webServer.Register(new HttpFolderResource("/highlight", "Highlight", false, false, true, false, HostDomainOptions.SameForAllDomains)); // Syntax highlighting library, provided by http://highlightjs.org
1431 webServer.Register(root = new HttpFolderResource(string.Empty, rootFolder, false, false, true, true, HostDomainOptions.UseDomainSubfolders)); // TODO: Add authentication mechanisms for PUT & DELETE.
1432 webServer.Register(httpxProxy = new HttpxProxy("/HttpxProxy", xmppClient, MaxChunkSize));
1433 webServer.Register("/", GoToDefaultPage);
1434 webServer.Register(new HttpConfigurableFileResource("/robots.txt", Path.Combine(rootFolder, "robots.txt"), PlainTextCodec.DefaultContentType, true));
1435 webServer.Register(new HttpConfigurableFileResource("/favicon.ico", Path.Combine(rootFolder, "favicon.ico"), ImageCodec.ContentTypeIcon, false));
1436 webServer.Register(new ClientEvents());
1437 webServer.Register(new ClientEventsWebSocket());
1438 webServer.Register(new Login());
1439 webServer.Register(new Logout());
1440 webServer.Register(new MasterJavascript(webServer));
1441 webServer.Register(new Echo());
1442 webServer.Register(new WebResources.Ping());
1443 webServer.Register(new ProposeContract());
1444 webServer.Register(new UrlShortener());
1445
1446 Icon FavIcon = new Icon(new Uri(GetUrl("/favicon.ico")),
1448 Icon[] Icons = new Icon[] { FavIcon };
1449
1450 mcpSniffers = new XmlFileSnifferSet(appDataFolder + "MCP" + Path.DirectorySeparatorChar +
1451 "Sniffers", "MCP Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml", TimeSpan.FromHours(8),
1452 appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
1453 7, BinaryPresentationMethod.ByteCount);
1454
1455 webServer.Register(new Mcp.Content.InternetContentMcpServer("/MCP/Content", Icons, null, mcpSniffers));
1456 webServer.Register(new Mcp.Events.EventLogMcpServer("/MCP/EventLog", Icons, null, mcpSniffers));
1457 webServer.Register(new Mcp.Files.FileStorageMcpServer("/MCP/Files",
1458 Path.Combine(appDataFolder, "MCP", "Files"), Icons, null, mcpSniffers));
1459
1460 if (emoji1_24x24 is null)
1461 {
1462 emoji1_24x24 = new Emoji1LocalFiles(Emoji1SourceFileType.Svg, 24, 24, "/Graphics/Emoji1/svg/%FILENAME%",
1463 Path.Combine(runtimeFolder, "Graphics", "Emoji1.zip"), Path.Combine(appDataFolder, "Graphics"));
1464
1465 MarkdownSettings.SetDefaultEmojiSource(emoji1_24x24, true);
1466 MarkdownToHtmlConverter.EmojiSource = emoji1_24x24;
1467 MarkdownToHtmlConverter.RootFolder = rootFolder;
1468 }
1469
1470 root.AllowTypeConversion();
1471
1472 XmlElement DefaultHttpResponseHeaders = Config.DocumentElement["DefaultHttpResponseHeaders"];
1473 if (!(DefaultHttpResponseHeaders is null))
1474 {
1475 foreach (XmlNode N in DefaultHttpResponseHeaders.ChildNodes)
1476 {
1477 if (N is XmlElement E && E.LocalName == "DefaultHttpResponseHeader")
1478 {
1479 string HeaderKey = XML.Attribute(E, "key");
1480 string HeaderValue = XML.Attribute(E, "value");
1481
1482 root.AddDefaultResponseHeader(HeaderKey, HeaderValue);
1483 }
1484 }
1485 }
1486
1487 XmlElement FileFolders = Config.DocumentElement["FileFolders"];
1488 if (!(FileFolders is null))
1489 {
1490 foreach (XmlNode N in FileFolders.ChildNodes)
1491 {
1492 if (N is XmlElement E && E.LocalName == "FileFolder")
1493 {
1494 string WebFolder = XML.Attribute(E, "webFolder");
1495 string FolderPath = XML.Attribute(E, "folderPath");
1496
1497 HttpFolderResource FileFolder = new HttpFolderResource(WebFolder, FolderPath, false, false, true, true, HostDomainOptions.SameForAllDomains);
1498 webServer.Register(FileFolder);
1499
1500 foreach (XmlNode N2 in E.ChildNodes)
1501 {
1502 if (N2 is XmlElement E2 && E2.LocalName == "DefaultHttpResponseHeader")
1503 {
1504 string HeaderKey = XML.Attribute(E2, "key");
1505 string HeaderValue = XML.Attribute(E2, "value");
1506
1507 FileFolder.AddDefaultResponseHeader(HeaderKey, HeaderValue);
1508 }
1509 }
1510 }
1511 }
1512 }
1513
1514 XmlElement VanityResources = Config.DocumentElement["VanityResources"];
1515 if (!(VanityResources is null))
1516 {
1517 foreach (XmlNode N in VanityResources.ChildNodes)
1518 {
1519 if (N is XmlElement E && E.LocalName == "VanityResource")
1520 {
1521 string RegEx = XML.Attribute(E, "regex");
1522 string Url = XML.Attribute(E, "url");
1523
1524 try
1525 {
1526 webServer.RegisterVanityResource(RegEx, Url);
1527 }
1528 catch (Exception ex)
1529 {
1530 Log.Error("Unable to register vanity resource: " + ex.Message,
1531 new KeyValuePair<string, object>("RegEx", RegEx),
1532 new KeyValuePair<string, object>("Url", Url));
1533 }
1534 }
1535 }
1536 }
1537
1538 XmlElement Redirections = Config.DocumentElement["Redirections"];
1539 if (!(Redirections is null))
1540 {
1541 foreach (XmlNode N in Redirections.ChildNodes)
1542 {
1543 if (N is XmlElement E && E.LocalName == "Redirection")
1544 {
1545 string Resource = XML.Attribute(E, "resource");
1546 string Location = XML.Attribute(E, "location");
1547 bool IncludeSubPaths = XML.Attribute(E, "includeSubPaths", false);
1548 bool Permanent = XML.Attribute(E, "permanent", false);
1549
1550 try
1551 {
1552 webServer.Register(new HttpRedirectionResource(Resource, Location, IncludeSubPaths, Permanent));
1553 }
1554 catch (Exception ex)
1555 {
1556 Log.Error("Unable to register redirection: " + ex.Message,
1557 new KeyValuePair<string, object>("Resource", Resource),
1558 new KeyValuePair<string, object>("Location", Location),
1559 new KeyValuePair<string, object>("IncludeSubPaths", IncludeSubPaths),
1560 new KeyValuePair<string, object>("Permanent", Permanent));
1561 }
1562 }
1563 }
1564 }
1565
1566 XmlElement ReverseProxy = Config.DocumentElement["ReverseProxy"];
1567 if (!(ReverseProxy is null))
1568 {
1569 foreach (XmlNode N in ReverseProxy.ChildNodes)
1570 {
1571 if (!(N is XmlElement E))
1572 continue;
1573
1574 switch (E.LocalName)
1575 {
1576 case "ProxyResource":
1577 string LocalResource = XML.Attribute(E, "localResource");
1578 string RemoteDomain = XML.Attribute(E, "remoteDomain");
1579 string RemoteFolder = XML.Attribute(E, "remoteFolder");
1580 bool Encrypted = XML.Attribute(E, "encrypted", false);
1581 int RemotePort = XML.Attribute(E, "remotePort", Encrypted ? HttpServer.DefaultHttpsPort : HttpServer.DefaultHttpPort);
1582 bool UseSession = XML.Attribute(E, "useSession", false);
1583 int TimeoutMs = XML.Attribute(E, "timeoutMs", 10000);
1584 string Privilege = XML.Attribute(E, "privilege");
1585
1586 try
1587 {
1588 if (string.IsNullOrEmpty(Privilege))
1589 {
1590 webServer.Register(new HttpReverseProxyResource(LocalResource, RemoteDomain, RemotePort, RemoteFolder, Encrypted,
1591 TimeSpan.FromMilliseconds(TimeoutMs), UseSession));
1592 }
1593 else
1594 {
1595 webServer.Register(new HttpReverseProxyResource(LocalResource, RemoteDomain, RemotePort, RemoteFolder, Encrypted,
1596 TimeSpan.FromMilliseconds(TimeoutMs), UseSession, HttpModule.GetAuthenticationSchemes(Privilege),
1597 Privilege));
1598 }
1599 }
1600 catch (Exception ex)
1601 {
1602 Log.Error("Unable to register reverse proxy: " + ex.Message,
1603 new KeyValuePair<string, object>("LocalResource", LocalResource),
1604 new KeyValuePair<string, object>("RemoteDomain", RemoteDomain),
1605 new KeyValuePair<string, object>("Encrypted", Encrypted),
1606 new KeyValuePair<string, object>("RemotePort", RemotePort),
1607 new KeyValuePair<string, object>("RemoteFolder", RemoteFolder),
1608 new KeyValuePair<string, object>("UseSession", UseSession),
1609 new KeyValuePair<string, object>("TimeoutMs", TimeoutMs));
1610 }
1611 break;
1612
1613 case "ProxyDomain":
1614 string LocalDomain = XML.Attribute(E, "localDomain");
1615 RemoteDomain = XML.Attribute(E, "remoteDomain");
1616 RemoteFolder = XML.Attribute(E, "remoteFolder");
1617 Encrypted = XML.Attribute(E, "encrypted", false);
1618 RemotePort = XML.Attribute(E, "remotePort", Encrypted ? HttpServer.DefaultHttpsPort : HttpServer.DefaultHttpPort);
1619 UseSession = XML.Attribute(E, "useSession", false);
1620 TimeoutMs = XML.Attribute(E, "timeoutMs", 10000);
1621 Privilege = XML.Attribute(E, "privilege");
1622
1623 try
1624 {
1625 if (string.IsNullOrEmpty(Privilege))
1626 {
1627 webServer.RegisterDomainProxy(LocalDomain, new HttpReverseProxyResource(
1628 "/", RemoteDomain, RemotePort, RemoteFolder, Encrypted,
1629 TimeSpan.FromMilliseconds(TimeoutMs), UseSession));
1630 }
1631 else
1632 {
1633 webServer.RegisterDomainProxy(LocalDomain, new HttpReverseProxyResource(
1634 "/", RemoteDomain, RemotePort, RemoteFolder, Encrypted,
1635 TimeSpan.FromMilliseconds(TimeoutMs), UseSession,
1637 }
1638 }
1639 catch (Exception ex)
1640 {
1641 Log.Error("Unable to register reverse proxy: " + ex.Message,
1642 new KeyValuePair<string, object>("LocalDomain", LocalDomain),
1643 new KeyValuePair<string, object>("RemoteDomain", RemoteDomain),
1644 new KeyValuePair<string, object>("Encrypted", Encrypted),
1645 new KeyValuePair<string, object>("RemotePort", RemotePort),
1646 new KeyValuePair<string, object>("RemoteFolder", RemoteFolder),
1647 new KeyValuePair<string, object>("UseSession", UseSession),
1648 new KeyValuePair<string, object>("TimeoutMs", TimeoutMs));
1649 }
1650 break;
1651 }
1652 }
1653 }
1654
1655 await LoadScriptResources();
1656
1657 httpxServer = new HttpxServer(xmppClient, webServer, MaxChunkSize);
1658 Types.SetModuleParameter("HTTPX", httpxProxy);
1659 Types.SetModuleParameter("HTTPXS", httpxServer);
1660
1661 httpxProxy.IbbClient = ibbClient;
1662 httpxServer.IbbClient = ibbClient;
1663
1664 httpxProxy.Socks5Proxy = socksProxy;
1665 httpxServer.Socks5Proxy = socksProxy;
1666
1667 if (xmppCredentials.Sniffer || HttpSniffersPerEndpoint)
1668 {
1669 ISniffer Sniffer;
1670
1671 Sniffer = new XmlFileSniffer(appDataFolder + "HTTP" + Path.DirectorySeparatorChar +
1672 "HTTP Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml",
1673 appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
1674 7, BinaryPresentationMethod.ByteCount);
1675 webServer.Add(Sniffer);
1676
1677 if (HttpSniffersPerEndpoint)
1678 {
1679 xmlFileSnifferCache = new Cache<string, XmlFileSniffer>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromHours(1));
1680 xmlFileSnifferCache.Removed += XmlFileSnifferCache_Removed;
1681
1682 webServer.GetCustomSniffers += GetCustomHttpSniffers;
1683 }
1684 }
1685
1686 await ClientEvents.PushEvent(ClientEvents.GetTabIDs(), "Reload", string.Empty);
1687
1688 try
1689 {
1690 coapEndpoint = new CoapEndpoint();
1691 Types.SetModuleParameter("CoAP", coapEndpoint);
1692 }
1693 catch (Exception ex)
1694 {
1695 Log.Exception(ex);
1696 }
1697
1699 IDataSource[] InitialSources = null;
1700
1701 try
1702 {
1703 InitialSources = new IDataSource[]
1704 {
1705 new GroupSource(),
1706 new JobSource(),
1707 new MeteringTopology(),
1708 new OutputSource(),
1709 new ProcessorSource(),
1710 new ReportsDataSource()
1711 };
1712
1713 Sources = new GetDataSourcesEventArgs(InitialSources);
1714 }
1715 catch (Exception)
1716 {
1717 await RepairIfInproperShutdown();
1718
1719 try
1720 {
1721 InitialSources ??= new IDataSource[]
1722 {
1723 new GroupSource(),
1724 new JobSource(),
1725 new MeteringTopology(),
1726 new OutputSource(),
1727 new ProcessorSource(),
1728 new ReportsDataSource()
1729 };
1730
1731 Sources = new GetDataSourcesEventArgs(InitialSources);
1732 }
1733 catch (Exception ex)
1734 {
1735 Log.Exception(ex);
1736 Sources = new GetDataSourcesEventArgs();
1737 }
1738 }
1739
1740 await ReportsDataSource.RegisterRootNode(new ReportFilesFolder(reportsFolder, "Report Files", null));
1741
1742 Types.GetLoadedModules(); // Makes sure all modules are instantiated, allowing static constructors to add
1743 // appropriate data sources, if necessary.
1744
1745 await GetDataSources.Raise(typeof(Gateway), Sources);
1746
1747 concentratorServer = await ConcentratorServer.Create(xmppClient, thingRegistryClient, provisioningClient, Sources.Sources);
1748 avatarClient = new AvatarClient(xmppClient, pepClient);
1749
1750 Types.SetModuleParameter("Concentrator", concentratorServer);
1751 Types.SetModuleParameter("Sources", concentratorServer.DataSources);
1753 Types.SetModuleParameter("Sensor", concentratorServer.SensorServer);
1754 Types.SetModuleParameter("Control", concentratorServer.ControlServer);
1755 Types.SetModuleParameter("Registry", thingRegistryClient);
1756 Types.SetModuleParameter("Provisioning", provisioningClient);
1757 Types.SetModuleParameter("Contracts", contractsClient);
1758 Types.SetModuleParameter("Avatar", avatarClient);
1759 Types.SetModuleParameter("Scheduler", scheduler);
1760 Types.SetModuleParameter("FavIcon", GetUrl("/favicon.ico"));
1761
1762 if (HasDomain)
1764
1765 if (FirstStart)
1766 {
1767 concentratorServer.SensorServer.AssignAuthority += SensorServer_AssignAuthority;
1768 MeteringTopology.OnNewMomentaryValues += NewMomentaryValues;
1769 ProvisionedMeteringNode.QrCodeUrlRequested += ProvisionedMeteringNode_QrCodeUrlRequested;
1770 }
1771
1772 DeleteOldDataSourceEvents(null);
1773
1774 try
1775 {
1776 string BinaryFolder = AppDomain.CurrentDomain.BaseDirectory;
1777 string[] LanguageFiles = Directory.GetFiles(BinaryFolder, "*.lng", SearchOption.AllDirectories);
1778 string FileName;
1779
1780 if (LanguageFiles.Length > 0)
1781 {
1782 XmlSchema Schema = XSL.LoadSchema(Translator.SchemaResource, typeof(Translator).Assembly);
1783
1784 foreach (string LanguageFile in LanguageFiles)
1785 {
1786 string Xml = null;
1787
1788 try
1789 {
1790 FileName = LanguageFile;
1791 if (FileName.StartsWith(BinaryFolder))
1792 FileName = FileName[BinaryFolder.Length..];
1793
1794 DateTime LastWriteTime = File.GetLastWriteTimeUtc(LanguageFile);
1795 DateTime LastImportedTime = await RuntimeSettings.GetAsync(FileName, DateTime.MinValue);
1796
1797 if (LastWriteTime > LastImportedTime)
1798 {
1799 Log.Informational("Importing language file.", FileName);
1800
1801 Xml = await Files.ReadAllTextAsync(LanguageFile);
1802 XmlDocument Doc = XML.ParseXml(Xml, true);
1803
1805
1806 using (XmlReader r = new XmlNodeReader(Doc))
1807 {
1808 await Translator.ImportAsync(r);
1809 }
1810
1811 RuntimeSettings.Set(FileName, LastWriteTime);
1812 }
1813 }
1814 catch (XmlException ex)
1815 {
1816 ex = XML.AnnotateException(ex, Xml);
1817 Log.Exception(ex, LanguageFile);
1818 }
1819 catch (Exception ex)
1820 {
1821 Log.Exception(ex, LanguageFile);
1822 }
1823 }
1824 }
1825
1826 foreach (string UnhandledException in Directory.GetFiles(appDataFolder, "UnhandledException*.txt", SearchOption.TopDirectoryOnly))
1827 {
1828 try
1829 {
1830 string Msg = await Files.ReadAllTextAsync(UnhandledException);
1831 File.Delete(UnhandledException);
1832
1833 StringBuilder sb = new StringBuilder();
1834
1835 sb.AppendLine("Unhandled Exception");
1836 sb.AppendLine("=======================");
1837 sb.AppendLine();
1838 sb.AppendLine("```");
1839 sb.AppendLine(Msg);
1840 sb.AppendLine("```");
1841
1842 Log.Emergency(sb.ToString());
1843 }
1844 catch (Exception ex)
1845 {
1846 Log.Emergency(ex, UnhandledException);
1847 }
1848 }
1849
1850 ChunkedList<IModule> FailedModules = new ChunkedList<IModule>();
1851
1852 if (await Types.StartAllModules(int.MaxValue, new ModuleStartOrder(), null, FailedModules))
1853 Log.Informational("Server started.");
1854 else
1855 {
1856 StringBuilder sb = new StringBuilder();
1857
1858 sb.AppendLine("Unable to start all modules. The following modules failed to load:");
1859 sb.AppendLine();
1860
1861 foreach (IModule Module in FailedModules)
1862 {
1863 sb.Append("* `");
1864 sb.Append(Module.GetType().FullName);
1865 sb.AppendLine("`");
1866 }
1867
1868 Log.Critical(sb.ToString());
1869 }
1870
1872 await ProcessServiceConfigurations(false);
1873
1874 if (!(NewConfigurations is null))
1875 {
1876 foreach (SystemConfiguration Configuration in NewConfigurations)
1877 {
1878 StringBuilder sb = new StringBuilder();
1879
1880 sb.AppendLine("New System Configuration");
1881 sb.AppendLine("=============================");
1882 sb.AppendLine();
1883 sb.AppendLine("A new system configuration is available.");
1884 sb.AppendLine("It has been set to simplified configuration, to not stop processing.");
1885 sb.AppendLine("You should review the configuration however, as soon as possible.");
1886 sb.AppendLine();
1887 sb.Append("[Click here to review the new system configuration](http");
1888
1890 sb.Append('s');
1891
1892 sb.Append("://");
1894 sb.Append(Configuration.Resource);
1895 sb.AppendLine(").");
1896
1897 Log.Alert(sb.ToString());
1898 }
1899 }
1900 }
1901 catch (Exception ex)
1902 {
1903 Log.Exception(ex);
1904 }
1905 finally
1906 {
1907 if (!(webServer is null))
1908 {
1909 webServer.ResourceOverride = null;
1910 webServer.ResourceOverrideFilter = null;
1911
1912 if (webServer.GetPorts(true, true).Length == 0) // No ports opened
1913 ScheduleEvent(AttemptReopenPorts, DateTime.Now.AddMinutes(1), null);
1914 }
1915
1916 if (!(startingServer is null))
1917 {
1918 await startingServer.ReleaseMutex();
1919 startingServer.Dispose();
1920 startingServer = null;
1921 }
1922
1923 if (xmppClient.State != XmppState.Connected)
1924 await xmppClient.Connect();
1925 }
1926 }
1927 catch (Exception ex)
1928 {
1929 Log.Exception(ex);
1930
1931 if (!(startingServer is null))
1932 {
1933 await startingServer.ReleaseMutex();
1934 startingServer.Dispose();
1935 startingServer = null;
1936 }
1937
1938 if (!(gatewayRunning is null))
1939 {
1940 await gatewayRunning.ReleaseMutex();
1941 gatewayRunning.Dispose();
1942 gatewayRunning = null;
1943 }
1944
1945 ExceptionDispatchInfo.Capture(ex).Throw();
1946 }
1947
1948 return true;
1949 }
1950
1954 public static async Task CheckWAF()
1955 {
1956 string WafFile = Path.Combine(appDataFolder, WebApplicationFirewallLocalFileName);
1957 if (File.Exists(WafFile))
1958 {
1959 try
1960 {
1961 DateTime LastUpdated = File.GetLastWriteTimeUtc(WafFile);
1962
1963 if (LastUpdated > wafTimestamp)
1964 {
1965 if (webServer.WebApplicationFirewall is null)
1966 {
1967 WebApplicationFirewall Waf = WebApplicationFirewall.LoadFromFile(WafFile, loginAuditor, appDataFolder);
1968 webServer.WebApplicationFirewall = Waf;
1969 Log.Informational("Web Application Firewall configuration loaded.", WafFile);
1970 }
1971 else
1972 {
1973 await webServer.WebApplicationFirewall.Reload();
1974 Log.Informational("Web Application Firewall configuration reloaded due to file change.", WafFile);
1975 }
1976
1977 wafTimestamp = LastUpdated;
1978 }
1979 }
1980 catch (Exception ex)
1981 {
1982 Log.Exception(ex, WafFile);
1983 }
1984 }
1985 }
1986
1987 private static void AttemptReopenPorts(object _)
1988 {
1989 webServer.NetworkChanged();
1990
1991 if (webServer.GetPorts(true, true).Length == 0) // No ports opened
1992 ScheduleEvent(AttemptReopenPorts, DateTime.Now.AddMinutes(1), null);
1993 }
1994
1995 private static Task GetCustomHttpSniffers(object Sender, CustomSniffersEventArgs e)
1996 {
1997 int i, c = e.Sniffers?.Length ?? 0;
1998
1999 for (i = 0; i < c; i++)
2000 {
2001 if (e.Sniffers[i] is XmlFileSniffer)
2002 {
2003 string RemoteIp = e.RemoteEndpoint.RemovePortNumber();
2004
2005 if (!(xmlFileSnifferCache?.TryGetValue(RemoteIp, out XmlFileSniffer Cached) ?? false))
2006 {
2007 Cached = new XmlFileSniffer(appDataFolder + "HTTP" + Path.DirectorySeparatorChar +
2008 RemoteIp.Replace(':', '_') + Path.DirectorySeparatorChar +
2009 "HTTP Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml",
2010 appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
2011 7, BinaryPresentationMethod.ByteCount);
2012
2013 Cached.OnBeforeWrite += (sender, e2) =>
2014 {
2015 xmlFileSnifferCache?.Ping(RemoteIp);
2016 };
2017
2018 xmlFileSnifferCache?.Add(RemoteIp, Cached);
2019 }
2020
2021 e.Sniffers[i] = Cached;
2022 }
2023 }
2024
2025 return Task.CompletedTask;
2026 }
2027
2028 private static async Task XmlFileSnifferCache_Removed(object Sender, CacheItemEventArgs<string, XmlFileSniffer> e)
2029 {
2030 await e.Value.DisposeAsync();
2031 }
2032
2033 private static Task ProvisionedMeteringNode_QrCodeUrlRequested(object Sender, GetQrCodeUrlEventArgs e)
2034 {
2035 StringBuilder Link = new StringBuilder();
2036 Link.Append("https://");
2037
2038 if (HasDomain)
2039 Link.Append(Domain);
2040 else
2041 Link.Append(XmppClient.Domain);
2042
2043 Link.Append("/QR/");
2044 Link.Append(WebUtility.UrlEncode(e.Text));
2045 Link.Append("?w=400&h=400&q=2");
2046
2047 e.Url = Link.ToString();
2048
2049 return Task.CompletedTask;
2050 }
2051
2052 private static async Task StartingMd(HttpRequest Request, HttpResponse Response)
2053 {
2054 if (string.IsNullOrEmpty(webServer?.ResourceOverride))
2055 throw new TemporaryRedirectException("/");
2056
2057 string Markdown;
2058
2059 try
2060 {
2061 Markdown = await Files.ReadAllTextAsync(Path.Combine(rootFolder, "Starting.md"));
2062 }
2063 catch (Exception)
2064 {
2065 StringBuilder sb = new StringBuilder();
2066
2067 sb.AppendLine("Title: Starting");
2068 sb.AppendLine("Description: The starting page will be displayed while the service is being started.");
2069 sb.AppendLine("Cache-Control: max-age=0, no-cache, no-store");
2070 sb.AppendLine("Refresh: 2");
2071 sb.AppendLine();
2072 sb.AppendLine("============================================================================================================================================");
2073 sb.AppendLine();
2074 sb.AppendLine("Starting Service");
2075 sb.AppendLine("====================");
2076 sb.AppendLine();
2077 sb.AppendLine("Please wait while the service is being started. This page will update automatically.");
2078
2079 Markdown = sb.ToString();
2080 }
2081
2082 Variables v = Request.Session ?? new Variables();
2083 MarkdownSettings Settings = new MarkdownSettings(emoji1_24x24, true, v);
2084 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(Markdown, Settings);
2085 string Html = await Doc.GenerateHTML();
2086
2087 Response.ContentType = "text/html; charset=utf-8";
2088 await Response.Write(true, System.Text.Encoding.UTF8.GetBytes(Html));
2089 await Response.SendResponse();
2090 }
2091
2092 private static Task GoToDefaultPage(HttpRequest Request, HttpResponse Response)
2093 {
2094 if (TryGetDefaultPage(Request, out string DefaultPage))
2095 return Response.SendResponse(new TemporaryRedirectException(DefaultPage));
2096 else
2097 return Response.SendResponse(new NotFoundException("No default page defined."));
2098 }
2099
2100 private class ModuleStartOrder : IComparer<IModule>
2101 {
2102 private readonly DependencyOrder dependencyOrder = new DependencyOrder();
2103
2104 public int Compare(IModule x, IModule y)
2105 {
2106 int c1 = this.ModuleCategory(x);
2107 int c2 = this.ModuleCategory(y);
2108
2109 int i = c1 - c2;
2110 if (i != 0)
2111 return i;
2112
2113 return this.dependencyOrder.Compare(x, y);
2114 }
2115
2116 private int ModuleCategory(IModule x)
2117 {
2118 if (x is Persistence.LifeCycle.DatabaseModule)
2119 return 1;
2120 else if (x is Runtime.Transactions.TransactionModule)
2121 return 2;
2122 else if (x is NetworkingModule)
2123 return int.MaxValue;
2124 else
2125 return 3;
2126 }
2127 }
2128
2129 private static async Task RepairIfInproperShutdown()
2130 {
2131 IDatabaseProvider DatabaseProvider = Database.Provider;
2132 Type ProviderType = DatabaseProvider.GetType();
2133 PropertyInfo AutoRepairReportFolder = ProviderType.GetProperty("AutoRepairReportFolder");
2134 AutoRepairReportFolder?.SetValue(DatabaseProvider, Path.Combine(AppDataFolder, "Backup"));
2135
2136 MethodInfo MI = ProviderType.GetMethod("RepairIfInproperShutdown", new Type[] { typeof(string) });
2137
2138 if (!(MI is null))
2139 {
2140 Task T = MI.Invoke(DatabaseProvider, new object[] { AppDataFolder + "Transforms" + Path.DirectorySeparatorChar + "DbStatXmlToHtml.xslt" }) as Task;
2141
2142 if (T is Task<string[]> StringArrayTask)
2143 DatabaseConfiguration.RepairedCollections = await StringArrayTask;
2144 else if (!(T is null))
2145 await T;
2146 }
2147 }
2148
2149 private static async Task WriteWebServerOpenPorts()
2150 {
2151 StringBuilder sb = new StringBuilder();
2152
2153 foreach (int Port in webServer.OpenPorts)
2154 sb.AppendLine(Port.ToString());
2155
2156 try
2157 {
2158 await Files.WriteAllTextAsync(appDataFolder + "Ports.txt", sb.ToString());
2159 }
2160 catch (Exception ex)
2161 {
2162 Log.Exception(ex);
2163 }
2164 }
2165
2166 private static void Assert_UnauthorizedAccess(object Sender, UnauthorizedAccessEventArgs e)
2167 {
2168 DateTime Now = DateTime.Now;
2169 string Key = e.Trace.ToString();
2170
2171 lock (lastUnauthorizedAccess)
2172 {
2173 if (lastUnauthorizedAccess.TryGetValue(Key, out DateTime TP) && (Now - TP).TotalHours < 1)
2174 return;
2175
2176 lastUnauthorizedAccess[Key] = Now;
2177 }
2178
2179 StringBuilder Markdown = new StringBuilder();
2180
2181 Markdown.AppendLine("Unauthorized access detected and prevented.");
2182 Markdown.AppendLine("===============================================");
2183 Markdown.AppendLine();
2184 Markdown.AppendLine("| Details ||");
2185 Markdown.AppendLine("|:---|:---|");
2186 Markdown.Append("| Method | `");
2187 Markdown.Append(e.Method.Name);
2188 Markdown.AppendLine("` |");
2189 Markdown.Append("| Type | `");
2190 Markdown.Append(e.Type.FullName);
2191 Markdown.AppendLine("` |");
2192 Markdown.Append("| Assembly | `");
2193 Markdown.Append(e.Assembly.GetName().Name);
2194 Markdown.AppendLine("` |");
2195 Markdown.Append("| Date | ");
2196 Markdown.Append(MarkdownDocument.Encode(Now.ToShortDateString()));
2197 Markdown.AppendLine(" |");
2198 Markdown.Append("| Time | ");
2199 Markdown.Append(MarkdownDocument.Encode(Now.ToLongTimeString()));
2200 Markdown.AppendLine(" |");
2201 Markdown.AppendLine();
2202 Markdown.AppendLine("Stack Trace:");
2203 Markdown.AppendLine();
2204 Markdown.AppendLine("```");
2205 Markdown.AppendLine(Log.CleanStackTrace(e.Trace.ToString()));
2206 Markdown.AppendLine("```");
2207
2208 SendNotification(Markdown.ToString());
2209 }
2210
2211 private static void CheckContentFiles(string ManifestFileName, Dictionary<string, CopyOptions> ContentOptions)
2212 {
2213 try
2214 {
2215 XmlDocument Doc = XML.LoadFromFile(ManifestFileName, true);
2216
2217 if (!(Doc.DocumentElement is null) &&
2218 Doc.DocumentElement.LocalName == "Module" &&
2219 Doc.DocumentElement.NamespaceURI == "http://waher.se/Schema/ModuleManifest.xsd")
2220 {
2221 CheckContentFiles(Doc.DocumentElement, runtimeFolder, runtimeFolder, appDataFolder, ContentOptions);
2222 }
2223 }
2224 catch (Exception ex)
2225 {
2226 Log.Exception(ex, ManifestFileName);
2227 }
2228 }
2229
2230 private enum CopyOptions
2231 {
2232 IfNewer,
2233 Always,
2234 IfNotExists
2235 }
2236
2237 private static void CheckContentFiles(XmlElement Element, string RuntimeFolder, string RuntimeSubfolder, string AppDataSubFolder,
2238 Dictionary<string, CopyOptions> ContentOptions)
2239 {
2240 bool AppDataFolderChecked = false;
2241
2242 foreach (XmlNode N in Element.ChildNodes)
2243 {
2244 if (N is XmlElement E)
2245 {
2246 switch (E.LocalName)
2247 {
2248 case "Folder":
2249 string Name = XML.Attribute(E, "name");
2250 CheckContentFiles(E, RuntimeFolder, Path.Combine(RuntimeSubfolder, Name), Path.Combine(AppDataSubFolder, Name),
2251 ContentOptions);
2252 break;
2253
2254 case "Content":
2255 Name = XML.Attribute(E, "fileName");
2256 CopyOptions CopyOptions = XML.Attribute(E, "copy", CopyOptions.IfNewer);
2257
2258 string s = Path.Combine(RuntimeSubfolder, Name);
2259 if (!File.Exists(s))
2260 {
2261 s = Path.Combine(RuntimeFolder, Name);
2262 if (!File.Exists(s))
2263 break;
2264 }
2265
2266 if (!AppDataFolderChecked)
2267 {
2268 AppDataFolderChecked = true;
2269
2270 if (!Directory.Exists(AppDataSubFolder))
2271 Directory.CreateDirectory(AppDataSubFolder);
2272 }
2273
2274 string s2 = Path.Combine(AppDataSubFolder, Name);
2275
2276 if (CopyOptions == CopyOptions.Always || !File.Exists(s2))
2277 {
2278 File.Copy(s, s2, true);
2279 ContentOptions[s2] = CopyOptions;
2280 }
2281 else
2282 {
2283 DateTime TP = File.GetLastWriteTimeUtc(s);
2284 DateTime TP2 = File.GetLastWriteTimeUtc(s2);
2285
2286 if (TP > TP2 &&
2287 (!ContentOptions.TryGetValue(s2, out CopyOptions CopyOptions2) ||
2288 CopyOptions2 != CopyOptions.Always))
2289 {
2290 File.Copy(s, s2, true);
2291 ContentOptions[s2] = CopyOptions;
2292 }
2293 }
2294 break;
2295 }
2296 }
2297 }
2298 }
2299
2300 private static void CheckInstallUtilityFiles(string ManifestFileName)
2301 {
2302 try
2303 {
2304 XmlDocument Doc = XML.LoadFromFile(ManifestFileName, true);
2305
2306 if (!(Doc.DocumentElement is null) &&
2307 Doc.DocumentElement.LocalName == "Module" &&
2308 Doc.DocumentElement.NamespaceURI == "http://waher.se/Schema/ModuleManifest.xsd")
2309 {
2310 string InstallUtilityFolder = Path.Combine(runtimeFolder, "InstallUtility");
2311 bool NoticeLogged = false;
2312
2313 if (!Directory.Exists(InstallUtilityFolder))
2314 Directory.CreateDirectory(InstallUtilityFolder);
2315
2316 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
2317 {
2318 if (N is XmlElement E)
2319 {
2320 switch (E.LocalName)
2321 {
2322 case "Assembly":
2323 string Name = XML.Attribute(E, "fileName");
2324 CopyOptions CopyOptions = XML.Attribute(E, "copy", CopyOptions.IfNewer);
2325
2326 string s = Path.Combine(runtimeFolder, Name);
2327 if (!File.Exists(s))
2328 break;
2329
2330 string s2 = Path.Combine(InstallUtilityFolder, Name);
2331
2332 if (CopyOptions == CopyOptions.IfNewer && File.Exists(s2))
2333 {
2334 DateTime TP = File.GetLastWriteTimeUtc(s);
2335 DateTime TP2 = File.GetLastWriteTimeUtc(s2);
2336
2337 if (TP <= TP2)
2338 break;
2339 }
2340
2341 if (!NoticeLogged)
2342 {
2343 NoticeLogged = true;
2344 Log.Notice("Copying Installation Utility executable files to InstallUtility subfolder.");
2345 }
2346
2347 File.Copy(s, s2, true);
2348 break;
2349 }
2350 }
2351 }
2352 }
2353 }
2354 catch (Exception ex)
2355 {
2356 Log.Exception(ex, ManifestFileName);
2357 }
2358 }
2359
2360 internal static bool ConsoleOutput => consoleOutput;
2361
2362 internal static async Task ConfigureXmpp(XmppConfiguration Configuration)
2363 {
2364 xmppCredentials = Configuration.GetCredentials();
2365 xmppClient = new XmppClient(xmppCredentials, "en", typeof(Gateway).Assembly);
2366 xmppClient.OnValidateSender += XmppClient_OnValidateSender;
2367 Types.SetModuleParameter("XMPP", xmppClient);
2368
2369 if (xmppCredentials.Sniffer)
2370 {
2371 ISniffer Sniffer;
2372
2373 if (consoleOutput)
2374 {
2375 Sniffer = new ConsoleOutSniffer(BinaryPresentationMethod.ByteCount, LineEnding.PadWithSpaces);
2376 xmppClient.Add(Sniffer);
2377 }
2378
2379 Sniffer = new XmlFileSniffer(appDataFolder + "XMPP" + Path.DirectorySeparatorChar +
2380 "XMPP Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml",
2381 appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
2382 7, BinaryPresentationMethod.ByteCount);
2383 xmppClient.Add(Sniffer);
2384 }
2385
2386 if (!string.IsNullOrEmpty(xmppCredentials.Events))
2387 {
2388 string s = xmppCredentials.Events;
2389 int i = s.IndexOf('.');
2390 if (i > 0)
2391 s = s[(i + 1)..];
2392
2393 if (!IsDomain(s, true))
2394 {
2395 Log.Register(new EventFilter("XMPP Event Filter",
2396 new XmppEventSink("XMPP Event Sink", xmppClient, xmppCredentials.Events, false),
2397 EventType.Critical, (Event) => string.IsNullOrEmpty(Event.Facility)));
2398 }
2399 }
2400
2401 if (!string.IsNullOrEmpty(xmppCredentials.ThingRegistry))
2402 {
2403 thingRegistryClient = new ThingRegistryClient(xmppClient, xmppCredentials.ThingRegistry);
2404 thingRegistryClient.Claimed += ThingRegistryClient_Claimed;
2405 thingRegistryClient.Disowned += ThingRegistryClient_Disowned;
2406 thingRegistryClient.Removed += ThingRegistryClient_Removed;
2407 }
2408
2409 if (!string.IsNullOrEmpty(xmppCredentials.Provisioning))
2410 provisioningClient = new ProvisioningClient(xmppClient, xmppCredentials.Provisioning);
2411 else
2412 provisioningClient = null;
2413
2414 scheduler.Add(DateTime.Now.AddMinutes(1), CheckConnection, null);
2415
2416 xmppClient.OnStateChanged += XmppClient_OnStateChanged;
2417
2418 ibbClient = new Networking.XMPP.InBandBytestreams.IbbClient(xmppClient, MaxChunkSize);
2419 Types.SetModuleParameter("IBB", ibbClient);
2420
2421 socksProxy = new Socks5Proxy(xmppClient);
2422 Types.SetModuleParameter("SOCKS5", socksProxy);
2423
2424 sensorClient = new SensorClient(xmppClient);
2425 controlClient = new ControlClient(xmppClient);
2426 concentratorClient = new ConcentratorClient(xmppClient);
2427 synchronizationClient = new SynchronizationClient(xmppClient);
2428 pepClient = new PepClient(xmppClient, XmppConfiguration.Instance.PubSub);
2429
2430 if (!string.IsNullOrEmpty(XmppConfiguration.Instance.LegalIdentities))
2431 {
2432 contractsClient = new ContractsClient(xmppClient, XmppConfiguration.Instance.LegalIdentities);
2433 contractsClient.SetKeySettingsInstance(string.Empty, true);
2434
2435 await contractsClient.LoadKeys(true);
2436 }
2437 else
2438 contractsClient = null;
2439
2440 if (!string.IsNullOrEmpty(XmppConfiguration.Instance.MultiUserChat))
2441 mucClient = new MultiUserChatClient(xmppClient, XmppConfiguration.Instance.MultiUserChat);
2442 else
2443 mucClient = null;
2444
2445 if (!string.IsNullOrEmpty(XmppConfiguration.Instance.SoftwareUpdates))
2446 {
2447 string PackagesFolder = Path.Combine(appDataFolder, "Packages");
2448 if (!Directory.Exists(PackagesFolder))
2449 Directory.CreateDirectory(PackagesFolder);
2450
2451 softwareUpdateClient = new SoftwareUpdateClient(xmppClient, XmppConfiguration.Instance.SoftwareUpdates, PackagesFolder);
2452 }
2453 else
2454 softwareUpdateClient = null;
2455
2456 if (!string.IsNullOrEmpty(XmppConfiguration.Instance.Geo))
2457 geoClient = new GeoClient(xmppClient, XmppConfiguration.Instance.Geo);
2458 else
2459 geoClient = null;
2460
2461 mailClient = new MailClient(xmppClient);
2462 mailClient.MailReceived += MailClient_MailReceived;
2463 }
2464
2465 internal static async Task ConfigureDomain(DomainConfiguration Configuration)
2466 {
2467 int i, c = Configuration.AlternativeDomains?.Length ?? 0;
2468
2469 domain = Configuration.Domain;
2470 alternativeDomains = new CaseInsensitiveString[c];
2471
2472 for (i = 0; i < c; i++)
2473 alternativeDomains[i] = Configuration.AlternativeDomains[i];
2474
2475 if (Configuration.UseDomainName)
2476 {
2477 if (Configuration.DynamicDns)
2478 {
2479 await Configuration.CheckDynamicIp();
2480
2481 if (Configuration.DynDnsInterval > 0)
2482 {
2483 if (checkIp > DateTime.MinValue)
2484 {
2485 scheduler.Remove(checkIp);
2486 checkIp = DateTime.MinValue;
2487 }
2488
2489 checkIp = scheduler.Add(DateTime.Now.AddSeconds(Configuration.DynDnsInterval), CheckIp, Configuration);
2490 }
2491 }
2492
2493 if (Configuration.UseEncryption && Configuration.HasCertificate)
2494 {
2495 await UpdateCertificate(Configuration);
2496
2497 if (checkCertificate > DateTime.MinValue)
2498 {
2499 scheduler.Remove(checkCertificate);
2500 checkCertificate = DateTime.MinValue;
2501 }
2502
2503 checkCertificate = scheduler.Add(DateTime.Now.AddHours(0.5 + NextDouble()), CheckCertificate, Configuration);
2504 }
2505 else
2506 certificate = null;
2507 }
2508 else
2509 certificate = null;
2510
2512 Users.Register(ComputeUserPasswordHash, "DIGEST-SHA3-256", LoginAuditor, domain, true);
2513
2514 await Privileges.LoadAll();
2515 await Roles.LoadAll();
2516 }
2517
2524 public static byte[] ComputeUserPasswordHash(string UserName, string Password)
2525 {
2526 SHA3_256 H = new SHA3_256();
2527 return H.ComputeVariable(System.Text.Encoding.UTF8.GetBytes(UserName + ":" + domain.Value + ":" + Password));
2528 }
2529
2530 internal static async Task<bool> UpdateCertificate(DomainConfiguration Configuration)
2531 {
2532 try
2533 {
2534 if (!(Configuration.PFX is null))
2535 certificate = new X509Certificate2(Configuration.PFX, Configuration.Password);
2536 else
2537 {
2538 RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
2539 RSA.ImportCspBlob(Configuration.PrivateKey);
2540
2541 certificate = new X509Certificate2(Configuration.Certificate)
2542 {
2543 PrivateKey = RSA
2544 };
2545 }
2546
2547 try
2548 {
2549 webServer?.UpdateCertificate(Certificate);
2550
2551 foreach (IEventSink Sink in Log.Sinks)
2552 {
2553 if (Sink is ITlsCertificateEndpoint TlsCertificateEndpoint)
2554 TlsCertificateEndpoint.UpdateCertificate(Certificate);
2555 }
2556
2557 await OnNewCertificate.Raise(typeof(Gateway), new Events.CertificateEventArgs(certificate));
2558 }
2559 catch (Exception ex)
2560 {
2561 Log.Exception(ex);
2562 }
2563
2564 return true;
2565 }
2566 catch (Exception ex)
2567 {
2568 Log.Exception(ex);
2569 }
2570
2571 return false;
2572 }
2573
2574 private static async void CheckCertificate(object P)
2575 {
2576 DomainConfiguration Configuration = (DomainConfiguration)P;
2577 DateTime Now = DateTime.Now;
2578
2579 try
2580 {
2581 if (Now.AddDays(50) >= certificate.NotAfter)
2582 {
2583 Log.Notice("Updating certificate");
2584
2585 if (await Configuration.CreateCertificate())
2586 {
2587 Log.Notice("Certificate created.");
2588 if (!await UpdateCertificate(Configuration))
2589 Log.Error("Unable to update gatetway with new certificate.");
2590 }
2591 else
2592 {
2593 int DaysLeft = (int)Math.Round((certificate.NotAfter - Now.Date).TotalDays);
2594
2595 if (DaysLeft < 2)
2596 Log.Emergency("Unable to generate new certificate.", domain);
2597 else if (DaysLeft < 5)
2598 Log.Alert("Unable to generate new certificate.", domain);
2599 else if (DaysLeft < 10)
2600 Log.Critical("Unable to generate new certificate.", domain);
2601 else if (DaysLeft < 20)
2602 Log.Error("Unable to generate new certificate.", domain);
2603 else
2604 Log.Warning("Unable to generate new certificate.", domain);
2605 }
2606 }
2607 }
2608 catch (Exception ex)
2609 {
2610 int DaysLeft = (int)Math.Round((certificate.NotAfter - Now.Date).TotalDays);
2611
2612 if (DaysLeft < 2)
2613 Log.Emergency(ex, domain);
2614 else if (DaysLeft < 5)
2615 Log.Alert(ex, domain);
2616 else
2617 Log.Exception(ex);
2618 }
2619 finally
2620 {
2621 checkCertificate = scheduler.Add(DateTime.Now.AddDays(0.5 + NextDouble()), CheckCertificate, Configuration);
2622 }
2623 }
2624
2628 public static event EventHandlerAsync<Events.CertificateEventArgs> OnNewCertificate = null;
2629
2630 private static async void CheckIp(object P)
2631 {
2632 DomainConfiguration Configuration = (DomainConfiguration)P;
2633
2634 try
2635 {
2636 await Configuration.CheckDynamicIp();
2637 }
2638 catch (Exception ex)
2639 {
2640 Log.Exception(ex);
2641 }
2642 finally
2643 {
2644 checkIp = scheduler.Add(DateTime.Now.AddSeconds(Configuration.DynDnsInterval), CheckIp, Configuration);
2645 }
2646 }
2647
2648 private static async void DeleteOldDataSourceEvents(object P)
2649 {
2650 try
2651 {
2652 TimeSpan Limit = TimeSpan.FromDays(7);
2653
2654 await MeteringTopology.DeleteOldEvents(Limit);
2655 await GroupSource.DeleteOldEvents(Limit);
2656 await JobSource.DeleteOldEvents(Limit);
2657 await ProcessorSource.DeleteOldEvents(Limit);
2658 await OutputSource.DeleteOldEvents(Limit);
2659 }
2660 catch (Exception ex)
2661 {
2662 Log.Exception(ex);
2663 }
2664 finally
2665 {
2666 ScheduleEvent(DeleteOldDataSourceEvents, DateTime.Today.AddDays(1).AddHours(4), null);
2667 }
2668 }
2669
2674
2678 public static event EventHandlerAsync<GetDataSourcesEventArgs> GetDataSources = null;
2679
2683 public static CommunicationLayer FirstChanceExceptions => firstChanceExceptions;
2684
2688 private static void Initialize()
2689 {
2690 string Folder = Assembly.GetExecutingAssembly().Location;
2691 if (string.IsNullOrEmpty(Folder))
2692 Folder = AppDomain.CurrentDomain.BaseDirectory;
2693
2694 runtimeFolder = Path.GetDirectoryName(Folder);
2695
2696 Directory.SetCurrentDirectory(runtimeFolder);
2697
2698 TypesLoader.Initialize(runtimeFolder, (FileName) =>
2699 {
2700 FileName = Path.GetFileName(FileName).ToLower();
2701 if (FileName.StartsWith("api-ms-win-") ||
2702 FileName.StartsWith("system.") ||
2703 FileName.StartsWith("microsoft.") ||
2704 FileName.StartsWith("windows.") ||
2705 FileName.StartsWith("waher.client.") ||
2706 FileName.StartsWith("waher.utility.") ||
2707 FileName.StartsWith("mscordaccore_x86_x86_") ||
2708 FileName.StartsWith("sos_x86_x86_"))
2709 {
2710 return false;
2711 }
2712
2713 switch (FileName)
2714 {
2715 case "clrcompression.dll":
2716 case "clretwrc.dll":
2717 case "clrgc.dll":
2718 case "clrjit.dll":
2719 case "coreclr.dll":
2720 case "dbgshim.dll":
2721 case "hostpolicy.dll":
2722 case "hostfxr.dll":
2723 case "libegl.dll":
2724 case "libglesv2.dll":
2725 case "libskiasharp.dll":
2726 case "libzstd.dll":
2727 case "mongocrypt.dll":
2728 case "mscordaccore.dll":
2729 case "mscordbi.dll":
2730 case "mscorlib.dll":
2731 case "mscorrc.debug.dll":
2732 case "mscorrc.dll":
2733 case "msquic.dll":
2734 case "netstandard.dll":
2735 case "snappy32.dll":
2736 case "snappy64.dll":
2737 case "snappier.dll":
2738 case "sni.dll":
2739 case "sos.dll":
2740 case "sos.netcore.dll":
2741 case "ucrtbase.dll":
2742 case "windowsbase.dll":
2743 case "waher.persistence.fileslw.dll":
2744 case "waher.persistence.serialization.dll":
2745 case "zstdsharp.dll":
2746 return false;
2747 }
2748
2749 return true;
2750 });
2751 }
2752
2753 private static bool CopyFile(string From, string To, CopyOptions CopyOptions)
2754 {
2755 if (From == To)
2756 return false;
2757
2758 if (!File.Exists(From))
2759 return false;
2760
2761 if (CopyOptions != CopyOptions.Always && File.Exists(To))
2762 {
2763 if (CopyOptions == CopyOptions.IfNotExists)
2764 return false;
2765 else if (CopyOptions == CopyOptions.IfNewer)
2766 {
2767 DateTime ToTP = File.GetLastWriteTimeUtc(To);
2768 DateTime FromTP = File.GetLastWriteTimeUtc(From);
2769
2770 if (ToTP >= FromTP)
2771 return false;
2772 }
2773 }
2774
2775 File.Copy(From, To, true);
2776
2777 return true;
2778 }
2779
2780 private static void CopyFolder(string From, string To, string Mask, CopyOptions CopyOptions)
2781 {
2782 if (Directory.Exists(From))
2783 {
2784 if (!Directory.Exists(To))
2785 Directory.CreateDirectory(To);
2786
2787 string[] Files = Directory.GetFiles(From, Mask, SearchOption.TopDirectoryOnly);
2788
2789 foreach (string File in Files)
2790 {
2791 string FileName = Path.GetFileName(File);
2792 CopyFile(File, Path.Combine(To, FileName), CopyOptions);
2793 }
2794 }
2795 }
2796
2797 private static void CopyFolders(string From, string To, CopyOptions CopyOptions)
2798 {
2799 if (Directory.Exists(From))
2800 {
2801 CopyFolder(From, To, "*.*", CopyOptions);
2802
2803 string[] Folders = Directory.GetDirectories(From, "*.*", SearchOption.TopDirectoryOnly);
2804
2805 foreach (string Folder in Folders)
2806 {
2807 string FolderName = Path.GetFileName(Folder);
2808 CopyFolders(Folder, Path.Combine(To, FolderName), CopyOptions);
2809 }
2810 }
2811 }
2812
2816 public static async Task Stop()
2817 {
2818 if (stopped)
2819 {
2820 Log.Notice("Request to stop Gateway, but Gateway already stopped.");
2821 return;
2822 }
2823
2824 Log.Informational("Server shutting down.");
2825
2826 bool StopInternalProvider = !(internalProvider is null) && Database.Provider != internalProvider;
2827
2828 stopped = true;
2829 try
2830 {
2831 SafeDispose(scheduler);
2832 scheduler = null;
2833
2834 try
2835 {
2836 await PlantUml.Terminate();
2837 }
2838 catch (Exception ex)
2839 {
2840 Log.Exception(ex);
2841 }
2842
2843 try
2844 {
2845 await GraphViz.Terminate();
2846 }
2847 catch (Exception ex)
2848 {
2849 Log.Exception(ex);
2850 }
2851
2852 try
2853 {
2854 await Script.Threading.Functions.Background.TerminateTasks(10000);
2855 }
2856 catch (Exception ex)
2857 {
2858 Log.Exception(ex);
2859 }
2860
2861 try
2862 {
2863 await Types.StopAllModules();
2864 }
2865 catch (Exception ex)
2866 {
2867 Log.Exception(ex);
2868 }
2869
2870 Database.CollectionRepaired -= Database_CollectionRepaired;
2871
2872 if (StopInternalProvider)
2873 {
2874 try
2875 {
2876 await internalProvider.Stop();
2877 }
2878 catch (Exception ex)
2879 {
2880 Log.Exception(ex);
2881 }
2882 }
2883
2884 if (!(startingServer is null))
2885 {
2886 await startingServer.ReleaseMutex();
2887 SafeDispose(startingServer);
2888 startingServer = null;
2889 }
2890
2891 if (!(gatewayRunning is null))
2892 {
2893 await gatewayRunning.ReleaseMutex();
2894 SafeDispose(gatewayRunning);
2895 gatewayRunning = null;
2896 }
2897
2898 if (!(configurations is null))
2899 {
2900 foreach (SystemConfiguration Configuration in configurations)
2901 {
2902 try
2903 {
2904 if (Configuration is IDisposableAsync DAsync)
2905 await SafeDispose(DAsync);
2906 else if (Configuration is IDisposable D)
2907 SafeDispose(D);
2908 }
2909 catch (Exception ex)
2910 {
2911 Log.Exception(ex);
2912 }
2913 }
2914
2915 configurations = null;
2916 }
2917
2918 SafeDispose(ibbClient);
2919 ibbClient = null;
2920
2921 SafeDispose(httpxProxy);
2922 httpxProxy = null;
2923
2924 SafeDispose(httpxServer);
2925 httpxServer = null;
2926
2927 SafeDispose(provisioningClient);
2928 provisioningClient = null;
2929
2930 SafeDispose(thingRegistryClient);
2931 thingRegistryClient = null;
2932
2933 SafeDispose(concentratorServer);
2934 concentratorServer = null;
2935
2936 SafeDispose(avatarClient);
2937 avatarClient = null;
2938
2939 SafeDispose(sensorClient);
2940 sensorClient = null;
2941
2942 SafeDispose(controlClient);
2943 controlClient = null;
2944
2945 SafeDispose(concentratorClient);
2946 concentratorClient = null;
2947
2948 SafeDispose(synchronizationClient);
2949 synchronizationClient = null;
2950
2951 SafeDispose(pepClient);
2952 pepClient = null;
2953
2954 SafeDispose(mucClient);
2955 mucClient = null;
2956
2957 SafeDispose(mailClient);
2958 mailClient = null;
2959
2960 SafeDispose(contractsClient);
2961 contractsClient = null;
2962
2963 SafeDispose(softwareUpdateClient);
2964 softwareUpdateClient = null;
2965
2966 SafeDispose(geoClient);
2967 geoClient = null;
2968
2969 if (!(xmppClient is null))
2970 {
2971 try
2972 {
2973 await xmppClient.OfflineAndDisposeAsync();
2974 }
2975 catch (Exception ex)
2976 {
2977 Log.Exception(ex);
2978 }
2979
2980 xmppClient = null;
2981 }
2982
2983 await SafeDispose(coapEndpoint);
2984 coapEndpoint = null;
2985
2986 InternetContent.LocalDomainCheck -= InternetContent_LocalDomainCheck;
2987
2988 if (!(webServer is null))
2989 {
2990 try
2991 {
2992 await webServer.RemoveRange(webServer.Sniffers, true);
2993 }
2994 catch (Exception ex)
2995 {
2996 Log.Exception(ex);
2997 }
2998
2999 await SafeDispose(webServer);
3000 webServer = null;
3001 }
3002
3003 SafeDispose(xmlFileSnifferCache);
3004 xmlFileSnifferCache = null;
3005
3006 SafeDispose(jwtFactory);
3007 jwtFactory = null;
3008
3009 SafeDispose(mcpSniffers);
3010 mcpSniffers = null;
3011
3012 root = null;
3013
3014 if (exportExceptions)
3015 {
3016 lock (exceptionFile)
3017 {
3018 exportExceptions = false;
3019 firstChanceExceptions = null;
3020
3021 exceptionFile.WriteLine(new string('-', 80));
3022 exceptionFile.Write("End of export: ");
3023 exceptionFile.WriteLine(DateTime.Now.ToString());
3024
3025 exceptionFile.Flush();
3026 exceptionFile.Close();
3027 }
3028
3029 exceptionFile = null;
3030 }
3031 }
3032 finally
3033 {
3034 Persistence.LifeCycle.DatabaseModule.Flush().Wait(60000);
3035
3036 if (StopInternalProvider)
3037 internalProvider.Flush().Wait(60000);
3038 }
3039 }
3040
3045 public static void SafeDispose(IDisposable Object)
3046 {
3047 if (!(Object is null))
3048 {
3049 try
3050 {
3051 Object.Dispose();
3052 }
3053 catch (Exception ex)
3054 {
3055 Log.Exception(ex);
3056 }
3057 }
3058 }
3059
3064 public static async Task SafeDispose(IDisposableAsync Object)
3065 {
3066 if (!(Object is null))
3067 {
3068 try
3069 {
3070 await Object.DisposeAsync();
3071 }
3072 catch (Exception ex)
3073 {
3074 Log.Exception(ex);
3075 }
3076 }
3077 }
3078
3082 public static X509Certificate2 Certificate => certificate ?? Types.TryGetModuleParameter<X509Certificate2>("X509");
3083
3087 public static CaseInsensitiveString Domain => domain;
3088
3092 public static bool HasDomain
3093 {
3094 get
3095 {
3097 return false;
3098
3100 return false;
3101
3102 switch (domain.LowerCase)
3103 {
3104 case "localhost":
3105 case "example.com":
3106 case "example2.com":
3107 case "example3.com":
3108 case "example.org":
3109 case "example2.org":
3110 case "example3.org":
3111 return false;
3112
3113 default:
3114 return true;
3115 }
3116 }
3117 }
3118
3122 public static CaseInsensitiveString[] AlternativeDomains => alternativeDomains;
3123
3127 public static string InstanceName => instance;
3128
3132 public static string AppDataFolder => appDataFolder ?? Types.TryGetModuleParameter<string>("AppData");
3133
3137 public static string RuntimeFolder => runtimeFolder ?? Types.TryGetModuleParameter<string>("Runtime");
3138
3142 public static string RootFolder => rootFolder ?? Types.TryGetModuleParameter<string>("Root");
3143
3147 public static string ReportsFolder => reportsFolder ?? Types.TryGetModuleParameter<string>("Reports");
3148
3152 public static HttpFolderResource Root => root;
3153
3157 public static string ApplicationName
3158 {
3159 get => applicationName;
3160 internal set => applicationName = value;
3161 }
3162
3166 public static Emoji1LocalFiles Emoji1_24x24 => emoji1_24x24;
3167
3171 public static bool Configuring => configuring;
3172
3176 public static IDatabaseProvider InternalDatabase => internalProvider;
3177
3181 public static string ConfigFilePath => Path.Combine(appDataFolder, GatewayConfigLocalFileName);
3182
3186 public static JwtFactory JwtFactory => jwtFactory;
3187
3191 public static ISnifferSet McpSniffers => mcpSniffers;
3192
3198 public static int[] GetConfigPorts(string Protocol)
3199 {
3200 List<int> Result = new List<int>();
3201
3202 foreach (KeyValuePair<string, int> P in ports)
3203 {
3204 if (P.Key == Protocol)
3205 Result.Add(P.Value);
3206 }
3207
3208 return Result.ToArray();
3209 }
3210
3215 public static string[] GetProtocols()
3216 {
3217 SortedDictionary<string, bool> Protocols = new SortedDictionary<string, bool>();
3218
3219 foreach (KeyValuePair<string, int> P in ports)
3220 Protocols[P.Key] = true;
3221
3222 string[] Result = new string[Protocols.Count];
3223 Protocols.Keys.CopyTo(Result, 0);
3224
3225 return Result;
3226 }
3227
3233 public static Task Terminate()
3234 {
3235 EventHandlerAsync h = OnTerminate ?? throw new InvalidOperationException("No OnTerminate event handler set.");
3236 return h.Raise(instance, EventArgs.Empty, false);
3237 }
3238
3243 public static event EventHandlerAsync OnTerminate = null;
3244
3251 public static bool TryGetDefaultPage(HttpRequest Request, out string DefaultPage)
3252 {
3253 return TryGetDefaultPage(Request.Header.Host?.Value ?? string.Empty, out DefaultPage);
3254 }
3255
3262 public static bool TryGetDefaultPage(string Host, out string DefaultPage)
3263 {
3264 if (defaultPageByHostName.TryGetValue(Host, out DefaultPage))
3265 return true;
3266
3267 if (Host.StartsWith("www.", StringComparison.CurrentCultureIgnoreCase) && defaultPageByHostName.TryGetValue(Host[4..], out DefaultPage))
3268 return true;
3269
3270 if (defaultPageByHostName.TryGetValue(string.Empty, out DefaultPage))
3271 return true;
3272
3273 DefaultPage = string.Empty;
3274 return false;
3275 }
3276
3277 internal static void SetDefaultPages(params KeyValuePair<string, string>[] DefaultPages)
3278 {
3279 Dictionary<string, string> List = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
3280
3281 foreach (KeyValuePair<string, string> P in DefaultPages)
3282 List[P.Key] = P.Value;
3283
3284 defaultPageByHostName = List;
3285 }
3286
3287 #endregion
3288
3289 #region XMPP
3290
3291 private static Task XmppClient_OnValidateSender(object Sender, ValidateSenderEventArgs e)
3292 {
3293 RosterItem Item;
3294 string BareJid = e.FromBareJID.ToLower();
3295
3296 if (string.IsNullOrEmpty(BareJid) ||
3297 (!(xmppClient is null) &&
3298 (BareJid == xmppClient.Domain.ToLower() ||
3299 BareJid == xmppClient.BareJID.ToLower())))
3300 {
3301 e.Accept();
3302 }
3303 else if (BareJid.IndexOf('@') > 0 &&
3304 (xmppClient is null ||
3305 (Item = xmppClient.GetRosterItem(BareJid)) is null ||
3306 Item.State == SubscriptionState.None ||
3307 Item.State == SubscriptionState.Remove ||
3308 Item.State == SubscriptionState.Unknown))
3309 {
3310 foreach (XmlNode N in e.Stanza.ChildNodes)
3311 {
3312 if (N.LocalName == "query" && N.NamespaceURI == XmppClient.NamespaceServiceDiscoveryInfo)
3313 return Task.CompletedTask;
3314 }
3315
3316 e.Reject();
3317 }
3318
3319 return Task.CompletedTask;
3320 }
3321
3322 private static async void CheckConnection(object State)
3323 {
3324 try
3325 {
3326 if (!stopped && !NetworkingModule.Stopping)
3327 {
3328 scheduler.Add(DateTime.Now.AddMinutes(1), CheckConnection, null);
3329
3330 XmppState? State2 = xmppClient?.State;
3331 if (State2.HasValue &&
3332 (State2 == XmppState.Offline || State2 == XmppState.Error || State2 == XmppState.Authenticating) &&
3333 !(xmppClient is null))
3334 {
3335 try
3336 {
3337 await xmppClient.Reconnect();
3338 }
3339 catch (Exception ex)
3340 {
3341 Log.Exception(ex);
3342 }
3343 }
3344
3345 await CheckBackup();
3346
3347 await MinuteTick.Raise(null, EventArgs.Empty);
3348 }
3349 }
3350 catch (Exception ex)
3351 {
3352 Log.Exception(ex);
3353 }
3354 }
3355
3359 public static event EventHandlerAsync MinuteTick = null;
3360
3361 private static async Task XmppClient_OnStateChanged(object _, XmppState NewState)
3362 {
3363 switch (NewState)
3364 {
3365 case XmppState.Connected:
3366 connected = true;
3367
3368 MarkdownToHtmlConverter.BareJID = xmppClient.BareJID;
3369
3370 if (!registered && !(thingRegistryClient is null))
3371 {
3372 _ = Task.Run(async () =>
3373 {
3374 try
3375 {
3376 await Register();
3377 }
3378 catch (Exception ex)
3379 {
3380 Log.Exception(ex);
3381 }
3382 });
3383 }
3384
3385 if (!socksProxy.HasProxy)
3386 await socksProxy.StartSearch(null);
3387 break;
3388
3389 case XmppState.Offline:
3390 immediateReconnect = connected;
3391 connected = false;
3392
3393 if (immediateReconnect &&
3394 !(xmppClient is null) &&
3396 {
3397 await xmppClient.Reconnect();
3398 }
3399 break;
3400 }
3401 }
3402
3407 public static async Task CheckLocalLogin(HttpRequest Request)
3408 {
3409 Profiler Profiler = new Profiler();
3410 Profiler.Start();
3411
3412 ProfilerThread Thread = Profiler.CreateThread("Check Local Login", ProfilerThreadType.Sequential);
3413
3414 Thread.Start();
3415 try
3416 {
3417 Thread.NewState("Checks");
3418
3419 string RemoteEndpoint = Request.RemoteEndPoint;
3420 string From;
3421 int i;
3422 bool DoLog = false;
3423
3424 if (Request.Header.TryGetQueryParameter("debug", out string s) &&
3425 CommonTypes.TryParse(s, out bool b))
3426 {
3427 DoLog = b;
3428 }
3429
3430 if (Request.Session is null)
3431 {
3432 if (DoLog)
3433 Log.Debug("No local login: No session.");
3434
3435 return;
3436 }
3437
3438 if (Request.Session.TryGetVariable("from", out Variable v))
3439 {
3440 if (string.IsNullOrEmpty(From = v.ValueObject as string))
3441 From = "/";
3442 }
3443 else
3444 From = Request.Header.ResourcePart;
3445
3446 if (Request.Session.TryGetVariable("User", out v) &&
3447 v.ValueObject is IUser &&
3448 !string.IsNullOrEmpty(From) &&
3449 !From.Contains("Login"))
3450 {
3451 if (DoLog)
3452 Log.Debug("Already logged in.");
3453
3454 await Request.Response.SendResponse(new SeeOtherException(From));
3455 return;
3456 }
3457
3458 if (!loopbackIntefaceAvailable && (XmppConfiguration.Instance is null || !XmppConfiguration.Instance.Complete || configuring))
3459 {
3460 LoginAuditor.Success("User logged in by default, since XMPP not configued and loopback interface not available.",
3461 string.Empty, Request.RemoteEndPoint, "Web");
3462
3463 await Login.DoLogin(Request, From);
3464 return;
3465 }
3466
3467 if (DoLog)
3468 Log.Debug("Checking for local login from: " + RemoteEndpoint);
3469
3470 i = RemoteEndpoint.LastIndexOf(':');
3471 if (i < 0 || !int.TryParse(RemoteEndpoint[(i + 1)..], out int Port))
3472 {
3473 if (DoLog)
3474 Log.Debug("Invalid port number: " + RemoteEndpoint);
3475
3476 return;
3477 }
3478
3479 if (!IPAddress.TryParse(RemoteEndpoint[..i], out IPAddress Address))
3480 {
3481 if (DoLog)
3482 Log.Debug("Invalid IP Address: " + RemoteEndpoint);
3483
3484 return;
3485 }
3486
3487 if (!IsLocalCall(Address, Request, DoLog))
3488 {
3489 if (DoLog)
3490 Log.Debug("Request not local: " + RemoteEndpoint);
3491
3492 return;
3493 }
3494
3495#if !MONO
3496 if (XmppConfiguration.Instance is null || !XmppConfiguration.Instance.Complete || configuring)
3497#endif
3498 {
3499 LoginAuditor.Success("Local user logged in.", string.Empty, Request.RemoteEndPoint, "Web");
3500 await Login.DoLogin(Request, From);
3501 return;
3502 }
3503
3504#if !MONO
3505 try
3506 {
3507 string FileName;
3508 string Arguments;
3509 bool WaitForExit;
3510
3511 switch (Environment.OSVersion.Platform)
3512 {
3513 case PlatformID.Win32S:
3514 case PlatformID.Win32Windows:
3515 case PlatformID.Win32NT:
3516 case PlatformID.WinCE:
3517 FileName = "netstat.exe";
3518 Arguments = "-a -n -o";
3519 WaitForExit = false;
3520
3521 Thread.NewState("netstat.exe");
3522 break;
3523
3524 case PlatformID.Unix:
3525 case PlatformID.MacOSX:
3526 FileName = "netstat";
3527 Arguments = "-anv -p tcp";
3528 WaitForExit = true;
3529
3530 Thread.NewState("netstat");
3531 break;
3532
3533 default:
3534 if (DoLog)
3535 Log.Debug("No local login: Unsupported operating system: " + Environment.OSVersion.Platform.ToString());
3536
3537 return;
3538 }
3539
3540 using Process Proc = new Process();
3541 ProcessStartInfo StartInfo = new ProcessStartInfo()
3542 {
3543 FileName = FileName,
3544 Arguments = Arguments,
3545 WindowStyle = ProcessWindowStyle.Hidden,
3546 UseShellExecute = false,
3547 RedirectStandardInput = true,
3548 RedirectStandardOutput = true,
3549 RedirectStandardError = true
3550 };
3551
3552 DateTime Start = DateTime.Now;
3553
3554 Proc.StartInfo = StartInfo;
3555 Proc.Start();
3556
3557 if (WaitForExit)
3558 {
3559 Proc.WaitForExit(5000);
3560 if (!Proc.HasExited)
3561 return;
3562 }
3563
3564 string Output = Proc.StandardOutput.ReadToEnd();
3565 DateTime Return = DateTime.Now;
3566
3567 Thread.Interval(Start, Return, "Shell");
3568
3569 if (DoLog)
3570 Log.Debug("Netstat output:\r\n\r\n" + Output);
3571
3572 if (Proc.ExitCode != 0)
3573 {
3574 Thread.Exception(new Exception("Exit code: " + Proc.ExitCode.ToString()));
3575
3576 if (DoLog)
3577 Log.Debug("Netstat exit code: " + Proc.ExitCode.ToString());
3578
3579 return;
3580 }
3581
3582 string[] Rows = Output.Split(CommonTypes.CRLF, StringSplitOptions.RemoveEmptyEntries);
3583
3584 foreach (string Row in Rows)
3585 {
3586 string[] Tokens = Regex.Split(Row, @"\s+");
3587
3588 switch (Environment.OSVersion.Platform)
3589 {
3590 case PlatformID.Win32S:
3591 case PlatformID.Win32Windows:
3592 case PlatformID.Win32NT:
3593 case PlatformID.WinCE:
3594 if (Tokens.Length < 6)
3595 break;
3596
3597 if (Tokens[1] != "TCP")
3598 break;
3599
3600 if (!SameEndpoint(Tokens[2], RemoteEndpoint))
3601 break;
3602
3603 if (Tokens[4] != "ESTABLISHED")
3604 break;
3605
3606 if (!int.TryParse(Tokens[5], out int PID))
3607 break;
3608
3609 Process P = Process.GetProcessById(PID);
3610 int CurrentSession = WTSGetActiveConsoleSessionId();
3611
3612 if (P.SessionId == CurrentSession)
3613 {
3614 LoginAuditor.Success("Local user logged in.", string.Empty, Request.RemoteEndPoint, "Web");
3615 await Login.DoLogin(Request, From);
3616 return;
3617 }
3618 break;
3619
3620 case PlatformID.Unix:
3621 case PlatformID.MacOSX:
3622 if (Tokens.Length < 9)
3623 break;
3624
3625 if (Tokens[0] != "tcp4" && Tokens[0] != "tcp6")
3626 break;
3627
3628 if (!SameEndpoint(Tokens[4], RemoteEndpoint))
3629 break;
3630
3631 if (Tokens[5] != "ESTABLISHED")
3632 break;
3633
3634 if (!int.TryParse(Tokens[8], out PID))
3635 break;
3636
3637 P = Process.GetProcessById(PID);
3638 CurrentSession = Process.GetCurrentProcess().SessionId;
3639
3640 if (P.SessionId == CurrentSession)
3641 {
3642 LoginAuditor.Success("Local user logged in.", string.Empty, Request.RemoteEndPoint, "Web");
3643 await Login.DoLogin(Request, From);
3644 return;
3645 }
3646 break;
3647
3648 default:
3649 if (DoLog)
3650 Log.Debug("No local login: Unsupported operating system: " + Environment.OSVersion.Platform.ToString());
3651
3652 return;
3653 }
3654 }
3655 }
3656 catch (HttpException ex)
3657 {
3658 Thread.Exception(ex);
3659
3660 if (DoLog)
3661 Log.Exception(ex);
3662
3663 ExceptionDispatchInfo.Capture(ex).Throw();
3664 }
3665 catch (Exception ex)
3666 {
3667 Thread.Exception(ex);
3668
3669 if (DoLog)
3670 Log.Exception(ex);
3671
3672 return;
3673 }
3674#endif
3675 }
3676 finally
3677 {
3678 Thread.Stop();
3679 Profiler.Stop();
3680
3681 double TotalSeconds = Profiler.ElapsedSeconds;
3682
3683 if (TotalSeconds >= 1.0)
3684 {
3685 string Uml = Profiler.ExportPlantUml(TimeUnit.MilliSeconds);
3686
3687 Log.Debug("Long local login check.\r\n\r\n```uml\r\n" + Uml + "\r\n```");
3688 }
3689 }
3690 }
3691
3692 private static bool SameEndpoint(string EP1, string EP2)
3693 {
3694 if (string.Compare(EP1, EP2, true) == 0)
3695 return true;
3696
3697 switch (Environment.OSVersion.Platform)
3698 {
3699 case PlatformID.Unix:
3700 case PlatformID.MacOSX:
3701 int i = EP1.LastIndexOf('.');
3702 if (i < 0)
3703 break;
3704
3705 if (!int.TryParse(EP1[(i + 1)..], out int Port1))
3706 break;
3707
3708 if (!IPAddress.TryParse(EP1[..i], out IPAddress Addr1))
3709 break;
3710
3711 i = EP2.LastIndexOf(':');
3712 if (i < 0)
3713 break;
3714
3715 if (!int.TryParse(EP2[(i + 1)..], out int Port2) || Port1 != Port2)
3716 break;
3717
3718 if (!IPAddress.TryParse(EP2[..i], out IPAddress Addr2))
3719 break;
3720
3721 string s1 = Addr1.ToString();
3722 string s2 = Addr2.ToString();
3723
3724 if (string.Compare(s1, s2, true) == 0)
3725 return true;
3726
3727 break;
3728 }
3729
3730 return false;
3731 }
3732
3733 private static readonly IPAddress ipv6Local = IPAddress.Parse("[::1]");
3734 private static readonly IPAddress ipv4Local = IPAddress.Parse("127.0.0.1");
3735
3736 private static bool IsLocalCall(IPAddress Address, HttpRequest Request, bool DoLog)
3737 {
3738 if (Address.Equals(ipv4Local) || Address.Equals(ipv6Local))
3739 return true;
3740
3741 string s = Request.Header.Host?.Value.RemovePortNumber() ?? string.Empty;
3742
3743 if (string.Compare(s, "localhost", true) != 0)
3744 {
3745 if (!IPAddress.TryParse(s, out IPAddress IP) || !Address.Equals(IP))
3746 {
3747 if (DoLog)
3748 Log.Debug("Host is not localhost or an IP Address: " + (Request.Header.Host?.Value ?? string.Empty));
3749
3750 return false;
3751 }
3752 }
3753
3754 try
3755 {
3756 foreach (NetworkInterface Interface in NetworkInterface.GetAllNetworkInterfaces())
3757 {
3758 if (Interface.OperationalStatus != OperationalStatus.Up)
3759 continue;
3760
3761 IPInterfaceProperties Properties = Interface.GetIPProperties();
3762
3763 foreach (UnicastIPAddressInformation UnicastAddress in Properties.UnicastAddresses)
3764 {
3765 if (Address.Equals(UnicastAddress.Address))
3766 return true;
3767 }
3768 }
3769
3770 if (DoLog)
3771 Log.Debug("IP Address not found among network adapters: " + Address.ToString());
3772
3774 {
3775 if (DoLog)
3776 Log.Debug("IP Address public: " + Address.ToString());
3777
3778 return false;
3779 }
3780
3781 if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) &&
3782 string.IsNullOrEmpty(XmppConfiguration.Instance?.Host) &&
3783 Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true")
3784 {
3785 return true;
3786 }
3787
3788 return false;
3789 }
3790 catch (Exception ex)
3791 {
3792 if (DoLog)
3793 Log.Debug(ex.Message);
3794
3795 return false;
3796 }
3797 }
3798
3799#if !MONO
3800 [DllImport("kernel32.dll")]
3801 static extern int WTSGetActiveConsoleSessionId();
3802#endif
3803
3810 {
3812 }
3813
3820 public static RequiredPrivileges LoggedIn(string UserVariable, string[] Privileges)
3821 {
3822 return new RequiredPrivileges(
3824 {
3825 new SessionAuthentication(UserVariable, webServer)
3826 },
3827 Privileges);
3828 }
3829
3836 {
3837 return LoggedIn(SessionAuthentication.DefaultUserVariable, Authorization);
3838 }
3839
3846 public static RequiredPrivileges LoggedIn(string UserVariable, IAuthorization<HttpRequest> Authorization)
3847 {
3848 return new RequiredPrivileges(
3850 {
3851 new SessionAuthentication(UserVariable, webServer)
3852 },
3853 Authorization);
3854 }
3855
3860 public static LoginAuditor LoginAuditor => loginAuditor;
3861
3869 {
3870 return AssertUserAuthenticated(Request.Session, Privilege);
3871 }
3872
3879 public static IUser AssertUserAuthenticated(Variables Session, string Privilege)
3880 {
3881 IUser User = null;
3882
3883 if (Session is null ||
3884 !Session.TryGetVariable("User", out Variable v) ||
3885 ((User = v.ValueObject as IUser) is null) ||
3887 {
3888 throw ForbiddenException.AccessDenied(string.Empty, User?.UserName, Privilege);
3889 }
3890
3891 return User;
3892 }
3893
3900 public static IUser AssertUserAuthenticated(HttpRequest Request, string[] Privileges)
3901 {
3902 return AssertUserAuthenticated(Request.Session, Privileges);
3903 }
3904
3911 public static IUser AssertUserAuthenticated(Variables Session, string[] Privileges)
3912 {
3913 if (Session is null ||
3914 !Session.TryGetVariable("User", out Variable v) ||
3915 (!(v.ValueObject is IUser User)))
3916 {
3917 throw ForbiddenException.AccessDenied(string.Empty, string.Empty, string.Empty);
3918 }
3919
3920 foreach (string Privilege in Privileges)
3921 {
3924 }
3925
3926 return User;
3927 }
3928
3929 private static Task SensorServer_AssignAuthority(object Sender, AuthorityEventArgs e)
3930 {
3931 if (e.Authority is null)
3932 {
3933 if (!string.IsNullOrEmpty(provisioningClient?.OwnerJid) &&
3934 provisioningClient.OwnerJid == e.BareJid)
3935 {
3936 e.Authority = new MaximumAuthority(e.BareJid);
3937 }
3938 else
3939 {
3940 RosterItem Item = xmppClient?.GetRosterItem(e.BareJid);
3941
3942 if (!(Item is null) &&
3943 (Item.State == SubscriptionState.Both ||
3944 Item.State == SubscriptionState.From))
3945 {
3946 e.Authority = new ViewOnlyAuthority(e.BareJid, MeteringTopology.SourceID);
3947 }
3948 }
3949 }
3950
3951 return Task.CompletedTask;
3952 }
3953
3954 #endregion
3955
3956 #region Thing Registry
3957
3958 private static Task ThingRegistryClient_Claimed(object Sender, ClaimedEventArgs e)
3959 {
3960 if (e.Node.IsEmpty)
3961 {
3962 ownerJid = e.JID;
3963 Log.Informational("Gateway has been claimed.", ownerJid, new KeyValuePair<string, object>("Public", e.IsPublic));
3964 }
3965
3966 return Task.CompletedTask;
3967 }
3968
3969 private static Task ThingRegistryClient_Disowned(object Sender, Networking.XMPP.Provisioning.Events.NodeEventArgs e)
3970 {
3971 if (e.Node.IsEmpty)
3972 {
3973 Log.Informational("Gateway has been disowned.", ownerJid);
3974 ownerJid = string.Empty;
3975 Task.Run(Register);
3976 }
3977
3978 return Task.CompletedTask;
3979 }
3980
3981 private static Task ThingRegistryClient_Removed(object Sender, Networking.XMPP.Provisioning.Events.NodeEventArgs e)
3982 {
3983 if (e.Node.IsEmpty)
3984 Log.Informational("Gateway has been removed from the public registry.", ownerJid);
3985
3986 return Task.CompletedTask;
3987 }
3988
3989 private static async Task Register()
3990 {
3991 string Key = Guid.NewGuid().ToString().Replace("-", string.Empty);
3992
3993 // For info on tag names, see: http://xmpp.org/extensions/xep-0347.html#tags
3994 MetaDataTag[] MetaData = new MetaDataTag[]
3995 {
3996 new MetaDataStringTag("KEY", Key),
3997 new MetaDataStringTag("CLASS", "Gateway"),
3998 new MetaDataStringTag("MAN", "waher.se"),
3999 new MetaDataStringTag("MODEL", "Waher.IoTGateway"),
4000 new MetaDataStringTag("PURL", "https://github.com/PeterWaher/IoTGateway#iotgateway"),
4001 new MetaDataNumericTag("V", 1.0)
4002 };
4003
4004 if (!(GetMetaData is null))
4005 MetaData = await GetMetaData(MetaData);
4006
4007 await thingRegistryClient.RegisterThing(MetaData, async (sender2, e2) =>
4008 {
4009 if (e2.Ok)
4010 {
4011 registered = true;
4012
4013 if (e2.IsClaimed)
4014 ownerJid = e2.OwnerJid;
4015 else
4016 ownerJid = string.Empty;
4017
4018 RegistrationEventHandler h = RegistrationSuccessful;
4019 if (!(h is null))
4020 await h(MetaData, e2);
4021 }
4022 }, null);
4023 }
4024
4028 public static event GetRegistryMetaDataEventHandler GetMetaData = null;
4029
4033 public static event RegistrationEventHandler RegistrationSuccessful = null;
4034
4038 public static XmppClient XmppClient => xmppClient ?? Types.TryGetModuleParameter<XmppClient>("XMPP");
4039
4043 public static ThingRegistryClient ThingRegistryClient => thingRegistryClient ?? Types.TryGetModuleParameter<ThingRegistryClient>("Registry");
4044
4048 public static ProvisioningClient ProvisioningClient => provisioningClient ?? Types.TryGetModuleParameter<ProvisioningClient>("Provisioning");
4049
4053 public static ConcentratorServer ConcentratorServer => concentratorServer ?? Types.TryGetModuleParameter<ConcentratorServer>("Concentrator");
4054
4058 public static AvatarClient AvatarClient => avatarClient ?? Types.TryGetModuleParameter<AvatarClient>("Avatar");
4059
4063 public static SensorClient SensorClient => sensorClient;
4064
4068 public static ControlClient ControlClient => controlClient;
4069
4073 public static ConcentratorClient ConcentratorClient => concentratorClient;
4074
4078 public static SynchronizationClient SynchronizationClient => synchronizationClient;
4079
4083 public static PepClient PepClient => pepClient;
4084
4088 public static MultiUserChatClient MucClient => mucClient;
4089
4093 public static PubSubClient PubSubClient => pepClient.PubSubClient;
4094
4098 public static SoftwareUpdateClient SoftwareUpdateClient => softwareUpdateClient;
4099
4103 public static GeoClient GeoClient => geoClient;
4104
4108 public static MailClient MailClient => mailClient;
4109
4113 public static OAuth2Environment OAuthEnvironment => oauthEnvironment;
4114
4118 public static HttpServer HttpServer => webServer ?? Types.TryGetModuleParameter<HttpServer>("HTTP");
4119
4123 public static HttpxServer HttpxServer => httpxServer ?? Types.TryGetModuleParameter<HttpxServer>("HTTPXS");
4124
4128 public static HttpxProxy HttpxProxy => httpxProxy ?? Types.TryGetModuleParameter<HttpxProxy>("HTTPX");
4129
4133 public static Socks5Proxy Socks5Proxy => socksProxy ?? Types.TryGetModuleParameter<Socks5Proxy>("SOCKS5");
4134
4138 public static CoapEndpoint CoapEndpoint => coapEndpoint ?? Types.TryGetModuleParameter<CoapEndpoint>("CoAP");
4139
4140 // TODO: Teman: http://mmistakes.github.io/skinny-bones-jekyll/, http://jekyllrb.com/
4141
4142 #endregion
4143
4144 #region Service Commands
4145
4153 public static async Task<bool> ExecuteServiceCommand(int CommandNr)
4154 {
4156
4157 lock (serviceCommandByNr)
4158 {
4159 if (!serviceCommandByNr.TryGetValue(CommandNr, out h))
4160 h = null;
4161 }
4162
4163 if (h is null)
4164 {
4165 Log.Warning("Service command lacking command handler invoked.", CommandNr.ToString());
4166 return false;
4167 }
4168 else
4169 {
4170 try
4171 {
4172 await h(null, EventArgs.Empty);
4173 }
4174 catch (Exception ex)
4175 {
4176 Log.Exception(ex);
4177 }
4178
4179 return true;
4180 }
4181 }
4182
4188 public static int RegisterServiceCommand(EventHandlerAsync Callback)
4189 {
4190 int i;
4191
4192 lock (serviceCommandByNr)
4193 {
4194 if (serviceCommandNrByCallback.TryGetValue(Callback, out i))
4195 return i;
4196
4197 i = nextServiceCommandNr++;
4198
4199 serviceCommandNrByCallback[Callback] = i;
4200 serviceCommandByNr[i] = Callback;
4201 }
4202
4203 return i;
4204 }
4205
4211 public static bool UnregisterServiceCommand(EventHandlerAsync Callback)
4212 {
4213 lock (serviceCommandByNr)
4214 {
4215 if (serviceCommandNrByCallback.TryGetValue(Callback, out int i))
4216 {
4217 serviceCommandByNr.Remove(i);
4218 serviceCommandNrByCallback.Remove(Callback);
4219
4220 return true;
4221 }
4222 }
4223
4224 return false;
4225 }
4226
4230 public static int BeforeUninstallCommandNr => beforeUninstallCommandNr;
4231
4235 public static event EventHandlerAsync OnBeforeUninstall = null;
4236
4237 private static Task BeforeUninstall(object Sender, EventArgs e)
4238 {
4239 return OnBeforeUninstall.Raise(Sender, e, false);
4240 }
4241
4242 #endregion
4243
4244 #region Scheduling
4245
4253 public static DateTime ScheduleEvent(Action<object> Callback, DateTime When, object State)
4254 {
4255 return scheduler?.Add(When, Callback, State) ?? DateTime.MinValue;
4256 }
4257
4265 public static DateTime ScheduleEvent(Func<object, Task> Callback, DateTime When, object State)
4266 {
4267 return scheduler?.Add(When, Callback, State) ?? DateTime.MinValue;
4268 }
4269
4275 public static bool CancelScheduledEvent(DateTime When)
4276 {
4277 return scheduler?.Remove(When) ?? false;
4278 }
4279
4280 #endregion
4281
4282 #region Random number generation
4283
4288 public static double NextDouble()
4289 {
4290 byte[] b = new byte[8];
4291
4292 lock (rnd)
4293 {
4294 rnd.GetBytes(b);
4295 }
4296
4297 double d = BitConverter.ToUInt64(b, 0);
4298 d /= ulong.MaxValue;
4299
4300 return d;
4301 }
4302
4311 public static int NextInteger(int Max)
4312 {
4313 if (Max < 0)
4314 throw new ArgumentOutOfRangeException("Must be non-negative.", nameof(Max));
4315
4316 if (Max == 0)
4317 return 0;
4318
4319 int Result;
4320
4321 do
4322 {
4323 Result = (int)(NextDouble() * Max);
4324 }
4325 while (Result >= Max);
4326
4327 return Result;
4328 }
4329
4335 public static byte[] NextBytes(int NrBytes)
4336 {
4337 if (NrBytes < 0)
4338 throw new ArgumentException("Number of bytes must be non-negative.", nameof(NrBytes));
4339
4340 byte[] Result = new byte[NrBytes];
4341
4342 lock (rnd)
4343 {
4344 rnd.GetBytes(Result);
4345 }
4346
4347 return Result;
4348 }
4349
4354 public static void NextBytes(byte[] Buffer)
4355 {
4356 NextBytes(Buffer, 0, Buffer.Length);
4357 }
4358
4365 public static void NextBytes(byte[] Buffer, int Offset, int Count)
4366 {
4367 lock (rnd)
4368 {
4369 rnd.GetBytes(Buffer, Offset, Count);
4370 }
4371 }
4372
4373 #endregion
4374
4375 #region Momentary values
4376
4381 public static Task NewMomentaryValues(params Field[] Values)
4382 {
4383 return concentratorServer?.SensorServer?.NewMomentaryValues(Values) ?? Task.CompletedTask;
4384 }
4385
4391 public static Task NewMomentaryValues(IThingReference Reference, params Field[] Values)
4392 {
4393 return concentratorServer?.SensorServer?.NewMomentaryValues(Reference, Values) ?? Task.CompletedTask;
4394 }
4395
4400 public static Task NewMomentaryValues(IEnumerable<Field> Values)
4401 {
4402 return concentratorServer?.SensorServer?.NewMomentaryValues(Values) ?? Task.CompletedTask;
4403 }
4404
4410 public static Task NewMomentaryValues(IThingReference Reference, IEnumerable<Field> Values)
4411 {
4412 return concentratorServer?.SensorServer?.NewMomentaryValues(Reference, Values) ?? Task.CompletedTask;
4413 }
4414
4415 #endregion
4416
4417 #region Personal Eventing Protocol
4418
4424 {
4425 if (pepClient is null)
4426 throw new Exception("No PEP client available.");
4427
4428 return pepClient.Publish(PersonalEvent, null, null);
4429 }
4430
4436 public static void RegisterHandler(Type PersonalEventType, EventHandlerAsync<PersonalEventNotificationEventArgs> Handler)
4437 {
4438 pepClient?.RegisterHandler(PersonalEventType, Handler);
4439 }
4440
4447 public static bool UnregisterHandler(Type PersonalEventType, EventHandlerAsync<PersonalEventNotificationEventArgs> Handler)
4448 {
4449 if (pepClient is null)
4450 return false;
4451 else
4452 return pepClient.UnregisterHandler(PersonalEventType, Handler);
4453 }
4454
4459 public static event EventHandlerAsync<ItemNotificationEventArgs> PubSubItemNotification
4460 {
4461 add => pepClient.NonPepItemNotification += value;
4462 remove => pepClient.NonPepItemNotification -= value;
4463 }
4464
4465 #endregion
4466
4467 #region Backups
4468
4469 private static async Task<bool> CheckBackup()
4470 {
4471 bool Result = false;
4472
4473 try
4474 {
4475 if (await Export.GetAutomaticBackupsAsync())
4476 {
4477 DateTime Now = DateTime.Now;
4478 TimeSpan Timepoint = await Export.GetBackupTimeAsync();
4479 DateTime EstimatedTime = Now.Date + Timepoint;
4480 DateTime LastBackup = await Export.GetLastBackupAsync();
4481
4482 if ((Timepoint.Hours == Now.Hour && Timepoint.Minutes == Now.Minute) ||
4483 (lastBackupTimeCheck.HasValue && lastBackupTimeCheck.Value < EstimatedTime && Now >= EstimatedTime) ||
4484 LastBackup.AddDays(1) < EstimatedTime)
4485 {
4486 lastBackupTimeCheck = Now;
4487 await DoBackup();
4488
4489 Result = true;
4490 }
4491 }
4492 }
4493 catch (Exception ex)
4494 {
4495 Log.Exception(ex);
4496 }
4497
4498 return Result;
4499 }
4500
4504 public static async Task DoBackup()
4505 {
4506 DateTime Now = DateTime.Now;
4507
4508 await Export.SetLastBackupAsync(Now);
4509
4510 StartExport.ExportInfo ExportInfo = await StartExport.GetExporter("Encrypted", false, Array.Empty<string>());
4511
4512 ExportFormat.UpdateClientsFileUpdated(ExportInfo.LocalBackupFileName, -1, Now);
4513
4514 List<string> Folders = new List<string>();
4515
4516 foreach (Export.FolderCategory FolderCategory in Export.GetRegisteredFolders())
4517 Folders.AddRange(FolderCategory.Folders);
4518
4519 await StartExport.DoExport(ExportInfo, true, false, true, Folders.ToArray());
4520
4521 long KeepDays = await Export.GetKeepDaysAsync();
4522 long KeepMonths = await Export.GetKeepMonthsAsync();
4523 long KeepYears = await Export.GetKeepYearsAsync();
4524 string ExportFolder = await Export.GetFullExportFolderAsync();
4525 string KeyFolder = await Export.GetFullKeyExportFolderAsync();
4526
4527 DeleteOldFiles(ExportFolder, KeepDays, KeepMonths, KeepYears, Now, true);
4528 if (ExportFolder != KeyFolder)
4529 DeleteOldFiles(KeyFolder, KeepDays, KeepMonths, KeepYears, Now, true);
4530
4531 DeleteOldFiles(Path.GetTempPath(), 7, 0, 0, Now, false);
4532
4533 await OnAfterBackup.Raise(typeof(Gateway), EventArgs.Empty);
4534 }
4535
4536 private static DateTime? lastBackupTimeCheck = null;
4537
4542 public static event EventHandlerAsync OnAfterBackup = null;
4543
4549 public static void DeleteOldFiles(string Path, long KeepDays)
4550 {
4551 DeleteOldFiles(Path, KeepDays, 0, 0, DateTime.Now, false);
4552 }
4553
4554 private static void DeleteOldFiles(string Path, long KeepDays, long KeepMonths,
4555 long KeepYears, DateTime Now, bool LogIndividualFileEvents)
4556 {
4557 int Count = 0;
4558
4559 try
4560 {
4561 if (!Directory.Exists(Path))
4562 return;
4563
4564 string[] Files = Directory.GetFiles(Path, "*.*", SearchOption.AllDirectories);
4565 DateTime CreationTime;
4566
4567 foreach (string FileName in Files)
4568 {
4569 try
4570 {
4571 CreationTime = File.GetCreationTime(FileName);
4572
4573 if (KeepMonths > 0 && CreationTime.Day == 1)
4574 {
4575 if (KeepYears > 0 && CreationTime.Month == 1) // Yearly
4576 {
4577 if (Now.Year - CreationTime.Year <= KeepYears)
4578 continue;
4579 }
4580 else // Monthly
4581 {
4582 if ((Now.Year * 12 + Now.Month - (CreationTime.Year * 12 + Now.Month)) <= KeepMonths)
4583 continue;
4584 }
4585 }
4586 else // Daily
4587 {
4588 if ((Now.Date - CreationTime.Date).TotalDays <= KeepDays)
4589 continue;
4590 }
4591
4592 File.Delete(FileName);
4593 Count++;
4594
4595 if (LogIndividualFileEvents)
4596 Log.Informational("File deleted.", FileName);
4597
4598 ExportFormat.UpdateClientsFileDeleted(System.IO.Path.GetFileName(FileName));
4599 }
4600 catch (Exception ex)
4601 {
4602 Log.Exception(ex, FileName);
4603 }
4604 }
4605 }
4606 catch (Exception ex)
4607 {
4608 Log.Exception(ex);
4609 }
4610 finally
4611 {
4612 if (Count > 0 && !LogIndividualFileEvents)
4613 {
4614 if (Count == 1)
4615 Log.Informational("1 file deleted.", Path);
4616 else
4617 Log.Informational(Count.ToString() + " files deleted.", Path);
4618 }
4619 }
4620 }
4621
4622 private static Task Database_CollectionRepaired(object Sender, CollectionRepairedEventArgs e)
4623 {
4624 StringBuilder Msg = new StringBuilder();
4625
4626 Msg.Append("Collection repaired: ");
4627 Msg.AppendLine(e.Collection);
4628
4629 if (!(e.Flagged is null))
4630 {
4631 foreach (FlagSource Source in e.Flagged)
4632 {
4633 Msg.AppendLine();
4634 Msg.Append("Reason: ");
4635 Msg.Append(MarkdownDocument.Encode(Source.Reason));
4636
4637 if (Source.Count > 1)
4638 {
4639 Msg.Append(" (");
4640 Msg.Append(Source.Count.ToString());
4641 Msg.Append(" times)");
4642 }
4643
4644 Msg.AppendLine();
4645 Msg.AppendLine();
4646 Msg.AppendLine("StackTrace:");
4647 Msg.AppendLine();
4648 Msg.AppendLine("```");
4649 Msg.AppendLine(Source.StackTrace);
4650 Msg.AppendLine("```");
4651 }
4652 }
4653
4654 Log.Alert(Msg.ToString(), e.Collection);
4655
4656 return Task.CompletedTask;
4657 }
4658
4659 #endregion
4660
4661 #region Notifications
4662
4663 private static Task MailClient_MailReceived(object Sender, MailEventArgs e)
4664 {
4665 return MailReceived.Raise(Sender, e);
4666 }
4667
4671 public static event EventHandlerAsync<MailEventArgs> MailReceived = null;
4672
4677 public static Task SendNotification(Graph Graph)
4678 {
4679 return SendNotification(Content.Markdown.Functions.ToMarkdown.GraphToMarkdown(Graph));
4680 }
4681
4686 public static Task SendNotification(PixelInformation Pixels)
4687 {
4688 return SendNotification(Content.Markdown.Functions.ToMarkdown.PixelsToMarkdown(Pixels));
4689 }
4690
4695 public static Task SendNotification(string Markdown)
4696 {
4697 return SendNotification(Markdown, string.Empty, false);
4698 }
4699
4705 public static Task SendNotification(string Markdown, string MessageId)
4706 {
4707 return SendNotification(Markdown, MessageId, false);
4708 }
4709
4715 public static Task SendNotificationUpdate(string Markdown, string MessageId)
4716 {
4717 return SendNotification(Markdown, MessageId, true);
4718 }
4719
4726 private static async Task SendNotification(string Markdown, string MessageId, bool Update)
4727 {
4728 try
4729 {
4730 CaseInsensitiveString[] Addresses = GetNotificationAddresses();
4731 (string Text, string Html) = await ConvertMarkdown(Markdown);
4732
4733 foreach (CaseInsensitiveString AdminAddress in Addresses)
4734 {
4735 try
4736 {
4737 await SendNotification(AdminAddress, Markdown, Text, Html, MessageId, Update);
4738 }
4739 catch (Exception ex)
4740 {
4741 Log.Exception(ex, AdminAddress);
4742 }
4743 }
4744
4745 Addresses = GetNotificationWebHooks();
4746 if (Addresses.Length > 0)
4747 {
4748 Dictionary<string, object> Data = new Dictionary<string, object>()
4749 {
4750 { "Markdown", Markdown },
4751 { "Text", Text },
4752 { "Html", Html }
4753 };
4754
4755 foreach (CaseInsensitiveString AdminUrl in Addresses)
4756 {
4757 try
4758 {
4759 ContentResponse Response = await InternetContent.PostAsync(new Uri(AdminUrl), Data, certificate);
4760 if (Response.HasError)
4761 Log.Error(Response.Error, AdminUrl);
4762 }
4763 catch (Exception ex)
4764 {
4765 Log.Exception(ex, AdminUrl);
4766 }
4767 }
4768 }
4769 }
4770 catch (Exception ex)
4771 {
4772 Log.Exception(ex);
4773 }
4774 }
4775
4776 private static Task<(string, string)> ConvertMarkdown(string Markdown)
4777 {
4778 return ConvertMarkdown(Markdown, true, true);
4779 }
4780
4781 private static async Task<(string, string)> ConvertMarkdown(string Markdown, bool TextVersion, bool HtmlVersion)
4782 {
4783 if (TextVersion || HtmlVersion)
4784 {
4785 MarkdownSettings Settings = new MarkdownSettings()
4786 {
4787 ParseMetaData = false
4788 };
4790 {
4791 XmlEntitiesOnly = true
4792 };
4793 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(Markdown, Settings);
4794 string Text = TextVersion ? await Doc.GeneratePlainText() : null;
4795 string Html = HtmlVersion ? HtmlDocument.GetBody(await Doc.GenerateHTML(HtmlSettings)) : null;
4796
4797 return (Text, Html);
4798 }
4799 else
4800 return (null, null);
4801 }
4802
4808 {
4810 }
4811
4817 {
4819 }
4820
4821 private static async Task SendNotification(string To, string Markdown, string Text, string Html, string MessageId, bool Update)
4822 {
4823 if (!(XmppClient is null) && XmppClient.State == XmppState.Connected)
4824 {
4826 if (Item is null || (Item.State != SubscriptionState.To && Item.State != SubscriptionState.Both))
4827 {
4828 await xmppClient.RequestPresenceSubscription(To);
4829 ScheduleEvent(Resend, DateTime.Now.AddMinutes(15), new object[] { To, Markdown, Text, Html, MessageId, Update });
4830 }
4831 else
4832 await SendChatMessage(MessageType.Chat, Markdown, Text, Html, To, MessageId, string.Empty, Update);
4833 }
4834 else
4835 ScheduleEvent(Resend, DateTime.Now.AddSeconds(30), new object[] { To, Markdown, Text, Html, MessageId, Update });
4836 }
4837
4843 public static Task SendChatMessage(string Markdown, string To)
4844 {
4845 return SendChatMessage(Markdown, To, string.Empty);
4846 }
4847
4854 public static Task SendChatMessage(string Markdown, string To, string MessageId)
4855 {
4856 return SendChatMessage(Markdown, To, MessageId, string.Empty);
4857 }
4858
4866 public static async Task SendChatMessage(string Markdown, string To, string MessageId, string ThreadId)
4867 {
4868 (string Text, string Html) = await ConvertMarkdown(Markdown);
4869 await SendChatMessage(MessageType.Chat, Markdown, Text, Html, To, MessageId, ThreadId, false);
4870 }
4871
4878 public static Task SendChatMessageUpdate(string Markdown, string To, string MessageId)
4879 {
4880 return SendChatMessageUpdate(Markdown, To, MessageId, string.Empty);
4881 }
4882
4890 public static async Task SendChatMessageUpdate(string Markdown, string To, string MessageId, string ThreadId)
4891 {
4892 (string Text, string Html) = await ConvertMarkdown(Markdown);
4893 await SendChatMessage(MessageType.Chat, Markdown, Text, Html, To, MessageId, ThreadId, true);
4894 }
4895
4901 public static Task SendGroupChatMessage(string Markdown, string To)
4902 {
4903 return SendGroupChatMessage(Markdown, To, string.Empty);
4904 }
4905
4912 public static Task SendGroupChatMessage(string Markdown, string To, string MessageId)
4913 {
4914 return SendGroupChatMessage(Markdown, To, MessageId, string.Empty);
4915 }
4916
4924 public static async Task SendGroupChatMessage(string Markdown, string To, string MessageId, string ThreadId)
4925 {
4926 (string Text, string Html) = await ConvertMarkdown(Markdown);
4927 await SendChatMessage(MessageType.GroupChat, Markdown, Text, Html, To, MessageId, ThreadId, false);
4928 }
4929
4936 public static Task SendGroupChatMessageUpdate(string Markdown, string To, string MessageId)
4937 {
4938 return SendGroupChatMessageUpdate(Markdown, To, MessageId, string.Empty);
4939 }
4940
4948 public static async Task SendGroupChatMessageUpdate(string Markdown, string To, string MessageId, string ThreadId)
4949 {
4950 (string Text, string Html) = await ConvertMarkdown(Markdown);
4951 await SendChatMessage(MessageType.GroupChat, Markdown, Text, Html, To, MessageId, ThreadId, true);
4952 }
4953
4959 public static Task<string> GetMultiFormatChatMessageXml(string Markdown)
4960 {
4961 return GetMultiFormatChatMessageXml(Markdown, true, true);
4962 }
4963
4971 public static async Task<string> GetMultiFormatChatMessageXml(string Markdown, bool TextVersion, bool HtmlVersion)
4972 {
4973 (string Text, string Html) = await ConvertMarkdown(Markdown, TextVersion, HtmlVersion);
4974 return GetMultiFormatChatMessageXml(Text, Html, Markdown);
4975 }
4976
4984 public static string GetMultiFormatChatMessageXml(string Text, string Html, string Markdown)
4985 {
4986 StringBuilder Xml = new StringBuilder();
4987 AppendMultiFormatChatMessageXml(Xml, Text, Html, Markdown);
4988 return Xml.ToString();
4989 }
4990
4998 public static void AppendMultiFormatChatMessageXml(StringBuilder Xml, string Text, string Html, string Markdown)
4999 {
5000 if (string.IsNullOrEmpty(Text))
5001 Xml.Append("<body/>");
5002 else
5003 {
5004 Xml.Append("<body>");
5005
5006 if (Text.Contains("]]>"))
5007 Xml.Append(XML.Encode(Text));
5008 else
5009 {
5010 Xml.Append("<![CDATA[");
5011 Xml.Append(Text);
5012 Xml.Append("]]>");
5013 }
5014
5015 Xml.Append("</body>");
5016 }
5017
5018 if (!string.IsNullOrEmpty(Markdown))
5019 {
5020 Xml.Append("<content xmlns=\"urn:xmpp:content\" type=\"text/markdown\">");
5021
5022 if (Markdown.Contains("]]>"))
5023 Xml.Append(XML.Encode(Markdown));
5024 else
5025 {
5026 Xml.Append("<![CDATA[");
5027 Xml.Append(Markdown);
5028 Xml.Append("]]>");
5029 }
5030
5031 Xml.Append("</content>");
5032 }
5033
5034 if (!string.IsNullOrEmpty(Html))
5035 {
5036 Xml.Append("<html xmlns='http://jabber.org/protocol/xhtml-im'>");
5037 Xml.Append("<body xmlns='http://www.w3.org/1999/xhtml'>");
5038
5039 HtmlDocument Doc = new HtmlDocument("<root>" + Html + "</root>");
5040 IEnumerable<HtmlNode> Children = (Doc.Body ?? Doc.Root).Children;
5041
5042 if (!(Children is null))
5043 {
5044 foreach (HtmlNode N in Children)
5045 N.Export(Xml);
5046 }
5047
5048 Xml.Append("</body></html>");
5049 }
5050 }
5051
5052 private static async Task SendChatMessage(MessageType Type, string Markdown, string Text, string Html, string To, string MessageId, string ThreadId, bool Update)
5053 {
5054 if (!(XmppClient is null) && XmppClient.State == XmppState.Connected)
5055 {
5056 StringBuilder Xml = new StringBuilder();
5057
5058 AppendMultiFormatChatMessageXml(Xml, Text, Html, Markdown);
5059
5060 if (Update && !string.IsNullOrEmpty(MessageId))
5061 {
5062 Xml.Append("<replace id='");
5063 Xml.Append(MessageId);
5064 Xml.Append("' xmlns='urn:xmpp:message-correct:0'/>");
5065
5066 MessageId = string.Empty;
5067 }
5068
5069 await xmppClient.SendMessage(QoSLevel.Unacknowledged, Type, MessageId, To, Xml.ToString(), string.Empty,
5070 string.Empty, string.Empty, ThreadId, string.Empty, null, null);
5071 }
5072 }
5073
5079 public static string GetUrl(string LocalResource)
5080 {
5081 return GetUrl(LocalResource, HttpServer);
5082 }
5083
5090 public static string GetUrl(string LocalResource, HttpServer Server)
5091 {
5092 if (LocalResource.StartsWith("http://") ||
5093 LocalResource.StartsWith("https://"))
5094 {
5095 return LocalResource;
5096 }
5097
5098 StringBuilder sb = new StringBuilder();
5099 int DefaultPort;
5100 int[] Ports;
5101
5102 sb.Append("http");
5103
5104 if (certificate is null)
5105 {
5106 Ports = Server?.OpenHttpPorts ?? GetConfigPorts("HTTP");
5107 DefaultPort = 80;
5108 }
5109 else
5110 {
5111 sb.Append('s');
5112 Ports = Server?.OpenHttpsPorts ?? GetConfigPorts("HTTPS");
5113 DefaultPort = 443;
5114 }
5115
5116 sb.Append("://");
5118 sb.Append(domain);
5119 /*else if (httpxProxy?.ServerlessMessaging?.Network.State == PeerToPeerNetworkState.Ready)
5120 sb.Append(httpxProxy.ServerlessMessaging.Network.ExternalAddress.ToString());
5121
5122 TODO: P2P & Serverless messaging: Recognize HTTP request, and redirect to local HTTP Server, and return response.
5123 */
5124 else
5125 {
5126 IPAddress IP4 = null;
5127 IPAddress IP6 = null;
5128
5129 if (!(Server is null))
5130 {
5131 foreach (IPAddress Addr in Server.LocalIpAddresses)
5132 {
5133 if (IPAddress.IsLoopback(Addr))
5134 continue;
5135
5136 switch (Addr.AddressFamily)
5137 {
5138 case System.Net.Sockets.AddressFamily.InterNetwork:
5139 IP4 ??= Addr;
5140 break;
5141
5142 case System.Net.Sockets.AddressFamily.InterNetworkV6:
5143 IP6 ??= Addr;
5144 break;
5145 }
5146 }
5147 }
5148
5149 if (!(IP4 is null))
5150 sb.Append(IP4.ToString());
5151 else if (!(IP6 is null))
5152 sb.Append(IP6.ToString());
5153 else
5154 sb.Append(Dns.GetHostName());
5155 }
5156
5157 if (Array.IndexOf(Ports, DefaultPort) < 0 && Ports.Length > 0)
5158 {
5159 sb.Append(":");
5160 sb.Append(Ports[0].ToString());
5161 }
5162
5163 sb.Append(LocalResource);
5164
5165 return sb.ToString();
5166 }
5167
5174 public static bool IsDomain(string DomainOrHost, bool IncludeAlternativeDomains)
5175 {
5177 {
5178 if (DomainOrHost == domain)
5179 return true;
5180
5181 if (IncludeAlternativeDomains && !(alternativeDomains is null))
5182 {
5183 foreach (CaseInsensitiveString s in alternativeDomains)
5184 {
5185 if (s == DomainOrHost)
5186 return true;
5187 }
5188 }
5189 }
5190 else
5191 {
5192 if (DomainOrHost == "localhost" || string.IsNullOrEmpty(DomainOrHost))
5193 return true;
5194
5195 if (!(webServer is null))
5196 {
5197 foreach (IPAddress Addr in webServer.LocalIpAddresses)
5198 {
5199 if (Addr.ToString() == DomainOrHost)
5200 return true;
5201 }
5202 }
5203
5204 if (DomainOrHost == Dns.GetHostName())
5205 return true;
5206 }
5207
5208 return false;
5209 }
5210
5211 private static void InternetContent_LocalDomainCheck(object sender, LocalDomainEventArgs e)
5212 {
5213 e.IsLocal = IsDomain(e.DomainOrHost, e.IncludeAlternativeDomains);
5214 }
5215
5216 private static async Task Resend(object P)
5217 {
5218 object[] P2 = (object[])P;
5219 await SendNotification((string)P2[0], (string)P2[1], (string)P2[2], (string)P2[3], (string)P2[4], (bool)P2[5]);
5220 }
5221
5222 #endregion
5223
5224 #region Settings
5225
5229 internal static async Task SimplifiedConfiguration()
5230 {
5231 foreach (SystemConfiguration Configuration in configurations)
5232 {
5233 if (!Configuration.Complete)
5234 {
5235 bool Updated = await Configuration.EnvironmentConfiguration();
5236
5237 if (!Configuration.Complete && await Configuration.SimplifiedConfiguration())
5238 Updated = true;
5239
5240 if (Updated)
5241 {
5242 await Configuration.MakeCompleted();
5243 await Database.Update(Configuration);
5244 }
5245 }
5246 }
5247 }
5248
5255 public static WebMenuItem[] GetSettingsMenu(HttpRequest Request, string UserVariable)
5256 {
5257 List<WebMenuItem> Result = new List<WebMenuItem>();
5258 Variables Session = Request.Session;
5259 if (Session is null)
5260 return Array.Empty<WebMenuItem>();
5261
5262 Language Language = ScriptExtensions.Constants.Language.GetLanguageAsync(Session).Result;
5263
5264 if (Session is null ||
5265 !Session.TryGetVariable(UserVariable, out Variable v) ||
5266 !(v.ValueObject is IUser User))
5267 {
5268 Result.Add(new WebMenuItem("Login", "/Login.md"));
5269 }
5270 else
5271 {
5272 if (!(configurations is null))
5273 {
5274 foreach (SystemConfiguration Configuration in configurations)
5275 {
5276 if (User.HasPrivilege("Settings." + Configuration.GetType().FullName))
5277 Result.Add(new WebMenuItem(Configuration.Title(Language).Result, Configuration.Resource));
5278 }
5279 }
5280
5281 if (!Session.TryGetVariable(Login.AutoLoginVariableName, out v) ||
5282 !(v.ValueObject is bool AutoLogin) || !AutoLogin)
5283 {
5284 Result.Add(new WebMenuItem("Logout", "/Logout"));
5285 }
5286 }
5287
5288 return Result.ToArray();
5289 }
5290
5291 #endregion
5292
5293 #region Smart Contracts
5294
5299 {
5300 get => contractsClient ?? Types.TryGetModuleParameter<ContractsClient>("Contracts");
5301 set
5302 {
5303 if (contractsClient is null ||
5304 contractsClient == value ||
5305 (value is null && LegalIdentityConfiguration.Instance is null))
5306 {
5307 contractsClient = value;
5308 }
5309 else
5310 throw new InvalidOperationException("Not allowed to set a new Contracts Client class.");
5311 }
5312 }
5313
5317 public static string LatestApprovedLegalIdentityId => LegalIdentityConfiguration.LatestApprovedLegalIdentityId;
5318
5326 public static async Task RequestContractSignature(Contract Contract, string Role, string Purpose)
5327 {
5328 bool RoleFound = false;
5329
5330 if (Contract is null)
5331 throw new ArgumentException("Contract cannot be null.", nameof(Contract));
5332
5333 // TODO: Check contract server signature is valid.
5334
5335 foreach (Networking.XMPP.Contracts.Role R in Contract.Roles)
5336 {
5337 if (R.Name == Role)
5338 {
5339 RoleFound = true;
5340 break;
5341 }
5342 }
5343
5344 if (!RoleFound)
5345 throw new ArgumentException("Invalid role.", nameof(Role));
5346
5347 if (string.IsNullOrEmpty(Purpose) || Purpose.IndexOfAny(CommonTypes.CRLF) >= 0)
5348 throw new ArgumentException("Invalid purpose.", nameof(Purpose));
5349
5350 try
5351 {
5352 string Module = string.Empty;
5353 int Skip = 1;
5354
5355 while (true)
5356 {
5357 StackFrame Frame = new StackFrame(Skip);
5358 MethodBase Method = Frame.GetMethod();
5359 if (Method is null)
5360 break;
5361
5362 Type Type = Method.DeclaringType;
5363 Assembly Assembly = Type.Assembly;
5364 Module = Assembly.GetName().Name;
5365
5366 if (Type != typeof(Gateway) && !Module.StartsWith("System."))
5367 break;
5368
5369 Skip++;
5370 }
5371
5372 string Markdown;
5373 ContractSignatureRequest Request = await Database.FindFirstIgnoreRest<ContractSignatureRequest>(new FilterAnd(
5374 new FilterFieldEqualTo("ContractId", Contract.ContractId),
5375 new FilterFieldEqualTo("Role", Role),
5376 new FilterFieldEqualTo("Module", Module),
5377 new FilterFieldEqualTo("Provider", Contract.Provider),
5378 new FilterFieldEqualTo("Purpose", Purpose)));
5379
5380
5381 if (Request is null)
5382 {
5383 Request = new ContractSignatureRequest()
5384 {
5385 Received = DateTime.Now,
5386 Signed = null,
5387 ContractId = Contract.ContractId,
5388 Role = Role,
5389 Module = Module,
5390 Provider = Contract.Provider,
5391 Purpose = Purpose
5392 };
5393
5394 Request.SetContract(Contract);
5395
5396 await Database.Insert(Request);
5397
5398 Markdown = await Files.ReadAllTextAsync(Path.Combine(rootFolder, "SignatureRequest.md"));
5399
5400 int i = Markdown.IndexOf("~~~~~~");
5401 int c = Markdown.Length;
5402
5403 if (i >= 0)
5404 {
5405 i += 6;
5406 while (i < c && Markdown[i] == '~')
5407 i++;
5408
5409 Markdown = Markdown[i..].TrimStart();
5410 }
5411
5412 i = Markdown.IndexOf("~~~~~~");
5413 if (i > 0)
5414 Markdown = Markdown[..i].TrimEnd();
5415
5417 Variables["RequestId"] = Request.ObjectId;
5418 Variables["Request"] = Request;
5419
5420 MarkdownSettings Settings = new MarkdownSettings(emoji1_24x24, false, Variables);
5421 Markdown = await MarkdownDocument.Preprocess(Markdown, Settings);
5422
5423 StringBuilder sb = new StringBuilder(Markdown);
5424
5425 sb.AppendLine();
5426 sb.Append("Link: [`");
5427 sb.Append(Request.ContractId);
5428 sb.Append("](");
5429 sb.Append(GetUrl("/SignatureRequest.md?RequestId=" + Request.ObjectId));
5430 sb.AppendLine(")");
5431
5432 Markdown = sb.ToString();
5433 }
5434 else
5435 {
5436 Markdown = "**Reminder**: Smart Contract [" + Request.ContractId + "](" +
5437 GetUrl("/SignatureRequest.md?RequestId=" + Request.ObjectId) + ") is waiting for your signature.";
5438 }
5439
5440 await SendNotification(Markdown);
5441 }
5442 catch (Exception ex)
5443 {
5444 Log.Exception(ex);
5445 }
5446 }
5447
5458 public static Task<bool> PetitionLegalIdentity(string LegalId, string PetitionId, string Purpose,
5459 EventHandlerAsync<LegalIdentityPetitionResponseEventArgs> Callback, TimeSpan Timeout,
5460 HttpRequest Request)
5461 {
5462 return LegalIdentityConfiguration.Instance.PetitionLegalIdentity(LegalId, PetitionId, Purpose, Callback, Timeout, Request);
5463 }
5464
5475 public static Task<bool> PetitionLegalIdentity(string LegalId, string PetitionId, string Purpose, string Password,
5476 EventHandlerAsync<LegalIdentityPetitionResponseEventArgs> Callback, TimeSpan Timeout)
5477 {
5478 return LegalIdentityConfiguration.Instance.PetitionLegalIdentity(LegalId, PetitionId, Purpose, Password, Callback, Timeout);
5479 }
5480
5491 public static Task<bool> PetitionContract(string ContractId, string PetitionId, string Purpose,
5492 EventHandlerAsync<ContractPetitionResponseEventArgs> Callback, TimeSpan Timeout,
5493 HttpRequest Request)
5494 {
5495 return LegalIdentityConfiguration.Instance.PetitionContract(ContractId, PetitionId, Purpose, Callback, Timeout, Request);
5496 }
5497
5508 public static Task<bool> PetitionContract(string ContractId, string PetitionId, string Purpose, string Password,
5509 EventHandlerAsync<ContractPetitionResponseEventArgs> Callback, TimeSpan Timeout)
5510 {
5511 return LegalIdentityConfiguration.Instance.PetitionContract(ContractId, PetitionId, Purpose, Password, Callback, Timeout);
5512 }
5513
5514 #endregion
5515
5516 #region Finding Files
5517
5527 public static string[] FindFiles(Environment.SpecialFolder[] Folders, string Pattern, bool IncludeSubfolders, bool BreakOnFirst)
5528 {
5529 return FileSystem.FindFiles(Folders, Pattern, IncludeSubfolders, BreakOnFirst);
5530 }
5531
5541 public static string[] FindFiles(string[] Folders, string Pattern, bool IncludeSubfolders, bool BreakOnFirst)
5542 {
5543 return FileSystem.FindFiles(Folders, Pattern, IncludeSubfolders, BreakOnFirst);
5544 }
5545
5555 public static string[] FindFiles(string[] Folders, string Pattern, bool IncludeSubfolders, int MaxCount)
5556 {
5557 return FileSystem.FindFiles(Folders, Pattern, IncludeSubfolders, MaxCount);
5558 }
5559
5569 public static string[] FindFiles(string[] Folders, string Pattern, int SubfolderDepth, int MaxCount)
5570 {
5571 return FileSystem.FindFiles(Folders, Pattern, SubfolderDepth, MaxCount);
5572 }
5573
5580 public static string[] GetFolders(Environment.SpecialFolder[] Folders, params string[] AppendWith)
5581 {
5582 return FileSystem.GetFolders(Folders, AppendWith);
5583 }
5584
5594 public static string FindLatestFile(Environment.SpecialFolder[] Folders, string Pattern, bool IncludeSubfolders)
5595 {
5596 return FileSystem.FindLatestFile(Folders, Pattern, IncludeSubfolders);
5597 }
5598
5608 public static string FindLatestFile(string[] Folders, string Pattern, bool IncludeSubfolders)
5609 {
5610 return FileSystem.FindLatestFile(Folders, Pattern, IncludeSubfolders);
5611 }
5612
5622 public static string FindLatestFile(string[] Folders, string Pattern, int SubfolderDepth)
5623 {
5624 return FileSystem.FindLatestFile(Folders, Pattern, SubfolderDepth);
5625 }
5626
5627 #endregion
5628
5629 #region Custom Errors
5630
5631 private static readonly Dictionary<string, KeyValuePair<DateTime, MarkdownDocument>> defaultDocuments = new Dictionary<string, KeyValuePair<DateTime, MarkdownDocument>>();
5632
5633 private static async Task WebServer_CustomError(object Sender, CustomErrorEventArgs e)
5634 {
5635 HttpFieldAccept Accept = e.Request?.Header?.Accept;
5636 if (Accept is null || Accept.Value == "*/*")
5637 return;
5638
5640 {
5641 string Html = await GetCustomErrorHtml(e.Request, e.StatusCode.ToString() + ".md", e.ContentType, e.Content);
5642
5643 if (!string.IsNullOrEmpty(Html))
5644 e.SetContent("text/html; charset=utf-8", System.Text.Encoding.UTF8.GetBytes(Html));
5645 }
5646 }
5647
5656 public static async Task<string> GetCustomErrorHtml(HttpRequest Request, string LocalFileName, string ContentType, byte[] Content)
5657 {
5658 bool IsText;
5659 bool IsMarkdown;
5660 bool IsEmpty;
5661
5662 if (string.IsNullOrEmpty(ContentType))
5663 {
5664 IsText = IsMarkdown = false;
5665 IsEmpty = true;
5666 }
5667 else
5668 {
5669 IsText = ContentType.StartsWith(PlainTextCodec.DefaultContentType);
5670 IsMarkdown = ContentType.StartsWith(MarkdownCodec.ContentType);
5671 IsEmpty = false;
5672 }
5673
5674 if (IsEmpty || IsText || IsMarkdown)
5675 {
5676 MarkdownDocument Doc;
5677 DateTime TP;
5678
5679 lock (defaultDocuments)
5680 {
5681 if (defaultDocuments.TryGetValue(LocalFileName, out KeyValuePair<DateTime, MarkdownDocument> P))
5682 {
5683 TP = P.Key;
5684 Doc = P.Value;
5685 }
5686 else
5687 {
5688 TP = DateTime.MinValue;
5689 Doc = null;
5690 }
5691 }
5692
5693 string FullFileName = Path.Combine(appDataFolder, "Default", LocalFileName);
5694
5695 if (File.Exists(FullFileName))
5696 {
5697 DateTime TP2 = File.GetLastWriteTimeUtc(FullFileName);
5699 MarkdownSettings Settings;
5700 MarkdownDocument Detail;
5701 string Markdown;
5702 bool SessionLocked = false;
5703
5704 try
5705 {
5706 if (Doc is null || TP2 > TP)
5707 {
5708 Markdown = await Files.ReadAllTextAsync(FullFileName);
5709 Settings = new MarkdownSettings(emoji1_24x24, true)
5710 {
5711 RootFolder = rootFolder,
5713 };
5714
5716 {
5718 SessionLocked = true;
5719
5720 SessionVariables.CurrentRequest = Request;
5721 SessionVariables.CurrentResponse = Request.Response;
5722 }
5723
5724 Doc = await MarkdownDocument.CreateAsync(Markdown, Settings, RootFolder, string.Empty, string.Empty);
5725
5726 lock (defaultDocuments)
5727 {
5728 defaultDocuments[LocalFileName] = new KeyValuePair<DateTime, MarkdownDocument>(TP2, Doc);
5729 }
5730 }
5731
5732 if (IsEmpty || Content is null)
5733 Detail = null;
5734 else
5735 {
5736 InternetContent.ParseContentType(ref ContentType,
5737 out System.Text.Encoding Encoding, out _);
5738
5739 Encoding ??= System.Text.Encoding.UTF8;
5740
5741 Markdown = Strings.GetString(Content, Encoding);
5742 if (IsText)
5743 {
5744 MarkdownSettings Settings2 = new MarkdownSettings(null, false);
5745 Detail = await MarkdownDocument.CreateAsync("```\r\n" + Markdown + "\r\n```", Settings2);
5746 }
5747 else
5748 Detail = await MarkdownDocument.CreateAsync(Markdown, Doc.Settings);
5749 }
5750
5751 if (!(Doc.Tag is MultiReadSingleWriteObject DocSynchObj))
5752 {
5753 DocSynchObj = new MultiReadSingleWriteObject(Doc);
5754 Doc.Tag = DocSynchObj;
5755 }
5756
5757 if (await DocSynchObj.TryBeginWrite(30000))
5758 {
5759 try
5760 {
5761 Doc.Detail = Detail;
5762 return await Doc.GenerateHTML();
5763 }
5764 finally
5765 {
5766 await DocSynchObj.EndWrite();
5767 }
5768 }
5769 else
5770 throw new ServiceUnavailableException("Unable to generate custom HTML error document.");
5771 }
5772 finally
5773 {
5774 if (SessionLocked)
5775 {
5776 SessionVariables.CurrentRequest = null;
5777 SessionVariables.CurrentResponse = null;
5778
5780 }
5781 }
5782 }
5783 else
5784 {
5785 if (!(Doc is null))
5786 {
5787 lock (defaultDocuments)
5788 {
5789 defaultDocuments.Remove(LocalFileName);
5790 }
5791 }
5792 }
5793 }
5794
5795 return null;
5796 }
5797
5798 #endregion
5799
5800 #region Sniffers & Events
5801
5812 public static string AddWebSniffer(string SnifferId, HttpRequest Request, ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
5813 {
5814 return AddWebSniffer(SnifferId, Request, BinaryPresentationMethod.ByteCount, ComLayer, UserVariable, Privileges);
5815 }
5816
5828 public static string AddWebSniffer(string SnifferId, HttpRequest Request, BinaryPresentationMethod BinaryPresentationMethod,
5829 ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
5830 {
5831 return AddWebSniffer(SnifferId, Request, TimeSpan.FromHours(1), BinaryPresentationMethod, ComLayer, UserVariable, Privileges);
5832 }
5833
5846 public static string AddWebSniffer(string SnifferId, HttpRequest Request, TimeSpan MaxLife,
5847 BinaryPresentationMethod BinaryPresentationMethod, ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
5848 {
5849 string Resource = Request.Header.ResourcePart;
5850 int i = Resource.IndexOfAny(new char[] { '?', '#' });
5851 if (i > 0)
5852 Resource = Resource[..i];
5853
5854 return AddWebSniffer(SnifferId, Resource, MaxLife, BinaryPresentationMethod, ComLayer, UserVariable, Privileges);
5855 }
5856
5869 public static string AddWebSniffer(string SnifferId, string PageResource, TimeSpan MaxLife,
5870 BinaryPresentationMethod BinaryPresentationMethod, ICommunicationLayer ComLayer, string UserVariable, params string[] Privileges)
5871 {
5872 bool Found = false;
5873
5874 foreach (ISniffer Sniffer in ComLayer)
5875 {
5876 if (Sniffer is WebSniffer WebSniffer && WebSniffer.SnifferId == SnifferId)
5877 {
5878 Found = true;
5879 break;
5880 }
5881 }
5882
5883 if (!Found)
5884 {
5885 WebSniffer Sniffer = new WebSniffer(SnifferId, PageResource, MaxLife, BinaryPresentationMethod, ComLayer, UserVariable, Privileges);
5886 ComLayer.Add(Sniffer);
5887 }
5888
5889 return "\r\n\r\n![Sniffer](/Sniffers/Sniffer.md)\r\n\r\n";
5890 }
5891
5901 public static void AddWebEventSink(string SinkId, HttpRequest Request, string UserVariable, params string[] Privileges)
5902 {
5903 AddWebEventSink(SinkId, Request, TimeSpan.FromHours(1), UserVariable, Privileges);
5904 }
5905
5916 public static void AddWebEventSink(string SinkId, HttpRequest Request, TimeSpan MaxLife, string UserVariable, params string[] Privileges)
5917 {
5918 string Resource = Request.Header.ResourcePart;
5919 int i = Resource.IndexOfAny(new char[] { '?', '#' });
5920 if (i > 0)
5921 Resource = Resource[..i];
5922
5923 AddWebEventSink(SinkId, Resource, MaxLife, UserVariable, Privileges);
5924 }
5925
5936 public static void AddWebEventSink(string SinkId, string PageResource, TimeSpan MaxLife, string UserVariable, params string[] Privileges)
5937 {
5938 bool Found = false;
5939
5940 foreach (IEventSink Sink in Log.Sinks)
5941 {
5942 if (Sink is WebEventSink WebEventSink && WebEventSink.ObjectID == SinkId)
5943 {
5944 Found = true;
5945 break;
5946 }
5947 }
5948
5949 if (!Found)
5950 {
5951 WebEventSink Sink = new WebEventSink(SinkId, PageResource, MaxLife, UserVariable, Privileges);
5952 Log.Register(Sink);
5953 }
5954 }
5955
5956 #endregion
5957
5958 #region Script Resources
5959
5967 public static async Task<bool> AddScriptResource(string ResourceName, Expression Expression, string ReferenceFileName)
5968 {
5969 if (!await RemoveScriptResource(ResourceName, true))
5970 return false;
5971
5972 webServer.Register(new HttpScriptResource(ResourceName, Expression, ReferenceFileName, true));
5973
5974 await RuntimeSettings.SetAsync("Gateway.ScriptResource." + ResourceName, ReferenceFileName + " ||| " + Expression.Script);
5975
5976 return true;
5977 }
5978
5986 public static async Task<bool> AddScriptResource(string ResourceName, ScriptNode Expression, string ReferenceFileName)
5987 {
5988 if (!await RemoveScriptResource(ResourceName, true))
5989 return false;
5990
5991 webServer.Register(new HttpScriptResource(ResourceName, Expression, ReferenceFileName, true));
5992
5993 await RuntimeSettings.SetAsync("Gateway.ScriptResource." + ResourceName, ReferenceFileName + " ||| " + Expression.SubExpression);
5994
5995 return true;
5996 }
5997
6003 public static Task<bool> RemoveScriptResource(string ResourceName)
6004 {
6005 return RemoveScriptResource(ResourceName, false);
6006 }
6007
6014 private static async Task<bool> RemoveScriptResource(string ResourceName, bool ConsiderNonexistantRemoved)
6015 {
6016 if (!webServer.TryGetResource(ref ResourceName, false, out HttpResource Resource, out string SubPath))
6017 return false;
6018
6019 if (!string.IsNullOrEmpty(SubPath))
6020 return ConsiderNonexistantRemoved;
6021
6022 if (!(Resource is HttpScriptResource))
6023 return false;
6024
6025 webServer.Unregister(Resource);
6026
6027 await RuntimeSettings.DeleteAsync("Gateway.ScriptResource." + ResourceName);
6028
6029 return true;
6030 }
6031
6032 private static async Task LoadScriptResources()
6033 {
6034 Dictionary<string, object> Settings = await RuntimeSettings.GetWhereKeyLikeAsync("Gateway.ScriptResource.*", "*");
6035
6036 foreach (KeyValuePair<string, object> Setting in Settings)
6037 {
6038 if (!(Setting.Value is string Value))
6039 {
6040 Log.Error("Invalid Runtime setting found and ignored.",
6041 new KeyValuePair<string, object>("Key", Setting.Key),
6042 new KeyValuePair<string, object>("Value", Setting.Value));
6043
6044 continue;
6045 }
6046
6047 string ResourceName = Setting.Key[23..];
6048 string ReferenceFileName;
6049 int i;
6050 Expression Exp;
6051
6052 i = Value.IndexOf(" ||| ");
6053 if (i < 0)
6054 ReferenceFileName = string.Empty;
6055 else
6056 {
6057 ReferenceFileName = Value[..i];
6058 Value = Value[(i + 5)..];
6059 }
6060
6061 try
6062 {
6063 Exp = new Expression(Value);
6064 webServer.Register(new HttpScriptResource(ResourceName, Exp, ReferenceFileName, true));
6065 }
6066 catch (Exception ex)
6067 {
6068 Log.Error("Invalid Runtime setting script. Resource could not be added.",
6069 new KeyValuePair<string, object>("Resource", ResourceName),
6070 new KeyValuePair<string, object>("Error", ex.Message),
6071 new KeyValuePair<string, object>("ReferenceFileName", ReferenceFileName),
6072 new KeyValuePair<string, object>("Script", Value));
6073
6074 continue;
6075 }
6076 }
6077 }
6078
6085 public static Task<int> ProcessNewServiceConfigurations()
6086 {
6087 return ProcessServiceConfigurations(true);
6088 }
6089
6090 private static async Task<int> ProcessServiceConfigurations(bool OnlyIfChanged)
6091 {
6092 string[] ConfigurationFiles = Directory.GetFiles(appDataFolder, "*.config", SearchOption.TopDirectoryOnly);
6093 int NrExecuted = 0;
6094
6095 foreach (string ConfigurationFile in ConfigurationFiles)
6096 {
6097 if (await ProcessServiceConfigurationFile(ConfigurationFile, OnlyIfChanged))
6098 NrExecuted++;
6099 }
6100
6101 return NrExecuted;
6102 }
6103
6104 private const string ServiceConfigurationRoot = "ServiceConfiguration";
6105 private const string ServiceConfigurationNamespace = "http://waher.se/Schema/ServiceConfiguration.xsd";
6106
6114 public static async Task<bool> ProcessServiceConfigurationFile(string ConfigurationFileName, bool OnlyIfChanged)
6115 {
6116 try
6117 {
6118 ConfigurationFileName = Path.GetFullPath(ConfigurationFileName);
6119
6120 string DirectoryName = Path.GetDirectoryName(ConfigurationFileName);
6121 if (!DirectoryName.EndsWith(new string(Path.DirectorySeparatorChar, 1)))
6122 DirectoryName += Path.DirectorySeparatorChar;
6123
6124 if (string.Compare(DirectoryName, appDataFolder, true) != 0)
6125 return false;
6126
6127 if (!File.Exists(ConfigurationFileName))
6128 return false;
6129
6130 XmlDocument Doc = XML.LoadFromFile(ConfigurationFileName);
6131
6132 if (Doc.DocumentElement.LocalName != ServiceConfigurationRoot || Doc.DocumentElement.NamespaceURI != ServiceConfigurationNamespace)
6133 return false;
6134
6135 XSL.Validate(Path.GetFileName(ConfigurationFileName), Doc, ServiceConfigurationRoot, ServiceConfigurationNamespace,
6136 XSL.LoadSchema(typeof(Gateway).Namespace + ".Schema.ServiceConfiguration.xsd", typeof(Gateway).Assembly));
6137
6138 bool ExecuteInitScript = await Content.Markdown.Functions.InitScriptFile.NeedsExecution(ConfigurationFileName);
6139
6140 if (OnlyIfChanged && !ExecuteInitScript)
6141 return false;
6142
6143 Log.Notice("Applying Service Configurations.", ConfigurationFileName);
6144
6145 webServer.UnregisterVanityResources(ConfigurationFileName);
6146
6147 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
6148 {
6149 if (!(N is XmlElement E))
6150 continue;
6151
6152 switch (E.LocalName)
6153 {
6154 case "VanityResources":
6155 foreach (XmlNode N2 in E.ChildNodes)
6156 {
6157 if (N2 is XmlElement E2 && E2.LocalName == "VanityResource")
6158 {
6159 string RegEx = XML.Attribute(E2, "regex");
6160 string Url = XML.Attribute(E2, "url");
6161
6162 try
6163 {
6164 webServer.RegisterVanityResource(RegEx, Url, ConfigurationFileName);
6165 }
6166 catch (Exception ex)
6167 {
6168 Log.Error("Unable to register vanity resource: " + ex.Message,
6169 new KeyValuePair<string, object>("RegEx", RegEx),
6170 new KeyValuePair<string, object>("Url", Url));
6171 }
6172 }
6173 }
6174 break;
6175
6176 case "StartupScript": // Always execute
6177 Expression Exp = new Expression(E.InnerText);
6179 await Exp.EvaluateAsync(v);
6180 break;
6181
6182 case "InitializationScript": // Execute, only if changed
6183 if (ExecuteInitScript)
6184 {
6185 Exp = new Expression(E.InnerText);
6187 await Exp.EvaluateAsync(v);
6188 }
6189 break;
6190 }
6191 }
6192
6193 return true;
6194 }
6195 catch (Exception ex)
6196 {
6197 Log.Exception(ex, ConfigurationFileName);
6198 return false;
6199 }
6200 }
6201
6202 #endregion
6203
6204 #region Profiling
6205
6206 private static async Task WebServer_ConnectionProfiled(object Sender, ProfilingEventArgs e)
6207 {
6208 try
6209 {
6211 DateTime Now = DateTime.UtcNow;
6212 StringBuilder sb = new StringBuilder();
6213
6214 sb.Append("Profiling ");
6215 sb.Append(Now.Year.ToString("D4"));
6216 sb.Append('-');
6217 sb.Append(Now.Month.ToString("D2"));
6218 sb.Append('-');
6219 sb.Append(Now.Day.ToString("D2"));
6220 sb.Append('T');
6221 sb.Append(Now.Hour.ToString("D2"));
6222 sb.Append('_');
6223 sb.Append(Now.Minute.ToString("D2"));
6224 sb.Append('_');
6225 sb.Append(Now.Second.ToString("D2"));
6226 sb.Append('_');
6227 sb.Append(Now.Millisecond.ToString("D3"));
6228 sb.Append(".uml");
6229
6230 string Folder = Path.Combine(appDataFolder, "HTTP");
6231
6232 if (!httpProfilingFolderChecked)
6233 {
6234 if (!Directory.Exists(Folder))
6235 Directory.CreateDirectory(Folder);
6236
6237 httpProfilingFolderChecked = true;
6238 }
6239
6240 int NrNodes = 0;
6241 string BaseFileName = sb.ToString();
6242 string FileName = Path.Combine(Folder, BaseFileName);
6243 string Uml = Profiler.ExportPlantUml(TimeUnit.Seconds);
6244 string Uml2 = e.FlowControl?.ExportPlantUml(out NrNodes);
6245
6246 await Files.WriteAllTextAsync(FileName, Uml);
6247
6248 if (NrNodes > 0)
6249 {
6250 FileName = Path.Combine(Folder, BaseFileName.Replace("Profiling ", "States "));
6251 await Files.WriteAllTextAsync(FileName, Uml2);
6252 }
6253
6254 StringBuilder Markdown = new StringBuilder();
6255
6256 Markdown.AppendLine("```uml");
6257 Markdown.AppendLine(Uml.TrimEnd());
6258 Markdown.AppendLine("```");
6259
6260 await SendNotification(Markdown.ToString());
6261
6262 if (NrNodes > 0)
6263 {
6264 Markdown.Clear();
6265 Markdown.AppendLine("```uml");
6266 Markdown.AppendLine(Uml.TrimEnd());
6267 Markdown.AppendLine("```");
6268
6269 await SendNotification(Markdown.ToString());
6270 }
6271 }
6272 catch (Exception ex)
6273 {
6274 Log.Exception(ex);
6275 httpProfilingFolderChecked = false;
6276 }
6277 }
6278
6279 private static bool httpProfilingFolderChecked = false;
6280
6281 #endregion
6282
6283 #region Local, Temporary, and short URLs
6284
6293 public static string GetShortUrl(string Url, bool OneTimeUse)
6294 {
6295 return UrlShortener.GetShortUrl(Url, OneTimeUse);
6296 }
6297
6305 public static bool TryGetLocalResourceFileName(string Resource, string Host, out string FileName)
6306 {
6307 if (!Uri.TryCreate(Resource, UriKind.RelativeOrAbsolute, out Uri ParsedResource))
6308 {
6309 FileName = null;
6310 return false;
6311 }
6312
6313 if (ParsedResource.IsAbsoluteUri)
6314 {
6315 if (!IsDomain(ParsedResource.Host, true))
6316 {
6317 FileName = null;
6318 return false;
6319 }
6320
6321 Resource = ParsedResource.LocalPath;
6322 }
6323
6324 if (!string.IsNullOrEmpty(Host) &&
6325 HttpServer.TryGetFileName("/" + Host + Resource, out FileName))
6326 {
6327 return true;
6328 }
6329
6330 return HttpServer.TryGetFileName(Resource, out FileName);
6331 }
6332
6333 #endregion
6334
6335 #region Nonce values
6336
6342 public static Task<bool> HasNonceBeenUsed(string Nonce)
6343 {
6344 return nonceValues?.ContainsKeyAsync(Nonce) ?? Task.FromResult(false);
6345 }
6346
6351 public static Task RegisterNonceValue(string Nonce)
6352 {
6353 return nonceValues?.AddAsync(Nonce, true) ?? Task.CompletedTask;
6354 }
6355
6356 #endregion
6357
6358 }
6359}
Helps with parsing of commong data types.
Definition: CommonTypes.cs:15
static readonly char[] CRLF
Contains the CR LF character sequence.
Definition: CommonTypes.cs:19
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:48
Contains information about a response to a content request.
bool HasError
If an error occurred.
Exception Error
Error response.
Provides emojis from Emoji One (http://emojione.com/) stored as local files.
HTML encoder/decoder.
Definition: HtmlCodec.cs:15
const string DefaultContentType
Default Content-Type for HTML: text/html
Definition: HtmlCodec.cs:26
HtmlElement Root
Root element.
Definition: HtmlDocument.cs:73
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, Dictionary< string, string > Namespaces)
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 void SetDefaultTimeout(int Timeout, bool Lock)
Sets the default timeout of internet access methods, in milliseconds.
static Task< ContentResponse > PostAsync(Uri Uri, object Data, params KeyValuePair< string, string >[] Headers)
Posts to a resource, using a Uniform Resource Identifier (or Locator).
static bool ParseContentType(ref string ContentType, out Encoding Encoding, out KeyValuePair< string, string >[] Fields)
Parses a Content-Type, providing the base Content-Type, character encoding, if any,...
Local domain check event arguments.
bool IncludeAlternativeDomains
If alternative domains are to be checked.
string DomainOrHost
Domain or host name.
Class managing GraphViz integration into Markdown documents.
Definition: GraphViz.cs:61
static async Task Terminate()
Terminates GraphViz processing.
Definition: GraphViz.cs:145
static void Init(string ContentRootFolder)
Initializes the GraphViz-Markdown integration.
Definition: GraphViz.cs:91
Class managing 2D XML Layout integration into Markdown documents.
Definition: XmlLayout.cs:44
static void Init(string ContentRootFolder)
Initializes the Layout2D-Markdown integration.
Definition: XmlLayout.cs:67
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.
static void SetDefaultEmojiSource(IEmojiSource EmojiSource, bool Lock)
Sets the default emoji source.
Class managing PlantUML integration into Markdown documents.
Definition: PlantUml.cs:56
static void Init(string ContentRootFolder)
Initializes the PlantUML-Markdown integration.
Definition: PlantUml.cs:80
static async Task Terminate()
Terminates PlantUML processing.
Definition: PlantUml.cs:131
Contains settings that the HTML export uses to customize HTML output.
Definition: HtmlSettings.cs:7
Web Script encoder/decoder.
Definition: WsCodec.cs:15
static bool IsRawEncodingAllowedLocked
If the IsRawEncodingAllowed setting is locked.
Definition: WsCodec.cs:41
static void AllowRawEncoding(bool Allow, bool Lock)
If raw encoding of web script should be allowed.
Definition: WsCodec.cs:24
const string ContentType
Markdown content type.
Definition: WsCodec.cs:53
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:179
Plain text encoder/decoder.
const string DefaultContentType
text/plain
Helps with common XML-related tasks.
Definition: XML.cs:21
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:1062
static XmlDocument LoadFromFile(string FileName)
Loads an XML document from a file.
Definition: XML.cs:1808
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:29
static XmlException AnnotateException(XmlException ex)
Creates a new XML Exception object, with reference to the source XML file, for information.
Definition: XML.cs:1762
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Definition: XML.cs:713
Static class managing loading of XSL resources stored as embedded resources or in content files.
Definition: XSL.cs:16
static XmlSchema LoadSchema(string ResourceName)
Loads an XML schema from an embedded resource.
Definition: XSL.cs:24
static void Validate(string ObjectID, XmlDocument Xml, params XmlSchema[] Schemas)
Validates an XML document given a set of XML schemas.
Definition: XSL.cs:134
Converts an object to a ZIP File.
static void ProtectContentType(string ContentType)
Protects a content type, so that it cannot be included in generated zip files by external parties thr...
Class representing an event.
Definition: Event.cs:11
void Avoid(IEventSink EventSink)
If the event sink EventSink should be avoided when logging the event.
Definition: Event.cs:190
string Facility
Facility can be either a facility in the network sense or in the system sense.
Definition: Event.cs:152
Outputs sniffed data to a text file.
Outputs sniffed data to an XML file.
Filters incoming events and passes remaining events to a secondary event sink.
Definition: EventFilter.cs:11
Sends logged events to a collection of event sinks.
Definition: EventSinks.cs:9
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static string CleanStackTrace(string StackTrace)
Cleans a Stack Trace string, removing entries from the asynchronous execution model,...
Definition: Log.cs:194
static IEventSink[] Sinks
Registered sinks.
Definition: Log.cs:132
static void Register(IEventSink EventSink)
Registers an event sink with the event log. Call Unregister(IEventSink) to unregister it,...
Definition: Log.cs:30
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1657
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
Definition: Log.cs:576
static void 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:1027
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Definition: Log.cs:692
static void 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:1447
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Definition: Log.cs:344
static void Notice(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a notice event.
Definition: Log.cs:460
static void 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:228
static void Alert(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an alert event.
Definition: Log.cs:1237
static async void Event(Event Event)
Logs an event. It will be distributed to registered event sinks.
Definition: Log.cs:138
virtual string ObjectID
Object ID, used when logging events.
Definition: LogObject.cs:26
Event sink sending events to a topic on an MQTT server. Events are sent as XML fragments.
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.
Writes logged events to an operating system pipe, for inter-process communication.
Creates an even sink that queues incoming events in a local persisted queue, for processing by in ord...
Definition: EventQueue.cs:14
Writes logged events to to a socket.
Event sink that sends events to a Syslog server using the Syslog protocol.
Event sink sending events to a remote service using POST.
Event sink sending events to a destination over the XMPP network.
Defines the Groups data source. This data source contains a tree structure of groups of nodes
Definition: GroupSource.cs:21
static async Task< int > DeleteOldEvents(TimeSpan MaxAge)
Deletes old data source events.
Definition: GroupSource.cs:290
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 ...
const string DefaultContentType
Default Content-Type for CSSX: text/x-cssx
Definition: CssxDecoder.cs:25
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:559
Static class managing data export.
Definition: Export.cs:18
static async Task< string > GetFullExportFolderAsync()
Full path to export folder.
Definition: Export.cs:22
static FolderCategory[] GetRegisteredFolders()
Gets registered exportable folders.
Definition: Export.cs:542
static async Task< string > GetFullKeyExportFolderAsync()
Full path to key folder.
Definition: Export.cs:35
static async Task SetLastBackupAsync(DateTime Value)
Set Timestamp of last backup.
Definition: Export.cs:505
static async Task< long > GetKeepMonthsAsync()
For how many months the monthly backups are kept.
Definition: Export.cs:429
static async Task< long > GetKeepYearsAsync()
For how many years the yearly backups are kept.
Definition: Export.cs:455
static async Task< DateTime > GetLastBackupAsync()
Get Timestamp of last backup.
Definition: Export.cs:494
static async Task< long > GetKeepDaysAsync()
For how many days backups are kept.
Definition: Export.cs:403
static async Task< bool > GetAutomaticBackupsAsync()
If automatic backups are activated
Definition: Export.cs:377
static async Task< TimeSpan > GetBackupTimeAsync()
Time of day to start performing backups.
Definition: Export.cs:481
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:147
static WebMenuItem[] GetSettingsMenu(HttpRequest Request, string UserVariable)
Gets the settings menu.
Definition: Gateway.cs:5255
static HttpxProxy HttpxProxy
HTTPX Proxy resource
Definition: Gateway.cs:4128
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:3087
static HttpServer HttpServer
HTTP Server
Definition: Gateway.cs:4118
static string InstanceName
Name of the current instance. Default instance=string.Empty
Definition: Gateway.cs:3127
static async Task 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:3407
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:6114
static Task Terminate()
Raises the OnTerminate event handler, letting the container executable know the application needs to ...
Definition: Gateway.cs:3233
static Task NewMomentaryValues(IEnumerable< Field > Values)
Reports newly measured values.
Definition: Gateway.cs:4400
static void RegisterHandler(Type PersonalEventType, EventHandlerAsync< PersonalEventNotificationEventArgs > Handler)
Registers an event handler of a specific type of personal events.
Definition: Gateway.cs:4436
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:5555
static string GetMultiFormatChatMessageXml(string Text, string Html, string Markdown)
Gets XML for a multi-formatted chat message.
Definition: Gateway.cs:4984
static void SafeDispose(IDisposable Object)
Disposes an object, catching and logging any exceptions.
Definition: Gateway.cs:3045
static bool IsDomain(string DomainOrHost, bool IncludeAlternativeDomains)
If a domain or host name represents the gateway.
Definition: Gateway.cs:5174
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:3868
static RequiredPrivileges LoggedIn(string UserVariable, IAuthorization< HttpRequest > Authorization)
Authentication mechanism that makes sure the call is made from a session with a valid authenticated u...
Definition: Gateway.cs:3846
static AvatarClient AvatarClient
XMPP Concentrator Server.
Definition: Gateway.cs:4058
static double NextDouble()
Generates a new floating-point value between 0 and 1, using a cryptographic random number generator.
Definition: Gateway.cs:4288
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:5812
const string WebApplicationFirewallLocalFileName
WAF.xml
Definition: Gateway.cs:156
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:5622
static Task SendNotification(PixelInformation Pixels)
Sends an image as a notification message to configured notification recipients.
Definition: Gateway.cs:4686
static X509Certificate2 Certificate
Domain certificate.
Definition: Gateway.cs:3082
static GetDatabaseProviderEventHandler GetDatabaseProvider
Event raised when the Gateway requires its database provider from the host.
Definition: Gateway.cs:2673
static Socks5Proxy Socks5Proxy
SOCKS5 Proxy
Definition: Gateway.cs:4133
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:5936
static CommunicationLayer FirstChanceExceptions
Observable layer where first chance exceptions can be monitored.
Definition: Gateway.cs:2683
static Task SendChatMessage(string Markdown, string To, string MessageId)
Sends a chat message to a recipient.
Definition: Gateway.cs:4854
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:5608
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:5569
static Task SendNotification(string Markdown, string MessageId)
Sends a notification message to configured notification recipients.
Definition: Gateway.cs:4705
static async Task SendChatMessageUpdate(string Markdown, string To, string MessageId, string ThreadId)
Sends a chat message update to a recipient.
Definition: Gateway.cs:4890
static EventHandlerAsync< Events.CertificateEventArgs > OnNewCertificate
Event raised when a new server certificate has been generated.
Definition: Gateway.cs:2628
static void DeleteOldFiles(string Path, long KeepDays)
Deletes old files in a folder.
Definition: Gateway.cs:4549
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:5916
static Task< bool > HasNonceBeenUsed(string Nonce)
Checks if a Nonce value has been used.
Definition: Gateway.cs:6342
static SensorClient SensorClient
XMPP Sensor Client.
Definition: Gateway.cs:4063
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:5541
static Task< bool > Start(bool ConsoleOutput, bool LoopbackIntefaceAvailable)
Starts the gateway.
Definition: Gateway.cs:265
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:5508
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:3900
static async Task SendGroupChatMessageUpdate(string Markdown, string To, string MessageId, string ThreadId)
Sends a group chat message update to a recipient.
Definition: Gateway.cs:4948
static Task< string > GetMultiFormatChatMessageXml(string Markdown)
Gets XML for a multi-formatted chat message.
Definition: Gateway.cs:4959
static async Task DoBackup()
Performs a backup of the system.
Definition: Gateway.cs:4504
static string RuntimeFolder
Runtime folder.
Definition: Gateway.cs:3137
static string ConfigFilePath
Full path to Gateway.config file.
Definition: Gateway.cs:3181
static LoginAuditor LoginAuditor
Current Login Auditor. Should be used by modules accepting user logins, to protect the system from un...
Definition: Gateway.cs:3860
static GeoClient GeoClient
XMPP Geo-spatial Publish/Subscribe Client, if such a compoent is available on the XMPP broker.
Definition: Gateway.cs:4103
static Task< bool > Start(bool ConsoleOutput)
Starts the gateway.
Definition: Gateway.cs:254
static async Task Stop()
Stops the gateway.
Definition: Gateway.cs:2816
static async Task< string > GetMultiFormatChatMessageXml(string Markdown, bool TextVersion, bool HtmlVersion)
Gets XML for a multi-formatted chat message.
Definition: Gateway.cs:4971
static async Task SendChatMessage(string Markdown, string To, string MessageId, string ThreadId)
Sends a chat message to a recipient.
Definition: Gateway.cs:4866
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:5846
static bool TryGetDefaultPage(HttpRequest Request, out string DefaultPage)
Tries to get the default page of a host.
Definition: Gateway.cs:3251
static SynchronizationClient SynchronizationClient
XMPP Synchronization Client.
Definition: Gateway.cs:4078
static PepClient PepClient
XMPP Personal Eventing Protocol (PEP) Client.
Definition: Gateway.cs:4083
static Task NewMomentaryValues(IThingReference Reference, IEnumerable< Field > Values)
Reports newly measured values.
Definition: Gateway.cs:4410
static Task< int > ProcessNewServiceConfigurations()
Processes new Service Configuration Files. This method should be called after installation of new ser...
Definition: Gateway.cs:6085
const string GatewayConfigNamespace
http://waher.se/Schema/GatewayConfiguration.xsd
Definition: Gateway.cs:166
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
Definition: Gateway.cs:4335
static string AppDataFolder
Application data folder.
Definition: Gateway.cs:3132
static string[] GetProtocols()
Gets the protocol names defined in the configuration file.
Definition: Gateway.cs:3215
static int[] GetConfigPorts(string Protocol)
Gets the port numbers defined for a given protocol in the configuration file.
Definition: Gateway.cs:3198
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:5901
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:5986
static Task SendNotification(Graph Graph)
Sends a graph as a notification message to configured notification recipients.
Definition: Gateway.cs:4677
static RequiredPrivileges LoggedIn(IAuthorization< HttpRequest > Authorization)
Authentication mechanism that makes sure the call is made from a session with a valid authenticated u...
Definition: Gateway.cs:3835
static EventHandlerAsync< GetDataSourcesEventArgs > GetDataSources
Event raised when the Gateway requires a set of data sources to publish.
Definition: Gateway.cs:2678
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:3879
static string GetUrl(string LocalResource)
Gets a URL for a resource.
Definition: Gateway.cs:5079
static async Task RequestContractSignature(Contract Contract, string Role, string Purpose)
Requests the operator to sign a smart contract.
Definition: Gateway.cs:5326
static void NextBytes(byte[] Buffer)
Generates random bytes into an array.
Definition: Gateway.cs:4354
static ContractsClient ContractsClient
XMPP Contracts Client, if such a compoent is available on the XMPP broker.
Definition: Gateway.cs:5299
static async Task< string > GetCustomErrorHtml(HttpRequest Request, string LocalFileName, string ContentType, byte[] Content)
Gets a custom error HTML document.
Definition: Gateway.cs:5656
static RequiredPrivileges LoggedIn(string[] Privileges)
Authentication mechanism that makes sure the call is made from a session with a valid authenticated u...
Definition: Gateway.cs:3809
static DateTime ScheduleEvent(Action< object > Callback, DateTime When, object State)
Schedules a one-time event.
Definition: Gateway.cs:4253
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:5967
const string GatewayConfigLocalName
GatewayConfiguration
Definition: Gateway.cs:161
static Task SendGroupChatMessage(string Markdown, string To)
Sends a group chat message to a recipient.
Definition: Gateway.cs:4901
static Task SendGroupChatMessage(string Markdown, string To, string MessageId)
Sends a group chat message to a recipient.
Definition: Gateway.cs:4912
static async Task CheckWAF()
Checks the Web Application Firewall file and loads or reloads it if necessary.
Definition: Gateway.cs:1954
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:2524
static HttpxServer HttpxServer
HTTPX Server
Definition: Gateway.cs:4123
static Task SendNotificationUpdate(string Markdown, string MessageId)
Sends a notification message to configured notification recipients.
Definition: Gateway.cs:4715
static RequiredPrivileges 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:3820
static ThingRegistryClient ThingRegistryClient
XMPP Thing Registry Client.
Definition: Gateway.cs:4043
static Task< bool > PetitionLegalIdentity(string LegalId, string PetitionId, string Purpose, EventHandlerAsync< LegalIdentityPetitionResponseEventArgs > Callback, TimeSpan Timeout, HttpRequest Request)
Petitions information about a legal identity from its owner.
Definition: Gateway.cs:5458
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:5594
static Task SendChatMessage(string Markdown, string To)
Sends a chat message to a recipient.
Definition: Gateway.cs:4843
static Task< bool > RemoveScriptResource(string ResourceName)
Removes a script resource from the web server hosted by the gateway.
Definition: Gateway.cs:6003
static bool TryGetDefaultPage(string Host, out string DefaultPage)
Tries to get the default page of a host.
Definition: Gateway.cs:3262
static ConcentratorClient ConcentratorClient
XMPP Concentrator Client.
Definition: Gateway.cs:4073
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:5527
static Task RegisterNonceValue(string Nonce)
Registers a nonce value.
Definition: Gateway.cs:6351
static async Task SafeDispose(IDisposableAsync Object)
Disposes an object, catching and logging any exceptions.
Definition: Gateway.cs:3064
static bool UnregisterHandler(Type PersonalEventType, EventHandlerAsync< PersonalEventNotificationEventArgs > Handler)
Unregisters an event handler of a specific type of personal events.
Definition: Gateway.cs:4447
static int NextInteger(int Max)
Returns a non-negative random integer that is less than the specified maximum.
Definition: Gateway.cs:4311
static Task SendChatMessageUpdate(string Markdown, string To, string MessageId)
Sends a chat message update to a recipient.
Definition: Gateway.cs:4878
static CaseInsensitiveString[] GetNotificationWebHooks()
Returns configured notification webhooks.
Definition: Gateway.cs:4816
static async Task< bool > Start(bool ConsoleOutput, bool LoopbackIntefaceAvailable, string InstanceName)
Starts the gateway.
Definition: Gateway.cs:277
static DateTime StartTime
Timepoint of starting the gateway.
Definition: Gateway.cs:247
static string[] GetFolders(Environment.SpecialFolder[] Folders, params string[] AppendWith)
Gets the physical locations of special folders.
Definition: Gateway.cs:5580
static CaseInsensitiveString[] GetNotificationAddresses()
Returns configured notification addresses.
Definition: Gateway.cs:4807
static Task NewMomentaryValues(IThingReference Reference, params Field[] Values)
Reports newly measured values.
Definition: Gateway.cs:4391
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:3911
static int RegisterServiceCommand(EventHandlerAsync Callback)
Registers an administrative service command.
Definition: Gateway.cs:4188
static XmppClient XmppClient
XMPP Client connection of gateway.
Definition: Gateway.cs:4038
static CoapEndpoint CoapEndpoint
CoAP Endpoint
Definition: Gateway.cs:4138
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:5475
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:4998
static void NextBytes(byte[] Buffer, int Offset, int Count)
Generates random bytes into an array.
Definition: Gateway.cs:4365
static bool HasDomain
If a domain name is configured.
Definition: Gateway.cs:3093
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:5869
static bool CancelScheduledEvent(DateTime When)
Cancels a scheduled event.
Definition: Gateway.cs:4275
static bool UnregisterServiceCommand(EventHandlerAsync Callback)
Unregisters an administrative service command.
Definition: Gateway.cs:4211
static string GetUrl(string LocalResource, HttpServer Server)
Gets a URL for a resource.
Definition: Gateway.cs:5090
static Task SendNotification(string Markdown)
Sends a notification message to configured notification recipients.
Definition: Gateway.cs:4695
static Task< bool > PetitionContract(string ContractId, string PetitionId, string Purpose, EventHandlerAsync< ContractPetitionResponseEventArgs > Callback, TimeSpan Timeout, HttpRequest Request)
Petitions information about a smart contract from its owner.
Definition: Gateway.cs:5491
static DateTime ScheduleEvent(Func< object, Task > Callback, DateTime When, object State)
Schedules a one-time event.
Definition: Gateway.cs:4265
static Task SendGroupChatMessageUpdate(string Markdown, string To, string MessageId)
Sends a group chat message update to a recipient.
Definition: Gateway.cs:4936
static async Task< bool > ExecuteServiceCommand(int CommandNr)
Executes a service command.
Definition: Gateway.cs:4153
static SoftwareUpdateClient SoftwareUpdateClient
XMPP Software Updates Client, if such a compoent is available on the XMPP broker.
Definition: Gateway.cs:4098
static ControlClient ControlClient
XMPP Control Client.
Definition: Gateway.cs:4068
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:5828
static string GetShortUrl(string Url, bool OneTimeUse)
Shortens a URL temporarily. Shortened URLs are available at most for 24 hours (if used) and 1h if not...
Definition: Gateway.cs:6293
static ProvisioningClient ProvisioningClient
XMPP Provisioning Client.
Definition: Gateway.cs:4048
static MailClient MailClient
XMPP Mail Client, if support for mail-extensions is available on the XMPP broker.
Definition: Gateway.cs:4108
static bool TryGetLocalResourceFileName(string Resource, string Host, out string FileName)
Tries to get a file name for a resource, if local.
Definition: Gateway.cs:6305
static async Task SendGroupChatMessage(string Markdown, string To, string MessageId, string ThreadId)
Sends a group chat message to a recipient.
Definition: Gateway.cs:4924
static Task PublishPersonalEvent(IPersonalEvent PersonalEvent)
Publishes a personal event on the XMPP network.
Definition: Gateway.cs:4423
const string GatewayConfigLocalFileName
Gateway.config
Definition: Gateway.cs:151
static Task NewMomentaryValues(params Field[] Values)
Reports newly measured values.
Definition: Gateway.cs:4381
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.
bool HasCertificate
If the configuration has a certificate.
CaseInsensitiveString[] Urls
Notification addresses.
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.
virtual Task DeferredConfiguration(HttpServer WebServer)
Performs a deferred configuration. Deferred configurations are such that could not be performed durin...
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 Geo
JID of geo-spatial publish/subscribe component.
string PubSub
JID of publish/subscribe component.
static XmppConfiguration Instance
Current instance of configuration.
string MultiUserChat
JID of Multi-User Chat service.
Echoes what the client sends in.
Definition: Echo.cs:11
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
const string AutoLoginVariableName
Variable to indicate if the user was automatically logged in. Not accessible via script.
Definition: Login.cs:29
Logs the user out from the gateway.
Definition: Logout.cs:10
A resource that returns as a single JavaScript file, the following four files:
Web Service for working with short URLs.
Definition: UrlShortener.cs:15
static string GetShortUrl(string Url, bool OneTimeUse)
Shortens a URL temporarily. Shortened URLs are available at most for 24 hours (if used) and 1h if not...
Sending events to the corresponding web page(s).
Definition: WebEventSink.cs:15
Sending sniffer events to the corresponding web page(s).
Definition: WebSniffer.cs:22
Defines the Jobs data source. This data source contains a tree structure of jobs of nodes
Definition: JobSource.cs:20
static async Task< int > DeleteOldEvents(TimeSpan MaxAge)
Deletes old data source events.
Definition: JobSource.cs:287
CoAP client. CoAP is defined in RFC7252: https://tools.ietf.org/html/rfc7252
Definition: CoapEndpoint.cs:35
Simple base class for classes implementing communication protocols.
void Exception(Exception Exception)
Called to inform the viewer of an exception state.
ISniffer[] Sniffers
Registered sniffers.
virtual void Add(ISniffer Sniffer)
ICommunicationLayer.Add
Task RemoveRange(IEnumerable< ISniffer > Sniffers)
Removes a set of sniffers, if registered.
Represents an HTTP authentication scheme that embeds a collection of authentication schemes,...
Authentication mechanism that makes sure the user has an established session with the web server.
const string DefaultUserVariable
Default user variable: User
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.
Event arguments for customizing sniffers based on remote endpoint.
ISniffer[] Sniffers
Sniffers to use for the connection.
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
static ForbiddenException AccessDenied(string ObjectId, string ActorId)
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 for all HTTP authentication schemes, as defined in RFC-7235: https://datatracker....
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:22
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:182
string RemoteEndPoint
Remote end-point.
Definition: HttpRequest.cs:243
SessionVariables Session
Contains session states, if the resource requires sessions, or null otherwise.
Definition: HttpRequest.cs:212
HttpResponse Response
HTTP Response object, if one has been assigned to the request.
Definition: HttpRequest.cs:254
Base class for all HTTP resources.
Definition: HttpResource.cs:23
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:23
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
Task Write(byte[] Data)
Returns binary data in the response.
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:41
void AddHttpsPorts(params int[] HttpsPorts)
Opens additional HTTPS ports, if not already open.
Definition: HttpServer.cs:553
int[] OpenHttpPorts
HTTP Ports successfully opened.
Definition: HttpServer.cs:768
void RegisterVanityResource(string RegexPattern, string MapTo)
Registers a vanity resource.
Definition: HttpServer.cs:2473
void ConfigureMutualTls(ClientCertificates ClientCertificates, bool TrustClientCertificates, bool LockSettings)
Configures Mutual-TLS capabilities of the server. Affects all connections, all resources.
Definition: HttpServer.cs:939
void UpdateCertificate(X509Certificate ServerCertificate)
Updates the server certificate
Definition: HttpServer.cs:927
IWebApplicationFirewall WebApplicationFirewall
Reference to Web Application Firewall (WAF) to help remove unwanted communication from the server
Definition: HttpServer.cs:1551
static SessionVariables CreateSessionVariables()
Creates a new collection of variables, that contains access to the global set of variables.
Definition: HttpServer.cs:2130
ILoginAuditor LoginAuditor
Reference to login-auditor to help remove malicious users from the server.
Definition: HttpServer.cs:1535
IPAddress[] LocalIpAddresses
IP Addresses receiving requests on.
Definition: HttpServer.cs:779
HttpResource Register(HttpResource Resource)
Registers a resource with the server.
Definition: HttpServer.cs:1568
bool TryGetResource(HttpRequest Request, out HttpResource Resource, out string SubPath)
Tries to get a resource from the server.
Definition: HttpServer.cs:1796
const int DefaultHttpPort
Default HTTP Port (80).
Definition: HttpServer.cs:45
bool TryGetFileName(string LocalUrl, out string FileName)
Tries to get the full path of a file-based resource.
Definition: HttpServer.cs:2427
int[] GetPorts(bool Http, bool Https)
Gets open ports
Definition: HttpServer.cs:849
int[] OpenPorts
Ports successfully opened.
Definition: HttpServer.cs:763
int[] OpenHttpsPorts
HTTPS Ports successfully opened.
Definition: HttpServer.cs:773
bool Unregister(HttpResource Resource)
Unregisters a resource from the server.
Definition: HttpServer.cs:1743
const int DefaultHttpsPort
Default HTTPS port (443).
Definition: HttpServer.cs:50
async void NetworkChanged()
Adapts the server to changes in the network. This method can be called automatically by calling the c...
Definition: HttpServer.cs:285
void SetHttp2ConnectionSettings(int InitialStreamWindowSize, int InitialConnectionWindowSize, int MaxFrameSize, int MaxConcurrentStreams, int HeaderTableSize, bool EnablePush, bool NoRfc7540Priorities, bool Lock)
HTTP/2 connection settings (SETTINGS).
Definition: HttpServer.cs:1051
int UnregisterVanityResources(object Tag)
Unregisters vanity resources tagged with a specific object.
Definition: HttpServer.cs:2506
HttpResource RegisterDomainProxy(string LocalDomain, HttpReverseProxyResource DomainProxy)
Registers a domain proxy resource with the server.
Definition: HttpServer.cs:1590
override void Add(ISniffer Sniffer)
ICommunicationLayer.Add
Definition: HttpServer.cs:1460
void AddHttpPorts(params int[] HttpPorts)
Opens additional HTTP ports, if not already open.
Definition: HttpServer.cs:391
An optionally-sized icon that can be displayed in a user interface.
Definition: Icon.cs:10
Base interface to add icons property.
Definition: Icons.cs:11
The server has not found anything matching the Request-URI. No indication is given of whether the con...
Provides OAUTH authorization server meta-data, as defined in RFC 8414. https://datatracker....
OAUTH authorize resource, as defined in RFC 6749. https://datatracker.ietf.org/doc/html/rfc6749
OAUTH device authorization resource, as defined in RFC 8628. https://datatracker.ietf....
OAUTH introspection resource, as defined in RFCs 7662. https://datatracker.ietf.org/doc/html/rfc7662
OAUTH client management resource, as defined in RFCs 7591. https://datatracker.ietf....
OAUTH dynamic registration resource, as defined in RFCs 7591 and 7592. https://datatracker....
OAUTH token resource, as defined in RFC 6749. https://datatracker.ietf.org/doc/html/rfc6749
Provides OAUTH resource meta-data, as defined in RFC 9728. https://datatracker.ietf....
Event arguments for profiling event handlers.
IFlowControl FlowControl
Flow control mechanism used.
Profiler Profiler
Profiler for session.
The response to the request can be found under a different URI and SHOULD be retrieved using a GET me...
The server is currently unable to handle the request due to a temporary overloading or maintenance of...
Collection of session variables.
async Task LockAsync()
Locks the collection. The collection is by default thread safe. But if longer transactions require un...
bool Locked
If the session variables are currently locked.
override bool TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
void Release()
Releases the collection, previously locked through a call to LockAsync().
The requested resource resides temporarily under a different URI. Since the redirection MAY be altere...
Manages an MQTT connection. Implements MQTT v3.1.1, as defined in http://docs.oasis-open....
Definition: MqttClient.cs:30
Module that controls the life cycle of communication.
static bool Stopping
If the system is stopping.
Manages registration of TCP and UDP ports in an Internet Gateway
static bool IsPublicAddress(IPAddress Address)
Checks if an IPv4 address is public.
Outputs sniffed data to the Console Output, serialized by ConsoleOut.
Outputs sniffed data to an XML file.
Maintains a set of XML-file-based sniffers. File output is organized into subfolders of a given folde...
Provides help with managing avatars.
Definition: AvatarClient.cs:25
Implements an XMPP concentrator client interface.
Implements an XMPP concentrator server interface.
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.
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 authority events, to associate a Bare JID with an authority that can authorize pr...
IRequestOrigin Authority
Authority associated with Bare JID
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.
Adds support for geo-spatial publish/subscribe communication pattern to an XMPP client.
Definition: GeoClient.cs:20
Implements a Proxy resource that allows Web clients to fetch HTTP-based resources over HTTPX.
Definition: HttpxProxy.cs:19
Class sending and receiving binary streams over XMPP using XEP-0047: In-band Bytestreams: https://xmp...
Definition: IbbClient.cs:20
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....
Client providing support for server mail-extension.
Definition: MailClient.cs:18
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:69
Task StartSearch(EventHandlerAsync Callback)
Starts the search of SOCKS5 proxies.
Definition: Socks5Proxy.cs:90
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
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:10
Implements an XMPP provisioning client interface.
string OwnerJid
JID of owner, if known or available.
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...
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
Task NewMomentaryValues(params Field[] Values)
Reports newly measured values.
Implements an XMPP interface for remote software updates.
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:58
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:1107
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:5447
const string NamespaceServiceDiscoveryInfo
http://jabber.org/protocol/disco#info
Definition: XmppClient.cs:117
Task RequestPresenceSubscription(string BareJid)
Requests subscription of presence information from a contact.
Definition: XmppClient.cs:4969
async Task Reconnect()
Reconnects a client after an error or if it's offline. Reconnecting, instead of creating a completely...
Definition: XmppClient.cs:1295
Task Connect()
Connects the client.
Definition: XmppClient.cs:641
string Domain
Current Domain.
Definition: XmppClient.cs:3492
RosterItem GetRosterItem(string BareJID)
Gets a roster item.
Definition: XmppClient.cs:4571
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.
Defines the Output data source. This data source contains a tree structure of output of nodes
Definition: OutputSource.cs:20
static async Task< int > DeleteOldEvents(TimeSpan MaxAge)
Deletes old data source events.
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static readonly CaseInsensitiveString Empty
Empty case-insensitive string
string LowerCase
Lower-case representation of the case-insensitive string.
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty 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:21
static Task< IPersistentDictionary > GetDictionary(string Collection)
Gets a persistent dictionary containing objects in a collection.
Definition: Database.cs:2307
static bool HasProvider
If a database provider is registered.
Definition: Database.cs:81
static void Register(IDatabaseProvider DatabaseProvider)
Registers a database provider for use from the static Database class, throughout the lifetime of the ...
Definition: Database.cs:33
static IDatabaseProvider Provider
Registered database provider.
Definition: Database.cs:59
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:238
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
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
Defines the Processors data source. This data source contains a tree structure of processor of nodes
static async Task< int > DeleteOldEvents(TimeSpan MaxAge)
Deletes old data source events.
Reports folder representing a folder on the file system containing file-based reports.
Defines the Reports data source. This data source contains a tree structure of reports published by d...
static async Task< bool > RegisterRootNode(ReportNode ReportRoot)
Registers a new report root node.
A folder of reports.
Implements an in-memory cache.
Definition: Cache.cs:17
bool Ping(KeyType Key)
Pings an entry in the cache, to keep it from being removed.
Definition: Cache.cs:300
void Add(KeyType Key, ValueType Value)
Adds an item to the cache.
Definition: Cache.cs:446
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
Contains static methods
Definition: Files.cs:14
static async Task< string > ReadAllTextAsync(string FileName)
Reads a text file asynchronously.
Definition: Files.cs:84
static Task WriteAllTextAsync(string FileName, string Text)
Creates a text file asynchronously.
Definition: Files.cs:95
Static class managing binary representations of strings.
Definition: Strings.cs:10
static string GetString(byte[] Data, int Offset, int Count, Encoding DefaultEncoding)
Gets a string from its binary representation, taking any Byte Order Mark (BOM) into account.
Definition: Strings.cs:148
Orders modules in dependency order.
Static class, loading and initializing assemblies dynamically.
Definition: TypesLoader.cs:14
static void Initialize()
Initializes the inventory engine, registering types and interfaces available in Types.
Definition: TypesLoader.cs:18
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
static Task< bool > StartAllModules(int Timeout)
Starts all loaded modules.
Definition: Types.cs:458
static void SetModuleParameter(string Name, object Value)
Sets a module parameter. This parameter value will be accessible to modules when they are loaded.
Definition: Types.cs:584
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Definition: Types.cs:607
static IModule[] GetLoadedModules()
Gets an array of loaded modules.
Definition: Types.cs:411
static object Instantiate(Type Type, params object[] Arguments)
Returns an instance of the type Type . If one needs to be created, it is. If the constructor requires...
Definition: Types.cs:1454
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:85
static Task StopAllModules()
Stops all modules.
Definition: Types.cs:328
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:68
void Stop()
Stops measuring time.
Definition: Profiler.cs:227
ProfilerThread CreateThread(string Name, ProfilerThreadType Type)
Creates a new profiler thread.
Definition: Profiler.cs:128
string ExportPlantUml(TimeUnit TimeUnit)
Exports events to PlantUML.
Definition: Profiler.cs:530
double ElapsedSeconds
Elapsed seconds since start.
Definition: Profiler.cs:241
void Start()
Starts measuring time.
Definition: Profiler.cs:217
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:14
bool Remove(DateTime When)
Removes an event scheduled for a given point in time.
Definition: Scheduler.cs:186
DateTime Add(DateTime When, Action< object > Callback, object State)
Adds an event.
Definition: Scheduler.cs:54
Class managing a script expression.
Definition: Expression.cs:41
string Script
Original script string.
Definition: Expression.cs:207
async Task< object > EvaluateAsync(Variables Variables)
Evaluates the expression, using the variables provided in the Variables collection....
Definition: Expression.cs:4461
Base class for graphs.
Definition: Graph.cs:88
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 TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
Definition: Variables.cs:56
Event arguments for the Assert.UnauthorizedAccess event.
Assembly Assembly
Assembly in which the type is defined.
A factory that can create and validate JWT tokens.
Definition: JwtFactory.cs:66
static JwtFactory CreateHmacSha256()
Creates a JWT factory that can create and validate JWT tokens using the HMAC-SHA256 algorithm.
Definition: JwtFactory.cs:123
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
A set of intervals specific for a given endpoint.
byte[] ComputeVariable(byte[] N)
Computes the SPONGE function, as defined in section 4 of NIST FIPS 202.
Definition: Keccak1600.cs:408
Implements the SHA3-256 hash function, as defined in section 6.1 in the NIST FIPS 202: https://nvlpub...
Definition: SHA3_256.cs:9
Corresponds to a privilege in the system.
Definition: Privilege.cs:16
Maintains the collection of all privileges in the system.
Definition: Privileges.cs:13
static async Task LoadAll()
Loads all privileges
Definition: Privileges.cs:81
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:24
string UserName
User Name
Definition: User.cs:60
bool HasPrivilege(string Privilege)
If the user has a given privilege.
Definition: User.cs:187
Maintains the collection of all users in the system.
Definition: Users.cs:24
static 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
static IUserSource Source
User source.
Definition: Users.cs:37
Web Application Firewall for HttpServer.
static WebApplicationFirewall LoadFromFile(string FileName, ILoginAuditor LoginAuditor, string AppDataFolder)
Loads a WAF definition from file.
Origin of request has maximum authority.
Origin of request has view-only authority.
static HttpAuthenticationScheme[] GetAuthenticationSchemes()
Gets an array of authentication schemes available to authorize access to a web resource.
Definition: HttpModule.cs:108
static async Task CheckLocalWebServerNode()
Checks if the Local Web Server Node has been created.
Definition: HttpModule.cs:301
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
Event arguments for events collecting data sources.
bool IsEmpty
If the reference is an empty reference.
Interface for asynchronously disposable objects.
Task DisposeAsync()
Disposes of the object, asynchronously.
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:10
Interface for sets of sniffers.
Definition: ISnifferSet.cs:9
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.
Persistent dictionary that can contain more entries than possible in the internal memory.
Task< bool > ContainsKeyAsync(string key)
Determines whether the System.Collections.Generic.IDictionary{string,object} contains an element with...
Task AddAsync(string key, object value)
Adds an element with the provided key and value to the System.Collections.Generic....
Interface for late-bound modules loaded at runtime.
Definition: IModule.cs:9
Basic authorization interface for objects of type T .
Interface for Mutual TLS (mTLS) Clients or TLS servers.
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.
Definition: ImplTypes.g.cs:58
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.
FromEventLevel
Allows events from a certain level.
SyslogEventSeparation
How events are separated in the Syslog event stream.
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 RegistrationEventHandler(MetaDataTag[] MetaData, RegistrationEventArgs e)
Delegate for registration callback methods.
HostDomainOptions
Options on how to handle domain names provided in the Host header.
MqttQualityOfService
MQTT Quality of Service level.
LineEnding
Type of line ending.
Definition: LineEnding.cs:7
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:17
ProfilerThreadType
Type of profiler thread.
Represents a duration value, as defined by the xsd:duration data type: http://www....
Definition: Duration.cs:14
static readonly Duration Zero
Zero value
Definition: Duration.cs:577