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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.19 - (hide annotations) (download)
Wed May 15 07:31:28 2002 UTC (22 years, 6 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 wakaba 1.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 wakaba 1.14 use vars qw($VERSION %REG);
15 wakaba 1.1 $VERSION = '1.00';
16 wakaba 1.14 use Carp ();
17 wakaba 1.15 use overload '@{}' => sub { shift->_delete_empty_field->{field} },
18     '""' => sub { shift->stringify },
19     fallback => 1;
20 wakaba 1.1
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 wakaba 1.15 =head1 CONSTRUCTORS
49    
50     The following methods construct new C<Message::Header> objects:
51    
52     =over 4
53    
54 wakaba 1.16 =cut
55    
56 wakaba 1.15 ## Initialize
57 wakaba 1.14 my %DEFAULT = (
58 wakaba 1.1 capitalize => 1,
59 wakaba 1.17 fold => 1,
60 wakaba 1.1 fold_length => 70,
61 wakaba 1.17 field_format_pattern => '%s: %s',
62 wakaba 1.14 #field_type => {},
63     format => 'mail-rfc2822',
64 wakaba 1.18 linebreak_strict => 0,
65 wakaba 1.14 mail_from => 0,
66     output_bcc => 0,
67     parse_all => 0,
68     sort => 'none',
69     translate_underscore => 1,
70 wakaba 1.18 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 wakaba 1.14 validate => 1,
105 wakaba 1.1 );
106 wakaba 1.14 $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 wakaba 1.18 p3p => 'Message::Field::Params',
114 wakaba 1.15 'auto-submitted' => 'Message::Field::ValueParams',
115 wakaba 1.17 'content-disposition' => 'Message::Field::ValueParams',
116 wakaba 1.14 link => 'Message::Field::ValueParams',
117     archive => 'Message::Field::ValueParams',
118     'x-face-type' => 'Message::Field::ValueParams',
119 wakaba 1.18 'x-mozilla-draft-info' => 'Message::Field::ValueParams',
120 wakaba 1.14
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 wakaba 1.19 'resent-user-agent' => 'Message::Field::UA',
127 wakaba 1.14 server => 'Message::Field::UA',
128    
129 wakaba 1.18 ## A message id
130     'content-id' => 'Message::Field::MsgID',
131     'message-id' => 'Message::Field::MsgID',
132     'resent-message-id' => 'Message::Field::MsgID',
133    
134 wakaba 1.15 ## Numeric value
135 wakaba 1.14 'content-length' => 'Message::Field::Numval',
136     lines => 'Message::Field::Numval',
137     'max-forwards' => 'Message::Field::Numval',
138     'mime-version' => 'Message::Field::Numval',
139 wakaba 1.15 'x-jsmail-priority' => 'Message::Field::Numval',
140 wakaba 1.16 'x-mail-count' => 'Message::Field::Numval',
141     'x-ml-count' => 'Message::Field::Numval',
142 wakaba 1.15 'x-priority' => 'Message::Field::Numval',
143 wakaba 1.14
144     path => 'Message::Field::Path',
145     };
146 wakaba 1.16 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 wakaba 1.18 original-recipient priority x-list-id
151 wakaba 1.16 sensitivity status x-face x-msmail-priority xref))
152 wakaba 1.4 {$DEFAULT{field_type}->{$_} = 'Message::Field::Structured'}
153 wakaba 1.16 ## Not supported yet, but to be supported...
154 wakaba 1.18 # x-list: unstructured, ml name
155 wakaba 1.16 for (qw(abuse-reports-to apparently-to approved approved-by bcc cc complaints-to
156 wakaba 1.15 delivered-to disposition-notification-to envelope-to
157 wakaba 1.16 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 wakaba 1.4 resent-cc resent-to resent-from resent-sender return-path
161 wakaba 1.16 return-receipt-to return-receipt-requested-to sender to x-abuse-reports-to
162 wakaba 1.19 x-admin x-approved x-beenthere x-confirm-reading-to
163 wakaba 1.4 x-complaints-to x-envelope-from x-envelope-sender
164 wakaba 1.16 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 wakaba 1.19 {$DEFAULT{field_type}->{$_} = 'Message::Field::Addresses'}
167 wakaba 1.18 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 wakaba 1.16 x-originalarrivaltime x-tcup-date))
171 wakaba 1.4 {$DEFAULT{field_type}->{$_} = 'Message::Field::Date'}
172 wakaba 1.18 for (qw(article-updates in-reply-to
173     obsoletes references replaces see-also supersedes))
174     {$DEFAULT{field_type}->{$_} = 'Message::Field::MsgIDs'}
175 wakaba 1.7 for (qw(accept accept-charset accept-encoding accept-language
176 wakaba 1.8 content-language
177 wakaba 1.12 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 wakaba 1.18 posted-to))
181 wakaba 1.7 {$DEFAULT{field_type}->{$_} = 'Message::Field::CSV'}
182 wakaba 1.18 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 wakaba 1.12 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 wakaba 1.1
192 wakaba 1.18 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 wakaba 1.14 ## 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 wakaba 1.18 $self->_init_by_format ($self->{option}->{format}, $self->{option});
273 wakaba 1.14 # 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 wakaba 1.18
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 wakaba 1.14 }
309    
310 wakaba 1.15 =item Message::Header->new ([%initial-fields/options])
311 wakaba 1.14
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 wakaba 1.15 Example:
316 wakaba 1.1
317 wakaba 1.14 $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 wakaba 1.1
324     =cut
325    
326     sub new ($;%) {
327     my $class = shift;
328 wakaba 1.14 my $self = bless {}, $class;
329     $self->_init (@_);
330 wakaba 1.1 $self;
331     }
332    
333 wakaba 1.15 =item Message::Header->parse ($header, [%initial-fields/options])
334 wakaba 1.1
335 wakaba 1.14 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 wakaba 1.1
339     =cut
340    
341     sub parse ($$;%) {
342     my $class = shift;
343     my $header = shift;
344 wakaba 1.14 my $self = bless {}, $class;
345 wakaba 1.18 $self->_init (@_); ## BUG: don't check linebreak_strict
346 wakaba 1.14 $header =~ s/\x0D?\x0A$REG{WSP}/\x20/gos; ## unfold
347 wakaba 1.1 for my $field (split /\x0D?\x0A/, $header) {
348     if ($field =~ /$REG{M_fromline}/) {
349 wakaba 1.9 my $body = $1;
350     $body = $self->_field_body ($body, 'mail-from')
351 wakaba 1.14 if $self->{option}->{parse_all};
352 wakaba 1.9 push @{$self->{field}}, {name => 'mail-from', body => $body};
353 wakaba 1.1 } elsif ($field =~ /$REG{M_field}/) {
354 wakaba 1.9 my ($name, $body) = (lc $1, $2);
355 wakaba 1.1 $name =~ s/$REG{WSP}+$//;
356     $body =~ s/$REG{WSP}+$//;
357 wakaba 1.14 $body = $self->_field_body ($body, $name) if $self->{option}->{parse_all};
358 wakaba 1.9 push @{$self->{field}}, {name => $name, body => $body};
359 wakaba 1.1 }
360     }
361     $self;
362     }
363    
364 wakaba 1.15 =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 wakaba 1.14 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 wakaba 1.18 if ($self->{option}->{linebreak_strict}) {
389     $field =~ s/\x0D\x0A//g;
390     } else {
391     $field =~ tr/\x0D\x0A//d;
392     }
393 wakaba 1.14 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 wakaba 1.15 =back
411    
412     =head1 METHODS
413    
414 wakaba 1.1 =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 wakaba 1.5 $field->{body} = $self->_field_body ($field->{body}, $name);
431     return $field->{body};
432 wakaba 1.1 } else {
433 wakaba 1.5 $field->{body} = $self->_field_body ($field->{body}, $name);
434     push @ret, $field->{body};
435 wakaba 1.1 }
436     }
437     }
438 wakaba 1.9 if ($#ret < 0) {
439     return $self->add ($name);
440     }
441 wakaba 1.1 @ret;
442     }
443    
444 wakaba 1.9 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 wakaba 1.2 =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 wakaba 1.4 my $i = shift;
471 wakaba 1.5 $self->{field}->[$i]->{body}
472     = $self->_field_body ($self->{field}->[$i]->{body}, $self->{field}->[$i]->{name});
473     $self->{field}->[$i]->{body};
474 wakaba 1.4 }
475    
476     sub _field_body ($$$) {
477     my $self = shift;
478     my ($body, $name) = @_;
479 wakaba 1.5 unless (ref $body) {
480 wakaba 1.4 my $type = $self->{option}->{field_type}->{$name}
481 wakaba 1.7 || $self->{option}->{field_type}->{':DEFAULT'};
482 wakaba 1.14 eval "require $type" or Carp::croak ("_field_body: $type: $@");
483 wakaba 1.5 unless ($body) {
484 wakaba 1.16 $body = $type->new (-field_name => $name,
485 wakaba 1.18 -format => $self->{option}->{format},
486 wakaba 1.19 -parse_all => $self->{option}->{parse_all});
487 wakaba 1.5 } else {
488 wakaba 1.14 $body = $type->parse ($body, -field_name => $name,
489 wakaba 1.16 -format => $self->{option}->{format},
490 wakaba 1.19 -parse_all => $self->{option}->{parse_all});
491 wakaba 1.5 }
492 wakaba 1.4 }
493 wakaba 1.5 $body;
494 wakaba 1.2 }
495    
496 wakaba 1.1 =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 wakaba 1.16 =item $hdr->add ($field-name, $field-body, [$name, $body, ...])
511 wakaba 1.1
512 wakaba 1.16 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 wakaba 1.1
517 wakaba 1.14 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 wakaba 1.1 =cut
532    
533 wakaba 1.14 sub add ($%) {
534 wakaba 1.1 my $self = shift;
535 wakaba 1.14 my %fields = @_;
536     my %option = %{$self->{option}};
537 wakaba 1.18 $option{parse} = 1 if defined wantarray;
538 wakaba 1.14 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 wakaba 1.8 }
552 wakaba 1.14 $body if $option{parse};
553 wakaba 1.1 }
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 wakaba 1.14 sub replace ($%) {
566 wakaba 1.1 my $self = shift;
567 wakaba 1.14 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 wakaba 1.15 my (%new_field);
572 wakaba 1.14 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 wakaba 1.15 my $body = (%new_field)[-1];
581 wakaba 1.1 for my $field (@{$self->{field}}) {
582 wakaba 1.14 if (defined $new_field{$field->{name}}) {
583 wakaba 1.15 $field->{body} = $new_field {$field->{name}};
584 wakaba 1.14 $new_field{$field->{name}} = undef;
585 wakaba 1.1 }
586     }
587 wakaba 1.14 for (keys %new_field) {
588     push @{$self->{field}}, {name => $_, body => $new_field{$_}};
589     }
590     $body if $option{parse};
591 wakaba 1.1 }
592    
593 wakaba 1.14 =head2 $self->delete ($field-name, [$name, ...])
594 wakaba 1.1
595     Deletes C<field> named as $field_name.
596    
597     =cut
598    
599 wakaba 1.14 sub delete ($@) {
600 wakaba 1.1 my $self = shift;
601 wakaba 1.16 my %delete; for (@_) {$delete{lc $_} = 1}
602 wakaba 1.1 for my $field (@{$self->{field}}) {
603 wakaba 1.14 undef $field if $delete{$field->{name}};
604 wakaba 1.1 }
605     }
606    
607 wakaba 1.2 =head2 $self->count ([$field_name])
608 wakaba 1.1
609     Returns the number of times the given C<field> appears.
610 wakaba 1.2 If no $field_name is given, returns the number
611     of fields. (Same as $#$self+1)
612 wakaba 1.1
613     =cut
614    
615 wakaba 1.2 sub count ($;$) {
616 wakaba 1.1 my $self = shift;
617     my ($name) = (lc shift);
618 wakaba 1.2 unless ($name) {
619     $self->_delete_empty_field ();
620     return $#{$self->{field}}+1;
621     }
622 wakaba 1.1 my $count = 0;
623     for my $field (@{$self->{field}}) {
624     if ($field->{name} eq $name) {
625     $count++;
626     }
627     }
628     $count;
629     }
630    
631 wakaba 1.14 =head2 $self->rename ($field-name, $new-name, [$old, $new,...])
632 wakaba 1.12
633 wakaba 1.14 Renames C<$field-name> as C<$new-name>.
634 wakaba 1.12
635     =cut
636    
637 wakaba 1.14 sub rename ($%) {
638 wakaba 1.12 my $self = shift;
639 wakaba 1.14 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 wakaba 1.12 for my $field (@{$self->{field}}) {
651 wakaba 1.14 if (length $new_name{$field->{name}}) {
652     $field->{name} = $new_name{$field->{name}};
653 wakaba 1.12 }
654     }
655 wakaba 1.14 $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 wakaba 1.12 }
697    
698 wakaba 1.1 =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 wakaba 1.14 my %params = @_;
707     my %option = %{$self->{option}};
708 wakaba 1.18 $option{format} = $params{-format} if $params{-format};
709     $self->_init_by_format ($option{format}, \%option);
710 wakaba 1.14 for (grep {/^-/} keys %params) {$option{substr ($_, 1)} = $params{$_}}
711 wakaba 1.1 my @ret;
712 wakaba 1.18 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 wakaba 1.12 }
760 wakaba 1.18 '';
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 wakaba 1.1 }
775 wakaba 1.14 *as_string = \&stringify;
776 wakaba 1.1
777 wakaba 1.12 =head2 $self->option ($option_name, [$option_value])
778 wakaba 1.1
779 wakaba 1.12 Set/gets new value of the option.
780 wakaba 1.1
781     =cut
782    
783 wakaba 1.14 sub option ($@) {
784 wakaba 1.1 my $self = shift;
785 wakaba 1.14 if (@_ == 1) {
786     return $self->{option}->{ shift (@_) };
787     }
788     while (my ($name, $value) = splice (@_, 0, 2)) {
789     $name =~ s/^-//;
790 wakaba 1.12 $self->{option}->{$name} = $value;
791     if ($name eq 'format') {
792     for my $f (@{$self->{field}}) {
793 wakaba 1.14 if (ref $f->{body}) {
794     $f->{body}->option (-format => $value);
795 wakaba 1.12 }
796     }
797     }
798     }
799 wakaba 1.1 }
800    
801 wakaba 1.4 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 wakaba 1.7 || $self->{option}->{field_type}->{':DEFAULT'};
810 wakaba 1.4 }
811    
812 wakaba 1.1 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 wakaba 1.17 sub _fold ($$;$) {
823 wakaba 1.1 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 wakaba 1.11 $x .= "$1\n "
841 wakaba 1.1 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 wakaba 1.14 =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 wakaba 1.1 =head1 EXAMPLE
891    
892     ## Print field list
893    
894     use Message::Header;
895     my $header = Message::Header->parse ($header);
896    
897 wakaba 1.2 for my $i (0..$#$header) {
898     print $header->field_name ($i), "\t=> ", $header->field_body ($i), "\n";
899 wakaba 1.1 }
900    
901    
902     ## Make simple header
903    
904 wakaba 1.2 use Message::Header;
905 wakaba 1.1 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 wakaba 1.14 =head1 ACKNOWLEDGEMENTS
920    
921     Some of codes are taken from other modules such as
922     HTTP::Header, Mail::Header.
923    
924 wakaba 1.1 =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 wakaba 1.19 $Date: 2002/05/14 13:50:11 $
947 wakaba 1.1
948     =cut
949    
950     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24