public void ThrowIfInvalid() { Recipients.ThrowIfNullOrEmpty(nameof(Recipients)); Subject.ThrowIfNullOrEmpty(nameof(Subject)); ViewPath.ThrowIfNullOrEmpty(nameof(ViewPath)); foreach (string recipient in Recipients) { if (!StringHelpers.IsValidEmail(recipient)) { throw new InvalidOperationException($"Email '{recipient}' is invalid"); } } if (Cc.Any()) { foreach (string cc in Cc) { if (!StringHelpers.IsValidEmail(cc)) { throw new InvalidOperationException($"Email '{cc}' is invalid"); } } } }
public void ThrowIfInvalid() { Recipients.ThrowIfNullOrEmpty(nameof(Recipients)); Subject.ThrowIfNullOrEmpty(nameof(Subject)); ViewPath.ThrowIfNullOrEmpty(nameof(ViewPath)); foreach (string recipient in Recipients) { if (!StringHelpers.IsValidEmail(recipient)) { throw new InvalidOperationException($"Email '{recipient}' is invalid"); } } if (Cc.Any()) { foreach (string cc in Cc) { if (!StringHelpers.IsValidEmail(cc)) { throw new InvalidOperationException($"Email '{cc}' is invalid"); } } Cc = Cc.Where(x => !Recipients.Contains(x)).ToArray(); } if (HiddenCc.Any()) { foreach (string cc in HiddenCc) { if (!StringHelpers.IsValidEmail(cc)) { throw new InvalidOperationException($"Email '{cc}' is invalid"); } } HiddenCc = HiddenCc .Where(x => !Cc.Contains(x)) .Where(x => !Recipients.Contains(x)).ToArray(); } }
/// <summary> /// Encode this and all nested e-mail body parts. /// </summary> internal AbstractEMailBuilder EncodeBodyparts() { var SignTheMail = false; var EncryptTheMail = false; EMailBodypart BodypartToBeSecured = null; #region Add attachments, if available... if (_Attachments == null || _Attachments.Count == 0) { BodypartToBeSecured = _EncodeBodyparts(); } else { BodypartToBeSecured = new EMailBodypart(ContentTypeBuilder: AEMail => new MailContentType(AEMail, MailContentTypes.multipart_mixed) { CharSet = "utf-8" }.GenerateMIMEBoundary(), ContentTransferEncoding: "8bit", Content: new String[] { "This is a multi-part message in MIME format." }, NestedBodyparts: new EMailBodypart[] { _EncodeBodyparts() }. Concat(_Attachments)); } #endregion #region Check security settings switch (SecurityLevel) { case EMailSecurity.autosign: if (From.SecretKeyRing != null && From.SecretKeyRing.Any() && Passphrase.IsNotNullOrEmpty()) { SignTheMail = true; } break; case EMailSecurity.sign: if (From.SecretKeyRing == null || !From.SecretKeyRing.Any() || Passphrase.IsNullOrEmpty()) { throw new ApplicationException("Can not sign the e-mail!"); } SignTheMail = true; break; case EMailSecurity.auto: if (From.SecretKeyRing != null && From.SecretKeyRing.Any() && Passphrase.IsNotNullOrEmpty()) { SignTheMail = true; } if (SignTheMail && (!To.Any() | To.Any(v => v.PublicKeyRing != null && v.PublicKeyRing.Any())) && (!Cc.Any() | Cc.Any(v => v.PublicKeyRing != null && v.PublicKeyRing.Any()))) { EncryptTheMail = true; } break; case EMailSecurity.encrypt: if (From.SecretKeyRing == null || !From.SecretKeyRing.Any() || Passphrase.IsNullOrEmpty() || To.Any(v => v.PublicKeyRing == null || !v.PublicKeyRing.Any()) || Cc.Any(v => v.PublicKeyRing == null || !v.PublicKeyRing.Any())) { throw new ApplicationException("Can not sign and encrypt the e-mail!"); } EncryptTheMail = true; break; } #endregion #region Sign the e-mail if (SignTheMail & !EncryptTheMail) { var DataToBeSigned = BodypartToBeSecured. // Include headers of this MIME body // https://tools.ietf.org/html/rfc1847 Security Multiparts for MIME: ToText(). // Any trailing whitespace MUST then be removed from the signed material Select(line => line.TrimEnd()). // Canonical text format with <CR><LF> line endings // https://tools.ietf.org/html/rfc3156 5. OpenPGP signed data Aggregate((a, b) => a + "\r\n" + b) //ToDo: Apply Content-Transfer-Encoding ; // MIME Security with OpenPGP (rfc3156, https://tools.ietf.org/html/rfc3156) // OpenPGP Message Format (rfc4880, https://tools.ietf.org/html/rfc4880) _Body = new EMailBodypart(ContentTypeBuilder: AMail => new MailContentType(AMail, MailContentTypes.multipart_signed) { MicAlg = "pgp-sha512", Protocol = "application/pgp-signature", CharSet = "utf-8", }, ContentTransferEncoding: "8bit", NestedBodyparts: new EMailBodypart[] { BodypartToBeSecured, new EMailBodypart(ContentTypeBuilder: AMail => new MailContentType(AMail, MailContentTypes.application_pgp__signature) { CharSet = "utf-8" }, // ContentTransferEncoding: "8bit", ContentDescription: "OpenPGP digital signature", ContentDisposition: ContentDispositions.attachment.ToString() + "; filename=\"signature.asc\"", Content: new String[] { OpenPGP.CreateSignature(new MemoryStream(DataToBeSigned.ToUTF8Bytes()), From.SecretKeyRing.First(), Passphrase, HashAlgorithm: HashAlgorithm). WriteTo(new MemoryStream(), CloseOutputStream: false). ToUTF8String() }) } ); } #endregion #region Encrypt the e-mail else if (SignTheMail & EncryptTheMail) { var Plaintext = BodypartToBeSecured.ToText().Aggregate((a, b) => a + "\r\n" + b).ToUTF8Bytes(); var Ciphertext = new MemoryStream(); OpenPGP.EncryptSignAndZip(InputStream: new MemoryStream(Plaintext), Length: (UInt64)Plaintext.Length, SecretKey: From.SecretKeyRing.First(), Passphrase: Passphrase, PublicKey: To.First().PublicKeyRing.First(), OutputStream: Ciphertext, SymmetricKeyAlgorithm: SymmetricKeyAlgorithm, HashAlgorithm: HashAlgorithm, CompressionAlgorithm: CompressionAlgorithm, ArmoredOutput: true, Filename: "encrypted.asc", LastModificationTime: DateTime.UtcNow); // MIME Security with OpenPGP (rfc3156, https://tools.ietf.org/html/rfc3156) // OpenPGP Message Format (rfc4880, https://tools.ietf.org/html/rfc4880) _Body = new EMailBodypart(ContentTypeBuilder: AMail => new MailContentType(AMail, MailContentTypes.multipart_encrypted) { Protocol = "application/pgp-encrypted", CharSet = "utf-8", }, ContentTransferEncoding: "8bit", NestedBodyparts: new EMailBodypart[] { new EMailBodypart(ContentTypeBuilder: AMail => new MailContentType(AMail, MailContentTypes.application_pgp__encrypted) { CharSet = "utf-8" }, ContentDescription: "PGP/MIME version identification", ContentDisposition: ContentDispositions.attachment.ToString() + "; filename=\"signature.asc\"", Content: new String[] { "Version: 1" }), new EMailBodypart(ContentTypeBuilder: AMail => new MailContentType(AMail, MailContentTypes.application_octet__stream) { CharSet = "utf-8" }, ContentDescription: "OpenPGP encrypted message", ContentDisposition: ContentDispositions.inline.ToString() + "; filename=\"encrypted.asc\"", Content: new String[] { Ciphertext.ToArray().ToUTF8String() }), } ); } #endregion else { this._Body = BodypartToBeSecured; } return(this); }
public override string ToString() { StringBuilder sb = new StringBuilder(); string boundary = Guid.NewGuid().ToString(); var date = DateTime.Now.ToString("ddd, d MMM yyyy H:m:s zz00"); var subject = !Equals(SubjectEncoding, Encoding.ASCII) ? EncodeQuotedPrintableHeader(Subject, SubjectEncoding) : Subject; AddHeader(sb, "From", FormatMailAddress(From, HeaderEncoding)); if (To.Any()) { AddHeader(sb, "To", String.Join(", ", To.Select(t => FormatMailAddress(t, HeaderEncoding)))); } if (Cc.Any()) { AddHeader(sb, "Cc", String.Join(", ", Cc.Select(t => FormatMailAddress(t, HeaderEncoding)))); } AddHeader(sb, "Subject", subject); AddHeader(sb, "MIME-Version", "1.0"); AddHeader(sb, "Date", date); AddHeader(sb, "Message-ID", MessageId); foreach (var additionalHeader in AdditionalHeaders) { AddHeader(additionalHeader.Name, additionalHeader.Value); } if (Attachments.Any()) { AddHeader(sb, "Content-Type", "multipart/mixed; boundary=" + boundary); AddLine(sb, ""); AddLine(sb, "--" + boundary); } string boundary2 = Guid.NewGuid().ToString(); AddHeader(sb, "Content-Type", "multipart/alternative; boundary=" + boundary2); AddLine(sb, ""); AddLine(sb, "--" + boundary2); AddHeader(sb, "Content-Type", "text/plain; charset=" + BodyEncoding.HeaderName); AddHeader(sb, "Content-Transfer-Encoding", "quoted-printable"); AddHeader(sb, "Content-Disposition", "inline"); AddLine(sb, ""); AddLine(sb, encodeQuotedPrintable(Text, BodyEncoding)); AddLine(sb, "--" + boundary2); AddHeader(sb, "Content-Type", "text/html; charset=" + BodyEncoding.HeaderName); AddHeader(sb, "Content-Transfer-Encoding", "quoted-printable"); AddHeader(sb, "Content-Disposition", "inline"); AddLine(sb, ""); AddLine(sb, encodeQuotedPrintable(Html, BodyEncoding)); AddLine(sb, "--" + boundary2 + "--"); if (Attachments.Any()) { AddLine(sb, ""); foreach (var attachment in Attachments) { AddLine(sb, "--" + boundary); if (attachment.Encoding != null) { AddHeader(sb, "Content-Type", attachment.Type + "; charset=" + attachment.Encoding.HeaderName); } else { AddHeader(sb, "Content-Type", attachment.Type); } AddHeader(sb, "Content-Transfer-Encoding", "base64"); AddHeader(sb, "Content-Disposition", "attachment; filename=" + attachment.Name); AddLine(sb, ""); AddLine(sb, Convert.ToBase64String(attachment.Data, Base64FormattingOptions.InsertLineBreaks)); } AddLine(sb, "--" + boundary + "--"); } return(sb.ToString()); }