Lines 9-50
Link Here
|
9 |
=head1 SYNOPSIS |
9 |
=head1 SYNOPSIS |
10 |
|
10 |
|
11 |
# Create an object for a content-type field: |
11 |
# Create an object for a content-type field: |
12 |
$field = new Mail::Field 'Content-type'; |
12 |
$field = new Mail::Field 'Content-type'; |
13 |
|
13 |
|
14 |
# Set some attributes: |
14 |
# Set some attributes: |
15 |
$field->param('_' => 'text/html'); |
15 |
$field->param('_' => 'text/html'); |
16 |
$field->param('charset' => 'us-ascii'); |
16 |
$field->param('charset' => 'us-ascii'); |
17 |
$field->param('boundary' => '---ABC---'); |
17 |
$field->param('boundary' => '---ABC---'); |
18 |
|
18 |
|
19 |
# Same: |
19 |
# Same: |
20 |
$field->set('_' => 'text/html', |
20 |
$field->set('_' => 'text/html', |
21 |
'charset' => 'us-ascii', |
21 |
'charset' => 'us-ascii', |
22 |
'boundary' => '---ABC---'); |
22 |
'boundary' => '---ABC---'); |
23 |
|
23 |
|
24 |
# Get an attribute, or undefined if not present: |
24 |
# Get an attribute, or undefined if not present: |
25 |
print "no id!" if defined($field->param('id')); |
25 |
print "no id!" if defined($field->param('id')); |
26 |
|
26 |
|
27 |
# Same, but use empty string for missing values: |
27 |
# Same, but use empty string for missing values: |
28 |
print "no id!" if ($field->paramstr('id') eq ''); |
28 |
print "no id!" if ($field->paramstr('id') eq ''); |
29 |
|
29 |
|
30 |
# Output as string: |
30 |
# Output as string: |
31 |
print $field->stringify, "\n"; |
31 |
print $field->stringify, "\n"; |
32 |
|
32 |
|
33 |
|
33 |
|
34 |
=head1 DESCRIPTION |
34 |
=head1 DESCRIPTION |
35 |
|
35 |
|
36 |
This is an abstract superclass of most MIME fields. It handles |
36 |
This is an abstract superclass of most MIME fields. It handles |
37 |
fields with a general syntax like this: |
37 |
fields with a general syntax like this: |
38 |
|
38 |
|
39 |
Content-Type: Message/Partial; |
39 |
Content-Type: Message/Partial; |
40 |
number=2; total=3; |
40 |
number=2; total=3; |
41 |
id="oc=jpbe0M2Yt4s@thumper.bellcore.com" |
41 |
id="oc=jpbe0M2Yt4s@thumper.bellcore.com" |
42 |
|
42 |
|
43 |
Comments are supported I<between> items, like this: |
43 |
Comments are supported I<between> items, like this: |
44 |
|
44 |
|
45 |
Content-Type: Message/Partial; (a comment) |
45 |
Content-Type: Message/Partial; (a comment) |
46 |
number=2 (another comment) ; (yet another comment) total=3; |
46 |
number=2 (another comment) ; (yet another comment) total=3; |
47 |
id="oc=jpbe0M2Yt4s@thumper.bellcore.com" |
47 |
id="oc=jpbe0M2Yt4s@thumper.bellcore.com" |
48 |
|
48 |
|
49 |
|
49 |
|
50 |
=head1 PUBLIC INTERFACE |
50 |
=head1 PUBLIC INTERFACE |
Lines 100-105
Link Here
|
100 |
# token = 1*<any (ASCII) CHAR except SPACE, CTLs, or tspecials> |
100 |
# token = 1*<any (ASCII) CHAR except SPACE, CTLs, or tspecials> |
101 |
# |
101 |
# |
102 |
my $TSPECIAL = '()<>@,;:\</[]?="'; |
102 |
my $TSPECIAL = '()<>@,;:\</[]?="'; |
|
|
103 |
|
104 |
#" Fix emacs highlighting... |
105 |
|
103 |
my $TOKEN = '[^ \x00-\x1f\x80-\xff' . "\Q$TSPECIAL\E" . ']+'; |
106 |
my $TOKEN = '[^ \x00-\x1f\x80-\xff' . "\Q$TSPECIAL\E" . ']+'; |
104 |
|
107 |
|
105 |
# Encoded token: |
108 |
# Encoded token: |
Lines 108-113
Link Here
|
108 |
# Pattern to match spaces or comments: |
111 |
# Pattern to match spaces or comments: |
109 |
my $SPCZ = '(?:\s|\([^\)]*\))*'; |
112 |
my $SPCZ = '(?:\s|\([^\)]*\))*'; |
110 |
|
113 |
|
|
|
114 |
# Pattern to match non-semicolon as fallback for broken MIME |
115 |
# produced by some viruses |
116 |
my $BADTOKEN = '[^;]+'; |
111 |
|
117 |
|
112 |
#------------------------------ |
118 |
#------------------------------ |
113 |
# |
119 |
# |
Lines 133-139
Link Here
|
133 |
'total' => 3, |
139 |
'total' => 3, |
134 |
'id' => "ocj=pbe0M2"); |
140 |
'id' => "ocj=pbe0M2"); |
135 |
|
141 |
|
136 |
Note that a single argument is taken to be a I<reference> to |
142 |
Note that a single argument is taken to be a I<reference> to |
137 |
a paramhash, while multiple args are taken to be the elements |
143 |
a paramhash, while multiple args are taken to be the elements |
138 |
of the paramhash themselves. |
144 |
of the paramhash themselves. |
139 |
|
145 |
|
Lines 160-175
Link Here
|
160 |
it as a hash reference. For example, here is a field with parameters: |
166 |
it as a hash reference. For example, here is a field with parameters: |
161 |
|
167 |
|
162 |
Content-Type: Message/Partial; |
168 |
Content-Type: Message/Partial; |
163 |
number=2; total=3; |
169 |
number=2; total=3; |
164 |
id="oc=jpbe0M2Yt4s@thumper.bellcore.com" |
170 |
id="oc=jpbe0M2Yt4s@thumper.bellcore.com" |
165 |
|
171 |
|
166 |
Here is how you'd extract them: |
172 |
Here is how you'd extract them: |
167 |
|
173 |
|
168 |
$params = $class->parse_params('content-type'); |
174 |
$params = $class->parse_params('content-type'); |
169 |
if ($$params{'_'} eq 'message/partial') { |
175 |
if ($$params{'_'} eq 'message/partial') { |
170 |
$number = $$params{'number'}; |
176 |
$number = $$params{'number'}; |
171 |
$total = $$params{'total'}; |
177 |
$total = $$params{'total'}; |
172 |
$id = $$params{'id'}; |
178 |
$id = $$params{'id'}; |
173 |
} |
179 |
} |
174 |
|
180 |
|
175 |
Like field names, parameter names are coerced to lowercase. |
181 |
Like field names, parameter names are coerced to lowercase. |
Lines 181-190
Link Here
|
181 |
|
187 |
|
182 |
=cut |
188 |
=cut |
183 |
|
189 |
|
|
|
190 |
sub rfc2231decode { |
191 |
my($val) = @_; |
192 |
my($enc, $lang, $rest); |
193 |
|
194 |
if ($val =~ m/^([^\']*)\'([^\']*)\'(.*)$/) { |
195 |
# SHOULD REALLY DO SOMETHING MORE INTELLIGENT WITH ENCODING!!! |
196 |
$enc = $1; |
197 |
$lang = $2; |
198 |
$rest = $3; |
199 |
$rest = rfc2231percent($rest); |
200 |
} elsif ($val =~ m/^([^\']*)\'([^\']*)$/) { |
201 |
$enc = $1; |
202 |
$rest = $2; |
203 |
$rest = rfc2231percent($rest); |
204 |
} else { |
205 |
$rest = rfc2231percent($val); |
206 |
} |
207 |
return $rest; |
208 |
} |
209 |
|
210 |
sub rfc2231percent { |
211 |
# Do percent-subsitution |
212 |
my($str) = @_; |
213 |
$str =~ s/%([0-9a-fA-F]{2})/pack("c", hex($1))/ge; |
214 |
return $str; |
215 |
} |
216 |
|
184 |
sub parse_params { |
217 |
sub parse_params { |
185 |
my ($self, $raw) = @_; |
218 |
my ($self, $raw) = @_; |
186 |
my %params = (); |
219 |
my %params = (); |
|
|
220 |
my %rfc2231params = (); |
187 |
my $param; |
221 |
my $param; |
|
|
222 |
my $val; |
223 |
my $part; |
188 |
|
224 |
|
189 |
# Get raw field, and unfold it: |
225 |
# Get raw field, and unfold it: |
190 |
defined($raw) or $raw = ''; |
226 |
defined($raw) or $raw = ''; |
Lines 200-208
Link Here
|
200 |
$raw =~ m/\G$SPCZ\;$SPCZ/og or last; # skip leading separator |
236 |
$raw =~ m/\G$SPCZ\;$SPCZ/og or last; # skip leading separator |
201 |
$raw =~ m/\G($PARAMNAME)\s*=\s*/og or last; # give up if not a param |
237 |
$raw =~ m/\G($PARAMNAME)\s*=\s*/og or last; # give up if not a param |
202 |
$param = lc($1); |
238 |
$param = lc($1); |
203 |
$raw =~ m/\G(\"([^\"]+)\")|\G($TOKEN)|\G($ENCTOKEN)/g or last; # give up if no value |
239 |
$raw =~ m/\G(\"([^\"]+)\")|\G($ENCTOKEN)|\G($BADTOKEN)|\G($TOKEN)/g or last; # give up if no value" |
204 |
my ($qstr, $str, $token, $enctoken) = ($1, $2, $3, $4); |
240 |
my ($qstr, $str, $enctoken, $badtoken, $token) = ($1, $2, $3, $4, $5); |
205 |
$params{$param} = defined($qstr) ? $str : (defined($token) ? $token : $enctoken); |
241 |
if (defined($badtoken)) { |
|
|
242 |
# Strip leading/trailing whitespace from badtoken |
243 |
$badtoken =~ s/^\s*//; |
244 |
$badtoken =~ s/\s*$//; |
245 |
} |
246 |
$val = defined($qstr) ? $str : |
247 |
(defined($enctoken) ? $enctoken : |
248 |
(defined($badtoken) ? $badtoken : $token)); |
249 |
|
250 |
# Do RFC 2231 processing |
251 |
if ($param =~ /\*/) { |
252 |
my($name, $num); |
253 |
# Pick out the parts of the parameter |
254 |
if ($param =~ m/^([^*]+)\*([^*]+)\*?$/) { |
255 |
# We have param*number* or param*number |
256 |
$name = $1; |
257 |
$num = $2; |
258 |
} else { |
259 |
# Fake a part of zero... not sure how to handle this properly |
260 |
$param =~ s/\*//g; |
261 |
$name = $param; |
262 |
$num = 0; |
263 |
} |
264 |
# Decode the value unless it was a quoted string |
265 |
if (!defined($qstr)) { |
266 |
$val = rfc2231decode($val); |
267 |
} |
268 |
$rfc2231params{$name}{$num} .= $val; |
269 |
} else { |
270 |
# Make a fake "part zero" for non-RFC2231 params |
271 |
$rfc2231params{$param}{"0"} = $val; |
272 |
} |
273 |
} |
274 |
|
275 |
# Extract reconstructed parameters |
276 |
foreach $param (keys %rfc2231params) { |
277 |
foreach $part (sort { $a <=> $b } keys %{$rfc2231params{$param}}) { |
278 |
$params{$param} .= $rfc2231params{$param}{$part}; |
279 |
} |
206 |
debug " field param <$param> = <$params{$param}>"; |
280 |
debug " field param <$param> = <$params{$param}>"; |
207 |
} |
281 |
} |
208 |
|
282 |
|
Lines 227-233
Link Here
|
227 |
|
301 |
|
228 |
# Allow use as constructor, for MIME::Head: |
302 |
# Allow use as constructor, for MIME::Head: |
229 |
ref($self) or $self = bless({}, $self); |
303 |
ref($self) or $self = bless({}, $self); |
230 |
|
304 |
|
231 |
# Get params, and stuff them into the self object: |
305 |
# Get params, and stuff them into the self object: |
232 |
$self->set($self->parse_params($string)); |
306 |
$self->set($self->parse_params($string)); |
233 |
} |
307 |
} |