package Courriel::Part::Multipart;
use strict;
use warnings;
use namespace::autoclean;
our $VERSION = '0.42';
use Courriel::HeaderAttribute;
use Courriel::Helpers qw( unique_boundary );
use Courriel::Types qw( ArrayRef NonEmptyStr Part );
use Email::MessageID;
use Moose;
use MooseX::StrictConstructor;
with 'Courriel::Role::Part';
has preamble => (
is => 'ro',
isa => NonEmptyStr,
predicate => 'has_preamble',
);
has epilogue => (
is => 'ro',
isa => NonEmptyStr,
predicate => 'has_epilogue',
);
has _parts => (
traits => ['Array'],
isa => ArrayRef [Part],
init_arg => 'parts',
required => 1,
handles => {
parts => 'elements',
part_count => 'count',
},
);
sub BUILD {
my $self = shift;
my $p = shift;
my $boundary = delete $p->{boundary} // unique_boundary;
my $existing = $self->content_type->attribute('boundary');
## no critic (Subroutines::ProtectPrivateSubs)
$self->content_type->_set_attribute(
boundary => Courriel::HeaderAttribute->new(
name => ( $existing ? $existing->name : 'boundary' ),
value => $boundary,
)
);
## use critic
$_->_set_container($self) for $self->parts;
return;
}
sub is_attachment {0}
sub is_inline {0}
sub is_multipart {1}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _stream_content {
my $self = shift;
my $output = shift;
$output->( $self->preamble, $Courriel::Helpers::CRLF )
if $self->has_preamble;
for my $part ( $self->parts ) {
$output->(
$Courriel::Helpers::CRLF,
'--',
$self->boundary,
$Courriel::Helpers::CRLF,
);
$part->stream_to( output => $output );
}
$output->(
$Courriel::Helpers::CRLF,
'--',
$self->boundary,
'--',
$Courriel::Helpers::CRLF
);
$output->( $self->epilogue, $Courriel::Helpers::CRLF )
if $self->has_epilogue;
return;
}
## use critic
sub boundary {
my $self = shift;
return $self->content_type->attribute_value('boundary');
}
__PACKAGE__->meta->make_immutable;
1;
# ABSTRACT: A part which contains other parts
__END__
=pod
=encoding utf-8
=head1 NAME
Courriel::Part::Multipart - A part which contains other parts
=head1 VERSION
version 0.42
=head1 SYNOPSIS
my $headers = $part->headers;
my $ct = $part->content_type;
for my $subpart ( $part->parts ) { ... }
=head1 DESCRIPTION
This class represents a multipart email part which contains other parts.
=head1 API
This class provides the following methods:
=head2 Courriel::Part::Multipart->new( ... )
This method creates a new part object. It accepts the following parameters:
=over 4
=item * parts
An array reference of part objects (either Single or Multipart). This is
required, but could be empty.
=item * content_type
A L<Courriel::Header::ContentType> object. This defaults to one with a mime type of
"multipart/mixed".
=item * boundary
The part boundary. If none is provided, a unique value will be generated.
=item * preamble
Content that appears before the first part boundary. This will be seen by
email clients that don't understand multipart messages.
=item * epilogue
Content that appears after the final part boundary. The spec allows for this,
but it's probably not very useful.
=item * headers
A L<Courriel::Headers> object containing headers for this part.
=back
=head2 $part->parts()
Returns an array (not a reference) of the parts this part contains.
=head2 $part->part_count()
Returns the number of parts this part contains.
=head2 $part->boundary()
Returns the part boundary.
=head2 $part->mime_type()
Returns the mime type for this part.
=head2 $part->content_type()
Returns the L<Courriel::Header::ContentType> object for this part.
=head2 $part->headers()
Returns the L<Courriel::Headers> object for this part.
=head2 $part->is_inline(), $part->is_attachment()
These methods always return false, but exist for the sake of providing a
consistent API between Single and Multipart part objects.
=head2 $part->is_multipart()
Returns true.
=head2 $part->preamble()
The preamble as passed to the constructor.
=head2 $part->epilogue()
The epilogue as passed to the constructor.
=head2 $part->container()
Returns the L<Courriel> or L<Courriel::Part::Multipart> object to which this
part belongs, if any. This is set when the part is added to another object.
=head2 $part->stream_to( output => $output )
This method will send the stringified part to the specified output. The
output can be a subroutine reference, a filehandle, or an object with a
C<print()> method. The output may be sent as a single string, as a list of
strings, or via multiple calls to the output.
=head2 $part->as_string()
Returns the part as a string, along with its headers. Lines will be terminated
with "\r\n".
=head1 ROLES
This class does the C<Courriel::Role::Part> and C<Courriel::Role::Streams>
roles.
=head1 SUPPORT
Bugs may be submitted through L<the RT bug tracker|http://rt.cpan.org/Public/Dist/Display.html?Name=Courriel>
(or L<bug-courriel@rt.cpan.org|mailto:bug-courriel@rt.cpan.org>).
I am also usually active on IRC as 'drolsky' on C<irc://irc.perl.org>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENCE
This software is Copyright (c) 2016 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
=cut