Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
XmppServer.cs
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.Net;
5using System.Net.Mail;
6using System.Net.NetworkInformation;
7using System.Net.Security;
8using System.Net.Sockets;
9using System.Reflection;
10using System.Security.Cryptography;
11using System.Security.Cryptography.X509Certificates;
12using System.Text;
13using System.Xml;
14using System.Threading;
15using System.Threading.Tasks;
16using Waher.Content;
21using Waher.Events;
41using Waher.Security;
43using Waher.Script;
57
59{
63 public class XmppServer : IDisposable, IRecipient, ISender
64 {
68 public const int DefaultC2sPort = 5222;
69
73 public const int DefaultS2sPort = 5269;
74
78 public const int DefaultConnectionBacklog = 10;
79
83 public const int DefaultBufferSize = 16384;
84
88 public const int MaxGroupLength = 1000;
89
93 public const int MaxNameLength = 1000;
94
98 public const string StanzaNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas";
99
103 public const string StreamsNamespace = "urn:ietf:params:xml:ns:xmpp-streams";
104
108 public const string SaslNamespace = "urn:ietf:params:xml:ns:xmpp-sasl";
109
113 public const string RosterNamespace = "jabber:iq:roster";
114
118 public const string DataFormsNamespace = "jabber:x:data";
119
123 public const string AvatarStorageNamespace = "storage:client:avatar";
124
128 public const string DiscoveryNamespace = "http://jabber.org/protocol/disco#info";
129
133 public const string DiscoveryItemsNamespace = "http://jabber.org/protocol/disco#items";
134
138 public const string ExtendedAddressingNamespace = "http://jabber.org/protocol/address";
139
143 public const string PrivateXmlStorageNamespace = "jabber:iq:private";
144
148 public const string VCardNamespace = "vcard-temp";
149
153 public const string RegisterNamespace = "jabber:iq:register";
154
158 public const string SoftwareVersionNamespace = "jabber:iq:version";
159
163 public const string OfflineMessagesNamespace = "msgoffline";
164
168 public const string BlockingCommandNamespace = "urn:xmpp:blocking";
169
173 public const string BlockingCommandErrorNamespace = "urn:xmpp:blocking:errors";
174
178 public const string PingNamespace = "urn:xmpp:ping";
179
183 public const string TimeNamespace = "urn:xmpp:time";
184
188 public const string DelayedDeliveryNamespace = "urn:xmpp:delay";
189
193 public const string OAuth1FormSignatureNamespace = "urn:xmpp:xdata:signature:oauth1";
194
198 public const string ReportingNamespace = "urn:xmpp:reporting:0";
199
203 public const string AbuseReportingNamespace = "urn:xmpp:reporting:reason:abuse:0";
204
208 public const string SpamReportingNamespace = "urn:xmpp:reporting:reason:spam:0";
209
213 public const string MailNamespace = "urn:xmpp:smtp";
214
218 public const string ContentNamespace = "urn:xmpp:content";
219
223 public const string MessagePushNamespace = "http://waher.se/Schema/PushNotification.xsd";
224
228 public const string AlternativesNamespace = "http://waher.se/Schema/Alternatives.xsd";
229
230 private static readonly RandomNumberGenerator rnd = RandomNumberGenerator.Create();
231 private static readonly Dictionary<CaseInsensitiveString, S2SRec> remoteDomainLookup = new Dictionary<CaseInsensitiveString, S2SRec>();
232 private static Scheduler scheduler = new Scheduler();
233 internal static readonly UTF8Encoding encoding = new UTF8Encoding(false, false);
234 private static readonly Cache<CaseInsensitiveString, PushNotificationToken> tokens =
235 new Cache<CaseInsensitiveString, PushNotificationToken>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromDays(62));
236 private static readonly Cache<string, PushNotificationRule> rules =
237 new Cache<string, PushNotificationRule>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromDays(62));
238
239 private readonly SmtpServer smtpServer = null;
240 private readonly HttpServer httpServer = null;
241 private RequestWhiteList requestWhiteList = null;
242 private LinkedList<TcpListener> c2sListeners = new LinkedList<TcpListener>();
243 private LinkedList<TcpListener> s2sListeners = new LinkedList<TcpListener>();
244 private Cache<CaseInsensitiveString, IClientConnection> clientConnections;
245 private Cache<CaseInsensitiveString, CaseInsensitiveString> services = new Cache<CaseInsensitiveString, CaseInsensitiveString>(int.MaxValue, TimeSpan.FromDays(1), TimeSpan.FromDays(1), true);
246 private readonly Dictionary<CaseInsensitiveString, List<IClientConnection>> connectionsPerBareJid = new Dictionary<CaseInsensitiveString, List<IClientConnection>>();
247 private readonly Dictionary<string, EventHandlerAsync<IqEventArgs>> iqGetHandlers = new Dictionary<string, EventHandlerAsync<IqEventArgs>>();
248 private readonly Dictionary<string, EventHandlerAsync<IqEventArgs>> iqSetHandlers = new Dictionary<string, EventHandlerAsync<IqEventArgs>>();
249 private readonly Dictionary<string, EventHandlerAsync<MessageEventArgs>> messageHandlers = new Dictionary<string, EventHandlerAsync<MessageEventArgs>>();
250 private readonly SortedDictionary<string, bool> features = new SortedDictionary<string, bool>();
251 private readonly SortedDictionary<CaseInsensitiveString, IComponent> componentsBySubdomain = new SortedDictionary<CaseInsensitiveString, IComponent>();
252 private readonly SortedDictionary<CaseInsensitiveString, IComponent> componentsByFulldomain = new SortedDictionary<CaseInsensitiveString, IComponent>();
253 private readonly Dictionary<string, PendingRequest> pendingRequestsById = new Dictionary<string, PendingRequest>();
254 private readonly SortedDictionary<DateTime, PendingRequest> pendingRequestsByTimeout = new SortedDictionary<DateTime, PendingRequest>();
255 private readonly IqResponses responses = new IqResponses(TimeSpan.FromMinutes(1));
256 private readonly SortedDictionary<CaseInsensitiveString, S2sEndpointStatistics> s2sStatistics = new SortedDictionary<CaseInsensitiveString, S2sEndpointStatistics>();
257 private Dictionary<string, Statistic> stanzasPerStanzaType = new Dictionary<string, Statistic>();
258 private Dictionary<string, Statistic> stanzasPerFromDomain = new Dictionary<string, Statistic>();
259 private Dictionary<string, Statistic> stanzasPerToDomain = new Dictionary<string, Statistic>();
260 private Dictionary<string, Statistic> stanzasPerFromBareJid = new Dictionary<string, Statistic>();
261 private Dictionary<string, Statistic> stanzasPerToBareJid = new Dictionary<string, Statistic>();
262 private Dictionary<string, Statistic> stanzasPerNamespace = new Dictionary<string, Statistic>();
263 private Dictionary<string, Statistic> stanzasPerFqn = new Dictionary<string, Statistic>();
264 private readonly object statSync = new object();
265 private DateTime lastStat = DateTime.Now;
266 private IComponent[] componentsStatic = new IComponent[0];
269 private SortedDictionary<DateTime, IS2SEndpoint> temporaryConnections = null;
270 private X509Certificate serverCertificate;
271 private readonly IXmppServerPersistenceLayer persistenceLayer;
272 private Accounts accounts;
273 private Timer secondTimer = null;
274 private readonly byte[] sha256DialbackSecret;
275 private readonly object synchObject = new object();
276 private readonly XmppAddress domainAddress;
277 private readonly Random gen = new Random();
278 private readonly CaseInsensitiveString domain;
279 private readonly CaseInsensitiveString[] alternativeDomains;
280 private readonly string serverName;
281 private readonly string serverVersion;
282 private readonly string serverOS;
283 private string domainSnifferPath = null;
284 private string clientSnifferPath = null;
285 private readonly bool encryptionRequired;
286 private bool disposed = false;
287 private readonly CommunicationLayer c2sSniffers = new CommunicationLayer(true);
288 private readonly CommunicationLayer s2sSniffers = new CommunicationLayer(true);
289 private readonly int defaultRetryTimeout = 5000;
290 private readonly int defaultNrRetries = 5;
291 private readonly int defaultMaxRetryTimeout = int.MaxValue;
292 private readonly bool defaultDropOff = true;
293 private long nrBytesRx = 0;
294 private long nrBytesTx = 0;
295 private long nrStanzas = 0;
296
297 #region Constructors
298
299 static XmppServer()
300 {
301 Log.Terminating += (Sender, e) =>
302 {
303 scheduler?.Dispose();
304 scheduler = null;
305 };
306 }
307
317 X509Certificate ServerCertificate, bool EncryptionRequired, IXmppServerPersistenceLayer PersistenceLayer)
318 {
320 }
321
333 int ClientToServerPort, int ServerToServerPort, X509Certificate ServerCertificate, bool EncryptionRequired,
334 IXmppServerPersistenceLayer PersistenceLayer)
335 {
336 return Create(Domain, AlternativeDomains, new int[] { ClientToServerPort }, new int[] { ServerToServerPort },
337 ServerCertificate, EncryptionRequired, PersistenceLayer);
338 }
339
351 int[] ClientToServerPorts, int[] ServerToServerPorts, X509Certificate ServerCertificate, bool EncryptionRequired,
352 IXmppServerPersistenceLayer PersistenceLayer)
353 {
354 return Create(Domain, AlternativeDomains, ClientToServerPorts, ServerToServerPorts, ServerCertificate, EncryptionRequired,
355 PersistenceLayer, null, null);
356 }
357
371 int[] ClientToServerPorts, int[] ServerToServerPorts, X509Certificate ServerCertificate, bool EncryptionRequired,
373 {
374 byte[] DialbackSecret = await PersistenceLayer.GetDialbackSecret();
375
376 if (mechanisms is null)
377 {
378 lock (synchObj)
379 {
380 if (first)
381 {
382 first = false;
383 Types.OnInvalidated += Types_OnInvalidated;
384 }
385 }
386
387 mechanisms = await GetMechanisms();
388 }
389
390 return new XmppServer(Domain, AlternativeDomains, ClientToServerPorts, ServerToServerPorts, ServerCertificate,
391 EncryptionRequired, PersistenceLayer, DialbackSecret, SmtpServer, HttpServer);
392 }
393
394 internal static IAuthenticationMechanism[] mechanisms = null;
395 private static readonly object synchObj = new object();
396 private static bool first = true;
397
398 private static async Task<IAuthenticationMechanism[]> GetMechanisms()
399 {
400 Dictionary<string, bool> MechanismsFound = new Dictionary<string, bool>();
401 List<IAuthenticationMechanism> Result = new List<IAuthenticationMechanism>();
402 ConstructorInfo CI;
403 IAuthenticationMechanism Mechanism;
404
406 {
407 if (T.IsAbstract)
408 continue;
409
410 CI = T.GetConstructor(Types.NoTypes);
411 if (CI is null)
412 continue;
413
414 try
415 {
416 Mechanism = (IAuthenticationMechanism)CI.Invoke(Types.NoParameters);
417
418 if (MechanismsFound.ContainsKey(Mechanism.Name))
419 throw new Exception("Authentication mechanism collision." + T.FullName + ": " + Mechanism.Name);
420
421 await Mechanism.Initialize();
422
423 MechanismsFound[Mechanism.Name] = true;
424 Result.Add(Mechanism);
425 }
426 catch (Exception ex)
427 {
428 Log.Exception(ex);
429 }
430 }
431
432 Result.Sort((m1, m2) => m2.Weight - m1.Weight);
433
434 return Result.ToArray();
435 }
436
437 private static async void Types_OnInvalidated(object Sender, EventArgs e)
438 {
439 try
440 {
441 mechanisms = await GetMechanisms();
442 }
443 catch (Exception ex)
444 {
445 Log.Exception(ex);
446 }
447 }
448
463 int[] ServerToServerPorts, X509Certificate ServerCertificate, bool EncryptionRequired,
464 IXmppServerPersistenceLayer PersistenceLayer, byte[] DialbackSecret, SmtpServer SmtpServer, HttpServer HttpServer)
465 {
466 this.persistenceLayer = PersistenceLayer;
467 this.serverCertificate = ServerCertificate;
468 this.encryptionRequired = EncryptionRequired;
469 this.domain = Domain;
470 this.alternativeDomains = AlternativeDomains ?? new CaseInsensitiveString[0];
471 this.domainAddress = new XmppAddress(this.domain);
472 this.accounts = new Accounts(this);
473 this.smtpServer = SmtpServer;
474 this.httpServer = HttpServer;
475
476 this.httpServer?.Register(this.requestWhiteList = new RequestWhiteList(this));
477
478 this.sha256DialbackSecret = Hashes.ComputeSHA256Hash(DialbackSecret);
479
480 if (EncryptionRequired && this.serverCertificate is null)
481 throw new ArgumentException("Server Certificate must be provided, if encryption is required.", nameof(ServerCertificate));
482
483 this.clientConnections = new Cache<CaseInsensitiveString, IClientConnection>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromMinutes(2), true);
484 this.clientConnections.Removed += this.ClientConnections_Removed;
485
486 this.s2sEndpoints = new Cache<CaseInsensitiveString, IS2SEndpoint>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromDays(1), true);
487 this.s2sEndpoints.Removed += this.S2sEndpoints_Removed;
488
489 Assembly ThisAssembly = typeof(XmppServer).Assembly;
490 StackTrace Trace = new StackTrace();
491 StackFrame[] Frames = Trace.GetFrames();
492 StackFrame Frame;
493 MethodBase Method;
494 Assembly Assembly;
495 int i = 1;
496 int c = Frames.Length;
497
498 do
499 {
500 Frame = Frames[i++];
501 Method = Frame.GetMethod();
502 Assembly = Method.DeclaringType.Assembly;
503 }
504 while (Assembly == ThisAssembly);
505
506 AssemblyName Name = Assembly.GetName();
507 string Title = string.Empty;
508 string Product = string.Empty;
509 string AssemblyName = Name.Name;
510
511 foreach (object Attribute in Assembly.GetCustomAttributes())
512 {
513 if (Attribute is AssemblyTitleAttribute AssemblyTitleAttribute)
514 Title = AssemblyTitleAttribute.Title;
515 else if (Attribute is AssemblyProductAttribute AssemblyProductAttribute)
516 Product = AssemblyProductAttribute.Product;
517 }
518
519 if (!string.IsNullOrEmpty(Title))
520 this.serverName = Title;
521 else if (!string.IsNullOrEmpty(Product))
522 this.serverName = Product;
523 else
524 this.serverName = AssemblyName;
525
526 this.serverVersion = Name.Version.ToString();
527 this.serverOS = Environment.OSVersion.ToString();
528
529 this.RegisterIqGetHandler("query", RosterNamespace, this.RosterQuery, true);
530 this.RegisterIqSetHandler("query", RosterNamespace, this.RosterSet, false);
531 this.RegisterIqGetHandler("query", DiscoveryNamespace, this.DiscoveryQueryGet, true); // XEP-0030
532 this.RegisterIqGetHandler("query", DiscoveryItemsNamespace, this.DiscoveryQueryItemsGet, true); // XEP-0030
533 this.RegisterIqGetHandler("query", PrivateXmlStorageNamespace, this.PrivateXmlStorageGet, true); // XEP-0049
534 this.RegisterIqSetHandler("query", PrivateXmlStorageNamespace, this.PrivateXmlStorageSet, false); // XEP-0049
535 this.RegisterIqGetHandler("vCard", VCardNamespace, this.VCardGet, true); // XEP-0054
536 this.RegisterIqSetHandler("vCard", VCardNamespace, this.VCardSet, false); // XEP-0054
537 this.RegisterIqGetHandler("query", RegisterNamespace, this.RegisterGet, true); // XEP-0077
538 this.RegisterIqSetHandler("query", RegisterNamespace, this.RegisterSet, false); // XEP-0077
539 this.RegisterIqGetHandler("query", SoftwareVersionNamespace, this.SoftwareVersionGet, true); // XEP-0092
540 this.RegisterIqGetHandler("blocklist", BlockingCommandNamespace, this.BlockListGet, true); // XEP-0191
541 this.RegisterIqSetHandler("block", BlockingCommandNamespace, this.BlockSet, false); // XEP-0191
542 this.RegisterIqSetHandler("unblock", BlockingCommandNamespace, this.UnblockSet, false); // XEP-0191
543 this.RegisterIqGetHandler("ping", PingNamespace, this.PingGet, true); // XEP-0199
544 this.RegisterIqGetHandler("time", TimeNamespace, this.TimeGet, true); // XEP-0202
545 this.RegisterIqGetHandler("get", MailNamespace, this.GetMailContent, true); // TODO: Write Mail XEP
546 this.RegisterIqSetHandler("delete", MailNamespace, this.DeleteMailContent, false);
547 this.RegisterIqSetHandler("newToken", MessagePushNamespace, this.NewTokenHandler, true);
548 this.RegisterIqSetHandler("removeToken", MessagePushNamespace, this.RemoveTokenHandler, false);
549 this.RegisterIqSetHandler("clearRules", MessagePushNamespace, this.ClearRulesHandler, false);
550 this.RegisterIqSetHandler("addRule", MessagePushNamespace, this.AddRuleHandler, false);
551 this.RegisterIqSetHandler("removeRule", MessagePushNamespace, this.RemoveRuleHandler, false);
552 this.features[OfflineMessagesNamespace] = true; // XEP-0160
553 this.features[OAuth1FormSignatureNamespace] = true; // XEP-0348
554 this.features[ReportingNamespace] = true; // XEP-0377
555 this.features[SpamReportingNamespace] = true; // XEP-0377
556 this.features[AbuseReportingNamespace] = true; // XEP-0377
557
558 this.Initialize(ClientToServerPorts, ServerToServerPorts);
559 }
560
561 private async void Initialize(int[] ClientToServerPorts, int[] ServerToServerPorts)
562 {
563 try
564 {
565 TcpListener Listener;
566
567 foreach (NetworkInterface Interface in NetworkInterface.GetAllNetworkInterfaces())
568 {
569 if (Interface.OperationalStatus != OperationalStatus.Up)
570 continue;
571
572 IPInterfaceProperties Properties = Interface.GetIPProperties();
573
574 foreach (UnicastIPAddressInformation UnicastAddress in Properties.UnicastAddresses)
575 {
576 if ((UnicastAddress.Address.AddressFamily == AddressFamily.InterNetwork && Socket.OSSupportsIPv4) ||
577 (UnicastAddress.Address.AddressFamily == AddressFamily.InterNetworkV6 && Socket.OSSupportsIPv6))
578 {
579 if (!(ClientToServerPorts is null))
580 {
581 foreach (int C2sPort in ClientToServerPorts)
582 {
583 try
584 {
585 await this.c2sSniffers.Information("Opening port " + C2sPort.ToString() + " on " + UnicastAddress.Address.ToString() + ".");
586
587 Listener = new TcpListener(UnicastAddress.Address, C2sPort);
588 Listener.Start(DefaultConnectionBacklog);
589 Listener.BeginAcceptTcpClient(this.AcceptTcpClientCallback, new object[] { Listener, false });
590 this.c2sListeners.AddLast(Listener);
591
592 await this.c2sSniffers.Information("Port " + C2sPort.ToString() + " on " + UnicastAddress.Address.ToString() + " opened.");
593 }
594 catch (Exception ex)
595 {
596 Log.Exception(ex, UnicastAddress.Address.ToString() + ":" + C2sPort);
597 }
598 }
599 }
600
601 if (!(ServerToServerPorts is null))
602 {
603 foreach (int S2sPort in ServerToServerPorts)
604 {
605 try
606 {
607 await this.s2sSniffers.Information("Opening port " + S2sPort.ToString() + " on " + UnicastAddress.Address.ToString() + ".");
608
609 Listener = new TcpListener(UnicastAddress.Address, S2sPort);
610 Listener.Start(DefaultConnectionBacklog);
611 Listener.BeginAcceptTcpClient(this.AcceptTcpClientCallback, new object[] { Listener, true });
612 this.s2sListeners.AddLast(Listener);
613
614 await this.s2sSniffers.Information("Port " + S2sPort.ToString() + " on " + UnicastAddress.Address.ToString() + " opened.");
615 }
616 catch (Exception ex)
617 {
618 Log.Exception(ex, UnicastAddress.Address.ToString() + ":" + S2sPort);
619 }
620 }
621 }
622 }
623 }
624 }
625
626 this.secondTimer = new Timer(this.SecondTimerCallback, null, 1000, 1000);
627
628 if (!(this.smtpServer is null))
629 this.smtpServer.MessageReceived += this.SmtpServer_MessageReceived;
630 }
631 catch (Exception ex)
632 {
633 Log.Exception(ex);
634 }
635 }
636
640 public Accounts Accounts => this.accounts;
641
645 public CommunicationLayer C2sSniffers => this.c2sSniffers;
646
650 public CommunicationLayer S2sSniffers => this.s2sSniffers;
651
655 public static Scheduler Scheduler => scheduler;
656
662 public string NewId(int NrBytes)
663 {
664 return Hashes.BinaryToString(GetRandomNumbers(NrBytes));
665 }
666
672 public static byte[] GetRandomNumbers(int NrBytes)
673 {
674 byte[] Result = new byte[NrBytes];
675
676 lock (rnd)
677 {
678 rnd.GetBytes(Result);
679 }
680
681 return Result;
682 }
683
689 public string GetRandomHexString(int NrBytes)
690 {
691 return Hashes.BinaryToString(GetRandomNumbers(NrBytes));
692 }
693
694 private async Task ClientConnections_Removed(object Sender, CacheItemEventArgs<CaseInsensitiveString, IClientConnection> e)
695 {
696 this.ConnectionClosed(e.Value);
697
698 await this.ClientConnectionRemoved.Raise(this, new ClientConnectionEventArgs(e.Key, e.Value));
699 await e.Value.DisposeAsync();
700 }
701
709 {
710 lock (this.s2sStatistics)
711 {
712 return this.s2sStatistics.TryGetValue(Endpoint, out Stat);
713 }
714 }
715
723 {
725
726 lock (this.s2sStatistics)
727 {
728 if (!this.s2sStatistics.TryGetValue(Endpoint, out Result))
729 {
730 Result = new S2sEndpointStatistics(Endpoint, Type);
731 this.s2sStatistics[Endpoint] = Result;
732 }
733 }
734
735 return Result;
736 }
737
738 internal void S2sEndpointDisposed(IS2SEndpoint Endpoint)
739 {
741
742 if (!(this.s2sEndpoints is null) && !(Endpoint is null) && !string.IsNullOrEmpty(Domain) &&
743 this.s2sEndpoints.TryGetValue(Domain, out IS2SEndpoint Endpoint2) && Endpoint == Endpoint2)
744 {
745 this.s2sEndpoints?.Remove(Domain);
746 }
747 }
748
749 private async Task S2sEndpoints_Removed(object Sender, CacheItemEventArgs<CaseInsensitiveString, IS2SEndpoint> e)
750 {
751 Log.Informational("Removing S2S connection.", e.Value.RemoteDomain, string.Empty, "XmppCloseS2s",
752 new KeyValuePair<string, object>("Reason", e.Reason));
753
754 EndpointStatistics Stat = this.GetS2sStatistics(e.Key, e.Value.Type);
755 Stat.EndpointDisconnected(e.Value, (long)((DateTime.Now - e.Value.Connected).TotalMilliseconds + 0.5));
756
758 XmppS2SEndpoint.OnStateChanged -= this.Endpoint_OnStateChanged;
759
760 await e.Value.DisposeAsync();
761
762 if (e.Reason != RemovedReason.Replaced)
763 await this.ServerConnectionRemoved.Raise(this, new ServerConnectionEventArgs(e.Key, e.Value));
764 }
765
770 {
771 get
772 {
773 return this.s2sEndpoints.GetKeys();
774 }
775 }
776
781 {
782 get => this.clientConnections.Count;
783 }
784
790 {
791 IClientConnection[] Connections = this.clientConnections.GetValues();
792
793 Array.Sort(Connections, (c1, c2) =>
794 {
795 return c1.FullJid.CompareTo(c2.FullJid);
796 });
797
798 return Connections;
799 }
800
807 public bool TryGetClientConnection(string FullJID, out IClientConnection Connection)
808 {
809 return this.clientConnections.TryGetValue(FullJID, out Connection);
810 }
811
818 public bool TryGetClientConnections(string BareJID, out IClientConnection[] Connections)
819 {
820 lock (this.connectionsPerBareJid)
821 {
822 if (this.connectionsPerBareJid.TryGetValue(BareJID, out List<IClientConnection> Connections2))
823 {
824 Connections = Connections2.ToArray();
825 return true;
826 }
827 }
828
829 Connections = null;
830 return false;
831 }
832
837 {
838 get => this.s2sEndpoints.Count;
839 }
840
846 {
847 lock (this.s2sStatistics)
848 {
849 S2sEndpointStatistics[] Result = new S2sEndpointStatistics[this.s2sStatistics.Count];
850 this.s2sStatistics.Values.CopyTo(Result, 0);
851 return Result;
852 }
853 }
854
861 public bool IsServerDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
862 {
863 if (Domain == this.domain)
864 return true;
865
866 if (IncludeAlternativeDomains)
867 {
868 foreach (CaseInsensitiveString s in this.alternativeDomains)
869 {
870 if (s == Domain)
871 return true;
872 }
873 }
874
875 return false;
876 }
877
882 {
883 get => this.domain;
884 }
885
890 {
891 get => this.alternativeDomains;
892 }
893
897 public X509Certificate ServerCertificate
898 {
899 get => this.serverCertificate;
900 }
901
906 public void UpdateCertificate(X509Certificate ServerCertificate)
907 {
908 this.serverCertificate = ServerCertificate;
909 }
910
915 {
916 get => this.encryptionRequired;
917 }
918
923 public string DomainSnifferPath
924 {
925 get => this.domainSnifferPath;
926 set => this.domainSnifferPath = value;
927 }
928
933 public string ClientSnifferPath
934 {
935 get => this.clientSnifferPath;
936 set => this.clientSnifferPath = value;
937 }
938
939 internal IXmppServerPersistenceLayer PersistenceLayer
940 {
941 get => this.persistenceLayer;
942 }
943
947 public void Dispose()
948 {
949 this.disposed = true;
950
951 if (!(this.httpServer is null))
952 {
953 this.httpServer.Unregister(this.requestWhiteList);
954 this.requestWhiteList = null;
955 }
956
957 if (!(this.smtpServer is null))
958 this.smtpServer.MessageReceived -= this.SmtpServer_MessageReceived;
959
960 this.secondTimer?.Dispose();
961 this.secondTimer = null;
962
963 if (!(this.s2sEndpoints is null))
964 {
965 this.s2sEndpoints.Clear();
966 this.s2sEndpoints.Dispose();
967 this.s2sEndpoints = null;
968 }
969
970 if (!(this.clientConnections is null))
971 {
972 this.clientConnections.Clear();
973 this.clientConnections.Dispose();
974 this.clientConnections = null;
975 }
976
977 if (!(this.sniffers is null))
978 {
979 this.sniffers.Clear();
980 this.sniffers.Dispose();
981 this.sniffers = null;
982 }
983
984 if (!(this.componentsBySubdomain is null))
985 {
986 foreach (IComponent Component in this.componentsStatic)
988
989 this.componentsBySubdomain.Clear();
990 this.componentsByFulldomain.Clear();
991 this.componentsStatic = new IComponent[0];
992 }
993
994 this.accounts?.Dispose();
995 this.accounts = null;
996
997 this.services?.Dispose();
998 this.services = null;
999
1000 this.responses.Dispose();
1001
1002 if (!(this.c2sListeners is null))
1003 {
1004 LinkedList<TcpListener> Listeners = this.c2sListeners;
1005 this.c2sListeners = null;
1006
1007 foreach (TcpListener Listener in Listeners)
1008 Listener.Stop();
1009 }
1010
1011 if (!(this.s2sListeners is null))
1012 {
1013 LinkedList<TcpListener> Listeners = this.s2sListeners;
1014 this.s2sListeners = null;
1015
1016 foreach (TcpListener Listener in Listeners)
1017 Listener.Stop();
1018 }
1019
1020 if (!(this.c2sSniffers is null))
1021 {
1022 foreach (ISniffer Sniffer in this.c2sSniffers)
1023 (Sniffer as IDisposable)?.Dispose();
1024 }
1025
1026 if (!(this.s2sSniffers is null))
1027 {
1028 foreach (ISniffer Sniffer in this.s2sSniffers)
1029 (Sniffer as IDisposable)?.Dispose();
1030 }
1031 }
1032
1036 public bool Disposed => this.disposed;
1037
1041 public int[] OpenC2SPorts
1042 {
1043 get
1044 {
1045 return this.GetOpenPorts(this.c2sListeners);
1046 }
1047 }
1048
1052 public int[] OpenS2SPorts
1053 {
1054 get
1055 {
1056 return this.GetOpenPorts(this.s2sListeners);
1057 }
1058 }
1059
1063 public IComponent[] Components => this.componentsStatic;
1064
1065 private int[] GetOpenPorts(LinkedList<TcpListener> Listeners)
1066 {
1067 SortedDictionary<int, bool> Open = new SortedDictionary<int, bool>();
1068
1069 if (!(Listeners is null))
1070 {
1071 IPEndPoint IPEndPoint;
1072
1073 foreach (TcpListener Listener in Listeners)
1074 {
1075 IPEndPoint = Listener.LocalEndpoint as IPEndPoint;
1076 if (!(IPEndPoint is null))
1077 Open[IPEndPoint.Port] = true;
1078 }
1079 }
1080
1081 int[] Result = new int[Open.Count];
1082 Open.Keys.CopyTo(Result, 0);
1083
1084 return Result;
1085 }
1086
1087 #endregion
1088
1089 #region Components
1090
1097 {
1098 lock (this.synchObject)
1099 {
1100 if (this.componentsBySubdomain.ContainsKey(Component.Subdomain))
1101 return false;
1102
1103 this.componentsBySubdomain[Component.Subdomain] = Component;
1104 this.componentsByFulldomain[Component.Subdomain + "." + this.domain] = Component;
1105
1106 foreach (CaseInsensitiveString cis in this.alternativeDomains)
1107 this.componentsByFulldomain[Component.Subdomain + "." + cis] = Component;
1108
1109 this.RebuildComponentsStaticLocked();
1110 }
1111
1112 return true;
1113 }
1114
1115 private void RebuildComponentsStaticLocked()
1116 {
1117 IComponent[] Static = new IComponent[this.componentsBySubdomain.Count];
1118 this.componentsBySubdomain.Values.CopyTo(Static, 0);
1119
1120 this.componentsStatic = Static;
1121 }
1122
1129 {
1130 bool Result = false;
1131
1132 lock (this.synchObject)
1133 {
1134 if (this.componentsBySubdomain.TryGetValue(Component.Subdomain, out IComponent Component2) &&
1135 Component == Component2)
1136 {
1137 Result = this.componentsBySubdomain.Remove(Component.Subdomain);
1138
1139 if (Result)
1140 {
1141 this.componentsByFulldomain.Remove(Component.Subdomain + "." + this.domain);
1142
1143 foreach (CaseInsensitiveString cis in this.alternativeDomains)
1144 this.componentsByFulldomain.Remove(Component.Subdomain + "." + cis);
1145
1146 this.RebuildComponentsStaticLocked();
1147 }
1148 }
1149 }
1150
1151 return Result;
1152 }
1153
1154 #endregion
1155
1156 #region Connections
1157
1158 private async void AcceptTcpClientCallback(IAsyncResult ar)
1159 {
1160 try
1161 {
1162 if (this.disposed ||
1163 !(ar?.AsyncState is object[] P) ||
1164 P.Length < 2 ||
1165 !(P[0] is TcpListener Listener) ||
1166 !(P[1] is bool S2S) ||
1168 {
1169 return;
1170 }
1171
1172 try
1173 {
1174 TcpClient Client = Listener.EndAcceptTcpClient(ar);
1175 ICommunicationLayer ComLayer;
1176
1177 TextTcpClient TextTcpClient = new TextTcpClient(Client, encoding, false, true);
1178 TextTcpClient.Bind(true);
1179
1180 if (S2S)
1181 {
1182 ComLayer = new XmppS2SEndpoint(TextTcpClient, this.serverCertificate, this, false, new InMemorySniffer());
1183 }
1184 else
1185 {
1186 ISniffer[] Sniffers;
1187
1188 if (!string.IsNullOrEmpty(this.clientSnifferPath))
1189 Sniffers = new ISniffer[] { new InMemorySniffer() };
1190 else if (this.c2sSniffers.HasSniffers)
1191 Sniffers = this.c2sSniffers.Sniffers;
1192 else
1193 Sniffers = new ISniffer[0];
1194
1195 ComLayer = new XmppClientConnection(TextTcpClient, this, Sniffers);
1196 }
1197
1199
1200 await ComLayer.Information("Connection accepted from " + Client.Client?.RemoteEndPoint?.ToString() + ".");
1201 }
1202 finally
1203 {
1204 if (!this.disposed)
1205 Listener.BeginAcceptTcpClient(this.AcceptTcpClientCallback, P);
1206 }
1207 }
1208 catch (SocketException)
1209 {
1210 // Ignore
1211 }
1212 catch (ObjectDisposedException)
1213 {
1214 // Ignore
1215 }
1216 catch (NullReferenceException)
1217 {
1218 // Ignore
1219 }
1220 catch (Exception ex)
1221 {
1222 if (this.c2sListeners is null)
1223 return;
1224
1225 Log.Exception(ex);
1226 }
1227 }
1228
1229 internal string GetTransformPath(bool S2S)
1230 {
1231 foreach (ISniffer Sniffer in S2S ? this.s2sSniffers.Sniffers : this.c2sSniffers.Sniffers)
1232 {
1233 if (Sniffer is XmlFileSniffer XmlFileSniffer)
1235 }
1236
1237 return null;
1238 }
1239
1248 public string GetDialbackKey(string ReceivingServer, string OriginatingServer, string ReceivingStreamId)
1249 {
1250 StringBuilder sb = new StringBuilder();
1251
1252 sb.Append(ReceivingServer);
1253 sb.Append(' ');
1254 sb.Append(OriginatingServer);
1255 sb.Append(' ');
1256 sb.Append(ReceivingStreamId);
1257
1258 byte[] Bin = Encoding.UTF8.GetBytes(sb.ToString());
1259 string Key = Hashes.ComputeHMACSHA256HashString(this.sha256DialbackSecret, Bin);
1260
1261 return Key;
1262 }
1263
1264 #endregion
1265
1266 #region Accounts
1267
1273 internal Task<IAccount> GetAccount(CaseInsensitiveString UserName)
1274 {
1275 return this.persistenceLayer.GetAccount(UserName);
1276 }
1277
1284 internal bool TryGetConnection(CaseInsensitiveString FullJid, out IClientConnection Connection)
1285 {
1286 return this.clientConnections.TryGetValue(FullJid, out Connection);
1287 }
1288
1294 internal bool TouchClientConnection(CaseInsensitiveString FullJid)
1295 {
1296 return this.clientConnections?.ContainsKey(FullJid) ?? false;
1297 }
1298
1304 internal bool TouchServerConnection(CaseInsensitiveString Domain)
1305 {
1306 return this.s2sEndpoints?.ContainsKey(Domain) ?? false;
1307 }
1308
1315 internal async Task<bool> RegisterFullJid(CaseInsensitiveString FullJid, IClientConnection Connection)
1316 {
1317 if (this.clientConnections.TryGetValue(FullJid, out IClientConnection Connection2))
1318 {
1319 if (!Connection2.CheckLive())
1320 this.clientConnections.Remove(FullJid);
1321 else
1322 return false;
1323 }
1324
1325 this.clientConnections[FullJid] = Connection;
1326
1327 CaseInsensitiveString BareJid = GetBareJID(FullJid);
1328
1329 lock (this.connectionsPerBareJid)
1330 {
1331 if (!this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1332 {
1333 Connections = new List<IClientConnection>();
1334 this.connectionsPerBareJid[BareJid] = Connections;
1335 }
1336
1337 Connections.Add(Connection);
1338 }
1339
1340 await this.ClientConnectionAdded.Raise(this, new ClientConnectionEventArgs(FullJid, Connection));
1341
1342 return true;
1343 }
1344
1348 public event EventHandlerAsync<ClientConnectionEventArgs> ClientConnectionAdded = null;
1349
1353 public event EventHandlerAsync<ClientConnectionEventArgs> ClientConnectionUpdated = null;
1354
1358 public event EventHandlerAsync<ClientConnectionEventArgs> ClientConnectionRemoved = null;
1359
1363 public event EventHandlerAsync<ServerConnectionEventArgs> ServerConnectionAdded = null;
1364
1368 public event EventHandlerAsync<ServerConnectionEventArgs> ServerConnectionUpdated = null;
1369
1373 public event EventHandlerAsync<ServerConnectionEventArgs> ServerConnectionRemoved = null;
1374
1381 internal async Task<CaseInsensitiveString> RegisterBareJid(CaseInsensitiveString BareJid, IClientConnection Connection)
1382 {
1383 CaseInsensitiveString FullJid;
1384
1385 do
1386 {
1387 FullJid = BareJid + "/" + this.NewId(16);
1388 }
1389 while (!this.disposed && this.clientConnections.ContainsKey(FullJid));
1390
1391 if (!this.disposed)
1392 {
1393 this.clientConnections[FullJid] = Connection;
1394
1395 lock (this.connectionsPerBareJid)
1396 {
1397 if (!this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1398 {
1399 Connections = new List<IClientConnection>();
1400 this.connectionsPerBareJid[BareJid] = Connections;
1401 }
1402
1403 Connections.Add(Connection);
1404 }
1405
1406 await this.ClientConnectionAdded.Raise(this, new ClientConnectionEventArgs(FullJid, Connection));
1407 }
1408
1409 return FullJid;
1410 }
1411
1416 internal void ConnectionClosed(IClientConnection Connection)
1417 {
1418 CaseInsensitiveString FullJid = Connection.FullJid;
1419 CaseInsensitiveString BareJid = Connection.BareJid;
1420
1421 this.clientConnections?.Remove(FullJid);
1422
1423 lock (this.connectionsPerBareJid)
1424 {
1425 if (this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1426 {
1427 int i, c = Connections.Count;
1428
1429 for (i = 0; i < c; i++)
1430 {
1431 if (Connections[i].FullJid == FullJid)
1432 {
1433 Connections.RemoveAt(i);
1434 c--;
1435
1436 if (c == 0)
1437 this.connectionsPerBareJid.Remove(BareJid);
1438
1439 break;
1440 }
1441 }
1442 }
1443 }
1444 }
1445
1447 {
1448 lock (this.connectionsPerBareJid)
1449 {
1450 if (this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1451 return Connections.ToArray();
1452 else
1453 return null;
1454 }
1455 }
1456
1463 {
1464 int i = JID.IndexOf('/');
1465 if (i > 0)
1466 return JID.Substring(0, i);
1467 else
1468 return JID;
1469 }
1470
1476 public Task<DateTime?> GetEarliestLoginOpportunity(IClientConnection Connection)
1477 {
1478 if (Connection is null)
1479 return null;
1480
1481 LoginAuditor Auditor = this.persistenceLayer.Auditor;
1482
1483 if (Auditor is null)
1484 return Task.FromResult<DateTime?>(null);
1485 else
1486 return Auditor.GetEarliestLoginOpportunity(Connection.RemoteEndpoint, Connection.Protocol);
1487 }
1488
1489 internal async Task<bool> Authenticate(string Mechanism, SslStream SslStream, IClientConnection Connection, string Data)
1490 {
1491 DateTime? Next = await this.GetEarliestLoginOpportunity(Connection);
1492
1493 if (Next.HasValue)
1494 {
1495 StringBuilder sb = new StringBuilder();
1496 DateTime TP = Next.Value;
1497 DateTime Today = DateTime.Today;
1498
1499 if (Next.Value == DateTime.MaxValue)
1500 {
1501 sb.Append("This endpoint (");
1502 sb.Append(Connection.RemoteEndpoint);
1503 sb.Append(") has been blocked from the system.");
1504 }
1505 else
1506 {
1507 sb.Append("Too many failed login attempts in a row registered. Try again after ");
1508 sb.Append(TP.ToLongTimeString());
1509
1510 if (TP.Date != Today)
1511 {
1512 if (TP.Date == Today.AddDays(1))
1513 sb.Append(" tomorrow");
1514 else
1515 {
1516 sb.Append(", ");
1517 sb.Append(TP.ToShortDateString());
1518 }
1519 }
1520
1521 sb.Append(". Remote Endpoint: ");
1522 sb.Append(Connection.RemoteEndpoint);
1523 }
1524
1525 return await Connection.SaslErrorTemporaryAuthFailure(sb.ToString(), "en");
1526 }
1527
1528 bool Found = false;
1529
1530 foreach (IAuthenticationMechanism M in mechanisms)
1531 {
1532 if (M.Name == Mechanism)
1533 {
1534 if (!M.Allowed(SslStream))
1535 return await Connection.SaslErrorMechanismTooWeak();
1536
1537 Connection.SetMechanism(M);
1538 Found = true;
1539
1540 try
1541 {
1542 bool? AuthResult = await M.AuthenticationRequest(Data, Connection, this.PersistenceLayer);
1543 if (AuthResult.HasValue)
1544 {
1545 if (AuthResult.Value)
1546 {
1547 if (!await Connection.BeginWrite("<success xmlns='" + SaslNamespace + "'/>", null, null))
1548 return false;
1549
1550 Connection.ResetState(true);
1551 }
1552 }
1553 }
1554 catch (Exception ex)
1555 {
1556 await Connection.Exception(ex);
1557 if (!await Connection.StreamErrorInvalidXml())
1558 return false;
1559 }
1560 break;
1561 }
1562 }
1563
1564 if (!Found && !await Connection.SaslErrorInvalidMechanism())
1565 return false;
1566
1567 return true;
1568 }
1569
1570 #endregion
1571
1572 #region Recipients
1573
1580 public async Task<Tuple<bool, IRecipient>> TryGetRecipient(XmppAddress To, XmppAddress From)
1581 {
1583
1584 if (this.disposed)
1585 return new Tuple<bool, IRecipient>(false, null);
1586
1587 if (CaseInsensitiveString.IsNullOrEmpty(ToCI) || this.IsServerDomain(ToCI, true))
1588 return new Tuple<bool, IRecipient>(true, this);
1589
1590 if (this.clientConnections.TryGetValue(ToCI, out IClientConnection Connection))
1591 return new Tuple<bool, IRecipient>(true, Connection);
1592
1593 if (this.IsServerDomain(To.Domain, true))
1594 {
1595 if (To.IsFullJID)
1596 return new Tuple<bool, IRecipient>(false, null);
1597
1598 if (To.HasAccount)
1599 {
1600 IAccount Account = await this.persistenceLayer.GetAccount(To.Account);
1601 if (Account is null)
1602 return new Tuple<bool, IRecipient>(false, null);
1603 else
1604 return new Tuple<bool, IRecipient>(true, new AccountRecipient(this.accounts, Account));
1605 }
1606 }
1607
1609
1610 lock (this.synchObject)
1611 {
1612 if (!this.componentsByFulldomain.TryGetValue(To.Domain, out Component))
1613 Component = null;
1614 }
1615
1616 if (!(Component is null))
1617 {
1619 return new Tuple<bool, IRecipient>(true, Component);
1620 else
1621 return new Tuple<bool, IRecipient>(false, null);
1622 }
1623
1624 try
1625 {
1626 IS2SEndpoint Endpoint = await this.GetS2sEndpoint(From.Domain, To.Domain, true, From + " wants to send a stanza to " + To);
1627
1628 return new Tuple<bool, IRecipient>(!(Endpoint is null), Endpoint);
1629 }
1630 catch (Exception)
1631 {
1632 return new Tuple<bool, IRecipient>(false, null);
1633 }
1634 }
1635
1636 #endregion
1637
1638 #region Stanzas
1639
1647 public void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler, bool PublishNamespaceAsFeature)
1648 {
1649 this.RegisterIqHandler(this.iqGetHandlers, LocalName, Namespace, Handler, PublishNamespaceAsFeature);
1650 }
1651
1659 public void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler, bool PublishNamespaceAsFeature)
1660 {
1661 this.RegisterIqHandler(this.iqSetHandlers, LocalName, Namespace, Handler, PublishNamespaceAsFeature);
1662 }
1663
1664 private void RegisterIqHandler(Dictionary<string, EventHandlerAsync<IqEventArgs>> Handlers, string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler,
1665 bool PublishNamespaceAsFeature)
1666 {
1667 string Key = LocalName + " " + Namespace;
1668
1669 lock (this.synchObject)
1670 {
1671 if (Handlers.ContainsKey(Key))
1672 throw new ArgumentException("Handler already registered.", nameof(LocalName));
1673
1674 Handlers[Key] = Handler;
1675
1676 if (PublishNamespaceAsFeature)
1677 this.features[Namespace] = true;
1678 }
1679 }
1680
1688 public void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync<MessageEventArgs> Handler, bool PublishNamespaceAsFeature)
1689 {
1690 string Key = LocalName + " " + Namespace;
1691
1692 lock (this.synchObject)
1693 {
1694 if (this.messageHandlers.ContainsKey(Key))
1695 throw new ArgumentException("Handler already registered.", nameof(LocalName));
1696
1697 this.messageHandlers[Key] = Handler;
1698
1699 if (PublishNamespaceAsFeature)
1700 this.features[Namespace] = true;
1701 }
1702 }
1703
1712 public bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler, bool RemoveNamespaceAsFeature)
1713 {
1714 return this.UnregisterIqHandler(this.iqGetHandlers, LocalName, Namespace, Handler, RemoveNamespaceAsFeature);
1715 }
1716
1725 public bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler, bool RemoveNamespaceAsFeature)
1726 {
1727 return this.UnregisterIqHandler(this.iqSetHandlers, LocalName, Namespace, Handler, RemoveNamespaceAsFeature);
1728 }
1729
1730 private bool UnregisterIqHandler(Dictionary<string, EventHandlerAsync<IqEventArgs>> Handlers, string LocalName, string Namespace, EventHandlerAsync<IqEventArgs> Handler,
1731 bool RemoveNamespaceAsFeature)
1732 {
1733 string Key = LocalName + " " + Namespace;
1734
1735 lock (this.synchObject)
1736 {
1737 if (!Handlers.TryGetValue(Key, out EventHandlerAsync<IqEventArgs> h))
1738 return false;
1739
1740 if (h != Handler)
1741 return false;
1742
1743 Handlers.Remove(Key);
1744
1745 if (RemoveNamespaceAsFeature)
1746 this.features.Remove(Namespace);
1747 }
1748
1749 return true;
1750 }
1751
1760 public bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync<MessageEventArgs> Handler, bool RemoveNamespaceAsFeature)
1761 {
1762 string Key = LocalName + " " + Namespace;
1763
1764 lock (this.synchObject)
1765 {
1766 if (!this.messageHandlers.TryGetValue(Key, out EventHandlerAsync<MessageEventArgs> h))
1767 return false;
1768
1769 if (h != Handler)
1770 return false;
1771
1772 this.messageHandlers.Remove(Key);
1773
1774 if (RemoveNamespaceAsFeature)
1775 this.features.Remove(Namespace);
1776 }
1777
1778 return true;
1779 }
1780
1781 internal async Task<bool> ProcessIq(string Id, XmppAddress To, XmppAddress From, string Type, string Language, Stanza Stanza, ISender Sender)
1782 {
1783 (bool Found, IRecipient Recipient) = await this.TryGetRecipient(To, From);
1784
1785 if (!Found)
1786 {
1787 if (Sender is null)
1788 return true;
1789 else
1790 return !(await Sender.IqErrorItemNotFound(Id, From, To, "Recipient not found: " + To.Address, "en") is null);
1791 }
1792 else if (Recipient is IClientConnection ToConnection &&
1793 await this.persistenceLayer.IsBlocked(From.BareJid, ToConnection.BareJid))
1794 {
1795 return !(await Sender.IqErrorServiceUnavailable(Id, From, To, string.Empty, string.Empty) is null);
1796 }
1797 else
1798 return await Recipient.IQ(Type, Id, To, From, Language, Stanza, Sender);
1799 }
1800
1811 public async Task<bool> IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
1812 {
1813 Dictionary<string, EventHandlerAsync<IqEventArgs>> Handlers;
1814 EventHandlerAsync<IqEventArgs> h = null;
1815 string Key;
1816 bool Blocked = await this.persistenceLayer.IsBlocked(From.BareJid, To.BareJid);
1817
1818 switch (Type)
1819 {
1820 case "get":
1821 if (this.responses.TryGet(From.Address, Id, out string ResponseXml, out bool Ok))
1822 {
1823 if (Ok)
1824 return await Sender.IqResult(Id, From, To, ResponseXml);
1825 else
1826 return await Sender.IqError(Id, From, To, ResponseXml);
1827 }
1828 else
1829 Handlers = this.iqGetHandlers;
1830 break;
1831
1832 case "set":
1833 if (this.responses.TryGet(From.Address, Id, out ResponseXml, out Ok))
1834 {
1835 if (Ok)
1836 return await Sender.IqResult(Id, From, To, ResponseXml);
1837 else
1838 return await Sender.IqError(Id, From, To, ResponseXml);
1839 }
1840 else
1841 Handlers = this.iqSetHandlers;
1842 break;
1843
1844 case "result":
1845 case "error":
1846 if (Blocked)
1847 return true;
1848
1849 return await this.ProcessResponse(Type, Id, To, From, Language, true, false, Stanza, Sender);
1850
1851 default:
1852 if (Sender is null)
1853 return true;
1854 else
1855 return !(await Sender.IqErrorBadRequest(Id, From, To, "Invalid type.", "en") is null);
1856 }
1857
1858 if (Blocked)
1859 {
1860 if (Sender is null)
1861 return true;
1862 else
1863 return !(await Sender.IqErrorServiceUnavailable(Id, From, To, string.Empty, string.Empty) is null);
1864 }
1865 else
1866 {
1867 IqEventArgs e = new IqEventArgs(Sender, Stanza.StanzaElement, Id, Type, To, From, Language, this.responses);
1868
1869 lock (this.synchObject)
1870 {
1871 foreach (XmlNode N in Stanza.StanzaElement.ChildNodes)
1872 {
1873 if (!(N is XmlElement E))
1874 continue;
1875
1876 Key = E.LocalName + " " + E.NamespaceURI;
1877 if (Handlers.TryGetValue(Key, out h))
1878 {
1879 e.Query = E;
1880 break;
1881 }
1882 else
1883 h = null;
1884 }
1885 }
1886
1887 if (h is null)
1888 {
1889 return !(await Sender.IqError(Id, From, To, "cancel", "<feature-not-implemented xmlns='" + StanzaNamespace + "'/>",
1890 string.Empty, string.Empty) is null);
1891 }
1892 else
1893 {
1894 try
1895 {
1896 await h(this, e);
1897 return true;
1898 }
1899 catch (Exception ex)
1900 {
1901 if (Sender is null)
1902 return true;
1903 else
1904 return !(await Sender.IqError(Id, From, To, ex) is null);
1905 }
1906 }
1907 }
1908 }
1909
1910 internal async Task<bool> ProcessResponse(string Type, string Id, XmppAddress To, XmppAddress From, string Language,
1911 bool IqResponse, bool PresenceResponse, Stanza Stanza, ISender Sender)
1912 {
1913 if (!string.IsNullOrEmpty(Id))
1914 {
1915 PendingRequest Rec = null;
1916 bool Ok = (Type == "result");
1917
1918 lock (this.synchObject)
1919 {
1920 if (this.pendingRequestsById.TryGetValue(Id, out Rec))
1921 {
1922 this.pendingRequestsById.Remove(Id);
1923 this.pendingRequestsByTimeout.Remove(Rec.Timeout);
1924 }
1925 else
1926 Rec = null;
1927 }
1928
1929 if (!(Rec is null))
1930 {
1931 if (IqResponse)
1932 await Rec.IqCallback.Raise(this, new IqResultEventArgs(Stanza.StanzaElement, Id, To, From, Language, Ok, Rec.State));
1933
1934 if (PresenceResponse)
1935 await Rec.PresenceCallback.Raise(this, new PresenceEventArgs(Sender, Type, Id, To, From, Language, Stanza, Rec.State));
1936 }
1937 }
1938
1939 return true;
1940 }
1941
1952 public Task<bool> IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
1953 {
1954 return this.IQ(Type, Id, To, From, Language, ToStanza("iq", Type, Id, To, From, Language, ContentXml), Sender);
1955 }
1956
1957 internal static Stanza ToStanza(string StanzaType, string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
1958 {
1959 StringBuilder Xml = new StringBuilder();
1960 StringBuilder Xml2 = new StringBuilder();
1961 int ContentStart, ContentLen;
1962
1963 Xml.Append("<stream:stream to='");
1964 Xml.Append(XML.Encode(To.Domain));
1965 Xml.Append("' from='");
1966 Xml.Append(XML.Encode(From.Domain));
1967 Xml.Append("' version='1.0' xml:lang='");
1968 Xml.Append(XML.Encode(Language));
1969 Xml.Append("' xmlns='jabber:server' xmlns:stream='");
1971 Xml.Append("'>");
1972
1973 Xml2.Append('<');
1974 Xml2.Append(StanzaType);
1975
1976 if (!string.IsNullOrEmpty(Type))
1977 {
1978 Xml2.Append(" type='");
1979 Xml2.Append(Type);
1980 Xml2.Append('\'');
1981 }
1982
1983 if (!string.IsNullOrEmpty(Id))
1984 {
1985 Xml2.Append(" id='");
1986 Xml2.Append(XML.Encode(Id));
1987 Xml2.Append('\'');
1988 }
1989
1990 if (!From.IsEmpty)
1991 {
1992 Xml2.Append(" from='");
1993 Xml2.Append(XML.Encode(From.Address));
1994 Xml2.Append('\'');
1995 }
1996
1997 if (!To.IsEmpty)
1998 {
1999 Xml2.Append(" to='");
2000 Xml2.Append(XML.Encode(To.Address));
2001 Xml2.Append('\'');
2002 }
2003
2004 if (!string.IsNullOrEmpty(Language))
2005 {
2006 Xml2.Append(" xml:lang='");
2007 Xml2.Append(XML.Encode(Language));
2008 Xml2.Append('\'');
2009 }
2010
2011 if (string.IsNullOrEmpty(ContentXml))
2012 {
2013 Xml2.Append("/>");
2014 ContentStart = ContentLen = 0;
2015 }
2016 else
2017 {
2018 Xml2.Append('>');
2019
2020 ContentStart = Xml2.Length;
2021 ContentLen = ContentXml.Length;
2022
2023 Xml2.Append(ContentXml);
2024 Xml2.Append("</");
2025 Xml2.Append(StanzaType);
2026 Xml2.Append('>');
2027 }
2028
2029 string s = Xml2.ToString();
2030
2031 Xml.Append(s);
2032 Xml.Append("</stream:stream>");
2033
2034 try
2035 {
2036 XmlDocument Doc = new XmlDocument()
2037 {
2038 PreserveWhitespace = true
2039 };
2040 Doc.LoadXml(Xml.ToString());
2041
2042 return new Stanza(Doc.DocumentElement, s, ContentStart, ContentLen);
2043 }
2044 catch (Exception ex)
2045 {
2046 throw new Exception("Invalid XML:\r\n\r\n" + ex.Message + "\r\n\r\n" + Xml.ToString());
2047 }
2048 }
2049
2060 public async Task<bool> Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
2061 {
2062 bool ToLocal = this.IsServerDomain(To.Domain, true);
2063
2064 if (ToLocal)
2065 {
2066 if (await this.persistenceLayer.IsBlocked(From.BareJid, To.BareJid))
2067 return await (Sender?.MessageErrorServiceUnavailable(From, To, string.Empty, string.Empty) ?? Task.FromResult(true));
2068 else if (await this.persistenceLayer.IsBlocked(To.BareJid, From.BareJid))
2069 {
2070 return await Sender.Message("error", Id, From, To, string.Empty, "<error type='cancel'><not-acceptable xmlns='" + StanzaNamespace +
2071 "'/><blocked xmlns='" + BlockingCommandErrorNamespace + "'/></error>");
2072 }
2073 else
2074 {
2075 if (To.IsBareJID)
2076 {
2077 IClientConnection[] Connections = this.GetClientConnections(To.BareJid);
2078
2079 if (!(Connections is null) && Connections.Length > 0)
2080 {
2081 bool Forwarded = false;
2082
2083 foreach (IClientConnection Connection in Connections)
2084 {
2085 try
2086 {
2087 if (await Connection.Message(Type, Id, To, From, Language, Stanza, Sender))
2088 Forwarded = true;
2089 }
2090 catch (Exception ex)
2091 {
2092 try
2093 {
2094 await Connection.Exception(ex);
2095 await Connection.DisposeAsync();
2096 }
2097 catch (Exception ex2)
2098 {
2099 Log.Exception(ex2);
2100 }
2101 }
2102 }
2103
2104 if (Forwarded)
2105 return true;
2106 }
2107
2108 if (string.IsNullOrEmpty(Type) || Type == "normal" || Type == "chat")
2109 {
2110 if (await this.persistenceLayer.StoreOfflineMessage(Type, Id, To, From, Language, Stanza))
2111 {
2112 if (Sender is ICommunicationLayer ComLayer)
2113 await ComLayer.Information("Message stored for later delivery.");
2114 }
2115 else
2116 return await (Sender?.MessageErrorServiceUnavailable(From, To, string.Empty, string.Empty) ?? Task.FromResult(true));
2117 }
2118 }
2119 else
2120 {
2121 if (this.TryGetConnection(To.Address, out IClientConnection Connection))
2122 {
2123 bool Forwarded = false;
2124
2125 try
2126 {
2127 Forwarded = await Connection.Message(Type, Id, To, From, Language, Stanza, Sender);
2128 }
2129 catch (Exception ex)
2130 {
2131 await Connection.Exception(ex);
2132 await Connection.DisposeAsync();
2133 }
2134
2135 if (Forwarded)
2136 return true;
2137 }
2138
2139 return await (Sender?.MessageErrorServiceUnavailable(From, To, string.Empty, string.Empty) ?? Task.FromResult(true));
2140 }
2141 }
2142
2143 return true;
2144 }
2145 else
2146 {
2148
2149 lock (this.synchObject)
2150 {
2151 if (!this.componentsByFulldomain.TryGetValue(To.Domain, out Component))
2152 Component = null;
2153 }
2154
2155 if (!(Component is null))
2156 {
2158 return await Component.Message(Type, Id, To, From, Language, Stanza, Sender);
2159 }
2160
2161 IS2SEndpoint Endpoint;
2162
2163 try
2164 {
2165 Endpoint = await this.GetS2sEndpoint(From.Domain, To.Domain, true, "Sending message from " + From.Address + " to " + To.Address);
2166 if (Endpoint is null)
2167 return true;
2168 }
2169 catch (Exception ex)
2170 {
2171 if (!(Sender is null))
2172 return await (Sender?.MessageErrorServiceUnavailable(From, To, ex.Message, string.Empty) ?? Task.FromResult(true));
2173
2174 return true;
2175 }
2176
2177 return await Endpoint.Message(Type, Id, To, From, Language, Stanza, Sender);
2178 }
2179 }
2180
2189 public static void RegisterRemoteDomain(CaseInsensitiveString RemoteDomain, string Host, int Port, bool TrustCertificate)
2190 {
2191 lock (remoteDomainLookup)
2192 {
2193 remoteDomainLookup[RemoteDomain] = new S2SRec()
2194 {
2195 Domain = RemoteDomain,
2196 Host = Host,
2197 Port = Port,
2198 TrustCertificate = TrustCertificate
2199 };
2200 }
2201 }
2202
2212 public static void RegisterRemoteDomain(CaseInsensitiveString RemoteDomain, CaseInsensitiveString ServerDomain, string Host, int Port, bool TrustCertificate)
2213 {
2214 lock (remoteDomainLookup)
2215 {
2216 remoteDomainLookup[RemoteDomain] = new S2SRec()
2217 {
2218 Domain = ServerDomain,
2219 Host = Host,
2220 Port = Port,
2221 TrustCertificate = TrustCertificate
2222 };
2223 }
2224 }
2225
2226 internal async void RegisterS2SEndpoint(IS2SEndpoint Endpoint)
2227 {
2229 EndpointStatistics Stat = this.GetS2sStatistics(Domain, Endpoint.Type);
2230 EventHandlerAsync<ServerConnectionEventArgs> h;
2231
2232 Stat.EndpointConnected(Endpoint);
2233
2234 if (this.s2sEndpoints.ContainsKey(Domain))
2235 h = this.ServerConnectionUpdated;
2236 else
2237 h = this.ServerConnectionAdded;
2238
2239 this.s2sEndpoints[Domain] = Endpoint;
2240
2241 await h.Raise(this, new ServerConnectionEventArgs(Domain, Endpoint));
2242
2243 if (Endpoint is XmppS2SEndpoint XmppS2SEndpoint)
2244 XmppS2SEndpoint.OnStateChanged += this.Endpoint_OnStateChanged;
2245 }
2246
2247 private async Task Endpoint_OnStateChanged(object Sender, EventArgs e)
2248 {
2249 if (Sender is XmppS2SEndpoint Endpoint)
2250 await this.ServerConnectionUpdated.Raise(this, new ServerConnectionEventArgs(Endpoint.RemoteDomain, Endpoint));
2251 }
2252
2259 public bool TryGetS2sEndpoint(string RemoteDomain, out IS2SEndpoint Endpoint)
2260 {
2261 return this.s2sEndpoints.TryGetValue(RemoteDomain, out Endpoint);
2262 }
2263
2264 internal void RegisterAsTemporary(IS2SEndpoint Endpoint)
2265 {
2266 lock (this.synchObject)
2267 {
2268 if (this.temporaryConnections is null)
2269 this.temporaryConnections = new SortedDictionary<DateTime, IS2SEndpoint>();
2270
2271 DateTime TP = DateTime.Now.AddMinutes(1);
2272
2273 while (this.temporaryConnections.ContainsKey(TP))
2274 TP = TP.AddTicks(this.gen.Next(10));
2275
2276 this.temporaryConnections[TP] = Endpoint;
2277 }
2278 }
2279
2280 internal static async Task<S2SRec> GetDomainFromDns(CaseInsensitiveString DomainName)
2281 {
2282 S2SRec Result;
2283 string SrvMsg;
2284
2285 try
2286 {
2287 SRV SRV = await DnsResolver.LookupServiceEndpoint(DomainName, "xmpp-server", "tcp");
2288 if (SRV is null)
2289 SrvMsg = "Unable to get SRV record for xmpp-server/tcp of " + DomainName;
2290 else
2291 {
2292 Result = new S2SRec()
2293 {
2294 Domain = DomainName,
2295 Host = SRV.TargetHost,
2296 Port = SRV.Port,
2297 TrustCertificate = false,
2298 Type = S2sType.XMPP
2299 };
2300
2301 return Result;
2302 }
2303 }
2304 catch (Exception ex)
2305 {
2306 SrvMsg = ex.Message;
2307 }
2308
2309 try
2310 {
2311 string[] Hosts = await DnsResolver.LookupMailExchange(DomainName);
2312 if (Hosts.Length > 0 && !string.IsNullOrEmpty(Hosts[0]))
2313 {
2314 try
2315 {
2316 using (TcpClient TestClient = new TcpClient())
2317 {
2318 await TestClient.ConnectAsync(DomainName, DefaultS2sPort);
2319
2320 Log.Notice("Federated XMPP server connection. SRV DNS settings not found: " + SrvMsg, DomainName);
2321
2322 Result = new S2SRec()
2323 {
2324 Domain = DomainName,
2325 Host = DomainName,
2326 Port = DefaultS2sPort,
2327 TrustCertificate = false,
2328 Type = S2sType.XMPP
2329 };
2330
2331 return Result;
2332 }
2333 }
2334 catch (Exception)
2335 {
2336 // Ignore. Just a test to check for XMPP if DNS settings not available or incorrect.
2337 }
2338
2339 Log.Notice("Federated mail server connection. XMPP server not found: " + SrvMsg, DomainName);
2340
2341 Result = new S2SRec()
2342 {
2343 Domain = DomainName,
2344 Host = Hosts[0],
2346 TrustCertificate = false,
2347 Type = S2sType.SMTP
2348 };
2349
2350 return Result;
2351 }
2352 }
2353 catch (Exception)
2354 {
2355 // Ignore
2356 }
2357
2358 return null;
2359 }
2360
2366 public static async Task<S2SRec> GetDomain(CaseInsensitiveString DomainOrSubdomain)
2367 {
2368 S2SRec Result;
2369
2370 lock (remoteDomainLookup)
2371 {
2372 if (remoteDomainLookup.TryGetValue(DomainOrSubdomain, out Result))
2373 return Result;
2374 }
2375
2376 Result = await GetDomainFromDns(DomainOrSubdomain);
2377 if (!(Result is null))
2378 {
2379 if (Result.Type == S2sType.XMPP && Result.Domain.EndsWith("." + Result.Host))
2380 Result.Domain = Result.Host; // Component
2381
2382 lock (remoteDomainLookup)
2383 {
2384 remoteDomainLookup[DomainOrSubdomain] = Result;
2385 }
2386
2387 return Result;
2388 }
2389
2390 CaseInsensitiveString[] Parts = DomainOrSubdomain.Split('.');
2391 if (Parts.Length < 2)
2392 throw new Exception("Invalid domain or subdomain name: " + DomainOrSubdomain);
2393
2394 int c = Parts.Length;
2395 int i = c - 2;
2396 CaseInsensitiveString Domain = Parts[i] + "." + Parts[i + 1];
2397 S2SRec Rec;
2398
2399 while (i >= 0)
2400 {
2401 Rec = null;
2402
2403 if (Domain != DomainOrSubdomain)
2404 {
2405 Rec = await GetDomainFromDns(Domain);
2406 if (!(Rec is null))
2407 Result = Rec;
2408 }
2409
2410 if (Rec is null)
2411 {
2412 try
2413 {
2414 IPHostEntry Entry = await System.Net.Dns.GetHostEntryAsync(Domain);
2415 if (Entry.AddressList.Length > 0)
2416 {
2417 using (TcpClient TestClient = new TcpClient())
2418 {
2419 await TestClient.ConnectAsync(Domain, DefaultS2sPort);
2420 TestClient.Close();
2421 }
2422
2423 Result = Rec = new S2SRec()
2424 {
2425 Domain = Domain,
2426 Host = Domain,
2427 Port = DefaultS2sPort,
2428 Type = S2sType.XMPP,
2429 TrustCertificate = false
2430 };
2431 }
2432 }
2433 catch (Exception)
2434 {
2435 // Subdomains might not be registered in DNS.
2436 }
2437 }
2438
2439 i--;
2440 if (i >= 0)
2441 Domain = Parts[i] + "." + Domain;
2442
2443 if (Rec is null && !(Result is null) && Result.Type == S2sType.XMPP)
2444 break;
2445 }
2446
2447 if (!(Result is null))
2448 {
2449 lock (remoteDomainLookup)
2450 {
2451 remoteDomainLookup[DomainOrSubdomain] = Result;
2452 }
2453
2454 return Result;
2455 }
2456
2457 throw new Exception("Invalid S2S domain or subdomain name: " + DomainOrSubdomain);
2458 }
2459
2463 public enum S2sType
2464 {
2468 XMPP,
2469
2473 SMTP
2474 }
2475
2479 public class S2SRec
2480 {
2485
2490
2495
2499 public int Port;
2500
2504 public bool TrustCertificate;
2505 }
2506
2507 internal async Task<IS2SEndpoint> GetS2sEndpoint(CaseInsensitiveString LocalDomain, CaseInsensitiveString RemoteDomain, bool ReuseExisting, string Reason)
2508 {
2511 S2SRec Rec;
2512 S2sType Type;
2513 int Port;
2514 bool RecFound;
2515 bool TrustCertificate;
2516
2517 if (this.disposed)
2518 return null;
2519
2520 lock (remoteDomainLookup)
2521 {
2522 RecFound = remoteDomainLookup.TryGetValue(RemoteDomain, out Rec);
2523 }
2524
2525 if (!RecFound)
2526 Rec = await GetDomain(RemoteDomain);
2527
2528 RemoteDomain = Rec.Domain;
2529 Host = Rec.Host;
2530 Port = Rec.Port;
2531 TrustCertificate = Rec.TrustCertificate;
2532 Type = Rec.Type;
2533
2534 if (this.IsServerDomain(RemoteDomain, true)) // Non-existing sub-domain pointing to itself, creating loop.
2535 return null;
2536
2537 if (ReuseExisting && this.s2sEndpoints.TryGetValue(RemoteDomain, out IS2SEndpoint Endpoint))
2538 {
2539 if (!((XmppS2SEndpoint = Endpoint as XmppS2SEndpoint) is null) &&
2541 {
2542 this.s2sEndpoints.Remove(RemoteDomain);
2543 await Endpoint.DisposeAsync();
2544 }
2545 else
2546 return Endpoint;
2547 }
2548
2549 IS2SEndpoint Result;
2550
2551 switch (Type)
2552 {
2553 case S2sType.XMPP:
2554 if (CaseInsensitiveString.IsNullOrEmpty(this.domain))
2555 throw new NotSupportedException("S2S connections not supported. No domain defined.");
2556 else if (this.serverCertificate is null)
2557 throw new NotSupportedException("S2S connections not supported. No server certificate defined.");
2558
2559 Log.Informational("Opening S2S connection. " + Reason, RemoteDomain, string.Empty, "XmppOpenS2s");
2560
2561 if (!this.IsServerDomain(LocalDomain, true))
2562 {
2563 int i = LocalDomain.IndexOf('.');
2564 if (i > 0)
2565 {
2566 CaseInsensitiveString s = LocalDomain.Substring(i + 1);
2567 if (this.IsServerDomain(s, true))
2568 LocalDomain = s;
2569 }
2570 }
2571
2572 XmppS2SEndpoint = new XmppS2SEndpoint(LocalDomain, RemoteDomain, Port, this.serverCertificate, this, TrustCertificate);
2573 Result = XmppS2SEndpoint;
2574
2575 this.AddS2SSniffers(Result, RemoteDomain);
2576
2577 await XmppS2SEndpoint.Information("Opening S2S connection to " + Host + ". " + Reason);
2578 Task _ = XmppS2SEndpoint.Connect(Host);
2579
2580 break;
2581
2582 case S2sType.SMTP:
2583 if (this.smtpServer is null)
2584 throw new NotSupportedException("Integration with SMTP not enabled.");
2585
2586 Result = new SmtpS2SEndpoint(LocalDomain, RemoteDomain, this.smtpServer, this);
2587 break;
2588
2589 default:
2590 throw new NotSupportedException("S2S Connection type not supported: " + Type.ToString());
2591 }
2592
2593 if (ReuseExisting)
2594 this.RegisterS2SEndpoint(Result);
2595 else
2596 this.RegisterAsTemporary(Result);
2597
2598 return Result;
2599 }
2600
2601 internal void AddS2SSniffers(ICommunicationLayer Endpoint, string RemoteDomain)
2602 {
2603 if (!string.IsNullOrEmpty(this.domainSnifferPath))
2604 Endpoint.Add(this.GetSniffer(RemoteDomain, true));
2605 else if (this.s2sSniffers.HasSniffers)
2606 {
2607 foreach (ISniffer Sniffer in this.s2sSniffers.Sniffers)
2608 Endpoint.Add(Sniffer);
2609 }
2610 }
2611
2612 internal static bool TrustCertificate(CaseInsensitiveString RemoteDomain)
2613 {
2614 lock (remoteDomainLookup)
2615 {
2616 if (remoteDomainLookup.TryGetValue(RemoteDomain, out S2SRec Rec))
2617 return Rec.TrustCertificate;
2618 else
2619 return false;
2620 }
2621 }
2622
2633 public Task<bool> Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
2634 {
2635 return this.Message(Type, Id, To, From, Language, ToStanza("message", Type, Id, To, From, Language, ContentXml), Sender);
2636 }
2637
2647 public async Task<bool> Presence(string Type, string Id, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
2648 {
2649 if (Sender is IClientConnection FromConnection)
2650 {
2651 switch (Type)
2652 {
2653 case "subscribe":
2654 case "subscribed":
2655 case "unsubscribe":
2656 case "unsubscribed":
2657 return await (Sender?.PresenceErrorNotAllowed(Id, From, this.domainAddress, string.Empty, string.Empty) ?? Task.FromResult(true));
2658 }
2659
2660 CaseInsensitiveString FromUserName = FromConnection.UserName;
2661 IAccount FromAccount = await this.persistenceLayer.GetAccount(FromUserName);
2662
2663 FromConnection.LastPresence = new PresenceEventArgs(Sender, Type, Id, XmppAddress.Empty, From, Language, Stanza, null);
2664 await this.PushPresence(FromConnection.BareAddress, Type, Id, From, Language, Stanza, false, Sender);
2665
2666 foreach (IRosterItem Item in await this.persistenceLayer.GetRoster(FromUserName))
2667 {
2668 if (Item.BareJid != FromConnection.BareJid &&
2669 (Item.Subscription == SubscriptionStatus.both || Item.Subscription == SubscriptionStatus.from))
2670 {
2671 await this.PushPresence(new XmppAddress(Item.BareJid), Type, Id, From, Language, Stanza, false, Sender);
2672 }
2673 }
2674
2675 await this.ClientConnectionUpdated.Raise(this, new ClientConnectionEventArgs(From.Address, FromConnection));
2676 }
2677
2678 return true;
2679 }
2680
2691 public async Task<bool> Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
2692 {
2693 bool ToLocal = this.IsServerDomain(To.Domain, true);
2694 IAccount ToAccount = ToLocal ? await this.persistenceLayer.GetAccount(To.Account) : null;
2695 CaseInsensitiveString FromUserName = null;
2696 IClientConnection FromConnection = Sender as IClientConnection;
2697 bool FromLocal = !(FromConnection is null);
2698 IAccount FromAccount = null;
2699 PresenceEventArgs LastPresence = null;
2701 bool UseBareJids = false;
2702
2703 if (!string.IsNullOrEmpty(Id))
2704 {
2705 PendingRequest Rec = null;
2706 bool Ok = (Type != "error");
2707
2708 lock (this.synchObject)
2709 {
2710 if (this.pendingRequestsById.TryGetValue(Id, out Rec))
2711 {
2712 this.pendingRequestsById.Remove(Id);
2713 this.pendingRequestsByTimeout.Remove(Rec.Timeout);
2714 }
2715 else
2716 Rec = null;
2717 }
2718
2719 if (!(Rec?.PresenceCallback is null))
2720 await Rec.PresenceCallback.Raise(this, new PresenceEventArgs(Sender, Type, Id, To, From, Language, Stanza, Rec.State));
2721 }
2722
2723 if (ToLocal && ToAccount is null)
2724 return await (Sender?.PresenceErrorItemNotFound(Id, From, To, string.Empty, string.Empty) ?? Task.FromResult(true));
2725
2726 if (FromLocal)
2727 {
2728 FromUserName = FromConnection.UserName;
2729 FromAccount = await this.persistenceLayer.GetAccount(FromUserName);
2730
2731 if (FromAccount is null)
2732 return await (Sender?.PresenceErrorNotAllowed(Id, From, this.domainAddress, string.Empty, string.Empty) ?? Task.FromResult(true));
2733 }
2734
2735 switch (Type)
2736 {
2737 case "subscribe":
2738 if (FromLocal)
2739 {
2740 if (await this.persistenceLayer.IsBlocked(From.BareJid, To.BareJid))
2741 return true;
2742
2743 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.BareJid);
2744
2745 if (!(Item is null))
2746 {
2747 if (!Item.PendingSubscription)
2748 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, Item.Subscription, true, Item.Groups);
2749 }
2750 else
2751 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, string.Empty, SubscriptionStatus.none, true, null);
2752
2753 if (!(Item is null))
2754 await this.PushIq(From.BareJid, Item);
2755 }
2756
2757 UseBareJids = true;
2758 break;
2759
2760 case "subscribed":
2761 if (ToLocal)
2762 {
2763 Item = await this.persistenceLayer.GetRosterItem(To.Account, From.BareJid);
2764
2765 if (!(Item is null))
2766 {
2767 if (Item.Subscription == SubscriptionStatus.from)
2768 {
2769 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.both,
2770 false, Item.Groups);
2771 }
2772 else if (Item.Subscription == SubscriptionStatus.none)
2773 {
2774 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.to,
2775 false, Item.Groups);
2776 }
2777 else
2778 Item = null;
2779
2780 if (!(Item is null))
2781 await this.PushIq(To.BareJid, Item);
2782 }
2783 else
2784 break; // No subscription request to accept.
2785 }
2786
2787 if (FromLocal)
2788 {
2789 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.BareJid);
2790 if (!(Item is null))
2791 {
2792 if (Item.Subscription == SubscriptionStatus.to)
2793 {
2794 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.both,
2795 Item.PendingSubscription, Item.Groups);
2796 }
2797 else if (Item.Subscription == SubscriptionStatus.none)
2798 {
2799 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.from,
2800 Item.PendingSubscription, Item.Groups);
2801 }
2802 else
2803 Item = null;
2804 }
2805 else
2806 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, string.Empty, SubscriptionStatus.from, false, null);
2807
2808 if (!(Item is null))
2809 await this.PushIq(From.BareJid, Item);
2810
2811 LastPresence = FromConnection.LastPresence;
2812 }
2813
2814 UseBareJids = true;
2815 break;
2816
2817 case "unsubscribed":
2818 if (ToLocal)
2819 {
2820 Item = await this.persistenceLayer.GetRosterItem(To.Account, From.BareJid);
2821
2822 if (!(Item is null))
2823 {
2824 if (Item.Subscription == SubscriptionStatus.to)
2825 {
2826 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.none,
2827 false, Item.Groups);
2828 }
2829 else if (Item.Subscription == SubscriptionStatus.both)
2830 {
2831 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.from,
2832 false, Item.Groups);
2833 }
2834 else if (Item.PendingSubscription)
2835 {
2836 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, Item.Subscription,
2837 false, Item.Groups);
2838 }
2839 else
2840 Item = null;
2841
2842 if (!(Item is null))
2843 await this.PushIq(To.BareJid, Item);
2844 }
2845 }
2846
2847 if (FromLocal)
2848 {
2849 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.BareJid);
2850 if (!(Item is null))
2851 {
2852 if (Item.Subscription == SubscriptionStatus.from)
2853 {
2854 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.none,
2855 Item.PendingSubscription, Item.Groups);
2856 }
2857 else if (Item.Subscription == SubscriptionStatus.both)
2858 {
2859 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.to,
2860 Item.PendingSubscription, Item.Groups);
2861 }
2862 else
2863 Item = null;
2864
2865 if (!(Item is null))
2866 await this.PushIq(From.BareJid, Item);
2867 }
2868 }
2869
2870 UseBareJids = true;
2871 break;
2872
2873 case "unsubscribe":
2874 if (ToLocal)
2875 {
2876 Item = await this.persistenceLayer.GetRosterItem(To.Account, From.BareJid);
2877
2878 if (!(Item is null))
2879 {
2880 if (Item.Subscription == SubscriptionStatus.from)
2881 {
2882 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.none,
2883 false, Item.Groups);
2884 }
2885 else if (Item.Subscription == SubscriptionStatus.both)
2886 {
2887 Item = await this.persistenceLayer.SetRosterItem(To.Account, From.BareJid, Item.Name, SubscriptionStatus.to,
2888 false, Item.Groups);
2889 }
2890 else
2891 Item = null;
2892
2893 if (!(Item is null))
2894 await this.PushIq(To.BareJid, Item);
2895 }
2896 else
2897 break; // No subscription request to unsubscribe from.
2898 }
2899
2900 if (FromLocal)
2901 {
2902 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.BareJid);
2903 if (!(Item is null))
2904 {
2905 if (Item.Subscription == SubscriptionStatus.to)
2906 {
2907 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.none,
2908 false, Item.Groups);
2909 }
2910 else if (Item.Subscription == SubscriptionStatus.both)
2911 {
2912 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, SubscriptionStatus.from,
2913 false, Item.Groups);
2914 }
2915 else if (Item.PendingSubscription)
2916 {
2917 Item = await this.persistenceLayer.SetRosterItem(FromUserName, To.BareJid, Item.Name, Item.Subscription,
2918 false, Item.Groups);
2919 }
2920 else
2921 Item = null;
2922
2923 if (!(Item is null))
2924 await this.PushIq(From.BareJid, Item);
2925 }
2926 }
2927
2928 await this.PushPresence(To, Type, Id, From.ToBareJID(), Language, Stanza, UseBareJids, Sender);
2929
2930 Type = "unavailable";
2931
2932 Stanza = null;
2933 UseBareJids = true;
2934 break;
2935
2936 case "probe":
2937 if (ToLocal)
2938 {
2939 if (ToAccount is null ||
2940 await this.persistenceLayer.IsBlocked(From.BareJid, To.BareJid) ||
2941 (From.HasAccount && (
2942 (Item = await this.persistenceLayer.GetRosterItem(To.Account, From.BareJid)) is null ||
2943 (Item.Subscription != SubscriptionStatus.both && Item.Subscription != SubscriptionStatus.from))))
2944 {
2945 if (!await (Sender?.Presence("unsubscribed", Id, From, To, string.Empty, string.Empty) ?? Task.FromResult(true)))
2946 return false;
2947 }
2948 else
2949 {
2950 bool Sent = false;
2951
2952 if (To.IsBareJID)
2953 {
2954 IClientConnection[] Connections = this.GetClientConnections(To.BareJid);
2955 if (!(Connections is null))
2956 {
2957 foreach (IClientConnection Connection in Connections)
2958 {
2959 if (!((LastPresence = Connection.LastPresence) is null) && LastPresence.To.IsEmpty)
2960 {
2961 if (!await Sender.Presence(LastPresence.Type, Id, From, LastPresence.From, LastPresence.Language,
2962 LastPresence.Stanza?.Content))
2963 {
2964 return false;
2965 }
2966
2967 Sent = true;
2968 }
2969 }
2970 }
2971 }
2972 else if (this.TryGetConnection(To.Address, out IClientConnection Connection))
2973 {
2974 LastPresence = Connection.LastPresence;
2975 if (!await (Sender?.Presence(LastPresence.Type, Id, From, LastPresence.From, LastPresence.Language, LastPresence.Stanza?.Content) ?? Task.FromResult(true)))
2976 return false;
2977 Sent = true;
2978 }
2979
2980 if (!Sent && !await (Sender?.Presence("unavailable", Id, From, To, string.Empty, string.Empty) ?? Task.FromResult(true)))
2981 return false;
2982 }
2983
2984 return true; // Don't propagate presence probes to clients.
2985 }
2986 break;
2987
2988 case "":
2989 case "error":
2990 case "unavailable":
2991 break;
2992
2993 default:
2994 if (!await (Sender?.PresenceErrorBadRequest(Id, From, this.domainAddress, "Invalid type.", "en") ?? Task.FromResult(true)))
2995 return false;
2996 break;
2997 }
2998
2999 await this.PushPresence(To, Type, Id, UseBareJids ? From.ToBareJID() : From, Language, Stanza, UseBareJids, Sender);
3000
3001 if (!(LastPresence is null))
3002 {
3003 await this.PushPresence(To, LastPresence.Type, LastPresence.Id, LastPresence.From, LastPresence.Language,
3004 LastPresence.Stanza, false, Sender);
3005 }
3006
3007 return true;
3008 }
3009
3010 private async Task<bool> PushPresence(XmppAddress To, string Type, string Id, XmppAddress From, string Language, Stanza Stanza,
3011 bool UseBareJids, ISender Sender)
3012 {
3013 bool FromLocal = this.IsServerDomain(From.Domain, true);
3014 PresenceEventArgs e = null;
3015
3016 if (FromLocal)
3017 {
3018 e = new PresenceEventArgs(Sender, Type, Id, To, From, Language, Stanza, null);
3019 await this.OnPresenceLocalSender.Raise(this, e);
3020
3021 if (e.ResponseSent)
3022 return true;
3023 }
3024
3025 bool ToLocal = To.Domain == From.Domain ? FromLocal : this.IsServerDomain(To.Domain, true);
3026
3027 if (ToLocal)
3028 {
3029 IClientConnection[] Connections = this.GetClientConnections(To.BareJid);
3030 await this.PushPresence(Type, Id, From, Language, Stanza is null ? string.Empty : Stanza.Content, Connections, UseBareJids);
3031
3032 await this.OnPresenceLocalRecipient.Raise(this, e ?? new PresenceEventArgs(Sender, Type, Id, To, From, Language, Stanza, null));
3033 }
3034 else
3035 {
3036 Tuple<bool, IRecipient> Rec = await this.TryGetRecipient(To, From);
3037
3038 if (Rec.Item1)
3039 await Rec.Item2.Presence(Type, Id, To, From, Language, Stanza, Sender);
3040 }
3041
3042 return true;
3043 }
3044
3048 public event EventHandlerAsync<PresenceEventArgs> OnPresenceLocalRecipient = null;
3049
3053 public event EventHandlerAsync<PresenceEventArgs> OnPresenceLocalSender = null;
3054
3065 public Task<bool> Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
3066 {
3067 return this.Presence(Type, Id, To, From, Language, ToStanza("presence", Type, Id, To, From, Language, ContentXml), Sender);
3068 }
3069
3076 public static void GetErrorInformation(Exception ex, out string Type, out string Xml)
3077 {
3078 if (ex is StanzaExceptionException stEx)
3079 {
3080 Type = stEx.ErrorType;
3081 Xml = "<" + stEx.ErrorStanzaName + " xmlns='" + StanzaNamespace + "'/>";
3082 return;
3083 }
3084 else if (ex is HttpException)
3085 {
3086 Type = null;
3087 Xml = null;
3088
3089 if (ex is HTTP.BadRequestException)
3090 {
3091 Type = "modify";
3092 Xml = "bad-request";
3093 }
3094 else if (ex is HTTP.ConflictException)
3095 {
3096 Type = "cancel";
3097 Xml = "conflict";
3098 }
3099 else if (ex is HTTP.ForbiddenException)
3100 {
3101 Type = "auth";
3102 Xml = "forbidden";
3103 }
3104 else if (ex is HTTP.NotImplementedException)
3105 {
3106 Type = "cancel";
3107 Xml = "feature-not-implemented";
3108 }
3109 else if (ex is HTTP.MovedPermanentlyException || ex is HTTP.GoneException)
3110 {
3111 Type = "cancel";
3112 Xml = "gone";
3113 }
3114 else if (ex is HTTP.InternalServerErrorException)
3115 {
3116 Type = "cancel";
3117 Xml = "internal-server-error";
3118 }
3119 else if (ex is HTTP.NotFoundException)
3120 {
3121 Type = "cancel";
3122 Xml = "item-not-found";
3123 }
3124 else if (ex is HTTP.NotAcceptableException || ex is HTTP.UnsupportedMediaTypeException)
3125 {
3126 Type = "modify";
3127 Xml = "not-acceptable";
3128 }
3129 else if (ex is HTTP.MethodNotAllowedException)
3130 {
3131 Type = "cancel";
3132 Xml = "not-allowed";
3133 }
3134 else if (ex is HTTP.TooManyRequestsException || ex is HTTP.InsufficientStorageException)
3135 {
3136 Type = "wait";
3137 Xml = "resource-constraint";
3138 }
3139 else if (ex is HTTP.ServiceUnavailableException)
3140 {
3141 Type = "cancel";
3142 Xml = "service-unavailable";
3143 }
3144 else if (ex is HTTP.NetworkAuthenticationRequiredException)
3145 {
3146 Type = "auth";
3147 Xml = "not-authorized";
3148 }
3149
3150 if (!(Xml is null))
3151 {
3152 Xml = "<" + Xml + " xmlns='" + StanzaNamespace + "'/>";
3153 return;
3154 }
3155 }
3156
3157 Type = "cancel";
3158 Xml = "<internal-server-error xmlns='" + StanzaNamespace + "'/>";
3159 }
3160
3161 #endregion
3162
3163 #region Request/Response
3164
3174 public async Task<bool> GetLastPresence(CaseInsensitiveString BareJid, EventHandlerAsync<PresenceEventArgs> Callback, object State)
3175 {
3176 int i = BareJid.IndexOf('@');
3177 if (i >= 0)
3178 {
3179 CaseInsensitiveString UserName = BareJid.Substring(0, i);
3180 CaseInsensitiveString Domain = BareJid.Substring(i + 1);
3181
3182 if (this.IsServerDomain(Domain, true))
3183 {
3184 IClientConnection[] Connections = this.GetClientConnections(BareJid);
3185 if (!(Connections is null))
3186 {
3187 PresenceEventArgs Last = null;
3188
3189 foreach (IClientConnection Connection in Connections)
3190 {
3191 if (!(Connection?.LastPresence is null))
3192 {
3193 PresenceEventArgs LastPresence = Connection.LastPresence;
3194 if (Last is null || LastPresence.Timestamp > Last.Timestamp)
3195 Last = LastPresence;
3196 }
3197 }
3198
3199 if (Last is null)
3200 return false;
3201
3202 await Callback.Raise(this, new PresenceEventArgs(null, Last.Type, Last.Id, Last.To, Last.From, Last.Language, Last.Stanza, State));
3203
3204 return true;
3205 }
3206 else
3207 return false;
3208 }
3209 else
3210 {
3211 IS2SEndpoint Endpoint;
3212
3213 try
3214 {
3215 Endpoint = await this.GetS2sEndpoint(this.domain, Domain, true, "Performing presence probe on " + BareJid);
3216 }
3217 catch (Exception)
3218 {
3219 return false;
3220 }
3221
3222 if (Endpoint is XmppS2SEndpoint XmppS2SEndpoint)
3223 {
3224 PendingRequest Request = this.PrepareRequest(null, (Sender, e) =>
3225 {
3226 return Callback.Raise(this, new PresenceEventArgs(null, e.Type, e.Id, e.To, e.From, e.Language, e.Stanza, e.State));
3227 }, null, State, 10000, 0, false, 10000, this.domainAddress, new XmppAddress(BareJid), string.Empty, string.Empty);
3228
3229 await XmppS2SEndpoint.SendStanza("presence", "probe", Request.Id, new XmppAddress(BareJid), this.domainAddress, string.Empty, string.Empty, this);
3230 // TODO: Errors in SendStanza should be returned, which does not happen since last parameter is null.
3231
3232 return true;
3233 }
3234 else
3235 return false;
3236 }
3237 }
3238 else
3239 return false;
3240 }
3241
3242 private PendingRequest PrepareRequest(EventHandlerAsync<IqResultEventArgs> IqCallback, EventHandlerAsync<PresenceEventArgs> PresenceCallback,
3243 EventHandlerAsync<PendingRequestEventArgs> ResendCallback, object State, int RetryTimeout, int NrRetries, bool DropOff, int MaxRetryTimeout,
3244 XmppAddress From, XmppAddress To, string Language, string ContentXml)
3245 {
3246 lock (this.synchObject)
3247 {
3248 string Id;
3249
3250 do
3251 {
3252 Id = this.NewId(16);
3253 }
3254 while (this.pendingRequestsById.ContainsKey(Id));
3255
3256 PendingRequest PendingRequest;
3257
3258 if (!(IqCallback is null))
3259 {
3260 PendingRequest = new PendingRequest(Id, RetryTimeout, NrRetries, DropOff, MaxRetryTimeout)
3261 {
3262 IqCallback = IqCallback,
3263 ResendCallback = ResendCallback,
3264 State = State,
3265 From = From,
3266 To = To,
3267 Id = Id,
3268 Language = Language,
3269 ContentXml = ContentXml
3270 };
3271 }
3272 else
3273 {
3274 PendingRequest = new PendingRequest(Id, RetryTimeout, NrRetries, DropOff, MaxRetryTimeout)
3275 {
3276 PresenceCallback = PresenceCallback,
3277 ResendCallback = ResendCallback,
3278 State = State,
3279 From = From,
3280 To = To,
3281 Id = Id,
3282 Language = Language,
3283 ContentXml = ContentXml
3284 };
3285 }
3286
3287 DateTime TP = PendingRequest.Timeout;
3288
3289 if (this.pendingRequestsByTimeout.ContainsKey(TP))
3290 {
3291 Random Rnd = new Random();
3292
3293 while (this.pendingRequestsByTimeout.ContainsKey(TP))
3294 TP = TP.AddTicks(Rnd.Next(100) + 1);
3295 }
3296
3297 PendingRequest.Timeout = TP;
3298
3299 this.pendingRequestsById[Id] = PendingRequest;
3300 this.pendingRequestsByTimeout[TP] = PendingRequest;
3301
3302 return PendingRequest;
3303 }
3304 }
3305
3317 public Task<bool> SendIqRequest(string Type, string From, string To, string Language, string ContentXml,
3318 EventHandlerAsync<IqResultEventArgs> Callback, object State)
3319 {
3320 return this.SendIqRequest(Type, new XmppAddress(From), new XmppAddress(To), Language, ContentXml, Callback, State);
3321 }
3322
3334 public async Task<bool> SendIqRequest(string Type, XmppAddress From, XmppAddress To, string Language, string ContentXml,
3335 EventHandlerAsync<IqResultEventArgs> Callback, object State)
3336 {
3337 (bool Found, IRecipient Recipient) = await this.TryGetRecipient(To, From);
3338
3339 if (Found)
3340 {
3341 PendingRequest Request = this.PrepareRequest(Callback, null, async (Sender, e) =>
3342 {
3343 await Recipient.IQ(Type, e.Request.Id, e.Request.To, e.Request.From,
3344 e.Request.Language, e.Request.ContentXml, this);
3345
3346 }, State, this.defaultRetryTimeout, this.defaultNrRetries, this.defaultDropOff,
3347 this.defaultMaxRetryTimeout, From, To, Language, ContentXml);
3348
3349 await Recipient.IQ(Type, Request.Id, To, From, Language, ContentXml, this);
3350
3351 return true;
3352 }
3353 else
3354 {
3355 IqResultEventArgs e = new IqResultEventArgs(null, string.Empty, From, To, Language, false, State);
3356 await Callback.Raise(this, e);
3357
3358 return false;
3359 }
3360 }
3361
3371 public Task<IqResultEventArgs> IqRequest(string Type, string From, string To, string Language, string ContentXml)
3372 {
3373 return this.IqRequest(Type, new XmppAddress(From), new XmppAddress(To), Language, ContentXml);
3374 }
3375
3385 public async Task<IqResultEventArgs> IqRequest(string Type, XmppAddress From, XmppAddress To, string Language, string ContentXml)
3386 {
3387 TaskCompletionSource<IqResultEventArgs> Result = new TaskCompletionSource<IqResultEventArgs>();
3388
3389 if (await this.SendIqRequest(Type, From, To, Language, ContentXml, (Sender, e) =>
3390 {
3391 Result.TrySetResult(e);
3392 return Task.CompletedTask;
3393
3394 }, null))
3395 {
3396 return await Result.Task;
3397 }
3398 else
3399 throw new InvalidOperationException("Unable to send request.");
3400 }
3401
3412 public Task<bool> SendMessage(string Type, string Id, string From, string To, string Language, string ContentXml)
3413 {
3414 return this.SendMessage(Type, Id, new XmppAddress(From), new XmppAddress(To), Language, ContentXml);
3415 }
3416
3427 public async Task<bool> SendMessage(string Type, string Id, XmppAddress From, XmppAddress To, string Language, string ContentXml)
3428 {
3429 (bool Found, IRecipient Recipient) = await this.TryGetRecipient(To, From);
3430
3431 if (Found)
3432 {
3433 await Recipient.Message(Type, Id, To, From, Language, ContentXml, this);
3434 return true;
3435 }
3436 else
3437 return false;
3438 }
3439
3440 private async void SecondTimerCallback(object State)
3441 {
3442 try
3443 {
3444 LinkedList<KeyValuePair<DateTime, IS2SEndpoint>> ToRemove = null;
3445 List<PendingRequest> Retries = null;
3446 DateTime Now = DateTime.Now;
3447 DateTime TP;
3448 bool Retry;
3449
3450 lock (this.synchObject)
3451 {
3452 foreach (KeyValuePair<DateTime, PendingRequest> P in this.pendingRequestsByTimeout)
3453 {
3454 if (P.Key <= Now)
3455 {
3456 if (Retries is null)
3457 Retries = new List<PendingRequest>();
3458
3459 Retries.Add(P.Value);
3460 }
3461 else
3462 break;
3463 }
3464
3465 if (!(this.temporaryConnections is null))
3466 {
3467 foreach (KeyValuePair<DateTime, IS2SEndpoint> P in this.temporaryConnections)
3468 {
3469 if (P.Key < Now)
3470 {
3471 if (ToRemove is null)
3472 ToRemove = new LinkedList<KeyValuePair<DateTime, IS2SEndpoint>>();
3473
3474 ToRemove.AddLast(P);
3475 }
3476 else
3477 break;
3478 }
3479
3480 if (!(ToRemove is null))
3481 {
3482 foreach (KeyValuePair<DateTime, IS2SEndpoint> P in ToRemove)
3483 this.temporaryConnections.Remove(P.Key);
3484
3485 if (this.temporaryConnections.Count == 0)
3486 this.temporaryConnections = null;
3487 }
3488 }
3489 }
3490
3491 if (!(Retries is null))
3492 {
3493 foreach (PendingRequest Request in Retries)
3494 {
3495 lock (this.synchObject)
3496 {
3497 this.pendingRequestsByTimeout.Remove(Request.Timeout);
3498
3499 if (Retry = Request.CanRetry())
3500 {
3501 TP = Request.Timeout;
3502
3503 if (this.pendingRequestsByTimeout.ContainsKey(TP))
3504 {
3505 Random Rnd = new Random();
3506
3507 while (this.pendingRequestsByTimeout.ContainsKey(TP))
3508 TP = TP.AddTicks(Rnd.Next(100) + 1);
3509 }
3510
3511 Request.Timeout = TP;
3512
3513 this.pendingRequestsByTimeout[Request.Timeout] = Request;
3514 }
3515 else
3516 this.pendingRequestsById.Remove(Request.Id);
3517 }
3518
3519 try
3520 {
3521 if (Retry)
3522 await Request.ResendCallback.Raise(this, new PendingRequestEventArgs(Request));
3523 else
3524 {
3525 if (!(Request.IqCallback is null))
3526 {
3527 StringBuilder Xml = new StringBuilder();
3528
3529 Xml.Append("<iq xmlns='jabber:server' type='error' from='");
3530 Xml.Append(Request.To);
3531 Xml.Append("' id='");
3532 Xml.Append(Request.Id);
3533 Xml.Append("'><error type='wait'><recipient-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>");
3534 Xml.Append("<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Timeout.</text></error></iq>");
3535
3536 XmlDocument Doc = new XmlDocument()
3537 {
3538 PreserveWhitespace = true
3539 };
3540 Doc.LoadXml(Xml.ToString());
3541
3542 IqResultEventArgs e = new IqResultEventArgs(Doc.DocumentElement, Request.Id, XmppAddress.Empty, Request.To, string.Empty, false,
3543 Request.State);
3544
3545 await Request.IqCallback.Raise(this, e);
3546 }
3547 else if (!(Request.PresenceCallback is null))
3548 {
3549 Stanza Stanza = ToStanza("presence", "error", Request.Id, XmppAddress.Empty, Request.To, string.Empty,
3550 "<error type='wait'><recipient-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3551 "<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Timeout.</text></error>");
3552
3553 PresenceEventArgs e = new PresenceEventArgs(null, "error", Request.Id, XmppAddress.Empty, Request.To, string.Empty,
3554 Stanza, Request.State);
3555
3556 await Request.PresenceCallback.Raise(this, e);
3557 }
3558 }
3559 }
3560 catch (Exception ex)
3561 {
3562 Log.Exception(ex);
3563 }
3564 }
3565 }
3566
3567 if (!(ToRemove is null))
3568 {
3569 foreach (KeyValuePair<DateTime, IS2SEndpoint> P in ToRemove)
3570 await P.Value.DisposeAsync();
3571 }
3572 }
3573 catch (Exception ex)
3574 {
3575 Log.Exception(ex);
3576 }
3577 }
3578
3579 #region ISender
3580
3589 public virtual async Task<string> IqError(string Id, XmppAddress To, XmppAddress From, Exception ex)
3590 {
3591 GetErrorInformation(ex, out _, out string Xml);
3592 Stanza Stanza = ToStanza("iq", "error", Id, To, From, string.Empty, Xml);
3593
3594 await this.ProcessResponse("error", Id, To, From, string.Empty, true, false, Stanza, this);
3595
3596 return Xml;
3597 }
3598
3607 public virtual async Task<bool> IqError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
3608 {
3609 Stanza Stanza = ToStanza("iq", "error", Id, To, From, string.Empty, ErrorXml);
3610
3611 await this.ProcessResponse("error", Id, To, From, string.Empty, true, false, Stanza, this);
3612
3613 return true;
3614 }
3615
3624 public virtual async Task<bool> IqResult(string Id, XmppAddress To, XmppAddress From, string ResultXml)
3625 {
3626 Stanza Stanza = ToStanza("iq", "result", Id, To, From, string.Empty, ResultXml);
3627
3628 await this.ProcessResponse("result", Id, To, From, string.Empty, true, false, Stanza, this);
3629
3630 return true;
3631 }
3632
3641 public virtual async Task<bool> PresenceError(string Id, XmppAddress To, XmppAddress From, Exception ex)
3642 {
3643 GetErrorInformation(ex, out _, out string Xml);
3644 Stanza Stanza = ToStanza("presence", "error", Id, To, From, string.Empty, Xml);
3645
3646 await this.ProcessResponse("error", Id, To, From, string.Empty, false, true, Stanza, this);
3647
3648 return true;
3649 }
3650
3659 public virtual async Task<bool> PresenceError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
3660 {
3661 Stanza Stanza = ToStanza("presence", "error", Id, To, From, string.Empty, ErrorXml);
3662
3663 await this.ProcessResponse("error", Id, To, From, string.Empty, false, true, Stanza, this);
3664
3665 return true;
3666 }
3667
3678 public virtual async Task<bool> Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
3679 {
3680 Stanza Stanza = ToStanza("presence", Type, Id, To, From, string.Empty, ContentXml);
3681
3682 await this.ProcessResponse(Type, Id, To, From, string.Empty, true, false, Stanza, this);
3683
3684 return true;
3685 }
3686
3697 public virtual Task<bool> Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
3698 {
3699 return Task.FromResult(true); // Do nothing by default.
3700 }
3701
3710 public virtual Task<bool> MessageError(string Id, XmppAddress To, XmppAddress From, Exception ex)
3711 {
3712 return Task.FromResult(true); // Do nothing by default.
3713 }
3714
3715 #endregion
3716
3717 #endregion
3718
3719 #region Roster
3720
3721 private async Task RosterQuery(object Sender, IqEventArgs e)
3722 {
3724 {
3725 await e.IqErrorForbidden(e.To, "You can only access your own roster.", "en");
3726 return;
3727 }
3728
3730 IEnumerable<IRosterItem> Roster = await this.persistenceLayer.GetRoster(UserName);
3731 if (Roster is null)
3732 {
3733 await e.IqErrorItemNotFound(e.To, "Roster not found.", "en");
3734 return;
3735 }
3736
3737 string Ver = XML.Attribute(e.Query, "ver");
3738
3739 List<IRosterItem> A = new List<IRosterItem>();
3740 A.AddRange(Roster);
3741 A.Sort((i1, i2) => i1.BareJid.CompareTo(i2.BareJid));
3742
3743 StringBuilder Xml = new StringBuilder();
3744
3745 foreach (IRosterItem Item in A)
3746 this.Serialize(Xml, Item);
3747
3748 string s = Xml.ToString();
3749 string Hash = Hashes.ComputeSHA1HashString(Encoding.UTF8.GetBytes(s));
3750
3751 if (Ver == Hash)
3752 await e.IqResult(string.Empty, e.From.ToBareJID());
3753 else
3754 {
3755 Xml.Clear();
3756 Xml.Append("<query xmlns='");
3757 Xml.Append(RosterNamespace);
3758 Xml.Append("' ver='");
3759 Xml.Append(Hash);
3760 Xml.Append("'>");
3761 Xml.Append(s);
3762 Xml.Append("</query>");
3763
3764 await e.IqResult(Xml.ToString(), e.From.ToBareJID());
3765 }
3766 }
3767
3768 private void Serialize(StringBuilder Xml, IRosterItem Item)
3769 {
3770 Xml.Append("<item jid='");
3771 Xml.Append(XML.Encode(Item.BareJid));
3772
3773 if (Item.PendingSubscription && (Item.Subscription != SubscriptionStatus.both && Item.Subscription != SubscriptionStatus.to))
3774 Xml.Append("' ask='subscribe");
3775
3776 if (!string.IsNullOrEmpty(Item.Name))
3777 {
3778 Xml.Append("' name='");
3779 Xml.Append(XML.Encode(Item.Name));
3780 }
3781
3782 if (Item.Subscription != SubscriptionStatus.none)
3783 {
3784 Xml.Append("' subscription='");
3785 Xml.Append(Item.Subscription.ToString());
3786 }
3787
3788 string[] Groups = Item.Groups;
3789 if (Groups is null || Groups.Length == 0)
3790 Xml.Append("'/>");
3791 else
3792 {
3793 Xml.Append("'>");
3794
3795 foreach (string Group in Groups)
3796 {
3797 Xml.Append("<group>");
3798 Xml.Append(XML.Encode(Group));
3799 Xml.Append("</group>");
3800 }
3801
3802 Xml.Append("</item>");
3803 }
3804 }
3805
3806 private async Task RosterSet(object Sender, IqEventArgs e)
3807 {
3809 {
3810 await e.IqErrorForbidden(e.To, "You can only access your own roster.", "en");
3811 return;
3812 }
3813
3814 XmlElement E;
3815 List<string> Groups = null;
3817 CaseInsensitiveString Jid = null;
3818 string Name = null;
3819 string s;
3820 SubscriptionStatus Subscription = SubscriptionStatus.none;
3822 bool First = true;
3823
3824 foreach (XmlNode N in e.Query.ChildNodes)
3825 {
3826 E = N as XmlElement;
3827 if (E is null)
3828 continue;
3829
3830 if (E.LocalName != "item")
3831 continue;
3832
3833 if (First)
3834 First = false;
3835 else
3836 {
3837 await e.IqErrorBadRequest(e.To, "Multiple items not allowed.", "en");
3838 return;
3839 }
3840
3841 Jid = XML.Attribute(E, "jid");
3842 Name = XML.Attribute(E, "name");
3843 PendingSubscription = XML.Attribute(E, "ask") == "subscribe";
3844 Subscription = XML.Attribute(E, "subscription", SubscriptionStatus.none);
3845
3846 if (Name.Length > MaxNameLength)
3847 {
3848 await e.IqErrorNotAcceptable(e.To, "Name too long.", "en");
3849 return;
3850 }
3851
3852 foreach (XmlNode N2 in N.ChildNodes)
3853 {
3854 E = N2 as XmlElement;
3855 if (E is null)
3856 continue;
3857
3858 if (E.LocalName != "group")
3859 continue;
3860
3861 if (Groups is null)
3862 Groups = new List<string>();
3863
3864 s = E.InnerText;
3865 if (string.IsNullOrEmpty(s) || s.Length > MaxGroupLength)
3866 {
3867 await e.IqErrorNotAcceptable(e.To, "Group name too long.", "en");
3868 return;
3869 }
3870 else if (Groups.Contains(s))
3871 {
3872 await e.IqErrorBadRequest(e.To, "Group name occurred twice: " + s, "en");
3873 return;
3874 }
3875
3876 Groups.Add(s);
3877 }
3878 }
3879
3881 {
3882 await e.IqErrorBadRequest(e.To, "JID not specificed.", "en");
3883 return;
3884 }
3885
3886 IRosterItem Item = await this.persistenceLayer.SetRosterItem(UserName, Jid, Name, null, null, Groups?.ToArray());
3887 if (!(Item is null))
3888 {
3889 if (Subscription == SubscriptionStatus.remove)
3890 {
3891 if (await this.persistenceLayer.RemoveRosterItem(UserName, Jid))
3892 {
3893 if (Item.Subscription == SubscriptionStatus.both || Item.Subscription == SubscriptionStatus.from)
3894 await this.PushPresence(new XmppAddress(Item.BareJid), "unavailable", string.Empty, e.From, string.Empty, (Stanza)null, true, this);
3895
3896 if (Item.Subscription == SubscriptionStatus.both || Item.Subscription == SubscriptionStatus.to)
3897 {
3898 await this.PushPresence(new XmppAddress(Item.BareJid), "unsubscribe", string.Empty, e.From.ToBareJID(), string.Empty, (Stanza)null, true, this);
3899 await this.PushPresence(e.From.ToBareJID(), "unsubscribed", string.Empty, new XmppAddress(Item.BareJid), string.Empty, (Stanza)null, true, this);
3900 }
3901
3902 if (Item.Subscription == SubscriptionStatus.both || Item.Subscription == SubscriptionStatus.from)
3903 {
3904 await this.PushPresence(e.From.ToBareJID(), "unsubscribe", string.Empty, new XmppAddress(Item.BareJid), string.Empty, (Stanza)null, true, this);
3905 await this.PushPresence(new XmppAddress(Item.BareJid), "unsubscribed", string.Empty, e.From.ToBareJID(), string.Empty, (Stanza)null, true, this);
3906 }
3907
3908 await e.IqResult(string.Empty, e.To);
3909
3910 StringBuilder Xml = new StringBuilder();
3911
3912 Xml.Append("<query xmlns='");
3913 Xml.Append(RosterNamespace);
3914 Xml.Append("'><item jid='");
3915 Xml.Append(Jid);
3916 Xml.Append("' subscription='remove'></item></query>");
3917
3918 await this.PushIq("set", false, XmppAddress.Empty, string.Empty, Xml.ToString(), this.GetClientConnections(e.From.BareJid));
3919 }
3920 else
3921 await e.IqErrorItemNotFound(e.To, string.Empty, string.Empty);
3922 }
3923 else
3924 {
3925 await e.IqResult(string.Empty, e.To);
3926 await this.PushIq(ClientConnection.BareJid, Item);
3927 }
3928 }
3929 else
3930 await e.IqErrorItemNotFound(e.To, string.Empty, string.Empty);
3931 }
3932
3933 private Task PushIq(CaseInsensitiveString BareJid, IRosterItem Item)
3934 {
3935 StringBuilder Xml = new StringBuilder();
3936
3937 Xml.Append("<query xmlns='");
3938 Xml.Append(RosterNamespace);
3939 Xml.Append("'>");
3940
3941 this.Serialize(Xml, Item);
3942
3943 Xml.Append("</query>");
3944
3945 return this.PushIq("set", false, XmppAddress.Empty, string.Empty, Xml.ToString(), this.GetClientConnections(BareJid));
3946 }
3947
3948 private async Task PushIq(string Type, bool IncludeTo, XmppAddress From, string Language, string Xml, IClientConnection[] Connections)
3949 {
3950 if (!(Connections is null))
3951 {
3952 foreach (IClientConnection Connection in Connections)
3953 {
3954 if (Connection.State == XmppConnectionState.Active)
3955 {
3956 try
3957 {
3958 await Connection.IQ(Type, this.NewId(16), IncludeTo ? Connection.Address : XmppAddress.Empty, From, Language, Xml, this);
3959 }
3960 catch (Exception ex)
3961 {
3962 try
3963 {
3964 await Connection.Exception(ex);
3965 await Connection.DisposeAsync();
3966 }
3967 catch (Exception ex2)
3968 {
3969 Log.Exception(ex2);
3970 }
3971 }
3972 }
3973 }
3974 }
3975 }
3976
3977 private async Task PushPresence(string Type, string Id, XmppAddress From, string Language, string Xml, IClientConnection[] Connections,
3978 bool UseBareJids)
3979 {
3980 if (!(Connections is null))
3981 {
3982 foreach (IClientConnection Connection in Connections)
3983 {
3984 if (Connection.State == XmppConnectionState.Active)
3985 {
3986 try
3987 {
3988 await Connection.Presence(Type, string.IsNullOrEmpty(Id) ? this.NewId(16) : Id,
3989 UseBareJids ? Connection.BareAddress : Connection.Address, From, Language, Xml, this);
3990 }
3991 catch (Exception ex)
3992 {
3993 try
3994 {
3995 await Connection.Exception(ex);
3996 await Connection.DisposeAsync();
3997 }
3998 catch (Exception ex2)
3999 {
4000 Log.Exception(ex2);
4001 }
4002 }
4003 }
4004 }
4005 }
4006 }
4007
4008 #endregion
4009
4010 #region Ping XEP-0199
4011
4012 private Task PingGet(object Sender, IqEventArgs e)
4013 {
4014 e.IqResult(string.Empty, e.To);
4015
4016 return Task.CompletedTask;
4017 }
4018
4019 #endregion
4020
4021 #region Discovery XEP-0030
4022
4023 private Task DiscoveryQueryGet(object Sender, IqEventArgs e)
4024 {
4025 XmlElement E = e.Query;
4026 string Node = XML.Attribute(E, "node");
4027 if (!string.IsNullOrEmpty(Node))
4028 {
4029 e.IqErrorItemNotFound(e.To, "Node not found.", "en");
4030 return Task.CompletedTask;
4031 }
4032
4033 StringBuilder Xml = new StringBuilder();
4034
4035 Xml.Append("<query xmlns='");
4036 Xml.Append(DiscoveryNamespace);
4037 Xml.Append("'><identity category='server' type='im'/>"); // https://xmpp.org/registrar/disco-categories.html
4038
4039 lock (this.synchObject)
4040 {
4041 foreach (string Feature in this.features.Keys)
4042 {
4043 Xml.Append("<feature var='");
4044 Xml.Append(XML.Encode(Feature));
4045 Xml.Append("'/>");
4046 }
4047 }
4048
4049 Xml.Append("</query>");
4050
4051 e.IqResult(Xml.ToString(), e.To);
4052
4053 return Task.CompletedTask;
4054 }
4055
4056 private Task DiscoveryQueryItemsGet(object Sender, IqEventArgs e)
4057 {
4058 XmlElement E = e.Query;
4059 string Node = XML.Attribute(E, "node");
4060 if (!string.IsNullOrEmpty(Node))
4061 {
4062 e.IqErrorItemNotFound(e.To, "Node not found.", "en");
4063 return Task.CompletedTask;
4064 }
4065
4066 StringBuilder Xml = new StringBuilder();
4067
4068 Xml.Append("<query xmlns='");
4069 Xml.Append(DiscoveryItemsNamespace);
4070 Xml.Append("'>");
4071
4072 foreach (IComponent Component in this.componentsStatic)
4073 {
4074 Xml.Append("<item jid='");
4075 Xml.Append(XML.Encode(Component.Subdomain));
4076 Xml.Append('.');
4077 Xml.Append(XML.Encode(e.To.IsEmpty ? e.From.Domain : e.To.Domain));
4078 Xml.Append("' name='");
4079 Xml.Append(XML.Encode(Component.Name));
4080 Xml.Append("'/>");
4081 }
4082
4083 Xml.Append("</query>");
4084
4085 e.IqResult(Xml.ToString(), e.To);
4086
4087 return Task.CompletedTask;
4088 }
4089
4090 #endregion
4091
4092 #region Software Version (XEP-0092)
4093
4094 private Task SoftwareVersionGet(object Sender, IqEventArgs e)
4095 {
4096 StringBuilder Xml = new StringBuilder();
4097
4098 Xml.Append("<query xmlns='");
4099 Xml.Append(SoftwareVersionNamespace);
4100 Xml.Append("'><name>");
4101 Xml.Append(XML.Encode(this.serverName));
4102 Xml.Append("</name><version>");
4103 Xml.Append(XML.Encode(this.serverVersion));
4104 Xml.Append("</version><os>");
4105 Xml.Append(XML.Encode(this.serverOS));
4106 Xml.Append("</os></query>");
4107
4108 e.IqResult(Xml.ToString(), e.To);
4109
4110 return Task.CompletedTask;
4111 }
4112
4113 #endregion
4114
4115 #region Entity Time (XEP-0202)
4116
4117 private Task TimeGet(object Sender, IqEventArgs e)
4118 {
4119 StringBuilder Xml = new StringBuilder();
4120 DateTimeOffset Time = DateTimeOffset.Now;
4121 TimeSpan TimeZone = Time.Offset;
4122 DateTime Utc = Time.UtcDateTime;
4123
4124 Xml.Append("<time xmlns='");
4125 Xml.Append(TimeNamespace);
4126 Xml.Append("'><tzo>");
4127
4128 if (TimeZone == TimeSpan.Zero)
4129 Xml.Append("Z");
4130 else
4131 {
4132 if (TimeZone < TimeSpan.Zero)
4133 {
4134 Xml.Append('-');
4135 TimeZone = -TimeZone;
4136 }
4137 else
4138 Xml.Append('+');
4139
4140 Xml.Append(TimeZone.Hours.ToString("D2"));
4141 Xml.Append(':');
4142 Xml.Append(TimeZone.Minutes.ToString("D2"));
4143 }
4144
4145 Xml.Append("</tzo><utc>");
4146 Xml.Append(XML.Encode(Utc));
4147 Xml.Append("</utc></time>");
4148
4149 e.IqResult(Xml.ToString(), e.To);
4150
4151 return Task.CompletedTask;
4152 }
4153
4154 #endregion
4155
4156 #region vCard (XEP-0054)
4157
4158 private async Task VCardGet(object Sender, IqEventArgs e)
4159 {
4160 if (!e.From.HasAccount || !this.IsServerDomain(e.From.Domain, true))
4161 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
4162 else
4163 {
4164 string s = await this.persistenceLayer.GetVCard(e.From.Account);
4165
4166 if (string.IsNullOrEmpty(s))
4167 await e.IqErrorItemNotFound(e.To, "vCard not found.", "en");
4168 else
4169 await e.IqResult("<vCard xmlns='" + VCardNamespace + "'>" + s + "</vCard>", e.To);
4170 }
4171 }
4172
4173 private async Task VCardSet(object Sender, IqEventArgs e)
4174 {
4175 if (!e.From.HasAccount || !this.IsServerDomain(e.From.Domain, true))
4176 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
4177 else
4178 {
4179 CaseInsensitiveString UserName = e.From.Account;
4180 string VCard = e.Query.InnerXml;
4181
4182 if (!await this.persistenceLayer.SetVCard(UserName, VCard))
4183 await e.IqErrorItemNotFound(e.To, "Unable to set vCard.", "en");
4184 else
4185 await e.IqResult(string.Empty, e.To);
4186 }
4187 }
4188
4189 #endregion
4190
4191 #region Register (XEP-0077) & Form signatures (XEP-0348)
4192
4193 private const string RegistrationInstructions = "Register your new account, by filling in the details below.";
4194
4195 internal async Task<bool> CanRegister(IClientConnection Connection)
4196 {
4197 DateTime? Next = await this.GetEarliestLoginOpportunity(Connection);
4198 return !Next.HasValue;
4199 }
4200
4201 internal async Task<bool> CanRegister(IClientConnection Connection, IqEventArgs e)
4202 {
4203 DateTime? Next = await this.GetEarliestLoginOpportunity(Connection);
4204
4205 if (Next.HasValue)
4206 {
4207 StringBuilder sb = new StringBuilder();
4208 DateTime TP = Next.Value;
4209 DateTime Today = DateTime.Today;
4210
4211 if (Next.Value == DateTime.MaxValue)
4212 {
4213 sb.Append("This endpoint (");
4214 sb.Append(Connection.RemoteEndpoint);
4215 sb.Append(") has been blocked from the system.");
4216
4217 await e.IqErrorForbidden(e.To, sb.ToString(), "en");
4218 return false;
4219 }
4220 else
4221 {
4222 sb.Append("Too many failed login attempts in a row registered. Try again after ");
4223 sb.Append(TP.ToLongTimeString());
4224
4225 if (TP.Date != Today)
4226 {
4227 if (TP.Date == Today.AddDays(1))
4228 sb.Append(" tomorrow");
4229 else
4230 {
4231 sb.Append(", ");
4232 sb.Append(TP.ToShortDateString());
4233 }
4234 }
4235
4236 sb.Append(". Remote Endpoint: ");
4237 sb.Append(Connection.RemoteEndpoint);
4238
4239 await e.IqErrorNotAllowed(e.To, sb.ToString(), "en");
4240 return false;
4241 }
4242 }
4243
4244 return true;
4245 }
4246
4247 private async Task RegisterGet(object Sender, IqEventArgs e)
4248 {
4249 if (!(e.Sender is IClientConnection Connection))
4250 {
4251 await e.IqErrorForbidden(e.To, "Only clients can register.", "en");
4252 return;
4253 }
4254
4255 if (!await this.CanRegister(Connection, e))
4256 return;
4257
4258 StringBuilder Xml = new StringBuilder();
4259 byte[] Token = GetRandomNumbers(32);
4260 byte[] Secret = GetRandomNumbers(32);
4261
4262 Xml.Append("<query xmlns='");
4263 Xml.Append(RegisterNamespace);
4264 Xml.Append("'><instructions>");
4265 Xml.Append(RegistrationInstructions);
4266 Xml.Append("</instructions>");
4267 Xml.Append("<x xmlns='jabber:x:data' type='form'>");
4268 Xml.Append("<title>Contest Registration</title>");
4269 Xml.Append("<instructions>");
4270 Xml.Append(RegistrationInstructions);
4271 Xml.Append("</instructions>");
4272 Xml.Append("<field type='hidden' var='FORM_TYPE'>");
4273 Xml.Append("<value>urn:xmpp:xdata:signature:oauth1</value>");
4274 Xml.Append("</field>");
4275 Xml.Append("<field type='text-single' label='User Name:' var='username'>");
4276 Xml.Append("<required/>");
4277 Xml.Append("</field>");
4278 Xml.Append("<field type='text-private' label='Password:' var='password'>");
4279 Xml.Append("<required/>");
4280 Xml.Append("</field>");
4281 Xml.Append("<field type='text-single' label='Email Address' var='email'/>");
4282 Xml.Append("<field type='text-single' label='Phone Number' var='phone'/>");
4283 Xml.Append("<field type='hidden' var='oauth_version'>");
4284 Xml.Append("<value>1.0</value>");
4285 Xml.Append("</field>");
4286 Xml.Append("<field type='hidden' var='oauth_signature_method'>");
4287 Xml.Append("<value>HMAC-SHA1</value>");
4288 Xml.Append("</field>");
4289 Xml.Append("<field type='hidden' var='oauth_token'>");
4290 Xml.Append("<value>");
4291 Xml.Append(Hashes.BinaryToString(Token));
4292 Xml.Append("</value>");
4293 Xml.Append("</field>");
4294 Xml.Append("<field type='hidden' var='oauth_token_secret'>");
4295 Xml.Append("<value>");
4296 Xml.Append(Hashes.BinaryToString(Secret));
4297 Xml.Append("</value>");
4298 Xml.Append("</field>");
4299 Xml.Append("<field type='hidden' var='oauth_nonce'>");
4300 Xml.Append("<value/>");
4301 Xml.Append("</field>");
4302 Xml.Append("<field type='hidden' var='oauth_timestamp'>");
4303 Xml.Append("<value/>");
4304 Xml.Append("</field>");
4305 Xml.Append("<field type='hidden' var='oauth_consumer_key'>");
4306 Xml.Append("<value/>");
4307 Xml.Append("</field>");
4308 Xml.Append("<field type='hidden' var='oauth_signature'>");
4309 Xml.Append("<value/>");
4310 Xml.Append("</field>");
4311 Xml.Append("</x>");
4312 Xml.Append("</query>");
4313
4314 await e.IqResult(Xml.ToString(), e.To);
4315 }
4316
4317 private async Task RegisterSet(object Sender, IqEventArgs e)
4318 {
4319 if (!(e.Sender is IClientConnection Connection))
4320 {
4321 await e.IqErrorForbidden(e.To, "Only clients can register.", "en");
4322 return;
4323 }
4324
4325 if (!await this.CanRegister(Connection, e))
4326 return;
4327
4328 CaseInsensitiveString UserName = null;
4329 string Password = null;
4330 bool Remove = false;
4331 int Count = 0;
4332
4333 foreach (XmlNode N in e.Query.ChildNodes)
4334 {
4335 if (!(N is XmlElement E))
4336 continue;
4337
4338 Count++;
4339 if (E.NamespaceURI == RegisterNamespace)
4340 {
4341 switch (E.LocalName)
4342 {
4343 case "username":
4344 UserName = E.InnerText;
4345 break;
4346
4347 case "password":
4348 Password = E.InnerText;
4349 break;
4350
4351 case "remove":
4352 Remove = true;
4353 break;
4354 }
4355 }
4356 else if (E.LocalName == "x" && E.NamespaceURI == DataFormsNamespace && XML.Attribute((XmlElement)N, "type") == "submit")
4357 {
4358 if (Connection.State != XmppConnectionState.Authenticating)
4359 {
4360 await e.IqErrorNotAllowed(e.To, "Step only allowed during authentication phase.", "en");
4361 return;
4362 }
4363
4364 string FormType = null;
4365 CaseInsensitiveString EMail = null;
4366 CaseInsensitiveString PhoneNr = null;
4367 string OAuthVersion = null;
4368 string OAuthSignatureMethod = null;
4369 string OAuthToken = null;
4370 string OAuthTokenSecret = null;
4371 string OAuthNonce = null;
4372 string OAuthTimestamp = null;
4373 string OAuthConsumerKey = null;
4374 string OAuthSignature = null;
4375
4376 foreach (XmlNode N2 in E.ChildNodes)
4377 {
4378 if (N2.LocalName == "field")
4379 {
4380 string Var = XML.Attribute((XmlElement)N2, "var");
4381 string Value = null;
4382
4383 foreach (XmlNode N3 in N2.ChildNodes)
4384 {
4385 if (N3.LocalName == "value")
4386 {
4387 Value = N3.InnerText;
4388 break;
4389 }
4390 }
4391
4392 if (Value is null)
4393 continue;
4394
4395 switch (Var)
4396 {
4397 case "FORM_TYPE":
4398 FormType = Value;
4399 break;
4400
4401 case "username":
4402 UserName = Value;
4403 break;
4404
4405 case "password":
4406 Password = Value;
4407 break;
4408
4409 case "email":
4410 EMail = Value;
4411 break;
4412
4413 case "phone":
4414 PhoneNr = Value;
4415 break;
4416
4417 case "oauth_version":
4418 OAuthVersion = Value;
4419 break;
4420
4421 case "oauth_signature_method":
4422 OAuthSignatureMethod = Value;
4423 break;
4424
4425 case "oauth_token":
4426 OAuthToken = Value;
4427 break;
4428
4429 case "oauth_token_secret":
4430 OAuthTokenSecret = Value;
4431 break;
4432
4433 case "oauth_nonce":
4434 OAuthNonce = Value;
4435 break;
4436
4437 case "oauth_timestamp":
4438 OAuthTimestamp = Value;
4439 break;
4440
4441 case "oauth_consumer_key":
4442 OAuthConsumerKey = Value;
4443 break;
4444
4445 case "oauth_signature":
4446 OAuthSignature = Value;
4447 break;
4448
4449 default:
4450 break;
4451 }
4452 }
4453 }
4454
4455 bool Signed = false;
4456 bool Logged = false;
4457
4458 if (FormType == "urn:xmpp:xdata:signature:oauth1" && OAuthVersion == "1.0" && !string.IsNullOrEmpty(UserName) &&
4459 !string.IsNullOrEmpty(Password) && !(EMail is null) && !(PhoneNr is null) && !string.IsNullOrEmpty(OAuthSignatureMethod) &&
4460 !string.IsNullOrEmpty(OAuthToken) && !string.IsNullOrEmpty(OAuthTokenSecret) && !string.IsNullOrEmpty(OAuthNonce) &&
4461 !string.IsNullOrEmpty(OAuthTimestamp) && !string.IsNullOrEmpty(OAuthConsumerKey) && !string.IsNullOrEmpty(OAuthSignature))
4462 {
4463 string KeySecret = await this.persistenceLayer.GetApiKeySecret(OAuthConsumerKey);
4464 if (!string.IsNullOrEmpty(KeySecret))
4465 {
4466 StringBuilder PStr = new StringBuilder();
4467
4468 PStr.Append("email=");
4469 PStr.Append(OAuthEncode(EMail));
4470 PStr.Append("&FORM_TYPE=");
4471 PStr.Append(OAuthEncode(FormType));
4472 PStr.Append("&oauth_consumer_key=");
4473 PStr.Append(OAuthEncode(OAuthConsumerKey));
4474 PStr.Append("&oauth_nonce=");
4475 PStr.Append(OAuthEncode(OAuthNonce));
4476 PStr.Append("&oauth_signature_method=");
4477 PStr.Append(OAuthEncode(OAuthSignatureMethod));
4478 PStr.Append("&oauth_timestamp=");
4479 PStr.Append(OAuthEncode(OAuthTimestamp));
4480 PStr.Append("&oauth_token=");
4481 PStr.Append(OAuthEncode(OAuthToken));
4482 PStr.Append("&oauth_version=");
4483 PStr.Append(OAuthEncode(OAuthVersion));
4484 PStr.Append("&password=");
4485 PStr.Append(OAuthEncode(Password));
4486 PStr.Append("&phone=");
4487 PStr.Append(OAuthEncode(PhoneNr));
4488 PStr.Append("&username=");
4489 PStr.Append(OAuthEncode(UserName));
4490
4491 StringBuilder BStr = new StringBuilder();
4492
4493 BStr.Append("submit&&"); // No to-field.
4494 BStr.Append(OAuthEncode(PStr.ToString()));
4495
4496 byte[] Key = Encoding.ASCII.GetBytes(OAuthEncode(KeySecret) + "&" + OAuthEncode(OAuthTokenSecret));
4497 byte[] Hash = null;
4498
4499 switch (OAuthSignatureMethod)
4500 {
4501 case "HMAC-SHA1":
4502 Hash = Hashes.ComputeHMACSHA1Hash(Key, Encoding.ASCII.GetBytes(BStr.ToString()));
4503 break;
4504
4505 default:
4506 Logged = true;
4507 LoginAuditor.Fail("Registration form signature failed. Unhandled signature method requested.", UserName, Connection.RemoteEndpoint, Connection.Protocol,
4508 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
4509 new KeyValuePair<string, object>("Method", OAuthSignatureMethod),
4510 new KeyValuePair<string, object>("EMail", EMail?.Value),
4511 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
4512 break;
4513 }
4514
4515 if (!(Hash is null))
4516 {
4517 string Signature = OAuthEncode(Convert.ToBase64String(Hash));
4518 Signed = Signature == OAuthSignature;
4519
4520 if (!Signed)
4521 {
4522 Logged = true;
4523 LoginAuditor.Fail("Registration form signature failed.", UserName, Connection.RemoteEndpoint, Connection.Protocol,
4524 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
4525 new KeyValuePair<string, object>("Method", OAuthSignatureMethod),
4526 new KeyValuePair<string, object>("EMail", EMail?.Value),
4527 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
4528 }
4529 }
4530 }
4531 else
4532 {
4533 Logged = true;
4534 LoginAuditor.Fail("Registration form signature failed. Invalid API key used.", UserName, Connection.RemoteEndpoint, Connection.Protocol,
4535 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
4536 new KeyValuePair<string, object>("EMail", EMail?.Value),
4537 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
4538 }
4539 }
4540 else
4541 {
4542 Logged = true;
4543 LoginAuditor.Fail("Registration form signature failed. Signature parameters not provided.", UserName, Connection.RemoteEndpoint, Connection.Protocol);
4544 }
4545
4546 if (Signed)
4547 {
4548 if (string.IsNullOrEmpty(UserName))
4549 await e.IqErrorBadRequest(e.To, "User name cannot be empty.", "en");
4550
4551 if (UserName.Length > 1023)
4552 await e.IqErrorBadRequest(e.To, "User name too long.", "en");
4553
4554 foreach (char ch in UserName.ToCharArray())
4555 {
4556 if (char.IsWhiteSpace(ch))
4557 {
4558 await e.IqErrorNotAllowed(e.To, "White-space characters not allowed in user names.", "en");
4559 return;
4560 }
4561 else if (ch == '@')
4562 {
4563 await e.IqErrorNotAllowed(e.To, "@ characters not allowed in user names.", "en");
4564 return;
4565 }
4566 }
4567
4568 if (!IsValidUserName(UserName))
4569 {
4570 await e.IqErrorBadRequest(e.To, "User Name contains prohibited characters.", "en");
4571 return;
4572 }
4573
4574 KeyValuePair<IAccount, string[]> P = await this.persistenceLayer.CreateAccount(OAuthConsumerKey, UserName, Password, EMail, PhoneNr, Connection.RemoteEndpoint);
4575 IAccount Account = P.Key;
4576 if (Account is null)
4577 {
4578 string[] Alternatives = P.Value;
4579 StringBuilder Xml = new StringBuilder();
4580
4581 Xml.Append("<conflict xmlns='");
4582 Xml.Append(StanzaNamespace);
4583 Xml.Append("'/>");
4584
4585 if (!(Alternatives is null) && Alternatives.Length > 0)
4586 {
4587 Xml.Append("<alternatives xmlns='");
4588 Xml.Append(AlternativesNamespace);
4589 Xml.Append("'>");
4590
4591 foreach (string Alternative in Alternatives)
4592 {
4593 Xml.Append("<alternative>");
4594 Xml.Append(XML.Encode(Alternative));
4595 Xml.Append("</alternative>");
4596 }
4597
4598 Xml.Append("</alternatives>");
4599 }
4600
4601 await e.Sender.IqError(e.Id, e.From, e.To, "cancel", Xml.ToString(),
4602 "Account name already exists, or API key limit reached.", "en");
4603
4604 Logged = true;
4605 LoginAuditor.Fail("Registration failed. Signature OK, but account already exists, or API key limit reached.", UserName, Connection.RemoteEndpoint, Connection.Protocol,
4606 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
4607 new KeyValuePair<string, object>("Method", OAuthSignatureMethod),
4608 new KeyValuePair<string, object>("EMail", EMail?.Value),
4609 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
4610 }
4611 else
4612 {
4613 await e.IqResult(string.Empty, e.To);
4614
4615 Logged = true;
4616 LoginAuditor.Success("Registration successful and account created.", UserName, Connection.RemoteEndpoint, Connection.Protocol,
4617 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
4618 new KeyValuePair<string, object>("Method", OAuthSignatureMethod),
4619 new KeyValuePair<string, object>("EMail", EMail?.Value),
4620 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
4621 }
4622 }
4623 else
4624 {
4625 if (!Logged)
4626 {
4627 LoginAuditor.Fail("Registration form signature failed.", UserName, Connection.RemoteEndpoint, Connection.Protocol,
4628 new KeyValuePair<string, object>("ApiKey", OAuthConsumerKey),
4629 new KeyValuePair<string, object>("Method", OAuthSignatureMethod),
4630 new KeyValuePair<string, object>("EMail", EMail?.Value),
4631 new KeyValuePair<string, object>("PhoneNr", PhoneNr?.Value));
4632 }
4633
4634 await e.IqErrorBadRequest(e.To, "Form signature incorrect.", "en");
4635 }
4636
4637 return;
4638 }
4639 }
4640
4641 if (Remove)
4642 {
4643 if (Count > 1)
4644 await e.IqErrorBadRequest(e.To, "Remove element must be only child element.", "en");
4645 else if (Connection.State != XmppConnectionState.Active)
4646 await e.IqErrorForbidden(e.To, "Only allowed to remove your own account, while account is active.", "en");
4647 else if (!this.IsServerDomain(e.From.Domain, true))
4648 {
4649 await e.IqError("auth", "<registration-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>", e.To,
4650 "Only accounts registered on the broker can remove their accounts.", "en");
4651 }
4652 else if (await this.persistenceLayer.DeleteAccount(e.From.Account, Connection.RemoteEndpoint))
4653 {
4654 await e.IqResult(string.Empty, e.To);
4655 Connection.AccountDeleted();
4656 }
4657 else
4658 {
4659 await e.IqError("auth", "<registration-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>", e.To,
4660 "Only accounts registered on the broker can remove their accounts.", "en");
4661 }
4662
4663 return;
4664 }
4665
4666 if (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password))
4667 {
4668 if (Connection.State != XmppConnectionState.Active || Connection.UserName != UserName ||
4669 !await this.persistenceLayer.ChangePassword(UserName, Password))
4670 {
4671 await e.IqErrorNotAllowed(e.To, "Password change not allowed.", "en");
4672 }
4673 else
4674 await e.IqResult(string.Empty, e.To);
4675 }
4676 else
4677 await e.IqErrorBadRequest(e.To, "Empty user names or passwords not allowed.", "en");
4678 }
4679
4685 public static bool IsValidUserName(string UserName)
4686 {
4687 foreach (char ch in UserName)
4688 {
4689 switch (ch)
4690 {
4691 // From XMPP spec (RFC 6122):
4692 case '"':
4693 case '&':
4694 case '\'':
4695 case '/':
4696 case ':':
4697 case '<':
4698 case '>':
4699 case '@':
4700
4701 // Disallow space
4702 case ' ':
4703
4704 // Invalid as file name characters (for file sniffers, etc.)
4705 //case '"':
4706 //case '<':
4707 //case '>':
4708 case '|':
4709 case '\0':
4710 case '\u0001':
4711 case '\u0002':
4712 case '\u0003':
4713 case '\u0004':
4714 case '\u0005':
4715 case '\u0006':
4716 case '\a':
4717 case '\b':
4718 case '\t':
4719 case '\n':
4720 case '\v':
4721 case '\f':
4722 case '\r':
4723 case '\u000e':
4724 case '\u000f':
4725 case '\u0010':
4726 case '\u0011':
4727 case '\u0012':
4728 case '\u0013':
4729 case '\u0014':
4730 case '\u0015':
4731 case '\u0016':
4732 case '\u0017':
4733 case '\u0018':
4734 case '\u0019':
4735 case '\u001a':
4736 case '\u001b':
4737 case '\u001c':
4738 case '\u001d':
4739 case '\u001e':
4740 case '\u001f':
4741 //case ':':
4742 case '*':
4743 case '?':
4744 case '\\':
4745 //case '/':
4746 return false;
4747 }
4748 }
4749
4750 return true;
4751 }
4752
4753 private static string OAuthEncode(string s)
4754 {
4755 StringBuilder Result = new StringBuilder();
4756
4757 foreach (char ch in s)
4758 {
4759 if (OAuthReserved.IndexOf(ch) < 0)
4760 {
4761 Result.Append("%");
4762 Result.Append(((int)ch).ToString("X2"));
4763 }
4764 else
4765 Result.Append(ch);
4766 }
4767
4768 return Result.ToString();
4769 }
4770
4771 private const string OAuthReserved = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
4772
4773 #endregion
4774
4775 #region Blocking Command (XEP-0191) and Spam Reporting (XEP-0377).
4776
4777 private async Task BlockListGet(object Sender, IqEventArgs e)
4778 {
4779 if (!(e.Sender is IClientConnection Connection) || e.From.BareJid != Connection.BareJid)
4780 {
4781 await e.IqErrorForbidden(e.To, "Access to block list can only be granted from the corresponding client.", "en");
4782 return;
4783 }
4784
4785 IEnumerable<CaseInsensitiveString> BlockList = await this.persistenceLayer.GetBlockList(Connection.UserName);
4786 if (BlockList is null)
4787 {
4788 await e.IqErrorItemNotFound(e.To, "List not found.", "en");
4789 return;
4790 }
4791
4792 Connection.WantsBlockList = true;
4793
4794 StringBuilder Xml = new StringBuilder();
4795
4796 Xml.Append("<blocklist xmlns='");
4797 Xml.Append(BlockingCommandNamespace);
4798 Xml.Append("'>");
4799
4800 foreach (CaseInsensitiveString Jid in BlockList)
4801 {
4802 Xml.Append("<item jid='");
4803 Xml.Append(Jid);
4804 Xml.Append("'/>");
4805 }
4806
4807 Xml.Append("</blocklist>");
4808
4809 await e.IqResult(Xml.ToString(), e.To);
4810 }
4811
4812 private async Task BlockSet(object Sender, IqEventArgs e)
4813 {
4814 if (!(e.Sender is IClientConnection Connection) || e.From.BareJid != Connection.BareJid)
4815 {
4816 await e.IqErrorForbidden(e.To, "Access to block list can only be granted from the corresponding client.", "en");
4817 return;
4818 }
4819
4821 List<CaseInsensitiveString> BareJids = null;
4822 string Text = string.Empty;
4823 string TextLanguage = string.Empty;
4824 CaseInsensitiveString BareJid;
4825
4826 foreach (XmlNode N in e.Query.ChildNodes)
4827 {
4828 switch (N.LocalName)
4829 {
4830 case "item":
4831 BareJid = XML.Attribute((XmlElement)N, "jid");
4833 {
4834 await e.IqErrorBadRequest(e.To, "Empty JID.", "en");
4835 return;
4836 }
4837
4838 if (BareJids is null)
4839 BareJids = new List<CaseInsensitiveString>();
4840
4841 BareJids.Add(BareJid);
4842 break;
4843
4844 case "report":
4845 foreach (XmlNode N2 in N.ChildNodes)
4846 {
4847 switch (N2.LocalName)
4848 {
4849 case "text":
4850 XmlElement E = (XmlElement)N2;
4851 Text = E.InnerText;
4852 TextLanguage = XML.Attribute(E, "xml:lang");
4853 break;
4854
4855 case "spam":
4856 Reason = BlockingReason.Spam;
4857 break;
4858
4859 case "abuse":
4860 Reason = BlockingReason.Abuse;
4861 break;
4862 }
4863 }
4864 break;
4865 }
4866 }
4867
4868 if (BareJids is null)
4869 {
4870 await e.IqErrorBadRequest(e.To, "Empty list.", "en");
4871 return;
4872 }
4873
4874 StringBuilder Xml = null;
4875
4876 foreach (CaseInsensitiveString BareJid2 in BareJids)
4877 {
4878 if (await this.persistenceLayer.AddBlock(Connection.UserName, BareJid2, Reason, Text, TextLanguage))
4879 {
4880 IClientConnection[] Connections = this.GetClientConnections(Connection.BareJid);
4881
4882 if (!(Connections is null))
4883 {
4884 foreach (IClientConnection Connection2 in Connections)
4885 {
4886 if (Connection2 != Connection && Connection2.WantsBlockList)
4887 {
4888 if (Xml is null)
4889 Xml = new StringBuilder();
4890 else
4891 Xml.Clear();
4892
4893 Xml.Append("<block xmlns='");
4894 Xml.Append(BlockingCommandNamespace);
4895 Xml.Append("'><item jid='");
4896 Xml.Append(BareJid2);
4897 Xml.Append("'/></block>");
4898
4899 try
4900 {
4901 await Connection2.IQ("set", string.Empty, Connection2.Address, e.To, string.Empty, Xml.ToString(), this);
4902 }
4903 catch (Exception ex)
4904 {
4905 await Connection.Exception(ex);
4906 await Connection.DisposeAsync();
4907 }
4908 }
4909 }
4910 }
4911 }
4912 }
4913
4914 await e.IqResult(string.Empty, e.To);
4915 }
4916
4917 private async Task UnblockSet(object Sender, IqEventArgs e)
4918 {
4919 if (!(e.Sender is IClientConnection Connection) || e.From.BareJid != Connection.BareJid)
4920 {
4921 await e.IqErrorForbidden(e.To, "Access to block list can only be granted from the corresponding client.", "en");
4922 return;
4923 }
4924
4925 List<CaseInsensitiveString> BareJids = null;
4926 CaseInsensitiveString BareJid;
4927
4928 foreach (XmlNode N in e.Query.ChildNodes)
4929 {
4930 if (N.LocalName == "item")
4931 {
4932 BareJid = XML.Attribute((XmlElement)N, "jid");
4934 {
4935 await e.IqErrorBadRequest(e.To, "Empty JID.", "en");
4936 return;
4937 }
4938
4939 if (BareJids is null)
4940 BareJids = new List<CaseInsensitiveString>();
4941
4942 BareJids.Add(BareJid);
4943 }
4944 }
4945
4946 if (BareJids is null)
4947 {
4948 if (await this.persistenceLayer.ClearBlocks(Connection.UserName))
4949 {
4950 IClientConnection[] Connections = this.GetClientConnections(Connection.BareJid);
4951
4952 if (!(Connections is null))
4953 {
4954 foreach (IClientConnection Connection2 in Connections)
4955 {
4956 if (Connection2 != Connection && Connection2.WantsBlockList)
4957 {
4958 try
4959 {
4960 await Connection2.IQ("set", string.Empty, Connection2.Address, e.To, string.Empty,
4961 "<unblock xmlns='" + BlockingCommandNamespace + "'/>", this);
4962 }
4963 catch (Exception ex)
4964 {
4965 try
4966 {
4967 await Connection.Exception(ex);
4968 await Connection.DisposeAsync();
4969 }
4970 catch (Exception ex2)
4971 {
4972 Log.Exception(ex2);
4973 }
4974 }
4975 }
4976 }
4977 }
4978 }
4979 }
4980 else
4981 {
4982 StringBuilder Xml = null;
4983
4984 foreach (CaseInsensitiveString BareJid2 in BareJids)
4985 {
4986 if (await this.persistenceLayer.Unblock(Connection.UserName, BareJid2))
4987 {
4988 IClientConnection[] Connections = this.GetClientConnections(Connection.BareJid);
4989
4990 if (!(Connections is null))
4991 {
4992 foreach (IClientConnection Connection2 in Connections)
4993 {
4994 if (Connection2 != Connection && Connection2.WantsBlockList)
4995 {
4996 if (Xml is null)
4997 Xml = new StringBuilder();
4998 else
4999 Xml.Clear();
5000
5001 Xml.Append("<unblock xmlns='");
5002 Xml.Append(BlockingCommandNamespace);
5003 Xml.Append("'><item jid='");
5004 Xml.Append(BareJid2);
5005 Xml.Append("'/></unblock>");
5006
5007 try
5008 {
5009 await Connection2.IQ("set", string.Empty, Connection2.Address, e.To, string.Empty, Xml.ToString(), this);
5010 }
5011 catch (Exception ex)
5012 {
5013 await Connection.Exception(ex);
5014 await Connection.DisposeAsync();
5015 }
5016 }
5017 }
5018 }
5019 }
5020 }
5021 }
5022
5023 await e.IqResult(string.Empty, e.To);
5024 }
5025
5026 #endregion
5027
5028 #region Sniffers
5029
5030 internal XmlFileSniffer GetSniffer(string Key, bool S2S)
5031 {
5032 string FileName;
5033
5034 if (S2S)
5035 FileName = this.domainSnifferPath.Replace("%DOMAIN%", Key);
5036 else
5037 FileName = this.clientSnifferPath.Replace("%ENDPOINT%", Key);
5038
5039 if (!(this.sniffers is null) && this.sniffers.TryGetValue(FileName, out XmlFileSniffer XmlFileSniffer))
5040 return XmlFileSniffer;
5041
5042 if (S2S)
5043 {
5044 if (this.s2sEndpoints.TryGetValue(Key, out IS2SEndpoint Endpoint) && Endpoint.HasSniffers)
5045 {
5046 foreach (ISniffer Sniffer in Endpoint.Sniffers)
5047 {
5048 if (Sniffer is XmlFileSniffer XmlFileSniffer2)
5049 return XmlFileSniffer2;
5050 }
5051 }
5052 }
5053 else
5054 {
5055 IClientConnection[] Connections = this.GetClientConnections(Key + "@" + this.domain);
5056 if (!(Connections is null))
5057 {
5058 foreach (IClientConnection Connection in Connections)
5059 {
5060 if (Connection is ICommunicationLayer ComLayer && ComLayer.HasSniffers)
5061 {
5062 foreach (ISniffer Sniffer in ComLayer.Sniffers)
5063 {
5064 if (Sniffer is XmlFileSniffer XmlFileSniffer2)
5065 return XmlFileSniffer2;
5066 }
5067 }
5068 }
5069 }
5070 }
5071
5072 return new XmlFileSniffer(FileName, this.GetTransformPath(S2S), 7, BinaryPresentationMethod.ByteCount);
5073 }
5074
5075 internal void CacheSniffers(IEnumerable<ISniffer> Sniffers)
5076 {
5077 foreach (ISniffer Sniffer in Sniffers)
5078 {
5079 if (Sniffer is XmlFileSniffer XmlFileSniffer)
5080 this.CacheSniffer(XmlFileSniffer);
5081 else if (Sniffer is IDisposable Disposable)
5082 Disposable.Dispose();
5083 }
5084 }
5085
5086 internal void CacheSniffer(XmlFileSniffer Sniffer)
5087 {
5088 if (this.sniffers is null)
5089 {
5090 this.sniffers = new Cache<CaseInsensitiveString, XmlFileSniffer>(int.MaxValue, TimeSpan.MaxValue, new TimeSpan(1, 1, 0), true);
5091 this.sniffers.Removed += this.Sniffers_Removed;
5092 }
5093
5094 this.sniffers.Add(Sniffer.FileName, Sniffer);
5095 }
5096
5097 private Task Sniffers_Removed(object Sender, CacheItemEventArgs<CaseInsensitiveString, XmlFileSniffer> e)
5098 {
5099 if (this.disposed || (DateTime.Now - e.Value.LastEvent).TotalMinutes > 30)
5100 e.Value.Dispose();
5101
5102 return Task.CompletedTask;
5103 }
5104
5105 #endregion
5106
5107 #region Statistics
5108
5113 internal void DataReceived(int NrRead)
5114 {
5115 lock (this.statSync)
5116 {
5117 this.nrBytesRx += NrRead;
5118 }
5119 }
5120
5125 internal void DataTransmitted(int NrWritten)
5126 {
5127 lock (this.statSync)
5128 {
5129 this.nrBytesTx += NrWritten;
5130 }
5131 }
5132
5141 internal void IncCounters(string Stanza, string Type, XmppAddress From, XmppAddress To, XmlElement StanzaElement)
5142 {
5143 string Namespace = null;
5144 string LocalName = null;
5145 string LocalNameBak = null;
5146 string ns = StanzaElement.NamespaceURI;
5147
5148 foreach (XmlNode N in StanzaElement.ChildNodes)
5149 {
5150 if (N is XmlElement E)
5151 {
5152 if (E.NamespaceURI != ns)
5153 {
5154 Namespace = E.NamespaceURI;
5155 LocalName = E.LocalName;
5156 break;
5157 }
5158 else if (LocalNameBak is null)
5159 LocalNameBak = E.LocalName;
5160 }
5161 }
5162
5163 if (Namespace is null)
5164 {
5165 Namespace = StanzaElement.NamespaceURI;
5166 LocalName = LocalNameBak ?? string.Empty;
5167 }
5168
5169 lock (this.statSync)
5170 {
5171 this.nrStanzas++;
5172
5173 this.IncLocked(Stanza + "#" + Type, this.stanzasPerStanzaType);
5174 this.IncLocked(From.Domain, this.stanzasPerFromDomain);
5175 this.IncLocked(From.BareJid, this.stanzasPerFromBareJid);
5176 this.IncLocked(To.Domain, this.stanzasPerToDomain);
5177 this.IncLocked(To.BareJid, this.stanzasPerToBareJid);
5178 this.IncLocked(Namespace, this.stanzasPerNamespace);
5179 this.IncLocked(Namespace + "#" + LocalName, this.stanzasPerFqn);
5180 }
5181 }
5182
5183 private void IncLocked(string Key, Dictionary<string, Statistic> Stat)
5184 {
5185 if (!Stat.TryGetValue(Key, out Statistic Rec))
5186 {
5187 Rec = new Statistic(1);
5188 Stat[Key] = Rec;
5189 }
5190 else
5191 Rec.Inc();
5192 }
5193
5199 {
5201 DateTime TP = DateTime.Now;
5202
5203 lock (this.statSync)
5204 {
5205 Result = new Statistics.CommunicationStatistics()
5206 {
5207 StanzasPerStanzaType = this.stanzasPerStanzaType,
5208 StanzasPerFromDomain = this.stanzasPerFromDomain,
5209 StanzasPerToDomain = this.stanzasPerToDomain,
5210 StanzasPerFromBareJid = this.stanzasPerFromBareJid,
5211 StanzasPerToBareJid = this.stanzasPerToBareJid,
5212 StanzasPerNamespace = this.stanzasPerNamespace,
5213 StanzasPerFqn = this.stanzasPerFqn,
5214 LastStat = this.lastStat,
5215 CurrentStat = TP,
5216 NrBytesRx = this.nrBytesRx,
5217 NrBytesTx = this.nrBytesTx,
5218 NrStanzas = this.nrStanzas
5219 };
5220
5221 this.stanzasPerStanzaType = new Dictionary<string, Statistic>();
5222 this.stanzasPerFromDomain = new Dictionary<string, Statistic>();
5223 this.stanzasPerToDomain = new Dictionary<string, Statistic>();
5224 this.stanzasPerFromBareJid = new Dictionary<string, Statistic>();
5225 this.stanzasPerToBareJid = new Dictionary<string, Statistic>();
5226 this.stanzasPerNamespace = new Dictionary<string, Statistic>();
5227 this.stanzasPerFqn = new Dictionary<string, Statistic>();
5228 this.lastStat = TP;
5229 this.nrBytesRx = 0;
5230 this.nrBytesTx = 0;
5231 this.nrStanzas = 0;
5232 }
5233
5234 return Result;
5235 }
5236
5237 #endregion
5238
5239 #region XEP-0049: Private XML Storage
5240
5241 internal async Task PrivateXmlStorageGet(object Sender, IqEventArgs e)
5242 {
5243 if (!this.clientConnections.TryGetValue(e.From.Address, out IClientConnection Connection))
5244 {
5245 await e.IqErrorForbidden(e.To, "Private storage only accessible from clients directly connected to the server.", "en");
5246 return;
5247 }
5248
5249 IAccount FromAccount = await this.persistenceLayer.GetAccount(Connection.UserName);
5250 if (FromAccount is null)
5251 {
5252 await e.IqErrorItemNotFound(e.To, "Account no longer found.", "en");
5253 return;
5254 }
5255
5256 StringBuilder Xml = new StringBuilder();
5257 bool Empty = true;
5258 bool Found;
5259
5260 Xml.Append("<query xmlns='");
5261 Xml.Append(PrivateXmlStorageNamespace);
5262 Xml.Append("'>");
5263
5264 foreach (XmlNode N in e.Query.ChildNodes)
5265 {
5266 if (N is XmlElement E)
5267 {
5268 Empty = false;
5269 Found = false;
5270
5271 foreach (PersistedElement Element in await Database.Find<PersistedElement>(new FilterAnd(
5272 new FilterFieldEqualTo("Account", FromAccount.UserName),
5273 new FilterFieldEqualTo("Namespace", E.NamespaceURI),
5274 new FilterFieldEqualTo("LocalName", E.LocalName))))
5275 {
5276 if (Found)
5277 await Database.DeleteLazy(Element);
5278 else
5279 {
5280 Xml.Append(Element.Xml);
5281 Found = true;
5282 }
5283 }
5284
5285 if (!Found)
5286 {
5287 await e.IqErrorItemNotFound(e.To, "Element not found.", "en");
5288 return;
5289 }
5290 }
5291 }
5292
5293 if (Empty)
5294 {
5295 await e.IqErrorBadRequest(e.To, "Query is empty.", "en");
5296 return;
5297 }
5298
5299 Xml.Append("</query>");
5300
5301 await e.IqResult(Xml.ToString(), e.To);
5302 }
5303
5304 internal async Task PrivateXmlStorageSet(object Sender, IqEventArgs e)
5305 {
5306 if (!this.clientConnections.TryGetValue(e.From.Address, out IClientConnection Connection))
5307 {
5308 await e.IqErrorForbidden(e.To, "Private storage only accessible from clients directly connected to the server.", "en");
5309 return;
5310 }
5311
5312 IAccount FromAccount = await this.persistenceLayer.GetAccount(Connection.UserName);
5313 if (FromAccount is null)
5314 {
5315 await e.IqErrorItemNotFound(e.To, "Account no longer found.", "en");
5316 return;
5317 }
5318
5319 bool Empty = true;
5320 bool Found;
5321
5322 foreach (XmlNode N in e.Query.ChildNodes)
5323 {
5324 if (N is XmlElement E)
5325 {
5326 bool DeleteElement = IsEmptyPrivateXml(E);
5327
5328 Found = false;
5329 Empty = false;
5330
5331 foreach (PersistedElement Element in await Database.Find<PersistedElement>(new FilterAnd(
5332 new FilterFieldEqualTo("Account", FromAccount.UserName),
5333 new FilterFieldEqualTo("Namespace", E.NamespaceURI),
5334 new FilterFieldEqualTo("LocalName", E.LocalName))))
5335 {
5336 if (Found)
5337 await Database.DeleteLazy(Element);
5338 else
5339 {
5340 if (DeleteElement)
5341 await Database.DeleteLazy(Element);
5342 else
5343 {
5344 Element.Xml = E.OuterXml;
5345 Element.Updated = DateTime.UtcNow;
5346
5347 await Database.UpdateLazy(Element);
5348 }
5349
5350 Found = true;
5351 }
5352 }
5353
5354 if (Found || DeleteElement)
5355 continue;
5356
5357 DateTime TP = DateTime.UtcNow;
5358
5360 {
5361 Account = FromAccount.UserName,
5362 Namespace = E.NamespaceURI,
5363 LocalName = E.LocalName,
5364 Created = TP,
5365 Updated = TP,
5366 Xml = E.OuterXml
5367 });
5368 }
5369 }
5370
5371 if (Empty)
5372 await e.IqErrorNotAcceptable(e.To, "Empty query element.", "en");
5373 else
5374 await e.IqResult(string.Empty, e.To);
5375 }
5376
5377 private static bool IsEmptyPrivateXml(XmlElement E)
5378 {
5379 if (E.HasChildNodes)
5380 return false;
5381
5382 foreach (XmlAttribute Attr in E.Attributes)
5383 {
5384 if (Attr.Name != "xmlns")
5385 return false;
5386 }
5387
5388 return true;
5389 }
5390
5391 #endregion
5392
5393 #region SMTP integration
5394
5395 private Task SmtpServer_MessageReceived(object Sender, SmtpMessageEventArgs e)
5396 {
5397 return this.ProcessMessage(e.Message);
5398 }
5399
5405 {
5406 try
5407 {
5408 List<KeyValuePair<string, object>> Tags = new List<KeyValuePair<string, object>>();
5409
5410 foreach (KeyValuePair<string, string> P in Message.AllHeaders)
5411 Tags.Add(new KeyValuePair<string, object>(P.Key, P.Value));
5412
5413 Log.Informational("Mail received.", Tags.ToArray());
5414
5415 /*try
5416 {
5417 string FileName = @"C:\ProgramData\IoT Gateway\SMTP\" + Guid.NewGuid().ToString();
5418 await Resources.WriteAllBytesAsync(FileName + ".bin", Message.TransformedBody ?? Message.UntransformedBody);
5419 await Resources.WriteAllTextAsync(FileName + ".txt", Message.ContentType);
5420 Log.Informational("Incoming mail saved to disk: " + FileName);
5421 }
5422 catch (Exception ex)
5423 {
5424 Log.Exception(ex);
5425 }*/
5426
5427 Dictionary<CaseInsensitiveString, bool> Processed = new Dictionary<CaseInsensitiveString, bool>();
5428
5429 await this.ProcessMessage(Message, Message.To, Processed);
5430 await this.ProcessMessage(Message, Message.Cc, Processed);
5431 await this.ProcessMessage(Message, Message.Bcc, Processed);
5432 }
5433 catch (Exception ex)
5434 {
5435 Log.Exception(ex);
5436 }
5437 }
5438
5439 private async Task ProcessMessage(SmtpMessage Message, IEnumerable<MailAddress> Recipients, Dictionary<CaseInsensitiveString, bool> Processed)
5440 {
5441 if (!(Recipients is null))
5442 {
5443 foreach (MailAddress Recipient in Recipients)
5444 {
5445 if (!Processed.ContainsKey(Recipient.Address))
5446 {
5447 Processed[Recipient.Address] = true;
5448 await this.ProcessMessage(Message, Recipient);
5449 }
5450 }
5451 }
5452 }
5453
5454 private readonly Dictionary<string, DateTime> lastBounce = new Dictionary<string, DateTime>();
5455
5456 private bool CanReturnBounceMessage(MailAddress Recipient, MailAddress Sender)
5457 {
5458 string Key = Recipient.Address + " | " + Sender.Address;
5459 DateTime TP;
5460
5461 lock (this.lastBounce)
5462 {
5463 if (this.lastBounce.ContainsKey(Key))
5464 return false;
5465 }
5466
5467 TP = scheduler.Add(DateTime.Now.AddHours(4), (P) =>
5468 {
5469 lock (this.lastBounce)
5470 {
5471 this.lastBounce.Remove((string)P);
5472 }
5473 }, Key);
5474
5475 lock (this.lastBounce)
5476 {
5477 this.lastBounce[Key] = TP;
5478 }
5479
5480 return true;
5481 }
5482
5483 private async Task ProcessMessage(SmtpMessage Message, MailAddress Recipient)
5484 {
5485 try
5486 {
5487 XmppAddress Addr = new XmppAddress(Recipient.Address);
5488 if (this.IsServerDomain(Addr.Domain, true))
5489 {
5490 IRosterItem Item = await this.persistenceLayer.GetRosterItem(Addr.Account, Message.FromMail.Address);
5491 StringBuilder Markdown;
5492
5493 if (Item is null ||
5494 (Item.Subscription != SubscriptionStatus.both &&
5495 Item.Subscription != SubscriptionStatus.from &&
5496 Item.Subscription != SubscriptionStatus.to)) // If not white-listed
5497 {
5498 if (!this.CanReturnBounceMessage(Recipient, Message.FromMail))
5499 {
5500 Log.Notice("Mail discarded.", Recipient.Address, Message.FromMail.Address);
5501 return;
5502 }
5503
5504 Markdown = new StringBuilder();
5505
5506 Markdown.AppendLine("Welcome");
5507 Markdown.AppendLine("===========");
5508 Markdown.AppendLine();
5509
5510 Markdown.Append("The mail server at **");
5511 Markdown.Append(MarkdownDocument.Encode(this.domain));
5512 Markdown.AppendLine("** only forwards mail messages from approved senders.");
5513 Markdown.Append("Since **");
5514 Markdown.Append(MarkdownDocument.Encode(Message.FromMail.Address));
5515 Markdown.Append("** has not been approved by **");
5516 Markdown.Append(MarkdownDocument.Encode(Recipient.Address));
5517 Markdown.AppendLine("**, your mail has not been forwarded.");
5518 Markdown.AppendLine();
5519
5520 if (this.httpServer.OpenHttpsPorts.Length > 0 || this.httpServer.OpenHttpPorts.Length > 0)
5521 {
5522 Markdown.Append("If you want, you can send a request to **");
5523 Markdown.Append(MarkdownDocument.Encode(Recipient.Address));
5524 Markdown.Append("** to become approved, by following this link: [Request approval](");
5525 Markdown.Append("http");
5526
5527 if (this.httpServer.OpenHttpsPorts.Length > 0)
5528 {
5529 Markdown.Append("s://");
5530 Markdown.Append(this.domain);
5531
5532 if (Array.IndexOf(this.httpServer.OpenHttpsPorts, HttpServer.DefaultHttpsPort) < 0)
5533 {
5534 Markdown.Append(':');
5535 Markdown.Append(this.httpServer.OpenHttpsPorts[0]);
5536 }
5537 }
5538 else
5539 {
5540 Markdown.Append("://");
5541 Markdown.Append(this.domain);
5542
5543 if (Array.IndexOf(this.httpServer.OpenHttpPorts, HttpServer.DefaultHttpPort) < 0)
5544 {
5545 Markdown.Append(':');
5546 Markdown.Append(this.httpServer.OpenHttpPorts[0]);
5547 }
5548 }
5549
5550 string Expires = DateTime.Now.AddDays(1).Ticks.ToString();
5551
5552 Markdown.Append("/RequestWhiteList?Sender=");
5553 Markdown.Append(XML.HtmlValueEncode(Message.FromMail.Address));
5554 Markdown.Append("&Receiver=");
5555 Markdown.Append(XML.HtmlValueEncode(Recipient.Address));
5556 Markdown.Append("&Expires=P");
5557 Markdown.Append(Expires);
5558 Markdown.Append("&MAC=");
5559
5560 StringBuilder sb = new StringBuilder();
5561 sb.Append(Message.FromMail.Address);
5562 sb.Append(" | ");
5563 sb.Append(Recipient.Address);
5564 sb.Append(" | ");
5565 sb.Append(Expires);
5566
5567 if (RequestWhiteList.whiteListKey is null)
5568 {
5569 string Key = await RuntimeSettings.GetAsync("WhiteList.Key", string.Empty);
5570 if (string.IsNullOrEmpty(Key))
5571 {
5572 Key = Convert.ToBase64String(GetRandomNumbers(32));
5573 await RuntimeSettings.SetAsync("WhiteList.Key", Key);
5574 }
5575
5576 RequestWhiteList.whiteListKey = Key;
5577 }
5578
5579 string MAC = Hashes.ComputeHMACSHA256HashString(System.Convert.FromBase64String(RequestWhiteList.whiteListKey),
5580 Encoding.UTF8.GetBytes(sb.ToString()));
5581
5582 Markdown.Append(MAC);
5583 Markdown.AppendLine(")");
5584 Markdown.AppendLine();
5585
5586 Log.Notice("Mail discarded. Bounce message returned.", Recipient.Address, Message.FromMail.Address,
5587 new KeyValuePair<string, object>("Sender", Message.FromMail.Address),
5588 new KeyValuePair<string, object>("Receiver", Recipient.Address),
5589 new KeyValuePair<string, object>("Expires", Expires),
5590 new KeyValuePair<string, object>("MAC", MAC));
5591 }
5592
5593 await this.SendMailMessage(Recipient.Address, Message.FromMail.Address, "Approval required", Markdown.ToString());
5594 }
5595 else
5596 {
5597 XmppAddress From = new XmppAddress(Message.FromMail.Address);
5598 XmppAddress To = new XmppAddress(Recipient.Address);
5599 ISender Sender = await this.GetS2sEndpoint(To.Domain, From.Domain, true, "Forwarding e-mail.");
5600 List<EmbeddedContent> Attachments = null;
5601 List<EmbeddedContent> Inline = null;
5602 object Decoded = Message.DecodedBody;
5603 string PlainText = null;
5604 HtmlDocument Html = null;
5606
5607 if (!(Sender is null))
5608 {
5609 if (!(Message.Attachments is null) && Message.Attachments.Length > 0)
5610 {
5611 Attachments = new List<EmbeddedContent>();
5612 Attachments.AddRange(Message.Attachments);
5613 }
5614
5615 if (!(Message.InlineObjects is null) && Message.InlineObjects.Length > 0)
5616 {
5617 Inline = new List<EmbeddedContent>();
5618 Inline.AddRange(Message.InlineObjects);
5619 }
5620
5621 if (!(Decoded is null) && Decoded is MultipartContent MultipartContent)
5622 {
5623 LinkedList<MultipartContent> ToProcess = new LinkedList<MultipartContent>();
5624
5625 ToProcess.AddLast(MultipartContent);
5626 Decoded = null;
5627
5628 while (!(ToProcess.First is null))
5629 {
5630 MultipartContent = ToProcess.First.Value;
5631 ToProcess.RemoveFirst();
5632
5634 {
5636 {
5637 if (PlainText is null && EmbeddedContent.Decoded is string s2)
5638 PlainText = s2;
5639 else if (Html is null && EmbeddedContent.Decoded is HtmlDocument Html2)
5640 Html = Html2;
5641 else if (MarkdownDocument is null && EmbeddedContent.Decoded is MarkdownDocument MarkdownDocument2)
5642 MarkdownDocument = MarkdownDocument2;
5643 else if (EmbeddedContent.Decoded is MultipartContent MultipartContent2)
5644 ToProcess.AddLast(MultipartContent2);
5645 else if (Decoded is null)
5646 Decoded = EmbeddedContent.Decoded;
5647 else
5648 {
5649 if (Attachments is null)
5650 Attachments = new List<EmbeddedContent>();
5651
5652 Attachments.Add(EmbeddedContent);
5653 }
5654 }
5655 }
5656 else
5657 {
5659 {
5661 {
5662 case ContentDisposition.Attachment:
5663 if (Attachments is null)
5664 Attachments = new List<EmbeddedContent>();
5665
5666 Attachments.Add(EmbeddedContent);
5667 break;
5668
5669 case ContentDisposition.Inline:
5670 if (Inline is null)
5671 Inline = new List<EmbeddedContent>();
5672
5673 Inline.Add(EmbeddedContent);
5674 break;
5675
5676 default:
5677 if (EmbeddedContent.Decoded is MultipartContent MultipartContent2)
5678 ToProcess.AddLast(MultipartContent2);
5679 else if (Decoded is null)
5680 Decoded = EmbeddedContent.Decoded;
5681 else
5682 {
5683 if (Attachments is null)
5684 Attachments = new List<EmbeddedContent>();
5685
5686 Attachments.Add(EmbeddedContent);
5687 }
5688 break;
5689 }
5690 }
5691 }
5692 }
5693 }
5694
5695 if (!(Decoded is null))
5696 {
5697 if (PlainText is null && Decoded is string s)
5698 PlainText = s;
5699 else if (Html is null && Decoded is HtmlDocument Html2)
5700 Html = Html2;
5701 else if (MarkdownDocument is null && Decoded is MarkdownDocument MarkdownDocument2)
5702 MarkdownDocument = MarkdownDocument2;
5703 }
5704
5705 if (!(MarkdownDocument is null))
5706 {
5707 // TODO: Check message, and block items that can pose a security issue (for instance, inclusion script, javascript, etc.).
5708
5709 if (string.IsNullOrEmpty(PlainText))
5710 PlainText = await MarkdownDocument.GeneratePlainText();
5711
5712 if (Html is null)
5713 Html = new HtmlDocument(await MarkdownDocument.GenerateHTML());
5714 }
5715
5716 StringBuilder Content = new StringBuilder();
5717 bool HasBody = false;
5718
5719 if (!string.IsNullOrEmpty(Message.Subject))
5720 {
5721 Content.Append("<subject>");
5722 Content.Append(XML.Encode(Message.Subject));
5723 Content.Append("</subject>");
5724 }
5725
5726 if (!string.IsNullOrEmpty(Message.MessageID))
5727 {
5728 Content.Append("<thread>");
5729 Content.Append(XML.Encode(Message.MessageID));
5730 Content.Append("</thread>");
5731 }
5732
5733 if (!string.IsNullOrEmpty(PlainText))
5734 {
5735 Content.Append("<body>");
5736 Content.Append(XML.Encode(PlainText));
5737 Content.Append("</body>");
5738
5739 HasBody = true;
5740 }
5741
5742 if (!(Html?.Body is null))
5743 {
5744 Content.Append("<html xmlns='http://jabber.org/protocol/xhtml-im'>");
5745 Content.Append("<body xmlns='http://www.w3.org/1999/xhtml'>");
5746
5747 if (Html.Body.HasChildren)
5748 {
5749 foreach (HtmlNode N2 in Html.Body.Children)
5750 this.XmlEncode(N2, Content);
5751 }
5752
5753 Content.Append("</body></html>");
5754
5755 HasBody = true;
5756 }
5757
5758 if (!(MarkdownDocument is null))
5759 {
5760 Content.Append("<content xmlns='");
5761 Content.Append(ContentNamespace);
5762 Content.Append("' type='text/markdown'>");
5763 Content.Append(XML.HtmlValueEncode(await MarkdownDocument.GenerateMarkdown(true)));
5764 Content.Append("</content>");
5765
5766 HasBody = true;
5767 }
5768
5769 if (!HasBody)
5770 {
5771 Markdown = new StringBuilder();
5772
5773 Markdown.AppendLine("Unable to process incoming message");
5774 Markdown.AppendLine("=======================================");
5775 Markdown.AppendLine();
5776
5777 Markdown.Append("The mail server at **");
5778 Markdown.Append(MarkdownDocument.Encode(this.domain));
5779 Markdown.AppendLine("** was unable to forward the mail message to **");
5780 Markdown.Append(MarkdownDocument.Encode(Recipient.Address));
5781 Markdown.Append("**), since the content type `");
5782 Markdown.Append(Message.ContentType);
5783 Markdown.AppendLine("` is not handled.");
5784
5785 await this.SendMailMessage(Recipient.Address, Message.FromMail.Address, "Unable to process incoming message", Markdown.ToString());
5786
5787 Log.Notice("Mail discarded due to unhandled Content-Type.", Recipient.Address, Message.FromMail.Address,
5788 new KeyValuePair<string, object>("Content-Type", Message.ContentType));
5789
5790 return;
5791 }
5792
5793 Content.Append("<mailInfo xmlns='urn:xmpp:smtp' contentType='");
5794 Content.Append(XML.Encode(Message.ContentType));
5795
5796 if (!string.IsNullOrEmpty(Message.MessageID))
5797 {
5798 Content.Append("' id='");
5799 Content.Append(XML.Encode(Message.MessageID));
5800 }
5801
5802 Content.Append("' priority='");
5803 Content.Append(((int)Message.Priority).ToString());
5804
5805 if (Message.Date.HasValue)
5806 {
5807 Content.Append("' date='");
5808 Content.Append(XML.Encode(Message.Date.Value));
5809 }
5810
5811 Content.Append("' fromMail='");
5812 Content.Append(XML.Encode(Message.FromMail.Address));
5813
5814 Content.Append("' fromHeader='");
5815 Content.Append(XML.Encode(Message.FromHeader.ToString()));
5816
5817 Content.Append("' sender='");
5818 Content.Append(XML.Encode(Message.Sender.ToString()));
5819
5820 DateTime TP = DateTime.Now;
5822 {
5823 BareJid = Addr.BareJid,
5824 ContentType = Message.ContentType,
5825 Content = Message.TransformedBody ?? Message.UntransformedBody,
5826 Created = TP,
5827 ContentId = Guid.NewGuid().ToString()
5828 };
5829
5831
5832 Content.Append("' size='");
5833 Content.Append((MailContent.Content?.Length ?? 0).ToString());
5834
5835 Content.Append("' cid='");
5836 Content.Append(MailContent.ContentId);
5837
5838 Content.Append("'><headers xmlns='http://jabber.org/protocol/shim'>");
5839
5840 foreach (KeyValuePair<string, string> P in Message.AllHeaders)
5841 {
5842 Content.Append("<header name='");
5843 Content.Append(XML.Encode(P.Key));
5844 Content.Append("'>");
5845 Content.Append(XML.Encode(P.Value));
5846 Content.Append("</header>");
5847 }
5848
5849 Content.Append("</headers>");
5850
5851 await this.Serialize(Content, Attachments?.ToArray(), "attachment", Addr);
5852 await this.Serialize(Content, Inline?.ToArray(), "inline", Addr);
5853
5854 Content.Append("</mailInfo>");
5855
5856 await this.Message("chat", string.Empty, To, From, string.Empty, Content.ToString(), Sender);
5857 }
5858 }
5859 }
5860 else // Relay to other domain
5861 {
5862 IS2SEndpoint S2sEndpoint = await this.GetS2sEndpoint(this.domain, Addr.Domain, true, "Relaying incoming mail message.");
5863 if (S2sEndpoint is SmtpS2SEndpoint SmtpS2SEndpoint)
5864 await SmtpS2SEndpoint.RelayMessage(Message, Recipient); // Only relay to other SMTP servers
5865 }
5866 }
5867 catch (Exception ex)
5868 {
5869 Log.Exception(ex);
5870 }
5871 }
5872
5873 private void XmlEncode(HtmlNode N, StringBuilder Output)
5874 {
5875 if (N is HtmlElement E)
5876 {
5877 if (E.Name.IndexOf(':') >= 0)
5878 return;
5879
5880 switch (E.Name.ToUpper())
5881 {
5882 case "SCRIPT":
5883 case "META":
5884 case "STYLE":
5885 case "APPLET":
5886 case "EMBED":
5887 case "IFRAME":
5888 case "NOEMBED":
5889 case "OBJECT":
5890 case "PARAM":
5891 case "CANVAS":
5892 case "NOSCRIPT":
5893 case "BUTTON":
5894 case "FORM":
5895 case "INPUT":
5896 case "OUTPUT":
5897 case "SELECT":
5898 case "TEXTAREA":
5899 case "DETAILS":
5900 case "DIALOG":
5901 case "MENU":
5902 case "MENUITEM":
5903 case "SUMMARY":
5904 case "CONTENT":
5905 case "ELEMENT":
5906 case "SHADOW":
5907 case "SLOT":
5908 case "TEMPLATE":
5909 case "COMMAND":
5910 case "DIR":
5911 case "FRAME":
5912 case "FRAMESET":
5913 return;
5914 }
5915
5916 Output.Append('<');
5917 Output.Append(E.Name);
5918
5919 if (E.HasAttributes)
5920 {
5921 foreach (HtmlAttribute Attr in E.Attributes)
5922 {
5923 if (Attr.Name.IndexOf(':') >= 0)
5924 continue;
5925
5926 Output.Append(' ');
5927 Output.Append(Attr.Name);
5928 Output.Append("=\"");
5929 Output.Append(XML.HtmlAttributeEncode(Attr.Value));
5930 Output.Append('"');
5931 }
5932 }
5933
5934 if (E.HasChildren)
5935 {
5936 Output.Append('>');
5937
5938 foreach (HtmlNode N2 in E.Children)
5939 this.XmlEncode(N2, Output);
5940
5941 Output.Append("</");
5942 Output.Append(E.Name);
5943 Output.Append('>');
5944 }
5945 else if (E.IsEmptyElement)
5946 Output.Append("/>");
5947 else
5948 {
5949 Output.Append("></");
5950 Output.Append(E.Name);
5951 Output.Append('>');
5952 }
5953 }
5954 else if (N is HtmlText Text)
5955 Output.Append(XML.Encode(Text.InlineText));
5956 else if (N is HtmlEntity Entity)
5957 {
5958 if (N is HtmlEntityUnicode EntityUnicode)
5959 {
5960 char ch = (char)EntityUnicode.Code;
5961
5962 switch (ch)
5963 {
5964 case '<':
5965 Output.Append("&lt;");
5966 break;
5967
5968 case '>':
5969 Output.Append("&gt;");
5970 break;
5971
5972 case '"':
5973 Output.Append("&quot;");
5974 break;
5975
5976 case '\'':
5977 Output.Append("&apos;");
5978 break;
5979
5980 case '&':
5981 Output.Append("&amp;");
5982 break;
5983
5984 default:
5985 Output.Append(ch);
5986 break;
5987 }
5988 }
5989 else
5990 {
5991 switch (Entity.EntityName.ToLower())
5992 {
5993 case "lt":
5994 case "gt":
5995 case "quot":
5996 case "apos":
5997 case "amp":
5998 Output.Append('&');
5999 Output.Append(Entity.EntityName);
6000 Output.Append(';');
6001 break;
6002
6003 default:
6004 Output.Append(HtmlEntity.EntityToCharacter(Entity.EntityName));
6005 break;
6006 }
6007 }
6008 }
6009 else if (N is CDATA CDATA)
6010 Output.Append(XML.Encode(CDATA.Content));
6011 }
6012
6013 private async Task Serialize(StringBuilder Content, IEnumerable<EmbeddedContent> Objects, string ElementName, XmppAddress Recipient)
6014 {
6015 if (!(Objects is null))
6016 {
6017 foreach (EmbeddedContent EmbeddedContent in Objects)
6018 {
6020 {
6021 BareJid = Recipient.BareJid,
6023 Content = EmbeddedContent.TransferDecoded ?? EmbeddedContent.Raw,
6024 Created = DateTime.Now,
6025 ContentId = Guid.NewGuid().ToString()
6026 };
6027
6029
6030 Content.Append('<');
6031 Content.Append(ElementName);
6032 Content.Append(" contentType='");
6033 Content.Append(XML.Encode(EmbeddedContent.ContentType));
6034
6035 if (!string.IsNullOrEmpty(EmbeddedContent.Description))
6036 {
6037 Content.Append("' description='");
6038 Content.Append(XML.Encode(EmbeddedContent.Description));
6039 }
6040
6041 if (!string.IsNullOrEmpty(EmbeddedContent.FileName))
6042 {
6043 Content.Append("' fileName='");
6044 Content.Append(XML.Encode(EmbeddedContent.FileName));
6045 }
6046
6047 if (!string.IsNullOrEmpty(EmbeddedContent.Name))
6048 {
6049 Content.Append("' name='");
6050 Content.Append(XML.Encode(EmbeddedContent.Name));
6051 }
6052
6053 if (!string.IsNullOrEmpty(EmbeddedContent.ID))
6054 {
6055 Content.Append("' id='");
6056 Content.Append(XML.Encode(EmbeddedContent.ID));
6057 }
6058
6059 Content.Append("' cid='");
6060 Content.Append(XML.Encode(MailContent.ContentId));
6061
6062 Content.Append("' size='");
6063 Content.Append((MailContent.Content?.Length ?? 0).ToString());
6064
6065 Content.Append("'/>");
6066 }
6067 }
6068 }
6069
6070 private async Task GetMailContent(object Sender, IqEventArgs e)
6071 {
6072 string ContentId = XML.Attribute(e.Query, "cid");
6073 string ContentType = XML.Attribute(e.Query, "type");
6074
6075 MailContent Content = await Database.FindFirstDeleteRest<MailContent>(new FilterFieldEqualTo("ContentId", ContentId));
6076 if (Content is null)
6077 {
6078 await e.IqErrorItemNotFound(e.To, "Mail content item not found.", "en");
6079 return;
6080 }
6081
6082 if (Content.BareJid != e.From.BareJid)
6083 {
6084 await e.IqErrorForbidden(e.To, "Not authorized access.", "en");
6085 return;
6086 }
6087
6088 byte[] Data = null;
6089
6090 if (!string.IsNullOrEmpty(ContentType) && string.Compare(Content.ContentType, ContentType, true) != 0)
6091 {
6092 try
6093 {
6094 object Decoded = await InternetContent.DecodeAsync(Content.ContentType, Content.Content, null);
6095 if (Decoded is MultipartContent MultipartContent)
6096 {
6097 LinkedList<MultipartContent> ToProcess = new LinkedList<MultipartContent>();
6098 ToProcess.AddLast(MultipartContent);
6099
6100 while (Data is null && !(ToProcess.First is null))
6101 {
6102 MultipartContent = ToProcess.First.Value;
6103 ToProcess.RemoveFirst();
6104
6106 {
6107 if (string.Compare(Obj.ContentType, ContentType, true) == 0)
6108 {
6109 Data = Obj.TransferDecoded ?? Obj.Raw;
6110 break;
6111 }
6112
6113 if (!Obj.ContentType.StartsWith("multipart/"))
6114 continue;
6115
6117 if (!(MultipartContent is null))
6118 ToProcess.AddLast(MultipartContent);
6119 }
6120 }
6121 }
6122
6123 if (Data is null)
6124 {
6125 await e.IqErrorItemNotFound(e.To, "Content-Type not found in mail object.", "en");
6126 return;
6127 }
6128 }
6129 catch (Exception)
6130 {
6131 await e.IqErrorNotAcceptable(e.To, "Unable to decode mail object.", "en");
6132 return;
6133 }
6134 }
6135 else
6136 {
6137 ContentType = Content.ContentType;
6138 Data = Content.Content;
6139 }
6140
6141 StringBuilder Xml = new StringBuilder();
6142
6143 Xml.Append("<content type='");
6144 Xml.Append(XML.Encode(ContentType));
6145 Xml.Append("' xmlns='");
6146 Xml.Append(MailNamespace);
6147
6148 if (Content.Content is null)
6149 Xml.Append("'/>");
6150 else
6151 {
6152 Xml.Append("'>");
6153 Xml.Append(Convert.ToBase64String(Data));
6154 Xml.Append("</content>");
6155 }
6156
6157 await e.IqResult(Xml.ToString(), e.To);
6158 }
6159
6160 private async Task DeleteMailContent(object Sender, IqEventArgs e)
6161 {
6162 string ContentId = XML.Attribute(e.Query, "cid");
6163
6164 MailContent Content = await Database.FindFirstDeleteRest<MailContent>(new FilterFieldEqualTo("ContentId", ContentId));
6165 if (Content is null)
6166 {
6167 await e.IqErrorItemNotFound(e.To, "Mail content item not found.", "en");
6168 return;
6169 }
6170
6171 if (Content.BareJid != e.From.BareJid)
6172 {
6173 await e.IqErrorForbidden(e.To, "Not authorized access.", "en");
6174 return;
6175 }
6176
6177 await Database.DeleteLazy(Content);
6178
6179 await e.IqResult(string.Empty, e.To);
6180 }
6181
6187 public async Task<int> DeleteOldMailContent(DateTime OlderThan)
6188 {
6189 int Nr = 0;
6190
6191 foreach (MailContent Content in await Database.FindDelete<MailContent>(new FilterFieldLesserOrEqualTo("Created", OlderThan)))
6192 Nr++;
6193
6194 return Nr;
6195 }
6196
6204 public async Task<bool> SendMailMessage(CaseInsensitiveString From, CaseInsensitiveString To, string Subject, string Markdown)
6205 {
6206 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(Markdown);
6207 string HTML = "<html><body>" + HtmlDocument.GetBody(await Doc.GenerateHTML()) + "</body></html>";
6208 string PlainText = await Doc.GeneratePlainText();
6209
6210 return await this.smtpServer.SendMessage(From, To, Subject, new EmbeddedContent[]
6211 {
6212 new EmbeddedContent()
6213 {
6214 ContentType = "text/html; charset=utf-8",
6215 Raw = Encoding.UTF8.GetBytes(HTML)
6216 },
6217 new EmbeddedContent()
6218 {
6219 ContentType = "text/plain; charset=utf-8",
6220 Raw = Encoding.UTF8.GetBytes(PlainText)
6221 },
6222 new EmbeddedContent()
6223 {
6224 ContentType = "text/markdown; charset=utf-8",
6225 Raw = Encoding.UTF8.GetBytes(Markdown)
6226 }
6227 }, new EmbeddedContent[0]);
6228 }
6229
6230 #endregion
6231
6232 #region Service Discovery
6233
6239 public Task<ServiceDiscoveryResult> ServiceDiscoveryAsync(string To)
6240 {
6241 return this.ServiceDiscoveryAsync(To, string.Empty);
6242 }
6243
6250 public async Task<ServiceDiscoveryResult> ServiceDiscoveryAsync(string To, string Node)
6251 {
6252 StringBuilder Xml = new StringBuilder();
6253 bool CacheResponse = string.IsNullOrEmpty(Node) && (string.IsNullOrEmpty(To) || this.IsServerDomain(To, true));
6254 TaskCompletionSource<ServiceDiscoveryResult> Result = new TaskCompletionSource<ServiceDiscoveryResult>();
6255
6256 Xml.Append("<query xmlns='");
6257 Xml.Append(DiscoveryNamespace);
6258
6259 if (!string.IsNullOrEmpty(Node))
6260 {
6261 Xml.Append("' node='");
6262 Xml.Append(XML.Encode(Node));
6263 }
6264
6265 Xml.Append("'/>");
6266
6267 await this.SendIqRequest("get", this.domainAddress, new XmppAddress(To), string.Empty,
6268 Xml.ToString(), (Sender, e) =>
6269 {
6270 if (e.Ok)
6271 {
6272 Dictionary<string, bool> Features = new Dictionary<string, bool>();
6273 List<Identity> Identities = new List<Identity>();
6274
6275 foreach (XmlNode N in e.Response.ChildNodes)
6276 {
6277 if (N.LocalName == "query")
6278 {
6279 foreach (XmlNode N2 in N.ChildNodes)
6280 {
6281 switch (N2.LocalName)
6282 {
6283 case "identity":
6284 Identities.Add(new Identity((XmlElement)N2));
6285 break;
6286
6287 case "feature":
6288 Features[XML.Attribute((XmlElement)N2, "var")] = true;
6289 break;
6290 }
6291 }
6292 }
6293 }
6294
6295 Result.TrySetResult(new ServiceDiscoveryResult(Identities.ToArray(), Features));
6296 }
6297 else if (string.IsNullOrEmpty(e.ErrorText))
6298 Result.TrySetException(new Exception("Unable to perform service discovery."));
6299 else
6300 Result.TrySetException(new Exception(e.ErrorText));
6301
6302 return Task.CompletedTask;
6303
6304 }, null);
6305
6306 return await Result.Task;
6307 }
6308
6313 public Task<Item[]> ServiceItemsDiscoveryAsync(string To)
6314 {
6315 return this.ServiceItemsDiscoveryAsync(To, string.Empty);
6316 }
6317
6323 public async Task<Item[]> ServiceItemsDiscoveryAsync(string To, string Node)
6324 {
6325 StringBuilder Xml = new StringBuilder();
6326 TaskCompletionSource<Item[]> Result = new TaskCompletionSource<Item[]>();
6327
6328 Xml.Append("<query xmlns='");
6329 Xml.Append(DiscoveryItemsNamespace);
6330
6331 if (!string.IsNullOrEmpty(Node))
6332 {
6333 Xml.Append("' node='");
6334 Xml.Append(XML.Encode(Node));
6335 }
6336
6337 Xml.Append("'/>");
6338
6339 await this.SendIqRequest("get", this.domainAddress, new XmppAddress(To),
6340 string.Empty, Xml.ToString(), (Sender, e) =>
6341 {
6342 if (e.Ok)
6343 {
6344 List<Item> Items = new List<Item>();
6345
6346 foreach (XmlNode N in e.Response.ChildNodes)
6347 {
6348 if (N.LocalName == "query")
6349 {
6350 foreach (XmlNode N2 in N.ChildNodes)
6351 {
6352 if (N2.LocalName == "item")
6353 Items.Add(new Item((XmlElement)N2));
6354 }
6355 }
6356 }
6357
6358 Result.TrySetResult(Items.ToArray());
6359 }
6360 else if (string.IsNullOrEmpty(e.ErrorText))
6361 Result.TrySetException(new Exception("Unable to perform service items discovery."));
6362 else
6363 Result.TrySetException(new Exception(e.ErrorText));
6364
6365 return Task.CompletedTask;
6366
6367 }, null);
6368
6369 return await Result.Task;
6370 }
6371
6372 #endregion
6373
6374 #region Finding components
6375
6382 public async Task<CaseInsensitiveString> FindComponentAsync(CaseInsensitiveString Jid, CaseInsensitiveString Feature)
6383 {
6384 CaseInsensitiveString Key = Jid + " " + Feature;
6385
6386 lock (this.services)
6387 {
6388 if (this.services.TryGetValue(Key, out CaseInsensitiveString Service))
6389 return Service;
6390 }
6391
6392 string BareJid = GetBareJID(Jid);
6393 int i = BareJid.IndexOf('@');
6394 string Domain = BareJid.Substring(i + 1);
6395
6396 ServiceDiscoveryResult e = await this.ServiceDiscoveryAsync(Domain);
6397 string Result = null;
6398
6399 if (e.HasFeature(Feature))
6400 Result = Domain;
6401 else
6402 {
6403 Item[] Items = await this.ServiceItemsDiscoveryAsync(Domain);
6404
6405 foreach (Item Component in Items)
6406 {
6407 e = await this.ServiceDiscoveryAsync(Component.JID);
6408 if (e.HasFeature(Feature))
6409 {
6410 Result = Component.JID;
6411 break;
6412 }
6413 }
6414 }
6415
6416 if (!string.IsNullOrEmpty(Result))
6417 {
6418 lock (this.services)
6419 {
6420 this.services[Key] = Result;
6421 }
6422 }
6423
6424 return Result;
6425 }
6426
6427 #endregion
6428
6429 #region Push Notification
6430
6431 #region New Token
6432
6433 private async Task NewTokenHandler(object Sender, IqEventArgs e)
6434 {
6436
6437 if (!e.From.HasAccount)
6438 {
6439 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
6440 return;
6441 }
6442
6443 if (!this.IsServerDomain(e.From.Domain, true))
6444 {
6445 await e.IqErrorForbidden(e.To, "Push Forwarding service only available for clients on broker.", "en");
6446 return;
6447 }
6448
6449 if (!Enum.TryParse<PushMessagingService>(XML.Attribute(e.Query, "service"), out PushMessagingService Service))
6450 {
6451 await e.IqErrorBadRequest(e.To, "Unrecognized service.", "en");
6452 return;
6453 }
6454
6455 if (!Enum.TryParse<ClientType>(XML.Attribute(e.Query, "clientType"), out ClientType ClientType))
6456 {
6457 await e.IqErrorBadRequest(e.To, "Unrecognized client type.", "en");
6458 return;
6459 }
6460
6461 string Token = XML.Attribute(e.Query, "token");
6462 if (string.IsNullOrEmpty(Token))
6463 {
6464 await e.IqErrorBadRequest(e.To, "No token provided.", "en");
6465 return;
6466 }
6467
6468 PushNotificationToken TokenObj = await TryGetPushNotificationToken(BareJid);
6469 DateTime TP = DateTime.UtcNow;
6470
6471 if (TokenObj is null)
6472 {
6473 TokenObj = new PushNotificationToken()
6474 {
6475 BareJid = BareJid,
6476 Created = TP,
6477 NrUpdates = 1,
6478 Token = Token,
6479 Service = Service,
6481 Updated = TP
6482 };
6483
6484 tokens[BareJid] = TokenObj;
6485
6486 await Database.Insert(TokenObj);
6487 }
6488 else
6489 {
6490 TokenObj.Token = Token;
6491 TokenObj.Service = Service;
6492 TokenObj.ClientType = ClientType;
6493 TokenObj.Updated = TP;
6494 TokenObj.NrUpdates++;
6495
6496 await Database.Update(TokenObj);
6497 }
6498
6499 await e.IqResult(string.Empty, e.To);
6500 }
6501
6507 public static async Task<PushNotificationToken> TryGetPushNotificationToken(CaseInsensitiveString BareJid)
6508 {
6509 if (tokens.TryGetValue(BareJid, out PushNotificationToken Token))
6510 return Token;
6511
6512 Token = await Database.FindFirstDeleteRest<PushNotificationToken>(new FilterFieldEqualTo("BareJid", BareJid));
6513 tokens[BareJid] = Token; // Store null, if none is found, to avoid repetitive searches.
6514
6515 return Token;
6516 }
6517
6518 #endregion
6519
6520 #region Remove Token
6521
6522 private async Task RemoveTokenHandler(object Sender, IqEventArgs e)
6523 {
6525
6526 if (!e.From.HasAccount)
6527 {
6528 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
6529 return;
6530 }
6531
6532 if (!this.IsServerDomain(e.From.Domain, true))
6533 {
6534 await e.IqErrorForbidden(e.To, "Push Forwarding service only available for clients on broker.", "en");
6535 return;
6536 }
6537
6538 PushNotificationToken TokenObj = await TryGetPushNotificationToken(BareJid);
6539 if (TokenObj is null)
6540 {
6541 await e.IqErrorItemNotFound(e.To, "Token not found.", "en");
6542 return;
6543 }
6544
6545 tokens.Remove(BareJid);
6546 await Database.Delete(TokenObj);
6547
6548 await e.IqResult(string.Empty, e.To);
6549 }
6550
6551 #endregion
6552
6553 #region Clear Rules
6554
6555 private async Task ClearRulesHandler(object Sender, IqEventArgs e)
6556 {
6558
6559 if (!e.From.HasAccount)
6560 {
6561 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
6562 return;
6563 }
6564
6565 if (!this.IsServerDomain(e.From.Domain, true))
6566 {
6567 await e.IqErrorForbidden(e.To, "Push Forwarding service only available for clients on broker.", "en");
6568 return;
6569 }
6570
6571 foreach (PushNotificationRule Rule in await Database.FindDelete<PushNotificationRule>(new FilterFieldEqualTo("BareJid", BareJid)))
6572 rules.Remove(RuleKey(Rule));
6573
6574 await e.IqResult(string.Empty, e.To);
6575 }
6576
6577 private static string RuleKey(PushNotificationRule Rule)
6578 {
6579 return RuleKey(Rule.BareJid, Rule.MessageType, Rule.LocalName, Rule.Namespace);
6580 }
6581
6582 private static string RuleKey(CaseInsensitiveString BareJid, string MessageType, string LocalName, string Namespace)
6583 {
6584 StringBuilder sb = new StringBuilder();
6585
6586 sb.Append(BareJid.LowerCase);
6587 sb.Append(' ');
6588 sb.Append(MessageType);
6589 sb.Append(' ');
6590 sb.Append(LocalName);
6591 sb.Append(' ');
6592 sb.Append(Namespace);
6593
6594 return sb.ToString();
6595 }
6596
6597 #endregion
6598
6599 #region Add Rule
6600
6601 private async Task AddRuleHandler(object Sender, IqEventArgs e)
6602 {
6604
6605 if (!e.From.HasAccount)
6606 {
6607 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
6608 return;
6609 }
6610
6611 if (!this.IsServerDomain(e.From.Domain, true))
6612 {
6613 await e.IqErrorForbidden(e.To, "Push Forwarding service only available for clients on broker.", "en");
6614 return;
6615 }
6616
6617 string MessageType = XML.Attribute(e.Query, "type");
6618 string LocalName = XML.Attribute(e.Query, "localName");
6619 string Namespace = XML.Attribute(e.Query, "namespace");
6620 string Channel = XML.Attribute(e.Query, "channel");
6621 string MessageVariable = XML.Attribute(e.Query, "variable");
6622 string PatternMatchingScript = null;
6623 string ContentScript = null;
6624
6625 foreach (XmlNode N in e.Query.ChildNodes)
6626 {
6627 if (N is XmlElement E && E.NamespaceURI == MessagePushNamespace)
6628 {
6629 switch (E.LocalName)
6630 {
6631 case "PatternMatching":
6632 PatternMatchingScript = E.InnerText.Trim();
6633
6634 try
6635 {
6636 Expression Exp = new Expression(PatternMatchingScript);
6637
6638 if (!CheckExpressionSafe(Exp, out ScriptNode Prohibited))
6639 {
6640 await e.IqErrorForbidden(e.To, "Pattern Matching Script contains prohibited elements: " +
6641 Prohibited?.SubExpression, "en");
6642
6643 return;
6644 }
6645 }
6646 catch (Exception)
6647 {
6648 await e.IqErrorBadRequest(e.To, "Invalid pattern-matching script.", "en");
6649 return;
6650 }
6651 break;
6652
6653 case "Content":
6654 ContentScript = E.InnerText.Trim();
6655
6656 try
6657 {
6658 Expression Exp = new Expression(ContentScript);
6659
6660 if (!CheckExpressionSafe(Exp, out ScriptNode Prohibited))
6661 {
6662 await e.IqErrorForbidden(e.To, "Content Script contains prohibited elements: " +
6663 Prohibited?.SubExpression, "en");
6664
6665 return;
6666 }
6667 }
6668 catch (Exception)
6669 {
6670 await e.IqErrorBadRequest(e.To, "Invalid content script.", "en");
6671 return;
6672 }
6673 break;
6674
6675 default:
6676 await e.IqErrorBadRequest(e.To, "Unrecognized child element: " + e.Language, "en");
6677 return;
6678 }
6679 }
6680 }
6681
6682 PushNotificationRule Rule = await TryGetPushNotificationRule(BareJid, MessageType, LocalName, Namespace);
6683
6684 if (Rule is null)
6685 {
6686 Rule = new PushNotificationRule()
6687 {
6688 BareJid = BareJid,
6689 LocalName = LocalName,
6690 Namespace = Namespace,
6692 Channel = Channel,
6693 MessageVariable = MessageVariable,
6694 PatternMatchingScript = PatternMatchingScript,
6695 ContentScript = ContentScript
6696 };
6697
6698 await Database.Insert(Rule);
6699
6700 rules[RuleKey(Rule)] = Rule;
6701 }
6702 else
6703 {
6704 Rule.Channel = Channel;
6705 Rule.MessageVariable = MessageVariable;
6706 Rule.PatternMatchingScript = PatternMatchingScript;
6707 Rule.ContentScript = ContentScript;
6708
6709 await Database.Update(Rule);
6710 }
6711
6712 await e.IqResult(string.Empty, e.To);
6713 }
6714
6715 private readonly static Assembly scriptContent = typeof(GraphEncoder).Assembly;
6716 private readonly static Assembly[] prohibitedAssemblies = new Assembly[]
6717 {
6718 scriptContent, // Waher.Script.Content
6719 typeof(FractalGraph).Assembly, // Waher.Script.Fractals
6720 typeof(WhoIs).Assembly, // Waher.Script.Networking
6721 typeof(Select).Assembly, // Waher.Script.Persistence
6722 typeof(FtsCollection).Assembly, // Waher.Script FullTextSearch
6723 typeof(ConnectMsSql).Assembly, // Waher.Script.Data
6724 typeof(ShellExecute).Assembly // Waher.Script.System
6725 };
6726
6733 public static bool CheckExpressionSafe(Expression Expression, out ScriptNode Prohibited)
6734 {
6735 return CheckExpressionSafe(Expression, false, false, false, out Prohibited);
6736 }
6737
6747 public static bool CheckExpressionSafe(Expression Expression, bool AllowNamedMembers, bool AllowError,
6748 bool AllowCustomFunctions, out ScriptNode Prohibited)
6749 {
6750 ScriptNode Prohibited2 = null;
6751 bool Safe = Expression.ForAll((ScriptNode Node, out ScriptNode NewNode, object State) =>
6752 {
6753 NewNode = null;
6754
6755 Assembly Assembly = Node.GetType().Assembly;
6756
6757 foreach (Assembly A in prohibitedAssemblies)
6758 {
6759 if (A.FullName == Assembly.FullName)
6760 {
6761 if (A == scriptContent &&
6762 (Node is Script.Content.Functions.Duration ||
6763 Node.GetType().Namespace == typeof(Utf8Encode).Namespace))
6764 {
6765 return true;
6766 }
6767
6768 Prohibited2 = Node;
6769 return false;
6770 }
6771 }
6772
6773 if ((Node is NamedMember && !AllowNamedMembers) ||
6774 (Node is NamedMemberAssignment && !AllowNamedMembers) ||
6775 (Node is LambdaDefinition && !AllowCustomFunctions) ||
6776 Node is NamedMethodCall ||
6777 Node is DynamicFunctionCall ||
6778 Node is DynamicMember ||
6779 Node is Create ||
6780 Node is Destroy ||
6781 (Node is Error && !AllowError))
6782 {
6783 Prohibited2 = Node;
6784 return false;
6785 }
6786
6787 return true;
6788
6789 }, null, SearchMethod.TreeOrder);
6790
6791 Prohibited = Prohibited2;
6792 return Safe;
6793 }
6794
6803 public static async Task<PushNotificationRule> TryGetPushNotificationRule(CaseInsensitiveString BareJid, string MessageType,
6804 string LocalName, string Namespace)
6805 {
6806 string Key = RuleKey(BareJid, MessageType, LocalName, Namespace);
6807
6808 if (rules.TryGetValue(Key, out PushNotificationRule Rule))
6809 return Rule;
6810
6811 Rule = await Database.FindFirstDeleteRest<PushNotificationRule>(new FilterAnd(
6812 new FilterFieldEqualTo("BareJid", BareJid),
6813 new FilterFieldEqualTo("MessageType", MessageType),
6814 new FilterFieldEqualTo("LocalName", LocalName),
6815 new FilterFieldEqualTo("Namespace", Namespace)));
6816 rules[Key] = Rule; // Store null, if none is found, to avoid repetitive searches.
6817
6818 return Rule;
6819 }
6820
6821 #endregion
6822
6823 #region Remove Rule
6824
6825 private async Task RemoveRuleHandler(object Sender, IqEventArgs e)
6826 {
6828
6829 if (!e.From.HasAccount)
6830 {
6831 await e.IqErrorItemNotFound(e.To, "Account not found.", "en");
6832 return;
6833 }
6834
6835 if (!this.IsServerDomain(e.From.Domain, true))
6836 {
6837 await e.IqErrorForbidden(e.To, "Push Forwarding service only available for clients on broker.", "en");
6838 return;
6839 }
6840
6841 string MessageType = XML.Attribute(e.Query, "type");
6842 string LocalName = XML.Attribute(e.Query, "localName");
6843 string Namespace = XML.Attribute(e.Query, "namespace");
6844
6845 PushNotificationRule Rule = await TryGetPushNotificationRule(BareJid, MessageType, LocalName, Namespace);
6846
6847 if (Rule is null)
6848 {
6849 await e.IqErrorItemNotFound(e.To, "Rule not found.", "en");
6850 return;
6851 }
6852
6853 rules.Remove(RuleKey(Rule));
6854 await Database.Delete(Rule);
6855
6856 await e.IqResult(string.Empty, e.To);
6857 }
6858
6859 #endregion
6860
6861 #endregion
6862
6863 // TODO: Retries
6864 }
6865}
CDATA content.
Definition: CDATA.cs:12
string Content
CDATA Content
Definition: CDATA.cs:33
string Name
Attribute 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:12
static string EntityToCharacter(string Entity)
Converts an HTML entity into a character.
Definition: HtmlEntity.cs:71
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< object > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
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, untrasnformed 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:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
static string HtmlValueEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe or the quote.
Definition: XML.cs:209
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:27
static string HtmlAttributeEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe.
Definition: XML.cs:119
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Definition: Log.cs:334
static void Notice(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a notice event.
Definition: Log.cs:450
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.
void Continue()
Continues reading from the socket, if paused in an event handler.
Simple base class for classes implementing communication protocols.
Task Information(string Comment)
Called to inform the viewer of something.
DNS resolver, as defined in:
Definition: DnsResolver.cs:32
static async Task< string[]> LookupMailExchange(string DomainName)
Looks up the Mail Exchanges related to a given domain name.
Definition: DnsResolver.cs:620
static Task< SRV > LookupServiceEndpoint(string DomainName, string ServiceName, string Protocol)
Looks up a service endpoint for a domain. If multiple are available, an appropriate one is selected a...
Definition: DnsResolver.cs:825
Base class of all HTTP Exceptions.
Implements an HTTP server.
Definition: HttpServer.cs:36
HttpResource Register(HttpResource Resource)
Registers a resource with the server.
Definition: HttpServer.cs:1287
const int DefaultHttpPort
Default HTTP Port (80).
Definition: HttpServer.cs:40
const int DefaultHttpsPort
Default HTTPS port (443).
Definition: HttpServer.cs:45
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
DateTimeOffset? Date
Date of message, if defined
Definition: SmtpMessage.cs:64
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
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:44
const int DefaultSmtpPort
Default SMTP Port (25).
Definition: SmtpServer.cs:48
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
Base class for components.
Definition: Component.cs:16
CaseInsensitiveString Subdomain
Subdomain name.
Definition: Component.cs:76
virtual void Dispose()
IDisposable.Dispose
Definition: Component.cs:101
async Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Message stanza.
Definition: Component.cs:481
virtual bool SupportsAccounts
If the component supports accounts (true), or if the subdomain name is the only valid address.
Definition: Component.cs:115
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:243
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:201
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:187
XmppAddress To
To address attribute
Definition: IqEventArgs.cs:88
async Task IqError(string ErrorType, string Xml, XmppAddress From, string ErrorText, string Language)
Returns an error response to the current request.
Definition: IqEventArgs.cs:137
Task IqErrorBadRequest(XmppAddress From, string ErrorText, string Language)
Returns a bad-request error.
Definition: IqEventArgs.cs:159
Task IqErrorForbidden(XmppAddress From, string ErrorText, string Language)
Returns a forbidden error.
Definition: IqEventArgs.cs:229
Maintains a set of IQ responses, for a limited time.
Definition: IqResponses.cs:18
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.
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
bool HasFeature(string Feature)
Checks if the remote entity supports a specific feature.
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:10
string Content
Literal XML content.
Definition: Stanza.cs:54
XmlElement StanzaElement
Stanza element.
Definition: Stanza.cs:114
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
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
Manages an XMPP server-to-server connection.
XmppS2sState State
Current state of connection.
Task< bool > Connect()
Connects to the server.
Connectivity information for a domain.
Definition: XmppServer.cs:2480
CaseInsensitiveString Host
Host name
Definition: XmppServer.cs:2494
S2sType Type
Type of S2S connection
Definition: XmppServer.cs:2484
bool TrustCertificate
If certificate should be trusted
Definition: XmppServer.cs:2504
CaseInsensitiveString Domain
Domain name
Definition: XmppServer.cs:2489
Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Message stanza.
Definition: XmppServer.cs:2633
const string DiscoveryItemsNamespace
http://jabber.org/protocol/disco#items (XEP-0030)
Definition: XmppServer.cs:133
string ClientSnifferPath
If separate sniffers are to be created for each connected client, set this property to the file path ...
Definition: XmppServer.cs:934
const string ExtendedAddressingNamespace
http://jabber.org/protocol/address (XEP-0033)
Definition: XmppServer.cs:138
const string AbuseReportingNamespace
urn:xmpp:reporting:reason:abuse:0 (XEP-0377)
Definition: XmppServer.cs:203
string GetRandomHexString(int NrBytes)
Generates a random hexadecimal string.
Definition: XmppServer.cs:689
Task< DateTime?> GetEarliestLoginOpportunity(IClientConnection Connection)
Evaluates when a client is allowed to login.
Definition: XmppServer.cs:1476
async Task< bool > GetLastPresence(CaseInsensitiveString BareJid, EventHandlerAsync< PresenceEventArgs > Callback, object State)
Gets the last presence of a bare JID.
Definition: XmppServer.cs:3174
int[] OpenS2SPorts
S2S Ports successfully opened.
Definition: XmppServer.cs:1053
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:370
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:2189
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters a message handler.
Definition: XmppServer.cs:1760
const string RosterNamespace
jabber:iq:roster (RFC 6121)
Definition: XmppServer.cs:113
const string VCardNamespace
vcard-temp (XEP-0054)
Definition: XmppServer.cs:148
static Scheduler Scheduler
Scheduler
Definition: XmppServer.cs:655
const string BlockingCommandNamespace
urn:xmpp:blocking (XEP-0191)
Definition: XmppServer.cs:168
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Set handler.
Definition: XmppServer.cs:1659
Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Presence stanza.
Definition: XmppServer.cs:3065
EventHandlerAsync< PresenceEventArgs > OnPresenceLocalSender
If a local sender sends a presence stanza
Definition: XmppServer.cs:3053
IClientConnection[] GetClientConnections()
Get active client connections
Definition: XmppServer.cs:789
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:332
int[] OpenC2SPorts
C2S Ports successfully opened.
Definition: XmppServer.cs:1042
const string DiscoveryNamespace
http://jabber.org/protocol/disco#info (XEP-0030)
Definition: XmppServer.cs:128
void UpdateCertificate(X509Certificate ServerCertificate)
Updates the server certificate
Definition: XmppServer.cs:906
const string MessagePushNamespace
http://waher.se/Schema/PushNotification.xsd
Definition: XmppServer.cs:223
CommunicationLayer S2sSniffers
Sniffers for XMPP S2S communication.
Definition: XmppServer.cs:650
Task< IqResultEventArgs > IqRequest(string Type, string From, string To, string Language, string ContentXml)
Sends an IQ stanza to a recipient.
Definition: XmppServer.cs:3371
S2sEndpointStatistics[] GetServerConnectionStatistics()
Gets S2S connection statistics.
Definition: XmppServer.cs:845
EventHandlerAsync< ClientConnectionEventArgs > ClientConnectionUpdated
Event raised when a client connection has been updated.
Definition: XmppServer.cs:1353
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:3412
const int DefaultConnectionBacklog
Default Connection backlog (10).
Definition: XmppServer.cs:78
static void GetErrorInformation(Exception ex, out string Type, out string Xml)
Converts an Exception to an XMPP error message.
Definition: XmppServer.cs:3076
S2sEndpointStatistics GetS2sStatistics(CaseInsensitiveString Endpoint, string Type)
Gets available S2S Endpoint statistics for an endpoint.
Definition: XmppServer.cs:722
bool TryGetClientConnections(string BareJID, out IClientConnection[] Connections)
Tries to get available connections for a given client.
Definition: XmppServer.cs:818
const string SaslNamespace
urn:ietf:params:xml:ns:xmpp-sasl
Definition: XmppServer.cs:108
Task< Item[]> ServiceItemsDiscoveryAsync(string To)
Performs a service items discovery request
Definition: XmppServer.cs:6313
static byte[] GetRandomNumbers(int NrBytes)
Generates a set of random numbers.
Definition: XmppServer.cs:672
virtual Task< bool > MessageError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends a message error stanza.
Definition: XmppServer.cs:3710
virtual async Task< bool > IqError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
Sends an IQ error stanza.
Definition: XmppServer.cs:3607
bool TryGetClientConnection(string FullJID, out IClientConnection Connection)
Tries to get an active client connection.
Definition: XmppServer.cs:807
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:3427
async Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Message stanza.
Definition: XmppServer.cs:2060
virtual async Task< bool > PresenceError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends a presence error stanza.
Definition: XmppServer.cs:3641
Task< ServiceDiscoveryResult > ServiceDiscoveryAsync(string To)
Performs a service discovery request
Definition: XmppServer.cs:6239
async Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
IQ stanza.
Definition: XmppServer.cs:1811
virtual Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Sends a message stanza.
Definition: XmppServer.cs:3697
async Task< IqResultEventArgs > IqRequest(string Type, XmppAddress From, XmppAddress To, string Language, string ContentXml)
Sends an IQ stanza to a recipient.
Definition: XmppServer.cs:3385
async Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Presence stanza.
Definition: XmppServer.cs:2691
const string ReportingNamespace
urn:xmpp:reporting:0 (XEP-0377)
Definition: XmppServer.cs:198
const string BlockingCommandErrorNamespace
urn:xmpp:blocking:errors (XEP-0191)
Definition: XmppServer.cs:173
const string ContentNamespace
urn:xmpp:content
Definition: XmppServer.cs:218
Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
IQ stanza.
Definition: XmppServer.cs:1952
virtual async Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Sends a presence stanza.
Definition: XmppServer.cs:3678
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:2212
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:3317
const string DataFormsNamespace
jabber:x:data (XEP-0004)
Definition: XmppServer.cs:118
EventHandlerAsync< ServerConnectionEventArgs > ServerConnectionAdded
Event raised when a server connection has been added.
Definition: XmppServer.cs:1363
const string OfflineMessagesNamespace
msgoffline (XEP-0160)
Definition: XmppServer.cs:163
static async Task< PushNotificationToken > TryGetPushNotificationToken(CaseInsensitiveString BareJid)
Tries to get a push notification token for a client, if one exists.
Definition: XmppServer.cs:6507
static bool IsValidUserName(string UserName)
Checks if a user name contains invalid characters.
Definition: XmppServer.cs:4685
CaseInsensitiveString[] RemoteConnections
Current remote connections.
Definition: XmppServer.cs:770
const string AlternativesNamespace
http://waher.se/Schema/AlternativeNames.xsd
Definition: XmppServer.cs:228
bool TryGetS2sStatistics(CaseInsensitiveString Endpoint, out S2sEndpointStatistics Stat)
Tries to get available S2S Endpoint statistics for an endpoint.
Definition: XmppServer.cs:708
int NrServerConnections
Number of server connections.
Definition: XmppServer.cs:837
bool Disposed
If the server has been disposed.
Definition: XmppServer.cs:1036
const string SoftwareVersionNamespace
jabber:iq:version (XEP-0092)
Definition: XmppServer.cs:158
string DomainSnifferPath
If separate sniffers are to be created for each federated domain, set this property to the file path ...
Definition: XmppServer.cs:924
const int DefaultBufferSize
Default buffer size (16384).
Definition: XmppServer.cs:83
const string PrivateXmlStorageNamespace
jabber:iq:private (XEP-0049)
Definition: XmppServer.cs:143
CommunicationLayer C2sSniffers
Sniffers for XMPP C2S communication.
Definition: XmppServer.cs:645
bool UnregisterComponent(IComponent Component)
Unregisters a component from the server.
Definition: XmppServer.cs:1128
const string PingNamespace
urn:xmpp:ping (XEP-0199)
Definition: XmppServer.cs:178
bool IsServerDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the server domain, or optionally, an alternative domain.
Definition: XmppServer.cs:861
IComponent[] Components
Registered components
Definition: XmppServer.cs:1063
X509Certificate ServerCertificate
Server domain certificate.
Definition: XmppServer.cs:898
bool EncryptionRequired
If C2S encryption is requried.
Definition: XmppServer.cs:915
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Get handler.
Definition: XmppServer.cs:1712
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:1248
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:350
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
Definition: XmppServer.cs:1647
const int DefaultS2sPort
Default Server-to-Server Port (5269).
Definition: XmppServer.cs:73
CaseInsensitiveString Domain
Domain name.
Definition: XmppServer.cs:882
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:6747
const string TimeNamespace
urn:xmpp:time (XEP-0202)
Definition: XmppServer.cs:183
const int DefaultC2sPort
Default Client-to-Server Port (5222).
Definition: XmppServer.cs:68
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:6733
async Task< CaseInsensitiveString > FindComponentAsync(CaseInsensitiveString Jid, CaseInsensitiveString Feature)
Finds a component having a specific feature, servicing a JID.
Definition: XmppServer.cs:6382
async 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:3334
const string RegisterNamespace
jabber:iq:register (XEP-0077)
Definition: XmppServer.cs:153
const string StanzaNamespace
urn:ietf:params:xml:ns:xmpp-stanzas (RFC 6120)
Definition: XmppServer.cs:98
bool TryGetS2sEndpoint(string RemoteDomain, out IS2SEndpoint Endpoint)
Tries to get a server-to-server connection state object.
Definition: XmppServer.cs:2259
void Dispose()
IDisposable.Dispose
Definition: XmppServer.cs:947
static Task< XmppServer > Create(CaseInsensitiveString Domain, CaseInsensitiveString[] AlternativeDomains, X509Certificate ServerCertificate, bool EncryptionRequired, IXmppServerPersistenceLayer PersistenceLayer)
Creates an instance of an XMPP server.
Definition: XmppServer.cs:316
EventHandlerAsync< ClientConnectionEventArgs > ClientConnectionRemoved
Event raised when a client connection has been removed.
Definition: XmppServer.cs:1358
virtual async Task< bool > IqResult(string Id, XmppAddress To, XmppAddress From, string ResultXml)
Sends an IQ result stanza.
Definition: XmppServer.cs:3624
Statistics.CommunicationStatistics GetCommunicationStatisticsSinceLast()
Gets communication statistics since last call.
Definition: XmppServer.cs:5198
const string DelayedDeliveryNamespace
urn:xmpp:delay (XEP-0203)
Definition: XmppServer.cs:188
static async Task< S2SRec > GetDomain(CaseInsensitiveString DomainOrSubdomain)
Gets information about a domain.
Definition: XmppServer.cs:2366
const string MailNamespace
urn:xmpp:smtp
Definition: XmppServer.cs:213
EventHandlerAsync< ClientConnectionEventArgs > ClientConnectionAdded
Event raised when a client connection has been added.
Definition: XmppServer.cs:1348
const string OAuth1FormSignatureNamespace
urn:xmpp:xdata:signature:oauth1 (XEP-0348)
Definition: XmppServer.cs:193
EventHandlerAsync< ServerConnectionEventArgs > ServerConnectionRemoved
Event raised when a server connection has been removed.
Definition: XmppServer.cs:1373
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:6803
const string StreamsNamespace
urn:ietf:params:xml:ns:xmpp-streams
Definition: XmppServer.cs:103
string NewId(int NrBytes)
Generates a new ID.
Definition: XmppServer.cs:662
const string AvatarStorageNamespace
storage:client:avatar (XEP-0008)
Definition: XmppServer.cs:123
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Set handler.
Definition: XmppServer.cs:1725
virtual async Task< string > IqError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends an IQ error stanza.
Definition: XmppServer.cs:3589
EventHandlerAsync< ServerConnectionEventArgs > ServerConnectionUpdated
Event raised when a server connection has been updated.
Definition: XmppServer.cs:1368
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers a message handler.
Definition: XmppServer.cs:1688
async Task< Item[]> ServiceItemsDiscoveryAsync(string To, string Node)
Performs a service items discovery request
Definition: XmppServer.cs:6323
async Task ProcessMessage(SmtpMessage Message)
Processes an incoming SMTP message.
Definition: XmppServer.cs:5404
virtual async Task< bool > PresenceError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
Sends a presence error stanza.
Definition: XmppServer.cs:3659
async Task< bool > Presence(string Type, string Id, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Presence stanza.
Definition: XmppServer.cs:2647
static CaseInsensitiveString GetBareJID(CaseInsensitiveString JID)
Gets the Bare JID from a JID, which may be a Full JID.
Definition: XmppServer.cs:1462
CaseInsensitiveString[] AlternativeDomains
Alternative domain names.
Definition: XmppServer.cs:890
bool RegisterComponent(IComponent Component)
Registers a component with the server.
Definition: XmppServer.cs:1096
const string SpamReportingNamespace
urn:xmpp:reporting:reason:abuse:0 (XEP-0377)
Definition: XmppServer.cs:208
async Task< bool > SendMailMessage(CaseInsensitiveString From, CaseInsensitiveString To, string Subject, string Markdown)
Sends a mail message
Definition: XmppServer.cs:6204
async Task< ServiceDiscoveryResult > ServiceDiscoveryAsync(string To, string Node)
Performs a service discovery request
Definition: XmppServer.cs:6250
async Task< Tuple< bool, IRecipient > > TryGetRecipient(XmppAddress To, XmppAddress From)
Tries to get the recipient of a stanza.
Definition: XmppServer.cs:1580
EventHandlerAsync< PresenceEventArgs > OnPresenceLocalRecipient
If a local recipient receives a presence stanza
Definition: XmppServer.cs:3048
int NrClientConnections
Number of client connections.
Definition: XmppServer.cs:781
async Task< int > DeleteOldMailContent(DateTime OlderThan)
Deletes old mail content.
Definition: XmppServer.cs:6187
Represents a case-insensitive string.
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[] 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:19
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:879
static async Task DeleteLazy(object Object)
Deletes an object in the database, if unlocked. If locked, object will be deleted at next opportunity...
Definition: Database.cs:756
static async Task InsertLazy(object Object)
Inserts an object into the database, if unlocked. If locked, object will be inserted at next opportun...
Definition: Database.cs:156
static async Task UpdateLazy(object Object)
Updates an object in the database, if unlocked. If locked, object will be updated at next opportunity...
Definition: Database.cs:687
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:717
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:247
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
This filter selects objects that conform to all child-filters provided.
Definition: FilterAnd.cs:10
This filter selects objects that have a named field equal to a given value.
This filter selects objects that have a named field lesser or equal to a given value.
Implements an in-memory cache.
Definition: Cache.cs:15
ValueType[] GetValues()
Gets all available values in the cache.
Definition: Cache.cs:275
bool ContainsKey(KeyType Key)
Checks if a key is available in the cache.
Definition: Cache.cs:296
void Dispose()
IDisposable.Dispose
Definition: Cache.cs:74
int Count
Number of items in cache
Definition: Cache.cs:229
bool Remove(KeyType Key)
Removes an item from the cache.
Definition: Cache.cs:451
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Definition: Cache.cs:203
KeyType[] GetKeys()
Gets all available keys in the cache.
Definition: Cache.cs:258
void Add(KeyType Key, ValueType Value)
Adds an item to the cache.
Definition: Cache.cs:338
void Clear()
Clears the cache.
Definition: Cache.cs:484
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 that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static Type[] NoTypes
Contains an empty array of types.
Definition: Types.cs:543
static object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:548
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:84
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
static async Task< bool > SetAsync(string Key, string Value)
Sets a string-valued setting.
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
Definition: Scheduler.cs:26
void Dispose()
IDisposable.Dispose
Definition: Scheduler.cs:46
DateTime Add(DateTime When, ScheduledEventCallback Callback, object State)
Adds an event.
Definition: Scheduler.cs:66
Encodes graphs as images
Definition: GraphEncoder.cs:16
Generates a callback function based on script.
Definition: Callback.cs:20
Creates a connection to an external MS SQL database.
Definition: ConnectMsSql.cs:16
Class managing a script expression.
Definition: Expression.cs:39
bool ForAll(ScriptNodeEventHandler Callback, object State, bool DepthFirst)
Calls the callback method for all script nodes defined for the expression.
Definition: Expression.cs:5196
Defines a clickable fractal graph in the complex plane.
Definition: FractalGraph.cs:22
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:13
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
Executes a SELECT statement against the object database.
Definition: Select.cs:20
ShellExecute(FileName,Arguments,WorkFolder)
Definition: ShellExecute.cs:15
Contains methods for simple hash calculations.
Definition: Hashes.cs:59
static string ComputeHMACSHA256HashString(byte[] Key, byte[] Data)
Computes the HMAC-SHA-256 hash of a block of binary data.
Definition: Hashes.cs:574
static byte[] ComputeHMACSHA1Hash(byte[] Key, byte[] Data)
Computes the HMAC-SHA-1 hash of a block of binary data.
Definition: Hashes.cs:556
static string BinaryToString(byte[] Data)
Converts an array of bytes to a string with their hexadecimal representations (in lower case).
Definition: Hashes.cs:65
static byte[] ComputeSHA256Hash(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Definition: Hashes.cs:348
static string ComputeSHA1HashString(byte[] Data)
Computes the SHA-1 hash of a block of binary data.
Definition: Hashes.cs:274
Class that monitors login events, and help applications determine malicious intent....
Definition: LoginAuditor.cs:26
static async void Success(string Message, string UserName, string RemoteEndpoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a successful login attempt.
async Task< DateTime?> GetEarliestLoginOpportunity(string RemoteEndpoint, string Protocol)
Checks when a remote endpoint can login.
static async void Fail(string Message, string UserName, string RemoteEndpoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a failed login attempt.
Task Information(string Comment)
Called to inform the viewer of something.
Task 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 Protocol
String representing protocol being used.
void ResetState(bool Authenticated)
Resets the state machine.
CaseInsensitiveString UserName
User name
string RemoteEndpoint
Remote endpoint.
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
Definition: ISniffer.cs:11
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
Task DisposeAsync()
Disposes the connection
string Type
Type of endpoint
Definition: IEndpoint.cs:15
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.
Interface for roster items.
Definition: IRosterItem.cs:43
Interface for XMPP S2S endpoints
Definition: IS2sEndpoint.cs:10
CaseInsensitiveString RemoteDomain
Connection to domain.
Definition: IS2sEndpoint.cs:23
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.
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.
XmppS2sState
State of XMPP connection.
Definition: XmppS2sState.cs:7
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:12