File Coverage

lib/Haineko/SMTPD/Session.pm
Criterion Covered Total %
statement 120 136 88.2
branch 32 60 53.3
condition 20 30 66.6
subroutine 19 19 100.0
pod 3 12 25.0
total 194 257 75.4


line stmt bran cond sub pod time code
1             package Haineko::SMTPD::Session;
2 3     3   3002 use feature ':5.10';
  3         8  
  3         389  
3 3     3   19 use strict;
  3         6  
  3         106  
4 3     3   17 use warnings;
  3         6  
  3         89  
5 3     3   942 use Class::Accessor::Lite;
  3         1117  
  3         27  
6 3     3   25045 use Haineko::SMTPD::Response;
  3         8  
  3         112  
7 3     3   2494 use Haineko::SMTPD::Address;
  3         8  
  3         92  
8 3     3   1201 use Time::Piece;
  3         21752  
  3         26  
9              
10             my $rwaccessors = [
11             'stage', # (Integer)
12             'started', # (Time::Piece) When it connected
13             'response', # (Ref->Array->Haineko::SMTPD::Response) SMTP Reponse
14             'addresser', # (Haineko::SMTPD::Address) Envelope sender
15             'recipient', # (Ref->Arrey->Haineko::SMTPD::Address) Envelope recipients
16             ];
17             my $roaccessors = [
18             'queueid', # (String) Queue ID
19             'referer', # (String) HTTP REFERER
20             'useragent', # (String) User agent name
21             'remoteaddr', # (String) Client IP address
22             'remoteport', # (String) Client port number
23             ];
24             my $woaccessors = [];
25             Class::Accessor::Lite->mk_accessors( @$rwaccessors );
26             Class::Accessor::Lite->mk_ro_accessors( @$roaccessors );
27              
28              
29             sub new {
30 15     15 1 573 my $class = shift;
31 15         91 my $argvs = { @_ };
32 15   100     82 my $nekor = $argvs->{'response'} || undef;
33 15   50     124 my $nekos = {
      66        
34             'stage' => $argvs->{'stage'} // 0,
35             'started' => Time::Piece->new,
36             'queueid' => $argvs->{'queueid'} || __PACKAGE__->make_queueid,
37             };
38              
39 15 100       866 if( $nekor ) {
40 2 100       13 if( ref $nekor eq 'Haineko::SMTPD::Response' ) {
    50          
41             # Response in the argument is an object
42 1         3 $nekos->{'response'} = [ $nekor ];
43              
44             } elsif( ref $nekor eq 'ARRAY' ) {
45             # Response in the argument is an array reference
46 1         3 $nekos->{'response'} = [];
47 1         3 for my $e ( @$nekor ) {
48             # Check each item:
49             # Haineko::SMTPD::Response object or HASH reference
50 1 50       8 if( ref $e eq 'Haineko::SMTPD::Response' ) {
    50          
51 0         0 push @{ $nekos->{'response'} }, $e;
  0         0  
52              
53             } elsif( ref $e eq 'HASH' ) {
54             # Create Haineko::SMTPD::Response object from the HASH reference
55 1         2 push @{ $nekos->{'response'} }, Haineko::SMTPD::Response->new( %$e );
  1         8  
56             }
57             }
58             }
59             }
60 15   100     30 map { $nekos->{ $_ } ||= $argvs->{ $_ } || undef } @$roaccessors;
  75   100     423  
61              
62 15         20 while(1) {
63             # Create email address objects
64 15         23 my $c = 'Haineko::SMTPD::Address';
65 15         29 my $r = [];
66 15   100     80 my $t = $argvs->{'recipient'} || [];
67              
68 15         33 map { push @$r, $c->new( 'address' => $_ ) } @$t;
  1         8  
69 15 100       43 $nekos->{'recipient'} = $r if scalar @$r;
70              
71 15 100       66 last unless defined $argvs->{'addresser'};
72 1         4 $nekos->{'addresser'} = $c->new( 'address' => $argvs->{'addresser'} );
73              
74 1         3 last;
75             }
76 15         85 return bless $nekos, __PACKAGE__;
77             }
78              
79             sub make_queueid {
80 17     17 1 11924 my $class = shift;
81 17         65 my $size1 = 16;
82 17         150 my $time1 = new Time::Piece;
83 17         2056 my $chars = [ '0'..'9', 'A'..'Z', 'a'..'x' ];
84 17         44 my $idstr = q();
85 17         127 my $queue = {
86             'Y' => $chars->[ $time1->_year % 60 ],
87             'M' => $chars->[ $time1->_mon ],
88             'D' => $chars->[ $time1->mday ],
89             'h' => $chars->[ $time1->hour ],
90             'm' => $chars->[ $time1->min ],
91             's' => $chars->[ $time1->sec ],
92             'q' => $chars->[ int rand(60) ],
93             'p' => sprintf( "%05d", $$ ),
94             };
95              
96 17         1136 $idstr .= $queue->{ $_ } for ( qw/Y M D h m s q p/ );
97              
98 17         51 while(1) {
99 68         123 $idstr .= $chars->[ int rand( scalar( @$chars ) ) ];
100 68 100       180 last if length $idstr == $size1;
101             }
102 17         273 return $idstr;
103             }
104              
105             sub done {
106 28     28 0 480 my $class = shift;
107 28   50     83 my $argvs = shift || return 0; # (String) SMTP Command
108 28         146 my $value = {
109             'ehlo' => ( 1 << 0 ),
110             'auth' => ( 1 << 1 ),
111             'mail' => ( 1 << 2 ),
112             'rcpt' => ( 1 << 3 ),
113             'data' => ( 1 << 4 ),
114             'quit' => ( 1 << 5 ),
115             };
116 28   50     160 return $value->{ $argvs } || 0;
117             }
118              
119             sub add_response {
120 14     14 0 25 my $self = shift;
121 14   50     52 my $argv = shift || return $self;
122              
123 14 50       50 return $self unless ref $argv eq 'Haineko::SMTPD::Response';
124 14         20 push @{ $self->{'response'} }, $argv;
  14         52  
125 14         32 return $self;
126             }
127              
128             sub ehlo {
129 9     9 0 591 my $self = shift;
130 9   50     27 my $argv = shift || 0; # (Integer)
131 9         61 my $ehlo = __PACKAGE__->done('ehlo');
132 9 50       36 $self->{'stage'} = $ehlo if $argv;
133 9 50       42 return $self->{'stage'} & $ehlo ? 1 : 0;
134             }
135              
136             sub auth {
137 1     1 0 652 my $self = shift;
138 1   50     5 my $argv = shift || 0;
139 1         5 my $auth = __PACKAGE__->done('auth');
140 1 50       5 $self->{'stage'} |= $auth if $argv;
141 1 50       6 return $self->{'stage'} & $auth ? 1 : 0;
142             }
143              
144             sub mail {
145 7     7 0 577 my $self = shift;
146 7   50     27 my $argv = shift || 0;
147 7         20 my $mail = __PACKAGE__->done('mail');
148 7 50       25 $self->{'stage'} |= $mail if $argv;
149 7 50       30 return $self->{'stage'} & $mail ? 1 : 0;
150             }
151              
152             sub rcpt {
153 3     3 0 522 my $self = shift;
154 3   50     13 my $argv = shift || 0;
155 3         11 my $rcpt = __PACKAGE__->done('rcpt');
156 3 50       12 $self->{'stage'} |= $rcpt if $argv;
157 3 50       15 return $self->{'stage'} & $rcpt ? 1 : 0;
158             }
159              
160             sub data {
161 1     1 0 552 my $self = shift;
162 1   50     5 my $argv = shift || 0;
163 1         3 my $data = __PACKAGE__->done('data');
164 1 50       5 $self->{'stage'} |= $data if $argv;
165 1 50       5 return $self->{'stage'} & $data ? 1 : 0;
166             }
167              
168             sub rset {
169 1     1 0 512 my $self = shift;
170 1         5 $self->{'stage'} = __PACKAGE__->done('ehlo');
171 1         3 return 1;
172             }
173              
174             sub quit {
175 1     1 0 537 my $self = shift;
176 1         4 $self->{'stage'} = 0;
177 1         3 return 1;
178             }
179              
180             sub damn {
181 13     13 1 100 my $self = shift;
182 13         25 my $smtp = {};
183              
184 13         40 for my $e ( @$rwaccessors, @$roaccessors ) {
185              
186 130 100       506 next if $e =~ m/(?:response|addresser|recipient|started|stage)/;
187 65         157 $smtp->{ $e } = $self->{ $e };
188             }
189              
190 13         19 while(1) {
191 13 50       49 last unless defined $self->{'addresser'};
192 0 0       0 last unless ref $self->{'addresser'};
193 0 0       0 last unless ref $self->{'addresser'} eq 'Haineko::SMTPD::Address';
194              
195 0         0 $smtp->{'addresser'} = $self->{'addresser'}->address;
196 0         0 last;
197             }
198              
199 13         21 while(1) {
200 13 50       45 last unless defined $self->{'recipient'};
201 0 0       0 last unless ref $self->{'recipient'} eq 'ARRAY';
202              
203 0         0 $smtp->{'recipient'} = [];
204 0         0 for my $e ( @{ $self->{'recipient'} } ) {
  0         0  
205              
206 0 0       0 next unless ref $e eq 'Haineko::SMTPD::Address';
207 0         0 push @{ $smtp->{'recipient'} }, $e->address;
  0         0  
208             }
209 0         0 last;
210             }
211              
212 13         25 while(1) {
213 13 50       37 last unless defined $self->{'response'};
214 13 50       159 last unless ref $self->{'response'} eq 'ARRAY';
215              
216 13         33 $smtp->{'response'} = [];
217 13         23 for my $e ( @{ $self->{'response'} } ) {
  13         34  
218 15 50       37 next unless ref $e eq 'Haineko::SMTPD::Response';
219 15         20 push @{ $smtp->{'response'} }, $e->damn;
  15         50  
220             }
221              
222 13 50       23 last if scalar @{ $smtp->{'response'} };
  13         42  
223 0         0 $smtp->{'response'} = [ Haineko::SMTPD::Response->new ];
224 0         0 last;
225             }
226              
227 13         655 $smtp->{'timestamp'} = {
228             'datetime' => $self->started->cdate,
229             'unixtime' => $self->started->epoch,
230             };
231 13         921 return $smtp;
232             }
233              
234             1;
235             __END__