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)