/[suikacvs]/test/cvs
Suika

Contents of /test/cvs

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (hide annotations) (download)
Wed Mar 13 13:06:47 2002 UTC (22 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.1: +35 -4 lines
2002-03-13  wakaba <w@suika.fam.cx>

	* Header.pm: New module.
	
	* ChangeLog: New file.

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     use vars qw($VERSION %REG %DEFAULT);
15     $VERSION = '1.00';
16    
17     use overload '@{}' => sub {shift->_delete_empty_field()->{field}},
18     '""' => sub {shift->stringify};
19    
20     $REG{WSP} = qr/[\x09\x20]/;
21     $REG{FWS} = qr/[\x09\x20]*/;
22     $REG{M_field} = qr/^([^\x3A]+):$REG{FWS}([\x00-\xFF]*)$/;
23     $REG{M_fromline} = qr/^\x3E?From$REG{WSP}+([\x00-\xFF]*)$/;
24     $REG{UNSAFE_field_name} = qr/[\x00-\x20\x3A\x7F-\xFF]/;
25    
26     =head2 options
27    
28     These options can be getten/set by C<get_option>/C<set_option>
29     method.
30    
31     =head3 capitalize = 0/1
32    
33     (First character of) C<field-name> is capitalized
34     when C<stringify>. (Default = 1)
35    
36     =head3 fold_length = numeric value
37    
38     Length of line used to fold. (Default = 70)
39    
40     =head3 mail_from = 0/1
41    
42     Outputs "From " line (known as Un*x From, Mail-From, and so on)
43     when C<stringify>. (Default = 0)
44    
45     =cut
46    
47     %DEFAULT = (
48     capitalize => 1,
49     fold_length => 70,
50     mail_from => 0,
51     );
52    
53     =head2 Message::Header->new ([%option])
54    
55     Returns new Message::Header instance. Some options can be
56     specified as hash.
57    
58     =cut
59    
60     sub new ($;%) {
61     my $class = shift;
62     my $self = bless {option => {@_}}, $class;
63     for (keys %DEFAULT) {$self->{option}->{$_} ||= $DEFAULT{$_}}
64     $self;
65     }
66    
67     =head2 Message::Header->parse ($header, [%option])
68    
69     Parses given C<header> and return a new Message::Header
70     object. Some options can be specified as hash.
71    
72     =cut
73    
74     sub parse ($$;%) {
75     my $class = shift;
76     my $header = shift;
77     my $self = bless {option => {@_}}, $class;
78     for (keys %DEFAULT) {$self->{option}->{$_} ||= $DEFAULT{$_}}
79     $header =~ s/\x0D?\x0A$REG{WSP}+/\x20/gos; ## unfold
80     for my $field (split /\x0D?\x0A/, $header) {
81     if ($field =~ /$REG{M_fromline}/) {
82     push @{$self->{field}}, {name => 'mail-from', body => $1};
83     } elsif ($field =~ /$REG{M_field}/) {
84     my ($name, $body) = ($1, $2);
85     $name =~ s/$REG{WSP}+$//;
86     $body =~ s/$REG{WSP}+$//;
87     push @{$self->{field}}, {name => lc $name, body => $body};
88     }
89     }
90     $self;
91     }
92    
93     =head2 $self->field ($field_name)
94    
95     Returns C<field-body> of given C<field-name>.
96     When there are two or more C<field>s whose name is C<field-name>,
97     this method return all C<field-body>s as array. (On scalar
98     context, only first one is returned.)
99    
100     =cut
101    
102     sub field ($$) {
103     my $self = shift;
104     my $name = lc shift;
105     my @ret;
106     for my $field (@{$self->{field}}) {
107     if ($field->{name} eq $name) {
108     unless (wantarray) {
109     return $field->{body};
110     } else {
111     push @ret, $field->{body};
112     }
113     }
114     }
115     @ret;
116     }
117    
118 wakaba 1.2 =head2 $self->field_name ($index)
119    
120     Returns C<field-name> of $index'th C<field>.
121    
122     =head2 $self->field_body ($index)
123    
124     Returns C<field-body> of $index'th C<field>.
125    
126     =cut
127    
128     sub field_name ($$) {
129     my $self = shift;
130     $self->{field}->[shift]->{name};
131     }
132     sub field_body ($$) {
133     my $self = shift;
134     $self->{field}->[shift]->{body};
135     }
136    
137 wakaba 1.1 =head2 $self->field_name_list ()
138    
139     Returns list of all C<field-name>s. (Even if there are two
140     or more C<field>s which have same C<field-name>, this method
141     returns ALL names.)
142    
143     =cut
144    
145     sub field_name_list ($) {
146     my $self = shift;
147     $self->_delete_empty_field ();
148     map {$_->{name}} @{$self->{field}};
149     }
150    
151     =head2 $self->add ($field_name, $field_body)
152    
153     Adds an new C<field>. It is not checked whether
154     the field which named $field_body is already exist or not.
155     If you don't want duplicated C<field>s, use C<replace> method.
156    
157     =cut
158    
159     sub add ($$$) {
160     my $self = shift;
161     my ($name, $body) = (lc shift, shift);
162     return 0 if $name =~ /$REG{UNSAFE_field_name}/;
163     push @{$self->{field}}, {name => $name, body => $body};
164     $self;
165     }
166    
167     =head2 $self->relace ($field_name, $field_body)
168    
169     Set the C<field-body> named C<field-name> as $field_body.
170     If $field_name C<field> is already exists, it is replaced
171     by new $field_body value. If not, new C<field> is inserted.
172     (If there are some C<field> named as $field_name,
173     first one is used and the others are not changed.)
174    
175     =cut
176    
177     sub replace ($$$) {
178     my $self = shift;
179     my ($name, $body) = (lc shift, shift);
180     return 0 if $name =~ /$REG{UNSAFE_field_name}/;
181     for my $field (@{$self->{field}}) {
182     if ($field->{name} eq $name) {
183     $field->{body} = $body;
184     return $self;
185     }
186     }
187     push @{$self->{field}}, {name => $name, body => $body};
188     $self;
189     }
190    
191     =head2 $self->delete ($field_name, [$index])
192    
193     Deletes C<field> named as $field_name.
194     If $index is specified, only $index'th C<field> is deleted.
195     If not, ($index == 0), all C<field>s that have the C<field-name>
196     $field_name are deleted.
197    
198     =cut
199    
200     sub delete ($$;$) {
201     my $self = shift;
202     my ($name, $index) = (lc shift, shift);
203     my $i = 0;
204     for my $field (@{$self->{field}}) {
205     if ($field->{name} eq $name) {
206     $i++;
207     if ($index == 0 || $i == $index) {
208     undef $field;
209     return $self if $i == $index;
210     }
211     }
212     }
213     $self;
214     }
215    
216 wakaba 1.2 =head2 $self->count ([$field_name])
217 wakaba 1.1
218     Returns the number of times the given C<field> appears.
219 wakaba 1.2 If no $field_name is given, returns the number
220     of fields. (Same as $#$self+1)
221 wakaba 1.1
222     =cut
223    
224 wakaba 1.2 sub count ($;$) {
225 wakaba 1.1 my $self = shift;
226     my ($name) = (lc shift);
227 wakaba 1.2 unless ($name) {
228     $self->_delete_empty_field ();
229     return $#{$self->{field}}+1;
230     }
231 wakaba 1.1 my $count = 0;
232     for my $field (@{$self->{field}}) {
233     if ($field->{name} eq $name) {
234     $count++;
235     }
236     }
237     $count;
238     }
239    
240     =head2 $self->stringify ([%option])
241    
242     Returns the C<header> as a string.
243    
244     =cut
245    
246     sub stringify ($;%) {
247     my $self = shift;
248     my %OPT = @_;
249     my @ret;
250     $OPT{capitalize} ||= $self->{option}->{capitalize};
251     $OPT{mail_from} ||= $self->{option}->{mail_from};
252     push @ret, 'From '.$self->field ('mail-from') if $OPT{mail_from};
253     for my $field (@{$self->{field}}) {
254     my $name = $field->{name};
255     next unless $field->{name};
256     next if !$OPT{mail_from} && $name eq 'mail-from';
257     $name =~ s/((?:^|-)[a-z])/uc($1)/ge if $OPT{capitalize};
258     push @ret, $name.': '.$self->fold ($field->{body});
259     }
260     join "\n", @ret;
261     }
262    
263     =head2 $self->get_option ($option_name)
264    
265     Returns value of the option.
266    
267     =head2 $self->set_option ($option_name, $option_value)
268    
269     Set new value of the option.
270    
271     =cut
272    
273     sub get_option ($$) {
274     my $self = shift;
275     my ($name) = @_;
276     $self->{option}->{$name};
277     }
278     sub set_option ($$$) {
279     my $self = shift;
280     my ($name, $value) = @_;
281     $self->{option}->{$name} = $value;
282     $self;
283     }
284    
285     sub _delete_empty_field ($) {
286     my $self = shift;
287     my @ret;
288     for my $field (@{$self->{field}}) {
289     push @ret, $field if $field->{name};
290     }
291     $self->{field} = \@ret;
292     $self;
293     }
294    
295     sub fold ($$;$) {
296     my $self = shift;
297     my $string = shift;
298     my $len = shift || $self->{option}->{fold_length};
299     $len = 60 if $len < 60;
300    
301     ## This code is taken from Mail::Header 1.43 in MailTools,
302     ## by Graham Barr (Maintained by Mark Overmeer <mailtools@overmeer.net>).
303     my $max = int($len - 5); # 4 for leading spcs + 1 for [\,\;]
304     my $min = int($len * 4 / 5) - 4;
305     my $ml = $len;
306    
307     if (length($string) > $ml) {
308     #Split the line up
309     # first bias towards splitting at a , or a ; >4/5 along the line
310     # next split a whitespace
311     # else we are looking at a single word and probably don't want to split
312     my $x = "";
313     $x .= "$1\n "
314     while($string =~ s/^$REG{WSP}*(
315     [^"]{$min,$max}?[\,\;]
316     |[^"]{1,$max}$REG{WSP}
317     |[^\s"]*(?:"[^"]*"[^\s"]*)+$REG{WSP}
318     |[^\s"]+$REG{WSP}
319     )
320     //x);
321     $x .= $string;
322     $string = $x;
323     $string =~ s/(\A$REG{WSP}+|$REG{WSP}+\Z)//sog;
324     $string =~ s/\s+\n/\n/sog;
325     }
326     $string;
327     }
328    
329     =head1 EXAMPLE
330    
331     ## Print field list
332    
333     use Message::Header;
334     my $header = Message::Header->parse ($header);
335    
336 wakaba 1.2 ## Next sample is better.
337     #for my $field (@$header) {
338     # print $field->{name}, "\t=> ", $field->{body}, "\n";
339     #}
340    
341     for my $i (0..$#$header) {
342     print $header->field_name ($i), "\t=> ", $header->field_body ($i), "\n";
343 wakaba 1.1 }
344    
345    
346     ## Make simple header
347    
348 wakaba 1.2 use Message::Header;
349 wakaba 1.1 use Message::Field::Address;
350     my $header = new Message::Header;
351    
352     my $from = new Message::Field::Address;
353     $from->add ('foo@foo.example', name => 'F. Foo');
354     my $to = new Message::Field::Address;
355     $to->add ('bar@bar.example', name => 'Mr. Bar');
356     $to->add ('hoge@foo.example', name => 'Hoge-san');
357     $header->add ('From' => $from);
358     $header->add ('To' => $to);
359     $header->add ('Subject' => 'Re: Meeting');
360     $header->add ('References' => '<hoge.msgid%foo@foo.example>');
361     print $header;
362    
363     =head1 LICENSE
364    
365     Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
366    
367     This program is free software; you can redistribute it and/or modify
368     it under the terms of the GNU General Public License as published by
369     the Free Software Foundation; either version 2 of the License, or
370     (at your option) any later version.
371    
372     This program is distributed in the hope that it will be useful,
373     but WITHOUT ANY WARRANTY; without even the implied warranty of
374     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
375     GNU General Public License for more details.
376    
377     You should have received a copy of the GNU General Public License
378     along with this program; see the file COPYING. If not, write to
379     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
380     Boston, MA 02111-1307, USA.
381    
382     =head1 CHANGE
383    
384     See F<ChangeLog>.
385    
386     =cut
387    
388     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24