A multipart body part.
A multipart body part.
Inheritance: BodyPart
Example #1
0
        static bool TryGetMultipartAlternativeBody(BodyPartMultipart multipart, bool html, out BodyPartText body)
        {
            // walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful
            for (int i = multipart.BodyParts.Count - 1; i >= 0; i--)
            {
                var          multi = multipart.BodyParts[i] as BodyPartMultipart;
                BodyPartText text  = null;

                if (multi != null)
                {
                    if (multi.ContentType.IsMimeType("multipart", "related"))
                    {
                        text = GetMultipartRelatedRoot(multi) as BodyPartText;
                    }
                    else if (multi.ContentType.IsMimeType("multipart", "alternative"))
                    {
                        // Note: nested multipart/alternatives make no sense... yet here we are.
                        if (TryGetMultipartAlternativeBody(multi, html, out body))
                        {
                            return(true);
                        }
                    }
                }
                else
                {
                    text = multipart.BodyParts[i] as BodyPartText;
                }

                if (text != null && (html ? text.IsHtml : text.IsPlain))
                {
                    body = text;
                    return(true);
                }
            }

            body = null;

            return(false);
        }
Example #2
0
        static BodyPart GetMultipartRelatedRoot(BodyPartMultipart related)
        {
            string start = related.ContentType.Parameters["start"];
            string contentId;

            if (start == null)
            {
                return(related.BodyParts.Count > 0 ? related.BodyParts[0] : null);
            }

            if ((contentId = MimeUtils.EnumerateReferences(start).FirstOrDefault()) == null)
            {
                contentId = start;
            }

            var cid = new Uri(string.Format("cid:{0}", contentId));

            for (int i = 0; i < related.BodyParts.Count; i++)
            {
                var basic = related.BodyParts[i] as BodyPartBasic;

                if (basic != null && (basic.ContentId == contentId || basic.ContentLocation == cid))
                {
                    return(basic);
                }

                var multipart = related.BodyParts[i] as BodyPartMultipart;

                if (multipart != null && multipart.ContentLocation == cid)
                {
                    return(multipart);
                }
            }

            return(null);
        }
Example #3
0
        static bool TryParse(string text, ref int index, string path, out BodyPart part)
        {
            ContentDisposition disposition;
            ContentType        contentType;

            string[] array;
            string   nstring;
            Uri      location;
            uint     number;

            part = null;

            while (index < text.Length && text[index] == ' ')
            {
                index++;
            }

            if (index >= text.Length || text[index] != '(')
            {
                if (index + 3 <= text.Length && text.Substring(index, 3) == "NIL")
                {
                    index += 3;
                    return(true);
                }

                return(false);
            }

            index++;

            if (index >= text.Length)
            {
                return(false);
            }

            if (text[index] == '(')
            {
                var prefix    = path.Length > 0 ? path + "." : string.Empty;
                var multipart = new BodyPartMultipart();
                IList <BodyPart> children;

                if (!TryParse(text, ref index, prefix, out children))
                {
                    return(false);
                }

                foreach (var child in children)
                {
                    multipart.BodyParts.Add(child);
                }

                if (!TryParse(text, ref index, true, out contentType))
                {
                    return(false);
                }

                multipart.ContentType = contentType;

                if (!TryParse(text, ref index, out disposition))
                {
                    return(false);
                }

                multipart.ContentDisposition = disposition;

                if (!TryParse(text, ref index, out array))
                {
                    return(false);
                }

                multipart.ContentLanguage = array;

                if (!TryParse(text, ref index, out location))
                {
                    return(false);
                }

                multipart.ContentLocation = location;

                part = multipart;
            }
            else
            {
                BodyPartMessage message = null;
                BodyPartText    txt     = null;
                BodyPartBasic   basic;

                if (!TryParse(text, ref index, false, out contentType))
                {
                    return(false);
                }

                if (contentType.IsMimeType("message", "rfc822"))
                {
                    basic = message = new BodyPartMessage();
                }
                else if (contentType.IsMimeType("text", "*"))
                {
                    basic = txt = new BodyPartText();
                }
                else
                {
                    basic = new BodyPartBasic();
                }

                basic.ContentType = contentType;

                if (!TryParse(text, ref index, out nstring))
                {
                    return(false);
                }

                basic.ContentId = nstring;

                if (!TryParse(text, ref index, out nstring))
                {
                    return(false);
                }

                basic.ContentDescription = nstring;

                if (!TryParse(text, ref index, out nstring))
                {
                    return(false);
                }

                basic.ContentTransferEncoding = nstring;

                if (!TryParse(text, ref index, out number))
                {
                    return(false);
                }

                basic.Octets = number;

                if (!TryParse(text, ref index, out nstring))
                {
                    return(false);
                }

                basic.ContentMd5 = nstring;

                if (!TryParse(text, ref index, out disposition))
                {
                    return(false);
                }

                basic.ContentDisposition = disposition;

                if (!TryParse(text, ref index, out array))
                {
                    return(false);
                }

                basic.ContentLanguage = array;

                if (!TryParse(text, ref index, out location))
                {
                    return(false);
                }

                basic.ContentLocation = location;

                if (message != null)
                {
                    Envelope envelope;
                    BodyPart body;

                    if (!Envelope.TryParse(text, ref index, out envelope))
                    {
                        return(false);
                    }

                    message.Envelope = envelope;

                    if (!TryParse(text, ref index, path, out body))
                    {
                        return(false);
                    }

                    message.Body = body;

                    if (!TryParse(text, ref index, out number))
                    {
                        return(false);
                    }

                    message.Lines = number;
                }
                else if (txt != null)
                {
                    if (!TryParse(text, ref index, out number))
                    {
                        return(false);
                    }

                    txt.Lines = number;
                }

                part = basic;
            }

            part.PartSpecifier = path;

            if (index >= text.Length || text[index] != ')')
            {
                return(false);
            }

            index++;

            return(true);
        }
Example #4
0
		static bool TryGetMessageBody (BodyPartMultipart multipart, bool html, out BodyPartText body)
		{
			BodyPartMultipart multi;
			BodyPartText text;

			if (multipart.ContentType.IsMimeType ("multipart", "alternative"))
				return TryGetMultipartAlternativeBody (multipart, html, out body);

			if (!multipart.ContentType.IsMimeType ("multipart", "related")) {
				// Note: This is probably a multipart/mixed... and if not, we can still treat it like it is.
				for (int i = 0; i < multipart.BodyParts.Count; i++) {
					multi = multipart.BodyParts[i] as BodyPartMultipart;

					// descend into nested multiparts, if there are any...
					if (multi != null) {
						if (TryGetMessageBody (multi, html, out body))
							return true;

						// The text body should never come after a multipart.
						break;
					}

					text = multipart.BodyParts[i] as BodyPartText;

					// Look for the first non-attachment text part (realistically, the body text will
					// preceed any attachments, but I'm not sure we can rely on that assumption).
					if (text != null && !text.IsAttachment) {
						if (html ? text.IsHtml : text.IsPlain) {
							body = text;
							return true;
						}

						// Note: the first text/* part in a multipart/mixed is the text body.
						// If it's not in the format we're looking for, then it doesn't exist.
						break;
					}
				}
			} else {
				// Note: If the multipart/related root document is HTML, then this is the droid we are looking for.
				var root = GetMultipartRelatedRoot (multipart);

				text = root as BodyPartText;

				if (text != null) {
					body = (html ? text.IsHtml : text.IsPlain) ? text : null;
					return body != null;
				}

				// maybe the root is another multipart (like multipart/alternative)?
				multi = root as BodyPartMultipart;

				if (multi != null)
					return TryGetMessageBody (multi, html, out body);
			}

			body = null;

			return false;
		}
Example #5
0
		static bool TryGetMultipartAlternativeBody (BodyPartMultipart multipart, bool html, out BodyPartText body)
		{
			// walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful
			for (int i = multipart.BodyParts.Count - 1; i >= 0; i--) {
				var multi = multipart.BodyParts[i] as BodyPartMultipart;
				BodyPartText text = null;

				if (multi != null) {
					if (multi.ContentType.IsMimeType ("multipart", "related")) {
						text = GetMultipartRelatedRoot (multi) as BodyPartText;
					} else if (multi.ContentType.IsMimeType ("multipart", "alternative")) {
						// Note: nested multipart/alternatives make no sense... yet here we are.
						if (TryGetMultipartAlternativeBody (multi, html, out body))
							return true;
					}
				} else {
					text = multipart.BodyParts[i] as BodyPartText;
				}

				if (text != null && (html ? text.IsHtml : text.IsPlain)) {
					body = text;
					return true;
				}
			}

			body = null;

			return false;
		}
Example #6
0
		static BodyPart GetMultipartRelatedRoot (BodyPartMultipart related)
		{
			string start = related.ContentType.Parameters["start"];
			string contentId;

			if (start == null)
				return related.BodyParts.Count > 0 ? related.BodyParts[0] : null;

			if ((contentId = MimeUtils.EnumerateReferences (start).FirstOrDefault ()) == null)
				contentId = start;

			var cid = new Uri (string.Format ("cid:{0}", contentId));

			for (int i = 0; i < related.BodyParts.Count; i++) {
				var basic = related.BodyParts[i] as BodyPartBasic;

				if (basic != null && (basic.ContentId == contentId || basic.ContentLocation == cid))
					return basic;

				var multipart = related.BodyParts[i] as BodyPartMultipart;

				if (multipart != null && multipart.ContentLocation == cid)
					return multipart;
			}

			return null;
		}
Example #7
0
        static bool TryGetMessageBody(BodyPartMultipart multipart, bool html, out BodyPartText body)
        {
            BodyPartMultipart multi;
            BodyPartText      text;

            if (multipart.ContentType.IsMimeType("multipart", "alternative"))
            {
                return(TryGetMultipartAlternativeBody(multipart, html, out body));
            }

            if (!multipart.ContentType.IsMimeType("multipart", "related"))
            {
                // Note: This is probably a multipart/mixed... and if not, we can still treat it like it is.
                for (int i = 0; i < multipart.BodyParts.Count; i++)
                {
                    multi = multipart.BodyParts[i] as BodyPartMultipart;

                    // descend into nested multiparts, if there are any...
                    if (multi != null)
                    {
                        if (TryGetMessageBody(multi, html, out body))
                        {
                            return(true);
                        }

                        // The text body should never come after a multipart.
                        break;
                    }

                    text = multipart.BodyParts[i] as BodyPartText;

                    // Look for the first non-attachment text part (realistically, the body text will
                    // preceed any attachments, but I'm not sure we can rely on that assumption).
                    if (text != null && !text.IsAttachment)
                    {
                        if (html ? text.IsHtml : text.IsPlain)
                        {
                            body = text;
                            return(true);
                        }

                        // Note: the first text/* part in a multipart/mixed is the text body.
                        // If it's not in the format we're looking for, then it doesn't exist.
                        break;
                    }
                }
            }
            else
            {
                // Note: If the multipart/related root document is HTML, then this is the droid we are looking for.
                var root = GetMultipartRelatedRoot(multipart);

                text = root as BodyPartText;

                if (text != null)
                {
                    body = (html ? text.IsHtml : text.IsPlain) ? text : null;
                    return(body != null);
                }

                // maybe the root is another multipart (like multipart/alternative)?
                multi = root as BodyPartMultipart;

                if (multi != null)
                {
                    return(TryGetMessageBody(multi, html, out body));
                }
            }

            body = null;

            return(false);
        }
Example #8
0
		static BodyPartMultipart CreateMultipart (string type, string subtype, string partSpecifier, params BodyPart[] bodyParts)
		{
			var multipart = new BodyPartMultipart { ContentType = CreateContentType (type, subtype, partSpecifier) };
			foreach (var bodyPart in bodyParts)
				multipart.BodyParts.Add (bodyPart);
			return multipart;
		}
Example #9
0
		static bool TryParse (string text, ref int index, string path, out BodyPart part)
		{
			ContentDisposition disposition;
			ContentType contentType;
			string[] array;
			string nstring;
			Uri location;
			uint number;

			part = null;

			while (index < text.Length && text[index] == ' ')
				index++;

			if (index >= text.Length || text[index] != '(') {
				if (index + 3 <= text.Length && text.Substring (index, 3) == "NIL") {
					index += 3;
					return true;
				}

				return false;
			}

			index++;

			if (index >= text.Length)
				return false;

			if (text[index] == '(') {
				var prefix = path.Length > 0 ? path + "." : string.Empty;
				var multipart = new BodyPartMultipart ();
				IList<BodyPart> children;

				if (!TryParse (text, ref index, prefix, out children))
					return false;

				foreach (var child in children)
					multipart.BodyParts.Add (child);

				if (!TryParse (text, ref index, true, out contentType))
					return false;

				multipart.ContentType = contentType;

				if (!TryParse (text, ref index, out disposition))
					return false;

				multipart.ContentDisposition = disposition;

				if (!TryParse (text, ref index, out array))
					return false;

				multipart.ContentLanguage = array;

				if (!TryParse (text, ref index, out location))
					return false;

				multipart.ContentLocation = location;

				part = multipart;
			} else {
				BodyPartMessage message = null;
				BodyPartText txt = null;
				BodyPartBasic basic;

				if (!TryParse (text, ref index, false, out contentType))
					return false;

				if (contentType.Matches ("message", "rfc822"))
					basic = message = new BodyPartMessage ();
				else if (contentType.Matches ("text", "*"))
					basic = txt = new BodyPartText ();
				else
					basic = new BodyPartBasic ();

				basic.ContentType = contentType;

				if (!TryParse (text, ref index, out nstring))
					return false;

				basic.ContentId = nstring;

				if (!TryParse (text, ref index, out nstring))
					return false;

				basic.ContentDescription = nstring;

				if (!TryParse (text, ref index, out nstring))
					return false;

				basic.ContentTransferEncoding = nstring;

				if (!TryParse (text, ref index, out number))
					return false;

				basic.Octets = number;

				if (!TryParse (text, ref index, out nstring))
					return false;

				basic.ContentMd5 = nstring;

				if (!TryParse (text, ref index, out disposition))
					return false;

				basic.ContentDisposition = disposition;

				if (!TryParse (text, ref index, out array))
					return false;

				basic.ContentLanguage = array;

				if (!TryParse (text, ref index, out location))
					return false;

				basic.ContentLocation = location;

				if (message != null) {
					Envelope envelope;
					BodyPart body;

					if (!Envelope.TryParse (text, ref index, out envelope))
						return false;

					message.Envelope = envelope;

					if (!TryParse (text, ref index, path, out body))
						return false;

					message.Body = body;

					if (!TryParse (text, ref index, out number))
						return false;

					message.Lines = number;
				} else if (txt != null) {
					if (!TryParse (text, ref index, out number))
						return false;

					txt.Lines = number;
				}

				part = basic;
			}

			part.PartSpecifier = path;

			if (index >= text.Length || text[index] != ')')
				return false;

			index++;

			return true;
		}
Example #10
0
		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);
			}
		}
Example #11
0
		async void RenderMultipartRelated (IMailFolder folder, UniqueId uid, BodyPartMultipart bodyPart)
		{
			// download the entire multipart/related for simplicity since we'll probably end up needing all of the image attachments anyway...
			var related = await folder.GetBodyPartAsync (uid, bodyPart) as MultipartRelated;

			RenderMultipartRelated (related);
		}