#!/usr/bin/perl -w

# Copyright (c) 2005 - 2007 George Nistorica
# All rights reserved.
# This file is part of POE::Component::Client::SMTP
# POE::Component::Client::SMTP is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.  See the LICENSE
# file that comes with this distribution for more details.

use strict;

# check that by default the transaction log is disabled when SMTP_Success - ARG1 undefined
# check that by default the transaction log is disabled when SMTP_Failure - ARG1 undefined
# check that when enabled, you really get the transaction log when SMTP_Success - ARG1
# check that when enabled, you really get the transaction log when SMTP_Failure - ARG2

use lib '../lib';
use Test::More tests => 9;    # including use_ok
use Data::Dumper;
use Carp;

BEGIN { use_ok("IO::Socket::INET"); }
BEGIN { use_ok("POE"); }
BEGIN { use_ok("POE::Wheel::ListenAccept"); }
BEGIN { use_ok("POE::Component::Server::TCP"); }
BEGIN { use_ok("POE::Component::Client::SMTP"); }

# the tests we're running
my %test = (
    'transaction_log_disabled_smtp_failure' => 0,
    'transaction_log_disabled_smtp_success' => 0,
    'transaction_log_enabled_smtp_failure'  => 0,
    'transaction_log_enabled_smtp_success'  => 0,
);
my $smtp_message;
my @recipients;
my $from;
my $debug = 0;

$smtp_message = create_smtp_message();
@recipients   = qw(
  george@localhost,
  root@localhost,
  george.nistorica@localhost,
);
$from = 'george@localhost';

##### SMTP server vars
my $port                  = 25252;
my $EOL                   = "\015\012";
my @smtp_server_responses = (
    "220 localhost ESMTP POE::Component::Client::SMTP Test Server",
    "250-localhost$EOL"
      . "250-PIPELINING$EOL"
      . "250-SIZE 250000000$EOL"
      . "250-VRFY$EOL"
      . "250-ETRN$EOL"
      . "250 8BITMIME",
    "250 Ok",                                 # mail from
    "250 Ok",                                 # rcpt to:
    "250 Ok",                                 # rcpt to:, cc
    "250 Ok",                                 # rctp to:, bcc
    "354 End data with <CR><LF>.<CR><LF>",    # data
    "250 Ok: queued as 549B14484F",           # end data
    "221 Bye",                                # quit
);

# create the SMTP server session
POE::Component::Server::TCP->new(
    Port                  => $port,
    Address               => "localhost",
    Domain                => AF_INET,
    Alias                 => "smtp_server",
    Error                 => \&error_handler,               # Optional.
    ClientInput           => \&handle_client_input,         # Required.
    ClientConnected       => \&handle_client_connect,       # Optional.
    ClientDisconnected    => \&handle_client_disconnect,    # Optional.
    ClientError           => \&handle_client_error,         # Optional.
    ClientFlushed         => \&handle_client_flush,         # Optional.
    ClientFilter          => "POE::Filter::Line",           # Optional.
    ClientInputFilter     => "POE::Filter::Line",           # Optional.
    ClientOutputFilter    => "POE::Filter::Line",           # Optional.
    ClientShutdownOnError => 1,                             #
);

# create the pococlsmtp master session
# 4 of them :)

foreach my $key ( keys %test ) {
    POE::Session->create(
        inline_states => {
            _start             => \&start_session,
            _stop              => \&stop_session,
            send_mail          => \&spawn_pococlsmt,
            pococlsmtp_success => \&smtp_send_success,
            pococlsmtp_failure => \&smtp_send_failure,
        },
        heap => { 'test' => $key, }    # store the test name for each session
    );
}

POE::Kernel->run();

# run tests
foreach my $key ( keys %test ) {
    my $name = $key;
    $name =~ s/_/ /g;
    is( $test{$key}, 1, $name );
}

sub start_session {
    carp "start_session" if ( $debug == 2 );
    $_[KERNEL]->yield("send_mail");
}

sub spawn_pococlsmt {
    carp "spawn_pococlsmt" if ( $debug == 2 );
    my $heap       = $_[HEAP];
    my %parameters = (
        From         => $from,
        To           => \@recipients,
        SMTP_Success => 'pococlsmtp_success',
        SMTP_Failure => 'pococlsmtp_failure',
        Server       => 'localhost',
        Port         => $port,
        Body         => $smtp_message,
        Context      => "test context",
        Debug        => 0,
    );

# depending on which test we're running there are some things to be
# modified as well. look also for the Server how it does handle client connection
    if ( $heap->{'test'} eq 'transaction_log_enabled_smtp_success' ) {
        $parameters{'TransactionLog'} = 1;
    }
    if ( $heap->{'test'} eq 'transaction_log_enabled_smtp_failure' ) {
        $parameters{'TransactionLog'} = 1;
        $parameters{'MyHostname'}     = 'Fail';
    }
    elsif ( $heap->{'test'} eq 'transaction_log_disabled_smtp_failure' ) {
        $parameters{'MyHostname'} = 'Fail';
    }
    POE::Component::Client::SMTP->send( %parameters );
}

sub stop_session {

    # stop server
    carp "stop_session" if ( $debug == 2 );
    $_[KERNEL]->call( smtp_server => "shutdown" );
}

sub smtp_send_success {
    my ( $arg0, $arg1, $heap ) = @_[ ARG0, ARG1, HEAP ];
    print "SMTP_Success: ARG0, ", Dumper($arg0), "\nARG1, ", Dumper($arg1), "\n"
      if $debug;

    if ( $heap->{'test'} eq 'transaction_log_disabled_smtp_success' ) {
        if ( not defined $arg1 ) {
            $test{ $heap->{'test'} } = 1;
        }
    }
    elsif ( $heap->{'test'} eq 'transaction_log_enabled_smtp_success' ) {

        # do we have a transaction log?
        if ( defined $arg1 ) {

            # this is how it should be
            if ( compare_transaction_logs( $arg1, return_transaction_log() ) ) {
                $test{ $heap->{'test'} } = 1;
            }
        }
    }

}

sub smtp_send_failure {
    my ( $arg0, $arg1, $arg2, $heap ) = @_[ ARG0, ARG1, ARG2, HEAP ];
    print "SMTP_Failure: ARG0, ", Dumper($arg0), "\nARG1, ", Dumper($arg1), "\n"
      if $debug;

    if ( $heap->{'test'} eq 'transaction_log_disabled_smtp_failure' ) {
        if ( not defined $arg2 ) {
            $test{ $heap->{'test'} } = 1;
        }
    }
    elsif ( $heap->{'test'} eq 'transaction_log_enabled_smtp_failure' ) {
        if ( defined $arg2 ) {
            if (
                compare_transaction_logs(
                    $arg2, return_failed_transaction_log()
                )
              )
            {
                $test{ $heap->{'test'} } = 1;
            }
        }
    }
}

sub create_smtp_message {
    my $body = <<EOB;
To: George Nistorica <george\@localhost>
CC: Root <george\@localhost>
Bcc: Alter Ego <george.nistorica\@localhost>
From: Charlie Root <george\@localhost>
Subject: Email test

Sent with $POE::Component::Client::SMTP::VERSION
EOB

    return $body;
}

sub error_handler {
    carp "Something nasty happened";
    exit 100;
}

sub handle_client_input {
    my ( $heap, $input ) = @_[ HEAP, ARG0 ];
    carp "handle_client_input" if ( $debug == 2 );

    if ( $input =~ /^ehlo fail/i or $input =~ /^helo fail/i ) {

        # this is for the error part
        $heap->{'client'}->put('500 error');
    }
    elsif ( $input =~ /^(ehlo|helo|mail from:|rcpt to:|data|\.|quit)/i ) {
        my $client = $heap->{'client'};
        $heap->{'client'}
          ->put( shift @{ $heap->{'smtp_server_responses'}->{$client} } );
    }
}

sub handle_client_connect {
    my $heap   = $_[HEAP];
    my $client = $heap->{'client'};
    @{ $heap->{'smtp_server_responses'}->{$client} } = @smtp_server_responses;
    $heap->{'client'}
      ->put( shift @{ $heap->{'smtp_server_responses'}->{$client} } );
}

sub handle_client_disconnect {
    my $heap   = $_[HEAP];
    my $client = $heap->{'client'};
    delete $heap->{'smtp_server_responses'}->{$client};
    carp "handle_client_disconnect" if ( $debug == 2 );
}

sub handle_client_error {
    my $heap   = $_[HEAP];
    my $client = $heap->{'client'};
    delete $heap->{'smtp_server_responses'}->{$client};
    carp "handle_client_error" if ( $debug == 2 );
}

sub handle_client_flush {
    carp "handle_client_flush" if ( $debug == 2 );
}

sub return_failed_transaction_log {
    my @transaction_log = (
        '<- 220 localhost ESMTP POE::Component::Client::SMTP Test Server',
        '-> HELO Fail', '<- 500 error'
    );

    return \@transaction_log;
}

sub return_transaction_log {
    my @transaction_log = (
        '<- 220 localhost ESMTP POE::Component::Client::SMTP Test Server',
        '-> HELO localhost',
        '<- 250-localhost',
        '<- 250-PIPELINING',
        '<- 250-SIZE 250000000',
        '<- 250-VRFY',
        '<- 250-ETRN',
        '<- 250 8BITMIME',
        '-> MAIL FROM: <george@localhost>',
        '<- 250 Ok',
        '-> RCPT TO: <george@localhost,>',
        '<- 250 Ok',
        '-> RCPT TO: <root@localhost,>',
        '<- 250 Ok',
        '-> RCPT TO: <george.nistorica@localhost,>',
        '<- 250 Ok',
        '-> DATA',
        '<- 354 End data with <CR><LF>.<CR><LF>',
        '-> To: George Nistorica <george@localhost>
CC: Root <george@localhost>
Bcc: Alter Ego <george.nistorica@localhost>
From: Charlie Root <george@localhost>
Subject: Email test

Sent with ' . $POE::Component::Client::SMTP::VERSION . '

' . "\r" . '.',

        '<- 250 Ok: queued as 549B14484F',
        '-> QUIT',
        '<- 221 Bye'
    );

    return \@transaction_log;
}

sub compare_transaction_logs {
    my $transaction_log          = shift;
    my $expected_transaction_log = shift;

    my $same = 1;

    my ( @actual, @expected );

    foreach my $line ( @{$transaction_log} ) {
        $line =~ s /(\r)|(\n)|(\r\n)//g;

        #         push @actual, split //, $line;
    }
    foreach my $line ( @{$expected_transaction_log} ) {
        $line =~ s /(\r)|(\n)|(\r\n)//g;

        #         push @expected, split //, $line;
    }

    if ( scalar @{$transaction_log} != scalar @{$expected_transaction_log} ) {
        warn "Transaction logs differ!";
        $same = 0;
    }
    else {
        for ( my $i = 0 ; $i < scalar @{$transaction_log} ; $i++ ) {
            if ( $transaction_log->[$i] ne $expected_transaction_log->[$i] ) {
                $same = 0;
                last;
            }
        }
    }

#     if ( scalar @actual != scalar @expected ){
#         print "Number of transaction log characters differ!\n";
#     }
#
#     for (my $i = 0; $i<scalar(@actual); $i++){
#         if ( $actual[$i] ne $expected[$i]){
#             print "Element: \"$actual[$i]\" differs from: \"expected[$i]\"\n";
#         }
#     }

    return $same;
}


syntax highlighted by Code2HTML, v. 0.9.1