File Coverage

blib/lib/NetSDS/App/SMTPD.pm
Criterion Covered Total %
statement 15 15 100.0
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 20 20 100.0


line stmt bran cond sub pod time code
1             package NetSDS::App::SMTPD;
2              
3 2     2   15894 use 5.8.0;
  2         8  
  2         97  
4 2     2   12 use strict;
  2         4  
  2         67  
5 2     2   10 use warnings;
  2         5  
  2         76  
6              
7             package NetSDS::App::SMTPD::Socket;
8              
9 2     2   2116 use IO::Socket;
  2         42956  
  2         10  
10 2     2   1363 use base 'NetSDS::App';
  2         5  
  2         332  
11              
12             use version; our $VERSION = '1.301';
13              
14             sub new {
15             my ( $proto, %args ) = @_;
16              
17             my $class = ref $proto || $proto;
18             my $self = ( %args ? $class->SUPER::new(%args) : bless {}, $class );
19            
20             return $self->create_socket( $args{'port'} );
21             }
22              
23             sub create_socket {
24             my $self = shift;
25             my $socket = IO::Socket->new;
26              
27             $socket->socket( PF_INET, SOCK_STREAM, scalar getprotobyname('tcp') );
28             $socket->blocking(0);
29             $self->{'_socket'} = $socket;
30             return $self;
31             }
32              
33             sub get_socket_handle { +shift->{'_socket'} }
34             sub close { +shift->get_socket_handle->close }
35              
36             package NetSDS::App::SMTPD::Client;
37              
38             use Net::Server::Mail::SMTP;
39             use base 'NetSDS::App::SMTPD::Socket';
40              
41             sub set_smtp {
42             my $self = shift;
43             $self->{'ip'} = shift;
44              
45             $self->{'_smtp'} = Net::Server::Mail::SMTP->new( socket => $self->get_socket_handle );
46             return $self;
47             }
48              
49             sub set_callback { +shift->get_smtp->set_callback(@_) }
50             sub process { +shift->get_smtp->process(@_) }
51             sub get_smtp { +shift->{'_smtp'} }
52             sub get_header { $_[0]->{'headers'}{ lc $_[1] } }
53             sub get_msg { +shift->{'msg'} }
54             sub get_ip { +shift->{'ip'} }
55              
56             sub get_mail {
57             my ( $self, $data ) = @_;
58             my @lines = split /\r\n(?! )/, $$data;
59              
60             $self->{'headers'} = {};
61             my $i;
62              
63             for ( $i = 0 ; $lines[$i] ; $i++ ) {
64             my ( $key, $value ) = split /:\s*/, $lines[$i], 2;
65              
66             $key = lc $key;
67              
68             if ( exists $self->{'headers'}{$key} ) {
69             unless ( ref $self->{'headers'}{$key} ) {
70             my $temp = $self->{'headers'}{$key};
71             $self->{'headers'}{$key} = [ $temp, $value ];
72             } else {
73             push @{ $self->{'headers'}{$key} }, $value;
74             }
75             } else {
76             $self->{'headers'}{$key} = $value; #TODO fix me could be several Received
77             }
78             }
79              
80             $self->{'msg'} = join "\r\n", @lines[ $i + 1 .. $#lines ];
81             return 1;
82             } ## end sub get_mail
83              
84             package NetSDS::App::SMTPD;
85              
86             use base 'NetSDS::App::SMTPD::Socket';
87             use IO::Socket;
88              
89             sub create_socket {
90             my ( $self, $port ) = @_;
91             $port ||= 2525;
92             return unless $port;
93              
94             $self->SUPER::create_socket;
95              
96             setsockopt( $self->get_socket_handle, SOL_SOCKET, SO_REUSEADDR, 1 );
97             bind( $self->get_socket_handle, sockaddr_in( $port, INADDR_ANY ) ) or die "Can't use port $port";
98             listen( $self->get_socket_handle, SOMAXCONN ) or die "Can't listen on port: $port";
99              
100             return $self;
101             }
102              
103             sub can_read {
104             my $self = shift;
105             my $rin = '';
106              
107             vec( $rin, fileno( $self->get_socket_handle ), 1 ) = 1;
108             return select( $rin, undef, undef, undef );
109             }
110              
111             sub accept {
112             my $self = shift;
113             $self->can_read;
114              
115             my $client = NetSDS::App::SMTPD::Client->new;
116             my $peer = accept( $client->get_socket_handle, $self->get_socket_handle );
117              
118             if ($peer) {
119             $client->set_smtp( inet_ntoa( ( sockaddr_in($peer) )[1] ) );
120             $self->speak( "connection from ip [" . $client->get_ip . "]" );
121             $client->set_callback( DATA => \&data, $client );
122              
123             return $client;
124             }
125             }
126              
127             sub data {
128             my ( $smtp, $data ) = @_;
129             return $smtp->{'_context'}->get_mail($data);
130             }
131              
132             sub process {
133             my $self = shift;
134             my $client = $self->accept;
135              
136             return unless $client;
137             $client->process;
138              
139             $client->close;
140             $self->speak( "connection from ip [" . $client->get_ip . "] closed" );
141              
142             return $client;
143             }
144              
145             1;
146              
147             __END__