From a049cf5ec64045807ce6b6fc3be4acddd4a475b4 Mon Sep 17 00:00:00 2001 From: Andy Edinborough Date: Mon, 9 Apr 2012 08:48:28 -0500 Subject: [PATCH] Additional updates for proper encoding (#54); also respects `name` header value when reading in attachments (closes #63) --- Attachment.cs | 9 ++++----- HeaderCollection.cs | 4 ++-- HeaderObject.cs | 20 ++++++++++++++++---- ImapClient.cs | 2 +- MailMessage.cs | 7 ++----- Utilities.cs | 16 ++++++++-------- 6 files changed, 33 insertions(+), 25 deletions(-) diff --git a/Attachment.cs b/Attachment.cs index 11ffbc5..91d7943 100644 --- a/Attachment.cs +++ b/Attachment.cs @@ -1,12 +1,11 @@ using System; -using System.ComponentModel; namespace AE.Net.Mail { public class Attachment : ObjectWHeaders { public string Filename { - get { return Headers["Content-Disposition"]["filename"]; } + get { return Headers["Content-Disposition"]["filename"].NotEmpty(Headers["Content-Disposition"]["name"]); } } - + private string _ContentDisposition; private string ContentDisposition { get { return _ContentDisposition ?? (_ContentDisposition = Headers["Content-Disposition"].Value.ToLower()); } @@ -36,10 +35,10 @@ public byte[] GetData() { try { data = Convert.FromBase64String(Body); } catch (Exception) { - data = System.Text.Encoding.UTF8.GetBytes(Body); + data = Encoding.GetBytes(Body); } } else { - data = System.Text.Encoding.UTF8.GetBytes(Body); + data = Encoding.GetBytes(Body); } return data; } diff --git a/HeaderCollection.cs b/HeaderCollection.cs index 338bb5a..df648c5 100644 --- a/HeaderCollection.cs +++ b/HeaderCollection.cs @@ -144,8 +144,8 @@ public MailAddress[] GetAddresses(string header) { } - public static HeaderDictionary Parse(string headers) { - headers = Utilities.DecodeWords(headers); + public static HeaderDictionary Parse(string headers, System.Text.Encoding encoding) { + headers = Utilities.DecodeWords(headers, encoding); var temp = new Dictionary(StringComparer.OrdinalIgnoreCase); var lines = headers.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); int i; diff --git a/HeaderObject.cs b/HeaderObject.cs index 32b4c48..31245e6 100644 --- a/HeaderObject.cs +++ b/HeaderObject.cs @@ -6,7 +6,7 @@ public abstract class ObjectWHeaders { private HeaderDictionary _Headers; public HeaderDictionary Headers { get { - return _Headers ?? (_Headers = HeaderDictionary.Parse(RawHeaders)); + return _Headers ?? (_Headers = HeaderDictionary.Parse(RawHeaders, Encoding)); } internal set { _Headers = value; @@ -35,12 +35,24 @@ public string Charset { } } + System.Text.Encoding _DefaultEncoding; + System.Text.Encoding _Encoding; + public System.Text.Encoding Encoding { + get { + return _Encoding ?? (_Encoding = Utilities.ParseCharsetToEncoding(Charset, _DefaultEncoding)); + } + set { + _DefaultEncoding = value; + if (_Encoding != null) //Encoding has been initialized from the specified Charset + _Encoding = value; + } + } + public string Body { get; set; } internal void SetBody(string value) { - var encoding = Utilities.ParseCharsetToEncoding(Charset); if (ContentTransferEncoding.Is("quoted-printable")) { - value = Utilities.DecodeQuotedPrintable(value, encoding); + value = Utilities.DecodeQuotedPrintable(value, Encoding); } else if (ContentTransferEncoding.Is("base64") //only decode the content if it is a text document @@ -48,7 +60,7 @@ internal void SetBody(string value) { && Utilities.IsValidBase64String(value)) { var data = Convert.FromBase64String(value); using (var mem = new System.IO.MemoryStream(data)) - using (var str = new System.IO.StreamReader(mem, encoding)) + using (var str = new System.IO.StreamReader(mem, Encoding)) value = str.ReadToEnd(); ContentTransferEncoding = string.Empty; diff --git a/ImapClient.cs b/ImapClient.cs index e3e3024..d071a02 100644 --- a/ImapClient.cs +++ b/ImapClient.cs @@ -410,7 +410,7 @@ public MailMessage[] GetMessages(string start, string end, bool uid, bool header if (response[0] != '*' || !response.Contains("FETCH (")) continue; - var mail = new MailMessage(); + var mail = new MailMessage { Encoding = Encoding }; var imapHeaders = ParseImapHeader(response.Substring(response.IndexOf('(') + 1)); mail.Size = (imapHeaders["BODY[HEADER]"] ?? imapHeaders["BODY[]"]).Trim('{', '}').ToInt(); diff --git a/MailMessage.cs b/MailMessage.cs index 6842c1c..ca6e160 100644 --- a/MailMessage.cs +++ b/MailMessage.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.IO; using System.Linq; using System.Net.Mail; @@ -78,7 +76,6 @@ public MailMessage() { public string Raw { get; private set; } public MailPriority Importance { get; set; } - public void Load(string message, bool headersOnly = false) { Raw = message; using (var reader = new StringReader(message)) { @@ -141,7 +138,7 @@ public void Load(TextReader reader, bool headersOnly = false) { Importance = Headers.GetEnum("Importance"); Subject = Headers["Subject"].RawValue; } - + private void ParseMime(TextReader reader, string boundary) { string data, bounderInner = "--" + boundary, @@ -153,7 +150,7 @@ private void ParseMime(TextReader reader, string boundary) { while (data != null && !data.StartsWith(bounderOuter)) { data = reader.ReadLine(); - var a = new Attachment(); + var a = new Attachment { Encoding = Encoding }; var part = new StringBuilder(); // read part header diff --git a/Utilities.cs b/Utilities.cs index e1079ae..1bb1ce7 100644 --- a/Utilities.cs +++ b/Utilities.cs @@ -84,7 +84,7 @@ internal static bool StartsWithWhiteSpace(this string line) { internal static string DecodeQuotedPrintable(string value, Encoding encoding = null) { if (encoding == null) { - encoding = System.Text.Encoding.UTF8; + encoding = System.Text.Encoding.Default; } if (value.IndexOf('_') > -1 && value.IndexOf(' ') == -1) @@ -125,11 +125,11 @@ internal static string DecodeBase64(string data, Encoding encoding = null) { return data; } var bytes = Convert.FromBase64String(data); - return (encoding ?? System.Text.Encoding.UTF8).GetString(bytes); + return (encoding ?? System.Text.Encoding.Default).GetString(bytes); } #region OpenPOP.NET - internal static string DecodeWords(string encodedWords) { + internal static string DecodeWords(string encodedWords, Encoding @default) { if (string.IsNullOrEmpty(encodedWords)) return string.Empty; @@ -162,7 +162,7 @@ internal static string DecodeWords(string encodedWords) { string charset = match.Groups["Charset"].Value; // Get the encoding which corrosponds to the character set - Encoding charsetEncoding = ParseCharsetToEncoding(charset); + Encoding charsetEncoding = ParseCharsetToEncoding(charset, @default); // Store decoded text here when done string decodedText; @@ -204,9 +204,9 @@ internal static string DecodeWords(string encodedWords) { /// The character set to parse /// An encoding which corresponds to the character set /// If is - public static Encoding ParseCharsetToEncoding(string characterSet) { + public static Encoding ParseCharsetToEncoding(string characterSet, Encoding @default) { if (string.IsNullOrEmpty(characterSet)) - return Encoding.Default; + return @default ?? Encoding.Default; string charSetUpper = characterSet.ToUpperInvariant(); if (charSetUpper.Contains("WINDOWS") || charSetUpper.Contains("CP")) { @@ -219,12 +219,12 @@ public static Encoding ParseCharsetToEncoding(string characterSet) { int codepageNumber = int.Parse(charSetUpper, System.Globalization.CultureInfo.InvariantCulture); return Encoding.GetEncodings().Where(x => x.CodePage == codepageNumber) - .Select(x => x.GetEncoding()).FirstOrDefault() ?? Encoding.Default; + .Select(x => x.GetEncoding()).FirstOrDefault() ?? @default ?? Encoding.Default; } // It seems there is no codepage value in the characterSet. It must be a named encoding return Encoding.GetEncodings().Where(x => x.Name.Is(characterSet)) - .Select(x => x.GetEncoding()).FirstOrDefault() ?? Encoding.Default; + .Select(x => x.GetEncoding()).FirstOrDefault() ?? @default ?? System.Text.Encoding.Default; } #endregion