littlelion has asked for the wisdom of the Perl Monks concerning the following question:

I have been using Perl for a good long time and I am tripping over something simple (I just know it). What am I doing wrong here?

My test case is pulled fairly directly from the examples in the documentation.

The environment:
% perl --version This is perl 5, version 16, subversion 3 (v5.16.3) built for x86_6 +4-linux-thread-multi % cat /etc/redhat-release CentOS Linux release 7.3.1611 (Core)
The problem:
% testmail.pl no data in this part at ./testmail.pp line 25.
The code:
#!/usr/bin/perl -w use strict; use warnings; use MIME::Lite; use Data::Dumper; my $msg = MIME::Lite->new(); dumpObject($msg); $msg->build(to => 'user1 <user@fqdn.org>', from => 'user2 <user2@fqdn. +org>'); $msg->build(subject => 'a message subject'); dumpObject($msg); $msg->attach( Type => 'text/plain', Data => 'This is the body text of the email', ); dumpObject($msg); $msg->send; # <------ 'no data' error here sub dumpObject { my ($object) = @_; my $output = Data::Dumper->Dump([$object], ['foo']); $output =~ s/\$foo = //; print $output; }

Full output with dumpObject() on. Notice that MIME::Lite::attach() created 2 parts, one (part0) master with no data and part1 with the text data, but send() barfs on this. Is this a bug in the module or am I doing something wrong?

After MIME::Lite->new:
./testmail.pp bless( { 'SubAttrs' => {}, 'Parts' => [], 'Attrs' => {}, 'Header' => [] }, 'MIME::Lite' );
After the build()s:
bless( { 'SubAttrs' => {}, 'Parts' => [], 'Attrs' => { 'content-disposition' => 'inline', 'content-type' => 'text/plain', 'mime-version' => '1.0', 'content-length' => undef, 'content-transfer-encoding' => '8bit' }, 'Header' => [ [ 'date', 'Sat, 29 Jul 2017 12:31:40 -0700' ], [ 'to', 'user1 <user@fqdn.org>' ], [ 'from', 'user2 <user2@fqdn.org>' ], [ 'x-mailer', 'MIME::Lite 3.030 (F2.84; T1.38; B3.13; Q3.13)' ], [ 'date', 'Sat, 29 Jul 2017 12:31:40 -0700' ], [ 'subject', 'a message' ] ] }, 'MIME::Lite' );
After the attach():
bless( { 'SubAttrs' => { 'content-type' => { 'boundary' => '_----------=_1501356700259120' } }, 'Parts' => [ bless( { 'SubAttrs' => {}, 'Parts' => [], 'FH' => undef, 'Attrs' => { 'content-disposition' => 'inline', 'content-type' => 'text/plain', 'content-length' => undef, 'content-transfer-encoding' => '8bit' }, 'Path' => undef, 'Data' => undef, 'Header' => [] }, 'MIME::Lite' ), bless( { 'SubAttrs' => {}, 'Parts' => [], 'Attrs' => { 'content-disposition' => 'inline', 'content-type' => 'text/plain', 'content-length' => undef, 'content-transfer-encoding' => '8bit' }, 'Data' => 'This is the body text of the email', 'Header' => [] }, 'MIME::Lite' ) ], 'Attrs' => { 'content-type' => 'multipart/mixed', 'mime-version' => '1.0', 'content-transfer-encoding' => '7bit' }, 'Header' => [ [ 'date', 'Sat, 29 Jul 2017 12:31:40 -0700' ], [ 'to', 'user1 <user@fqdn.org>' ], [ 'from', 'user2 <user2@fqdn.org>' ], [ 'date', 'Sat, 29 Jul 2017 12:31:40 -0700' ], [ 'subject', 'a message' ], [ 'x-mailer', 'MIME::Lite 3.030 (F2.84; T1.38; B3.13; Q3.13)' ] ] }, 'MIME::Lite' ); no data in this part at ./testmail.pp line 21.
From MIME::Lite->print_simple_body():
if ( defined( $self->{Data} ) ) { ... elsif ( defined( $self->{Path} ) || defined( $self->{FH} ) ) { ... else { Carp::croak "no data in this part\n";
From MIME::Lite->attach():
=item attach PART =item attach PARAMHASH... I<Instance method.> Add a new part to this message, and return the new part. If you supply a single PART argument, it will be regarded as a MIME::Lite object to be attached. Otherwise, this method assumes that you are giving in the pairs of a PARAMHASH which will be sent into C<new()> to create the new part. One of the possibly-quite-useful hacks thrown into this is the "attach-to-singlepart" hack: if you attempt to attach a part (let's call it "part 1") to a message that doesn't have a content-type of "multipart" or "message", the following happens: =over 4 =item * A new part (call it "part 0") is made. =item * The MIME attributes and data (but I<not> the other headers) are cut from the "self" message, and pasted into "part 0". =item * The "self" is turned into a "multipart/mixed" message. =item * The new "part 0" is added to the "self", and I<then> "part 1" is added +. =back One of the nice side-effects is that you can create a text message and then add zero or more attachments to it, much in the same way that a user agent like Netscape allows you to do. =cut sub attach { my $self = shift; my $attrs = $self->{Attrs}; my $sub_attrs = $self->{SubAttrs}; ### Create new part, if necessary: my $part1 = ( ( @_ == 1 ) ? shift: ref($self)->new( Top => 0, @_ ) + ); ### Do the "attach-to-singlepart" hack: if ( $attrs->{'content-type'} !~ m{^(multipart|message)/}i ) { ### Create part zero: my $part0 = ref($self)->new; ### Cut MIME stuff from self, and paste into part zero: foreach (qw(SubAttrs Attrs Data Path FH)) { $part0->{$_} = $self->{$_}; delete( $self->{$_} ); } $part0->top_level(0); ### clear top-level attributes ### Make self a top-level multipart: $attrs = $self->{Attrs} ||= {}; ### reset (sam: bug? th +is doesn't reset anything since Attrs is already a hash-ref) $sub_attrs = $self->{SubAttrs} ||= {}; ### reset $attrs->{'content-type'} = 'multipart/mixed'; $sub_attrs->{'content-type'}{'boundary'} = gen_boundary() +; $attrs->{'content-transfer-encoding'} = '7bit'; $self->top_level(1); ### activate top-level attributes ### Add part 0: push @{ $self->{Parts} }, $part0; } ### Add the new part: push @{ $self->{Parts} }, $part1; $part1; }
It looks like MIME::Lite expects to be created either with content in new() or with 'Type' set to /^(multipart|message)/ before you can add additional content. I did not find this obvious from the documentation. Thanks for any thoughts, Matt

Replies are listed 'Best First'.
Re: Error in MIME::Lite?
by shmem (Chancellor) on Jul 30, 2017 at 00:51 UTC
    Notice that MIME::Lite::attach() created 2 parts, one (part0) master with no data and part1 with the text data, but send() barfs on this. Is this a bug in the module or am I doing something wrong?

    Not a bug. If you create a message, there should be data in it, even if it is no data:

    qwurx [shmem] ~> perl -MMIME::Lite -le '$m=MIME::Lite->new(Data=>"foo" +);$m->print' Content-Disposition: inline Content-Transfer-Encoding: 8bit Content-Type: text/plain MIME-Version: 1.0 X-Mailer: MIME::Lite 3.030 (F2.85; T2.09; A2.13; B3.14; Q3.13) Date: Sun, 30 Jul 2017 02:30:09 +0200 foo

    Also:

    qwurx [shmem] ~> perl -MMIME::Lite -le '$m=MIME::Lite->new;$m->build(f +rom => "me", to =>"you", Data => "kisses"); $m->print' Content-Disposition: inline Content-Transfer-Encoding: 8bit Content-Type: text/plain MIME-Version: 1.0 X-Mailer: MIME::Lite 3.030 (F2.85; T2.09; A2.13; B3.14; Q3.13) Date: Sun, 30 Jul 2017 02:32:39 +0200 From: me To: you kisses

    These are just plain messages. You can also build a message with no data using Data => "" in the constructor or build(). MIME::Lite builds a multipart message only if there are, well, multiple parts.

    use MIME::Lite; $m = MIME::Lite->new; $m->build( from => 'me', to => 'you', Data => '', subject => 'Foo Bar', ); $m->attach( Type => 'text/plain', Data => 'this is an attachment' ); $m->print; __END__ Content-Transfer-Encoding: 7bit Content-Type: multipart/mixed; boundary="_----------=_150137533476690" MIME-Version: 1.0 Date: Sun, 30 Jul 2017 02:42:14 +0200 From: me To: you Subject: Foo Bar X-Mailer: MIME::Lite 3.030 (F2.85; T2.09; A2.13; B3.14; Q3.13) This is a multi-part message in MIME format. --_----------=_150137533476690 Content-Disposition: inline Content-Transfer-Encoding: 8bit Content-Type: text/plain --_----------=_150137533476690 Content-Disposition: inline Content-Transfer-Encoding: 8bit Content-Type: text/plain this is an attachment --_----------=_150137533476690--

    What you have gotten wrong is this part in your code

    $msg->attach( Type => 'text/plain', Data => 'This is the body text of the email', );

    It isn't the body text of the mail. It is an attachment. The body part is the first part of the multipart message, and you must provide data for it (even an empty string works) either in the constructor, or the build or the data method invoked upon the MIME::Lite object.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: Error in MIME::Lite?
by Anonymous Monk on Jul 29, 2017 at 20:12 UTC

    Hi,

    Why are you calling build more than once?

    The documentation does not show that usage

      I called build() twice for clarity. The test case fails with a single build() call.
      $msg->build(to => 'user1 <user@fqdn.org>', from => 'user2 <user2@fqdn. +org>', subject => 'a message subject'); % ./testmail.pp no data in this part at ./testmail.pp line 21.
      The problem is that the MIME::Lite->attach() creates a new part with no content unless the content-type is already set to multipart. This is not legal as far as MIME::Lite->send() is concerned.

      I tried setting content-type but that breaks the MIME formatting of the message (all the steps that attach() is doing unless it detects multipart).

        Hi

        I think build wants data also