private static string ReadUnfoldedLine(UnbufferedReader streamReader, Encoding customEncoding) { string line = streamReader.ReadLine(MAX_LINE_LENGTH, customEncoding); if (!string.IsNullOrEmpty(line) && !streamReader.EndOfStream && streamReader.BaseStream.CanSeek) { /** * RFC 822, 3.1.1. LONG HEADER FIELDS * Each header field can be viewed as a single, logical line of * ASCII characters, comprising a field-name and a field-body. * For convenience, the field-body portion of this conceptual * entity can be split into a multiple-line representation; this * is called "folding". The general rule is that wherever there * may be linear-white-space (NOT simply LWSP-chars), a CRLF * immediately followed by AT LEAST one LWSP-char may instead be * inserted. * [...] * The process of moving from this folded multiple-line * representation of a header field to its single line represen- * tation is called "unfolding". Unfolding is accomplished by * regarding CRLF immediately followed by a LWSP-char as * equivalent to the LWSP-char. **/ long previousStreamPosition = streamReader.BaseStream.Position; //check if next line is folded string nextLine = streamReader.ReadLine(MAX_LINE_LENGTH, customEncoding); while (!string.IsNullOrEmpty(nextLine) && Char.IsWhiteSpace(nextLine[0])) { //unfold the stream according to RFC 822 line = line.TrimEnd((char)0x0d, (char)0x0a) + nextLine.TrimStart(); previousStreamPosition = streamReader.BaseStream.Position; nextLine = streamReader.ReadLine(MAX_LINE_LENGTH, customEncoding); } streamReader.BaseStream.Seek(previousStreamPosition, SeekOrigin.Begin); } return(line); }
internal static void ReadHeaderAttributes(System.IO.Stream stream, long partStartIndex, out System.Collections.Specialized.NameValueCollection attributes) { stream.Position = partStartIndex; UnbufferedReader streamReader = new UnbufferedReader(stream); attributes = new System.Collections.Specialized.NameValueCollection(); string line = streamReader.ReadLine(200); char[] headerParameterSeparators = { ';' }; //TODO: parse attribute headers properly, i.e. the stuff BEFORE ";" while (line != null && line.Length > 0 && stream.Position < stream.Length)//read the part headers { string[] headerDataCollection = line.Split(headerParameterSeparators); /** * ==HEADER EXAMPLE 1 (SMTP)== * Content-Type: application/octet-stream; * .name="secretrendezvous.docx" * Content-Transfer-Encoding: base64 * Content-Disposition: attachment; * .filename="secretrendezvous.docx" * * ==HEADER EXAMPLE 2 (SMTP)== * Content-Type: text/plain; * .charset="iso-8859-1" * Content-Transfer-Encoding: quoted-printable * * ==HEADER EXAMPLE 3 (HTTP POST)== * Content-Disposition: form-data; name="check_type_diff" * * ==HEADER EXAMPLE 4 (HTTP POST)== * Content-Disposition: form-data; name="image"; filename="C:\jx-3p.txt" * Content-Type: text/plain * * ==Another tricky RFC 2047 example== * Subject: =?UTF-8?Q?[Bokserkomania]_V=C3=A4nligen_moderera:_"Slipy_w_ksi=C4=99?= =?UTF-8?Q?=C5=BCyce"?= */ for (int i = 0; i < headerDataCollection.Length; i++) { try { if (headerDataCollection[i].Contains("=\"") && headerDataCollection[i].Length > headerDataCollection[i].IndexOf('\"') + 1) { string parameterName = headerDataCollection[i].Substring(0, headerDataCollection[i].IndexOf('=')).Trim(); string parameterValue = headerDataCollection[i].Substring(headerDataCollection[i].IndexOf('\"') + 1, headerDataCollection[i].LastIndexOf('\"') - headerDataCollection[i].IndexOf('\"') - 1).Trim(); parameterValue = Rfc2047Parser.DecodeRfc2047Parts(parameterValue); attributes.Add(parameterName, parameterValue); } else if (headerDataCollection[i].Contains(": ")) { string parameterName = headerDataCollection[i].Substring(0, headerDataCollection[i].IndexOf(':')).Trim(); string parameterValue = headerDataCollection[i].Substring(headerDataCollection[i].IndexOf(':') + 1).Trim(); parameterValue = Rfc2047Parser.DecodeRfc2047Parts(parameterValue); attributes.Add(parameterName, parameterValue); } } catch (Exception e) { //TODO log exception somewhere... } } line = streamReader.ReadLine(200); } }
internal static void ReadHeaderAttributes(System.IO.Stream stream, long partStartIndex, out System.Collections.Specialized.NameValueCollection attributes, bool useOnlyASCII, Encoding customEncoding = null) { stream.Position = partStartIndex; UnbufferedReader streamReader = new UnbufferedReader(stream); attributes = new System.Collections.Specialized.NameValueCollection(); string line = streamReader.ReadLine(200, customEncoding); char[] headerParameterSeparators = { ';' }; //TODO: parse attribute headers properly, i.e. the stuff BEFORE ";" while (line != null && line.Length > 0 && stream.Position < stream.Length)//read the part headers { string[] headerDataCollection = line.Split(headerParameterSeparators); bool skipNextLine = false; /** * ==HEADER EXAMPLE 1 (SMTP)== * Content-Type: application/octet-stream; * .name="secretrendezvous.docx" * Content-Transfer-Encoding: base64 * Content-Disposition: attachment; * .filename="secretrendezvous.docx" * * ==HEADER EXAMPLE 2 (SMTP, part inside multipart/mixed) - notice the missing quotation marks around the filename * Content-Type: application/zip * Content-Disposition: attachment; filename=00000459735.zip * Content-Transfer-Encoding: base64 * * ==HEADER EXAMPLE 3 (SMTP)== * Content-Type: text/plain; * .charset="iso-8859-1" * Content-Transfer-Encoding: quoted-printable * * ==HEADER EXAMPLE 4 (HTTP POST)== * Content-Disposition: form-data; name="check_type_diff" * * ==HEADER EXAMPLE 5 (HTTP POST)== * Content-Disposition: form-data; name="image"; filename="C:\jx-3p.txt" * Content-Type: text/plain * * ==Another tricky RFC 2047 example== * Subject: =?UTF-8?Q?[Bokserkomania]_V=C3=A4nligen_moderera:_"Slipy_w_ksi=C4=99?= =?UTF-8?Q?=C5=BCyce"?= * * ==UTF8 encoded filename in HTTP POST, RFC 5987== * Content-Disposition: form-data; name="Filedata"; filename="测试文档-Document for test.docx" * ^^--where filename is UTF8 encoded * * ==SMTP email with Simplified Chinese in attribute values== prior to this we've seen: charset="gb2312" * Content-Type: application/octet-stream; * name="ºÎÀ½£¸ß¼¶ÃØÊé¡¢ÖúÀíºÍÐÐÕþ¹ÜÀíÕß¹¤×÷ЧÂÊÌáÉýѵÁ·Óª.zip" * Content-Transfer-Encoding: base64 * Content-Disposition: attachment; * filename="ºÎÀ½£¸ß¼¶ÃØÊé¡¢ÖúÀíºÍÐÐÕþ¹ÜÀíÕß¹¤×÷ЧÂÊÌáÉýѵÁ·Óª.zip" * * ==POP3 example with multi-line boundary in Content-Type== * Content-Type: multipart/related; boundary="----MIME delimiter for [0x0d 0x0a] * sendEmail-118138.401377113" * * ==SMTP example== * Content-Type: text/plain; charset=utf-8; format=flowed * content-transfer-encoding: quoted-printable * * ==SMTP example== * Content-Type: application/octet-stream; name="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" * Content-Transfer-Encoding: base64 * */ for (int i = 0; i < headerDataCollection.Length; i++) { try { if (headerDataCollection[i].Contains("=\"") && headerDataCollection[i].Length > headerDataCollection[i].IndexOf('\"') + 1) { string parameterName = headerDataCollection[i].Substring(0, headerDataCollection[i].IndexOf('=')).Trim(); int length = headerDataCollection[i].LastIndexOf('\"') - headerDataCollection[i].IndexOf('\"') - 1; while (length < 0 && line != null && line.Length > 0 && stream.Position < stream.Length) { if (i == headerDataCollection.Length - 1) { //try reading the next line. line = streamReader.ReadLine(200, customEncoding); char quote = '\"'; if (line.Contains("" + quote) && line.IndexOf(quote) == line.LastIndexOf(quote) && line.EndsWith("" + quote)) { headerDataCollection[i] += line;//append next line to value length = headerDataCollection[i].LastIndexOf('\"') - headerDataCollection[i].IndexOf('\"') - 1; } else { skipNextLine = true; break; } } else { continue;//go to next parameter } } string parameterValue = headerDataCollection[i].Substring(headerDataCollection[i].IndexOf('\"') + 1, length).Trim(); parameterValue = Rfc2047Parser.DecodeRfc2047Parts(parameterValue); if (useOnlyASCII) { attributes.Add(Utils.StringManglerUtil.ConvertToAsciiIfUnicode(parameterName), Utils.StringManglerUtil.ConvertToAsciiIfUnicode(parameterValue)); } else { attributes.Add(parameterName, parameterValue); } } else if (headerDataCollection[i].Contains("name=") || headerDataCollection[i].Contains("charset=") || headerDataCollection[i].Contains("format=")) { string parameterName = headerDataCollection[i].Substring(0, headerDataCollection[i].IndexOf('=')).Trim(); string parameterValue = headerDataCollection[i].Substring(headerDataCollection[i].IndexOf('=') + 1).Trim(); parameterValue = Rfc2047Parser.DecodeRfc2047Parts(parameterValue); if (useOnlyASCII) { attributes.Add(Utils.StringManglerUtil.ConvertToAsciiIfUnicode(parameterName), Utils.StringManglerUtil.ConvertToAsciiIfUnicode(parameterValue)); } else { attributes.Add(parameterName, parameterValue); } } else if (headerDataCollection[i].Contains(": ")) { string parameterName = headerDataCollection[i].Substring(0, headerDataCollection[i].IndexOf(':')).Trim(); string parameterValue = headerDataCollection[i].Substring(headerDataCollection[i].IndexOf(':') + 1).Trim(); parameterValue = Rfc2047Parser.DecodeRfc2047Parts(parameterValue); if (useOnlyASCII) { attributes.Add(Utils.StringManglerUtil.ConvertToAsciiIfUnicode(parameterName), Utils.StringManglerUtil.ConvertToAsciiIfUnicode(parameterValue)); } else { attributes.Add(parameterName, parameterValue); } } } catch (Exception e) { SharedUtils.Logger.Log("Exception when parsing MIME data: " + e.Message, SharedUtils.Logger.EventLogEntryType.Error); } } if (skipNextLine) { skipNextLine = false; } else { line = streamReader.ReadLine(200, customEncoding); } } }
internal static void ReadHeaderAttributes(System.IO.Stream stream, long partStartIndex, out System.Collections.Specialized.NameValueCollection attributes, Encoding customEncoding = null) { stream.Position = partStartIndex; UnbufferedReader streamReader = new UnbufferedReader(stream); attributes = new System.Collections.Specialized.NameValueCollection(); string line = streamReader.ReadLine(200, customEncoding); char[] headerParameterSeparators = { ';' }; //TODO: parse attribute headers properly, i.e. the stuff BEFORE ";" while (line != null && line.Length > 0 && stream.Position < stream.Length)//read the part headers { string[] headerDataCollection = line.Split(headerParameterSeparators); /** * ==HEADER EXAMPLE 1 (SMTP)== * Content-Type: application/octet-stream; * .name="secretrendezvous.docx" * Content-Transfer-Encoding: base64 * Content-Disposition: attachment; * .filename="secretrendezvous.docx" * * ==HEADER EXAMPLE 2 (SMTP, part inside multipart/mixed) - notice the missing quotation marks around the filename * Content-Type: application/zip * Content-Disposition: attachment; filename=00000459735.zip * Content-Transfer-Encoding: base64 * * ==HEADER EXAMPLE 3 (SMTP)== * Content-Type: text/plain; * .charset="iso-8859-1" * Content-Transfer-Encoding: quoted-printable * * ==HEADER EXAMPLE 4 (HTTP POST)== * Content-Disposition: form-data; name="check_type_diff" * * ==HEADER EXAMPLE 5 (HTTP POST)== * Content-Disposition: form-data; name="image"; filename="C:\jx-3p.txt" * Content-Type: text/plain * * ==Another tricky RFC 2047 example== * Subject: =?UTF-8?Q?[Bokserkomania]_V=C3=A4nligen_moderera:_"Slipy_w_ksi=C4=99?= =?UTF-8?Q?=C5=BCyce"?= * * ==UTF8 encoded filename in HTTP POST, RFC 5987== * Content-Disposition: form-data; name="Filedata"; filename="测试文档-Document for test.docx" * ^^--where filename is UTF8 encoded * * ==SMTP email with Simplified Chinese in attribute values== prior to this we've seen: charset="gb2312" * Content-Type: application/octet-stream; * name="ºÎÀ½£¸ß¼¶ÃØÊé¡¢ÖúÀíºÍÐÐÕþ¹ÜÀíÕß¹¤×÷ЧÂÊÌáÉýѵÁ·Óª.zip" * Content-Transfer-Encoding: base64 * Content-Disposition: attachment; * filename="ºÎÀ½£¸ß¼¶ÃØÊé¡¢ÖúÀíºÍÐÐÕþ¹ÜÀíÕß¹¤×÷ЧÂÊÌáÉýѵÁ·Óª.zip" * * */ for (int i = 0; i < headerDataCollection.Length; i++) { try { if (headerDataCollection[i].Contains("=\"") && headerDataCollection[i].Length > headerDataCollection[i].IndexOf('\"') + 1) { string parameterName = headerDataCollection[i].Substring(0, headerDataCollection[i].IndexOf('=')).Trim(); string parameterValue = headerDataCollection[i].Substring(headerDataCollection[i].IndexOf('\"') + 1, headerDataCollection[i].LastIndexOf('\"') - headerDataCollection[i].IndexOf('\"') - 1).Trim(); parameterValue = Rfc2047Parser.DecodeRfc2047Parts(parameterValue); attributes.Add(parameterName, parameterValue); } else if (headerDataCollection[i].Contains("name=")) { string parameterName = headerDataCollection[i].Substring(0, headerDataCollection[i].IndexOf('=')).Trim(); string parameterValue = headerDataCollection[i].Substring(headerDataCollection[i].IndexOf('=') + 1).Trim(); parameterValue = Rfc2047Parser.DecodeRfc2047Parts(parameterValue); attributes.Add(parameterName, parameterValue); } else if (headerDataCollection[i].Contains(": ")) { string parameterName = headerDataCollection[i].Substring(0, headerDataCollection[i].IndexOf(':')).Trim(); string parameterValue = headerDataCollection[i].Substring(headerDataCollection[i].IndexOf(':') + 1).Trim(); parameterValue = Rfc2047Parser.DecodeRfc2047Parts(parameterValue); attributes.Add(parameterName, parameterValue); } } catch (Exception e) { //TODO log exception somewhere... } } line = streamReader.ReadLine(200, customEncoding); } }