line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#-*- perl -*- |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package MIME::EncWords; |
4
|
|
|
|
|
|
|
require 5.005; |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=head1 NAME |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
MIME::EncWords - deal with RFC 2047 encoded words (improved) |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 SYNOPSIS |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
I is aimed to be another implimentation |
13
|
|
|
|
|
|
|
of L so that it will achieve more exact conformance with |
14
|
|
|
|
|
|
|
RFC 2047 (formerly RFC 1522) specifications. Additionally, it contains |
15
|
|
|
|
|
|
|
some improvements. |
16
|
|
|
|
|
|
|
Following synopsis and descriptions are inherited from its inspirer, |
17
|
|
|
|
|
|
|
then added descriptions on improvements (B<**>) or changes and |
18
|
|
|
|
|
|
|
clarifications (B<*>).> |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
Before reading further, you should see L to make sure that |
21
|
|
|
|
|
|
|
you understand where this module fits into the grand scheme of things. |
22
|
|
|
|
|
|
|
Go on, do it now. I'll wait. |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
Ready? Ok... |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
use MIME::EncWords qw(:all); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
### Decode the string into another string, forgetting the charsets: |
29
|
|
|
|
|
|
|
$decoded = decode_mimewords( |
30
|
|
|
|
|
|
|
'To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= ', |
31
|
|
|
|
|
|
|
); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
### Split string into array of decoded [DATA,CHARSET] pairs: |
34
|
|
|
|
|
|
|
@decoded = decode_mimewords( |
35
|
|
|
|
|
|
|
'To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= ', |
36
|
|
|
|
|
|
|
); |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
### Encode a single unsafe word: |
39
|
|
|
|
|
|
|
$encoded = encode_mimeword("\xABFran\xE7ois\xBB"); |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
### Encode a string, trying to find the unsafe words inside it: |
42
|
|
|
|
|
|
|
$encoded = encode_mimewords("Me and \xABFran\xE7ois\xBB in town"); |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head1 DESCRIPTION |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
Fellow Americans, you probably won't know what the hell this module |
47
|
|
|
|
|
|
|
is for. Europeans, Russians, et al, you probably do. C<:-)>. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
For example, here's a valid MIME header you might get: |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
From: =?US-ASCII?Q?Keith_Moore?= |
52
|
|
|
|
|
|
|
To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= |
53
|
|
|
|
|
|
|
CC: =?ISO-8859-1?Q?Andr=E9_?= Pirard |
54
|
|
|
|
|
|
|
Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?= |
55
|
|
|
|
|
|
|
=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?= |
56
|
|
|
|
|
|
|
=?US-ASCII?Q?.._cool!?= |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
The fields basically decode to (sorry, I can only approximate the |
59
|
|
|
|
|
|
|
Latin characters with 7 bit sequences /o and 'e): |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
From: Keith Moore |
62
|
|
|
|
|
|
|
To: Keld J/orn Simonsen |
63
|
|
|
|
|
|
|
CC: Andr'e Pirard |
64
|
|
|
|
|
|
|
Subject: If you can read this you understand the example... cool! |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
B: Fellow Americans, Europeans, you probably won't know |
67
|
|
|
|
|
|
|
what the hell this module is for. East Asians, et al, you probably do. |
68
|
|
|
|
|
|
|
C<(^_^)>. |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
For example, here's a valid MIME header you might get: |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
Subject: =?EUC-KR?B?sNTAuLinKGxhemluZXNzKSwgwvzB9ri7seIoaW1w?= |
73
|
|
|
|
|
|
|
=?EUC-KR?B?YXRpZW5jZSksILGzuLgoaHVicmlzKQ==?= |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
The fields basically decode to (sorry, I cannot approximate the |
76
|
|
|
|
|
|
|
non-Latin multibyte characters with any 7 bit sequences): |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
Subject: ???(laziness), ????(impatience), ??(hubris) |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=head1 PUBLIC INTERFACE |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=over 4 |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=cut |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
### Pragmas: |
87
|
5
|
|
|
5
|
|
1106890
|
use strict; |
|
5
|
|
|
|
|
16
|
|
|
5
|
|
|
|
|
289
|
|
88
|
5
|
|
|
5
|
|
27
|
use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS @ISA $Config); |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
511
|
|
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
### Exporting: |
91
|
5
|
|
|
5
|
|
31
|
use Exporter; |
|
5
|
|
|
|
|
24
|
|
|
5
|
|
|
|
|
587
|
|
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
%EXPORT_TAGS = (all => [qw(decode_mimewords |
94
|
|
|
|
|
|
|
encode_mimeword |
95
|
|
|
|
|
|
|
encode_mimewords)]); |
96
|
|
|
|
|
|
|
Exporter::export_ok_tags(qw(all)); |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
### Inheritance: |
99
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
### Other modules: |
102
|
5
|
|
|
5
|
|
32
|
use Carp qw(croak carp); |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
318
|
|
103
|
5
|
|
|
5
|
|
12919
|
use MIME::Base64; |
|
5
|
|
|
|
|
6496
|
|
|
5
|
|
|
|
|
391
|
|
104
|
5
|
|
|
5
|
|
410796
|
use MIME::Charset qw(:trans); |
|
5
|
|
|
|
|
987599
|
|
|
5
|
|
|
|
|
2155
|
|
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
my @ENCODE_SUBS = qw(FB_CROAK is_utf8 resolve_alias); |
107
|
|
|
|
|
|
|
if (MIME::Charset::USE_ENCODE) { |
108
|
5
|
|
|
5
|
|
29
|
eval "use ".MIME::Charset::USE_ENCODE." \@ENCODE_SUBS;"; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
306
|
|
109
|
|
|
|
|
|
|
if ($@) { # Perl 5.7.3 + Encode 0.40 |
110
|
|
|
|
|
|
|
eval "use ".MIME::Charset::USE_ENCODE." qw(is_utf8);"; |
111
|
|
|
|
|
|
|
require MIME::Charset::_Compat; |
112
|
|
|
|
|
|
|
for my $sub (@ENCODE_SUBS) { |
113
|
5
|
|
|
5
|
|
61
|
no strict "refs"; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
576
|
|
114
|
|
|
|
|
|
|
*{$sub} = \&{"MIME::Charset::_Compat::$sub"} |
115
|
|
|
|
|
|
|
unless $sub eq 'is_utf8'; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
} else { |
119
|
|
|
|
|
|
|
require Unicode::String; |
120
|
|
|
|
|
|
|
require MIME::Charset::_Compat; |
121
|
|
|
|
|
|
|
for my $sub (@ENCODE_SUBS) { |
122
|
5
|
|
|
5
|
|
31
|
no strict "refs"; |
|
5
|
|
|
|
|
21
|
|
|
5
|
|
|
|
|
37074
|
|
123
|
|
|
|
|
|
|
*{$sub} = \&{"MIME::Charset::_Compat::$sub"}; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
#------------------------------ |
128
|
|
|
|
|
|
|
# |
129
|
|
|
|
|
|
|
# Globals... |
130
|
|
|
|
|
|
|
# |
131
|
|
|
|
|
|
|
#------------------------------ |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
### The package version, both in 1.23 style *and* usable by MakeMaker: |
134
|
|
|
|
|
|
|
$VERSION = '1.014.2'; |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
### Public Configuration Attributes |
137
|
|
|
|
|
|
|
$Config = { |
138
|
|
|
|
|
|
|
%{$MIME::Charset::Config}, # Detect7bit, Replacement, Mapping |
139
|
|
|
|
|
|
|
Charset => 'ISO-8859-1', |
140
|
|
|
|
|
|
|
Encoding => 'A', |
141
|
|
|
|
|
|
|
Field => undef, |
142
|
|
|
|
|
|
|
Folding => "\n", |
143
|
|
|
|
|
|
|
MaxLineLen => 76, |
144
|
|
|
|
|
|
|
Minimal => 'YES', |
145
|
|
|
|
|
|
|
}; |
146
|
|
|
|
|
|
|
eval { require MIME::EncWords::Defaults; }; |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
### Private Constants |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
my $PRINTABLE = "\\x21-\\x7E"; |
151
|
|
|
|
|
|
|
#my $NONPRINT = "\\x00-\\x1F\\x7F-\\xFF"; |
152
|
|
|
|
|
|
|
my $NONPRINT = qr{[^$PRINTABLE]}; # Improvement: Unicode support. |
153
|
|
|
|
|
|
|
my $UNSAFE = qr{[^\x01-\x20$PRINTABLE]}; |
154
|
|
|
|
|
|
|
my $WIDECHAR = qr{[^\x00-\xFF]}; |
155
|
|
|
|
|
|
|
my $ASCIITRANS = qr{^(?:HZ-GB-2312|UTF-7)$}i; |
156
|
|
|
|
|
|
|
my $ASCIIINCOMPAT = qr{^UTF-(?:16|32)(?:BE|LE)?$}i; |
157
|
|
|
|
|
|
|
my $DISPNAMESPECIAL = "\\x22(),:;<>\\x40\\x5C"; # RFC5322 name-addr specials. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
#------------------------------ |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# _utf_to_unicode CSETOBJ, STR |
162
|
|
|
|
|
|
|
# Private: Convert UTF-16*/32* to Unicode or UTF-8. |
163
|
|
|
|
|
|
|
sub _utf_to_unicode { |
164
|
90
|
|
|
90
|
|
148
|
my $csetobj = shift; |
165
|
90
|
|
|
|
|
201
|
my $str = shift; |
166
|
|
|
|
|
|
|
|
167
|
90
|
100
|
|
|
|
493
|
return $str if is_utf8($str); |
168
|
|
|
|
|
|
|
|
169
|
48
|
|
|
|
|
238
|
return $csetobj->decode($str) |
170
|
|
|
|
|
|
|
if MIME::Charset::USE_ENCODE(); |
171
|
|
|
|
|
|
|
|
172
|
0
|
|
|
|
|
0
|
my $cset = $csetobj->as_string; |
173
|
0
|
|
|
|
|
0
|
my $unistr = Unicode::String->new(); |
174
|
0
|
0
|
0
|
|
|
0
|
if ($cset eq 'UTF-16' or $cset eq 'UTF-16BE') { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
175
|
0
|
|
|
|
|
0
|
$unistr->utf16($str); |
176
|
|
|
|
|
|
|
} elsif ($cset eq 'UTF-16LE') { |
177
|
0
|
|
|
|
|
0
|
$unistr->utf16le($str); |
178
|
|
|
|
|
|
|
} elsif ($cset eq 'UTF-32' or $cset eq 'UTF-32BE') { |
179
|
0
|
|
|
|
|
0
|
$unistr->utf32($str); |
180
|
|
|
|
|
|
|
} elsif ($cset eq 'UTF-32LE') { |
181
|
0
|
|
|
|
|
0
|
$unistr->utf32le($str); |
182
|
|
|
|
|
|
|
} else { |
183
|
0
|
|
|
|
|
0
|
croak "unknown transformation '$cset'"; |
184
|
|
|
|
|
|
|
} |
185
|
0
|
|
|
|
|
0
|
return $unistr->utf8; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
#------------------------------ |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
# _decode_B STRING |
191
|
|
|
|
|
|
|
# Private: used by _decode_header() to decode "B" encoding. |
192
|
|
|
|
|
|
|
# Improvement by this module: sanity check on encoded sequence. |
193
|
|
|
|
|
|
|
sub _decode_B { |
194
|
57
|
|
|
57
|
|
82
|
my $str = shift; |
195
|
57
|
50
|
33
|
|
|
835
|
unless ((length($str) % 4 == 0) and |
196
|
|
|
|
|
|
|
$str =~ m|^[A-Za-z0-9+/]+={0,2}$|) { |
197
|
0
|
|
|
|
|
0
|
return undef; |
198
|
|
|
|
|
|
|
} |
199
|
57
|
|
|
|
|
414
|
return decode_base64($str); |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# _decode_Q STRING |
203
|
|
|
|
|
|
|
# Private: used by _decode_header() to decode "Q" encoding, which is |
204
|
|
|
|
|
|
|
# almost, but not exactly, quoted-printable. :-P |
205
|
|
|
|
|
|
|
# Improvement by this module: sanity check on encoded sequence (>=1.012.3). |
206
|
|
|
|
|
|
|
sub _decode_Q { |
207
|
49
|
|
|
49
|
|
81
|
my $str = shift; |
208
|
49
|
50
|
|
|
|
770
|
if ($str =~ /=(?![0-9a-fA-F][0-9a-fA-F])/) { #XXX:" " and "\t" are allowed |
209
|
0
|
|
|
|
|
0
|
return undef; |
210
|
|
|
|
|
|
|
} |
211
|
49
|
|
|
|
|
142
|
$str =~ s/_/\x20/g; # RFC 2047, Q rule 2 |
212
|
49
|
|
|
|
|
179
|
$str =~ s/=([0-9a-fA-F]{2})/pack("C", hex($1))/ge; # RFC 2047, Q rule 1 |
|
182
|
|
|
|
|
752
|
|
213
|
49
|
|
|
|
|
127
|
$str; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# _encode_B STRING |
217
|
|
|
|
|
|
|
# Private: used by encode_mimeword() to encode "B" encoding. |
218
|
|
|
|
|
|
|
sub _encode_B { |
219
|
73
|
|
|
73
|
|
110
|
my $str = shift; |
220
|
73
|
|
|
|
|
605
|
encode_base64($str, ''); |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# _encode_Q STRING |
224
|
|
|
|
|
|
|
# Private: used by encode_mimeword() to encode "Q" encoding, which is |
225
|
|
|
|
|
|
|
# almost, but not exactly, quoted-printable. :-P |
226
|
|
|
|
|
|
|
# Improvement by this module: Spaces are escaped by ``_''. |
227
|
|
|
|
|
|
|
sub _encode_Q { |
228
|
71
|
|
|
71
|
|
175
|
my $str = shift; |
229
|
|
|
|
|
|
|
# Restrict characters to those listed in RFC 2047 section 5 (3) |
230
|
71
|
|
|
|
|
322
|
$str =~ s{[^-!*+/0-9A-Za-z]}{ |
231
|
489
|
100
|
|
|
|
2602
|
$& eq "\x20"? "_": sprintf("=%02X", ord($&)) |
232
|
|
|
|
|
|
|
}eog; |
233
|
71
|
|
|
|
|
437
|
$str; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
#------------------------------ |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=item decode_mimewords ENCODED, [OPTS...] |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
I |
241
|
|
|
|
|
|
|
Go through the string looking for RFC 2047-style "Q" |
242
|
|
|
|
|
|
|
(quoted-printable, sort of) or "B" (base64) encoding, and decode them. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
B splits the ENCODED string into a list of decoded |
245
|
|
|
|
|
|
|
C<[DATA, CHARSET]> pairs, and returns that list. Unencoded |
246
|
|
|
|
|
|
|
data are returned in a 1-element array C<[DATA]>, giving an effective |
247
|
|
|
|
|
|
|
CHARSET of C. |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
$enc = '=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= '; |
250
|
|
|
|
|
|
|
foreach (decode_mimewords($enc)) { |
251
|
|
|
|
|
|
|
print "", ($_[1] || 'US-ASCII'), ": ", $_[0], "\n"; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
B<**> |
255
|
|
|
|
|
|
|
However, adjacent encoded-words with same charset will be concatenated |
256
|
|
|
|
|
|
|
to handle multibyte sequences safely. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
B<**> |
259
|
|
|
|
|
|
|
Language information defined by RFC2231, section 5 will be additonal |
260
|
|
|
|
|
|
|
third element, if any. |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
B<*> |
263
|
|
|
|
|
|
|
Whitespaces surrounding unencoded data will not be stripped so that |
264
|
|
|
|
|
|
|
compatibility with L will be ensured. |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
B joins the "data" elements of the above |
267
|
|
|
|
|
|
|
list together, and returns that. I |
268
|
|
|
|
|
|
|
and probably I what you want, but if you know that all charsets |
269
|
|
|
|
|
|
|
in the ENCODED string are identical, it might be useful to you. |
270
|
|
|
|
|
|
|
(Before you use this, please see L, |
271
|
|
|
|
|
|
|
which is probably what you want.) |
272
|
|
|
|
|
|
|
B<**> |
273
|
|
|
|
|
|
|
See also "Charset" option below. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
In the event of a syntax error, $@ will be set to a description |
276
|
|
|
|
|
|
|
of the error, but parsing will continue as best as possible (so as to |
277
|
|
|
|
|
|
|
get I back when decoding headers). |
278
|
|
|
|
|
|
|
$@ will be false if no error was detected. |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
B<*> |
281
|
|
|
|
|
|
|
Malformed encoded-words will be kept encoded. |
282
|
|
|
|
|
|
|
In this case $@ will be set. |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
Any arguments past the ENCODED string are taken to define a hash of options. |
285
|
|
|
|
|
|
|
B<**> |
286
|
|
|
|
|
|
|
When Unicode/multibyte support is disabled |
287
|
|
|
|
|
|
|
(see L), |
288
|
|
|
|
|
|
|
these options will not have any effects. |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=over 4 |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
=item Charset |
293
|
|
|
|
|
|
|
B<**> |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
Name of character set by which data elements in scalar context |
296
|
|
|
|
|
|
|
will be converted. |
297
|
|
|
|
|
|
|
The default is no conversion. |
298
|
|
|
|
|
|
|
If this option is specified as special value C<"_UNICODE_">, |
299
|
|
|
|
|
|
|
returned value will be Unicode string. |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
B: |
302
|
|
|
|
|
|
|
This feature is still information-lossy, I when C<"_UNICODE_"> is |
303
|
|
|
|
|
|
|
specified. |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=item Detect7bit |
306
|
|
|
|
|
|
|
B<**> |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
Try to detect 7-bit charset on unencoded portions. |
309
|
|
|
|
|
|
|
Default is C<"YES">. |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=cut |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
#=item Field |
314
|
|
|
|
|
|
|
# |
315
|
|
|
|
|
|
|
#Name of the mail field this string came from. I |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=item Mapping |
318
|
|
|
|
|
|
|
B<**> |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
In scalar context, specify mappings actually used for charset names. |
321
|
|
|
|
|
|
|
C<"EXTENDED"> uses extended mappings. |
322
|
|
|
|
|
|
|
C<"STANDARD"> uses standardized strict mappings. |
323
|
|
|
|
|
|
|
Default is C<"EXTENDED">. |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
=back |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=cut |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
sub decode_mimewords { |
330
|
72
|
|
|
72
|
1
|
4483175
|
my $encstr = shift; |
331
|
72
|
|
|
|
|
1438
|
my %params = @_; |
332
|
72
|
|
|
|
|
730
|
my %Params = &_getparams(\%params, |
333
|
|
|
|
|
|
|
NoDefault => [qw(Charset)], # default is no conv. |
334
|
|
|
|
|
|
|
YesNo => [qw(Detect7bit)], |
335
|
|
|
|
|
|
|
Others => [qw(Mapping)], |
336
|
|
|
|
|
|
|
Obsoleted => [qw(Field)], |
337
|
|
|
|
|
|
|
ToUpper => [qw(Charset Mapping)], |
338
|
|
|
|
|
|
|
); |
339
|
72
|
|
|
|
|
12767
|
my $cset = MIME::Charset->new($Params{Charset}, |
340
|
|
|
|
|
|
|
Mapping => $Params{Mapping}); |
341
|
|
|
|
|
|
|
# unfolding: normalize linear-white-spaces and orphan newlines. |
342
|
72
|
50
|
|
|
|
26807
|
$encstr =~ s/(?:[\r\n]+[\t ])*[\r\n]+([\t ]|\Z)/$1? " ": ""/eg; |
|
39
|
|
|
|
|
961
|
|
343
|
72
|
|
|
|
|
175
|
$encstr =~ s/[\r\n]+/ /g; |
344
|
|
|
|
|
|
|
|
345
|
72
|
|
|
|
|
92
|
my @tokens; |
346
|
72
|
|
|
|
|
377
|
$@ = ''; ### error-return |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
### Decode: |
349
|
72
|
|
|
|
|
99
|
my ($word, $charset, $language, $encoding, $enc, $dec); |
350
|
72
|
|
|
|
|
100
|
my $spc = ''; |
351
|
72
|
|
|
|
|
202
|
pos($encstr) = 0; |
352
|
72
|
|
|
|
|
136
|
while (1) { |
353
|
265
|
100
|
|
|
|
858
|
last if (pos($encstr) >= length($encstr)); |
354
|
193
|
|
|
|
|
259
|
my $pos = pos($encstr); ### save it |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
### Case 1: are we looking at "=?..?..?="? |
357
|
193
|
100
|
|
|
|
1283
|
if ($encstr =~ m{\G # from where we left off.. |
358
|
|
|
|
|
|
|
=\?([^?]*) # "=?" + charset + |
359
|
|
|
|
|
|
|
\?([bq]) # "?" + encoding + |
360
|
|
|
|
|
|
|
\?([^?]+) # "?" + data maybe with spcs + |
361
|
|
|
|
|
|
|
\?= # "?=" |
362
|
|
|
|
|
|
|
([\r\n\t ]*) |
363
|
|
|
|
|
|
|
}xgi) { |
364
|
106
|
|
|
|
|
434
|
($word, $charset, $encoding, $enc) = ($&, $1, lc($2), $3); |
365
|
106
|
|
|
|
|
605
|
my $tspc = $4; |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
# RFC 2231 section 5 extension |
368
|
106
|
100
|
|
|
|
483
|
if ($charset =~ s/^([^\*]*)\*(.*)/$1/) { |
369
|
4
|
|
50
|
|
|
14
|
$language = $2 || undef; |
370
|
4
|
|
50
|
|
|
11
|
$charset ||= undef; |
371
|
|
|
|
|
|
|
} else { |
372
|
102
|
|
|
|
|
133
|
$language = undef; |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
106
|
100
|
|
|
|
1475
|
if ($encoding eq 'q') { |
376
|
49
|
|
|
|
|
107
|
$dec = _decode_Q($enc); |
377
|
|
|
|
|
|
|
} else { |
378
|
57
|
|
|
|
|
131
|
$dec = _decode_B($enc); |
379
|
|
|
|
|
|
|
} |
380
|
106
|
50
|
|
|
|
304
|
unless (defined $dec) { |
381
|
0
|
|
|
|
|
0
|
$@ .= qq|Illegal sequence in "$word" (pos $pos)\n|; |
382
|
0
|
|
|
|
|
0
|
push @tokens, [$spc.$word]; |
383
|
0
|
|
|
|
|
0
|
$spc = ''; |
384
|
0
|
|
|
|
|
0
|
next; |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
|
387
|
106
|
|
|
|
|
1487
|
{ local $@; |
|
106
|
|
|
|
|
322
|
|
388
|
106
|
100
|
50
|
|
|
1605
|
if (scalar(@tokens) and |
|
|
100
|
100
|
|
|
|
|
|
|
50
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
389
|
|
|
|
|
|
|
lc($charset || "") eq lc($tokens[-1]->[1] || "") and |
390
|
|
|
|
|
|
|
resolve_alias($charset) and |
391
|
|
|
|
|
|
|
(!${tokens[-1]}[2] and !$language or |
392
|
|
|
|
|
|
|
lc(${tokens[-1]}[2]) eq lc($language))) { # Concat words if possible. |
393
|
24
|
|
|
|
|
91401
|
$tokens[-1]->[0] .= $dec; |
394
|
|
|
|
|
|
|
} elsif ($language) { |
395
|
4
|
|
|
|
|
12
|
push @tokens, [$dec, $charset, $language]; |
396
|
|
|
|
|
|
|
} elsif ($charset) { |
397
|
78
|
|
|
|
|
219
|
push @tokens, [$dec, $charset]; |
398
|
|
|
|
|
|
|
} else { |
399
|
0
|
|
|
|
|
0
|
push @tokens, [$dec]; |
400
|
|
|
|
|
|
|
} |
401
|
106
|
|
|
|
|
190
|
$spc = $tspc; |
402
|
|
|
|
|
|
|
} |
403
|
106
|
|
|
|
|
501
|
next; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
### Case 2: are we looking at a bad "=?..." prefix? |
407
|
|
|
|
|
|
|
### We need this to detect problems for case 3, which stops at "=?": |
408
|
87
|
|
|
|
|
168
|
pos($encstr) = $pos; # reset the pointer. |
409
|
87
|
100
|
|
|
|
300
|
if ($encstr =~ m{\G=\?}xg) { |
410
|
6
|
|
|
|
|
16
|
$@ .= qq|unterminated "=?..?..?=" in "$encstr" (pos $pos)\n|; |
411
|
6
|
|
|
|
|
18
|
push @tokens, [$spc.'=?']; |
412
|
6
|
|
|
|
|
9
|
$spc = ''; |
413
|
6
|
|
|
|
|
10
|
next; |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
### Case 3: are we looking at ordinary text? |
417
|
81
|
|
|
|
|
208
|
pos($encstr) = $pos; # reset the pointer. |
418
|
81
|
50
|
|
|
|
802
|
if ($encstr =~ m{\G # from where we left off... |
419
|
|
|
|
|
|
|
(.*? # shortest possible string, |
420
|
|
|
|
|
|
|
\n*) # followed by 0 or more NLs, |
421
|
|
|
|
|
|
|
(?=(\Z|=\?)) # terminated by "=?" or EOS |
422
|
|
|
|
|
|
|
}xgs) { |
423
|
81
|
50
|
|
|
|
220
|
length($1) or croak "MIME::EncWords: internal logic err: empty token\n"; |
424
|
81
|
|
|
|
|
3088
|
push @tokens, [$spc.$1]; |
425
|
81
|
|
|
|
|
202
|
$spc = ''; |
426
|
81
|
|
|
|
|
119
|
next; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
### Case 4: bug! |
430
|
0
|
|
|
|
|
0
|
croak "MIME::EncWords: unexpected case:\n($encstr) pos $pos\n\t". |
431
|
|
|
|
|
|
|
"Please alert developer.\n"; |
432
|
|
|
|
|
|
|
} |
433
|
72
|
50
|
|
|
|
182
|
push @tokens, [$spc] if $spc; |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
# Detect 7-bit charset |
436
|
72
|
50
|
|
|
|
227
|
if ($Params{Detect7bit} ne "NO") { |
437
|
72
|
|
|
|
|
83
|
local $@; |
438
|
72
|
|
|
|
|
182
|
foreach my $t (@tokens) { |
439
|
169
|
100
|
100
|
|
|
2012
|
unless ($t->[0] =~ $UNSAFE or $t->[1]) { |
440
|
87
|
|
|
|
|
273
|
my $charset = MIME::Charset::_detect_7bit_charset($t->[0]); |
441
|
87
|
50
|
33
|
|
|
6832
|
if ($charset and $charset ne &MIME::Charset::default()) { |
442
|
0
|
|
|
|
|
0
|
$t->[1] = $charset; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
} |
447
|
|
|
|
|
|
|
|
448
|
72
|
100
|
|
|
|
497
|
if (wantarray) { |
449
|
24
|
|
|
|
|
241
|
@tokens; |
450
|
|
|
|
|
|
|
} else { |
451
|
111
|
|
|
|
|
1159
|
join('', map { |
452
|
48
|
|
|
|
|
91
|
&_convert($_->[0], $_->[1], $cset, $Params{Mapping}) |
453
|
|
|
|
|
|
|
} @tokens); |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
#------------------------------ |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# _convert RAW, FROMCHARSET, TOCHARSET, MAPPING |
460
|
|
|
|
|
|
|
# Private: used by decode_mimewords() to convert string by other charset |
461
|
|
|
|
|
|
|
# or to decode to Unicode. |
462
|
|
|
|
|
|
|
# When source charset is unknown and Unicode string is requested, at first |
463
|
|
|
|
|
|
|
# try well-formed UTF-8 then fallback to ISO-8859-1 so that almost all |
464
|
|
|
|
|
|
|
# non-ASCII bytes will be preserved. |
465
|
|
|
|
|
|
|
sub _convert($$$$) { |
466
|
111
|
|
|
111
|
|
150
|
my $s = shift; |
467
|
111
|
|
|
|
|
186
|
my $charset = shift; |
468
|
111
|
|
|
|
|
137
|
my $cset = shift; |
469
|
111
|
|
|
|
|
140
|
my $mapping = shift; |
470
|
111
|
50
|
|
|
|
305
|
return $s unless &MIME::Charset::USE_ENCODE; |
471
|
111
|
100
|
|
|
|
320
|
return $s unless $cset->as_string; |
472
|
66
|
50
|
66
|
|
|
823
|
croak "unsupported charset ``".$cset->as_string."''" |
473
|
|
|
|
|
|
|
unless $cset->decoder or $cset->as_string eq "_UNICODE_"; |
474
|
|
|
|
|
|
|
|
475
|
66
|
|
|
|
|
725
|
local($@); |
476
|
66
|
|
|
|
|
207
|
$charset = MIME::Charset->new($charset, Mapping => $mapping); |
477
|
66
|
50
|
66
|
|
|
14421778
|
if ($charset->as_string and $charset->as_string eq $cset->as_string) { |
478
|
0
|
|
|
|
|
0
|
return $s; |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
# build charset object to transform string from $charset to $cset. |
481
|
66
|
|
|
|
|
789
|
$charset->encoder($cset); |
482
|
|
|
|
|
|
|
|
483
|
66
|
|
|
|
|
1369
|
my $converted = $s; |
484
|
66
|
50
|
33
|
|
|
660
|
if (is_utf8($s) or $s =~ $WIDECHAR) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
485
|
0
|
0
|
|
|
|
0
|
if ($charset->output_charset ne "_UNICODE_") { |
486
|
0
|
|
|
|
|
0
|
$converted = $charset->encode($s); |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
} elsif ($charset->output_charset eq "_UNICODE_") { |
489
|
37
|
100
|
|
|
|
383
|
if (!$charset->decoder) { |
490
|
18
|
50
|
|
|
|
143
|
if ($s =~ $UNSAFE) { |
491
|
0
|
|
|
|
|
0
|
$@ = ''; |
492
|
0
|
|
|
|
|
0
|
eval { |
493
|
0
|
|
|
|
|
0
|
$charset = MIME::Charset->new("UTF-8", |
494
|
|
|
|
|
|
|
Mapping => 'STANDARD'); |
495
|
0
|
|
|
|
|
0
|
$converted = $charset->decode($converted, FB_CROAK()); |
496
|
|
|
|
|
|
|
}; |
497
|
0
|
0
|
|
|
|
0
|
if ($@) { |
498
|
0
|
|
|
|
|
0
|
$converted = $s; |
499
|
0
|
|
|
|
|
0
|
$charset = MIME::Charset->new("ISO-8859-1", |
500
|
|
|
|
|
|
|
Mapping => 'STANDARD'); |
501
|
0
|
|
|
|
|
0
|
$converted = $charset->decode($converted, 0); |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
} else { |
505
|
19
|
|
|
|
|
121
|
$converted = $charset->decode($s); |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
} elsif ($charset->decoder) { |
508
|
18
|
|
|
|
|
329
|
$converted = $charset->encode($s); |
509
|
|
|
|
|
|
|
} |
510
|
66
|
|
|
|
|
1472
|
return $converted; |
511
|
|
|
|
|
|
|
} |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
#------------------------------ |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=item encode_mimeword RAW, [ENCODING], [CHARSET] |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
I |
518
|
|
|
|
|
|
|
Encode a single RAW "word" that has unsafe characters. |
519
|
|
|
|
|
|
|
The "word" will be encoded in its entirety. |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
### Encode "<>": |
522
|
|
|
|
|
|
|
$encoded = encode_mimeword("\xABFran\xE7ois\xBB"); |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
You may specify the ENCODING (C<"Q"> or C<"B">), which defaults to C<"Q">. |
525
|
|
|
|
|
|
|
B<**> |
526
|
|
|
|
|
|
|
You may also specify it as ``special'' value: C<"S"> to choose shorter |
527
|
|
|
|
|
|
|
one of either C<"Q"> or C<"B">. |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
You may specify the CHARSET, which defaults to C. |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
B<*> |
532
|
|
|
|
|
|
|
Spaces will be escaped with ``_'' by C<"Q"> encoding. |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=cut |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
sub encode_mimeword { |
537
|
144
|
|
|
144
|
1
|
410
|
my $word = shift; |
538
|
144
|
|
50
|
|
|
1978
|
my $encoding = uc(shift || 'Q'); # not overridden. |
539
|
144
|
|
50
|
|
|
427
|
my $charset = shift || 'ISO-8859-1'; # ditto. |
540
|
144
|
|
50
|
|
|
673
|
my $language = uc(shift || ""); # ditto. |
541
|
|
|
|
|
|
|
|
542
|
144
|
50
|
|
|
|
747
|
if (ref $charset) { |
543
|
144
|
50
|
33
|
|
|
2024
|
if (is_utf8($word) or $word =~ /$WIDECHAR/) { |
544
|
0
|
|
|
|
|
0
|
$word = $charset->undecode($word, 0); |
545
|
|
|
|
|
|
|
} |
546
|
144
|
|
|
|
|
504
|
$charset = $charset->as_string; |
547
|
|
|
|
|
|
|
} else { |
548
|
0
|
|
|
|
|
0
|
$charset = uc($charset); |
549
|
|
|
|
|
|
|
} |
550
|
144
|
|
|
|
|
900
|
my $encstr; |
551
|
144
|
100
|
|
|
|
1518
|
if ($encoding eq 'Q') { |
|
|
50
|
|
|
|
|
|
552
|
71
|
|
|
|
|
470
|
$encstr = &_encode_Q($word); |
553
|
|
|
|
|
|
|
} elsif ($encoding eq "S") { |
554
|
0
|
|
|
|
|
0
|
my ($B, $Q) = (&_encode_B($word), &_encode_Q($word)); |
555
|
0
|
0
|
|
|
|
0
|
if (length($B) < length($Q)) { |
556
|
0
|
|
|
|
|
0
|
$encoding = "B"; |
557
|
0
|
|
|
|
|
0
|
$encstr = $B; |
558
|
|
|
|
|
|
|
} else { |
559
|
0
|
|
|
|
|
0
|
$encoding = "Q"; |
560
|
0
|
|
|
|
|
0
|
$encstr = $Q; |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
} else { # "B" |
563
|
73
|
|
|
|
|
109
|
$encoding = "B"; |
564
|
73
|
|
|
|
|
393
|
$encstr = &_encode_B($word); |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
|
567
|
144
|
50
|
|
|
|
444
|
if ($language) { |
568
|
0
|
|
|
|
|
0
|
return "=?$charset*$language?$encoding?$encstr?="; |
569
|
|
|
|
|
|
|
} else { |
570
|
144
|
|
|
|
|
821
|
return "=?$charset?$encoding?$encstr?="; |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
#------------------------------ |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=item encode_mimewords RAW, [OPTS] |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
I |
579
|
|
|
|
|
|
|
Given a RAW string, try to find and encode all "unsafe" sequences |
580
|
|
|
|
|
|
|
of characters: |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
### Encode a string with some unsafe "words": |
583
|
|
|
|
|
|
|
$encoded = encode_mimewords("Me and \xABFran\xE7ois\xBB"); |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
Returns the encoded string. |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
B<**> |
588
|
|
|
|
|
|
|
RAW may be a Unicode string when Unicode/multibyte support is enabled |
589
|
|
|
|
|
|
|
(see L). |
590
|
|
|
|
|
|
|
Furthermore, RAW may be a reference to that returned |
591
|
|
|
|
|
|
|
by L on array context. In latter case "Charset" |
592
|
|
|
|
|
|
|
option (see below) will be overridden (see also a note below). |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
B: |
595
|
|
|
|
|
|
|
B<*> |
596
|
|
|
|
|
|
|
When RAW is an arrayref, |
597
|
|
|
|
|
|
|
adjacent encoded-words (i.e. elements having non-ASCII charset element) |
598
|
|
|
|
|
|
|
are concatenated. Then they are split taking |
599
|
|
|
|
|
|
|
care of character boundaries of multibyte sequences when Unicode/multibyte |
600
|
|
|
|
|
|
|
support is enabled. |
601
|
|
|
|
|
|
|
Portions for unencoded data should include surrounding whitespace(s), or |
602
|
|
|
|
|
|
|
they will be merged into adjoining encoded-word(s). |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
Any arguments past the RAW string are taken to define a hash of options: |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
=over 4 |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=item Charset |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
Encode all unsafe stuff with this charset. Default is 'ISO-8859-1', |
611
|
|
|
|
|
|
|
a.k.a. "Latin-1". |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=item Detect7bit |
614
|
|
|
|
|
|
|
B<**> |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
When "Encoding" option (see below) is specified as C<"a"> and "Charset" |
617
|
|
|
|
|
|
|
option is unknown, try to detect 7-bit charset on given RAW string. |
618
|
|
|
|
|
|
|
Default is C<"YES">. |
619
|
|
|
|
|
|
|
When Unicode/multibyte support is disabled, |
620
|
|
|
|
|
|
|
this option will not have any effects |
621
|
|
|
|
|
|
|
(see L). |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=item Encoding |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
The encoding to use, C<"q"> or C<"b">. |
626
|
|
|
|
|
|
|
B<**> |
627
|
|
|
|
|
|
|
You may also specify ``special'' values: C<"a"> will automatically choose |
628
|
|
|
|
|
|
|
recommended encoding to use (with charset conversion if alternative |
629
|
|
|
|
|
|
|
charset is recommended: see L); |
630
|
|
|
|
|
|
|
C<"s"> will choose shorter one of either C<"q"> or C<"b">. |
631
|
|
|
|
|
|
|
B: |
632
|
|
|
|
|
|
|
B<*> |
633
|
|
|
|
|
|
|
As of release 1.005, The default was changed from C<"q"> |
634
|
|
|
|
|
|
|
(the default on MIME::Words) to C<"a">. |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
=item Field |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
Name of the mail field this string will be used in. |
639
|
|
|
|
|
|
|
B<**> |
640
|
|
|
|
|
|
|
Length of mail field name will be considered in the first line of |
641
|
|
|
|
|
|
|
encoded header. |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
=item Folding |
644
|
|
|
|
|
|
|
B<**> |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
A Sequence to fold encoded lines. The default is C<"\n">. |
647
|
|
|
|
|
|
|
If empty string C<""> is specified, encoded-words exceeding line length |
648
|
|
|
|
|
|
|
(see L below) will be split by SPACE. |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
B: |
651
|
|
|
|
|
|
|
B<*> |
652
|
|
|
|
|
|
|
Though RFC 5322 (formerly RFC 2822) states that the lines in |
653
|
|
|
|
|
|
|
Internet messages are delimited by CRLF (C<"\r\n">), |
654
|
|
|
|
|
|
|
this module chose LF (C<"\n">) as a default to keep backward compatibility. |
655
|
|
|
|
|
|
|
When you use the default, you might need converting newlines |
656
|
|
|
|
|
|
|
before encoded headers are thrown into session. |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
=item Mapping |
659
|
|
|
|
|
|
|
B<**> |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
Specify mappings actually used for charset names. |
662
|
|
|
|
|
|
|
C<"EXTENDED"> uses extended mappings. |
663
|
|
|
|
|
|
|
C<"STANDARD"> uses standardized strict mappings. |
664
|
|
|
|
|
|
|
The default is C<"EXTENDED">. |
665
|
|
|
|
|
|
|
When Unicode/multibyte support is disabled, |
666
|
|
|
|
|
|
|
this option will not have any effects |
667
|
|
|
|
|
|
|
(see L). |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
=item MaxLineLen |
670
|
|
|
|
|
|
|
B<**> |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
Maximum line length excluding newline. |
673
|
|
|
|
|
|
|
The default is 76. |
674
|
|
|
|
|
|
|
Negative value means unlimited line length (as of release 1.012.3). |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
=item Minimal |
677
|
|
|
|
|
|
|
B<**> |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Takes care of natural word separators (i.e. whitespaces) |
680
|
|
|
|
|
|
|
in the text to be encoded. |
681
|
|
|
|
|
|
|
If C<"NO"> is specified, this module will encode whole text |
682
|
|
|
|
|
|
|
(if encoding needed) not regarding whitespaces; |
683
|
|
|
|
|
|
|
encoded-words exceeding line length will be split based only on their |
684
|
|
|
|
|
|
|
lengths. |
685
|
|
|
|
|
|
|
Default is C<"YES"> by which minimal portions of text are encoded. |
686
|
|
|
|
|
|
|
If C<"DISPNAME"> is specified, portions including special characters |
687
|
|
|
|
|
|
|
described in RFC5322 (formerly RFC2822, RFC822) address specification |
688
|
|
|
|
|
|
|
(section 3.4) are also encoded. |
689
|
|
|
|
|
|
|
This is useful for encoding display-name of address fields. |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
B: |
692
|
|
|
|
|
|
|
As of release 0.040, default has been changed to C<"YES"> to ensure |
693
|
|
|
|
|
|
|
compatibility with MIME::Words. |
694
|
|
|
|
|
|
|
On earlier releases, this option was fixed to be C<"NO">. |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
B: |
697
|
|
|
|
|
|
|
C<"DISPNAME"> option was introduced at release 1.012. |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
=item Replacement |
700
|
|
|
|
|
|
|
B<**> |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
See L. |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=back |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=cut |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
sub encode_mimewords { |
709
|
90
|
|
|
90
|
1
|
134363
|
my $words = shift; |
710
|
90
|
|
|
|
|
360
|
my %params = @_; |
711
|
90
|
|
|
|
|
874
|
my %Params = &_getparams(\%params, |
712
|
|
|
|
|
|
|
YesNo => [qw(Detect7bit)], |
713
|
|
|
|
|
|
|
Others => [qw(Charset Encoding Field Folding |
714
|
|
|
|
|
|
|
Mapping MaxLineLen Minimal |
715
|
|
|
|
|
|
|
Replacement)], |
716
|
|
|
|
|
|
|
ToUpper => [qw(Charset Encoding Mapping Minimal |
717
|
|
|
|
|
|
|
Replacement)], |
718
|
|
|
|
|
|
|
); |
719
|
90
|
50
|
|
|
|
3677
|
croak "unsupported encoding ``$Params{Encoding}''" |
720
|
|
|
|
|
|
|
unless $Params{Encoding} =~ /^[ABQS]$/; |
721
|
|
|
|
|
|
|
# newline and following WSP |
722
|
90
|
|
|
|
|
478
|
my ($fwsbrk, $fwsspc); |
723
|
90
|
50
|
|
|
|
463
|
if ($Params{Folding} =~ m/^([\r\n]*)([\t ]?)$/) { |
724
|
90
|
|
|
|
|
444
|
$fwsbrk = $1; |
725
|
90
|
|
50
|
|
|
751
|
$fwsspc = $2 || " "; |
726
|
|
|
|
|
|
|
} else { |
727
|
0
|
|
|
|
|
0
|
croak sprintf "illegal folding sequence ``\\x%*v02X''", '\\x', |
728
|
|
|
|
|
|
|
$Params{Folding}; |
729
|
|
|
|
|
|
|
} |
730
|
|
|
|
|
|
|
# charset objects |
731
|
90
|
|
|
|
|
1206
|
my $charsetobj = MIME::Charset->new($Params{Charset}, |
732
|
|
|
|
|
|
|
Mapping => $Params{Mapping}); |
733
|
90
|
|
|
|
|
101830
|
my $ascii = MIME::Charset->new("US-ASCII", Mapping => 'STANDARD'); |
734
|
90
|
|
|
|
|
11447
|
$ascii->encoder($ascii); |
735
|
|
|
|
|
|
|
# lengths |
736
|
90
|
100
|
|
|
|
1641
|
my $firstlinelen = $Params{MaxLineLen} - |
737
|
|
|
|
|
|
|
($Params{Field}? length("$Params{Field}: "): 0); |
738
|
90
|
|
|
|
|
487
|
my $maxrestlen = $Params{MaxLineLen} - length($fwsspc); |
739
|
|
|
|
|
|
|
# minimal encoding flag |
740
|
90
|
50
|
|
|
|
862
|
if (!$Params{Minimal}) { |
|
|
50
|
|
|
|
|
|
741
|
0
|
|
|
|
|
0
|
$Params{Minimal} = 'NO'; |
742
|
|
|
|
|
|
|
} elsif ($Params{Minimal} !~ /^(NO|DISPNAME)$/) { |
743
|
90
|
|
|
|
|
288
|
$Params{Minimal} = 'YES'; |
744
|
|
|
|
|
|
|
} |
745
|
|
|
|
|
|
|
# unsafe ASCII sequences |
746
|
90
|
50
|
|
|
|
1778
|
my $UNSAFEASCII = ($maxrestlen <= 1)? |
747
|
|
|
|
|
|
|
qr{(?: =\? )}ox: |
748
|
|
|
|
|
|
|
qr{(?: =\? | [$PRINTABLE]{$Params{MaxLineLen}} )}x; |
749
|
90
|
50
|
|
|
|
4169
|
$UNSAFEASCII = qr{(?: [$DISPNAMESPECIAL] | $UNSAFEASCII )}x |
750
|
|
|
|
|
|
|
if $Params{Minimal} eq 'DISPNAME'; |
751
|
|
|
|
|
|
|
|
752
|
90
|
100
|
|
|
|
421
|
unless (ref($words) eq "ARRAY") { |
753
|
|
|
|
|
|
|
# workaround for UTF-16* & UTF-32*: force UTF-8. |
754
|
66
|
100
|
|
|
|
761
|
if ($charsetobj->as_string =~ /$ASCIIINCOMPAT/) { |
755
|
24
|
|
|
|
|
361
|
$words = _utf_to_unicode($charsetobj, $words); |
756
|
24
|
|
|
|
|
807
|
$charsetobj = MIME::Charset->new('UTF-8'); |
757
|
|
|
|
|
|
|
} |
758
|
|
|
|
|
|
|
|
759
|
66
|
|
|
|
|
2761
|
my @words = (); |
760
|
|
|
|
|
|
|
# unfolding: normalize linear-white-spaces and orphan newlines. |
761
|
66
|
50
|
|
|
|
2023
|
$words =~ s/(?:[\r\n]+[\t ])*[\r\n]+([\t ]|\Z)/$1? " ": ""/eg; |
|
1
|
|
|
|
|
25
|
|
762
|
66
|
|
|
|
|
288
|
$words =~ s/[\r\n]+/ /g; |
763
|
|
|
|
|
|
|
# split if required |
764
|
66
|
50
|
|
|
|
1087
|
if ($Params{Minimal} =~ /YES|DISPNAME/) { |
765
|
66
|
|
|
|
|
329
|
my ($spc, $unsafe_last) = ('', 0); |
766
|
66
|
|
|
|
|
1255
|
foreach my $w (split(/([\t ]+)/, $words)) { |
767
|
640
|
50
|
66
|
|
|
3771
|
next unless scalar(@words) or length($w); # skip garbage |
768
|
640
|
100
|
|
|
|
1898
|
if ($w =~ /[\t ]/) { |
769
|
287
|
|
|
|
|
1123
|
$spc = $w; |
770
|
287
|
|
|
|
|
1853
|
next; |
771
|
|
|
|
|
|
|
} |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
# workaround for ``ASCII transformation'' charsets |
774
|
353
|
|
|
|
|
577
|
my $u = $w; |
775
|
353
|
100
|
|
|
|
1703
|
if ($charsetobj->as_string =~ /$ASCIITRANS/) { |
776
|
6
|
|
|
|
|
51
|
if (MIME::Charset::USE_ENCODE) { |
777
|
6
|
50
|
33
|
|
|
179
|
if (is_utf8($w) or $w =~ /$WIDECHAR/) { |
778
|
0
|
|
|
|
|
0
|
$w = $charsetobj->undecode($u); |
779
|
|
|
|
|
|
|
} else { |
780
|
6
|
|
|
|
|
17
|
$u = $charsetobj->decode($w); |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
} elsif ($w =~ /[+~]/) { #FIXME: for pre-Encode environment |
783
|
|
|
|
|
|
|
$u = "x$w"; |
784
|
|
|
|
|
|
|
} |
785
|
|
|
|
|
|
|
} |
786
|
353
|
100
|
|
|
|
10499
|
if (scalar(@words)) { |
787
|
287
|
100
|
100
|
|
|
7844
|
if (($w =~ /$NONPRINT|$UNSAFEASCII/ or $u ne $w) xor |
|
|
|
100
|
|
|
|
|
788
|
|
|
|
|
|
|
$unsafe_last) { |
789
|
61
|
100
|
|
|
|
209
|
if ($unsafe_last) { |
790
|
40
|
|
|
|
|
103
|
push @words, $spc.$w; |
791
|
|
|
|
|
|
|
} else { |
792
|
21
|
|
|
|
|
35
|
$words[-1] .= $spc; |
793
|
21
|
|
|
|
|
75
|
push @words, $w; |
794
|
|
|
|
|
|
|
} |
795
|
61
|
|
|
|
|
125
|
$unsafe_last = not $unsafe_last; |
796
|
|
|
|
|
|
|
} else { |
797
|
226
|
|
|
|
|
694
|
$words[-1] .= $spc.$w; |
798
|
|
|
|
|
|
|
} |
799
|
|
|
|
|
|
|
} else { |
800
|
66
|
|
|
|
|
468
|
push @words, $spc.$w; |
801
|
66
|
|
66
|
|
|
2982
|
$unsafe_last = |
802
|
|
|
|
|
|
|
($w =~ /$NONPRINT|$UNSAFEASCII/ or $u ne $w); |
803
|
|
|
|
|
|
|
} |
804
|
353
|
|
|
|
|
1191
|
$spc = ''; |
805
|
|
|
|
|
|
|
} |
806
|
66
|
50
|
|
|
|
647
|
if ($spc) { |
807
|
0
|
0
|
|
|
|
0
|
if (scalar(@words)) { |
808
|
0
|
|
|
|
|
0
|
$words[-1] .= $spc; |
809
|
|
|
|
|
|
|
} else { # only WSPs |
810
|
0
|
|
|
|
|
0
|
push @words, $spc; |
811
|
|
|
|
|
|
|
} |
812
|
|
|
|
|
|
|
} |
813
|
|
|
|
|
|
|
} else { |
814
|
0
|
|
|
|
|
0
|
@words = ($words); |
815
|
|
|
|
|
|
|
} |
816
|
66
|
|
|
|
|
157
|
$words = [map { [$_, $Params{Charset}] } @words]; |
|
127
|
|
|
|
|
643
|
|
817
|
|
|
|
|
|
|
} |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
# Translate / concatenate words. |
820
|
90
|
|
|
|
|
157
|
my @triplets; |
821
|
90
|
|
|
|
|
298
|
foreach (@$words) { |
822
|
211
|
|
|
|
|
1509
|
my ($s, $cset) = @$_; |
823
|
211
|
50
|
|
|
|
663
|
next unless length($s); |
824
|
211
|
|
100
|
|
|
1996
|
my $csetobj = MIME::Charset->new($cset || "", |
825
|
|
|
|
|
|
|
Mapping => $Params{Mapping}); |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
# workaround for UTF-16*/UTF-32*: force UTF-8 |
828
|
211
|
100
|
100
|
|
|
28060
|
if ($csetobj->as_string and $csetobj->as_string =~ /$ASCIIINCOMPAT/) { |
829
|
66
|
|
|
|
|
2438
|
$s = _utf_to_unicode($csetobj, $s); |
830
|
66
|
|
|
|
|
765
|
$csetobj = MIME::Charset->new('UTF-8'); |
831
|
|
|
|
|
|
|
} |
832
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
# determine charset and encoding |
834
|
|
|
|
|
|
|
# try defaults only if 7-bit charset detection is not required |
835
|
211
|
|
|
|
|
14699
|
my $enc; |
836
|
211
|
|
|
|
|
293
|
my $obj = $csetobj; |
837
|
211
|
100
|
|
|
|
1130
|
unless ($obj->as_string) { |
838
|
45
|
100
|
33
|
|
|
2828
|
if ($Params{Encoding} ne "A" or $Params{Detect7bit} eq "NO" or |
|
|
|
66
|
|
|
|
|
839
|
|
|
|
|
|
|
$s =~ /$UNSAFE/) { |
840
|
6
|
|
|
|
|
12
|
$obj = $charsetobj; |
841
|
|
|
|
|
|
|
} |
842
|
|
|
|
|
|
|
} |
843
|
211
|
|
|
|
|
1934
|
($s, $cset, $enc) = |
844
|
|
|
|
|
|
|
$obj->header_encode($s, |
845
|
|
|
|
|
|
|
Detect7bit => $Params{Detect7bit}, |
846
|
|
|
|
|
|
|
Replacement => $Params{Replacement}, |
847
|
|
|
|
|
|
|
Encoding => $Params{Encoding}); |
848
|
|
|
|
|
|
|
# Resolve 'S' encoding based on global length. See (*). |
849
|
211
|
100
|
33
|
|
|
51293
|
$enc = 'S' |
|
|
|
66
|
|
|
|
|
850
|
|
|
|
|
|
|
if defined $enc and |
851
|
|
|
|
|
|
|
($Params{Encoding} eq 'S' or |
852
|
|
|
|
|
|
|
$Params{Encoding} eq 'A' and $obj->header_encoding eq 'S'); |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
# pure ASCII |
855
|
211
|
100
|
66
|
|
|
4036
|
if ($cset eq "US-ASCII" and !$enc and $s =~ /$UNSAFEASCII/) { |
|
|
|
100
|
|
|
|
|
856
|
|
|
|
|
|
|
# pure ASCII with unsafe sequences should be encoded |
857
|
4
|
|
33
|
|
|
20
|
$cset = $csetobj->output_charset || |
858
|
|
|
|
|
|
|
$charsetobj->output_charset || |
859
|
|
|
|
|
|
|
$ascii->output_charset; |
860
|
4
|
|
|
|
|
72
|
$csetobj = MIME::Charset->new($cset, |
861
|
|
|
|
|
|
|
Mapping => $Params{Mapping}); |
862
|
|
|
|
|
|
|
# Preserve original Encoding option unless it was 'A'. |
863
|
4
|
100
|
50
|
|
|
325
|
$enc = ($Params{Encoding} eq 'A') ? |
864
|
|
|
|
|
|
|
($csetobj->header_encoding || 'Q') : |
865
|
|
|
|
|
|
|
$Params{Encoding}; |
866
|
|
|
|
|
|
|
} else { |
867
|
207
|
|
|
|
|
957
|
$csetobj = MIME::Charset->new($cset, |
868
|
|
|
|
|
|
|
Mapping => $Params{Mapping}); |
869
|
|
|
|
|
|
|
} |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
# Now no charset translations are needed. |
872
|
211
|
|
|
|
|
22999
|
$csetobj->encoder($csetobj); |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
# Concatenate adjacent ``words'' so that multibyte sequences will |
875
|
|
|
|
|
|
|
# be handled safely. |
876
|
|
|
|
|
|
|
# Note: Encoded-word and unencoded text must not adjoin without |
877
|
|
|
|
|
|
|
# separating whitespace(s). |
878
|
211
|
100
|
|
|
|
19215
|
if (scalar(@triplets)) { |
879
|
121
|
|
|
|
|
197
|
my ($last, $lastenc, $lastcsetobj) = @{$triplets[-1]}; |
|
121
|
|
|
|
|
652
|
|
880
|
121
|
100
|
50
|
|
|
446
|
if ($csetobj->decoder and |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
881
|
|
|
|
|
|
|
($lastcsetobj->as_string || "") eq $csetobj->as_string and |
882
|
|
|
|
|
|
|
($lastenc || "") eq ($enc || "")) { |
883
|
26
|
|
|
|
|
817
|
$triplets[-1]->[0] .= $s; |
884
|
26
|
|
|
|
|
345
|
next; |
885
|
|
|
|
|
|
|
} elsif (!$lastenc and $enc and $last !~ /[\r\n\t ]$/) { |
886
|
7
|
50
|
|
|
|
619
|
if ($last =~ /^(.*)([\r\n\t ])([$PRINTABLE]+)$/s) { |
|
|
0
|
|
|
|
|
|
887
|
7
|
|
|
|
|
39
|
$triplets[-1]->[0] = $1.$2; |
888
|
7
|
|
|
|
|
28
|
$s = $3.$s; |
889
|
|
|
|
|
|
|
} elsif ($lastcsetobj->as_string eq "US-ASCII") { |
890
|
0
|
|
|
|
|
0
|
$triplets[-1]->[0] .= $s; |
891
|
0
|
|
|
|
|
0
|
$triplets[-1]->[1] = $enc; |
892
|
0
|
|
|
|
|
0
|
$triplets[-1]->[2] = $csetobj; |
893
|
0
|
|
|
|
|
0
|
next; |
894
|
|
|
|
|
|
|
} |
895
|
|
|
|
|
|
|
} elsif ($lastenc and !$enc and $s !~ /^[\r\n\t ]/) { |
896
|
16
|
50
|
|
|
|
946
|
if ($s =~ /^([$PRINTABLE]+)([\r\n\t ])(.*)$/s) { |
|
|
0
|
|
|
|
|
|
897
|
16
|
|
|
|
|
105
|
$triplets[-1]->[0] .= $1; |
898
|
16
|
|
|
|
|
76
|
$s = $2.$3; |
899
|
|
|
|
|
|
|
} elsif ($csetobj->as_string eq "US-ASCII") { |
900
|
0
|
|
|
|
|
0
|
$triplets[-1]->[0] .= $s; |
901
|
0
|
|
|
|
|
0
|
next; |
902
|
|
|
|
|
|
|
} |
903
|
|
|
|
|
|
|
} |
904
|
|
|
|
|
|
|
} |
905
|
185
|
|
|
|
|
3915
|
push @triplets, [$s, $enc, $csetobj]; |
906
|
|
|
|
|
|
|
} |
907
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
# (*) Resolve 'S' encoding based on global length. |
909
|
90
|
100
|
|
|
|
207
|
my @s_enc = grep { $_->[1] and $_->[1] eq 'S' } @triplets; |
|
185
|
|
|
|
|
1517
|
|
910
|
90
|
100
|
|
|
|
410
|
if (scalar @s_enc) { |
911
|
42
|
|
|
|
|
54
|
my $enc; |
912
|
42
|
100
|
|
|
|
91
|
my $b = scalar grep { $_->[1] and $_->[1] eq 'B' } @triplets; |
|
84
|
|
|
|
|
438
|
|
913
|
42
|
100
|
|
|
|
216
|
my $q = scalar grep { $_->[1] and $_->[1] eq 'Q' } @triplets; |
|
84
|
|
|
|
|
326
|
|
914
|
|
|
|
|
|
|
# 'A' chooses 'B' or 'Q' when all other encoded-words have same enc. |
915
|
42
|
100
|
66
|
|
|
771
|
if ($Params{Encoding} eq 'A' and $b and ! $q) { |
|
|
50
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
916
|
7
|
|
|
|
|
11
|
$enc = 'B'; |
917
|
|
|
|
|
|
|
} elsif ($Params{Encoding} eq 'A' and ! $b and $q) { |
918
|
0
|
|
|
|
|
0
|
$enc = 'Q'; |
919
|
|
|
|
|
|
|
# Otherwise, assuming 'Q', when characters to be encoded are more than |
920
|
|
|
|
|
|
|
# 6th of total (plus a little fraction), 'B' will win. |
921
|
|
|
|
|
|
|
# Note: This might give 'Q' so great advantage... |
922
|
|
|
|
|
|
|
} else { |
923
|
35
|
|
|
|
|
72
|
my @no_enc = grep { ! $_->[1] } @triplets; |
|
63
|
|
|
|
|
164
|
|
924
|
35
|
|
|
|
|
269
|
my $total = length join('', map { $_->[0] } (@no_enc, @s_enc)); |
|
63
|
|
|
|
|
235
|
|
925
|
35
|
|
|
|
|
136
|
my $q = scalar(() = join('', map { $_->[0] } @s_enc) =~ |
|
35
|
|
|
|
|
855
|
|
926
|
|
|
|
|
|
|
m{[^- !*+/0-9A-Za-z]}g); |
927
|
35
|
100
|
|
|
|
220
|
if ($total + 8 < $q * 6) { |
928
|
21
|
|
|
|
|
145
|
$enc = 'B'; |
929
|
|
|
|
|
|
|
} else { |
930
|
14
|
|
|
|
|
48
|
$enc = 'Q'; |
931
|
|
|
|
|
|
|
} |
932
|
|
|
|
|
|
|
} |
933
|
42
|
|
|
|
|
159
|
foreach (@triplets) { |
934
|
84
|
100
|
100
|
|
|
436
|
$_->[1] = $enc if $_->[1] and $_->[1] eq 'S'; |
935
|
|
|
|
|
|
|
} |
936
|
|
|
|
|
|
|
} |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
# chop leading FWS |
939
|
90
|
|
33
|
|
|
1110
|
while (scalar(@triplets) and $triplets[0]->[0] =~ s/^[\r\n\t ]+//) { |
940
|
0
|
0
|
|
|
|
0
|
shift @triplets unless length($triplets[0]->[0]); |
941
|
|
|
|
|
|
|
} |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
# Split long ``words''. |
944
|
90
|
|
|
|
|
459
|
my @splitwords; |
945
|
|
|
|
|
|
|
my $restlen; |
946
|
90
|
50
|
|
|
|
269
|
if ($Params{MaxLineLen} < 0) { |
947
|
0
|
|
|
|
|
0
|
@splitwords = @triplets; |
948
|
|
|
|
|
|
|
} else { |
949
|
90
|
|
|
|
|
136
|
$restlen = $firstlinelen; |
950
|
90
|
|
|
|
|
181
|
foreach (@triplets) { |
951
|
185
|
|
|
|
|
817
|
my ($s, $enc, $csetobj) = @$_; |
952
|
|
|
|
|
|
|
|
953
|
185
|
|
|
|
|
625
|
my @s = &_split($s, $enc, $csetobj, $restlen, $maxrestlen); |
954
|
185
|
|
|
|
|
500
|
push @splitwords, @s; |
955
|
185
|
|
|
|
|
230
|
my ($last, $lastenc, $lastcsetobj) = @{$s[-1]}; |
|
185
|
|
|
|
|
475
|
|
956
|
185
|
|
|
|
|
1101
|
my $lastlen; |
957
|
185
|
100
|
|
|
|
374
|
if ($lastenc) { |
958
|
99
|
|
|
|
|
322
|
$lastlen = $lastcsetobj->encoded_header_len($last, $lastenc); |
959
|
|
|
|
|
|
|
} else { |
960
|
86
|
|
|
|
|
124
|
$lastlen = length($last); |
961
|
|
|
|
|
|
|
} |
962
|
185
|
100
|
|
|
|
2149
|
$restlen = $maxrestlen if scalar @s > 1; # has split; new line(s) fed |
963
|
185
|
|
|
|
|
286
|
$restlen -= $lastlen; |
964
|
185
|
100
|
|
|
|
10518
|
$restlen = $maxrestlen if $restlen <= 1; |
965
|
|
|
|
|
|
|
} |
966
|
|
|
|
|
|
|
} |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
# Do encoding. |
969
|
90
|
|
|
|
|
391
|
my @lines; |
970
|
90
|
|
|
|
|
123
|
$restlen = $firstlinelen; |
971
|
90
|
|
|
|
|
195
|
foreach (@splitwords) { |
972
|
279
|
|
|
|
|
721
|
my ($str, $encoding, $charsetobj) = @$_; |
973
|
279
|
50
|
|
|
|
795
|
next unless length($str); |
974
|
|
|
|
|
|
|
|
975
|
279
|
|
|
|
|
456
|
my $s; |
976
|
279
|
100
|
|
|
|
497
|
if (!$encoding) { |
977
|
135
|
|
|
|
|
200
|
$s = $str; |
978
|
|
|
|
|
|
|
} else { |
979
|
144
|
|
|
|
|
433
|
$s = encode_mimeword($str, $encoding, $charsetobj); |
980
|
|
|
|
|
|
|
} |
981
|
|
|
|
|
|
|
|
982
|
279
|
100
|
100
|
|
|
2962
|
my $spc = (scalar(@lines) and $lines[-1] =~ /[\r\n\t ]$/ or |
983
|
|
|
|
|
|
|
$s =~ /^[\r\n\t ]/)? '': ' '; |
984
|
279
|
100
|
|
|
|
1883
|
if (!scalar(@lines)) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
985
|
90
|
|
|
|
|
810
|
push @lines, $s; |
986
|
|
|
|
|
|
|
} elsif ($Params{MaxLineLen} < 0) { |
987
|
0
|
|
|
|
|
0
|
$lines[-1] .= $spc.$s; |
988
|
|
|
|
|
|
|
} elsif (length($lines[-1].$spc.$s) <= $restlen) { |
989
|
91
|
|
|
|
|
399
|
$lines[-1] .= $spc.$s; |
990
|
|
|
|
|
|
|
} else { |
991
|
98
|
100
|
|
|
|
1015
|
if ($lines[-1] =~ s/([\r\n\t ]+)$//) { |
992
|
4
|
|
|
|
|
87
|
$s = $1.$s; |
993
|
|
|
|
|
|
|
} |
994
|
98
|
|
|
|
|
474
|
$s =~ s/^[\r\n]*[\t ]//; # strip only one WSP replaced by FWS |
995
|
98
|
|
|
|
|
166
|
push @lines, $s; |
996
|
98
|
|
|
|
|
265
|
$restlen = $maxrestlen; |
997
|
|
|
|
|
|
|
} |
998
|
|
|
|
|
|
|
} |
999
|
|
|
|
|
|
|
|
1000
|
90
|
|
|
|
|
3240
|
join($fwsbrk.$fwsspc, @lines); |
1001
|
|
|
|
|
|
|
} |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
#------------------------------ |
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
# _split RAW, ENCODING, CHARSET_OBJECT, ROOM_OF_FIRST_LINE, MAXRESTLEN |
1006
|
|
|
|
|
|
|
# Private: used by encode_mimewords() to split a string into |
1007
|
|
|
|
|
|
|
# (encoded or non-encoded) words. |
1008
|
|
|
|
|
|
|
# Returns an array of arrayrefs [SUBSTRING, ENCODING, CHARSET]. |
1009
|
|
|
|
|
|
|
sub _split { |
1010
|
185
|
|
|
185
|
|
328
|
my $str = shift; |
1011
|
185
|
|
|
|
|
215
|
my $encoding = shift; |
1012
|
185
|
|
|
|
|
224
|
my $charset = shift; |
1013
|
185
|
|
|
|
|
208
|
my $restlen = shift; |
1014
|
185
|
|
|
|
|
219
|
my $maxrestlen = shift; |
1015
|
|
|
|
|
|
|
|
1016
|
185
|
50
|
33
|
|
|
1228
|
if (!$charset->as_string or $charset->as_string eq '8BIT') {# Undecodable. |
1017
|
0
|
|
|
|
|
0
|
$str =~ s/[\r\n]+[\t ]*|\x00/ /g; # Eliminate hostile characters. |
1018
|
0
|
|
|
|
|
0
|
return ([$str, undef, $charset]); |
1019
|
|
|
|
|
|
|
} |
1020
|
185
|
100
|
66
|
|
|
3806
|
if (!$encoding and $charset->as_string eq 'US-ASCII') { # Pure ASCII. |
1021
|
86
|
|
|
|
|
819
|
return &_split_ascii($str, $restlen, $maxrestlen); |
1022
|
|
|
|
|
|
|
} |
1023
|
99
|
50
|
50
|
|
|
403
|
if (!$charset->decoder and MIME::Charset::USE_ENCODE) { # Unsupported. |
1024
|
0
|
|
|
|
|
0
|
return ([$str, $encoding, $charset]); |
1025
|
|
|
|
|
|
|
} |
1026
|
|
|
|
|
|
|
|
1027
|
99
|
|
|
|
|
996
|
my (@splitwords, $ustr, $first); |
1028
|
99
|
|
|
|
|
305
|
while (length($str)) { |
1029
|
144
|
100
|
|
|
|
545
|
if ($charset->encoded_header_len($str, $encoding) <= $restlen) { |
1030
|
98
|
|
|
|
|
3407
|
push @splitwords, [$str, $encoding, $charset]; |
1031
|
98
|
|
|
|
|
372
|
last; |
1032
|
|
|
|
|
|
|
} |
1033
|
46
|
|
|
|
|
1759
|
$ustr = $str; |
1034
|
46
|
50
|
33
|
|
|
24561
|
if (!(is_utf8($ustr) or $ustr =~ /$WIDECHAR/) and |
|
|
|
50
|
|
|
|
|
1035
|
|
|
|
|
|
|
MIME::Charset::USE_ENCODE) { |
1036
|
46
|
|
|
|
|
319
|
$ustr = $charset->decode($ustr); |
1037
|
|
|
|
|
|
|
} |
1038
|
46
|
|
|
|
|
2281
|
($first, $str) = &_clip_unsafe($ustr, $encoding, $charset, $restlen); |
1039
|
|
|
|
|
|
|
# retry splitting if failed |
1040
|
46
|
100
|
66
|
|
|
409
|
if ($first and !$str and |
|
|
|
100
|
|
|
|
|
1041
|
|
|
|
|
|
|
$maxrestlen < $charset->encoded_header_len($first, $encoding)) { |
1042
|
4
|
|
|
|
|
75
|
($first, $str) = &_clip_unsafe($ustr, $encoding, $charset, |
1043
|
|
|
|
|
|
|
$maxrestlen); |
1044
|
|
|
|
|
|
|
} |
1045
|
46
|
|
|
|
|
364
|
push @splitwords, [$first, $encoding, $charset]; |
1046
|
46
|
|
|
|
|
128
|
$restlen = $maxrestlen; |
1047
|
|
|
|
|
|
|
} |
1048
|
99
|
|
|
|
|
321
|
return @splitwords; |
1049
|
|
|
|
|
|
|
} |
1050
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
# _split_ascii RAW, ROOM_OF_FIRST_LINE, MAXRESTLEN |
1052
|
|
|
|
|
|
|
# Private: used by encode_mimewords() to split an US-ASCII string into |
1053
|
|
|
|
|
|
|
# (encoded or non-encoded) words. |
1054
|
|
|
|
|
|
|
# Returns an array of arrayrefs [SUBSTRING, undef, "US-ASCII"]. |
1055
|
|
|
|
|
|
|
sub _split_ascii { |
1056
|
86
|
|
|
86
|
|
302
|
my $s = shift; |
1057
|
86
|
|
|
|
|
177
|
my $restlen = shift; |
1058
|
86
|
|
|
|
|
102
|
my $maxrestlen = shift; |
1059
|
86
|
|
33
|
|
|
191
|
$restlen ||= $maxrestlen; |
1060
|
|
|
|
|
|
|
|
1061
|
86
|
|
|
|
|
109
|
my @splitwords; |
1062
|
86
|
|
|
|
|
433
|
my $ascii = MIME::Charset->new("US-ASCII", Mapping => 'STANDARD'); |
1063
|
86
|
|
|
|
|
8964
|
foreach my $line (split(/(?:[\t ]*[\r\n]+)+/, $s)) { |
1064
|
86
|
|
|
|
|
217
|
my $spc = ''; |
1065
|
86
|
|
|
|
|
1250
|
foreach my $word (split(/([\t ]+)/, $line)) { |
1066
|
990
|
100
|
100
|
|
|
4565
|
next unless scalar(@splitwords) or $word; # skip first garbage |
1067
|
926
|
100
|
|
|
|
4095
|
if ($word =~ /[\t ]/) { |
1068
|
467
|
|
|
|
|
683
|
$spc = $word; |
1069
|
467
|
|
|
|
|
936
|
next; |
1070
|
|
|
|
|
|
|
} |
1071
|
|
|
|
|
|
|
|
1072
|
459
|
|
|
|
|
948
|
my $cont = $spc.$word; |
1073
|
459
|
|
|
|
|
727
|
my $elen = length($cont); |
1074
|
459
|
50
|
|
|
|
1120
|
next unless $elen; |
1075
|
459
|
100
|
|
|
|
933
|
if (scalar(@splitwords)) { |
1076
|
|
|
|
|
|
|
# Concatenate adjacent words so that encoded-word and |
1077
|
|
|
|
|
|
|
# unencoded text will adjoin with separating whitespace. |
1078
|
373
|
100
|
|
|
|
1021
|
if ($elen <= $restlen) { |
1079
|
324
|
|
|
|
|
937
|
$splitwords[-1]->[0] .= $cont; |
1080
|
324
|
|
|
|
|
557
|
$restlen -= $elen; |
1081
|
|
|
|
|
|
|
} else { |
1082
|
49
|
|
|
|
|
157
|
push @splitwords, [$cont, undef, $ascii]; |
1083
|
49
|
|
|
|
|
70
|
$restlen = $maxrestlen - $elen; |
1084
|
|
|
|
|
|
|
} |
1085
|
|
|
|
|
|
|
} else { |
1086
|
86
|
|
|
|
|
309
|
push @splitwords, [$cont, undef, $ascii]; |
1087
|
86
|
|
|
|
|
145
|
$restlen -= $elen; |
1088
|
|
|
|
|
|
|
} |
1089
|
459
|
|
|
|
|
963
|
$spc = ''; |
1090
|
|
|
|
|
|
|
} |
1091
|
86
|
100
|
|
|
|
388
|
if ($spc) { |
1092
|
30
|
50
|
|
|
|
75
|
if (scalar(@splitwords)) { |
1093
|
30
|
|
|
|
|
56
|
$splitwords[-1]->[0] .= $spc; |
1094
|
30
|
|
|
|
|
87
|
$restlen -= length($spc); |
1095
|
|
|
|
|
|
|
} else { # only WSPs |
1096
|
0
|
|
|
|
|
0
|
push @splitwords, [$spc, undef, $ascii]; |
1097
|
0
|
|
|
|
|
0
|
$restlen = $maxrestlen - length($spc); |
1098
|
|
|
|
|
|
|
} |
1099
|
|
|
|
|
|
|
} |
1100
|
|
|
|
|
|
|
} |
1101
|
86
|
|
|
|
|
689
|
return @splitwords; |
1102
|
|
|
|
|
|
|
} |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
# _clip_unsafe UNICODE, ENCODING, CHARSET_OBJECT, ROOM_OF_FIRST_LINE |
1105
|
|
|
|
|
|
|
# Private: used by encode_mimewords() to bite off one encodable |
1106
|
|
|
|
|
|
|
# ``word'' from a Unicode string. |
1107
|
|
|
|
|
|
|
# Note: When Unicode/multibyte support is not enabled, character |
1108
|
|
|
|
|
|
|
# boundaries of multibyte string shall be broken! |
1109
|
|
|
|
|
|
|
sub _clip_unsafe { |
1110
|
50
|
|
|
50
|
|
92
|
my $ustr = shift; |
1111
|
50
|
|
|
|
|
78
|
my $encoding = shift; |
1112
|
50
|
|
|
|
|
67
|
my $charset = shift; |
1113
|
50
|
|
|
|
|
99
|
my $restlen = shift; |
1114
|
50
|
50
|
|
|
|
186
|
return ("", "") unless length($ustr); |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
# Seek maximal division point. |
1117
|
50
|
|
|
|
|
241
|
my ($shorter, $longer) = (0, length($ustr)); |
1118
|
50
|
|
|
|
|
131
|
while ($shorter < $longer) { |
1119
|
239
|
|
|
|
|
574
|
my $cur = ($shorter + $longer + 1) >> 1; |
1120
|
239
|
|
|
|
|
767
|
my $enc = substr($ustr, 0, $cur); |
1121
|
239
|
|
|
|
|
255
|
if (MIME::Charset::USE_ENCODE ne '') { |
1122
|
239
|
|
|
|
|
1249
|
$enc = $charset->undecode($enc); |
1123
|
|
|
|
|
|
|
} |
1124
|
239
|
|
|
|
|
23365
|
my $elen = $charset->encoded_header_len($enc, $encoding); |
1125
|
239
|
100
|
|
|
|
7478
|
if ($elen <= $restlen) { |
1126
|
129
|
|
|
|
|
435
|
$shorter = $cur; |
1127
|
|
|
|
|
|
|
} else { |
1128
|
110
|
|
|
|
|
310
|
$longer = $cur - 1; |
1129
|
|
|
|
|
|
|
} |
1130
|
|
|
|
|
|
|
} |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
# Make sure that combined characters won't be divided. |
1133
|
50
|
|
|
|
|
63
|
my ($fenc, $renc); |
1134
|
50
|
|
|
|
|
80
|
my $max = length($ustr); |
1135
|
50
|
|
|
|
|
62
|
while (1) { |
1136
|
50
|
|
|
|
|
87
|
$@ = ''; |
1137
|
50
|
|
|
|
|
77
|
eval { |
1138
|
50
|
|
|
|
|
366
|
($fenc, $renc) = |
1139
|
|
|
|
|
|
|
(substr($ustr, 0, $shorter), substr($ustr, $shorter)); |
1140
|
50
|
|
|
|
|
74
|
if (MIME::Charset::USE_ENCODE ne '') { |
1141
|
|
|
|
|
|
|
# FIXME: croak if $renc =~ /^\p{M}/ |
1142
|
50
|
|
|
|
|
214
|
$fenc = $charset->undecode($fenc, FB_CROAK()); |
1143
|
50
|
|
|
|
|
6442
|
$renc = $charset->undecode($renc, FB_CROAK()); |
1144
|
|
|
|
|
|
|
} |
1145
|
|
|
|
|
|
|
}; |
1146
|
50
|
50
|
|
|
|
1767
|
last unless ($@); |
1147
|
|
|
|
|
|
|
|
1148
|
0
|
|
|
|
|
0
|
$shorter++; |
1149
|
0
|
0
|
|
|
|
0
|
unless ($shorter < $max) { # Unencodable character(s) may be included. |
1150
|
0
|
|
|
|
|
0
|
return ($charset->undecode($ustr), ""); |
1151
|
|
|
|
|
|
|
} |
1152
|
|
|
|
|
|
|
} |
1153
|
|
|
|
|
|
|
|
1154
|
50
|
100
|
|
|
|
134
|
if (length($fenc)) { |
1155
|
46
|
|
|
|
|
209
|
return ($fenc, $renc); |
1156
|
|
|
|
|
|
|
} else { |
1157
|
4
|
|
|
|
|
23
|
return ($renc, ""); |
1158
|
|
|
|
|
|
|
} |
1159
|
|
|
|
|
|
|
} |
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
#------------------------------ |
1162
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
# _getparams HASHREF, OPTS |
1164
|
|
|
|
|
|
|
# Private: used to get option parameters. |
1165
|
|
|
|
|
|
|
sub _getparams { |
1166
|
162
|
|
|
162
|
|
282
|
my $params = shift; |
1167
|
162
|
|
|
|
|
727
|
my %params = @_; |
1168
|
162
|
|
|
|
|
254
|
my %Params; |
1169
|
|
|
|
|
|
|
my %GotParams; |
1170
|
162
|
|
|
|
|
390
|
foreach my $k (qw(NoDefault YesNo Others Obsoleted ToUpper)) { |
1171
|
810
|
|
100
|
|
|
3571
|
$Params{$k} = $params{$k} || []; |
1172
|
|
|
|
|
|
|
} |
1173
|
162
|
|
|
|
|
712
|
foreach my $k (keys %$params) { |
1174
|
338
|
|
|
|
|
507
|
my $supported = 0; |
1175
|
338
|
|
|
|
|
748
|
foreach my $i (@{$Params{NoDefault}}, @{$Params{YesNo}}, |
|
338
|
|
|
|
|
2313
|
|
|
338
|
|
|
|
|
636
|
|
|
338
|
|
|
|
|
543
|
|
1176
|
338
|
|
|
|
|
2546
|
@{$Params{Others}}, @{$Params{Obsoleted}}) { |
1177
|
1286
|
100
|
|
|
|
3536
|
if (lc $i eq lc $k) { |
1178
|
338
|
|
|
|
|
1242
|
$GotParams{$i} = $params->{$k}; |
1179
|
338
|
|
|
|
|
627
|
$supported = 1; |
1180
|
338
|
|
|
|
|
832
|
last; |
1181
|
|
|
|
|
|
|
} |
1182
|
|
|
|
|
|
|
} |
1183
|
338
|
50
|
|
|
|
1078
|
carp "unknown or deprecated option ``$k''" unless $supported; |
1184
|
|
|
|
|
|
|
} |
1185
|
|
|
|
|
|
|
# get defaults |
1186
|
162
|
|
|
|
|
304
|
foreach my $i (@{$Params{YesNo}}, @{$Params{Others}}) { |
|
162
|
|
|
|
|
313
|
|
|
162
|
|
|
|
|
610
|
|
1187
|
954
|
100
|
|
|
|
4318
|
$GotParams{$i} = $Config->{$i} unless defined $GotParams{$i}; |
1188
|
|
|
|
|
|
|
} |
1189
|
|
|
|
|
|
|
# yesno params |
1190
|
162
|
|
|
|
|
296
|
foreach my $i (@{$Params{YesNo}}) { |
|
162
|
|
|
|
|
682
|
|
1191
|
162
|
50
|
33
|
|
|
1111
|
if (!$GotParams{$i} or uc $GotParams{$i} eq "NO") { |
1192
|
0
|
|
|
|
|
0
|
$GotParams{$i} = "NO"; |
1193
|
|
|
|
|
|
|
} else { |
1194
|
162
|
|
|
|
|
866
|
$GotParams{$i} = "YES"; |
1195
|
|
|
|
|
|
|
} |
1196
|
|
|
|
|
|
|
} |
1197
|
|
|
|
|
|
|
# normalize case |
1198
|
162
|
|
|
|
|
291
|
foreach my $i (@{$Params{ToUpper}}) { |
|
162
|
|
|
|
|
587
|
|
1199
|
594
|
|
66
|
|
|
3199
|
$GotParams{$i} &&= uc $GotParams{$i}; |
1200
|
|
|
|
|
|
|
} |
1201
|
162
|
|
|
|
|
3107
|
return %GotParams; |
1202
|
|
|
|
|
|
|
} |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
#------------------------------ |
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
=back |
1207
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
=head2 Configuration Files |
1209
|
|
|
|
|
|
|
B<**> |
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
Built-in defaults of option parameters for L |
1212
|
|
|
|
|
|
|
(except 'Charset' option) and |
1213
|
|
|
|
|
|
|
L can be overridden by configuration files: |
1214
|
|
|
|
|
|
|
F and F. |
1215
|
|
|
|
|
|
|
For more details read F. |
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
=head1 VERSION |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
Consult C<$VERSION> variable. |
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
Development versions of this module may be found at |
1222
|
|
|
|
|
|
|
L. |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
=head1 SEE ALSO |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
L, |
1227
|
|
|
|
|
|
|
L |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
=head1 AUTHORS |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
The original version of function decode_mimewords() is derived from |
1232
|
|
|
|
|
|
|
L module that was written by: |
1233
|
|
|
|
|
|
|
Eryq (F), ZeeGee Software Inc (F). |
1234
|
|
|
|
|
|
|
David F. Skoll (dfs@roaringpenguin.com) http://www.roaringpenguin.com |
1235
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
Other stuff are rewritten or added by: |
1237
|
|
|
|
|
|
|
Hatuka*nezumi - IKEDA Soji . |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
This program is free software; you can redistribute |
1240
|
|
|
|
|
|
|
it and/or modify it under the same terms as Perl itself. |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
=cut |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
1; |