| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package AnyEvent::SMTP::Server; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | =head1 NAME | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | AnyEvent::SMTP::Server - Simple asyncronous SMTP Server | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | =cut | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 12 |  |  | 12 |  | 65370 | use Carp; | 
|  | 12 |  |  |  |  | 19 |  | 
|  | 12 |  |  |  |  | 1032 |  | 
| 10 | 12 |  |  | 12 |  | 68 | use AnyEvent; | 
|  | 12 |  |  |  |  | 22 |  | 
|  | 12 |  |  |  |  | 286 |  | 
| 11 | 12 |  |  | 12 |  | 783 | use common::sense; | 
|  | 12 |  |  |  |  | 25 |  | 
|  | 12 |  |  |  |  | 226 |  | 
| 12 |  |  |  |  |  |  | m{# trying to cheat with cpants game ;) | 
| 13 |  |  |  |  |  |  | use strict; | 
| 14 |  |  |  |  |  |  | use warnings; | 
| 15 |  |  |  |  |  |  | }x; | 
| 16 |  |  |  |  |  |  |  | 
| 17 | 12 |  |  | 12 |  | 968 | use base 'Object::Event'; | 
|  | 12 |  |  |  |  | 25 |  | 
|  | 12 |  |  |  |  | 13670 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 12 |  |  | 12 |  | 117527 | use AnyEvent::Handle; | 
|  | 12 |  |  |  |  | 111727 |  | 
|  | 12 |  |  |  |  | 577 |  | 
| 20 | 12 |  |  | 12 |  | 130 | use AnyEvent::Socket; | 
|  | 12 |  |  |  |  | 26 |  | 
|  | 12 |  |  |  |  | 2228 |  | 
| 21 | 12 |  |  | 12 |  | 99 | use AnyEvent::Util; | 
|  | 12 |  |  |  |  | 28 |  | 
|  | 12 |  |  |  |  | 1956 |  | 
| 22 |  |  |  |  |  |  |  | 
| 23 | 12 |  |  | 12 |  | 11794 | use Sys::Hostname; | 
|  | 12 |  |  |  |  | 15495 |  | 
|  | 12 |  |  |  |  | 1182 |  | 
| 24 | 12 |  |  | 12 |  | 10339 | use Mail::Address; | 
|  | 12 |  |  |  |  | 31423 |  | 
|  | 12 |  |  |  |  | 448 |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 | 12 |  |  | 12 |  | 7324 | use AnyEvent::SMTP::Conn; | 
|  | 12 |  |  |  |  | 30 |  | 
|  | 12 |  |  |  |  | 2153 |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 | 12 |  |  | 12 |  | 79 | our $VERSION = $AnyEvent::SMTP::VERSION;use AnyEvent::SMTP (); | 
|  | 12 |  |  |  |  | 20 |  | 
|  | 12 |  |  |  |  | 58479 |  | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | our %CMD = map { $_ => 1 } qw( HELO EHLO MAIL RCPT QUIT DATA EXPN VRFY NOOP HELP RSET ); | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | use AnyEvent::SMTP::Server 'smtp_server'; | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | smtp_server undef, 2525, sub { | 
| 37 |  |  |  |  |  |  | my $mail = shift; | 
| 38 |  |  |  |  |  |  | warn "Received mail from $mail->{from} to $mail->{to}\n$mail->{data}\n"; | 
| 39 |  |  |  |  |  |  | }; | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | # or | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | use AnyEvent::SMTP::Server; | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | my $server = AnyEvent::SMTP::Server->new( | 
| 46 |  |  |  |  |  |  | port => 2525, | 
| 47 |  |  |  |  |  |  | mail_validate => sub { | 
| 48 |  |  |  |  |  |  | my ($m,$addr) = @_; | 
| 49 |  |  |  |  |  |  | if ($good) { return 1 } else { return 0, 513, 'Bad sender.' } | 
| 50 |  |  |  |  |  |  | }, | 
| 51 |  |  |  |  |  |  | rcpt_validate => sub { | 
| 52 |  |  |  |  |  |  | my ($m,$addr) = @_; | 
| 53 |  |  |  |  |  |  | if ($good) { return 1 } else { return 0, 513, 'Bad recipient.' } | 
| 54 |  |  |  |  |  |  | }, | 
| 55 |  |  |  |  |  |  | data_validate => sub { | 
| 56 |  |  |  |  |  |  | my ($m,$data) = @_; | 
| 57 |  |  |  |  |  |  | my $size = length $data; | 
| 58 |  |  |  |  |  |  | if ($size > $max_email_size) { | 
| 59 |  |  |  |  |  |  | return 0, 552, 'REJECTED: message size limit exceeded'; | 
| 60 |  |  |  |  |  |  | } else { | 
| 61 |  |  |  |  |  |  | return 1; | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  | }, | 
| 64 |  |  |  |  |  |  | ); | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | $server->reg_cb( | 
| 67 |  |  |  |  |  |  | client => sub { | 
| 68 |  |  |  |  |  |  | my ($s,$con) = @_; | 
| 69 |  |  |  |  |  |  | warn "Client from $con->{host}:$con->{port} connected\n"; | 
| 70 |  |  |  |  |  |  | }, | 
| 71 |  |  |  |  |  |  | disconnect => sub { | 
| 72 |  |  |  |  |  |  | my ($s,$con) = @_; | 
| 73 |  |  |  |  |  |  | warn "Client from $con->{host}:$con->{port} gone\n"; | 
| 74 |  |  |  |  |  |  | }, | 
| 75 |  |  |  |  |  |  | mail => sub { | 
| 76 |  |  |  |  |  |  | my ($s,$mail) = @_; | 
| 77 |  |  |  |  |  |  | warn "Received mail from ($mail->{host}:$mail->{port}) $mail->{from} to $mail->{to}\n$mail->{data}\n"; | 
| 78 |  |  |  |  |  |  | }, | 
| 79 |  |  |  |  |  |  | ); | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | $server->start; | 
| 82 |  |  |  |  |  |  | AnyEvent->condvar->recv; | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | Simple asyncronous SMTP server. Authorization not implemented yet. Patches are welcome | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | =head1 FUNCTIONS | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | =head2 smtp_server $host, $port, $cb->(MAIL) | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | =head1 METHODS | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | =head2 new %args; | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | =over 4 | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | =item hosthame | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | Server FQDN | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | =item host | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | Address to listen on. by default - undef (0.0.0.0) | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | =item port | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | Port to listen on | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | =back | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | =head2 start | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | Creates tcp server and starts to listen | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | =head2 stop | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | Closes all opened connections and shutdown server | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | =head1 EVENTS | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | =over 4 | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | =item ready() | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | Invoked when server is ready | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | =item client($connection) | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | Invoked when client connects | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | =item disconnect($connection) | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | Invoked when client disconnects | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | =item mail($mail) | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | Invoked when server received complete mail message | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | $mail = { | 
| 141 |  |  |  |  |  |  | from => ..., | 
| 142 |  |  |  |  |  |  | to   => [ ... ], | 
| 143 |  |  |  |  |  |  | data => '...', | 
| 144 |  |  |  |  |  |  | host => 'remote addr', | 
| 145 |  |  |  |  |  |  | port => 'remote port', | 
| 146 |  |  |  |  |  |  | helo => 'HELO/EHLO string', | 
| 147 |  |  |  |  |  |  | }; | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | =back | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | =cut | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | sub import { | 
| 154 | 2 |  |  | 2 |  | 20 | my $me = shift; | 
| 155 | 2 |  |  |  |  | 7 | my $pkg = caller; | 
| 156 |  |  |  |  |  |  |  | 
| 157 | 2 | 50 |  |  |  | 47 | @_ or return; | 
| 158 | 0 |  |  |  |  | 0 | for (@_) { | 
| 159 | 0 | 0 |  |  |  | 0 | if ( $_ eq 'smtp_server') { | 
| 160 | 0 |  |  |  |  | 0 | *{$pkg.'::'.$_} = \&$_; | 
|  | 0 |  |  |  |  | 0 |  | 
| 161 |  |  |  |  |  |  | } else { | 
| 162 | 0 |  |  |  |  | 0 | croak "$_ is not exported by $me"; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  | } | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | sub smtp_server { | 
| 168 | 5 |  |  | 5 | 1 | 17482 | my ($host,$port,$cb) = @_; | 
| 169 | 5 |  |  |  |  | 437 | my $server = AnyEvent::SMTP::Server->new( | 
| 170 |  |  |  |  |  |  | host => $host, | 
| 171 |  |  |  |  |  |  | port => $port, | 
| 172 |  |  |  |  |  |  | ); | 
| 173 |  |  |  |  |  |  | $server->reg_cb( | 
| 174 |  |  |  |  |  |  | mail => sub { | 
| 175 | 41 |  |  | 41 |  | 1645 | $cb->($_[1]); | 
| 176 |  |  |  |  |  |  | }, | 
| 177 | 5 |  |  |  |  | 112 | ); | 
| 178 | 5 |  |  |  |  | 599 | $server->start; | 
| 179 |  |  |  |  |  |  | defined wantarray | 
| 180 | 0 |  |  | 0 |  | 0 | ? AnyEvent::Util::guard { $server->stop; %$server = (); } | 
|  | 0 |  |  |  |  | 0 |  | 
| 181 |  |  |  |  |  |  | : () | 
| 182 | 5 | 50 |  |  |  | 955 | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | sub new { | 
| 185 | 7 |  |  | 7 | 1 | 524 | my $pkg = shift; | 
| 186 | 7 |  |  |  |  | 332 | my $self = bless { @_ }, $pkg; | 
| 187 | 7 | 50 |  |  |  | 448 | $self->{hostname} = hostname() unless defined $self->{hostname}; | 
| 188 |  |  |  |  |  |  | $self->set_exception_cb( sub { | 
| 189 | 0 |  |  | 0 |  | 0 | my ($e, $event, @args) = @_; | 
| 190 | 0 |  |  |  |  | 0 | my $ex = $@; | 
| 191 | 0 | 0 |  |  |  | 0 | if (exists $self->{event_failed}) { | 
| 192 | 0 |  |  |  |  | 0 | $self->{event_failed} = $ex; | 
| 193 | 0 |  |  |  |  | 0 | return; | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  | #warn "exception: $self, $self->{current_con} (@args) [$@]"; | 
| 196 | 0 |  |  |  |  | 0 | my $con = $self->{current_con}; | 
| 197 | 0 | 0 |  |  |  | 0 | if (!$con) { | 
| 198 | 0 |  |  |  |  | 0 | local $::self = $self; | 
| 199 | 0 |  |  |  |  | 0 | local $::con; | 
| 200 | 0 |  |  |  |  | 0 | local $::event = $event; | 
| 201 |  |  |  |  |  |  | { | 
| 202 | 0 |  |  |  |  | 0 | package DB; | 
| 203 | 0 |  |  |  |  | 0 | my $i = 0; | 
| 204 | 0 |  |  |  |  | 0 | while (my @c = caller(++$i)) { | 
| 205 | 0 |  |  |  |  | 0 | warn "$i. [@DB::args]"; | 
| 206 | 0 | 0 |  |  |  | 0 | next if @DB::args < 2; | 
| 207 | 0 | 0 | 0 |  |  | 0 | last if $DB::args[0] == $::self and $DB::args[1] eq $::event and UNIVERSAL::isa($DB::args[2], 'AnyEvent::SMTP::Conn'); | 
|  |  |  | 0 |  |  |  |  | 
| 208 |  |  |  |  |  |  | } | 
| 209 | 0 |  |  |  |  | 0 | $::con = $DB::args[2]; | 
| 210 |  |  |  |  |  |  | } | 
| 211 | 0 |  |  |  |  | 0 | $con = $::con; | 
| 212 |  |  |  |  |  |  | } | 
| 213 | 0 | 0 |  |  |  | 0 | if ($con) { | 
| 214 | 0 |  |  |  |  | 0 | my $msg = "500 INTERNAL ERROR"; | 
| 215 | 0 | 0 |  |  |  | 0 | if ($self->{devel}) { | 
| 216 | 0 |  |  |  |  | 0 | $ex =~ s{(?:\r?\n)+}{ }sg; | 
| 217 | 0 |  |  |  |  | 0 | $ex =~ s{\s+$}{}s; | 
| 218 | 0 |  |  |  |  | 0 | $msg .= ": ".$ex; | 
| 219 |  |  |  |  |  |  | } | 
| 220 | 0 |  |  |  |  | 0 | $con->reply($msg); | 
| 221 |  |  |  |  |  |  | } | 
| 222 | 0 |  |  |  |  | 0 | warn "exception during $event : $ex"; | 
| 223 | 7 |  |  |  |  | 773 | } ); | 
| 224 |  |  |  |  |  |  | $self->reg_cb( | 
| 225 |  |  |  |  |  |  | command => sub { | 
| 226 | 164 |  |  | 164 |  | 3515 | my ($s,$con,$com) = @_; | 
| 227 | 164 |  |  |  |  | 211 | my ($cmd, @args); | 
| 228 | 164 |  |  |  |  | 290 | for ($com) { | 
| 229 | 164 |  |  |  |  | 658 | s/^\s+//;s/\s+$//; | 
|  | 164 |  |  |  |  | 821 |  | 
| 230 | 164 | 50 |  |  |  | 394 | length or last; | 
| 231 | 164 |  |  |  |  | 795 | ($cmd, @args) = split /\s+/; | 
| 232 | 164 |  |  |  |  | 672 | $cmd = uc $cmd; | 
| 233 |  |  |  |  |  |  | } | 
| 234 | 164 | 50 |  |  |  | 836 | if (exists $CMD{$cmd}) { | 
| 235 | 164 |  |  |  |  | 3935 | $s->handle( $con, $cmd, @args ); | 
| 236 |  |  |  |  |  |  | } else { | 
| 237 | 0 |  |  |  |  | 0 | warn "$cmd @args"; | 
| 238 | 0 |  |  |  |  | 0 | $con->reply("500 Learn to type!"); | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  | #warn "Got command @_"; | 
| 241 |  |  |  |  |  |  | }, | 
| 242 |  |  |  |  |  |  | HELO => sub { | 
| 243 | 41 |  |  | 41 |  | 795 | my ($s,$con,@args) = @_; | 
| 244 | 41 |  |  |  |  | 153 | $con->{helo} = "@args"; | 
| 245 | 41 |  |  |  |  | 206 | $con->new_m(); | 
| 246 | 41 |  |  |  |  | 174 | $con->ok("I'm ready."); | 
| 247 |  |  |  |  |  |  | }, | 
| 248 |  |  |  |  |  |  | EHLO => sub { | 
| 249 | 0 |  |  | 0 |  | 0 | my ($s,$con,@args) = @_; | 
| 250 | 0 |  |  |  |  | 0 | $con->{helo} = "@args"; | 
| 251 | 0 |  |  |  |  | 0 | $con->new_m(); | 
| 252 | 0 |  |  |  |  | 0 | $con->ok("Go on."); | 
| 253 |  |  |  |  |  |  | }, | 
| 254 |  |  |  |  |  |  | RSET => sub { | 
| 255 | 0 |  |  | 0 |  | 0 | my ($s,$con,@args) = @_; | 
| 256 | 0 |  |  |  |  | 0 | $con->new_m(); | 
| 257 | 0 |  |  |  |  | 0 | $con->ok; | 
| 258 |  |  |  |  |  |  | }, | 
| 259 |  |  |  |  |  |  | MAIL => sub { | 
| 260 | 41 |  |  | 41 |  | 930 | my ($s,$con,@args) = @_; | 
| 261 | 41 |  |  |  |  | 113 | my $from = join ' ',@args; | 
| 262 | 41 | 50 |  |  |  | 346 | $from =~ s{^from:}{}i or return $con->reply('501 Usage: MAIL FROM:'); | 
| 263 | 41 | 50 |  |  |  | 5817 | $con->{helo} or return $con->reply("503 Error: send HELO/EHLO first"); | 
| 264 | 41 |  |  |  |  | 68 | my @addrs; | 
| 265 | 41 | 50 |  |  |  | 298 | if ($from !~ /^\s*<>\s*$/) { | 
| 266 | 41 |  |  |  |  | 375 | @addrs = map { $_->address } Mail::Address->parse($from); | 
|  | 41 |  |  |  |  | 8579 |  | 
| 267 | 41 | 50 |  |  |  | 654 | @addrs == 1 or return $con->reply('501 Usage: MAIL FROM:'); | 
| 268 |  |  |  |  |  |  | } else { | 
| 269 | 0 |  |  |  |  | 0 | @addrs = (''); | 
| 270 |  |  |  |  |  |  | } | 
| 271 | 41 | 50 |  |  |  | 142 | if ($self->{mail_validate}) { | 
| 272 | 0 |  |  |  |  | 0 | my ($res,$err,$errstr) = $self->{mail_validate}->($con->{m}, $addrs[0]); | 
| 273 | 0 | 0 |  |  |  | 0 | $res or return $con->reply("$err $errstr"); | 
| 274 |  |  |  |  |  |  | } | 
| 275 | 41 |  |  |  |  | 128 | $con->{m}{from} = $addrs[0]; | 
| 276 | 41 |  |  |  |  | 151 | $con->ok; | 
| 277 |  |  |  |  |  |  | }, | 
| 278 |  |  |  |  |  |  | RCPT => sub { | 
| 279 | 41 |  |  | 41 |  | 9740 | my ($s,$con,@args) = @_; | 
| 280 | 41 |  |  |  |  | 286 | my $to = join ' ',@args; | 
| 281 | 41 | 50 |  |  |  | 249 | $to =~ s{^to:}{}i or return $con->reply('501 Usage: RCPT TO:'); | 
| 282 | 41 | 50 |  |  |  | 361 | defined $con->{m}{from} or return $con->reply("503 Error: need MAIL command"); | 
| 283 | 41 |  |  |  |  | 358 | my @addrs = map { $_->address } Mail::Address->parse($to); | 
|  | 41 |  |  |  |  | 7587 |  | 
| 284 | 41 | 50 |  |  |  | 477 | @addrs or return $con->reply('501 Usage: RCPT TO:'); | 
| 285 | 41 | 50 |  |  |  | 134 | if ($self->{rcpt_validate}) { | 
| 286 | 0 |  |  |  |  | 0 | my ($res,$err,$errstr) = $self->{rcpt_validate}->($con->{m}, $addrs[0]); | 
| 287 | 0 | 0 |  |  |  | 0 | $res or return $con->reply("$err $errstr"); | 
| 288 |  |  |  |  |  |  | } | 
| 289 | 41 |  | 50 |  |  | 232 | push @{ $con->{m}{to} ||= [] }, $addrs[0]; | 
|  | 41 |  |  |  |  | 1203 |  | 
| 290 | 41 |  |  |  |  | 161 | $con->ok; | 
| 291 |  |  |  |  |  |  | }, | 
| 292 |  |  |  |  |  |  | DATA => sub { | 
| 293 | 41 |  |  | 41 |  | 695 | my ($s,$con) = @_; | 
| 294 | 41 | 50 |  |  |  | 324 | defined $con->{m}{from} or return $con->reply("503 Error: need MAIL command"); | 
| 295 | 41 | 50 |  |  |  | 134 | $con->{m}{to}   or return $con->reply("554 Error: need RCPT command"); | 
| 296 | 41 |  |  |  |  | 152 | $con->reply("354 End data with ."); | 
| 297 |  |  |  |  |  |  | $con->data(cb => sub { | 
| 298 | 41 |  |  |  |  | 150 | my $data = shift; | 
| 299 | 41 | 50 |  |  |  | 223 | if ($self->{data_validate}) { | 
| 300 | 0 |  |  |  |  | 0 | my ($res,$err,$errstr) = $self->{data_validate}->($con->{m}, $data); | 
| 301 | 0 | 0 |  |  |  | 0 | $res or return $con->reply("$err $errstr"); | 
| 302 |  |  |  |  |  |  | } | 
| 303 | 41 |  |  |  |  | 211 | $con->{m}{data} = $data; | 
| 304 | 41 |  |  |  |  | 143 | local $s->{event_failed} = 0; | 
| 305 | 41 |  |  |  |  | 323 | local $s->{current_con} = $con; | 
| 306 | 41 |  |  |  |  | 321 | $s->event( mail => delete $con->{m} ); | 
| 307 | 41 | 50 |  |  |  | 922 | if ($s->{event_failed}) { | 
| 308 | 0 |  |  |  |  | 0 | $con->reply("500 Internal Server Error"); | 
| 309 |  |  |  |  |  |  | } else { | 
| 310 | 41 |  |  |  |  | 4800 | $con->ok("I'll take it"); | 
| 311 |  |  |  |  |  |  | } | 
| 312 | 41 |  |  |  |  | 394 | }); | 
| 313 |  |  |  |  |  |  | }, | 
| 314 |  |  |  |  |  |  | QUIT => sub { | 
| 315 | 0 |  |  | 0 |  | 0 | my ($s,$con,$to,@args) = @_; | 
| 316 | 0 |  |  |  |  | 0 | $con->reply("221 Bye."); | 
| 317 | 0 |  |  |  |  | 0 | $con->close; | 
| 318 | 0 |  |  |  |  | 0 | return; | 
| 319 |  |  |  |  |  |  | }, | 
| 320 | 0 |  |  | 0 |  | 0 | HELP => sub { $_[1]->reply("214 No help available.") }, | 
| 321 | 0 |  |  | 0 |  | 0 | NOOP => sub { $_[1]->reply("252 Ok.") }, | 
| 322 | 0 |  |  | 0 |  | 0 | EXPN => sub { $_[1]->reply("252 Nice try.") }, | 
| 323 | 0 |  |  | 0 |  | 0 | VRFY => sub { $_[1]->reply("252 Nice try.") }, | 
| 324 | 7 |  |  |  |  | 1694 | ); | 
| 325 | 7 |  |  |  |  | 8397 | $self; | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | sub stop { | 
| 329 | 2 |  |  | 2 | 1 | 529 | my $self = shift; | 
| 330 | 2 |  |  |  |  | 3 | for (keys %{ $self->{c} }) { | 
|  | 2 |  |  |  |  | 206 |  | 
| 331 | 1 | 50 |  |  |  | 12 | $self->{c}{$_} and $self->{c}{$_}->close; | 
| 332 |  |  |  |  |  |  | } | 
| 333 | 2 |  |  |  |  | 6 | delete $self->{c}; | 
| 334 | 2 |  |  |  |  | 11 | delete $self->{s}; | 
| 335 | 2 |  |  |  |  | 77 | return; | 
| 336 |  |  |  |  |  |  | } | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | sub start { | 
| 339 | 7 |  |  | 7 | 1 | 94 | my $self = shift; | 
| 340 | 7 | 50 |  |  |  | 61 | $self->eventcan('command') or croak "Server implementation $self doesn't parses commands"; | 
| 341 |  |  |  |  |  |  | #$self->{engine} or croak "Server implementation $self doesn't have engine"; | 
| 342 |  |  |  |  |  |  | $self->{s} = tcp_server $self->{host}, $self->{port}, sub { | 
| 343 | 47 |  |  | 47 |  | 309833 | my ($fh,$host,$port) = @_; | 
| 344 | 47 | 50 |  |  |  | 324 | unless ($fh) { | 
| 345 | 0 |  |  |  |  | 0 | $self->event( error => "couldn't accept client: $!" ); | 
| 346 | 0 |  |  |  |  | 0 | return; | 
| 347 |  |  |  |  |  |  | } | 
| 348 | 47 |  |  |  |  | 274 | $self->accept_connection(@_); | 
| 349 |  |  |  |  |  |  | }, sub { | 
| 350 | 7 |  |  | 7 |  | 3234 | my ($sock,$host,$port) = @_; | 
| 351 |  |  |  |  |  |  | #$self->{sock} = $sock; | 
| 352 | 7 | 50 |  |  |  | 54 | $self->{host} = $host unless defined $self->{host}; | 
| 353 | 7 | 50 |  |  |  | 327 | $self->{port} = $port unless defined $self->{port}; | 
| 354 | 7 | 50 |  |  |  | 39 | warn "Server started on port $self->{port}\n" if $self->{debug}; | 
| 355 | 7 |  |  |  |  | 172 | $self->event(ready => ()); | 
| 356 | 7 |  |  |  |  | 261 | return undef; | 
| 357 | 7 |  |  |  |  | 319 | }; | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | } | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | sub accept_connection { | 
| 362 | 47 |  |  | 47 | 0 | 123 | my ($self,$fh,$host,$port) = @_; | 
| 363 | 47 | 50 |  |  |  | 194 | warn "Client connected $host:$port \n" if $self->{debug}; | 
| 364 | 47 |  |  |  |  | 918 | my $con = AnyEvent::SMTP::Conn->new( | 
| 365 |  |  |  |  |  |  | fh => $fh, | 
| 366 |  |  |  |  |  |  | host => $host, | 
| 367 |  |  |  |  |  |  | port => $port, | 
| 368 |  |  |  |  |  |  | debug => $self->{debug}, | 
| 369 |  |  |  |  |  |  | ); | 
| 370 | 47 |  |  |  |  | 278 | $self->{c}{int $con} = $con; | 
| 371 |  |  |  |  |  |  | $con->reg_cb( | 
| 372 |  |  |  |  |  |  | disconnect => sub { | 
| 373 | 46 |  |  | 46 |  | 1978 | delete $self->{c}{int $_[0]}; | 
| 374 | 46 |  |  |  |  | 411 | $self->event( disconnect => $_[0], $_[1] ); | 
| 375 |  |  |  |  |  |  | }, | 
| 376 |  |  |  |  |  |  | command => sub { | 
| 377 | 164 |  |  | 164 |  | 4305 | $self->event( command => @_ ) | 
| 378 |  |  |  |  |  |  | }, | 
| 379 | 47 |  |  |  |  | 1172 | ); | 
| 380 | 47 |  |  |  |  | 17110 | $self->eventif( client => $con ); | 
| 381 | 47 |  |  |  |  | 403 | $con->reply("220 $self->{hostname} AnyEvent::SMTP Ready."); | 
| 382 | 47 |  |  |  |  | 1370 | $con->want_command; | 
| 383 |  |  |  |  |  |  | } | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | sub eventif { | 
| 386 |  |  |  |  |  |  | #my ($self,$name) = @_; | 
| 387 | 211 |  |  | 211 | 0 | 326 | my $self = shift;my $name = shift; | 
|  | 211 |  |  |  |  | 362 |  | 
| 388 | 211 | 100 |  |  |  | 1219 | return 0 unless $self->eventcan($name); | 
| 389 | 165 |  |  |  |  | 769 | $self->event($name => @_); | 
| 390 | 165 |  |  |  |  | 4821 | return 1; | 
| 391 |  |  |  |  |  |  | #goto &{ $self->can('event') }; | 
| 392 |  |  |  |  |  |  | } | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | sub eventcan { | 
| 395 | 218 |  |  | 218 | 0 | 491 | my $self = shift; | 
| 396 | 218 |  |  |  |  | 337 | my $name = shift; | 
| 397 | 218 | 100 |  |  |  | 975 | return undef unless exists $self->{__oe_events}{$name}; | 
| 398 | 172 |  |  |  |  | 397 | return scalar @{ $self->{__oe_events}{$name} }; | 
|  | 172 |  |  |  |  | 719 |  | 
| 399 |  |  |  |  |  |  | } | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | sub handle { | 
| 402 | 164 |  |  | 164 | 0 | 405 | my ($self,$con, $cmd, @args ) = @_; | 
| 403 |  |  |  |  |  |  | $self->eventif( $cmd => $con, @args ) | 
| 404 | 164 | 50 |  |  |  | 623 | or do { | 
| 405 | 0 |  |  |  |  |  | $con->reply("500 Not Supported"); | 
| 406 | 0 |  |  |  |  |  | warn "$cmd event not handled ($cmd @args)"; | 
| 407 | 0 |  |  |  |  |  | 0; | 
| 408 |  |  |  |  |  |  | }; | 
| 409 |  |  |  |  |  |  | } | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | =head1 BUGS | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | Bug reports are welcome in CPAN's request tracker L | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | =head1 AUTHOR | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | Mons Anderson, C<<  >> | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | =head1 COPYRIGHT & LICENSE | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | Copyright 2009 Mons Anderson, all rights reserved. | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | This program is free software; you can redistribute it and/or modify it | 
| 424 |  |  |  |  |  |  | under the same terms as Perl itself. | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | =cut | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | 1; |