2using System.Collections.Generic;
5using System.Threading.Tasks;
34 outgoingMails.Removed += OutgoingMails_Removed;
36 Log.Terminating += (Sender, e) =>
66 public override string Type =>
"SMTP";
71 if (
string.Compare(
Type,
"chat",
true) == 0)
73 string Key = From.Address +
" | " + To.
Address;
74 outgoingMails.
TryGetValue(Key, out OutgoingMail Mail);
82 string Markdown =
null;
83 string Subject =
null;
84 string ReplaceId =
null;
85 bool Immediate =
false;
89 if (N is XmlElement E)
94 Subject = E.InnerText;
102 if (E.NamespaceURI ==
"urn:xmpp:message-correct:0")
107 foreach (XmlNode N2
in E.ChildNodes)
109 if (N2 is XmlElement E2 && E2.LocalName ==
"body")
118 switch (E.NamespaceURI)
132 Markdown = E.InnerText;
141 byte[] Bin = Convert.FromBase64String(E.InnerText);
152 TransferDecoded = Bin
166 if (!(Markdown is
null) && (Text is
null || Html is
null))
178 if (Subject is
null && Text is
null && Html is
null)
183 Mail =
new OutgoingMail()
190 outgoingMails[Key] = Mail;
193 if (!
string.IsNullOrEmpty(ReplaceId) &&
194 !(Mail.ById is
null) &&
195 Mail.ById.TryGetValue(ReplaceId, out OutgoingMailRec Rec))
203 if (
string.IsNullOrEmpty(Mail.Subject))
204 Mail.Subject = Subject;
205 else if (Mail.Subject != Subject && !
string.IsNullOrEmpty(Subject))
208 Mail.Subject = Subject;
209 Mail.Records.Clear();
213 if (
string.IsNullOrEmpty(Id) && !
string.IsNullOrEmpty(ReplaceId))
216 Rec =
new OutgoingMailRec()
224 Mail.Records.AddLast(Rec);
226 if (!
string.IsNullOrEmpty(Id))
228 if (Mail.ById is
null)
229 Mail.ById =
new Dictionary<string, OutgoingMailRec>();
236 outgoingMails.
Remove(Key);
259 private static async Task<bool> Send(OutgoingMail Mail)
261 List<EmbeddedContent> Attachments =
null;
262 List<EmbeddedContent> Inline =
null;
263 StringBuilder PlainText =
null;
264 StringBuilder Html =
null;
267 foreach (OutgoingMailRec Rec
in Mail.Records)
269 if (Id is
null && !
string.IsNullOrEmpty(Rec.Id))
272 if (!
string.IsNullOrEmpty(Rec.Text))
274 if (PlainText is
null)
275 PlainText =
new StringBuilder();
277 PlainText.AppendLine(Rec.Text.Trim());
280 if (!
string.IsNullOrEmpty(Rec.Html))
283 Html =
new StringBuilder();
285 Html.AppendLine(Rec.Html.Trim());
288 if (!(Rec.Embedded is
null))
296 Inline =
new List<EmbeddedContent>();
298 Inline.Add(Embedded);
303 if (Attachments is
null)
304 Attachments =
new List<EmbeddedContent>();
306 Attachments.Add(Embedded);
313 List<EmbeddedContent> Alternatives =
new List<EmbeddedContent>();
314 string Subject = Mail.Subject;
317 if (!(PlainText is
null))
319 s = PlainText.ToString();
324 Raw = Encoding.UTF8.GetBytes(s)
327 if (
string.IsNullOrEmpty(Subject))
328 Subject = FirstRow(s);
338 Raw = Encoding.UTF8.GetBytes(s)
341 if (!(Inline is
null) && Inline.Count > 0)
349 byte[] Encoded = P.Key;
359 Alternatives.Add(HtmlContent);
361 if (
string.IsNullOrEmpty(Subject))
364 LinkedList<HtmlNode> ToProcess =
new LinkedList<HtmlNode>();
365 string FirstHeader =
null;
366 int FirstHeaderLevel =
int.MaxValue;
367 string FirstParagraph =
null;
370 ToProcess.AddLast(N);
372 while (!(ToProcess.First is
null))
375 ToProcess.RemoveFirst();
379 if (
string.Compare(E.Name,
"P",
true) == 0)
381 if (FirstParagraph is
null)
382 FirstParagraph = GetText(E);
384 else if (E.Name.ToUpper().StartsWith(
"H") &&
int.TryParse(E.Name.Substring(1), out
int Level))
386 if (Level < FirstHeaderLevel)
388 FirstHeader = GetText(E);
389 FirstHeaderLevel = Level;
396 ToProcess.AddLast(N2);
401 Subject = FirstRow(FirstHeader);
402 if (
string.IsNullOrEmpty(Subject))
403 Subject = FirstRow(FirstParagraph);
407 if (
string.IsNullOrEmpty(Subject))
410 return await Mail.Endpoint.smtpServer.SendMessage(Mail.From, Mail.To, Subject, Id, Alternatives.ToArray(), Attachments?.ToArray());
413 private static string FirstRow(
string s)
420 return s.Substring(0, i).TrimEnd();
425 StringBuilder Output =
new StringBuilder();
427 return Output.ToString();
430 private static void GetText(
HtmlElement E, StringBuilder Output)
432 if (
string.Compare(E.
Name,
"BR",
true) == 0)
442 Output.Append(Text.InlineText);
455 await this.xmppServer.Presence(
"subscribed", Id, From, To, Language,
string.Empty,
this);
457 StringBuilder Markdown =
new StringBuilder();
459 Markdown.AppendLine(
"e-Mail Invitation");
460 Markdown.AppendLine(
"======================");
461 Markdown.AppendLine();
463 Markdown.Append(
"**");
465 Markdown.Append(
"** has added you to its white-list. This means you can send e-mail to **");
467 Markdown.Append(
"** from **");
469 Markdown.AppendLine(
"**.");
471 if (!await this.xmppServer.SendMailMessage(From.
BareJid, To.
BareJid,
"Added to white-list", Markdown.
ToString()))
478 await this.xmppServer.Presence(
"unsubscribed", Id, From, To, Language,
string.Empty,
this);
480 Markdown =
new StringBuilder();
482 Markdown.AppendLine(
"White-list removal");
483 Markdown.AppendLine(
"======================");
484 Markdown.AppendLine();
486 Markdown.Append(
"**");
488 Markdown.Append(
"** has removed you from its white-list. This means you can no longer send e-mail to **");
490 Markdown.Append(
"** from **");
492 Markdown.AppendLine(
"**.");
494 if (!await this.xmppServer.SendMailMessage(From.
BareJid, To.
BareJid,
"Removed from white-list", Markdown.
ToString()))
501 Markdown =
new StringBuilder();
503 Markdown.AppendLine(
"Request approved");
504 Markdown.AppendLine(
"===================");
505 Markdown.AppendLine();
507 Markdown.Append(
"Your request to be permitted to send e-mail to **");
509 Markdown.Append(
"** from **");
511 Markdown.AppendLine(
"** has been approved.");
513 if (!await this.xmppServer.SendMailMessage(From.
BareJid, To.
BareJid,
"White-listing request approved", Markdown.
ToString()))
520 Markdown =
new StringBuilder();
522 Markdown.AppendLine(
"Request rejected");
523 Markdown.AppendLine(
"===================");
524 Markdown.AppendLine();
526 Markdown.Append(
"Your request to be permitted to send e-mail to **");
528 Markdown.Append(
"** from **");
530 Markdown.AppendLine(
"** has been rejected.");
532 if (!await this.xmppServer.SendMailMessage(From.
BareJid, To.
BareJid,
"White-listing request rejected", Markdown.
ToString()))
547 StringBuilder sb =
new StringBuilder();
549 sb.Append(
"IQ stanzas not supported for mail recipients. From: ");
554 return !(await Sender.IqErrorServiceUnavailable(Id, From, To, sb.
ToString(),
"en") is
null);
563 return this.
IQ(Type, Id, To, From, Language,
Stanza?.Content, Sender);
569 return this.
Message(Type, Id, To, From, Language,
Stanza?.Content, Sender);
575 return this.
Presence(Type, Id, To, From, Language,
Stanza?.Content, Sender);
581 return Task.FromResult(
true);
587 return Task.FromResult(
string.Empty);
593 return Task.FromResult(
true);
599 return this.
Message(Type, Id, To, From, Language, ContentXml,
null);
605 return Task.FromResult(
true);
611 return this.
Presence(Type, Id, To, From, Language, ContentXml,
null);
617 return Task.FromResult(
true);
623 return Task.FromResult(
true);
634 StringBuilder Received =
new StringBuilder();
636 Received.Append(
"from ");
637 Received.Append(
Message.ClientName);
638 Received.Append(
" [");
639 Received.Append(
Message.RemoteEndpoint);
640 Received.Append(
"] by ");
641 Received.Append(this.smtpServer.Domain);
642 Received.Append(
" with ");
643 Received.Append(typeof(
SmtpServer).Namespace);
644 Received.Append(
" ");
645 Received.Append(typeof(
SmtpServer).Assembly.ImageRuntimeVersion);
646 Received.Append(
"; ");
649 KeyValuePair<string, string>[] Headers =
new KeyValuePair<string, string>[Message.AllHeaders.Length + 1];
651 Message.AllHeaders.CopyTo(Headers, 1);
652 Headers[0] =
new KeyValuePair<string, string>(
"Received", Received.ToString());
654 return this.smtpServer.SendMessage(
Message.FromMail.Address, Recipient.Address, Headers,
Message.UntransformedBody, DateTime.Now);
Helps with parsing of commong data types.
static readonly char[] CRLF
Contains the CR LF character sequence.
static string EncodeRfc822(DateTime Timestamp)
Encodes a date and time, according to RFC 822 §5.
string Content
CDATA Content
const string DefaultContentType
Default Content-Type for HTML: text/html
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.
bool HasChildren
If the element has children.
IEnumerable< HtmlNode > Children
Child nodes, or null if none.
Base class for all HTML nodes.
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.
const string ContentType
Markdown content type.
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 ...
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 content embedded in other content.
ContentDisposition Disposition
Disposition of embedded object.
string ContentType
Content-Type of embedded object.
Represents related content, encoded with multipart/related
Plain text encoder/decoder.
const string DefaultContentType
text/plain
Helps with common XML-related tasks.
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
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 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.
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
ISniffer[] Sniffers
Registered sniffers.
Represents one message received over SMTP
Implements a simple SMTP Server, as defined in:
Abstract base class for server connections.
CaseInsensitiveString LocalDomain
Local domain name.
CaseInsensitiveString RemoteDomain
Connection to domain.
Manages the connection with an SMTP server.
override async Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Sends an IQ stanza. If stanza was sent.
SmtpS2SEndpoint(CaseInsensitiveString LocalDomain, CaseInsensitiveString RemoteDomain, SmtpServer SmtpServer, XmppServer XmppServer, params ISniffer[] Sniffers)
Manages the connection with an SMTP server.
override Task< bool > IqError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
Sends an IQ error stanza. If stanza was sent.
override Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Sends an IQ stanza. If stanza was sent.
override Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Sends a message stanza. If stanza was sent.
override Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Sends a presence stanza. If stanza was sent.
override Task< bool > IqResult(string Id, XmppAddress To, XmppAddress From, string ResultXml)
Sends an IQ result stanza. If stanza was sent.
override Task< string > IqError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends an IQ error stanza. If stanza was sent.
Task< bool > RelayMessage(SmtpMessage Message, MailAddress Recipient)
Relays a mail message
override Task< bool > PresenceError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends a presence error stanza. If stanza was sent.
override Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Sends a presence stanza. If stanza was sent.
override Task< bool > PresenceError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
Sends a presence error stanza. If stanza was sent.
bool TrustServer
If server should be trusted, regardless if the operating system could validate its certificate or not...
override async Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Sends a message stanza. If stanza was sent.
override string Type
Type of endpoint
override async Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Sends a presence stanza. If stanza was sent.
override Task< bool > MessageError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends a message error stanza. If stanza was sent.
override Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Sends a message stanza. If stanza was sent.
Contains information about a stanza.
XmlElement StanzaElement
Stanza element.
Contains information about one XMPP address.
override string ToString()
object.ToString()
CaseInsensitiveString Address
XMPP Address
CaseInsensitiveString BareJid
Bare JID
const string ContentNamespace
urn:xmpp:content
const string MailNamespace
urn:xmpp:smtp
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
override string ToString()
Implements an in-memory cache.
void Dispose()
IDisposable.Dispose
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.
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
Interface for senders of stanzas.
ContentDisposition
Content disposition
ContentType
DTLS Record content type.