/[suikacvs]/test/cvs
Suika

Contents of /test/cvs

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.19 - (show annotations) (download)
Wed May 15 07:31:28 2002 UTC (21 years, 11 months ago) by wakaba
Branch: MAIN
Changes since 1.18: +6 -9 lines
2002-05-15  wakaba <w@suika.fam.cx>

	* Header.pm:
	- Add Resent-User-Agent: field support.
	- Use Message::Field::Addresses instead of 
	Message::Field::Address.

1
2 =head1 NAME
3
4 Message::Header Perl module
5
6 =head1 DESCRIPTION
7
8 Perl module for RFC 822/2822 message C<header>.
9
10 =cut
11
12 package Message::Header;
13 use strict;
14 use vars qw($VERSION %REG);
15 $VERSION = '1.00';
16 use Carp ();
17 use overload '@{}' => sub { shift->_delete_empty_field->{field} },
18 '""' => sub { shift->stringify },
19 fallback => 1;
20
21 $REG{WSP} = qr/[\x09\x20]/;
22 $REG{FWS} = qr/[\x09\x20]*/;
23 $REG{M_field} = qr/^([^\x3A]+):$REG{FWS}([\x00-\xFF]*)$/;
24 $REG{M_fromline} = qr/^\x3E?From$REG{WSP}+([\x00-\xFF]*)$/;
25 $REG{UNSAFE_field_name} = qr/[\x00-\x20\x3A\x7F-\xFF]/;
26
27 =head2 options
28
29 These options can be getten/set by C<get_option>/C<set_option>
30 method.
31
32 =head3 capitalize = 0/1
33
34 (First character of) C<field-name> is capitalized
35 when C<stringify>. (Default = 1)
36
37 =head3 fold_length = numeric value
38
39 Length of line used to fold. (Default = 70)
40
41 =head3 mail_from = 0/1
42
43 Outputs "From " line (known as Un*x From, Mail-From, and so on)
44 when C<stringify>. (Default = 0)
45
46 =cut
47
48 =head1 CONSTRUCTORS
49
50 The following methods construct new C<Message::Header> objects:
51
52 =over 4
53
54 =cut
55
56 ## Initialize
57 my %DEFAULT = (
58 capitalize => 1,
59 fold => 1,
60 fold_length => 70,
61 field_format_pattern => '%s: %s',
62 #field_type => {},
63 format => 'mail-rfc2822',
64 linebreak_strict => 0,
65 mail_from => 0,
66 output_bcc => 0,
67 parse_all => 0,
68 sort => 'none',
69 translate_underscore => 1,
70 uri_mailto_safe => {
71 ## 1 all (no check) 2 no trace & bcc & from
72 ## 3 no sender's info 4 (default) (currently not used)
73 ## 5 only a few
74 ':default' => 4,
75 'cc' => 4,
76 'bcc' => 1,
77 'body' => 1,
78 'comment' => 5,
79 'content-id' => 1,
80 'date' => 1,
81 'from' => 1,
82 'keywords' => 5,
83 'list-id' => 1,
84 'mail-from' => 1,
85 'message-id' => 1,
86 'received' => 1,
87 'resent-bcc' => 1,
88 'resent-date' => 1,
89 'resent-from' => 1,
90 'resent-sender' => 1,
91 'return-path' => 1,
92 'sender' => 1,
93 'subject' => 5,
94 'summary' => 5,
95 'to' => 4,
96 'user-agent' => 3,
97 'x-face' => 2,
98 'x-mailer' => 3,
99 'x-nsubject' => 5,
100 'x-received' => 1,
101 'x400-received' => 1,
102 },
103 uri_mailto_safe_level => 4,
104 validate => 1,
105 );
106 $DEFAULT{field_type} = {
107 ':DEFAULT' => 'Message::Field::Unstructured',
108
109 received => 'Message::Field::Received',
110 'x-received' => 'Message::Field::Received',
111
112 'content-type' => 'Message::Field::ContentType',
113 p3p => 'Message::Field::Params',
114 'auto-submitted' => 'Message::Field::ValueParams',
115 'content-disposition' => 'Message::Field::ValueParams',
116 link => 'Message::Field::ValueParams',
117 archive => 'Message::Field::ValueParams',
118 'x-face-type' => 'Message::Field::ValueParams',
119 'x-mozilla-draft-info' => 'Message::Field::ValueParams',
120
121 subject => 'Message::Field::Subject',
122 'x-nsubject' => 'Message::Field::Subject',
123
124 'list-software' => 'Message::Field::UA',
125 'user-agent' => 'Message::Field::UA',
126 'resent-user-agent' => 'Message::Field::UA',
127 server => 'Message::Field::UA',
128
129 ## A message id
130 'content-id' => 'Message::Field::MsgID',
131 'message-id' => 'Message::Field::MsgID',
132 'resent-message-id' => 'Message::Field::MsgID',
133
134 ## Numeric value
135 'content-length' => 'Message::Field::Numval',
136 lines => 'Message::Field::Numval',
137 'max-forwards' => 'Message::Field::Numval',
138 'mime-version' => 'Message::Field::Numval',
139 'x-jsmail-priority' => 'Message::Field::Numval',
140 'x-mail-count' => 'Message::Field::Numval',
141 'x-ml-count' => 'Message::Field::Numval',
142 'x-priority' => 'Message::Field::Numval',
143
144 path => 'Message::Field::Path',
145 };
146 for (qw(archive cancel-lock content-features content-md5
147 disposition-notification-options encoding
148 importance injector-info
149 pics-label posted-and-mailed precedence list-id message-type
150 original-recipient priority x-list-id
151 sensitivity status x-face x-msmail-priority xref))
152 {$DEFAULT{field_type}->{$_} = 'Message::Field::Structured'}
153 ## Not supported yet, but to be supported...
154 # x-list: unstructured, ml name
155 for (qw(abuse-reports-to apparently-to approved approved-by bcc cc complaints-to
156 delivered-to disposition-notification-to envelope-to
157 errors-to from mail-copies-to mail-followup-to mail-reply-to
158 notice-requested-upon-delivery-to read-receipt-to register-mail-reply-requested-by
159 reply-to resent-bcc
160 resent-cc resent-to resent-from resent-sender return-path
161 return-receipt-to return-receipt-requested-to sender to x-abuse-reports-to
162 x-admin x-approved x-beenthere x-confirm-reading-to
163 x-complaints-to x-envelope-from x-envelope-sender
164 x-envelope-to x-ml-address x-ml-command x-ml-to x-nfrom x-nto
165 x-rcpt-to x-sender x-x-sender))
166 {$DEFAULT{field_type}->{$_} = 'Message::Field::Addresses'}
167 for (qw(client-date date date-received delivery-date expires
168 expire-date nntp-posting-date posted posted-date received-date
169 reply-by resent-date
170 x-originalarrivaltime x-tcup-date))
171 {$DEFAULT{field_type}->{$_} = 'Message::Field::Date'}
172 for (qw(article-updates in-reply-to
173 obsoletes references replaces see-also supersedes))
174 {$DEFAULT{field_type}->{$_} = 'Message::Field::MsgIDs'}
175 for (qw(accept accept-charset accept-encoding accept-language
176 content-language
177 content-transfer-encoding encrypted followup-to keywords
178 list-archive list-digest list-help list-owner
179 list-post list-subscribe list-unsubscribe list-url uri newsgroups
180 posted-to))
181 {$DEFAULT{field_type}->{$_} = 'Message::Field::CSV'}
182 for (qw(x-brother x-boss x-classmate x-daughter x-dearfriend x-favoritesong
183 x-friend x-me
184 x-moe x-respect
185 x-sublimate x-son x-sister x-wife))
186 {$DEFAULT{field_type}->{$_} = 'Message::Field::CSV'} ## NOT M::F::XMOE!
187 for (qw(content-alias content-base content-location location referer
188 url x-home-page x-http_referer
189 x-info x-pgp-key x-ml-url x-uri x-url x-web))
190 {$DEFAULT{field_type}->{$_} = 'Message::Field::URI'}
191
192 my %header_goodcase = (
193 'article-i.d.' => 'Article-I.D.',
194 'content-id' => 'Content-ID',
195 'content-md5' => 'Content-MD5',
196 'content-sgml-entity' => 'Content-SGML-Entity',
197 etag => 'ETag',
198 fax => 'FAX',
199 'pics-label' => 'PICS-Label',
200 'list-url' => 'List-URL',
201 'list-id' => 'List-ID',
202 'message-id' => 'Message-ID',
203 'mime-version' => 'MIME-Version',
204 'nic' => 'NIC',
205 'nntp-posting-date' => 'NNTP-Posting-Date',
206 'nntp-posting-host' => 'NNTP-Posting-Host',
207 'resent-message-id' => 'Resent-Message-ID',
208 te => 'TE',
209 url => 'URL',
210 'www-authenticate' => 'WWW-Authenticate',
211 'x-dearfriend' => 'X-DearFriend',
212 'x-mime-autoconverted' => 'X-MIME-Autoconverted',
213 'x-nntp-posting-date' => 'X-NNTP-Posting-Date',
214 'x-nntp-posting-host' => 'X-NNTP-Posting-Host',
215 'x-uri' => 'X-URI',
216 'x-url' => 'X-URL',
217 );
218 $DEFAULT{capitalize} = sub {
219 my $self = shift;
220 my $name = shift;
221 if ($header_goodcase{$name}) {
222 return $header_goodcase{$name};
223 }
224 $name =~ s/(?:^|-)cgi-/uc $&/ge;
225 $name =~ s/(?:^|-)[a-z]/uc $&/ge;
226 $name;
227 };
228
229 ## taken from L<HTTP::Header>
230 # "Good Practice" order of HTTP message headers:
231 # - General-Headers
232 # - Request-Headers
233 # - Response-Headers
234 # - Entity-Headers
235 # (From draft-ietf-http-v11-spec-rev-01, Nov 21, 1997)
236 my @header_order = qw(
237 mail-from x-envelope-from relay-version path status
238
239 cache-control connection date pragma transfer-encoding upgrade trailer via
240
241 accept accept-charset accept-encoding accept-language
242 authorization expect from host
243 if-modified-since if-match if-none-match if-range if-unmodified-since
244 max-forwards proxy-authorization range referer te user-agent
245
246 accept-ranges age location proxy-authenticate retry-after server vary
247 warning www-authenticate
248
249 mime-version
250 allow content-base content-encoding content-language content-length
251 content-location content-md5 content-range content-type
252 etag expires last-modified content-style-type content-script-type
253 link
254
255 xref
256 );
257 my %header_order;
258
259 sub _init ($;%) {
260 my $self = shift;
261 my %options = @_;
262 $self->{field} = [];
263 $self->{option} = \%DEFAULT;
264 my @new_fields = ();
265 for my $name (keys %options) {
266 if (substr ($name, 0, 1) eq '-') {
267 $self->{option}->{substr ($name, 1)} = $options{$name};
268 } else {
269 push @new_fields, ($name => $options{$name});
270 }
271 }
272 $self->_init_by_format ($self->{option}->{format}, $self->{option});
273 # Make alternative representations of @header_order. This is used
274 # for sorting.
275 my $i = 1;
276 for (@header_order) {
277 $header_order{$_} = $i++ unless $header_order{$_};
278 }
279
280 $self->add (@new_fields, -parse => $self->{option}->{parse_all})
281 if $#new_fields > -1;
282 }
283
284 sub _init_by_format ($$\%) {
285 my $self = shift;
286 my ($format, $option) = @_;
287 if ($format =~ /rfc822/) {
288 $header_goodcase{bcc} = 'bcc';
289 $header_goodcase{cc} = 'cc';
290 $header_goodcase{'resent-bcc'} = 'Resent-bcc';
291 $header_goodcase{'resent-cc'} = 'Resent-cc';
292 } elsif ($format =~ /cgi/) {
293 unshift @header_order, qw(content-type location);
294 $option->{sort} = 'good-practice';
295 $option->{fold} = 0;
296 } elsif ($format =~ /http/) {
297 $option->{sort} = 'good-practice';
298 }
299 if ($format =~ /uri-url-mailto/) {
300 $option->{output_bcc} = 0;
301 $option->{capitalize} = 0;
302 $option->{field_format_pattern} = '%s=%s';
303 $option->{fold} = sub {
304 $_[1] =~ s/([^:@+\$A-Za-z0-9\-_.!~*])/sprintf('%%%02X', ord $1)/ge;
305 $_[1];
306 };
307 }
308 }
309
310 =item Message::Header->new ([%initial-fields/options])
311
312 Constructs a new C<Message::Headers> object. You might pass some initial
313 C<field-name>-C<field-body> pairs and/or options as parameters to the constructor.
314
315 Example:
316
317 $hdr = new Message::Headers
318 Date => 'Thu, 03 Feb 1994 00:00:00 +0000',
319 Content_Type => 'text/html',
320 Content_Location => 'http://www.foo.example/',
321 -format => 'mail-rfc2822' ## not to be header field
322 ;
323
324 =cut
325
326 sub new ($;%) {
327 my $class = shift;
328 my $self = bless {}, $class;
329 $self->_init (@_);
330 $self;
331 }
332
333 =item Message::Header->parse ($header, [%initial-fields/options])
334
335 Parses given C<header> and constructs a new C<Message::Headers>
336 object. You might pass some additional C<field-name>-C<field-body> pairs
337 or/and initial options as parameters to the constructor.
338
339 =cut
340
341 sub parse ($$;%) {
342 my $class = shift;
343 my $header = shift;
344 my $self = bless {}, $class;
345 $self->_init (@_); ## BUG: don't check linebreak_strict
346 $header =~ s/\x0D?\x0A$REG{WSP}/\x20/gos; ## unfold
347 for my $field (split /\x0D?\x0A/, $header) {
348 if ($field =~ /$REG{M_fromline}/) {
349 my $body = $1;
350 $body = $self->_field_body ($body, 'mail-from')
351 if $self->{option}->{parse_all};
352 push @{$self->{field}}, {name => 'mail-from', body => $body};
353 } elsif ($field =~ /$REG{M_field}/) {
354 my ($name, $body) = (lc $1, $2);
355 $name =~ s/$REG{WSP}+$//;
356 $body =~ s/$REG{WSP}+$//;
357 $body = $self->_field_body ($body, $name) if $self->{option}->{parse_all};
358 push @{$self->{field}}, {name => $name, body => $body};
359 }
360 }
361 $self;
362 }
363
364 =item Message::Header->parse_array (\@header, [%initial-fields/options])
365
366 Parses given C<header> and constructs a new C<Message::Headers>
367 object. Same as C<Message::Header-E<lt>parse> but this method
368 is given an array reference. You might pass some additional
369 C<field-name>-C<field-body> pairs or/and initial options
370 as parameters to the constructor.
371
372 =cut
373
374 sub parse_array ($\@;%) {
375 my $class = shift;
376 my $header = shift;
377 Carp::croak "parse_array: first argument is not an array reference"
378 unless ref $header eq 'ARRAY';
379 my $self = bless {}, $class;
380 $self->_init (@_);
381 while (1) {
382 my $field = shift @$header;
383 while (1) {
384 if ($$header[0] =~ /^$REG{WSP}/) {
385 $field .= shift @$header;
386 } else {last}
387 }
388 if ($self->{option}->{linebreak_strict}) {
389 $field =~ s/\x0D\x0A//g;
390 } else {
391 $field =~ tr/\x0D\x0A//d;
392 }
393 if ($field =~ /$REG{M_fromline}/) {
394 my $body = $1;
395 $body = $self->_field_body ($body, 'mail-from')
396 if $self->{option}->{parse_all};
397 push @{$self->{field}}, {name => 'mail-from', body => $body};
398 } elsif ($field =~ /$REG{M_field}/) {
399 my ($name, $body) = (lc $1, $2);
400 $name =~ s/$REG{WSP}+$//;
401 $body =~ s/$REG{WSP}+$//;
402 $body = $self->_field_body ($body, $name) if $self->{option}->{parse_all};
403 push @{$self->{field}}, {name => $name, body => $body};
404 }
405 last if $#$header < 0;
406 }
407 $self;
408 }
409
410 =back
411
412 =head1 METHODS
413
414 =head2 $self->field ($field_name)
415
416 Returns C<field-body> of given C<field-name>.
417 When there are two or more C<field>s whose name is C<field-name>,
418 this method return all C<field-body>s as array. (On scalar
419 context, only first one is returned.)
420
421 =cut
422
423 sub field ($$) {
424 my $self = shift;
425 my $name = lc shift;
426 my @ret;
427 for my $field (@{$self->{field}}) {
428 if ($field->{name} eq $name) {
429 unless (wantarray) {
430 $field->{body} = $self->_field_body ($field->{body}, $name);
431 return $field->{body};
432 } else {
433 $field->{body} = $self->_field_body ($field->{body}, $name);
434 push @ret, $field->{body};
435 }
436 }
437 }
438 if ($#ret < 0) {
439 return $self->add ($name);
440 }
441 @ret;
442 }
443
444 sub field_exist ($$) {
445 my $self = shift;
446 my $name = lc shift;
447 my @ret;
448 for my $field (@{$self->{field}}) {
449 return 1 if ($field->{name} eq $name);
450 }
451 0;
452 }
453
454 =head2 $self->field_name ($index)
455
456 Returns C<field-name> of $index'th C<field>.
457
458 =head2 $self->field_body ($index)
459
460 Returns C<field-body> of $index'th C<field>.
461
462 =cut
463
464 sub field_name ($$) {
465 my $self = shift;
466 $self->{field}->[shift]->{name};
467 }
468 sub field_body ($$) {
469 my $self = shift;
470 my $i = shift;
471 $self->{field}->[$i]->{body}
472 = $self->_field_body ($self->{field}->[$i]->{body}, $self->{field}->[$i]->{name});
473 $self->{field}->[$i]->{body};
474 }
475
476 sub _field_body ($$$) {
477 my $self = shift;
478 my ($body, $name) = @_;
479 unless (ref $body) {
480 my $type = $self->{option}->{field_type}->{$name}
481 || $self->{option}->{field_type}->{':DEFAULT'};
482 eval "require $type" or Carp::croak ("_field_body: $type: $@");
483 unless ($body) {
484 $body = $type->new (-field_name => $name,
485 -format => $self->{option}->{format},
486 -parse_all => $self->{option}->{parse_all});
487 } else {
488 $body = $type->parse ($body, -field_name => $name,
489 -format => $self->{option}->{format},
490 -parse_all => $self->{option}->{parse_all});
491 }
492 }
493 $body;
494 }
495
496 =head2 $self->field_name_list ()
497
498 Returns list of all C<field-name>s. (Even if there are two
499 or more C<field>s which have same C<field-name>, this method
500 returns ALL names.)
501
502 =cut
503
504 sub field_name_list ($) {
505 my $self = shift;
506 $self->_delete_empty_field ();
507 map {$_->{name}} @{$self->{field}};
508 }
509
510 =item $hdr->add ($field-name, $field-body, [$name, $body, ...])
511
512 Adds some field name/body pairs. Even if there are
513 one or more fields named given C<$field-name>,
514 given name/body pairs are ADDed. Use C<replace>
515 to remove same-name-fields.
516
517 Instead of field name-body pair, you might pass some options.
518 Four options are available for this method.
519
520 C<-parse>: Parses and validates C<field-body>, and returns
521 C<field-body> object. (When multiple C<field-body>s are
522 added, returns only last one.) (Default: C<defined wantarray>)
523
524 C<-prepend>: New fields are not appended,
525 but prepended to current fields. (Default: C<0>)
526
527 C<-translate-underscore>: Do C<field-name> =~ tr/_/-/. (Default: C<1>)
528
529 C<-validate>: Checks whether C<field-name> is valid or not.
530
531 =cut
532
533 sub add ($%) {
534 my $self = shift;
535 my %fields = @_;
536 my %option = %{$self->{option}};
537 $option{parse} = 1 if defined wantarray;
538 for (grep {/^-/} keys %fields) {$option{substr ($_, 1)} = $fields{$_}}
539 my $body;
540 for (grep {/^[^-]/} keys %fields) {
541 my $name = lc $_; $body = $fields{$_};
542 $name =~ tr/_/-/ if $option{translate_underscore};
543 Carp::croak "add: $name: invalid field-name"
544 if $option{validate} && $name =~ /$REG{UNSAFE_field_name}/;
545 $body = $self->_field_body ($body, $name) if $option{parse};
546 if ($option{prepend}) {
547 unshift @{$self->{field}}, {name => $name, body => $body};
548 } else {
549 push @{$self->{field}}, {name => $name, body => $body};
550 }
551 }
552 $body if $option{parse};
553 }
554
555 =head2 $self->relace ($field_name, $field_body)
556
557 Set the C<field-body> named C<field-name> as $field_body.
558 If $field_name C<field> is already exists, it is replaced
559 by new $field_body value. If not, new C<field> is inserted.
560 (If there are some C<field> named as $field_name,
561 first one is used and the others are not changed.)
562
563 =cut
564
565 sub replace ($%) {
566 my $self = shift;
567 my %params = @_;
568 my %option = %{$self->{option}};
569 $option{parse} = defined wantarray unless defined $option{parse};
570 for (grep {/^-/} keys %params) {$option{substr ($_, 1)} = $params{$_}}
571 my (%new_field);
572 for (grep {/^[^-]/} keys %params) {
573 my $name = lc $_;
574 $name =~ tr/_/-/ if $option{translate_underscore};
575 Carp::croak "replace: $name: invalid field-name"
576 if $option{validate} && $name =~ /$REG{UNSAFE_field_name}/;
577 $params{$_} = $self->_field_body ($params{$_}, $name) if $option{parse};
578 $new_field{$name} = $params{$_};
579 }
580 my $body = (%new_field)[-1];
581 for my $field (@{$self->{field}}) {
582 if (defined $new_field{$field->{name}}) {
583 $field->{body} = $new_field {$field->{name}};
584 $new_field{$field->{name}} = undef;
585 }
586 }
587 for (keys %new_field) {
588 push @{$self->{field}}, {name => $_, body => $new_field{$_}};
589 }
590 $body if $option{parse};
591 }
592
593 =head2 $self->delete ($field-name, [$name, ...])
594
595 Deletes C<field> named as $field_name.
596
597 =cut
598
599 sub delete ($@) {
600 my $self = shift;
601 my %delete; for (@_) {$delete{lc $_} = 1}
602 for my $field (@{$self->{field}}) {
603 undef $field if $delete{$field->{name}};
604 }
605 }
606
607 =head2 $self->count ([$field_name])
608
609 Returns the number of times the given C<field> appears.
610 If no $field_name is given, returns the number
611 of fields. (Same as $#$self+1)
612
613 =cut
614
615 sub count ($;$) {
616 my $self = shift;
617 my ($name) = (lc shift);
618 unless ($name) {
619 $self->_delete_empty_field ();
620 return $#{$self->{field}}+1;
621 }
622 my $count = 0;
623 for my $field (@{$self->{field}}) {
624 if ($field->{name} eq $name) {
625 $count++;
626 }
627 }
628 $count;
629 }
630
631 =head2 $self->rename ($field-name, $new-name, [$old, $new,...])
632
633 Renames C<$field-name> as C<$new-name>.
634
635 =cut
636
637 sub rename ($%) {
638 my $self = shift;
639 my %params = @_;
640 my %option = %{$self->{option}};
641 for (grep {/^-/} keys %params) {$option{substr ($_, 1)} = $params{$_}}
642 my %new_name;
643 for (grep {/^[^-]/} keys %params) {
644 my ($old => $new) = (lc $_ => lc $params{$_});
645 $new =~ tr/_/-/ if $option{translate_underscore};
646 Carp::croak "rename: $new: invalid field-name"
647 if $option{validate} && $new =~ /$REG{UNSAFE_field_name}/;
648 $new_name{$old} = $new;
649 }
650 for my $field (@{$self->{field}}) {
651 if (length $new_name{$field->{name}}) {
652 $field->{name} = $new_name{$field->{name}};
653 }
654 }
655 $self if defined wantarray;
656 }
657
658
659 =item $self->scan(\&doit)
660
661 Apply a subroutine to each header field in turn. The callback routine is
662 called with two parameters; the name of the field and a single value.
663 If the header has more than one value, then the routine is called once
664 for each value.
665
666 =cut
667
668 sub scan ($&) {
669 my ($self, $sub) = @_;
670 my $sort;
671 $sort = \&_header_cmp if $self->{option}->{sort} eq 'good-practice';
672 $sort = {$a cmp $b} if $self->{option}->{sort} eq 'alphabetic';
673 my @field = @{$self->{field}};
674 if (ref $sort) {
675 @field = sort $sort @{$self->{field}};
676 }
677 for my $field (@field) {
678 next if $field->{name} =~ /^_/;
679 &$sub($field->{name} => $field->{body});
680 }
681 }
682
683 # Compare function which makes it easy to sort headers in the
684 # recommended "Good Practice" order.
685 ## taken from HTTP::Header
686 sub _header_cmp
687 {
688 my ($na, $nb) = ($a->{name}, $b->{name});
689 # Unknown headers are assign a large value so that they are
690 # sorted last. This also helps avoiding a warning from -w
691 # about comparing undefined values.
692 $header_order{$na} = 999 unless defined $header_order{$na};
693 $header_order{$nb} = 999 unless defined $header_order{$nb};
694
695 $header_order{$na} <=> $header_order{$nb} || $na cmp $nb;
696 }
697
698 =head2 $self->stringify ([%option])
699
700 Returns the C<header> as a string.
701
702 =cut
703
704 sub stringify ($;%) {
705 my $self = shift;
706 my %params = @_;
707 my %option = %{$self->{option}};
708 $option{format} = $params{-format} if $params{-format};
709 $self->_init_by_format ($option{format}, \%option);
710 for (grep {/^-/} keys %params) {$option{substr ($_, 1)} = $params{$_}}
711 my @ret;
712 my $_stringify = sub {
713 my ($name, $body) = (@_);
714 return unless length $name;
715 return if $option{mail_from} && $name eq 'mail-from';
716 return if !$option{output_bcc} && ($name eq 'bcc' || $name eq 'resent-bcc');
717 if ($option{format} =~ /uri-url-mailto/) {
718 return if (( $option{uri_mailto_safe}->{$name}
719 || $option{uri_mailto_safe}->{':default'})
720 < $option{uri_mailto_safe_level});
721 if ($name eq 'to') {
722 $body = $self->field ('to');
723 return unless ref $body && $body->have_group;
724 }
725 }
726 my $fbody;
727 if (ref $body) {
728 $fbody = $body->stringify (-format => $option{format});
729 } else {
730 $fbody = $body;
731 }
732 return unless length $fbody;
733 unless ($option{linebreak_strict}) {
734 ## bare \x0D and bare \x0A are unsafe
735 $fbody =~ s/\x0D(?=[^\x09\x0A\x20])/\x0D\x20/g;
736 $fbody =~ s/\x0A(?=[^\x09\x20])/\x0A\x20/g;
737 } else {
738 $fbody =~ s/\x0D\x0A(?=[^\x09\x20])/\x0D\x0A\x20/g;
739 }
740 if (ref $option{capitalize}) {
741 $name = &{$option{capitalize}} ($self, $name);
742 } elsif ($option{capitalize}) {
743 $name =~ s/((?:^|-)[a-z])/uc($1)/ge;
744 }
745 if (ref $option{fold}) {
746 $fbody = &{$option{fold}} ($self, $fbody);
747 } elsif ($option{fold}) {
748 $fbody = $self->_fold ($fbody);
749 }
750 push @ret, sprintf $option{field_format_pattern}, $name, $fbody;
751 };
752 if ($option{format} =~ /uri-url-mailto-to/) {
753 if ($self->field_exist ('to')) {
754 my $to = $self->field ('to');
755 unless ($to->have_group) {
756 my $fbody = $to->stringify (-format => $option{format});
757 return &{$option{fold}} ($self, $fbody);
758 }
759 }
760 '';
761 } elsif ($option{format} =~ /uri-url-mailto/) {
762 $self->scan ($_stringify);
763 my $ret = join ('&', @ret);
764 $ret;
765 } else {
766 if ($option{mail_from}) {
767 my $fromline = $self->field ('mail-from');
768 push @ret, 'From '.$fromline if $fromline;
769 }
770 $self->scan ($_stringify);
771 my $ret = join ("\n", @ret);
772 $ret? $ret."\n": '';
773 }
774 }
775 *as_string = \&stringify;
776
777 =head2 $self->option ($option_name, [$option_value])
778
779 Set/gets new value of the option.
780
781 =cut
782
783 sub option ($@) {
784 my $self = shift;
785 if (@_ == 1) {
786 return $self->{option}->{ shift (@_) };
787 }
788 while (my ($name, $value) = splice (@_, 0, 2)) {
789 $name =~ s/^-//;
790 $self->{option}->{$name} = $value;
791 if ($name eq 'format') {
792 for my $f (@{$self->{field}}) {
793 if (ref $f->{body}) {
794 $f->{body}->option (-format => $value);
795 }
796 }
797 }
798 }
799 }
800
801 sub field_type ($$;$) {
802 my $self = shift;
803 my $field_name = shift;
804 my $new_field_type = shift;
805 if ($new_field_type) {
806 $self->{option}->{field_type}->{$field_name} = $new_field_type;
807 }
808 $self->{option}->{field_type}->{$field_name}
809 || $self->{option}->{field_type}->{':DEFAULT'};
810 }
811
812 sub _delete_empty_field ($) {
813 my $self = shift;
814 my @ret;
815 for my $field (@{$self->{field}}) {
816 push @ret, $field if $field->{name};
817 }
818 $self->{field} = \@ret;
819 $self;
820 }
821
822 sub _fold ($$;$) {
823 my $self = shift;
824 my $string = shift;
825 my $len = shift || $self->{option}->{fold_length};
826 $len = 60 if $len < 60;
827
828 ## This code is taken from Mail::Header 1.43 in MailTools,
829 ## by Graham Barr (Maintained by Mark Overmeer <mailtools@overmeer.net>).
830 my $max = int($len - 5); # 4 for leading spcs + 1 for [\,\;]
831 my $min = int($len * 4 / 5) - 4;
832 my $ml = $len;
833
834 if (length($string) > $ml) {
835 #Split the line up
836 # first bias towards splitting at a , or a ; >4/5 along the line
837 # next split a whitespace
838 # else we are looking at a single word and probably don't want to split
839 my $x = "";
840 $x .= "$1\n "
841 while($string =~ s/^$REG{WSP}*(
842 [^"]{$min,$max}?[\,\;]
843 |[^"]{1,$max}$REG{WSP}
844 |[^\s"]*(?:"[^"]*"[^\s"]*)+$REG{WSP}
845 |[^\s"]+$REG{WSP}
846 )
847 //x);
848 $x .= $string;
849 $string = $x;
850 $string =~ s/(\A$REG{WSP}+|$REG{WSP}+\Z)//sog;
851 $string =~ s/\s+\n/\n/sog;
852 }
853 $string;
854 }
855
856 =head2 $self->clone ()
857
858 Returns a copy of Message::Header object.
859
860 =cut
861
862 sub clone ($) {
863 my $self = shift;
864 my $clone = new Message::Header;
865 for my $name (%{$self->{option}}) {
866 if (ref $self->{option}->{$name} eq 'HASH') {
867 $clone->{option}->{$name} = {%{$self->{option}->{$name}}};
868 } elsif (ref $self->{option}->{$name} eq 'ARRAY') {
869 $clone->{option}->{$name} = [@{$self->{option}->{$name}}];
870 } else {
871 $clone->{option}->{$name} = $self->{option}->{$name};
872 }
873 }
874 for (@{$self->{field}}) {
875 $clone->add ($_->{name}, scalar $_->{body});
876 }
877 $clone;
878 }
879
880 =head1 NOTE
881
882 =head2 C<field-name>
883
884 The header field name is not case sensitive. To make the life
885 easier for perl users who wants to avoid quoting before the => operator,
886 you can use '_' as a synonym for '-' in header field names
887 (this behaviour can be suppressed by setting
888 C<translate_underscore> option to C<0> value).
889
890 =head1 EXAMPLE
891
892 ## Print field list
893
894 use Message::Header;
895 my $header = Message::Header->parse ($header);
896
897 for my $i (0..$#$header) {
898 print $header->field_name ($i), "\t=> ", $header->field_body ($i), "\n";
899 }
900
901
902 ## Make simple header
903
904 use Message::Header;
905 use Message::Field::Address;
906 my $header = new Message::Header;
907
908 my $from = new Message::Field::Address;
909 $from->add ('foo@foo.example', name => 'F. Foo');
910 my $to = new Message::Field::Address;
911 $to->add ('bar@bar.example', name => 'Mr. Bar');
912 $to->add ('hoge@foo.example', name => 'Hoge-san');
913 $header->add ('From' => $from);
914 $header->add ('To' => $to);
915 $header->add ('Subject' => 'Re: Meeting');
916 $header->add ('References' => '<hoge.msgid%foo@foo.example>');
917 print $header;
918
919 =head1 ACKNOWLEDGEMENTS
920
921 Some of codes are taken from other modules such as
922 HTTP::Header, Mail::Header.
923
924 =head1 LICENSE
925
926 Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
927
928 This program is free software; you can redistribute it and/or modify
929 it under the terms of the GNU General Public License as published by
930 the Free Software Foundation; either version 2 of the License, or
931 (at your option) any later version.
932
933 This program is distributed in the hope that it will be useful,
934 but WITHOUT ANY WARRANTY; without even the implied warranty of
935 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
936 GNU General Public License for more details.
937
938 You should have received a copy of the GNU General Public License
939 along with this program; see the file COPYING. If not, write to
940 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
941 Boston, MA 02111-1307, USA.
942
943 =head1 CHANGE
944
945 See F<ChangeLog>.
946 $Date: 2002/05/15 07:31:28 $
947
948 =cut
949
950 1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24