use strict;
use warnings;
package Email::MIME::Encode;
# ABSTRACT: a private helper for MIME header encoding
$Email::MIME::Encode::VERSION = '1.940';
use Email::Address;
use Encode ();
use MIME::Base64();
my %address_list_headers = map { $_ => undef } qw(from sender reply-to to cc bcc);
my %no_mime_headers = map { $_ => undef } qw(date message-id in-reply-to references downgraded-message-id downgraded-in-reply-to downgraded-references);
sub maybe_mime_encode_header {
my ($header, $val, $charset) = @_;
$header = lc $header;
my $header_length = length($header) + length(": ");
my $min_wrap_length = 78 - $header_length + 1;
return $val
unless _needs_encode($val) || $val =~ /[^\s]{$min_wrap_length,}/;
$header =~ s/^resent-//i;
return $val
if exists $no_mime_headers{$header};
return _address_list_encode($val, $charset)
if exists $address_list_headers{$header};
return mime_encode($val, $charset, $header_length);
}
sub _needs_encode {
my ($val) = @_;
return defined $val && $val =~ /(?:\P{ASCII}|=\?|[^\s]{79,}|^\s+|\s+$)/s;
}
sub _needs_encode_addr {
my ($val) = @_;
return _needs_encode($val) || ( defined $val && $val =~ /[:;,]/ );
}
sub _address_list_encode {
my ($val, $charset) = @_;
my @addrs = Email::Address->parse($val);
foreach (@addrs) {
my $phrase = $_->phrase;
# try to not split phrase into more encoded words (hence 0 for header_length)
# rather fold header around mime encoded word
$_->phrase(mime_encode($phrase, $charset, 0))
if _needs_encode_addr($phrase);
my $comment = $_->comment;
$_->comment(mime_encode($comment, $charset, 0))
if _needs_encode_addr($comment);
}
return join(', ', map { $_->format } @addrs);
}
# XXX this is copied directly out of Courriel::Header
# eventually, this should be extracted out into something that could be shared
sub mime_encode {
my ($text, $charset, $header_length) = @_;
$header_length = 0 unless defined $header_length;
my $enc_obj = Encode::find_encoding($charset);
my $head = '=?' . $enc_obj->mime_name() . '?B?';
my $tail = '?=';
my $mime_length = length($head) + length($tail);
# This code is copied from Mail::Message::Field::Full in the Mail-Box
# distro.
my $real_length = int( ( 75 - $mime_length ) / 4 ) * 3;
my $first_length = int( ( 75 - $header_length - $mime_length ) / 4 ) * 3;
my @result;
my $chunk = q{};
my $first_processed = 0;
while ( length( my $chr = substr( $text, 0, 1, '' ) ) ) {
my $chr = $enc_obj->encode( $chr, 0 );
if ( length($chunk) + length($chr) > ( $first_processed ? $real_length : $first_length ) ) {
if ( length($chunk) > 0 ) {
push @result, $head . MIME::Base64::encode_base64( $chunk, q{} ) . $tail;
$chunk = q{};
}
$first_processed = 1
unless $first_processed;
}
$chunk .= $chr;
}
push @result, $head . MIME::Base64::encode_base64( $chunk, q{} ) . $tail
if length $chunk;
return join q{ }, @result;
}
sub maybe_mime_decode_header {
my ($header, $val) = @_;
$header = lc $header;
$header =~ s/^resent-//i;
return $val
if exists $no_mime_headers{$header};
return mime_decode($val);
}
sub mime_decode {
my ($text) = @_;
return undef unless defined $text;
# The eval is to cope with unknown encodings, like Latin-62, or other
# nonsense that gets put in there by spammers and weirdos
# -- rjbs, 2014-12-04
local $@;
my $result = eval { Encode::decode("MIME-Header", $text) };
return defined $result ? $result : $text;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Email::MIME::Encode - a private helper for MIME header encoding
=head1 VERSION
version 1.940
=head1 AUTHORS
=over 4
=item *
Ricardo SIGNES <rjbs@cpan.org>
=item *
Casey West <casey@geeknest.com>
=item *
Simon Cozens <simon@cpan.org>
=back
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2004 by Simon Cozens and Casey West.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut