/[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 - (hide annotations) (download)
Fri Jul 26 12:42:00 2002 UTC (23 years, 11 months 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 wakaba 1.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 wakaba 1.15 use vars qw(%DEFAULT $VERSION);
16 wakaba 1.32 $VERSION=do{my @r=(q$Revision: 1.31 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
17 wakaba 1.1
18 wakaba 1.15 require Message::Util;
19 wakaba 1.8 require Message::Header;
20 wakaba 1.15 require Message::MIME::MediaType;
21     require Message::MIME::Encoding;
22 wakaba 1.9 use overload '""' => sub { $_[0]->stringify },
23     fallback => 1;
24 wakaba 1.1
25 wakaba 1.14 ## Initialize of this class -- called by constructors
26     %DEFAULT = (
27 wakaba 1.15 -_METHODS => [qw|header body content_type id|],
28 wakaba 1.17 -_MEMBERS => [qw|header body _cte|],
29 wakaba 1.18 ## entity_header -- Don't clone.
30 wakaba 1.15 -accept_coderange => '7bit', ## 7bit / 8bit / binary
31 wakaba 1.18 #add_ua => 1,
32 wakaba 1.15 -body_default_charset => 'iso-2022-int-1',
33 wakaba 1.18 -body_default_charset_input => 'iso-2022-int-1',
34     -body_default_media_type => 'text',
35     -body_default_media_subtype => 'plain',
36 wakaba 1.17 -cte_default => '7bit',
37 wakaba 1.8 #fill_date => 1,
38 wakaba 1.15 -fill_date_name => 'date',
39 wakaba 1.24 -fill_md5 => 0,
40     -fill_md5_name => 'md5',
41 wakaba 1.8 #fill_msgid => 1,
42 wakaba 1.15 -fill_msgid_name => 'message-id',
43 wakaba 1.19 -force_mime_entity => 0,
44 wakaba 1.15 -format => 'mail-rfc2822',
45 wakaba 1.28 -guess_media_type => 1,
46 wakaba 1.30 #internal_charset_name
47 wakaba 1.22 -header_default_charset => 'iso-2022-int-1',
48     -header_default_charset_input => 'iso-2022-int-1',
49 wakaba 1.26 -hook_init_fill_options => sub {},
50     -hook_stringify_fill_fields => sub {},
51 wakaba 1.15 -linebreak_strict => 0, ## BUG: not work perfectly
52     -parse_all => 0,
53 wakaba 1.24 -recalc_md5 => 1,
54 wakaba 1.17 -text_coderange => 'binary',
55     ## '8bit' (MIME text/*) / 'binary' (HTTP text/*)
56 wakaba 1.8 #ua_field_name => 'user-agent',
57 wakaba 1.21 -ua_use_Config => 1,
58     -ua_use_Win32 => 1,
59 wakaba 1.15 -uri_mailto_safe_level => 4,
60 wakaba 1.14 );
61     sub _init ($;%) {
62     my $self = shift;
63     my %options = @_;
64 wakaba 1.15 $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 wakaba 1.14
72 wakaba 1.8 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 wakaba 1.18 } elsif ($name eq 'entity_header') {
77     $self->{entity_header} = $options{entity_header};
78 wakaba 1.8 } else {
79 wakaba 1.18 push @new_fields, ($name => $options{$name});
80 wakaba 1.8 }
81     }
82 wakaba 1.18
83 wakaba 1.8 my $format = $self->{option}->{format};
84 wakaba 1.14 if ($format =~ /http/) {
85 wakaba 1.24 $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 wakaba 1.15 $self->{option}->{accept_coderange} = 'binary';
89 wakaba 1.17 $self->{option}->{text_coderange} = 'binary';
90     $self->{option}->{cte_default} = 'binary';
91 wakaba 1.14 } else {
92 wakaba 1.24 $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 wakaba 1.17 $self->{option}->{text_coderange} = '8bit';
96 wakaba 1.15 if ($format =~ /news-usefor|smtp-8bitmime/) {
97     $self->{option}->{accept_coderange} = '8bit';
98 wakaba 1.18 #$self->{option}->{cte_default} = '8bit';
99 wakaba 1.15 } else {
100     $self->{option}->{accept_coderange} = '7bit';
101     }
102 wakaba 1.14 }
103 wakaba 1.24 $self->{option}->{fill_msgid_ns} = $Message::Header::NS_phname2uri{'x-rfc822'};
104     $self->{option}->{fill_mimever_ns} = $Message::Header::NS_phname2uri{'x-rfc822'};
105 wakaba 1.8 unless (defined $self->{option}->{fill_date}) {
106 wakaba 1.18 $self->{option}->{fill_date} = $format !~ /mime-entity|cgi|uri-url-mailto/;
107 wakaba 1.8 }
108     unless (defined $self->{option}->{fill_msgid}) {
109 wakaba 1.18 $self->{option}->{fill_msgid} = $format !~ /mime-entity|http|uri-url-mailto/;
110 wakaba 1.8 }
111 wakaba 1.15 unless (defined $self->{option}->{fill_ct}) {
112     $self->{option}->{fill_ct} = $format !~ /http/;
113     }
114 wakaba 1.8 unless (defined $self->{option}->{fill_mimever}) {
115 wakaba 1.18 $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 wakaba 1.8 }
120 wakaba 1.22 unless ($self->{option}->{fill_ua_name}) {
121 wakaba 1.14 $self->{option}->{fill_ua_name} = $format =~ /response|cgi|uri-url-mailto/?
122 wakaba 1.8 'server': 'user-agent';
123     }
124 wakaba 1.26 &{ $self->{option}->{hook_init_fill_options} } ($self, $self->{option});
125 wakaba 1.8 @new_fields;
126     }
127    
128     =head1 CONSTRUCTORS
129    
130     The following methods construct new C<Message::Entity> objects:
131 wakaba 1.3
132 wakaba 1.8 =over 4
133    
134 wakaba 1.9 =item Message::Entity->new ([%initial-fields/options])
135 wakaba 1.1
136 wakaba 1.9 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 wakaba 1.1
148     =cut
149    
150     sub new ($;%) {
151     my $class = shift;
152 wakaba 1.8 my $self = bless {}, $class;
153     my %new_field = $self->_init (@_);
154 wakaba 1.22 if (defined $new_field{body}) {
155 wakaba 1.8 $self->{body} = $new_field{body}; $new_field{body} = undef;
156 wakaba 1.18 $self->{body} = $self->_parse_value ([$self->content_type] => $self->{body})
157 wakaba 1.8 if $self->{option}->{parse_all};
158     }
159 wakaba 1.14 $self->{header} = new Message::Header
160     -format => $self->{option}->{format},
161 wakaba 1.22 -header_default_charset => $self->{option}->{header_default_charset},
162     -header_default_charset_input => $self->{option}->{header_default_charset_input},
163 wakaba 1.8 -parse_all => $self->{option}->{parse_all}, %new_field;
164 wakaba 1.1 $self;
165     }
166    
167 wakaba 1.9 =item Message::Entity->parse ($message, [%options])
168 wakaba 1.1
169 wakaba 1.9 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 wakaba 1.8
173 wakaba 1.1 =cut
174    
175     sub parse ($$;%) {
176     my $class = shift;
177     my $message = shift;
178 wakaba 1.8 my $self = bless {}, $class;
179     my %new_field = $self->_init (@_);
180 wakaba 1.20 my $nl = "\x0D\x0A";
181 wakaba 1.25 unless ($self->{option}->{linebreak_strict}) {
182 wakaba 1.23 $nl = Message::Util::decide_newline ($message);
183 wakaba 1.20 }
184 wakaba 1.24 ## BUG: binary unsafe yet!
185 wakaba 1.20 my @header = ();
186     my @body = split /$nl/, $message;
187 wakaba 1.8 while (1) {
188     my $line = shift @body;
189     unless (length($line)) {
190     last;
191 wakaba 1.1 } else {
192 wakaba 1.8 push @header, $line;
193 wakaba 1.1 }
194     }
195 wakaba 1.8 $new_field{body} = undef if $new_field{body};
196     $self->{header} = parse_array Message::Header \@header,
197 wakaba 1.22 -header_default_charset => $self->{option}->{header_default_charset},
198     -header_default_charset_input => $self->{option}->{header_default_charset_input},
199 wakaba 1.8 -parse_all => $self->{option}->{parse_all},
200     -format => $self->{option}->{format}, %new_field;
201 wakaba 1.24 $self->{body} = join ($nl, @body) . $nl;
202 wakaba 1.18 $self->{body} = $self->_parse_value ([$self->content_type] => $self->{body})
203 wakaba 1.8 if $self->{option}->{parse_all};
204 wakaba 1.1 $self;
205     }
206    
207 wakaba 1.9 =back
208    
209 wakaba 1.8 =head1 METHODS
210    
211 wakaba 1.1 =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 wakaba 1.9 ## TODO: to be compatible with HTTP::Message
221 wakaba 1.1 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 wakaba 1.4 $self->{header} = Message::Header->parse ($new_header,
228 wakaba 1.22 -header_default_charset => $self->{option}->{header_default_charset},
229     -header_default_charset_input => $self->{option}->{header_default_charset_input},
230 wakaba 1.8 -parse_all => $self->{option}->{parse_all},
231     -format => $self->{option}->{format});
232 wakaba 1.1 }
233 wakaba 1.14 unless (ref $self->{header} || length $self->{header}) {
234     $self->{header} = new Message::Header (
235 wakaba 1.22 -header_default_charset => $self->{option}->{header_default_charset},
236     -header_default_charset_input => $self->{option}->{header_default_charset_input},
237 wakaba 1.14 -parse_all => $self->{option}->{parse_all},
238     -format => $self->{option}->{format});
239 wakaba 1.2 }
240 wakaba 1.1 $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 wakaba 1.18 $self->{body} = $self->_parse_value ([$self->content_type] => $self->{body})
257 wakaba 1.3 unless ref $self->{body};
258 wakaba 1.1 $self->{body};
259     }
260    
261 wakaba 1.18 ## [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 wakaba 1.24 ## 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 wakaba 1.14 ## $self->_parse_value ($type, $value);
292     sub _parse_value ($$$) {
293 wakaba 1.3 my $self = shift;
294 wakaba 1.18 my ($mt,$mst) = @{ shift(@_) };
295 wakaba 1.14 my $value = shift;
296     return $value if ref $value;
297    
298     ## decode
299     $value = $self->_decode_body ($value);
300    
301 wakaba 1.18 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 wakaba 1.25 -linebreak_strict => $self->{option}->{linebreak_strict},
316 wakaba 1.18 -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 wakaba 1.30 -internal_charset_name => $self->{option}->{internal_charset_name},
322 wakaba 1.18 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 wakaba 1.14 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 wakaba 1.18 return $vtype->parse ($value, %vopt);
342 wakaba 1.3 } else {
343 wakaba 1.14 eval "require $vtype" or Carp::croak qq{<parse>: $vtype: Can't load package: $@};
344 wakaba 1.18 return $vtype->new (%vopt);
345 wakaba 1.3 }
346     }
347    
348 wakaba 1.14 sub _decode_body ($$) {
349     my $self = shift;
350     my $value = shift;
351     ## MIME CTE
352 wakaba 1.22 my $cte = $self->{_cte} || '';
353 wakaba 1.14 my $ctef = $self->header->field ('content-transfer-encoding',
354     -new_item_unless_exist => 0);
355     $cte = $ctef->value if ref $ctef;
356 wakaba 1.15 my $f = $Message::MIME::Encoding::DECODER{$cte};
357 wakaba 1.14 if (ref $f) {
358     ($value, $cte) = &$f ($self, $value);
359     }
360     $self->{_cte} = $cte;
361     $value;
362     }
363    
364 wakaba 1.15 sub _encode_body ($$\%) {
365 wakaba 1.14 my $self = shift;
366     my $value = shift;
367 wakaba 1.15 my $option = shift;
368 wakaba 1.14 ## MIME CTE
369 wakaba 1.17 my $current_cte = $self->{_cte} || 'binary';
370     my $ctef = $self->{header}->field ('content-transfer-encoding',
371 wakaba 1.14 -new_item_unless_exist => 0);
372 wakaba 1.15 my $cte = ''; $cte = lc $ctef->value if ref $ctef;
373 wakaba 1.16 my %enoption;
374 wakaba 1.15 ## 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 wakaba 1.18 unless ref $mt_def;
379 wakaba 1.15 $mt_def = $Message::MIME::MediaType::type{'/default'}->{'/default'}
380     unless ref $mt_def;
381 wakaba 1.16 $enoption{mt_is_text} = 1
382     if $mt eq 'text' || $mt eq 'multipart' || $mt eq 'message';
383 wakaba 1.23 $enoption{mt_is_text} = 1 if $mt_def->{text_content};
384 wakaba 1.16 my ($charset, $charset_def) = '';
385     if ($mt_def->{mime_charset}) {
386     ## If CT is able to have its charset parameter,
387 wakaba 1.17 my $ct = $self->{header}->field ('content-type',
388 wakaba 1.16 -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 wakaba 1.31 ## Note: 'encoding_after_encode' option's value is hardcoded.
395 wakaba 1.16 }
396 wakaba 1.18 } else { ## Don't have mime style "charset" parameter
397     $charset_def = {mime_text => 1};
398 wakaba 1.16 }
399     $charset_def = {} unless ref $charset_def; ## dummy
400 wakaba 1.31 #if ($charset_def->{mime_text} != 1) { ## See also Note above
401     if (Message::MIME::Charset::is_mime_text ($charset || '*default') != 1) {
402 wakaba 1.18 $enoption{mt_is_text} = 0 if $mt eq 'text';
403 wakaba 1.17 my $ct = $self->{header}->field ('content-type');
404     $ct->not_mime_text ($option->{text_coderange} eq 'binary'? 0:1);
405     }
406 wakaba 1.15 ## If accept CTE list is defined,
407 wakaba 1.16 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 wakaba 1.15 }
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 wakaba 1.16 my $cr = $self->Message::MIME::Encoding::decide_coderange
425     ($decoded, \%enoption);
426 wakaba 1.15 if ($option->{accept_coderange} eq '8bit') {
427     if ($cr eq 'binary') {
428 wakaba 1.16 $cte = $charset_def->{cte_7bit_preferred}
429     || $mt_def->{cte_7bit_preferred} || 'base64';
430 wakaba 1.15 $en = $Message::MIME::Encoding::ENCODER{$cte};
431     }
432     } elsif ($option->{accept_coderange} eq '7bit') {
433     if ($cr eq 'binary' || $cr eq '8bit') {
434 wakaba 1.16 $cte = $charset_def->{cte_7bit_preferred}
435     || $mt_def->{cte_7bit_preferred} || 'base64';
436 wakaba 1.15 $en = $Message::MIME::Encoding::ENCODER{$cte};
437 wakaba 1.18 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 wakaba 1.15 }
442     }
443     if ($e eq 'binary') {
444 wakaba 1.16 ($value, $e) = &$en ($self, $decoded, \%enoption);
445 wakaba 1.17 $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 wakaba 1.15 unless ref $ctef;
453     $ctef->value ($e);
454 wakaba 1.17 } elsif (ref $ctef) {
455     $ctef->value ('');
456     }
457 wakaba 1.15 } else {
458 wakaba 1.17 $ctef = $self->{header}->field ('content-transfer-encoding')
459 wakaba 1.15 unless ref $ctef;
460     $ctef->value ($current_cte);
461     }
462 wakaba 1.14 } else { ## Can't encode by given CTE
463 wakaba 1.17 $ctef = $self->{header}->field ('content-transfer-encoding')
464 wakaba 1.15 unless ref $ctef;
465 wakaba 1.14 $ctef->value ($current_cte);
466     }
467     }
468 wakaba 1.17 if (ref $ctef && $ctef->value eq '') {
469     $self->{header}->delete ('content-transfer-encoding');
470     }
471 wakaba 1.14 $value;
472     }
473    
474 wakaba 1.1 =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 wakaba 1.8 my %params = @_;
483     my %option = %{$self->{option}};
484     for (grep {/^-/} keys %params) {$option{substr ($_, 1)} = $params{$_}}
485 wakaba 1.18 my ($header, $body, $body0);
486 wakaba 1.14 if (ref $self->{body}) {
487 wakaba 1.18 $self->{body}->entity_header ($self->{header});
488 wakaba 1.27 $body0 = $self->{body}->stringify (-parent_format => $option{format},
489 wakaba 1.14 -linebreak_strict => $option{linebreak_strict});
490     } else {
491 wakaba 1.18 $body0 = $self->{body};
492 wakaba 1.14 }
493 wakaba 1.18 $body = $self->_encode_body ($body0, \%option);
494 wakaba 1.14 if (ref $self->{header}) {
495 wakaba 1.15 my %exist;
496     for ($self->{header}->field_name_list) {$exist{$_} = 1}
497 wakaba 1.26 &{ $option{hook_stringify_fill_fields} } ($self, \%exist, \%option);
498 wakaba 1.15 my $ns_content = $Message::Header::NS_phname2uri{content};
499 wakaba 1.14 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 wakaba 1.8 if $from;
513 wakaba 1.15 } # fill_msgid
514 wakaba 1.24 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 wakaba 1.15 my $ismime = 0;
528     for (keys %exist) {if (/:$ns_content$/) { $ismime = 1; last }}
529 wakaba 1.18 unless ($ismime) {
530 wakaba 1.19 $ismime = 1 if $option{force_mime_entity};
531 wakaba 1.24 $ismime = 1 if $option{fill_md5};
532 wakaba 1.18 $ismime = 1 if $option{body_default_media_type} ne 'text';
533     $ismime = 1 if $option{body_default_media_subtype} ne 'plain';
534     }
535 wakaba 1.15 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 wakaba 1.18 $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 wakaba 1.15 }
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 wakaba 1.14 $self->{header}->add ('mime-version' => '1.0',
547     -parse => 0, -ns => $option{fill_mimever_ns});
548 wakaba 1.8 }
549 wakaba 1.15 } # $ismime
550 wakaba 1.14 if ($option{format} =~ /uri-url-mailto/ && $exist{'type:$ns_content'}
551 wakaba 1.12 && $option{uri_mailto_safe_level} > 1) {
552     $self->{header}->field ('content-type')->media_type ('text/plain');
553     }
554 wakaba 1.21 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 wakaba 1.32 -date => q$Date: 2002/07/22 07:48:28 $,
559 wakaba 1.21 );
560     }
561 wakaba 1.12 $header = $self->{header}->stringify (-format => $option{format},
562     -linebreak_strict => $option{linebreak_strict},
563     -uri_mailto_safe_level => $option{uri_mailto_safe_level});
564 wakaba 1.7 } else {
565     $header = $self->{header};
566 wakaba 1.12 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 wakaba 1.7 }
572 wakaba 1.12 if ($option{format} =~ /uri-url-mailto/) {
573 wakaba 1.14 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 wakaba 1.12 }
589     } else {
590 wakaba 1.15 $header .= "\x0D\x0A" if $header && $header !~ /\x0D\x0A$/;
591     $header."\x0D\x0A".$body;
592 wakaba 1.12 }
593 wakaba 1.1 }
594 wakaba 1.8 *as_string = \&stringify;
595 wakaba 1.1
596    
597 wakaba 1.9 =head1 SHORTCUT METHOD FOR MESSAGE PROPERTIES
598    
599     =over 4
600    
601     =item $self->content_type ([%options])
602 wakaba 1.1
603 wakaba 1.9 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 wakaba 1.1
609 wakaba 1.9 Default is C<text/plain>.
610 wakaba 1.3
611 wakaba 1.9 Example:
612 wakaba 1.3
613 wakaba 1.9 $msg->field ('Content-Type')->media_type ('text/html');
614     print $msg->content_type; ## text/html
615 wakaba 1.3
616     =cut
617    
618     sub content_type ($;%) {
619 wakaba 1.8 my $self = shift;
620 wakaba 1.15 my $ct = $self->{header}->field ('content-type', -new_item_unless_exist => 0);
621 wakaba 1.28 my ($mt, $mst);
622 wakaba 1.15 unless (ref $ct) {
623 wakaba 1.28 $mt = $self->{option}->{body_default_media_type};
624     $mst = $self->{option}->{body_default_media_subtype};
625     if ($mt ne 'text' || $mst ne 'plain') {
626 wakaba 1.18 $ct = $self->{header}->field ('content-type');
627 wakaba 1.28 $ct->media_type_major ($mt);
628     $ct->media_type_minor ($mst);
629     }
630 wakaba 1.29 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 wakaba 1.28 }
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 wakaba 1.29 $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 wakaba 1.28 $ct->delete ('charset');
677     }
678     }
679 wakaba 1.18 }
680 wakaba 1.15 }
681     if (wantarray) {
682 wakaba 1.28 ($mt, $mst);
683 wakaba 1.15 } else {
684     $ct->media_type;
685     }
686 wakaba 1.1 }
687 wakaba 1.15 *media_type = \&content_type;
688 wakaba 1.1
689 wakaba 1.9 =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 wakaba 1.5 sub id ($) {
699     my $self = shift;
700     return scalar $self->{header}->field ('message-id')->id
701     if $self->{header}->field_exist ('message-id');
702 wakaba 1.9 return scalar $self->{header}->field ('content-id')->id
703     if $self->{header}->field_exist ('content-id');
704 wakaba 1.8 '';
705 wakaba 1.5 }
706    
707 wakaba 1.9 =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 wakaba 1.18 my %option = @_;
732 wakaba 1.9 while (my ($name, $value) = splice (@_, 0, 2)) {
733     $self->{option}->{$name} = $value;
734     }
735 wakaba 1.32 if ($option{-recursive} && ($self->content_type)[0] ne 'message') {
736 wakaba 1.18 $self->{header}->option (%option);
737     $self->{body}->option (%option) if ref $self->{body};
738     }
739 wakaba 1.9 }
740    
741     =item $self->clone ()
742 wakaba 1.8
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 wakaba 1.14 $clone->{option} = Message::Util::make_clone ($self->{option});
751     for (@{$self->{option}->{_MEMBERS}}) {
752     $clone->{$_} = Message::Util::make_clone ($self->{$_});
753 wakaba 1.8 }
754     $clone;
755     }
756 wakaba 1.5
757 wakaba 1.14 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 wakaba 1.30 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 wakaba 1.9 =back
783    
784 wakaba 1.11 =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 wakaba 1.12 =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 wakaba 1.11 =back
941    
942 wakaba 1.1 =head1 EXAMPLE
943    
944     use Message::Entity;
945 wakaba 1.9 my $msg = new Message::Entity From => '[email protected]',
946     Subject => 'Example message',
947     To => '[email protected]',
948     -format => 'mail-rfc2822',
949     body => $body;
950 wakaba 1.1 $msg->header ($header);
951     $msg->body ($body);
952     print $msg;
953    
954 wakaba 1.3 =head1 SEE ALSO
955    
956     Message::* Perl modules
957     <http://suika.fam.cx/~wakaba/Message-pm/>
958    
959 wakaba 1.1 =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 wakaba 1.32 $Date: 2002/07/22 07:48:28 $
982 wakaba 1.1
983     =cut
984    
985     1;

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24