Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identifing Multi-Boundary Messages with Embedded Messages #148

Closed
LeeBear35 opened this issue Jun 18, 2015 · 12 comments
Closed

Identifing Multi-Boundary Messages with Embedded Messages #148

LeeBear35 opened this issue Jun 18, 2015 · 12 comments
Labels
bug Something isn't working

Comments

@LeeBear35
Copy link

Jeffery,
I have some multi-part, multi-boundary messages that have Embedded Messages, but the BodyParts array does not give any means to identify the message in the proper content-type, instead it returns the embedded messages parts.

Here is a sample email expanded upon from the RFC1341 sample to include an embedded email, it does parse in Outlook correctly:

From: Nathaniel Borenstein nsb@bellcore.com
To: Ned Freed ned@innosoft.com
Subject: Sample message
MIME-Version: 1.0
Content-type: multipart/mixed; boundary="simple boundary"

This is the preamble. It is to be ignored, though it
is a handy place for mail composers to include an
explanatory note to non-MIME compliant readers.
--simple boundary

This is implicitly typed plain ASCII text.
It does NOT end with a linebreak.
--simple boundary
Content-type: text/plain; charset=us-ascii

This is explicitly typed plain ASCII text.
It DOES end with a linebreak.

--simple boundary
Content-Type: message/rfc822

From: Jerry Mouse I@GetTheCat.com
To: Tom Cat I@AmTheCat.com
Subject: Wiskers
MIME-Version: 1.0
Content-type: multipart/mixed; boundary="TomAndJerry boundary"

This is the preamble. It is to be ignored, though it
is a handy place for mail composers to include an
explanatory note to non-MIME compliant readers.
--TomAndJerry boundary

Implicite Message
--TomAndJerry boundary
Content-type: text/plain; charset=us-ascii

Explicite Message

--TomAndJerry boundary--
--simple boundary--
This is the epilogue. It is also to be ignored.

[EOM]

Enumerating the parts returns:
BodyParts:
[MimeKit.TextPart] {This is implicitly typed plain ASCII text. It does NOT end with a line break. }
[MimeKit.TextPart] {Content-type: text/plain; charset=us-ascii This is explicitly typed plain ASCII text. It DOES end with a line break. }
[MimeKit.TextPart] {Implicit Message}
[MimeKit.TextPart] {Content-type: text/plain; charset=us-ascii Explicit Message}

And the text for the message is set correctly.
TextBody: {This is implicitly typed plain ASCII text. It does NOT end with a line break. }

However I think that the body parts should be:
[MimeKit.TextPart] {This is implicitly typed plain ASCII text. It does NOT end with a line break. }
[MimeKit.TextPart] {Content-type: text/plain; charset=us-ascii This is explicitly typed plain ASCII text. It DOES end with a line break. }
[MimeKit.MessagePart] {Wiskers}

@jstedfast
Copy link
Owner

I would recommend not using the BodyParts enumeration, it was added to placate people who didn't know anything about MIME and think messages are just a flat list of parts.

@jstedfast
Copy link
Owner

I would have to break API to change this behavior to do what you want. MimeMessage.BodyParts is an IEnumerable of MimeParts, but MessagePart does not subclass MimePart, it subclasses MimeEntity because its content is not a stream, but rather a MimeMessage.

@jstedfast jstedfast added the bug Something isn't working label Jun 18, 2015
@LeeBear35
Copy link
Author

What are you suggesting in place of enumerating BodyParts? What I am looking to do is get a list of the named, or in this case unnamed attachments to be able to view the email in the same fashion that MS Outlook does.

@jstedfast
Copy link
Owner

I would recommend taking a look at the MessageReader sample in the repo and starting with that.

You might be able to make it cleaner by using a MimeVisitor (I'll probably be rewriting that sample to use the Visitor API one of these days) and just keeping track of the mime parts that you want to show as attachments as you traverse over the MIME structure.

The following 2 documentation urls might be helpful (in addition to the sample app I mentioned):

http://www.mimekit.net/docs/html/WorkingWithMessages.htm
http://www.mimekit.net/docs/html/FrequentlyAskedQuestions.htm#MessageBody

For the FAQ, I realize you aren't looking to get the message body, you want the attachments, but in order to correctly collect the attachments, you have to understand what makes up the message body (everything that is not the body is an attachment).

Hope that helps.

@LeeBear35
Copy link
Author

Thank, I will look into this, I would think using the MimeIterator would be the way to go.
I did not realize the limitation to using the BodyParts collection. I might have been able to make it work but there were no entries with Content-Type = message/rfc822

@jstedfast
Copy link
Owner

Right, that's because MessagePart could not be returned by BodyParts or Attachments because it is not a subclass of MimePart.

I've changed BodyParts to stop traversing into MessagePart and instead return the MessagePart, but this breaks API and hopefully won't break anyone's code too much.

@LeeBear35
Copy link
Author

Wow, I would not have expected that.

@LeeBear35
Copy link
Author

Since MessagePart is not a MimeEntity, why not return a MimeEntity and treat it like an unnamed attachment. Then the calling application if it sees that the MimeEntity has a content-type of rfc822/message it can create a MessagePart from the MimeEntity.

It would be cleaner I would think.

@jstedfast
Copy link
Owner

MessagePart is a MimeEntity, it's not a MimePart.

The reason that MessagePart is not a MimePart is because it allows the MimeParser to parse the MimeMessage content of message/rfc822 parts as it parses the container message, thus eliminating duplicate passes over the data.

@Sune1337
Copy link

I think this change broke some code for me.
I used to do:

foreach (var attachment in message.Attachments.Where(a => a.IsAttachment))
{
    var decodedStream = attachment.ContentObject.Open()
}

After this change the ContentObject doesn't exist.
I changed my code into:

foreach (var mimeEntity in message.Attachments.Where(a => a.IsAttachment))
{
    var mimePart = mimeEntity as MimePart;
    if (mimePart == null)
    {
        continue;
    }

    var decodedStream = mimePart.ContentObject.Open();
}

I'm looking to iterate files that are attached to an email; not files that are attached to an email that is attached to another email; i hope that made sense :)

The original code i used seems to follow the instructions on
http://www.mimekit.net/docs/html/WorkingWithMessages.htm#EnumeratingBodyParts
and
http://www.mimekit.net/docs/html/WorkingWithMessages.htm#DecodingContent

For my purpose, the above code works; that is, if i attach a PDF file to an email and send it to an email account i poll using ImapClient (from MailKit) i can read this PDF correctly.

My question is; is the above code a "correct" way of saving attachments from a MimeMessage? Is there another way of iterating attachments without the need to cast them to MimePart?

@jstedfast
Copy link
Owner

You could do:

foreach (var attachment in message.Attachments..OfType<MimePart> ().Where(a => a.IsAttachment))

I'll update the docs (I was going to regenerate them tonight anyway).

The change I made means that you'll no longer get attachments to attached messages (which sounds like something you want).

@Sune1337
Copy link

That's a little shorter and prettier, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants