MIME-Lite

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

1.140     2000-04-27
        - Fixed bug in support for "To", "Cc", and "Bcc" in send_by_smtp():
          multiple (comma-separated) addresses should now work fine. (thanks,
          John Mason)
        - Added automatic verification that attached data files exist.

1.137     2000-03-22
        - Added support for "Cc" and "Bcc" in send_by_smtp(). (thanks, Lucas
          Maneos)
        - Chooses a better default content-transfer-encoding if the
          content-type is "image/*", "audio/*", etc.
        - Fixed bug in QP-encoding where a non-local $_ was being modified.
          (thanks, Jochen Stenzel)
        - Removed references to $`, $', and $&.
        - Added an example of how to send HTML files with enclosed in-line
          images.

1.133     1999-04-17
        - Fixed bug in "Data" handling: arrayrefs were not being handled
          properly.

lib/MIME/Lite.pm  view on Meta::CPAN

#pod
#pod
#pod =head2 Attach a pre-prepared part to a message
#pod
#pod     ### Create a standalone part:
#pod     $part = MIME::Lite->new(
#pod         Top      => 0,
#pod         Type     =>'text/html',
#pod         Data     =>'<H1>Hello</H1>',
#pod     );
#pod     $part->attr('content-type.charset' => 'UTF-8');
#pod     $part->add('X-Comment' => 'A message for you');
#pod
#pod     ### Attach it to any message:
#pod     $msg->attach($part);
#pod
#pod
#pod =head2 Print a message to a filehandle
#pod
#pod     ### Write it to a filehandle:
#pod     $msg->print(\*STDOUT);

lib/MIME/Lite.pm  view on Meta::CPAN

#pod I<Instance method.>
#pod Add a new part to this message, and return the new part.
#pod
#pod If you supply a single PART argument, it will be regarded
#pod as a MIME::Lite object to be attached.  Otherwise, this
#pod method assumes that you are giving in the pairs of a PARAMHASH
#pod which will be sent into C<new()> to create the new part.
#pod
#pod One of the possibly-quite-useful hacks thrown into this is the
#pod "attach-to-singlepart" hack: if you attempt to attach a part (let's
#pod call it "part 1") to a message that doesn't have a content-type
#pod of "multipart" or "message", the following happens:
#pod
#pod =over 4
#pod
#pod =item *
#pod
#pod A new part (call it "part 0") is made.
#pod
#pod =item *
#pod

lib/MIME/Lite.pm  view on Meta::CPAN


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?  this 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;

lib/MIME/Lite.pm  view on Meta::CPAN

    ( defined( $params{Data} ) + defined( $params{Path} ) + defined( $params{FH} ) <= 1 )
      or Carp::croak "supply exactly zero or one of (Data|Path|FH).\n";

    ### Create new instance, if necessary:
    ref($self) or $self = $self->new;


    ### CONTENT-TYPE....
    ###

    ### Get content-type or content-type-macro:
    my $type = ( $params{Type} || ( $AUTO_CONTENT_TYPE ? 'AUTO' : 'TEXT' ) );

    ### Interpret content-type-macros:
    if    ( $type eq 'TEXT' )   { $type = 'text/plain'; }
    elsif ( $type eq 'HTML' )   { $type = 'text/html'; }
    elsif ( $type eq 'BINARY' ) { $type = 'application/octet-stream' }
    elsif ( $type eq 'AUTO' )   { $type = $self->suggest_type( $params{Path} ); }

    ### We now have a content-type; set it:
    $type = lc($type);
    my $attrs  = $self->{Attrs};
    my $sub_attrs  = $self->{SubAttrs};
    $attrs->{'content-type'} = $type;

    ### Get some basic attributes from the content type:
    my $is_multipart = ( $type =~ m{^(multipart)/}i );

    ### Add in the multipart boundary:
    if ($is_multipart) {
        my $boundary = gen_boundary();
        $sub_attrs->{'content-type'}{'boundary'} = $boundary;
    }


    ### CONTENT-ID...
    ###
    if ( defined $params{Id} ) {
        my $id = $params{Id};
        $id = "<$id>" unless $id =~ /\A\s*<.*>\s*\z/;
        $attrs->{'content-id'} = $id;
    }

lib/MIME/Lite.pm  view on Meta::CPAN


#------------------------------

#pod =item attr ATTR,[VALUE]
#pod
#pod I<Instance method.>
#pod Set MIME attribute ATTR to the string VALUE.
#pod ATTR is converted to all-lowercase.
#pod This method is normally used to set/get MIME attributes:
#pod
#pod     $msg->attr("content-type"         => "text/html");
#pod     $msg->attr("content-type.charset" => "US-ASCII");
#pod     $msg->attr("content-type.name"    => "homepage.html");
#pod
#pod This would cause the final output to look something like this:
#pod
#pod     Content-type: text/html; charset=US-ASCII; name="homepage.html"
#pod
#pod Note that the special empty sub-field tag indicates the anonymous
#pod first sub-field.
#pod
#pod Giving VALUE as undefined will cause the contents of the named
#pod subfield to be deleted.
#pod
#pod Supplying no VALUE argument just returns the attribute's value:
#pod
#pod     $type = $msg->attr("content-type");        ### returns "text/html"
#pod     $name = $msg->attr("content-type.name");   ### returns "homepage.html"
#pod
#pod =cut


sub attr {
    my ( $self, $attr, $value ) = @_;
    my $attrs = $self->{Attrs};

    $attr = lc($attr);

lib/MIME/Lite.pm  view on Meta::CPAN

}


#------------------------------

#pod =item field_order FIELD,...FIELD
#pod
#pod I<Class/instance method.>
#pod Change the order in which header fields are output for this object:
#pod
#pod     $msg->field_order('from', 'to', 'content-type', 'subject');
#pod
#pod When used as a class method, changes the default settings for
#pod all objects:
#pod
#pod     MIME::Lite->field_order('from', 'to', 'content-type', 'subject');
#pod
#pod Case does not matter: all field names will be coerced to lowercase.
#pod In either case, supply the empty array to restore the default ordering.
#pod
#pod =cut


sub field_order {
    my $self = shift;
    if ( ref($self) ) {

lib/MIME/Lite.pm  view on Meta::CPAN

#pod content-disposition.
#pod
#pod =cut


sub filename {
    my ( $self, $filename ) = @_;
    my $sub_attrs = $self->{SubAttrs};

    if ( @_ > 1 ) {
        $sub_attrs->{'content-type'}{'name'} = $filename;
        $sub_attrs->{'content-disposition'}{'filename'} = $filename;
    }
    return $sub_attrs->{'content-disposition'}{'filename'};
}

#------------------------------

#pod =item get TAG,[INDEX]
#pod
#pod I<Instance method.>

lib/MIME/Lite.pm  view on Meta::CPAN



#----
# Miko's note: I wasn't quite sure how to handle this, so I waited to hear
# what you think.  Given that the content-length isn't always required,
# and given the performance cost of calculating it from a file handle,
# I thought it might make more sense to add some sort of computelength
# property. If computelength is false, then the length simply isn't
# computed.  What do you think?
#
# Eryq's reply:  I agree; for now, we can silently leave out the content-type.

sub get_length {
    my $self = shift;
    my $attrs = $self->{Attrs};

    my $is_multipart = ( $attrs->{'content-type'} =~ m{^multipart/}i );
    my $enc = lc( $attrs->{'content-transfer-encoding'} || 'binary' );
    my $length;
    if ( !$is_multipart && ( $enc eq "binary" ) ) {    ### might figure it out cheap:
        if ( defined( $self->{Data} ) ) {              ### it's in core
            $length = length( $self->{Data} );
        } elsif ( defined( $self->{FH} ) ) {           ### it's in a filehandle
            ### no-op: it's expensive, so don't bother
        } elsif ( defined( $self->{Path} ) ) {         ### it's a simple file!
            $length = ( -s $self->{Path} ) if ( -e $self->{Path} );
        }

lib/MIME/Lite.pm  view on Meta::CPAN

    my ( $self, @a ) = @_;
    my ($expl) = @a;
    local $QUIET = 1;

    ### Scrub me:
    if ( !@a ) {    ### guess

        ### Scrub length always:
        $self->replace( 'content-length', '' );

        ### Scrub disposition if no filename, or if content-type has same info:
        if ( !$self->_safe_attr('content-disposition.filename')
             || $self->_safe_attr('content-type.name') )
        {
            $self->replace( 'content-disposition', '' );
        }

        ### Scrub encoding if effectively unencoded:
        if ( $self->_safe_attr('content-transfer-encoding') =~ /^(7bit|8bit|binary)$/i ) {
            $self->replace( 'content-transfer-encoding', '' );
        }

        ### Scrub charset if US-ASCII:
        if ( $self->_safe_attr('content-type.charset') =~ /^(us-ascii)/i ) {
            $self->attr( 'content-type.charset' => undef );
        }

        ### TBD: this is not really right for message/digest:
        if (     ( keys %{ $self->{Attrs}{'content-type'} } == 1 )
             and ( $self->_safe_attr('content-type') eq 'text/plain' ) )
        {
            $self->replace( 'content-type', '' );
        }
    } elsif ( $expl and ( ref($expl) eq 'ARRAY' ) ) {
        foreach ( @{$expl} ) { $self->replace( $_, '' ); }
    }

    ### Scrub my kids:
    foreach ( @{ $self->{Parts} } ) { $_->scrub(@a); }
}

#pod =back

lib/MIME/Lite.pm  view on Meta::CPAN

#pod The new current value is returned.
#pod
#pod =cut


sub binmode {
    my $self = shift;
    $self->{Binmode} = shift if (@_);    ### argument? set override
    return ( defined( $self->{Binmode} )
             ? $self->{Binmode}
             : ( $self->{Attrs}{"content-type"} !~ m{^(text|message)/}i )
    );
}

#------------------------------

#pod =item data [DATA]
#pod
#pod I<Instance method.>
#pod Get/set the literal DATA of the message.  The DATA may be
#pod either a scalar, or a reference to an array of scalars (which

lib/MIME/Lite.pm  view on Meta::CPAN

    } else {
        return ( $type eq 'multipart' ) ? 'binary' : 'base64';
    }
}

#------------------------------
#
# =item suggest_type PATH
#
# I<Class/instance method.>
# Suggest the content-type for this attached path.
# We always fall back to "application/octet-stream" if no good guess
# can be made, so don't use this if you don't mean it!
#
sub suggest_type {
    my ( $self, $path ) = @_;

    ### If there's no path, bail:
    $path or return 'application/octet-stream';

    ### Consult MIME::Types, maybe:

lib/MIME/Lite.pm  view on Meta::CPAN


sub print_body {
    my ( $self, $out, $is_smtp ) = @_;
    my $attrs = $self->{Attrs};
    my $sub_attrs = $self->{SubAttrs};

    ### Coerce into a printable output handle:
    $out = MIME::Lite::IO_Handle->wrap($out);

    ### Output either the body or the parts.
    ###   Notice that we key off of the content-type!  We expect fewer
    ###   accidents that way, since the syntax will always match the MIME type.
    my $type = $attrs->{'content-type'};
    if ( $type =~ m{^multipart/}i ) {
        my $boundary = $sub_attrs->{'content-type'}{'boundary'};

        ### Preamble:
        $out->print( defined( $self->{Preamble} )
                     ? $self->{Preamble}
                     : "This is a multi-part message in MIME format.\n"
        );

        ### Parts:
        my $part;
        foreach $part ( @{ $self->{Parts} } ) {

lib/MIME/Lite.pm  view on Meta::CPAN

    );

=head2 Attach a pre-prepared part to a message

    ### Create a standalone part:
    $part = MIME::Lite->new(
        Top      => 0,
        Type     =>'text/html',
        Data     =>'<H1>Hello</H1>',
    );
    $part->attr('content-type.charset' => 'UTF-8');
    $part->add('X-Comment' => 'A message for you');

    ### Attach it to any message:
    $msg->attach($part);

=head2 Print a message to a filehandle

    ### Write it to a filehandle:
    $msg->print(\*STDOUT);

lib/MIME/Lite.pm  view on Meta::CPAN

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 *

lib/MIME/Lite.pm  view on Meta::CPAN


I<Note:> the name comes from Mail::Header.

=item attr ATTR,[VALUE]

I<Instance method.>
Set MIME attribute ATTR to the string VALUE.
ATTR is converted to all-lowercase.
This method is normally used to set/get MIME attributes:

    $msg->attr("content-type"         => "text/html");
    $msg->attr("content-type.charset" => "US-ASCII");
    $msg->attr("content-type.name"    => "homepage.html");

This would cause the final output to look something like this:

    Content-type: text/html; charset=US-ASCII; name="homepage.html"

Note that the special empty sub-field tag indicates the anonymous
first sub-field.

Giving VALUE as undefined will cause the contents of the named
subfield to be deleted.

Supplying no VALUE argument just returns the attribute's value:

    $type = $msg->attr("content-type");        ### returns "text/html"
    $name = $msg->attr("content-type.name");   ### returns "homepage.html"

=item delete TAG

I<Instance method.>
Delete field TAG with the given VALUE to the end of the header.
The TAG will be converted to all-lowercase.

    $msg->delete("Subject");

I<Note:> the name comes from Mail::Header.

=item field_order FIELD,...FIELD

I<Class/instance method.>
Change the order in which header fields are output for this object:

    $msg->field_order('from', 'to', 'content-type', 'subject');

When used as a class method, changes the default settings for
all objects:

    MIME::Lite->field_order('from', 'to', 'content-type', 'subject');

Case does not matter: all field names will be coerced to lowercase.
In either case, supply the empty array to restore the default ordering.

=item fields

I<Instance method.>
Return the full header for the object, as a ref to an array
of C<[TAG, VALUE]> pairs, where each TAG is all-lowercase.
Note that any fields the user has explicitly set will override the

lib/MIME/Lite.pm  view on Meta::CPAN

    $msg->scrub;

You can scrub() any part of a multipart message independently;
just be aware that it works recursively.  Before you scrub,
note the rules that I follow:

=over 4

=item Content-type

You can safely scrub the "content-type" attribute if, and only if,
the part is of type "text/plain" with charset "us-ascii".

=item Content-transfer-encoding

You can safely scrub the "content-transfer-encoding" attribute
if, and only if, the part uses "7bit", "8bit", or "binary" encoding.
You are far better off doing this if your lines are under 1000
characters.  Generally, that means you I<can> scrub it for plain
text, and you can I<not> scrub this for images, etc.

=item Content-disposition

You can safely scrub the "content-disposition" attribute
if you trust the mail reader to do the right thing when it decides
whether to show an attachment inline or as a link.  Be aware
that scrubbing both the content-disposition and the content-type
means that there is no way to "recommend" a filename for the attachment!

B<Note:> there are reports of brain-dead MUAs out there that
do the wrong thing if you I<provide> the content-disposition.
If your attachments keep showing up inline or vice-versa,
try scrubbing this attribute.

=item Content-length

You can always scrub "content-length" safely.

lib/MIME/Lite.pm  view on Meta::CPAN

    my $subject = "\241Aqu\355 est\341!";
    my $text    = "\277Quieres ganar muchos \x{20ac}'s?";

    ### Create a new message encoded in UTF-8:
    my $msg = MIME::Lite->new(
        From    => 'me@myhost.com',
        To      => encode( 'MIME-Header', $to ),
        Subject => encode( 'MIME-Header', $subject ),
        Data    => encode_utf8($text)
    );
    $msg->attr( 'content-type' => 'text/plain; charset=utf-8' );
    $msg->send;

B<Note:>

=over

=item *

The above example assumes that the values you want to encode are in
Perl's "internal" form, i.e. the strings contain decoded UTF-8
characters, not the bytes that represent those characters.

See L<perlunitut>, L<perluniintro>, L<perlunifaq> and L<Encode> for
more.

=item *

If, for the body of the email,  you want to use a character set
other than UTF-8, then you should encode appropriately, and set the
correct C<content-type>, eg:

    ...
    Data => encode('iso-8859-15',$text)
    ...

    $msg->attr( 'content-type' => 'text/plain; charset=iso-8859-15' );

=item *

For the message headers, L<Encode::MIME::Header> only support UTF-8,
but most modern mail clients should be able to handle this.  It is not
a problem to have your headers in a different encoding from the message
body.

=back

lib/MIME/Lite.pm  view on Meta::CPAN

#pod     $msg->scrub;
#pod
#pod You can scrub() any part of a multipart message independently;
#pod just be aware that it works recursively.  Before you scrub,
#pod note the rules that I follow:
#pod
#pod =over 4
#pod
#pod =item Content-type
#pod
#pod You can safely scrub the "content-type" attribute if, and only if,
#pod the part is of type "text/plain" with charset "us-ascii".
#pod
#pod =item Content-transfer-encoding
#pod
#pod You can safely scrub the "content-transfer-encoding" attribute
#pod if, and only if, the part uses "7bit", "8bit", or "binary" encoding.
#pod You are far better off doing this if your lines are under 1000
#pod characters.  Generally, that means you I<can> scrub it for plain
#pod text, and you can I<not> scrub this for images, etc.
#pod
#pod =item Content-disposition
#pod
#pod You can safely scrub the "content-disposition" attribute
#pod if you trust the mail reader to do the right thing when it decides
#pod whether to show an attachment inline or as a link.  Be aware
#pod that scrubbing both the content-disposition and the content-type
#pod means that there is no way to "recommend" a filename for the attachment!
#pod
#pod B<Note:> there are reports of brain-dead MUAs out there that
#pod do the wrong thing if you I<provide> the content-disposition.
#pod If your attachments keep showing up inline or vice-versa,
#pod try scrubbing this attribute.
#pod
#pod =item Content-length
#pod
#pod You can always scrub "content-length" safely.

lib/MIME/Lite.pm  view on Meta::CPAN

#pod     my $subject = "\241Aqu\355 est\341!";
#pod     my $text    = "\277Quieres ganar muchos \x{20ac}'s?";
#pod
#pod     ### Create a new message encoded in UTF-8:
#pod     my $msg = MIME::Lite->new(
#pod         From    => 'me@myhost.com',
#pod         To      => encode( 'MIME-Header', $to ),
#pod         Subject => encode( 'MIME-Header', $subject ),
#pod         Data    => encode_utf8($text)
#pod     );
#pod     $msg->attr( 'content-type' => 'text/plain; charset=utf-8' );
#pod     $msg->send;
#pod
#pod B<Note:>
#pod
#pod =over
#pod
#pod =item *
#pod
#pod The above example assumes that the values you want to encode are in
#pod Perl's "internal" form, i.e. the strings contain decoded UTF-8
#pod characters, not the bytes that represent those characters.
#pod
#pod See L<perlunitut>, L<perluniintro>, L<perlunifaq> and L<Encode> for
#pod more.
#pod
#pod =item *
#pod
#pod If, for the body of the email,  you want to use a character set
#pod other than UTF-8, then you should encode appropriately, and set the
#pod correct C<content-type>, eg:
#pod
#pod     ...
#pod     Data => encode('iso-8859-15',$text)
#pod     ...
#pod
#pod     $msg->attr( 'content-type' => 'text/plain; charset=iso-8859-15' );
#pod
#pod =item *
#pod
#pod For the message headers, L<Encode::MIME::Header> only support UTF-8,
#pod but most modern mail clients should be able to handle this.  It is not
#pod a problem to have your headers in a different encoding from the message
#pod body.
#pod
#pod =back
#pod

t/head.t  view on Meta::CPAN

ok(!defined($me->get('Received')),
       "delete: deletion of RECEIVED worked");

# Test "replace" [1 test]:
$me->replace('subject', "Hellooooo, nurse!");
is($me->get('SUBJECT'),
    "Hellooooo, nurse!",
    "replace: replace of SUBJECT worked");

# Test "attr" [2 tests]:
$me->attr('content-type.charset', 'US-ASCII');
is($me->attr('content-type.charset'),
    'US-ASCII',
    "attr: replace of charset worked");
#
my ($ct) = map {($_->[0] eq 'content-type') ? $_->[1] : ()} @{$me->fields};

is($ct,
    'text/plain; charset="US-ASCII"',
    "attr: replace of charset worked on whole line");

done_testing;



( run in 1.734 second using v1.01-cache-2.11-cpan-d7f47b0818f )