File Coverage

blib/lib/Net/SMTP/Server/AnyEvent.pm
Criterion Covered Total %
statement 18 112 16.0
branch 0 32 0.0
condition 0 17 0.0
subroutine 6 15 40.0
pod 2 2 100.0
total 26 178 14.6


line stmt bran cond sub pod time code
1             package Net::SMTP::Server::AnyEvent;
2            
3 1     1   23801 use 5.006;
  1         4  
  1         40  
4 1     1   6 use strict;
  1         2  
  1         42  
5 1     1   6 use warnings FATAL => 'all';
  1         6  
  1         51  
6 1     1   1027 use AnyEvent::Socket;
  1         36771  
  1         167  
7 1     1   1408 use AnyEvent::Handle;
  1         14611  
  1         1481  
8            
9            
10             =head1 NAME
11            
12             Net::SMTP::Server::AnyEvent - Expiremental SMTP server using AnyEvent!
13            
14             =head1 VERSION
15            
16             Version 0.02
17            
18             =cut
19            
20             our $VERSION = '0.02';
21            
22            
23             =head1 SYNOPSIS
24            
25             An attempt at an SMTP server using AnyEvent. This server so far is NOT yet capable of relay and supports only the basic functions. At this point this module is EXPIREMENTAL, so use at your own risk. Functionality can change at any time.
26            
27             use Net::SMTP::Server::AnyEvent;
28            
29             my $smtp = Net::SMTP::Server::AnyEvent->new(
30             Host=>'127.0.0.1',
31             Port=>25,
32             Debug=>1,
33             Commands=>{
34             EHLO=>sub{
35             my ($self,$conn,$data)=@_;
36             print "HELLO!\n";
37             },
38             }
39             );
40            
41            
42            
43             =head1 SUBROUTINES/METHODS
44            
45             =head2 new
46            
47             Host - Host of server
48             Port - Port of server
49             Debug - Enable debug output (Default: 0) [Optional]
50             Commands - Overload commands sent to the server [Optional]
51            
52             EHLO, MAIL, RCPT, DATA, DATASEND, DATAEND, QUIT
53            
54             =cut
55            
56            
57             sub new {
58 0     0 1   my $class=shift;
59 0           my $self={};
60 0           my %new;
61            
62            
63            
64 0   0       bless($self, $class||'Net::SMTP::Server::AnyEvent');
65            
66            
67 0 0         if ($#_ % 2 == 0) {
68 0           $new{Host}=shift;
69             }
70 0           %new=@_;
71 0 0 0       $self->{debug} = (($new{Debug}||0) >= 1) ? $new{Debug}:0;
72 0   0       $self->{debug_path} = $new{DebugPath}||'debug_[HOST]_[PORT].txt';
73 0           $self->{new}=\%new;
74            
75            
76 0           return $self;
77             }
78            
79             sub start {
80 0     0 1   my $self=shift;
81            
82 0 0         if (exists($self->{new}{Hosts})) {
83 0           $self->_HOSTS($self->{new}{Hosts});
84             } else {
85 0           $self->_HOSTS([$self->{new}]);
86             }
87            
88 0           AnyEvent->condvar->recv;
89            
90             }
91            
92             =head2 start
93            
94             Starts the smtp server
95            
96             $smtp->start();
97            
98             =cut
99            
100             sub _HOSTS {
101 0     0     my $self=shift;
102 0           my $hosts=shift;
103            
104 0           foreach my $host (@{$hosts}) {
  0            
105            
106 0   0       my $h=$host->{Host}||'127.0.0.1';
107 0   0       my $p=$host->{Port}||25;
108            
109 0           $self->{host}{ $h }{ $p }=$h;
110 0           $self->{port}{ $h }{ $p }=$p;
111 0   0       $self->{commands}{ $h }{ $p }=$host->{Commands}||{};
112            
113            
114 0 0         if ($self->{debug} == 2) {
115 0           my $path=''.$self->{debug_path};
116 0           $path=~s/\[HOST\]/$h/gs;
117 0           $path=~s/\[PORT\]/$p/gs;
118 0           open( $self->{debug_fh}{ $h.':'.$p } , '>>'.$path );
119 0           binmode( $self->{debug_fh}{ $h.':'.$p } , ':utf8' );
120             }
121            
122 0           $self->_CONNECT( $h, $p );
123            
124             }
125            
126             }
127            
128             sub _CONNECT {
129 0     0     my $self=shift;
130 0           my $h=shift;
131 0           my $p=shift;
132            
133 0 0         $self->_DEBUG([$h,$p],"Listening on $self->{host}{ $h }{ $p } on port $self->{port}{ $h }{ $p }") if $self->{debug} >= 1;
134            
135             tcp_server $self->{host}{ $h }{ $p }, $self->{port}{ $h }{ $p }, sub {
136 0     0     my ($fh, $host, $port) = @_;
137 0           my $handle;
138             $handle = AnyEvent::Handle->new(
139             fh => $fh,
140             poll => 'r',
141             on_read => sub {
142 0           my ($self_read) = @_;
143             $self->{handle}{ $h }{ $p }{ $handle }->push_read (line => sub {
144 0           my ($hdl, $buf) = @_;
145 0 0         $self->_DEBUG([$h,$p,$hdl],$buf) if $self->{debug} >= 1;
146 0           $self->_PROCESS([$h,$p,$hdl],$buf);
147 0           });
148             },
149             on_eof => sub {
150 0           my ($hdl) = @_;
151 0           $hdl->destroy();
152             },
153 0           );
154            
155 0           $self->{handle}{ $h }{ $p }{ $handle }=$handle;
156 0           $self->{data_mode}{ $h }{ $p }{ $handle }='none';
157 0           $self->_WRITE([$h,$p,$handle],"220 $self->{host}{ $h }{ $p } ESMTP Postfix");
158            
159            
160 0           };
161            
162            
163            
164             }
165            
166             sub _PROCESS {
167 0     0     my $self=shift;
168 0           my $k=shift;
169 0           my $buf=shift;
170            
171 0 0 0       if ($buf=~m/^EHLO (.*?)$/is) {
    0          
    0          
    0          
    0          
172 0           $self->_COMMAND($k,'EHLO',$1);
173 0           $self->_WRITE($k,"250-${$k}[0]\015\012250-SIZE 31457280\015\012250 OK");
  0            
174             } elsif ($buf=~m/^QUIT$/is) {
175 0           $self->_COMMAND($k,'QUIT');
176 0           $self->_WRITE($k,'221 Service closing transmission channel');
177 0           ${ $k }[2]->destroy();
  0            
178             } elsif ($buf=~m/^DATA$/is) {
179 0           $self->_COMMAND($k,'DATA');
180 0           $self->{data_mode}{ $k->[0] }{ $k->[1] }{ $k->[2] }='data';
181 0           $self->{store}{data}{ $k->[0] }{ $k->[1] }{ $k->[2] }='';
182 0           $self->_WRITE($k,'354 End data with .');
183             } elsif ($self->{data_mode}{ $k->[0] }{ $k->[1] }{ $k->[2] } eq 'data' and $buf eq '.') {
184 0           $self->_COMMAND($k,'DATAEND',$self->{store}{data}{ $k->[0] }{ $k->[1] }{ $k->[2] });
185 0           $self->{data_mode}{ $k->[0] }{ $k->[1] }{ $k->[2] }='none';
186 0           delete($self->{store}{data}{ $k->[0] }{ $k->[1] }{ $k->[2] });
187 0           $self->_WRITE($k,'250 OK, Message Received');
188             } elsif ( $self->{data_mode}{ $k->[0] }{ $k->[1] }{ $k->[2] } eq 'data' ) {
189 0           $self->_COMMAND($k,'DATASEND',$buf);
190 0           $self->{store}{data}{ $k->[0] }{ $k->[1] }{ $k->[2] }.=$buf;
191             } else {
192            
193 0 0         if ($buf=~m/^MAIL FROM:(?: |)(.*?)$/is) {
    0          
194 0           $self->_COMMAND($k,'MAIL',$1);
195             } elsif ($buf=~m/^RCPT TO:(?: |)(.*?)$/is) {
196 0           $self->_COMMAND($k,'RCPT',$1);
197             } else {
198 0           $self->_COMMAND($k,'DEFAULT',$buf);
199             }
200 0           $self->_WRITE($k,'250 OK');
201             }
202             }
203            
204             sub _COMMAND {
205 0     0     my $self=shift;
206 0           my $k=shift;
207 0           my $cmd=shift;
208 0           my $data=shift;
209            
210 1     1   13 no strict;
  1         2  
  1         355  
211 0 0         &{ $self->{commands}{ $k->[0] }{ $k->[1] }{ $cmd } }($self,$k,$data) if exists($self->{commands}{ $k->[0] }{ $k->[1] }{ $cmd });
  0            
212            
213             }
214            
215             sub _WRITE {
216 0     0     my $self=shift;
217 0           my $k=shift;
218 0           my $cont=shift;
219            
220 0 0         $self->_DEBUG($k,'> '.$cont) if $self->{debug} >= 1;
221 0           ${$k}[2]->push_write($cont."\015\012")
  0            
222            
223             }
224            
225             sub _DEBUG {
226 0     0     my $self=shift;
227 0           my $k=shift;
228 0   0       my $str=shift||'';
229 0 0         if ($self->{debug} == 1) {
230 0           print '['.$k->[0].':'.$k->[1].'] '.$str."\r\n";
231             } else {
232 0           syswrite $self->{debug_fh}{ $k->[0].':'.$k->[1] }, '['.$k->[0].':'.$k->[1].'] '.$str."\r\n";
233            
234             }
235             }
236            
237            
238             =head1 AUTHOR
239            
240             KnowZero
241            
242             =head1 BUGS
243            
244             Please report any bugs or feature requests to C, or through
245             the web interface at L. I will be notified, and then you'll
246             automatically be notified of progress on your bug as I make changes.
247            
248            
249            
250            
251             =head1 SUPPORT
252            
253             You can find documentation for this module with the perldoc command.
254            
255             perldoc Net::SMTP::Server::AnyEvent
256            
257            
258             You can also look for information at:
259            
260             =over 4
261            
262             =item * RT: CPAN's request tracker (report bugs here)
263            
264             L
265            
266             =item * AnnoCPAN: Annotated CPAN documentation
267            
268             L
269            
270             =item * CPAN Ratings
271            
272             L
273            
274             =item * Search CPAN
275            
276             L
277            
278             =back
279            
280            
281             =head1 ACKNOWLEDGEMENTS
282            
283            
284             =head1 LICENSE AND COPYRIGHT
285            
286             Copyright 2013 KnowZero.
287            
288             This program is free software; you can redistribute it and/or modify it
289             under the terms of the the Artistic License (2.0). You may obtain a
290             copy of the full license at:
291            
292             L
293            
294             Any use, modification, and distribution of the Standard or Modified
295             Versions is governed by this Artistic License. By using, modifying or
296             distributing the Package, you accept this license. Do not use, modify,
297             or distribute the Package, if you do not accept this license.
298            
299             If your Modified Version has been derived from a Modified Version made
300             by someone other than you, you are nevertheless required to ensure that
301             your Modified Version complies with the requirements of this license.
302            
303             This license does not grant you the right to use any trademark, service
304             mark, tradename, or logo of the Copyright Holder.
305            
306             This license includes the non-exclusive, worldwide, free-of-charge
307             patent license to make, have made, use, offer to sell, sell, import and
308             otherwise transfer the Package with respect to any patent claims
309             licensable by the Copyright Holder that are necessarily infringed by the
310             Package. If you institute patent litigation (including a cross-claim or
311             counterclaim) against any party alleging that the Package constitutes
312             direct or contributory patent infringement, then this Artistic License
313             to you shall terminate on the date that such litigation is filed.
314            
315             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
316             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
317             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
318             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
319             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
320             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
321             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
322             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
323            
324            
325             =cut
326            
327             1; # End of Net::SMTP::Server::AnyEvent