The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# ============================================================================
package Mail::Builder::Attachment;
# ============================================================================

use namespace::autoclean;
use Moose;
with qw(Mail::Builder::Role::File);
use Mail::Builder::TypeConstraints;

use MIME::Types;
use Carp;
use Encode;

our $VERSION = $Mail::Builder::VERSION;

has 'name' => (
    is          => 'rw',
    isa         => 'Str',
    lazy_build  => 1,
    trigger     => sub { shift->clear_cache },
);

has 'mimetype' => (
    is          => 'rw',
    isa         => 'Mail::Builder::Type::Mimetype',
    lazy_build  => 1,
    trigger     => sub { shift->clear_cache },
);


around BUILDARGS => sub {
    my $orig = shift;
    my $class = shift;

    if ( scalar @_ == 1 && ref $_[0] eq 'HASH' ) {
        return $class->$orig($_[0]);
    }
    else {
        my $params = {
            file    => $_[0],
        };
        if (defined $_[1]) {
            $params->{name} = $_[1];
        }
        if (defined $_[2]) {
            $params->{mimetype} = $_[2];
        }
        return $class->$orig($params);
    }
};

sub _build_mimetype {
    my ($self) = @_;
    
    my $filename = $self->filename;
    my $filetype;
    
    if (defined $filename
        && lc($filename->basename) =~ /\.([0-9a-z]{1,4})$/)  {
        my $mimetype = MIME::Types->new->mimeTypeOf($1);
        $filetype = $mimetype->type
            if defined $mimetype;
    }
    
    unless (defined $filetype) {
        my $filecontent = $self->filecontent;
        $filetype = $self->_check_magic_string($filecontent);
    }
    
    $filetype ||= 'application/octet-stream';
    
    return $filetype;
}

sub _build_name {
    my ($self) = @_;
    
    my $filename = $self->filename;
    my $name;
    
    if (defined $filename) {
        $name = $filename->basename;
    }
    
    unless (defined $name
        && $name !~ m/^\s*$/) {
        croak('Could not determine the attachment name automatically');
    }
    
    return $name;
}

sub serialize {
    my ($self) = @_;
    
    return $self->cache
        if ($self->has_cache);
    
    my $file = $self->file;
    my $accessor;
    my $value;
    
    if (blessed $file) {
        if ($file->isa('IO::File')) {
            $accessor = 'Data';
            $value = $self->filecontent;
        } elsif ($file->isa('Path::Class::File')) {
            $accessor = 'Path';
            $value = $file->stringify;
        }
    } else {
        $accessor = 'Data';
        $value = $$file;
    }
    
    my $entity = MIME::Entity->build(
        Disposition     => 'attachment',
        Type            => $self->mimetype,
        Top             => 0,
        Filename        => Mail::Builder::Utils::encode_mime($self->name),
        Encoding        => 'base64',
        $accessor       => $value,
    );
    
    $self->cache($entity);
    
    return $entity;
}

__PACKAGE__->meta->make_immutable;

1;

=encoding utf8

=head1 NAME

Mail::Builder::Attachment - Class for handling e-mail attachments

=head1 SYNOPSIS

  use Mail::Builder::Attachment;
  
  my $attachment1 = Mail::Builder::Attachment->new({
      file  => 'path/to/attachment.pdf',
      name  => 'LoveLetter.txt.vbs',
  });
  
  my $attachment2 = Mail::Builder::Attachment->new($fh);
  
  my $attachment_entity = $attachment1->serialize;
  
=head1 DESCRIPTION

This class handles e-mail attachments for Mail::Builder.

=head1 METHODS

=head2 Constructor

=head3 new

The constructor can be called in multiple ways

 Mail::Builder::Attachment->new({
     file       => Path | Path::Class::File | IO::File | FH | ScalarRef,
     [ name     => Attachment filename, ]
     [ mimetype => MIME type, ]
 })
 OR
 Mail::Builder::Image->new(
    Path | Path::Class::File | IO::File | FH | ScalarRef
    [, Attachment filename [, MIME type ]]
 )

See L<Accessors> for more details.

=head2 Public Methods

=head3 serialize

Returns the attachment as a L<MIME::Entity> object.

=head3 filename

If possible, returns the filename of the attachment file as a
L<Path::Class::File> object.

=head3 filecontent

Returns the content of the attachment file.

=head3 filehandle

If possible, returns a filehandle for the attachment file as a
L<IO::File> object.

=head2 Accessors

=head3 name

Name of the attachment as used in the e-mail message. If no name is provided
the current filename will be used.

=head3 mimetype

Mime type of the attachment.

If not provided the mime type is determined by analyzing the filename
extension.

=head3 file

Attachment file. Can be a

=over

=item * Path (or a Path::Class::File object)

=item * Filehandle (or an IO::File object)

=item * ScalarRef containing the attachment data

=back

=head1 AUTHOR

    Maroš Kollár
    CPAN ID: MAROS
    maros [at] k-1.com
    http://www.k-1.com

=cut