Mail-Verp-0.06/0000755000175000017500000000000010641354740012255 5ustar gwolfgwolfMail-Verp-0.06/t/0000755000175000017500000000000010641354740012520 5ustar gwolfgwolfMail-Verp-0.06/t/1.t0000644000175000017500000000426210641352057013050 0ustar gwolfgwolf# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl 1.t'
#########################
use Test;
BEGIN { plan tests => 122 };
use Mail::Verp;
ok(1); # If we made it this far, we're ok.
#########################
my $x = Mail::Verp->new;
ok(defined($x) && $x->isa('Mail::Verp'));
my $sender = 'local@source.com';
my @recipients = (
'standard@example.com', [qw(local standard=example.com@source.com)],
'remote+foo@example.com', [qw(local remote+2Bfoo=example.com@source.com)],
'node42!ann@old.example.com', [qw(local node42+21ann=old.example.com@source.com)],
'=@example.com', [qw(local ==example.com@source.com)],
);
=pod
print STDERR "$s $r1 encodes -> ", $x->encode($s, $r1), "\n";
print STDERR "$s $r2 encodes -> ", $x->encode($s, $r2), "\n";
=cut
#Test various address types
for (my $i = 0; $i < @recipients; $i += 2) {
my ($recipient, $verp) = @recipients[$i, $i+1];
#test various separators, including default separator
for my $sep (undef, qw(- +)) {
#test use of object method and class method calls.
my @refs = ('object' => $x, 'class' => 'Mail::Verp');
for (my $j = 0; $j < @refs; $j += 2) {
my ($encoder_name, $encoder) = @refs[$j, $j+1];
my $expected = join(defined($sep) ? $sep : $encoder->separator, @$verp);
$encoder->separator($sep) if defined $sep;
my $sep_str = $sep || '';
my $encoded = $encoder->encode($sender, $recipient);
ok($encoded, $expected,
"encoded address using $encoder_name instance with separator [$sep_str]");
#decode each encoding with both an object and class method call.
for (my $k = 0; $k < @refs; $k += 2) {
my ($decoder_name, $decoder) = @refs[$k, $k+1];
$decoder->separator($sep) if defined $sep;
my ($decoded_sender, $decoded_recipient) = $decoder->decode($encoded);
ok($decoded_sender, $sender,
"encoded with $encoder_name and separator [$sep_str], decoded sender with $decoder_name");
ok($decoded_recipient, $recipient,
"encoded with $encoder_name and separator [$sep_str], decoded recipient with $decoder_name");
}
}
}
}
Mail-Verp-0.06/doc/0000755000175000017500000000000011016573751013024 5ustar gwolfgwolfMail-Verp-0.06/doc/verp.txt0000644000175000017500000000762310641340517014544 0ustar gwolfgwolfVariable Envelope Return Paths
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
The fundamental problem in managing a large mailing list is matching
bounce messages to subscription addresses.
Often a bounce message refers to a failing address that does not
appear on the mailing list. One of the mailing list subscribers is
forwarding messages to that address. Which subscriber? As the list
grows, this question becomes more and more difficult to answer.
Sometimes a bounce message doesn't identify the address that failed.
On occasion it doesn't even include a copy of the original message.
See RFC 1211 for an extensive collection of horror stories.
In theory, one could solve this problem with the DSN option and DSN
format described in RFC 1891, RFC 1892, and RFC 1894. Unfortunately,
the DSN option is useless unless it is supported by every
intermediate MTA. The complexity of RFC 1891 means that it will be
many years, perhaps infinitely many, before DSNs are universally
supported. Furthermore, the complexity of RFC 1894 means that parsing
the subscriber address is difficult even on the occasions that the
address is available.
Variable envelope return paths (VERPs) completely eliminate this
problem _right now_. They automatically and reliably identify the
subscription address relevant to each bounce message. They provide
the address in a form that is trivial for automated bounce handlers
to parse. They require support from the local mailer, but they do not
require support from any other hosts.
2. Variable envelope return paths
Here is how VERPs work: each recipient of the message sees a
different envelope sender address. When a message to the
djb-sos@silverton.berkeley.edu mailing list is sent to
God@heaven.af.mil, for example, it has the following envelope sender:
djb-sos-owner-God=heaven.af.mil@silverton.berkeley.edu
If the message bounces, the bounce message will be sent back to
djb-sos-owner-God=heaven.af.mil@silverton.berkeley.edu.
If God is forwarding His mail, the bounce message will still go to
djb-sos-owner-God=heaven.af.mil@silverton.berkeley.edu. No matter how
uninformative the bounce message is, it will display God's
subscription address in its envelope.
Another benefit of VERPs is that God Himself can see what address He
used to subscribe.
Making VERPs work requires two pieces of local software support.
First: it must be easy to modify the outgoing sender address
separately for each envelope recipient. For example, with one mailer,
qmail, a user can simply touch ~/.qmail-list-owner and
~/.qmail-list-owner-default to apply VERPs to user-list.
Second, and more important: it must be easy to identify a collection
of addresses, such as djb-sos-owner-*, and send all mail for those
addresses to one place, while preserving the * information. Under
qmail, all user-list-owner-* mail will be sent to the user once he
touches ~/.qmail-list-owner-default. Sending the mail through an
automated bounce-handling program is just as easy.
With older mailers, applying VERPs would require setting up a new
user-list-owner-recipient alias for each new recipient. This
inconvenience has prevented VERPs from being widely exploited, even
though the idea is not new.
3. Per-message VERPs
VERPs are not restricted to distinguishing mailing list subscribers;
they can also be used to distinguish messages.
For example, a user can send one message with an envelope sender
address of user-dsn-1, the next message with user-dsn-2, and so on.
As long as the local mailer gives all user-dsn-* back to that user,
he can reliably match up incoming bounces with outgoing messages.
Per-message VERPs can be combined with per-recipient VERPs. Every
application of RFC 1891's ORCPT and ENVID can be handled with
VERPs---easily, reliably, and right now.
Mail-Verp-0.06/Verp.pm0000644000175000017500000001243010641354424013526 0ustar gwolfgwolfpackage Mail::Verp;
use 5.000;
use strict;
use Carp;
use vars qw($VERSION @ENCODE_MAP @DECODE_MAP $DEFAULT_SEPARATOR $SEPARATOR);
$VERSION = '0.06';
my @chars = qw(@ : % ! - [ ]);
@ENCODE_MAP = map { quotemeta($_), sprintf '%.2X', ord($_) } ('+', @chars);
@DECODE_MAP = map { sprintf('%.2X', ord($_)), $_ } (@chars, '+');
$DEFAULT_SEPARATOR = '-'; #used as a constant
$SEPARATOR = $DEFAULT_SEPARATOR; #used by class methods. Can be changed.
sub separator {
my $self = shift;
#called as class or instance object?
my $var = ref($self) ? \$self->{separator} : \$SEPARATOR;
my $value = $$var;
if (@_){
$$var = shift;
}
return $value;
}
sub new
{
my $self = shift;
$self = bless { separator => $DEFAULT_SEPARATOR, @_ }, ref($self) || $self;
$self->separator($self->{separator});
return $self;
}
sub encode
{
my $self = shift;
my $sender = shift;
my $recipient = shift;
unless ($sender){
carp "Missing sender address";
return;
}
unless ($recipient){
carp "Missing recipient address";
return;
}
my ($slocal, $sdomain) = $sender =~ m/(.+)\@([^\@]+)$/;
unless ($slocal && $sdomain){
carp "Cannot parse sender address [$sender]";
return;
}
my ($rlocal, $rdomain) = $recipient =~ m/(.+)\@([^\@]+)$/;
unless ($rlocal && $rdomain){
carp "Cannot parse recipient address [$recipient]";
return;
}
for (my $i = 0; $i < @ENCODE_MAP; $i += 2) {
for my $t ($rlocal, $rdomain){
$t =~ s/$ENCODE_MAP[$i]/+$ENCODE_MAP[$i + 1]/g;
}
}
return join('', $slocal, $self->separator, $rlocal, '=', $rdomain, '@', $sdomain);
}
sub decode
{
my $self = shift;
my $address = shift;
unless ($address){
carp "Missing encoded address";
return;
}
my $separator = $self->separator;
if (my ($slocal, $rlocal, $rdomain, $sdomain) =
$address =~ m/^(.+?)\Q${separator}\E(.+)=([^=\@]+)\@(.+)/){
# warn "$address $slocal $rlocal $rdomain $sdomain\n";
for (my $i = 0; $i < @DECODE_MAP; $i += 2) {
for my $t ($rlocal, $rdomain){
$t =~ s/\+$DECODE_MAP[$i]/$DECODE_MAP[$i + 1]/ig;
}
}
return (qq[$slocal\@$sdomain], qq[$rlocal\@$rdomain]) if wantarray;
return qq[$rlocal\@$rdomain];
}
else {
return $address;
}
}
1;
__END__
=head1 NAME
Mail::Verp - encodes and decodes Variable Envelope Return Paths (VERP) addresses.
=head1 SYNOPSIS
use Mail::Verp;
#Using class methods
#Change separator to something else
Mail::Verp->separator('+');
#Create a VERP envelope sender of an email to recipient@example.net.
my $verp_email = Mail::Verp->encode('sender@example.com', 'recipient@example.net');
#If a bounce comes back, decode $verp_email to figure out
#the original recipient of the bounced mail.
my ($sender, $recipient) = Mail::Verp->decode($verp_email);
#Using instance methods
my $verp = Mail::Verp->new(separator => '+');
#Create a VERP envelope sender of an email to recipient@example.net.
my $verp_email = $verp->encode('sender@example.com', 'recipient@example.net');
#Decode a bounce
my ($sender, $recipient) = $verp->decode($verp_email);
=head1 ABSTRACT
Mail::Verp encodes and decodes Variable Envelope Return Paths (VERP) email addresses.
=head1 DESCRIPTION
Mail::Verp encodes the address of an email recipient into the envelope
sender address so that a bounce can be more easily handled even if the original recipient
is forwarding their mail to another address and the remote Mail Transport Agents send back
unhelpful bounce messages. The module can also be used to decode bounce recipient addresses.
=head1 FUNCTIONS
=over
=item new()
Primarily useful to save typing. So instead of typing C you can say
S<< my $x = Mail::Verp->new; >> then use C<$x> whereever C is usually required.
Accepts an optional C argument for changing the separator, which defaults
to hyphen '-'. The value can also be changed using the C accessor.
S<< my $x = Mail::Verp->new(separator => '+'); >>
=item encode(LOCAL-ADDRESS, REMOTE-ADDRESS)
Encodes LOCAL-ADDRESS, REMOTE-ADDRESS into a verped address suitable for use
as an envelope return address. It may also be useful to use the same address in
Errors-To and Reply-To headers to compensate for broken Mail Transport Agents.
Uses current separator value.
=item decode(VERPED-ADDRESS)
Decodes VERPED-ADDRESS into its constituent parts.
Returns LOCAL-ADDRESS and REMOTE-ADDRESS in list context, REMOTE-ADDRESS in scalar context.
Returns VERPED-ADDRESS if the decoding fails.
Uses current separator value.
=item separator
Returns current value of the VERP C
=item separator(SEPARATOR)
Sets new value for VERP C and returns the previous value.
=back
=head2 EXPORT
None.
=head1 SEE ALSO
DJ Bernstein details verps here: http://cr.yp.to/proto/verp.txt.
Sam Varshavchik proposes an encoding here: http://www.courier-mta.org/draft-varshavchik-verp-smtpext.txt.
=head1 AUTHOR
Gyepi Sam Egyepi@cpan.orgE
=head1 COPYRIGHT AND LICENSE
Copyright 2007 by Gyepi Sam
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut
Mail-Verp-0.06/README0000644000175000017500000000122110641352707013132 0ustar gwolfgwolfEmail/Verp version 0.06
=======================
This is the README file for Mail::Verp, which
creates Variable Envelope Return Paths (VERP) addresses
as detailed by http://cr.yp.to/proto/verp.txt.
The encoding follows the methods described by
http://www.courier-mta.org/draft-varshavchik-verp-smtpext.txt.
INSTALLATION
To install this module type the following:
perl Makefile.PL
make
make test
make install
DEPENDENCIES
This module requires:
Carp.pm
COPYRIGHT AND LICENCE
Copyright (C) 2007 Gyepi Sam
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
Mail-Verp-0.06/Changes0000644000175000017500000000173310641352645013556 0ustar gwolfgwolfRevision history for Perl extension Mail::Verp.
0.06 Fri Jun 19, 2007
- Make class methods use default separator as advertised.
Thanks to Peter Leonard
- Allow correct decoding of addresses with embedded equals sign (=).
Thanks to Joe Edmonds
- Added more and better tests!
0.05 Wed Aug 17, 2005
- Add accessor and parameter setting for separator.
Thanks to Daniel Senie
0.04 Tue Nov 11 2003
- Simpler solution for case insensitivity decoding.
0.03 Tue Nov 11 2003
- Decode case insensitively: some MTAs lowercase the bounce address.
Thanks to Adam Augustine
- Changed author email address.
0.02 Fri Aug 8 22:56:52 2003
- Fixed possible bug to allow for multiple hyphens in local address
- Expanded and clarified documentation
0.01 Wed Apr 16 16:02:50 2003
- original version; created by h2xs 1.22 with options
-X -b 5.0.0 -O -nMail::Verp
Mail-Verp-0.06/Makefile.PL0000644000175000017500000000075410641340517014232 0ustar gwolfgwolfuse 5.000;
use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
WriteMakefile(
'NAME' => 'Mail::Verp',
'VERSION_FROM' => 'Verp.pm', # finds $VERSION
'PREREQ_PM' => {}, # e.g., Module::Name => 1.1
($] >= 5.005 ? ## Add these new keywords supported since 5.005
(ABSTRACT_FROM => 'Verp.pm', # retrieve abstract from module
AUTHOR => 'Gyepi Sam ') : ()),
);
Mail-Verp-0.06/META.yml0000644000175000017500000000045010641354740013525 0ustar gwolfgwolf# http://module-build.sourceforge.net/META-spec.html
#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX#
name: Mail-Verp
version: 0.06
version_from: Verp.pm
installdirs: site
requires:
distribution_type: module
generated_by: ExtUtils::MakeMaker version 6.30_01
Mail-Verp-0.06/MANIFEST0000644000175000017500000000026510641340517013406 0ustar gwolfgwolfChanges
Makefile.PL
MANIFEST
README
Verp.pm
t/1.t
doc/draft-varshavchik-verp-smtpext.txt
doc/verp.txt
META.yml Module meta-data (added by MakeMaker)