Find the position of an attachment using MimeKit

69 Views Asked by At

I have a MIME email message that I am parsing using MimeKit. I am able to load in the message and get all the attachment entities like so:

MimeMessage mimeMessage = MimeMessage.Load(decodedDataStream);

int attachmentCount = 0;
foreach (MimeEntity attachment in mimeMessage.Attachments)
{
    attachmentCount++;

    string fileName = "-a" + attachmentCount;

    using (FileStream stream = File.Create(Path.Combine(message.Id + fileName)))
    {
            if (attachment is MessagePart)
            {
                    var part = (MessagePart)attachment;

                    part.Message.WriteTo(stream);
            }
            else
            {
                    var part = (MimePart)attachment;

                    part.Content.WriteTo(stream);
            }
    }
}

My question is: Is there any way to get the file offset / position of a particular attachment.

My requirement is to get the location of each attachment in the MIME file. Is there any way I can achieve this? I can see that LumiSoft has a feature to get the PositionSectionStart / PositionBodyStart.

LumiSoft

Does MimeKit have any way of doing this, or is there some other way to get the file offset for an attachment?

1

There are 1 best solutions below

1
jstedfast On
class MimeOffsets
{
    public string MimeType { get; set; }

    public long? MboxMarkerOffset { get; set; }

    public int LineNumber { get; set; }

    public long BeginOffset { get; set; }

    public long HeadersEndOffset { get; set; }

    public long EndOffset { get; set; }

    public MimeOffsets Message { get; set; }

    public List<MimeOffsets> Children { get; set; }

    public long Octets { get; set; }

    public int? Lines { get; set; }
}

public static void MimeOffsetsExample (string fileName)
{
    using (var stream = fileName.OpenRead (fileName)) {
        var messages = new Dictionary<MimeMessage, MimeOffsets> ();
        var entities = new Dictionary<MimeEntity, MimeOffsets> ();
        MimeOffsets messageOffsets = null;

        var parser = new MimeParser (stream, MimeFormat.Entity);

        // Connect a handler to track MimeMessage begin offsets
        parser.MimeMessageBegin += delegate (sender, args) {
            var parser = (MimeParser) sender;

            // Create a new MimeOffsets for this message.
            var offsets = new MimeOffsets {
                BeginOffset = args.BeginOffset,
                LineNumber = args.LineNumber
            };

            if (args.Parent != null) {
                // If we get here, then it means that the MimeMessage is part of
                // a message/rfc822 "attachment".
                var parentOffsets = entities[args.Parent];
                parentOffsets.Message = offsets;
            } else {
                // Otherwise, this is the top-level MimeMessage.
                offsets.MboxMarkerOffset = parser.MboxMarkerOffset;
                messageOffsets = offsets;
            }

            messages.Add (args.Message, offsets);
        };

        // Connect a handler to track MimeMessage end offsets
        parser.MimeMessageEnd += delegate (sender, args) {
            // Our MimeMessageBegin event handler already created a MimeOffsets for
            // this message. Use the `messages` dictionary to retrieve it.
            var offsets = messages[args.Message];

            // Track the size of the MimeMessage in octets (aka bytes), the offset
            // for the end of the header block, and the end of the message.
            offsets.Octets = args.EndOffset - args.HeadersEndOffset;
            offsets.HeadersEndOffset = args.HeadersEndOffset;
            offsets.EndOffset = args.EndOffset;
        };

        // Connect a handler to track MimeEntity begin offsets
        parser.MimeEntityBegin += delegate (sender, args) {
            // Create a new MimeOffsets for this MIME entity (which could be a MimePart, MessagePart, or Multipart).
            var offsets = new MimeOffsets {
                MimeType = args.Entity.ContentType.MimeType,
                BeginOffset = args.BeginOffset,
                LineNumber = args.LineNumber
            };

            if (args.Parent != null && entities.TryGetValue (args.Parent, out var parentOffsets)) {
                parentOffsets.Children ??= new List<MimeOffsets> ();
                parentOffsets.Children.Add (offsets);
            }

            entities.Add (args.Entity, offsets);
        };

        // Connect a handler to track MimeEntity end offsets
        parser.MimeEntityEnd += delegate (sender, args) {
            // Our MimeEntityBegin event handler already created a MimeOffsets for
            // this entity. Use the `entities` dictionary to retrieve it.
            var offsets = entities[args.Entity];

            // Track the size of the MimeEntity in octets (aka bytes), the offset
            // for the end of the header block, the end of the entity, and the
            // line count.
            offsets.Octets = args.EndOffset - args.HeadersEndOffset;
            offsets.HeadersEndOffset = args.HeadersEndOffset;
            offsets.EndOffset = args.EndOffset;
            offsets.Lines = args.Lines;
        };

        // Parse the message (which will emit the events as appropriate).
        var message = parser.ParseMessage ();

        // Now we can find out the offsets of each attachment:
        foreach (var attachment in message.Attachments) {
            var offsets = entities[attachment];

            Console.WriteLine ($"The offsets for the {bodyPart.ContentType} attachment are:");
            Console.WriteLine ($"  - LineNumber: {offsets.LineNumber}")
            Console.WriteLine ($"  - BeginOffset: {offsets.BeginOffset}");
            Console.WriteLine ($"  - HeadersEndOffset: {offsets.HeadersEndOffset}"); // Note: This is also where the *content* begins.
            Console.WriteLine ($"  - EndOffset: {offsets.BeginOffset}");
            Console.WriteLine ($"  - Octets: {offsets.Octets}");
            Console.WriteLine ($"  - Lines: {offsets.Lines}");
        }
    }
}