diff options
Diffstat (limited to 'Master/tlpkg/tlperl/lib/Encode/MIME/Header.pm')
-rw-r--r-- | Master/tlpkg/tlperl/lib/Encode/MIME/Header.pm | 516 |
1 files changed, 349 insertions, 167 deletions
diff --git a/Master/tlpkg/tlperl/lib/Encode/MIME/Header.pm b/Master/tlpkg/tlperl/lib/Encode/MIME/Header.pm index ba6adba4758..ad14dba3740 100644 --- a/Master/tlpkg/tlperl/lib/Encode/MIME/Header.pm +++ b/Master/tlpkg/tlperl/lib/Encode/MIME/Header.pm @@ -1,22 +1,25 @@ package Encode::MIME::Header; use strict; use warnings; -no warnings 'redefine'; -our $VERSION = do { my @r = ( q$Revision: 2.19 $ =~ /\d+/g ); sprintf "%d." . "%02d" x $#r, @r }; -use Encode qw(find_encoding encode_utf8 decode_utf8); -use MIME::Base64; -use Carp; +our $VERSION = do { my @r = ( q$Revision: 2.24 $ =~ /\d+/g ); sprintf "%d." . "%02d" x $#r, @r }; + +use Carp (); +use Encode (); +use MIME::Base64 (); my %seed = ( - decode_b => '1', # decodes 'B' encoding ? - decode_q => '1', # decodes 'Q' encoding ? - encode => 'B', # encode with 'B' or 'Q' ? - bpl => 75, # bytes per line + decode_b => 1, # decodes 'B' encoding ? + decode_q => 1, # decodes 'Q' encoding ? + encode => 'B', # encode with 'B' or 'Q' ? + charset => 'UTF-8', # encode charset + bpl => 75, # bytes per line ); -$Encode::Encoding{'MIME-Header'} = - bless { %seed, Name => 'MIME-Header', } => __PACKAGE__; +$Encode::Encoding{'MIME-Header'} = bless { + %seed, + Name => 'MIME-Header', +} => __PACKAGE__; $Encode::Encoding{'MIME-B'} = bless { %seed, @@ -26,7 +29,7 @@ $Encode::Encoding{'MIME-B'} = bless { $Encode::Encoding{'MIME-Q'} = bless { %seed, - decode_q => 1, + decode_b => 0, encode => 'Q', Name => 'MIME-Q', } => __PACKAGE__; @@ -36,150 +39,265 @@ use parent qw(Encode::Encoding); sub needs_lines { 1 } sub perlio_ok { 0 } +# RFC 2047 and RFC 2231 grammar +my $re_charset = qr/[!"#\$%&'+\-0-9A-Z\\\^_`a-z\{\|\}~]+/; +my $re_language = qr/[A-Za-z]{1,8}(?:-[0-9A-Za-z]{1,8})*/; +my $re_encoding = qr/[QqBb]/; +my $re_encoded_text = qr/[^\?]*/; +my $re_encoded_word = qr/=\?$re_charset(?:\*$re_language)?\?$re_encoding\?$re_encoded_text\?=/; +my $re_capture_encoded_word = qr/=\?($re_charset)((?:\*$re_language)?)\?($re_encoding\?$re_encoded_text)\?=/; +my $re_capture_encoded_word_split = qr/=\?($re_charset)((?:\*$re_language)?)\?($re_encoding)\?($re_encoded_text)\?=/; + +# in strict mode check also for valid base64 characters and also for valid quoted printable codes +my $re_encoding_strict_b = qr/[Bb]/; +my $re_encoding_strict_q = qr/[Qq]/; +my $re_encoded_text_strict_b = qr/[0-9A-Za-z\+\/]*={0,2}/; +my $re_encoded_text_strict_q = qr/(?:[^\?\s=]|=[0-9A-Fa-f]{2})*/; +my $re_encoded_word_strict = qr/=\?$re_charset(?:\*$re_language)?\?(?:$re_encoding_strict_b\?$re_encoded_text_strict_b|$re_encoding_strict_q\?$re_encoded_text_strict_q)\?=/; +my $re_capture_encoded_word_strict = qr/=\?($re_charset)((?:\*$re_language)?)\?($re_encoding_strict_b\?$re_encoded_text_strict_b|$re_encoding_strict_q\?$re_encoded_text_strict_q)\?=/; + +my $re_newline = qr/(?:\r\n|[\r\n])/; + +# in strict mode encoded words must be always separated by spaces or tabs (or folded newline) +# except in comments when separator between words and comment round brackets can be omitted +my $re_word_begin_strict = qr/(?:(?:[ \t]|\A)\(?|(?:[^\\]|\A)\)\()/; +my $re_word_sep_strict = qr/(?:$re_newline?[ \t])+/; +my $re_word_end_strict = qr/(?:\)\(|\)?(?:$re_newline?[ \t]|\z))/; + +my $re_match = qr/()((?:$re_encoded_word\s*)*$re_encoded_word)()/; +my $re_match_strict = qr/($re_word_begin_strict)((?:$re_encoded_word_strict$re_word_sep_strict)*$re_encoded_word_strict)(?=$re_word_end_strict)/; + +my $re_capture = qr/$re_capture_encoded_word(?:\s*)?/; +my $re_capture_strict = qr/$re_capture_encoded_word_strict$re_word_sep_strict?/; + +our $STRICT_DECODE = 0; + sub decode($$;$) { - use utf8; - my ( $obj, $str, $chk ) = @_; - # zap spaces between encoded words - $str =~ s/\?=\s+=\?/\?==\?/gos; - - # multi-line header to single line - $str =~ s/(?:\r\n|[\r\n])[ \t]//gos; - - 1 while ( $str =~ - s/(=\?[-0-9A-Za-z_]+\?[Qq]\?)([^?]*?)\?=\1([^?]*?\?=)/$1$2$3/ ) - ; # Concat consecutive QP encoded mime headers - # Fixes breaking inside multi-byte characters - - $str =~ s{ - =\? # begin encoded word - ([-0-9A-Za-z_]+) # charset (encoding) - (?:\*[A-Za-z]{1,8}(?:-[A-Za-z]{1,8})*)? # language (RFC 2231) - \?([QqBb])\? # delimiter - (.*?) # Base64-encodede contents - \?= # end encoded word - }{ - if (uc($2) eq 'B'){ - $obj->{decode_b} or croak qq(MIME "B" unsupported); - decode_b($1, $3, $chk); - } elsif (uc($2) eq 'Q'){ - $obj->{decode_q} or croak qq(MIME "Q" unsupported); - decode_q($1, $3, $chk); - } else { - croak qq(MIME "$2" encoding is nonexistent!); + my ($obj, $str, $chk) = @_; + + my $re_match_decode = $STRICT_DECODE ? $re_match_strict : $re_match; + my $re_capture_decode = $STRICT_DECODE ? $re_capture_strict : $re_capture; + + my $stop = 0; + my $output = substr($str, 0, 0); # to propagate taintedness + + # decode each line separately, match whole continuous folded line at one call + 1 while not $stop and $str =~ s{^((?:[^\r\n]*(?:$re_newline[ \t])?)*)($re_newline)?}{ + + my $line = $1; + my $sep = defined $2 ? $2 : ''; + + $stop = 1 unless length($line) or length($sep); + + # NOTE: this code partially could break $chk support + # in non strict mode concat consecutive encoded mime words with same charset, language and encoding + # fixes breaking inside multi-byte characters + 1 while not $STRICT_DECODE and $line =~ s/$re_capture_encoded_word_split\s*=\?\1\2\?\3\?($re_encoded_text)\?=/=\?$1$2\?$3\?$4$5\?=/so; + + # process sequence of encoded MIME words at once + 1 while not $stop and $line =~ s{^(.*?)$re_match_decode}{ + + my $begin = $1 . $2; + my $words = $3; + + $begin =~ tr/\r\n//d; + $output .= $begin; + + # decode one MIME word + 1 while not $stop and $words =~ s{^(.*?)($re_capture_decode)}{ + + $output .= $1; + my $orig = $2; + my $charset = $3; + my ($mime_enc, $text) = split /\?/, $5; + + $text =~ tr/\r\n//d; + + my $enc = Encode::find_mime_encoding($charset); + + # in non strict mode allow also perl encoding aliases + if ( not defined $enc and not $STRICT_DECODE ) { + # make sure that decoded string will be always strict UTF-8 + $charset = 'UTF-8' if lc($charset) eq 'utf8'; + $enc = Encode::find_encoding($charset); + } + + if ( not defined $enc ) { + Carp::croak qq(Unknown charset "$charset") if not ref $chk and $chk & Encode::DIE_ON_ERR; + Carp::carp qq(Unknown charset "$charset") if not ref $chk and $chk & Encode::WARN_ON_ERR; + $stop = 1 if not ref $chk and $chk & Encode::RETURN_ON_ERR; + $output .= ($output =~ /(?:\A|[ \t])$/ ? '' : ' ') . $orig unless $stop; # $orig mime word is separated by whitespace + $stop ? $orig : ''; + } else { + if ( uc($mime_enc) eq 'B' and $obj->{decode_b} ) { + my $decoded = _decode_b($enc, $text, $chk); + $stop = 1 if not defined $decoded and not ref $chk and $chk & Encode::RETURN_ON_ERR; + $output .= (defined $decoded ? $decoded : $text) unless $stop; + $stop ? $orig : ''; + } elsif ( uc($mime_enc) eq 'Q' and $obj->{decode_q} ) { + my $decoded = _decode_q($enc, $text, $chk); + $stop = 1 if not defined $decoded and not ref $chk and $chk & Encode::RETURN_ON_ERR; + $output .= (defined $decoded ? $decoded : $text) unless $stop; + $stop ? $orig : ''; + } else { + Carp::croak qq(MIME "$mime_enc" unsupported) if not ref $chk and $chk & Encode::DIE_ON_ERR; + Carp::carp qq(MIME "$mime_enc" unsupported) if not ref $chk and $chk & Encode::WARN_ON_ERR; + $stop = 1 if not ref $chk and $chk & Encode::RETURN_ON_ERR; + $output .= ($output =~ /(?:\A|[ \t])$/ ? '' : ' ') . $orig unless $stop; # $orig mime word is separated by whitespace + $stop ? $orig : ''; + } + } + + }se; + + if ( not $stop ) { + $output .= $words; + $words = ''; + } + + $words; + + }se; + + if ( not $stop ) { + $line =~ tr/\r\n//d; + $output .= $line . $sep; + $line = ''; + $sep = ''; } - }egox; - $_[1] = $str if $chk; - return $str; -} -sub decode_b { - my $enc = shift; - my $d = find_encoding($enc) or croak qq(Unknown encoding "$enc"); - my $db64 = decode_base64(shift); - my $chk = shift; - return $d->name eq 'utf8' - ? Encode::decode_utf8($db64) - : $d->decode( $db64, $chk || Encode::FB_PERLQQ ); -} + $line . $sep; -sub decode_q { - my ( $enc, $q, $chk ) = @_; - my $d = find_encoding($enc) or croak qq(Unknown encoding "$enc"); - $q =~ s/_/ /go; - $q =~ s/=([0-9A-Fa-f]{2})/pack("C", hex($1))/ego; - return $d->name eq 'utf8' - ? Encode::decode_utf8($q) - : $d->decode( $q, $chk || Encode::FB_PERLQQ ); -} + }se; -my $especials = - join( '|' => map { quotemeta( chr($_) ) } - unpack( "C*", qq{()<>,;:"'/[]?=} ) ); + $_[1] = $str if not ref $chk and $chk and !($chk & Encode::LEAVE_SRC); + return $output; +} -my $re_encoded_word = qr{ - =\? # begin encoded word - (?:[-0-9A-Za-z_]+) # charset (encoding) - (?:\*[A-Za-z]{1,8}(?:-[A-Za-z]{1,8})*)? # language (RFC 2231) - \?(?:[QqBb])\? # delimiter - (?:.*?) # Base64-encodede contents - \?= # end encoded word -}xo; +sub _decode_b { + my ($enc, $text, $chk) = @_; + # MIME::Base64::decode ignores everything after a '=' padding character + # in non strict mode split string after each sequence of padding characters and decode each substring + my $octets = $STRICT_DECODE ? + MIME::Base64::decode($text) : + join('', map { MIME::Base64::decode($_) } split /(?<==)(?=[^=])/, $text); + return _decode_octets($enc, $octets, $chk); +} -my $re_especials = qr{$re_encoded_word|$especials}xo; +sub _decode_q { + my ($enc, $text, $chk) = @_; + $text =~ s/_/ /go; + $text =~ s/=([0-9A-Fa-f]{2})/pack('C', hex($1))/ego; + return _decode_octets($enc, $text, $chk); +} -# cf: -# https://rt.cpan.org/Ticket/Display.html?id=88717 -# https://www.ietf.org/rfc/rfc0822.txt -my $re_linear_white_space = qr{(?:[ \t]|\r\n?)}; +sub _decode_octets { + my ($enc, $octets, $chk) = @_; + $chk &= ~Encode::LEAVE_SRC if not ref $chk and $chk; + local $Carp::CarpLevel = $Carp::CarpLevel + 1; # propagate Carp messages back to caller + my $output = $enc->decode($octets, $chk); + return undef if not ref $chk and $chk and $octets ne ''; + return $output; +} sub encode($$;$) { - my ( $obj, $str, $chk ) = @_; - my @line = (); - for my $line ( split /\r\n|[\r\n]/o, $str ) { - my ( @word, @subline ); - if ($line =~ /\A([\w\-]+:\s+)(.*)\z/o) { - push @word, $1, $obj->_encode($2); # "X-Header-Name: ..." + my ($obj, $str, $chk) = @_; + my $output = $obj->_fold_line($obj->_encode_string($str, $chk)); + $_[1] = $str if not ref $chk and $chk and !($chk & Encode::LEAVE_SRC); + return $output . substr($str, 0, 0); # to propagate taintedness +} + +sub _fold_line { + my ($obj, $line) = @_; + my $bpl = $obj->{bpl}; + my $output = ''; + + while ( length($line) ) { + if ( $line =~ s/^(.{0,$bpl})(\s|\z)// ) { + $output .= $1; + $output .= "\r\n" . $2 if length($line); + } elsif ( $line =~ s/(\s)(.*)$// ) { + $output .= $line; + $line = $2; + $output .= "\r\n" . $1 if length($line); } else { - push @word, $obj->_encode($line); # anything else + $output .= $line; + last; } - my $subline = ''; - for my $word (@word) { - use bytes (); - if ( bytes::length($subline) + bytes::length($word) > - $obj->{bpl} - 1 ) - { - push @subline, $subline; - $subline = ''; - } - $subline .= ' ' if ($subline =~ /\?=$/ and $word =~ /^=\?/); - $subline .= $word; - } - length($subline) and push @subline, $subline; - push @line, join( "\n " => grep !/^$/, @subline ); } - $_[1] = '' if $chk; - return (substr($str, 0, 0) . join( "\n", @line )); -} - -use constant HEAD => '=?UTF-8?'; -use constant TAIL => '?='; -use constant SINGLE => { B => \&_encode_b, Q => \&_encode_q, }; -sub _encode { - my ( $o, $str ) = @_; - my $enc = $o->{encode}; - my $llen = ( $o->{bpl} - length(HEAD) - 2 - length(TAIL) ); + return $output; +} - # to coerce a floating-point arithmetics, the following contains - # .0 in numbers -- dankogai - $llen *= $enc eq 'B' ? 3.0 / 4.0 : 1.0 / 3.0; +sub _encode_string { + my ($obj, $str, $chk) = @_; + my $wordlen = $obj->{bpl} > 76 ? 76 : $obj->{bpl}; + my $enc = Encode::find_mime_encoding($obj->{charset}); + my $enc_chk = (not ref $chk and $chk) ? ($chk | Encode::LEAVE_SRC) : $chk; my @result = (); - my $chunk = ''; - while ( length( my $chr = substr( $str, 0, 1, '' ) ) ) { - use bytes (); - if ( bytes::length($chunk) + bytes::length($chr) > $llen ) { - push @result, SINGLE->{$enc}($chunk); - $chunk = ''; + my $octets = ''; + while ( length( my $chr = substr($str, 0, 1, '') ) ) { + my $seq; + { + local $Carp::CarpLevel = $Carp::CarpLevel + 1; # propagate Carp messages back to caller + $seq = $enc->encode($chr, $enc_chk); + } + if ( not length($seq) ) { + substr($str, 0, 0, $chr); + last; + } + if ( $obj->_encoded_word_len($octets . $seq) > $wordlen ) { + push @result, $obj->_encode_word($octets); + $octets = ''; } - $chunk .= $chr; + $octets .= $seq; } - length($chunk) and push @result, SINGLE->{$enc}($chunk); - return @result; + length($octets) and push @result, $obj->_encode_word($octets); + $_[1] = $str if not ref $chk and $chk and !($chk & Encode::LEAVE_SRC); + return join(' ', @result); +} + +sub _encode_word { + my ($obj, $octets) = @_; + my $charset = $obj->{charset}; + my $encode = $obj->{encode}; + my $text = $encode eq 'B' ? _encode_b($octets) : _encode_q($octets); + return "=?$charset?$encode?$text?="; +} + +sub _encoded_word_len { + my ($obj, $octets) = @_; + my $charset = $obj->{charset}; + my $encode = $obj->{encode}; + my $text_len = $encode eq 'B' ? _encoded_b_len($octets) : _encoded_q_len($octets); + return length("=?$charset?$encode??=") + $text_len; } sub _encode_b { - HEAD . 'B?' . encode_base64( encode_utf8(shift), '' ) . TAIL; + my ($octets) = @_; + return MIME::Base64::encode($octets, ''); +} + +sub _encoded_b_len { + my ($octets) = @_; + return ( length($octets) + 2 ) / 3 * 4; } +my $re_invalid_q_char = qr/[^0-9A-Za-z !*+\-\/]/; + sub _encode_q { - my $chunk = shift; - $chunk = encode_utf8($chunk); - $chunk =~ s{ - ([^0-9A-Za-z]) - }{ - join("" => map {sprintf "=%02X", $_} unpack("C*", $1)) - }egox; - return HEAD . 'Q?' . $chunk . TAIL; + my ($octets) = @_; + $octets =~ s{($re_invalid_q_char)}{ + join('', map { sprintf('=%02X', $_) } unpack('C*', $1)) + }egox; + $octets =~ s/ /_/go; + return $octets; +} + +sub _encoded_q_len { + my ($octets) = @_; + my $invalid_count = () = $octets =~ /$re_invalid_q_char/sgo; + return ( $invalid_count * 3 ) + ( length($octets) - $invalid_count ); } 1; @@ -187,55 +305,119 @@ __END__ =head1 NAME -Encode::MIME::Header -- MIME 'B' and 'Q' header encoding +Encode::MIME::Header -- MIME encoding for an unstructured email header =head1 SYNOPSIS - use Encode qw/encode decode/; - $utf8 = decode('MIME-Header', $header); - $header = encode('MIME-Header', $utf8); + use Encode qw(encode decode); -=head1 ABSTRACT + my $mime_str = encode("MIME-Header", "Sample:Text \N{U+263A}"); + # $mime_str is "=?UTF-8?B?U2FtcGxlOlRleHQg4pi6?=" -This module implements RFC 2047 Mime Header Encoding. There are 3 -variant encoding names; C<MIME-Header>, C<MIME-B> and C<MIME-Q>. The -difference is described below + my $mime_q_str = encode("MIME-Q", "Sample:Text \N{U+263A}"); + # $mime_q_str is "=?UTF-8?Q?Sample=3AText_=E2=98=BA?=" - decode() encode() - ---------------------------------------------- - MIME-Header Both B and Q =?UTF-8?B?....?= - MIME-B B only; Q croaks =?UTF-8?B?....?= - MIME-Q Q only; B croaks =?UTF-8?Q?....?= + my $str = decode("MIME-Header", + "=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=\r\n " . + "=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=" + ); + # $str is "If you can read this you understand the example." -=head1 DESCRIPTION + use Encode qw(decode :fallbacks); + use Encode::MIME::Header; + local $Encode::MIME::Header::STRICT_DECODE = 1; + my $strict_string = decode("MIME-Header", $mime_string, FB_CROAK); + # use strict decoding and croak on errors -When you decode(=?I<encoding>?I<X>?I<ENCODED WORD>?=), I<ENCODED WORD> -is extracted and decoded for I<X> encoding (B for Base64, Q for -Quoted-Printable). Then the decoded chunk is fed to -decode(I<encoding>). So long as I<encoding> is supported by Encode, -any source encoding is fine. +=head1 ABSTRACT -When you encode, it just encodes UTF-8 string with I<X> encoding then -quoted with =?UTF-8?I<X>?....?= . The parts that RFC 2047 forbids to -encode are left as is and long lines are folded within 76 bytes per -line. +This module implements L<RFC 2047|https://tools.ietf.org/html/rfc2047> MIME +encoding for an unstructured field body of the email header. It can also be +used for L<RFC 822|https://tools.ietf.org/html/rfc822> 'text' token. However, +it cannot be used directly for the whole header with the field name or for the +structured header fields like From, To, Cc, Message-Id, etc... There are 3 +encoding names supported by this module: C<MIME-Header>, C<MIME-B> and +C<MIME-Q>. -=head1 BUGS +=head1 DESCRIPTION -It would be nice to support encoding to non-UTF8, such as =?ISO-2022-JP? -and =?ISO-8859-1?= but that makes the implementation too complicated. -These days major mail agents all support =?UTF-8? so I think it is -just good enough. +Decode method takes an unstructured field body of the email header (or +L<RFC 822|https://tools.ietf.org/html/rfc822> 'text' token) as its input and +decodes each MIME encoded-word from input string to a sequence of bytes +according to L<RFC 2047|https://tools.ietf.org/html/rfc2047> and +L<RFC 2231|https://tools.ietf.org/html/rfc2231>. Subsequently, each sequence +of bytes with the corresponding MIME charset is decoded with +L<the Encode module|Encode> and finally, one output string is returned. Text +parts of the input string which do not contain MIME encoded-word stay +unmodified in the output string. Folded newlines between two consecutive MIME +encoded-words are discarded, others are preserved in the output string. +C<MIME-B> can decode Base64 variant, C<MIME-Q> can decode Quoted-Printable +variant and C<MIME-Header> can decode both of them. If L<Encode module|Encode> +does not support particular MIME charset or chosen variant then an action based +on L<CHECK flags|Encode/Handling Malformed Data> is performed (by default, the +MIME encoded-word is not decoded). + +Encode method takes a scalar string as its input and uses +L<strict UTF-8|Encode/UTF-8 vs. utf8 vs. UTF8> encoder for encoding it to UTF-8 +bytes. Then a sequence of UTF-8 bytes is encoded into MIME encoded-words +(C<MIME-Header> and C<MIME-B> use a Base64 variant while C<MIME-Q> uses a +Quoted-Printable variant) where each MIME encoded-word is limited to 75 +characters. MIME encoded-words are separated by C<CRLF SPACE> and joined to +one output string. Output string is suitable for unstructured field body of +the email header. + +Both encode and decode methods propagate +L<CHECK flags|Encode/Handling Malformed Data> when encoding and decoding the +MIME charset. -Due to popular demand, 'MIME-Header-ISO_2022_JP' was introduced by -Makamaka. Thre are still too many MUAs especially cellular phone -handsets which does not grok UTF-8. +=head1 BUGS -=head1 SEE ALSO +Versions prior to 2.22 (part of Encode 2.83) have a malfunctioning decoder +and encoder. The MIME encoder infamously inserted additional spaces or +discarded white spaces between consecutive MIME encoded-words, which led to +invalid MIME headers produced by this module. The MIME decoder had a tendency +to discard white spaces, incorrectly interpret data or attempt to decode Base64 +MIME encoded-words as Quoted-Printable. These problems were fixed in version +2.22. It is highly recommended not to use any version prior 2.22! + +Versions prior to 2.24 (part of Encode 2.87) ignored +L<CHECK flags|Encode/Handling Malformed Data>. The MIME encoder used +L<not strict utf8|Encode/UTF-8 vs. utf8 vs. UTF8> encoder for input Unicode +strings which could lead to invalid UTF-8 sequences. MIME decoder used also +L<not strict utf8|Encode/UTF-8 vs. utf8 vs. UTF8> decoder and additionally +called the decode method with a C<Encode::FB_PERLQQ> flag (thus user-specified +L<CHECK flags|Encode/Handling Malformed Data> were ignored). Moreover, it +automatically croaked when a MIME encoded-word contained unknown encoding. +Since version 2.24, this module uses +L<strict UTF-8|Encode/UTF-8 vs. utf8 vs. UTF8> encoder and decoder. And +L<CHECK flags|Encode/Handling Malformed Data> are correctly propagated. + +Since version 2.22 (part of Encode 2.83), the MIME encoder should be fully +compliant to L<RFC 2047|https://tools.ietf.org/html/rfc2047> and +L<RFC 2231|https://tools.ietf.org/html/rfc2231>. Due to the aforementioned +bugs in previous versions of the MIME encoder, there is a I<less strict> +compatible mode for the MIME decoder which is used by default. It should be +able to decode MIME encoded-words encoded by pre 2.22 versions of this module. +However, note that this is not correct according to +L<RFC 2047|https://tools.ietf.org/html/rfc2047>. + +In default I<not strict> mode the MIME decoder attempts to decode every substring +which looks like a MIME encoded-word. Therefore, the MIME encoded-words do not +need to be separated by white space. To enforce a correct I<strict> mode, set +variable C<$Encode::MIME::Header::STRICT_DECODE> to 1 e.g. by localizing: + + use Encode::MIME::Header; + local $Encode::MIME::Header::STRICT_DECODE = 1; + +=head1 AUTHORS + +Pali E<lt>pali@cpan.orgE<gt> -L<Encode> +=head1 SEE ALSO -RFC 2047, L<http://www.faqs.org/rfcs/rfc2047.html> and many other -locations. +L<Encode>, +L<RFC 822|https://tools.ietf.org/html/rfc822>, +L<RFC 2047|https://tools.ietf.org/html/rfc2047>, +L<RFC 2231|https://tools.ietf.org/html/rfc2231> =cut |