* lib/Qpsmtpd/Address.pm
Add POD to describe how to use the objects. Make the addr_cmp method private (no need to expose it). * t/qpsmtpd-address.t Include tests of overloaded comparison, including sorting. git-svn-id: https://svn.perl.org/qpsmtpd/branches/0.31@559 958fd67b-6ff1-0310-b445-bb7760255be9
This commit is contained in:
parent
3b09cc25d7
commit
37ec3b151e
@ -1,11 +1,61 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
package Qpsmtpd::Address;
|
package Qpsmtpd::Address;
|
||||||
use strict;
|
use strict;
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
Qpsmtpd::Address - Lightweight E-Mail address objects
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
Based originally on cut and paste from Mail::Address and including
|
||||||
|
every jot and tittle from RFC-2821/2822 on what is a legal e-mail
|
||||||
|
address for use during the SMTP transaction.
|
||||||
|
|
||||||
|
=head1 USAGE
|
||||||
|
|
||||||
|
my $rcpt = Qpsmtpd::Address->new('<email.address@example.com>');
|
||||||
|
|
||||||
|
The objects created can be used as is, since they automatically
|
||||||
|
stringify to a standard form, and they have an overloaded comparison
|
||||||
|
for easy testing of values.
|
||||||
|
|
||||||
|
=head1 METHODS
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
use overload (
|
use overload (
|
||||||
'""' => \&format,
|
'""' => \&format,
|
||||||
'cmp' => \&addr_cmp,
|
'cmp' => \&_addr_cmp,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
=head2 new()
|
||||||
|
|
||||||
|
Can be called two ways:
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item * Qpsmtpd::Address->new('<full_address@example.com>')
|
||||||
|
|
||||||
|
The normal mode of operation is to pass the entire contents of the
|
||||||
|
RCPT TO: command from the SMTP transaction. The value will be fully
|
||||||
|
parsed via the L<canonify> method, using the full RFC 2821 rules.
|
||||||
|
|
||||||
|
=item * Qpsmtpd::Address->new("user", "host")
|
||||||
|
|
||||||
|
If the caller has already split the address from the domain/host,
|
||||||
|
this mode will not L<canonify> the input values. This is not
|
||||||
|
recommended in cases of user-generated input for that reason. This
|
||||||
|
can be used to generate Qpsmtpd::Address objects for accounts like
|
||||||
|
"<postmaster>" or indeed for the bounce address "<>".
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
The resulting objects can be stored in arrays or used in plugins to
|
||||||
|
test for equality (like in badmailfrom).
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my ($class, $user, $host) = @_;
|
my ($class, $user, $host) = @_;
|
||||||
my $self = {};
|
my $self = {};
|
||||||
@ -118,6 +168,15 @@ sub new {
|
|||||||
#
|
#
|
||||||
# (We ignore all obs forms)
|
# (We ignore all obs forms)
|
||||||
|
|
||||||
|
=head2 canonify()
|
||||||
|
|
||||||
|
Primarily an internal method, it is used only on the path portion of
|
||||||
|
an e-mail message, as defined in RFC-2821 (this is the part inside the
|
||||||
|
angle brackets and does not include the "human readable" portion of an
|
||||||
|
address). It returns a list of (local-part, domain).
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
sub canonify {
|
sub canonify {
|
||||||
my ($dummy, $path) = @_;
|
my ($dummy, $path) = @_;
|
||||||
my $atom = '[a-zA-Z0-9!#\$\%\&\x27\*\+\x2D\/=\?\^_`{\|}~]+';
|
my $atom = '[a-zA-Z0-9!#\$\%\&\x27\*\+\x2D\/=\?\^_`{\|}~]+';
|
||||||
@ -155,10 +214,29 @@ sub canonify {
|
|||||||
return (undef);
|
return (undef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head2 parse()
|
||||||
|
|
||||||
|
Retained as a compatibility method, it is completely equivalent
|
||||||
|
to new() called with a single parameter.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
sub parse { # retain for compatibility only
|
sub parse { # retain for compatibility only
|
||||||
return shift->new(shift);
|
return shift->new(shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head2 address()
|
||||||
|
|
||||||
|
Can be used to reset the value of an existing Q::A object, in which
|
||||||
|
case it takes a parameter with or without the angle brackets.
|
||||||
|
|
||||||
|
Returns the stringified representation of the address. NOTE: does
|
||||||
|
not escape any of the characters that need escaping, nor does it
|
||||||
|
include the surrounding angle brackets. For that purpose, see
|
||||||
|
L<format>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
sub address {
|
sub address {
|
||||||
my ($self, $val) = @_;
|
my ($self, $val) = @_;
|
||||||
if ( defined($val) ) {
|
if ( defined($val) ) {
|
||||||
@ -171,6 +249,18 @@ sub address {
|
|||||||
. ( defined $self->{_host} ? '@'.$self->{_host} : '' );
|
. ( defined $self->{_host} ? '@'.$self->{_host} : '' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head2 format()
|
||||||
|
|
||||||
|
Returns the canonical stringified representation of the address. It
|
||||||
|
does escape any characters requiring it (per RFC-2821/2822) and it
|
||||||
|
does include the surrounding angle brackets. It is also the default
|
||||||
|
stringification operator, so the following are equivalent:
|
||||||
|
|
||||||
|
print $rcpt->format();
|
||||||
|
print $rcpt;
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
sub format {
|
sub format {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
my $qchar = '[^a-zA-Z0-9!#\$\%\&\x27\*\+\x2D\/=\?\^_`{\|}~.]';
|
my $qchar = '[^a-zA-Z0-9!#\$\%\&\x27\*\+\x2D\/=\?\^_`{\|}~.]';
|
||||||
@ -182,17 +272,31 @@ sub format {
|
|||||||
return "<".$self->address().">";
|
return "<".$self->address().">";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head2 user()
|
||||||
|
|
||||||
|
Returns the "localpart" of the address, per RFC-2821, or the portion
|
||||||
|
before the '@' sign.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
sub user {
|
sub user {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
return $self->{_user};
|
return $self->{_user};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head2 host()
|
||||||
|
|
||||||
|
Returns the "domain" part of the address, per RFC-2821, or the portion
|
||||||
|
after the '@' sign.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
sub host {
|
sub host {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
return $self->{_host};
|
return $self->{_host};
|
||||||
}
|
}
|
||||||
|
|
||||||
sub addr_cmp {
|
sub _addr_cmp {
|
||||||
require UNIVERSAL;
|
require UNIVERSAL;
|
||||||
my ($left, $right, $swap) = @_;
|
my ($left, $right, $swap) = @_;
|
||||||
my $class = ref($left);
|
my $class = ref($left);
|
||||||
@ -212,4 +316,11 @@ sub addr_cmp {
|
|||||||
return ($left cmp $right);
|
return ($left cmp $right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright 2004-2005 Peter J. Holzer. See the LICENSE file for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
$^W = 1;
|
$^W = 1;
|
||||||
|
|
||||||
use Test::More tests => 25;
|
use Test::More tests => 27;
|
||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
use_ok('Qpsmtpd::Address');
|
use_ok('Qpsmtpd::Address');
|
||||||
@ -69,5 +69,30 @@ is ("$ao", $as, "overloaded stringify $as");
|
|||||||
$as = 'foo@foo.x.example.com';
|
$as = 'foo@foo.x.example.com';
|
||||||
ok ($ao = Qpsmtpd::Address->parse("<$as>"), "parse <$as>");
|
ok ($ao = Qpsmtpd::Address->parse("<$as>"), "parse <$as>");
|
||||||
is ($ao && $ao->address, $as, "address $as");
|
is ($ao && $ao->address, $as, "address $as");
|
||||||
|
ok ($ao eq $as, "overloaded 'cmp' operator");
|
||||||
|
|
||||||
|
my @unsorted_list = map { Qpsmtpd::Address->new($_) }
|
||||||
|
qw(
|
||||||
|
"musa_ibrah@caramail.comandrea.luger"@wifo.ac.at
|
||||||
|
foo@example.com
|
||||||
|
ask@perl.org
|
||||||
|
foo@foo.x.example.com
|
||||||
|
jpeacock@cpan.org
|
||||||
|
test@example.com
|
||||||
|
);
|
||||||
|
|
||||||
|
# NOTE that this is sorted by _host_ not by _domain_
|
||||||
|
my @sorted_list = map { Qpsmtpd::Address->new($_) }
|
||||||
|
qw(
|
||||||
|
jpeacock@cpan.org
|
||||||
|
foo@example.com
|
||||||
|
test@example.com
|
||||||
|
foo@foo.x.example.com
|
||||||
|
ask@perl.org
|
||||||
|
"musa_ibrah@caramail.comandrea.luger"@wifo.ac.at
|
||||||
|
);
|
||||||
|
|
||||||
|
my @test_list = sort @unsorted_list;
|
||||||
|
|
||||||
|
is_deeply( \@test_list, \@sorted_list, "sort via overloaded 'cmp' operator");
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user