/[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.33 - (hide annotations) (download)
Sat Jul 27 00:39:54 2002 UTC (24 years ago) by wakaba
Branch: MAIN
Changes since 1.32: +129 -74 lines
2002-07-26  Wakaba <w@suika.fam.cx>

	* Entity.pm:
	- (fill_missing_fields): New option.
	- (fill_source, fill_destination): New options.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24