/// <summary> /// Parses headers from message or mime entry. /// </summary> /// <param name="entryStrm">Stream from where to read headers.</param> /// <returns>Returns header lines.</returns> public static string ParseHeaders(Stream entryStrm) { /* Rfc 2822 3.1. GENERAL DESCRIPTION * A message consists of header fields and, optionally, a body. * The body is simply a sequence of lines containing ASCII charac- * ters. It is separated from the headers by a null line (i.e., a * line with nothing preceding the CRLF). */ byte[] crlf = new byte[] { (byte)'\r', (byte)'\n' }; MemoryStream msHeaders = new MemoryStream(); StreamLineReader r = new StreamLineReader(entryStrm); byte[] lineData = r.ReadLine(); while (lineData != null) { if (lineData.Length == 0) { break; } msHeaders.Write(lineData, 0, lineData.Length); msHeaders.Write(crlf, 0, crlf.Length); lineData = r.ReadLine(); } return(System.Text.Encoding.Default.GetString(msHeaders.ToArray())); }
/// <summary> /// Parses header fields from stream. Stream position stays where header reading ends. /// </summary> /// <param name="stream">Stream from where to parse.</param> public void Parse(Stream stream) { /* Rfc 2822 2.2 Header Fields * Header fields are lines composed of a field name, followed by a colon * (":"), followed by a field body, and terminated by CRLF. A field * name MUST be composed of printable US-ASCII characters (i.e., * characters that have values between 33 and 126, inclusive), except * colon. A field body may be composed of any US-ASCII characters, * except for CR and LF. However, a field body may contain CRLF when * used in header "folding" and "unfolding" as described in section * 2.2.3. All field bodies MUST conform to the syntax described in * sections 3 and 4 of this standard. * * Rfc 2822 2.2.3 Long Header Fields * The process of moving from this folded multiple-line representation * of a header field to its single line representation is called * "unfolding". Unfolding is accomplished by simply removing any CRLF * that is immediately followed by WSP. Each header field should be * treated in its unfolded form for further syntactic and semantic * evaluation. * * Example: * Subject: aaaaa<CRLF> * <TAB or SP>aaaaa<CRLF> */ m_pHeaderFields.Clear(); StreamLineReader r = new StreamLineReader(stream); string line = r.ReadLineString(); while (line != null) { // End of header reached if (line == "") { break; } // Store current header line and read next. We need to read 1 header line to ahead, // because of multiline header fields. string headerField = line; line = r.ReadLineString(); // See if header field is multiline. See comment above. while (line != null && (line.StartsWith("\t") || line.StartsWith(" "))) { headerField += line; line = r.ReadLineString(); } string[] name_value = headerField.Split(new char[] { ':' }, 2); // There must be header field name and value, otherwise invalid header field if (name_value.Length == 2) { Add(name_value[0] + ":", Core.CanonicalDecode(name_value[1].Trim())); } } }
/// <summary> /// Does period handling. /// </summary> /// <param name="strm">Input stream.</param> /// <param name="add_Remove">If true add periods, else removes periods.</param> /// <param name="setStrmPosTo0">If true sets stream position to 0.</param> /// <returns></returns> public static MemoryStream DoPeriodHandling(Stream strm, bool add_Remove, bool setStrmPosTo0) { MemoryStream replyData = new MemoryStream(); byte[] crlf = new byte[] { (byte)'\r', (byte)'\n' }; if (setStrmPosTo0) { strm.Position = 0; } StreamLineReader r = new StreamLineReader(strm); byte[] line = r.ReadLine(); // Loop through all lines while (line != null) { if (line.Length > 0) { if (line[0] == (byte)'.') { /* Add period Rfc 2821 4.5.2 * - Before sending a line of mail text, the SMTP client checks the * first character of the line. If it is a period, one additional * period is inserted at the beginning of the line. */ if (add_Remove) { replyData.WriteByte((byte)'.'); replyData.Write(line, 0, line.Length); } /* Remove period Rfc 2821 4.5.2 * If the first character is a period , the first characteris deleted. */ else { replyData.Write(line, 1, line.Length - 1); } } else { replyData.Write(line, 0, line.Length); } } replyData.Write(crlf, 0, crlf.Length); // Read next line line = r.ReadLine(); } replyData.Position = 0; return(replyData); }
/// <summary> /// Scans invalid CR or LF combination in stream. Returns true if contains invalid CR or LF combination. /// </summary> /// <param name="strm">Stream which to check.</param> /// <returns>Returns true if contains invalid CR or LF combination.</returns> public static bool ScanInvalid_CR_or_LF(Stream strm) { StreamLineReader lineReader = new StreamLineReader(strm); byte[] line = lineReader.ReadLine(); while (line != null) { foreach (byte b in line) { // Contains CR or LF. It cannot conatian such sumbols, because CR must be paired with LF // and we currently reading lines with CRLF combination. if (b == 10 || b == 13) { return(true); } } line = lineReader.ReadLine(); } return(false); }