/[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.18 - (hide annotations) (download)
Tue May 14 13:50:11 2002 UTC (22 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.17: +198 -49 lines
2002-05-14  wakaba <w@suika.fam.cx>

	* Entity.pm (pod:uri-url-mailto-*): New list-items.
	(stringify): Output mailto: URL when format =~ url-mailto.
	* Header.pm (stringify): Ditto.
	* Util.pm: Bugs are fixed.
	(remove_meaningless_wsp): New function.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24