5using System.Diagnostics;
8using System.Net.NetworkInformation;
11using System.Reflection;
13using System.Security.Cryptography.X509Certificates;
15using System.Threading;
16using System.Threading.Tasks;
235 private static readonly RandomNumberGenerator rnd = RandomNumberGenerator.Create();
236 private static readonly Dictionary<CaseInsensitiveString, S2SRec> remoteDomainLookup =
new Dictionary<CaseInsensitiveString, S2SRec>();
238 internal static readonly UTF8Encoding encoding =
new UTF8Encoding(
false,
false);
244 private readonly
SmtpServer smtpServer =
null;
245 private readonly
HttpServer httpServer =
null;
247 private LinkedList<TcpListener> c2sListeners =
new LinkedList<TcpListener>();
248 private LinkedList<TcpListener> s2sListeners =
new LinkedList<TcpListener>();
251 private readonly Dictionary<CaseInsensitiveString, List<IClientConnection>> connectionsPerBareJid =
new Dictionary<CaseInsensitiveString, List<IClientConnection>>();
252 private readonly Dictionary<string, EventHandlerAsync<IqEventArgs>> iqGetHandlers =
new Dictionary<string, EventHandlerAsync<IqEventArgs>>();
253 private readonly Dictionary<string, EventHandlerAsync<IqEventArgs>> iqSetHandlers =
new Dictionary<string, EventHandlerAsync<IqEventArgs>>();
254 private readonly Dictionary<string, EventHandlerAsync<MessageEventArgs>> messageHandlers =
new Dictionary<string, EventHandlerAsync<MessageEventArgs>>();
255 private readonly SortedDictionary<string, bool> features =
new SortedDictionary<string, bool>();
256 private readonly SortedDictionary<CaseInsensitiveString, IComponent> componentsBySubdomain =
new SortedDictionary<CaseInsensitiveString, IComponent>();
257 private readonly SortedDictionary<CaseInsensitiveString, IComponent> componentsByFulldomain =
new SortedDictionary<CaseInsensitiveString, IComponent>();
258 private readonly Dictionary<string, PendingRequest> pendingRequestsById =
new Dictionary<string, PendingRequest>();
259 private readonly SortedDictionary<DateTime, PendingRequest> pendingRequestsByTimeout =
new SortedDictionary<DateTime, PendingRequest>();
260 private readonly
IqResponses responses =
new IqResponses(TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(10));
261 private readonly SortedDictionary<CaseInsensitiveString, S2sEndpointStatistics> s2sStatistics =
new SortedDictionary<CaseInsensitiveString, S2sEndpointStatistics>();
262 private Dictionary<string, Statistic> stanzasPerStanzaType =
new Dictionary<string, Statistic>();
263 private Dictionary<string, Statistic> stanzasPerFromDomain =
new Dictionary<string, Statistic>();
264 private Dictionary<string, Statistic> stanzasPerToDomain =
new Dictionary<string, Statistic>();
265 private Dictionary<string, Statistic> stanzasPerFromBareJid =
new Dictionary<string, Statistic>();
266 private Dictionary<string, Statistic> stanzasPerToBareJid =
new Dictionary<string, Statistic>();
267 private Dictionary<string, Statistic> stanzasPerNamespace =
new Dictionary<string, Statistic>();
268 private Dictionary<string, Statistic> stanzasPerFqn =
new Dictionary<string, Statistic>();
269 private readonly
object statSync =
new object();
270 private DateTime lastStat = DateTime.Now;
275 private SortedDictionary<DateTime, IS2SEndpoint> temporaryConnections =
null;
276 private X509Certificate serverCertificate;
279 private Timer secondTimer =
null;
280 private readonly
byte[] sha256DialbackSecret;
281 private readonly
object synchObject =
new object();
283 private readonly Random gen =
new Random();
286 private readonly
string serverName;
287 private readonly
string serverVersion;
288 private readonly
string serverOS;
289 private string domainSnifferPath =
null;
290 private string clientSnifferPath =
null;
291 private readonly
bool encryptionRequired;
292 private bool disposed =
false;
295 private readonly
int defaultRetryTimeout = 5000;
296 private readonly
int defaultNrRetries = 5;
297 private readonly
int defaultMaxRetryTimeout =
int.MaxValue;
298 private readonly
bool defaultDropOff =
true;
299 private long nrBytesRx = 0;
300 private long nrBytesTx = 0;
301 private long nrStanzas = 0;
307 Log.Terminating += (Sender, e) =>
311 return Task.CompletedTask;
383 if (mechanisms is
null)
390 Types.OnInvalidated += Types_OnInvalidated;
394 mechanisms = await GetMechanisms();
402 private static readonly
object synchObj =
new object();
403 private static bool first =
true;
405 private static async Task<IAuthenticationMechanism[]> GetMechanisms()
407 Dictionary<string, bool> MechanismsFound =
new Dictionary<string, bool>();
408 List<IAuthenticationMechanism> Result =
new List<IAuthenticationMechanism>();
425 if (MechanismsFound.ContainsKey(Mechanism.
Name))
426 throw new Exception(
"Authentication mechanism collision." + T.FullName +
": " + Mechanism.
Name);
430 MechanismsFound[Mechanism.
Name] =
true;
431 Result.Add(Mechanism);
439 Result.Sort((m1, m2) => m2.Weight - m1.Weight);
441 return Result.ToArray();
444 private static async
void Types_OnInvalidated(
object Sender, EventArgs e)
448 mechanisms = await GetMechanisms();
488 throw new ArgumentException(
"Server Certificate must be provided, if encryption is required.", nameof(
ServerCertificate));
491 this.clientConnections.Removed += this.ClientConnections_Removed;
494 this.s2sEndpoints.Removed += this.S2sEndpoints_Removed;
496 Assembly ThisAssembly = typeof(
XmppServer).Assembly;
497 StackTrace Trace =
new StackTrace();
498 StackFrame[] Frames = Trace.GetFrames();
503 int c = Frames.Length;
508 Method = Frame.GetMethod();
509 Assembly = Method.DeclaringType.Assembly;
511 while (Assembly == ThisAssembly);
513 AssemblyName Name = Assembly.GetName();
514 string Title =
string.Empty;
515 string Product =
string.Empty;
516 string AssemblyName = Name.Name;
518 foreach (
object Attribute
in Assembly.GetCustomAttributes())
520 if (Attribute is AssemblyTitleAttribute AssemblyTitleAttribute)
521 Title = AssemblyTitleAttribute.Title;
522 else if (Attribute is AssemblyProductAttribute AssemblyProductAttribute)
523 Product = AssemblyProductAttribute.Product;
526 if (!
string.IsNullOrEmpty(Title))
527 this.serverName = Title;
528 else if (!
string.IsNullOrEmpty(Product))
529 this.serverName = Product;
531 this.serverName = AssemblyName;
533 this.serverVersion = Name.Version.ToString();
534 this.serverOS = Environment.OSVersion.ToString();
540 this.
RegisterIqGetHandler(
"query", PrivateXmlStorageNamespace, this.PrivateXmlStorageGet,
true);
541 this.
RegisterIqSetHandler(
"query", PrivateXmlStorageNamespace, this.PrivateXmlStorageSet,
false);
565 this.Initialize(ClientToServerPorts, ServerToServerPorts);
568 private void Initialize(
int[] ClientToServerPorts,
int[] ServerToServerPorts)
572 TcpListener Listener;
574 foreach (NetworkInterface Interface
in NetworkInterface.GetAllNetworkInterfaces())
576 if (Interface.OperationalStatus != OperationalStatus.Up)
579 IPInterfaceProperties
Properties = Interface.GetIPProperties();
581 foreach (UnicastIPAddressInformation UnicastAddress
in Properties.UnicastAddresses)
583 if ((UnicastAddress.Address.AddressFamily == AddressFamily.InterNetwork && Socket.OSSupportsIPv4) ||
584 (UnicastAddress.Address.AddressFamily == AddressFamily.InterNetworkV6 && Socket.OSSupportsIPv6))
586 if (!(ClientToServerPorts is
null))
588 foreach (
int C2sPort
in ClientToServerPorts)
592 this.c2sSniffers.Information(
"Opening port " + C2sPort.ToString() +
" on " + UnicastAddress.Address.ToString() +
".");
594 Listener =
new TcpListener(UnicastAddress.Address, C2sPort);
596 Listener.BeginAcceptTcpClient(this.AcceptTcpClientCallback,
new object[] { Listener,
false });
597 this.c2sListeners.AddLast(Listener);
599 this.c2sSniffers.Information(
"Port " + C2sPort.ToString() +
" on " + UnicastAddress.Address.ToString() +
" opened.");
603 Log.
Exception(ex, UnicastAddress.Address.ToString() +
":" + C2sPort);
608 if (!(ServerToServerPorts is
null))
610 foreach (
int S2sPort
in ServerToServerPorts)
614 this.s2sSniffers.Information(
"Opening port " + S2sPort.ToString() +
" on " + UnicastAddress.Address.ToString() +
".");
616 Listener =
new TcpListener(UnicastAddress.Address, S2sPort);
618 Listener.BeginAcceptTcpClient(this.AcceptTcpClientCallback,
new object[] { Listener,
true });
619 this.s2sListeners.AddLast(Listener);
621 this.s2sSniffers.Information(
"Port " + S2sPort.ToString() +
" on " + UnicastAddress.Address.ToString() +
" opened.");
625 Log.
Exception(ex, UnicastAddress.Address.ToString() +
":" + S2sPort);
633 this.secondTimer =
new Timer(this.SecondTimerCallback,
null, 1000, 1000);
635 if (!(this.smtpServer is
null))
636 this.smtpServer.MessageReceived += this.SmtpServer_MessageReceived;
681 byte[] Result =
new byte[NrBytes];
685 rnd.GetBytes(Result);
703 this.ConnectionClosed(e.
Value);
706 await e.
Value.DisposeAsync();
717 lock (this.s2sStatistics)
719 return this.s2sStatistics.TryGetValue(Endpoint, out Stat);
733 lock (this.s2sStatistics)
735 if (!this.s2sStatistics.TryGetValue(Endpoint, out Result))
738 this.s2sStatistics[Endpoint] = Result;
745 internal void S2sEndpointDisposed(
IS2SEndpoint Endpoint)
749 if (!(this.s2sEndpoints is
null) &&
750 !(Endpoint is
null) &&
753 Endpoint == Endpoint2)
761#if LogToWebHookTester
763 _ = Task.Run(async () =>
768 new Dictionary<string, object>()
770 {
"id", e.Value.Id.ToString() },
771 {
"event",
"S2sEndpoints_Removed" },
772 {
"remote", e.Key.Value },
773 {
"value", JSON.Encode(e.Value, true) },
774 {
"reason", e.Reason }
776 new KeyValuePair<string, string>(
"Accept",
"application/json"));
785 e.
Value.RemoteDomain,
string.Empty,
"XmppCloseS2s",
786 new KeyValuePair<string, object>(
"Reason", e.
Reason));
793 XmppS2SEndpoint.OnStateChanged -= this.Endpoint_OnStateChanged;
797 await e.
Value.DisposeAsync(
"S2S connection removed: " + e.
Reason.ToString());
810 return this.s2sEndpoints.GetKeys();
817 public int NrClientConnections
819 get => this.clientConnections.
Count;
830 Array.Sort(Connections, (c1, c2) =>
846 return this.clientConnections.
TryGetValue(FullJID, out Connection);
857 lock (this.connectionsPerBareJid)
859 if (this.connectionsPerBareJid.TryGetValue(BareJID, out List<IClientConnection> Connections2))
861 Connections = Connections2.ToArray();
873 public int NrServerConnections
875 get => this.s2sEndpoints.Count;
884 lock (this.s2sStatistics)
887 this.s2sStatistics.Values.CopyTo(Result, 0);
900 if (Domain == this.domain)
903 if (IncludeAlternativeDomains)
931 get => this.alternativeDomains;
937 public X509Certificate ServerCertificate
939 get => this.serverCertificate;
948 this.serverCertificate = ServerCertificate;
954 public bool EncryptionRequired
956 get => this.encryptionRequired;
963 public string DomainSnifferPath
965 get => this.domainSnifferPath;
966 set => this.domainSnifferPath = value;
973 public string ClientSnifferPath
975 get => this.clientSnifferPath;
976 set => this.clientSnifferPath = value;
989 this.disposed =
true;
991 if (!(this.httpServer is
null))
993 this.httpServer.Unregister(this.requestWhiteList);
994 this.requestWhiteList =
null;
997 if (!(this.smtpServer is
null))
998 this.smtpServer.MessageReceived -= this.SmtpServer_MessageReceived;
1000 this.secondTimer?.Dispose();
1001 this.secondTimer =
null;
1003 this.s2sEndpoints?.Clear();
1004 this.s2sEndpoints?.Dispose();
1005 this.s2sEndpoints =
null;
1007 this.clientConnections?.
Clear();
1008 this.clientConnections?.
Dispose();
1009 this.clientConnections =
null;
1011 this.sniffers?.
Clear();
1013 this.sniffers =
null;
1015 this.shortTermCache?.
Clear();
1016 this.shortTermCache?.
Dispose();
1017 this.shortTermCache =
null;
1019 if (!(this.componentsBySubdomain is
null))
1024 this.componentsBySubdomain.Clear();
1025 this.componentsByFulldomain.Clear();
1026 this.componentsStatic = Array.Empty<
IComponent>();
1030 this.accounts =
null;
1033 this.services =
null;
1035 this.responses.Dispose();
1037 if (!(this.c2sListeners is
null))
1039 LinkedList<TcpListener> Listeners = this.c2sListeners;
1040 this.c2sListeners =
null;
1042 foreach (TcpListener Listener
in Listeners)
1046 if (!(this.s2sListeners is
null))
1048 LinkedList<TcpListener> Listeners = this.s2sListeners;
1049 this.s2sListeners =
null;
1051 foreach (TcpListener Listener
in Listeners)
1055 if (!(this.c2sSniffers is
null))
1057 foreach (
ISniffer Sniffer
in this.c2sSniffers)
1058 (Sniffer as IDisposable)?.Dispose();
1061 if (!(this.s2sSniffers is
null))
1063 foreach (
ISniffer Sniffer
in this.s2sSniffers)
1064 (Sniffer as IDisposable)?.Dispose();
1071 public bool Disposed => this.disposed;
1076 public int[] OpenC2SPorts
1080 return this.GetOpenPorts(this.c2sListeners);
1087 public int[] OpenS2SPorts
1091 return this.GetOpenPorts(this.s2sListeners);
1100 private int[] GetOpenPorts(LinkedList<TcpListener> Listeners)
1102 SortedDictionary<int, bool> Open =
new SortedDictionary<int, bool>();
1104 if (!(Listeners is
null))
1106 IPEndPoint IPEndPoint;
1108 foreach (TcpListener Listener
in Listeners)
1110 IPEndPoint = Listener.LocalEndpoint as IPEndPoint;
1111 if (!(IPEndPoint is
null))
1112 Open[IPEndPoint.Port] =
true;
1116 int[] Result =
new int[Open.Count];
1117 Open.Keys.CopyTo(Result, 0);
1133 lock (this.synchObject)
1139 this.componentsByFulldomain[Component.Subdomain +
"." + this.domain] =
Component;
1142 this.componentsByFulldomain[Component.Subdomain +
"." + cis] =
Component;
1144 this.RebuildComponentsStaticLocked();
1150 private void RebuildComponentsStaticLocked()
1153 this.componentsBySubdomain.Values.CopyTo(Static, 0);
1155 this.componentsStatic = Static;
1165 bool Result =
false;
1167 lock (this.synchObject)
1181 this.RebuildComponentsStaticLocked();
1193 private void AcceptTcpClientCallback(IAsyncResult ar)
1197 if (this.disposed ||
1198 !(ar?.AsyncState is
object[] P) ||
1200 !(P[0] is TcpListener Listener) ||
1201 !(P[1] is
bool S2S) ||
1209 TcpClient Client = Listener.EndAcceptTcpClient(ar);
1218 this.serverCertificate,
this,
false,
new InMemorySniffer(
"XMPP S2S In-memory Sniffer"));
1219 ComLayer = Endpoint;
1221 Task.Run(async () =>
1230 if (!
string.IsNullOrEmpty(this.clientSnifferPath))
1232 else if (this.c2sSniffers.HasSniffers)
1233 Sniffers = this.c2sSniffers.Sniffers;
1235 Sniffers = Array.Empty<
ISniffer>();
1247 Listener.BeginAcceptTcpClient(this.AcceptTcpClientCallback, P);
1250 catch (SocketException)
1254 catch (ObjectDisposedException)
1258 catch (NullReferenceException)
1262 catch (Exception ex)
1264 if (this.c2sListeners is
null)
1271 internal string GetTransformPath(
bool S2S)
1273 foreach (
ISniffer Sniffer
in S2S ? this.s2sSniffers.Sniffers :
this.c2sSniffers.Sniffers)
1290 public string GetDialbackKey(
string ReceivingServer,
string OriginatingServer,
string ReceivingStreamId)
1292 StringBuilder sb =
new StringBuilder();
1294 sb.Append(ReceivingServer);
1296 sb.Append(OriginatingServer);
1298 sb.Append(ReceivingStreamId);
1300 byte[] Bin = Encoding.UTF8.GetBytes(sb.ToString());
1317 return this.persistenceLayer.GetAccount(UserName);
1328 return this.clientConnections.
TryGetValue(FullJid, out Connection);
1338 return this.clientConnections?.
ContainsKey(FullJid) ??
false;
1348 return this.s2sEndpoints?.ContainsKey(Domain) ??
false;
1361 if (!Connection2.CheckLive())
1362 this.clientConnections.
Remove(FullJid);
1367 this.clientConnections[FullJid] = Connection;
1371 lock (this.connectionsPerBareJid)
1373 if (!this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1375 Connections =
new List<IClientConnection>();
1376 this.connectionsPerBareJid[BareJid] = Connections;
1379 Connections.Add(Connection);
1390 public event EventHandlerAsync<ClientConnectionEventArgs> ClientConnectionAdded =
null;
1395 public event EventHandlerAsync<ClientConnectionEventArgs> ClientConnectionUpdated =
null;
1400 public event EventHandlerAsync<ClientConnectionEventArgs> ClientConnectionRemoved =
null;
1405 public event EventHandlerAsync<ServerConnectionEventArgs> ServerConnectionAdded =
null;
1410 public event EventHandlerAsync<ServerConnectionEventArgs> ServerConnectionUpdated =
null;
1415 public event EventHandlerAsync<ServerConnectionEventArgs> ServerConnectionRemoved =
null;
1429 FullJid = BareJid +
"/" + this.NewId(16);
1431 while (!this.disposed && this.clientConnections.
ContainsKey(FullJid));
1435 this.clientConnections[FullJid] = Connection;
1437 lock (this.connectionsPerBareJid)
1439 if (!this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1441 Connections =
new List<IClientConnection>();
1442 this.connectionsPerBareJid[BareJid] = Connections;
1445 Connections.Add(Connection);
1463 this.clientConnections?.
Remove(FullJid);
1465 lock (this.connectionsPerBareJid)
1467 if (this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1469 int i, c = Connections.Count;
1471 for (i = 0; i < c; i++)
1473 if (Connections[i].FullJid == FullJid)
1475 Connections.RemoveAt(i);
1479 this.connectionsPerBareJid.Remove(BareJid);
1490 lock (this.connectionsPerBareJid)
1492 if (this.connectionsPerBareJid.TryGetValue(BareJid, out List<IClientConnection> Connections))
1493 return Connections.ToArray();
1520 if (Connection is
null)
1525 if (Auditor is
null)
1526 return Task.FromResult<DateTime?>(
null);
1531 internal async Task<bool> Authenticate(
string Mechanism, SslStream SslStream,
IClientConnection Connection,
string Data)
1533 DateTime? Next = await this.GetEarliestLoginOpportunity(Connection);
1537 StringBuilder sb =
new StringBuilder();
1538 DateTime TP = Next.Value;
1539 DateTime Today = DateTime.Today;
1541 if (Next.Value == DateTime.MaxValue)
1543 sb.Append(
"This endpoint (");
1545 sb.Append(
") has been blocked from the system.");
1549 sb.Append(
"Too many failed login attempts in a row registered. Try again after ");
1550 sb.Append(TP.ToLongTimeString());
1552 if (TP.Date != Today)
1554 if (TP.Date == Today.AddDays(1))
1555 sb.Append(
" tomorrow");
1559 sb.Append(TP.ToShortDateString());
1563 sb.Append(
". Remote Endpoint: ");
1574 if (M.
Name == Mechanism)
1585 if (AuthResult.HasValue)
1587 if (AuthResult.Value)
1589 if (!await Connection.
BeginWrite(
"<success xmlns='" + SaslNamespace +
"'/>",
null,
null))
1596 catch (Exception ex)
1637 if (this.IsServerDomain(To.
Domain,
true))
1644 IAccount Account = await this.persistenceLayer.GetAccount(To.
Account);
1645 if (Account is
null)
1648 return new AccountRecipient(this.accounts, Account);
1654 lock (this.synchObject)
1656 if (!this.componentsByFulldomain.TryGetValue(To.
Domain, out
Component))
1669 true, From.
Address +
" wants to send a stanza to " + To.
Address);
1673 catch (Exception ex)
1691 public void RegisterIqGetHandler(
string LocalName,
string Namespace, EventHandlerAsync<IqEventArgs> Handler,
bool PublishNamespaceAsFeature)
1693 this.RegisterIqHandler(this.iqGetHandlers, LocalName, Namespace, Handler, PublishNamespaceAsFeature);
1703 public void RegisterIqSetHandler(
string LocalName,
string Namespace, EventHandlerAsync<IqEventArgs> Handler,
bool PublishNamespaceAsFeature)
1705 this.RegisterIqHandler(this.iqSetHandlers, LocalName, Namespace, Handler, PublishNamespaceAsFeature);
1708 private void RegisterIqHandler(Dictionary<
string, EventHandlerAsync<IqEventArgs>> Handlers,
string LocalName,
string Namespace, EventHandlerAsync<IqEventArgs> Handler,
1709 bool PublishNamespaceAsFeature)
1711 string Key = LocalName +
" " + Namespace;
1713 lock (this.synchObject)
1715 if (Handlers.ContainsKey(Key))
1716 throw new ArgumentException(
"Handler already registered.", nameof(LocalName));
1718 Handlers[Key] = Handler;
1720 if (PublishNamespaceAsFeature)
1721 this.features[Namespace] =
true;
1732 public void RegisterMessageHandler(
string LocalName,
string Namespace, EventHandlerAsync<MessageEventArgs> Handler,
bool PublishNamespaceAsFeature)
1734 string Key = LocalName +
" " + Namespace;
1736 lock (this.synchObject)
1738 if (this.messageHandlers.ContainsKey(Key))
1739 throw new ArgumentException(
"Handler already registered.", nameof(LocalName));
1741 this.messageHandlers[Key] = Handler;
1743 if (PublishNamespaceAsFeature)
1744 this.features[Namespace] =
true;
1756 public bool UnregisterIqGetHandler(
string LocalName,
string Namespace, EventHandlerAsync<IqEventArgs> Handler,
bool RemoveNamespaceAsFeature)
1758 return this.UnregisterIqHandler(this.iqGetHandlers, LocalName, Namespace, Handler, RemoveNamespaceAsFeature);
1769 public bool UnregisterIqSetHandler(
string LocalName,
string Namespace, EventHandlerAsync<IqEventArgs> Handler,
bool RemoveNamespaceAsFeature)
1771 return this.UnregisterIqHandler(this.iqSetHandlers, LocalName, Namespace, Handler, RemoveNamespaceAsFeature);
1774 private bool UnregisterIqHandler(Dictionary<
string, EventHandlerAsync<IqEventArgs>> Handlers,
string LocalName,
string Namespace, EventHandlerAsync<IqEventArgs> Handler,
1775 bool RemoveNamespaceAsFeature)
1777 string Key = LocalName +
" " + Namespace;
1779 lock (this.synchObject)
1781 if (!Handlers.TryGetValue(Key, out EventHandlerAsync<IqEventArgs> h))
1787 Handlers.Remove(Key);
1789 if (RemoveNamespaceAsFeature)
1790 this.features.Remove(Namespace);
1804 public bool UnregisterMessageHandler(
string LocalName,
string Namespace, EventHandlerAsync<MessageEventArgs> Handler,
bool RemoveNamespaceAsFeature)
1806 string Key = LocalName +
" " + Namespace;
1808 lock (this.synchObject)
1810 if (!this.messageHandlers.TryGetValue(Key, out EventHandlerAsync<MessageEventArgs> h))
1816 this.messageHandlers.Remove(Key);
1818 if (RemoveNamespaceAsFeature)
1819 this.features.Remove(Namespace);
1827 IRecipient Recipient = await this.TryGetRecipient(To, From);
1829 if (Recipient is
null)
1835 string Message =
"Recipient not found: " + To.
Address;
1838 new KeyValuePair<string, object>(
"From", From?.Address),
1839 new KeyValuePair<string, object>(
"To", To?.Address));
1841 return !(await Sender.IqErrorItemNotFound(Id, From, To, Message,
"en") is
null);
1845 await this.persistenceLayer.IsBlocked(From.
BareJid, ToConnection.BareJid))
1847 return !(await Sender.IqErrorServiceUnavailable(Id, From, To,
string.Empty,
string.Empty) is
null);
1850 return await Recipient.
IQ(Type, Id, To, From, Language,
Stanza, Sender);
1865 Dictionary<string, EventHandlerAsync<IqEventArgs>> Handlers;
1866 EventHandlerAsync<IqEventArgs> h =
null;
1870 bool Blocked = await this.persistenceLayer.IsBlocked(From.
BareJid, To.
BareJid);
1875 if (this.responses.TryGet(From.
Address, Id,
true, out Response, out
bool Created))
1879 KeyValuePair<string, bool> P = await Response.
GetResponse();
1882 return await Sender.
IqResult(Id, From, To, P.Key);
1884 return await Sender.
IqError(Id, From, To, P.Key);
1888 Handlers = this.iqGetHandlers;
1889 Counter =
"XMPP.Server.Get";
1893 if (this.responses.TryGet(From.
Address, Id,
true, out Response, out Created))
1897 KeyValuePair<string, bool> P = await Response.GetResponse();
1900 return await Sender.
IqResult(Id, From, To, P.Key);
1902 return await Sender.
IqError(Id, From, To, P.Key);
1906 Handlers = this.iqSetHandlers;
1907 Counter =
"XMPP.Server.Set";
1915 return await this.ProcessResponse(Type, Id, To, From, Language,
true,
false,
Stanza, Sender);
1921 return !(await Sender.IqErrorBadRequest(Id, From, To,
"Invalid type.",
"en") is
null);
1930 Response.SetResult(
string.Empty,
false);
1935 string ErrorXml = await Sender.IqErrorServiceUnavailable(Id, From, To,
string.Empty,
string.Empty);
1936 Response.SetResult(ErrorXml,
false);
1937 return !(ErrorXml is
null);
1941 lock (this.synchObject)
1945 if (!(N is XmlElement E))
1948 Key = E.LocalName +
" " + E.NamespaceURI;
1949 if (Handlers.TryGetValue(Key, out h))
1951 Counter +=
"." + E.LocalName;
1964 Response.SetResult(
string.Empty,
false);
1969 string ErrorXml = await Sender.
IqError(Id, From, To,
"cancel",
"<feature-not-implemented xmlns='" + StanzaNamespace +
"'/>",
string.Empty,
string.Empty);
1970 Response.SetResult(ErrorXml,
false);
1971 return !(ErrorXml is
null);
1981 catch (Exception ex)
1985 Response.SetResult(
string.Empty,
false);
1990 string ErrorXml = await Sender.
IqError(Id, From, To, ex);
1991 Response.SetResult(ErrorXml,
false);
1992 return !(ErrorXml is
null);
1997 internal async Task<bool> ProcessResponse(
string Type,
string Id,
XmppAddress To,
XmppAddress From,
string Language,
2000 if (!
string.IsNullOrEmpty(Id))
2002 PendingRequest Rec =
null;
2003 bool Ok = Type ==
"result";
2005 lock (this.synchObject)
2007 if (this.pendingRequestsById.TryGetValue(Id, out Rec))
2009 this.pendingRequestsById.Remove(Id);
2010 this.pendingRequestsByTimeout.Remove(Rec.Timeout);
2021 if (!(Rec.ShortTermCacheKey is
null))
2022 this.shortTermCache[Rec.ShortTermCacheKey] = e;
2024 await Rec.IqCallback.Raise(
this, e);
2027 if (PresenceResponse)
2030 await Rec.PresenceCallback.Raise(
this, e);
2050 return this.IQ(Type, Id, To, From, Language, ToStanza(
"iq", Type, Id, To, From, Language, ContentXml), Sender);
2053 internal static Stanza ToStanza(
string StanzaType,
string Type,
string Id,
XmppAddress To,
XmppAddress From,
string Language,
string ContentXml)
2055 StringBuilder Xml =
new StringBuilder();
2056 StringBuilder Xml2 =
new StringBuilder();
2057 int ContentStart, ContentLen;
2059 Xml.Append(
"<stream:stream to='");
2061 Xml.Append(
"' from='");
2063 Xml.Append(
"' version='1.0' xml:lang='");
2065 Xml.Append(
"' xmlns='jabber:server' xmlns:stream='");
2070 Xml2.Append(StanzaType);
2072 if (!
string.IsNullOrEmpty(Type))
2074 Xml2.Append(
" type='");
2079 if (!
string.IsNullOrEmpty(Id))
2081 Xml2.Append(
" id='");
2088 Xml2.Append(
" from='");
2095 Xml2.Append(
" to='");
2100 if (!
string.IsNullOrEmpty(Language))
2102 Xml2.Append(
" xml:lang='");
2107 if (
string.IsNullOrEmpty(ContentXml))
2110 ContentStart = ContentLen = 0;
2116 ContentStart = Xml2.Length;
2117 ContentLen = ContentXml.Length;
2119 Xml2.Append(ContentXml);
2121 Xml2.Append(StanzaType);
2125 string s = Xml2.ToString();
2128 Xml.Append(
"</stream:stream>");
2132 XmlDocument Doc =
XML.
ParseXml(Xml.ToString(),
true);
2134 return new Stanza(Doc.DocumentElement, s, ContentStart, ContentLen);
2136 catch (Exception ex)
2138 throw new Exception(
"Invalid XML:\r\n\r\n" + ex.Message +
"\r\n\r\n" + Xml.ToString());
2154 bool ToLocal = this.IsServerDomain(To.
Domain,
true);
2158 if (await this.persistenceLayer.IsBlocked(From.
BareJid, To.
BareJid))
2159 return await (Sender?.MessageErrorServiceUnavailable(From, To,
string.Empty,
string.Empty) ?? Task.FromResult(
true));
2160 else if (await this.persistenceLayer.IsBlocked(To.
BareJid, From.
BareJid))
2162 return await Sender.
Message(
"error", Id, From, To,
string.Empty,
"<error type='cancel'><not-acceptable xmlns='" + StanzaNamespace +
2163 "'/><blocked xmlns='" + BlockingCommandErrorNamespace +
"'/></error>");
2171 if (!(Connections is
null) && Connections.Length > 0)
2173 bool Forwarded =
false;
2179 if (await Connection.
Message(Type, Id, To, From, Language,
Stanza, Sender))
2182 catch (Exception ex)
2189 catch (Exception ex2)
2200 if (
string.IsNullOrEmpty(Type) || Type ==
"normal" || Type ==
"chat")
2202 if (await this.persistenceLayer.StoreOfflineMessage(Type, Id, To, From, Language,
Stanza))
2205 ComLayer.Information(
"Message stored for later delivery.");
2208 return await (Sender?.MessageErrorServiceUnavailable(From, To,
string.Empty,
string.Empty) ?? Task.FromResult(
true));
2213 EventHandlerAsync<MessageEventArgs> h =
null;
2215 string Counter =
"XMPP.Server.Message";
2217 lock (this.synchObject)
2221 if (!(N is XmlElement E))
2224 string Key = E.LocalName +
" " + E.NamespaceURI;
2225 if (this.messageHandlers.TryGetValue(Key, out h))
2227 Counter +=
"." + E.LocalName;
2249 catch (Exception ex)
2251 return await (Sender?.MessageError(Id, From, To, ex) ?? Task.FromResult(
true));
2259 bool Forwarded =
false;
2263 Forwarded = await Connection.Message(Type, Id, To, From, Language,
Stanza, Sender);
2265 catch (Exception ex)
2267 Connection.Exception(ex);
2268 await Connection.DisposeAsync();
2275 return await (Sender?.MessageErrorServiceUnavailable(From, To,
string.Empty,
string.Empty) ?? Task.FromResult(
true));
2285 lock (this.synchObject)
2287 if (!this.componentsByFulldomain.TryGetValue(To.
Domain, out
Component))
2301 Endpoint = await this.GetS2sEndpoint(From.
Domain, To.
Domain,
true,
"Sending message from " + From.
Address +
" to " + To.
Address);
2302 if (Endpoint is
null)
2305 catch (Exception ex)
2307 if (!(Sender is
null))
2308 return await (Sender?.MessageErrorServiceUnavailable(From, To, ex.Message,
string.
Empty) ?? Task.FromResult(
true));
2313 return await Endpoint.
Message(Type, Id, To, From, Language,
Stanza, Sender);
2327 lock (remoteDomainLookup)
2329 remoteDomainLookup[RemoteDomain] =
new S2SRec()
2331 Domain = RemoteDomain,
2334 TrustCertificate = TrustCertificate
2350 lock (remoteDomainLookup)
2352 remoteDomainLookup[RemoteDomain] =
new S2SRec()
2354 Domain = ServerDomain,
2357 TrustCertificate = TrustCertificate
2369 lock (remoteDomainLookup)
2371 return remoteDomainLookup.ContainsKey(RemoteDomain);
2375 internal async
void RegisterS2SEndpoint(
IS2SEndpoint Endpoint)
2379 EventHandlerAsync<ServerConnectionEventArgs> h;
2383 if (this.s2sEndpoints?.TryGetValue(Domain, out
IS2SEndpoint Prev) ??
false)
2385#if LogToWebHookTester
2387 _ = Task.Run(async () =>
2392 new Dictionary<string, object>()
2394 {
"id_prev", Prev.Id.ToString() },
2395 {
"id_updated", Endpoint.Id.ToString() },
2396 {
"event",
"RegisterS2SEndpoint" },
2397 {
"remote", Domain.Value },
2398 {
"prev", JSON.Encode(Prev, true) },
2399 {
"updated", JSON.Encode(Endpoint, true) },
2400 {
"reason",
"Updating" }
2402 new KeyValuePair<string, string>(
"Accept",
"application/json"));
2404 catch (Exception ex)
2410 h = this.ServerConnectionUpdated;
2412 if (Prev != Endpoint)
2413 this.s2sEndpoints[Domain] = Endpoint;
2417 if (Prev == Endpoint)
2422 h = this.ServerConnectionAdded;
2424 this.s2sEndpoints?.
Add(Domain, Endpoint);
2426#if LogToWebHookTester
2428 _ = Task.Run(async () =>
2433 new Dictionary<string, object>()
2435 {
"id", Endpoint.
Id.ToString() },
2436 {
"event",
"RegisterS2SEndpoint" },
2437 {
"remote", Domain?.
Value },
2439 {
"reason",
"Registering" }
2441 new KeyValuePair<string, string>(
"Accept",
"application/json"));
2443 catch (Exception ex)
2453 XmppS2SEndpoint.OnStateChanged += this.Endpoint_OnStateChanged;
2456 private async Task Endpoint_OnStateChanged(
object Sender, EventArgs e)
2470 return this.s2sEndpoints.TryGetValue(RemoteDomain, out Endpoint);
2473 internal void RegisterAsTemporary(
IS2SEndpoint Endpoint)
2475 lock (this.synchObject)
2477 this.temporaryConnections ??=
new SortedDictionary<DateTime, IS2SEndpoint>();
2479 DateTime TP = DateTime.Now.AddMinutes(1);
2481 while (this.temporaryConnections.ContainsKey(TP))
2482 TP = TP.AddTicks(this.gen.Next(10));
2484 this.temporaryConnections[TP] = Endpoint;
2497 SrvMsg =
"Unable to get SRV record for xmpp-server/tcp of " + DomainName;
2500 Result =
new S2SRec()
2502 Domain = DomainName,
2505 TrustCertificate =
false,
2512 catch (Exception ex)
2514 SrvMsg = ex.Message;
2520 if (!(Hosts is
null) && Hosts.Length > 0 && !
string.IsNullOrEmpty(Hosts[0]))
2524 using TcpClient TestClient =
new TcpClient();
2526 await TestClient.ConnectAsync(DomainName, DefaultS2sPort);
2528 Log.
Notice(
"Federated XMPP server connection. SRV DNS settings not found: " + SrvMsg, DomainName);
2530 Result =
new S2SRec()
2532 Domain = DomainName,
2534 Port = DefaultS2sPort,
2535 TrustCertificate =
false,
2546 Log.
Notice(
"Federated mail server connection. XMPP server not found: " + SrvMsg, DomainName);
2548 Result =
new S2SRec()
2550 Domain = DomainName,
2553 TrustCertificate =
false,
2577 lock (remoteDomainLookup)
2579 if (remoteDomainLookup.TryGetValue(DomainOrSubdomain, out Result))
2583 Result = await GetDomainFromDns(DomainOrSubdomain);
2584 if (!(Result is
null))
2587 Result.Domain = Result.
Host;
2589 lock (remoteDomainLookup)
2591 remoteDomainLookup[DomainOrSubdomain] = Result;
2599 throw new Exception(
"Invalid domain or subdomain name: " + DomainOrSubdomain);
2610 if (Domain != DomainOrSubdomain)
2612 Rec = await GetDomainFromDns(Domain);
2621 IPHostEntry Entry = await
System.
Net.Dns.GetHostEntryAsync(Domain);
2622 if (Entry.AddressList.Length > 0)
2624 using (TcpClient TestClient =
new TcpClient())
2626 await TestClient.ConnectAsync(Domain, DefaultS2sPort);
2630 Result = Rec =
new S2SRec()
2634 Port = DefaultS2sPort,
2636 TrustCertificate =
false
2648 Domain = Parts[i] +
"." + Domain;
2650 if (Rec is
null && !(Result is
null) && Result.
Type ==
S2sType.XMPP)
2654 if (!(Result is
null))
2656 lock (remoteDomainLookup)
2658 remoteDomainLookup[DomainOrSubdomain] = Result;
2664 throw new Exception(
"Invalid S2S domain or subdomain name: " + DomainOrSubdomain);
2723 bool TrustCertificate;
2728 lock (remoteDomainLookup)
2730 RecFound = remoteDomainLookup.TryGetValue(RemoteDomain, out Rec);
2734 Rec = await GetDomain(RemoteDomain);
2736 RemoteDomain = Rec.
Domain;
2742 if (this.IsServerDomain(RemoteDomain,
true))
2747 QueuedStanza[] QueuedStanzas =
null;
2749 if (ReuseExisting && this.s2sEndpoints.TryGetValue(RemoteDomain, out
IS2SEndpoint Existing))
2754 Log.
Informational(
"Removes stale S2S connection.", RemoteDomain,
string.Empty,
"XmppStaleS2s");
2756#if LogToWebHookTester
2758 _ = Task.Run(async () =>
2763 new Dictionary<string, object>()
2765 {
"id", Existing.Id.ToString() },
2766 {
"event",
"Removing stale S2S endpoint" },
2767 {
"remote", RemoteDomain.Value },
2768 {
"value", JSON.Encode(Existing, true) },
2769 {
"reason",
"Getting" },
2770 {
"state", XmppS2SEndpoint.State },
2771 {
"created", XmppS2SEndpoint.CreationTimeUtc.ToString() },
2772 {
"connect", XmppS2SEndpoint.ConnectTimeUtc.ToString() },
2773 {
"connected", XmppS2SEndpoint.ConnectedTimeUtc.ToString() },
2775 new KeyValuePair<string, string>(
"Accept",
"application/json"));
2777 catch (Exception ex)
2784 await this.s2sEndpoints.RemoveAsync(RemoteDomain);
2795 if (this.HasDomain || RecFound)
2797 Log.
Informational(
"Opening S2S connection. " + Reason, RemoteDomain,
string.Empty,
"XmppOpenS2s");
2799 if (!this.IsServerDomain(LocalDomain,
true))
2801 int i = LocalDomain.
IndexOf(
'.');
2805 if (this.IsServerDomain(s,
true))
2811 Port, this.serverCertificate,
this, TrustCertificate,
2812 !ReuseExisting, QueuedStanzas);
2818 this.AddS2SSniffers(Result, RemoteDomain);
2822#if LogToWebHookTester
2824 _ = Task.Run(async () =>
2829 new Dictionary<string, object>()
2831 {
"id", XmppS2SEndpoint.Id.ToString() },
2832 {
"event",
"Creating S2S endpoint" },
2833 {
"remote", RemoteDomain.Value },
2834 {
"value", JSON.Encode(XmppS2SEndpoint, true) },
2835 {
"reason",
"Getting" }
2837 new KeyValuePair<string, string>(
"Accept",
"application/json"));
2839 catch (Exception ex)
2845 _ = Task.Run(async () =>
2851 catch (Exception ex)
2861 await this.GetParentConnection.Raise(
this, e);
2864 throw new NotSupportedException(
"S2S connections not supported. No domain or certificate defined, and no parent client connection available.");
2866 Log.
Informational(
"Tunneling S2S connection over client connection to parent. " + Reason, RemoteDomain,
string.Empty,
"XmppOpenS2sTunnel");
2873 if (this.smtpServer is
null)
2874 throw new NotSupportedException(
"Integration with SMTP not enabled.");
2876 Result =
new SmtpS2SEndpoint(LocalDomain, RemoteDomain, this.smtpServer,
this);
2879 scheduler.
Add(DateTime.Now.AddMinutes(10),
this.RemoveSmtpConnection, Result);
2883 throw new NotSupportedException(
"S2S Connection type not supported: " + Type.ToString());
2887 this.RegisterS2SEndpoint(Result);
2889 this.RegisterAsTemporary(Result);
2904 private Task RemoveSmtpConnection(
object State)
2908 lock (remoteDomainLookup)
2910 remoteDomainLookup.Remove(Endpoint.RemoteDomain);
2913 this.s2sEndpoints.Remove(Endpoint.RemoteDomain);
2916 return Task.CompletedTask;
2927 public bool HasDomain
2934 switch (this.domain.LowerCase)
2938 case "example2.com":
2939 case "example3.com":
2941 case "example2.org":
2942 case "example3.org":
2953 if (!
string.IsNullOrEmpty(this.domainSnifferPath))
2954 Endpoint.
Add(this.GetSniffer(RemoteDomain,
true));
2955 else if (this.s2sSniffers.HasSniffers)
2957 foreach (
ISniffer Sniffer
in this.s2sSniffers.Sniffers)
2958 Endpoint.
Add(Sniffer);
2964 lock (remoteDomainLookup)
2966 if (remoteDomainLookup.TryGetValue(RemoteDomain, out S2SRec Rec))
2967 return Rec.TrustCertificate;
2985 return this.Message(Type, Id, To, From, Language, ToStanza(
"message", Type, Id, To, From, Language, ContentXml), Sender);
3006 case "unsubscribed":
3007 return await (Sender?.PresenceErrorNotAllowed(Id, From, this.domainAddress,
string.Empty,
string.Empty) ?? Task.FromResult(
true));
3011 IAccount FromAccount = await this.persistenceLayer.GetAccount(FromUserName);
3014 await this.PushPresence(FromConnection.BareAddress, Type, Id, From, Language,
Stanza,
false, Sender);
3016 foreach (
IRosterItem Item in await this.persistenceLayer.GetRoster(FromUserName))
3018 if (
Item.BareJid != FromConnection.BareJid &&
3021 await this.PushPresence(
new XmppAddress(
Item.BareJid), Type, Id, From, Language,
Stanza,
false, Sender);
3043 bool ToLocal = this.IsServerDomain(To.
Domain,
true);
3044 IAccount ToAccount = ToLocal ? await this.persistenceLayer.GetAccount(To.
Account) :
null;
3047 bool FromLocal = !(FromConnection is
null);
3051 bool UseBareJids =
false;
3053 if (!
string.IsNullOrEmpty(Id))
3055 PendingRequest Rec =
null;
3056 bool Ok = (Type !=
"error");
3058 lock (this.synchObject)
3060 if (this.pendingRequestsById.TryGetValue(Id, out Rec))
3062 this.pendingRequestsById.Remove(Id);
3063 this.pendingRequestsByTimeout.Remove(Rec.Timeout);
3069 if (!(Rec?.PresenceCallback is
null))
3070 await Rec.PresenceCallback.Raise(
this,
new PresenceEventArgs(Sender, Type, Id, To, From, Language,
Stanza, Rec.State));
3073 if (ToLocal && ToAccount is
null)
3074 return await (Sender?.PresenceErrorItemNotFound(Id, From, To,
string.Empty,
string.Empty) ?? Task.FromResult(
true));
3078 FromUserName = FromConnection.
UserName;
3079 FromAccount = await this.persistenceLayer.GetAccount(FromUserName);
3081 if (FromAccount is
null)
3082 return await (Sender?.PresenceErrorNotAllowed(Id, From, this.domainAddress,
string.Empty,
string.Empty) ?? Task.FromResult(
true));
3090 if (await this.persistenceLayer.IsBlocked(From.
BareJid, To.
BareJid))
3093 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.
BareJid);
3095 if (!(
Item is
null))
3097 if (!
Item.PendingSubscription)
3103 if (!(
Item is
null))
3115 if (!(
Item is
null))
3120 false,
Item.Groups);
3125 false,
Item.Groups);
3130 if (!(
Item is
null))
3139 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.
BareJid);
3140 if (!(
Item is
null))
3145 Item.PendingSubscription,
Item.Groups);
3150 Item.PendingSubscription,
Item.Groups);
3158 if (!(
Item is
null))
3167 case "unsubscribed":
3172 if (!(
Item is
null))
3177 false,
Item.Groups);
3182 false,
Item.Groups);
3184 else if (
Item.PendingSubscription)
3187 false,
Item.Groups);
3192 if (!(
Item is
null))
3199 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.
BareJid);
3200 if (!(
Item is
null))
3205 Item.PendingSubscription,
Item.Groups);
3210 Item.PendingSubscription,
Item.Groups);
3215 if (!(
Item is
null))
3228 if (!(
Item is
null))
3233 false,
Item.Groups);
3238 false,
Item.Groups);
3243 if (!(
Item is
null))
3252 Item = await this.persistenceLayer.GetRosterItem(FromUserName, To.
BareJid);
3253 if (!(
Item is
null))
3258 false,
Item.Groups);
3263 false,
Item.Groups);
3265 else if (
Item.PendingSubscription)
3268 false,
Item.Groups);
3273 if (!(
Item is
null))
3278 await this.PushPresence(To, Type, Id, From.
ToBareJID(), Language,
Stanza, UseBareJids, Sender);
3280 Type =
"unavailable";
3289 if (ToAccount is
null ||
3290 await this.persistenceLayer.IsBlocked(From.
BareJid, To.
BareJid) ||
3292 (
Item = await
this.persistenceLayer.GetRosterItem(To.
Account, From.
BareJid)) is
null ||
3295 if (!await (Sender?.Presence(
"unsubscribed", Id, From, To,
string.Empty,
string.Empty) ?? Task.FromResult(
true)))
3305 if (!(Connections is
null))
3324 LastPresence = Connection.LastPresence;
3325 if (!await (Sender?.Presence(LastPresence.
Type, Id, From, LastPresence.
From, LastPresence.
Language, LastPresence.
Stanza?.
Content) ?? Task.FromResult(
true)))
3330 if (!Sent && !await (Sender?.Presence(
"unavailable", Id, From, To,
string.Empty,
string.Empty) ?? Task.FromResult(
true)))
3344 if (!await (Sender?.PresenceErrorBadRequest(Id, From, this.domainAddress,
"Invalid type.",
"en") ?? Task.FromResult(
true)))
3349 await this.PushPresence(To, Type, Id, UseBareJids ? From.
ToBareJID() : From, Language,
Stanza, UseBareJids, Sender);
3351 if (!(LastPresence is
null))
3353 await this.PushPresence(To, LastPresence.
Type, LastPresence.
Id, LastPresence.
From, LastPresence.
Language,
3354 LastPresence.
Stanza,
false, Sender);
3361 bool UseBareJids,
ISender Sender)
3363 bool FromLocal = this.IsServerDomain(From.
Domain,
true);
3369 await this.OnPresenceLocalSender.Raise(
this, e);
3375 bool ToLocal = To.Domain == From.Domain ? FromLocal : this.IsServerDomain(To.
Domain,
true);
3380 await this.PushPresence(Type, Id, From, Language,
Stanza is
null ?
string.Empty :
Stanza.
Content, Connections, UseBareJids);
3382 await this.OnPresenceLocalRecipient.Raise(
this, e ??
new PresenceEventArgs(Sender, Type, Id, To, From, Language,
Stanza,
null));
3386 IRecipient Recipient = await this.TryGetRecipient(To, From);
3388 if (!(Recipient is
null))
3389 await Recipient.
Presence(Type, Id, To, From, Language,
Stanza, Sender);
3398 public event EventHandlerAsync<PresenceEventArgs> OnPresenceLocalRecipient =
null;
3403 public event EventHandlerAsync<PresenceEventArgs> OnPresenceLocalSender =
null;
3417 return this.Presence(Type, Id, To, From, Language, ToStanza(
"presence", Type, Id, To, From, Language, ContentXml), Sender);
3430 Type = stEx.ErrorType;
3431 Xml =
"<" + stEx.ErrorStanzaName +
" xmlns='" + StanzaNamespace +
"'/>";
3439 if (ex is HTTP.BadRequestException)
3442 Xml =
"bad-request";
3444 else if (ex is HTTP.ConflictException)
3449 else if (ex is HTTP.ForbiddenException)
3454 else if (ex is HTTP.NotImplementedException)
3457 Xml =
"feature-not-implemented";
3459 else if (ex is HTTP.MovedPermanentlyException || ex is HTTP.GoneException)
3464 else if (ex is HTTP.InternalServerErrorException)
3467 Xml =
"internal-server-error";
3469 else if (ex is HTTP.NotFoundException)
3472 Xml =
"item-not-found";
3474 else if (ex is HTTP.NotAcceptableException || ex is HTTP.UnsupportedMediaTypeException)
3477 Xml =
"not-acceptable";
3479 else if (ex is HTTP.MethodNotAllowedException)
3482 Xml =
"not-allowed";
3484 else if (ex is HTTP.TooManyRequestsException || ex is HTTP.InsufficientStorageException)
3487 Xml =
"resource-constraint";
3489 else if (ex is HTTP.ServiceUnavailableException)
3492 Xml =
"service-unavailable";
3494 else if (ex is HTTP.NetworkAuthenticationRequiredException)
3497 Xml =
"not-authorized";
3502 Xml =
"<" + Xml +
" xmlns='" + StanzaNamespace +
"'/>";
3508 Xml =
"<internal-server-error xmlns='" + StanzaNamespace +
"'/>";
3513 #region Request/Response
3532 if (this.IsServerDomain(Domain,
true))
3535 if (!(Connections is
null))
3541 if (!(Connection?.LastPresence is
null))
3544 if (Last is
null || LastPresence.
Timestamp > Last.Timestamp)
3545 Last = LastPresence;
3552 await
Callback.Raise(
this,
new PresenceEventArgs(
null, Last.Type, Last.Id, Last.To, Last.From, Last.Language, Last.Stanza, State));
3565 Endpoint = await this.GetS2sEndpoint(this.domain, Domain,
true,
"Performing presence probe on " + BareJid);
3574 PendingRequest Request = this.PrepareRequest(
null, (Sender, e) =>
3577 },
null, State, 10000, 0,
false, 10000, this.domainAddress,
new XmppAddress(BareJid),
string.
Empty,
string.
Empty,
null);
3592 private PendingRequest PrepareRequest(EventHandlerAsync<IqResultEventArgs> IqCallback, EventHandlerAsync<PresenceEventArgs> PresenceCallback,
3593 EventHandlerAsync<PendingRequestEventArgs> ResendCallback,
object State,
int RetryTimeout,
int NrRetries,
bool DropOff,
int MaxRetryTimeout,
3596 lock (this.synchObject)
3602 Id = this.NewId(16);
3604 while (this.pendingRequestsById.ContainsKey(Id));
3606 PendingRequest PendingRequest;
3608 if (!(IqCallback is
null))
3610 PendingRequest =
new PendingRequest(Id, RetryTimeout, NrRetries, DropOff, MaxRetryTimeout)
3612 IqCallback = IqCallback,
3613 ResendCallback = ResendCallback,
3618 Language = Language,
3619 ContentXml = ContentXml,
3620 ShortTermCacheKey = ShortTermCacheKey
3625 PendingRequest =
new PendingRequest(Id, RetryTimeout, NrRetries, DropOff, MaxRetryTimeout)
3627 PresenceCallback = PresenceCallback,
3628 ResendCallback = ResendCallback,
3633 Language = Language,
3634 ContentXml = ContentXml
3638 DateTime TP = PendingRequest.Timeout;
3640 if (this.pendingRequestsByTimeout.ContainsKey(TP))
3642 Random Rnd =
new Random();
3644 while (this.pendingRequestsByTimeout.ContainsKey(TP))
3645 TP = TP.AddTicks(Rnd.Next(100) + 1);
3648 PendingRequest.Timeout = TP;
3650 this.pendingRequestsById[Id] = PendingRequest;
3651 this.pendingRequestsByTimeout[TP] = PendingRequest;
3653 return PendingRequest;
3668 public Task<bool>
SendIqRequest(
string Type,
string From,
string To,
string Language,
3669 string ContentXml, EventHandlerAsync<IqResultEventArgs>
Callback,
object State)
3672 Language, ContentXml,
false,
Callback, State);
3688 public Task<bool>
SendIqRequest(
string Type,
string From,
string To,
string Language,
3689 string ContentXml,
bool CheckShortTermCache,
3690 EventHandlerAsync<IqResultEventArgs>
Callback,
object State)
3693 Language, ContentXml, CheckShortTermCache,
Callback, State);
3708 string Language,
string ContentXml, EventHandlerAsync<IqResultEventArgs>
Callback,
3711 return this.SendIqRequest(Type, From, To, Language, ContentXml,
false,
Callback, State);
3728 string Language,
string ContentXml,
bool CheckShortTermCache,
3729 EventHandlerAsync<IqResultEventArgs>
Callback,
object State)
3731 IRecipient Recipient = await this.TryGetRecipient(To, From);
3733 if (Recipient is
null)
3743 if (CheckShortTermCache)
3745 StringBuilder sb =
new StringBuilder();
3747 sb.AppendLine(Type);
3750 sb.AppendLine(Language);
3751 sb.AppendLine(ContentXml);
3753 Key = sb.ToString();
3757 await
Callback.Raise(
this, Result);
3762 PendingRequest Request = this.PrepareRequest(
Callback,
null, async (Sender, e) =>
3764 await Recipient.
IQ(Type, e.Request.
Id, e.Request.
To, e.Request.
From,
3765 e.Request.
Language, e.Request.ContentXml,
this);
3767 }, State, this.defaultRetryTimeout, this.defaultNrRetries, this.defaultDropOff,
3768 this.defaultMaxRetryTimeout, From, To, Language, ContentXml, Key);
3770 await Recipient.
IQ(Type, Request.Id, To, From, Language, ContentXml,
this);
3784 public Task<IqResultEventArgs>
IqRequest(
string Type,
string From,
string To,
3785 string Language,
string ContentXml)
3801 public Task<IqResultEventArgs>
IqRequest(
string Type,
string From,
string To,
3802 string Language,
string ContentXml,
bool CheckShortTermCache)
3805 ContentXml, CheckShortTermCache);
3819 return this.IqRequest(Type, From, To, Language, ContentXml,
false);
3834 string Language,
string ContentXml,
bool CheckShortTermCache)
3836 TaskCompletionSource<IqResultEventArgs> Result =
new TaskCompletionSource<IqResultEventArgs>();
3838 if (await this.SendIqRequest(Type, From, To, Language, ContentXml, CheckShortTermCache,
3841 Result.TrySetResult(e);
3842 return Task.CompletedTask;
3846 return await Result.Task;
3849 throw new InvalidOperationException(
"Unable to send request.");
3862 public Task<bool>
SendMessage(
string Type,
string Id,
string From,
string To,
string Language,
string ContentXml)
3879 IRecipient Recipient = await this.TryGetRecipient(To, From);
3881 if (Recipient is
null)
3885 await Recipient.
Message(Type, Id, To, From, Language, ContentXml,
this);
3890 private async
void SecondTimerCallback(
object State)
3894 LinkedList<KeyValuePair<DateTime, IS2SEndpoint>> ToRemove =
null;
3895 List<PendingRequest> Retries =
null;
3896 DateTime Now = DateTime.Now;
3900 lock (this.synchObject)
3902 foreach (KeyValuePair<DateTime, PendingRequest> P
in this.pendingRequestsByTimeout)
3906 Retries ??=
new List<PendingRequest>();
3907 Retries.Add(P.Value);
3913 if (!(this.temporaryConnections is
null))
3915 foreach (KeyValuePair<DateTime, IS2SEndpoint> P
in this.temporaryConnections)
3919 ToRemove ??=
new LinkedList<KeyValuePair<DateTime, IS2SEndpoint>>();
3920 ToRemove.AddLast(P);
3926 if (!(ToRemove is
null))
3928 foreach (KeyValuePair<DateTime, IS2SEndpoint> P
in ToRemove)
3929 this.temporaryConnections.Remove(P.Key);
3931 if (this.temporaryConnections.Count == 0)
3932 this.temporaryConnections =
null;
3937 if (!(Retries is
null))
3939 foreach (PendingRequest Request
in Retries)
3941 lock (this.synchObject)
3943 this.pendingRequestsByTimeout.Remove(Request.Timeout);
3945 if (Retry = Request.CanRetry())
3947 TP = Request.Timeout;
3949 if (this.pendingRequestsByTimeout.ContainsKey(TP))
3951 Random Rnd =
new Random();
3953 while (this.pendingRequestsByTimeout.ContainsKey(TP))
3954 TP = TP.AddTicks(Rnd.Next(100) + 1);
3957 Request.Timeout = TP;
3959 this.pendingRequestsByTimeout[Request.Timeout] = Request;
3962 this.pendingRequestsById.Remove(Request.Id);
3968 await Request.ResendCallback.Raise(
this,
new PendingRequestEventArgs(Request));
3971 if (!(Request.IqCallback is
null))
3973 StringBuilder Xml =
new StringBuilder();
3975 Xml.Append(
"<iq xmlns='jabber:server' type='error' from='");
3976 Xml.Append(Request.To);
3977 Xml.Append(
"' id='");
3978 Xml.Append(Request.Id);
3979 Xml.Append(
"'><error type='wait'><recipient-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>");
3980 Xml.Append(
"<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Timeout.</text></error></iq>");
3982 XmlDocument Doc =
XML.
ParseXml(Xml.ToString(),
true);
3987 await Request.IqCallback.Raise(
this, e);
3989 else if (!(Request.PresenceCallback is
null))
3992 "<error type='wait'><recipient-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3993 "<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Timeout.</text></error>");
3998 await Request.PresenceCallback.Raise(
this, e);
4002 catch (Exception ex)
4009 if (!(ToRemove is
null))
4011 foreach (KeyValuePair<DateTime, IS2SEndpoint> P
in ToRemove)
4012 await P.Value.DisposeAsync(
"Removing temporary connection.");
4015 catch (Exception ex)
4033 GetErrorInformation(ex, out
_, out
string Xml);
4034 Stanza Stanza = ToStanza(
"iq",
"error", Id, To, From,
string.Empty, Xml);
4036 await this.ProcessResponse(
"error", Id, To, From,
string.Empty,
true,
false,
Stanza,
this);
4051 Stanza Stanza = ToStanza(
"iq",
"error", Id, To, From,
string.Empty, ErrorXml);
4053 await this.ProcessResponse(
"error", Id, To, From,
string.Empty,
true,
false,
Stanza,
this);
4068 Stanza Stanza = ToStanza(
"iq",
"result", Id, To, From,
string.Empty, ResultXml);
4070 await this.ProcessResponse(
"result", Id, To, From,
string.Empty,
true,
false,
Stanza,
this);
4085 GetErrorInformation(ex, out
_, out
string Xml);
4086 Stanza Stanza = ToStanza(
"presence",
"error", Id, To, From,
string.Empty, Xml);
4088 await this.ProcessResponse(
"error", Id, To, From,
string.Empty,
false,
true,
Stanza,
this);
4103 Stanza Stanza = ToStanza(
"presence",
"error", Id, To, From,
string.Empty, ErrorXml);
4105 await this.ProcessResponse(
"error", Id, To, From,
string.Empty,
false,
true,
Stanza,
this);
4122 Stanza Stanza = ToStanza(
"presence", Type, Id, To, From,
string.Empty, ContentXml);
4124 await this.ProcessResponse(Type, Id, To, From,
string.Empty,
true,
false,
Stanza,
this);
4141 return Task.FromResult(
true);
4154 return Task.FromResult(
true);
4163 private async Task RosterQuery(
object Sender,
IqEventArgs e)
4172 IEnumerable<IRosterItem> Roster = await this.persistenceLayer.GetRoster(UserName);
4181 List<IRosterItem>
A =
new List<IRosterItem>();
4183 A.Sort((i1, i2) => i1.BareJid.CompareTo(i2.BareJid));
4185 StringBuilder Xml =
new StringBuilder();
4188 this.Serialize(Xml,
Item);
4198 Xml.Append(
"<query xmlns='");
4199 Xml.Append(RosterNamespace);
4200 Xml.Append(
"' ver='");
4204 Xml.Append(
"</query>");
4212 Xml.Append(
"<item jid='");
4216 Xml.Append(
"' ask='subscribe");
4218 if (!
string.IsNullOrEmpty(
Item.
Name))
4220 Xml.Append(
"' name='");
4226 Xml.Append(
"' subscription='");
4230 string[] Groups =
Item.Groups;
4231 if (Groups is
null || Groups.Length == 0)
4237 foreach (
string Group
in Groups)
4239 Xml.Append(
"<group>");
4241 Xml.Append(
"</group>");
4244 Xml.Append(
"</item>");
4248 private async Task RosterSet(
object Sender,
IqEventArgs e)
4257 List<string> Groups =
null;
4266 foreach (XmlNode N
in e.
Query.ChildNodes)
4268 E = N as XmlElement;
4272 if (E.LocalName !=
"item")
4288 if (Name.Length > MaxNameLength)
4294 foreach (XmlNode N2
in N.ChildNodes)
4296 E = N2 as XmlElement;
4300 if (E.LocalName !=
"group")
4303 Groups ??=
new List<string>();
4306 if (
string.IsNullOrEmpty(s) || s.Length > MaxGroupLength)
4311 else if (Groups.Contains(s))
4327 IRosterItem Item = await this.persistenceLayer.SetRosterItem(UserName, Jid, Name,
null,
null, Groups?.ToArray());
4328 if (!(
Item is
null))
4332 if (await this.persistenceLayer.RemoveRosterItem(UserName, Jid))
4351 StringBuilder Xml =
new StringBuilder();
4353 Xml.Append(
"<query xmlns='");
4354 Xml.Append(RosterNamespace);
4355 Xml.Append(
"'><item jid='");
4357 Xml.Append(
"' subscription='remove'></item></query>");
4376 StringBuilder Xml =
new StringBuilder();
4378 Xml.Append(
"<query xmlns='");
4379 Xml.Append(RosterNamespace);
4382 this.Serialize(Xml,
Item);
4384 Xml.Append(
"</query>");
4391 if (!(Connections is
null))
4399 await Connection.
IQ(Type, this.NewId(16), IncludeTo ? Connection.
Address :
XmppAddress.
Empty, From, Language, Xml,
this);
4401 catch (Exception ex)
4408 catch (Exception ex2)
4418 private async Task PushPresence(
string Type,
string Id,
XmppAddress From,
string Language,
string Xml,
IClientConnection[] Connections,
4421 if (!(Connections is
null))
4429 await Connection.
Presence(Type,
string.IsNullOrEmpty(Id) ? this.NewId(16) : Id,
4430 UseBareJids ? Connection.
BareAddress : Connection.
Address, From, Language, Xml,
this);
4432 catch (Exception ex)
4439 catch (Exception ex2)
4451 #region Ping XEP-0199
4453 private Task PingGet(
object Sender,
IqEventArgs e)
4457 return Task.CompletedTask;
4462 #region Discovery XEP-0030
4464 private Task DiscoveryQueryGet(
object Sender,
IqEventArgs e)
4466 XmlElement E = e.
Query;
4468 if (!
string.IsNullOrEmpty(Node))
4471 return Task.CompletedTask;
4474 StringBuilder Xml =
new StringBuilder();
4476 Xml.Append(
"<query xmlns='");
4477 Xml.Append(DiscoveryNamespace);
4478 Xml.Append(
"'><identity category='server' type='im'/>");
4480 lock (this.synchObject)
4482 foreach (
string Feature
in this.features.Keys)
4484 Xml.Append(
"<feature var='");
4490 Xml.Append(
"</query>");
4494 return Task.CompletedTask;
4497 private Task DiscoveryQueryItemsGet(
object Sender,
IqEventArgs e)
4499 XmlElement E = e.
Query;
4501 if (!
string.IsNullOrEmpty(Node))
4504 return Task.CompletedTask;
4507 StringBuilder Xml =
new StringBuilder();
4509 Xml.Append(
"<query xmlns='");
4510 Xml.Append(DiscoveryItemsNamespace);
4515 Xml.Append(
"<item jid='");
4519 Xml.Append(
"' name='");
4524 Xml.Append(
"</query>");
4528 return Task.CompletedTask;
4533 #region Software Version (XEP-0092)
4535 private Task SoftwareVersionGet(
object Sender,
IqEventArgs e)
4537 StringBuilder Xml =
new StringBuilder();
4539 Xml.Append(
"<query xmlns='");
4540 Xml.Append(SoftwareVersionNamespace);
4541 Xml.Append(
"'><name>");
4542 Xml.Append(
XML.
Encode(
this.serverName));
4543 Xml.Append(
"</name><version>");
4544 Xml.Append(
XML.
Encode(
this.serverVersion));
4545 Xml.Append(
"</version><os>");
4547 Xml.Append(
"</os></query>");
4551 return Task.CompletedTask;
4556 #region Entity Time (XEP-0202)
4558 private Task TimeGet(
object Sender,
IqEventArgs e)
4560 StringBuilder Xml =
new StringBuilder();
4561 DateTimeOffset Time = DateTimeOffset.Now;
4562 TimeSpan TimeZone = Time.Offset;
4563 DateTime Utc = Time.UtcDateTime;
4565 Xml.Append(
"<time xmlns='");
4566 Xml.Append(TimeNamespace);
4567 Xml.Append(
"'><tzo>");
4569 if (TimeZone == TimeSpan.Zero)
4573 if (TimeZone < TimeSpan.Zero)
4576 TimeZone = -TimeZone;
4581 Xml.Append(TimeZone.Hours.ToString(
"D2"));
4583 Xml.Append(TimeZone.Minutes.ToString(
"D2"));
4586 Xml.Append(
"</tzo><utc>");
4588 Xml.Append(
"</utc></time>");
4592 return Task.CompletedTask;
4597 #region vCard (XEP-0054)
4599 private async Task VCardGet(
object Sender,
IqEventArgs e)
4605 string s = await this.persistenceLayer.GetVCard(e.
From.
Account);
4607 if (
string.IsNullOrEmpty(s))
4610 await e.
IqResult(
"<vCard xmlns='" + VCardNamespace +
"'>" + s +
"</vCard>", e.
To);
4614 private async Task VCardSet(
object Sender,
IqEventArgs e)
4621 string VCard = e.
Query.InnerXml;
4623 if (!await this.persistenceLayer.SetVCard(UserName, VCard))
4632 #region Register (XEP-0077) & Form signatures (XEP-0348)
4634 private const string RegistrationInstructions =
"Register your new account, by filling in the details below.";
4638 DateTime? Next = await this.GetEarliestLoginOpportunity(Connection);
4639 return !Next.HasValue;
4644 DateTime? Next = await this.GetEarliestLoginOpportunity(Connection);
4648 StringBuilder sb =
new StringBuilder();
4649 DateTime TP = Next.Value;
4650 DateTime Today = DateTime.Today;
4652 if (Next.Value == DateTime.MaxValue)
4654 sb.Append(
"This endpoint (");
4656 sb.Append(
") has been blocked from the system.");
4663 sb.Append(
"Too many failed login attempts in a row registered. Try again after ");
4664 sb.Append(TP.ToLongTimeString());
4666 if (TP.Date != Today)
4668 if (TP.Date == Today.AddDays(1))
4669 sb.Append(
" tomorrow");
4673 sb.Append(TP.ToShortDateString());
4677 sb.Append(
". Remote Endpoint: ");
4688 private async Task RegisterGet(
object Sender,
IqEventArgs e)
4696 if (!await this.CanRegister(Connection, e))
4699 StringBuilder Xml =
new StringBuilder();
4700 byte[] Token = GetRandomNumbers(32);
4701 byte[] Secret = GetRandomNumbers(32);
4703 Xml.Append(
"<query xmlns='");
4704 Xml.Append(RegisterNamespace);
4705 Xml.Append(
"'><instructions>");
4706 Xml.Append(RegistrationInstructions);
4707 Xml.Append(
"</instructions>");
4708 Xml.Append(
"<x xmlns='jabber:x:data' type='form'>");
4709 Xml.Append(
"<title>Contest Registration</title>");
4710 Xml.Append(
"<instructions>");
4711 Xml.Append(RegistrationInstructions);
4712 Xml.Append(
"</instructions>");
4713 Xml.Append(
"<field type='hidden' var='FORM_TYPE'>");
4714 Xml.Append(
"<value>urn:xmpp:xdata:signature:oauth1</value>");
4715 Xml.Append(
"</field>");
4716 Xml.Append(
"<field type='text-single' label='User Name:' var='username'>");
4717 Xml.Append(
"<required/>");
4718 Xml.Append(
"</field>");
4719 Xml.Append(
"<field type='text-private' label='Password:' var='password'>");
4720 Xml.Append(
"<required/>");
4721 Xml.Append(
"</field>");
4722 Xml.Append(
"<field type='text-single' label='Email Address' var='email'/>");
4723 Xml.Append(
"<field type='text-single' label='Phone Number' var='phone'/>");
4724 Xml.Append(
"<field type='hidden' var='oauth_version'>");
4725 Xml.Append(
"<value>1.0</value>");
4726 Xml.Append(
"</field>");
4727 Xml.Append(
"<field type='hidden' var='oauth_signature_method'>");
4728 Xml.Append(
"<value>HMAC-SHA1</value>");
4729 Xml.Append(
"</field>");
4730 Xml.Append(
"<field type='hidden' var='oauth_token'>");
4731 Xml.Append(
"<value>");
4733 Xml.Append(
"</value>");
4734 Xml.Append(
"</field>");
4735 Xml.Append(
"<field type='hidden' var='oauth_token_secret'>");
4736 Xml.Append(
"<value>");
4738 Xml.Append(
"</value>");
4739 Xml.Append(
"</field>");
4740 Xml.Append(
"<field type='hidden' var='oauth_nonce'>");
4741 Xml.Append(
"<value/>");
4742 Xml.Append(
"</field>");
4743 Xml.Append(
"<field type='hidden' var='oauth_timestamp'>");
4744 Xml.Append(
"<value/>");
4745 Xml.Append(
"</field>");
4746 Xml.Append(
"<field type='hidden' var='oauth_consumer_key'>");
4747 Xml.Append(
"<value/>");
4748 Xml.Append(
"</field>");
4749 Xml.Append(
"<field type='hidden' var='oauth_signature'>");
4750 Xml.Append(
"<value/>");
4751 Xml.Append(
"</field>");
4753 Xml.Append(
"</query>");
4758 private async Task RegisterSet(
object Sender,
IqEventArgs e)
4766 if (!await this.CanRegister(Connection, e))
4770 string Password =
null;
4774 foreach (XmlNode N
in e.
Query.ChildNodes)
4776 if (!(N is XmlElement E))
4780 if (E.NamespaceURI == RegisterNamespace)
4782 switch (E.LocalName)
4785 UserName = E.InnerText;
4789 Password = E.InnerText;
4797 else if (E.LocalName ==
"x" && E.NamespaceURI == DataFormsNamespace &&
XML.
Attribute((XmlElement)N,
"type") ==
"submit")
4808 string OAuthVersion =
null;
4809 string OAuthSignatureMethod =
null;
4810 string OAuthToken =
null;
4811 string OAuthTokenSecret =
null;
4812 string OAuthNonce =
null;
4813 string OAuthTimestamp =
null;
4814 string OAuthConsumerKey =
null;
4815 string OAuthSignature =
null;
4817 foreach (XmlNode N2
in E.ChildNodes)
4819 if (N2.LocalName ==
"field")
4822 string Value =
null;
4824 foreach (XmlNode N3
in N2.ChildNodes)
4826 if (N3.LocalName ==
"value")
4828 Value = N3.InnerText;
4858 case "oauth_version":
4859 OAuthVersion = Value;
4862 case "oauth_signature_method":
4863 OAuthSignatureMethod = Value;
4870 case "oauth_token_secret":
4871 OAuthTokenSecret = Value;
4878 case "oauth_timestamp":
4879 OAuthTimestamp = Value;
4882 case "oauth_consumer_key":
4883 OAuthConsumerKey = Value;
4886 case "oauth_signature":
4887 OAuthSignature = Value;
4896 bool Signed =
false;
4897 bool Logged =
false;
4899 if (FormType ==
"urn:xmpp:xdata:signature:oauth1" && OAuthVersion ==
"1.0" && !
string.IsNullOrEmpty(UserName) &&
4900 !
string.IsNullOrEmpty(Password) && !(EMail is
null) && !(PhoneNr is
null) && !
string.IsNullOrEmpty(OAuthSignatureMethod) &&
4901 !
string.IsNullOrEmpty(OAuthToken) && !
string.IsNullOrEmpty(OAuthTokenSecret) && !
string.IsNullOrEmpty(OAuthNonce) &&
4902 !
string.IsNullOrEmpty(OAuthTimestamp) && !
string.IsNullOrEmpty(OAuthConsumerKey) && !
string.IsNullOrEmpty(OAuthSignature))
4904 string KeySecret = await this.persistenceLayer.GetApiKeySecret(OAuthConsumerKey);
4905 if (!
string.IsNullOrEmpty(KeySecret))
4907 StringBuilder PStr =
new StringBuilder();
4909 PStr.Append(
"email=");
4910 PStr.Append(OAuthEncode(EMail));
4911 PStr.Append(
"&FORM_TYPE=");
4912 PStr.Append(OAuthEncode(FormType));
4913 PStr.Append(
"&oauth_consumer_key=");
4914 PStr.Append(OAuthEncode(OAuthConsumerKey));
4915 PStr.Append(
"&oauth_nonce=");
4916 PStr.Append(OAuthEncode(OAuthNonce));
4917 PStr.Append(
"&oauth_signature_method=");
4918 PStr.Append(OAuthEncode(OAuthSignatureMethod));
4919 PStr.Append(
"&oauth_timestamp=");
4920 PStr.Append(OAuthEncode(OAuthTimestamp));
4921 PStr.Append(
"&oauth_token=");
4922 PStr.Append(OAuthEncode(OAuthToken));
4923 PStr.Append(
"&oauth_version=");
4924 PStr.Append(OAuthEncode(OAuthVersion));
4925 PStr.Append(
"&password=");
4926 PStr.Append(OAuthEncode(Password));
4927 PStr.Append(
"&phone=");
4928 PStr.Append(OAuthEncode(PhoneNr));
4929 PStr.Append(
"&username=");
4930 PStr.Append(OAuthEncode(UserName));
4932 StringBuilder BStr =
new StringBuilder();
4934 BStr.Append(
"submit&&");
4935 BStr.Append(OAuthEncode(PStr.ToString()));
4937 byte[] Key = Encoding.ASCII.GetBytes(OAuthEncode(KeySecret) +
"&" + OAuthEncode(OAuthTokenSecret));
4940 switch (OAuthSignatureMethod)
4948 LoginAuditor.
Fail(
"Registration form signature failed. Unhandled signature method requested.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
4949 new KeyValuePair<string, object>(
"ApiKey", OAuthConsumerKey),
4950 new KeyValuePair<string, object>(
"Method", OAuthSignatureMethod),
4951 new KeyValuePair<string, object>(
"EMail", EMail?.Value),
4952 new KeyValuePair<string, object>(
"PhoneNr", PhoneNr?.Value));
4956 if (!(Hash is
null))
4958 string Signature = OAuthEncode(Convert.ToBase64String(Hash));
4959 Signed = Signature == OAuthSignature;
4964 LoginAuditor.
Fail(
"Registration form signature failed.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
4965 new KeyValuePair<string, object>(
"ApiKey", OAuthConsumerKey),
4966 new KeyValuePair<string, object>(
"Method", OAuthSignatureMethod),
4967 new KeyValuePair<string, object>(
"EMail", EMail?.Value),
4968 new KeyValuePair<string, object>(
"PhoneNr", PhoneNr?.Value));
4975 LoginAuditor.
Fail(
"Registration form signature failed. Invalid API key used.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
4976 new KeyValuePair<string, object>(
"ApiKey", OAuthConsumerKey),
4977 new KeyValuePair<string, object>(
"EMail", EMail?.Value),
4978 new KeyValuePair<string, object>(
"PhoneNr", PhoneNr?.Value));
4984 LoginAuditor.
Fail(
"Registration form signature failed. Signature parameters not provided.", UserName, Connection.RemoteEndPoint, Connection.Protocol);
4989 if (
string.IsNullOrEmpty(UserName))
4992 if (UserName.
Length > 1023)
4997 if (
char.IsWhiteSpace(ch))
4999 await e.
IqErrorNotAllowed(e.
To,
"White-space characters not allowed in user names.",
"en");
5009 if (!IsValidUserName(UserName))
5015 KeyValuePair<IAccount, string[]> P = await this.persistenceLayer.CreateAccount(OAuthConsumerKey, UserName, Password, EMail, PhoneNr, Connection.RemoteEndPoint);
5017 if (Account is
null)
5019 string[] Alternatives = P.Value;
5020 StringBuilder Xml =
new StringBuilder();
5022 Xml.Append(
"<conflict xmlns='");
5023 Xml.Append(StanzaNamespace);
5026 if (!(Alternatives is
null) && Alternatives.Length > 0)
5028 Xml.Append(
"<alternatives xmlns='");
5029 Xml.Append(AlternativesNamespace);
5032 foreach (
string Alternative
in Alternatives)
5034 Xml.Append(
"<alternative>");
5036 Xml.Append(
"</alternative>");
5039 Xml.Append(
"</alternatives>");
5042 await e.
IqError(
"cancel", Xml.ToString(), e.
To,
5043 "Account name already exists, or API key limit reached.",
"en");
5046 LoginAuditor.
Fail(
"Registration failed. Signature OK, but account already exists, or API key limit reached.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
5047 new KeyValuePair<string, object>(
"ApiKey", OAuthConsumerKey),
5048 new KeyValuePair<string, object>(
"Method", OAuthSignatureMethod),
5049 new KeyValuePair<string, object>(
"EMail", EMail?.Value),
5050 new KeyValuePair<string, object>(
"PhoneNr", PhoneNr?.Value));
5057 LoginAuditor.
Success(
"Registration successful and account created.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
5058 new KeyValuePair<string, object>(
"ApiKey", OAuthConsumerKey),
5059 new KeyValuePair<string, object>(
"Method", OAuthSignatureMethod),
5060 new KeyValuePair<string, object>(
"EMail", EMail?.Value),
5061 new KeyValuePair<string, object>(
"PhoneNr", PhoneNr?.Value));
5068 LoginAuditor.
Fail(
"Registration form signature failed.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
5069 new KeyValuePair<string, object>(
"ApiKey", OAuthConsumerKey),
5070 new KeyValuePair<string, object>(
"Method", OAuthSignatureMethod),
5071 new KeyValuePair<string, object>(
"EMail", EMail?.Value),
5072 new KeyValuePair<string, object>(
"PhoneNr", PhoneNr?.Value));
5087 await e.
IqErrorForbidden(e.
To,
"Only allowed to remove your own account, while account is active.",
"en");
5088 else if (!this.IsServerDomain(e.
From.
Domain,
true))
5090 await e.
IqError(
"auth",
"<registration-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>", e.
To,
5091 "Only accounts registered on the broker can remove their accounts.",
"en");
5093 else if (await this.persistenceLayer.DeleteAccount(e.
From.
Account, Connection.RemoteEndPoint))
5096 Connection.AccountDeleted();
5100 await e.
IqError(
"auth",
"<registration-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>", e.
To,
5101 "Only accounts registered on the broker can remove their accounts.",
"en");
5107 if (!
string.IsNullOrEmpty(UserName) && !
string.IsNullOrEmpty(Password))
5110 !await
this.persistenceLayer.ChangePassword(UserName, Password))
5119 if (!(CurrentConnections is
null))
5139 foreach (
char ch
in UserName)
5205 private static string OAuthEncode(
string s)
5207 StringBuilder Result =
new StringBuilder();
5209 foreach (
char ch
in s)
5211 if (OAuthReserved.IndexOf(ch) < 0)
5214 Result.Append(((
int)ch).
ToString(
"X2"));
5220 return Result.ToString();
5223 private const string OAuthReserved =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
5227 #region Blocking Command (XEP-0191) and Spam Reporting (XEP-0377).
5229 private async Task BlockListGet(
object Sender,
IqEventArgs e)
5233 await e.
IqErrorForbidden(e.
To,
"Access to block list can only be granted from the corresponding client.",
"en");
5237 IEnumerable<CaseInsensitiveString> BlockList = await this.persistenceLayer.GetBlockList(Connection.UserName);
5238 if (BlockList is
null)
5244 Connection.WantsBlockList =
true;
5246 StringBuilder Xml =
new StringBuilder();
5248 Xml.Append(
"<blocklist xmlns='");
5249 Xml.Append(BlockingCommandNamespace);
5254 Xml.Append(
"<item jid='");
5259 Xml.Append(
"</blocklist>");
5264 private async Task BlockSet(
object Sender,
IqEventArgs e)
5268 await e.
IqErrorForbidden(e.
To,
"Access to block list can only be granted from the corresponding client.",
"en");
5273 List<CaseInsensitiveString> BareJids =
null;
5274 string Text =
string.Empty;
5275 string TextLanguage =
string.Empty;
5278 foreach (XmlNode N
in e.
Query.ChildNodes)
5280 switch (N.LocalName)
5290 BareJids ??=
new List<CaseInsensitiveString>();
5291 BareJids.Add(BareJid);
5295 foreach (XmlNode N2
in N.ChildNodes)
5297 switch (N2.LocalName)
5300 XmlElement E = (XmlElement)N2;
5318 if (BareJids is
null)
5324 StringBuilder Xml =
null;
5328 if (await this.persistenceLayer.AddBlock(Connection.UserName, BareJid2, Reason, Text, TextLanguage))
5330 IClientConnection[] Connections = this.GetClientConnections(Connection.BareJid);
5332 if (!(Connections is
null))
5339 Xml =
new StringBuilder();
5343 Xml.Append(
"<block xmlns='");
5344 Xml.Append(BlockingCommandNamespace);
5345 Xml.Append(
"'><item jid='");
5346 Xml.Append(BareJid2);
5347 Xml.Append(
"'/></block>");
5353 catch (Exception ex)
5355 Connection.Exception(ex);
5356 await Connection.DisposeAsync();
5367 private async Task UnblockSet(
object Sender,
IqEventArgs e)
5371 await e.
IqErrorForbidden(e.
To,
"Access to block list can only be granted from the corresponding client.",
"en");
5375 List<CaseInsensitiveString> BareJids =
null;
5378 foreach (XmlNode N
in e.
Query.ChildNodes)
5380 if (N.LocalName ==
"item")
5389 BareJids ??=
new List<CaseInsensitiveString>();
5390 BareJids.Add(BareJid);
5394 if (BareJids is
null)
5396 if (await this.persistenceLayer.ClearBlocks(Connection.UserName))
5398 IClientConnection[] Connections = this.GetClientConnections(Connection.BareJid);
5400 if (!(Connections is
null))
5408 await Connection2.
IQ(
"set",
string.Empty, Connection2.
Address, e.
To,
string.
Empty,
5409 "<unblock xmlns='" + BlockingCommandNamespace +
"'/>",
this);
5411 catch (Exception ex)
5415 Connection.Exception(ex);
5416 await Connection.DisposeAsync();
5418 catch (Exception ex2)
5430 StringBuilder Xml =
null;
5434 if (await this.persistenceLayer.Unblock(Connection.UserName, BareJid2))
5436 IClientConnection[] Connections = this.GetClientConnections(Connection.BareJid);
5438 if (!(Connections is
null))
5445 Xml =
new StringBuilder();
5449 Xml.Append(
"<unblock xmlns='");
5450 Xml.Append(BlockingCommandNamespace);
5451 Xml.Append(
"'><item jid='");
5452 Xml.Append(BareJid2);
5453 Xml.Append(
"'/></unblock>");
5459 catch (Exception ex)
5461 Connection.Exception(ex);
5462 await Connection.DisposeAsync();
5483 FileName = this.domainSnifferPath.Replace(
"%DOMAIN%", Key);
5485 FileName = this.clientSnifferPath.Replace(
"%ENDPOINT%", Key);
5492 if (this.s2sEndpoints.TryGetValue(Key, out
IS2SEndpoint Endpoint) &&
5493 Endpoint.HasSniffers)
5495 foreach (
ISniffer Sniffer
in Endpoint.Sniffers)
5498 return XmlFileSniffer2;
5504 IClientConnection[] Connections = this.GetClientConnections(Key +
"@" + this.domain);
5505 if (!(Connections is
null))
5514 return XmlFileSniffer2;
5524 internal async Task CacheSniffers(IEnumerable<ISniffer> Sniffers)
5526 foreach (
ISniffer Sniffer
in Sniffers)
5531 await DisposableAsync.DisposeAsync();
5532 else if (Sniffer is IDisposable Disposable)
5533 Disposable.Dispose();
5539 if (this.sniffers is
null)
5542 this.sniffers.Removed += this.Sniffers_Removed;
5550 if (this.disposed || (DateTime.Now - e.
Value.LastEvent).TotalMinutes > 30)
5551 return e.
Value.DisposeAsync();
5553 return Task.CompletedTask;
5564 internal void DataReceived(
int NrRead)
5566 lock (this.statSync)
5568 this.nrBytesRx += NrRead;
5576 internal void DataTransmitted(
int NrWritten)
5578 lock (this.statSync)
5580 this.nrBytesTx += NrWritten;
5594 string Namespace =
null;
5595 string LocalName =
null;
5596 string LocalNameBak =
null;
5597 string ns = StanzaElement.NamespaceURI;
5599 foreach (XmlNode N
in StanzaElement.ChildNodes)
5601 if (N is XmlElement E)
5603 if (E.NamespaceURI != ns)
5605 Namespace = E.NamespaceURI;
5606 LocalName = E.LocalName;
5610 LocalNameBak ??= E.LocalName;
5614 if (Namespace is
null)
5616 Namespace = StanzaElement.NamespaceURI;
5617 LocalName = LocalNameBak ??
string.Empty;
5620 lock (this.statSync)
5624 this.IncLocked(
Stanza +
"#" + Type, this.stanzasPerStanzaType);
5625 this.IncLocked(From.
Domain,
this.stanzasPerFromDomain);
5626 this.IncLocked(From.
BareJid,
this.stanzasPerFromBareJid);
5627 this.IncLocked(To.
Domain,
this.stanzasPerToDomain);
5628 this.IncLocked(To.
BareJid,
this.stanzasPerToBareJid);
5629 this.IncLocked(Namespace, this.stanzasPerNamespace);
5630 this.IncLocked(Namespace +
"#" + LocalName, this.stanzasPerFqn);
5634 private void IncLocked(
string Key, Dictionary<string, Statistic> Stat)
5636 if (!Stat.TryGetValue(Key, out
Statistic Rec))
5652 DateTime TP = DateTime.Now;
5654 lock (this.statSync)
5656 Result =
new Statistics.CommunicationStatistics()
5658 StanzasPerStanzaType = this.stanzasPerStanzaType,
5659 StanzasPerFromDomain = this.stanzasPerFromDomain,
5660 StanzasPerToDomain = this.stanzasPerToDomain,
5661 StanzasPerFromBareJid = this.stanzasPerFromBareJid,
5662 StanzasPerToBareJid = this.stanzasPerToBareJid,
5663 StanzasPerNamespace = this.stanzasPerNamespace,
5664 StanzasPerFqn = this.stanzasPerFqn,
5665 LastStat = this.lastStat,
5667 NrBytesRx = this.nrBytesRx,
5668 NrBytesTx = this.nrBytesTx,
5669 NrStanzas = this.nrStanzas
5672 this.stanzasPerStanzaType =
new Dictionary<string, Statistic>();
5673 this.stanzasPerFromDomain =
new Dictionary<string, Statistic>();
5674 this.stanzasPerToDomain =
new Dictionary<string, Statistic>();
5675 this.stanzasPerFromBareJid =
new Dictionary<string, Statistic>();
5676 this.stanzasPerToBareJid =
new Dictionary<string, Statistic>();
5677 this.stanzasPerNamespace =
new Dictionary<string, Statistic>();
5678 this.stanzasPerFqn =
new Dictionary<string, Statistic>();
5690 #region XEP-0049: Private XML Storage
5692 internal async Task PrivateXmlStorageGet(
object Sender,
IqEventArgs e)
5696 await e.
IqErrorForbidden(e.
To,
"Private storage only accessible from clients directly connected to the server.",
"en");
5700 IAccount FromAccount = await this.persistenceLayer.GetAccount(Connection.UserName);
5701 if (FromAccount is
null)
5707 StringBuilder Xml =
new StringBuilder();
5711 Xml.Append(
"<query xmlns='");
5712 Xml.Append(PrivateXmlStorageNamespace);
5715 foreach (XmlNode N
in e.
Query.ChildNodes)
5717 if (N is XmlElement E)
5731 Xml.Append(Element.
Xml);
5750 Xml.Append(
"</query>");
5755 internal async Task PrivateXmlStorageSet(
object Sender,
IqEventArgs e)
5759 await e.
IqErrorForbidden(e.
To,
"Private storage only accessible from clients directly connected to the server.",
"en");
5763 IAccount FromAccount = await this.persistenceLayer.GetAccount(Connection.UserName);
5764 if (FromAccount is
null)
5773 foreach (XmlNode N
in e.
Query.ChildNodes)
5775 if (N is XmlElement E)
5777 bool DeleteElement = IsEmptyPrivateXml(E);
5795 Element.Xml = E.OuterXml;
5796 Element.Updated = DateTime.UtcNow;
5805 if (Found || DeleteElement)
5808 DateTime TP = DateTime.UtcNow;
5813 Namespace = E.NamespaceURI,
5814 LocalName = E.LocalName,
5828 private static bool IsEmptyPrivateXml(XmlElement E)
5830 if (E.HasChildNodes)
5833 foreach (XmlAttribute Attr
in E.Attributes)
5835 if (Attr.Name !=
"xmlns")
5844 #region SMTP integration
5848 return this.ProcessMessage(e.
Message);
5859 List<KeyValuePair<string, object>> Tags =
new List<KeyValuePair<string, object>>();
5861 foreach (KeyValuePair<string, string> P
in Message.
AllHeaders)
5862 Tags.Add(
new KeyValuePair<string, object>(P.Key, P.Value));
5878 Dictionary<CaseInsensitiveString, bool> Processed =
new Dictionary<CaseInsensitiveString, bool>();
5880 await this.ProcessMessage(Message, Message.
To, Processed);
5881 await this.ProcessMessage(Message, Message.
Cc, Processed);
5882 await this.ProcessMessage(Message, Message.
Bcc, Processed);
5884 catch (Exception ex)
5890 private async Task ProcessMessage(
SmtpMessage Message, IEnumerable<MailAddress> Recipients, Dictionary<CaseInsensitiveString, bool> Processed)
5892 if (!(Recipients is
null))
5894 foreach (MailAddress Recipient
in Recipients)
5896 if (!Processed.ContainsKey(Recipient.Address))
5898 Processed[Recipient.Address] =
true;
5899 await this.ProcessMessage(Message, Recipient);
5905 private readonly Dictionary<string, DateTime> lastBounce =
new Dictionary<string, DateTime>();
5907 private bool CanReturnBounceMessage(MailAddress Recipient, MailAddress Sender)
5909 string Key = Recipient.Address +
" | " + Sender.Address;
5912 lock (this.lastBounce)
5914 if (this.lastBounce.ContainsKey(Key))
5918 TP = scheduler.
Add(DateTime.Now.AddHours(4), (P) =>
5920 lock (this.lastBounce)
5922 this.lastBounce.Remove((string)P);
5926 lock (this.lastBounce)
5928 this.lastBounce[Key] = TP;
5934 private async Task ProcessMessage(
SmtpMessage Message, MailAddress Recipient)
5939 if (this.IsServerDomain(Addr.
Domain,
true))
5942 StringBuilder Markdown;
5949 if (!this.CanReturnBounceMessage(Recipient, Message.
FromMail))
5955 Markdown =
new StringBuilder();
5957 Markdown.AppendLine(
"Welcome");
5958 Markdown.AppendLine(
"===========");
5959 Markdown.AppendLine();
5961 Markdown.Append(
"The mail server at **");
5963 Markdown.AppendLine(
"** only forwards mail messages from approved senders.");
5964 Markdown.Append(
"Since **");
5966 Markdown.Append(
"** has not been approved by **");
5968 Markdown.AppendLine(
"**, your mail has not been forwarded.");
5969 Markdown.AppendLine();
5971 if (this.httpServer.OpenHttpsPorts.Length > 0 ||
this.httpServer.OpenHttpPorts.Length > 0)
5973 Markdown.Append(
"If you want, you can send a request to **");
5975 Markdown.Append(
"** to become approved, by following this link: [Request approval](");
5976 Markdown.Append(
"http");
5978 if (this.httpServer.OpenHttpsPorts.Length > 0)
5980 Markdown.Append(
"s://");
5981 Markdown.Append(this.domain);
5985 Markdown.Append(
':');
5986 Markdown.Append(this.httpServer.OpenHttpsPorts[0]);
5991 Markdown.Append(
"://");
5992 Markdown.Append(this.domain);
5996 Markdown.Append(
':');
5997 Markdown.Append(this.httpServer.OpenHttpPorts[0]);
6001 string Expires = DateTime.Now.AddDays(1).Ticks.ToString();
6003 Markdown.Append(
"/RequestWhiteList?Sender=");
6005 Markdown.Append(
"&Receiver=");
6007 Markdown.Append(
"&Expires=P");
6008 Markdown.Append(Expires);
6009 Markdown.Append(
"&MAC=");
6011 StringBuilder sb =
new StringBuilder();
6012 sb.Append(Message.
FromMail.Address);
6014 sb.Append(Recipient.Address);
6021 if (
string.IsNullOrEmpty(Key))
6023 Key = Convert.ToBase64String(GetRandomNumbers(32));
6027 RequestWhiteList.whiteListKey = Key;
6031 Encoding.UTF8.GetBytes(sb.ToString()));
6033 Markdown.Append(MAC);
6034 Markdown.AppendLine(
")");
6035 Markdown.AppendLine();
6037 Log.
Notice(
"Mail discarded. Bounce message returned.", Recipient.Address, Message.
FromMail.Address,
6038 new KeyValuePair<string, object>(
"Sender", Message.
FromMail.Address),
6039 new KeyValuePair<string, object>(
"Receiver", Recipient.Address),
6040 new KeyValuePair<string, object>(
"Expires", Expires),
6041 new KeyValuePair<string, object>(
"MAC", MAC));
6044 await this.SendMailMessage(Recipient.Address, Message.
FromMail.Address,
"Approval required", Markdown.ToString());
6050 ISender Sender = await this.GetS2sEndpoint(To.
Domain, From.
Domain,
true,
"Forwarding e-mail.");
6051 List<EmbeddedContent> Attachments =
null;
6052 List<EmbeddedContent> Inline =
null;
6054 string PlainText =
null;
6058 if (!(Sender is
null))
6062 Attachments =
new List<EmbeddedContent>();
6068 Inline =
new List<EmbeddedContent>();
6074 LinkedList<MultipartContent> ToProcess =
new LinkedList<MultipartContent>();
6079 while (!(ToProcess.First is
null))
6082 ToProcess.RemoveFirst();
6095 ToProcess.AddLast(MultipartContent2);
6096 else if (Decoded is
null)
6100 Attachments ??=
new List<EmbeddedContent>();
6112 Attachments ??=
new List<EmbeddedContent>();
6117 Inline ??=
new List<EmbeddedContent>();
6123 ToProcess.AddLast(MultipartContent2);
6124 else if (Decoded is
null)
6128 Attachments ??=
new List<EmbeddedContent>();
6138 if (!(Decoded is
null))
6140 if (PlainText is
null && Decoded is
string s)
6142 else if (Html is
null && Decoded is
HtmlDocument Html2)
6152 if (
string.IsNullOrEmpty(PlainText))
6158 StringBuilder Content =
new StringBuilder();
6159 bool HasBody =
false;
6161 if (!
string.IsNullOrEmpty(Message.
Subject))
6163 Content.Append(
"<subject>");
6165 Content.Append(
"</subject>");
6168 if (!
string.IsNullOrEmpty(Message.
MessageID))
6170 Content.Append(
"<thread>");
6172 Content.Append(
"</thread>");
6175 if (!
string.IsNullOrEmpty(PlainText))
6177 Content.Append(
"<body>");
6179 Content.Append(
"</body>");
6184 if (!(Html?.Body is
null))
6186 Content.Append(
"<html xmlns='http://jabber.org/protocol/xhtml-im'>");
6187 Content.Append(
"<body xmlns='http://www.w3.org/1999/xhtml'>");
6189 if (Html.
Body.HasChildren)
6195 Content.Append(
"</body></html>");
6202 Content.Append(
"<content xmlns='");
6203 Content.Append(ContentNamespace);
6204 Content.Append(
"' type='text/markdown'>");
6206 Content.Append(
"</content>");
6213 Markdown =
new StringBuilder();
6215 Markdown.AppendLine(
"Unable to process incoming message");
6216 Markdown.AppendLine(
"=======================================");
6217 Markdown.AppendLine();
6219 Markdown.Append(
"The mail server at **");
6221 Markdown.AppendLine(
"** was unable to forward the mail message to **");
6223 Markdown.Append(
"**), since the content type `");
6225 Markdown.AppendLine(
"` is not handled.");
6227 await this.SendMailMessage(Recipient.Address, Message.
FromMail.Address,
"Unable to process incoming message", Markdown.ToString());
6229 Log.
Notice(
"Mail discarded due to unhandled Content-Type.", Recipient.Address, Message.
FromMail.Address,
6230 new KeyValuePair<string, object>(
"Content-Type", Message.
ContentType));
6235 Content.Append(
"<mailInfo xmlns='urn:xmpp:smtp' contentType='");
6238 if (!
string.IsNullOrEmpty(Message.
MessageID))
6240 Content.Append(
"' id='");
6244 Content.Append(
"' priority='");
6245 Content.Append(((
int)Message.
Priority).ToString());
6247 if (Message.
Date.HasValue)
6249 Content.Append(
"' date='");
6253 Content.Append(
"' fromMail='");
6256 Content.Append(
"' fromHeader='");
6259 Content.Append(
"' sender='");
6262 DateTime TP = DateTime.Now;
6269 ContentId = Guid.NewGuid().ToString()
6274 Content.Append(
"' size='");
6277 Content.Append(
"' cid='");
6280 Content.Append(
"'><headers xmlns='http://jabber.org/protocol/shim'>");
6282 foreach (KeyValuePair<string, string> P
in Message.
AllHeaders)
6284 Content.Append(
"<header name='");
6286 Content.Append(
"'>");
6288 Content.Append(
"</header>");
6291 Content.Append(
"</headers>");
6293 await this.Serialize(Content, Attachments?.ToArray(),
"attachment", Addr);
6294 await this.Serialize(Content, Inline?.ToArray(),
"inline", Addr);
6296 Content.Append(
"</mailInfo>");
6298 await this.Message(
"chat",
string.Empty, To, From,
string.Empty, Content.ToString(), Sender);
6304 IS2SEndpoint S2sEndpoint = await this.GetS2sEndpoint(this.domain, Addr.
Domain,
true,
"Relaying incoming mail message.");
6309 catch (Exception ex)
6322 switch (E.LocalName.ToUpper())
6359 Output.Append(E.LocalName);
6361 if (E.HasAttributes)
6370 Output.Append(
"=\"");
6380 foreach (
HtmlNode N2
in E.Children)
6383 Output.Append(
"</");
6384 Output.Append(E.LocalName);
6387 else if (E.IsEmptyElement)
6388 Output.Append(
"/>");
6391 Output.Append(
"></");
6392 Output.Append(E.LocalName);
6397 Output.Append(
XML.
Encode(Text.InlineText));
6402 char ch = (char)EntityUnicode.Code;
6407 Output.Append(
"<");
6411 Output.Append(
">");
6415 Output.Append(
""");
6419 Output.Append(
"'");
6423 Output.Append(
"&");
6433 switch (Entity.EntityName.ToLower())
6441 Output.Append(Entity.EntityName);
6455 private async Task Serialize(StringBuilder Content, IEnumerable<EmbeddedContent> Objects,
string ElementName,
XmppAddress Recipient)
6457 if (!(Objects is
null))
6466 Created = DateTime.Now,
6467 ContentId = Guid.NewGuid().ToString()
6472 Content.Append(
'<');
6473 Content.Append(ElementName);
6474 Content.Append(
" contentType='");
6479 Content.Append(
"' description='");
6485 Content.Append(
"' fileName='");
6491 Content.Append(
"' name='");
6497 Content.Append(
"' id='");
6501 Content.Append(
"' cid='");
6504 Content.Append(
"' size='");
6507 Content.Append(
"'/>");
6512 private async Task GetMailContent(
object Sender,
IqEventArgs e)
6518 if (Content is
null)
6532 if (!
string.IsNullOrEmpty(ContentType) &&
string.Compare(Content.
ContentType, ContentType,
true) != 0)
6545 LinkedList<MultipartContent> ToProcess =
new LinkedList<MultipartContent>();
6548 while (Data is
null && !(ToProcess.First is
null))
6551 ToProcess.RemoveFirst();
6555 if (
string.Compare(Obj.
ContentType, ContentType,
true) == 0)
6557 Data = Obj.TransferDecoded ?? Obj.
Raw;
6596 StringBuilder Xml =
new StringBuilder();
6598 Xml.Append(
"<content type='");
6600 Xml.Append(
"' xmlns='");
6601 Xml.Append(MailNamespace);
6608 Xml.Append(Convert.ToBase64String(Data));
6609 Xml.Append(
"</content>");
6615 private async Task DeleteMailContent(
object Sender,
IqEventArgs e)
6620 if (Content is
null)
6660 return await this.smtpServer.SendMessage(From, To, Subject,
new EmbeddedContent[]
6664 ContentType =
"text/html; charset=utf-8",
6665 Raw = Encoding.UTF8.GetBytes(HTML)
6669 ContentType =
"text/plain; charset=utf-8",
6670 Raw = Encoding.UTF8.GetBytes(PlainText)
6674 ContentType =
"text/markdown; charset=utf-8",
6675 Raw = Encoding.UTF8.GetBytes(Markdown)
6682 #region Service Discovery
6691 return this.ServiceDiscoveryAsync(To,
string.Empty);
6702 StringBuilder Xml =
new StringBuilder();
6703 bool CacheResponse =
string.IsNullOrEmpty(Node) && (
string.IsNullOrEmpty(To) || this.IsServerDomain(To,
true));
6704 TaskCompletionSource<ServiceDiscoveryResult> Result =
new TaskCompletionSource<ServiceDiscoveryResult>();
6706 Xml.Append(
"<query xmlns='");
6707 Xml.Append(DiscoveryNamespace);
6709 if (!
string.IsNullOrEmpty(Node))
6711 Xml.Append(
"' node='");
6717 await this.SendIqRequest(
"get", this.domainAddress,
new XmppAddress(To),
string.Empty,
6718 Xml.
ToString(),
true, (Sender, e) =>
6722 Dictionary<string, bool> Features = new Dictionary<string, bool>();
6723 List<Identity> Identities = new List<Identity>();
6725 foreach (XmlNode N in e.Response.ChildNodes)
6727 if (N.LocalName ==
"query")
6729 foreach (XmlNode N2 in N.ChildNodes)
6731 switch (N2.LocalName)
6734 Identities.Add(new Identity((XmlElement)N2));
6738 Features[XML.Attribute((XmlElement)N2,
"var")] = true;
6745 Result.TrySetResult(new ServiceDiscoveryResult(Identities.ToArray(), Features));
6747 else if (
string.IsNullOrEmpty(e.ErrorText))
6748 Result.TrySetException(
new Exception(
"Unable to perform service discovery."));
6750 Result.TrySetException(
new Exception(e.ErrorText));
6752 return Task.CompletedTask;
6756 return await Result.Task;
6765 return this.ServiceItemsDiscoveryAsync(To,
string.Empty);
6775 StringBuilder Xml =
new StringBuilder();
6776 TaskCompletionSource<Item[]> Result =
new TaskCompletionSource<Item[]>();
6778 Xml.Append(
"<query xmlns='");
6779 Xml.Append(DiscoveryItemsNamespace);
6781 if (!
string.IsNullOrEmpty(Node))
6783 Xml.Append(
"' node='");
6789 await this.SendIqRequest(
"get", this.domainAddress,
new XmppAddress(To),
6790 string.Empty, Xml.
ToString(),
true, (Sender, e) =>
6794 List<Item> Items = new List<Item>();
6796 foreach (XmlNode N in e.Response.ChildNodes)
6798 if (N.LocalName ==
"query")
6800 foreach (XmlNode N2 in N.ChildNodes)
6802 if (N2.LocalName ==
"item")
6803 Items.Add(new Item((XmlElement)N2));
6808 Result.TrySetResult(Items.ToArray());
6810 else if (
string.IsNullOrEmpty(e.ErrorText))
6811 Result.TrySetException(
new Exception(
"Unable to perform service items discovery."));
6813 Result.TrySetException(
new Exception(e.ErrorText));
6815 return Task.CompletedTask;
6819 return await Result.Task;
6824 #region Finding components
6836 lock (this.services)
6842 string BareJid = GetBareJID(Jid);
6843 int i = BareJid.IndexOf(
'@');
6844 string Domain = BareJid[(i + 1)..];
6847 string Result =
null;
6849 if (e.HasFeature(Feature))
6853 Item[] Items = await this.ServiceItemsDiscoveryAsync(Domain);
6857 e = await this.ServiceDiscoveryAsync(
Component.JID);
6858 if (e.HasFeature(Feature))
6866 if (!
string.IsNullOrEmpty(Result))
6868 lock (this.services)
6870 this.services[Key] = Result;
6879 #region Push Notification
6883 private async Task NewTokenHandler(
object Sender,
IqEventArgs e)
6893 if (!this.IsServerDomain(e.
From.
Domain,
true))
6895 await e.
IqErrorForbidden(e.
To,
"Push Forwarding service only available for clients on broker.",
"en");
6912 if (
string.IsNullOrEmpty(Token))
6919 DateTime TP = DateTime.UtcNow;
6921 if (TokenObj is
null)
6934 tokens[BareJid] = TokenObj;
6940 TokenObj.Token = Token;
6941 TokenObj.Service = Service;
6943 TokenObj.Updated = TP;
6963 tokens[BareJid] = Token;
6970 #region Remove Token
6972 private async Task RemoveTokenHandler(
object Sender,
IqEventArgs e)
6982 if (!this.IsServerDomain(e.
From.
Domain,
true))
6984 await e.
IqErrorForbidden(e.
To,
"Push Forwarding service only available for clients on broker.",
"en");
6989 if (TokenObj is
null)
6995 tokens.Remove(BareJid);
7005 private async Task ClearRulesHandler(
object Sender,
IqEventArgs e)
7015 if (!this.IsServerDomain(e.
From.
Domain,
true))
7017 await e.
IqErrorForbidden(e.
To,
"Push Forwarding service only available for clients on broker.",
"en");
7022 rules.Remove(RuleKey(Rule));
7034 StringBuilder sb =
new StringBuilder();
7040 sb.Append(LocalName);
7042 sb.Append(Namespace);
7044 return sb.ToString();
7051 private async Task AddRuleHandler(
object Sender,
IqEventArgs e)
7061 if (!this.IsServerDomain(e.
From.
Domain,
true))
7063 await e.
IqErrorForbidden(e.
To,
"Push Forwarding service only available for clients on broker.",
"en");
7072 string PatternMatchingScript =
null;
7073 string ContentScript =
null;
7075 foreach (XmlNode N
in e.
Query.ChildNodes)
7077 if (N is XmlElement E && E.NamespaceURI == MessagePushNamespace)
7079 switch (E.LocalName)
7081 case "PatternMatching":
7082 PatternMatchingScript = E.InnerText.Trim();
7088 if (!CheckExpressionSafe(Exp, out
ScriptNode Prohibited))
7090 await e.
IqErrorForbidden(e.
To,
"Pattern Matching Script contains prohibited elements: " +
7091 Prohibited?.SubExpression,
"en");
7104 ContentScript = E.InnerText.Trim();
7110 if (!CheckExpressionSafe(Exp, out
ScriptNode Prohibited))
7113 Prohibited?.SubExpression,
"en");
7139 LocalName = LocalName,
7140 Namespace = Namespace,
7143 MessageVariable = MessageVariable,
7144 PatternMatchingScript = PatternMatchingScript,
7145 ContentScript = ContentScript
7150 rules[RuleKey(Rule)] = Rule;
7154 Rule.Channel = Channel;
7155 Rule.MessageVariable = MessageVariable;
7156 Rule.PatternMatchingScript = PatternMatchingScript;
7157 Rule.ContentScript = ContentScript;
7165 private readonly
static Assembly scriptContent = typeof(
GraphEncoder).Assembly;
7166 private readonly
static Assembly scriptPersistence = typeof(
IncCounter).Assembly;
7167 private readonly
static Assembly[] prohibitedAssemblies =
new Assembly[]
7172 typeof(
WhoIs).Assembly,
7186 return CheckExpressionSafe(
Expression,
false,
false,
false, out Prohibited);
7199 bool AllowCustomFunctions, out
ScriptNode Prohibited)
7206 Assembly Assembly = Node.GetType().Assembly;
7208 foreach (Assembly
A in prohibitedAssemblies)
7210 if (
A.FullName == Assembly.FullName)
7212 if (A == scriptContent)
7214 if (Node is Script.Content.Functions.Duration ||
7215 Node.GetType().Namespace == typeof(Utf8Encode).Namespace)
7220 else if (
A == scriptPersistence)
7238 if ((Node is
NamedMember && !AllowNamedMembers) ||
7246 (Node is
Error && !AllowError))
7256 Prohibited = Prohibited2;
7269 string LocalName,
string Namespace)
7271 string Key = RuleKey(BareJid,
MessageType, LocalName, Namespace);
7290 private async Task RemoveRuleHandler(
object Sender,
IqEventArgs e)
7300 if (!this.IsServerDomain(e.
From.
Domain,
true))
7302 await e.
IqErrorForbidden(e.
To,
"Push Forwarding service only available for clients on broker.",
"en");
7318 rules.Remove(RuleKey(Rule));
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
string Content
CDATA Content
bool HasPrefix
If the attribute has a prefix.
string LocalName
Attribute local name.
string Value
Attribute value.
static string GetBody(string Html)
Extracts the contents of the BODY element in a HTML string.
Body Body
First BODY element of document, if found, null otherwise.
Base class for all HTML elements.
static string EntityToCharacter(string Entity)
Converts an HTML entity into a character.
HTML Entity, as a unicode number string.
Base class for all HTML nodes.
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > PostAsync(Uri Uri, object Data, params KeyValuePair< string, string >[] Headers)
Posts to a resource, using a Uniform Resource Identifier (or Locator).
static Task< ContentResponse > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
Helps with common JSON-related tasks.
static string Encode(string s)
Encodes a string for inclusion in JSON.
Contains a markdown document. This markdown document class supports original markdown,...
static string Encode(string s)
Encodes all special characters in a string so that it can be included in a markdown document without ...
Task< string > GenerateMarkdown()
Generates Markdown from the markdown text.
async Task< string > GeneratePlainText()
Generates Plain Text from the markdown text.
async Task< string > GenerateHTML()
Generates HTML from the markdown text.
static Task< MarkdownDocument > CreateAsync(string MarkdownText, params Type[] TransparentExceptionTypes)
Contains a markdown document. This markdown document class supports original markdown,...
Represents alternative versions of the same content, encoded with multipart/alternative
Represents content embedded in other content.
string FileName
Filename of embedded object.
ContentDisposition Disposition
Disposition of embedded object.
string Name
Name of embedded object.
string Description
Content-Description of embedded object, if defined.
string ContentType
Content-Type of embedded object.
object Decoded
Decoded body of embedded object. ContentType defines how TransferDecoded is transformed into Decoded.
byte[] TransferDecoded
Transformed body of embedded object. TransferEncoding defines how Raw is transformed into TransferDec...
byte[] Raw
Raw, untransformed body of embedded object.
string ID
Content-ID of embedded object, if defined.
Abstract base class for multipart content
EmbeddedContent[] Content
Embedded content.
Helps with common XML-related tasks.
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
static string HtmlValueEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe or the quote.
static string Encode(string s)
Encodes a string for use in XML.
static string HtmlAttributeEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe.
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Static class managing the application event log. Applications and services log events on this static ...
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.
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
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.
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.
Contains statistical information about one item.
void Bind()
Binds to a TcpClient that was already connected when provided to the constructor.
string RemoteEndPoint
Remote End-point of connection. This corresponds to the IP Endpoint of the remote party in normal cas...
void Continue()
Continues reading from the socket, if paused in an event handler.
Simple base class for classes implementing communication protocols.
void Information(string Comment)
Called to inform the viewer of something.
DNS resolver, as defined in:
static Task< SRV > TryLookupServiceEndpoint(string DomainName, string ServiceName, string Protocol)
Tries to look up a service endpoint for a domain. If multiple are available, an appropriate one is se...
static Task< string[]> TryLookupMailExchange(string DomainName)
Tries to look up the Mail Exchanges related to a given domain name.
string TargetHost
Target Host
Base class of all HTTP Exceptions.
Implements an HTTP server.
HttpResource Register(HttpResource Resource)
Registers a resource with the server.
const int DefaultHttpPort
Default HTTP Port (80).
const int DefaultHttpsPort
Default HTTPS port (443).
Module that controls the life cycle of communication.
static bool Stopping
If the system is stopping.
Client Connection event argument.
Event arguments for SMTP Message events.
SmtpMessage Message
Message object
Represents one message received over SMTP
string MessageID
Message-ID.
object DecodedBody
Decoded body. ContentType defines how TransformedBody is transformed into DecodedBody.
MailAddress[] To
Recipients, if defined by To mail headers.
DateTimeOffset? Date
Date of message, if defined
MailAddress[] Cc
Recipients, if defined by Cc mail headers.
MailAddress FromMail
From address, as specified by the client to initiate mail transfer.
MailAddress FromHeader
From address, as specified in the mail headers.
Priority Priority
Priority of message
EmbeddedContent[] Attachments
Any attachments, if specified.
MailAddress[] Bcc
Recipients, if defined by Bcc mail headers.
KeyValuePair< string, string >[] AllHeaders
All mail headers provided by client.
string ContentType
Content Type of message, if defined. Affects how TransformedBody is transformed into DecodedBody.
string Subject
Subject of message, if defined
EmbeddedContent[] InlineObjects
Any inline objects, if specified.
MailAddress Sender
Sender, as specified in the mail headers.
byte[] UntransformedBody
Raw, untrasnformed body of message.
Implements a simple SMTP Server, as defined in:
const int DefaultSmtpPort
Default SMTP Port (25).
Event arguments for connection events.
Sniffer that stores events in memory.
Outputs sniffed data to an XML file.
string FileName
File Name.
string Transform
Transform to use.
Implements a text-based TCP Client, by using the thread-safe full-duplex BinaryTcpClient.
Component managing accounts.
Client Connection event argument.
Abstract base class for XMPP client connections
CaseInsensitiveString UserName
User name
CaseInsensitiveString BareJid
Bare JID
const string StreamNamespace
http://etherx.jabber.org/streams
CaseInsensitiveString FullJid
Full JID
Task< bool > StreamErrorUnauthorized()
Returns an Unauthorized XML stream error.
Base class for components.
CaseInsensitiveString Subdomain
Subdomain name.
string Name
Component name.
virtual void Dispose()
IDisposable.Dispose
async Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Message stanza.
virtual bool SupportsAccounts
If the component supports accounts (true), or if the subdomain name is the only valid address.
Event arguments for IQ queries.
XmppAddress From
From address attribute
Task IqErrorNotAcceptable(XmppAddress From, string ErrorText, string Language)
Returns a not-acceptable error.
Task IqResult(string Xml, string From)
Returns a response to the current request.
Task IqErrorItemNotFound(XmppAddress From, string ErrorText, string Language)
Returns a item-not-found error.
XmlElement Query
Query element, if found, null otherwise.
Task IqErrorNotAllowed(XmppAddress From, string ErrorText, string Language)
Returns a not-allowed error.
XmppAddress To
To address attribute
async Task IqError(string ErrorType, string Xml, XmppAddress From, string ErrorText, string Language)
Returns an error response to the current request.
Task IqErrorBadRequest(XmppAddress From, string ErrorText, string Language)
Returns a bad-request error.
ISender Sender
Sender of stanza.
Task IqErrorForbidden(XmppAddress From, string ErrorText, string Language)
Returns a forbidden error.
Associates a specific IQ request with an IQ response.
async Task< KeyValuePair< string, bool > > GetResponse()
Gets the response of the IQ request.
Maintains a set of IQ responses, for a limited time.
Event arguments for responses to IQ queries.
byte[] Content
Encoded content.
string ContentId
Content ID
CaseInsensitiveString BareJid
Bare JID
string ContentType
Content-Type
Web resource for requesting white-list authentication of a client.
Event arguments for Messages.
Event arguments for events accessing parent XMPP client connections.
XmppClient Client
Reference to XMPP client parent connection
Presence information event arguments.
string Type
Type attribute.
string Language
Language attribute.
object State
State object.
XmppAddress To
To attribute.
XmppAddress From
From attribute.
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 Xml
XML of stored element.
Push Notification settings.
string Namespace
Namespace of XML content element in message
string MessageType
Message tpye
CaseInsensitiveString BareJid
Bare JID of device
string LocalName
Local Name of XML content element in message
Push Notification settings.
int NrUpdates
Number of times object has been updated. (Created once only, without further updates = 1).
Contains information about an item of an entity.
override string ToString()
Object.ToString()
Service discovery result.
Manages the connection with an SMTP server.
Task< bool > RelayMessage(SmtpMessage Message, MailAddress Recipient)
Relays a mail message
Contains information about a stanza.
string Content
Literal XML content.
XmlElement StanzaElement
Stanza element.
Contains communication statistics.
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.
override string ToString()
object.ToString()
bool IsBareJID
If the address is a Bare JID.
bool HasAccount
If the address has an account part.
bool IsEmpty
If the address is empty.
CaseInsensitiveString Domain
Domain
CaseInsensitiveString Address
XMPP Address
bool IsDomain
If the Address is a domain.
XmppAddress ToBareJID()
Returns the Bare JID as an XmppAddress object.
CaseInsensitiveString BareJid
Bare JID
static readonly XmppAddress Empty
Empty address.
CaseInsensitiveString Account
Account
bool IsFullJID
If the Address is a Full JID.
Class managing a connection.
Event arguments for events related to XMPP S2S endpoints.
Manages an XMPP server-to-server connection.
Task< bool > Connect(bool DisposeCurrent)
Connects to the server.
bool IsStale
If connection is stale.
Manages an XMPP server-to-server connection tunneled over an XMPP client connection to a parent broke...
Connectivity information for a domain.
CaseInsensitiveString Host
Host name
S2sType Type
Type of S2S connection
bool TrustCertificate
If certificate should be trusted
CaseInsensitiveString Domain
Domain name
Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Message stanza.
const string DiscoveryItemsNamespace
http://jabber.org/protocol/disco#items (XEP-0030)
Task< IqResultEventArgs > IqRequest(string Type, string From, string To, string Language, string ContentXml, bool CheckShortTermCache)
Sends an IQ stanza to a recipient.
const string ExtendedAddressingNamespace
http://jabber.org/protocol/address (XEP-0033)
const string AbuseReportingNamespace
urn:xmpp:reporting:reason:abuse:0 (XEP-0377)
string GetRandomHexString(int NrBytes)
Generates a random hexadecimal string.
Task< DateTime?> GetEarliestLoginOpportunity(IClientConnection Connection)
Evaluates when a client is allowed to login.
async Task< bool > GetLastPresence(CaseInsensitiveString BareJid, EventHandlerAsync< PresenceEventArgs > Callback, object State)
Gets the last presence of a bare JID.
Task< bool > SendIqRequest(string Type, string From, string To, string Language, string ContentXml, bool CheckShortTermCache, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ stanza to a recipient.
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.
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...
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters a message handler.
const string RosterNamespace
jabber:iq:roster (RFC 6121)
const string VCardNamespace
vcard-temp (XEP-0054)
static Scheduler Scheduler
Scheduler
const string BlockingCommandNamespace
urn:xmpp:blocking (XEP-0191)
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Set handler.
Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Presence stanza.
IClientConnection[] GetClientConnections()
Get active client connections
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.
IXmppServerPersistenceLayer PersistenceLayer
Reference to persistence layer
const string DiscoveryNamespace
http://jabber.org/protocol/disco#info (XEP-0030)
void UpdateCertificate(X509Certificate ServerCertificate)
Updates the server certificate
const string MessagePushNamespace
http://waher.se/Schema/PushNotification.xsd
CommunicationLayer S2sSniffers
Sniffers for XMPP S2S communication.
EventHandlerAsync< ParentConnectionEventArgs > GetParentConnection
Event raised when the server needs access to a parent connection.
static bool IsRemoteDomainRegistered(CaseInsensitiveString RemoteDomain)
Checks if a remote domain is registered.
Task< IqResultEventArgs > IqRequest(string Type, string From, string To, string Language, string ContentXml)
Sends an IQ stanza to a recipient.
S2sEndpointStatistics[] GetServerConnectionStatistics()
Gets S2S connection statistics.
Task< bool > SendMessage(string Type, string Id, string From, string To, string Language, string ContentXml)
Sends a Message stanza to a recipient.
const int DefaultConnectionBacklog
Default Connection backlog (10).
static void GetErrorInformation(Exception ex, out string Type, out string Xml)
Converts an Exception to an XMPP error message.
S2sEndpointStatistics GetS2sStatistics(CaseInsensitiveString Endpoint, string Type)
Gets available S2S Endpoint statistics for an endpoint.
async Task< bool > SendIqRequest(string Type, XmppAddress From, XmppAddress To, string Language, string ContentXml, bool CheckShortTermCache, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ stanza to a recipient.
bool TryGetClientConnections(string BareJID, out IClientConnection[] Connections)
Tries to get available connections for a given client.
const string SaslNamespace
urn:ietf:params:xml:ns:xmpp-sasl
Task< Item[]> ServiceItemsDiscoveryAsync(string To)
Performs a service items discovery request
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.
static byte[] GetRandomNumbers(int NrBytes)
Generates a set of random numbers.
virtual Task< bool > MessageError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends a message error stanza.
EventHandlerAsync< XmppS2SEndpointEventArgs > S2sEndpointRemoved
Event raised when an S2S endpoint is removed.
virtual async Task< bool > IqError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
Sends an IQ error stanza.
bool TryGetClientConnection(string FullJID, out IClientConnection Connection)
Tries to get an active client connection.
async Task< bool > SendMessage(string Type, string Id, XmppAddress From, XmppAddress To, string Language, string ContentXml)
Sends a Message stanza to a recipient.
async Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Message stanza.
virtual async Task< bool > PresenceError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends a presence error stanza.
Task< ServiceDiscoveryResult > ServiceDiscoveryAsync(string To)
Performs a service discovery request
async Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
IQ stanza.
virtual Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Sends a message stanza.
async Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Presence stanza.
const string ReportingNamespace
urn:xmpp:reporting:0 (XEP-0377)
const string BlockingCommandErrorNamespace
urn:xmpp:blocking:errors (XEP-0191)
const string ContentNamespace
urn:xmpp:content
Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
IQ stanza.
virtual async Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Sends a presence stanza.
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...
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.
const string DataFormsNamespace
jabber:x:data (XEP-0004)
const int MaxNameLength
1000
const string OfflineMessagesNamespace
msgoffline (XEP-0160)
EventHandlerAsync< XmppS2SEndpointEventArgs > S2sEndpointCreated
Event raised when an S2S endpoint is created.
static async Task< PushNotificationToken > TryGetPushNotificationToken(CaseInsensitiveString BareJid)
Tries to get a push notification token for a client, if one exists.
static bool IsValidUserName(string UserName)
Checks if a user name contains invalid characters.
const string AlternativesNamespace
http://waher.se/Schema/AlternativeNames.xsd
bool TryGetS2sStatistics(CaseInsensitiveString Endpoint, out S2sEndpointStatistics Stat)
Tries to get available S2S Endpoint statistics for an endpoint.
async Task< IqResultEventArgs > IqRequest(string Type, XmppAddress From, XmppAddress To, string Language, string ContentXml, bool CheckShortTermCache)
Sends an IQ stanza to a recipient.
const string SoftwareVersionNamespace
jabber:iq:version (XEP-0092)
const int DefaultBufferSize
Default buffer size (16384).
const string PrivateXmlStorageNamespace
jabber:iq:private (XEP-0049)
CommunicationLayer C2sSniffers
Sniffers for XMPP C2S communication.
bool UnregisterComponent(IComponent Component)
Unregisters a component from the server.
const string PingNamespace
urn:xmpp:ping (XEP-0199)
bool IsServerDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the server domain, or optionally, an alternative domain.
X509Certificate ServerCertificate
Server domain certificate.
bool EncryptionRequired
If C2S encryption is requried.
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Get handler.
string GetDialbackKey(string ReceivingServer, string OriginatingServer, string ReceivingStreamId)
Gets a dialback key, calculated according to XEP-0185: https://xmpp.org/extensions/xep-0185....
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.
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
const int DefaultS2sPort
Default Server-to-Server Port (5269).
CaseInsensitiveString Domain
Domain name.
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).
const string TimeNamespace
urn:xmpp:time (XEP-0202)
const int DefaultC2sPort
Default Client-to-Server Port (5222).
static bool CheckExpressionSafe(Expression Expression, out ScriptNode Prohibited)
Checks if an expression is safe to execute (if it comes from an external source).
async Task< CaseInsensitiveString > FindComponentAsync(CaseInsensitiveString Jid, CaseInsensitiveString Feature)
Finds a component having a specific feature, servicing a JID.
const string RegisterNamespace
jabber:iq:register (XEP-0077)
const string StanzaNamespace
urn:ietf:params:xml:ns:xmpp-stanzas (RFC 6120)
bool TryGetS2sEndpoint(string RemoteDomain, out IS2SEndpoint Endpoint)
Tries to get a server-to-server connection state object.
void Dispose()
IDisposable.Dispose
static Task< XmppServer > Create(CaseInsensitiveString Domain, CaseInsensitiveString[] AlternativeDomains, X509Certificate ServerCertificate, bool EncryptionRequired, IXmppServerPersistenceLayer PersistenceLayer)
Creates an instance of an XMPP server.
async Task< IRecipient > TryGetRecipient(XmppAddress To, XmppAddress From)
Tries to get the recipient of a stanza.
EventHandlerAsync< ClientConnectionEventArgs > ClientConnectionRemoved
Event raised when a client connection has been removed.
virtual async Task< bool > IqResult(string Id, XmppAddress To, XmppAddress From, string ResultXml)
Sends an IQ result stanza.
Statistics.CommunicationStatistics GetCommunicationStatisticsSinceLast()
Gets communication statistics since last call.
const string DelayedDeliveryNamespace
urn:xmpp:delay (XEP-0203)
static async Task< S2SRec > GetDomain(CaseInsensitiveString DomainOrSubdomain)
Gets information about a domain.
const string MailNamespace
urn:xmpp:smtp
const int MaxGroupLength
1000
const string OAuth1FormSignatureNamespace
urn:xmpp:xdata:signature:oauth1 (XEP-0348)
EventHandlerAsync< ServerConnectionEventArgs > ServerConnectionRemoved
Event raised when a server connection has been removed.
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.
const string StreamsNamespace
urn:ietf:params:xml:ns:xmpp-streams
string NewId(int NrBytes)
Generates a new ID.
const string AvatarStorageNamespace
storage:client:avatar (XEP-0008)
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Set handler.
virtual async Task< string > IqError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends an IQ error stanza.
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers a message handler.
async Task< Item[]> ServiceItemsDiscoveryAsync(string To, string Node)
Performs a service items discovery request
async Task ProcessMessage(SmtpMessage Message)
Processes an incoming SMTP message.
virtual async Task< bool > PresenceError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
Sends a presence error stanza.
async Task< bool > Presence(string Type, string Id, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Presence stanza.
static CaseInsensitiveString GetBareJID(CaseInsensitiveString JID)
Gets the Bare JID from a JID, which may be a Full JID.
CaseInsensitiveString[] AlternativeDomains
Alternative domain names.
bool RegisterComponent(IComponent Component)
Registers a component with the server.
Task< IqResultEventArgs > IqRequest(string Type, XmppAddress From, XmppAddress To, string Language, string ContentXml)
Sends an IQ stanza to a recipient.
Accounts Accounts
Accounts
const string SpamReportingNamespace
urn:xmpp:reporting:reason:abuse:0 (XEP-0377)
async Task< bool > SendMailMessage(CaseInsensitiveString From, CaseInsensitiveString To, string Subject, string Markdown)
Sends a mail message
async Task< ServiceDiscoveryResult > ServiceDiscoveryAsync(string To, string Node)
Performs a service discovery request
S2sType
Domain connection type
async Task< int > DeleteOldMailContent(DateTime OlderThan)
Deletes old mail content.
Base class for all stanza exceptions.
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static readonly CaseInsensitiveString Empty
Empty case-insensitive string
int Length
Gets the number of characters in the current CaseInsensitiveString object.
string LowerCase
Lower-case representation of the case-insensitive string.
int IndexOf(CaseInsensitiveString value, StringComparison comparisonType)
Reports the zero-based index of the first occurrence of the specified string in the current System....
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
int CompareTo(CaseInsensitiveString other)
Compares this instance with a specified System.CaseInsensitiveString object and indicates whether thi...
CaseInsensitiveString Remove(int startIndex)
Returns a new string in which all the characters in the current instance, beginning at a specified po...
CaseInsensitiveString[] Split(params char[] separator)
Returns a string array that contains the substrings in this instance that are delimited by elements o...
override string ToString()
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...
static Task< IEnumerable< object > > FindDelete(string Collection, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
static async Task Update(object Object)
Updates an object in the database.
static async Task Delete(object Object)
Deletes an object in the database.
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
This filter selects objects that conform to all child-filters provided.
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.
ValueType[] GetValues()
Gets all available values in the cache.
bool ContainsKey(KeyType Key)
Checks if a key is available in the cache.
void Dispose()
IDisposable.Dispose
int Count
Number of items in cache
bool Remove(KeyType Key)
Removes an item from the cache.
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
void Add(KeyType Key, ValueType Value)
Adds an item to the cache.
void Clear()
Clears the cache.
Event arguments for cache item removal events.
KeyType Key
Key of item that was removed.
ValueType Value
Value of item that was removed.
RemovedReason Reason
Reason for removing the item.
Static class managing persistent counters.
static Task< long > IncrementCounter(CaseInsensitiveString Key)
Increments a counter.
Static class that dynamically manages types and interfaces available in the runtime environment.
static Type[] NoTypes
Contains an empty array of types.
static object[] NoParameters
Contains an empty array of parameter values.
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
static async Task< bool > SetAsync(string Key, string Value)
Sets a string-valued setting.
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Static class of application-wide semaphores that can be used to order access to editable objects.
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
void Dispose()
IDisposable.Dispose
DateTime Add(DateTime When, Action< object > Callback, object State)
Adds an event.
Generates a callback function based on script.
Creates a connection to an external MS SQL database.
Class managing a script expression.
bool ForAll(ScriptNodeEventHandler Callback, object State, bool DepthFirst)
Calls the callback method for all script nodes defined for the expression.
Defines a clickable fractal graph in the complex plane.
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...
Destroys a value. If the function references a variable, the variable is also removed.
Extract the properties of a type or an object.
Removes a variable from the variables collection, without destroying its value.
Base class for all nodes in a parsed script tree.
Makes a WHOIS query regarding an IP address.
Named member Assignment operator.
Dynamic function call operator
Named method call operator.
Gets the current count of a counter
Tries to get the associated object value from a persisted hash value
ShellExecute(FileName,Arguments,WorkFolder[,TimeoutMs[,LogStandardOutput[,KillOnTimeout]]])
Contains methods for simple hash calculations.
static string ComputeHMACSHA256HashString(byte[] Key, byte[] Data)
Computes the HMAC-SHA-256 hash of a block of binary data.
static byte[] ComputeHMACSHA1Hash(byte[] Key, byte[] Data)
Computes the HMAC-SHA-1 hash of a block of binary data.
static string BinaryToString(byte[] Data)
Converts an array of bytes to a string with their hexadecimal representations (in lower case).
static byte[] ComputeSHA256Hash(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
static string ComputeSHA1HashString(byte[] Data)
Computes the SHA-1 hash of a block of binary data.
Class that monitors login events, and help applications determine malicious intent....
async Task< DateTime?> GetEarliestLoginOpportunity(string RemoteEndPoint, string Protocol)
Checks when a remote endpoint can login.
static async void Success(string Message, string UserName, string RemoteEndPoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a successful login attempt.
static void Fail(string Message, string UserName, string RemoteEndPoint, string Protocol)
Handles a failed login attempt.
Interface for asynchronously disposable objects.
Task DisposeAsync()
Disposes of the object, asynchronously.
void Information(string Comment)
Called to inform the viewer of something.
void Exception(string Exception)
Called to inform the viewer of an exception state.
Interface for observable classes implementing communication protocols.
bool HasSniffers
If there are sniffers registered on the object.
ISniffer[] Sniffers
Registered sniffers.
void Add(ISniffer Sniffer)
Adds a sniffer to the node.
Interface for SMTP user accounts.
CaseInsensitiveString UserName
User Name
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 Name
Name of the mechanism.
string RemoteEndPoint
Remote endpoint.
string Protocol
String representing protocol being used.
void ResetState(bool Authenticated)
Resets the state machine.
CaseInsensitiveString UserName
User name
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
Interface for XMPP user accounts.
Interface for client connections.
Task< bool > BeginWrite(string Xml, EventHandlerAsync< DeliveryEventArgs > Callback, object State)
Writes XML to the client.
PresenceEventArgs LastPresence
Last presence received.
XmppAddress Address
Full Address
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.
CaseInsensitiveString FullJid
Full JID
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.
XmppAddress BareAddress
Bare Address
CaseInsensitiveString BareJid
Bare JID
Task< bool > StreamErrorInvalidXml()
Returns a Invalid XML stream error.
Interface for components.
string Type
Type of endpoint
Interface for recipients of stanzas.
Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
IQ stanza.
Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Presence stanza.
Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Message stanza.
Interface for roster items.
Interface for XMPP S2S endpoints
CaseInsensitiveString RemoteDomain
Connection to domain.
Interface for senders of stanzas.
Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Message stanza.
Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Presence stanza.
Task< string > IqError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends an IQ Error stanza.
Task< bool > IqResult(string Id, XmppAddress To, XmppAddress From, string ResultXml)
Sends an IQ Result stanza.
Interface for XMPP Server persistence layers. The persistence layer should implement caching.
Task< byte[]> GetDialbackSecret()
Gets the Dialback secret, as defined in XEP-0185.
Interface for Mutual TLS (mTLS) Clients or TLS servers.
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.
PushMessagingService
Push messaging service used.
ClientType
Type of client requesting notification.
XmppConnectionState
State of XMPP connection.
SubscriptionStatus
Roster item subscription status enumeration.
BlockingReason
Reason for blocking an account.
MessageType
Type of message received.
PendingSubscription
Pending subscription states.
RemovedReason
Reason for removing the item.
SearchMethod
Method to traverse the expression structure
ContentType
DTLS Record content type.
Reason
Reason a token is not valid.