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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (hide annotations) (download)
Sun Jun 9 11:08:28 2002 UTC (22 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.5: +5 -4 lines
2002-06-09  wakaba <w@suika.fam.cx>

	* Addresses.pm (_delete_match): 'addr-spec': new 'by' option.
	* ContentType.pm:
	- (_save_param): Call _parse_param_value if parse_all.
	- (_parse_value): New function.  Check Message::MIME::MediaType.
	* CSV.pm (use_comment): New option.
	* Date.pm:
	- (zone): New method.
	- (set_datetime): Likewise.
	* Mailbox.pm (display_name): New method.
	* Numval.pm (use_comment): New option.
	* Param.pm (_parse_param_value): New function.
	* Structured.pm:
	- (_add_return_value, _replace_return_value): New functions.
	- (_parse_value): Sync with Message::Entity's.
	- (option): Sync with Message::Entity's.
	- (option): '-recursive': new option.
	- (_option_recursive): New function.

1 wakaba 1.1
2     =head1 NAME
3    
4 wakaba 1.4 Message::Field::MsgID --- Perl module for Message-ID
5     of Internet messages
6 wakaba 1.1
7     =head1 DESCRIPTION
8    
9 wakaba 1.4 This module supports C<msg-id> defined by RFC 2822.
10     Message-ID generating algorithm suggested by
11     draft-ietf-usefor-msg-id-alt-00 is also supported.
12 wakaba 1.1
13     =cut
14    
15     package Message::Field::MsgID;
16     use strict;
17 wakaba 1.4 use vars qw(@ISA %REG $VERSION);
18 wakaba 1.6 $VERSION=do{my @r=(q$Revision: 1.5 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
19 wakaba 1.4 require Message::Util;
20     require Message::Field::Structured;
21     push @ISA, qw(Message::Field::Structured);
22    
23     use overload '""' => sub { $_[0]->stringify },
24     fallback => 1;
25    
26     *REG = \%Message::Util::REG;
27     ## Inherited: comment, quoted_string, domain_literal
28     ## WSP, FWS, phrase, NON_atom
29     ## msg_id
30     ## M_quoted_string
31 wakaba 1.1
32 wakaba 1.4 =head1 CONSTRUCTORS
33 wakaba 1.1
34 wakaba 1.4 The following methods construct new objects:
35 wakaba 1.1
36 wakaba 1.4 =over 4
37 wakaba 1.1
38     =cut
39    
40 wakaba 1.4 ## Initialize of this class -- called by constructors
41     sub _init ($;%) {
42     my $self = shift;
43     my %options = @_;
44     my %DEFAULT = (
45     -encoding_after_encode => 'unknown-8bit',
46     -encoding_before_decode => 'unknown-8bit',
47     #field_param_name
48     #field_name
49     #format
50     -hash_name => '%none',
51     #hook_encode_string
52     #hook_decode_string
53     -software_name => 'MFMpm',
54     -software_name_hash => '%none',
55 wakaba 1.6 -validate => 0,
56 wakaba 1.4 );
57     $self->SUPER::_init (%DEFAULT, %options);
58 wakaba 1.1 }
59    
60 wakaba 1.4 =item $m = Message::Field::MsgID->new ([%options])
61 wakaba 1.1
62 wakaba 1.4 Constructs a new object. You might pass some options as parameters
63     to the constructor.
64 wakaba 1.1
65     =cut
66    
67 wakaba 1.4 sub new ($;%) {
68     my $self = shift->SUPER::new (@_);
69 wakaba 1.1 my %option = @_;
70 wakaba 1.4 if ($option{id_left} && $option{id_right}) {
71     $self->{id_left} = $option{id_left};
72     $self->{id_right} = $option{id_right};
73     } elsif ($option{addr_spec}
74     || (($option{fqdn} || $option{ip_address} || $option{uucp})
75     && ($option{login}))) {
76     $self->_newid (\%option);
77     }
78 wakaba 1.1 $self;
79     }
80    
81 wakaba 1.4 sub parse ($;$%) {
82     my $class = shift;
83     my $self = bless {}, $class;
84 wakaba 1.6 my $body = shift; my @c;
85 wakaba 1.4 $self->_init (@_);
86 wakaba 1.6 ($body, @c) = $self->Message::Util::delete_comment_to_array ($body);
87 wakaba 1.4
88     $body = Message::Util::remove_wsp ($body);
89     if ($body =~ /$REG{M_addr_spec}/) {
90     my %s = &{$self->{option}->{hook_decode_string}} ($self,
91     Message::Util::unquote_quoted_string ($1), type => 'quoted-string');
92     $self->{id_left} = $s{value};
93     #my %s = &{$self->{option}->{hook_decode_string}} ($self,
94     # Message::Util::unquote_if_domain_literal ($2), type => 'domain');
95     #$self->{id_right} = $s{value};
96     $self->{id_right} = $2;
97     }
98    
99     $self;
100 wakaba 1.1 }
101    
102 wakaba 1.4 sub _newid ($\%) {
103 wakaba 1.2 my $self = shift;
104 wakaba 1.4 my $o = shift;
105     $$o{addr_spec} = Message::Util::remove_wsp ($$o{addr_spec});
106     if ($$o{addr_spec} =~ /$REG{M_addr_spec}/) {
107     $$o{login} = $1; $$o{fqdn} = $2;
108     }
109     if ($self->{option}->{validate} && $$o{fqdn} =~
110 wakaba 1.5 /[.@](example\.(?:com|org|net)|localdomain|localhost|example|invalid|arpa)$/) {
111 wakaba 1.4 Carp::croak "Msg-ID generation: invalid TLD of FQDN: .$1";
112     }
113     if (!$$o{fqdn} && $$o{ip_address}) {
114     if ($self->{option}->{validate}
115     && $$o{ip_address}=~/([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/){
116     my ($c1, $c2, $c3, $c4) = ($1, $2, $3, $4);
117     Carp::croak "Msg-ID generation: invalid IPv4 address: $c1.$c2.$c3.$c4"
118     ## See [IANAREG] and draft-iana-special-ipv4
119     if ($c1 == 0) ## "this" network
120     || ($c1 == 10) ## private [RFC1918]
121     || ($c1 == 127) ## loopback
122     || ($c1 == 169 && $c2 == 254) ## "link local"
123     || ($c1 == 172 && 16 <= $c2 && $c2 < 32) ## private [RFC1918]
124     || ($c1 == 192 && (($c2 == 0 && $c3 == 2) ## "TEST-NET"
125     || ($c2 == 88 && $c3 == 99) ## 6to4 anycast [RFC3068]
126     || ($c2 == 168))) ## private [RFC1918]
127     || ($c1 == 198 && ($c2 == 18 || $c2 == 19)) ## benchmark [RFC2544]
128     || ($c1 >= 224); ## class D,E [RFC3171]
129     }
130     $$o{fqdn} ||= '['.$$o{ip_address}.']';
131     }
132     if (!$$o{fqdn} && $$o{uucp}) {
133     $$o{uucp} .= '.uucp' if $self->{option}->{validate} && $$o{uucp} !~ /\.uucp/i;
134     $$o{fqdn} = $$o{uucp};
135     }
136     Carp::croak "Msg-ID generation: no FQDN"
137     if $self->{option}->{validate} && !$$o{fqdn};
138    
139     $self->{id_right} = $$o{fqdn};
140    
141     Carp::croak "Msg-ID generation: no 'login'"
142     if $self->{option}->{validate} && !$$o{login};
143     $$o{login} = $self->_hash ($$o{login}, $self->{option}->{hash_name});
144    
145     my @s = ('0'..'9','a'..'z','-','=','_');
146 wakaba 1.5 my @t = ('0'..'9','a'..'z');
147     my $unique = $t[rand @t].$s[rand @s].$s[rand @s].$s[rand @s].'.';
148 wakaba 1.4 $unique .= join ('.', $self->_base39 (time), $self->_base39 ($$),
149     $self->_hash ($self->{option}->{software_name},
150     $self->{option}->{software_name_hash}));
151     $self->{id_left} = $unique
152     .'%'.($self->{option}->{hash_name} ne '%none'?
153     $self->_hash ($self->{option}->{hash_name}, '%none') .'%': '')
154     .$$o{login}
155     .($$o{subject_changed}? '-_-': '');
156    
157     $self;
158 wakaba 1.2 }
159 wakaba 1.1
160 wakaba 1.4 sub _hash ($$;$$) {
161 wakaba 1.1 my $self = shift;
162 wakaba 1.4 my ($str, $hash_name, $add_unsafe) = (shift, lc shift, shift);
163     $add_unsafe ||= qr#[/.=\x09\x20]#;
164     undef $hash_name if $hash_name eq '%none';
165     if ($hash_name eq 'md5') {
166     eval {require Digest::MD5} or Carp::croak "Msg-ID generation: $@";
167     $str = Digest::MD5::md5_base64 ($str);
168     } elsif ($hash_name eq 'sha1') {
169     eval {require Digest::SHA1} or Carp::croak "Msg-ID generation: $@";
170     $str = Digest::SHA1::sha1_base64 ($str);
171     } elsif ($hash_name eq 'md2') {
172     eval {require Digest::MD2} or Carp::croak "Msg-ID generation: $@";
173     $str = Digest::MD2::md2_base64 ($str);
174     } elsif ($hash_name eq 'crypt') {
175     my @s = ('0'..'9','A'..'Z','a'..'z');
176     my $salt = crypt('foobar', '$1$ab$') eq '$1$ab$uAP8qWqcFs3q.Gfl5PkL2.'?
177     '$1$'.join('', map($s[rand @s], 1..8)).'$': $s[rand @s].$s[rand @s];
178     $str = crypt ($str, $salt);
179 wakaba 1.1 }
180 wakaba 1.4 $str =~ s#($add_unsafe)#sprintf('=%02X', ord($1))#ge;
181     $str =~ s/($REG{NON_atext_dot})/sprintf('=%02X', ord($1))/ge;
182     $str;
183 wakaba 1.1 }
184    
185 wakaba 1.4 sub _base39 ($$) {
186 wakaba 1.1 my $self = shift;
187 wakaba 1.4 my $number = shift;
188     my @digit = ('0'..'9','a'..'z','-','=','_');
189     my $ret = '';
190    
191     my ($rem);
192     while ($number > 0) {
193     $rem = $number % @digit;
194     $ret = $digit[ $rem ].$ret;
195     $number = ($number - $rem) / @digit;
196     }
197     $ret;
198 wakaba 1.1 }
199    
200 wakaba 1.4 sub generate ($%) {
201 wakaba 1.1 my $self = shift;
202 wakaba 1.4 my %parameter = @_;
203     for (grep {/^-/} keys %parameter) {$parameter{substr ($_, 1)} = $parameter{$_}}
204     $self->_newid (\%parameter);
205 wakaba 1.1 }
206    
207 wakaba 1.4 sub id_left ($) {
208 wakaba 1.1 my $self = shift;
209 wakaba 1.4 my %e = &{$self->{option}->{hook_encode_string}} ($self,
210     $self->{id_left}, type => 'local-part');
211     Message::Util::quote_unsafe_string ($e{value},
212     unsafe_regex => qr/$REG{NON_atext_dot}|^\.|\.$/);
213 wakaba 1.1 }
214 wakaba 1.4 sub id_right ($) {
215 wakaba 1.3 my $self = shift;
216 wakaba 1.4 #my %e = &{$self->{option}->{hook_encode_string}} ($self,
217     # $self->{id_right}, type => 'domain');
218     #Message::Util::quote_unsafe_domain ($e{value});
219     Message::Util::quote_unsafe_domain ($self->{id_right});
220 wakaba 1.3 }
221 wakaba 1.4 sub content ($) {
222 wakaba 1.1 my $self = shift;
223 wakaba 1.4 my ($l, $r) = ($self->id_left, $self->id_right);
224     sprintf '%s@%s', $l, $r if $l && $r;
225 wakaba 1.1 }
226    
227 wakaba 1.4 sub stringify ($;%) {
228 wakaba 1.1 my $self = shift;
229 wakaba 1.4 my ($l, $r) = ($self->id_left, $self->id_right);
230     sprintf '<%s@%s>', $l, $r if $l && $r;
231 wakaba 1.1 }
232 wakaba 1.4 *as_string = \&stringify;
233 wakaba 1.1
234 wakaba 1.4 =head1 EXAMPLE
235 wakaba 1.1
236 wakaba 1.4 use Message::Field::MsgID;
237     my $from = 'foo@bar.example';
238     my $login = 'my-login-name';
239     my $domain = 'foo.bar.example';
240     my $ipv4 = '192.168.0.1';
241     my %mid;
242     $mid{no_crypt} = new Message::Field::MsgID
243     addr_spec => $from, -validate => 0;
244     $mid{md5} = new Message::Field::MsgID
245     login => $login, ip_address => $ipv4,
246     -hash_name => 'md5', -validate => 0;
247     $mid{sha1} = new Message::Field::MsgID
248     login => $login, fqdn => $domain, subject_changed => 1,
249     -hash_name => 'sha1', -validate => 0;
250     for (keys %mid) {
251     print $_, ":\t", $mid{$_}, "\n";
252     }
253     # sha1: <t-9.bbxkfu.xsem.MFMMpm%sha1%9pnH2R6iN8KSIMby+dPU0i3M8RU-_-@foo.bar.example>
254     # md5: <7fu.bbxkfu.xsem.MFMMpm%md5%eBpd+12mupwxZBc6kMWR9g@[192.168.0.1]>
255     # no_crypt: <3vy.bbxkfu.xsem.MFMMpm%foo@bar.example>
256    
257     ## IMPORTANT NOTE: This example uses -validate option with
258     ## '0' (does not validate) value since it uses example (invalid)
259     ## resource names, such as 'foo.bar.example'. Usually, this option
260     ## shall not be used (and default value = '1' = does validate
261     ## should be used).
262 wakaba 1.1
263 wakaba 1.4 =head1 SEE ALSO
264 wakaba 1.1
265 wakaba 1.4 RFC 822 E<lt>urn:ietf:rfc:822E<gt>, RFC 2822 E<lt>urn:ietf:rfc:2822E<gt>
266 wakaba 1.1
267 wakaba 1.4 RFC 850 E<lt>urn:ietf:rfc:850E<gt>, RFC 1036 E<lt>urn:ietf:rfc:1036E<gt>,
268     son-of-RFC1036, draft-ietf-usefor-article-06.txt
269     E<lt>urn:ietf:id:draft-ietf-usefor-article-06E<gt>
270 wakaba 1.1
271 wakaba 1.4 draft-ietf-usefor-message-id-01.txt
272     E<lt>urn:ietf:id:draft-ietf-usefor-message-id-01E<gt>
273 wakaba 1.1
274 wakaba 1.4 draft-ietf-usefor-msg-id-alt-00.txt
275     E<lt>urn:ietf:id:draft-ietf-usefor-msg-id-alt-00E<gt>
276 wakaba 1.1
277     =head1 LICENSE
278    
279     Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
280    
281     This program is free software; you can redistribute it and/or modify
282     it under the terms of the GNU General Public License as published by
283     the Free Software Foundation; either version 2 of the License, or
284     (at your option) any later version.
285    
286     This program is distributed in the hope that it will be useful,
287     but WITHOUT ANY WARRANTY; without even the implied warranty of
288     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
289     GNU General Public License for more details.
290    
291     You should have received a copy of the GNU General Public License
292     along with this program; see the file COPYING. If not, write to
293     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
294     Boston, MA 02111-1307, USA.
295    
296     =head1 CHANGE
297    
298     See F<ChangeLog>.
299 wakaba 1.6 $Date: 2002/05/14 13:42:40 $
300 wakaba 1.1
301     =cut
302    
303     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24