Skip Menu |

This queue is for tickets about the Mail-IMAPClient CPAN distribution.

Report information
The Basics
Id: 57337
Status: resolved
Priority: 0/
Queue: Mail-IMAPClient

People
Owner: PLOBBES [...] cpan.org
Requestors: ROBN [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: 3.03
Fixed in: 3.25



Subject: Correctly handle multiparts in BodyStructure.pm
The changes to BodyStructure.pm in 3.24 almost fixed the longstanding issues with part numbers returned by parts() and bodystructure() not correctly matching the part numbers accepted by FETCH. One of the changes made in 3.24 isn't quite right though, so it still isn't working properly Attached patch fixes it. The short of it is that a MULTIPART should only be removed from the tree if its the first and direct child of a MESSAGE. I'm no expert, I base this on the one vague example in RFC3501 and what I've seen from my own servers. This patch makes the output produced almost identical to what was produced by BodyStructure.pm from IMAPClient 2.2.9. The part numbers are the same, there's just some extra .TEXT parts. As an aside, I think its reasonable to expect .MIME and .TEXT pseudo-parts to be available all types of MESSAGEs, not just those with a MULTIPART under them. This at least seems to work on the servers I have access to. I'm not sure if they should be exposed via BodyStructure.pm though. Is there any better documentation available than the vague mess in RFC3501? Cheers, Rob.
Subject: bodystructure-fix.diff
diff --git a/lib/Mail/IMAPClient/BodyStructure.pm b/lib/Mail/IMAPClient/BodyStructure.pm index 4353d53..1420aa9 100644 --- a/lib/Mail/IMAPClient/BodyStructure.pm +++ b/lib/Mail/IMAPClient/BodyStructure.pm @@ -106,17 +106,23 @@ sub bodystructure my $prefix = $self->{_prefix} || ""; $prefix =~ s/\.?$/./; - # in a multipart message each subpart is one level "higher" - $prefix =~ s/\.\d+\.$/./ if ($self->{bodytype} eq 'MULTIPART'); - foreach my $p ( @{$self->{bodystructure}} ) { $partno++; - $p->{_prefix} = "$prefix$partno"; - - # BUG?: old code didn't add .TEXT sections, should we skip these? my $pno = $partno; - $pno = "TEXT" if ($partno == 1 and $self->{bodytype} eq 'MESSAGE'); + + # a message and the multipart inside of it "collapse" together + if ($partno == 1 and $self->{bodytype} eq 'MESSAGE' and $p->{bodytype} eq 'MULTIPART') { + # BUG: I think every message should really have HEAD (actually + # MIME) and TEXT. at least dovecot and iplanet appear to allow + # this even for non-multipart sections. so this bit has to be + # generalised a little + $pno = "TEXT"; + $p->{_prefix} = "$prefix"; + } + else { + $p->{_prefix} = "$prefix$partno"; + } $p->{_id} ||= "$prefix$pno"; push @parts, $p, $p->{bodystructure} ? $p->bodystructure : (); diff --git a/t/bodystructure.t b/t/bodystructure.t index 3784356..e3c34b6 100644 --- a/t/bodystructure.t +++ b/t/bodystructure.t @@ -31,7 +31,9 @@ is( # this: "1#2#2.HEAD#2.1#2.2#2.2.HEAD#2.2.1#2.2.1.1#2.2.1.2" # to: "1#2#2.HEAD#2.1#2.1.1#2.1.2#2.1.2.HEAD#2.1.2.1#2.1.2.1.1#2.1.2.1.1.1#2.1.2.1.1.2" # Patches to BodyStructure.pm in 3.24 changed it to this: - "1#2#2.HEAD#2.TEXT#2.1#2.2#2.2.HEAD#2.2.TEXT#2.2.1#2.2.1#2.2.2", + # "1#2#2.HEAD#2.TEXT#2.1#2.2#2.2.HEAD#2.2.TEXT#2.2.1#2.2.1#2.2.2", + # Patches to BodyStructure.pm in 3.XX changed it to this: + "1#2#2.HEAD#2.TEXT#2.1#2.2#2.2.HEAD#2.2.TEXT#2.2.1#2.2.1.1#2.2.1.2", 'parts' ); @@ -67,8 +69,8 @@ is_deeply( [ $bsobj->parts ], \@exp, 'bs5 parts' ) my $bs6 = q{(BODYSTRUCTURE (("text" "plain" ("charset" "UTF-8" "format" "flowed") NIL NIL "8bit" 82 6 NIL NIL NIL NIL)("message" "rfc822" ("name" "this is internal letter.eml") NIL NIL "7bit" 243436 ("Mon, 24 Aug 2009 10:51:22 +0400" "this is internal letter" ((NIL NIL "icestar" "inbox.ru")) ((NIL NIL "icestar" "inbox.ru")) ((NIL NIL "icestar" "inbox.ru")) ((NIL NIL "dima" "adriver.ru")) NIL NIL NIL "<4A92386A.9080307@inbox.ru>") (("text" "plain" ("charset" "UTF-8" "format" "flowed") NIL NIL "7bit" 116 7 NIL NIL NIL NIL)("text" "xml" ("name" "mediaplan.xml" "charset" "us-ascii") NIL NIL "base64" 31412 424 NIL ("inline" ("filename" "mediaplan.xml")) NIL NIL)("application" "zip" ("name" "banners2.zip") NIL NIL "base64" 209942 NIL ("inline" ("filename" "banners2.zip")) NIL NIL) "mixed" ("boundary" "------------070804080502030807020509") NIL NIL NIL) 3326 NIL ("inline" ("filename" "this is internal letter.eml")) NIL NIL) "mixed" ("boundary" "------------070704030806000803040203") NIL NIL NIL))}; $bsobj = Mail::IMAPClient::BodyStructure->new($bs6); -@exp = qw(1 2 2.HEAD 2.TEXT 2.1 2.2 2.3); ok( defined $bsobj, 'parsed sixth' ); +@exp = qw(1 2 2.HEAD 2.TEXT 2.1 2.2 2.3); is_deeply( [ $bsobj->parts ], \@exp, 'bs6 parts' ) or diag( join(" ", $bsobj->parts ) ); @@ -76,7 +78,7 @@ is_deeply( [ $bsobj->parts ], \@exp, 'bs6 parts' ) my $bs7 = q{(BODYSTRUCTURE (("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 20 1 NIL NIL NIL NIL)("message" "rfc822" NIL NIL NIL "7bit" 1810 ("Fri,07 May 2010 01:55:07 -0400" "wrap inner a" (("Phil Pearl" NIL "phil" "perkpartners.com")) (("Phil Pearl" NIL "phil" "perkpartners.com")) (("Phil Pearl" NIL "phil" "perkpartners.com")) ((NIL NIL "phil" "perkpartners.com")) NIL NIL NIL "<25015.1273211707@local>") (("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 27 3 NIL NIL NIL NIL)("message" "rfc822" NIL NIL NIL "7bit" 783 ("Fri, 07 May 2010 01:54:14 -0400" "inner msg #1" (("Phil Pearl" NIL "phil" "perkpartners.com")) (("Phil Pearl" NIL "phil" "perkpartners.com")) (("Phil Pearl" NIL "phil" "perkpartners.com")) ((NIL NIL "phil" "perkpartners.com")) NIL NIL NIL "<24986.1273211654@local>") ("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 25 3 NIL NIL NIL NIL) 23 NIL ("inline" ("filename" "52")) NIL NIL) "mixed" ("boundary" "=-=-=") NIL NIL NIL) 58 NIL ("inline" ("filename" "53")) NIL NIL) "mixed" ("boundary""==-=-=") NIL NIL NIL))}; $bsobj = Mail::IMAPClient::BodyStructure->new($bs7); -@exp = qw(1 2 2.HEAD 2.TEXT 2.1 2.2 2.2.HEAD 2.2.TEXT); +@exp = qw(1 2 2.HEAD 2.TEXT 2.1 2.2 2.2.HEAD 2.2.1); ok( defined $bsobj, 'parsed seventh' ); is_deeply( [ $bsobj->parts ], \@exp, 'bs7 parts' ) or diag( join(" ", $bsobj->parts ) ); @@ -85,7 +87,7 @@ is_deeply( [ $bsobj->parts ], \@exp, 'bs7 parts' ) my $bs8 = q{(BODYSTRUCTURE (("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 31 2 NIL NIL NIL NIL)("message" "rfc822" NIL NIL "My forwarded message" "7bit" 2833 ("Fri, 07 May 2010 01:55:40 -0400" "outer msg" (("Phil Pearl" NIL "phil" "perkpartners.com")) (("Phil Pearl" NIL "phil" "perkpartners.com")) (("Phil Pearl" NIL "phil" "perkpartners.com")) ((NIL NIL "phil" "perkpartners.com")) NIL NIL NIL "<25030.1273211740@local>") (("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 20 1 NIL NIL NIL NIL)("message" "rfc822" NIL NIL NIL "7bit" 1810 ("Fri, 07 May 2010 01:55:07 -0400" "wrap inner a" (("Phil Pearl" NIL "phil" "perkpartners.com")) (("Phil Pearl" NIL "phil" "perkpartners.com")) (("Phil Pearl" NIL "phil" "perkpartners.com")) ((NIL NIL "phil" "perkpartners.com")) NIL NIL NIL "<25015.1273211707@local>") (("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 27 3 NIL NIL NIL NIL)("message" "rfc822" NIL NIL NIL "7bit" 783 ("Fri, 07 May 2010 01:54:14 -0400" "inner msg #1" (("Phil Pearl" NIL "phil" "perkpartners.com")) (("Phil Pearl" NIL "phil" "perkpartners.com")) (("Phil Pearl" NIL "phil" "perkpartners.com")) ((NIL NIL "phil" "perkpartners.com")) NIL NIL NIL "<24986.1273211654@local>") ("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 25 3 NIL NIL NIL NIL) 23 NIL ("inline" ("filename" "52")) NIL NIL) "mixed" ("boundary" "=-=-=") NIL NIL NIL) 58 NIL ("inline" ("filename" "53")) NIL NIL) "mixed" ("boundary" "==-=-=") NIL NIL NIL) 91 NIL ("inline" ("filename" "52")) NIL NIL)("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 30 2 NIL NIL NIL NIL)("application" "octet-stream" NIL NIL "My attachment" "7bit" 76 NIL ("attachment" ("filename" ".signature.cell")) NIL NIL)("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 31 2 NIL NIL NIL NIL) "mixed" ("boundary" "===-=-=") NIL NIL NIL))}; $bsobj = Mail::IMAPClient::BodyStructure->new($bs8); -@exp = qw(1 2 2.HEAD 2.TEXT 2.1 2.2 2.2.HEAD 2.2.TEXT 2.2.1 2.2.2 2.2.2.HEAD 2.2.2.TEXT 3 4 5); +@exp = qw(1 2 2.HEAD 2.TEXT 2.1 2.2 2.2.HEAD 2.2.TEXT 2.2.1 2.2.2 2.2.2.HEAD 2.2.2.1 3 4 5); ok( defined $bsobj, 'parsed eighth' ); is_deeply( [ $bsobj->parts ], \@exp, 'bs8 parts' ) or diag( join(" ", $bsobj->parts ) );
Can you supply a sample RFC2822 message that requires/works with the patch provided? I could try to create my own but I'd rather just work off the a provided example if possible for my own testing. In other tests I did, I did not come across this problem but my tests are obviously non- exhaustive.
Sorry, I should've included my tests or even updated the real tests. I'll try to do that soon. Here's all my stuff though in case you get to it first. I've uploaded the raw messages I use for testing here: http://cataclysm.cx/random/mime/ These are a couple of MIME demo/test messages that we used years ago when first developing our webmail client. Attached is my test script and its dump of the bodystructure with various versions of Mail::IMAPClient. The script contains the bodystructure for these two messages as produced by iPlanet Messaging Server 5.2. We currently have 2.2.9 in production and the part numbers it produces match what comes back off the server. 3.23 and 3.24 don't match perfectly because they do the wrong things with multiparts. The addition of this patch makes things work correctly (with the addition of .TEXT, which our client ignores). Tip: A side-by-side diffing tool is brilliant for seeing what's going on here. vimdiff saved quite a bit of my hair here :P
Subject: 3.24+57337
Download 3.24+57337
application/octet-stream 2.1k

Message body not shown because it is not plain text.

Subject: 3.24
Download 3.24
application/octet-stream 2.1k

Message body not shown because it is not plain text.

Subject: 2.2.9
Download 2.2.9
application/octet-stream 1.7k

Message body not shown because it is not plain text.

Subject: 3.23
Download 3.23
application/octet-stream 2.2k

Message body not shown because it is not plain text.

Subject: bodystructure.pl

Message body is not shown because it is too large.

On Mon May 10 16:37:57 2010, ROBN wrote: ... Show quoted text
> I've uploaded the raw messages I use for testing here: > > http://cataclysm.cx/random/mime/
Rob, I am unable to reach cataclysm.cx. Looking at WHOIS it appears that the domain is currently in a "suspended" status. Do you have an alternate location to download? Or perhaps just email them to me (plobbes @ cpan dot org)? Thanks!
CC: ROBN [...] cpan.org, DJKERNEN__NO_SOLICITING__ [...] cpan.org
Subject: Re: [rt.cpan.org #57337] Correctly handle multiparts in BodyStructure.pm
Date: Mon, 17 May 2010 07:56:33 +1000
To: bug-Mail-IMAPClient [...] rt.cpan.org
From: Robert Norris <rob [...] cataclysm.cx>
Hi Phil, Sorry about that. My registrar broke something and I'm now yelling at them about it. Things are working again. Rob. On Sat, May 15, 2010 at 5:52 AM, Phil Pearl (Lobbes) via RT <bug-Mail-IMAPClient@rt.cpan.org> wrote: Show quoted text
> <URL: https://rt.cpan.org/Ticket/Display.html?id=57337 > > > On Mon May 10 16:37:57 2010, ROBN wrote: > ...
>> I've uploaded the raw messages I use for testing here: >> >>   http://cataclysm.cx/random/mime/
> > Rob, > I am unable to reach cataclysm.cx.  Looking at WHOIS it appears that the > domain is currently in a "suspended" status.  Do you have an alternate > location to download?  Or perhaps just email them to me (plobbes @ cpan > dot org)?  Thanks! >
Thanks for the files. I took your patch with some minor (non-functional) changes and integrated that with the code for the next release (3.25). I think we should be pretty close to good. I also added a test case using the mime torture message. I'm almost of the opinion that I shouldn't even include the .TEXT parts (unless we wen't crazy and did the other missing .<type> parts) but we'll leave it in there for now anyway. If you want a pre-release copy of IMAPClient/BodyStructure.pm let me know and I'll send it to you for your review.
Release 3.25 is out with a fix for this bug closing for now, but please let me know if you run into any problems!