1 module smtp.message; 2 3 import std.string; 4 import std.uuid; 5 6 import smtp.attachment; 7 8 /++ 9 Struct that holds name and address of a person holding e-mail box 10 and is capable of sending messages. 11 +/ 12 struct Recipient { 13 string address; 14 string name; 15 } 16 17 /++ 18 Implements SmtpMessage compositor. 19 Allows to get string representation of message and also send it via SmtpClient. 20 21 SmtpMessage is used by `SmtpClient.send` high-level method in order to compose 22 and send mail via SMTP. 23 +/ 24 struct SmtpMessage { 25 static string boundary; // Parts delmiter in multipart message 26 Recipient sender; // Specifies name/address of a sender 27 Recipient[] recipients; // Specifies names/adresses of recipients 28 string subject; // Message subject 29 string message; // Message text (body) 30 string replyTo; // Messages chains maked (reply-to:) 31 SmtpAttachment[] attachments; // Attachments to message 32 33 /++ 34 Initializes boundary for parts in multipart/mixed message type. 35 36 Boundary is a random sequence of chars that must divide message 37 into parts: message, and attachments. 38 +/ 39 static this() { 40 boundary = randomUUID().toString(); 41 } 42 43 /++ 44 Add attachments to the `SmtpMessage`. 45 +/ 46 void attach(SmtpAttachment[] a...) { 47 attachments ~= a; 48 } 49 50 /++ 51 Builds string representation for a list of copies-to-send over SMTP. 52 +/ 53 private string cc() const { 54 string tCc = "Cc:\"%s\" <%s>\r\n"; 55 string cc = ""; 56 if (recipients.length > 1) { 57 foreach(recipient; recipients) { 58 cc ~= format(tCc, recipient.name, recipient.address); 59 } 60 } else { 61 cc = ""; 62 } 63 return cc; 64 } 65 66 /++ 67 Builds message representation in case we have multipart/mixed MIME-type 68 of the message to send. 69 +/ 70 private string messageWithAttachments() const { 71 const string crlf = "\r\n"; 72 return "Content-Type: text/html; charset=utf-8" ~ crlf 73 ~ crlf 74 ~ message ~ crlf 75 ~ crlf ~ "--" ~ SmtpMessage.boundary ~ crlf; 76 } 77 78 /++ 79 Partly converts attachments to string for SMTP protocol representation 80 +/ 81 private string attachmentsToString() const { 82 string result = ""; 83 foreach(ref a; attachments) { 84 result ~= a.toString(boundary); 85 } 86 return result[0..$ - 2] ~ ".\r\n"; 87 } 88 89 /++ 90 This method converts SmtpMessage struct to string representation. 91 92 This string representation is a ready-to-send representation for 93 SMTP protocol. 94 +/ 95 string toString() const { 96 const string tFrom = "From: \"%s\" <%s>\r\n"; 97 const string tTo = "To: \"%s\" <%s>\r\n"; 98 const string tSubject = "Subject: %s\r\n"; 99 const string mime = "MIME-Version: 1.0\r\n"; 100 const string tMultipart = format("Content-Type: multipart/mixed; boundary=\"%s\"\r\n", SmtpMessage.boundary); 101 const string tReplyTo = "Reply-To:%s\r\n"; 102 const string crlf = "\r\n"; 103 104 if (!attachments.length) { 105 return format(tFrom, sender.name, sender.address) 106 ~ format(tTo, recipients[0].name, recipients[0].address) 107 ~ cc() 108 ~ format(tSubject, subject) 109 ~ format(tReplyTo, replyTo) 110 ~ crlf 111 ~ message ~ "." ~ crlf; 112 } else { 113 return format(tFrom, sender.name, sender.address) 114 ~ format(tTo, recipients[0].name, recipients[0].address) 115 ~ cc() 116 ~ format(tSubject, subject) 117 ~ mime 118 ~ tMultipart 119 ~ format(tReplyTo, replyTo) ~ crlf 120 ~ "--" ~ boundary ~ crlf 121 ~ messageWithAttachments() 122 ~ attachmentsToString(); 123 } 124 } 125 }