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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (hide annotations) (download)
Wed May 15 07:29:09 2002 UTC (22 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.1: +23 -2 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     Message::Field::Mailbox --- A perl module for an Internet
5     mail address (mailbox) which is part of Internet Messages
6    
7     =cut
8    
9     package Message::Field::Mailbox;
10     require 5.6.0;
11     use strict;
12     use re 'eval';
13     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     require Message::Field::Structured;
16     push @ISA, qw(Message::Field::Structured);
17    
18     %REG = %Message::Util::REG;
19     $REG{sub_domain} = qr/$REG{atext}|$REG{domain_literal}/;
20     $REG{domain} = qr/$REG{sub_domain}(?:$REG{FWS}\.$REG{FWS}$REG{sub_domain})*/;
21     $REG{route} = qr/\x40$REG{FWS}$REG{domain}(?:[\x09\x20,]*\x40$REG{FWS}$REG{domain})*$REG{FWS}:/;
22     $REG{SCM_angle_addr} = qr/<((?:$REG{quoted_string}|$REG{domain_literal}|$REG{comment}|[^\x22\x28\x5B\x3E])+)>|<>/;
23    
24     =head1 CONSTRUCTORS
25    
26     The following methods construct new objects:
27    
28     =over 4
29    
30     =cut
31    
32     ## Initialize of this class -- called by constructors
33     %DEFAULT = (
34     -_ARRAY_NAME => 'route',
35     -_ARRAY_VALTYPE => 'domain',
36 wakaba 1.2 -_MEMBERS => [qw|display_name route local_part domain keyword|],
37     -_METHODS => [qw|addr_spec display_name local_part domain keyword
38     have_group comment_add comment_delete comment_item
39     comment_count route_add route_count route_delete
40     route_item|],
41 wakaba 1.1 -allow_empty_addr_spec => 0,
42     -by => 'domain',
43     -comment_to_display_name => 1,
44     -default_domain => 'localhost',
45     #encoding_after_encode
46     #encoding_before_decode
47     #-encoding_after_encode_domain => 'unknown-8bit',
48     #-encoding_before_decode_domain => 'unknown-8bit',
49     -encoding_after_encode_local_part => 'unknown-8bit',
50     -encoding_before_decode_local_part => 'unknown-8bit',
51     #field_param_name
52     #field_name
53     -fill_domain => 1,
54     #format
55     #hook_encode_string
56     #hook_decode_string
57     -must_have_addr_spec => 1,
58     -output_angle_bracket => 1,
59     -output_comment => 1,
60     -output_display_name => 1,
61 wakaba 1.2 -output_keyword => 0,
62 wakaba 1.1 -output_route => 0,
63     -parse_all => 1, ## = parse_domain + parse_local_part
64     -parse_domain => 1,
65     -parse_local_part => 1, ## not implemented.
66     -use_keyword => 0,
67     );
68     sub _init ($;%) {
69     my $self = shift;
70     my %options = @_;
71     my $DEFAULT = Message::Util::make_clone (\%DEFAULT);
72     $self->SUPER::_init (%$DEFAULT, %options);
73     $self->{option}->{value_type}->{'domain'} = ['Message::Field::Domain', {
74     #-encoding_after_encode => $self->{option}->{encoding_after_encode_domain},
75     #-encoding_before_decode => $self->{option}->{encoding_before_decode_domain},
76     },];
77    
78     my $format = $self->{option}->{format};
79     my $field = $self->{option}->{field};
80     if ($format =~ /mail-rfc822/) {
81 wakaba 1.2 $self->{option}->{output_route} = 1
82 wakaba 1.1 if $field eq 'from' || $field eq 'resent-from'
83     || $field eq 'sender' || $field eq 'resent-sender'
84     || $field eq 'to' || $field eq 'cc' || $field eq 'bcc'
85     || $field eq 'resent-to' || $field eq 'resent-cc'
86     || $field eq 'resent-bcc' || $field eq 'reply-to'
87     || $field eq 'resent-reply-to' || $field eq 'return-path';
88     }
89     if ($self->{option}->{field_name} eq 'mail-copies-to') {
90     $self->{option}->{use_keyword} = 1;
91 wakaba 1.2 $self->{option}->{output_keyword} = 1;
92 wakaba 1.1 }
93     if ($self->{option}->{field_name} eq 'return-path') {
94     $self->{option}->{allow_empty_addr_spec} = 1;
95     $self->{option}->{output_display_name} = 0;
96     $self->{option}->{output_comment} = 0;
97     ## RFC [2]822 allows CFWS, but [2]821 does NOT.
98     }
99     }
100    
101     =item $addr = Message::Field::Address->new ([%options])
102    
103     Constructs a new object. You might pass some options as parameters
104     to the constructor.
105    
106     =cut
107    
108     ## Inherited
109    
110     =item $addr = Message::Field::Address->parse ($field-body, [%options])
111    
112     Constructs a new object with given field body. You might pass
113     some options as parameters to the constructor.
114    
115     =cut
116    
117     sub parse ($$;%) {
118     my $class = shift;
119     my $self = bless {}, $class;
120     my $body = shift;
121     $self->_init (@_);
122     ($body, @{$self->{comment}})
123     = $self->Message::Util::delete_comment_to_array ($body);
124     my $parse_lp = $self->{option}->{parse_local_part}
125     || $self->{option}->{parse_all};
126     my $parse_dm = $self->{option}->{parse_domain}
127     || $self->{option}->{parse_all};
128     my $addr_spec = '';
129     if ($body =~ /([^\x3C]*)$REG{SCM_angle_addr}/) {
130     my ($dn, $as) = ($1, $2);
131     $self->{display_name} = $self->Message::Util::decode_quoted_string ($dn);
132     $addr_spec = Message::Util::remove_meaningless_wsp ($as);
133     } elsif ($self->{option}->{use_keyword}
134     && $body =~ /^$REG{FWS}($REG{atext_dot})$REG{FWS}$/) {
135     #$self->{keyword} = Message::Util::remove_meaningless_wsp ($1);
136     $self->{keyword} = $1;
137     $self->{keyword} =~ tr/\x09\x20//d;
138     } else {
139     $addr_spec = Message::Util::remove_meaningless_wsp ($body);
140     }
141     if ($addr_spec =~ /^($REG{route})?((?:$REG{quoted_string}|[^\x22])+?)\x40((?:$REG{domain_literal}|[^\x5B\x40])+)$/) {
142     my $route = $1;
143     $self->{local_part} = $2; $self->{domain} = $3;
144     $self->{domain} = $self->_parse_value ('domain' => $self->{domain})
145     if $parse_dm;
146     $route =~ s{\x40$REG{FWS}($REG{domain})}{
147     my $d = $1;
148     $d = $self->_parse_value ('domain' => $d) if $parse_dm;
149     push @{$self->{route}}, $d;
150     }gex;
151     } elsif (length $addr_spec) {
152     $self->{local_part} = $addr_spec;
153     }
154     #if ($parse_lp && $self->{local_part}) {}
155     $self->{local_part}
156     = $self->Message::Util::decode_quoted_string ($self->{local_part},
157     type => 'word',
158     charset => $self->{option}->{encoding_before_decode_local_part});
159     $self;
160     }
161    
162     sub local_part ($;$) {
163     my $self = shift;
164     my $newlp = shift;
165     $self->{local_part} = $newlp if defined $newlp;
166     $self->{local_part};
167     }
168    
169     sub domain ($;$) {
170     my $self = shift;
171     my $newdomain = shift;
172     if (defined $newdomain) {
173     $newdomain = $self->_parse_value ('domain' => $newdomain)
174     if $self->{option}->{parse_domain} || $self->{option}->{parse_all};
175     $self->{domain} = $newdomain;
176     }
177     $self->{domain};
178     }
179    
180     sub keyword ($;$) {
181     my $self = shift;
182     return unless $self->{option}->{use_keyword};
183     my $newkey = shift;
184     $self->{keyword} = $newkey if defined $newkey;
185     $self->{keyword};
186     }
187    
188     sub route_add ($@) {shift->SUPER::add (@_)}
189     sub route_count ($@) {shift->SUPER::count (@_)}
190     sub route_delete ($@) {shift->SUPER::delete (@_)}
191     sub route_item ($@) {shift->SUPER::item (@_)}
192    
193     sub _delete_match ($$$\%\%) {
194     my $self = shift;
195     my ($by, $i, $list, $option) = @_;
196     return 0 unless ref $$i; ## Already removed
197     return 0 if $$option{type} && $$i->{type} ne $$option{type};
198     if ($by eq 'domain') {
199     $$i = $self->_parse_value ('domain' => $$i);
200     return 1 if $list->{$$i};
201     }
202     0;
203     }
204     *_item_match = \&_delete_match;
205    
206     ## Returns returned item value \$item-value, \%option
207     sub _item_return_value ($\$\%) {
208     if (ref ${$_[1]}) {
209     ${$_[1]};
210     } else {
211     ${$_[1]} = $_[0]->_parse_value (domain => ${$_[1]});
212     ${$_[1]};
213     }
214     }
215     sub _item_new_value ($$\%) {
216     $_[0]->_parse_value (domain => $_[2]->{by} eq 'domain'?$_[1]:'');
217     }
218    
219 wakaba 1.2 sub have_group ($) {0}
220    
221     sub addr_spec ($;%) {
222     my $self = shift;
223     my %o = (
224     -output_angle_bracket => 0,
225     -output_comment => 0,
226     -output_display_name => 0,
227     -output_keyword => 0,
228     -output_route => 0,
229     );
230     $self->stringify (%o, @_);
231     }
232    
233 wakaba 1.1 sub stringify ($;%) {
234     my $self = shift;
235     my %o = @_; my %option = %{$self->{option}};
236     for (grep {/^-/} keys %o) {$option{substr ($_, 1)} = $o{$_}}
237     my ($dn, $as, $cm) = ('', '', '');
238     if (length $self->{keyword}) {
239     if ($option{output_keyword}) {
240     my %s = &{$option{hook_encode_string}} ($self,
241     $self->{keyword}, type => 'phrase');
242     $as = Message::Util::quote_unsafe_string
243     ($s{value}, unsafe => 'NON_atext_dot');
244     } else {
245     $as = '('. $self->Message::Util::encode_ccontent ($self->{keyword}) .')';
246     }
247     } else {
248     if ($option{output_display_name}) {
249     if (length $self->{display_name}) {
250     my %s = &{$option{hook_encode_string}} ($self,
251     $self->{display_name}, type => 'phrase');
252     $dn = Message::Util::quote_unsafe_string
253     ($s{value}, unsafe => 'NON_atext_wsp') . ' ';
254     } elsif ($option{comment_to_display_name}) {
255     my $fullname = shift (@{$self->{comment}});
256     if (length $fullname) {
257     my %s = &{$option{hook_encode_string}} ($self,
258     $fullname, type => 'phrase');
259     $dn = Message::Util::quote_unsafe_string
260     ($s{value}, unsafe => 'NON_atext_wsp') . ' ';
261     }
262     }
263     } elsif ($option{output_comment} && length $self->{display_name}) {
264     $dn = ' ('. $self->Message::Util::encode_ccontent ($self->{display_name}) .')';
265     }
266     my %s = &{$option{hook_encode_string}} ($self,
267     $self->{local_part}, type => 'word',
268     charset => $option{encoding_after_encode_local_part});
269     $as = Message::Util::quote_unsafe_string ($s{value},
270     unsafe => $REG{NON_atext_dot});
271     my $d = '' . $self->{domain};
272     $d ||= $option{default_domain} if $option{fill_domain};
273     $as .= '@' . $d if length $d && length $as;
274     if ($as) {
275     if ($option{output_angle_bracket}) {
276     if ($option{output_route}) {
277     my $route = join ',', grep {$_ ne '@'}
278     map {'@'.$_} @{$self->{route}};
279     $as = $route . ':' . $as if $route;
280     }
281     $as = '<' . $as . '>';
282     }
283     } else {
284     $as = '<>' if $option{allow_empty_addr_spec}
285     && $option{output_angle_bracket};
286     return '' if !$option{allow_empty_addr_spec}
287     && $option{must_have_addr_spec};
288     }
289     }
290     if ($option{output_comment}) {
291     $cm = $self->_comment_stringify (\%option);
292     $cm = ' ' . $cm if $cm;
293     if ($dn && !$option{output_display_name}) {
294     $cm = $dn . $cm; $dn = '';
295     }
296     }
297     $dn . $as . $cm;
298     }
299    
300     =head1 LICENSE
301    
302     Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
303    
304     This program is free software; you can redistribute it and/or modify
305     it under the terms of the GNU General Public License as published by
306     the Free Software Foundation; either version 2 of the License, or
307     (at your option) any later version.
308    
309     This program is distributed in the hope that it will be useful,
310     but WITHOUT ANY WARRANTY; without even the implied warranty of
311     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
312     GNU General Public License for more details.
313    
314     You should have received a copy of the GNU General Public License
315     along with this program; see the file COPYING. If not, write to
316     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
317     Boston, MA 02111-1307, USA.
318    
319     =head1 CHANGE
320    
321     See F<ChangeLog>.
322 wakaba 1.2 $Date: 2002/05/14 13:42:40 $
323 wakaba 1.1
324     =cut
325    
326     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24