/[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.18 - (hide annotations) (download)
Sun Jun 9 11:20:24 2002 UTC (24 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.17: +104 -50 lines
2002-06-09  wakaba <w@suika.fam.cx>

	* Entity.pm:
	- (stringify): Minimumize MIME charset name when MIME'izing.
	- (mime-entity): New 'format' type.
	- (default_media_subtype): New option.  Now 'default_media_type'
	is used for only (narrower meaning of) media type.
	- (content_type): See 'default_media_type' and
	'default_media_subtype'.  (Was hardcoded as 'text/plain'.)
	- (option): '-resucrive': new option.
	* Header.pm (parse, parse_array): Don't discard invalid
	line (non-'field' line).  (Treat as "X-Unknown" field.)

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24