[svn:qpsmtpd] r559 - in branches/0.31: lib/Qpsmtpd t

[email protected] 2 Nov 2005 18:48:33 -0000
Newsgroups perl.cvs.qpsmtpd
Message-ID <[email protected]>
Author: jpeacock
Date: Wed Nov  2 10:48:32 2005
New Revision: 559

Modified:
   branches/0.31/lib/Qpsmtpd/Address.pm
   branches/0.31/t/qpsmtpd-address.t
Log:
* 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.

Modified: branches/0.31/lib/Qpsmtpd/Address.pm
==============================================================================
--- branches/0.31/lib/Qpsmtpd/Address.pm	(original)
+++ branches/0.31/lib/Qpsmtpd/Address.pm	Wed Nov  2 10:48:32 2005
@@ -1,11 +1,61 @@
+#!/usr/bin/perl -w
 package Qpsmtpd::Address;
 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 protected]>');
+
+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 (
     '""'   => \&format,
-    'cmp'  => \&addr_cmp,
+    'cmp'  => \&_addr_cmp,
 );
 
+=head2 new()
+
+Can be called two ways:
+
+=over 4 
+
+=item * Qpsmtpd::Address->new('<[email protected]>')
+
+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 {
     my ($class, $user, $host) = @_;
     my $self = {};
@@ -118,6 +168,15 @@ sub new {
 #
 # (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 {
     my ($dummy, $path) = @_;
     my $atom = '[a-zA-Z0-9!#\$\%\&\x27\*\+\x2D\/=\?\^_`{\|}~]+';
@@ -155,10 +214,29 @@ sub canonify {
     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
     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 {
     my ($self, $val) = @_;
     if ( defined($val) ) {
@@ -171,6 +249,18 @@ sub address {
          . ( 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 {
     my ($self) = @_;
     my $qchar = '[^a-zA-Z0-9!#\$\%\&\x27\*\+\x2D\/=\?\^_`{\|}~.]';
@@ -182,17 +272,31 @@ sub format {
     return "<".$self->address().">";
 }
 
+=head2 user()
+
+Returns the "localpart" of the address, per RFC-2821, or the portion
+before the '@' sign.
+
+=cut
+
 sub user {
     my ($self) = @_;
     return $self->{_user};
 }
 
+=head2 host()
+
+Returns the "domain" part of the address, per RFC-2821, or the portion
+after the '@' sign.
+
+=cut
+
 sub host {
     my ($self) = @_;
     return $self->{_host};
 }
 
-sub addr_cmp {
+sub _addr_cmp {
     require UNIVERSAL;
     my ($left, $right, $swap) = @_;
     my $class = ref($left);
@@ -211,5 +315,12 @@ sub addr_cmp {
 
     return ($left cmp $right);
 }
-	
+
+=head1 COPYRIGHT
+
+Copyright 2004-2005 Peter J. Holzer.  See the LICENSE file for more 
+information.
+
+=cut
+
 1;

Modified: branches/0.31/t/qpsmtpd-address.t
==============================================================================
--- branches/0.31/t/qpsmtpd-address.t	(original)
+++ branches/0.31/t/qpsmtpd-address.t	Wed Nov  2 10:48:32 2005
@@ -2,7 +2,7 @@
 use strict;
 $^W = 1;
 
-use Test::More tests => 25;
+use Test::More tests => 27;
 
 BEGIN {
     use_ok('Qpsmtpd::Address');
@@ -69,5 +69,30 @@ is ("$ao", $as, "overloaded stringify $a
 $as = '[email protected]';
 ok ($ao = Qpsmtpd::Address->parse("<$as>"), "parse <$as>");
 is ($ao && $ao->address, $as, "address $as");
+ok ($ao eq $as, "overloaded 'cmp' operator");
 
+my @unsorted_list = map { Qpsmtpd::Address->new($_) }
+	qw(
+	    "[email protected]"@wifo.ac.at
+	    [email protected]
+	    [email protected]
+	    [email protected]
+	    [email protected]
+	    [email protected]
+	);
+
+# NOTE that this is sorted by _host_ not by _domain_
+my @sorted_list = map { Qpsmtpd::Address->new($_) }
+	qw(
+	    [email protected]
+	    [email protected]
+	    [email protected]
+	    [email protected]
+	    [email protected]
+	    "[email protected]"@wifo.ac.at
+	);
+
+my @test_list = sort @unsorted_list;
+
+is_deeply( \@test_list, \@sorted_list, "sort via overloaded 'cmp' operator");