The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

#-------------------------------------------------------------------------
#
# Copyright:  (c) Andrew Wilson 2001
#
#-------------------------------------------------------------------------

=head1 NAME

tagged.t - Test Mail::Address::Tagged

=head1 SYNOPSIS

  perl t/tagged.t

=head1 DESCRIPTION

Tests Mail::Address::Tagged

=head1 BUGS

Nothing Known

=head1 TODO

Nothing Known

=cut

#-------------------------------------------------------------------------
# Set up our standard testing environment
#-------------------------------------------------------------------------

use strict;
use Test::More tests => 103;

BEGIN {
  $| = 1;
  $^W = 1;
  for ( qw(
           Mail::Address::Tagged
          ) ) {
    use_ok( $_ );
  }
}

my $tmda = Mail::Address::Tagged->new(key => 'PlibbleWibble',
                                      user => 'neo',
                                      host => 'the.matrix.com',);

isa_ok($tmda, 'Mail::Address::Tagged');

#-----------------------------------------------------------------------------
# Test valid_for and set_valid_for
#-----------------------------------------------------------------------------

is($tmda->valid_for,
   432000,
   'Valid_for defaults to correct figure');

is($tmda->set_valid_for('3Y'),
   94608000,
   "set_valid_for with years works");

is($tmda->valid_for,
   94608000,
   'Valid_for returns correct figure');

is($tmda->set_valid_for('2M'),
   5184000,
   "set_valid_for with monthss works");

is($tmda->valid_for,
   5184000,
   'Valid_for returns correct figure');

is($tmda->set_valid_for('7w'),
   4233600,
   "set_valid_for with weeks works");

is($tmda->valid_for,
   4233600,
   'Valid_for returns correct figure');

is($tmda->set_valid_for('10d'),
   864000,
   "set_valid_for with days works");

is($tmda->valid_for,
   864000,
   'Valid_for returns correct figure');

is($tmda->set_valid_for('4h'),
   14400,
   "set_valid_for with hours works");

is($tmda->valid_for,
   14400,
   'Valid_for returns correct figure');

is($tmda->set_valid_for('27m'),
   1620,
   "set_valid_for with minutes works");

is($tmda->valid_for,
   1620,
   'Valid_for returns correct figure');

is($tmda->set_valid_for('123456s'),
   123456,
   "set_valid_for with seconds works");

is($tmda->valid_for,
   123456,
   'Valid_for returns correct figure');

is($tmda->set_valid_for(),
   432000,
   "set_valid_for with no argument restores default");

is($tmda->valid_for,
   432000,
   'Valid_for returns correct figure');


#-----------------------------------------------------------------------------
# Test host, user, email and key
#-----------------------------------------------------------------------------

is($tmda->user,
   'neo',
   "User is stored and returned correctly.");

is($tmda->host,
   'the.matrix.com',
   'Host is stored correctly.');

is($tmda->email,
   'neo@the.matrix.com',
   'email address is correct.');

is($tmda->key,
   'PlibbleWibble',
   'Key is correct',);

$tmda = Mail::Address::Tagged->new(key   => 'ItsLifeJimButNotAsWeKnowIt',
                                   email => 'foo@bar.com',);

is($tmda->user,
   'foo',
   "User is stored and returned correctly.");

is($tmda->host,
   'bar.com',
   'Host is stored correctly.');

is($tmda->email,
   'foo@bar.com',
   'email address is correct.');

is($tmda->key,
   'ItsLifeJimButNotAsWeKnowIt',
   'Key is correct',);

#-----------------------------------------------------------------------------
# Test keyword and set_keyword
#-----------------------------------------------------------------------------

$tmda = Mail::Address::Tagged->new(key     => '01234567890123456789',
                                   email   => 'foo@bar.com',
                                   keyword => 'wibble',);

is($tmda->keyword,
   'wibble',
   "Keyword is returned correctly");

$tmda = Mail::Address::Tagged->new(key     => '01234567890123456789',
                                   email   => 'foo@bar.com',
                                   keyword => 'monger',);

is($tmda->keyword,
   'monger',
   "Keyword is returned correctly");

$tmda = Mail::Address::Tagged->new(key     => '01234567890123456789',
                                   email   => 'foo@bar.com',);

is($tmda->keyword,
   '',
   'when the keyword is omitted from the constructor, it returns the empty string');

is($tmda->set_keyword(),
   '',
   'set with no value returns the current value');

is($tmda->keyword,
   '',
   'check value not chagned');

is($tmda->set_keyword('perl-monger'),
   'perl-monger',
   'set retuns new value');

is($tmda->keyword,
   'perl-monger',
   'check value not has chagned');

is($tmda->set_keyword(),
   'perl-monger',
   'set with no value returns the current value');

is($tmda->keyword,
   'perl-monger',
   'check value not chagned');

is($tmda->set_keyword(''),
   '',
   'set with no emptystring returns the empty string');

is($tmda->keyword,
   '',
   'check value is now the empty string');

#-----------------------------------------------------------------------------
# Test wrap
#-----------------------------------------------------------------------------

is($tmda->wrap(''),
   $tmda->email,
   'wrap returns same as email function when called with empty string');

is($tmda->wrap('-wibble'),
   'foo-wibble@bar.com',
   'wrap works when provided with a string');

#-----------------------------------------------------------------------------
# Test make confirm
#-----------------------------------------------------------------------------

my $address = $tmda->make_confirm({time => 1234567890,
                                   pid  => 9876,
                                   keyword => 'accept',});

is(substr($address, 0, 3),
   $tmda->user,
   "Confirm email address has correct user");

is(substr($address, 3, 15),
   '-confirm-accept',
   "Email address is a confirmation address of the correct type");

is(substr($address, 18, 12),
   '.1234567890.',
   "Confirm email address contains time in correct location");

is(substr($address, 30, 4),
   '9876',
   "Confirm email address contains pid in correct location");

my $HMAC = substr($address, 34, 7);
ok($HMAC =~ /.[0-9a-f]{6}/,
   "Confirm email address contains HMAC in correct location");

is(substr($address, 41),
   '@bar.com',
   "Confirm email address is for correct domain");

$address = $tmda->make_confirm({time => 1234567890,
                                pid  => 9876,
                                keyword => 'done',
                               });

# note with different keyword HMAC apears at a different location
my $new_HMAC = substr($address, 32, 7);
ok($new_HMAC =~ /.[0-9a-f]{6}/,
   "Confirm email address contains HMAC in correct location");

isnt($HMAC,
     $new_HMAC,
     "Altering keyword changeds HMAC");

$address = $tmda->make_confirm({time => 2345678901,
                                pid  => 9876,
                                keyword => 'accept',
                               });

$new_HMAC = substr($address, 34, 7);
ok($new_HMAC =~ /.[0-9a-f]{6}/,
   "Confirm email address contains HMAC in correct location");

isnt($HMAC,
     $new_HMAC,
     "Altering time changeds HMAC");

$address = $tmda->make_confirm({time => 1234567890,
                                pid  => 4567,
                                keyword => 'accept',
                               });

$new_HMAC = substr($address, 34, 7);
ok($new_HMAC =~ /.[0-9a-f]{6}/,
   "Confirm email address contains HMAC in correct location");

isnt($HMAC,
     $new_HMAC,
     "Altering pid changeds HMAC");

$address = $tmda->make_confirm({time => 1234567890,
                                pid  => 9876,
                                keyword => 'accept',
                               });

$new_HMAC = substr($address, 34, 7);
ok($new_HMAC =~ /.[0-9a-f]{6}/,
   "Confirm email address contains HMAC in correct location");

is($HMAC,
   $new_HMAC,
   "returning all value to original returns original HMAC");

#-----------------------------------------------------------------------------
# Test dated
#-----------------------------------------------------------------------------

$tmda = Mail::Address::Tagged->new(key   => 'aa12354c34df35bbb',
                                   user => 'neo',
                                   host => 'the.matrix.com',);
$tmda->set_valid_for('0s');

$address = $tmda->make_dated(442890);

is(substr($address, 0, 3),
   $tmda->user,
   "Dated email address has correct user");

is(substr($address, 3, 7),
   '-dated-',
   "Email address is a dated address");

is(substr($address, 10, 7),
   '442890.',
   "Dated email address contains time in correct location");

$HMAC = substr($address, 16, 7);
ok($HMAC =~ /.[0-9a-f]{6}/,
   "Dated email address contains HMAC in correct location");

is(substr($address, 23),
   '@the.matrix.com',
   "Dated email address is for correct domain");

# return to default valid for
$tmda->set_valid_for();
$address = $tmda->make_dated(442890);

is(substr($address, 10, 7),
   '874890.',
   "Dated email address contains time in correct location (default valid_for)");

$new_HMAC = substr($address, 16, 7);
ok($new_HMAC =~ /.[0-9a-f]{6}/,
   "Confirm email address contains HMAC in correct location (default valid_for)");

isnt($HMAC,
     $new_HMAC,
     "default valid_for changes HMAC");

my $time = time;
$address = $tmda->make_dated();

my $res = $1 if ($address =~ /^neo-dated-(\d+)/);
ok($res >= $time,
   "Dated email address contains time in correct location (no time supplied)");

$new_HMAC = substr($address, 10 + length $res, 7);
ok($new_HMAC =~ /.[0-9a-f]{6}/,
   "Confirm email address contains HMAC in correct location (no time supplied)");

isnt($HMAC,
     $new_HMAC,
     "calling without time changes HMAC");

#-----------------------------------------------------------------------------
# Test sender
#-----------------------------------------------------------------------------
my $key = 'c34df35bbb1234567890';
$tmda = Mail::Address::Tagged->new(key   => $key,
                                   email => 'mandolin@neo-classical.org',);

$address = $tmda->make_sender('Willy.Wonka@the.chocolate-factory.com');

is(substr($address, 0, 8),
   $tmda->user,
   "Sender email address has correct user");

is(substr($address, 8, 8),
   '-sender-',
   "Email address is a dated address");

$HMAC = substr($address, 16, 7);
ok($HMAC =~ /[0-9a-f]{6}/,
   "Sender email address contains HMAC in correct location");

is(substr($address, 22),
   '@neo-classical.org',
   "Sender email address is for correct domain");

$address = $tmda->make_sender('willy.wonka@the.chocolate-factory.com');
$new_HMAC = substr($address, 16, 7);
ok($new_HMAC =~ /[0-9a-f]{6}/,
   "Confirm email address contains HMAC in correct location (default valid_for)");

is($HMAC,
   $new_HMAC,
   "HMAC doesn't change depending on case of sender address");

$address = $tmda->make_sender('billy.wonka@the.chocolate-factory.com');
$new_HMAC = substr($address, 16, 7);
ok($new_HMAC =~ /[0-9a-f]{6}/,
   "Confirm email address contains HMAC in correct location (default valid_for)");

  isnt($HMAC,
     $new_HMAC,
     "HMAC changes with a subtly different sender address");

#-----------------------------------------------------------------------------
# Test for_received confirm
#-----------------------------------------------------------------------------

{

  my $key = 'c34df35bbb1234567890';
  $tmda = Mail::Address::Tagged->new(key  => $key,
                                     user => 'mandolin',
                                     host => 'neo-classical.org',);

  $tmda->set_valid_for('120s');
  my $intime  = time();
  my $inpid   = 32768;
  my $address = $tmda->make_confirm({time => $intime,
                                     pid  => $inpid,
                                     keyword => 'accept',});

  my $tmda = Mail::Address::Tagged->for_received(key     => $key,
                                                 address => $address,
                                                 sender  => 'foo@bar.com');

  isa_ok($tmda, 'Mail::Address::Tagged');

  is($tmda->candidate_time,
     $intime,
     "Time is correct (from reconstructed confirm)");

  is($tmda->candidate_pid,
     $inpid,
     "pid is correct (from reconstructed confirm)");

  is($tmda->keyword,
     'accept',
     'keyword is correct (from reconstructed confirm)');

  is($tmda->type,
     'confirm',
     "type is correct (from reconstructed confirm)");

  is($tmda->sender,
     'foo@bar.com',
     'sender is correct (from reconstructed confirm)');

  ok($tmda->valid, "HMAC checks out");

  my ($one, $two) = split "@", $address;
  my $last = chop $one;
  if ($last eq '0') {
    $last = 1;
  } else {
    $last = 0;
  }

  $address = $one.$last."@".$two;

  $tmda = Mail::Address::Tagged->for_received(key     => $key,
                                              address => $address,
                                              sender  => 'foo@bar.com');

  isa_ok($tmda, 'Mail::Address::Tagged');
  ok(!$tmda->valid, "Altered HMAC no longer checks out");

#-----------------------------------------------------------------------------
# Test for_received dated
#-----------------------------------------------------------------------------

  $tmda = Mail::Address::Tagged->new(key  => $key,
                                     user => 'mandolin',
                                     host => 'neo-classical.org',);

  $tmda->set_valid_for('120s');
  $address = $tmda->make_dated($intime - 120);

  $tmda = Mail::Address::Tagged->for_received(key     => $key,
                                              address => $address,
                                              sender  => 'foo@bar.com');

  isa_ok($tmda, 'Mail::Address::Tagged');

  is($tmda->candidate_time,
     $intime,
     "Time is correct (from reconstructed confirm)");

  is($tmda->type,
     'dated',
     "type is correct (from reconstructed confirm)");

  is($tmda->sender,
     'foo@bar.com',
     'sender is correct (from reconstructed confirm)');

  ok($tmda->valid, "HMAC checks out");
  ok(!$tmda->expired, "address has not expired");

  ($one, $two) = split "@", $address;
  $last = chop $one;
  if ($last eq '0') {
    $last = 1;
  } else {
    $last = 0;
  }

  $address = $one.$last."@".$two;

  $tmda = Mail::Address::Tagged->for_received(key     => $key,
                                              address => $address,
                                              sender  => 'foo@bar.com');

  isa_ok($tmda, 'Mail::Address::Tagged');
  ok(!$tmda->valid, "Altered HMAC no longer checks out");

  # Set the expiry date to 1 second ago
  $address = $tmda->make_dated($intime - 121);

  $tmda = Mail::Address::Tagged->for_received(key     => $key,
                                              address => $address,
                                              sender  => 'foo@bar.com');

  isa_ok($tmda, 'Mail::Address::Tagged');
  ok($tmda->valid, "HMAC checks out (address is valid)");
  ok($tmda->expired, "address has expired (valid but expired)");


#-----------------------------------------------------------------------------
# Test for_received sender
#-----------------------------------------------------------------------------

  $tmda = Mail::Address::Tagged->new(key  => $key,
                                     user => 'mandolin',
                                     host => 'neo-classical.org',);

  $address = $tmda->make_sender('foo@bar.com');

  $tmda = Mail::Address::Tagged->for_received(key     => $key,
                                              address => $address,
                                              sender  => 'foo@bar.com');

  isa_ok($tmda, 'Mail::Address::Tagged');

  is($tmda->type,
     'sender',
     "type is correct (from reconstructed confirm)");

  is($tmda->sender,
     'foo@bar.com',
     'sender is correct (from reconstructed confirm)');

  ok($tmda->valid, "HMAC checks out");

  ($one, $two) = split "@", $address;
  $last = chop $one;
  if ($last eq '0') {
    $last = 1;
  } else {
    $last = 0;
  }

  $address = $one.$last."@".$two;

  $tmda = Mail::Address::Tagged->for_received(key     => $key,
                                              address => $address,
                                              sender  => 'foo@bar.com');

  isa_ok($tmda, 'Mail::Address::Tagged');
  ok(!$tmda->valid, "Altered HMAC no longer checks out");

  # Now with different address
  $tmda = Mail::Address::Tagged->for_received(key     => $key,
                                              address => $address,
                                              sender  => 'moo@bar.com');

  isa_ok($tmda, 'Mail::Address::Tagged');

  is($tmda->type,
     'sender',
     "type is correct (from reconstructed confirm)");

  is($tmda->sender,
     'moo@bar.com',
     'sender is correct (from reconstructed confirm)');

  ok(!$tmda->valid, "HMAC doesn't match");

}