/[suikacvs]/messaging/manakai/lib/Message/Field/Address.pm
Suika

Contents of /messaging/manakai/lib/Message/Field/Address.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.7 - (hide annotations) (download)
Tue May 14 13:42:40 2002 UTC (22 years, 11 months ago) by wakaba
Branch: MAIN
Changes since 1.6: +9 -1 lines
2002-05-15  wakaba <w@suika.fam.cx>

	* Addresses.pm, Mailbox.pm, Domain.pm
	(son-of-Address.pm's): New modules.
	* Structured.pm:
	- (method_available): New method.
	- (clone): Checks _MEMBERS option.
	- (comment_add, comment_count, comment_delete, comment_item):
	New methods.
	- (item): Implemented.
	- (_delete_empty): Commentout default action.
	- (add, replace): Fix bug (parse option didn't work).
	* MsgID.pm: Don't use non-(ALPHA / DIGIT) as the first
	character of id-left.
	* Date.pm: Understands month name "Sept".

1 wakaba 1.1
2     =head1 NAME
3    
4     Message::Field::Address Perl module
5    
6     =head1 DESCRIPTION
7    
8     Perl module for RFC 822/2822 address related C<field>s.
9    
10     =cut
11    
12     package Message::Field::Address;
13     require 5.6.0;
14     use strict;
15     use re 'eval';
16 wakaba 1.4 use vars qw(%DEFAULT %REG $VERSION);
17 wakaba 1.1 $VERSION = '1.00';
18 wakaba 1.4 use Message::Util;
19 wakaba 1.2 use overload '@{}' => sub {shift->{address}},
20     '""' => sub {shift->stringify};
21 wakaba 1.1
22     $REG{comment} = qr/\x28(?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x27\x2A-\x5B\x5D-\xFF]+|(??{$REG{comment}}))*\x29/;
23     $REG{quoted_string} = qr/\x22(?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\xFF])*\x22/;
24     $REG{domain_literal} = qr/\x5B(?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x5A\x5E-\xFF])*\x5D/;
25    
26     $REG{WSP} = qr/[\x20\x09]+/;
27     $REG{FWS} = qr/[\x20\x09]*/;
28     $REG{atext} = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2F\x30-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]+/;
29     $REG{dot_atom} = qr/$REG{atext}(?:$REG{FWS}\x2E$REG{FWS}$REG{atext})*/;
30     $REG{dot_word} = qr/(?:$REG{atext}|$REG{quoted_string})(?:$REG{FWS}\x2E$REG{FWS}(?:$REG{atext}|$REG{quoted_string}))*/;
31     $REG{phrase} = qr/(?:$REG{atext}|$REG{quoted_string})(?:$REG{atext}|$REG{quoted_string}|\.|$REG{FWS})*/;
32     $REG{obs_route} = qr/(?:\x40$REG{FWS}(?:$REG{dot_word}|$REG{domain_literal})(?:$REG{FWS},?$REG{FWS}\x40$REG{FWS}(?:$REG{dot_word}|$REG{domain_literal}))*):/;
33     $REG{addr_spec} = qr/$REG{dot_word}$REG{FWS}\x40$REG{FWS}(?:$REG{dot_atom}|$REG{domain_literal})/;
34 wakaba 1.5 $REG{mailbox} = qr/(?:(?:$REG{phrase})?<$REG{FWS}(?:(?:$REG{obs_route})?$REG{FWS}$REG{addr_spec}$REG{FWS})?>|$REG{addr_spec}|$REG{atext})/;
35 wakaba 1.1 $REG{mailbox_list} = qr/$REG{mailbox}(?:$REG{FWS},(?:$REG{FWS}$REG{mailbox})?)*/;
36 wakaba 1.5 $REG{address} = qr/(?:(?:$REG{phrase})?(?:<$REG{FWS}(?:(?:$REG{obs_route})?$REG{FWS}$REG{addr_spec}$REG{FWS})?>|:$REG{FWS}(?:$REG{mailbox_list}$REG{FWS})?;)|$REG{addr_spec}|$REG{atext})/;
37 wakaba 1.1 $REG{address_list} = qr/$REG{address}(?:$REG{FWS},(?:$REG{FWS}$REG{address})?)*/;
38     $REG{M_group} = qr/($REG{phrase}):/;
39     $REG{M_mailbox} = qr/(?:($REG{phrase})?<$REG{FWS}($REG{obs_route})?$REG{FWS}($REG{addr_spec})$REG{FWS}>|($REG{addr_spec}))/;
40 wakaba 1.5 $REG{M_mailbox_empty} = qr/(?:($REG{phrase})?<$REG{FWS}>)/;
41 wakaba 1.1 $REG{M_quoted_string} = qr/\x22((?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\xFF])*)\x22/;
42    
43     $REG{NON_atom} = qr/[^\x09\x20\x21\x23-\x27\x2A\x2B\x2D\x2F\x30-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]/;
44    
45 wakaba 1.4 %DEFAULT = (
46 wakaba 1.5 dont_reply => -1, ## See C<$self->dont_reply>
47     dont_reply_display_name => '',
48 wakaba 1.4 encoding_after_encode => '*default',
49     encoding_before_decode => '*default',
50 wakaba 1.6 format => 'rfc2822',
51 wakaba 1.4 hook_encode_string => #sub {shift; (value => shift, @_)},
52     \&Message::Util::encode_header_string,
53     hook_decode_string => #sub {shift; (value => shift, @_)},
54     \&Message::Util::decode_header_string,
55 wakaba 1.5 is_mailbox => -1, ## Allow multiple mail addresses?
56 wakaba 1.3 is_return_path => -1,
57     use_display_name => 1,
58 wakaba 1.5 use_dont_reply => -1, ## See C<$self->dont_reply>
59 wakaba 1.3 use_group => 1,
60 wakaba 1.5 use_keyword => -1, ## See C<$self->keyword>
61     use_keyword_multiple => -1,
62 wakaba 1.3 );
63 wakaba 1.5 ## RFC 2822 C<mailbox> => is_mailbox = +1, (use_group = -1)
64     ## RFC 2822 C<mailbox-list> => is_mailbox = -1, use_group = -1
65     ## RFC 2822 C<address-list> => is_mailbox = -1, use_group = +1
66 wakaba 1.3
67 wakaba 1.5 ## Initialization of option value by C<field-name> and
68     ## version of specification(s).
69 wakaba 1.3 sub _init_option ($$) {
70     my $self = shift;
71 wakaba 1.5 my $name = shift;
72     my $spec = $self->{option}->{format};
73 wakaba 1.6 my %is_mailbox_name = ( ## C<is_mailbox>
74     'complaints-to' => 1,
75     'mail-copies-to' => 1, 'resent-sender' => 1,
76     'return-path' => 1, sender => 1,
77     'x-complaints-to' => 1,
78     );
79     my %use_group_name = ( ## C<use_group>
80     'approved' => -1,
81     'from' => -1, 'resent-from' => -1,
82     );
83     if ($is_mailbox_name{$name}) {
84     $self->{option}->{is_mailbox} = $is_mailbox_name{$name};
85     } elsif ($use_group_name{$name}) {
86     $self->{option}->{use_group} = $use_group_name{$name};
87 wakaba 1.5 }
88     if ($spec eq 'usefor') {
89     if ($name eq 'reply-to') {
90     $self->{option}->{use_group} = -1;
91     $self->{option}->{use_dont_reply} = 1;
92     }
93     }
94     if ($name eq 'mail-copies-to') {
95     $self->{option}->{use_keyword} = 1;
96     }
97     if ($name eq 'return-path') {
98 wakaba 1.3 $self->{option}->{is_return_path} = 1;
99     $self->{option}->{use_display_name} = -1;
100     }
101     $self;
102     }
103    
104 wakaba 1.2 =head2 Message::Field::Address->new ()
105 wakaba 1.1
106     Return empty address object.
107    
108     =cut
109    
110 wakaba 1.3 sub new ($;%) {
111     my $self = bless {type => '_ROOT'}, shift;
112     my %option = @_;
113 wakaba 1.4 for (%DEFAULT) {$option{$_} ||= $DEFAULT{$_}}
114 wakaba 1.3 $self->{option} = \%option;
115     $self->_init_option ($self->{option}->{field_name});
116     $self;
117 wakaba 1.1 }
118    
119 wakaba 1.2 =head2 Message::Field::Address->parse ($unfolded_field_body)
120 wakaba 1.1
121     Parse structured C<field-body> contain of C<address-list>.
122    
123     =cut
124    
125 wakaba 1.3 sub parse ($$;%) {
126 wakaba 1.1 my $self = bless {}, shift;
127     my $field_body = shift;
128 wakaba 1.3 my %option = @_;
129 wakaba 1.4 for (%DEFAULT) {$option{$_} ||= $DEFAULT{$_}}
130 wakaba 1.3 $self->{option} = \%option;
131     $self->_init_option ($self->{option}->{field_name});
132 wakaba 1.1 $field_body = $self->delete_comment ($field_body);
133     my %addr = $self->parse_address_list ($field_body);
134     $self->{address} = $addr{address};
135     $self->{type} = $addr{type};
136     $self;
137     }
138    
139     =head2 $self->address ()
140    
141     Return address list in the format described in
142     L<$self-E<gt>parse_address_list ()>.
143    
144     =cut
145    
146     sub address ($) {@{shift->{address}}}
147    
148 wakaba 1.4 =head2 $self->addr_spec ([$index])
149    
150     Returns (C<$index>'th or all) C<addr-spec>.
151 wakaba 1.5 (First C<addr-spec>'s C<$index> is C<1>, not C<0>.)
152 wakaba 1.4
153     =cut
154    
155     sub addr_spec ($;$) {
156     my $self = shift;
157     my $i = shift;
158 wakaba 1.5 #return $self->{address}->[$i]->{addr_spec}
159     # if defined $i && ref $self->{address}->[$i];
160     #map {$_->{addr_spec}} @{$self->{address}};
161     my @r = (); my $j = 0;
162     for my $m (@{$self->{address}}) {
163     if ($m->{type} eq 'group') {
164     for my $n (@{$m->{address}}) {
165     $j++;
166     return $n->{addr_spec} if $j == $i;
167     push @r, $m->{addr_spec};
168     }
169     } else {
170     $j++;
171     return $m->{addr_spec} if $j == $i;
172     push @r, $m->{addr_spec};
173     }
174     }
175     @r;
176 wakaba 1.4 }
177    
178 wakaba 1.7 sub have_group ($) {
179     my $self = shift;
180     for (@{$self->{address}}) {
181     return 1 if $_->{type} eq 'group';
182     }
183     0;
184     }
185    
186 wakaba 1.1 =head2 $self->add ($addr_spec, [%option])
187    
188     Add an mail address to C<$self> (address object).
189     %option = (name => C<display-name>, route => C<route>,
190     group => C<display-name> of C<group>)
191    
192     Note that this method (and other methods) does not check
193     $addr_spec and $option{route} is valid or not.
194    
195     =cut
196    
197     sub add ($$;%) {
198     my $self = shift;
199     my ($addr, %option) = @_;
200     my $name = $option{name} || $option{display_name};
201     unless ($option{group}) {
202     push @{$self->{address}}, {type => 'mailbox',
203     addr_spec => $addr, display_name => $name, route => $option{route}};
204     } else {
205     for my $i (@{$self->{address}}) {
206     if ($i->{type} eq 'group' && $i->{display_name} eq $option{group}) {
207     push @{$i->{address}}, {type => 'mailbox',
208     addr_spec => $addr, display_name => $name, route => $option{route}};
209     return $self;
210     }
211     }
212     push @{$self->{address}}, {type => 'group', display_name => $option{group},
213     address => [
214     {type => 'mailbox',
215     addr_spec => $addr, display_name => $name, route => $option{route}}
216     ]};
217     }
218     $self;
219     }
220    
221 wakaba 1.3 sub stringify ($;%) {
222 wakaba 1.1 my $self = shift;
223 wakaba 1.3 my %option = @_;
224     $option{is_mailbox} ||= $self->{option}->{is_mailbox};
225     $option{is_return_path} ||= $self->{option}->{is_return_path};
226     $option{use_display_name} ||= $self->{option}->{use_display_name};
227     $option{use_group} ||= $self->{option}->{use_group};
228 wakaba 1.5 $option{use_keyword} ||= $self->{option}->{use_keyword};
229     $option{use_keyword_multiple} ||= $self->{option}->{use_keyword_multiple};
230     $option{use_dont_reply} ||= $self->{option}->{use_dont_reply};
231     $option{dont_reply} ||= $self->{option}->{dont_reply};
232     $option{dont_reply_display_name} = $self->{option}->{dont_reply_display_name}
233     unless defined $option{dont_reply_display_name};
234     if ($option{use_dont_reply}>0 && $option{dont_reply}>0) {
235     ## usefor-article Reply-To:
236     my $g_return = '';
237     if ($option{dont_reply_display_name} && $option{use_display_name}>0) {
238     my %s = &{$self->{option}->{hook_encode_string}} ($self,
239     $option{dont_reply_display_name}, type => 'phrase');
240     $g_return = $self->quote_unsafe_string ($s{value}) .' ';
241     }
242     $g_return .= '<>';
243     return $g_return;
244     }
245 wakaba 1.1 my @return;
246 wakaba 1.5 if ($option{use_keyword}>0) {
247     my @kw = grep {$self->{keyword}->{$_}>0} keys %{$self->{keyword}};
248     return $kw[0] if $kw[0] && $option{use_keyword_multiple}<0;
249     push @return, @kw;
250     }
251 wakaba 1.1 for my $address (@{$self->{address}}) {
252     my $return = '';
253     next if !$address->{addr_spec} && $address->{type} ne 'group';
254 wakaba 1.3 if ($address->{display_name} && $option{use_display_name}>0) {
255 wakaba 1.5 if ($address->{type} eq 'group' && $option{use_group}>0) {
256     my %s = &{$self->{option}->{hook_encode_string}} ($self,
257     $address->{display_name}, type => 'phrase');
258     $return = $self->quote_unsafe_string ($s{value}).': ';
259     #} else {
260     # my %s = &{$self->{option}->{hook_encode_string}} ($self,
261     # $address->{display_name}, type => 'comment');
262     # $s{value} =~ s/[\x28\x29\x5C]/\\$&/g;
263     # $return = '('.$s{value}.') ';
264     } elsif ($address->{type} ne 'group') {
265 wakaba 1.4 my %s = &{$self->{option}->{hook_encode_string}} ($self,
266     $address->{display_name}, type => 'phrase');
267 wakaba 1.5 $return = $self->quote_unsafe_string ($s{value}).' ';
268     }
269 wakaba 1.1 }
270     if ($address->{type} ne 'group') {
271     $return .= '<'.$address->{route}.$address->{addr_spec}.'>';
272     } else {
273     my (@g_return);
274     for my $mailbox (@{$address->{address}}) {
275     next unless $mailbox->{addr_spec};
276     my $g_return = '';
277 wakaba 1.4 if ($mailbox->{display_name} && $option{use_display_name}>0) {
278     my %s = &{$self->{option}->{hook_encode_string}} ($self,
279     $mailbox->{display_name}, type => 'phrase');
280     $g_return = $self->quote_unsafe_string ($s{value}) .' ';
281     }
282 wakaba 1.1 $g_return .= '<'.$mailbox->{route}.$mailbox->{addr_spec}.'>';
283     push @g_return, $g_return;
284 wakaba 1.3 last if $option{is_mailbox}>0;
285 wakaba 1.1 }
286     $return .= join ', ', @g_return;
287 wakaba 1.3 $return .= ';' if $address->{type} eq 'group' && $option{use_group}>0;
288 wakaba 1.1 }
289     push @return, $return;
290 wakaba 1.3 last if $option{is_mailbox}>0;
291     }
292 wakaba 1.5 if ($#return == -1) {
293     if ($option{is_return_path}>0) {
294     push @return, '<>';
295     #} elsif ($option{use_dont_reply}>0) { ## usefor-article Reply-To:
296     # my $g_return = '';
297     # if ($option{dont_reply_display_name} && $option{use_display_name}>0) {
298     # my %s = &{$self->{option}->{hook_encode_string}} ($self,
299     # $option{dont_reply_display_name}, type => 'phrase');
300     # $g_return = $self->quote_unsafe_string ($s{value}) .' ';
301     # }
302     # $g_return .= '<>';
303     # push @return, $g_return;
304     }
305 wakaba 1.1 }
306     join ', ', @return;
307     }
308    
309 wakaba 1.5 =head2 $self->dont_reply ([$don't_reply?, [$display_name]])
310    
311     Set/gets whether C<field-body> content is "don't reply!"
312     or not. Second argument is short string used as C<display-name>.
313    
314     This function returns an array of C<($don't_reply?, $display_name)>.
315     <$don't_reply> takes value C<1> (true) or C<-1> (false).
316    
317     Note: This don't reply convention is defined by draft-usefor-article.
318     You should not use this in RFC 2822 mail message.
319    
320     Enable this, set C<use_dont_reply> option to C<1>. (Default is C<-1>.
321    
322     Note: Set this value does not mean clear address list that
323     current C<Message::Field::Address> has. You can get it
324     as well as before setting new value. But C<stringify> method
325     does not output any of addresses if <$don't_reply> is C<1>.
326    
327     =head3 example
328    
329     my $a = Message::Field::Address->new (use_reply => 1);
330     $a->dont_reply (1, 'Please do not reply');
331     print $a; # "Please do not reply <>"
332    
333     =cut
334    
335     sub dont_reply ($;$$) {
336     my $self = shift;
337     my $dr = shift;
338     my $dname = shift;
339     $self->{option}->{dont_reply} = $dr if $dr;
340     $self->{option}->{dont_reply_display_name} = $dname if defined $dname;
341     ($self->{dont_reply}, $self->{dont_reply_display_name});
342     }
343    
344     =head2 $self->keyword ($keyword, [$true_of_false])
345    
346     Set/gets whether keyword is specified. C<$true_or_false>
347     takes values C<1> and C<-1>.
348    
349     This function is intended to be used for keyword(s) allowed
350     mail addresses field, such as C<Mail-Copies-To:> defined by
351     draft-usefor-article.
352    
353     There are two related options, C<use_keyword> and C<use_keyword_multiple>.
354     Former is on/off switch of this function. Later allows
355     multiple options/addresses, such as C<keyworda, E<lt>foo@bar.exampleE<gt>>.
356    
357     =cut
358    
359     sub keyword ($$;$) {
360     my $self = shift;
361     my $key = shift;
362     my $tf = shift;
363     $self->{keyword}->{$key} = $tf if $tf;
364     $self->{keyword}->{$key} || -1;
365     }
366    
367 wakaba 1.1 sub quote_unsafe_string ($$) {
368     my $self = shift;
369     my $string = shift;
370     if ($string =~ /$REG{NON_atom}/ || $string =~ /$REG{WSP}$REG{WSP}+/) {
371     $string =~ s/([\x22\x5C])/\x5C$1/g;
372     $string = '"'.$string.'"';
373     }
374     $string;
375     }
376    
377     =head2 $self->unquote_quoted_string ($string)
378    
379     Unquote C<quoted-string>. Get rid of C<DQUOTE>s and
380     C<REVERSED SOLIDUS> included in C<quoted-pair>.
381     This method is intended for internal use.
382    
383     =cut
384    
385     sub unquote_quoted_string ($$) {
386     my $self = shift;
387     my $quoted_string = shift;
388     $quoted_string =~ s{$REG{M_quoted_string}}{
389     my $qtext = $1;
390     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
391     $qtext;
392     }goex;
393     $quoted_string;
394     }
395    
396 wakaba 1.4 sub _decode_quoted_string ($$) {
397     my $self = shift;
398     my $quoted_string = shift;
399     $quoted_string =~ s{$REG{M_quoted_string}|([^\x22]+)}{
400     my ($qtext,$t) = ($1, $2);
401     if ($t) {
402     my %s = &{$self->{option}->{hook_decode_string}} ($self, $t,
403     type => 'value');
404     $s{value};
405     } else {
406     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
407     my %s = &{$self->{option}->{hook_decode_string}} ($self, $qtext,
408     type => 'value/quoted');
409     $s{value};
410     }
411     }goex;
412     $quoted_string;
413     }
414    
415 wakaba 1.1 =head2 $self->parse_mailbox ($mailbox)
416    
417     Parse C<mailbox> and return array of C<addr-spec>,
418     C<display-name> and C<route> (aka C<obs-route> of RFC 2822).
419     This method is intended for internal use.
420    
421     =cut
422    
423     sub parse_mailbox ($$) {
424     my $self = shift;
425     my $mailbox = shift;
426     if ($mailbox =~ /$REG{M_mailbox}/) {
427     my ($display_name, $route, $addr_spec) = ($1, $2, $3 || $4);
428     $display_name =~ s/$REG{WSP}+$//;
429 wakaba 1.4 $display_name = $self->_decode_quoted_string ($display_name);
430 wakaba 1.1 $addr_spec =~ s{($REG{quoted_string}|$REG{domain_literal})|$REG{WSP}}{$1}go;
431     $route =~ s{($REG{quoted_string}|$REG{domain_literal})|$REG{WSP}}{$1}go;
432     return ($addr_spec, $display_name, $route);
433 wakaba 1.5 } elsif ($mailbox =~ /$REG{M_mailbox_empty}/) {
434     my ($display_name) = ($1);
435     $display_name =~ s/$REG{WSP}+$//;
436     $display_name = $self->_decode_quoted_string ($display_name);
437     return ('', $display_name, 'dummy');
438     } elsif ($mailbox =~ /^$REG{atext}$/) {
439     $self->{keyword}->{$mailbox} = 1;
440 wakaba 1.1 }
441 wakaba 1.5 (undef, undef, undef);
442 wakaba 1.1 }
443    
444     =head2 $self->parse_address_list ($address_list)
445    
446     Parse C<address-list> and return hash.
447     This method is intended for internal use.
448    
449     =head3 Structure of hash returned by parse_address_list
450    
451     %address = (
452    
453     type => '_ROOT',
454     address => [
455    
456     ## mailbox
457     {
458     type => 'mailbox',
459     display_name => 'Foo H. Bar',
460     addr_spec => 'foo@bar.example',
461     route => '@hoge.example:',
462     },
463    
464     ## group
465     {
466     type => 'group',
467     display_name => 'The committee',
468     address => [
469    
470     ## mailbox
471     {
472     type => 'mailbox',
473     display_name => 'Tom (Director)',
474     addr_spec => 'tom@committee.example',
475     route => '',
476     }
477    
478     ],
479     },
480    
481     ],
482    
483     );
484    
485     =cut
486    
487     sub parse_address_list ($$) {
488     my $self = shift;
489     my $address_list = shift;
490     my %r_addr = (type => '_ROOT');
491     $address_list =~ s{($REG{address})}{
492     my $address = $1;
493     if ($address =~ /^$REG{M_group}/) {
494     my %r_group = (type => 'group', display_name => $1);
495     $r_group{display_name} =~ s/$REG{WSP}+$//;
496     $r_group{display_name} = $self->unquote_quoted_string ($r_group{display_name});
497     $address =~ s{($REG{mailbox})}{
498     my ($addr, $name, $route) = $self->parse_mailbox ($1);
499     push @{$r_group{address}}, {type => 'mailbox',
500 wakaba 1.5 display_name => $name, route => $route, addr_spec => $addr}
501     if $addr;
502 wakaba 1.1 }goex;
503     push @{$r_addr{address}}, \%r_group;
504     } else {
505     my ($addr, $name, $route) = $self->parse_mailbox ($address);
506 wakaba 1.5 if ($addr) {
507     push @{$r_addr{address}}, {type => 'mailbox',
508     display_name => $name, route => $route, addr_spec => $addr};
509     } elsif ($route) { # dummy
510     $self->{option}->{dont_reply} = 1;
511     $self->{option}->{dont_reply_display_name} = $name;
512     }
513 wakaba 1.1 }
514     }goex;
515     %r_addr;
516     }
517    
518     =head2 $self->delete_comment ($field_body)
519    
520     Remove all C<comment> in given strictured C<field-body>.
521     This method is intended for internal use.
522    
523     =cut
524    
525     sub delete_comment ($$) {
526     my $self = shift;
527     my $body = shift;
528     $body =~ s{($REG{quoted_string}|$REG{domain_literal})|$REG{comment}}{
529     my $o = $1; $o? $o : ' ';
530     }gex;
531     $body;
532     }
533    
534     =head1 EXAMPLE
535    
536     ## Compose field-body for To: field.
537    
538 wakaba 1.2 use Message::Field::Address;
539 wakaba 1.1 my $addr = new Message::Field::Address;
540     $addr->add ('foo@example.org', name => 'Mr. foo bar');
541     $addr->add ('webmaster@example.org', group => 'administrators');
542     $addr->add ('postmaster@example.org', group => 'administrators');
543    
544     my $field_body = $addr->stringify ();
545    
546    
547     ## Output parsed address-list tree.
548    
549     use Message::Field::Address;
550     my $addr = Message::Field::Address->parse ($field_body);
551    
552     for my $i (@$addr) {
553     if ($i->{type} eq 'group') {
554     print "\x40 $i->{display_name}: \n";
555     for my $j (@{$i->{address}}) {
556     print "\t- $j->{display_name} <$j->{route}$j->{addr_spec}>\n";
557     }
558     } else {
559     print "- $i->{display_name} <$i->{route}$i->{addr_spec}>\n";
560     }
561     }
562    
563     =head1 LICENSE
564    
565     Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
566    
567     This program is free software; you can redistribute it and/or modify
568     it under the terms of the GNU General Public License as published by
569     the Free Software Foundation; either version 2 of the License, or
570     (at your option) any later version.
571    
572     This program is distributed in the hope that it will be useful,
573     but WITHOUT ANY WARRANTY; without even the implied warranty of
574     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
575     GNU General Public License for more details.
576    
577     You should have received a copy of the GNU General Public License
578     along with this program; see the file COPYING. If not, write to
579     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
580     Boston, MA 02111-1307, USA.
581    
582     =head1 CHANGE
583    
584     See F<ChangeLog>.
585 wakaba 1.7 $Date: 2002/04/05 14:55:28 $
586 wakaba 1.1
587     =cut
588    
589     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24