Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
XmppServer.cs
1//#define LogToWebHookTester
2
3using System;
5using System.Diagnostics;
6using System.Net;
7using System.Net.Mail;
8using System.Net.NetworkInformation;
11using System.Reflection;
13using System.Security.Cryptography.X509Certificates;
14using System.Text;
15using System.Threading;
16using System.Threading.Tasks;
17using System.Xml;
18using Waher.Content;
23using Waher.Events;
46using Waher.Script;
60using Waher.Security;
62
64{
69 {
73 public const int DefaultC2sPort = 5222;
74
78 public const int DefaultS2sPort = 5269;
79
83 public const int DefaultConnectionBacklog = 10;
84
88 public const int DefaultBufferSize = 16384;
89
93 public const int MaxGroupLength = 1000;
94
98 public const int MaxNameLength = 1000;
99
103 public const string StanzaNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas";
104
108 public const string StreamsNamespace = "urn:ietf:params:xml:ns:xmpp-streams";
109
113 public const string SaslNamespace = "urn:ietf:params:xml:ns:xmpp-sasl";
114
118 public const string RosterNamespace = "jabber:iq:roster";
119
123 public const string DataFormsNamespace = "jabber:x:data";
124
128 public const string AvatarStorageNamespace = "storage:client:avatar";
129
133 public const string DiscoveryNamespace = "http://jabber.org/protocol/disco#info";
134
138 public const string DiscoveryItemsNamespace = "http://jabber.org/protocol/disco#items";
139
143 public const string ExtendedAddressingNamespace = "http://jabber.org/protocol/address";
144
148 public const string PrivateXmlStorageNamespace = "jabber:iq:private";
149
153 public const string VCardNamespace = "vcard-temp";
154
158 public const string RegisterNamespace = "jabber:iq:register";
159
163 public const string SoftwareVersionNamespace = "jabber:iq:version";
164
168 public const string OfflineMessagesNamespace = "msgoffline";
169
173 public const string BlockingCommandNamespace = "urn:xmpp:blocking";
174
178 public const string BlockingCommandErrorNamespace = "urn:xmpp:blocking:errors";
179
183 public const string PingNamespace = "urn:xmpp:ping";
184
188 public const string TimeNamespace = "urn:xmpp:time";
189
193 public const string DelayedDeliveryNamespace = "urn:xmpp:delay";
194
198 public const string OAuth1FormSignatureNamespace = "urn:xmpp:xdata:signature:oauth1";
199
203 public const string ReportingNamespace = "urn:xmpp:reporting:0";
204
208 public const string AbuseReportingNamespace = "urn:xmpp:reporting:reason:abuse:0";
209
213 public const string SpamReportingNamespace = "urn:xmpp:reporting:reason:spam:0";
214
218 public const string MailNamespace = "urn:xmpp:smtp";
219
223 public const string ContentNamespace = "urn:xmpp:content";
224
228 public const string MessagePushNamespace = "http://waher.se/Schema/PushNotification.xsd";
229
233 public const string AlternativesNamespace = "http://waher.se/Schema/Alternatives.xsd";
234
235 private static readonly RandomNumberGenerator rnd = RandomNumberGenerator.Create();
236 private static readonly Dictionary<CaseInsensitiveString, S2SRec> remoteDomainLookup = new Dictionary<CaseInsensitiveString, S2SRec>();
237 private static Scheduler scheduler = new Scheduler();
238 internal static readonly UTF8Encoding encoding = new UTF8Encoding(false, false);
239 private static readonly Cache<CaseInsensitiveString, PushNotificationToken> tokens =
240 new Cache<CaseInsensitiveString, PushNotificationToken>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromDays(62));
241 private static readonly Cache<string, PushNotificationRule> rules =
242 new Cache<string, PushNotificationRule>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromDays(62));
243
244 private readonly SmtpServer smtpServer = null;
245 private readonly HttpServer httpServer = null;
246 private RequestWhiteList requestWhiteList = null;
247 private LinkedList<TcpListener> c2sListeners = new LinkedList<TcpListener>();
248 private LinkedList<TcpListener> s2sListeners = new LinkedList<TcpListener>();
249 private Cache<CaseInsensitiveString, IClientConnection> clientConnections;
250 private Cache<CaseInsensitiveString, CaseInsensitiveString> services = new Cache<CaseInsensitiveString, CaseInsensitiveString>(int.MaxValue, TimeSpan.FromDays(1), TimeSpan.FromDays(1), true);
251 private readonly Dictionary<CaseInsensitiveString, List<IClientConnection>> connectionsPerBareJid = new Dictionary<CaseInsensitiveString, List<IClientConnection>>();
252 private readonly Dictionary<string, EventHandlerAsync<IqEventArgs>> iqGetHandlers = new Dictionary<string, EventHandlerAsync<IqEventArgs>>();
253 private readonly Dictionary<string, EventHandlerAsync<IqEventArgs>> iqSetHandlers = new Dictionary<string, EventHandlerAsync<IqEventArgs>>();
254 private readonly Dictionary<string, EventHandlerAsync<MessageEventArgs>> messageHandlers = new Dictionary<string, EventHandlerAsync<MessageEventArgs>>();
255 private readonly SortedDictionary<string, bool> features = new SortedDictionary<string, bool>();
256 private readonly SortedDictionary<CaseInsensitiveString, IComponent> componentsBySubdomain = new SortedDictionary<CaseInsensitiveString, IComponent>();
257 private readonly SortedDictionary<CaseInsensitiveString, IComponent> componentsByFulldomain = new SortedDictionary<CaseInsensitiveString, IComponent>();
258 private readonly Dictionary<string, PendingRequest> pendingRequestsById = new Dictionary<string, PendingRequest>();
259 private readonly SortedDictionary<DateTime, PendingRequest> pendingRequestsByTimeout = new SortedDictionary<DateTime, PendingRequest>();
260 private readonly IqResponses responses = new IqResponses(TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(10));
261 private readonly SortedDictionary<CaseInsensitiveString, S2sEndpointStatistics> s2sStatistics = new SortedDictionary<CaseInsensitiveString, S2sEndpointStatistics>();
262 private Dictionary<string, Statistic> stanzasPerStanzaType = new Dictionary<string, Statistic>();
263 private Dictionary<string, Statistic> stanzasPerFromDomain = new Dictionary<string, Statistic>();
264 private Dictionary<string, Statistic> stanzasPerToDomain = new Dictionary<string, Statistic>();
265 private Dictionary<string, Statistic> stanzasPerFromBareJid = new Dictionary<string, Statistic>();
266 private Dictionary<string, Statistic> stanzasPerToBareJid = new Dictionary<string, Statistic>();
267 private Dictionary<string, Statistic> stanzasPerNamespace = new Dictionary<string, Statistic>();
268 private Dictionary<string, Statistic> stanzasPerFqn = new Dictionary<string, Statistic>();
269 private readonly object statSync = new object();
270 private DateTime lastStat = DateTime.Now;
271 private IComponent[] componentsStatic = Array.Empty<IComponent>();
274 private Cache<string, IqResultEventArgs> shortTermCache = new Cache<string, IqResultEventArgs>(int.MaxValue, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
275 private SortedDictionary<DateTime, IS2SEndpoint> temporaryConnections = null;
276 private X509Certificate serverCertificate;
277 private readonly IXmppServerPersistenceLayer persistenceLayer;
278 private Accounts accounts;
279 private Timer secondTimer = null;
280 private readonly byte[] sha256DialbackSecret;
281 private readonly object synchObject = new object();
282 private readonly XmppAddress domainAddress;
283 private readonly Random gen = new Random();
284 private readonly CaseInsensitiveString domain;
285 private readonly CaseInsensitiveString[] alternativeDomains;
286 private readonly string serverName;
287 private readonly string serverVersion;
288 private readonly string serverOS;
289 private string domainSnifferPath = null;
290 private string clientSnifferPath = null;
291 private readonly bool encryptionRequired;
292 private bool disposed = false;
293 private readonly CommunicationLayer c2sSniffers = new CommunicationLayer(true);
294 private readonly CommunicationLayer s2sSniffers = new CommunicationLayer(true);
295 private readonly int defaultRetryTimeout = 5000;
296 private readonly int defaultNrRetries = 5;
297 private readonly int defaultMaxRetryTimeout = int.MaxValue;
298 private readonly bool defaultDropOff = true;
299 private long nrBytesRx = 0;
300 private long nrBytesTx = 0;
301 private long nrStanzas = 0;
302
303 #region Constructors
304
305 static XmppServer()
306 {
307 Log.Terminating += (Sender, e) =>
308 {
309 scheduler?.Dispose();
310 scheduler = null;
311 return Task.CompletedTask;
312 };
313 }
314
325 {
327 }
328
340 int ClientToServerPort, int ServerToServerPort, X509Certificate ServerCertificate, bool EncryptionRequired,
342 {
343 return Create(Domain, AlternativeDomains, new int[] { ClientToServerPort }, new int[] { ServerToServerPort },
345 }
346
358 int[] ClientToServerPorts, int[] ServerToServerPorts, X509Certificate ServerCertificate, bool EncryptionRequired,
360 {
361 return Create(Domain, AlternativeDomains, ClientToServerPorts, ServerToServerPorts, ServerCertificate, EncryptionRequired,
362 PersistenceLayer, null, null);
363 }
364
378 int[] ClientToServerPorts, int[] ServerToServerPorts, X509Certificate ServerCertificate, bool EncryptionRequired,
380 {
381 byte[] DialbackSecret = await PersistenceLayer.GetDialbackSecret();
382
383 if (mechanisms is null)
384 {
385 lock (synchObj)
386 {
387 if (first)
388 {
389 first = false;
390 Types.OnInvalidated += Types_OnInvalidated;
391 }
392 }
393
394 mechanisms = await GetMechanisms();
395 }
396
397 return new XmppServer(Domain, AlternativeDomains, ClientToServerPorts, ServerToServerPorts, ServerCertificate,
399 }
400
401 internal static IAuthenticationMechanism[] mechanisms = null;
402 private static readonly object synchObj = new object();
403 private static bool first = true;
404
405 private static async Task<IAuthenticationMechanism[]> GetMechanisms()
406 {
407 Dictionary<string, bool> MechanismsFound = new Dictionary<string, bool>();
408 List<IAuthenticationMechanism> Result = new List<IAuthenticationMechanism>();
409 ConstructorInfo CI;
410 IAuthenticationMechanism Mechanism;
411
413 {
414 if (T.IsAbstract)
415 continue;
416
417 CI = T.GetConstructor(Types.NoTypes);
418 if (CI is null)
419 continue;
420
421 try
422 {
423 Mechanism = (IAuthenticationMechanism)CI.Invoke(Types.NoParameters);
424
425 if (MechanismsFound.ContainsKey(Mechanism.Name))
426 throw new Exception("Authentication mechanism collision." + T.FullName + ": " + Mechanism.Name);
427
428 await Mechanism.Initialize();
429
430 MechanismsFound[Mechanism.Name] = true;
431 Result.Add(Mechanism);
432 }
433 catch (Exception ex)
434 {
435 Log.Exception(ex);
436 }
437 }
438
439 Result.Sort((m1, m2) => m2.Weight - m1.Weight);
440
441 return Result.ToArray();
442 }
443
444 private static async void Types_OnInvalidated(object Sender, EventArgs e)
445 {
446 try
447 {
448 mechanisms = await GetMechanisms();
449 }
450 catch (Exception ex)
451 {
452 Log.Exception(ex);
453 }
454 }
455
470 int[] ServerToServerPorts, X509Certificate ServerCertificate, bool EncryptionRequired,
472 {
473 this.persistenceLayer = PersistenceLayer;
474 this.serverCertificate = ServerCertificate;
475 this.encryptionRequired = EncryptionRequired;
476 this.domain = Domain;
477 this.alternativeDomains = AlternativeDomains ?? Array.Empty<CaseInsensitiveString>();
478 this.domainAddress = new XmppAddress(this.domain);
479 this.accounts = new Accounts(this);
480 this.smtpServer = SmtpServer;
481 this.httpServer = HttpServer;
482
483 this.httpServer?.Register(this.requestWhiteList = new RequestWhiteList(this));
484
485 this.sha256DialbackSecret = Hashes.ComputeSHA256Hash(DialbackSecret);
486
487 if (EncryptionRequired && this.serverCertificate is null)
488 throw new ArgumentException("Server Certificate must be provided, if encryption is required.", nameof(ServerCertificate));
489
490 this.clientConnections = new Cache<CaseInsensitiveString, IClientConnection>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromMinutes(2), true);
491 this.clientConnections.Removed += this.ClientConnections_Removed;
492
493 this.s2sEndpoints = new Cache<CaseInsensitiveString, IS2SEndpoint>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromDays(1), true);
494 this.s2sEndpoints.Removed += this.S2sEndpoints_Removed;
495
496 Assembly ThisAssembly = typeof(XmppServer).Assembly;
497 StackTrace Trace = new StackTrace();
498 StackFrame[] Frames = Trace.GetFrames();
499 StackFrame Frame;
500 MethodBase Method;
501 Assembly Assembly;
502 int i = 1;
503 int c = Frames.Length;
504
505 do
506 {
507 Frame = Frames[i++];
508 Method = Frame.GetMethod();
509 Assembly = Method.DeclaringType.Assembly;
510 }
511 while (Assembly == ThisAssembly);
512
513 AssemblyName Name = Assembly.GetName();
514 string Title = string.Empty;
515 string Product = string.Empty;
516 string AssemblyName = Name.Name;
517
518 foreach (object Attribute in Assembly.GetCustomAttributes())
519 {
520 if (Attribute is AssemblyTitleAttribute AssemblyTitleAttribute)
521 Title = AssemblyTitleAttribute.Title;
522 else if (Attribute is AssemblyProductAttribute AssemblyProductAttribute)
523 Product = AssemblyProductAttribute.Product;
524 }
525
526 if (!string.IsNullOrEmpty(Title))
527 this.serverName = Title;
528 else if (!string.IsNullOrEmpty(Product))
529 this.serverName = Product;
530 else
531 this.serverName = AssemblyName;
532
533 this.serverVersion = Name.Version.ToString();
534 this.serverOS = Environment.OSVersion.ToString();
535
536 this.RegisterIqGetHandler("query", RosterNamespace, this.RosterQuery, true);
537 this.RegisterIqSetHandler("query", RosterNamespace, this.RosterSet, false);
538 this.RegisterIqGetHandler("query", DiscoveryNamespace, this.DiscoveryQueryGet, true); // XEP-0030
539 this.RegisterIqGetHandler("query", DiscoveryItemsNamespace, this.DiscoveryQueryItemsGet, true); // XEP-0030
540 this.RegisterIqGetHandler("query", PrivateXmlStorageNamespace, this.PrivateXmlStorageGet, true); // XEP-0049
541 this.RegisterIqSetHandler("query", PrivateXmlStorageNamespace, this.PrivateXmlStorageSet, false); // XEP-0049
542 this.RegisterIqGetHandler("vCard", VCardNamespace, this.VCardGet, true); // XEP-0054
543 this.RegisterIqSetHandler("vCard", VCardNamespace, this.VCardSet, false); // XEP-0054
544 this.RegisterIqGetHandler("query", RegisterNamespace, this.RegisterGet, true); // XEP-0077
545 this.RegisterIqSetHandler("query", RegisterNamespace, this.RegisterSet, false); // XEP-0077
546 this.RegisterIqGetHandler("query", SoftwareVersionNamespace, this.SoftwareVersionGet, true); // XEP-0092
547 this.RegisterIqGetHandler("blocklist", BlockingCommandNamespace, this.BlockListGet, true); // XEP-0191
548 this.RegisterIqSetHandler("block", BlockingCommandNamespace, this.BlockSet, false); // XEP-0191
549 this.RegisterIqSetHandler("unblock", BlockingCommandNamespace, this.UnblockSet, false); // XEP-0191
550 this.RegisterIqGetHandler("ping", PingNamespace, this.PingGet, true); // XEP-0199
551 this.RegisterIqGetHandler("time", TimeNamespace, this.TimeGet, true); // XEP-0202
552 this.RegisterIqGetHandler("get", MailNamespace, this.GetMailContent, true); // TODO: Write Mail XEP
553 this.RegisterIqSetHandler("delete", MailNamespace, this.DeleteMailContent, false);
554 this.RegisterIqSetHandler("newToken", MessagePushNamespace, this.NewTokenHandler, true);
555 this.RegisterIqSetHandler("removeToken", MessagePushNamespace, this.RemoveTokenHandler, false);
556 this.RegisterIqSetHandler("clearRules", MessagePushNamespace, this.ClearRulesHandler, false);
557 this.RegisterIqSetHandler("addRule", MessagePushNamespace, this.AddRuleHandler, false);
558 this.RegisterIqSetHandler("removeRule", MessagePushNamespace, this.RemoveRuleHandler, false);
559 this.features[OfflineMessagesNamespace] = true; // XEP-0160
560 this.features[OAuth1FormSignatureNamespace] = true; // XEP-0348
561 this.features[ReportingNamespace] = true; // XEP-0377
562 this.features[SpamReportingNamespace] = true; // XEP-0377
563 this.features[AbuseReportingNamespace] = true; // XEP-0377
564
565 this.Initialize(ClientToServerPorts, ServerToServerPorts);
566 }
567
568 private void Initialize(int[] ClientToServerPorts, int[] ServerToServerPorts)
569 {
570 try
571 {
572 TcpListener Listener;
573
574 foreach (NetworkInterface Interface in NetworkInterface.GetAllNetworkInterfaces())
575 {
576 if (Interface.OperationalStatus != OperationalStatus.Up)
577 continue;
578
579 IPInterfaceProperties Properties = Interface.GetIPProperties();
580
581 foreach (UnicastIPAddressInformation UnicastAddress in Properties.UnicastAddresses)
582 {
583 if ((UnicastAddress.Address.AddressFamily == AddressFamily.InterNetwork && Socket.OSSupportsIPv4) ||
584 (UnicastAddress.Address.AddressFamily == AddressFamily.InterNetworkV6 && Socket.OSSupportsIPv6))
585 {
586 if (!(ClientToServerPorts is null))
587 {
588 foreach (int C2sPort in ClientToServerPorts)
589 {
590 try
591 {
592 this.c2sSniffers.Information("Opening port " + C2sPort.ToString() + " on " + UnicastAddress.Address.ToString() + ".");
593
594 Listener = new TcpListener(UnicastAddress.Address, C2sPort);
595 Listener.Start(DefaultConnectionBacklog);
596 Listener.BeginAcceptTcpClient(this.AcceptTcpClientCallback, new object[] { Listener, false });
597 this.c2sListeners.AddLast(Listener);
598
599 this.c2sSniffers.Information("Port " + C2sPort.ToString() + " on " + UnicastAddress.Address.ToString() + " opened.");
600 }
601 catch (Exception ex)
602 {
603 Log.Exception(ex, UnicastAddress.Address.ToString() + ":" + C2sPort);
604 }
605 }
606 }
607
608 if (!(ServerToServerPorts is null))
609 {
610 foreach (int S2sPort in ServerToServerPorts)
611 {
612 try
613 {
614 this.s2sSniffers.Information("Opening port " + S2sPort.ToString() + " on " + UnicastAddress.Address.ToString() + ".");
615
616 Listener = new TcpListener(UnicastAddress.Address, S2sPort);
617 Listener.Start(DefaultConnectionBacklog);
618 Listener.BeginAcceptTcpClient(this.AcceptTcpClientCallback, new object[] { Listener, true });
619 this.s2sListeners.AddLast(Listener);
620
621 this.s2sSniffers.Information("Port " + S2sPort.ToString() + " on " + UnicastAddress.Address.ToString() + " opened.");
622 }
623 catch (Exception ex)
624 {
625 Log.Exception(ex, UnicastAddress.Address.ToString() + ":" + S2sPort);
626 }
627 }
628 }
629 }
630 }
631 }
632
633 this.secondTimer = new Timer(this.SecondTimerCallback, null, 1000, 1000);
634
635 if (!(this.smtpServer is null))
636 this.smtpServer.MessageReceived += this.SmtpServer_MessageReceived;
637 }
638 catch (Exception ex)
639 {
640 Log.Exception(ex);
641 }
642 }
643
647 public Accounts Accounts => this.accounts;
648
652 public CommunicationLayer C2sSniffers => this.c2sSniffers;
653
657 public CommunicationLayer S2sSniffers => this.s2sSniffers;
658
662 public static Scheduler Scheduler => scheduler;
663
669 public string NewId(int NrBytes)
670 {
671 return Hashes.BinaryToString(GetRandomNumbers(NrBytes));
672 }
673
679 public static byte[] GetRandomNumbers(int NrBytes)
680 {
681 byte[] Result = new byte[NrBytes];
682
683 lock (rnd)
684 {
685 rnd.GetBytes(Result);
686 }
687
688 return Result;
689 }
690
696 public string GetRandomHexString(int NrBytes)
697 {
698 return Hashes.BinaryToString(GetRandomNumbers(NrBytes));
699 }
700
701 private async Task ClientConnections_Removed(object Sender, CacheItemEventArgs<CaseInsensitiveString, IClientConnection> e)
702 {
703 this.ConnectionClosed(e.Value);
704
705 await this.ClientConnectionRemoved.Raise(this, new ClientConnectionEventArgs(e.Key, e.Value));
706 await e.Value.DisposeAsync();
707 }
708
716 {
717 lock (this.s2sStatistics)
718 {
719 return this.s2sStatistics.TryGetValue(Endpoint, out Stat);
720 }
721 }
722
730 {
732
733 lock (this.s2sStatistics)
734 {
735 if (!this.s2sStatistics.TryGetValue(Endpoint, out Result))
736 {
737 Result = new S2sEndpointStatistics(Endpoint, Type);
738 this.s2sStatistics[Endpoint] = Result;
739 }
740 }
741
742 return Result;
743 }
744
745 internal void S2sEndpointDisposed(IS2SEndpoint Endpoint)
746 {
748
749 if (!(this.s2sEndpoints is null) &&
750 !(Endpoint is null) &&
752 this.s2sEndpoints.TryGetValue(Domain, out IS2SEndpoint Endpoint2) &&
753 Endpoint == Endpoint2)
754 {
755 this.s2sEndpoints?.Remove(Domain);
756 }
757 }
758
759 private async Task S2sEndpoints_Removed(object Sender, CacheItemEventArgs<CaseInsensitiveString, IS2SEndpoint> e)
760 {
761#if LogToWebHookTester
762 // TODO: Remove
763 _ = Task.Run(async () =>
764 {
765 try
766 {
767 await InternetContent.PostAsync(new Uri("https://lab.tagroot.io/WebHookTester/Show.ws?ID=" + this.domain.Value),
768 new Dictionary<string, object>()
769 {
770 { "id", e.Value.Id.ToString() },
771 { "event", "S2sEndpoints_Removed" },
772 { "remote", e.Key.Value },
773 { "value", JSON.Encode(e.Value, true) },
774 { "reason", e.Reason }
775 },
776 new KeyValuePair<string, string>("Accept", "application/json"));
777 }
778 catch (Exception ex)
779 {
780 Log.Exception(ex);
781 }
782 });
783#endif
784 Log.Informational("Removing S2S connection.",
785 e.Value.RemoteDomain, string.Empty, "XmppCloseS2s",
786 new KeyValuePair<string, object>("Reason", e.Reason));
787
788 EndpointStatistics Stat = this.GetS2sStatistics(e.Key, e.Value.Type);
789 Stat.EndpointDisconnected(e.Value, (long)((DateTime.Now - e.Value.Connected).TotalMilliseconds + 0.5));
790
792 {
793 XmppS2SEndpoint.OnStateChanged -= this.Endpoint_OnStateChanged;
795 }
796
797 await e.Value.DisposeAsync("S2S connection removed: " + e.Reason.ToString());
798
799 if (e.Reason != RemovedReason.Replaced)
800 await this.ServerConnectionRemoved.Raise(this, new ServerConnectionEventArgs(e.Key, e.Value));
801 }
802
806 public CaseInsensitiveString[] RemoteConnections
807 {
808 get
809 {
810 return this.s2sEndpoints.GetKeys();
811 }
812 }
813
817 public int NrClientConnections
818 {
819 get => this.clientConnections.Count;
820 }
821
827 {
828 IClientConnection[] Connections = this.clientConnections.GetValues();
829
830 Array.Sort(Connections, (c1, c2) =>
831 {
832 return c1.FullJid.CompareTo(c2.FullJid);
833 });
834
835 return Connections;
836 }
837
844 public bool TryGetClientConnection(string FullJID, out IClientConnection Connection)
845 {
846 return this.clientConnections.TryGetValue(FullJID, out Connection);
847 }
848
855 public bool TryGetClientConnections(string BareJID, out IClientConnection[] Connections)
856 {
857 lock (this.connectionsPerBareJid)
858 {
859 if (this.connectionsPerBareJid.TryGetValue(BareJID, out List<IClientConnection> Connections2))
860 {
861 Connections = Connections2.ToArray();
862 return true;
863 }
864 }
865
866 Connections = null;
867 return false;
868 }
869
873 public int NrServerConnections
874 {
875 get => this.s2sEndpoints.Count;
876 }
877
883 {
884 lock (this.s2sStatistics)
885 {
886 S2sEndpointStatistics[] Result = new S2sEndpointStatistics[this.s2sStatistics.Count];
887 this.s2sStatistics.Values.CopyTo(Result, 0);
888 return Result;
889 }
890 }
891
898 public bool IsServerDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
899 {
900 if (Domain == this.domain)
901 return true;
902
903 if (IncludeAlternativeDomains)
904 {
905 foreach (CaseInsensitiveString s in this.alternativeDomains)
906 {
907 if (s == Domain)
908 return true;
909 }
910 }
911
912 if (!this.HasDomain && CaseInsensitiveString.IsNullOrEmpty(Domain))
913 return true;
914
915 return false;
916 }
917
922 {
923 get => this.domain;
924 }
925
929 public CaseInsensitiveString[] AlternativeDomains
930 {
931 get => this.alternativeDomains;
932 }
933
937 public X509Certificate ServerCertificate
938 {
939 get => this.serverCertificate;
940 }
941
946 public void UpdateCertificate(X509Certificate ServerCertificate)
947 {
948 this.serverCertificate = ServerCertificate;
949 }
950
954 public bool EncryptionRequired
955 {
956 get => this.encryptionRequired;
957 }
958
963 public string DomainSnifferPath
964 {
965 get => this.domainSnifferPath;
966 set => this.domainSnifferPath = value;
967 }
968
973 public string ClientSnifferPath
974 {
975 get => this.clientSnifferPath;
976 set => this.clientSnifferPath = value;
977 }
978
982 public IXmppServerPersistenceLayer PersistenceLayer => this.persistenceLayer;
983
987 public void Dispose()
988 {
989 this.disposed = true;
990
991 if (!(this.httpServer is null))
992 {
993 this.httpServer.Unregister(this.requestWhiteList);
994 this.requestWhiteList = null;
995 }
996
997 if (!(this.smtpServer is null))
998 this.smtpServer.MessageReceived -= this.SmtpServer_MessageReceived;
999
1000 this.secondTimer?.Dispose();
1001 this.secondTimer = null;
1002
1003 this.s2sEndpoints?.Clear();
1004 this.s2sEndpoints?.Dispose();
1005 this.s2sEndpoints = null;
1006
1007 this.clientConnections?.Clear();
1008 this.clientConnections?.Dispose();
1009 this.clientConnections = null;
1010
1011 this.sniffers?.Clear();
1012 this.sniffers?.Dispose();
1013 this.sniffers = null;
1014
1015 this.shortTermCache?.Clear();
1016 this.shortTermCache?.Dispose();
1017 this.shortTermCache = null;
1018
1019 if (!(this.componentsBySubdomain is null))
1020 {
1021 foreach (IComponent Component in this.componentsStatic)
1023
1024 this.componentsBySubdomain.Clear();
1025 this.componentsByFulldomain.Clear();
1026 this.componentsStatic = Array.Empty<IComponent>();
1027 }
1028
1029 this.accounts?.Dispose();
1030 this.accounts = null;
1031
1032 this.services?.Dispose();
1033 this.services = null;
1034
1035 this.responses.Dispose();
1036
1037 if (!(this.c2sListeners is null))
1038 {
1039 LinkedList<TcpListener> Listeners = this.c2sListeners;
1040 this.c2sListeners = null;
1041
1042 foreach (TcpListener Listener in Listeners)
1043 Listener.Stop();
1044 }
1045
1046 if (!(this.s2sListeners is null))
1047 {
1048 LinkedList<TcpListener> Listeners = this.s2sListeners;
1049 this.s2sListeners = null;
1050
1051 foreach (TcpListener Listener in Listeners)
1052 Listener.Stop();
1053 }
1054
1055 if (!(this.c2sSniffers is null))
1056 {
1057 foreach (ISniffer Sniffer in this.c2sSniffers)
1058 (Sniffer as IDisposable)?.Dispose();
1059 }
1060
1061 if (!(this.s2sSniffers is null))
1062 {
1063 foreach (ISniffer Sniffer in this.s2sSniffers)
1064 (Sniffer as IDisposable)?.Dispose();
1065 }
1066 }
1067
1071 public bool Disposed => this.disposed;
1072
1076 public int[] OpenC2SPorts
1077 {
1078 get
1079 {
1080 return this.GetOpenPorts(this.c2sListeners);
1081 }
1082 }
1083
1087 public int[] OpenS2SPorts
1088 {
1089 get
1090 {
1091 return this.GetOpenPorts(this.s2sListeners);
1092 }
1093 }
1094
1098 public IComponent[] Components => this.componentsStatic;
1099
1100 private int[] GetOpenPorts(LinkedList<TcpListener> Listeners)
1101 {
1102 SortedDictionary<int, bool> Open = new SortedDictionary<int, bool>();
1103
1104 if (!(Listeners is null))
1105 {
1106 IPEndPoint IPEndPoint;
1107
1108 foreach (TcpListener Listener in Listeners)
1109 {
1110 IPEndPoint = Listener.LocalEndpoint as IPEndPoint;
1111 if (!(IPEndPoint is null))
1112 Open[IPEndPoint.Port] = true;
1113 }
1114 }
1115
1116 int[] Result = new int[Open.Count];
1117 Open.Keys.CopyTo(Result, 0);
1118
1119 return Result;
1120 }
1121
1122 #endregion
1123
1124 #region Components
1125
1132 {
1133 lock (this.synchObject)
1134 {
1135 if (this.componentsBySubdomain.ContainsKey(Component.Subdomain))
1136 return false;
1137
1138 this.componentsBySubdomain[Component.Subdomain] = Component;
1139 this.componentsByFulldomain[Component.Subdomain + "." + this.domain] = Component;
1140
1141 foreach (CaseInsensitiveString cis in this.alternativeDomains)
1142 this.componentsByFulldomain[Component.Subdomain + "." + cis] = Component;
1143
1144 this.RebuildComponentsStaticLocked();
1145 }
1146
1147 return true;
1148 }
1149
1150 private void RebuildComponentsStaticLocked()
1151 {
1152 IComponent[] Static = new IComponent[this.componentsBySubdomain.Count];
1153 this.componentsBySubdomain.Values.CopyTo(Static, 0);
1154
1155 this.componentsStatic = Static;
1156 }
1157
1164 {
1165 bool Result = false;
1166
1167 lock (this.synchObject)
1168 {
1169 if (this.componentsBySubdomain.TryGetValue(Component.Subdomain, out IComponent Component2) &&
1170 Component == Component2)
1171 {
1172 Result = this.componentsBySubdomain.Remove(Component.Subdomain);
1173
1174 if (Result)
1175 {
1176 this.componentsByFulldomain.Remove(Component.Subdomain + "." + this.domain);
1177
1178 foreach (CaseInsensitiveString cis in this.alternativeDomains)
1179 this.componentsByFulldomain.Remove(Component.Subdomain + "." + cis);
1180
1181 this.RebuildComponentsStaticLocked();
1182 }
1183 }
1184 }
1185
1186 return Result;
1187 }
1188
1189 #endregion
1190
1191 #region Connections
1192
1193 private void AcceptTcpClientCallback(IAsyncResult ar)
1194 {
1195 try
1196 {
1197 if (this.disposed ||
1198 !(ar?.AsyncState is object[] P) ||
1199 P.Length < 2 ||
1200 !(P[0] is TcpListener Listener) ||
1201 !(P[1] is bool S2S) ||
1203 {
1204 return;
1205 }
1206
1207 try
1208 {
1209 TcpClient Client = Listener.EndAcceptTcpClient(ar);
1210 ICommunicationLayer ComLayer;
1211
1212 TextTcpClient TextTcpClient = new TextTcpClient(Client, encoding, false, true);
1213 TextTcpClient.Bind(true);
1214
1215 if (S2S)
1216 {
1218 this.serverCertificate, this, false, new InMemorySniffer("XMPP S2S In-memory Sniffer"));
1219 ComLayer = Endpoint;
1220
1221 Task.Run(async () =>
1222 {
1223 await this.S2sEndpointCreated.Raise(this, new XmppS2SEndpointEventArgs(Endpoint));
1224 });
1225 }
1226 else
1227 {
1228 ISniffer[] Sniffers;
1229
1230 if (!string.IsNullOrEmpty(this.clientSnifferPath))
1231 Sniffers = new ISniffer[] { new InMemorySniffer("XMPP S2S In-memory Sniffer") };
1232 else if (this.c2sSniffers.HasSniffers)
1233 Sniffers = this.c2sSniffers.Sniffers;
1234 else
1235 Sniffers = Array.Empty<ISniffer>();
1236
1237 ComLayer = new XmppClientConnection(TextTcpClient, this, Sniffers);
1238 }
1239
1241
1242 ComLayer.Information("Connection accepted from " + TextTcpClient.RemoteEndPoint + ".");
1243 }
1244 finally
1245 {
1246 if (!this.disposed)
1247 Listener.BeginAcceptTcpClient(this.AcceptTcpClientCallback, P);
1248 }
1249 }
1250 catch (SocketException)
1251 {
1252 // Ignore
1253 }
1254 catch (ObjectDisposedException)
1255 {
1256 // Ignore
1257 }
1258 catch (NullReferenceException)
1259 {
1260 // Ignore
1261 }
1262 catch (Exception ex)
1263 {
1264 if (this.c2sListeners is null)
1265 return;
1266
1267 Log.Exception(ex);
1268 }
1269 }
1270
1271 internal string GetTransformPath(bool S2S)
1272 {
1273 foreach (ISniffer Sniffer in S2S ? this.s2sSniffers.Sniffers : this.c2sSniffers.Sniffers)
1274 {
1275 if (Sniffer is XmlFileSniffer XmlFileSniffer)
1277 }
1278
1279 return null;
1280 }
1281
1290 public string GetDialbackKey(string ReceivingServer, string OriginatingServer, string ReceivingStreamId)
1291 {
1292 StringBuilder sb = new StringBuilder();
1293
1294 sb.Append(ReceivingServer);
1295 sb.Append(' ');
1296 sb.Append(OriginatingServer);
1297 sb.Append(' ');
1298 sb.Append(ReceivingStreamId);
1299
1300 byte[] Bin = Encoding.UTF8.GetBytes(sb.ToString());
1301 string Key = Hashes.ComputeHMACSHA256HashString(this.sha256DialbackSecret, Bin);
1302
1303 return Key;
1304 }
1305
1306 #endregion
1307
1308 #region Accounts
1309
1315 internal Task<IAccount> GetAccount(CaseInsensitiveString UserName)
1316 {
1317 return this.persistenceLayer.GetAccount(UserName);
1318 }
1319
1326 internal bool TryGetConnection(CaseInsensitiveString FullJid, out IClientConnection Connection)
1327 {
1328 return this.clientConnections.TryGetValue(FullJid, out Connection);
1329 }
1330
1336 internal bool TouchClientConnection(CaseInsensitiveString FullJid)
1337 {
1338 return this.clientConnections?.ContainsKey(FullJid) ?? false;
1339 }
1340
1346 internal bool TouchServerConnection(CaseInsensitiveString Domain)
1347 {
1348 return this.s2sEndpoints?.ContainsKey(Domain) ?? false;
1349 }
1350
1357 internal async Task<bool> RegisterFullJid(CaseInsensitiveString FullJid, IClientConnection Connection)
1358 {
1359 if (this.clientConnections.TryGetValue(FullJid, out IClientConnection Connection2))
1360 {
1361 if (!Connection2.CheckLive())
1362 this.clientConnections.Remove(FullJid);
1363 else
1364 return false;
1365 }
1366
1367 this.clientConnections[FullJid] = Connection;
1368
1369 CaseInsensitiveString BareJid = GetBareJID(FullJid);
1370
1371 lock (this.connectionsPerBareJid)
1372 {
1373 if (!this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1374 {
1375 Connections = new List<IClientConnection>();
1376 this.connectionsPerBareJid[BareJid] = Connections;
1377 }
1378
1379 Connections.Add(Connection);
1380 }
1381
1382 await this.ClientConnectionAdded.Raise(this, new ClientConnectionEventArgs(FullJid, Connection));
1383
1384 return true;
1385 }
1386
1390 public event EventHandlerAsync<ClientConnectionEventArgs> ClientConnectionAdded = null;
1391
1395 public event EventHandlerAsync<ClientConnectionEventArgs> ClientConnectionUpdated = null;
1396
1400 public event EventHandlerAsync<ClientConnectionEventArgs> ClientConnectionRemoved = null;
1401
1405 public event EventHandlerAsync<ServerConnectionEventArgs> ServerConnectionAdded = null;
1406
1410 public event EventHandlerAsync<ServerConnectionEventArgs> ServerConnectionUpdated = null;
1411
1415 public event EventHandlerAsync<ServerConnectionEventArgs> ServerConnectionRemoved = null;
1416
1423 internal async Task<CaseInsensitiveString> RegisterBareJid(CaseInsensitiveString BareJid, IClientConnection Connection)
1424 {
1425 CaseInsensitiveString FullJid;
1426
1427 do
1428 {
1429 FullJid = BareJid + "/" + this.NewId(16);
1430 }
1431 while (!this.disposed && this.clientConnections.ContainsKey(FullJid));
1432
1433 if (!this.disposed)
1434 {
1435 this.clientConnections[FullJid] = Connection;
1436
1437 lock (this.connectionsPerBareJid)
1438 {
1439 if (!this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1440 {
1441 Connections = new List<IClientConnection>();
1442 this.connectionsPerBareJid[BareJid] = Connections;
1443 }
1444
1445 Connections.Add(Connection);
1446 }
1447
1448 await this.ClientConnectionAdded.Raise(this, new ClientConnectionEventArgs(FullJid, Connection));
1449 }
1450
1451 return FullJid;
1452 }
1453
1458 internal void ConnectionClosed(IClientConnection Connection)
1459 {
1460 CaseInsensitiveString FullJid = Connection.FullJid;
1461 CaseInsensitiveString BareJid = Connection.BareJid;
1462
1463 this.clientConnections?.Remove(FullJid);
1464
1465 lock (this.connectionsPerBareJid)
1466 {
1467 if (this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1468 {
1469 int i, c = Connections.Count;
1470
1471 for (i = 0; i < c; i++)
1472 {
1473 if (Connections[i].FullJid == FullJid)
1474 {
1475 Connections.RemoveAt(i);
1476 c--;
1477
1478 if (c == 0)
1479 this.connectionsPerBareJid.Remove(BareJid);
1480
1481 break;
1482 }
1483 }
1484 }
1485 }
1486 }
1487
1488 internal IClientConnection[] GetClientConnections(CaseInsensitiveString BareJid)
1489 {
1490 lock (this.connectionsPerBareJid)
1491 {
1492 if (this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1493 return Connections.ToArray();
1494 else
1495 return null;
1496 }
1497 }
1498
1505 {
1506 int i = JID.IndexOf('/');
1507 if (i > 0)
1508 return JID.Substring(0, i);
1509 else
1510 return JID;
1511 }
1512
1518 public Task<DateTime?> GetEarliestLoginOpportunity(IClientConnection Connection)
1519 {
1520 if (Connection is null)
1521 return null;
1522
1523 LoginAuditor Auditor = this.persistenceLayer.Auditor;
1524
1525 if (Auditor is null)
1526 return Task.FromResult<DateTime?>(null);
1527 else
1528 return Auditor.GetEarliestLoginOpportunity(Connection.RemoteEndPoint, Connection.Protocol);
1529 }
1530
1531 internal async Task<bool> Authenticate(string Mechanism, SslStream SslStream, IClientConnection Connection, string Data)
1532 {
1533 DateTime? Next = await this.GetEarliestLoginOpportunity(Connection);
1534
1535 if (Next.HasValue)
1536 {
1537 StringBuilder sb = new StringBuilder();
1538 DateTime TP = Next.Value;
1539 DateTime Today = DateTime.Today;
1540
1541 if (Next.Value == DateTime.MaxValue)
1542 {
1543 sb.Append("This endpoint (");
1544 sb.Append(Connection.RemoteEndPoint);
1545 sb.Append(") has been blocked from the system.");
1546 }
1547 else
1548 {
1549 sb.Append("Too many failed login attempts in a row registered. Try again after ");
1550 sb.Append(TP.ToLongTimeString());
1551
1552 if (TP.Date != Today)
1553 {
1554 if (TP.Date == Today.AddDays(1))
1555 sb.Append(" tomorrow");
1556 else
1557 {
1558 sb.Append(", ");
1559 sb.Append(TP.ToShortDateString());
1560 }
1561 }
1562
1563 sb.Append(". Remote Endpoint: ");
1564 sb.Append(Connection.RemoteEndPoint);
1565 }
1566
1567 return await Connection.SaslErrorTemporaryAuthFailure(sb.ToString(), "en");
1568 }
1569
1570 bool Found = false;
1571
1572 foreach (IAuthenticationMechanism M in mechanisms)
1573 {
1574 if (M.Name == Mechanism)
1575 {
1576 if (!M.Allowed(SslStream))
1577 return await Connection.SaslErrorMechanismTooWeak();
1578
1579 Connection.SetMechanism(M);
1580 Found = true;
1581
1582 try
1583 {
1584 bool? AuthResult = await M.AuthenticationRequest(Data, Connection, this.PersistenceLayer);
1585 if (AuthResult.HasValue)
1586 {
1587 if (AuthResult.Value)
1588 {
1589 if (!await Connection.BeginWrite("<success xmlns='" + SaslNamespace + "'/>", null, null))
1590 return false;
1591
1592 Connection.ResetState(true);
1593 }
1594 }
1595 }
1596 catch (Exception ex)
1597 {
1598 Connection.Exception(ex);
1599 if (!await Connection.StreamErrorInvalidXml())
1600 return false;
1601 }
1602 break;
1603 }
1604 }
1605
1606 if (!Found && !await Connection.SaslErrorInvalidMechanism())
1607 return false;
1608
1609 return true;
1610 }
1611
1612 #endregion
1613
1614 #region Recipients
1615
1622 public async Task<IRecipient> TryGetRecipient(XmppAddress To, XmppAddress From)
1623 {
1624 try
1625 {
1626 CaseInsensitiveString ToCI = To?.Address;
1627
1628 if (this.disposed)
1629 return null;
1630
1631 if (CaseInsensitiveString.IsNullOrEmpty(ToCI) || this.IsServerDomain(ToCI, true))
1632 return this;
1633
1634 if (this.clientConnections.TryGetValue(ToCI, out IClientConnection Connection))
1635 return Connection;
1636
1637 if (this.IsServerDomain(To.Domain, true))
1638 {
1639 if (To.IsFullJID)
1640 return null;
1641
1642 if (To.HasAccount)
1643 {
1644 IAccount Account = await this.persistenceLayer.GetAccount(To.Account);
1645 if (Account is null)
1646 return null;
1647 else
1648 return new AccountRecipient(this.accounts, Account);
1649 }
1650 }
1651
1653
1654 lock (this.synchObject)
1655 {
1656 if (!this.componentsByFulldomain.TryGetValue(To.Domain, out Component))
1657 Component = null;
1658 }
1659
1660 if (!(Component is null))
1661 {
1663 return Component;
1664 else
1665 return null;
1666 }
1667
1668 IS2SEndpoint Endpoint = await this.GetS2sEndpoint(From.Domain, To.Domain,
1669 true, From.Address + " wants to send a stanza to " + To.Address);
1670
1671 return Endpoint;
1672 }
1673 catch (Exception ex)
1674 {
1675 Log.Exception(ex);
1676 return null;
1677 }
1678 }
1679
1680 #endregion
1681
1682 #region Stanzas
1683
1691 public void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler, bool PublishNamespaceAsFeature)
1692 {
1693 this.RegisterIqHandler(this.iqGetHandlers, LocalName, Namespace, Handler, PublishNamespaceAsFeature);
1694 }
1695
1703 public void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler, bool PublishNamespaceAsFeature)
1704 {
1705 this.RegisterIqHandler(this.iqSetHandlers, LocalName, Namespace, Handler, PublishNamespaceAsFeature);
1706 }
1707
1708 private void RegisterIqHandler(Dictionary<string, EventHandlerAsync<IqEventArgs>> Handlers, string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler,
1709 bool PublishNamespaceAsFeature)
1710 {
1711 string Key = LocalName + " " + Namespace;
1712
1713 lock (this.synchObject)
1714 {
1715 if (Handlers.ContainsKey(Key))
1716 throw new ArgumentException("Handler already registered.", nameof(LocalName));
1717
1718 Handlers[Key] = Handler;
1719
1720 if (PublishNamespaceAsFeature)
1721 this.features[Namespace] = true;
1722 }
1723 }
1724
1732 public void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync<MessageEventArgs> Handler, bool PublishNamespaceAsFeature)
1733 {
1734 string Key = LocalName + " " + Namespace;
1735
1736 lock (this.synchObject)
1737 {
1738 if (this.messageHandlers.ContainsKey(Key))
1739 throw new ArgumentException("Handler already registered.", nameof(LocalName));
1740
1741 this.messageHandlers[Key] = Handler;
1742
1743 if (PublishNamespaceAsFeature)
1744 this.features[Namespace] = true;
1745 }
1746 }
1747
1756 public bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler, bool RemoveNamespaceAsFeature)
1757 {
1758 return this.UnregisterIqHandler(this.iqGetHandlers, LocalName, Namespace, Handler, RemoveNamespaceAsFeature);
1759 }
1760
1769 public bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler, bool RemoveNamespaceAsFeature)
1770 {
1771 return this.UnregisterIqHandler(this.iqSetHandlers, LocalName, Namespace, Handler, RemoveNamespaceAsFeature);
1772 }
1773
1774 private bool UnregisterIqHandler(Dictionary<string, EventHandlerAsync<IqEventArgs>> Handlers, string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler,
1775 bool RemoveNamespaceAsFeature)
1776 {
1777 string Key = LocalName + " " + Namespace;
1778
1779 lock (this.synchObject)
1780 {
1781 if (!Handlers.TryGetValue(Key, out EventHandlerAsync<IqEventArgs> h))
1782 return false;
1783
1784 if (h != Handler)
1785 return false;
1786
1787 Handlers.Remove(Key);
1788
1789 if (RemoveNamespaceAsFeature)
1790 this.features.Remove(Namespace);
1791 }
1792
1793 return true;
1794 }
1795
1804 public bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync<MessageEventArgs> Handler, bool RemoveNamespaceAsFeature)
1805 {
1806 string Key = LocalName + " " + Namespace;
1807
1808 lock (this.synchObject)
1809 {
1810 if (!this.messageHandlers.TryGetValue(Key, out EventHandlerAsync<MessageEventArgs> h))
1811 return false;
1812
1813 if (h != Handler)
1814 return false;
1815
1816 this.messageHandlers.Remove(Key);
1817
1818 if (RemoveNamespaceAsFeature)
1819 this.features.Remove(Namespace);
1820 }
1821
1822 return true;
1823 }
1824
1825 internal async Task<bool> ProcessIq(string Id, XmppAddress To, XmppAddress From, string Type, string Language, Stanza Stanza, ISender Sender)
1826 {
1827 IRecipient Recipient = await this.TryGetRecipient(To, From);
1828
1829 if (Recipient is null)
1830 {
1831 if (Sender is null)
1832 return true;
1833 else
1834 {
1835 string Message = "Recipient not found: " + To.Address;
1836
1837 Log.Warning(Message,
1838 new KeyValuePair<string, object>("From", From?.Address),
1839 new KeyValuePair<string, object>("To", To?.Address));
1840
1841 return !(await Sender.IqErrorItemNotFound(Id, From, To, Message, "en") is null);
1842 }
1843 }
1844 else if (Recipient is IClientConnection ToConnection &&
1845 await this.persistenceLayer.IsBlocked(From.BareJid, ToConnection.BareJid))
1846 {
1847 return !(await Sender.IqErrorServiceUnavailable(Id, From, To, string.Empty, string.Empty) is null);
1848 }
1849 else
1850 return await Recipient.IQ(Type, Id, To, From, Language, Stanza, Sender);
1851 }
1852
1863 public async Task<bool> IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
1864 {
1865 Dictionary<string, EventHandlerAsync<IqEventArgs>> Handlers;
1866 EventHandlerAsync<IqEventArgs> h = null;
1867 IqResponse Response;
1868 string Counter;
1869 string Key;
1870 bool Blocked = await this.persistenceLayer.IsBlocked(From.BareJid, To.BareJid);
1871
1872 switch (Type)
1873 {
1874 case "get":
1875 if (this.responses.TryGet(From.Address, Id, true, out Response, out bool Created))
1876 {
1877 if (!Created)
1878 {
1879 KeyValuePair<string, bool> P = await Response.GetResponse();
1880
1881 if (P.Value)
1882 return await Sender.IqResult(Id, From, To, P.Key);
1883 else
1884 return await Sender.IqError(Id, From, To, P.Key);
1885 }
1886 }
1887
1888 Handlers = this.iqGetHandlers;
1889 Counter = "XMPP.Server.Get";
1890 break;
1891
1892 case "set":
1893 if (this.responses.TryGet(From.Address, Id, true, out Response, out Created))
1894 {
1895 if (!Created)
1896 {
1897 KeyValuePair<string, bool> P = await Response.GetResponse();
1898
1899 if (P.Value)
1900 return await Sender.IqResult(Id, From, To, P.Key);
1901 else
1902 return await Sender.IqError(Id, From, To, P.Key);
1903 }
1904 }
1905
1906 Handlers = this.iqSetHandlers;
1907 Counter = "XMPP.Server.Set";
1908 break;
1909
1910 case "result":
1911 case "error":
1912 if (Blocked)
1913 return true;
1914
1915 return await this.ProcessResponse(Type, Id, To, From, Language, true, false, Stanza, Sender);
1916
1917 default:
1918 if (Sender is null)
1919 return true;
1920 else
1921 return !(await Sender.IqErrorBadRequest(Id, From, To, "Invalid type.", "en") is null);
1922 }
1923
1924 IqEventArgs e = new IqEventArgs(Sender, Stanza.StanzaElement, Id, Type, To, From, Language, Response);
1925
1926 if (Blocked)
1927 {
1928 if (Sender is null)
1929 {
1930 Response.SetResult(string.Empty, false);
1931 return false;
1932 }
1933 else
1934 {
1935 string ErrorXml = await Sender.IqErrorServiceUnavailable(Id, From, To, string.Empty, string.Empty);
1936 Response.SetResult(ErrorXml, false);
1937 return !(ErrorXml is null);
1938 }
1939 }
1940
1941 lock (this.synchObject)
1942 {
1943 foreach (XmlNode N in Stanza.StanzaElement.ChildNodes)
1944 {
1945 if (!(N is XmlElement E))
1946 continue;
1947
1948 Key = E.LocalName + " " + E.NamespaceURI;
1949 if (Handlers.TryGetValue(Key, out h))
1950 {
1951 Counter += "." + E.LocalName;
1952 e.Query = E;
1953 break;
1954 }
1955 else
1956 h = null;
1957 }
1958 }
1959
1960 if (h is null)
1961 {
1962 if (Sender is null)
1963 {
1964 Response.SetResult(string.Empty, false);
1965 return false;
1966 }
1967 else
1968 {
1969 string ErrorXml = await Sender.IqError(Id, From, To, "cancel", "<feature-not-implemented xmlns='" + StanzaNamespace + "'/>", string.Empty, string.Empty);
1970 Response.SetResult(ErrorXml, false);
1971 return !(ErrorXml is null);
1972 }
1973 }
1974
1975 try
1976 {
1977 await RuntimeCounters.IncrementCounter(Counter);
1978 await h(this, e);
1979 return true;
1980 }
1981 catch (Exception ex)
1982 {
1983 if (Sender is null)
1984 {
1985 Response.SetResult(string.Empty, false);
1986 return false;
1987 }
1988 else
1989 {
1990 string ErrorXml = await Sender.IqError(Id, From, To, ex);
1991 Response.SetResult(ErrorXml, false);
1992 return !(ErrorXml is null);
1993 }
1994 }
1995 }
1996
1997 internal async Task<bool> ProcessResponse(string Type, string Id, XmppAddress To, XmppAddress From, string Language,
1998 bool IqResponse, bool PresenceResponse, Stanza Stanza, ISender Sender)
1999 {
2000 if (!string.IsNullOrEmpty(Id))
2001 {
2002 PendingRequest Rec = null;
2003 bool Ok = Type == "result";
2004
2005 lock (this.synchObject)
2006 {
2007 if (this.pendingRequestsById.TryGetValue(Id, out Rec))
2008 {
2009 this.pendingRequestsById.Remove(Id);
2010 this.pendingRequestsByTimeout.Remove(Rec.Timeout);
2011 }
2012 else
2013 Rec = null;
2014 }
2015
2016 if (!(Rec is null))
2017 {
2018 if (IqResponse)
2019 {
2020 IqResultEventArgs e = new IqResultEventArgs(Stanza.StanzaElement, Id, To, From, Language, Ok, Rec.State);
2021 if (!(Rec.ShortTermCacheKey is null))
2022 this.shortTermCache[Rec.ShortTermCacheKey] = e;
2023
2024 await Rec.IqCallback.Raise(this, e);
2025 }
2026
2027 if (PresenceResponse)
2028 {
2029 PresenceEventArgs e = new PresenceEventArgs(Sender, Type, Id, To, From, Language, Stanza, Rec.State);
2030 await Rec.PresenceCallback.Raise(this, e);
2031 }
2032 }
2033 }
2034
2035 return true;
2036 }
2037
2048 public Task<bool> IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
2049 {
2050 return this.IQ(Type, Id, To, From, Language, ToStanza("iq", Type, Id, To, From, Language, ContentXml), Sender);
2051 }
2052
2053 internal static Stanza ToStanza(string StanzaType, string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
2054 {
2055 StringBuilder Xml = new StringBuilder();
2056 StringBuilder Xml2 = new StringBuilder();
2057 int ContentStart, ContentLen;
2058
2059 Xml.Append("<stream:stream to='");
2060 Xml.Append(XML.Encode(To.Domain));
2061 Xml.Append("' from='");
2062 Xml.Append(XML.Encode(From.Domain));
2063 Xml.Append("' version='1.0' xml:lang='");
2064 Xml.Append(XML.Encode(Language));
2065 Xml.Append("' xmlns='jabber:server' xmlns:stream='");
2067 Xml.Append("'>");
2068
2069 Xml2.Append('<');
2070 Xml2.Append(StanzaType);
2071
2072 if (!string.IsNullOrEmpty(Type))
2073 {
2074 Xml2.Append(" type='");
2075 Xml2.Append(Type);
2076 Xml2.Append('\'');
2077 }
2078
2079 if (!string.IsNullOrEmpty(Id))
2080 {
2081 Xml2.Append(" id='");
2082 Xml2.Append(XML.Encode(Id));
2083 Xml2.Append('\'');
2084 }
2085
2086 if (!From.IsEmpty)
2087 {
2088 Xml2.Append(" from='");
2089 Xml2.Append(XML.Encode(From.Address));
2090 Xml2.Append('\'');
2091 }
2092
2093 if (!To.IsEmpty)
2094 {
2095 Xml2.Append(" to='");
2096 Xml2.Append(XML.Encode(To.Address));
2097 Xml2.Append('\'');
2098 }
2099
2100 if (!string.IsNullOrEmpty(Language))
2101 {
2102 Xml2.Append(" xml:lang='");
2103 Xml2.Append(XML.Encode(Language));
2104 Xml2.Append('\'');
2105 }
2106
2107 if (string.IsNullOrEmpty(ContentXml))
2108 {
2109 Xml2.Append("/>");
2110 ContentStart = ContentLen = 0;
2111 }
2112 else
2113 {
2114 Xml2.Append('>');
2115
2116 ContentStart = Xml2.Length;
2117 ContentLen = ContentXml.Length;
2118
2119 Xml2.Append(ContentXml);
2120 Xml2.Append("</");
2121 Xml2.Append(StanzaType);
2122 Xml2.Append('>');
2123 }
2124
2125 string s = Xml2.ToString();
2126
2127 Xml.Append(s);
2128 Xml.Append("</stream:stream>");
2129
2130 try
2131 {
2132 XmlDocument Doc = XML.ParseXml(Xml.ToString(), true);
2133
2134 return new Stanza(Doc.DocumentElement, s, ContentStart, ContentLen);
2135 }
2136 catch (Exception ex)
2137 {
2138 throw new Exception("Invalid XML:\r\n\r\n" + ex.Message + "\r\n\r\n" + Xml.ToString());
2139 }
2140 }
2141
2152 public async Task<bool> Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
2153 {
2154 bool ToLocal = this.IsServerDomain(To.Domain, true);
2155
2156 if (ToLocal)
2157 {
2158 if (await this.persistenceLayer.IsBlocked(From.BareJid, To.BareJid))
2159 return await (Sender?.MessageErrorServiceUnavailable(From, To, string.Empty, string.Empty) ?? Task.FromResult(true));
2160 else if (await this.persistenceLayer.IsBlocked(To.BareJid, From.BareJid))
2161 {
2162 return await Sender.Message("error", Id, From, To, string.Empty, "<error type='cancel'><not-acceptable xmlns='" + StanzaNamespace +
2163 "'/><blocked xmlns='" + BlockingCommandErrorNamespace + "'/></error>");
2164 }
2165 else
2166 {
2167 if (To.IsBareJID)
2168 {
2169 IClientConnection[] Connections = this.GetClientConnections(To.BareJid);
2170
2171 if (!(Connections is null) && Connections.Length > 0)
2172 {
2173 bool Forwarded = false;
2174
2175 foreach (IClientConnection Connection in Connections)
2176 {
2177 try
2178 {
2179 if (await Connection.Message(Type, Id, To, From, Language, Stanza, Sender))
2180 Forwarded = true;
2181 }
2182 catch (Exception ex)
2183 {
2184 try
2185 {
2186 Connection.Exception(ex);
2187 await Connection.DisposeAsync();
2188 }
2189 catch (Exception ex2)
2190 {
2191 Log.Exception(ex2);
2192 }
2193 }
2194 }
2195
2196 if (Forwarded)
2197 return true;
2198 }
2199
2200 if (string.IsNullOrEmpty(Type) || Type == "normal" || Type == "chat")
2201 {
2202 if (await this.persistenceLayer.StoreOfflineMessage(Type, Id, To, From, Language, Stanza))
2203 {
2204 if (Sender is ICommunicationLayer ComLayer)
2205 ComLayer.Information("Message stored for later delivery.");
2206 }
2207 else
2208 return await (Sender?.MessageErrorServiceUnavailable(From, To, string.Empty, string.Empty) ?? Task.FromResult(true));
2209 }
2210 }
2211 else if (To.IsDomain)
2212 {
2213 EventHandlerAsync<MessageEventArgs> h = null;
2214 MessageEventArgs e = null;
2215 string Counter = "XMPP.Server.Message";
2216
2217 lock (this.synchObject)
2218 {
2219 foreach (XmlNode N in Stanza.StanzaElement.ChildNodes)
2220 {
2221 if (!(N is XmlElement E))
2222 continue;
2223
2224 string Key = E.LocalName + " " + E.NamespaceURI;
2225 if (this.messageHandlers.TryGetValue(Key, out h))
2226 {
2227 Counter += "." + E.LocalName;
2228
2229 e = new MessageEventArgs(Sender, Stanza.StanzaElement, Id, Type,
2230 To, From, Language)
2231 {
2232 Content = E
2233 };
2234 break;
2235 }
2236 else
2237 h = null;
2238 }
2239 }
2240
2241 if (!(h is null))
2242 {
2243 try
2244 {
2245 await RuntimeCounters.IncrementCounter(Counter);
2246 await h(this, e);
2247 return true;
2248 }
2249 catch (Exception ex)
2250 {
2251 return await (Sender?.MessageError(Id, From, To, ex) ?? Task.FromResult(true));
2252 }
2253 }
2254 }
2255 else
2256 {
2257 if (this.TryGetConnection(To.Address, out IClientConnection Connection))
2258 {
2259 bool Forwarded = false;
2260
2261 try
2262 {
2263 Forwarded = await Connection.Message(Type, Id, To, From, Language, Stanza, Sender);
2264 }
2265 catch (Exception ex)
2266 {
2267 Connection.Exception(ex);
2268 await Connection.DisposeAsync();
2269 }
2270
2271 if (Forwarded)
2272 return true;
2273 }
2274
2275 return await (Sender?.MessageErrorServiceUnavailable(From, To, string.Empty, string.Empty) ?? Task.FromResult(true));
2276 }
2277 }
2278
2279 return true;
2280 }
2281 else
2282 {
2284
2285 lock (this.synchObject)
2286 {
2287 if (!this.componentsByFulldomain.TryGetValue(To.Domain, out Component))
2288 Component = null;
2289 }
2290
2291 if (!(Component is null))
2292 {
2294 return await Component.Message(Type, Id, To, From, Language, Stanza, Sender);
2295 }
2296
2297 IS2SEndpoint Endpoint;
2298
2299 try
2300 {
2301 Endpoint = await this.GetS2sEndpoint(From.Domain, To.Domain, true, "Sending message from " + From.Address + " to " + To.Address);
2302 if (Endpoint is null)
2303 return true;
2304 }
2305 catch (Exception ex)
2306 {
2307 if (!(Sender is null))
2308 return await (Sender?.MessageErrorServiceUnavailable(From, To, ex.Message, string.Empty) ?? Task.FromResult(true));
2309
2310 return true;
2311 }
2312
2313 return await Endpoint.Message(Type, Id, To, From, Language, Stanza, Sender);
2314 }
2315 }
2316
2325 public static void RegisterRemoteDomain(CaseInsensitiveString RemoteDomain, string Host, int Port, bool TrustCertificate)
2326 {
2327 lock (remoteDomainLookup)
2328 {
2329 remoteDomainLookup[RemoteDomain] = new S2SRec()
2330 {
2331 Domain = RemoteDomain,
2332 Host = Host,
2333 Port = Port,
2334 TrustCertificate = TrustCertificate
2335 };
2336 }
2337 }
2338
2348 public static void RegisterRemoteDomain(CaseInsensitiveString RemoteDomain, CaseInsensitiveString ServerDomain, string Host, int Port, bool TrustCertificate)
2349 {
2350 lock (remoteDomainLookup)
2351 {
2352 remoteDomainLookup[RemoteDomain] = new S2SRec()
2353 {
2354 Domain = ServerDomain,
2355 Host = Host,
2356 Port = Port,
2357 TrustCertificate = TrustCertificate
2358 };
2359 }
2360 }
2361
2367 public static bool IsRemoteDomainRegistered(CaseInsensitiveString RemoteDomain)
2368 {
2369 lock (remoteDomainLookup)
2370 {
2371 return remoteDomainLookup.ContainsKey(RemoteDomain);
2372 }
2373 }
2374
2375 internal async void RegisterS2SEndpoint(IS2SEndpoint Endpoint)
2376 {
2377 CaseInsensitiveString Domain = Endpoint.RemoteDomain;
2378 EndpointStatistics Stat = this.GetS2sStatistics(Domain, Endpoint.Type);
2379 EventHandlerAsync<ServerConnectionEventArgs> h;
2380
2381 Stat.EndpointConnected(Endpoint);
2382
2383 if (this.s2sEndpoints?.TryGetValue(Domain, out IS2SEndpoint Prev) ?? false)
2384 {
2385#if LogToWebHookTester
2386 // TODO: Remove
2387 _ = Task.Run(async () =>
2388 {
2389 try
2390 {
2391 await InternetContent.PostAsync(new Uri("https://lab.tagroot.io/WebHookTester/Show.ws?ID=" + this.domain.Value),
2392 new Dictionary<string, object>()
2393 {
2394 { "id_prev", Prev.Id.ToString() },
2395 { "id_updated", Endpoint.Id.ToString() },
2396 { "event", "RegisterS2SEndpoint" },
2397 { "remote", Domain.Value },
2398 { "prev", JSON.Encode(Prev, true) },
2399 { "updated", JSON.Encode(Endpoint, true) },
2400 { "reason", "Updating" }
2401 },
2402 new KeyValuePair<string, string>("Accept", "application/json"));
2403 }
2404 catch (Exception ex)
2405 {
2406 Log.Exception(ex);
2407 }
2408 });
2409#endif
2410 h = this.ServerConnectionUpdated;
2411
2412 if (Prev != Endpoint)
2413 this.s2sEndpoints[Domain] = Endpoint;
2414
2415 await h.Raise(this, new ServerConnectionEventArgs(Domain, Endpoint));
2416
2417 if (Prev == Endpoint)
2418 return;
2419 }
2420 else
2421 {
2422 h = this.ServerConnectionAdded;
2423
2424 this.s2sEndpoints?.Add(Domain, Endpoint);
2425
2426#if LogToWebHookTester
2427 // TODO: Remove
2428 _ = Task.Run(async () =>
2429 {
2430 try
2431 {
2432 await InternetContent.PostAsync(new Uri("https://lab.tagroot.io/WebHookTester/Show.ws?ID=" + this.domain.Value),
2433 new Dictionary<string, object>()
2434 {
2435 { "id", Endpoint.Id.ToString() },
2436 { "event", "RegisterS2SEndpoint" },
2437 { "remote", Domain?.Value },
2438 { "value", JSON.Encode(Endpoint, true) },
2439 { "reason", "Registering" }
2440 },
2441 new KeyValuePair<string, string>("Accept", "application/json"));
2442 }
2443 catch (Exception ex)
2444 {
2445 Log.Exception(ex);
2446 }
2447 });
2448#endif
2449 await h.Raise(this, new ServerConnectionEventArgs(Domain, Endpoint));
2450 }
2451
2452 if (Endpoint is XmppS2SEndpoint XmppS2SEndpoint)
2453 XmppS2SEndpoint.OnStateChanged += this.Endpoint_OnStateChanged;
2454 }
2455
2456 private async Task Endpoint_OnStateChanged(object Sender, EventArgs e)
2457 {
2458 if (Sender is XmppS2SEndpoint Endpoint)
2459 await this.ServerConnectionUpdated.Raise(this, new ServerConnectionEventArgs(Endpoint.RemoteDomain, Endpoint));
2460 }
2461
2468 public bool TryGetS2sEndpoint(string RemoteDomain, out IS2SEndpoint Endpoint)
2469 {
2470 return this.s2sEndpoints.TryGetValue(RemoteDomain, out Endpoint);
2471 }
2472
2473 internal void RegisterAsTemporary(IS2SEndpoint Endpoint)
2474 {
2475 lock (this.synchObject)
2476 {
2477 this.temporaryConnections ??= new SortedDictionary<DateTime, IS2SEndpoint>();
2478
2479 DateTime TP = DateTime.Now.AddMinutes(1);
2480
2481 while (this.temporaryConnections.ContainsKey(TP))
2482 TP = TP.AddTicks(this.gen.Next(10));
2483
2484 this.temporaryConnections[TP] = Endpoint;
2485 }
2486 }
2487
2488 internal static async Task<S2SRec> GetDomainFromDns(CaseInsensitiveString DomainName)
2489 {
2490 S2SRec Result;
2491 string SrvMsg;
2492
2493 try
2494 {
2495 SRV SRV = await DnsResolver.TryLookupServiceEndpoint(DomainName, "xmpp-server", "tcp");
2496 if (SRV is null)
2497 SrvMsg = "Unable to get SRV record for xmpp-server/tcp of " + DomainName;
2498 else
2499 {
2500 Result = new S2SRec()
2501 {
2502 Domain = DomainName,
2503 Host = SRV.TargetHost,
2504 Port = SRV.Port,
2505 TrustCertificate = false,
2506 Type = S2sType.XMPP
2507 };
2508
2509 return Result;
2510 }
2511 }
2512 catch (Exception ex)
2513 {
2514 SrvMsg = ex.Message;
2515 }
2516
2517 try
2518 {
2519 string[] Hosts = await DnsResolver.TryLookupMailExchange(DomainName);
2520 if (!(Hosts is null) && Hosts.Length > 0 && !string.IsNullOrEmpty(Hosts[0]))
2521 {
2522 try
2523 {
2524 using TcpClient TestClient = new TcpClient();
2525
2526 await TestClient.ConnectAsync(DomainName, DefaultS2sPort);
2527
2528 Log.Notice("Federated XMPP server connection. SRV DNS settings not found: " + SrvMsg, DomainName);
2529
2530 Result = new S2SRec()
2531 {
2532 Domain = DomainName,
2533 Host = DomainName,
2534 Port = DefaultS2sPort,
2535 TrustCertificate = false,
2536 Type = S2sType.XMPP
2537 };
2538
2539 return Result;
2540 }
2541 catch (Exception)
2542 {
2543 // Ignore. Just a test to check for XMPP if DNS settings not available or incorrect.
2544 }
2545
2546 Log.Notice("Federated mail server connection. XMPP server not found: " + SrvMsg, DomainName);
2547
2548 Result = new S2SRec()
2549 {
2550 Domain = DomainName,
2551 Host = Hosts[0],
2553 TrustCertificate = false,
2554 Type = S2sType.SMTP
2555 };
2556
2557 return Result;
2558 }
2559 }
2560 catch (Exception)
2561 {
2562 // Ignore
2563 }
2564
2565 return null;
2566 }
2567
2573 public static async Task<S2SRec> GetDomain(CaseInsensitiveString DomainOrSubdomain)
2574 {
2575 S2SRec Result;
2576
2577 lock (remoteDomainLookup)
2578 {
2579 if (remoteDomainLookup.TryGetValue(DomainOrSubdomain, out Result))
2580 return Result;
2581 }
2582
2583 Result = await GetDomainFromDns(DomainOrSubdomain);
2584 if (!(Result is null))
2585 {
2586 if (Result.Type == S2sType.XMPP && Result.Domain.EndsWith("." + Result.Host))
2587 Result.Domain = Result.Host; // Component
2588
2589 lock (remoteDomainLookup)
2590 {
2591 remoteDomainLookup[DomainOrSubdomain] = Result;
2592 }
2593
2594 return Result;
2595 }
2596
2597 CaseInsensitiveString[] Parts = DomainOrSubdomain.Split('.');
2598 if (Parts.Length < 2)
2599 throw new Exception("Invalid domain or subdomain name: " + DomainOrSubdomain);
2600
2601 int c = Parts.Length;
2602 int i = c - 2;
2603 CaseInsensitiveString Domain = Parts[i] + "." + Parts[i + 1];
2604 S2SRec Rec;
2605
2606 while (i >= 0)
2607 {
2608 Rec = null;
2609
2610 if (Domain != DomainOrSubdomain)
2611 {
2612 Rec = await GetDomainFromDns(Domain);
2613 if (!(Rec is null))
2614 Result = Rec;
2615 }
2616
2617 if (Rec is null)
2618 {
2619 try
2620 {
2621 IPHostEntry Entry = await System.Net.Dns.GetHostEntryAsync(Domain);
2622 if (Entry.AddressList.Length > 0)
2623 {
2624 using (TcpClient TestClient = new TcpClient())
2625 {
2626 await TestClient.ConnectAsync(Domain, DefaultS2sPort);
2627 TestClient.Close();
2628 }
2629
2630 Result = Rec = new S2SRec()
2631 {
2632 Domain = Domain,
2633 Host = Domain,
2634 Port = DefaultS2sPort,
2635 Type = S2sType.XMPP,
2636 TrustCertificate = false
2637 };
2638 }
2639 }
2640 catch (Exception)
2641 {
2642 // Subdomains might not be registered in DNS.
2643 }
2644 }
2645
2646 i--;
2647 if (i >= 0)
2648 Domain = Parts[i] + "." + Domain;
2649
2650 if (Rec is null && !(Result is null) && Result.Type == S2sType.XMPP)
2651 break;
2652 }
2653
2654 if (!(Result is null))
2655 {
2656 lock (remoteDomainLookup)
2657 {
2658 remoteDomainLookup[DomainOrSubdomain] = Result;
2659 }
2660
2661 return Result;
2662 }
2663
2664 throw new Exception("Invalid S2S domain or subdomain name: " + DomainOrSubdomain);
2665 }
2666
2670 public enum S2sType
2671 {
2675 XMPP,
2676
2680 SMTP
2681 }
2682
2686 public class S2SRec
2687 {
2692
2697
2702
2706 public int Port;
2707
2711 public bool TrustCertificate;
2712 }
2713
2714 internal async Task<IS2SEndpoint> GetS2sEndpoint(CaseInsensitiveString LocalDomain,
2715 CaseInsensitiveString RemoteDomain, bool ReuseExisting, string Reason)
2716 {
2719 S2SRec Rec;
2720 S2sType Type;
2721 int Port;
2722 bool RecFound;
2723 bool TrustCertificate;
2724
2725 if (this.disposed)
2726 return null;
2727
2728 lock (remoteDomainLookup)
2729 {
2730 RecFound = remoteDomainLookup.TryGetValue(RemoteDomain, out Rec);
2731 }
2732
2733 if (!RecFound)
2734 Rec = await GetDomain(RemoteDomain);
2735
2736 RemoteDomain = Rec.Domain;
2737 Host = Rec.Host;
2738 Port = Rec.Port;
2739 TrustCertificate = Rec.TrustCertificate;
2740 Type = Rec.Type;
2741
2742 if (this.IsServerDomain(RemoteDomain, true)) // Non-existing sub-domain pointing to itself, creating loop.
2743 return null;
2744
2745 using Runtime.Threading.Semaphore S2sSemaphore = await Semaphores.BeginWrite("s2s:" + RemoteDomain.LowerCase);
2746
2747 QueuedStanza[] QueuedStanzas = null;
2748
2749 if (ReuseExisting && this.s2sEndpoints.TryGetValue(RemoteDomain, out IS2SEndpoint Existing))
2750 {
2751 if (!((XmppS2SEndpoint = Existing as XmppS2SEndpoint) is null) &&
2753 {
2754 Log.Informational("Removes stale S2S connection.", RemoteDomain, string.Empty, "XmppStaleS2s");
2755
2756#if LogToWebHookTester
2757 // TODO: Remove
2758 _ = Task.Run(async () =>
2759 {
2760 try
2761 {
2762 await InternetContent.PostAsync(new Uri("https://lab.tagroot.io/WebHookTester/Show.ws?ID=" + this.domain.Value),
2763 new Dictionary<string, object>()
2764 {
2765 { "id", Existing.Id.ToString() },
2766 { "event", "Removing stale S2S endpoint" },
2767 { "remote", RemoteDomain.Value },
2768 { "value", JSON.Encode(Existing, true) },
2769 { "reason", "Getting" },
2770 { "state", XmppS2SEndpoint.State },
2771 { "created", XmppS2SEndpoint.CreationTimeUtc.ToString() },
2772 { "connect", XmppS2SEndpoint.ConnectTimeUtc.ToString() },
2773 { "connected", XmppS2SEndpoint.ConnectedTimeUtc.ToString() },
2774 },
2775 new KeyValuePair<string, string>("Accept", "application/json"));
2776 }
2777 catch (Exception ex)
2778 {
2779 Log.Exception(ex);
2780 }
2781 });
2782#endif
2783 QueuedStanzas = XmppS2SEndpoint.GetAndClearQueuedStanzas();
2784 await this.s2sEndpoints.RemoveAsync(RemoteDomain); // Disposes the endpoint.
2785 }
2786 else
2787 return Existing;
2788 }
2789
2790 IS2SEndpoint Result;
2791
2792 switch (Type)
2793 {
2794 case S2sType.XMPP:
2795 if (this.HasDomain || RecFound)
2796 {
2797 Log.Informational("Opening S2S connection. " + Reason, RemoteDomain, string.Empty, "XmppOpenS2s");
2798
2799 if (!this.IsServerDomain(LocalDomain, true))
2800 {
2801 int i = LocalDomain.IndexOf('.');
2802 if (i > 0)
2803 {
2804 CaseInsensitiveString s = LocalDomain.Substring(i + 1);
2805 if (this.IsServerDomain(s, true))
2806 LocalDomain = s;
2807 }
2808 }
2809
2810 XmppS2SEndpoint = new XmppS2SEndpoint(LocalDomain, RemoteDomain,
2811 Port, this.serverCertificate, this, TrustCertificate,
2812 !ReuseExisting, QueuedStanzas);
2813
2814 await this.S2sEndpointCreated.Raise(this, new XmppS2SEndpointEventArgs(XmppS2SEndpoint));
2815
2816 Result = XmppS2SEndpoint;
2817
2818 this.AddS2SSniffers(Result, RemoteDomain);
2819
2820 XmppS2SEndpoint.Information("Opening S2S connection to " + Host + ". " + Reason);
2821
2822#if LogToWebHookTester
2823 // TODO: Remove
2824 _ = Task.Run(async () =>
2825 {
2826 try
2827 {
2828 await InternetContent.PostAsync(new Uri("https://lab.tagroot.io/WebHookTester/Show.ws?ID=" + this.domain.Value),
2829 new Dictionary<string, object>()
2830 {
2831 { "id", XmppS2SEndpoint.Id.ToString() },
2832 { "event", "Creating S2S endpoint" },
2833 { "remote", RemoteDomain.Value },
2834 { "value", JSON.Encode(XmppS2SEndpoint, true) },
2835 { "reason", "Getting" }
2836 },
2837 new KeyValuePair<string, string>("Accept", "application/json"));
2838 }
2839 catch (Exception ex)
2840 {
2841 Log.Exception(ex);
2842 }
2843 });
2844#endif
2845 _ = Task.Run(async () =>
2846 {
2847 try
2848 {
2849 await XmppS2SEndpoint.Connect(Host, false);
2850 }
2851 catch (Exception ex)
2852 {
2853 Log.Exception(ex);
2854 }
2855 });
2856 }
2857 else
2858 {
2860
2861 await this.GetParentConnection.Raise(this, e);
2862
2863 if (e.Client is null)
2864 throw new NotSupportedException("S2S connections not supported. No domain or certificate defined, and no parent client connection available.");
2865
2866 Log.Informational("Tunneling S2S connection over client connection to parent. " + Reason, RemoteDomain, string.Empty, "XmppOpenS2sTunnel");
2867
2868 Result = new XmppS2SOverParentEndpoint(e.Client, LocalDomain, RemoteDomain);
2869 }
2870 break;
2871
2872 case S2sType.SMTP:
2873 if (this.smtpServer is null)
2874 throw new NotSupportedException("Integration with SMTP not enabled.");
2875
2876 Result = new SmtpS2SEndpoint(LocalDomain, RemoteDomain, this.smtpServer, this);
2877
2878 if (ReuseExisting)
2879 scheduler.Add(DateTime.Now.AddMinutes(10), this.RemoveSmtpConnection, Result);
2880 break;
2881
2882 default:
2883 throw new NotSupportedException("S2S Connection type not supported: " + Type.ToString());
2884 }
2885
2886 if (ReuseExisting)
2887 this.RegisterS2SEndpoint(Result);
2888 else
2889 this.RegisterAsTemporary(Result);
2890
2891 return Result;
2892 }
2893
2897 public event EventHandlerAsync<XmppS2SEndpointEventArgs> S2sEndpointCreated;
2898
2902 public event EventHandlerAsync<XmppS2SEndpointEventArgs> S2sEndpointRemoved;
2903
2904 private Task RemoveSmtpConnection(object State)
2905 {
2906 if (State is SmtpS2SEndpoint Endpoint)
2907 {
2908 lock (remoteDomainLookup)
2909 {
2910 remoteDomainLookup.Remove(Endpoint.RemoteDomain);
2911 }
2912
2913 this.s2sEndpoints.Remove(Endpoint.RemoteDomain);
2914 }
2915
2916 return Task.CompletedTask;
2917 }
2918
2922 public event EventHandlerAsync<ParentConnectionEventArgs> GetParentConnection;
2923
2927 public bool HasDomain
2928 {
2929 get
2930 {
2931 if (CaseInsensitiveString.IsNullOrEmpty(this.domain) || this.serverCertificate is null)
2932 return false;
2933
2934 switch (this.domain.LowerCase)
2935 {
2936 case "localhost":
2937 case "example.com":
2938 case "example2.com":
2939 case "example3.com":
2940 case "example.org":
2941 case "example2.org":
2942 case "example3.org":
2943 return false;
2944
2945 default:
2946 return true;
2947 }
2948 }
2949 }
2950
2951 internal void AddS2SSniffers(ICommunicationLayer Endpoint, string RemoteDomain)
2952 {
2953 if (!string.IsNullOrEmpty(this.domainSnifferPath))
2954 Endpoint.Add(this.GetSniffer(RemoteDomain, true));
2955 else if (this.s2sSniffers.HasSniffers)
2956 {
2957 foreach (ISniffer Sniffer in this.s2sSniffers.Sniffers)
2958 Endpoint.Add(Sniffer);
2959 }
2960 }
2961
2962 internal static bool TrustCertificate(CaseInsensitiveString RemoteDomain)
2963 {
2964 lock (remoteDomainLookup)
2965 {
2966 if (remoteDomainLookup.TryGetValue(RemoteDomain, out S2SRec Rec))
2967 return Rec.TrustCertificate;
2968 else
2969 return false;
2970 }
2971 }
2972
2983 public Task<bool> Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
2984 {
2985 return this.Message(Type, Id, To, From, Language, ToStanza("message", Type, Id, To, From, Language, ContentXml), Sender);
2986 }
2987
2997 public async Task<bool> Presence(string Type, string Id, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
2998 {
2999 if (Sender is IClientConnection FromConnection)
3000 {
3001 switch (Type)
3002 {
3003 case "subscribe":
3004 case "subscribed":
3005 case "unsubscribe":
3006 case "unsubscribed":
3007 return await (Sender?.PresenceErrorNotAllowed(Id, From, this.domainAddress, string.Empty, string.Empty) ?? Task.FromResult(true));
3008 }
3009
3010 CaseInsensitiveString FromUserName = FromConnection.UserName;
3011 IAccount FromAccount = await this.persistenceLayer.GetAccount(FromUserName);
3012
3013 FromConnection.LastPresence = new PresenceEventArgs(Sender, Type, Id, XmppAddress.Empty, From, Language, Stanza, null);
3014 await this.PushPresence(FromConnection.BareAddress, Type, Id, From, Language, Stanza, false, Sender);
3015
3016 foreach (IRosterItem Item in await this.persistenceLayer.GetRoster(FromUserName))
3017 {
3018 if (Item.BareJid != FromConnection.BareJid &&
3019 (Item.Subscription == SubscriptionStatus.both || Item.Subscription == SubscriptionStatus.from))
3020 {
3021 await this.PushPresence(new XmppAddress(Item.BareJid), Type, Id, From, Language, Stanza, false, Sender);
3022 }
3023 }
3024
3025 await this.ClientConnectionUpdated.Raise(this, new ClientConnectionEventArgs(From.Address, FromConnection));
3026 }
3027
3028 return true;
3029 }
3030
3041 public async Task<bool> Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
3042 {
3043 bool ToLocal = this.IsServerDomain(To.Domain, true);
3044 IAccount ToAccount = ToLocal ? await this.persistenceLayer.GetAccount(To.Account) : null;
3045 CaseInsensitiveString FromUserName = null;
3046 IClientConnection FromConnection = Sender as IClientConnection;
3047 bool FromLocal = !(FromConnection is null);
3048 IAccount FromAccount = null;
3049 PresenceEventArgs LastPresence = null;
3051 bool UseBareJids = false;
3052
3053 if (!string.IsNullOrEmpty(Id))
3054 {
3055 PendingRequest Rec = null;
3056 bool Ok = (Type != "error");
3057
3058 lock (this.synchObject)
3059 {
3060 if (this.pendingRequestsById.TryGetValue(Id, out Rec))
3061 {
3062 this.pendingRequestsById.Remove(Id);
3063 this.pendingRequestsByTimeout.Remove(Rec.Timeout);
3064 }
3065 else
3066 Rec = null;
3067 }
3068
3069 if (!(Rec?.PresenceCallback is null))
3070 await Rec.PresenceCallback.Raise(this, new PresenceEventArgs(Sender, Type, Id, To, From, Language, Stanza, Rec.State));
3071 }
3072
3073 if (ToLocal && ToAccount is null)
3074 return await (Sender?.PresenceErrorItemNotFound(Id, From, To, string.Empty, string.Empty) ?? Task.FromResult(true));
3075
3076 if (FromLocal)
3077 {
3078 FromUserName = FromConnection.UserName;
3079 FromAccount = await this.persistenceLayer.GetAccount(FromUserName);
3080
3081 if (FromAccount is null)
3082 return await (Sender?.PresenceErrorNotAllowed(Id, From, this.domainAddress, string.Empty, string.Empty) ?? Task.FromResult(true));
3083 }
3084
3085 switch (Type)
3086 {
3087 case "subscribe":
3088 if (FromLocal)
3089 {
3090 if (await this.persistenceLayer.IsBlocked(From.BareJid, To.BareJid))
3091 return true;
3092
3093 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.BareJid);
3094
3095 if (!(Item is null))
3096 {
3097 if (!Item.PendingSubscription)
3098 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, Item.Subscription, true, Item.Groups);
3099 }
3100 else
3101 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, string.Empty, SubscriptionStatus.none, true, null);
3102
3103 if (!(Item is null))
3104 await this.PushIq(From.BareJid, Item);
3105 }
3106
3107 UseBareJids = true;
3108 break;
3109
3110 case "subscribed":
3111 if (ToLocal)
3112 {
3113 Item = await this.persistenceLayer.GetRosterItem(To.Account, From.BareJid);
3114
3115 if (!(Item is null))
3116 {
3117 if (Item.Subscription == SubscriptionStatus.from)
3118 {
3119 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.both,
3120 false, Item.Groups);
3121 }
3122 else if (Item.Subscription == SubscriptionStatus.none)
3123 {
3124 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.to,
3125 false, Item.Groups);
3126 }
3127 else
3128 Item = null;
3129
3130 if (!(Item is null))
3131 await this.PushIq(To.BareJid, Item);
3132 }
3133 else
3134 break; // No subscription request to accept.
3135 }
3136
3137 if (FromLocal)
3138 {
3139 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.BareJid);
3140 if (!(Item is null))
3141 {
3142 if (Item.Subscription == SubscriptionStatus.to)
3143 {
3144 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.both,
3145 Item.PendingSubscription, Item.Groups);
3146 }
3147 else if (Item.Subscription == SubscriptionStatus.none)
3148 {
3149 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.from,
3150 Item.PendingSubscription, Item.Groups);
3151 }
3152 else
3153 Item = null;
3154 }
3155 else
3156 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, string.Empty, SubscriptionStatus.from, false, null);
3157
3158 if (!(Item is null))
3159 await this.PushIq(From.BareJid, Item);
3160
3161 LastPresence = FromConnection.LastPresence;
3162 }
3163
3164 UseBareJids = true;
3165 break;
3166
3167 case "unsubscribed":
3168 if (ToLocal)
3169 {
3170 Item = await this.persistenceLayer.GetRosterItem(To.Account, From.BareJid);
3171
3172 if (!(Item is null))
3173 {
3174 if (Item.Subscription == SubscriptionStatus.to)
3175 {
3176 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.none,
3177 false, Item.Groups);
3178 }
3179 else if (Item.Subscription == SubscriptionStatus.both)
3180 {
3181 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.from,
3182 false, Item.Groups);
3183 }
3184 else if (Item.PendingSubscription)
3185 {
3186 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, Item.Subscription,
3187 false, Item.Groups);
3188 }
3189 else
3190 Item = null;
3191
3192 if (!(Item is null))
3193 await this.PushIq(To.BareJid, Item);
3194 }
3195 }
3196
3197 if (FromLocal)
3198 {
3199 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.BareJid);
3200 if (!(Item is null))
3201 {
3202 if (Item.Subscription == SubscriptionStatus.from)
3203 {
3204 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.none,
3205 Item.PendingSubscription, Item.Groups);
3206 }
3207 else if (Item.Subscription == SubscriptionStatus.both)
3208 {
3209 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.to,
3210 Item.PendingSubscription, Item.Groups);
3211 }
3212 else
3213 Item = null;
3214
3215 if (!(Item is null))
3216 await this.PushIq(From.BareJid, Item);
3217 }
3218 }
3219
3220 UseBareJids = true;
3221 break;
3222
3223 case "unsubscribe":
3224 if (ToLocal)
3225 {
3226 Item = await this.persistenceLayer.GetRosterItem(To.Account, From.BareJid);
3227
3228 if (!(Item is null))
3229 {
3230 if (Item.Subscription == SubscriptionStatus.from)
3231 {
3232 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.none,
3233 false, Item.Groups);
3234 }
3235 else if (Item.Subscription == SubscriptionStatus.both)
3236 {
3237 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.to,
3238 false, Item.Groups);
3239 }
3240 else
3241 Item = null;
3242
3243 if (!(Item is null))
3244 await this.PushIq(To.BareJid, Item);
3245 }
3246 else
3247 break; // No subscription request to unsubscribe from.
3248 }
3249
3250 if (FromLocal)
3251 {
3252 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.BareJid);
3253 if (!(Item is null))
3254 {
3255 if (Item.Subscription == SubscriptionStatus.to)
3256 {
3257 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.none,
3258 false, Item.Groups);
3259 }
3260 else if (Item.Subscription == SubscriptionStatus.both)
3261 {
3262 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.from,
3263 false, Item.Groups);
3264 }
3265 else if (Item.PendingSubscription)
3266 {
3267 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, Item.Subscription,
3268 false, Item.Groups);
3269 }
3270 else
3271 Item = null;
3272
3273 if (!(Item is null))
3274 await this.PushIq(From.BareJid, Item);
3275 }
3276 }
3277
3278 await this.PushPresence(To, Type, Id, From.ToBareJID(), Language, Stanza, UseBareJids, Sender);
3279
3280 Type = "unavailable";
3281
3282 Stanza = null;
3283 UseBareJids = true;
3284 break;
3285
3286 case "probe":
3287 if (ToLocal)
3288 {
3289 if (ToAccount is null ||
3290 await this.persistenceLayer.IsBlocked(From.BareJid, To.BareJid) ||
3291 (From.HasAccount && (
3292 (Item = await this.persistenceLayer.GetRosterItem(To.Account, From.BareJid)) is null ||
3293 (Item.Subscription != SubscriptionStatus.both && Item.Subscription != SubscriptionStatus.from))))
3294 {
3295 if (!await (Sender?.Presence("unsubscribed", Id, From, To, string.Empty, string.Empty) ?? Task.FromResult(true)))
3296 return false;
3297 }
3298 else
3299 {
3300 bool Sent = false;
3301
3302 if (To.IsBareJID)
3303 {
3304 IClientConnection[] Connections = this.GetClientConnections(To.BareJid);
3305 if (!(Connections is null))
3306 {
3307 foreach (IClientConnection Connection in Connections)
3308 {
3309 if (!((LastPresence = Connection.LastPresence) is null) && LastPresence.To.IsEmpty)
3310 {
3311 if (!await Sender.Presence(LastPresence.Type, Id, From, LastPresence.From, LastPresence.Language,
3312 LastPresence.Stanza?.Content))
3313 {
3314 return false;
3315 }
3316
3317 Sent = true;
3318 }
3319 }
3320 }
3321 }
3322 else if (this.TryGetConnection(To.Address, out IClientConnection Connection))
3323 {
3324 LastPresence = Connection.LastPresence;
3325 if (!await (Sender?.Presence(LastPresence.Type, Id, From, LastPresence.From, LastPresence.Language, LastPresence.Stanza?.Content) ?? Task.FromResult(true)))
3326 return false;
3327 Sent = true;
3328 }
3329
3330 if (!Sent && !await (Sender?.Presence("unavailable", Id, From, To, string.Empty, string.Empty) ?? Task.FromResult(true)))
3331 return false;
3332 }
3333
3334 return true; // Don't propagate presence probes to clients.
3335 }
3336 break;
3337
3338 case "":
3339 case "error":
3340 case "unavailable":
3341 break;
3342
3343 default:
3344 if (!await (Sender?.PresenceErrorBadRequest(Id, From, this.domainAddress, "Invalid type.", "en") ?? Task.FromResult(true)))
3345 return false;
3346 break;
3347 }
3348
3349 await this.PushPresence(To, Type, Id, UseBareJids ? From.ToBareJID() : From, Language, Stanza, UseBareJids, Sender);
3350
3351 if (!(LastPresence is null))
3352 {
3353 await this.PushPresence(To, LastPresence.Type, LastPresence.Id, LastPresence.From, LastPresence.Language,
3354 LastPresence.Stanza, false, Sender);
3355 }
3356
3357 return true;
3358 }
3359
3360 private async Task<bool> PushPresence(XmppAddress To, string Type, string Id, XmppAddress From, string Language, Stanza Stanza,
3361 bool UseBareJids, ISender Sender)
3362 {
3363 bool FromLocal = this.IsServerDomain(From.Domain, true);
3364 PresenceEventArgs e = null;
3365
3366 if (FromLocal)
3367 {
3368 e = new PresenceEventArgs(Sender, Type, Id, To, From, Language, Stanza, null);
3369 await this.OnPresenceLocalSender.Raise(this, e);
3370
3371 if (e.ResponseSent)
3372 return true;
3373 }
3374
3375 bool ToLocal = To.Domain == From.Domain ? FromLocal : this.IsServerDomain(To.Domain, true);
3376
3377 if (ToLocal)
3378 {
3379 IClientConnection[] Connections = this.GetClientConnections(To.BareJid);
3380 await this.PushPresence(Type, Id, From, Language, Stanza is null ? string.Empty : Stanza.Content, Connections, UseBareJids);
3381
3382 await this.OnPresenceLocalRecipient.Raise(this, e ?? new PresenceEventArgs(Sender, Type, Id, To, From, Language, Stanza, null));
3383 }
3384 else
3385 {
3386 IRecipient Recipient = await this.TryGetRecipient(To, From);
3387
3388 if (!(Recipient is null))
3389 await Recipient.Presence(Type, Id, To, From, Language, Stanza, Sender);
3390 }
3391
3392 return true;
3393 }
3394
3398 public event EventHandlerAsync<PresenceEventArgs> OnPresenceLocalRecipient = null;
3399
3403 public event EventHandlerAsync<PresenceEventArgs> OnPresenceLocalSender = null;
3404
3415 public Task<bool> Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
3416 {
3417 return this.Presence(Type, Id, To, From, Language, ToStanza("presence", Type, Id, To, From, Language, ContentXml), Sender);
3418 }
3419
3426 public static void GetErrorInformation(Exception ex, out string Type, out string Xml)
3427 {
3428 if (ex is StanzaExceptionException stEx)
3429 {
3430 Type = stEx.ErrorType;
3431 Xml = "<" + stEx.ErrorStanzaName + " xmlns='" + StanzaNamespace + "'/>";
3432 return;
3433 }
3434 else if (ex is HttpException)
3435 {
3436 Type = null;
3437 Xml = null;
3438
3439 if (ex is HTTP.BadRequestException)
3440 {
3441 Type = "modify";
3442 Xml = "bad-request";
3443 }
3444 else if (ex is HTTP.ConflictException)
3445 {
3446 Type = "cancel";
3447 Xml = "conflict";
3448 }
3449 else if (ex is HTTP.ForbiddenException)
3450 {
3451 Type = "auth";
3452 Xml = "forbidden";
3453 }
3454 else if (ex is HTTP.NotImplementedException)
3455 {
3456 Type = "cancel";
3457 Xml = "feature-not-implemented";
3458 }
3459 else if (ex is HTTP.MovedPermanentlyException || ex is HTTP.GoneException)
3460 {
3461 Type = "cancel";
3462 Xml = "gone";
3463 }
3464 else if (ex is HTTP.InternalServerErrorException)
3465 {
3466 Type = "cancel";
3467 Xml = "internal-server-error";
3468 }
3469 else if (ex is HTTP.NotFoundException)
3470 {
3471 Type = "cancel";
3472 Xml = "item-not-found";
3473 }
3474 else if (ex is HTTP.NotAcceptableException || ex is HTTP.UnsupportedMediaTypeException)
3475 {
3476 Type = "modify";
3477 Xml = "not-acceptable";
3478 }
3479 else if (ex is HTTP.MethodNotAllowedException)
3480 {
3481 Type = "cancel";
3482 Xml = "not-allowed";
3483 }
3484 else if (ex is HTTP.TooManyRequestsException || ex is HTTP.InsufficientStorageException)
3485 {
3486 Type = "wait";
3487 Xml = "resource-constraint";
3488 }
3489 else if (ex is HTTP.ServiceUnavailableException)
3490 {
3491 Type = "cancel";
3492 Xml = "service-unavailable";
3493 }
3494 else if (ex is HTTP.NetworkAuthenticationRequiredException)
3495 {
3496 Type = "auth";
3497 Xml = "not-authorized";
3498 }
3499
3500 if (!(Xml is null))
3501 {
3502 Xml = "<" + Xml + " xmlns='" + StanzaNamespace + "'/>";
3503 return;
3504 }
3505 }
3506
3507 Type = "cancel";
3508 Xml = "<internal-server-error xmlns='" + StanzaNamespace + "'/>";
3509 }
3510
3511 #endregion
3512
3513 #region Request/Response
3514
3524 public async Task<bool> GetLastPresence(CaseInsensitiveString BareJid, EventHandlerAsync<PresenceEventArgs> Callback, object State)
3525 {
3526 int i = BareJid.IndexOf('@');
3527 if (i >= 0)
3528 {
3529 CaseInsensitiveString UserName = BareJid.Substring(0, i);
3530 CaseInsensitiveString Domain = BareJid.Substring(i + 1);
3531
3532 if (this.IsServerDomain(Domain, true))
3533 {
3534 IClientConnection[] Connections = this.GetClientConnections(BareJid);
3535 if (!(Connections is null))
3536 {
3537 PresenceEventArgs Last = null;
3538
3539 foreach (IClientConnection Connection in Connections)
3540 {
3541 if (!(Connection?.LastPresence is null))
3542 {
3543 PresenceEventArgs LastPresence = Connection.LastPresence;
3544 if (Last is null || LastPresence.Timestamp > Last.Timestamp)
3545 Last = LastPresence;
3546 }
3547 }
3548
3549 if (Last is null)
3550 return false;
3551
3552 await Callback.Raise(this, new PresenceEventArgs(null, Last.Type, Last.Id, Last.To, Last.From, Last.Language, Last.Stanza, State));
3553
3554 return true;
3555 }
3556 else
3557 return false;
3558 }
3559 else
3560 {
3561 IS2SEndpoint Endpoint;
3562
3563 try
3564 {
3565 Endpoint = await this.GetS2sEndpoint(this.domain, Domain, true, "Performing presence probe on " + BareJid);
3566 }
3567 catch (Exception)
3568 {
3569 return false;
3570 }
3571
3572 if (Endpoint is XmppS2SEndpoint XmppS2SEndpoint)
3573 {
3574 PendingRequest Request = this.PrepareRequest(null, (Sender, e) =>
3575 {
3576 return Callback.Raise(this, new PresenceEventArgs(null, e.Type, e.Id, e.To, e.From, e.Language, e.Stanza, e.State));
3577 }, null, State, 10000, 0, false, 10000, this.domainAddress, new XmppAddress(BareJid), string.Empty, string.Empty, null);
3578
3579 await XmppS2SEndpoint.SendStanza("presence", "probe", Request.Id, new XmppAddress(BareJid), this.domainAddress, string.Empty, string.Empty, this);
3580 // TODO: Errors in SendStanza should be returned, which does not happen since last parameter is null.
3581
3582 return true;
3583 }
3584 else
3585 return false;
3586 }
3587 }
3588 else
3589 return false;
3590 }
3591
3592 private PendingRequest PrepareRequest(EventHandlerAsync<IqResultEventArgs> IqCallback, EventHandlerAsync<PresenceEventArgs> PresenceCallback,
3593 EventHandlerAsync<PendingRequestEventArgs> ResendCallback, object State, int RetryTimeout, int NrRetries, bool DropOff, int MaxRetryTimeout,
3594 XmppAddress From, XmppAddress To, string Language, string ContentXml, string ShortTermCacheKey)
3595 {
3596 lock (this.synchObject)
3597 {
3598 string Id;
3599
3600 do
3601 {
3602 Id = this.NewId(16);
3603 }
3604 while (this.pendingRequestsById.ContainsKey(Id));
3605
3606 PendingRequest PendingRequest;
3607
3608 if (!(IqCallback is null))
3609 {
3610 PendingRequest = new PendingRequest(Id, RetryTimeout, NrRetries, DropOff, MaxRetryTimeout)
3611 {
3612 IqCallback = IqCallback,
3613 ResendCallback = ResendCallback,
3614 State = State,
3615 From = From,
3616 To = To,
3617 Id = Id,
3618 Language = Language,
3619 ContentXml = ContentXml,
3620 ShortTermCacheKey = ShortTermCacheKey
3621 };
3622 }
3623 else
3624 {
3625 PendingRequest = new PendingRequest(Id, RetryTimeout, NrRetries, DropOff, MaxRetryTimeout)
3626 {
3627 PresenceCallback = PresenceCallback,
3628 ResendCallback = ResendCallback,
3629 State = State,
3630 From = From,
3631 To = To,
3632 Id = Id,
3633 Language = Language,
3634 ContentXml = ContentXml
3635 };
3636 }
3637
3638 DateTime TP = PendingRequest.Timeout;
3639
3640 if (this.pendingRequestsByTimeout.ContainsKey(TP))
3641 {
3642 Random Rnd = new Random();
3643
3644 while (this.pendingRequestsByTimeout.ContainsKey(TP))
3645 TP = TP.AddTicks(Rnd.Next(100) + 1);
3646 }
3647
3648 PendingRequest.Timeout = TP;
3649
3650 this.pendingRequestsById[Id] = PendingRequest;
3651 this.pendingRequestsByTimeout[TP] = PendingRequest;
3652
3653 return PendingRequest;
3654 }
3655 }
3656
3668 public Task<bool> SendIqRequest(string Type, string From, string To, string Language,
3669 string ContentXml, EventHandlerAsync<IqResultEventArgs> Callback, object State)
3670 {
3671 return this.SendIqRequest(Type, new XmppAddress(From), new XmppAddress(To),
3672 Language, ContentXml, false, Callback, State);
3673 }
3674
3688 public Task<bool> SendIqRequest(string Type, string From, string To, string Language,
3689 string ContentXml, bool CheckShortTermCache,
3690 EventHandlerAsync<IqResultEventArgs> Callback, object State)
3691 {
3692 return this.SendIqRequest(Type, new XmppAddress(From), new XmppAddress(To),
3693 Language, ContentXml, CheckShortTermCache, Callback, State);
3694 }
3695
3707 public Task<bool> SendIqRequest(string Type, XmppAddress From, XmppAddress To,
3708 string Language, string ContentXml, EventHandlerAsync<IqResultEventArgs> Callback,
3709 object State)
3710 {
3711 return this.SendIqRequest(Type, From, To, Language, ContentXml, false, Callback, State);
3712 }
3713
3727 public async Task<bool> SendIqRequest(string Type, XmppAddress From, XmppAddress To,
3728 string Language, string ContentXml, bool CheckShortTermCache,
3729 EventHandlerAsync<IqResultEventArgs> Callback, object State)
3730 {
3731 IRecipient Recipient = await this.TryGetRecipient(To, From);
3732
3733 if (Recipient is null)
3734 {
3735 IqResultEventArgs e = new IqResultEventArgs(null, string.Empty, From, To, Language, false, State);
3736 await Callback.Raise(this, e);
3737
3738 return false;
3739 }
3740
3741 string Key = null;
3742
3743 if (CheckShortTermCache)
3744 {
3745 StringBuilder sb = new StringBuilder();
3746
3747 sb.AppendLine(Type);
3748 sb.AppendLine(From.Address.Value);
3749 sb.AppendLine(To.Address.Value);
3750 sb.AppendLine(Language);
3751 sb.AppendLine(ContentXml);
3752
3753 Key = sb.ToString();
3754
3755 if (this.shortTermCache.TryGetValue(Key, out IqResultEventArgs Result))
3756 {
3757 await Callback.Raise(this, Result);
3758 return true;
3759 }
3760 }
3761
3762 PendingRequest Request = this.PrepareRequest(Callback, null, async (Sender, e) =>
3763 {
3764 await Recipient.IQ(Type, e.Request.Id, e.Request.To, e.Request.From,
3765 e.Request.Language, e.Request.ContentXml, this);
3766
3767 }, State, this.defaultRetryTimeout, this.defaultNrRetries, this.defaultDropOff,
3768 this.defaultMaxRetryTimeout, From, To, Language, ContentXml, Key);
3769
3770 await Recipient.IQ(Type, Request.Id, To, From, Language, ContentXml, this);
3771
3772 return true;
3773 }
3774
3784 public Task<IqResultEventArgs> IqRequest(string Type, string From, string To,
3785 string Language, string ContentXml)
3786 {
3787 return this.IqRequest(Type, new XmppAddress(From), new XmppAddress(To), Language, ContentXml, false);
3788 }
3789
3801 public Task<IqResultEventArgs> IqRequest(string Type, string From, string To,
3802 string Language, string ContentXml, bool CheckShortTermCache)
3803 {
3804 return this.IqRequest(Type, new XmppAddress(From), new XmppAddress(To), Language,
3805 ContentXml, CheckShortTermCache);
3806 }
3807
3817 public Task<IqResultEventArgs> IqRequest(string Type, XmppAddress From, XmppAddress To, string Language, string ContentXml)
3818 {
3819 return this.IqRequest(Type, From, To, Language, ContentXml, false);
3820 }
3821
3833 public async Task<IqResultEventArgs> IqRequest(string Type, XmppAddress From, XmppAddress To,
3834 string Language, string ContentXml, bool CheckShortTermCache)
3835 {
3836 TaskCompletionSource<IqResultEventArgs> Result = new TaskCompletionSource<IqResultEventArgs>();
3837
3838 if (await this.SendIqRequest(Type, From, To, Language, ContentXml, CheckShortTermCache,
3839 (Sender, e) =>
3840 {
3841 Result.TrySetResult(e);
3842 return Task.CompletedTask;
3843
3844 }, null))
3845 {
3846 return await Result.Task;
3847 }
3848 else
3849 throw new InvalidOperationException("Unable to send request.");
3850 }
3851
3862 public Task<bool> SendMessage(string Type, string Id, string From, string To, string Language, string ContentXml)
3863 {
3864 return this.SendMessage(Type, Id, new XmppAddress(From), new XmppAddress(To), Language, ContentXml);
3865 }
3866
3877 public async Task<bool> SendMessage(string Type, string Id, XmppAddress From, XmppAddress To, string Language, string ContentXml)
3878 {
3879 IRecipient Recipient = await this.TryGetRecipient(To, From);
3880
3881 if (Recipient is null)
3882 return false;
3883 else
3884 {
3885 await Recipient.Message(Type, Id, To, From, Language, ContentXml, this);
3886 return true;
3887 }
3888 }
3889
3890 private async void SecondTimerCallback(object State)
3891 {
3892 try
3893 {
3894 LinkedList<KeyValuePair<DateTime, IS2SEndpoint>> ToRemove = null;
3895 List<PendingRequest> Retries = null;
3896 DateTime Now = DateTime.Now;
3897 DateTime TP;
3898 bool Retry;
3899
3900 lock (this.synchObject)
3901 {
3902 foreach (KeyValuePair<DateTime, PendingRequest> P in this.pendingRequestsByTimeout)
3903 {
3904 if (P.Key <= Now)
3905 {
3906 Retries ??= new List<PendingRequest>();
3907 Retries.Add(P.Value);
3908 }
3909 else
3910 break;
3911 }
3912
3913 if (!(this.temporaryConnections is null))
3914 {
3915 foreach (KeyValuePair<DateTime, IS2SEndpoint> P in this.temporaryConnections)
3916 {
3917 if (P.Key < Now)
3918 {
3919 ToRemove ??= new LinkedList<KeyValuePair<DateTime, IS2SEndpoint>>();
3920 ToRemove.AddLast(P);
3921 }
3922 else
3923 break;
3924 }
3925
3926 if (!(ToRemove is null))
3927 {
3928 foreach (KeyValuePair<DateTime, IS2SEndpoint> P in ToRemove)
3929 this.temporaryConnections.Remove(P.Key);
3930
3931 if (this.temporaryConnections.Count == 0)
3932 this.temporaryConnections = null;
3933 }
3934 }
3935 }
3936
3937 if (!(Retries is null))
3938 {
3939 foreach (PendingRequest Request in Retries)
3940 {
3941 lock (this.synchObject)
3942 {
3943 this.pendingRequestsByTimeout.Remove(Request.Timeout);
3944
3945 if (Retry = Request.CanRetry())
3946 {
3947 TP = Request.Timeout;
3948
3949 if (this.pendingRequestsByTimeout.ContainsKey(TP))
3950 {
3951 Random Rnd = new Random();
3952
3953 while (this.pendingRequestsByTimeout.ContainsKey(TP))
3954 TP = TP.AddTicks(Rnd.Next(100) + 1);
3955 }
3956
3957 Request.Timeout = TP;
3958
3959 this.pendingRequestsByTimeout[Request.Timeout] = Request;
3960 }
3961 else
3962 this.pendingRequestsById.Remove(Request.Id);
3963 }
3964
3965 try
3966 {
3967 if (Retry)
3968 await Request.ResendCallback.Raise(this, new PendingRequestEventArgs(Request));
3969 else
3970 {
3971 if (!(Request.IqCallback is null))
3972 {
3973 StringBuilder Xml = new StringBuilder();
3974
3975 Xml.Append("<iq xmlns='jabber:server' type='error' from='");
3976 Xml.Append(Request.To);
3977 Xml.Append("' id='");
3978 Xml.Append(Request.Id);
3979 Xml.Append("'><error type='wait'><recipient-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>");
3980 Xml.Append("<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Timeout.</text></error></iq>");
3981
3982 XmlDocument Doc = XML.ParseXml(Xml.ToString(), true);
3983
3984 IqResultEventArgs e = new IqResultEventArgs(Doc.DocumentElement, Request.Id, XmppAddress.Empty, Request.To, string.Empty, false,
3985 Request.State);
3986
3987 await Request.IqCallback.Raise(this, e);
3988 }
3989 else if (!(Request.PresenceCallback is null))
3990 {
3991 Stanza Stanza = ToStanza("presence", "error", Request.Id, XmppAddress.Empty, Request.To, string.Empty,
3992 "<error type='wait'><recipient-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3993 "<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Timeout.</text></error>");
3994
3995 PresenceEventArgs e = new PresenceEventArgs(null, "error", Request.Id, XmppAddress.Empty, Request.To, string.Empty,
3996 Stanza, Request.State);
3997
3998 await Request.PresenceCallback.Raise(this, e);
3999 }
4000 }
4001 }
4002 catch (Exception ex)
4003 {
4004 Log.Exception(ex);
4005 }
4006 }
4007 }
4008
4009 if (!(ToRemove is null))
4010 {
4011 foreach (KeyValuePair<DateTime, IS2SEndpoint> P in ToRemove)
4012 await P.Value.DisposeAsync("Removing temporary connection.");
4013 }
4014 }
4015 catch (Exception ex)
4016 {
4017 Log.Exception(ex);
4018 }
4019 }
4020
4021 #region ISender
4022
4031 public virtual async Task<string> IqError(string Id, XmppAddress To, XmppAddress From, Exception ex)
4032 {
4033 GetErrorInformation(ex, out _, out string Xml);
4034 Stanza Stanza = ToStanza("iq", "error", Id, To, From, string.Empty, Xml);
4035
4036 await this.ProcessResponse("error", Id, To, From, string.Empty, true, false, Stanza, this);
4037
4038 return Xml;
4039 }
4040
4049 public virtual async Task<bool> IqError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
4050 {
4051 Stanza Stanza = ToStanza("iq", "error", Id, To, From, string.Empty, ErrorXml);
4052
4053 await this.ProcessResponse("error", Id, To, From, string.Empty, true, false, Stanza, this);
4054
4055 return true;
4056 }
4057
4066 public virtual async Task<bool> IqResult(string Id, XmppAddress To, XmppAddress From, string ResultXml)
4067 {
4068 Stanza Stanza = ToStanza("iq", "result", Id, To, From, string.Empty, ResultXml);
4069
4070 await this.ProcessResponse("result", Id, To, From, string.Empty, true, false, Stanza, this);
4071
4072 return true;
4073 }
4074
4083 public virtual async Task<bool> PresenceError(string Id, XmppAddress To, XmppAddress From, Exception ex)
4084 {
4085 GetErrorInformation(ex, out _, out string Xml);
4086 Stanza Stanza = ToStanza("presence", "error", Id, To, From, string.Empty, Xml);
4087
4088 await this.ProcessResponse("error", Id, To, From, string.Empty, false, true, Stanza, this);
4089
4090 return true;
4091 }
4092
4101 public virtual async Task<bool> PresenceError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
4102 {
4103 Stanza Stanza = ToStanza("presence", "error", Id, To, From, string.Empty, ErrorXml);
4104
4105 await this.ProcessResponse("error", Id, To, From, string.Empty, false, true, Stanza, this);
4106
4107 return true;
4108 }
4109
4120 public virtual async Task<bool> Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
4121 {
4122 Stanza Stanza = ToStanza("presence", Type, Id, To, From, string.Empty, ContentXml);
4123
4124 await this.ProcessResponse(Type, Id, To, From, string.Empty, true, false, Stanza, this);
4125
4126 return true;
4127 }
4128
4139 public virtual Task<bool> Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
4140 {
4141 return Task.FromResult(true); // Do nothing by default.
4142 }
4143
4152 public virtual Task<bool> MessageError(string Id, XmppAddress To, XmppAddress From, Exception ex)
4153 {
4154 return Task.FromResult(true); // Do nothing by default.
4155 }
4156
4157 #endregion
4158
4159 #endregion
4160
4161 #region Roster
4162
4163 private async Task RosterQuery(object Sender, IqEventArgs e)
4164 {
4166 {
4167 await e.IqErrorForbidden(e.To, "You can only access your own roster.", "en");
4168 return;
4169 }
4170
4172 IEnumerable<IRosterItem> Roster = await this.persistenceLayer.GetRoster(UserName);
4173 if (Roster is null)
4174 {
4175 await e.IqErrorItemNotFound(e.To, "Roster not found.", "en");
4176 return;
4177 }
4178
4179 string Ver = XML.Attribute(e.Query, "ver");
4180
4181 List<IRosterItem> A = new List<IRosterItem>();
4182 A.AddRange(Roster);
4183 A.Sort((i1, i2) => i1.BareJid.CompareTo(i2.BareJid));
4184
4185 StringBuilder Xml = new StringBuilder();
4186
4187 foreach (IRosterItem Item in A)
4188 this.Serialize(Xml, Item);
4189
4190 string s = Xml.ToString();
4191 string Hash = Hashes.ComputeSHA1HashString(Encoding.UTF8.GetBytes(s));
4192
4193 if (Ver == Hash)
4194 await e.IqResult(string.Empty, e.From.ToBareJID());
4195 else
4196 {
4197 Xml.Clear();
4198 Xml.Append("<query xmlns='");
4199 Xml.Append(RosterNamespace);
4200 Xml.Append("' ver='");
4201 Xml.Append(Hash);
4202 Xml.Append("'>");
4203 Xml.Append(s);
4204 Xml.Append("</query>");
4205
4206 await e.IqResult(Xml.ToString(), e.From.ToBareJID());
4207 }
4208 }
4209
4210 private void Serialize(StringBuilder Xml, IRosterItem Item)
4211 {
4212 Xml.Append("<item jid='");
4213 Xml.Append(XML.Encode(Item.BareJid));
4214
4215 if (Item.PendingSubscription && (Item.Subscription != SubscriptionStatus.both && Item.Subscription != SubscriptionStatus.to))
4216 Xml.Append("' ask='subscribe");
4217
4218 if (!string.IsNullOrEmpty(Item.Name))
4219 {
4220 Xml.Append("' name='");
4221 Xml.Append(XML.Encode(Item.Name));
4222 }
4223
4224 if (Item.Subscription != SubscriptionStatus.none)
4225 {
4226 Xml.Append("' subscription='");
4227 Xml.Append(Item.Subscription.ToString());
4228 }
4229
4230 string[] Groups = Item.Groups;
4231 if (Groups is null || Groups.Length == 0)
4232 Xml.Append("'/>");
4233 else
4234 {
4235 Xml.Append("'>");
4236
4237 foreach (string Group in Groups)
4238 {
4239 Xml.Append("<group>");
4240 Xml.Append(XML.Encode(Group));
4241 Xml.Append("</group>");
4242 }
4243
4244 Xml.Append("</item>");
4245 }
4246 }
4247
4248 private async Task RosterSet(object Sender, IqEventArgs e)
4249 {
4251 {
4252 await e.IqErrorForbidden(e.To, "You can only access your own roster.", "en");
4253 return;
4254 }
4255
4256 XmlElement E;
4257 List<string> Groups = null;
4259 CaseInsensitiveString Jid = null;
4260 string Name = null;
4261 string s;
4262 SubscriptionStatus Subscription = SubscriptionStatus.none;
4264 bool First = true;
4265
4266 foreach (XmlNode N in e.Query.ChildNodes)
4267 {
4268 E = N as XmlElement;
4269 if (E is null)
4270 continue;
4271
4272 if (E.LocalName != "item")
4273 continue;
4274
4275 if (First)
4276 First = false;
4277 else
4278 {
4279 await e.IqErrorBadRequest(e.To, "Multiple items not allowed.", "en");
4280 return;
4281 }
4282
4283 Jid = XML.Attribute(E, "jid");
4284 Name = XML.Attribute(E, "name");
4285 PendingSubscription = XML.Attribute(E, "ask") == "subscribe";
4286 Subscription = XML.Attribute(E, "subscription", SubscriptionStatus.none);
4287
4288 if (Name.Length > MaxNameLength)
4289 {
4290 await e.IqErrorNotAcceptable(e.To, "Name too long.", "en");
4291 return;
4292 }
4293
4294 foreach (XmlNode N2 in N.ChildNodes)
4295 {
4296 E = N2 as XmlElement;
4297 if (E is null)
4298 continue;
4299
4300 if (E.LocalName != "group")
4301 continue;
4302
4303 Groups ??= new List<string>();
4304
4305 s = E.InnerText;
4306 if (string.IsNullOrEmpty(s) || s.Length > MaxGroupLength)
4307 {
4308 await e.IqErrorNotAcceptable(e.To, "Group name too long.", "en");
4309 return;
4310 }
4311 else if (Groups.Contains(s))
4312 {
4313 await e.IqErrorBadRequest(e.To, "Group name occurred twice: " + s, "en");
4314 return;
4315 }
4316
4317 Groups.Add(s);
4318 }
4319 }
4320
4322 {
4323 await e.IqErrorBadRequest(e.To, "JID not specificed.", "en");
4324 return;
4325 }
4326
4327 IRosterItem Item = await this.persistenceLayer.SetRosterItem(UserName, Jid, Name, null, null, Groups?.ToArray());
4328 if (!(Item is null))
4329 {
4330 if (Subscription == SubscriptionStatus.remove)
4331 {
4332 if (await this.persistenceLayer.RemoveRosterItem(UserName, Jid))
4333 {
4334 if (Item.Subscription == SubscriptionStatus.both || Item.Subscription == SubscriptionStatus.from)
4335 await this.PushPresence(new XmppAddress(Item.BareJid), "unavailable", string.Empty, e.From, string.Empty, (Stanza)null, true, this);
4336
4337 if (Item.Subscription == SubscriptionStatus.both || Item.Subscription == SubscriptionStatus.to)
4338 {
4339 await this.PushPresence(new XmppAddress(Item.BareJid), "unsubscribe", string.Empty, e.From.ToBareJID(), string.Empty, (Stanza)null, true, this);
4340 await this.PushPresence(e.From.ToBareJID(), "unsubscribed", string.Empty, new XmppAddress(Item.BareJid), string.Empty, (Stanza)null, true, this);
4341 }
4342
4343 if (Item.Subscription == SubscriptionStatus.both || Item.Subscription == SubscriptionStatus.from)
4344 {
4345 await this.PushPresence(e.From.ToBareJID(), "unsubscribe", string.Empty, new XmppAddress(Item.BareJid), string.Empty, (Stanza)null, true, this);
4346 await this.PushPresence(new XmppAddress(Item.BareJid), "unsubscribed", string.Empty, e.From.ToBareJID(), string.Empty, (Stanza)null, true, this);
4347 }
4348
4349 await e.IqResult(string.Empty, e.To);
4350
4351 StringBuilder Xml = new StringBuilder();
4352
4353 Xml.Append("<query xmlns='");
4354 Xml.Append(RosterNamespace);
4355 Xml.Append("'><item jid='");
4356 Xml.Append(Jid);
4357 Xml.Append("' subscription='remove'></item></query>");
4358
4359 await this.PushIq("set", false, XmppAddress.Empty, string.Empty, Xml.ToString(), this.GetClientConnections(e.From.BareJid));
4360 }
4361 else
4362 await e.IqErrorItemNotFound(e.To, string.Empty, string.Empty);
4363 }
4364 else
4365 {
4366 await e.IqResult(string.Empty, e.To);
4367 await this.PushIq(ClientConnection.BareJid, Item);
4368 }
4369 }
4370 else
4371 await e.IqErrorItemNotFound(e.To, string.Empty, string.Empty);
4372 }
4373
4374 private Task PushIq(CaseInsensitiveString BareJid, IRosterItem Item)
4375 {
4376 StringBuilder Xml = new StringBuilder();
4377
4378 Xml.Append("<query xmlns='");
4379 Xml.Append(RosterNamespace);
4380 Xml.Append("'>");
4381
4382 this.Serialize(Xml, Item);
4383
4384 Xml.Append("</query>");
4385
4386 return this.PushIq("set", false, XmppAddress.Empty, string.Empty, Xml.ToString(), this.GetClientConnections(BareJid));
4387 }
4388
4389 private async Task PushIq(string Type, bool IncludeTo, XmppAddress From, string Language, string Xml, IClientConnection[] Connections)
4390 {
4391 if (!(Connections is null))
4392 {
4393 foreach (IClientConnection Connection in Connections)
4394 {
4395 if (Connection.State == XmppConnectionState.Active)
4396 {
4397 try
4398 {
4399 await Connection.IQ(Type, this.NewId(16), IncludeTo ? Connection.Address : XmppAddress.Empty, From, Language, Xml, this);
4400 }
4401 catch (Exception ex)
4402 {
4403 try
4404 {
4405 Connection.Exception(ex);
4406 await Connection.DisposeAsync();
4407 }
4408 catch (Exception ex2)
4409 {
4410 Log.Exception(ex2);
4411 }
4412 }
4413 }
4414 }
4415 }
4416 }
4417
4418 private async Task PushPresence(string Type, string Id, XmppAddress From, string Language, string Xml, IClientConnection[] Connections,
4419 bool UseBareJids)
4420 {
4421 if (!(Connections is null))
4422 {
4423 foreach (IClientConnection Connection in Connections)
4424 {
4425 if (Connection.State == XmppConnectionState.Active)
4426 {
4427 try
4428 {
4429 await Connection.Presence(Type, string.IsNullOrEmpty(Id) ? this.NewId(16) : Id,
4430 UseBareJids ? Connection.BareAddress : Connection.Address, From, Language, Xml, this);
4431 }
4432 catch (Exception ex)
4433 {
4434 try
4435 {
4436 Connection.Exception(ex);
4437 await Connection.DisposeAsync();
4438 }
4439 catch (Exception ex2)
4440 {
4441 Log.Exception(ex2);
4442 }
4443 }
4444 }
4445 }
4446 }
4447 }
4448
4449 #endregion
4450
4451 #region Ping XEP-0199
4452
4453 private Task PingGet(object Sender, IqEventArgs e)
4454 {
4455 e.IqResult(string.Empty, e.To);
4456
4457 return Task.CompletedTask;
4458 }
4459
4460 #endregion
4461
4462 #region Discovery XEP-0030
4463
4464 private Task DiscoveryQueryGet(object Sender, IqEventArgs e)
4465 {
4466 XmlElement E = e.Query;
4467 string Node = XML.Attribute(E, "node");
4468 if (!string.IsNullOrEmpty(Node))
4469 {
4470 e.IqErrorItemNotFound(e.To, "Node not found.", "en");
4471 return Task.CompletedTask;
4472 }
4473
4474 StringBuilder Xml = new StringBuilder();
4475
4476 Xml.Append("<query xmlns='");
4477 Xml.Append(DiscoveryNamespace);
4478 Xml.Append("'><identity category='server' type='im'/>"); // https://xmpp.org/registrar/disco-categories.html
4479
4480 lock (this.synchObject)
4481 {
4482 foreach (string Feature in this.features.Keys)
4483 {
4484 Xml.Append("<feature var='");
4485 Xml.Append(XML.Encode(Feature));
4486 Xml.Append("'/>");
4487 }
4488 }
4489
4490 Xml.Append("</query>");
4491
4492 e.IqResult(Xml.ToString(), e.To);
4493
4494 return Task.CompletedTask;
4495 }
4496
4497 private Task DiscoveryQueryItemsGet(object Sender, IqEventArgs e)
4498 {
4499 XmlElement E = e.Query;
4500 string Node = XML.Attribute(E, "node");
4501 if (!string.IsNullOrEmpty(Node))
4502 {
4503 e.IqErrorItemNotFound(e.To, "Node not found.", "en");
4504 return Task.CompletedTask;
4505 }
4506
4507 StringBuilder Xml = new StringBuilder();
4508
4509 Xml.Append("<query xmlns='");
4510 Xml.Append(DiscoveryItemsNamespace);
4511 Xml.Append("'>");
4512
4513 foreach (IComponent Component in this.componentsStatic)
4514 {
4515 Xml.Append("<item jid='");
4516 Xml.Append(XML.Encode(Component.Subdomain));
4517 Xml.Append('.');
4518 Xml.Append(XML.Encode(e.To.IsEmpty ? e.From.Domain : e.To.Domain));
4519 Xml.Append("' name='");
4520 Xml.Append(XML.Encode(Component.Name));
4521 Xml.Append("'/>");
4522 }
4523
4524 Xml.Append("</query>");
4525
4526 e.IqResult(Xml.ToString(), e.To);
4527
4528 return Task.CompletedTask;
4529 }
4530
4531 #endregion
4532
4533 #region Software Version (XEP-0092)
4534
4535 private Task SoftwareVersionGet(object Sender, IqEventArgs e)
4536 {
4537 StringBuilder Xml = new StringBuilder();
4538
4539 Xml.Append("<query xmlns='");
4540 Xml.Append(SoftwareVersionNamespace);
4541 Xml.Append("'><name>");
4542 Xml.Append(XML.Encode(this.serverName));
4543 Xml.Append("</name><version>");
4544 Xml.Append(XML.Encode(this.serverVersion));
4545 Xml.Append("</version><os>");
4546 Xml.Append(XML.Encode(this.serverOS));
4547 Xml.Append("</os></query>");
4548
4549 e.IqResult(Xml.ToString(), e.To);
4550
4551 return Task.CompletedTask;
4552 }
4553
4554 #endregion
4555
4556 #region Entity Time (XEP-0202)
4557
4558 private Task TimeGet(object Sender, IqEventArgs e)
4559 {
4560 StringBuilder Xml = new StringBuilder();
4561 DateTimeOffset Time = DateTimeOffset.Now;
4562 TimeSpan TimeZone = Time.Offset;
4563 DateTime Utc = Time.UtcDateTime;
4564
4565 Xml.Append("<time xmlns='");
4566 Xml.Append(TimeNamespace);
4567 Xml.Append("'><tzo>");
4568
4569 if (TimeZone == TimeSpan.Zero)
4570 Xml.Append("Z");
4571 else
4572 {
4573 if (TimeZone < TimeSpan.Zero)
4574 {
4575 Xml.Append('-');
4576 TimeZone = -TimeZone;
4577 }
4578 else
4579 Xml.Append('+');
4580
4581 Xml.Append(TimeZone.Hours.ToString("D2"));
4582 Xml.Append(':');
4583 Xml.Append(TimeZone.Minutes.ToString("D2"));
4584 }
4585
4586 Xml.Append("</tzo><utc>");
4587 Xml.Append(XML.Encode(Utc));
4588 Xml.Append("</utc></time>");
4589
4590 e.IqResult(Xml.ToString(), e.To);
4591
4592 return Task.CompletedTask;
4593 }
4594
4595 #endregion
4596
4597 #region vCard (XEP-0054)
4598
4599 private async Task VCardGet(object Sender, IqEventArgs e)
4600 {
4601 if (!e.From.HasAccount || !this.IsServerDomain(e.From.Domain, true))
4602 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
4603 else
4604 {
4605 string s = await this.persistenceLayer.GetVCard(e.From.Account);
4606
4607 if (string.IsNullOrEmpty(s))
4608 await e.IqErrorItemNotFound(e.To, "vCard not found.", "en");
4609 else
4610 await e.IqResult("<vCard xmlns='" + VCardNamespace + "'>" + s + "</vCard>", e.To);
4611 }
4612 }
4613
4614 private async Task VCardSet(object Sender, IqEventArgs e)
4615 {
4616 if (!e.From.HasAccount || !this.IsServerDomain(e.From.Domain, true))
4617 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
4618 else
4619 {
4620 CaseInsensitiveString UserName = e.From.Account;
4621 string VCard = e.Query.InnerXml;
4622
4623 if (!await this.persistenceLayer.SetVCard(UserName, VCard))
4624 await e.IqErrorItemNotFound(e.To, "Unable to set vCard.", "en");
4625 else
4626 await e.IqResult(string.Empty, e.To);
4627 }
4628 }
4629
4630 #endregion
4631
4632 #region Register (XEP-0077) & Form signatures (XEP-0348)
4633
4634 private const string RegistrationInstructions = "Register your new account, by filling in the details below.";
4635
4636 internal async Task<bool> CanRegister(IClientConnection Connection)
4637 {
4638 DateTime? Next = await this.GetEarliestLoginOpportunity(Connection);
4639 return !Next.HasValue;
4640 }
4641
4642 internal async Task<bool> CanRegister(IClientConnection Connection, IqEventArgs e)
4643 {
4644 DateTime? Next = await this.GetEarliestLoginOpportunity(Connection);
4645
4646 if (Next.HasValue)
4647 {
4648 StringBuilder sb = new StringBuilder();
4649 DateTime TP = Next.Value;
4650 DateTime Today = DateTime.Today;
4651
4652 if (Next.Value == DateTime.MaxValue)
4653 {
4654 sb.Append("This endpoint (");
4655 sb.Append(Connection.RemoteEndPoint);
4656 sb.Append(") has been blocked from the system.");
4657
4658 await e.IqErrorForbidden(e.To, sb.ToString(), "en");
4659 return false;
4660 }
4661 else
4662 {
4663 sb.Append("Too many failed login attempts in a row registered. Try again after ");
4664 sb.Append(TP.ToLongTimeString());
4665
4666 if (TP.Date != Today)
4667 {
4668 if (TP.Date == Today.AddDays(1))
4669 sb.Append(" tomorrow");
4670 else
4671 {
4672 sb.Append(", ");
4673 sb.Append(TP.ToShortDateString());
4674 }
4675 }
4676
4677 sb.Append(". Remote Endpoint: ");
4678 sb.Append(Connection.RemoteEndPoint);
4679
4680 await e.IqErrorNotAllowed(e.To, sb.ToString(), "en");
4681 return false;
4682 }
4683 }
4684
4685 return true;
4686 }
4687
4688 private async Task RegisterGet(object Sender, IqEventArgs e)
4689 {
4690 if (!(e.Sender is IClientConnection Connection))
4691 {
4692 await e.IqErrorForbidden(e.To, "Only clients can register.", "en");
4693 return;
4694 }
4695
4696 if (!await this.CanRegister(Connection, e))
4697 return;
4698
4699 StringBuilder Xml = new StringBuilder();
4700 byte[] Token = GetRandomNumbers(32);
4701 byte[] Secret = GetRandomNumbers(32);
4702
4703 Xml.Append("<query xmlns='");
4704 Xml.Append(RegisterNamespace);
4705 Xml.Append("'><instructions>");
4706 Xml.Append(RegistrationInstructions);
4707 Xml.Append("</instructions>");
4708 Xml.Append("<x xmlns='jabber:x:data' type='form'>");
4709 Xml.Append("<title>Contest Registration</title>");
4710 Xml.Append("<instructions>");
4711 Xml.Append(RegistrationInstructions);
4712 Xml.Append("</instructions>");
4713 Xml.Append("<field type='hidden' var='FORM_TYPE'>");
4714 Xml.Append("<value>urn:xmpp:xdata:signature:oauth1</value>");
4715 Xml.Append("</field>");
4716 Xml.Append("<field type='text-single' label='User Name:' var='username'>");
4717 Xml.Append("<required/>");
4718 Xml.Append("</field>");
4719 Xml.Append("<field type='text-private' label='Password:' var='password'>");
4720 Xml.Append("<required/>");
4721 Xml.Append("</field>");
4722 Xml.Append("<field type='text-single' label='Email Address' var='email'/>");
4723 Xml.Append("<field type='text-single' label='Phone Number' var='phone'/>");
4724 Xml.Append("<field type='hidden' var='oauth_version'>");
4725 Xml.Append("<value>1.0</value>");
4726 Xml.Append("</field>");
4727 Xml.Append("<field type='hidden' var='oauth_signature_method'>");
4728 Xml.Append("<value>HMAC-SHA1</value>");
4729 Xml.Append("</field>");
4730 Xml.Append("<field type='hidden' var='oauth_token'>");
4731 Xml.Append("<value>");
4732 Xml.Append(Hashes.BinaryToString(Token));
4733 Xml.Append("</value>");
4734 Xml.Append("</field>");
4735 Xml.Append("<field type='hidden' var='oauth_token_secret'>");
4736 Xml.Append("<value>");
4737 Xml.Append(Hashes.BinaryToString(Secret));
4738 Xml.Append("</value>");
4739 Xml.Append("</field>");
4740 Xml.Append("<field type='hidden' var='oauth_nonce'>");
4741 Xml.Append("<value/>");
4742 Xml.Append("</field>");
4743 Xml.Append("<field type='hidden' var='oauth_timestamp'>");
4744 Xml.Append("<value/>");
4745 Xml.Append("</field>");
4746 Xml.Append("<field type='hidden' var='oauth_consumer_key'>");
4747 Xml.Append("<value/>");
4748 Xml.Append("</field>");
4749 Xml.Append("<field type='hidden' var='oauth_signature'>");
4750 Xml.Append("<value/>");
4751 Xml.Append("</field>");
4752 Xml.Append("</x>");
4753 Xml.Append("</query>");
4754
4755 await e.IqResult(Xml.ToString(), e.To);
4756 }
4757
4758 private async Task RegisterSet(object Sender, IqEventArgs e)
4759 {
4760 if (!(e.Sender is IClientConnection Connection))
4761 {
4762 await e.IqErrorForbidden(e.To, "Only clients can register.", "en");
4763 return;
4764 }
4765
4766 if (!await this.CanRegister(Connection, e))
4767 return;
4768
4769 CaseInsensitiveString UserName = null;
4770 string Password = null;
4771 bool Remove = false;
4772 int Count = 0;
4773
4774 foreach (XmlNode N in e.Query.ChildNodes)
4775 {
4776 if (!(N is XmlElement E))
4777 continue;
4778
4779 Count++;
4780 if (E.NamespaceURI == RegisterNamespace)
4781 {
4782 switch (E.LocalName)
4783 {
4784 case "username":
4785 UserName = E.InnerText;
4786 break;
4787
4788 case "password":
4789 Password = E.InnerText;
4790 break;
4791
4792 case "remove":
4793 Remove = true;
4794 break;
4795 }
4796 }
4797 else if (E.LocalName == "x" && E.NamespaceURI == DataFormsNamespace && XML.Attribute((XmlElement)N, "type") == "submit")
4798 {
4799 if (Connection.State != XmppConnectionState.Authenticating)
4800 {
4801 await e.IqErrorNotAllowed(e.To, "Step only allowed during authentication phase.", "en");
4802 return;
4803 }
4804
4805 string FormType = null;
4806 CaseInsensitiveString EMail = null;
4807 CaseInsensitiveString PhoneNr = null;
4808 string OAuthVersion = null;
4809 string OAuthSignatureMethod = null;
4810 string OAuthToken = null;
4811 string OAuthTokenSecret = null;
4812 string OAuthNonce = null;
4813 string OAuthTimestamp = null;
4814 string OAuthConsumerKey = null;
4815 string OAuthSignature = null;
4816
4817 foreach (XmlNode N2 in E.ChildNodes)
4818 {
4819 if (N2.LocalName == "field")
4820 {
4821 string Var = XML.Attribute((XmlElement)N2, "var");
4822 string Value = null;
4823
4824 foreach (XmlNode N3 in N2.ChildNodes)
4825 {
4826 if (N3.LocalName == "value")
4827 {
4828 Value = N3.InnerText;
4829 break;
4830 }
4831 }
4832
4833 if (Value is null)
4834 continue;
4835
4836 switch (Var)
4837 {
4838 case "FORM_TYPE":
4839 FormType = Value;
4840 break;
4841
4842 case "username":
4843 UserName = Value;
4844 break;
4845
4846 case "password":
4847 Password = Value;
4848 break;
4849
4850 case "email":
4851 EMail = Value;
4852 break;
4853
4854 case "phone":
4855 PhoneNr = Value;
4856 break;
4857
4858 case "oauth_version":
4859 OAuthVersion = Value;
4860 break;
4861
4862 case "oauth_signature_method":
4863 OAuthSignatureMethod = Value;
4864 break;
4865
4866 case "oauth_token":
4867 OAuthToken = Value;
4868 break;
4869
4870 case "oauth_token_secret":
4871 OAuthTokenSecret = Value;
4872 break;
4873
4874 case "oauth_nonce":
4875 OAuthNonce = Value;
4876 break;
4877
4878 case "oauth_timestamp":
4879 OAuthTimestamp = Value;
4880 break;
4881
4882 case "oauth_consumer_key":
4883 OAuthConsumerKey = Value;
4884 break;
4885
4886 case "oauth_signature":
4887 OAuthSignature = Value;
4888 break;
4889
4890 default:
4891 break;
4892 }
4893 }
4894 }
4895
4896 bool Signed = false;
4897 bool Logged = false;
4898
4899 if (FormType == "urn:xmpp:xdata:signature:oauth1" && OAuthVersion == "1.0" && !string.IsNullOrEmpty(UserName) &&
4900 !string.IsNullOrEmpty(Password) && !(EMail is null) && !(PhoneNr is null) && !string.IsNullOrEmpty(OAuthSignatureMethod) &&
4901 !string.IsNullOrEmpty(OAuthToken) && !string.IsNullOrEmpty(OAuthTokenSecret) && !string.IsNullOrEmpty(OAuthNonce) &&
4902 !string.IsNullOrEmpty(OAuthTimestamp) && !string.IsNullOrEmpty(OAuthConsumerKey) && !string.IsNullOrEmpty(OAuthSignature))
4903 {
4904 string KeySecret = await this.persistenceLayer.GetApiKeySecret(OAuthConsumerKey);
4905 if (!string.IsNullOrEmpty(KeySecret))
4906 {
4907 StringBuilder PStr = new StringBuilder();
4908
4909 PStr.Append("email=");
4910 PStr.Append(OAuthEncode(EMail));
4911 PStr.Append("&FORM_TYPE=");
4912 PStr.Append(OAuthEncode(FormType));
4913 PStr.Append("&oauth_consumer_key=");
4914 PStr.Append(OAuthEncode(OAuthConsumerKey));
4915 PStr.Append("&oauth_nonce=");
4916 PStr.Append(OAuthEncode(OAuthNonce));
4917 PStr.Append("&oauth_signature_method=");
4918 PStr.Append(OAuthEncode(OAuthSignatureMethod));
4919 PStr.Append("&oauth_timestamp=");
4920 PStr.Append(OAuthEncode(OAuthTimestamp));
4921 PStr.Append("&oauth_token=");
4922 PStr.Append(OAuthEncode(OAuthToken));
4923 PStr.Append("&oauth_version=");
4924 PStr.Append(OAuthEncode(OAuthVersion));
4925 PStr.Append("&password=");
4926 PStr.Append(OAuthEncode(Password));
4927 PStr.Append("&phone=");
4928 PStr.Append(OAuthEncode(PhoneNr));
4929 PStr.Append("&username=");
4930 PStr.Append(OAuthEncode(UserName));
4931
4932 StringBuilder BStr = new StringBuilder();
4933
4934 BStr.Append("submit&&"); // No to-field.
4935 BStr.Append(OAuthEncode(PStr.ToString()));
4936
4937 byte[] Key = Encoding.ASCII.GetBytes(OAuthEncode(KeySecret) + "&" + OAuthEncode(OAuthTokenSecret));
4938 byte[] Hash = null;
4939
4940 switch (OAuthSignatureMethod)
4941 {
4942 case "HMAC-SHA1":
4943 Hash = Hashes.ComputeHMACSHA1Hash(Key, Encoding.ASCII.GetBytes(BStr.ToString()));
4944 break;
4945
4946 default:
4947 Logged = true;
4948 LoginAuditor.Fail("Registration form signature failed. Unhandled signature method requested.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
4949 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
4950 new KeyValuePair<string, object>("Method", OAuthSignatureMethod),
4951 new KeyValuePair<string, object>("EMail", EMail?.Value),
4952 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
4953 break;
4954 }
4955
4956 if (!(Hash is null))
4957 {
4958 string Signature = OAuthEncode(Convert.ToBase64String(Hash));
4959 Signed = Signature == OAuthSignature;
4960
4961 if (!Signed)
4962 {
4963 Logged = true;
4964 LoginAuditor.Fail("Registration form signature failed.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
4965 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
4966 new KeyValuePair<string, object>("Method", OAuthSignatureMethod),
4967 new KeyValuePair<string, object>("EMail", EMail?.Value),
4968 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
4969 }
4970 }
4971 }
4972 else
4973 {
4974 Logged = true;
4975 LoginAuditor.Fail("Registration form signature failed. Invalid API key used.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
4976 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
4977 new KeyValuePair<string, object>("EMail", EMail?.Value),
4978 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
4979 }
4980 }
4981 else
4982 {
4983 Logged = true;
4984 LoginAuditor.Fail("Registration form signature failed. Signature parameters not provided.", UserName, Connection.RemoteEndPoint, Connection.Protocol);
4985 }
4986
4987 if (Signed)
4988 {
4989 if (string.IsNullOrEmpty(UserName))
4990 await e.IqErrorBadRequest(e.To, "User name cannot be empty.", "en");
4991
4992 if (UserName.Length > 1023)
4993 await e.IqErrorBadRequest(e.To, "User name too long.", "en");
4994
4995 foreach (char ch in UserName.ToCharArray())
4996 {
4997 if (char.IsWhiteSpace(ch))
4998 {
4999 await e.IqErrorNotAllowed(e.To, "White-space characters not allowed in user names.", "en");
5000 return;
5001 }
5002 else if (ch == '@')
5003 {
5004 await e.IqErrorNotAllowed(e.To, "@ characters not allowed in user names.", "en");
5005 return;
5006 }
5007 }
5008
5009 if (!IsValidUserName(UserName))
5010 {
5011 await e.IqErrorBadRequest(e.To, "User Name contains prohibited characters.", "en");
5012 return;
5013 }
5014
5015 KeyValuePair<IAccount, string[]> P = await this.persistenceLayer.CreateAccount(OAuthConsumerKey, UserName, Password, EMail, PhoneNr, Connection.RemoteEndPoint);
5016 IAccount Account = P.Key;
5017 if (Account is null)
5018 {
5019 string[] Alternatives = P.Value;
5020 StringBuilder Xml = new StringBuilder();
5021
5022 Xml.Append("<conflict xmlns='");
5023 Xml.Append(StanzaNamespace);
5024 Xml.Append("'/>");
5025
5026 if (!(Alternatives is null) && Alternatives.Length > 0)
5027 {
5028 Xml.Append("<alternatives xmlns='");
5029 Xml.Append(AlternativesNamespace);
5030 Xml.Append("'>");
5031
5032 foreach (string Alternative in Alternatives)
5033 {
5034 Xml.Append("<alternative>");
5035 Xml.Append(XML.Encode(Alternative));
5036 Xml.Append("</alternative>");
5037 }
5038
5039 Xml.Append("</alternatives>");
5040 }
5041
5042 await e.IqError("cancel", Xml.ToString(), e.To,
5043 "Account name already exists, or API key limit reached.", "en");
5044
5045 Logged = true;
5046 LoginAuditor.Fail("Registration failed. Signature OK, but account already exists, or API key limit reached.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
5047 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
5048 new KeyValuePair<string, object>("Method", OAuthSignatureMethod),
5049 new KeyValuePair<string, object>("EMail", EMail?.Value),
5050 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
5051 }
5052 else
5053 {
5054 await e.IqResult(string.Empty, e.To);
5055
5056 Logged = true;
5057 LoginAuditor.Success("Registration successful and account created.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
5058 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
5059 new KeyValuePair<string, object>("Method", OAuthSignatureMethod),
5060 new KeyValuePair<string, object>("EMail", EMail?.Value),
5061 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
5062 }
5063 }
5064 else
5065 {
5066 if (!Logged)
5067 {
5068 LoginAuditor.Fail("Registration form signature failed.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
5069 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
5070 new KeyValuePair<string, object>("Method", OAuthSignatureMethod),
5071 new KeyValuePair<string, object>("EMail", EMail?.Value),
5072 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
5073 }
5074
5075 await e.IqErrorBadRequest(e.To, "Form signature incorrect.", "en");
5076 }
5077
5078 return;
5079 }
5080 }
5081
5082 if (Remove)
5083 {
5084 if (Count > 1)
5085 await e.IqErrorBadRequest(e.To, "Remove element must be only child element.", "en");
5086 else if (Connection.State != XmppConnectionState.Active)
5087 await e.IqErrorForbidden(e.To, "Only allowed to remove your own account, while account is active.", "en");
5088 else if (!this.IsServerDomain(e.From.Domain, true))
5089 {
5090 await e.IqError("auth", "<registration-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>", e.To,
5091 "Only accounts registered on the broker can remove their accounts.", "en");
5092 }
5093 else if (await this.persistenceLayer.DeleteAccount(e.From.Account, Connection.RemoteEndPoint))
5094 {
5095 await e.IqResult(string.Empty, e.To);
5096 Connection.AccountDeleted();
5097 }
5098 else
5099 {
5100 await e.IqError("auth", "<registration-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>", e.To,
5101 "Only accounts registered on the broker can remove their accounts.", "en");
5102 }
5103
5104 return;
5105 }
5106
5107 if (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password))
5108 {
5109 if (Connection.State != XmppConnectionState.Active || Connection.UserName != UserName ||
5110 !await this.persistenceLayer.ChangePassword(UserName, Password))
5111 {
5112 await e.IqErrorNotAllowed(e.To, "Password change not allowed.", "en");
5113 }
5114 else
5115 await e.IqResult(string.Empty, e.To);
5116
5117 IClientConnection[] CurrentConnections = this.GetClientConnections(e.From.BareJid);
5118
5119 if (!(CurrentConnections is null))
5120 {
5121 foreach (IClientConnection ClientConnection in CurrentConnections)
5122 {
5125 }
5126 }
5127 }
5128 else
5129 await e.IqErrorBadRequest(e.To, "Empty user names or passwords not allowed.", "en");
5130 }
5131
5137 public static bool IsValidUserName(string UserName)
5138 {
5139 foreach (char ch in UserName)
5140 {
5141 switch (ch)
5142 {
5143 // From XMPP spec (RFC 6122):
5144 case '"':
5145 case '&':
5146 case '\'':
5147 case '/':
5148 case ':':
5149 case '<':
5150 case '>':
5151 case '@':
5152
5153 // Disallow space
5154 case ' ':
5155
5156 // Invalid as file name characters (for file sniffers, etc.)
5157 //case '"':
5158 //case '<':
5159 //case '>':
5160 case '|':
5161 case '\0':
5162 case '\u0001':
5163 case '\u0002':
5164 case '\u0003':
5165 case '\u0004':
5166 case '\u0005':
5167 case '\u0006':
5168 case '\a':
5169 case '\b':
5170 case '\t':
5171 case '\n':
5172 case '\v':
5173 case '\f':
5174 case '\r':
5175 case '\u000e':
5176 case '\u000f':
5177 case '\u0010':
5178 case '\u0011':
5179 case '\u0012':
5180 case '\u0013':
5181 case '\u0014':
5182 case '\u0015':
5183 case '\u0016':
5184 case '\u0017':
5185 case '\u0018':
5186 case '\u0019':
5187 case '\u001a':
5188 case '\u001b':
5189 case '\u001c':
5190 case '\u001d':
5191 case '\u001e':
5192 case '\u001f':
5193 //case ':':
5194 case '*':
5195 case '?':
5196 case '\\':
5197 //case '/':
5198 return false;
5199 }
5200 }
5201
5202 return true;
5203 }
5204
5205 private static string OAuthEncode(string s)
5206 {
5207 StringBuilder Result = new StringBuilder();
5208
5209 foreach (char ch in s)
5210 {
5211 if (OAuthReserved.IndexOf(ch) < 0)
5212 {
5213 Result.Append("%");
5214 Result.Append(((int)ch).ToString("X2"));
5215 }
5216 else
5217 Result.Append(ch);
5218 }
5219
5220 return Result.ToString();
5221 }
5222
5223 private const string OAuthReserved = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
5224
5225 #endregion
5226
5227 #region Blocking Command (XEP-0191) and Spam Reporting (XEP-0377).
5228
5229 private async Task BlockListGet(object Sender, IqEventArgs e)
5230 {
5231 if (!(e.Sender is IClientConnection Connection) || e.From.BareJid != Connection.BareJid)
5232 {
5233 await e.IqErrorForbidden(e.To, "Access to block list can only be granted from the corresponding client.", "en");
5234 return;
5235 }
5236
5237 IEnumerable<CaseInsensitiveString> BlockList = await this.persistenceLayer.GetBlockList(Connection.UserName);
5238 if (BlockList is null)
5239 {
5240 await e.IqErrorItemNotFound(e.To, "List not found.", "en");
5241 return;
5242 }
5243
5244 Connection.WantsBlockList = true;
5245
5246 StringBuilder Xml = new StringBuilder();
5247
5248 Xml.Append("<blocklist xmlns='");
5249 Xml.Append(BlockingCommandNamespace);
5250 Xml.Append("'>");
5251
5252 foreach (CaseInsensitiveString Jid in BlockList)
5253 {
5254 Xml.Append("<item jid='");
5255 Xml.Append(Jid);
5256 Xml.Append("'/>");
5257 }
5258
5259 Xml.Append("</blocklist>");
5260
5261 await e.IqResult(Xml.ToString(), e.To);
5262 }
5263
5264 private async Task BlockSet(object Sender, IqEventArgs e)
5265 {
5266 if (!(e.Sender is IClientConnection Connection) || e.From.BareJid != Connection.BareJid)
5267 {
5268 await e.IqErrorForbidden(e.To, "Access to block list can only be granted from the corresponding client.", "en");
5269 return;
5270 }
5271
5273 List<CaseInsensitiveString> BareJids = null;
5274 string Text = string.Empty;
5275 string TextLanguage = string.Empty;
5276 CaseInsensitiveString BareJid;
5277
5278 foreach (XmlNode N in e.Query.ChildNodes)
5279 {
5280 switch (N.LocalName)
5281 {
5282 case "item":
5283 BareJid = XML.Attribute((XmlElement)N, "jid");
5285 {
5286 await e.IqErrorBadRequest(e.To, "Empty JID.", "en");
5287 return;
5288 }
5289
5290 BareJids ??= new List<CaseInsensitiveString>();
5291 BareJids.Add(BareJid);
5292 break;
5293
5294 case "report":
5295 foreach (XmlNode N2 in N.ChildNodes)
5296 {
5297 switch (N2.LocalName)
5298 {
5299 case "text":
5300 XmlElement E = (XmlElement)N2;
5301 Text = E.InnerText;
5302 TextLanguage = XML.Attribute(E, "xml:lang");
5303 break;
5304
5305 case "spam":
5306 Reason = BlockingReason.Spam;
5307 break;
5308
5309 case "abuse":
5310 Reason = BlockingReason.Abuse;
5311 break;
5312 }
5313 }
5314 break;
5315 }
5316 }
5317
5318 if (BareJids is null)
5319 {
5320 await e.IqErrorBadRequest(e.To, "Empty list.", "en");
5321 return;
5322 }
5323
5324 StringBuilder Xml = null;
5325
5326 foreach (CaseInsensitiveString BareJid2 in BareJids)
5327 {
5328 if (await this.persistenceLayer.AddBlock(Connection.UserName, BareJid2, Reason, Text, TextLanguage))
5329 {
5330 IClientConnection[] Connections = this.GetClientConnections(Connection.BareJid);
5331
5332 if (!(Connections is null))
5333 {
5334 foreach (IClientConnection Connection2 in Connections)
5335 {
5336 if (Connection2 != Connection && Connection2.WantsBlockList)
5337 {
5338 if (Xml is null)
5339 Xml = new StringBuilder();
5340 else
5341 Xml.Clear();
5342
5343 Xml.Append("<block xmlns='");
5344 Xml.Append(BlockingCommandNamespace);
5345 Xml.Append("'><item jid='");
5346 Xml.Append(BareJid2);
5347 Xml.Append("'/></block>");
5348
5349 try
5350 {
5351 await Connection2.IQ("set", string.Empty, Connection2.Address, e.To, string.Empty, Xml.ToString(), this);
5352 }
5353 catch (Exception ex)
5354 {
5355 Connection.Exception(ex);
5356 await Connection.DisposeAsync();
5357 }
5358 }
5359 }
5360 }
5361 }
5362 }
5363
5364 await e.IqResult(string.Empty, e.To);
5365 }
5366
5367 private async Task UnblockSet(object Sender, IqEventArgs e)
5368 {
5369 if (!(e.Sender is IClientConnection Connection) || e.From.BareJid != Connection.BareJid)
5370 {
5371 await e.IqErrorForbidden(e.To, "Access to block list can only be granted from the corresponding client.", "en");
5372 return;
5373 }
5374
5375 List<CaseInsensitiveString> BareJids = null;
5376 CaseInsensitiveString BareJid;
5377
5378 foreach (XmlNode N in e.Query.ChildNodes)
5379 {
5380 if (N.LocalName == "item")
5381 {
5382 BareJid = XML.Attribute((XmlElement)N, "jid");
5384 {
5385 await e.IqErrorBadRequest(e.To, "Empty JID.", "en");
5386 return;
5387 }
5388
5389 BareJids ??= new List<CaseInsensitiveString>();
5390 BareJids.Add(BareJid);
5391 }
5392 }
5393
5394 if (BareJids is null)
5395 {
5396 if (await this.persistenceLayer.ClearBlocks(Connection.UserName))
5397 {
5398 IClientConnection[] Connections = this.GetClientConnections(Connection.BareJid);
5399
5400 if (!(Connections is null))
5401 {
5402 foreach (IClientConnection Connection2 in Connections)
5403 {
5404 if (Connection2 != Connection && Connection2.WantsBlockList)
5405 {
5406 try
5407 {
5408 await Connection2.IQ("set", string.Empty, Connection2.Address, e.To, string.Empty,
5409 "<unblock xmlns='" + BlockingCommandNamespace + "'/>", this);
5410 }
5411 catch (Exception ex)
5412 {
5413 try
5414 {
5415 Connection.Exception(ex);
5416 await Connection.DisposeAsync();
5417 }
5418 catch (Exception ex2)
5419 {
5420 Log.Exception(ex2);
5421 }
5422 }
5423 }
5424 }
5425 }
5426 }
5427 }
5428 else
5429 {
5430 StringBuilder Xml = null;
5431
5432 foreach (CaseInsensitiveString BareJid2 in BareJids)
5433 {
5434 if (await this.persistenceLayer.Unblock(Connection.UserName, BareJid2))
5435 {
5436 IClientConnection[] Connections = this.GetClientConnections(Connection.BareJid);
5437
5438 if (!(Connections is null))
5439 {
5440 foreach (IClientConnection Connection2 in Connections)
5441 {
5442 if (Connection2 != Connection && Connection2.WantsBlockList)
5443 {
5444 if (Xml is null)
5445 Xml = new StringBuilder();
5446 else
5447 Xml.Clear();
5448
5449 Xml.Append("<unblock xmlns='");
5450 Xml.Append(BlockingCommandNamespace);
5451 Xml.Append("'><item jid='");
5452 Xml.Append(BareJid2);
5453 Xml.Append("'/></unblock>");
5454
5455 try
5456 {
5457 await Connection2.IQ("set", string.Empty, Connection2.Address, e.To, string.Empty, Xml.ToString(), this);
5458 }
5459 catch (Exception ex)
5460 {
5461 Connection.Exception(ex);
5462 await Connection.DisposeAsync();
5463 }
5464 }
5465 }
5466 }
5467 }
5468 }
5469 }
5470
5471 await e.IqResult(string.Empty, e.To);
5472 }
5473
5474 #endregion
5475
5476 #region Sniffers
5477
5478 internal XmlFileSniffer GetSniffer(string Key, bool S2S)
5479 {
5480 string FileName;
5481
5482 if (S2S)
5483 FileName = this.domainSnifferPath.Replace("%DOMAIN%", Key);
5484 else
5485 FileName = this.clientSnifferPath.Replace("%ENDPOINT%", Key);
5486
5487 if (!(this.sniffers is null) && this.sniffers.TryGetValue(FileName, out XmlFileSniffer XmlFileSniffer))
5488 return XmlFileSniffer;
5489
5490 if (S2S)
5491 {
5492 if (this.s2sEndpoints.TryGetValue(Key, out IS2SEndpoint Endpoint) &&
5493 Endpoint.HasSniffers)
5494 {
5495 foreach (ISniffer Sniffer in Endpoint.Sniffers)
5496 {
5497 if (Sniffer is XmlFileSniffer XmlFileSniffer2)
5498 return XmlFileSniffer2;
5499 }
5500 }
5501 }
5502 else
5503 {
5504 IClientConnection[] Connections = this.GetClientConnections(Key + "@" + this.domain);
5505 if (!(Connections is null))
5506 {
5507 foreach (IClientConnection Connection in Connections)
5508 {
5509 if (Connection is ICommunicationLayer ComLayer && ComLayer.HasSniffers)
5510 {
5511 foreach (ISniffer Sniffer in ComLayer.Sniffers)
5512 {
5513 if (Sniffer is XmlFileSniffer XmlFileSniffer2)
5514 return XmlFileSniffer2;
5515 }
5516 }
5517 }
5518 }
5519 }
5520
5521 return new XmlFileSniffer(FileName, this.GetTransformPath(S2S), 7, BinaryPresentationMethod.ByteCount);
5522 }
5523
5524 internal async Task CacheSniffers(IEnumerable<ISniffer> Sniffers)
5525 {
5526 foreach (ISniffer Sniffer in Sniffers)
5527 {
5528 if (Sniffer is XmlFileSniffer XmlFileSniffer)
5529 this.CacheSniffer(XmlFileSniffer);
5530 else if (Sniffer is IDisposableAsync DisposableAsync)
5531 await DisposableAsync.DisposeAsync();
5532 else if (Sniffer is IDisposable Disposable)
5533 Disposable.Dispose();
5534 }
5535 }
5536
5537 internal void CacheSniffer(XmlFileSniffer Sniffer)
5538 {
5539 if (this.sniffers is null)
5540 {
5541 this.sniffers = new Cache<CaseInsensitiveString, XmlFileSniffer>(int.MaxValue, TimeSpan.MaxValue, new TimeSpan(1, 1, 0), true);
5542 this.sniffers.Removed += this.Sniffers_Removed;
5543 }
5544
5545 this.sniffers.Add(Sniffer.FileName, Sniffer);
5546 }
5547
5548 private Task Sniffers_Removed(object Sender, CacheItemEventArgs<CaseInsensitiveString, XmlFileSniffer> e)
5549 {
5550 if (this.disposed || (DateTime.Now - e.Value.LastEvent).TotalMinutes > 30)
5551 return e.Value.DisposeAsync();
5552 else
5553 return Task.CompletedTask;
5554 }
5555
5556 #endregion
5557
5558 #region Statistics
5559
5564 internal void DataReceived(int NrRead)
5565 {
5566 lock (this.statSync)
5567 {
5568 this.nrBytesRx += NrRead;
5569 }
5570 }
5571
5576 internal void DataTransmitted(int NrWritten)
5577 {
5578 lock (this.statSync)
5579 {
5580 this.nrBytesTx += NrWritten;
5581 }
5582 }
5583
5592 internal void IncCounters(string Stanza, string Type, XmppAddress From, XmppAddress To, XmlElement StanzaElement)
5593 {
5594 string Namespace = null;
5595 string LocalName = null;
5596 string LocalNameBak = null;
5597 string ns = StanzaElement.NamespaceURI;
5598
5599 foreach (XmlNode N in StanzaElement.ChildNodes)
5600 {
5601 if (N is XmlElement E)
5602 {
5603 if (E.NamespaceURI != ns)
5604 {
5605 Namespace = E.NamespaceURI;
5606 LocalName = E.LocalName;
5607 break;
5608 }
5609 else
5610 LocalNameBak ??= E.LocalName;
5611 }
5612 }
5613
5614 if (Namespace is null)
5615 {
5616 Namespace = StanzaElement.NamespaceURI;
5617 LocalName = LocalNameBak ?? string.Empty;
5618 }
5619
5620 lock (this.statSync)
5621 {
5622 this.nrStanzas++;
5623
5624 this.IncLocked(Stanza + "#" + Type, this.stanzasPerStanzaType);
5625 this.IncLocked(From.Domain, this.stanzasPerFromDomain);
5626 this.IncLocked(From.BareJid, this.stanzasPerFromBareJid);
5627 this.IncLocked(To.Domain, this.stanzasPerToDomain);
5628 this.IncLocked(To.BareJid, this.stanzasPerToBareJid);
5629 this.IncLocked(Namespace, this.stanzasPerNamespace);
5630 this.IncLocked(Namespace + "#" + LocalName, this.stanzasPerFqn);
5631 }
5632 }
5633
5634 private void IncLocked(string Key, Dictionary<string, Statistic> Stat)
5635 {
5636 if (!Stat.TryGetValue(Key, out Statistic Rec))
5637 {
5638 Rec = new Statistic(1);
5639 Stat[Key] = Rec;
5640 }
5641 else
5642 Rec.Inc();
5643 }
5644
5650 {
5652 DateTime TP = DateTime.Now;
5653
5654 lock (this.statSync)
5655 {
5656 Result = new Statistics.CommunicationStatistics()
5657 {
5658 StanzasPerStanzaType = this.stanzasPerStanzaType,
5659 StanzasPerFromDomain = this.stanzasPerFromDomain,
5660 StanzasPerToDomain = this.stanzasPerToDomain,
5661 StanzasPerFromBareJid = this.stanzasPerFromBareJid,
5662 StanzasPerToBareJid = this.stanzasPerToBareJid,
5663 StanzasPerNamespace = this.stanzasPerNamespace,
5664 StanzasPerFqn = this.stanzasPerFqn,
5665 LastStat = this.lastStat,
5666 CurrentStat = TP,
5667 NrBytesRx = this.nrBytesRx,
5668 NrBytesTx = this.nrBytesTx,
5669 NrStanzas = this.nrStanzas
5670 };
5671
5672 this.stanzasPerStanzaType = new Dictionary<string, Statistic>();
5673 this.stanzasPerFromDomain = new Dictionary<string, Statistic>();
5674 this.stanzasPerToDomain = new Dictionary<string, Statistic>();
5675 this.stanzasPerFromBareJid = new Dictionary<string, Statistic>();
5676 this.stanzasPerToBareJid = new Dictionary<string, Statistic>();
5677 this.stanzasPerNamespace = new Dictionary<string, Statistic>();
5678 this.stanzasPerFqn = new Dictionary<string, Statistic>();
5679 this.lastStat = TP;
5680 this.nrBytesRx = 0;
5681 this.nrBytesTx = 0;
5682 this.nrStanzas = 0;
5683 }
5684
5685 return Result;
5686 }
5687
5688 #endregion
5689
5690 #region XEP-0049: Private XML Storage
5691
5692 internal async Task PrivateXmlStorageGet(object Sender, IqEventArgs e)
5693 {
5694 if (!this.clientConnections.TryGetValue(e.From.Address, out IClientConnection Connection))
5695 {
5696 await e.IqErrorForbidden(e.To, "Private storage only accessible from clients directly connected to the server.", "en");
5697 return;
5698 }
5699
5700 IAccount FromAccount = await this.persistenceLayer.GetAccount(Connection.UserName);
5701 if (FromAccount is null)
5702 {
5703 await e.IqErrorItemNotFound(e.To, "Account no longer found.", "en");
5704 return;
5705 }
5706
5707 StringBuilder Xml = new StringBuilder();
5708 bool Empty = true;
5709 bool Found;
5710
5711 Xml.Append("<query xmlns='");
5712 Xml.Append(PrivateXmlStorageNamespace);
5713 Xml.Append("'>");
5714
5715 foreach (XmlNode N in e.Query.ChildNodes)
5716 {
5717 if (N is XmlElement E)
5718 {
5719 Empty = false;
5720 Found = false;
5721
5722 foreach (PersistedElement Element in await Database.Find<PersistedElement>(new FilterAnd(
5723 new FilterFieldEqualTo("Account", FromAccount.UserName),
5724 new FilterFieldEqualTo("Namespace", E.NamespaceURI),
5725 new FilterFieldEqualTo("LocalName", E.LocalName))))
5726 {
5727 if (Found)
5728 await Database.Delete(Element);
5729 else
5730 {
5731 Xml.Append(Element.Xml);
5732 Found = true;
5733 }
5734 }
5735
5736 if (!Found)
5737 {
5738 await e.IqErrorItemNotFound(e.To, "Element not found.", "en");
5739 return;
5740 }
5741 }
5742 }
5743
5744 if (Empty)
5745 {
5746 await e.IqErrorBadRequest(e.To, "Query is empty.", "en");
5747 return;
5748 }
5749
5750 Xml.Append("</query>");
5751
5752 await e.IqResult(Xml.ToString(), e.To);
5753 }
5754
5755 internal async Task PrivateXmlStorageSet(object Sender, IqEventArgs e)
5756 {
5757 if (!this.clientConnections.TryGetValue(e.From.Address, out IClientConnection Connection))
5758 {
5759 await e.IqErrorForbidden(e.To, "Private storage only accessible from clients directly connected to the server.", "en");
5760 return;
5761 }
5762
5763 IAccount FromAccount = await this.persistenceLayer.GetAccount(Connection.UserName);
5764 if (FromAccount is null)
5765 {
5766 await e.IqErrorItemNotFound(e.To, "Account no longer found.", "en");
5767 return;
5768 }
5769
5770 bool Empty = true;
5771 bool Found;
5772
5773 foreach (XmlNode N in e.Query.ChildNodes)
5774 {
5775 if (N is XmlElement E)
5776 {
5777 bool DeleteElement = IsEmptyPrivateXml(E);
5778
5779 Found = false;
5780 Empty = false;
5781
5782 foreach (PersistedElement Element in await Database.Find<PersistedElement>(new FilterAnd(
5783 new FilterFieldEqualTo("Account", FromAccount.UserName),
5784 new FilterFieldEqualTo("Namespace", E.NamespaceURI),
5785 new FilterFieldEqualTo("LocalName", E.LocalName))))
5786 {
5787 if (Found)
5788 await Database.Delete(Element);
5789 else
5790 {
5791 if (DeleteElement)
5792 await Database.Delete(Element);
5793 else
5794 {
5795 Element.Xml = E.OuterXml;
5796 Element.Updated = DateTime.UtcNow;
5797
5798 await Database.Update(Element);
5799 }
5800
5801 Found = true;
5802 }
5803 }
5804
5805 if (Found || DeleteElement)
5806 continue;
5807
5808 DateTime TP = DateTime.UtcNow;
5809
5810 await Database.Insert(new PersistedElement()
5811 {
5812 Account = FromAccount.UserName,
5813 Namespace = E.NamespaceURI,
5814 LocalName = E.LocalName,
5815 Created = TP,
5816 Updated = TP,
5817 Xml = E.OuterXml
5818 });
5819 }
5820 }
5821
5822 if (Empty)
5823 await e.IqErrorNotAcceptable(e.To, "Empty query element.", "en");
5824 else
5825 await e.IqResult(string.Empty, e.To);
5826 }
5827
5828 private static bool IsEmptyPrivateXml(XmlElement E)
5829 {
5830 if (E.HasChildNodes)
5831 return false;
5832
5833 foreach (XmlAttribute Attr in E.Attributes)
5834 {
5835 if (Attr.Name != "xmlns")
5836 return false;
5837 }
5838
5839 return true;
5840 }
5841
5842 #endregion
5843
5844 #region SMTP integration
5845
5846 private Task SmtpServer_MessageReceived(object Sender, SmtpMessageEventArgs e)
5847 {
5848 return this.ProcessMessage(e.Message);
5849 }
5850
5855 public async Task ProcessMessage(SmtpMessage Message)
5856 {
5857 try
5858 {
5859 List<KeyValuePair<string, object>> Tags = new List<KeyValuePair<string, object>>();
5860
5861 foreach (KeyValuePair<string, string> P in Message.AllHeaders)
5862 Tags.Add(new KeyValuePair<string, object>(P.Key, P.Value));
5863
5864 Log.Informational("Mail received.", Tags.ToArray());
5865
5866 /*try
5867 {
5868 string FileName = @"C:\ProgramData\IoT Gateway\SMTP\" + Guid.NewGuid().ToString();
5869 await Resources.WriteAllBytesAsync(FileName + ".bin", Message.TransformedBody ?? Message.UntransformedBody);
5870 await Files.WriteAllTextAsync(FileName + ".txt", Message.ContentType);
5871 Log.Informational("Incoming mail saved to disk: " + FileName);
5872 }
5873 catch (Exception ex)
5874 {
5875 Log.Exception(ex);
5876 }*/
5877
5878 Dictionary<CaseInsensitiveString, bool> Processed = new Dictionary<CaseInsensitiveString, bool>();
5879
5880 await this.ProcessMessage(Message, Message.To, Processed);
5881 await this.ProcessMessage(Message, Message.Cc, Processed);
5882 await this.ProcessMessage(Message, Message.Bcc, Processed);
5883 }
5884 catch (Exception ex)
5885 {
5886 Log.Exception(ex);
5887 }
5888 }
5889
5890 private async Task ProcessMessage(SmtpMessage Message, IEnumerable<MailAddress> Recipients, Dictionary<CaseInsensitiveString, bool> Processed)
5891 {
5892 if (!(Recipients is null))
5893 {
5894 foreach (MailAddress Recipient in Recipients)
5895 {
5896 if (!Processed.ContainsKey(Recipient.Address))
5897 {
5898 Processed[Recipient.Address] = true;
5899 await this.ProcessMessage(Message, Recipient);
5900 }
5901 }
5902 }
5903 }
5904
5905 private readonly Dictionary<string, DateTime> lastBounce = new Dictionary<string, DateTime>();
5906
5907 private bool CanReturnBounceMessage(MailAddress Recipient, MailAddress Sender)
5908 {
5909 string Key = Recipient.Address + " | " + Sender.Address;
5910 DateTime TP;
5911
5912 lock (this.lastBounce)
5913 {
5914 if (this.lastBounce.ContainsKey(Key))
5915 return false;
5916 }
5917
5918 TP = scheduler.Add(DateTime.Now.AddHours(4), (P) =>
5919 {
5920 lock (this.lastBounce)
5921 {
5922 this.lastBounce.Remove((string)P);
5923 }
5924 }, Key);
5925
5926 lock (this.lastBounce)
5927 {
5928 this.lastBounce[Key] = TP;
5929 }
5930
5931 return true;
5932 }
5933
5934 private async Task ProcessMessage(SmtpMessage Message, MailAddress Recipient)
5935 {
5936 try
5937 {
5938 XmppAddress Addr = new XmppAddress(Recipient.Address);
5939 if (this.IsServerDomain(Addr.Domain, true))
5940 {
5941 IRosterItem Item = await this.persistenceLayer.GetRosterItem(Addr.Account, Message.FromMail.Address);
5942 StringBuilder Markdown;
5943
5944 if (Item is null ||
5945 (Item.Subscription != SubscriptionStatus.both &&
5946 Item.Subscription != SubscriptionStatus.from &&
5947 Item.Subscription != SubscriptionStatus.to)) // If not white-listed
5948 {
5949 if (!this.CanReturnBounceMessage(Recipient, Message.FromMail))
5950 {
5951 Log.Notice("Mail discarded.", Recipient.Address, Message.FromMail.Address);
5952 return;
5953 }
5954
5955 Markdown = new StringBuilder();
5956
5957 Markdown.AppendLine("Welcome");
5958 Markdown.AppendLine("===========");
5959 Markdown.AppendLine();
5960
5961 Markdown.Append("The mail server at **");
5962 Markdown.Append(MarkdownDocument.Encode(this.domain));
5963 Markdown.AppendLine("** only forwards mail messages from approved senders.");
5964 Markdown.Append("Since **");
5965 Markdown.Append(MarkdownDocument.Encode(Message.FromMail.Address));
5966 Markdown.Append("** has not been approved by **");
5967 Markdown.Append(MarkdownDocument.Encode(Recipient.Address));
5968 Markdown.AppendLine("**, your mail has not been forwarded.");
5969 Markdown.AppendLine();
5970
5971 if (this.httpServer.OpenHttpsPorts.Length > 0 || this.httpServer.OpenHttpPorts.Length > 0)
5972 {
5973 Markdown.Append("If you want, you can send a request to **");
5974 Markdown.Append(MarkdownDocument.Encode(Recipient.Address));
5975 Markdown.Append("** to become approved, by following this link: [Request approval](");
5976 Markdown.Append("http");
5977
5978 if (this.httpServer.OpenHttpsPorts.Length > 0)
5979 {
5980 Markdown.Append("s://");
5981 Markdown.Append(this.domain);
5982
5983 if (Array.IndexOf(this.httpServer.OpenHttpsPorts, HttpServer.DefaultHttpsPort) < 0)
5984 {
5985 Markdown.Append(':');
5986 Markdown.Append(this.httpServer.OpenHttpsPorts[0]);
5987 }
5988 }
5989 else
5990 {
5991 Markdown.Append("://");
5992 Markdown.Append(this.domain);
5993
5994 if (Array.IndexOf(this.httpServer.OpenHttpPorts, HttpServer.DefaultHttpPort) < 0)
5995 {
5996 Markdown.Append(':');
5997 Markdown.Append(this.httpServer.OpenHttpPorts[0]);
5998 }
5999 }
6000
6001 string Expires = DateTime.Now.AddDays(1).Ticks.ToString();
6002
6003 Markdown.Append("/RequestWhiteList?Sender=");
6004 Markdown.Append(XML.HtmlValueEncode(Message.FromMail.Address));
6005 Markdown.Append("&Receiver=");
6006 Markdown.Append(XML.HtmlValueEncode(Recipient.Address));
6007 Markdown.Append("&Expires=P");
6008 Markdown.Append(Expires);
6009 Markdown.Append("&MAC=");
6010
6011 StringBuilder sb = new StringBuilder();
6012 sb.Append(Message.FromMail.Address);
6013 sb.Append(" | ");
6014 sb.Append(Recipient.Address);
6015 sb.Append(" | ");
6016 sb.Append(Expires);
6017
6018 if (RequestWhiteList.whiteListKey is null)
6019 {
6020 string Key = await RuntimeSettings.GetAsync("WhiteList.Key", string.Empty);
6021 if (string.IsNullOrEmpty(Key))
6022 {
6023 Key = Convert.ToBase64String(GetRandomNumbers(32));
6024 await RuntimeSettings.SetAsync("WhiteList.Key", Key);
6025 }
6026
6027 RequestWhiteList.whiteListKey = Key;
6028 }
6029
6030 string MAC = Hashes.ComputeHMACSHA256HashString(System.Convert.FromBase64String(RequestWhiteList.whiteListKey),
6031 Encoding.UTF8.GetBytes(sb.ToString()));
6032
6033 Markdown.Append(MAC);
6034 Markdown.AppendLine(")");
6035 Markdown.AppendLine();
6036
6037 Log.Notice("Mail discarded. Bounce message returned.", Recipient.Address, Message.FromMail.Address,
6038 new KeyValuePair<string, object>("Sender", Message.FromMail.Address),
6039 new KeyValuePair<string, object>("Receiver", Recipient.Address),
6040 new KeyValuePair<string, object>("Expires", Expires),
6041 new KeyValuePair<string, object>("MAC", MAC));
6042 }
6043
6044 await this.SendMailMessage(Recipient.Address, Message.FromMail.Address, "Approval required", Markdown.ToString());
6045 }
6046 else
6047 {
6048 XmppAddress From = new XmppAddress(Message.FromMail.Address);
6049 XmppAddress To = new XmppAddress(Recipient.Address);
6050 ISender Sender = await this.GetS2sEndpoint(To.Domain, From.Domain, true, "Forwarding e-mail.");
6051 List<EmbeddedContent> Attachments = null;
6052 List<EmbeddedContent> Inline = null;
6053 object Decoded = Message.DecodedBody;
6054 string PlainText = null;
6055 HtmlDocument Html = null;
6057
6058 if (!(Sender is null))
6059 {
6060 if (!(Message.Attachments is null) && Message.Attachments.Length > 0)
6061 {
6062 Attachments = new List<EmbeddedContent>();
6063 Attachments.AddRange(Message.Attachments);
6064 }
6065
6066 if (!(Message.InlineObjects is null) && Message.InlineObjects.Length > 0)
6067 {
6068 Inline = new List<EmbeddedContent>();
6069 Inline.AddRange(Message.InlineObjects);
6070 }
6071
6072 if (!(Decoded is null) && Decoded is MultipartContent MultipartContent)
6073 {
6074 LinkedList<MultipartContent> ToProcess = new LinkedList<MultipartContent>();
6075
6076 ToProcess.AddLast(MultipartContent);
6077 Decoded = null;
6078
6079 while (!(ToProcess.First is null))
6080 {
6081 MultipartContent = ToProcess.First.Value;
6082 ToProcess.RemoveFirst();
6083
6085 {
6087 {
6088 if (PlainText is null && EmbeddedContent.Decoded is string s2)
6089 PlainText = s2;
6090 else if (Html is null && EmbeddedContent.Decoded is HtmlDocument Html2)
6091 Html = Html2;
6092 else if (MarkdownDocument is null && EmbeddedContent.Decoded is MarkdownDocument MarkdownDocument2)
6093 MarkdownDocument = MarkdownDocument2;
6094 else if (EmbeddedContent.Decoded is MultipartContent MultipartContent2)
6095 ToProcess.AddLast(MultipartContent2);
6096 else if (Decoded is null)
6097 Decoded = EmbeddedContent.Decoded;
6098 else
6099 {
6100 Attachments ??= new List<EmbeddedContent>();
6101 Attachments.Add(EmbeddedContent);
6102 }
6103 }
6104 }
6105 else
6106 {
6108 {
6110 {
6111 case ContentDisposition.Attachment:
6112 Attachments ??= new List<EmbeddedContent>();
6113 Attachments.Add(EmbeddedContent);
6114 break;
6115
6116 case ContentDisposition.Inline:
6117 Inline ??= new List<EmbeddedContent>();
6118 Inline.Add(EmbeddedContent);
6119 break;
6120
6121 default:
6122 if (EmbeddedContent.Decoded is MultipartContent MultipartContent2)
6123 ToProcess.AddLast(MultipartContent2);
6124 else if (Decoded is null)
6125 Decoded = EmbeddedContent.Decoded;
6126 else
6127 {
6128 Attachments ??= new List<EmbeddedContent>();
6129 Attachments.Add(EmbeddedContent);
6130 }
6131 break;
6132 }
6133 }
6134 }
6135 }
6136 }
6137
6138 if (!(Decoded is null))
6139 {
6140 if (PlainText is null && Decoded is string s)
6141 PlainText = s;
6142 else if (Html is null && Decoded is HtmlDocument Html2)
6143 Html = Html2;
6144 else if (MarkdownDocument is null && Decoded is MarkdownDocument MarkdownDocument2)
6145 MarkdownDocument = MarkdownDocument2;
6146 }
6147
6148 if (!(MarkdownDocument is null))
6149 {
6150 // TODO: Check message, and block items that can pose a security issue (for instance, inclusion script, javascript, etc.).
6151
6152 if (string.IsNullOrEmpty(PlainText))
6153 PlainText = await MarkdownDocument.GeneratePlainText();
6154
6155 Html ??= new HtmlDocument(await MarkdownDocument.GenerateHTML());
6156 }
6157
6158 StringBuilder Content = new StringBuilder();
6159 bool HasBody = false;
6160
6161 if (!string.IsNullOrEmpty(Message.Subject))
6162 {
6163 Content.Append("<subject>");
6164 Content.Append(XML.Encode(Message.Subject));
6165 Content.Append("</subject>");
6166 }
6167
6168 if (!string.IsNullOrEmpty(Message.MessageID))
6169 {
6170 Content.Append("<thread>");
6171 Content.Append(XML.Encode(Message.MessageID));
6172 Content.Append("</thread>");
6173 }
6174
6175 if (!string.IsNullOrEmpty(PlainText))
6176 {
6177 Content.Append("<body>");
6178 Content.Append(XML.Encode(PlainText));
6179 Content.Append("</body>");
6180
6181 HasBody = true;
6182 }
6183
6184 if (!(Html?.Body is null))
6185 {
6186 Content.Append("<html xmlns='http://jabber.org/protocol/xhtml-im'>");
6187 Content.Append("<body xmlns='http://www.w3.org/1999/xhtml'>");
6188
6189 if (Html.Body.HasChildren)
6190 {
6191 foreach (HtmlNode N2 in Html.Body.Children)
6192 this.XmlEncode(N2, Content);
6193 }
6194
6195 Content.Append("</body></html>");
6196
6197 HasBody = true;
6198 }
6199
6200 if (!(MarkdownDocument is null))
6201 {
6202 Content.Append("<content xmlns='");
6203 Content.Append(ContentNamespace);
6204 Content.Append("' type='text/markdown'>");
6205 Content.Append(XML.HtmlValueEncode(await MarkdownDocument.GenerateMarkdown(true)));
6206 Content.Append("</content>");
6207
6208 HasBody = true;
6209 }
6210
6211 if (!HasBody)
6212 {
6213 Markdown = new StringBuilder();
6214
6215 Markdown.AppendLine("Unable to process incoming message");
6216 Markdown.AppendLine("=======================================");
6217 Markdown.AppendLine();
6218
6219 Markdown.Append("The mail server at **");
6220 Markdown.Append(MarkdownDocument.Encode(this.domain));
6221 Markdown.AppendLine("** was unable to forward the mail message to **");
6222 Markdown.Append(MarkdownDocument.Encode(Recipient.Address));
6223 Markdown.Append("**), since the content type `");
6224 Markdown.Append(Message.ContentType);
6225 Markdown.AppendLine("` is not handled.");
6226
6227 await this.SendMailMessage(Recipient.Address, Message.FromMail.Address, "Unable to process incoming message", Markdown.ToString());
6228
6229 Log.Notice("Mail discarded due to unhandled Content-Type.", Recipient.Address, Message.FromMail.Address,
6230 new KeyValuePair<string, object>("Content-Type", Message.ContentType));
6231
6232 return;
6233 }
6234
6235 Content.Append("<mailInfo xmlns='urn:xmpp:smtp' contentType='");
6236 Content.Append(XML.Encode(Message.ContentType));
6237
6238 if (!string.IsNullOrEmpty(Message.MessageID))
6239 {
6240 Content.Append("' id='");
6241 Content.Append(XML.Encode(Message.MessageID));
6242 }
6243
6244 Content.Append("' priority='");
6245 Content.Append(((int)Message.Priority).ToString());
6246
6247 if (Message.Date.HasValue)
6248 {
6249 Content.Append("' date='");
6250 Content.Append(XML.Encode(Message.Date.Value));
6251 }
6252
6253 Content.Append("' fromMail='");
6254 Content.Append(XML.Encode(Message.FromMail.Address));
6255
6256 Content.Append("' fromHeader='");
6257 Content.Append(XML.Encode(Message.FromHeader.ToString()));
6258
6259 Content.Append("' sender='");
6260 Content.Append(XML.Encode(Message.Sender.ToString()));
6261
6262 DateTime TP = DateTime.Now;
6264 {
6265 BareJid = Addr.BareJid,
6266 ContentType = Message.ContentType,
6267 Content = Message.TransformedBody ?? Message.UntransformedBody,
6268 Created = TP,
6269 ContentId = Guid.NewGuid().ToString()
6270 };
6271
6273
6274 Content.Append("' size='");
6275 Content.Append((MailContent.Content?.Length ?? 0).ToString());
6276
6277 Content.Append("' cid='");
6278 Content.Append(MailContent.ContentId);
6279
6280 Content.Append("'><headers xmlns='http://jabber.org/protocol/shim'>");
6281
6282 foreach (KeyValuePair<string, string> P in Message.AllHeaders)
6283 {
6284 Content.Append("<header name='");
6285 Content.Append(XML.Encode(P.Key));
6286 Content.Append("'>");
6287 Content.Append(XML.Encode(P.Value));
6288 Content.Append("</header>");
6289 }
6290
6291 Content.Append("</headers>");
6292
6293 await this.Serialize(Content, Attachments?.ToArray(), "attachment", Addr);
6294 await this.Serialize(Content, Inline?.ToArray(), "inline", Addr);
6295
6296 Content.Append("</mailInfo>");
6297
6298 await this.Message("chat", string.Empty, To, From, string.Empty, Content.ToString(), Sender);
6299 }
6300 }
6301 }
6302 else // Relay to other domain
6303 {
6304 IS2SEndpoint S2sEndpoint = await this.GetS2sEndpoint(this.domain, Addr.Domain, true, "Relaying incoming mail message.");
6305 if (S2sEndpoint is SmtpS2SEndpoint SmtpS2SEndpoint)
6306 await SmtpS2SEndpoint.RelayMessage(Message, Recipient); // Only relay to other SMTP servers
6307 }
6308 }
6309 catch (Exception ex)
6310 {
6311 Log.Exception(ex);
6312 }
6313 }
6314
6315 private void XmlEncode(HtmlNode N, StringBuilder Output)
6316 {
6317 if (N is HtmlElement E)
6318 {
6319 if (E.HasPrefix)
6320 return;
6321
6322 switch (E.LocalName.ToUpper())
6323 {
6324 case "SCRIPT":
6325 case "META":
6326 case "STYLE":
6327 case "APPLET":
6328 case "EMBED":
6329 case "IFRAME":
6330 case "NOEMBED":
6331 case "OBJECT":
6332 case "PARAM":
6333 case "CANVAS":
6334 case "NOSCRIPT":
6335 case "BUTTON":
6336 case "FORM":
6337 case "INPUT":
6338 case "OUTPUT":
6339 case "SELECT":
6340 case "TEXTAREA":
6341 case "DETAILS":
6342 case "DIALOG":
6343 case "MENU":
6344 case "MENUITEM":
6345 case "SUMMARY":
6346 case "CONTENT":
6347 case "ELEMENT":
6348 case "SHADOW":
6349 case "SLOT":
6350 case "TEMPLATE":
6351 case "COMMAND":
6352 case "DIR":
6353 case "FRAME":
6354 case "FRAMESET":
6355 return;
6356 }
6357
6358 Output.Append('<');
6359 Output.Append(E.LocalName);
6360
6361 if (E.HasAttributes)
6362 {
6363 foreach (HtmlAttribute Attr in E.Attributes)
6364 {
6365 if (Attr.HasPrefix)
6366 continue;
6367
6368 Output.Append(' ');
6369 Output.Append(Attr.LocalName);
6370 Output.Append("=\"");
6371 Output.Append(XML.HtmlAttributeEncode(Attr.Value));
6372 Output.Append('"');
6373 }
6374 }
6375
6376 if (E.HasChildren)
6377 {
6378 Output.Append('>');
6379
6380 foreach (HtmlNode N2 in E.Children)
6381 this.XmlEncode(N2, Output);
6382
6383 Output.Append("</");
6384 Output.Append(E.LocalName);
6385 Output.Append('>');
6386 }
6387 else if (E.IsEmptyElement)
6388 Output.Append("/>");
6389 else
6390 {
6391 Output.Append("></");
6392 Output.Append(E.LocalName);
6393 Output.Append('>');
6394 }
6395 }
6396 else if (N is HtmlText Text)
6397 Output.Append(XML.Encode(Text.InlineText));
6398 else if (N is HtmlEntity Entity)
6399 {
6400 if (N is HtmlEntityUnicode EntityUnicode)
6401 {
6402 char ch = (char)EntityUnicode.Code;
6403
6404 switch (ch)
6405 {
6406 case '<':
6407 Output.Append("&lt;");
6408 break;
6409
6410 case '>':
6411 Output.Append("&gt;");
6412 break;
6413
6414 case '"':
6415 Output.Append("&quot;");
6416 break;
6417
6418 case '\'':
6419 Output.Append("&apos;");
6420 break;
6421
6422 case '&':
6423 Output.Append("&amp;");
6424 break;
6425
6426 default:
6427 Output.Append(ch);
6428 break;
6429 }
6430 }
6431 else
6432 {
6433 switch (Entity.EntityName.ToLower())
6434 {
6435 case "lt":
6436 case "gt":
6437 case "quot":
6438 case "apos":
6439 case "amp":
6440 Output.Append('&');
6441 Output.Append(Entity.EntityName);
6442 Output.Append(';');
6443 break;
6444
6445 default:
6446 Output.Append(HtmlEntity.EntityToCharacter(Entity.EntityName));
6447 break;
6448 }
6449 }
6450 }
6451 else if (N is CDATA CDATA)
6452 Output.Append(XML.Encode(CDATA.Content));
6453 }
6454
6455 private async Task Serialize(StringBuilder Content, IEnumerable<EmbeddedContent> Objects, string ElementName, XmppAddress Recipient)
6456 {
6457 if (!(Objects is null))
6458 {
6459 foreach (EmbeddedContent EmbeddedContent in Objects)
6460 {
6462 {
6463 BareJid = Recipient.BareJid,
6465 Content = EmbeddedContent.TransferDecoded ?? EmbeddedContent.Raw,
6466 Created = DateTime.Now,
6467 ContentId = Guid.NewGuid().ToString()
6468 };
6469
6471
6472 Content.Append('<');
6473 Content.Append(ElementName);
6474 Content.Append(" contentType='");
6475 Content.Append(XML.Encode(EmbeddedContent.ContentType));
6476
6477 if (!string.IsNullOrEmpty(EmbeddedContent.Description))
6478 {
6479 Content.Append("' description='");
6480 Content.Append(XML.Encode(EmbeddedContent.Description));
6481 }
6482
6483 if (!string.IsNullOrEmpty(EmbeddedContent.FileName))
6484 {
6485 Content.Append("' fileName='");
6486 Content.Append(XML.Encode(EmbeddedContent.FileName));
6487 }
6488
6489 if (!string.IsNullOrEmpty(EmbeddedContent.Name))
6490 {
6491 Content.Append("' name='");
6492 Content.Append(XML.Encode(EmbeddedContent.Name));
6493 }
6494
6495 if (!string.IsNullOrEmpty(EmbeddedContent.ID))
6496 {
6497 Content.Append("' id='");
6498 Content.Append(XML.Encode(EmbeddedContent.ID));
6499 }
6500
6501 Content.Append("' cid='");
6502 Content.Append(XML.Encode(MailContent.ContentId));
6503
6504 Content.Append("' size='");
6505 Content.Append((MailContent.Content?.Length ?? 0).ToString());
6506
6507 Content.Append("'/>");
6508 }
6509 }
6510 }
6511
6512 private async Task GetMailContent(object Sender, IqEventArgs e)
6513 {
6514 string ContentId = XML.Attribute(e.Query, "cid");
6515 string ContentType = XML.Attribute(e.Query, "type");
6516
6517 MailContent Content = await Database.FindFirstDeleteRest<MailContent>(new FilterFieldEqualTo("ContentId", ContentId));
6518 if (Content is null)
6519 {
6520 await e.IqErrorItemNotFound(e.To, "Mail content item not found.", "en");
6521 return;
6522 }
6523
6524 if (Content.BareJid != e.From.BareJid)
6525 {
6526 await e.IqErrorForbidden(e.To, "Not authorized access.", "en");
6527 return;
6528 }
6529
6530 byte[] Data = null;
6531
6532 if (!string.IsNullOrEmpty(ContentType) && string.Compare(Content.ContentType, ContentType, true) != 0)
6533 {
6534 try
6535 {
6536 ContentResponse Decoded = await InternetContent.DecodeAsync(Content.ContentType, Content.Content, null);
6537 if (Decoded.HasError)
6538 {
6539 await e.IqErrorNotAcceptable(e.To, "Unable to decode mail object.", "en");
6540 return;
6541 }
6542
6544 {
6545 LinkedList<MultipartContent> ToProcess = new LinkedList<MultipartContent>();
6546 ToProcess.AddLast(MultipartContent);
6547
6548 while (Data is null && !(ToProcess.First is null))
6549 {
6550 MultipartContent = ToProcess.First.Value;
6551 ToProcess.RemoveFirst();
6552
6554 {
6555 if (string.Compare(Obj.ContentType, ContentType, true) == 0)
6556 {
6557 Data = Obj.TransferDecoded ?? Obj.Raw;
6558 break;
6559 }
6560
6561 if (!Obj.ContentType.StartsWith("multipart/"))
6562 continue;
6563
6565 if (Item.HasError)
6566 {
6567 await e.IqErrorNotAcceptable(e.To, "Unable to decode mail object.", "en");
6568 return;
6569 }
6570
6571 MultipartContent = Item.Decoded as MultipartContent;
6572 if (!(MultipartContent is null))
6573 ToProcess.AddLast(MultipartContent);
6574 }
6575 }
6576 }
6577
6578 if (Data is null)
6579 {
6580 await e.IqErrorItemNotFound(e.To, "Content-Type not found in mail object.", "en");
6581 return;
6582 }
6583 }
6584 catch (Exception)
6585 {
6586 await e.IqErrorNotAcceptable(e.To, "Unable to decode mail object.", "en");
6587 return;
6588 }
6589 }
6590 else
6591 {
6592 ContentType = Content.ContentType;
6593 Data = Content.Content;
6594 }
6595
6596 StringBuilder Xml = new StringBuilder();
6597
6598 Xml.Append("<content type='");
6599 Xml.Append(XML.Encode(ContentType));
6600 Xml.Append("' xmlns='");
6601 Xml.Append(MailNamespace);
6602
6603 if (Content.Content is null)
6604 Xml.Append("'/>");
6605 else
6606 {
6607 Xml.Append("'>");
6608 Xml.Append(Convert.ToBase64String(Data));
6609 Xml.Append("</content>");
6610 }
6611
6612 await e.IqResult(Xml.ToString(), e.To);
6613 }
6614
6615 private async Task DeleteMailContent(object Sender, IqEventArgs e)
6616 {
6617 string ContentId = XML.Attribute(e.Query, "cid");
6618
6619 MailContent Content = await Database.FindFirstDeleteRest<MailContent>(new FilterFieldEqualTo("ContentId", ContentId));
6620 if (Content is null)
6621 {
6622 await e.IqErrorItemNotFound(e.To, "Mail content item not found.", "en");
6623 return;
6624 }
6625
6626 if (Content.BareJid != e.From.BareJid)
6627 {
6628 await e.IqErrorForbidden(e.To, "Not authorized access.", "en");
6629 return;
6630 }
6631
6632 await Database.Delete(Content);
6633
6634 await e.IqResult(string.Empty, e.To);
6635 }
6636
6642 public async Task<int> DeleteOldMailContent(DateTime OlderThan)
6643 {
6644 return await Database.Delete<MailContent>(new FilterFieldLesserOrEqualTo("Created", OlderThan));
6645 }
6646
6654 public async Task<bool> SendMailMessage(CaseInsensitiveString From, CaseInsensitiveString To, string Subject, string Markdown)
6655 {
6656 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(Markdown);
6657 string HTML = "<html><body>" + HtmlDocument.GetBody(await Doc.GenerateHTML()) + "</body></html>";
6658 string PlainText = await Doc.GeneratePlainText();
6659
6660 return await this.smtpServer.SendMessage(From, To, Subject, new EmbeddedContent[]
6661 {
6662 new EmbeddedContent()
6663 {
6664 ContentType = "text/html; charset=utf-8",
6665 Raw = Encoding.UTF8.GetBytes(HTML)
6666 },
6667 new EmbeddedContent()
6668 {
6669 ContentType = "text/plain; charset=utf-8",
6670 Raw = Encoding.UTF8.GetBytes(PlainText)
6671 },
6672 new EmbeddedContent()
6673 {
6674 ContentType = "text/markdown; charset=utf-8",
6675 Raw = Encoding.UTF8.GetBytes(Markdown)
6676 }
6677 }, Array.Empty<EmbeddedContent>());
6678 }
6679
6680 #endregion
6681
6682 #region Service Discovery
6683
6689 public Task<ServiceDiscoveryResult> ServiceDiscoveryAsync(string To)
6690 {
6691 return this.ServiceDiscoveryAsync(To, string.Empty);
6692 }
6693
6700 public async Task<ServiceDiscoveryResult> ServiceDiscoveryAsync(string To, string Node)
6701 {
6702 StringBuilder Xml = new StringBuilder();
6703 bool CacheResponse = string.IsNullOrEmpty(Node) && (string.IsNullOrEmpty(To) || this.IsServerDomain(To, true));
6704 TaskCompletionSource<ServiceDiscoveryResult> Result = new TaskCompletionSource<ServiceDiscoveryResult>();
6705
6706 Xml.Append("<query xmlns='");
6707 Xml.Append(DiscoveryNamespace);
6708
6709 if (!string.IsNullOrEmpty(Node))
6710 {
6711 Xml.Append("' node='");
6712 Xml.Append(XML.Encode(Node));
6713 }
6714
6715 Xml.Append("'/>");
6716
6717 await this.SendIqRequest("get", this.domainAddress, new XmppAddress(To), string.Empty,
6718 Xml.ToString(), true, (Sender, e) =>
6719 {
6720 if (e.Ok)
6721 {
6722 Dictionary<string, bool> Features = new Dictionary<string, bool>();
6723 List<Identity> Identities = new List<Identity>();
6724
6725 foreach (XmlNode N in e.Response.ChildNodes)
6726 {
6727 if (N.LocalName == "query")
6728 {
6729 foreach (XmlNode N2 in N.ChildNodes)
6730 {
6731 switch (N2.LocalName)
6732 {
6733 case "identity":
6734 Identities.Add(new Identity((XmlElement)N2));
6735 break;
6736
6737 case "feature":
6738 Features[XML.Attribute((XmlElement)N2, "var")] = true;
6739 break;
6740 }
6741 }
6742 }
6743 }
6744
6745 Result.TrySetResult(new ServiceDiscoveryResult(Identities.ToArray(), Features));
6746 }
6747 else if (string.IsNullOrEmpty(e.ErrorText))
6748 Result.TrySetException(new Exception("Unable to perform service discovery."));
6749 else
6750 Result.TrySetException(new Exception(e.ErrorText));
6751
6752 return Task.CompletedTask;
6753
6754 }, null);
6755
6756 return await Result.Task;
6757 }
6758
6763 public Task<Item[]> ServiceItemsDiscoveryAsync(string To)
6764 {
6765 return this.ServiceItemsDiscoveryAsync(To, string.Empty);
6766 }
6767
6773 public async Task<Item[]> ServiceItemsDiscoveryAsync(string To, string Node)
6774 {
6775 StringBuilder Xml = new StringBuilder();
6776 TaskCompletionSource<Item[]> Result = new TaskCompletionSource<Item[]>();
6777
6778 Xml.Append("<query xmlns='");
6779 Xml.Append(DiscoveryItemsNamespace);
6780
6781 if (!string.IsNullOrEmpty(Node))
6782 {
6783 Xml.Append("' node='");
6784 Xml.Append(XML.Encode(Node));
6785 }
6786
6787 Xml.Append("'/>");
6788
6789 await this.SendIqRequest("get", this.domainAddress, new XmppAddress(To),
6790 string.Empty, Xml.ToString(), true, (Sender, e) =>
6791 {
6792 if (e.Ok)
6793 {
6794 List<Item> Items = new List<Item>();
6795
6796 foreach (XmlNode N in e.Response.ChildNodes)
6797 {
6798 if (N.LocalName == "query")
6799 {
6800 foreach (XmlNode N2 in N.ChildNodes)
6801 {
6802 if (N2.LocalName == "item")
6803 Items.Add(new Item((XmlElement)N2));
6804 }
6805 }
6806 }
6807
6808 Result.TrySetResult(Items.ToArray());
6809 }
6810 else if (string.IsNullOrEmpty(e.ErrorText))
6811 Result.TrySetException(new Exception("Unable to perform service items discovery."));
6812 else
6813 Result.TrySetException(new Exception(e.ErrorText));
6814
6815 return Task.CompletedTask;
6816
6817 }, null);
6818
6819 return await Result.Task;
6820 }
6821
6822 #endregion
6823
6824 #region Finding components
6825
6832 public async Task<CaseInsensitiveString> FindComponentAsync(CaseInsensitiveString Jid, CaseInsensitiveString Feature)
6833 {
6834 CaseInsensitiveString Key = Jid + " " + Feature;
6835
6836 lock (this.services)
6837 {
6838 if (this.services.TryGetValue(Key, out CaseInsensitiveString Service))
6839 return Service;
6840 }
6841
6842 string BareJid = GetBareJID(Jid);
6843 int i = BareJid.IndexOf('@');
6844 string Domain = BareJid[(i + 1)..];
6845
6846 ServiceDiscoveryResult e = await this.ServiceDiscoveryAsync(Domain);
6847 string Result = null;
6848
6849 if (e.HasFeature(Feature))
6850 Result = Domain;
6851 else
6852 {
6853 Item[] Items = await this.ServiceItemsDiscoveryAsync(Domain);
6854
6855 foreach (Item Component in Items)
6856 {
6857 e = await this.ServiceDiscoveryAsync(Component.JID);
6858 if (e.HasFeature(Feature))
6859 {
6860 Result = Component.JID;
6861 break;
6862 }
6863 }
6864 }
6865
6866 if (!string.IsNullOrEmpty(Result))
6867 {
6868 lock (this.services)
6869 {
6870 this.services[Key] = Result;
6871 }
6872 }
6873
6874 return Result;
6875 }
6876
6877 #endregion
6878
6879 #region Push Notification
6880
6881 #region New Token
6882
6883 private async Task NewTokenHandler(object Sender, IqEventArgs e)
6884 {
6886
6887 if (!e.From.HasAccount)
6888 {
6889 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
6890 return;
6891 }
6892
6893 if (!this.IsServerDomain(e.From.Domain, true))
6894 {
6895 await e.IqErrorForbidden(e.To, "Push Forwarding service only available for clients on broker.", "en");
6896 return;
6897 }
6898
6899 if (!Enum.TryParse<PushMessagingService>(XML.Attribute(e.Query, "service"), out PushMessagingService Service))
6900 {
6901 await e.IqErrorBadRequest(e.To, "Unrecognized service.", "en");
6902 return;
6903 }
6904
6905 if (!Enum.TryParse<ClientType>(XML.Attribute(e.Query, "clientType"), out ClientType ClientType))
6906 {
6907 await e.IqErrorBadRequest(e.To, "Unrecognized client type.", "en");
6908 return;
6909 }
6910
6911 string Token = XML.Attribute(e.Query, "token");
6912 if (string.IsNullOrEmpty(Token))
6913 {
6914 await e.IqErrorBadRequest(e.To, "No token provided.", "en");
6915 return;
6916 }
6917
6918 PushNotificationToken TokenObj = await TryGetPushNotificationToken(BareJid);
6919 DateTime TP = DateTime.UtcNow;
6920
6921 if (TokenObj is null)
6922 {
6923 TokenObj = new PushNotificationToken()
6924 {
6925 BareJid = BareJid,
6926 Created = TP,
6927 NrUpdates = 1,
6928 Token = Token,
6929 Service = Service,
6931 Updated = TP
6932 };
6933
6934 tokens[BareJid] = TokenObj;
6935
6936 await Database.Insert(TokenObj);
6937 }
6938 else
6939 {
6940 TokenObj.Token = Token;
6941 TokenObj.Service = Service;
6942 TokenObj.ClientType = ClientType;
6943 TokenObj.Updated = TP;
6944 TokenObj.NrUpdates++;
6945
6946 await Database.Update(TokenObj);
6947 }
6948
6949 await e.IqResult(string.Empty, e.To);
6950 }
6951
6957 public static async Task<PushNotificationToken> TryGetPushNotificationToken(CaseInsensitiveString BareJid)
6958 {
6959 if (tokens.TryGetValue(BareJid, out PushNotificationToken Token))
6960 return Token;
6961
6962 Token = await Database.FindFirstDeleteRest<PushNotificationToken>(new FilterFieldEqualTo("BareJid", BareJid));
6963 tokens[BareJid] = Token; // Store null, if none is found, to avoid repetitive searches.
6964
6965 return Token;
6966 }
6967
6968 #endregion
6969
6970 #region Remove Token
6971
6972 private async Task RemoveTokenHandler(object Sender, IqEventArgs e)
6973 {
6975
6976 if (!e.From.HasAccount)
6977 {
6978 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
6979 return;
6980 }
6981
6982 if (!this.IsServerDomain(e.From.Domain, true))
6983 {
6984 await e.IqErrorForbidden(e.To, "Push Forwarding service only available for clients on broker.", "en");
6985 return;
6986 }
6987
6988 PushNotificationToken TokenObj = await TryGetPushNotificationToken(BareJid);
6989 if (TokenObj is null)
6990 {
6991 await e.IqErrorItemNotFound(e.To, "Token not found.", "en");
6992 return;
6993 }
6994
6995 tokens.Remove(BareJid);
6996 await Database.Delete(TokenObj);
6997
6998 await e.IqResult(string.Empty, e.To);
6999 }
7000
7001 #endregion
7002
7003 #region Clear Rules
7004
7005 private async Task ClearRulesHandler(object Sender, IqEventArgs e)
7006 {
7008
7009 if (!e.From.HasAccount)
7010 {
7011 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
7012 return;
7013 }
7014
7015 if (!this.IsServerDomain(e.From.Domain, true))
7016 {
7017 await e.IqErrorForbidden(e.To, "Push Forwarding service only available for clients on broker.", "en");
7018 return;
7019 }
7020
7021 foreach (PushNotificationRule Rule in await Database.FindDelete<PushNotificationRule>(new FilterFieldEqualTo("BareJid", BareJid)))
7022 rules.Remove(RuleKey(Rule));
7023
7024 await e.IqResult(string.Empty, e.To);
7025 }
7026
7027 private static string RuleKey(PushNotificationRule Rule)
7028 {
7029 return RuleKey(Rule.BareJid, Rule.MessageType, Rule.LocalName, Rule.Namespace);
7030 }
7031
7032 private static string RuleKey(CaseInsensitiveString BareJid, string MessageType, string LocalName, string Namespace)
7033 {
7034 StringBuilder sb = new StringBuilder();
7035
7036 sb.Append(BareJid.LowerCase);
7037 sb.Append(' ');
7038 sb.Append(MessageType);
7039 sb.Append(' ');
7040 sb.Append(LocalName);
7041 sb.Append(' ');
7042 sb.Append(Namespace);
7043
7044 return sb.ToString();
7045 }
7046
7047 #endregion
7048
7049 #region Add Rule
7050
7051 private async Task AddRuleHandler(object Sender, IqEventArgs e)
7052 {
7054
7055 if (!e.From.HasAccount)
7056 {
7057 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
7058 return;
7059 }
7060
7061 if (!this.IsServerDomain(e.From.Domain, true))
7062 {
7063 await e.IqErrorForbidden(e.To, "Push Forwarding service only available for clients on broker.", "en");
7064 return;
7065 }
7066
7067 string MessageType = XML.Attribute(e.Query, "type");
7068 string LocalName = XML.Attribute(e.Query, "localName");
7069 string Namespace = XML.Attribute(e.Query, "namespace");
7070 string Channel = XML.Attribute(e.Query, "channel");
7071 string MessageVariable = XML.Attribute(e.Query, "variable");
7072 string PatternMatchingScript = null;
7073 string ContentScript = null;
7074
7075 foreach (XmlNode N in e.Query.ChildNodes)
7076 {
7077 if (N is XmlElement E && E.NamespaceURI == MessagePushNamespace)
7078 {
7079 switch (E.LocalName)
7080 {
7081 case "PatternMatching":
7082 PatternMatchingScript = E.InnerText.Trim();
7083
7084 try
7085 {
7086 Expression Exp = new Expression(PatternMatchingScript);
7087
7088 if (!CheckExpressionSafe(Exp, out ScriptNode Prohibited))
7089 {
7090 await e.IqErrorForbidden(e.To, "Pattern Matching Script contains prohibited elements: " +
7091 Prohibited?.SubExpression, "en");
7092
7093 return;
7094 }
7095 }
7096 catch (Exception)
7097 {
7098 await e.IqErrorBadRequest(e.To, "Invalid pattern-matching script.", "en");
7099 return;
7100 }
7101 break;
7102
7103 case "Content":
7104 ContentScript = E.InnerText.Trim();
7105
7106 try
7107 {
7108 Expression Exp = new Expression(ContentScript);
7109
7110 if (!CheckExpressionSafe(Exp, out ScriptNode Prohibited))
7111 {
7112 await e.IqErrorForbidden(e.To, "Content Script contains prohibited elements: " +
7113 Prohibited?.SubExpression, "en");
7114
7115 return;
7116 }
7117 }
7118 catch (Exception)
7119 {
7120 await e.IqErrorBadRequest(e.To, "Invalid content script.", "en");
7121 return;
7122 }
7123 break;
7124
7125 default:
7126 await e.IqErrorBadRequest(e.To, "Unrecognized child element: " + e.Language, "en");
7127 return;
7128 }
7129 }
7130 }
7131
7132 PushNotificationRule Rule = await TryGetPushNotificationRule(BareJid, MessageType, LocalName, Namespace);
7133
7134 if (Rule is null)
7135 {
7136 Rule = new PushNotificationRule()
7137 {
7138 BareJid = BareJid,
7139 LocalName = LocalName,
7140 Namespace = Namespace,
7142 Channel = Channel,
7143 MessageVariable = MessageVariable,
7144 PatternMatchingScript = PatternMatchingScript,
7145 ContentScript = ContentScript
7146 };
7147
7148 await Database.Insert(Rule);
7149
7150 rules[RuleKey(Rule)] = Rule;
7151 }
7152 else
7153 {
7154 Rule.Channel = Channel;
7155 Rule.MessageVariable = MessageVariable;
7156 Rule.PatternMatchingScript = PatternMatchingScript;
7157 Rule.ContentScript = ContentScript;
7158
7159 await Database.Update(Rule);
7160 }
7161
7162 await e.IqResult(string.Empty, e.To);
7163 }
7164
7165 private readonly static Assembly scriptContent = typeof(GraphEncoder).Assembly;
7166 private readonly static Assembly scriptPersistence = typeof(IncCounter).Assembly;
7167 private readonly static Assembly[] prohibitedAssemblies = new Assembly[]
7168 {
7169 scriptContent, // Waher.Script.Content
7170 scriptPersistence, // Waher.Script.Persistence
7171 typeof(FractalGraph).Assembly, // Waher.Script.Fractals
7172 typeof(WhoIs).Assembly, // Waher.Script.Networking
7173 typeof(FtsCollection).Assembly, // Waher.Script FullTextSearch
7174 typeof(ConnectMsSql).Assembly, // Waher.Script.Data
7175 typeof(ShellExecute).Assembly // Waher.Script.System
7176 };
7177
7184 public static bool CheckExpressionSafe(Expression Expression, out ScriptNode Prohibited)
7185 {
7186 return CheckExpressionSafe(Expression, false, false, false, out Prohibited);
7187 }
7188
7198 public static bool CheckExpressionSafe(Expression Expression, bool AllowNamedMembers, bool AllowError,
7199 bool AllowCustomFunctions, out ScriptNode Prohibited)
7200 {
7201 ScriptNode Prohibited2 = null;
7202 bool Safe = Expression.ForAll((ScriptNode Node, out ScriptNode NewNode, object State) =>
7203 {
7204 NewNode = null;
7205
7206 Assembly Assembly = Node.GetType().Assembly;
7207
7208 foreach (Assembly A in prohibitedAssemblies)
7209 {
7210 if (A.FullName == Assembly.FullName)
7211 {
7212 if (A == scriptContent)
7213 {
7214 if (Node is Script.Content.Functions.Duration ||
7215 Node.GetType().Namespace == typeof(Utf8Encode).Namespace)
7216 {
7217 return true;
7218 }
7219 }
7220 else if (A == scriptPersistence)
7221 {
7222 if (Node is IncCounter ||
7223 Node is DecCounter ||
7224 Node is GetCounter ||
7225 Node is GetHashObject ||
7226 Node is PersistHash ||
7227 Node is VerifyHash)
7228 {
7229 return true;
7230 }
7231 }
7232
7233 Prohibited2 = Node;
7234 return false;
7235 }
7236 }
7237
7238 if ((Node is NamedMember && !AllowNamedMembers) ||
7239 (Node is NamedMemberAssignment && !AllowNamedMembers) ||
7240 (Node is LambdaDefinition && !AllowCustomFunctions) ||
7241 Node is NamedMethodCall ||
7242 Node is DynamicFunctionCall ||
7243 Node is DynamicMember ||
7244 Node is Create ||
7245 Node is Destroy ||
7246 (Node is Error && !AllowError))
7247 {
7248 Prohibited2 = Node;
7249 return false;
7250 }
7251
7252 return true;
7253
7254 }, null, SearchMethod.TreeOrder);
7255
7256 Prohibited = Prohibited2;
7257 return Safe;
7258 }
7259
7268 public static async Task<PushNotificationRule> TryGetPushNotificationRule(CaseInsensitiveString BareJid, string MessageType,
7269 string LocalName, string Namespace)
7270 {
7271 string Key = RuleKey(BareJid, MessageType, LocalName, Namespace);
7272
7273 if (rules.TryGetValue(Key, out PushNotificationRule Rule))
7274 return Rule;
7275
7276 Rule = await Database.FindFirstDeleteRest<PushNotificationRule>(new FilterAnd(
7277 new FilterFieldEqualTo("BareJid", BareJid),
7278 new FilterFieldEqualTo("MessageType", MessageType),
7279 new FilterFieldEqualTo("LocalName", LocalName),
7280 new FilterFieldEqualTo("Namespace", Namespace)));
7281 rules[Key] = Rule; // Store null, if none is found, to avoid repetitive searches.
7282
7283 return Rule;
7284 }
7285
7286 #endregion
7287
7288 #region Remove Rule
7289
7290 private async Task RemoveRuleHandler(object Sender, IqEventArgs e)
7291 {
7293
7294 if (!e.From.HasAccount)
7295 {
7296 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
7297 return;
7298 }
7299
7300 if (!this.IsServerDomain(e.From.Domain, true))
7301 {
7302 await e.IqErrorForbidden(e.To, "Push Forwarding service only available for clients on broker.", "en");
7303 return;
7304 }
7305
7306 string MessageType = XML.Attribute(e.Query, "type");
7307 string LocalName = XML.Attribute(e.Query, "localName");
7308 string Namespace = XML.Attribute(e.Query, "namespace");
7309
7310 PushNotificationRule Rule = await TryGetPushNotificationRule(BareJid, MessageType, LocalName, Namespace);
7311
7312 if (Rule is null)
7313 {
7314 await e.IqErrorItemNotFound(e.To, "Rule not found.", "en");
7315 return;
7316 }
7317
7318 rules.Remove(RuleKey(Rule));
7319 await Database.Delete(Rule);
7320
7321 await e.IqResult(string.Empty, e.To);
7322 }
7323
7324 #endregion
7325
7326 #endregion
7327
7328 // TODO: Retries
7329 }
7330}
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
CDATA content.
Definition: CDATA.cs:11
string Content
CDATA Content
Definition: CDATA.cs:32
bool HasPrefix
If the attribute has a prefix.
string LocalName
Attribute local name.
string Value
Attribute value.
static string GetBody(string Html)
Extracts the contents of the BODY element in a HTML string.
Body Body
First BODY element of document, if found, null otherwise.
Base class for all HTML elements.
Definition: HtmlElement.cs:13
static string EntityToCharacter(string Entity)
Converts an HTML entity into a character.
Definition: HtmlEntity.cs:73
HTML Entity, as a unicode number string.
Base class for all HTML nodes.
Definition: HtmlNode.cs:11
Static class managing encoding and decoding of internet content.
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 Task< ContentResponse > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
Helps with common JSON-related tasks.
Definition: JSON.cs:16
static string Encode(string s)
Encodes a string for inclusion in JSON.
Definition: JSON.cs:537
Contains a markdown document. This markdown document class supports original markdown,...
static string Encode(string s)
Encodes all special characters in a string so that it can be included in a markdown document without ...
Task< string > GenerateMarkdown()
Generates Markdown from the 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,...
Represents alternative versions of the same content, encoded with multipart/alternative
Represents content embedded in other content.
string FileName
Filename of embedded object.
ContentDisposition Disposition
Disposition of embedded object.
string Name
Name of embedded object.
string Description
Content-Description of embedded object, if defined.
string ContentType
Content-Type of embedded object.
object Decoded
Decoded body of embedded object. ContentType defines how TransferDecoded is transformed into Decoded.
byte[] TransferDecoded
Transformed body of embedded object. TransferEncoding defines how Raw is transformed into TransferDec...
byte[] Raw
Raw, untransformed body of embedded object.
string ID
Content-ID of embedded object, if defined.
Abstract base class for multipart content
EmbeddedContent[] Content
Embedded content.
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 string HtmlValueEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe or the quote.
Definition: XML.cs:211
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:29
static string HtmlAttributeEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe.
Definition: XML.cs:121
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Definition: XML.cs:713
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
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 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
Contains statistical information about one item.
Definition: Statistic.cs:9
void Bind()
Binds to a TcpClient that was already connected when provided to the constructor.
string RemoteEndPoint
Remote End-point of connection. This corresponds to the IP Endpoint of the remote party in normal cas...
void Continue()
Continues reading from the socket, if paused in an event handler.
Simple base class for classes implementing communication protocols.
void Information(string Comment)
Called to inform the viewer of something.
DNS resolver, as defined in:
Definition: DnsResolver.cs:32
static Task< SRV > TryLookupServiceEndpoint(string DomainName, string ServiceName, string Protocol)
Tries to look up a service endpoint for a domain. If multiple are available, an appropriate one is se...
static Task< string[]> TryLookupMailExchange(string DomainName)
Tries to look up the Mail Exchanges related to a given domain name.
Definition: DnsResolver.cs:776
Base class of all HTTP Exceptions.
Implements an HTTP server.
Definition: HttpServer.cs:41
HttpResource Register(HttpResource Resource)
Registers a resource with the server.
Definition: HttpServer.cs:1568
const int DefaultHttpPort
Default HTTP Port (80).
Definition: HttpServer.cs:45
const int DefaultHttpsPort
Default HTTPS port (443).
Definition: HttpServer.cs:50
Module that controls the life cycle of communication.
static bool Stopping
If the system is stopping.
Event arguments for SMTP Message events.
Represents one message received over SMTP
Definition: SmtpMessage.cs:13
object DecodedBody
Decoded body. ContentType defines how TransformedBody is transformed into DecodedBody.
Definition: SmtpMessage.cs:198
MailAddress[] To
Recipients, if defined by To mail headers.
Definition: SmtpMessage.cs:207
DateTimeOffset? Date
Date of message, if defined
Definition: SmtpMessage.cs:64
MailAddress[] Cc
Recipients, if defined by Cc mail headers.
Definition: SmtpMessage.cs:216
MailAddress FromMail
From address, as specified by the client to initiate mail transfer.
Definition: SmtpMessage.cs:122
MailAddress FromHeader
From address, as specified in the mail headers.
Definition: SmtpMessage.cs:131
Priority Priority
Priority of message
Definition: SmtpMessage.cs:55
EmbeddedContent[] Attachments
Any attachments, if specified.
Definition: SmtpMessage.cs:270
MailAddress[] Bcc
Recipients, if defined by Bcc mail headers.
Definition: SmtpMessage.cs:225
KeyValuePair< string, string >[] AllHeaders
All mail headers provided by client.
Definition: SmtpMessage.cs:158
string ContentType
Content Type of message, if defined. Affects how TransformedBody is transformed into DecodedBody.
Definition: SmtpMessage.cs:104
string Subject
Subject of message, if defined
Definition: SmtpMessage.cs:113
EmbeddedContent[] InlineObjects
Any inline objects, if specified.
Definition: SmtpMessage.cs:261
MailAddress Sender
Sender, as specified in the mail headers.
Definition: SmtpMessage.cs:140
byte[] UntransformedBody
Raw, untrasnformed body of message.
Definition: SmtpMessage.cs:176
Implements a simple SMTP Server, as defined in:
Definition: SmtpServer.cs:45
const int DefaultSmtpPort
Default SMTP Port (25).
Definition: SmtpServer.cs:49
Event arguments for connection events.
Sniffer that stores events in memory.
Outputs sniffed data to an XML file.
Implements a text-based TCP Client, by using the thread-safe full-duplex BinaryTcpClient.
Component managing accounts.
Definition: Accounts.cs:15
Abstract base class for XMPP client connections
const string StreamNamespace
http://etherx.jabber.org/streams
Task< bool > StreamErrorUnauthorized()
Returns an Unauthorized XML stream error.
Base class for components.
Definition: Component.cs:17
CaseInsensitiveString Subdomain
Subdomain name.
Definition: Component.cs:77
virtual void Dispose()
IDisposable.Dispose
Definition: Component.cs:102
async Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Message stanza.
Definition: Component.cs:510
virtual bool SupportsAccounts
If the component supports accounts (true), or if the subdomain name is the only valid address.
Definition: Component.cs:116
Event arguments for IQ queries.
Definition: IqEventArgs.cs:12
XmppAddress From
From address attribute
Definition: IqEventArgs.cs:93
Task IqErrorNotAcceptable(XmppAddress From, string ErrorText, string Language)
Returns a not-acceptable error.
Definition: IqEventArgs.cs:248
Task IqResult(string Xml, string From)
Returns a response to the current request.
Definition: IqEventArgs.cs:113
Task IqErrorItemNotFound(XmppAddress From, string ErrorText, string Language)
Returns a item-not-found error.
Definition: IqEventArgs.cs:206
XmlElement Query
Query element, if found, null otherwise.
Definition: IqEventArgs.cs:70
Task IqErrorNotAllowed(XmppAddress From, string ErrorText, string Language)
Returns a not-allowed error.
Definition: IqEventArgs.cs:192
XmppAddress To
To address attribute
Definition: IqEventArgs.cs:88
async Task IqError(string ErrorType, string Xml, XmppAddress From, string ErrorText, string Language)
Returns an error response to the current request.
Definition: IqEventArgs.cs:139
Task IqErrorBadRequest(XmppAddress From, string ErrorText, string Language)
Returns a bad-request error.
Definition: IqEventArgs.cs:164
Task IqErrorForbidden(XmppAddress From, string ErrorText, string Language)
Returns a forbidden error.
Definition: IqEventArgs.cs:234
Associates a specific IQ request with an IQ response.
Definition: IqResponse.cs:11
async Task< KeyValuePair< string, bool > > GetResponse()
Gets the response of the IQ request.
Definition: IqResponse.cs:57
Maintains a set of IQ responses, for a limited time.
Definition: IqResponses.cs:12
Event arguments for responses to IQ queries.
CaseInsensitiveString BareJid
Bare JID
Definition: MailContent.cs:57
Web resource for requesting white-list authentication of a client.
Event arguments for events accessing parent XMPP client connections.
XmppClient Client
Reference to XMPP client parent connection
Presence information event arguments.
bool ResponseSent
If a response has been sent back to the sender.
DateTimeOffset Timestamp
Timestamp of reception.
Contains information about one persisted XML element.
string Namespace
Namespace of XML content element in message
string LocalName
Local Name of XML content element in message
int NrUpdates
Number of times object has been updated. (Created once only, without further updates = 1).
Contains information about an item of an entity.
Definition: Item.cs:12
override string ToString()
Object.ToString()
Definition: Item.cs:55
Manages the connection with an SMTP server.
Task< bool > RelayMessage(SmtpMessage Message, MailAddress Recipient)
Relays a mail message
Contains information about a stanza.
Definition: Stanza.cs:9
string Content
Literal XML content.
Definition: Stanza.cs:53
XmlElement StanzaElement
Stanza element.
Definition: Stanza.cs:113
Mainstains information about connectivity from a specific endpoint.
void EndpointConnected(IEndpoint Endpoint)
Call this method when the endpoint connects.
void EndpointDisconnected(IEndpoint Endpoint, long ConnectionTimeMilliseconds)
Call this method when the endpoint disconnects.
Mainstains information about connectivity from a specific s2s endpoint.
Contains information about one XMPP address.
Definition: XmppAddress.cs:9
override string ToString()
object.ToString()
Definition: XmppAddress.cs:190
bool IsBareJID
If the address is a Bare JID.
Definition: XmppAddress.cs:159
bool HasAccount
If the address has an account part.
Definition: XmppAddress.cs:167
bool IsEmpty
If the address is empty.
Definition: XmppAddress.cs:183
CaseInsensitiveString Domain
Domain
Definition: XmppAddress.cs:97
CaseInsensitiveString Address
XMPP Address
Definition: XmppAddress.cs:37
bool IsDomain
If the Address is a domain.
Definition: XmppAddress.cs:175
XmppAddress ToBareJID()
Returns the Bare JID as an XmppAddress object.
Definition: XmppAddress.cs:215
CaseInsensitiveString BareJid
Bare JID
Definition: XmppAddress.cs:45
static readonly XmppAddress Empty
Empty address.
Definition: XmppAddress.cs:31
CaseInsensitiveString Account
Account
Definition: XmppAddress.cs:124
bool IsFullJID
If the Address is a Full JID.
Definition: XmppAddress.cs:151
Event arguments for events related to XMPP S2S endpoints.
Manages an XMPP server-to-server connection.
Task< bool > Connect(bool DisposeCurrent)
Connects to the server.
Manages an XMPP server-to-server connection tunneled over an XMPP client connection to a parent broke...
Connectivity information for a domain.
Definition: XmppServer.cs:2687
CaseInsensitiveString Host
Host name
Definition: XmppServer.cs:2701
S2sType Type
Type of S2S connection
Definition: XmppServer.cs:2691
bool TrustCertificate
If certificate should be trusted
Definition: XmppServer.cs:2711
CaseInsensitiveString Domain
Domain name
Definition: XmppServer.cs:2696
Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Message stanza.
Definition: XmppServer.cs:2983
const string DiscoveryItemsNamespace
http://jabber.org/protocol/disco#items (XEP-0030)
Definition: XmppServer.cs:138
Task< IqResultEventArgs > IqRequest(string Type, string From, string To, string Language, string ContentXml, bool CheckShortTermCache)
Sends an IQ stanza to a recipient.
Definition: XmppServer.cs:3801
const string ExtendedAddressingNamespace
http://jabber.org/protocol/address (XEP-0033)
Definition: XmppServer.cs:143
const string AbuseReportingNamespace
urn:xmpp:reporting:reason:abuse:0 (XEP-0377)
Definition: XmppServer.cs:208
string GetRandomHexString(int NrBytes)
Generates a random hexadecimal string.
Definition: XmppServer.cs:696
Task< DateTime?> GetEarliestLoginOpportunity(IClientConnection Connection)
Evaluates when a client is allowed to login.
Definition: XmppServer.cs:1518
async Task< bool > GetLastPresence(CaseInsensitiveString BareJid, EventHandlerAsync< PresenceEventArgs > Callback, object State)
Gets the last presence of a bare JID.
Definition: XmppServer.cs:3524
Task< bool > SendIqRequest(string Type, string From, string To, string Language, string ContentXml, bool CheckShortTermCache, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ stanza to a recipient.
Definition: XmppServer.cs:3688
static async Task< XmppServer > Create(CaseInsensitiveString Domain, CaseInsensitiveString[] AlternativeDomains, int[] ClientToServerPorts, int[] ServerToServerPorts, X509Certificate ServerCertificate, bool EncryptionRequired, IXmppServerPersistenceLayer PersistenceLayer, SmtpServer SmtpServer, HttpServer HttpServer)
Creates an instance of an XMPP server.
Definition: XmppServer.cs:377
static void RegisterRemoteDomain(CaseInsensitiveString RemoteDomain, string Host, int Port, bool TrustCertificate)
By default, remote domains are reached using DNS, and the default server-to-server port (5269) is use...
Definition: XmppServer.cs:2325
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters a message handler.
Definition: XmppServer.cs:1804
const string RosterNamespace
jabber:iq:roster (RFC 6121)
Definition: XmppServer.cs:118
const string VCardNamespace
vcard-temp (XEP-0054)
Definition: XmppServer.cs:153
static Scheduler Scheduler
Scheduler
Definition: XmppServer.cs:662
const string BlockingCommandNamespace
urn:xmpp:blocking (XEP-0191)
Definition: XmppServer.cs:173
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Set handler.
Definition: XmppServer.cs:1703
Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Presence stanza.
Definition: XmppServer.cs:3415
IClientConnection[] GetClientConnections()
Get active client connections
Definition: XmppServer.cs:826
static Task< XmppServer > Create(CaseInsensitiveString Domain, CaseInsensitiveString[] AlternativeDomains, int ClientToServerPort, int ServerToServerPort, X509Certificate ServerCertificate, bool EncryptionRequired, IXmppServerPersistenceLayer PersistenceLayer)
Creates an instance of an XMPP server.
Definition: XmppServer.cs:339
IXmppServerPersistenceLayer PersistenceLayer
Reference to persistence layer
Definition: XmppServer.cs:982
const string DiscoveryNamespace
http://jabber.org/protocol/disco#info (XEP-0030)
Definition: XmppServer.cs:133
void UpdateCertificate(X509Certificate ServerCertificate)
Updates the server certificate
Definition: XmppServer.cs:946
const string MessagePushNamespace
http://waher.se/Schema/PushNotification.xsd
Definition: XmppServer.cs:228
CommunicationLayer S2sSniffers
Sniffers for XMPP S2S communication.
Definition: XmppServer.cs:657
EventHandlerAsync< ParentConnectionEventArgs > GetParentConnection
Event raised when the server needs access to a parent connection.
Definition: XmppServer.cs:2922
static bool IsRemoteDomainRegistered(CaseInsensitiveString RemoteDomain)
Checks if a remote domain is registered.
Definition: XmppServer.cs:2367
Task< IqResultEventArgs > IqRequest(string Type, string From, string To, string Language, string ContentXml)
Sends an IQ stanza to a recipient.
Definition: XmppServer.cs:3784
S2sEndpointStatistics[] GetServerConnectionStatistics()
Gets S2S connection statistics.
Definition: XmppServer.cs:882
Task< bool > SendMessage(string Type, string Id, string From, string To, string Language, string ContentXml)
Sends a Message stanza to a recipient.
Definition: XmppServer.cs:3862
const int DefaultConnectionBacklog
Default Connection backlog (10).
Definition: XmppServer.cs:83
static void GetErrorInformation(Exception ex, out string Type, out string Xml)
Converts an Exception to an XMPP error message.
Definition: XmppServer.cs:3426
S2sEndpointStatistics GetS2sStatistics(CaseInsensitiveString Endpoint, string Type)
Gets available S2S Endpoint statistics for an endpoint.
Definition: XmppServer.cs:729
async Task< bool > SendIqRequest(string Type, XmppAddress From, XmppAddress To, string Language, string ContentXml, bool CheckShortTermCache, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ stanza to a recipient.
Definition: XmppServer.cs:3727
bool TryGetClientConnections(string BareJID, out IClientConnection[] Connections)
Tries to get available connections for a given client.
Definition: XmppServer.cs:855
const string SaslNamespace
urn:ietf:params:xml:ns:xmpp-sasl
Definition: XmppServer.cs:113
Task< Item[]> ServiceItemsDiscoveryAsync(string To)
Performs a service items discovery request
Definition: XmppServer.cs:6763
Task< bool > SendIqRequest(string Type, XmppAddress From, XmppAddress To, string Language, string ContentXml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ stanza to a recipient.
Definition: XmppServer.cs:3707
static byte[] GetRandomNumbers(int NrBytes)
Generates a set of random numbers.
Definition: XmppServer.cs:679
virtual Task< bool > MessageError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends a message error stanza.
Definition: XmppServer.cs:4152
EventHandlerAsync< XmppS2SEndpointEventArgs > S2sEndpointRemoved
Event raised when an S2S endpoint is removed.
Definition: XmppServer.cs:2902
virtual async Task< bool > IqError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
Sends an IQ error stanza.
Definition: XmppServer.cs:4049
bool TryGetClientConnection(string FullJID, out IClientConnection Connection)
Tries to get an active client connection.
Definition: XmppServer.cs:844
async Task< bool > SendMessage(string Type, string Id, XmppAddress From, XmppAddress To, string Language, string ContentXml)
Sends a Message stanza to a recipient.
Definition: XmppServer.cs:3877
async Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Message stanza.
Definition: XmppServer.cs:2152
virtual async Task< bool > PresenceError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends a presence error stanza.
Definition: XmppServer.cs:4083
Task< ServiceDiscoveryResult > ServiceDiscoveryAsync(string To)
Performs a service discovery request
Definition: XmppServer.cs:6689
async Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
IQ stanza.
Definition: XmppServer.cs:1863
virtual Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Sends a message stanza.
Definition: XmppServer.cs:4139
async Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Presence stanza.
Definition: XmppServer.cs:3041
const string ReportingNamespace
urn:xmpp:reporting:0 (XEP-0377)
Definition: XmppServer.cs:203
const string BlockingCommandErrorNamespace
urn:xmpp:blocking:errors (XEP-0191)
Definition: XmppServer.cs:178
const string ContentNamespace
urn:xmpp:content
Definition: XmppServer.cs:223
Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
IQ stanza.
Definition: XmppServer.cs:2048
virtual async Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Sends a presence stanza.
Definition: XmppServer.cs:4120
static void RegisterRemoteDomain(CaseInsensitiveString RemoteDomain, CaseInsensitiveString ServerDomain, string Host, int Port, bool TrustCertificate)
By default, remote domains are reached using DNS, and the default server-to-server port (5269) is use...
Definition: XmppServer.cs:2348
Task< bool > SendIqRequest(string Type, string From, string To, string Language, string ContentXml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ stanza to a recipient.
Definition: XmppServer.cs:3668
const string DataFormsNamespace
jabber:x:data (XEP-0004)
Definition: XmppServer.cs:123
const string OfflineMessagesNamespace
msgoffline (XEP-0160)
Definition: XmppServer.cs:168
EventHandlerAsync< XmppS2SEndpointEventArgs > S2sEndpointCreated
Event raised when an S2S endpoint is created.
Definition: XmppServer.cs:2897
static async Task< PushNotificationToken > TryGetPushNotificationToken(CaseInsensitiveString BareJid)
Tries to get a push notification token for a client, if one exists.
Definition: XmppServer.cs:6957
static bool IsValidUserName(string UserName)
Checks if a user name contains invalid characters.
Definition: XmppServer.cs:5137
const string AlternativesNamespace
http://waher.se/Schema/AlternativeNames.xsd
Definition: XmppServer.cs:233
bool TryGetS2sStatistics(CaseInsensitiveString Endpoint, out S2sEndpointStatistics Stat)
Tries to get available S2S Endpoint statistics for an endpoint.
Definition: XmppServer.cs:715
async Task< IqResultEventArgs > IqRequest(string Type, XmppAddress From, XmppAddress To, string Language, string ContentXml, bool CheckShortTermCache)
Sends an IQ stanza to a recipient.
Definition: XmppServer.cs:3833
const string SoftwareVersionNamespace
jabber:iq:version (XEP-0092)
Definition: XmppServer.cs:163
const int DefaultBufferSize
Default buffer size (16384).
Definition: XmppServer.cs:88
const string PrivateXmlStorageNamespace
jabber:iq:private (XEP-0049)
Definition: XmppServer.cs:148
CommunicationLayer C2sSniffers
Sniffers for XMPP C2S communication.
Definition: XmppServer.cs:652
bool UnregisterComponent(IComponent Component)
Unregisters a component from the server.
Definition: XmppServer.cs:1163
const string PingNamespace
urn:xmpp:ping (XEP-0199)
Definition: XmppServer.cs:183
bool IsServerDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the server domain, or optionally, an alternative domain.
Definition: XmppServer.cs:898
X509Certificate ServerCertificate
Server domain certificate.
Definition: XmppServer.cs:938
bool EncryptionRequired
If C2S encryption is requried.
Definition: XmppServer.cs:955
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Get handler.
Definition: XmppServer.cs:1756
string GetDialbackKey(string ReceivingServer, string OriginatingServer, string ReceivingStreamId)
Gets a dialback key, calculated according to XEP-0185: https://xmpp.org/extensions/xep-0185....
Definition: XmppServer.cs:1290
static Task< XmppServer > Create(CaseInsensitiveString Domain, CaseInsensitiveString[] AlternativeDomains, int[] ClientToServerPorts, int[] ServerToServerPorts, X509Certificate ServerCertificate, bool EncryptionRequired, IXmppServerPersistenceLayer PersistenceLayer)
Creates an instance of an XMPP server.
Definition: XmppServer.cs:357
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
Definition: XmppServer.cs:1691
const int DefaultS2sPort
Default Server-to-Server Port (5269).
Definition: XmppServer.cs:78
CaseInsensitiveString Domain
Domain name.
Definition: XmppServer.cs:922
static bool CheckExpressionSafe(Expression Expression, bool AllowNamedMembers, bool AllowError, bool AllowCustomFunctions, out ScriptNode Prohibited)
Checks if an expression is safe to execute (if it comes from an external source).
Definition: XmppServer.cs:7198
const string TimeNamespace
urn:xmpp:time (XEP-0202)
Definition: XmppServer.cs:188
const int DefaultC2sPort
Default Client-to-Server Port (5222).
Definition: XmppServer.cs:73
static bool CheckExpressionSafe(Expression Expression, out ScriptNode Prohibited)
Checks if an expression is safe to execute (if it comes from an external source).
Definition: XmppServer.cs:7184
async Task< CaseInsensitiveString > FindComponentAsync(CaseInsensitiveString Jid, CaseInsensitiveString Feature)
Finds a component having a specific feature, servicing a JID.
Definition: XmppServer.cs:6832
const string RegisterNamespace
jabber:iq:register (XEP-0077)
Definition: XmppServer.cs:158
const string StanzaNamespace
urn:ietf:params:xml:ns:xmpp-stanzas (RFC 6120)
Definition: XmppServer.cs:103
bool TryGetS2sEndpoint(string RemoteDomain, out IS2SEndpoint Endpoint)
Tries to get a server-to-server connection state object.
Definition: XmppServer.cs:2468
void Dispose()
IDisposable.Dispose
Definition: XmppServer.cs:987
static Task< XmppServer > Create(CaseInsensitiveString Domain, CaseInsensitiveString[] AlternativeDomains, X509Certificate ServerCertificate, bool EncryptionRequired, IXmppServerPersistenceLayer PersistenceLayer)
Creates an instance of an XMPP server.
Definition: XmppServer.cs:323
async Task< IRecipient > TryGetRecipient(XmppAddress To, XmppAddress From)
Tries to get the recipient of a stanza.
Definition: XmppServer.cs:1622
EventHandlerAsync< ClientConnectionEventArgs > ClientConnectionRemoved
Event raised when a client connection has been removed.
Definition: XmppServer.cs:1400
virtual async Task< bool > IqResult(string Id, XmppAddress To, XmppAddress From, string ResultXml)
Sends an IQ result stanza.
Definition: XmppServer.cs:4066
Statistics.CommunicationStatistics GetCommunicationStatisticsSinceLast()
Gets communication statistics since last call.
Definition: XmppServer.cs:5649
const string DelayedDeliveryNamespace
urn:xmpp:delay (XEP-0203)
Definition: XmppServer.cs:193
static async Task< S2SRec > GetDomain(CaseInsensitiveString DomainOrSubdomain)
Gets information about a domain.
Definition: XmppServer.cs:2573
const string MailNamespace
urn:xmpp:smtp
Definition: XmppServer.cs:218
const string OAuth1FormSignatureNamespace
urn:xmpp:xdata:signature:oauth1 (XEP-0348)
Definition: XmppServer.cs:198
EventHandlerAsync< ServerConnectionEventArgs > ServerConnectionRemoved
Event raised when a server connection has been removed.
Definition: XmppServer.cs:1415
static async Task< PushNotificationRule > TryGetPushNotificationRule(CaseInsensitiveString BareJid, string MessageType, string LocalName, string Namespace)
Tries to get a push notification token for a client, if one exists.
Definition: XmppServer.cs:7268
const string StreamsNamespace
urn:ietf:params:xml:ns:xmpp-streams
Definition: XmppServer.cs:108
string NewId(int NrBytes)
Generates a new ID.
Definition: XmppServer.cs:669
const string AvatarStorageNamespace
storage:client:avatar (XEP-0008)
Definition: XmppServer.cs:128
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Set handler.
Definition: XmppServer.cs:1769
virtual async Task< string > IqError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends an IQ error stanza.
Definition: XmppServer.cs:4031
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers a message handler.
Definition: XmppServer.cs:1732
async Task< Item[]> ServiceItemsDiscoveryAsync(string To, string Node)
Performs a service items discovery request
Definition: XmppServer.cs:6773
async Task ProcessMessage(SmtpMessage Message)
Processes an incoming SMTP message.
Definition: XmppServer.cs:5855
virtual async Task< bool > PresenceError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
Sends a presence error stanza.
Definition: XmppServer.cs:4101
async Task< bool > Presence(string Type, string Id, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Presence stanza.
Definition: XmppServer.cs:2997
static CaseInsensitiveString GetBareJID(CaseInsensitiveString JID)
Gets the Bare JID from a JID, which may be a Full JID.
Definition: XmppServer.cs:1504
CaseInsensitiveString[] AlternativeDomains
Alternative domain names.
Definition: XmppServer.cs:930
bool RegisterComponent(IComponent Component)
Registers a component with the server.
Definition: XmppServer.cs:1131
Task< IqResultEventArgs > IqRequest(string Type, XmppAddress From, XmppAddress To, string Language, string ContentXml)
Sends an IQ stanza to a recipient.
Definition: XmppServer.cs:3817
const string SpamReportingNamespace
urn:xmpp:reporting:reason:abuse:0 (XEP-0377)
Definition: XmppServer.cs:213
async Task< bool > SendMailMessage(CaseInsensitiveString From, CaseInsensitiveString To, string Subject, string Markdown)
Sends a mail message
Definition: XmppServer.cs:6654
async Task< ServiceDiscoveryResult > ServiceDiscoveryAsync(string To, string Node)
Performs a service discovery request
Definition: XmppServer.cs:6700
async Task< int > DeleteOldMailContent(DateTime OlderThan)
Deletes old mail content.
Definition: XmppServer.cs:6642
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static readonly CaseInsensitiveString Empty
Empty case-insensitive string
int Length
Gets the number of characters in the current CaseInsensitiveString object.
string LowerCase
Lower-case representation of the case-insensitive string.
int IndexOf(CaseInsensitiveString value, StringComparison comparisonType)
Reports the zero-based index of the first occurrence of the specified string in the current System....
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
int CompareTo(CaseInsensitiveString other)
Compares this instance with a specified System.CaseInsensitiveString object and indicates whether thi...
CaseInsensitiveString Remove(int startIndex)
Returns a new string in which all the characters in the current instance, beginning at a specified po...
CaseInsensitiveString[] Split(params char[] separator)
Returns a string array that contains the substrings in this instance that are delimited by elements o...
CaseInsensitiveString Substring(int startIndex, int length)
Retrieves a substring from this instance. The substring starts at a specified character position and ...
char[] ToCharArray(int startIndex, int length)
Copies the characters in a specified substring in this instance to a Unicode character array.
bool EndsWith(CaseInsensitiveString value, StringComparison comparisonType)
Determines whether the end of this string instance matches the specified string when compared using t...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static Task< IEnumerable< object > > FindDelete(string Collection, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
Definition: Database.cs:1442
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.
This filter selects objects that have a named field lesser or equal to a given value.
Implements an in-memory cache.
Definition: Cache.cs:17
ValueType[] GetValues()
Gets all available values in the cache.
Definition: Cache.cs:383
bool ContainsKey(KeyType Key)
Checks if a key is available in the cache.
Definition: Cache.cs:404
void Dispose()
IDisposable.Dispose
Definition: Cache.cs:99
int Count
Number of items in cache
Definition: Cache.cs:337
bool Remove(KeyType Key)
Removes an item from the cache.
Definition: Cache.cs:616
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Definition: Cache.cs:311
void Add(KeyType Key, ValueType Value)
Adds an item to the cache.
Definition: Cache.cs:446
void Clear()
Clears the cache.
Definition: Cache.cs:679
Event arguments for cache item removal events.
KeyType Key
Key of item that was removed.
ValueType Value
Value of item that was removed.
RemovedReason Reason
Reason for removing the item.
Static class managing persistent counters.
static Task< long > IncrementCounter(CaseInsensitiveString Key)
Increments a counter.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
static Type[] NoTypes
Contains an empty array of types.
Definition: Types.cs:567
static object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:572
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:85
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
static async Task< bool > SetAsync(string Key, string Value)
Sets a string-valued setting.
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Definition: Semaphore.cs:19
Static class of application-wide semaphores that can be used to order access to editable objects.
Definition: Semaphores.cs:17
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
Definition: Semaphores.cs:91
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
Definition: Scheduler.cs:14
void Dispose()
IDisposable.Dispose
Definition: Scheduler.cs:34
DateTime Add(DateTime When, Action< object > Callback, object State)
Adds an event.
Definition: Scheduler.cs:54
Encodes graphs as images
Definition: GraphEncoder.cs:15
Generates a callback function based on script.
Definition: Callback.cs:20
Creates a connection to an external MS SQL database.
Definition: ConnectMsSql.cs:15
Class managing a script expression.
Definition: Expression.cs:41
bool ForAll(ScriptNodeEventHandler Callback, object State, bool DepthFirst)
Calls the callback method for all script nodes defined for the expression.
Definition: Expression.cs:5456
Defines a clickable fractal graph in the complex plane.
Definition: FractalGraph.cs:23
Matches a Full-Text-Search Index with a Database Collection.
Creates an object of a specific class. The first argument must evaluate to the type that is to be cre...
Definition: Create.cs:17
Destroys a value. If the function references a variable, the variable is also removed.
Definition: Destroy.cs:14
Throws an exception.
Definition: Error.cs:11
Extract the properties of a type or an object.
Definition: Properties.cs:16
Removes a variable from the variables collection, without destroying its value.
Definition: Remove.cs:16
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
Makes a WHOIS query regarding an IP address.
Definition: WhoIs.cs:16
Gets the current count of a counter
Definition: GetCounter.cs:12
Tries to get the associated object value from a persisted hash value
ShellExecute(FileName,Arguments,WorkFolder[,TimeoutMs[,LogStandardOutput[,KillOnTimeout]]])
Definition: ShellExecute.cs:18
Contains methods for simple hash calculations.
Definition: Hashes.cs:57
static string ComputeHMACSHA256HashString(byte[] Key, byte[] Data)
Computes the HMAC-SHA-256 hash of a block of binary data.
Definition: Hashes.cs:724
static byte[] ComputeHMACSHA1Hash(byte[] Key, byte[] Data)
Computes the HMAC-SHA-1 hash of a block of binary data.
Definition: Hashes.cs:677
static string BinaryToString(byte[] Data)
Converts an array of bytes to a string with their hexadecimal representations (in lower case).
Definition: Hashes.cs:63
static byte[] ComputeSHA256Hash(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Definition: Hashes.cs:469
static string ComputeSHA1HashString(byte[] Data)
Computes the SHA-1 hash of a block of binary data.
Definition: Hashes.cs:395
Class that monitors login events, and help applications determine malicious intent....
Definition: LoginAuditor.cs:26
async Task< DateTime?> GetEarliestLoginOpportunity(string RemoteEndPoint, string Protocol)
Checks when a remote endpoint can login.
static async void Success(string Message, string UserName, string RemoteEndPoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a successful login attempt.
static void Fail(string Message, string UserName, string RemoteEndPoint, string Protocol)
Handles a failed login attempt.
Interface for asynchronously disposable objects.
Task DisposeAsync()
Disposes of the object, asynchronously.
void Information(string Comment)
Called to inform the viewer of something.
void Exception(string Exception)
Called to inform the viewer of an exception state.
Interface for observable classes implementing communication protocols.
bool HasSniffers
If there are sniffers registered on the object.
ISniffer[] Sniffers
Registered sniffers.
void Add(ISniffer Sniffer)
Adds a sniffer to the node.
Interface for SMTP user accounts.
Definition: IAccount.cs:11
CaseInsensitiveString UserName
User Name
Definition: IAccount.cs:24
Interface for authentication mechanisms.
Task< bool?> AuthenticationRequest(string Data, ISaslServerSide Connection, ISaslPersistenceLayer PersistenceLayer)
Authentication request has been made.
bool Allowed(SslStream SslStream)
Checks if a mechanism is allowed during the current conditions.
Task Initialize()
Performs intitialization of the mechanism. Can be used to set static properties that will be used thr...
string RemoteEndPoint
Remote endpoint.
string Protocol
String representing protocol being used.
void ResetState(bool Authenticated)
Resets the state machine.
CaseInsensitiveString UserName
User name
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
Definition: ISniffer.cs:10
Interface for XMPP user accounts.
Definition: IAccount.cs:9
Task< bool > BeginWrite(string Xml, EventHandlerAsync< DeliveryEventArgs > Callback, object State)
Writes XML to the client.
PresenceEventArgs LastPresence
Last presence received.
Task< bool > SaslErrorInvalidMechanism()
Returns a Invalid Mechanism SASL error.
Task< bool > SaslErrorMechanismTooWeak()
Returns a Mechanism too weak SASL error.
Task< bool > SaslErrorTemporaryAuthFailure(string Message, string Language)
Returns a Authentication Failure SASL error.
XmppConnectionState State
Connection state.
bool WantsBlockList
If connection is interested in block list events.
void SetMechanism(IAuthenticationMechanism Mechanism)
Sets the authentication mechanism for the connection.
Task< bool > StreamErrorInvalidXml()
Returns a Invalid XML stream error.
Interface for components.
Definition: IComponent.cs:10
string Type
Type of endpoint
Definition: IEndpoint.cs:14
Interface for recipients of stanzas.
Definition: IRecipient.cs:9
Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
IQ stanza.
Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Presence stanza.
Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Message stanza.
Interface for roster items.
Definition: IRosterItem.cs:43
Interface for XMPP S2S endpoints
Definition: IS2sEndpoint.cs:11
CaseInsensitiveString RemoteDomain
Connection to domain.
Definition: IS2sEndpoint.cs:29
Interface for senders of stanzas.
Definition: ISender.cs:10
Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Message stanza.
Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Presence stanza.
Task< string > IqError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends an IQ Error stanza.
Task< bool > IqResult(string Id, XmppAddress To, XmppAddress From, string ResultXml)
Sends an IQ Result stanza.
Interface for XMPP Server persistence layers. The persistence layer should implement caching.
Task< byte[]> GetDialbackSecret()
Gets the Dialback secret, as defined in XEP-0185.
Interface for Mutual TLS (mTLS) Clients or TLS servers.
Definition: ImplTypes.g.cs:58
ContentDisposition
Content disposition
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.
BinaryPresentationMethod
How binary data is to be presented.
FormType
Type of data form.
Definition: FormType.cs:7
ClientType
Type of client requesting notification.
Definition: ClientType.cs:7
XmppConnectionState
State of XMPP connection.
SubscriptionStatus
Roster item subscription status enumeration.
Definition: IRosterItem.cs:10
BlockingReason
Reason for blocking an account.
MessageType
Type of message received.
Definition: MessageType.cs:7
PendingSubscription
Pending subscription states.
Definition: RosterItem.cs:54
RemovedReason
Reason for removing the item.
SearchMethod
Method to traverse the expression structure
Definition: ScriptNode.cs:38
ContentType
DTLS Record content type.
Definition: Enumerations.cs:11
Reason
Reason a token is not valid.
Definition: JwtFactory.cs:15