CryptographicMimeVisitor Class

Represents a visitor for cryptographic MIME trees.

Definition

Namespace: MimeKit.Cryptography
Assembly: MimeKit.Cryptography (in MimeKit.Cryptography.dll) Version: 5.0.0
C#
public abstract class CryptographicMimeVisitor : MimeVisitor
Inheritance
Object    MimeVisitor    CryptographicMimeVisitor

Remarks

This class is designed to be inherited to create more specialized classes whose functionality requires traversing, examining or copying a MIME tree.

Example

C#
                    /// <summary>
/// Visits a MimeMessage and generates HTML suitable to be rendered by a browser control.
/// </summary>
class HtmlPreviewVisitor : MimeVisitor
{
    List<MultipartRelated> stack = new List<MultipartRelated> ();
    List<MimeEntity> attachments = new List<MimeEntity> ();
    string body;

    /// <summary>
    /// Creates a new HtmlPreviewVisitor.
    /// </summary>
    public HtmlPreviewVisitor ()
    {
    }

    /// <summary>
    /// The list of attachments that were in the MimeMessage.
    /// </summary>
    public IList<MimeEntity> Attachments {
        get { return attachments; }
    }

    /// <summary>
    /// The HTML string that can be set on the BrowserControl.
    /// </summary>
    public string HtmlBody {
        get { return body ?? string.Empty; }
    }

    protected override void VisitMultipartAlternative (MultipartAlternative alternative)
    {
        // walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful
        for (int i = alternative.Count - 1; i >= 0 && body == null; i--)
            alternative[i].Accept (this);
    }

    protected override void VisitMultipartRelated (MultipartRelated related)
    {
        var root = related.Root;

        // push this multipart/related onto our stack
        stack.Add (related);

        // visit the root document
        root.Accept (this);

        // pop this multipart/related off our stack
        stack.RemoveAt (stack.Count - 1);
    }

    // look up the image based on the img src url within our multipart/related stack
    bool TryGetImage (string url, out MimePart image)
    {
        UriKind kind;
        int index;
        Uri uri;

        if (Uri.IsWellFormedUriString (url, UriKind.Absolute))
            kind = UriKind.Absolute;
        else if (Uri.IsWellFormedUriString (url, UriKind.Relative))
            kind = UriKind.Relative;
        else
            kind = UriKind.RelativeOrAbsolute;

        try {
            uri = new Uri (url, kind);
        } catch {
            image = null;
            return false;
        }

        for (int i = stack.Count - 1; i >= 0; i--) {
            if ((index = stack[i].IndexOf (uri)) == -1)
                continue;

            image = stack[i][index] as MimePart;
            return image != null;
        }

        image = null;

        return false;
    }

    /// <summary>
    /// Get a data: URI for the image attachment.
    /// </summary>
    /// <remarks>
    /// Encodes the image attachment into a string suitable for setting as a src= attribute value in
    /// an img tag.
    /// </remarks>
    /// <returns>The data: URI.</returns>
    /// <param name="image">The image attachment.</param>
    string GetDataUri (MimePart image)
    {
        using (var memory = new MemoryStream ()) {
            image.Content.DecodeTo (memory);
            var buffer = memory.GetBuffer ();
            var length = (int) memory.Length;
            var base64 = Convert.ToBase64String (buffer, 0, length);

            return string.Format ("data:{0};base64,{1}", image.ContentType.MimeType, base64);
        }
    }

    // Replaces <img src=...> urls that refer to images embedded within the message with
    // "file://" urls that the browser control will actually be able to load.
    void HtmlTagCallback (HtmlTagContext ctx, HtmlWriter htmlWriter)
    {
        if (ctx.TagId == HtmlTagId.Meta && !ctx.IsEndTag) {
            bool isContentType = false;

            ctx.WriteTag (htmlWriter, false);

            // replace charsets with "utf-8" since our output will be in utf-8 (and not whatever the original charset was)
            foreach (var attribute in ctx.Attributes) {
                if (attribute.Id == HtmlAttributeId.Charset) {
                    htmlWriter.WriteAttributeName (attribute.Name);
                    htmlWriter.WriteAttributeValue ("utf-8");
                } else if (isContentType && attribute.Id == HtmlAttributeId.Content) {
                    htmlWriter.WriteAttributeName (attribute.Name);
                    htmlWriter.WriteAttributeValue ("text/html; charset=utf-8");
                } else {
                    if (attribute.Id == HtmlAttributeId.HttpEquiv && attribute.Value != null
                        && attribute.Value.Equals ("Content-Type", StringComparison.OrdinalIgnoreCase))
                        isContentType = true;

                    htmlWriter.WriteAttribute (attribute);
                }
            }
        } else if (ctx.TagId == HtmlTagId.Image && !ctx.IsEndTag && stack.Count > 0) {
            ctx.WriteTag (htmlWriter, false);

            // replace the src attribute with a "data:" URL
            foreach (var attribute in ctx.Attributes) {
                if (attribute.Id == HtmlAttributeId.Src) {
                    if (!TryGetImage (attribute.Value, out var image)) {
                        htmlWriter.WriteAttribute (attribute);
                        continue;
                    }

                    var dataUri = GetDataUri (image);

                    htmlWriter.WriteAttributeName (attribute.Name);
                    htmlWriter.WriteAttributeValue (dataUri);
                } else {
                    htmlWriter.WriteAttribute (attribute);
                }
            }
        } else {
            // pass the tag through to the output
            ctx.WriteTag (htmlWriter, true);
        }
    }

    protected override void VisitTextPart (TextPart entity)
    {
        TextConverter converter;

        if (body != null) {
            // since we've already found the body, treat this as an attachment
            attachments.Add (entity);
            return;
        }

        if (entity.IsHtml) {
            converter = new HtmlToHtml {
                HtmlTagCallback = HtmlTagCallback
            };
        } else if (entity.IsFlowed) {
            var flowed = new FlowedToHtml ();
            string delsp;

            if (entity.ContentType.Parameters.TryGetValue ("delsp", out delsp))
                flowed.DeleteSpace = delsp.Equals ("yes", StringComparison.OrdinalIgnoreCase);

            converter = flowed;
        } else {
            converter = new TextToHtml ();
        }

        body = converter.Convert (entity.Text);
    }

    protected override void VisitTnefPart (TnefPart entity)
    {
        // extract any attachments in the MS-TNEF part
        using (var tnef = entity.LoadTnefMessage ()) {
            // the converted message (and its attachments) outlives the TnefMessage
            var result = tnef.ConvertToMime ();

            attachments.AddRange (result.Message.Attachments);
        }
    }

    protected override void VisitMessagePart (MessagePart entity)
    {
        // treat message/rfc822 parts as attachments
        attachments.Add (entity);
    }

    protected override void VisitMimePart (MimePart entity)
    {
        // realistically, if we've gotten this far, then we can treat this as an attachment
        // even if the IsAttachment property is false.
        attachments.Add (entity);
    }
}
                  

Constructors

CryptographicMimeVisitor Initialize a new instance of the CryptographicMimeVisitor class.

Methods

Equals Determines whether the specified object is equal to the current object.
(Inherited from Object)
Finalize Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.
(Inherited from Object)
GetHashCode Serves as the default hash function.
(Inherited from Object)
GetType Gets the Type of the current instance.
(Inherited from Object)
MemberwiseClone Creates a shallow copy of the current Object.
(Inherited from Object)
ToString Returns a string that represents the current object.
(Inherited from Object)
Visit(MimeEntity) Dispatches the entity to one of the more specialized visit methods in this class.
(Inherited from MimeVisitor)
Visit(MimeMessage) Dispatches the message to one of the more specialized visit methods in this class.
(Inherited from MimeVisitor)
VisitApplicationPgpEncrypted Visit the application/pgp-encrypted MIME entity.
VisitApplicationPgpSignature Visit the application/pgp-signature MIME entity.
VisitApplicationPkcs7Mime Visit the application/pkcs7-mime MIME entity.
VisitApplicationPkcs7Signature Visit the application/pkcs7-signature MIME entity.
VisitBody Visit the body of the message.
(Inherited from MimeVisitor)
VisitChildren Visit the children of a Multipart.
(Inherited from MimeVisitor)
VisitMessage Visit the message contained within a message/rfc822 or message/news MIME entity.
(Inherited from MimeVisitor)
VisitMessageDeliveryStatus Visit the message/delivery-status MIME entity.
(Inherited from MimeVisitor)
VisitMessageDispositionNotification Visit the message/disposition-notification MIME entity.
(Inherited from MimeVisitor)
VisitMessageFeedbackReport Visit the message/feedback-report MIME entity.
(Inherited from MimeVisitor)
VisitMessagePart Visit the message/rfc822 or message/news MIME entity.
(Inherited from MimeVisitor)
VisitMessagePartial Visit the message/partial MIME entity.
(Inherited from MimeVisitor)
VisitMimeEntity Visit the abstract MIME entity.
(Inherited from MimeVisitor)
VisitMimeMessage Visit the MIME message.
(Inherited from MimeVisitor)
VisitMimePart Visit the abstract MIME part entity.
(Inherited from MimeVisitor)
VisitMultipart Visit the abstract multipart MIME entity.
(Inherited from MimeVisitor)
VisitMultipartAlternative Visit the multipart/alternative MIME entity.
(Inherited from MimeVisitor)
VisitMultipartEncrypted Visit the multipart/encrypted MIME entity.
VisitMultipartRelated Visit the multipart/related MIME entity.
(Inherited from MimeVisitor)
VisitMultipartReport Visit the multipart/report MIME entity.
(Inherited from MimeVisitor)
VisitMultipartSigned Visit the multipart/signed MIME entity.
VisitTextPart Visit the text-based MIME part entity.
(Inherited from MimeVisitor)
VisitTextRfc822Headers Visit the text/rfc822-headers MIME entity.
(Inherited from MimeVisitor)
VisitTnefPart Visit the Microsoft TNEF MIME part entity.
(Inherited from MimeVisitor)

See Also