line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Email::MIME::RFC2047::Encoder; |
2
|
|
|
|
|
|
|
$Email::MIME::RFC2047::Encoder::VERSION = '0.95'; |
3
|
5
|
|
|
5
|
|
21989
|
use strict; |
|
5
|
|
|
|
|
6
|
|
|
5
|
|
|
|
|
132
|
|
4
|
5
|
|
|
5
|
|
17
|
use warnings; |
|
5
|
|
|
|
|
6
|
|
|
5
|
|
|
|
|
121
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
# ABSTRACT: Encoding of non-ASCII MIME headers |
7
|
|
|
|
|
|
|
|
8
|
5
|
|
|
5
|
|
486
|
use Encode (); |
|
5
|
|
|
|
|
6999
|
|
|
5
|
|
|
|
|
85
|
|
9
|
5
|
|
|
5
|
|
406
|
use MIME::Base64 (); |
|
5
|
|
|
|
|
441
|
|
|
5
|
|
|
|
|
4404
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
my $rfc_specials = '()<>\[\]:;\@\\,."'; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
sub new { |
14
|
11
|
|
|
11
|
1
|
5684
|
my $package = shift; |
15
|
11
|
50
|
|
|
|
47
|
my $options = ref($_[0]) ? $_[0] : { @_ }; |
16
|
|
|
|
|
|
|
|
17
|
11
|
|
|
|
|
21
|
my ($encoding, $method) = ($options->{encoding}, $options->{method}); |
18
|
|
|
|
|
|
|
|
19
|
11
|
100
|
|
|
|
23
|
if (!defined($encoding)) { |
20
|
6
|
|
|
|
|
7
|
$encoding = 'utf-8'; |
21
|
6
|
50
|
|
|
|
19
|
$method = 'Q' if !defined($method); |
22
|
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
else { |
24
|
5
|
50
|
|
|
|
19
|
$method = 'B' if !defined($method); |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
11
|
50
|
|
|
|
46
|
my $encoder = Encode::find_encoding($encoding) |
28
|
|
|
|
|
|
|
or die("encoding '$encoding' not found"); |
29
|
|
|
|
|
|
|
|
30
|
11
|
|
|
|
|
649
|
my $self = { |
31
|
|
|
|
|
|
|
encoding => $encoding, |
32
|
|
|
|
|
|
|
encoder => $encoder, |
33
|
|
|
|
|
|
|
method => uc($method), |
34
|
|
|
|
|
|
|
}; |
35
|
|
|
|
|
|
|
|
36
|
11
|
|
|
|
|
35
|
return bless($self, $package); |
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub encode_text { |
40
|
13
|
|
|
13
|
1
|
4652
|
my ($self, $string) = @_; |
41
|
|
|
|
|
|
|
|
42
|
13
|
|
|
|
|
25
|
return $self->_encode('text', $string); |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub encode_phrase { |
46
|
28
|
|
|
28
|
1
|
4529
|
my ($self, $string) = @_; |
47
|
|
|
|
|
|
|
|
48
|
28
|
|
|
|
|
50
|
return $self->_encode('phrase', $string); |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub _encode { |
52
|
41
|
|
|
41
|
|
44
|
my ($self, $mode, $string) = @_; |
53
|
|
|
|
|
|
|
|
54
|
41
|
|
|
|
|
51
|
my $encoder = $self->{encoder}; |
55
|
41
|
|
|
|
|
35
|
my $result = ''; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# $string is split on whitespace. Each $word is categorized into |
58
|
|
|
|
|
|
|
# 'mime', 'quoted' or 'text'. The intermediate result of the conversion of |
59
|
|
|
|
|
|
|
# consecutive words of the same types is accumulated in $buffer. |
60
|
|
|
|
|
|
|
# The type of the buffer is tracked in $buffer_type. |
61
|
|
|
|
|
|
|
# The method _finish_buffer is called to finish the encoding of the |
62
|
|
|
|
|
|
|
# buffered content and append to the result. |
63
|
41
|
|
|
|
|
33
|
my $buffer = ''; |
64
|
41
|
|
|
|
|
38
|
my $buffer_type; |
65
|
|
|
|
|
|
|
|
66
|
41
|
|
|
|
|
165
|
for my $word (split(/\s+/, $string)) { |
67
|
109
|
100
|
|
|
|
164
|
next if $word eq ''; # ignore leading white space |
68
|
|
|
|
|
|
|
|
69
|
107
|
|
|
|
|
126
|
$word =~ s/[\x00-\x1f\x7f]//g; # better remove control chars |
70
|
|
|
|
|
|
|
|
71
|
107
|
|
|
|
|
64
|
my $word_type; |
72
|
|
|
|
|
|
|
|
73
|
107
|
100
|
|
|
|
345
|
if ($word =~ /[\x80-\x{10ffff}]|(^=\?.*\?=\z)/s) { |
|
|
100
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# also encode any word that starts with '=?' and ends with '?=' |
75
|
54
|
|
|
|
|
44
|
$word_type = 'mime'; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
elsif ($mode eq 'phrase') { |
78
|
40
|
|
|
|
|
36
|
$word_type = 'quoted'; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
else { |
81
|
13
|
|
|
|
|
11
|
$word_type = 'text'; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
107
|
100
|
100
|
|
|
291
|
$self->_finish_buffer(\$result, $buffer_type, \$buffer) |
85
|
|
|
|
|
|
|
if $buffer ne '' && $buffer_type ne $word_type; |
86
|
107
|
|
|
|
|
79
|
$buffer_type = $word_type; |
87
|
|
|
|
|
|
|
|
88
|
107
|
100
|
|
|
|
168
|
if ($word_type eq 'text') { |
|
|
100
|
|
|
|
|
|
89
|
13
|
100
|
|
|
|
18
|
$result .= ' ' if $result ne ''; |
90
|
13
|
|
|
|
|
20
|
$result .= $word; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
elsif ($word_type eq 'quoted') { |
93
|
40
|
100
|
|
|
|
57
|
$buffer .= ' ' if $buffer ne ''; |
94
|
40
|
|
|
|
|
58
|
$buffer .= $word; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
else { |
97
|
54
|
|
|
|
|
52
|
my $max_len = 75 - 7 - length($self->{encoding}); |
98
|
54
|
50
|
|
|
|
81
|
$max_len = 3 * ($max_len >> 2) if $self->{method} eq 'B'; |
99
|
|
|
|
|
|
|
|
100
|
54
|
|
|
|
|
39
|
my @chars; |
101
|
54
|
100
|
|
|
|
73
|
push(@chars, ' ') if $buffer ne ''; |
102
|
54
|
|
|
|
|
97
|
push(@chars, split(//, $word)); |
103
|
|
|
|
|
|
|
|
104
|
54
|
|
|
|
|
60
|
for my $char (@chars) { |
105
|
224
|
|
|
|
|
128
|
my $chunk; |
106
|
|
|
|
|
|
|
|
107
|
224
|
50
|
|
|
|
573
|
if ($self->{method} eq 'B') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
108
|
0
|
|
|
|
|
0
|
$chunk = $encoder->encode($char); |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
elsif ($char =~ /[()<>@,;:\\".\[\]=?_]/) { |
111
|
|
|
|
|
|
|
# special character |
112
|
16
|
|
|
|
|
21
|
$chunk = sprintf('=%02x', ord($char)); |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
elsif ($char =~ /[\x80-\x{10ffff}]/) { |
115
|
|
|
|
|
|
|
# non-ASCII character |
116
|
|
|
|
|
|
|
|
117
|
100
|
|
|
|
|
205
|
my $enc_char = $encoder->encode($char); |
118
|
100
|
|
|
|
|
75
|
$chunk = ''; |
119
|
|
|
|
|
|
|
|
120
|
100
|
|
|
|
|
134
|
for my $byte (unpack('C*', $enc_char)) { |
121
|
196
|
|
|
|
|
261
|
$chunk .= sprintf('=%02x', $byte); |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
elsif ($char eq ' ') { |
125
|
28
|
|
|
|
|
24
|
$chunk = '_'; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
else { |
128
|
80
|
|
|
|
|
63
|
$chunk = $char; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
224
|
100
|
|
|
|
280
|
if (length($buffer) + length($chunk) <= $max_len) { |
132
|
218
|
|
|
|
|
238
|
$buffer .= $chunk; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
else { |
135
|
6
|
|
|
|
|
11
|
$self->_finish_buffer(\$result, 'mime', \$buffer); |
136
|
6
|
|
|
|
|
11
|
$buffer = $chunk; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
41
|
100
|
|
|
|
115
|
$self->_finish_buffer(\$result, $buffer_type, \$buffer) |
143
|
|
|
|
|
|
|
if $buffer ne ''; |
144
|
|
|
|
|
|
|
|
145
|
41
|
|
|
|
|
93
|
return $result; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
sub _finish_buffer { |
149
|
59
|
|
|
59
|
|
65
|
my ($self, $result, $buffer_type, $buffer) = @_; |
150
|
|
|
|
|
|
|
|
151
|
59
|
100
|
|
|
|
95
|
$$result .= ' ' if $$result ne ''; |
152
|
|
|
|
|
|
|
|
153
|
59
|
100
|
|
|
|
92
|
if ($buffer_type eq 'quoted') { |
|
|
50
|
|
|
|
|
|
154
|
27
|
100
|
|
|
|
147
|
if ($$buffer =~ /[$rfc_specials]/) { |
155
|
|
|
|
|
|
|
# use quoted string if buffer contains special chars |
156
|
9
|
|
|
|
|
23
|
$$buffer =~ s/[\\"]/\\$&/g; |
157
|
|
|
|
|
|
|
|
158
|
9
|
|
|
|
|
20
|
$$result .= qq("$$buffer"); |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
else { |
161
|
18
|
|
|
|
|
29
|
$$result .= $$buffer; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
elsif ($buffer_type eq 'mime') { |
165
|
32
|
|
|
|
|
66
|
$$result .= "=?$self->{encoding}?$self->{method}?"; |
166
|
|
|
|
|
|
|
|
167
|
32
|
50
|
|
|
|
45
|
if ($self->{method} eq 'B') { |
168
|
0
|
|
|
|
|
0
|
$$result .= MIME::Base64::encode_base64($$buffer, ''); |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
else { |
171
|
32
|
|
|
|
|
34
|
$$result .= $$buffer; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
32
|
|
|
|
|
31
|
$$result .= '?='; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
59
|
|
|
|
|
58
|
$$buffer = ''; |
178
|
|
|
|
|
|
|
|
179
|
59
|
|
|
|
|
59
|
return; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
1; |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
__END__ |