/// <summary> /// Sends an email with fully custom headers.</summary> /// <param name="from"> /// The "envelope From" address presented to the MTA.</param> /// <param name="to"> /// The "envelope To" address presented to the MTA.</param> /// <param name="headers"> /// Zero or more headers. Headers must be in the correct format, including escaping, and excluding the linebreak /// that separates headers (but including any line breaks required within the header). This method does not /// validate the headers in any way, and does not include From, To, Date or any other headers unless explicitly /// passed in through this argument.</param> /// <param name="bodyPlain"> /// Plain-text version of the e-mail.</param> /// <param name="bodyHtml"> /// HTML version of the e-mail.</param> public void SendEmail(MailAddress from, IEnumerable <MailAddress> to, IEnumerable <string> headers, string bodyPlain, string bodyHtml) { if (from == null) { throw new ArgumentNullException(nameof(from)); } if (to == null) { throw new ArgumentNullException(nameof(to)); } if (to.Count() == 0) { return; } if (bodyPlain == null && bodyHtml == null) { throw new ArgumentException("You must have either a plain-text or an HTML to your e-mail (or both).", nameof(bodyHtml)); } if (bodyPlain == null) { bodyPlain = "This e-mail is only available in HTML format."; } if (headers == null) { throw new ArgumentNullException(nameof(headers)); } var toHeader = to.Select(t => @"""{0}"" <{1}>".Fmt(t.DisplayName, t.Address)).JoinString(", "); _log.Debug(2, "Sending email to " + toHeader); sendAndExpect(@"MAIL FROM: <{0}>".Fmt(from.Address), 250); foreach (var toAddr in to) { sendAndExpect(@"RCPT TO: <{0}>".Fmt(toAddr.Address), 250); } sendAndExpect(@"DATA", 354); // sends the input to the SMTP server, followed by a newline, while correctly escaping all lines starting with a "." Action <string> sendLines = str => { if (str.StartsWith(".")) { _writer.Write('.'); } _writer.Write(str.Replace("\r\n.", "\r\n..")); _writer.Write("\r\n"); _log.Debug(9, ">> " + str); }; foreach (var header in headers) { sendLines(header); } if (bodyHtml != null) { string boundary1 = new string(Enumerable.Range(0, 64).Select(dummy => { var i = Rnd.Next(62); return((char)(i < 10 ? '0' + i : i < 36 ? 'a' + i - 10 : 'A' + i - 36)); }).ToArray()); string boundary2 = new string(Enumerable.Range(0, 64).Select(dummy => { var i = Rnd.Next(62); return((char)(i < 10 ? '0' + i : i < 36 ? 'a' + i - 10 : 'A' + i - 36)); }).ToArray()); string boundary3 = new string(Enumerable.Range(0, 64).Select(dummy => { var i = Rnd.Next(62); return((char)(i < 10 ? '0' + i : i < 36 ? 'a' + i - 10 : 'A' + i - 36)); }).ToArray()); sendLines(@"Content-Type: multipart/mixed; boundary={0}".Fmt(boundary1)); sendLines(""); sendLines(@"--{0}".Fmt(boundary1)); sendLines(@"Content-Type: multipart/related; boundary=""{0}""".Fmt(boundary2)); sendLines(""); sendLines(@"--{0}".Fmt(boundary2)); sendLines(@"Content-Type: multipart/alternative; boundary=""{0}""".Fmt(boundary3)); sendLines(""); sendLines(@"--{0}".Fmt(boundary3)); sendLines(@"Content-Type: text/plain; charset=UTF-8"); sendLines(@"Content-Transfer-Encoding: quoted-printable"); sendLines(""); sendLines(toQuotedPrintable(bodyPlain)); sendLines(@"--{0}".Fmt(boundary3)); sendLines(@"Content-Type: text/html; charset=UTF-8"); sendLines(@"Content-Transfer-Encoding: quoted-printable"); sendLines(""); sendLines(toQuotedPrintable(bodyHtml)); sendLines(@"--{0}--".Fmt(boundary3)); sendLines(""); sendLines(@"--{0}--".Fmt(boundary2)); sendLines(""); sendLines(@"--{0}--".Fmt(boundary1)); } else { sendLines(@"Content-Type: text/plain; charset=UTF-8"); sendLines(@"Content-Transfer-Encoding: quoted-printable"); sendLines(""); sendLines(toQuotedPrintable(bodyPlain)); } sendAndExpect(".", 250); _log.Debug(1, "Sent successfully."); }
/// <summary> /// Generates a random string of the specified length, taking characters from the specified arsenal of characters.</summary> /// <param name="length"> /// Length of the string to generate.</param> /// <param name="takeCharactersFrom"> /// Arsenal to take random characters from. (Default is upper- and lower-case letters and digits.)</param> /// <param name="rnd"> /// If not <c>null</c>, uses the specified random number generator.</param> public static string GenerateString(int length, string takeCharactersFrom = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", Random rnd = null) { if (takeCharactersFrom == null) { throw new ArgumentNullException(nameof(takeCharactersFrom)); } return(new string(Ut.NewArray(length, i => takeCharactersFrom[rnd == null ? Rnd.Next(takeCharactersFrom.Length) : rnd.Next(takeCharactersFrom.Length)]))); }