public async Task<ParseResult> Parse(MessageReader reader, ContentType type = ContentType.Text, ContentSubtype subtype = ContentSubtype.Plain, Boundary boundary = null) { headers = new Headers(entity, type, subtype); if ((await headers.Parse(reader)) == ParseResult.Failed) return ParseResult.Failed; content = new Content(entity); ParseResult result = await content.Parse(reader, headers.contentType, headers.contentSubtype, (headers.boundary != null) ? headers.boundary : boundary); return result; }
public async Task<ParseResult> Parse(MessageReader reader) { // parse until empty line, which start the body String line = ""; bool foundContentType = false; bool boundaryRequired = false; // could there be an empty line in FWS? I think just crlf is not allowed in FWS // if starts with the blank line then there is no header // ends with blank line // there are empty lines before the headers start. how many? bool first = true; while ((line = await reader.ReadLineAsync()) != null && (first || !first && line != "")) { first = false; if (line != "") WriteWithCrlf(line); else continue; // hack, could there be a header line not matching if (!Regex.IsMatch(line, "^([^ :]+[ ]*:)|([ \t]+)")) { return ParseStatus(ParseResult.Failed, "invalid headers"); } else if (foundContentType && boundaryRequired && boundary == null) { boundary = Boundary.Parse(line); } else if (foundContentType == false) { Match m = re_content.Match(line); if (m.Success) { ContentType type = ContentType.Text; ContentSubtype subtype = ContentSubtype.Plain; String tp = m.Groups[1].Value.ToLower(); String sbtp = m.Groups[2].Value.ToLower(); if (types.TryGetValue(tp, out type) == true) contentType = type; else contentType = ContentType.Other; if (subtypes.TryGetValue(sbtp, out subtype) == true) contentSubtype = subtype; else contentSubtype = ContentSubtype.Other; contentTypeFullStr = (tp==""?"text":tp) + "/" + (sbtp==""?"plain":sbtp); foundContentType = true; if (contentType == ContentType.Multipart) { boundaryRequired = true; boundary = Boundary.Parse(m.Groups[3].Value); } } } } if (boundaryRequired) { if (boundary == null) return ParseStatus(ParseResult.Failed, "multipart media part with no boundary"); else Boundary.Add(boundary); } SetSize(); WriteCrlf(); // delimeter between headers and body, not part of the headers, so not included in size if (line == null) return ParseResult.Eof; else return ParseResult.Ok; }
public async Task<ParseResult> Parse(MessageReader reader, ContentType type = ContentType.Text, ContentSubtype subtype = ContentSubtype.Plain, Boundary boundary = null) { if (type == ContentType.Multipart) { dataType = DataType.Multipart; while (true) { String line = await reader.ReadLineAsync(); if (line == null) { SetSize(); return ParseResult.Eof; } else if (EmailParser.IsPostmark(line)) { // consumed too much, probably missing boundary? reader.PushCacheLine(line); SetSize(); return ParseResult.Postmark; } WriteWithCrlf(line); // find open boundary if (boundary.IsOpen(line)) { Email email = null; ParseResult res; do { // consume all parts, consisting of header (optional) and content // the boundary token delimets the part // the close boundary completes multipart parsing // content in the multipart is responsible for consuming it's delimeter (end) // exception is the last part which is also multipart email = new Email(entity); Add(email); } while ((res = await email.Parse(reader, type, subtype, boundary)) == ParseResult.OkMultipart); // Ok // if the last part is a multipart or message? itself then it doesn't consume the close boundary // or more parts, continue parsing until all parts and close boundary are consumed /*if (Ok(res) && (data.Last<Email>().content.dataType == DataType.Multipart || data.Last<Email>().content.dataType == DataType.Message))*/ if (res == ParseResult.Ok && boundary.NotClosed()) continue; if (res != ParseResult.Failed) SetSize(); return res; } else if (boundary.IsClose(line, reader)) { SetSize(); return ParseResult.Ok; // OkMultipart } } } else if (type == ContentType.Message) { dataType = DataType.Message; Email email = new Email(entity); Add(email); ParseResult res = await email.Parse(reader, type, subtype, boundary); if (res != ParseResult.Failed) SetSize(); return res; } else { dataType = DataType.Data; while (true) { String line = await reader.ReadLineAsync(); if (line == null) { SetSize(); return ParseResult.Eof; } else if (EmailParser.IsPostmark(line)) { // consumed too much, probably closing boundary is missing ? reader.PushCacheLine(line); SetSize(); return ParseResult.Postmark; } else if (boundary != null && boundary.IsOpen(line)) { SetSize(); RewindLastCrlfSize(); WriteWithCrlf(line); return ParseResult.OkMultipart; //Ok } else if (boundary != null && boundary.IsClose(line, reader)) { SetSize(); RewindLastCrlfSize(); WriteWithCrlf(line); return ParseResult.Ok; //OkMultipart } else WriteWithCrlf(line); } } }
public static void Add(Boundary boundary) { boundaries.Add(boundary); }
public Headers(MemoryStream entity, ContentType outerType = ContentType.Text, ContentSubtype outerSubtype = ContentSubtype.Plain):base(entity) { if (outerType == ContentType.Multipart && outerSubtype == ContentSubtype.Digest) { contentType = ContentType.Message; contentSubtype = ContentSubtype.Rfc822; contentTypeFullStr = "message/rfc822"; } else { contentType = ContentType.Text; contentSubtype = ContentSubtype.Plain; contentTypeFullStr = "text/plain"; } boundary = null; }
void RemoveBoundary(Boundary boundary) { // close boundary removes all boundaries up in the stack, handles missing close boundary int i = boundaries.FindLastIndex(b => b.boundary == boundary.boundary); if (i >= 0) boundaries.RemoveRange(i, boundaries.Count - i); }
public async Task <ParseResult> Parse(MessageReader reader, ContentType type = ContentType.Text, ContentSubtype subtype = ContentSubtype.Plain, Boundary boundary = null) { headers = new Headers(entity, type, subtype); if ((await headers.Parse(reader)) == ParseResult.Failed) { return(ParseResult.Failed); } content = new Content(entity); ParseResult result = await content.Parse(reader, headers.contentType, headers.contentSubtype, (headers.boundary != null)?headers.boundary : boundary); return(result); }
public async Task <ParseResult> Parse(MessageReader reader, ContentType type = ContentType.Text, ContentSubtype subtype = ContentSubtype.Plain, Boundary boundary = null) { if (type == ContentType.Multipart) { dataType = DataType.Multipart; while (true) { String line = await reader.ReadLineAsync(); if (line == null) { SetSize(); return(ParseResult.Eof); } else if (EmailParser.IsPostmark(line)) { // consumed too much, probably missing boundary? reader.PushCacheLine(line); SetSize(); return(ParseResult.Postmark); } WriteWithCrlf(line); // find open boundary if (boundary.IsOpen(line)) { Email email = null; ParseResult res; do { // consume all parts, consisting of header (optional) and content // the boundary token delimets the part // the close boundary completes multipart parsing // content in the multipart is responsible for consuming it's delimeter (end) // exception is the last part which is also multipart email = new Email(entity); Add(email); } while ((res = await email.Parse(reader, type, subtype, boundary)) == ParseResult.OkMultipart); // Ok // if the last part is a multipart or message? itself then it doesn't consume the close boundary // or more parts, continue parsing until all parts and close boundary are consumed /*if (Ok(res) && (data.Last<Email>().content.dataType == DataType.Multipart || * data.Last<Email>().content.dataType == DataType.Message))*/ if (res == ParseResult.Ok && boundary.NotClosed()) { continue; } if (res != ParseResult.Failed) { SetSize(); } return(res); } else if (boundary.IsClose(line, reader)) { SetSize(); return(ParseResult.Ok); // OkMultipart } } } else if (type == ContentType.Message) { dataType = DataType.Message; Email email = new Email(entity); Add(email); ParseResult res = await email.Parse(reader, type, subtype, boundary); if (res != ParseResult.Failed) { SetSize(); } return(res); } else { dataType = DataType.Data; while (true) { String line = await reader.ReadLineAsync(); if (line == null) { SetSize(); return(ParseResult.Eof); } else if (EmailParser.IsPostmark(line)) { // consumed too much, probably closing boundary is missing ? reader.PushCacheLine(line); SetSize(); return(ParseResult.Postmark); } else if (boundary != null && boundary.IsOpen(line)) { SetSize(); RewindLastCrlfSize(); WriteWithCrlf(line); return(ParseResult.OkMultipart); //Ok } else if (boundary != null && boundary.IsClose(line, reader)) { SetSize(); RewindLastCrlfSize(); WriteWithCrlf(line); return(ParseResult.Ok); //OkMultipart } else { WriteWithCrlf(line); } } } }
public async Task <ParseResult> Parse(MessageReader reader) { // parse until empty line, which start the body String line = ""; bool foundContentType = false; bool boundaryRequired = false; // could there be an empty line in FWS? I think just crlf is not allowed in FWS // if starts with the blank line then there is no header // ends with blank line // there are empty lines before the headers start. how many? bool first = true; while ((line = await reader.ReadLineAsync()) != null && (first || !first && line != "")) { first = false; if (line != "") { WriteWithCrlf(line); } else { continue; } // hack, could there be a header line not matching if (!Regex.IsMatch(line, "^([^ :]+[ ]*:)|([ \t]+)")) { return(ParseStatus(ParseResult.Failed, "invalid headers")); } else if (foundContentType && boundaryRequired && boundary == null) { boundary = Boundary.Parse(line); } else if (foundContentType == false) { Match m = re_content.Match(line); if (m.Success) { ContentType type = ContentType.Text; ContentSubtype subtype = ContentSubtype.Plain; String tp = m.Groups[1].Value.ToLower(); String sbtp = m.Groups[2].Value.ToLower(); if (types.TryGetValue(tp, out type) == true) { contentType = type; } else { contentType = ContentType.Other; } if (subtypes.TryGetValue(sbtp, out subtype) == true) { contentSubtype = subtype; } else { contentSubtype = ContentSubtype.Other; } contentTypeFullStr = (tp == ""?"text":tp) + "/" + (sbtp == ""?"plain":sbtp); foundContentType = true; if (contentType == ContentType.Multipart) { boundaryRequired = true; boundary = Boundary.Parse(m.Groups[3].Value); } } } } if (boundaryRequired) { if (boundary == null) { return(ParseStatus(ParseResult.Failed, "multipart media part with no boundary")); } else { Boundary.Add(boundary); } } SetSize(); WriteCrlf(); // delimeter between headers and body, not part of the headers, so not included in size if (line == null) { return(ParseResult.Eof); } else { return(ParseResult.Ok); } }