Page 1 of 1

multipart/mixed

Posted: Fri May 16, 2008 6:30 pm
by gmorehoudh
Does Swift not handle multipart mixed? I'm talking about a message with a structure like this:

MULTIPART/MIXED :: 7BIT
1 -- 0K :: TEXT/PLAIN :: 7BIT
2 -- 180K :: APPLICATION/PDF :: BASE64 :: A Test PDF.pdf
3 -- 0K :: TEXT/PLAIN :: 7BIT

Basically, the PDF is inline between the content of the two text parts, but when I add a text part, then the pdf, then the next text part, Swift seems to want to wrap it as a multipart/mixed within a multipart/alternative. (This could very well just be my bad coding, I've been banging my head on it for 3 days). Right now it still shows up correctly in Apple Mail, but since I'm trying to forward messages in largely the same form I got them in, I'd prefer that they not be wrapped as multipart/alternative when they weren't sent that way.

Also it's losing the filename right now, but that's my own fault I'm sure.

Here, with content replaced with [...], is what I get sent to me out of Swift:

Code: Select all

 
To: [...]
From: [...]
Reply-To: [...]
Subject: fff
Date: Fri, 16 May 2008 16:25:39 -0700
X-LibVersion: 3.3.2
MIME-Version: 1.0
Content-Type: multipart/alternative;
 boundary="_=_swift-447790849482e17f3735cc2.49469771_=_"
Content-Transfer-Encoding: 7bit
Message-ID: <20080516232539.21318.300265094.swift@admin2.[...].com>
 
This is a message in multipart MIME format.  Your mail client should not
be displaying this. Consider upgrading your mail client to view this
message correctly.
--_=_swift-447790849482e17f3735cc2.49469771_=_
Content-Type: multipart/mixed; boundary="_=_swift-152836247482e17f3737820.33237384_=_"
Content-Transfer-Encoding: 7bit
 
 
--_=_swift-152836247482e17f3737820.33237384_=_
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 7bit
 
Testing.
 
##REMOVELINK##
 
 
--_=_swift-152836247482e17f3737820.33237384_=_
Content-Type: application/pdf; charset=utf-8
Content-Transfer-Encoding: base64
 
[...]
 
--_=_swift-152836247482e17f3737820.33237384_=_
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 7bit
 
 
 
 
sdfsddfsf
 
 
 
--_=_swift-152836247482e17f3737820.33237384_=_--
 
--_=_swift-447790849482e17f3735cc2.49469771_=_--
 

Re: multipart/mixed

Posted: Sun May 18, 2008 4:39 am
by Chris Corbyn
If you want to send like that then you will have to send everything as an Attachment type rather than using Mime parts. Multipart/mixed is attachment data:

Code: Select all

$message = new Swift_Message('Subject');
$message->attach(new Swift_Message_Attachment('Part 1 data', 'filename.txt', 'text/plain'));
$message->attach(new Swift_Message_Attachment(new Swift_File('pdf-file.pdf'), 'the-pdf.pdf', 'application/pdf'));
$message->attach(new Swift_Message_Attachment('Part 3 data', 'filename.txt', 'text/plain'));
This seems like a very unusual message structure however.

Re: multipart/mixed

Posted: Mon May 19, 2008 2:42 pm
by gmorehoudh
Let me back up and explain why I'm doing all these crazy hijinx so you understand what I'm trying to do before I respond to your reply specifically. As you might remember I was tasked with writing a mail forwarder for our PR department and came here to ask a bunch of questions. Here's how it works:

1. People send a message they want forwarded to a particular mailbox
2. They load up my script from our intranet, and it's provided with a list of addresses to forward to.
3. They click a button next to the message in the IMAP mailbox they want to forward
4. It gets sent out with Swift.

Step 4 of course is the interesting part. The first version simply got the original message body via IMAP, figured out the MIME type, content type, transfer encoding and MIME boundary, and laboriously created a Swift message that would use all these with the existing body, then sent it off. We were then informed that to comply with US law, we'd need to insert remove links for people to click to take themselves off our mailing lists and we'd like to do a custom link each time since we're already doing per-user VERP anyway -- meaning step 4 just got more complicated, since I now have to take the message and rebuild it using Swift since I can't just search and replace in a pre-bundled message due to various encodings, etc. So that's where I'm at now.

Now my response to your reply:

Are you saying multipart/mixed itself is purely an attachment type? I don't believe so. From my understanding it's used to bundle up chunks of varying mime types to be displayed in order. Here are some examples I'm testing with now. As you can see, not everything is file data at all -- from the simplest message below, there's a text part, a PDF to be displayed inline (if possible), then another text part.

From Apple Mail:

MULTIPART/MIXED :: 7BIT
1 -- 0K :: TEXT/PLAIN :: 7BIT
2 -- 180K :: APPLICATION/PDF :: BASE64 :: Workshop PR.pdf
3 -- 0K :: TEXT/PLAIN :: 7BIT

this is forwarded as

multipart/alternative
-- multipart/mixed
---- text/plain
---- application/pdf
---- text/plain

* * *

From GMail:

MULTIPART/MIXED :: 7BIT
1 -- MULTIPART/ALTERNATIVE :: 7BIT
1.1 ---- 0K :: TEXT/PLAIN :: 7BIT
1.2 ---- 0K :: TEXT/HTML :: 7BIT
1.3 ---- 1K :: TEXT/CALENDAR :: 7BIT
2 -- 2K :: APPLICATION/ICS :: BASE64 :: invite20080519T150000.ics
3 -- 49K :: IMAGE/JPEG :: BASE64 :: lolmeerkat.jpg

forwarded as

multipart/alternative
-- multipart/mixed
---- multipart/alternative
------ text/plain
------ text/html
------ text/calendar
---- application/ics
---- image/jpeg

* * *

From Apple Mail:

MULTIPART/ALTERNATIVE :: 7BIT
1 -- 0K :: TEXT/PLAIN :: QUOTED-PRINTABLE
2 -- MULTIPART/MIXED :: 7BIT
2.1 ---- 0K :: TEXT/HTML :: 7BIT
2.2 ---- 136K :: APPLICATION/OCTET-STREAM :: BASE64 :: Workshop PR.doc
2.3 ---- 0K :: TEXT/HTML :: 7BIT
2.4 ---- 180K :: APPLICATION/PDF :: BASE64 :: Workshop PR.pdf
2.5 ---- 2K :: TEXT/HTML :: QUOTED-PRINTABLE

forwarded wrapped the same way as above

* * *

So you can see, all these messages are being wrapped in an extra multipart/alternative wrapper -- still trying to determine if that's my code or yours -- but I'd like to preserve the original MIME tree if at all possible for the simple reason that the people who wrote mailers probably know a good deal more than me about how to create MIME messages that are readable in most or all other clients, and I don't want to introduce a change to the messages that will cause people to be unable to read them.

As for the other aspect of your reply, I'm not using anything having to do with any files, even for attached files. Here's some hopefully readable code that's used to build the message for forwarding out. (The $mime_tree is essentially what you see displayed in the nice formatted printouts above. It's an array of arrays, basically in a tree structure (subtrees are in $leaf['parts']).

Code: Select all

/**
 * Recurse through a mime tree and make a Swift_Message_Part of every node, including non-leaf nodes.
 * However, 
 */
function swiftify_mime_tree($mbox, $msgno, &$mime_tree) {
    // not using array_walk_recursive due to bugs, see http://bugs.php.net/bug.php?id=42850
    
    // $index must be used because $dummynode is a copy, not a reference :/
    foreach($mime_tree as $index => $dummynode) {
        if(is_array($mime_tree[$index]['parts'])) {
            // recurse immediately
            swiftify_mime_tree($mbox, $msgno, $mime_tree[$index]['parts']);
            // now that the children of the current node have been swiftified, we wrap them up in their own Swift_Message_Part
            $mime_tree[$index]['swift_part'] = new Swift_Message_Part(null, strtolower($mime_tree[$index]['mime_type']), strtolower($mime_tree[$index]['encoding']), strtolower($mime_tree[$index]['charset']));
            foreach($mime_tree[$index]['parts'] as $subpart_index => $dummynode2) {
                $mime_tree[$index]['swift_part']->addChild($mime_tree[$index]['parts'][$subpart_index]['swift_part']);
            }
        } else {
            // make a part for a leaf node.  the type we make is based on some attributes such as filename, disposition (inline or attachment), etc.
            if($mime_tree[$index]['filename']) {
                // the part has a filename, it should be attached as such
                if(isset($mime_tree[$index]['id'])) {
                    // the part has an id (usually for embedding inline in html emails), so attach it as a swift embedded file
                    $mime_tree[$index]['swift_part'] = new Swift_Message_EmbeddedFile(decode_part(imap_fetchbody($mbox, $msgno, $mime_tree[$index]['pid']), strtolower($mime_tree[$index]['encoding'])),
                                                                                      $mime_tree[$index]['filename'],
                                                                                      strtolower($mime_tree[$index]['mime_type']),
                                                                                      $mime_tree[$index]['id'],
                                                                                      strtolower($mime_tree[$index]['encoding']));
                } else {
                    // no id, just attach as an attachment with the given disposition, if any
                    $mime_tree[$index]['swift_part'] = new Swift_Message_Attachment(decode_part(imap_fetchbody($mbox, $msgno, $mime_tree[$index]['pid']), strtolower($mime_tree[$index]['encoding'])),
                                                                                    $mime_tree[$index]['filename'],
                                                                                    strtolower($mime_tree[$index]['mime_type']),
                                                                                    strtolower($mime_tree[$index]['encoding']),
                                                                                    $mime_tree[$index]['disposition']);                                                                           
                }
            } else {
                // don't do anything special, just attach the part as a normal message part
                $mime_tree[$index]['swift_part'] = new Swift_Message_Part(decode_part(imap_fetchbody($mbox, $msgno, $mime_tree[$index]['pid']), strtolower($mime_tree[$index]['encoding'])),
                                                                          strtolower($mime_tree[$index]['mime_type']),
                                                                          strtolower($mime_tree[$index]['encoding']),
                                                                          strtolower($mime_tree[$index]['charset']));
            }
        }
    }
    
    return;
}

Re: multipart/mixed

Posted: Mon May 19, 2008 4:04 pm
by gmorehoudh
Okay I think I've pretty much confirmed that it is Swift that's wrapping my message in a multipart/alternative container every time. Here is the code that actually creates the Swift message out of the results of swiftify_mime_tree above:

Code: Select all

/**
 * Build a multipart message in Swift given an IMAP stream object and message number.
 * Replace needle with replacetext.
 *
 * If null, return a Swift_Message_Part instead of a Swift_Message
 */
function rebuild_multipart_message($mbox, $msgno, $subject, $mime_tree = null) {
    if(!isset($mime_tree)) {
        $mime_tree = build_mime_tree(imap_fetchstructure($mbox, $msgno));
        $message = new Swift_Message($subject, null, strtolower($mime_tree[0]['mime_type']));
    }
 
    swiftify_mime_tree($mbox, $msgno, $mime_tree);
 
    $message->attach($mime_tree[0]['swift_part']);
    return $message;
}
To be clear, $mime_tree[0]['swift_part'] contains the entire mime tree I want the message to be made up of, not wrapped.

Also, in unrelated news I just got Decorator plugin doing the search and replace properly on the swift message, at least according to my limited tests. (Yes I upgraded to 3.3.3 first). Hooray for that!

Re: multipart/mixed

Posted: Tue May 20, 2008 7:01 am
by Chris Corbyn
Ok I see. Technically multipart/mixed is for displaying (potentially) varying mime types in the order they are given indeed. Sorry, I usually just over-simplify things when I'm responding here ;)

You don't have a huge amount of control over the way entities are nested really -- it's very difficult to code that aspect of the library. V4 does it a lot better but even then this would be very complex.

However, if you just add everything as an Attachment (a Swift_Message_Attachment) then Swift will not add a multipart/alternative wrapper and will place everything under a multipart/mixed content-type with just the one level of nesting.

You could of course "attach" an already composed multipart/alternative entity.

You problem has given me a new challenge however. I love it when people come to me with problems like this because I'm a one man band and never think of everything ;) I'm going to tackle this with version 4 and hopefully make it more flexible in the process.

Re: multipart/mixed

Posted: Tue May 20, 2008 11:52 am
by gmorehoudh
Thanks Chris. I may have to table this project until next week but I will definitely get back to you with any problems or questions. I'll also be watching for the v4 release!

Re: multipart/mixed

Posted: Fri Jul 25, 2008 6:31 pm
by gmorehoudh
I would love to see Swift 4 soon, as I'm going to need to fix this and hopefully upgrade all these scripts to Swift 4 at the same time. :)