/// <summary> /// Parses the supplied <paramref name="lines"/> into constituent <see cref="MimePart"/> instances /// </summary> /// <remarks> /// This is the main parser interface. The <see cref="MimePart"/> instances expected to be returned are <see cref="Header"/> (one for each header), and <see cref="Body"/> /// </remarks> /// <param name="lines">The enumeration of <see cref="StringSegment"/> lines to parse</param> /// <returns>An enumeration of the constituent <see cref="MimePart"/> instances parsed from the <paramref name="lines"/></returns> public static IEnumerable <MimePart> ReadMimeParts(IEnumerable <StringSegment> lines) { if (lines == null) { throw new ArgumentNullException("lines"); } MimePartType expectedPartType = MimePartType.Header; Header header = null; Body body = null; foreach (StringSegment line in lines) { switch (expectedPartType) { default: throw new MimeException(MimeError.Unexpected); case MimePartType.Header: if (line.IsEmpty) { if (header != null) { yield return(header); header = null; } yield return(new MimePart(MimePartType.HeaderBoundary, line)); // // Done with the header section. Onto the Body // expectedPartType = MimePartType.Body; break; } if (MimeStandard.IsWhitespace(line[0])) { // Line folding. This line belongs to the current header if (header == null) { throw new MimeException(MimeError.InvalidHeader); } header.AppendSourceText(line); break; } if (header != null) { yield return(header); } header = new Header(line); break; case MimePartType.Body: if (body == null) { body = new Body(line); } else { body.AppendSourceText(line); } break; } } if (header != null) { yield return(header); } if (body != null) { yield return(body); } }
/// <summary> /// Parses set of <see cref="StringSegment"/> supplying the individual lines of a <c>multipart</c> body, returning contituent parts. /// </summary> /// <param name="bodyLines">The <c>multipart</c> body lines</param> /// <param name="boundary">The <c>multipart</c> boundary string</param> /// <returns>The constituent body parts.</returns> public static IEnumerable <MimePart> ReadBodyParts(IEnumerable <StringSegment> bodyLines, string boundary) { if (bodyLines == null) { throw new ArgumentNullException("bodyLines"); } if (string.IsNullOrEmpty(boundary)) { throw new MimeException(MimeError.MissingBoundarySeparator); } IEnumerator <StringSegment> lineEnumerator = bodyLines.GetEnumerator(); StringSegment part = StringSegment.Null; StringSegment prevLine = StringSegment.Null; MimePartType expectedPart = MimePartType.BodyPrologue; // // As per the Multipart Spec: // The boundary delimiter is actually CRLF--boundary // So we must maintain a stack (prevLine).. // while (expectedPart != MimePartType.BodyEpilogue && lineEnumerator.MoveNext()) { StringSegment line = lineEnumerator.Current; if (IsBoundary(line, boundary)) { if (!part.IsNull) { part.Union(prevLine); } // // When we hit a boundary, we yield the part we've collected so far // MimePartType partType = expectedPart; switch (expectedPart) { default: throw new MimeException(MimeError.InvalidBodySubpart); case MimePartType.BodyPrologue: expectedPart = MimePartType.BodyPart; break; case MimePartType.BodyPart: if (IsBoundaryEnd(line)) { expectedPart = MimePartType.BodyEpilogue; } break; } if (!part.IsNull) { yield return(new MimePart(partType, part)); } prevLine = StringSegment.Null; part = StringSegment.Null; } else { if (!prevLine.IsNull) { part.Union(prevLine); } prevLine = line; } } if (expectedPart != MimePartType.BodyEpilogue) { throw new MimeException(MimeError.InvalidBodySubpart); } // Epilogue part = StringSegment.Null; while (lineEnumerator.MoveNext()) { part.Union(lineEnumerator.Current); } if (!part.IsEmpty) { yield return(new MimePart(MimePartType.BodyEpilogue, part)); } }