/[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.2 - (hide annotations) (download)
Tue May 14 13:42:40 2002 UTC (22 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.1: +141 -29 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 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     $VERSION=do{my @r=(q$Revision: 1.1 $=~/\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     $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    
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     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.2 ##TODO: addr_spec
262    
263 wakaba 1.1 ## stringify: Inherited
264     #*as_string = \&stringify;
265    
266     sub stringify ($;%) {
267     my $self = shift;
268     my %o = @_;
269     my %option = %{$self->{option}};
270     for (grep {/^-/} keys %o) {$option{substr ($_, 1)} = $o{$_}}
271     my $m = $self->SUPER::stringify (@_);
272     my $g = '';
273     if ($option{is_group}) {
274     my %s = &{$option{hook_encode_string}} ($self,
275     $self->{group_name}, type => 'phrase');
276     $g .= Message::Util::quote_unsafe_string
277     ($s{value}, unsafe => 'NON_atext_wsp');
278     }
279     if ($option{output_comment} && $option{output_group_name_comment}) {
280     if (!$option{is_group} && length $self->{group_name}) {
281     $g .= ' ('. $self->Message::Util::encode_ccontent ($self->{group_name}) .':)';
282     }
283     for (@{$self->{group_name_comment}}) {
284     $g .= ' ('. $self->Message::Util::encode_ccontent ($_) .')';
285     }
286     }
287     if ($option{is_group}) {
288     $m = $g . (length $m? ': ': ':') . $m . ';';
289     } else {
290 wakaba 1.2 $m = $g . (length $g? ' ': '') . $m;
291 wakaba 1.1 }
292     if ($option{output_comment} && !$option{output_group_name_comment}) {
293     for (@{$self->{group_name_comment}}) {
294     $m .= ' ('. $self->Message::Util::encode_ccontent ($_) .')';
295     }
296     }
297     if ($option{output_comment}) {
298     for (@{$self->{comment}}) {
299     $m .= ' ('. $self->Message::Util::encode_ccontent ($_) .')';
300     }
301     }
302     $m;
303     }
304     *as_string = \&stringify;
305     sub _stringify_item ($$\%) {
306     my $self = shift;
307     my $item = shift;
308     my $option = shift;
309     if (!$$option{can_have_group} && ref $item->{value}) {
310     $item->{value}->stringify (-is_group => 0);
311     } else {
312     $item->{value};
313     }
314     }
315    
316     ## option, value_type, clone, method_available: Inherited
317    
318     =head1 LICENSE
319    
320     Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
321    
322     This program is free software; you can redistribute it and/or modify
323     it under the terms of the GNU General Public License as published by
324     the Free Software Foundation; either version 2 of the License, or
325     (at your option) any later version.
326    
327     This program is distributed in the hope that it will be useful,
328     but WITHOUT ANY WARRANTY; without even the implied warranty of
329     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
330     GNU General Public License for more details.
331    
332     You should have received a copy of the GNU General Public License
333     along with this program; see the file COPYING. If not, write to
334     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
335     Boston, MA 02111-1307, USA.
336    
337     =head1 CHANGE
338    
339     See F<ChangeLog>.
340 wakaba 1.2 $Date: 2002/05/08 09:11:31 $
341 wakaba 1.1
342     =cut
343    
344     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24