/[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.3 - (hide annotations) (download)
Fri May 17 05:42:27 2002 UTC (22 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.2: +20 -12 lines
2002-05-17  wakaba <w@suika.fam.cx>

	* Mailbox.pm: Bug fix and minor changes.
	* URI.pm: Remade.  It is now designed by M::F::Mailbox
	like interface.

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 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::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 wakaba 1.3 -unsafe_rule_display_name => 'NON_http_attribute_char_wsp',
67     -unsafe_rule_local_part => 'NON_atext_dot',
68     -unsafe_rule_keyword => 'NON_atext_dot',
69 wakaba 1.1 -use_keyword => 0,
70     );
71     sub _init ($;%) {
72     my $self = shift;
73     my %options = @_;
74     my $DEFAULT = Message::Util::make_clone (\%DEFAULT);
75     $self->SUPER::_init (%$DEFAULT, %options);
76 wakaba 1.3 $self->{option}->{value_type}->{domain} = ['Message::Field::Domain', {
77 wakaba 1.1 #-encoding_after_encode => $self->{option}->{encoding_after_encode_domain},
78     #-encoding_before_decode => $self->{option}->{encoding_before_decode_domain},
79     },];
80    
81     my $format = $self->{option}->{format};
82 wakaba 1.3 my $field = $self->{option}->{field_name};
83 wakaba 1.1 if ($format =~ /mail-rfc822/) {
84 wakaba 1.2 $self->{option}->{output_route} = 1
85 wakaba 1.1 if $field eq 'from' || $field eq 'resent-from'
86     || $field eq 'sender' || $field eq 'resent-sender'
87     || $field eq 'to' || $field eq 'cc' || $field eq 'bcc'
88     || $field eq 'resent-to' || $field eq 'resent-cc'
89     || $field eq 'resent-bcc' || $field eq 'reply-to'
90     || $field eq 'resent-reply-to' || $field eq 'return-path';
91     }
92 wakaba 1.3 if ($field eq 'mail-copies-to') {
93 wakaba 1.1 $self->{option}->{use_keyword} = 1;
94 wakaba 1.2 $self->{option}->{output_keyword} = 1;
95 wakaba 1.1 }
96 wakaba 1.3 if ($field eq 'return-path') {
97 wakaba 1.1 $self->{option}->{allow_empty_addr_spec} = 1;
98     $self->{option}->{output_display_name} = 0;
99     $self->{option}->{output_comment} = 0;
100     ## RFC [2]822 allows CFWS, but [2]821 does NOT.
101     }
102 wakaba 1.3 if ($format =~ /http/) {
103     $self->{option}->{unsafe_rule_local_part} = 'NON_http_attribute_char_wsp';
104     }
105 wakaba 1.1 }
106    
107 wakaba 1.3 =item $m = Message::Field::Mailbox->new ([%options])
108 wakaba 1.1
109     Constructs a new object. You might pass some options as parameters
110     to the constructor.
111    
112     =cut
113    
114     ## Inherited
115    
116 wakaba 1.3 =item $m = Message::Field::Mailbox->parse ($field-body, [%options])
117 wakaba 1.1
118     Constructs a new object with given field body. You might pass
119     some options as parameters to the constructor.
120    
121     =cut
122    
123     sub parse ($$;%) {
124     my $class = shift;
125     my $self = bless {}, $class;
126     my $body = shift;
127     $self->_init (@_);
128     ($body, @{$self->{comment}})
129     = $self->Message::Util::delete_comment_to_array ($body);
130     my $parse_lp = $self->{option}->{parse_local_part}
131     || $self->{option}->{parse_all};
132     my $parse_dm = $self->{option}->{parse_domain}
133     || $self->{option}->{parse_all};
134     my $addr_spec = '';
135     if ($body =~ /([^\x3C]*)$REG{SCM_angle_addr}/) {
136     my ($dn, $as) = ($1, $2);
137 wakaba 1.3 $dn =~ s/^$REG{WSP}+//; $dn =~ s/$REG{WSP}+$//;
138 wakaba 1.1 $self->{display_name} = $self->Message::Util::decode_quoted_string ($dn);
139     $addr_spec = Message::Util::remove_meaningless_wsp ($as);
140     } elsif ($self->{option}->{use_keyword}
141     && $body =~ /^$REG{FWS}($REG{atext_dot})$REG{FWS}$/) {
142     #$self->{keyword} = Message::Util::remove_meaningless_wsp ($1);
143     $self->{keyword} = $1;
144     $self->{keyword} =~ tr/\x09\x20//d;
145     } else {
146     $addr_spec = Message::Util::remove_meaningless_wsp ($body);
147     }
148     if ($addr_spec =~ /^($REG{route})?((?:$REG{quoted_string}|[^\x22])+?)\x40((?:$REG{domain_literal}|[^\x5B\x40])+)$/) {
149     my $route = $1;
150     $self->{local_part} = $2; $self->{domain} = $3;
151     $self->{domain} = $self->_parse_value ('domain' => $self->{domain})
152     if $parse_dm;
153     $route =~ s{\x40$REG{FWS}($REG{domain})}{
154     my $d = $1;
155     $d = $self->_parse_value ('domain' => $d) if $parse_dm;
156     push @{$self->{route}}, $d;
157     }gex;
158     } elsif (length $addr_spec) {
159     $self->{local_part} = $addr_spec;
160     }
161     #if ($parse_lp && $self->{local_part}) {}
162     $self->{local_part}
163     = $self->Message::Util::decode_quoted_string ($self->{local_part},
164     type => 'word',
165     charset => $self->{option}->{encoding_before_decode_local_part});
166     $self;
167     }
168    
169     sub local_part ($;$) {
170     my $self = shift;
171     my $newlp = shift;
172     $self->{local_part} = $newlp if defined $newlp;
173     $self->{local_part};
174     }
175    
176     sub domain ($;$) {
177     my $self = shift;
178     my $newdomain = shift;
179     if (defined $newdomain) {
180     $newdomain = $self->_parse_value ('domain' => $newdomain)
181     if $self->{option}->{parse_domain} || $self->{option}->{parse_all};
182     $self->{domain} = $newdomain;
183     }
184     $self->{domain};
185     }
186    
187     sub keyword ($;$) {
188     my $self = shift;
189     return unless $self->{option}->{use_keyword};
190     my $newkey = shift;
191     $self->{keyword} = $newkey if defined $newkey;
192     $self->{keyword};
193     }
194    
195     sub route_add ($@) {shift->SUPER::add (@_)}
196     sub route_count ($@) {shift->SUPER::count (@_)}
197     sub route_delete ($@) {shift->SUPER::delete (@_)}
198     sub route_item ($@) {shift->SUPER::item (@_)}
199    
200     sub _delete_match ($$$\%\%) {
201     my $self = shift;
202     my ($by, $i, $list, $option) = @_;
203     return 0 unless ref $$i; ## Already removed
204     return 0 if $$option{type} && $$i->{type} ne $$option{type};
205     if ($by eq 'domain') {
206     $$i = $self->_parse_value ('domain' => $$i);
207     return 1 if $list->{$$i};
208     }
209     0;
210     }
211     *_item_match = \&_delete_match;
212    
213     ## Returns returned item value \$item-value, \%option
214     sub _item_return_value ($\$\%) {
215     if (ref ${$_[1]}) {
216     ${$_[1]};
217     } else {
218     ${$_[1]} = $_[0]->_parse_value (domain => ${$_[1]});
219     ${$_[1]};
220     }
221     }
222     sub _item_new_value ($$\%) {
223     $_[0]->_parse_value (domain => $_[2]->{by} eq 'domain'?$_[1]:'');
224     }
225    
226 wakaba 1.2 sub have_group ($) {0}
227    
228     sub addr_spec ($;%) {
229     my $self = shift;
230     my %o = (
231     -output_angle_bracket => 0,
232     -output_comment => 0,
233     -output_display_name => 0,
234     -output_keyword => 0,
235     -output_route => 0,
236     );
237     $self->stringify (%o, @_);
238     }
239    
240 wakaba 1.1 sub stringify ($;%) {
241     my $self = shift;
242     my %o = @_; my %option = %{$self->{option}};
243     for (grep {/^-/} keys %o) {$option{substr ($_, 1)} = $o{$_}}
244     my ($dn, $as, $cm) = ('', '', '');
245     if (length $self->{keyword}) {
246     if ($option{output_keyword}) {
247     my %s = &{$option{hook_encode_string}} ($self,
248     $self->{keyword}, type => 'phrase');
249     $as = Message::Util::quote_unsafe_string
250 wakaba 1.3 ($s{value}, unsafe => $option{unsafe_rule_keyword});
251 wakaba 1.1 } else {
252     $as = '('. $self->Message::Util::encode_ccontent ($self->{keyword}) .')';
253     }
254     } else {
255     if ($option{output_display_name}) {
256     if (length $self->{display_name}) {
257     my %s = &{$option{hook_encode_string}} ($self,
258     $self->{display_name}, type => 'phrase');
259     $dn = Message::Util::quote_unsafe_string
260 wakaba 1.3 ($s{value}, unsafe => $option{unsafe_rule_display_name}) . ' ';
261 wakaba 1.1 } elsif ($option{comment_to_display_name}) {
262     my $fullname = shift (@{$self->{comment}});
263     if (length $fullname) {
264     my %s = &{$option{hook_encode_string}} ($self,
265     $fullname, type => 'phrase');
266     $dn = Message::Util::quote_unsafe_string
267 wakaba 1.3 ($s{value}, unsafe => $option{unsafe_rule_display_name}) . ' ';
268 wakaba 1.1 }
269     }
270     } elsif ($option{output_comment} && length $self->{display_name}) {
271     $dn = ' ('. $self->Message::Util::encode_ccontent ($self->{display_name}) .')';
272     }
273     my %s = &{$option{hook_encode_string}} ($self,
274     $self->{local_part}, type => 'word',
275     charset => $option{encoding_after_encode_local_part});
276     $as = Message::Util::quote_unsafe_string ($s{value},
277 wakaba 1.3 unsafe => $option{unsafe_rule_local_part});
278 wakaba 1.1 my $d = '' . $self->{domain};
279     $d ||= $option{default_domain} if $option{fill_domain};
280     $as .= '@' . $d if length $d && length $as;
281     if ($as) {
282     if ($option{output_angle_bracket}) {
283     if ($option{output_route}) {
284     my $route = join ',', grep {$_ ne '@'}
285     map {'@'.$_} @{$self->{route}};
286     $as = $route . ':' . $as if $route;
287     }
288     $as = '<' . $as . '>';
289     }
290     } else {
291     $as = '<>' if $option{allow_empty_addr_spec}
292     && $option{output_angle_bracket};
293     return '' if !$option{allow_empty_addr_spec}
294     && $option{must_have_addr_spec};
295     }
296     }
297     if ($option{output_comment}) {
298     $cm = $self->_comment_stringify (\%option);
299     $cm = ' ' . $cm if $cm;
300     if ($dn && !$option{output_display_name}) {
301     $cm = $dn . $cm; $dn = '';
302     }
303     }
304     $dn . $as . $cm;
305     }
306 wakaba 1.3 *as_string = \&stringify;
307 wakaba 1.1
308     =head1 LICENSE
309    
310     Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
311    
312     This program is free software; you can redistribute it and/or modify
313     it under the terms of the GNU General Public License as published by
314     the Free Software Foundation; either version 2 of the License, or
315     (at your option) any later version.
316    
317     This program is distributed in the hope that it will be useful,
318     but WITHOUT ANY WARRANTY; without even the implied warranty of
319     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
320     GNU General Public License for more details.
321    
322     You should have received a copy of the GNU General Public License
323     along with this program; see the file COPYING. If not, write to
324     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
325     Boston, MA 02111-1307, USA.
326    
327     =head1 CHANGE
328    
329     See F<ChangeLog>.
330 wakaba 1.3 $Date: 2002/05/15 07:29:09 $
331 wakaba 1.1
332     =cut
333    
334     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24