jstedfast / MailKit

A cross-platform .NET library for IMAP, POP3, and SMTP.

Home Page:http://www.mimekit.net

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question: How to edit attachments in the IEnumerable<MimeEntity> Attachments property of message object?

xx7Ahmed7xx opened this issue · comments

I have read message as following:

var inbox = client.Inbox;
var messages = inbox.Fetch(inbox.Count - 5, inbox.Count - 1, MessageSummaryItems.UniqueId | MessageSummaryItems.BodyStructure);

Now how could I replace attachment(s) if found, with different ones? I have already implemented saving the new messages..

I would recommend either using the MimeIterator or MimeVisitor to traverse the MIME structure of the message so that you always know what the parent MIME (multipart and/or message-part) part is which would allow you to replace it.

Personally, for your task, I'd recommend MimeIterator as being the simplest.

using (var iter = new MimeIterator (message)) {
    while (iter.MoveNext ()) {
        if (iter.Parent is Multipart multipart && iter.Current is MimePart part && part.IsAttachment) {
            int index = multipart.IndexOf(part);

            multipart[index] = replacementPart;
        }
    }
}

How can I create a new MimeEntity from a file to put as the replacementPart ?
@jstedfast

This is the code I have come up with so far, i cannot quite understand how to remove the attachments from Attachments object because it is readonly...

var newAttachments = Directory.EnumerateFiles(attachsFolder);
foreach (var attachment in newAttachments)
{
    using (var iter = new MimeIterator(message))
    {
        while (iter.MoveNext())
        {
            if (iter.Parent is Multipart multipart && iter.Current is MimePart part && part.IsAttachment)
            {
                multipart[multipart.IndexOf(part)] = new MimePart()
                {
                    Content = new MimeContent(File.OpenRead(attachment)),
                    ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
                    ContentTransferEncoding = ContentEncoding.Base64,
                    FileName = Path.GetFileName(attachment),
                };
            }
        }
    }
}
foreach (var attachment in message.Attachments)
{
    newMessage.Attachments.Append(attachment);
}

I don't know if this is the right approach, i will be testing for correctness anyway.
New Edit: the if condition is never hit, even though first iter seems to have one of it's current's as the object which contains the file that i want to replace...

This is the code I have come up with so far, i cannot quite understand how to remove the attachments from Attachments object because it is readonly...

You can't. The Attachments property walks the entire MIME tree of the message and yield return's the MimeParts that are attachments. There is no actual List<MimePart> data structure anywhere that references the message's Attachments. That's why you can't set, Add() or Remove() anything from the MimeMessage.Attachments property. Pretend that property doesn't exist for what you are trying to do because it will not help you.

Next, I recommend reading up on the MIME specifications that you can find here: https://github.com/jstedfast/MimeKit/blob/master/RFCs.md - specifically, read RFC2045. The rest are about encodings which aren't important (MimeKit handles all that for you). While MimeKit handles the parsing of messages according to the rules in RFC2045, you NEED to understand the MIME structure so that you can do what you are trying to accomplish.

MIME is NOT a list of attachments and a body. It is a tree structure and you need to understand how it is structured for your task.

Then you need to make sure to duplicate that structure in the newMessage that you are creating.

The next thing you'll need to remember is that when the message goes out of scope, it will eventually get disposed (unless it gets Dispose()'d directly by you), at which point, all of the child MimeParts will also get disposed. You'll need to make sure to account for that if you are going to re-use any parts from message in newMessage.

I have some knowledge about the topic in terms of networking and data transfering...
But right now I'm just trying to solve the issue, and yes. I know it is not a simple data structure but it is just a simple task and go, not some big project that i'm making a full-featured solution for it...
So as of programming, I don't know what exactly to do with the Current or Parent, as explained i could find the attachment that i need in the Current, but don't know how to access this one and edit it with the code provided ....
Thanks for clarifying!
Edit: to explain more, I mean using VS 2022 debugger i could inspect the object and find what i need.
image

The next loop iteration will descend into its children.

In other words: iter.MoveNext() will move to the child.

Your if-statement looks wrong: if (iter.Current is Multipart multipart && iter.Current is MimePart part && part.IsAttachment)

See anything wrong with that statement? :-)

You need if (iter.Parent is Multipart multipart && iter.Current is MimePart part && part.IsAttachment)

Thank you, although it looks identical to your last response but this one actually went through the condition and parsed the needed content.
Then my last code worked and did the job and now new files with new names and new content appear in the new message ;)
image
image

Do you think this is the best approach to put all files in the directory into attachments? or the code needs to be changed.?

The code needs to be changed because you have have more files in the directory than the original message has attachments.

How can I achieve it so that whatever files I have in the folder, get loaded as an attachment?

In general, the top-level Multipart of the message is a multipart/mixed which contains all of the attachments. So just add them all to that part.

If it's not a multipart/mixed, create a new multipart/mixed, add the existing message.Body to that, then add all the remaining attachments.

This is why I keep urging you to study up on MIME and the common MIME structures.

Thank you for the effort, I know I have been asking alot of questions, but this will help in future incase someone wants something related to the work i'm trying to do 😂👀