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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (hide annotations) (download)
Wed May 15 07:29:09 2002 UTC (22 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.2: +17 -5 lines
2002-05-15  wakaba <w@suika.fam.cx>

	* Address.pm: Removed.
	* Domain.pm: Bug fix (doesn't treat four-sub-domain-
	domain as (invalid) IPv4 address).
	* Mailbox.pm (addr_spec): New method.
	* Addresses.pm (addr_spec): Likewise.
	
	* Structured.pm (option): Doesn't treat ( "-" option-name )
	as an alias of option-name.  (A ( "-" option-name ) should
	be recognized as an option for 'option' method.)

1 wakaba 1.1
2     =head1 NAME
3    
4 wakaba 1.2 Message::Field::Addresses --- Perl module for comma separated
5     Internet mail address list
6 wakaba 1.1
7     =cut
8    
9     package Message::Field::Addresses;
10     require 5.6.0;
11     use strict;
12     use re 'eval';
13 wakaba 1.2 use vars qw(%DEFAULT @ISA %REG $VERSION);
14 wakaba 1.3 $VERSION=do{my @r=(q$Revision: 1.2 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
15 wakaba 1.1 require Message::Field::CSV;
16     push @ISA, qw(Message::Field::CSV);
17 wakaba 1.2 %REG = %Message::Field::CSV::REG;
18 wakaba 1.1 $REG{SC_angle_addr} = qr/<(?:$REG{quoted_string}|$REG{domain_literal}|$REG{comment}|[^\x22\x28\x5B\x3E])+>|<>/;
19 wakaba 1.3 $REG{SC_group} = qr/:(?:$REG{comment}|$REG{quoted_string}|(??{$REG{SC_group}})|$REG{domain_literal}|$REG{SC_angle_addr}|[^\x22\x28\x5B\x3A\x3E\x3B])*;/;
20 wakaba 1.1
21     =head1 CONSTRUCTORS
22    
23     The following methods construct new objects:
24    
25     =over 4
26    
27     =cut
28    
29     ## Initialize of this class -- called by constructors
30 wakaba 1.2 %DEFAULT = (
31     -_METHODS => [qw|add count delete item display_name is_group value_type scan
32     comment_add comment_count comment_delete comment_item|],
33     -_MEMBERS => [qw|group_name group_name_comment|],
34 wakaba 1.1 -by => 'display-name', ## Default key for item, delete,...
35     -can_have_group => 1,
36     #encoding_after_encode
37     #encoding_before_decode
38     #field_name
39     #field_param_name
40     #format
41     #hook_encode_string
42     #hook_decode_string
43     -is_group => 0,
44 wakaba 1.2 #max
45 wakaba 1.1 -output_comment => 1,
46     -output_group_name_comment => 1,
47     #parse_all
48     -remove_comment => 0, ## This option works for PARENT class
49     #value_type
50     );
51 wakaba 1.2 sub _init ($;%) {
52     my $self = shift;
53     my %options = @_;
54     my $DEFAULT = Message::Util::make_clone (\%DEFAULT);
55     $self->SUPER::_init (%$DEFAULT, %options);
56     my (%mailbox, %group);
57 wakaba 1.1
58 wakaba 1.2 $self->{option}->{can_have_group} = 0
59     if $self->{option}->{field_param_name} eq 'group';
60    
61     my $field = $self->{option}->{field_name};
62     my $format = $self->{option}->{format};
63     ## rfc1036 = RFC 1036 + son-of-RFC1036
64     if ($field eq 'from' || $field eq 'resent-from') {
65     $self->{option}->{can_have_group} = 0;
66     $self->{option}->{max} = 1 if $format =~ /rfc1036|http/;
67     } elsif ($field eq 'mail-copies-to') {
68     $mailbox{-use_keyword} = 1;
69     } elsif ($field eq 'reply-to') {
70     $self->{option}->{can_have_group} = 0;
71     $self->{option}->{max} = 1 if $format =~ /rfc1036/;
72     } elsif ($field eq 'approved' || $field eq 'x-approved') {
73     $self->{option}->{can_have_group} = 0;
74     $self->{option}->{max} = 1 if $format =~ /news-rfc1036/;
75     }
76    
77     $self->{option}->{value_type}->{mailbox} = ['Message::Field::Mailbox',
78     {%mailbox}];
79     $self->{option}->{value_type}->{group} = ['Message::Field::Addresses',
80     {-is_group => 1, %group}];
81 wakaba 1.1 }
82    
83 wakaba 1.2 =item $addrs = Message::Field::Addresses->new ([%options])
84    
85     Constructs a new object. You might pass some options as parameters
86     to the constructor.
87    
88     =cut
89    
90     ## Inherited
91    
92     =item $addrs = Message::Field::Addresses->parse ($field-body, [%options])
93    
94     Constructs a new object with given field body. You might pass
95     some options as parameters to the constructor.
96    
97     =cut
98    
99     ## Inherited
100 wakaba 1.1
101     sub _parse_list ($$) {
102     my $self = shift;
103     my $fb = shift;
104     my @ids;
105     if ($self->{option}->{is_group}) {
106     $fb =~ s{^((?:$REG{quoted_string}|$REG{comment}|[^\x22\x28\x2C\x3A\x3C\x5B])+):}{
107     my ($gname, @gcomment) = Message::Util::delete_comment_to_array ($self, $1);
108     $self->{group_name} = Message::Util::decode_quoted_string ($self, $gname);
109     $self->{group_name_comment} = \@gcomment;
110     ''}gex;
111     $fb =~ s{;((?:$REG{comment}|$REG{WSP})*)$}{
112     my (undef, @gcomment) = Message::Util::delete_comment_to_array ($self, $1);
113     $self->{comment} = \@gcomment;
114     ''}gex;
115     }
116     $fb =~ s{(?:$REG{quoted_string}|$REG{comment}|[^\x22\x28\x2C\x3A\x3C\x5B]|$REG{SC_group}|$REG{SC_angle_addr}|$REG{domain_literal})+}{
117     my $s = $&; $s =~ s/^$REG{WSP}+//; $s =~ s/$REG{WSP}+$//;
118 wakaba 1.3 if ($s =~ /^(?:$REG{quoted_string}|$REG{comment}|[^\x22\x28\x2C\x3A-\x3C\x5B])*:/) {
119 wakaba 1.2 $s = $self->_parse_value (group => $s) if $self->{option}->{parse_all};
120     $s = {type => 'group', value => $s};
121 wakaba 1.1 } else { ## address or keyword
122 wakaba 1.2 $s = $self->_parse_value (mailbox => $s) if $self->{option}->{parse_all};
123     $s = {type => 'mailbox', value => $s};
124 wakaba 1.1 }
125     push @ids, $s;
126     }goex;
127     @ids;
128     }
129    
130     =back
131    
132     =head1 METHODS
133    
134     =over 4
135    
136 wakaba 1.2 =item $addrs->add ({-name => $value}, $addr1, $addr2, $addr3,...)
137    
138     Adds mail address(es).
139    
140     First argument is hash reference to name/value pairs
141     of options. This is optional.
142    
143     Following is list of additional items. Each item
144     can be given as array reference. An array reference
145     is interpreted as [$item-body, $item-option-name =>
146     $item-option-value, $name => $value,...].
147     Available item-options are:
148    
149     =over 2
150    
151     =item C<group>
152    
153     Group name which C<$item-body> belongs to. If there
154     is no such name of group, new group is created.
155    
156     =item C<type> = 'mailbox' / 'group' (default 'group')
157    
158     Format of C<$item-body>. If 'group' is specified,
159     <$item-body> is treated as RFC 2822 group. Otherwise,
160     it is added as a mailbox.
161    
162     =back
163    
164     =item $count = $addrs->count ([%options])
165    
166     Returns the number of items. A 'type' option is available.
167     For example, C<$addrs-E<gt>count (-type =E<gt> 'group')>
168     returns the number of groups.
169    
170     =item $addrs->delete ({%options}, $item-key, $key,...)
171    
172     Deletes items that are matched with (one of) given key.
173     C<{%options}> is optional.
174    
175     C<by> option is used to specify what sort of value given keys are.
176     C<display-name>, the default value, indicates
177     keys are display-name of items to be removed.
178    
179     For C<by> option, value C<index> is also available.
180    
181     C<type> option is also available. Its value is 'mailbox'
182     and 'group'. Default is both of them.
183    
184 wakaba 1.1 =cut
185    
186     ## add, count, delete: Inherited
187    
188     sub _add_array_check ($$\%) {
189     my $self = shift;
190     my ($value, $option) = @_;
191     my $value_option = {};
192     if (ref $value eq 'ARRAY') {
193     ($value, %$value_option) = @$value;
194     }
195     if (length $value_option->{group}) {
196 wakaba 1.2 my $g = $self->item ($value_option->{group}, -type => 'group');
197     delete $value_option->{group};
198     $g->add (Message::Util::make_clone ($option), [$value, %$value_option]);
199     (0);
200     } else {
201     my $type = $value_option->{type} || 'mailbox';
202     $value = $self->_parse_value ($type => $value) if $$option{parse};
203     $$option{parse} = 0;
204     (1, value => {type => $type, value => $value});
205 wakaba 1.1 }
206     }
207    
208     sub _delete_match ($$$\%\%) {
209     my $self = shift;
210     my ($by, $i, $list, $option) = @_;
211     return 0 unless ref $$i; ## Already removed
212     return 0 if $$option{type} && $$i->{type} ne $$option{type};
213     if ($by eq 'display-name') {
214 wakaba 1.2 $$i->{value} = $self->_parse_value ($$i->{type}, $$i->{value});
215     return 1 if ref $$i->{value} && $$list{$$i->{value}->display_name};
216 wakaba 1.1 }
217     0;
218     }
219     *_item_match = \&_delete_match;
220    
221     ## Returns returned item value \$item-value, \%option
222     sub _item_return_value ($\$\%) {
223     if (ref ${$_[1]}) {
224     ${$_[1]}->{value};
225     } else {
226 wakaba 1.2 ${$_[1]}->{value} = $_[0]->_parse_value (${$_[1]}->{type}, ${$_[1]}->{value});
227     ${$_[1]}->{value};
228 wakaba 1.1 }
229     }
230    
231 wakaba 1.2 ## Returns returned (new created) item value $name, \%option
232     sub _item_new_value ($$\%) {
233     my $type = $_[2]->{type} || 'mailbox';
234     my $v = $_[0]->_parse_value ($type, '');
235     $v->display_name ($_[1]) if ref $v && length $_[1] && $_[2]->{by} eq 'display-name';
236     {type => $type, value => $v};
237     }
238    
239 wakaba 1.1 sub is_group ($;$) {
240     if (defined $_[1]) {
241     $_[0]->{option}->{is_group} = $_[1];
242     }
243     $_[0]->{option}->{is_group};
244     }
245    
246 wakaba 1.2 sub have_group ($) {
247     my $self = shift;
248     for (@{$self->{$self->{option}->{_ARRAY_NAME}}}) {
249     return 1 if $_->{type} eq 'group';
250     }
251     0;
252     }
253    
254 wakaba 1.1 sub display_name ($;$) {
255     if (defined $_[1]) {
256     $_[0]->{group_name} = $_[1];
257     }
258     $_[0]->{group_name};
259     }
260    
261 wakaba 1.3 sub addr_spec ($;%) {
262     my $self = shift;
263     my @a;
264     for (@{$self->{$self->{option}->{_ARRAY_NAME}}}) {
265     $_->{value} = $self->_parse_value ($_->{value}) unless ref $_->{value};
266     if (ref $_->{value}) {
267     push @a, $_->{value}->addr_spec (@_);
268     } elsif (length $_->{value}) {
269     push @a, $_->{value};
270     }
271     }
272     wantarray? @a: $a[0];
273     }
274 wakaba 1.2
275 wakaba 1.1 ## stringify: Inherited
276     #*as_string = \&stringify;
277    
278     sub stringify ($;%) {
279     my $self = shift;
280     my %o = @_;
281     my %option = %{$self->{option}};
282     for (grep {/^-/} keys %o) {$option{substr ($_, 1)} = $o{$_}}
283     my $m = $self->SUPER::stringify (@_);
284     my $g = '';
285     if ($option{is_group}) {
286     my %s = &{$option{hook_encode_string}} ($self,
287     $self->{group_name}, type => 'phrase');
288     $g .= Message::Util::quote_unsafe_string
289     ($s{value}, unsafe => 'NON_atext_wsp');
290     }
291     if ($option{output_comment} && $option{output_group_name_comment}) {
292     if (!$option{is_group} && length $self->{group_name}) {
293     $g .= ' ('. $self->Message::Util::encode_ccontent ($self->{group_name}) .':)';
294     }
295     for (@{$self->{group_name_comment}}) {
296     $g .= ' ('. $self->Message::Util::encode_ccontent ($_) .')';
297     }
298     }
299     if ($option{is_group}) {
300     $m = $g . (length $m? ': ': ':') . $m . ';';
301     } else {
302 wakaba 1.2 $m = $g . (length $g? ' ': '') . $m;
303 wakaba 1.1 }
304     if ($option{output_comment} && !$option{output_group_name_comment}) {
305     for (@{$self->{group_name_comment}}) {
306     $m .= ' ('. $self->Message::Util::encode_ccontent ($_) .')';
307     }
308     }
309     if ($option{output_comment}) {
310     for (@{$self->{comment}}) {
311     $m .= ' ('. $self->Message::Util::encode_ccontent ($_) .')';
312     }
313     }
314     $m;
315     }
316     *as_string = \&stringify;
317     sub _stringify_item ($$\%) {
318     my $self = shift;
319     my $item = shift;
320     my $option = shift;
321     if (!$$option{can_have_group} && ref $item->{value}) {
322     $item->{value}->stringify (-is_group => 0);
323     } else {
324     $item->{value};
325     }
326     }
327    
328     ## option, value_type, clone, method_available: Inherited
329    
330     =head1 LICENSE
331    
332     Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
333    
334     This program is free software; you can redistribute it and/or modify
335     it under the terms of the GNU General Public License as published by
336     the Free Software Foundation; either version 2 of the License, or
337     (at your option) any later version.
338    
339     This program is distributed in the hope that it will be useful,
340     but WITHOUT ANY WARRANTY; without even the implied warranty of
341     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
342     GNU General Public License for more details.
343    
344     You should have received a copy of the GNU General Public License
345     along with this program; see the file COPYING. If not, write to
346     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
347     Boston, MA 02111-1307, USA.
348    
349     =head1 CHANGE
350    
351     See F<ChangeLog>.
352 wakaba 1.3 $Date: 2002/05/14 13:42:40 $
353 wakaba 1.1
354     =cut
355    
356     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24