2using System.Collections.Generic;
 
    4using System.Net.NetworkInformation;
 
    5using System.Net.Sockets;
 
    6using System.Security.Cryptography.X509Certificates;
 
    8using System.Text.RegularExpressions;
 
    9using System.Threading.Tasks;
 
   70        private static string salutation = 
null;
 
   71        private static DateTime salutationExpires = DateTime.MinValue;
 
   73        private LinkedList<TcpListener> listeners = 
new LinkedList<TcpListener>();
 
   76        private X509Certificate serverCertificate;
 
   79        private readonly 
bool encryptionRequired;
 
   80        private readonly 
int maxMessageSize;
 
   81        private readonly 
string[] ip4DnsBlackLists;
 
   82        private readonly 
string[] ip6DnsBlackLists;
 
   84        private string smtpSnifferPath = 
null;
 
   85        private bool disposed = 
false;
 
   88        private string[] relayDomains = 
null;
 
   89        private Regex[] relayDomainsEx = 
null;
 
   90        private string relayHost = 
string.Empty;
 
   91        private string relayUserName = 
string.Empty;
 
   92        private string relayPassword = 
string.Empty;
 
   93        private int relayPort = 587;
 
   94        private bool useRelayServer = 
false;
 
   95        private bool relayLocked = 
false;
 
  112            string[] Ip4DnsBlackLists, 
string[] Ip6DnsBlackLists, 
SpfExpression[] SpfExpressions)
 
  114                EncryptionRequired, PersistenceLayer, Ip4DnsBlackLists, Ip6DnsBlackLists, SpfExpressions)
 
  132            string[] Ip4DnsBlackLists, 
string[] Ip6DnsBlackLists, 
SpfExpression[] SpfExpressions)
 
  134                Ip4DnsBlackLists, Ip6DnsBlackLists, SpfExpressions)
 
  154            this.persistenceLayer = PersistenceLayer;
 
  158            this.maxMessageSize = MaxMessageSize;
 
  159            this.ip4DnsBlackLists = Ip4DnsBlackLists;
 
  160            this.ip6DnsBlackLists = Ip6DnsBlackLists;
 
  161            this.spfExpressions = SpfExpressions;
 
  164                throw new ArgumentException(
"Server Certificate must be provided, if encryption is required.", nameof(
ServerCertificate));
 
  167            this.clientConnections.Removed += this.ClientConnections_Removed;
 
  169            this.Initialize(Ports);
 
  172        private async 
void Initialize(
int[] Ports)
 
  176                TcpListener Listener;
 
  178                foreach (NetworkInterface Interface 
in NetworkInterface.GetAllNetworkInterfaces())
 
  180                    if (Interface.OperationalStatus != OperationalStatus.Up)
 
  183                    IPInterfaceProperties Properties = Interface.GetIPProperties();
 
  185                    foreach (UnicastIPAddressInformation UnicastAddress 
in Properties.UnicastAddresses)
 
  187                        if ((UnicastAddress.Address.AddressFamily == AddressFamily.InterNetwork && Socket.OSSupportsIPv4) ||
 
  188                            (UnicastAddress.Address.AddressFamily == AddressFamily.InterNetworkV6 && Socket.OSSupportsIPv6))
 
  190                            if (!(Ports is 
null))
 
  192                                foreach (
int Port 
in Ports)
 
  196                                        await this.externalSniffers.Information(
"Opening port " + Port.ToString() + 
" on " + UnicastAddress.Address.ToString() + 
".");
 
  198                                        Listener = 
new TcpListener(UnicastAddress.Address, Port);
 
  200                                        Listener.BeginAcceptTcpClient(this.AcceptTcpClientCallback, Listener);
 
  201                                        this.listeners.AddLast(Listener);
 
  203                                        await this.externalSniffers.Information(
"Port " + Port.ToString() + 
" on " + UnicastAddress.Address.ToString() + 
" opened.");
 
  207                                        Log.
Exception(ex, UnicastAddress.Address.ToString() + 
":" + Port);
 
  232            get => this.smtpSnifferPath;
 
  233            set => this.smtpSnifferPath = value;
 
  241                await e.
Value.DisposeAsync();
 
  254            get => this.clientConnections.
Count;
 
  259            List<SmtpClientConnection> Connections = 
new List<SmtpClientConnection>();
 
  261            foreach (Guid Id 
in this.clientConnections.
GetKeys())
 
  264                    Connections.Add(Connection);
 
  267            Connections.Sort((c1, c2) =>
 
  269                return c1.UserName.CompareTo(c2.UserName);
 
  272            return Connections.ToArray();
 
  275        public bool TryGetClientConnection(Guid ID, out SmtpClientConnection Connection)
 
  277            return this.clientConnections.
TryGetValue(ID, out Connection);
 
  293            get => this.serverCertificate;
 
  310            get => this.encryptionRequired;
 
  315            get => this.persistenceLayer;
 
  318        internal string[] Ip4DnsBlackLists => this.ip4DnsBlackLists;
 
  319        internal string[] Ip6DnsBlackLists => this.ip6DnsBlackLists;
 
  320        internal SpfExpression[] SpfExpressions => this.spfExpressions;
 
  327            this.disposed = 
true;
 
  329            if (!(this.clientConnections is 
null))
 
  331                this.clientConnections.
Clear();
 
  332                this.clientConnections.
Dispose();
 
  333                this.clientConnections = 
null;
 
  336            if (!(this.listeners is 
null))
 
  338                LinkedList<TcpListener> Listeners = this.listeners;
 
  339                this.listeners = 
null;
 
  341                foreach (TcpListener Listener 
in Listeners)
 
  345            if (!(this.sniffers is 
null))
 
  347                this.sniffers.
Clear();
 
  349                this.sniffers = 
null;
 
  352            if (!(this.externalSniffers is 
null))
 
  354                foreach (
ISniffer Sniffer 
in this.externalSniffers)
 
  355                    (Sniffer as IDisposable)?.
Dispose();
 
  366                return this.GetOpenPorts(this.listeners);
 
  370        private int[] GetOpenPorts(LinkedList<TcpListener> Listeners)
 
  372            SortedDictionary<int, bool> Open = 
new SortedDictionary<int, bool>();
 
  374            if (!(Listeners is 
null))
 
  376                IPEndPoint IPEndPoint;
 
  378                foreach (TcpListener Listener 
in Listeners)
 
  380                    IPEndPoint = Listener.LocalEndpoint as IPEndPoint;
 
  381                    if (!(IPEndPoint is 
null))
 
  382                        Open[IPEndPoint.Port] = 
true;
 
  386            int[] Result = 
new int[Open.Count];
 
  387            Open.Keys.CopyTo(Result, 0);
 
  396        private async 
void AcceptTcpClientCallback(IAsyncResult ar)
 
  403                TcpListener Listener = (TcpListener)ar.AsyncState;
 
  407                    TcpClient Client = Listener.EndAcceptTcpClient(ar);
 
  408                    SmtpClientConnection ClientConnection;
 
  411                    if (!
string.IsNullOrEmpty(this.smtpSnifferPath))
 
  413                    else if (this.externalSniffers.HasSniffers)
 
  414                        Sniffers = this.externalSniffers.Sniffers;
 
  421                    ClientConnection = 
new SmtpClientConnection(
BinaryTcpClient, 
this, this.persistenceLayer, this.maxMessageSize, Sniffers);
 
  422                    await ClientConnection.Information(
"Connection accepted from " + Client.Client.RemoteEndPoint.ToString() + 
".");
 
  424                    this.clientConnections[ClientConnection.ID] = ClientConnection;
 
  429                    await ClientConnection.BeginWrite(
"220 " + this.domain + 
" ESMTP Sendmail ...\r\n", 
null, 
null);
 
  434                        Listener.BeginAcceptTcpClient(this.AcceptTcpClientCallback, Listener);
 
  437            catch (SocketException)
 
  441            catch (ObjectDisposedException)
 
  445            catch (NullReferenceException)
 
  451                if (this.listeners is 
null)
 
  458        internal string GetTransformPath()
 
  460            foreach (
ISniffer Sniffer 
in this.externalSniffers.Sniffers)
 
  469        internal void Closed(SmtpClientConnection Connection)
 
  471            this.clientConnections?.
Remove(Connection.ID);
 
  485            return this.persistenceLayer.GetAccount(UserName);
 
  491        public event EventHandlerAsync<ClientConnectionEventArgs> ClientConnectionAdded = 
null;
 
  496        public event EventHandlerAsync<ClientConnectionEventArgs> ClientConnectionRemoved = 
null;
 
  505        public event EventHandlerAsync<SmtpMessageEventArgs> MessageReceived = 
null;
 
  520            FileName = this.smtpSnifferPath.Replace(
"%ENDPOINT%", Key);
 
  528        internal void CacheSniffers(IEnumerable<ISniffer> Sniffers)
 
  530            foreach (
ISniffer Sniffer 
in Sniffers)
 
  534                else if (Sniffer is IDisposable Disposable)
 
  535                    Disposable.Dispose();
 
  541            if (this.sniffers is 
null)
 
  544                this.sniffers.Removed += this.Sniffers_Removed;
 
  552            if (this.disposed || (DateTime.Now - e.
Value.LastEvent).TotalMinutes > 30)
 
  555            return Task.CompletedTask;
 
  573            string UserName, 
string Password, 
string[] RelayDomains, 
bool LockSettings)
 
  575            if (this.useRelayServer != UseRelayServer ||
 
  576                this.relayHost != HostName ||
 
  577                this.relayPort != PortNumber ||
 
  578                this.relayUserName != UserName ||
 
  579                this.relayPassword != Password ||
 
  580                !AreSame(this.relayDomains, RelayDomains))
 
  582                if (this.relayLocked)
 
  583                    throw new InvalidOperationException(
"Relay settings locked.");
 
  585                this.useRelayServer = UseRelayServer;
 
  586                this.relayHost = HostName;
 
  587                this.relayPort = PortNumber;
 
  588                this.relayUserName = UserName;
 
  589                this.relayPassword = Password;
 
  590                this.relayDomains = RelayDomains;
 
  591                this.relayDomainsEx = 
null;
 
  595                this.relayLocked = 
true;
 
  598        private static bool AreSame(
string[] A1, 
string[] A2)
 
  600            if ((A1 is 
null) ^ (A2 is 
null))
 
  612            for (i = 0; i < c; i++)
 
  628            if (this.relayDomains is 
null)
 
  631            int i, c = this.relayDomains.Length;
 
  633            for (i = 0; i < c; i++)
 
  635                string s = this.relayDomains[i];
 
  637                if (
string.Compare(s, Domain, 
true) == 0)
 
  640                if (s.IndexOf(
'*') < 0)
 
  643                if (this.relayDomainsEx is 
null)
 
  644                    this.relayDomainsEx = 
new Regex[this.relayDomains.Length];
 
  646                if (this.relayDomainsEx[i] is 
null)
 
  647                    this.relayDomainsEx[i] = 
new Regex(
Database.
WildcardToRegex(s, 
"*"), RegexOptions.Singleline | RegexOptions.IgnoreCase);
 
  649                Match M = this.relayDomainsEx[i].Match(Domain);
 
  650                if (M.Success && M.Index == 0 && M.Length == Domain.Length)
 
  665            string Subject, 
object[] AlternativeBodies)
 
  667            int i, c = AlternativeBodies.Length;
 
  670            for (i = 0; i < c; i++)
 
  673                byte[] EncodedBody = P.Key;
 
  674                string BodyContentType = P.Value;
 
  677                    ContentType = BodyContentType,
 
  678                    TransferDecoded = EncodedBody
 
  682            return await this.SendMessage(From, To, Subject, Alternatives, 
null);
 
  695            return this.SendMessage(From, To, Subject, 
string.Empty, Alternatives, Attachments);
 
  712            byte[] BodyBin = P.Key;
 
  713            string ContentType = P.Value;
 
  715            if (!(Attachments is 
null) && Attachments.Length > 0)
 
  720                    ContentType = ContentType,
 
  723                Array.Copy(Attachments, 0, Mixed, 1, Attachments.Length);
 
  727                ContentType = P.Value;
 
  730            DateTime Now = DateTime.Now;
 
  731            List<KeyValuePair<string, string>> Headers = 
new List<KeyValuePair<string, string>>()
 
  733                new KeyValuePair<string, string>(
"MIME-VERSION", 
"1.0"),
 
  734                new KeyValuePair<string, string>(
"FROM", From),
 
  735                new KeyValuePair<string, string>(
"TO", To),
 
  736                new KeyValuePair<string, string>(
"SUBJECT", Subject),
 
  738                new KeyValuePair<string, string>(
"IMPORTANCE", 
"normal"),
 
  739                new KeyValuePair<string, string>(
"X-PRIORITY", 
"3"),
 
  740                new KeyValuePair<string, string>(
"MESSAGE-ID", 
string.IsNullOrEmpty(MessageId) ? Guid.NewGuid().ToString() : MessageId),
 
  741                new KeyValuePair<string, string>(
"CONTENT-TYPE", ContentType)
 
  744            return await this.SendMessage(From, To, Headers.ToArray(), BodyBin, Now);
 
  759            byte[] Data, DateTime Start)
 
  763                throw new ArgumentException(
"Invalid mail address: " + To, nameof(To));
 
  771            if (this.useRelayServer)
 
  773                Host = this.relayHost;
 
  774                Port = this.relayPort;
 
  775                UserName = this.relayUserName;
 
  776                Password = this.relayPassword;
 
  781                if (Exchanges is 
null || Exchanges.Length == 0)
 
  782                    throw new ArgumentException(
"No mail exchange at " + Domain + 
".", nameof(To));
 
  785                Port = DefaultSmtpPort;
 
  790            return await this.SendMessage(Domain, Host, Port, UserName, Password, From, To, Headers, Data, Start);
 
  804        public async Task<bool> 
SendMessage(
string Domain, 
string Host, 
int Port, 
string UserName, 
string Password,
 
  807            Exception Exception = 
null;
 
  813                    this.GetSniffer(Host + 
" OUT")))
 
  816                    await Client.
EHLO(await GetSalutation(this.domain));    
 
  819                    await Client.
DATA(Headers, Data);
 
  825            catch (TimeoutException ex)
 
  845                Log.
Error(
"Unable to send message.\r\n\r\n" + Exception?.Message,
 
  846                    new KeyValuePair<string, object>(
"From", From),
 
  847                    new KeyValuePair<string, object>(
"To", To));
 
  854            DateTime TP = DateTime.Now;
 
  855            double Minutes = (TP - Start).TotalMinutes;
 
  857            if (Minutes >= 24 * 60)
 
  861                TP = TP.AddMinutes(1);
 
  862            else if (Minutes < 60)
 
  863                TP = TP.AddMinutes(5);
 
  865                TP = TP.AddMinutes(15);
 
  870            Scheduler.
Add(TP, this.Resend, 
new object[] { Domain, Host, Port, UserName, Password, From, To, Headers, Data, Start });
 
  875        private async 
void Resend(
object State)
 
  879                object[] P = (
object[])State;
 
  880                string Domain = (string)P[0];
 
  881                string Host = (string)P[1];
 
  882                int Port = (int)P[2];
 
  883                string UserName = (string)P[3];
 
  884                string Password = (string)P[4];
 
  887                KeyValuePair<string, string>[] Headers = (KeyValuePair<string, string>[])P[7];
 
  888                byte[] Data = (
byte[])P[8];
 
  889                DateTime Start = (DateTime)P[9];
 
  891                await this.SendMessage(Domain, Host, Port, UserName, Password, From, To, Headers, Data, Start);
 
  906            if (salutationExpires < DateTime.Now)
 
  909            if (salutation is 
null)
 
  913                    foreach (NetworkInterface Interface 
in NetworkInterface.GetAllNetworkInterfaces())
 
  915                        if (Interface.OperationalStatus != OperationalStatus.Up)
 
  918                        IPInterfaceProperties Properties = Interface.GetIPProperties();
 
  920                        foreach (UnicastIPAddressInformation UnicastAddress 
in Properties.UnicastAddresses)
 
  922                            if (UnicastAddress.Address.AddressFamily == AddressFamily.InterNetwork && Socket.OSSupportsIPv4)
 
  924                                if (IsPublicAddress(UnicastAddress.Address))
 
  928                                        string AddrStr = UnicastAddress.Address.ToString();
 
  931                                        foreach (
string Name 
in Names)
 
  937                                                foreach (IPAddress Addr 
in Addresses)
 
  939                                                    if (Addr.ToString() == AddrStr)
 
  946                                                if (!(salutation is 
null))
 
  955                                        if (!(salutation is 
null))
 
  966                        if (!(salutation is 
null))
 
  975                if (salutation is 
null)
 
  976                    salutation = DefaultDomain;
 
  978                salutationExpires = DateTime.Now.AddHours(1);
 
  991            if (Address.AddressFamily == AddressFamily.InterNetwork && Socket.OSSupportsIPv4)
 
  993                byte[] Addr = Address.GetAddressBytes();
 
  998                else if (Addr[0] == 10)
 
 1001                else if (Addr[0] == 172 && Addr[1] >= 16 && Addr[1] <= 31)
 
 1004                else if (Addr[0] == 192 && Addr[1] == 168)
 
 1007                else if (Addr[0] == 169 && Addr[1] == 254)
 
Helps with parsing of commong data types.
 
static string EncodeRfc822(DateTime Timestamp)
Encodes a date and time, according to RFC 822 §5.
 
Static class managing encoding and decoding of internet content.
 
static Task< KeyValuePair< byte[], string > > EncodeAsync(object Object, Encoding Encoding, params string[] AcceptedContentTypes)
Encodes an object.
 
Represents alternative versions of the same content, encoded with multipart/alternative
 
Represents content embedded in other content.
 
Represents mixed content, encoded with multipart/mixed
 
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 Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
 
Implements a binary TCP Client, by encapsulating a TcpClient. It also makes the use of TcpClient safe...
 
void Bind()
Binds to a TcpClient that was already connected when provided to the constructor.
 
void Continue()
Continues reading from the socket, if paused in an event handler.
 
Simple base class for classes implementing communication protocols.
 
DNS resolver, as defined in:
 
static async Task< string[]> LookupDomainName(IPAddress Address)
Looks up the domain name pointing to a specific IP address.
 
static async Task< string[]> LookupMailExchange(string DomainName)
Looks up the Mail Exchanges related to a given domain name.
 
static async Task< IPAddress[]> LookupIP4Addresses(string DomainName)
Looks up the IPv4 addresses related to a given domain name.
 
Module that controls the life cycle of communication.
 
static bool Stopping
If the system is stopping.
 
Base class for temporary SMTP-related exceptions.
 
Client Connection event argument.
 
Class managing a connection.
 
Event arguments for SMTP Message events.
 
Represents one message received over SMTP
 
Implements a simple SMTP Server, as defined in:
 
const int DefaultSmtpRelayPort
Secondary SMTP Port (587).
 
EventHandlerAsync< ClientConnectionEventArgs > ClientConnectionRemoved
Event raised when a client connection has been removed.
 
static bool IsPublicAddress(IPAddress Address)
Checks if an IPv4 address is public.
 
SmtpServer(CaseInsensitiveString Domain, int MaxMessageSize, X509Certificate ServerCertificate, bool EncryptionRequired, ISaslPersistenceLayer PersistenceLayer, string[] Ip4DnsBlackLists, string[] Ip6DnsBlackLists, SpfExpression[] SpfExpressions)
Creates an instance of an SMTP server.
 
static async Task< string > GetSalutation(string DefaultDomain)
Gets the proper salutation name for the server.
 
async Task< bool > SendMessage(CaseInsensitiveString From, CaseInsensitiveString To, string Subject, object[] AlternativeBodies)
Sends a mail message
 
async Task< bool > SendMessage(string Domain, string Host, int Port, string UserName, string Password, CaseInsensitiveString From, CaseInsensitiveString To, KeyValuePair< string, string >[] Headers, byte[] Data, DateTime Start)
Sends a mail message
 
CommunicationLayer ExternalSniffers
External Sniffers for SMTP communication.
 
const int DefaultConnectionBacklog
Default Connection backlog (10).
 
int NrClientConnections
Number of client connections.
 
async Task< bool > SendMessage(CaseInsensitiveString From, CaseInsensitiveString To, string Subject, string MessageId, EmbeddedContent[] Alternatives, EmbeddedContent[] Attachments)
Sends a mail message
 
SmtpServer(CaseInsensitiveString Domain, int[] Ports, int MaxMessageSize, X509Certificate ServerCertificate, bool EncryptionRequired, ISaslPersistenceLayer PersistenceLayer, string[] Ip4DnsBlackLists, string[] Ip6DnsBlackLists, SpfExpression[] SpfExpressions)
Implements an SMTP server.
 
EventHandlerAsync< ClientConnectionEventArgs > ClientConnectionAdded
Event raised when a client connection has been added.
 
CaseInsensitiveString Domain
Domain name.
 
int[] OpenPorts
Ports successfully opened.
 
bool EncryptionRequired
If C2S encryption is requried.
 
void UpdateCertificate(X509Certificate ServerCertificate)
Updates the server certificate
 
Task< bool > SendMessage(CaseInsensitiveString From, CaseInsensitiveString To, string Subject, EmbeddedContent[] Alternatives, EmbeddedContent[] Attachments)
Sends a mail message
 
void SetRelaySettings(bool UseRelayServer, string HostName, int PortNumber, string UserName, string Password, string[] RelayDomains, bool LockSettings)
Sets mail relay settings.
 
void Dispose()
IDisposable.Dispose
 
async Task< bool > SendMessage(CaseInsensitiveString From, CaseInsensitiveString To, KeyValuePair< string, string >[] Headers, byte[] Data, DateTime Start)
Sends a mail message
 
string SmtpSnifferPath
If separate sniffers are to be created for each connected client, set this property to the file path ...
 
SmtpServer(CaseInsensitiveString Domain, int Port, int MaxMessageSize, X509Certificate ServerCertificate, bool EncryptionRequired, ISaslPersistenceLayer PersistenceLayer, string[] Ip4DnsBlackLists, string[] Ip6DnsBlackLists, SpfExpression[] SpfExpressions)
Creates an instance of an SMTP server.
 
const string SmtpRelayPrivilegeID
SmtpRelay
 
bool CanRelayForDomain(string Domain)
If the server is permitted to relay messages from a particular domain.
 
X509Certificate ServerCertificate
Server domain certificate.
 
const int DefaultSmtpPort
Default SMTP Port (25).
 
const int DefaultBufferSize
Default buffer size (16384).
 
async Task Connect()
Connects to the server.
 
async Task< string > EHLO(string Domain)
Sends the EHLO command.
 
async Task QUIT()
Executes the QUIT command.
 
async Task MAIL_FROM(string Sender)
Executes the MAIL FROM command.
 
async Task DATA(KeyValuePair< string, string >[] Headers, byte[] Body)
Executes the DATA command.
 
async Task RCPT_TO(string Receiver)
Executes the RCPT TO command.
 
Sniffer that stores events in memory.
 
Outputs sniffed data to an XML file.
 
string FileName
File Name.
 
string Transform
Transform to use.
 
Represents a 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....
 
CaseInsensitiveString Trim()
Removes all leading and trailing white-space characters from the current CaseInsensitiveString object...
 
CaseInsensitiveString Substring(int startIndex, int length)
Retrieves a substring from this instance. The substring starts at a specified character position and ...
 
Static interface for database persistence. In order to work, a database provider has to be assigned t...
 
static string WildcardToRegex(string s, string Wildcard)
Converts a wildcard string to a regular expression string.
 
Implements an in-memory 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.
 
KeyType[] GetKeys()
Gets all available keys in 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.
 
ValueType Value
Value of item that was removed.
 
Static class that dynamically manages types and interfaces available in the runtime environment.
 
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
 
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
 
DateTime Add(DateTime When, ScheduledEventCallback Callback, object State)
Adds an event.
 
Contains information about a SPF string.
 
Interface for XMPP Server persistence layers. The persistence layer should implement caching.
 
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
 
BinaryPresentationMethod
How binary data is to be presented.