/[suikacvs]/test/cvs
Suika

Contents of /test/cvs

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (show annotations) (download)
Wed Mar 13 14:47:07 2002 UTC (22 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.2: +3 -1 lines
2002-03-13  wakaba <w@suika.fam.cx>

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

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 =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 =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 =head2 $self->count ([$field_name])
217
218 Returns the number of times the given C<field> appears.
219 If no $field_name is given, returns the number
220 of fields. (Same as $#$self+1)
221
222 =cut
223
224 sub count ($;$) {
225 my $self = shift;
226 my ($name) = (lc shift);
227 unless ($name) {
228 $self->_delete_empty_field ();
229 return $#{$self->{field}}+1;
230 }
231 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 my $ret = join ("\n", @ret);
261 $ret? $ret."\n": "";
262 }
263
264 =head2 $self->get_option ($option_name)
265
266 Returns value of the option.
267
268 =head2 $self->set_option ($option_name, $option_value)
269
270 Set new value of the option.
271
272 =cut
273
274 sub get_option ($$) {
275 my $self = shift;
276 my ($name) = @_;
277 $self->{option}->{$name};
278 }
279 sub set_option ($$$) {
280 my $self = shift;
281 my ($name, $value) = @_;
282 $self->{option}->{$name} = $value;
283 $self;
284 }
285
286 sub _delete_empty_field ($) {
287 my $self = shift;
288 my @ret;
289 for my $field (@{$self->{field}}) {
290 push @ret, $field if $field->{name};
291 }
292 $self->{field} = \@ret;
293 $self;
294 }
295
296 sub fold ($$;$) {
297 my $self = shift;
298 my $string = shift;
299 my $len = shift || $self->{option}->{fold_length};
300 $len = 60 if $len < 60;
301
302 ## This code is taken from Mail::Header 1.43 in MailTools,
303 ## by Graham Barr (Maintained by Mark Overmeer <mailtools@overmeer.net>).
304 my $max = int($len - 5); # 4 for leading spcs + 1 for [\,\;]
305 my $min = int($len * 4 / 5) - 4;
306 my $ml = $len;
307
308 if (length($string) > $ml) {
309 #Split the line up
310 # first bias towards splitting at a , or a ; >4/5 along the line
311 # next split a whitespace
312 # else we are looking at a single word and probably don't want to split
313 my $x = "";
314 $x .= "$1\n "
315 while($string =~ s/^$REG{WSP}*(
316 [^"]{$min,$max}?[\,\;]
317 |[^"]{1,$max}$REG{WSP}
318 |[^\s"]*(?:"[^"]*"[^\s"]*)+$REG{WSP}
319 |[^\s"]+$REG{WSP}
320 )
321 //x);
322 $x .= $string;
323 $string = $x;
324 $string =~ s/(\A$REG{WSP}+|$REG{WSP}+\Z)//sog;
325 $string =~ s/\s+\n/\n/sog;
326 }
327 $string;
328 }
329
330 =head1 EXAMPLE
331
332 ## Print field list
333
334 use Message::Header;
335 my $header = Message::Header->parse ($header);
336
337 ## Next sample is better.
338 #for my $field (@$header) {
339 # print $field->{name}, "\t=> ", $field->{body}, "\n";
340 #}
341
342 for my $i (0..$#$header) {
343 print $header->field_name ($i), "\t=> ", $header->field_body ($i), "\n";
344 }
345
346
347 ## Make simple header
348
349 use Message::Header;
350 use Message::Field::Address;
351 my $header = new Message::Header;
352
353 my $from = new Message::Field::Address;
354 $from->add ('foo@foo.example', name => 'F. Foo');
355 my $to = new Message::Field::Address;
356 $to->add ('bar@bar.example', name => 'Mr. Bar');
357 $to->add ('hoge@foo.example', name => 'Hoge-san');
358 $header->add ('From' => $from);
359 $header->add ('To' => $to);
360 $header->add ('Subject' => 'Re: Meeting');
361 $header->add ('References' => '<hoge.msgid%foo@foo.example>');
362 print $header;
363
364 =head1 LICENSE
365
366 Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
367
368 This program is free software; you can redistribute it and/or modify
369 it under the terms of the GNU General Public License as published by
370 the Free Software Foundation; either version 2 of the License, or
371 (at your option) any later version.
372
373 This program is distributed in the hope that it will be useful,
374 but WITHOUT ANY WARRANTY; without even the implied warranty of
375 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
376 GNU General Public License for more details.
377
378 You should have received a copy of the GNU General Public License
379 along with this program; see the file COPYING. If not, write to
380 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
381 Boston, MA 02111-1307, USA.
382
383 =head1 CHANGE
384
385 See F<ChangeLog>.
386 $Date: 2002/03/13 14:47:07 $
387
388 =cut
389
390 1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24