File Coverage

lib/Haineko/SMTPD/Relay/Haineko.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Haineko::SMTPD::Relay::Haineko;
2 1     1   4148 use parent 'Haineko::SMTPD::Relay';
  1         395  
  1         6  
3 1     1   47 use strict;
  1         2  
  1         34  
4 1     1   5 use warnings;
  1         1  
  1         26  
5 1     1   1605 use Furl;
  0            
  0            
6             use Try::Tiny;
7             use Time::Piece;
8             use Haineko::JSON;
9             use Haineko::SMTPD::Response;
10              
11             sub new {
12             my $class = shift;
13             my $argvs = { @_ };
14              
15             $argvs->{'time'} ||= Time::Piece->new;
16             $argvs->{'sleep'} ||= 5;
17             $argvs->{'timeout'} ||= 30;
18             return bless $argvs, __PACKAGE__;
19             }
20              
21             sub sendmail {
22             my $self = shift;
23              
24             my $hainekourl = sprintf( "http://%s:%d/submit", $self->{'host'}, $self->{'port'} );
25             my $parameters = {
26             'ehlo' => $self->{'ehlo'},
27             'mail' => $self->{'mail'},
28             'rcpt' => [ $self->{'rcpt'} ],
29             'header' => {
30             'from' => $self->{'head'}->{'From'},
31             'subject' => $self->{'head'}->{'Subject'},
32             'charset' => $self->{'attr'}->{'charset'},
33             },
34             'body' => $self->{'body'},
35             };
36              
37             if( $self->{'head'}->{'Reply-To'} ) {
38             $parameters->{'header'}->{'replyto'} = $self->{'head'}->{'Reply-To'};
39             }
40              
41             my $jsonstring = Haineko::JSON->dumpjson( $parameters );
42             my $httpheader = [];
43             my $httpobject = undef;
44             my $htresponse = undef;
45             my $hainekores = undef;
46              
47             if( $self->{'username'} && $self->{'password'} ) {
48             # Encode credentials for Basic-Authentication
49             require MIME::Base64;
50             my $v = MIME::Base64::encode_base64( $self->{'username'}.':'.$self->{'password'} );
51             $httpheader = [ 'Authorization' => sprintf( "Basic %s", $v ) ];
52             }
53              
54             $httpobject = Furl->new(
55             'agent' => __PACKAGE__,
56             'timeout' => 10,
57             'headers' => $httpheader,
58             'ssl_opts' => { 'SSL_verify_mode' => 0 }
59             );
60              
61             my $smtpstatus = 0;
62             my $retryuntil = $self->{'retry'} || 0;
63              
64             my $sendmailto = sub {
65             $htresponse = $httpobject->post( $hainekourl, $httpheader, $jsonstring );
66              
67             return 0 unless defined $htresponse;
68             return 0 unless $htresponse->is_success;
69              
70             $smtpstatus = 1;
71             return 1;
72             };
73              
74             while(1) {
75             last if $sendmailto->();
76             last if $retryuntil == 0;
77              
78             $retryuntil--;
79             sleep $self->{'sleep'};
80             }
81              
82             if( defined $htresponse ) {
83             # Check the response from another Haineko
84             my $htcontents = undef;
85             my $nekoparams = {
86             'code' => $htresponse->code,
87             'host' => $self->{'host'},
88             'port' => $self->{'port'},
89             'rcpt' => $self->{'rcpt'},
90             'error' => $htresponse->is_success ? 0 : 1,
91             'mailer' => 'Haineko',
92             'message' => [],
93             'command' => 'POST',
94             };
95              
96             if( $htresponse->body =~ m/Cannot connect to\s/ ) {
97             # Cannot connect to 192.0.2.1:2794: timeout at
98             $self->response( Haineko::SMTPD::Response->r( 'conn', 'cannot-connect' ) );
99             map { $self->response->{ $_ } = $self->{ $_ } } ( qw|host port rcpt| );
100              
101             } else {
102             # Received as a JSON ?
103             try {
104             my $c = $htresponse->body || q();
105             my $v = {};
106              
107             if( $c =~ m/(Failed to send HTTP request)/ ) {
108             # Failed to send HTTP request
109             # $htcontents = { 'response' => $1 };
110             $nekoparams->{'message'} = [ $1 ];
111             } else {
112             # Response maybe JSON
113             $htcontents = Haineko::JSON->loadjson( $c ) || {};
114             $v = $htcontents->{'response'} || {};
115             $nekoparams->{'dsn'} ||= $v->{'dsn'};
116             push @{ $nekoparams->{'message'} }, ( $v->{'message'}->[-1] || q() );
117             }
118              
119             } catch {
120              
121             $nekoparams->{'message'}->[0] = $_;
122             };
123             $self->response( Haineko::SMTPD::Response->new( %$nekoparams ) );
124             }
125             } else {
126             $self->response( Haineko::SMTPD::Response->r( 'conn', 'cannot-connect' ) );
127             map { $self->response->{ $_ } = $self->{ $_ } } ( qw|host port rcpt| );
128             }
129              
130             return $smtpstatus;
131             }
132              
133             1;
134             __END__