diff --git a/MimeKit/MimeMessage.cs b/MimeKit/MimeMessage.cs
index ed42145fde..3b1ff630c7 100644
--- a/MimeKit/MimeMessage.cs
+++ b/MimeKit/MimeMessage.cs
@@ -1023,57 +1023,6 @@ public MimeEntity Body {
get; set;
}
- static bool TryGetMultipartBody (Multipart multipart, TextFormat format, out string body)
- {
- if (multipart is MultipartAlternative alternative) {
- body = alternative.GetTextBody (format);
- return body != null;
- }
-
- if (multipart is MultipartRelated related) {
- // Note: If the multipart/related root document is HTML, then this is the droid we are looking for.
- var root = related.Root;
-
- if (root is TextPart text) {
- body = text.IsFormat (format) ? text.Text : null;
- return body != null;
- }
-
- // maybe the root is another multipart (like multipart/alternative)?
- if (root is Multipart multi)
- return TryGetMultipartBody (multi, format, out body);
- } else {
- // Note: This is probably a multipart/mixed... and if not, we can still treat it like it is.
- for (int i = 0; i < multipart.Count; i++) {
- // descend into nested multiparts, if there are any...
- if (multipart[i] is Multipart multi) {
- if (TryGetMultipartBody (multi, format, out body))
- return true;
-
- // The text body should never come after a multipart.
- break;
- }
-
- // Look for the first non-attachment text part (realistically, the body text will
- // precede any attachments, but I'm not sure we can rely on that assumption).
- if (multipart[i] is TextPart text && !text.IsAttachment) {
- if (text.IsFormat (format)) {
- body = MultipartAlternative.GetText (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;
- }
- }
- }
-
- body = null;
-
- return false;
- }
-
///
/// Get the text body of the message if it exists.
///
@@ -1108,11 +1057,10 @@ public string HtmlBody {
public string GetTextBody (TextFormat format)
{
if (Body is Multipart multipart) {
- if (TryGetMultipartBody (multipart, format, out var text))
- return text;
- } else {
- if (Body is TextPart body && body.IsFormat (format) && !body.IsAttachment)
- return body.Text;
+ if (multipart.TryGetValue (format, out var body))
+ return MultipartAlternative.GetText (body);
+ } else if (Body is TextPart text && text.IsFormat (format) && !text.IsAttachment) {
+ return MultipartAlternative.GetText (text);
}
return null;
diff --git a/MimeKit/Multipart.cs b/MimeKit/Multipart.cs
index f90935e700..7bd0fc9019 100644
--- a/MimeKit/Multipart.cs
+++ b/MimeKit/Multipart.cs
@@ -34,6 +34,7 @@
using System.Security.Cryptography;
using MimeKit.IO;
+using MimeKit.Text;
using MimeKit.Utils;
using MimeKit.Encodings;
@@ -343,6 +344,51 @@ public override void Accept (MimeVisitor visitor)
visitor.VisitMultipart (this);
}
+ ///
+ /// Get the preferred message body if it exists.
+ ///
+ ///
+ /// Gets the preferred message body if it exists.
+ ///
+ /// The preferred text format.
+ /// The MIME part containing the message body in the preferred text format.
+ /// true if the body part is found; otherwise, false.
+ ///
+ /// The has been disposed.
+ ///
+ public virtual bool TryGetValue (TextFormat format, out TextPart body)
+ {
+ CheckDisposed ();
+
+ for (int i = 0; i < Count; i++) {
+ // Descend into nested multiparts if there are any...
+ if (this[i] is Multipart multipart) {
+ if (multipart.TryGetValue (format, out body))
+ return true;
+
+ // The text body should never come after a multipart.
+ break;
+ }
+
+ // Look for the first non-attachment text part (realistically, the body text will
+ // precede any attachments, but I'm not sure we can rely on that assumption).
+ if (this[i] is TextPart text && !text.IsAttachment) {
+ if (text.IsFormat (format)) {
+ 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;
+ }
+ }
+
+ body = null;
+
+ return false;
+ }
+
internal static string FoldPreambleOrEpilogue (FormatOptions options, string text, bool isEpilogue)
{
var builder = new ValueStringBuilder (256);
diff --git a/MimeKit/MultipartAlternative.cs b/MimeKit/MultipartAlternative.cs
index e8f7a69c73..744dacb954 100644
--- a/MimeKit/MultipartAlternative.cs
+++ b/MimeKit/MultipartAlternative.cs
@@ -170,34 +170,43 @@ internal static string GetText (TextPart text)
///
public string GetTextBody (TextFormat format)
{
- CheckDisposed ();
+ if (TryGetValue (format, out var body))
+ return GetText (body);
- // walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful
- for (int i = Count - 1; i >= 0; i--) {
- if (this[i] is MultipartAlternative alternative) {
- // Note: nested multipart/alternative parts make no sense... yet here we are.
- return alternative.GetTextBody (format);
- }
-
- TextPart text;
+ return null;
+ }
- if (this[i] is MultipartRelated related) {
- var root = related.Root;
+ ///
+ /// Get the preferred message body if it exists.
+ ///
+ ///
+ /// Gets the preferred message body if it exists.
+ ///
+ /// The preferred text format.
+ /// The MIME part containing the message body in the preferred text format.
+ /// true if the body part is found; otherwise, false.
+ ///
+ /// The has been disposed.
+ ///
+ public override bool TryGetValue (TextFormat format, out TextPart body)
+ {
+ CheckDisposed ();
- alternative = root as MultipartAlternative;
- if (alternative != null)
- return alternative.GetTextBody (format);
+ // Walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful.
+ for (int i = Count - 1; i >= 0; i--) {
+ // Descend into child multiparts.
+ if (this[i] is Multipart multipart && multipart.TryGetValue (format, out body))
+ return true;
- text = root as TextPart;
- } else {
- text = this[i] as TextPart;
+ if (this[i] is TextPart text && text.IsFormat (format)) {
+ body = text;
+ return true;
}
-
- if (text != null && text.IsFormat (format))
- return GetText (text);
}
- return null;
+ body = null;
+
+ return false;
}
}
}
diff --git a/MimeKit/MultipartRelated.cs b/MimeKit/MultipartRelated.cs
index e099dd471e..8584a17a41 100644
--- a/MimeKit/MultipartRelated.cs
+++ b/MimeKit/MultipartRelated.cs
@@ -28,6 +28,7 @@
using System.IO;
using System.Linq;
+using MimeKit.Text;
using MimeKit.Utils;
namespace MimeKit {
@@ -214,6 +215,39 @@ public override void Accept (MimeVisitor visitor)
visitor.VisitMultipartRelated (this);
}
+ ///
+ /// Get the preferred message body if it exists.
+ ///
+ ///
+ /// Gets the preferred message body if it exists.
+ ///
+ /// The preferred text format.
+ /// The MIME part containing the message body in the preferred text format.
+ /// true if the body part is found; otherwise, false.
+ ///
+ /// The has been disposed.
+ ///
+ public override bool TryGetValue (TextFormat format, out TextPart body)
+ {
+ CheckDisposed ();
+
+ // Note: If the multipart/related root document is HTML, then this is the droid we are looking for.
+ var root = Root;
+
+ if (root is TextPart text) {
+ body = text.IsFormat (format) ? text : null;
+ return body != null;
+ }
+
+ // The root may be a multipart such as a multipart/alternative.
+ if (root is Multipart multipart)
+ return multipart.TryGetValue (format, out body);
+
+ body = null;
+
+ return false;
+ }
+
///
/// Check if the contains a part matching the specified URI.
///