/[suikacvs]/test/cvs
Suika

Contents of /test/cvs

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.16 - (hide annotations) (download)
Fri Apr 19 12:00:36 2002 UTC (22 years ago) by wakaba
Branch: MAIN
Changes since 1.15: +37 -20 lines
2002-04-05  wakaba <w@suika.fam.cx>

	* Util.pm: Add some functions from Message::Field::Structured.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24