diff --git a/email.d b/email.d index 2c5d734..6c704ca 100644 --- a/email.d +++ b/email.d @@ -8,6 +8,7 @@ pragma(lib, "curl"); import std.base64; import std.string; +import std.range; import arsd.characterencodings; @@ -188,13 +189,14 @@ class EmailMessage { { MimeContainer mimeMessage; + enum NO_TRANSFER_ENCODING = "Content-Transfer-Encoding: 8bit"; if(isHtml) { auto alternative = new MimeContainer("multipart/alternative"); - alternative.stuff ~= new MimeContainer("text/plain; charset=UTF-8", textBody); - alternative.stuff ~= new MimeContainer("text/html; charset=UTF-8", htmlBody); + alternative.stuff ~= new MimeContainer("text/plain; charset=UTF-8", textBody).with_header(NO_TRANSFER_ENCODING); + alternative.stuff ~= new MimeContainer("text/html; charset=UTF-8", htmlBody).with_header(NO_TRANSFER_ENCODING); mimeMessage = alternative; } else { - mimeMessage = new MimeContainer("text/plain; charset=UTF-8", textBody); + mimeMessage = new MimeContainer("text/plain; charset=UTF-8", textBody).with_header(NO_TRANSFER_ENCODING); } top = mimeMessage; } @@ -211,7 +213,7 @@ class EmailMessage { auto mimeAttachment = new MimeContainer(attachment.type ~ "; name=\""~attachment.filename~"\""); mimeAttachment.headers ~= "Content-Transfer-Encoding: base64"; mimeAttachment.headers ~= "Content-ID: <" ~ attachment.id ~ ">"; - mimeAttachment.content = Base64.encode(cast(const(ubyte)[]) attachment.content); + mimeAttachment.content = encodeBase64Mime(cast(const(ubyte)[]) attachment.content); mimeRelated.stuff ~= mimeAttachment; } @@ -233,7 +235,7 @@ class EmailMessage { if(attachment.id.length) mimeAttachment.headers ~= "Content-ID: <" ~ attachment.id ~ ">"; - mimeAttachment.content = Base64.encode(cast(const(ubyte)[]) attachment.content); + mimeAttachment.content = encodeBase64Mime(cast(const(ubyte)[]) attachment.content); mimeMixed.stuff ~= mimeAttachment; } @@ -1152,6 +1154,24 @@ immutable(ubyte)[] decodeQuotedPrintable(string text) { return ret; } +/// Add header UFCS helper +auto with_header(MimeContainer container, string header){ + container.headers ~= header; + return container; +} + +/// Base64 range encoder UFCS helper. +alias base64encode = Base64.encoder; + +/// Base64 encoded data with line length of 76 as mandated by RFC 2045 Section 6.8 +string encodeBase64Mime(const(ubyte[]) content, string LINESEP = "\r\n") { + enum LINE_LENGTH = 76; + /// Only 6 bit of every byte are used; log2(64) = 6 + enum int SOURCE_CHUNK_LENGTH = LINE_LENGTH * 6/8; + + return cast(immutable(char[]))content.chunks(SOURCE_CHUNK_LENGTH).base64encode.join(LINESEP); +} + /+ void main() { import std.file;