public void TestSimplePlainTextBody() { const string expected = "(\"text\" \"plain\" (\"charset\" \"us-ascii\" \"name\" \"body.txt\") NIL NIL \"7bit\" 3028 NIL NIL NIL NIL 92)"; BodyPartText text, parsed; BodyPart body; text = new BodyPartText { ContentType = new ContentType("text", "plain") { Charset = "us-ascii", Name = "body.txt" }, ContentTransferEncoding = "7bit", Octets = 3028, Lines = 92, }; Assert.IsTrue(text.IsPlain); Assert.IsFalse(text.IsHtml); Assert.IsFalse(text.IsAttachment); Assert.AreEqual("body.txt", text.FileName); Assert.AreEqual(expected, text.ToString()); Assert.IsTrue(BodyPart.TryParse(expected, out body)); Assert.IsInstanceOf <BodyPartText> (body); parsed = (BodyPartText)body; Assert.IsTrue(parsed.ContentType.IsMimeType("text", "plain"), "Content-Type did not match."); Assert.AreEqual("us-ascii", parsed.ContentType.Charset, "charset param did not match"); Assert.AreEqual("body.txt", parsed.ContentType.Name, "name param did not match"); Assert.AreEqual("7bit", parsed.ContentTransferEncoding, "Content-Transfer-Encoding did not match."); Assert.AreEqual(3028, parsed.Octets, "Octet count did not match."); Assert.AreEqual(92, parsed.Lines, "Line count did not match."); Assert.AreEqual(expected, parsed.ToString()); }
static BodyPartBasic CreateText(string type, string subtype, string partSpecifier, bool attachment) { var text = new BodyPartText { ContentType = CreateContentType(type, subtype, partSpecifier) }; if (attachment) { text.ContentDisposition = new ContentDisposition(ContentDisposition.Attachment); } return(text); }
/// <summary> /// Visit the text-based MIME part entity. /// </summary> /// <remarks> /// Visits the text-based MIME part entity. /// </remarks> /// <param name="entity">The text-based body part.</param> protected internal virtual void VisitBodyPartText(BodyPartText entity) { VisitBodyPartBasic(entity); }
public static BodyPart ParseBody (ImapEngine engine, string path, CancellationToken cancellationToken) { var token = engine.ReadToken (cancellationToken); if (token.Type == ImapTokenType.Nil) return null; if (token.Type != ImapTokenType.OpenParen) throw ImapEngine.UnexpectedToken (token, false); token = engine.PeekToken (cancellationToken); if (token.Type == ImapTokenType.OpenParen) return ParseMultipart (engine, path, null, cancellationToken); ContentType type; string value; if (!ParseContentType (engine, cancellationToken, out type, out value)) { // GMail breakage... yay! What we have is a nested multipart with // the same boundary as its parent. return ParseMultipart (engine, path, value, cancellationToken); } var id = ReadNStringToken (engine, false, cancellationToken); var desc = ReadNStringToken (engine, true, cancellationToken); // Note: technically, body-fld-enc, is not allowed to be NIL, but we need to deal with broken servers... var enc = ReadNStringToken (engine, false, cancellationToken); var octets = ReadNumber (engine, cancellationToken); BodyPartBasic body; if (type.Matches ("message", "rfc822")) { var mesg = new BodyPartMessage (); // Note: GMail (and potentially other IMAP servers) will send body-part-basic // expressions instead of body-part-msg expressions when they encounter // message/rfc822 MIME parts that are illegally encoded using base64 (or // quoted-printable?). According to rfc3501, IMAP servers are REQUIRED to // send body-part-msg expressions for message/rfc822 parts, however, it is // understandable why GMail (and other IMAP servers?) do what they do in this // particular case. // // For examples, see issue #32 and issue #59. // // The workaround is to check for the expected '(' signifying an envelope token. // If we do not get an '(', then we are likely looking at the Content-MD5 token // which gets handled below. token = engine.PeekToken (cancellationToken); if (token.Type == ImapTokenType.OpenParen) { mesg.Envelope = ParseEnvelope (engine, cancellationToken); mesg.Body = ParseBody (engine, path, cancellationToken); mesg.Lines = ReadNumber (engine, cancellationToken); } body = mesg; } else if (type.Matches ("text", "*")) { var text = new BodyPartText (); text.Lines = ReadNumber (engine, cancellationToken); body = text; } else { body = new BodyPartBasic (); } body.ContentTransferEncoding = enc; body.ContentDescription = desc; body.PartSpecifier = path; body.ContentType = type; body.ContentId = id; body.Octets = octets; // if we are parsing a BODYSTRUCTURE, we may get some more tokens before the ')' token = engine.PeekToken (cancellationToken); if (token.Type != ImapTokenType.CloseParen) { body.ContentMd5 = ReadNStringToken (engine, false, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentDisposition = ParseContentDisposition (engine, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLanguage = ParseContentLanguage (engine, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLocation = ParseContentLocation (engine, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) SkipBodyExtensions (engine, cancellationToken); // read the ')' token = engine.ReadToken (cancellationToken); if (token.Type != ImapTokenType.CloseParen) throw ImapEngine.UnexpectedToken (token, false); return body; }
async void RenderText(IMailFolder folder, UniqueId uid, BodyPartText bodyPart) { var entity = await folder.GetBodyPartAsync(uid, bodyPart); RenderText((TextPart)entity); }
public void TestBodyPartCollection() { var text = new BodyPartText { ContentType = new ContentType("text", "plain"), ContentLocation = new Uri("body", UriKind.Relative) }; var image1 = new BodyPartBasic { ContentType = new ContentType("image", "jpeg"), ContentLocation = new Uri("http://localhost/image1.jpg") }; var image2 = new BodyPartBasic { ContentType = new ContentType("image", "jpeg"), ContentId = "image2@localhost" }; var list = new BodyPartCollection(); var parts = new BodyPart[3]; int i = 0; Assert.Throws <ArgumentNullException> (() => list.Add(null)); Assert.Throws <ArgumentNullException> (() => list.Remove(null)); Assert.Throws <ArgumentNullException> (() => list.Contains(null)); Assert.Throws <ArgumentNullException> (() => list.IndexOf(null)); Assert.Throws <ArgumentNullException> (() => list.CopyTo(null, 0)); Assert.Throws <ArgumentOutOfRangeException> (() => list.CopyTo(parts, -1)); Assert.Throws <ArgumentOutOfRangeException> (() => { var x = list[0]; }); Assert.IsFalse(list.IsReadOnly); Assert.AreEqual(0, list.Count); list.Add(text); Assert.AreEqual(1, list.Count); Assert.IsTrue(list.Contains(text)); Assert.IsFalse(list.Contains(image1)); Assert.AreEqual(0, list.IndexOf(new Uri("body", UriKind.Relative))); Assert.AreEqual(-1, list.IndexOf(new Uri("http://localhost/image1.jpg"))); Assert.AreEqual(-1, list.IndexOf(new Uri("cid:image2@localhost"))); Assert.AreEqual(text, list[0]); list.Add(image1); Assert.AreEqual(2, list.Count); Assert.IsTrue(list.Contains(text)); Assert.IsTrue(list.Contains(image1)); Assert.AreEqual(0, list.IndexOf(new Uri("body", UriKind.Relative))); Assert.AreEqual(1, list.IndexOf(new Uri("http://localhost/image1.jpg"))); Assert.AreEqual(-1, list.IndexOf(new Uri("cid:image2@localhost"))); Assert.AreEqual(text, list[0]); Assert.AreEqual(image1, list[1]); Assert.IsTrue(list.Remove(text)); Assert.AreEqual(1, list.Count); Assert.IsFalse(list.Contains(text)); Assert.IsTrue(list.Contains(image1)); Assert.AreEqual(-1, list.IndexOf(new Uri("body", UriKind.Relative))); Assert.AreEqual(0, list.IndexOf(new Uri("http://localhost/image1.jpg"))); Assert.AreEqual(-1, list.IndexOf(new Uri("cid:image2@localhost"))); Assert.AreEqual(image1, list[0]); list.Clear(); Assert.AreEqual(0, list.Count); list.Add(text); list.Add(image1); list.Add(image2); list.CopyTo(parts, 0); Assert.AreEqual(0, list.IndexOf(new Uri("body", UriKind.Relative))); Assert.AreEqual(1, list.IndexOf(new Uri("http://localhost/image1.jpg"))); Assert.AreEqual(2, list.IndexOf(new Uri("cid:image2@localhost"))); foreach (var part in list) { Assert.AreEqual(parts[i++], part); } i = 0; foreach (var part in (IEnumerable)list) { Assert.AreEqual(parts[i++], part); } }
public static BodyPart ParseBody(ImapEngine engine, string path, CancellationToken cancellationToken) { var token = engine.ReadToken(cancellationToken); if (token.Type == ImapTokenType.Nil) { return(null); } if (token.Type != ImapTokenType.OpenParen) { throw ImapEngine.UnexpectedToken(token, false); } token = engine.PeekToken(cancellationToken); if (token.Type == ImapTokenType.OpenParen) { return(ParseMultipart(engine, path, cancellationToken)); } var type = ParseContentType(engine, cancellationToken); var id = ReadNStringToken(engine, false, cancellationToken); var desc = ReadNStringToken(engine, true, cancellationToken); var enc = ReadStringToken(engine, cancellationToken); var octets = ReadNumber(engine, cancellationToken); BodyPartBasic body; if (type.Matches("message", "rfc822")) { var mesg = new BodyPartMessage(); mesg.Envelope = ParseEnvelope(engine, cancellationToken); mesg.Body = ParseBody(engine, path, cancellationToken); mesg.Lines = ReadNumber(engine, cancellationToken); body = mesg; } else if (type.Matches("text", "*")) { var text = new BodyPartText(); text.Lines = ReadNumber(engine, cancellationToken); body = text; } else { body = new BodyPartBasic(); } body.ContentTransferEncoding = enc; body.ContentDescription = desc; body.PartSpecifier = path; body.ContentType = type; body.ContentId = id; body.Octets = octets; // if we are parsing a BODYSTRUCTURE, we may get some more tokens before the ')' token = engine.PeekToken(cancellationToken); if (token.Type != ImapTokenType.CloseParen) { body.ContentMd5 = ReadNStringToken(engine, false, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentDisposition = ParseContentDisposition(engine, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLanguage = ParseContentLocation(engine, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLocation = ReadNStringToken(engine, false, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { SkipBodyExtensions(engine, cancellationToken); } // read the ')' token = engine.ReadToken(cancellationToken); if (token.Type != ImapTokenType.CloseParen) { throw ImapEngine.UnexpectedToken(token, false); } return(body); }
public static BodyPart ParseBody(ImapEngine engine, string path, CancellationToken cancellationToken) { var token = engine.ReadToken (cancellationToken); if (token.Type == ImapTokenType.Nil) return null; if (token.Type != ImapTokenType.OpenParen) throw ImapEngine.UnexpectedToken (token, false); token = engine.PeekToken (cancellationToken); if (token.Type == ImapTokenType.OpenParen) return ParseMultipart (engine, path, cancellationToken); var type = ParseContentType (engine, cancellationToken); var id = ReadNStringToken (engine, false, cancellationToken); var desc = ReadNStringToken (engine, true, cancellationToken); var enc = ReadStringToken (engine, cancellationToken); var octets = ReadNumber (engine, cancellationToken); BodyPartBasic body; if (type.Matches ("message", "rfc822")) { var mesg = new BodyPartMessage (); mesg.Envelope = ParseEnvelope (engine, cancellationToken); mesg.Body = ParseBody (engine, path, cancellationToken); mesg.Lines = ReadNumber (engine, cancellationToken); body = mesg; } else if (type.Matches ("text", "*")) { var text = new BodyPartText (); text.Lines = ReadNumber (engine, cancellationToken); body = text; } else { body = new BodyPartBasic (); } body.ContentTransferEncoding = enc; body.ContentDescription = desc; body.PartSpecifier = path; body.ContentType = type; body.ContentId = id; body.Octets = octets; // if we are parsing a BODYSTRUCTURE, we may get some more tokens before the ')' token = engine.PeekToken (cancellationToken); if (token.Type != ImapTokenType.CloseParen) { body.ContentMd5 = ReadNStringToken (engine, false, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentDisposition = ParseContentDisposition (engine, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLanguage = ParseContentLocation (engine, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLocation = ReadNStringToken (engine, false, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) SkipBodyExtensions (engine, cancellationToken); // read the ')' token = engine.ReadToken (cancellationToken); if (token.Type != ImapTokenType.CloseParen) throw ImapEngine.UnexpectedToken (token, false); return body; }
public static BodyPart ParseBody(ImapEngine engine, string path, CancellationToken cancellationToken) { var token = engine.ReadToken(cancellationToken); if (token.Type == ImapTokenType.Nil) { return(null); } if (token.Type != ImapTokenType.OpenParen) { throw ImapEngine.UnexpectedToken(token, false); } token = engine.PeekToken(cancellationToken); if (token.Type == ImapTokenType.OpenParen) { return(ParseMultipart(engine, path, cancellationToken)); } var type = ParseContentType(engine, cancellationToken); var id = ReadNStringToken(engine, false, cancellationToken); var desc = ReadNStringToken(engine, true, cancellationToken); // Note: technically, body-fld-enc, is not allowed to be NIL, but we need to deal with broken servers... var enc = ReadNStringToken(engine, false, cancellationToken); var octets = ReadNumber(engine, cancellationToken); BodyPartBasic body; if (type.Matches("message", "rfc822")) { var mesg = new BodyPartMessage(); // Note: GMail's support for message/rfc822 body parts is broken. Essentially, // GMail treats message/rfc822 parts as if they were basic body parts. // // For examples, see issue #32 and issue #59. // // The workaround is to check for the expected '(' signifying an envelope token. // If we do not get an '(', then we are likely looking at the Content-MD5 token // which gets handled below. token = engine.PeekToken(cancellationToken); if (!engine.IsGMail || token.Type == ImapTokenType.OpenParen) { mesg.Envelope = ParseEnvelope(engine, cancellationToken); mesg.Body = ParseBody(engine, path, cancellationToken); mesg.Lines = ReadNumber(engine, cancellationToken); } body = mesg; } else if (type.Matches("text", "*")) { var text = new BodyPartText(); text.Lines = ReadNumber(engine, cancellationToken); body = text; } else { body = new BodyPartBasic(); } body.ContentTransferEncoding = enc; body.ContentDescription = desc; body.PartSpecifier = path; body.ContentType = type; body.ContentId = id; body.Octets = octets; // if we are parsing a BODYSTRUCTURE, we may get some more tokens before the ')' token = engine.PeekToken(cancellationToken); if (token.Type != ImapTokenType.CloseParen) { body.ContentMd5 = ReadNStringToken(engine, false, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentDisposition = ParseContentDisposition(engine, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLanguage = ParseContentLanguage(engine, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLocation = ParseContentLocation(engine, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { SkipBodyExtensions(engine, cancellationToken); } // read the ')' token = engine.ReadToken(cancellationToken); if (token.Type != ImapTokenType.CloseParen) { throw ImapEngine.UnexpectedToken(token, false); } return(body); }
async Task <string> RenderTextAsync(IMailFolder folder, UniqueId uid, BodyPartText bodyPart) { var entity = await folder.GetBodyPartAsync(uid, bodyPart); return(RenderText((TextPart)entity)); }
public static BodyPart ParseBody(ImapEngine engine, string path, CancellationToken cancellationToken) { var token = engine.ReadToken (cancellationToken); if (token.Type == ImapTokenType.Nil) return null; if (token.Type != ImapTokenType.OpenParen) throw ImapEngine.UnexpectedToken (token, false); token = engine.PeekToken (cancellationToken); if (token.Type == ImapTokenType.OpenParen) return ParseMultipart (engine, path, cancellationToken); var type = ParseContentType (engine, cancellationToken); var id = ReadNStringToken (engine, false, cancellationToken); var desc = ReadNStringToken (engine, true, cancellationToken); // Note: technically, body-fld-enc, is not allowed to be NIL, but we need to deal with broken servers... var enc = ReadNStringToken (engine, false, cancellationToken); var octets = ReadNumber (engine, cancellationToken); BodyPartBasic body; if (type.Matches ("message", "rfc822")) { var mesg = new BodyPartMessage (); // Note: GMail's support for message/rfc822 body parts is broken. Essentially, // GMail treats message/rfc822 parts as if they were basic body parts. // // For examples, see issue #32 and issue #59. // // The workaround is to check for the expected '(' signifying an envelope token. // If we do not get an '(', then we are likely looking at the Content-MD5 token // which gets handled below. token = engine.PeekToken (cancellationToken); if (!engine.IsGMail || token.Type == ImapTokenType.OpenParen) { mesg.Envelope = ParseEnvelope (engine, cancellationToken); mesg.Body = ParseBody (engine, path, cancellationToken); mesg.Lines = ReadNumber (engine, cancellationToken); } body = mesg; } else if (type.Matches ("text", "*")) { var text = new BodyPartText (); text.Lines = ReadNumber (engine, cancellationToken); body = text; } else { body = new BodyPartBasic (); } body.ContentTransferEncoding = enc; body.ContentDescription = desc; body.PartSpecifier = path; body.ContentType = type; body.ContentId = id; body.Octets = octets; // if we are parsing a BODYSTRUCTURE, we may get some more tokens before the ')' token = engine.PeekToken (cancellationToken); if (token.Type != ImapTokenType.CloseParen) { body.ContentMd5 = ReadNStringToken (engine, false, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentDisposition = ParseContentDisposition (engine, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLanguage = ParseContentLanguage (engine, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLocation = ParseContentLocation (engine, cancellationToken); token = engine.PeekToken (cancellationToken); } if (token.Type != ImapTokenType.CloseParen) SkipBodyExtensions (engine, cancellationToken); // read the ')' token = engine.ReadToken (cancellationToken); if (token.Type != ImapTokenType.CloseParen) throw ImapEngine.UnexpectedToken (token, false); return body; }
async void RenderRelated(IMailFolder folder, UniqueId uid, BodyPartMultipart related) { var start = related.ContentType.Parameters["start"]; BodyPartText root = null; if (!string.IsNullOrEmpty(start)) { // if the 'start' parameter is set, it overrides the default behavior of using the first // body part as the main document. root = related.BodyParts.OfType <BodyPartText> ().FirstOrDefault(x => x.ContentId == start); } else if (related.BodyParts.Count > 0) { // this will generally either be a text/html part (which is what we are looking for) or a multipart/alternative var multipart = related.BodyParts[0] as BodyPartMultipart; if (multipart != null) { if (multipart.ContentType.Matches("multipart", "alternative") && multipart.BodyParts.Count > 0) { // find the last text/html part (which will be the closest to what the sender saw in their WYSIWYG editor) // or, failing that, the last text part. for (int i = multipart.BodyParts.Count; i > 0; i--) { var bodyPart = multipart.BodyParts[i - 1] as BodyPartText; if (bodyPart == null) { continue; } if (bodyPart.ContentType.Matches("text", "html")) { root = bodyPart; break; } if (root == null) { root = bodyPart; } } } } else { root = related.BodyParts[0] as BodyPartText; } } if (root == null) { return; } var text = await folder.GetBodyPartAsync(uid, root) as TextPart; if (text != null && text.ContentType.Matches("text", "html")) { var doc = new HtmlAgilityPack.HtmlDocument(); var saved = new Dictionary <MimePart, string> (); TextPart html; doc.LoadHtml(text.Text); // find references to related MIME parts and replace them with links to links to the saved attachments foreach (var img in doc.DocumentNode.SelectNodes("//img[@src]")) { var src = img.Attributes["src"]; int index; Uri uri; if (src == null || src.Value == null) { continue; } // parse the <img src=...> attribute value into a Uri if (Uri.IsWellFormedUriString(src.Value, UriKind.Absolute)) { uri = new Uri(src.Value, UriKind.Absolute); } else { uri = new Uri(src.Value, UriKind.Relative); } // locate the index of the attachment within the multipart/related (if it exists) if ((index = related.BodyParts.IndexOf(uri)) != -1) { var bodyPart = related.BodyParts[index] as BodyPartBasic; if (bodyPart == null) { // the body part is not a basic leaf part (IOW it's a multipart or message-part) continue; } var attachment = await folder.GetBodyPartAsync(uid, bodyPart) as MimePart; // make sure the referenced part is a MimePart (as opposed to another Multipart or MessagePart) if (attachment == null) { continue; } string fileName; // save the attachment (if we haven't already saved it) if (!saved.TryGetValue(attachment, out fileName)) { fileName = attachment.FileName; if (string.IsNullOrEmpty(fileName)) { fileName = Guid.NewGuid().ToString(); } if (!Directory.Exists(uid.ToString())) { Directory.CreateDirectory(uid.ToString()); } fileName = Path.Combine(uid.ToString(), fileName); using (var stream = File.Create(fileName)) attachment.ContentObject.DecodeTo(stream); saved.Add(attachment, fileName); } // replace the <img src=...> value with the local file name src.Value = "file://" + Path.GetFullPath(fileName); } } if (saved.Count > 0) { // we had to make some modifications to the original html part, so create a new // (temporary) text/html part to render html = new TextPart("html"); using (var writer = new StringWriter()) { doc.Save(writer); html.Text = writer.GetStringBuilder().ToString(); } } else { html = text; } RenderText(html); } else if (text != null) { RenderText(text); } }
private static async void RenderText(IMailFolder folder, UniqueId uid, BodyPartText bodyPart, WebBrowserEditabil pWebBrowser) { var entity = await folder.GetBodyPartAsync(uid, bodyPart); RenderText((TextPart)entity, pWebBrowser); }
/// <summary> /// Visit the text-based MIME part entity. /// </summary> /// <remarks> /// Visits the text-based MIME part entity. /// </remarks> /// <param name="entity">The text-based body part.</param> protected internal virtual void VisitBodyPartText (BodyPartText entity) { VisitBodyPartBasic (entity); }
public static BodyPart ParseBody(ImapEngine engine, string path, CancellationToken cancellationToken) { var token = engine.ReadToken(cancellationToken); if (token.Type == ImapTokenType.Nil) { return(null); } if (token.Type != ImapTokenType.OpenParen) { throw ImapEngine.UnexpectedToken(token, false); } token = engine.PeekToken(cancellationToken); if (token.Type == ImapTokenType.OpenParen) { return(ParseMultipart(engine, path, cancellationToken)); } var type = ParseContentType(engine, cancellationToken); var id = ReadNStringToken(engine, false, cancellationToken); var desc = ReadNStringToken(engine, true, cancellationToken); // Note: technically, body-fld-enc, is not allowed to be NIL, but we need to deal with broken servers... var enc = ReadNStringToken(engine, false, cancellationToken); var octets = ReadNumber(engine, cancellationToken); BodyPartBasic body; if (type.Matches("message", "rfc822")) { var mesg = new BodyPartMessage(); // Note: GMail (and potentially other IMAP servers) will send body-part-basic // expressions instead of body-part-msg expressions when they encounter // message/rfc822 MIME parts that are illegally encoded using base64 (or // quoted-printable?). According to rfc3501, IMAP servers are REQUIRED to // send body-part-msg expressions for message/rfc822 parts, however, it is // understandable why GMail (and other IMAP servers?) do what they do in this // particular case. // // For examples, see issue #32 and issue #59. // // The workaround is to check for the expected '(' signifying an envelope token. // If we do not get an '(', then we are likely looking at the Content-MD5 token // which gets handled below. token = engine.PeekToken(cancellationToken); if (token.Type == ImapTokenType.OpenParen) { mesg.Envelope = ParseEnvelope(engine, cancellationToken); mesg.Body = ParseBody(engine, path, cancellationToken); mesg.Lines = ReadNumber(engine, cancellationToken); } body = mesg; } else if (type.Matches("text", "*")) { var text = new BodyPartText(); text.Lines = ReadNumber(engine, cancellationToken); body = text; } else { body = new BodyPartBasic(); } body.ContentTransferEncoding = enc; body.ContentDescription = desc; body.PartSpecifier = path; body.ContentType = type; body.ContentId = id; body.Octets = octets; // if we are parsing a BODYSTRUCTURE, we may get some more tokens before the ')' token = engine.PeekToken(cancellationToken); if (token.Type != ImapTokenType.CloseParen) { body.ContentMd5 = ReadNStringToken(engine, false, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentDisposition = ParseContentDisposition(engine, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLanguage = ParseContentLanguage(engine, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { body.ContentLocation = ParseContentLocation(engine, cancellationToken); token = engine.PeekToken(cancellationToken); } if (token.Type != ImapTokenType.CloseParen) { SkipBodyExtensions(engine, cancellationToken); } // read the ')' token = engine.ReadToken(cancellationToken); if (token.Type != ImapTokenType.CloseParen) { throw ImapEngine.UnexpectedToken(token, false); } return(body); }