/[suikacvs]/messaging/manakai/lib/Message/Entity.pm
Suika

Contents of /messaging/manakai/lib/Message/Entity.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.32 - (show annotations) (download)
Fri Jul 26 12:42:00 2002 UTC (24 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.31: +4 -7 lines
2002-07-25  Wakaba <w@suika.fam.cx>

	* Tool.pm: New module.

1
2 =head1 NAME
3
4 Message::Entity Perl module
5
6 =head1 DESCRIPTION
7
8 Perl module for RFC 822/2822 C<message>.
9 MIME multipart will be also supported (but not implemented yet).
10
11 =cut
12
13 package Message::Entity;
14 use strict;
15 use vars qw(%DEFAULT $VERSION);
16 $VERSION=do{my @r=(q$Revision: 1.31 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
17
18 require Message::Util;
19 require Message::Header;
20 require Message::MIME::MediaType;
21 require Message::MIME::Encoding;
22 use overload '""' => sub { $_[0]->stringify },
23 fallback => 1;
24
25 ## Initialize of this class -- called by constructors
26 %DEFAULT = (
27 -_METHODS => [qw|header body content_type id|],
28 -_MEMBERS => [qw|header body _cte|],
29 ## entity_header -- Don't clone.
30 -accept_coderange => '7bit', ## 7bit / 8bit / binary
31 #add_ua => 1,
32 -body_default_charset => 'iso-2022-int-1',
33 -body_default_charset_input => 'iso-2022-int-1',
34 -body_default_media_type => 'text',
35 -body_default_media_subtype => 'plain',
36 -cte_default => '7bit',
37 #fill_date => 1,
38 -fill_date_name => 'date',
39 -fill_md5 => 0,
40 -fill_md5_name => 'md5',
41 #fill_msgid => 1,
42 -fill_msgid_name => 'message-id',
43 -force_mime_entity => 0,
44 -format => 'mail-rfc2822',
45 -guess_media_type => 1,
46 #internal_charset_name
47 -header_default_charset => 'iso-2022-int-1',
48 -header_default_charset_input => 'iso-2022-int-1',
49 -hook_init_fill_options => sub {},
50 -hook_stringify_fill_fields => sub {},
51 -linebreak_strict => 0, ## BUG: not work perfectly
52 -parse_all => 0,
53 -recalc_md5 => 1,
54 -text_coderange => 'binary',
55 ## '8bit' (MIME text/*) / 'binary' (HTTP text/*)
56 #ua_field_name => 'user-agent',
57 -ua_use_Config => 1,
58 -ua_use_Win32 => 1,
59 -uri_mailto_safe_level => 4,
60 );
61 sub _init ($;%) {
62 my $self = shift;
63 my %options = @_;
64 $self->{option} = {};
65 my $o = Message::Util::make_clone (\%DEFAULT);
66 for my $name (keys %$o) {
67 if (substr ($name, 0, 1) eq '-') {
68 $self->{option}->{substr ($name, 1)} = $$o{$name};
69 }
70 }
71
72 my @new_fields = ();
73 for my $name (keys %options) {
74 if (substr ($name, 0, 1) eq '-') {
75 $self->{option}->{substr ($name, 1)} = $options{$name};
76 } elsif ($name eq 'entity_header') {
77 $self->{entity_header} = $options{entity_header};
78 } else {
79 push @new_fields, ($name => $options{$name});
80 }
81 }
82
83 my $format = $self->{option}->{format};
84 if ($format =~ /http/) {
85 $self->{option}->{fill_date_ns} = $Message::Header::NS_phname2uri{'x-http'};
86 $self->{option}->{fill_msgid_from_ns} = $Message::Header::NS_phname2uri{'x-http'};
87 $self->{option}->{fill_ua_ns} = $Message::Header::NS_phname2uri{'x-http'};
88 $self->{option}->{accept_coderange} = 'binary';
89 $self->{option}->{text_coderange} = 'binary';
90 $self->{option}->{cte_default} = 'binary';
91 } else {
92 $self->{option}->{fill_date_ns} = $Message::Header::NS_phname2uri{'x-rfc822'};
93 $self->{option}->{fill_msgid_from_ns} = $Message::Header::NS_phname2uri{'x-rfc822'};
94 $self->{option}->{fill_ua_ns} = $Message::Header::NS_phname2uri{'x-rfc822'};
95 $self->{option}->{text_coderange} = '8bit';
96 if ($format =~ /news-usefor|smtp-8bitmime/) {
97 $self->{option}->{accept_coderange} = '8bit';
98 #$self->{option}->{cte_default} = '8bit';
99 } else {
100 $self->{option}->{accept_coderange} = '7bit';
101 }
102 }
103 $self->{option}->{fill_msgid_ns} = $Message::Header::NS_phname2uri{'x-rfc822'};
104 $self->{option}->{fill_mimever_ns} = $Message::Header::NS_phname2uri{'x-rfc822'};
105 unless (defined $self->{option}->{fill_date}) {
106 $self->{option}->{fill_date} = $format !~ /mime-entity|cgi|uri-url-mailto/;
107 }
108 unless (defined $self->{option}->{fill_msgid}) {
109 $self->{option}->{fill_msgid} = $format !~ /mime-entity|http|uri-url-mailto/;
110 }
111 unless (defined $self->{option}->{fill_ct}) {
112 $self->{option}->{fill_ct} = $format !~ /http/;
113 }
114 unless (defined $self->{option}->{fill_mimever}) {
115 $self->{option}->{fill_mimever} = $format !~ /http|mime-entity/;
116 }
117 unless (defined $self->{option}->{add_ua}) {
118 $self->{option}->{add_ua} = $format !~ /mime-entity/;
119 }
120 unless ($self->{option}->{fill_ua_name}) {
121 $self->{option}->{fill_ua_name} = $format =~ /response|cgi|uri-url-mailto/?
122 'server': 'user-agent';
123 }
124 &{ $self->{option}->{hook_init_fill_options} } ($self, $self->{option});
125 @new_fields;
126 }
127
128 =head1 CONSTRUCTORS
129
130 The following methods construct new C<Message::Entity> objects:
131
132 =over 4
133
134 =item Message::Entity->new ([%initial-fields/options])
135
136 Constructs a new C<Message::Entity> object. You might pass some initial
137 C<field-name>-C<field-body> pairs and/or options as parameters to the constructor.
138
139 Example:
140
141 $msg = new Message::Entity
142 Date => 'Thu, 03 Feb 1994 00:00:00 +0000',
143 Content_Type => 'text/html',
144 X_URI => '<http://www.foo.example/>',
145 -format => 'mail-rfc2822' ## not to be header field
146 ;
147
148 =cut
149
150 sub new ($;%) {
151 my $class = shift;
152 my $self = bless {}, $class;
153 my %new_field = $self->_init (@_);
154 if (defined $new_field{body}) {
155 $self->{body} = $new_field{body}; $new_field{body} = undef;
156 $self->{body} = $self->_parse_value ([$self->content_type] => $self->{body})
157 if $self->{option}->{parse_all};
158 }
159 $self->{header} = new Message::Header
160 -format => $self->{option}->{format},
161 -header_default_charset => $self->{option}->{header_default_charset},
162 -header_default_charset_input => $self->{option}->{header_default_charset_input},
163 -parse_all => $self->{option}->{parse_all}, %new_field;
164 $self;
165 }
166
167 =item Message::Entity->parse ($message, [%options])
168
169 Parses given C<message> (a message entity) and constructs a new C<Message::Entity>
170 object. You might pass some additional C<field-name>-C<field-body> pairs
171 or/and initial options as parameters to the constructor.
172
173 =cut
174
175 sub parse ($$;%) {
176 my $class = shift;
177 my $message = shift;
178 my $self = bless {}, $class;
179 my %new_field = $self->_init (@_);
180 my $nl = "\x0D\x0A";
181 unless ($self->{option}->{linebreak_strict}) {
182 $nl = Message::Util::decide_newline ($message);
183 }
184 ## BUG: binary unsafe yet!
185 my @header = ();
186 my @body = split /$nl/, $message;
187 while (1) {
188 my $line = shift @body;
189 unless (length($line)) {
190 last;
191 } else {
192 push @header, $line;
193 }
194 }
195 $new_field{body} = undef if $new_field{body};
196 $self->{header} = parse_array Message::Header \@header,
197 -header_default_charset => $self->{option}->{header_default_charset},
198 -header_default_charset_input => $self->{option}->{header_default_charset_input},
199 -parse_all => $self->{option}->{parse_all},
200 -format => $self->{option}->{format}, %new_field;
201 $self->{body} = join ($nl, @body) . $nl;
202 $self->{body} = $self->_parse_value ([$self->content_type] => $self->{body})
203 if $self->{option}->{parse_all};
204 $self;
205 }
206
207 =back
208
209 =head1 METHODS
210
211 =head2 $self->header ([$new_header])
212
213 Returns Message::Header unless $new_header.
214 Set $new_header instead of current C<header>.
215 If !ref $new_header, Message::Header->parse is automatically
216 called.
217
218 =cut
219
220 ## TODO: to be compatible with HTTP::Message
221 sub header ($;$) {
222 my $self = shift;
223 my $new_header = shift;
224 if (ref $new_header) {
225 $self->{header} = $new_header;
226 } elsif ($new_header) {
227 $self->{header} = Message::Header->parse ($new_header,
228 -header_default_charset => $self->{option}->{header_default_charset},
229 -header_default_charset_input => $self->{option}->{header_default_charset_input},
230 -parse_all => $self->{option}->{parse_all},
231 -format => $self->{option}->{format});
232 }
233 unless (ref $self->{header} || length $self->{header}) {
234 $self->{header} = new Message::Header (
235 -header_default_charset => $self->{option}->{header_default_charset},
236 -header_default_charset_input => $self->{option}->{header_default_charset_input},
237 -parse_all => $self->{option}->{parse_all},
238 -format => $self->{option}->{format});
239 }
240 $self->{header};
241 }
242
243 =head2 $self->body ([$new_body])
244
245 Returns C<body> as string unless $new_body.
246 Set $new_body instead of current C<body>.
247
248 =cut
249
250 sub body ($;$) {
251 my $self = shift;
252 my $new_body = shift;
253 if ($new_body) {
254 $self->{body} = $new_body;
255 }
256 $self->{body} = $self->_parse_value ([$self->content_type] => $self->{body})
257 unless ref $self->{body};
258 $self->{body};
259 }
260
261 ## [SG]et its entity header. This method is or can be used
262 ## when Message::Entity is used as a body (such as message/rfc822).
263 sub entity_header ($;$) {
264 my $self = shift;
265 my $new_header = shift;
266 if (ref $new_header) {
267 $self->{entity_header} = $new_header;
268 }
269 $self->{entity_header};
270 }
271
272 ## Note: If you once parse body (including parse_all => 1 option),
273 ## it might make validation failed.
274 sub md5_check ($) {
275 my $self = shift;
276 my $md5f = $self->{header}->field ('content-md5', -new_item_unless_exist => 0);
277 my $md5; $md5 = $md5f->value if ref $md5f;
278 unless ($md5) {
279 Carp::carp "md5_check: MD5 checksum not found";
280 return undef;
281 }
282 my $MD5;
283 eval q{
284 require Digest::MD5; require MIME::Base64;
285 $MD5 = MIME::Base64::encode (Digest::MD5::md5 ($self->{body}));
286 $MD5 =~ tr/\x09\x0A\x0D\x20//d;
287 } or Carp::croak $@;
288 return $MD5 eq $md5? 1 : 0;
289 }
290
291 ## $self->_parse_value ($type, $value);
292 sub _parse_value ($$$) {
293 my $self = shift;
294 my ($mt,$mst) = @{ shift(@_) };
295 my $value = shift;
296 return $value if ref $value;
297
298 ## decode
299 $value = $self->_decode_body ($value);
300
301 my $mt_def = $Message::MIME::MediaType::type{$mt}->{$mst};
302 $mt_def = $Message::MIME::MediaType::type{$mt}->{'/default'} unless ref $mt_def;
303 $mt_def = $Message::MIME::MediaType::type{'/default'}->{'/default'}
304 unless ref $mt_def;
305 my $handler = $mt_def->{handler}
306 || $Message::MIME::MediaType::type{$mt}->{'/default'}->{handler}
307 || $Message::MIME::MediaType::type{'/default'}->{'/default'}->{handler};
308 ## Ummmmmm....
309 if (ref $handler eq 'CODE') {
310 $handler = &$handler ($self, $mt, $mst);
311 }
312 my $vtype = $handler->[0];
313 my %vopt = (
314 -format => $self->{option}->{format},
315 -linebreak_strict => $self->{option}->{linebreak_strict},
316 -media_type => $mt,
317 -media_subtype => $mst,
318 -parse_all => $self->{option}->{parse_all},
319 -body_default_charset => $self->{option}->{body_default_charset},
320 -body_default_charset_input => $self->{option}->{body_default_charset_input},
321 -internal_charset_name => $self->{option}->{internal_charset_name},
322 entity_header => $self->{header},
323 );
324 ## Media type specified option/parameters
325 if (ref $handler->[1] eq 'HASH') {
326 for (keys %{$handler->[1]}) {
327 $vopt{$_} = ${$handler->[1]}{$_};
328 }
329 }
330 ## Inherited options
331 if (ref $handler->[2] eq 'ARRAY') {
332 for (@{$handler->[2]}) {
333 $vopt{'-'.$_} = $self->{option}->{$_};
334 }
335 }
336
337 if ($vtype eq ':none:') {
338 return $value;
339 } elsif (defined $value) {
340 eval "require $vtype" or Carp::croak qq{<parse>: $vtype: Can't load package: $@};
341 return $vtype->parse ($value, %vopt);
342 } else {
343 eval "require $vtype" or Carp::croak qq{<parse>: $vtype: Can't load package: $@};
344 return $vtype->new (%vopt);
345 }
346 }
347
348 sub _decode_body ($$) {
349 my $self = shift;
350 my $value = shift;
351 ## MIME CTE
352 my $cte = $self->{_cte} || '';
353 my $ctef = $self->header->field ('content-transfer-encoding',
354 -new_item_unless_exist => 0);
355 $cte = $ctef->value if ref $ctef;
356 my $f = $Message::MIME::Encoding::DECODER{$cte};
357 if (ref $f) {
358 ($value, $cte) = &$f ($self, $value);
359 }
360 $self->{_cte} = $cte;
361 $value;
362 }
363
364 sub _encode_body ($$\%) {
365 my $self = shift;
366 my $value = shift;
367 my $option = shift;
368 ## MIME CTE
369 my $current_cte = $self->{_cte} || 'binary';
370 my $ctef = $self->{header}->field ('content-transfer-encoding',
371 -new_item_unless_exist => 0);
372 my $cte = ''; $cte = lc $ctef->value if ref $ctef;
373 my %enoption;
374 ## Get media type of entity body and its accept CTE list
375 my ($mt,$mst) = $self->content_type;
376 my $mt_def = $Message::MIME::MediaType::type{$mt}->{$mst};
377 $mt_def = $Message::MIME::MediaType::type{$mt}->{'/default'}
378 unless ref $mt_def;
379 $mt_def = $Message::MIME::MediaType::type{'/default'}->{'/default'}
380 unless ref $mt_def;
381 $enoption{mt_is_text} = 1
382 if $mt eq 'text' || $mt eq 'multipart' || $mt eq 'message';
383 $enoption{mt_is_text} = 1 if $mt_def->{text_content};
384 my ($charset, $charset_def) = '';
385 if ($mt_def->{mime_charset}) {
386 ## If CT is able to have its charset parameter,
387 my $ct = $self->{header}->field ('content-type',
388 -new_item_unless_exist => 0);
389 $charset = $ct->parameter ('charset') if ref $ct;
390 if ($charset) {
391 $charset_def = $Message::MIME::Charset::CHARSET{$charset};
392 } else {
393 $charset_def = $Message::MIME::Charset::CHARSET{'*default'};
394 ## Note: 'encoding_after_encode' option's value is hardcoded.
395 }
396 } else { ## Don't have mime style "charset" parameter
397 $charset_def = {mime_text => 1};
398 }
399 $charset_def = {} unless ref $charset_def; ## dummy
400 #if ($charset_def->{mime_text} != 1) { ## See also Note above
401 if (Message::MIME::Charset::is_mime_text ($charset || '*default') != 1) {
402 $enoption{mt_is_text} = 0 if $mt eq 'text';
403 my $ct = $self->{header}->field ('content-type');
404 $ct->not_mime_text ($option->{text_coderange} eq 'binary'? 0:1);
405 }
406 ## If accept CTE list is defined,
407 for my $def ($charset_def, $mt_def) {
408 if (ref $def->{accept_cte} eq 'ARRAY') {
409 my $f = 1; for (@{$def->{accept_cte}}) {
410 if ($cte eq $_) {$f = 0; last}
411 }
412 if ($f) { ## If CTE is not accepted,
413 $cte = $def->{accept_cte}->[0];
414 }
415 }
416 }
417 if ($current_cte eq 'binary' || ($current_cte && $current_cte ne $cte)) {
418 my $de = $Message::MIME::Encoding::DECODER{$current_cte};
419 my $en = $Message::MIME::Encoding::ENCODER{$cte || 'binary'};
420 if (ref $de && ref $en) {
421 my ($e, $decoded);
422 ($decoded, $e) = &$de ($self, $value);
423 ## Check transparent coderange
424 my $cr = $self->Message::MIME::Encoding::decide_coderange
425 ($decoded, \%enoption);
426 if ($option->{accept_coderange} eq '8bit') {
427 if ($cr eq 'binary') {
428 $cte = $charset_def->{cte_7bit_preferred}
429 || $mt_def->{cte_7bit_preferred} || 'base64';
430 $en = $Message::MIME::Encoding::ENCODER{$cte};
431 }
432 } elsif ($option->{accept_coderange} eq '7bit') {
433 if ($cr eq 'binary' || $cr eq '8bit') {
434 $cte = $charset_def->{cte_7bit_preferred}
435 || $mt_def->{cte_7bit_preferred} || 'base64';
436 $en = $Message::MIME::Encoding::ENCODER{$cte};
437 if ($mt eq 'message') {
438 my $ct = $self->{header}->field ('content-type');
439 $ct->not_mime_text ($option->{text_coderange} eq 'binary'? 0:1);
440 }
441 }
442 }
443 if ($e eq 'binary') {
444 ($value, $e) = &$en ($self, $decoded, \%enoption);
445 $e = '' if ($e eq $option->{cte_default});
446 $e = '' if $e eq '7bit'
447 && ( $option->{cte_default} eq '8bit'
448 || $option->{cte_default} eq 'binary');
449 $e = '' if $e eq '8bit' && $option->{cte_default} eq 'binary';
450 if ($e) {
451 $ctef = $self->{header}->field ('content-transfer-encoding')
452 unless ref $ctef;
453 $ctef->value ($e);
454 } elsif (ref $ctef) {
455 $ctef->value ('');
456 }
457 } else {
458 $ctef = $self->{header}->field ('content-transfer-encoding')
459 unless ref $ctef;
460 $ctef->value ($current_cte);
461 }
462 } else { ## Can't encode by given CTE
463 $ctef = $self->{header}->field ('content-transfer-encoding')
464 unless ref $ctef;
465 $ctef->value ($current_cte);
466 }
467 }
468 if (ref $ctef && $ctef->value eq '') {
469 $self->{header}->delete ('content-transfer-encoding');
470 }
471 $value;
472 }
473
474 =head2 $self->stringify ([%option])
475
476 Returns the C<message> as a string.
477
478 =cut
479
480 sub stringify ($;%) {
481 my $self = shift;
482 my %params = @_;
483 my %option = %{$self->{option}};
484 for (grep {/^-/} keys %params) {$option{substr ($_, 1)} = $params{$_}}
485 my ($header, $body, $body0);
486 if (ref $self->{body}) {
487 $self->{body}->entity_header ($self->{header});
488 $body0 = $self->{body}->stringify (-parent_format => $option{format},
489 -linebreak_strict => $option{linebreak_strict});
490 } else {
491 $body0 = $self->{body};
492 }
493 $body = $self->_encode_body ($body0, \%option);
494 if (ref $self->{header}) {
495 my %exist;
496 for ($self->{header}->field_name_list) {$exist{$_} = 1}
497 &{ $option{hook_stringify_fill_fields} } ($self, \%exist, \%option);
498 my $ns_content = $Message::Header::NS_phname2uri{content};
499 if ($option{fill_date}
500 && !$exist{$option{fill_date_name}.':'.$option{fill_date_ns}}) {
501 $self->{header}->field
502 ($option{fill_date_name}, -ns => $option{fill_date_ns})->unix_time (time);
503 }
504 if ($option{fill_msgid}
505 && !$exist{$option{fill_msgid_name}.':'.$option{fill_msgid_ns}}) {
506 my $from = $self->{header}->field
507 ('from', -ns => $option{fill_msgid_from_ns}, -new_item_unless_exist => 0);
508 $from = $from->addr_spec if ref $from;
509 $self->{header}->field
510 ($option{fill_msgid_name}, -ns => $option{fill_msgid_ns})
511 ->generate (addr_spec => $from)
512 if $from;
513 } # fill_msgid
514 if (($option{fill_md5} && !$exist{ $option{fill_md5_name} .':'. $ns_content})
515 || ($option{recalc_md5} && $exist{ $option{fill_md5_name} .':'. $ns_content})) {
516 my $md5;
517 eval q{
518 require Digest::MD5; require MIME::Base64;
519 $md5 = MIME::Base64::encode (Digest::MD5::md5 ($body0));
520 $md5 =~ tr/\x09\x0A\x0D\x20//d;
521 } or Carp::carp $@;
522 if ($md5) {
523 my $md5f = $self->{header}->field ($option{fill_md5_name}, -ns => $ns_content);
524 $md5f->value ($md5);
525 }
526 }
527 my $ismime = 0;
528 for (keys %exist) {if (/:$ns_content$/) { $ismime = 1; last }}
529 unless ($ismime) {
530 $ismime = 1 if $option{force_mime_entity};
531 $ismime = 1 if $option{fill_md5};
532 $ismime = 1 if $option{body_default_media_type} ne 'text';
533 $ismime = 1 if $option{body_default_media_subtype} ne 'plain';
534 }
535 if ($ismime) {
536 if ($option{fill_ct} && !$exist{'type:'.$ns_content}) {
537 my $ct = $self->{header}->field ('type',
538 -parse => 1, -ns => $ns_content);
539 $ct->media_type ($option{body_default_media_type}.'/'
540 .$option{body_default_media_subtype});
541 $ct->replace (Message::MIME::Charset::name_minimumize ($option{body_default_charset} => $body0));
542 }
543 if ($option{fill_mimever}
544 && !$exist{'mime-version:'.$option{fill_mimever_ns}}) {
545 ## BUG: doesn't support rfc10]49, HTTP (ie. non-MIME) content-*: fields
546 $self->{header}->add ('mime-version' => '1.0',
547 -parse => 0, -ns => $option{fill_mimever_ns});
548 }
549 } # $ismime
550 if ($option{format} =~ /uri-url-mailto/ && $exist{'type:$ns_content'}
551 && $option{uri_mailto_safe_level} > 1) {
552 $self->{header}->field ('content-type')->media_type ('text/plain');
553 }
554 if ($option{add_ua}) {
555 $self->{header}->field ($option{fill_ua_name})->add_our_name (
556 -use_Config => $option{ua_use_Config},
557 -use_Win32 => $option{ua_use_Win32},
558 -date => q$Date: 2002/07/22 07:48:28 $,
559 );
560 }
561 $header = $self->{header}->stringify (-format => $option{format},
562 -linebreak_strict => $option{linebreak_strict},
563 -uri_mailto_safe_level => $option{uri_mailto_safe_level});
564 } else {
565 $header = $self->{header};
566 unless ($option{linebreak_strict}) {
567 ## bare \x0D and bare \x0A are unsafe
568 $header =~ s/\x0D(?=[^\x09\x0A\x20])/\x0D\x20/g;
569 $header =~ s/\x0A(?=[^\x09\x20])/\x0A\x20/g;
570 }
571 }
572 if ($option{format} =~ /uri-url-mailto/) {
573 if ($option{format} =~ /rfc1738/) {
574 my $to = $self->{header}->stringify (-format => $option{format},
575 -uri_mailto_safe_level => $option{uri_mailto_safe_level});
576 $to? 'mailto:'.$to: '';
577 } else {
578 my $f = $option{format}; $f =~ s/-mailto/-mailto-to/;
579 my $to = $self->{header}->stringify (-format => $f,
580 -uri_mailto_safe_level => $option{uri_mailto_safe_level});
581 $body =~ s/([^:@+\$A-Za-z0-9\-_.!~*])/sprintf('%%%02X', ord $1)/ge;
582 if (length $body) {
583 $header .= '&' if $header;
584 $header .= 'body='.$body;
585 }
586 $header = '?'.$header if $header;
587 $to||$header? 'mailto:'.$to.$header: '';
588 }
589 } else {
590 $header .= "\x0D\x0A" if $header && $header !~ /\x0D\x0A$/;
591 $header."\x0D\x0A".$body;
592 }
593 }
594 *as_string = \&stringify;
595
596
597 =head1 SHORTCUT METHOD FOR MESSAGE PROPERTIES
598
599 =over 4
600
601 =item $self->content_type ([%options])
602
603 Returns Internet media type of message body
604 (aka MIME type, content type). Only media type
605 (type/subtype pair) is returned, i.e. no parameter
606 is returned, if any. To get such value, or to set
607 new value, use C<field> method.
608
609 Default is C<text/plain>.
610
611 Example:
612
613 $msg->field ('Content-Type')->media_type ('text/html');
614 print $msg->content_type; ## text/html
615
616 =cut
617
618 sub content_type ($;%) {
619 my $self = shift;
620 my $ct = $self->{header}->field ('content-type', -new_item_unless_exist => 0);
621 my ($mt, $mst);
622 unless (ref $ct) {
623 $mt = $self->{option}->{body_default_media_type};
624 $mst = $self->{option}->{body_default_media_subtype};
625 if ($mt ne 'text' || $mst ne 'plain') {
626 $ct = $self->{header}->field ('content-type');
627 $ct->media_type_major ($mt);
628 $ct->media_type_minor ($mst);
629 }
630 if ($self->{option}->{guess_media_type} && $self->{body} && !ref $self->{body}) {
631 if ($self->{body} =~ /^-----BEGIN PGP SIGNED MESSAGE-----\x0D?$/m
632 && $self->{body} =~ /^-----BEGIN PGP SIGNATURE-----\x0D?$/m
633 && $self->{body} =~ /^-----END PGP SIGNATURE-----\x0D?$/m) {
634 $ct = $self->{header}->field ('content-type') unless ref $ct;
635 $mt = $ct->media_type_major ('text');
636 $mst = $ct->media_type_minor ('x-pgp-cleartext-signed');
637 } elsif ($self->{body} =~ /^-----BEGIN PGP [A-Z\x20]+-----\x0D?$/m
638 && $self->{body} =~ /^-----END PGP [A-Z\x20]+-----\x0D?$/m) {
639 $ct = $self->{header}->field ('content-type') unless ref $ct;
640 $mt = $ct->media_type_major ('application');
641 $mst = $ct->media_type_minor ('pgp');
642 $ct->parameter (format => 'text');
643 } elsif ($self->{body} =~ /^-+ start of forwarded message \(RFC 934 encapsulation\) -+\x0D?$/m) {
644 $ct = $self->{header}->field ('content-type') unless ref $ct;
645 $mt = $ct->media_type_major ('text');
646 $mst = $ct->media_type_minor ('x-message-rfc934');
647 } elsif ($self->{body} =~ /^-{70,70}\x0D?$/m
648 && $self->{body} =~ /^-{30,30}\x0D?$/m
649 && $self->{body} =~ /\x0D?\x0A-{30,30}\x0D?\x0A\x0D?\x0AEnd of.+?Digest.*?\x0D?\x0A\*+(?:\x0D?\x0A)*$/s) {
650 $ct = $self->{header}->field ('content-type') unless ref $ct;
651 $mt = $ct->media_type_major ('text');
652 $mst = $ct->media_type_minor ('x-message-rfc1153');
653 } elsif ($self->{body} =~ /^-----PRIVACY-ENHANCED MESSAGE BOUNDARY-----\x0D?$/m) {
654 $ct = $self->{header}->field ('content-type') unless ref $ct;
655 $mt = $ct->media_type_major ('text');
656 $mst = $ct->media_type_minor ('x-message-pem');
657 }
658 }
659 } else {
660 ($mt, $mst) = ($ct->media_type_major, $ct->media_type_minor);
661 }
662 if ($self->{option}->{guess_media_type}) {
663 if ($mt eq 'text' && $mst eq 'plain') {
664 my $mls = $self->{header}->field ('x-mlserver', -new_item_unless_exist => 0);
665 if (ref $mls && $mls =~ /fml/) {
666 my $s = $self->{header}->field ('subject', -new_item_unless_exist => 0);
667 if (index ($s, 'RFC934(mh-burst)') >= 0) {
668 $ct = $self->{header}->field ('content-type') unless ref $ct;
669 $mt = $ct->media_type_major ('text');
670 $mst = $ct->media_type_minor ('x-message-rfc934');
671 $ct->delete ('charset');
672 } elsif (index ($s, 'Digest (RFC1153)') >= 0) {
673 $ct = $self->{header}->field ('content-type') unless ref $ct;
674 $mt = $ct->media_type_major ('text');
675 $mst = $ct->media_type_minor ('x-message-rfc1153');
676 $ct->delete ('charset');
677 }
678 }
679 }
680 }
681 if (wantarray) {
682 ($mt, $mst);
683 } else {
684 $ct->media_type;
685 }
686 }
687 *media_type = \&content_type;
688
689 =item $self->id
690
691 Returns ID of message entity. If there are C<Message-ID:>
692 field, its value is returned. Unless, but there are
693 C<Content-ID:> field, it is returned. Without both of
694 fields, C<""> is returned.
695
696 =cut
697
698 sub id ($) {
699 my $self = shift;
700 return scalar $self->{header}->field ('message-id')->id
701 if $self->{header}->field_exist ('message-id');
702 return scalar $self->{header}->field ('content-id')->id
703 if $self->{header}->field_exist ('content-id');
704 '';
705 }
706
707 =back
708
709 =head1 MISC. METHODS
710
711 =over 4
712
713 =item $self->option ( $option-name / $option-name, $option-value, ...)
714
715 If @_ == 1, returns option value. Else...
716
717 Set option value. You can pass multiple option name-value pair
718 as parameter. Example:
719
720 $msg->option (format => 'mail-rfc822',
721 capitalize => 0);
722 print $msg->option ('format'); ## mail-rfc822
723
724 =cut
725
726 sub option ($@) {
727 my $self = shift;
728 if (@_ == 1) {
729 return $self->{option}->{ $_[0] };
730 }
731 my %option = @_;
732 while (my ($name, $value) = splice (@_, 0, 2)) {
733 $self->{option}->{$name} = $value;
734 }
735 if ($option{-recursive} && ($self->content_type)[0] ne 'message') {
736 $self->{header}->option (%option);
737 $self->{body}->option (%option) if ref $self->{body};
738 }
739 }
740
741 =item $self->clone ()
742
743 Returns a copy of Message::Entity object.
744
745 =cut
746
747 sub clone ($) {
748 my $self = shift;
749 my $clone = new Message::Entity;
750 $clone->{option} = Message::Util::make_clone ($self->{option});
751 for (@{$self->{option}->{_MEMBERS}}) {
752 $clone->{$_} = Message::Util::make_clone ($self->{$_});
753 }
754 $clone;
755 }
756
757 my %_method_default_list = qw(new 1 parse 1 stringify 1 option 1 clone 1 method_available 1);
758 sub method_available ($$) {
759 my $self = shift;
760 my $name = shift;
761 return 1 if $_method_default_list{$name};
762 for (@{$self->{option}->{_METHODS}}) {
763 return 1 if $_ eq $name;
764 }
765 0;
766 }
767
768 sub import ($;%) {
769 my $self = shift;
770 my %option = @_;
771 for (keys %option) {
772 $DEFAULT{$_} = $option{$_};
773 }
774 if ($option{-body_default_charset} && !$option{-body_default_charset_input}) {
775 $DEFAULT{-body_default_charset_input} = $option{-body_default_charset};
776 }
777 if ($option{-header_default_charset} && !$option{-header_default_charset_input}) {
778 $DEFAULT{-header_default_charset_input} = $option{-header_default_charset};
779 }
780 }
781
782 =back
783
784 =head1 C<format>
785
786 =over 2
787
788 =item mail-rfc650
789
790 Internet mail message, defined by IETF RFC 650
791
792 =item mail-rfc724
793
794 Internet mail message, defined by IETF RFC 724
795
796 =item mail-rfc733
797
798 Internet mail message, defined by IETF RFC 733
799
800 =item mail-rfc822
801
802 Internet mail message, defined by IETF RFC 822
803
804 =item mail-rfc2822
805
806 Internet mail message, defined by IETF RFC 2822
807
808 =item mime-1.0
809
810 MIME entity
811
812 =item mime-1.0-rfc1341
813
814 MIME entity, defined by RFC 1341 (and RFC 1342)
815
816 =item mime-1.0-rfc1521
817
818 MIME entity, defined by RFC 1521 and 1522
819
820 =item mime-1.0-rfc2045
821
822 MIME entity, defined by RFC 2045,..., 2049
823
824 =item news-bnews
825
826 Usenet Bnews format
827
828 =item news-rfc850
829
830 Usenet news format, defined by IETF RFC 850
831
832 =item news-rfc1036
833
834 Usenet news format, defined by IETF RFC 1036
835
836 =item news-son-of-rfc1036
837
838 Usenet news format, defined by son-of-RFC1036
839
840 =item news-usefor
841
842 Usenet news format, defined by usefor-article (IETF Internet Draft)
843
844 =item http-1.0-rfc1945
845
846 HTTP/1.0 message, defined by IETF RFC 1945
847
848 =item http-1.0-rfc1945-request
849
850 HTTP/1.0 request message, defined by IETF RFC 1945
851
852 =item http-1.0-rfc1945-response
853
854 HTTP/1.0 response message, defined by IETF RFC 1945
855
856 =item http-1.1-rfc2068
857
858 HTTP/1.1 message, defined by IETF RFC 2068
859
860 =item http-1.1-rfc2068-request
861
862 HTTP/1.1 request message, defined by IETF RFC 2068
863
864 =item http-1.1-rfc2068-response
865
866 HTTP/1.1 response message, defined by IETF RFC 2068
867
868 =item http-1.1-rfc2616
869
870 HTTP/1.1 message, defined by IETF RFC 2616
871
872 =item http-1.1-rfc2616-request
873
874 HTTP/1.1 request message, defined by IETF RFC 2616
875
876 =item http-1.1-rfc2616-response
877
878 HTTP/1.1 response message, defined by IETF RFC 2616
879
880 =item http-cgi-1.1
881
882 CGI/1.1 output (for HTTP), defined by coar-cgi-v11 (IETF Internet Draft)
883
884 =item http-1.0-cgi-1.1
885
886 CGI/1.1 output (for HTTP/1.0), defined by coar-cgi-v11 (IETF Internet Draft)
887
888 =item http-1.1-cgi-1.1
889
890 CGI/1.1 output (for HTTP/1.1), defined by coar-cgi-v11 (IETF Internet Draft)
891
892 =item http-cgi-1.2
893
894 CGI/1.2 output, defined by coar-cgi-v12 (to be IETF Internet Draft)
895
896 =item http-1.0-cgi-1.2
897
898 CGI/1.2 output (for HTTP/1.0), defined by coar-cgi-v11 (IETF Internet Draft)
899
900 =item http-1.1-cgi-1.2
901
902 CGI/1.2 output (for HTTP/1.1), defined by coar-cgi-v11 (IETF Internet Draft)
903
904 =item http-sip-2.0
905
906 SIP/2.0 message, defined by IETF RFC 2543
907
908 =item http-sip-2.0-request
909
910 SIP/2.0 request message, defined by IETF RFC 2543
911
912 =item http-sip-2.0-response
913
914 SIP/2.0 response message, defined by IETF RFC 2543
915
916 =item http-sip-cgi
917
918 SIP/2.0 CGI (IETF Internet Draft)
919
920 =item cpim-1.0
921
922 CPIM/1.0 (IETF Internet Draft)
923
924 =item uri-url-mailto-mail-rfc822, uri-url-mailto-mail-rfc2822
925
926 mailto: URL scheme
927
928 =item uri-url-mailto-rfc1738
929
930 mailto: URL scheme (defined by RFC 1738)
931
932 =item uri-url-mailto-rfc2368, uri-url-mailto-rfc2822
933
934 mailto: URL scheme (defined by RFC 2368)
935
936 =item uri-url-mailto-to-mail-rfc822, uri-url-mailto-to-mail-rfc2822
937
938 C<to> part of mailto: URL scheme (for internal use only)
939
940 =back
941
942 =head1 EXAMPLE
943
944 use Message::Entity;
945 my $msg = new Message::Entity From => '[email protected]',
946 Subject => 'Example message',
947 To => '[email protected]',
948 -format => 'mail-rfc2822',
949 body => $body;
950 $msg->header ($header);
951 $msg->body ($body);
952 print $msg;
953
954 =head1 SEE ALSO
955
956 Message::* Perl modules
957 <http://suika.fam.cx/~wakaba/Message-pm/>
958
959 =head1 LICENSE
960
961 Copyright 2002 wakaba E<lt>[email protected]<gt>.
962
963 This program is free software; you can redistribute it and/or modify
964 it under the terms of the GNU General Public License as published by
965 the Free Software Foundation; either version 2 of the License, or
966 (at your option) any later version.
967
968 This program is distributed in the hope that it will be useful,
969 but WITHOUT ANY WARRANTY; without even the implied warranty of
970 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
971 GNU General Public License for more details.
972
973 You should have received a copy of the GNU General Public License
974 along with this program; see the file COPYING. If not, write to
975 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
976 Boston, MA 02111-1307, USA.
977
978 =head1 CHANGE
979
980 See F<ChangeLog>.
981 $Date: 2002/07/22 07:48:28 $
982
983 =cut
984
985 1;

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24