File Coverage

blib/lib/Mail/Milter/Authentication/Protocol/SMTP.pm
Criterion Covered Total %
statement 463 659 70.2
branch 136 260 52.3
condition 21 40 52.5
subroutine 33 38 86.8
pod 23 25 92.0
total 676 1022 66.1


line stmt bran cond sub pod time code
1             package Mail::Milter::Authentication::Protocol::SMTP;
2 127     127   5568 use 5.20.0;
  127         574  
3 127     127   918 use strict;
  127         333  
  127         2731  
4 127     127   605 use warnings;
  127         322  
  127         4186  
5 127     127   761 use Mail::Milter::Authentication::Pragmas;
  127         271  
  127         847  
6             # ABSTRACT: SMTP protocol handling
7             our $VERSION = '3.20230911'; # VERSION
8 127     127   33466 use Digest::MD5 qw{ md5_hex };
  127         281  
  127         7163  
9 127     127   58687 use Email::Date::Format qw{ email_date };
  127         86964  
  127         8042  
10 127     127   1125 use IO::Socket::INET;
  127         649  
  127         4717  
11 127     127   85211 use IO::Socket::UNIX;
  127         546  
  127         3316  
12 127     127   218660 use IO::Socket;
  127         326  
  127         651  
13 127     127   133976 use Net::IP;
  127         333  
  127         1231824  
14              
15             sub register_metrics {
16             return {
17 17     17 1 171 'mail_processed_total' => 'Number of emails processed',
18             };
19             }
20              
21             sub get_smtp_config {
22 163     163 1 513 my ( $self ) = @_;
23 163         1048 my $client_details = $self->get_client_details();
24 163         7875 my $smtp_config;
25              
26 163 50       1077 if ( exists( $self->{'config'}->{'smtp'}->{ $client_details } ) ) {
27 0         0 $smtp_config = $self->{'config'}->{'smtp'}->{ $client_details };
28             }
29             else {
30 163         533 $smtp_config = $self->{'config'}->{'smtp'};
31             }
32              
33 163         641 return $smtp_config;
34             }
35              
36             sub queue_type {
37 37     37 1 169 my ( $self ) = @_;
38 37         178 my $smtp_config = $self->get_smtp_config();
39 37 50       1282 return $smtp_config->{'queue_type'} eq 'before' ? 'before' : 'after';
40             }
41              
42             sub smtp_status {
43 1787     1787 1 5329 my ( $self, $status ) = @_;
44 1787         4085 my $smtp = $self->{'smtp'};
45 1787         17955 $PROGRAM_NAME = $Mail::Milter::Authentication::Config::IDENT . ':' . $status . '(' . $self->{'count'} . '.' . $smtp->{'count'} . ')';
46             }
47              
48             sub smtp_init {
49 211     211 1 664 my ( $self ) = @_;
50              
51 211 100       935 return if $self->{'smtp'}->{'init_required'} == 0;
52              
53 43         230 my $handler = $self->{'handler'}->{'_Handler'};
54 43         156 my $smtp = $self->{'smtp'};
55              
56 43         980 $handler->set_symbol( 'C', 'j', $smtp->{'server_name'} );
57 43         314 $handler->set_symbol( 'C', '{rcpt_host}', $smtp->{'server_name'} );
58              
59 43         1611 $smtp->{'queue_id'} = substr( uc md5_hex( "Authentication Milter Client $PID " . time() . rand(100) ) , -11 );
60 43         409 $handler->set_symbol( 'C', 'i', $self->smtp_queue_id() );
61              
62 43         217 $smtp->{'count'}++ ;
63 43         712 $handler->dbgout( 'SMTP Transaction count', $self->{'count'} . '.' . $smtp->{'count'} , LOG_DEBUG );
64              
65 43         251 $smtp->{'init_required'} = 0;
66             }
67              
68             sub protocol_process_request {
69 37     37 1 294 my ( $self ) = @_;
70              
71 37         256 my $handler = $self->{'handler'}->{'_Handler'};
72 37         421 $handler->top_setup_callback();
73              
74 37         303 my $config = $self->{ 'config' };
75 37   50     273 my $seconds = $config->{'content_timeout'} // 300;
76 37         535 $handler->set_overall_timeout( $seconds * 1000000 );
77              
78             $self->{'smtp'} = {
79             'fwd_helo_host' => undef,
80             'fwd_connect_ip' => undef,
81             'fwd_connect_host' => undef,
82             'fwd_ident' => undef,
83             'helo_host' => q{},
84             'mail_from' => q{},
85             'rcpt_to' => [],
86             'has_mail_from' => 0,
87             'has_data' => 0,
88             'connect_ip' => $self->{'server'}->{'peeraddr'},
89 37         2123 'connect_host' => $self->{'server'}->{'peeraddr'},
90             'last_command' => 0,
91             'headers' => [],
92             'body' => q{},
93             'using_lmtp' => 0,
94             'lmtp_rcpt' => [],
95             'init_required' => 1,
96             'string' => q{},
97             'count' => 0,
98             };
99              
100             # If we have a UNIX connection then these will be undef,
101             # Set them to localhost to avoid warnings later.
102 37 50       249 if ( ! $self->{'smtp'}->{'connect_ip'} ) { $self->{'smtp'}->{'connect_ip'} = '127.0.0.1'; }
  37         233  
103              
104 37 50       259 if ( $self->{'smtp'}->{'connect_ip'} eq '127.0.0.1' ) {
105 37         226 $self->{'smtp'}->{'connect_host'} = 'localhost';
106             }
107             else {
108             # TODO do a reverse lookup here!
109             }
110              
111 37         240 my $smtp = $self->{'smtp'};
112 37         225 my $socket = $self->{'socket'};
113              
114 37         550 my $smtp_config = $self->get_smtp_config();
115 37   50     357 $smtp->{'server_name'} = $smtp_config->{'server_name'} || 'server.example.com';
116 37   50     449 $smtp->{'smtp_timeout_in'} = $smtp_config->{'timeout_in'} || 60;
117 37   50     341 $smtp->{'smtp_timeout_out'} = $smtp_config->{'timeout_out'} || 60;
118              
119 37         4490 print $socket "220 " . $smtp->{'server_name'} . " ESMTP AuthenticationMilter\r\n";
120              
121 37         764 $self->smtp_init();
122              
123             COMMAND:
124 37         244 while ( ! $smtp->{'last_command'} ) {
125              
126 331         1176 my $command;
127 331     0   7016 local $SIG{'ALRM'} = sub{ die "Timeout\n" };
  0         0  
128 331         2996 alarm( $smtp->{'smtp_timeout_in'} );
129 331         1218 eval {
130 331         95383 $command = <$socket>;
131             };
132 331 50       2921 if ( my $error = $@ ) {
133 0         0 $self->logerror( "Read Error: $error" );
134 0         0 last COMMAND;
135             }
136 331         2444 alarm( 0 );
137              
138 331 50       1394 if ( ! $command ) {
139 0         0 $self->logdebug( "receive NULL command" );
140 0         0 last COMMAND;
141             }
142              
143 331         3882 $command =~ s/\r?\n$//;
144 331         1382 my $uccommand = uc $command;
145              
146 331         2749 $self->logdebug( "receive command $command" );
147              
148 331 50       1200 if ( exists ( $smtp_config->{ 'debug_triggers' } ) ) {
149 0         0 my $triggers = $smtp_config->{ 'debug_triggers' };
150 0         0 foreach my $trigger ( @$triggers ) {
151 0 0       0 if ( $command =~ /$trigger/ ) {
152 0         0 $self->enable_extra_debugging();
153             }
154             }
155             }
156              
157 331         793 my $returncode = SMFIS_CONTINUE;
158              
159 331 100       4371 if ( $uccommand =~ /^EHLO/ ) {
    50          
    50          
    100          
    100          
    100          
    100          
    100          
    50          
160 37         477 $self->smtp_command_ehlo( $command );
161             }
162             elsif ( $uccommand =~ /^LHLO/ ) {
163 0         0 $self->smtp_command_lhlo( $command );
164             }
165             elsif ( $uccommand =~ /^HELO/ ) {
166 0         0 $self->smtp_command_helo( $command );
167             }
168             elsif ( $uccommand =~ /^XFORWARD/ ) {
169 126         612 $self->smtp_command_xforward( $command );
170             }
171             elsif ( $uccommand =~ /^MAIL FROM:/ ) {
172 42         242 $self->smtp_init();
173 42         446 $self->smtp_command_mailfrom( $command );
174             }
175             elsif ( $uccommand =~ /^RCPT TO:/ ) {
176 42         514 $self->smtp_command_rcptto( $command );
177             }
178             elsif ( $uccommand =~ /^RSET/ ) {
179 6         53 $self->smtp_command_rset( $command );
180             }
181             elsif ( $uccommand =~ /^DATA/ ) {
182 42         294 $handler->set_overall_timeout( $seconds * 1000000 );
183 42         619 $self->smtp_command_data( $command );
184             }
185             elsif ( $uccommand =~ /^QUIT/ ){
186 36         177 $self->smtp_status('smtp.i.quit');
187 36         1519 print $socket "221 2.0.0 Bye\n";
188 36         823 last COMMAND;
189             }
190             else {
191 0         0 $self->smtp_status('smtp.i.unknown');
192 0         0 $self->logerror( "Unknown SMTP command: $command" );
193 0         0 print $socket "502 5.5.2 I don't understand\r\n";
194             }
195              
196             }
197 37         326 $handler->clear_overall_timeout();
198              
199 37         162 $self->smtp_status('smtp.close');
200              
201 37         462 $self->close_destination_socket();
202              
203 37         359 delete $self->{'smtp'};
204             }
205              
206             sub smtp_queue_id {
207 125     125 1 407 my ( $self ) = @_;
208 125         441 my $smtp = $self->{'smtp'};
209 125         414 my $queue_id = $smtp->{'queue_id'};
210 125 50 33     577 if ( $smtp->{'fwd_ident'} && $smtp->{'fwd_ident'} ne '[UNAVAILABLE]' ) {
211 0         0 $queue_id .= '.' . $smtp->{'fwd_ident'};
212             }
213 125         3056 return $queue_id;
214             }
215              
216             sub command_param {
217 247     247 1 703 my ( $command, $index ) = @_;
218 247         553 my $p = q{};
219 247 50       903 if ( length( $command ) >= $index ) {
220 247         801 $p = substr( $command, $index );
221             }
222 247         835 return $p;
223             }
224              
225             # Splits the given command parameter line into a list of separate
226             # path (<foo@example.com>) and esmtp-param (KEYWORD=VALUE) strings.
227             sub split_command_params {
228 84     84 0 285 my ( $line ) = @_;
229 84         494 $line =~ s/^\s+//;
230 84   66     416 my $path_end = find_path_end( $line ) || length( $line );
231 84         258 my $path = substr( $line, 0, $path_end );
232 84         218 $line = substr( $line, $path_end );
233 84         276 $line =~ s/^\s+//;
234 84         863 return ( $path, split( /\s+/, $line ) );
235             }
236              
237             sub find_path_end {
238 84     84 0 267 my ( $input ) = @_;
239 84         202 my $len = length( $input );
240 84         177 my @qstack;
241 84         394 for ( my $i = 0; $i < $len; $i++ ) {
242 1567         2739 my $c = substr( $input, $i, 1 );
243 1567 100 100     8599 if ( @qstack && $c eq $qstack[-1] ) {
    50 33        
    100 66        
    50 33        
    100 100        
244             # end of a quoted string
245 16         74 pop @qstack;
246             } elsif ( $c eq '"' ) {
247             # start of a "" quoted string
248 0         0 push @qstack, '"';
249             } elsif ( $c eq '<' && ( !@qstack || $qstack[-1] eq '>' ) ) {
250             # start of a <> quoted string (possibly inside of another <>)
251 16         99 push @qstack, '>';
252             } elsif ( $c eq '\\' && @qstack ) {
253             # escaped character within a quoted string
254 0         0 $i++;
255             } elsif ( $c eq ' ' && !@qstack ) {
256             # found the end
257 1         10 return $i;
258             }
259             }
260 83 50       583 return ( @qstack ? undef : $len );
261             }
262              
263             sub smtp_command_lhlo {
264 0     0 1 0 my ( $self, $command ) = @_;
265 0         0 my $smtp = $self->{'smtp'};
266 0         0 my $socket = $self->{'socket'};
267 0         0 my $handler = $self->{'handler'}->{'_Handler'};
268 0         0 $self->smtp_status('smtp.i.lhlo');
269              
270 0         0 $smtp->{'using_lmtp'} = 1;
271              
272 0 0       0 if ( $smtp->{'has_data'} ) {
273 0         0 $self->logerror( "Out of Order SMTP command: $command" );
274 0         0 print $socket "501 5.5.2 Out of Order\r\n";
275 0         0 return;
276             }
277 0         0 $smtp->{'helo_host'} = command_param( $command,5 );
278 0         0 print $socket "250-" . $smtp->{'server_name'} . "\r\n";
279 0 0       0 if ( $self->queue_type() eq 'before' ) {
280 0         0 print $socket "250-XFORWARD NAME ADDR HELO \r\n";
281             }
282             else {
283 0         0 print $socket "250-XFORWARD NAME ADDR HELO IDENT \r\n";
284             }
285 0         0 print $socket "250-PIPELINING\r\n";
286 0         0 print $socket "250-ENHANCEDSTATUSCODES\r\n";
287 0         0 print $socket "250 8BITMIME\r\n";
288             }
289              
290             sub smtp_command_ehlo {
291 37     37 1 213 my ( $self, $command ) = @_;
292 37         145 my $smtp = $self->{'smtp'};
293 37         118 my $socket = $self->{'socket'};
294 37         159 my $handler = $self->{'handler'}->{'_Handler'};
295 37         379 $self->smtp_status('smtp.i.ehlo');
296              
297 37 50       385 if ( $smtp->{'has_data'} ) {
298 0         0 $self->logerror( "Out of Order SMTP command: $command" );
299 0         0 print $socket "501 5.5.2 Out of Order\r\n";
300 0         0 return;
301             }
302 37         224 $smtp->{'helo_host'} = command_param( $command,5 );
303 37         1922 print $socket "250-" . $smtp->{'server_name'} . "\r\n";
304 37 50       596 if ( $self->queue_type() eq 'before' ) {
305 0         0 print $socket "250-XFORWARD NAME ADDR HELO \r\n";
306             }
307             else {
308 37         1769 print $socket "250-XFORWARD NAME ADDR HELO IDENT \r\n";
309             }
310 37         564 print $socket "250-PIPELINING\r\n";
311 37         419 print $socket "250-ENHANCEDSTATUSCODES\r\n";
312 37         1272 print $socket "250 8BITMIME\r\n";
313             }
314              
315             sub smtp_command_helo {
316 0     0 1 0 my ( $self, $command ) = @_;
317 0         0 my $smtp = $self->{'smtp'};
318 0         0 my $socket = $self->{'socket'};
319 0         0 my $handler = $self->{'handler'}->{'_Handler'};
320 0         0 $self->smtp_status('smtp.i.helo');
321              
322 0 0       0 if ( $smtp->{'has_data'} ) {
323 0         0 $self->logerror( "Out of Order SMTP command: $command" );
324 0         0 print $socket "501 5.5.2 Out of Order\r\n";
325 0         0 return;
326             }
327 0         0 $smtp->{'helo_host'} = command_param( $command,5 );
328 0         0 print $socket "250 " . $smtp->{'server_name'} . " Hi " . $smtp->{'helo_host'} . "\r\n";
329             }
330              
331             sub smtp_command_xforward {
332 126     126 1 468 my ( $self, $command ) = @_;
333 126         310 my $smtp = $self->{'smtp'};
334 126         314 my $socket = $self->{'socket'};
335 126         323 my $handler = $self->{'handler'}->{'_Handler'};
336 126         435 $self->smtp_status('smtp.i.xforward');
337              
338 126         681 $self->smtp_init();
339              
340 126 50       552 if ( $smtp->{'has_data'} ) {
341 0         0 $self->logerror( "Out of Order SMTP command: $command" );
342 0         0 print $socket "503 5.5.2 Out of Order\r\n";
343 0         0 return;
344             }
345 126         391 my $xdata = command_param( $command,9 );
346 126         673 foreach my $entry ( split( q{ }, $xdata ) ) {
347 126         738 my ( $key, $value ) = split( '=', $entry, 2 );
348 126 100       915 if ( $key eq 'NAME' ) {
    100          
    50          
    0          
349 42         240 $smtp->{'fwd_connect_host'} = $value;
350             }
351             elsif ( $key eq 'ADDR' ) {
352 42         255 $smtp->{'fwd_connect_ip'} = $value;
353             }
354             elsif ( $key eq 'HELO' ) {
355 42         184 $smtp->{'fwd_helo_host'} = $value;
356             }
357             elsif ( $key eq 'IDENT' ) {
358 0 0       0 if ( $self->queue_type() eq 'before' ) {
359 0         0 $self->logerror( "XForward IDENT received in before queue mode: $key=$value" );
360             }
361             else {
362 0         0 $smtp->{'fwd_ident'} = $value;
363 0         0 $handler->set_symbol( 'C', 'i', $self->smtp_queue_id() );
364 0         0 $handler->dbgout( 'Upstream ID', $value, LOG_INFO );
365             }
366             }
367             else {
368             # NOP
369 0         0 $self->logerror( "Unknown XForward Entry: $key=$value" );
370             ### log it here though
371             }
372             }
373 126         7966 print $socket "250 2.0.0 Ok\r\n";
374             }
375              
376             sub smtp_command_rset {
377 6     6 1 32 my ( $self, $command ) = @_;
378 6         25 my $smtp = $self->{'smtp'};
379 6         18 my $socket = $self->{'socket'};
380 6         24 $self->smtp_status('smtp.i.rset');
381 6         40 $smtp->{'mail_from'} = q{};
382 6         34 $smtp->{'rcpt_to'} = [];
383 6         31 $smtp->{'headers'} = [];
384 6         21 $smtp->{'body'} = q{};
385 6         17 $smtp->{'has_data'} = 0;
386 6         15 $smtp->{'has_mail_from'} = 0;
387 6         20 $smtp->{'fwd_connect_host'} = undef;
388 6         15 $smtp->{'fwd_connect_ip'} = undef;
389 6         16 $smtp->{'fwd_helo_host'} = undef;
390 6         16 $smtp->{'fwd_ident'} = undef;
391 6         18 $smtp->{'lmtp_rcpt'} = [];
392 6         17 $smtp->{'string'} = q{};
393 6         40 $self->{'handler'}->{'_Handler'}->top_close_callback();
394              
395 6         29 $smtp->{'init_required'} = 1;
396 6         31 $self->smtp_init();
397              
398 6         36 my $smtp_conf = $self->get_smtp_config();
399              
400 6         22 my $handler = $self->{'handler'}->{'_Handler'};
401 6 50       35 if ( $handler->{'exit_on_close'} ) {
402 0         0 $smtp->{'last_command'} = 1;
403 0         0 $handler->dbgout( 'EXIT ON CLOSE REQUESTED', 'closing on RSET', LOG_INFO );
404 0         0 print $socket "421 4.3.2 Pipeline limit reached\r\n";
405 0         0 return;
406             }
407 6 50       20 if ( $smtp_conf->{'pipeline_limit'} ) {
408 6         16 my $count = $smtp->{'count'};
409 6         19 my $limit = $smtp_conf->{'pipeline_limit'};
410 6 100       27 if ( $count > $limit ) {
411 1         4 $smtp->{'last_command'} = 1;
412 1         7 $handler->dbgout( 'SMTP Pipeline limit reached', 'closing on RSET', LOG_INFO );
413 1         50 print $socket "421 4.3.2 Pipeline limit reached\r\n";
414 1         24 return;
415             }
416             }
417 5         654 print $socket "250 2.0.0 Ok\r\n";
418             }
419              
420             sub smtp_command_mailfrom {
421 42     42 1 207 my ( $self, $command ) = @_;
422 42         172 my $smtp = $self->{'smtp'};
423 42         150 my $socket = $self->{'socket'};
424 42         156 my $handler = $self->{'handler'}->{'_Handler'};
425 42         174 $self->smtp_status('smtp.i.mailfrom');
426              
427 42         154 my $returncode;
428 42 50       403 if ( $smtp->{'has_data'} ) {
429 0         0 $self->logerror( "Out of Order SMTP command: $command" );
430 0         0 print $socket "503 5.5.2 Out of Order\r\n";
431 0         0 return;
432             }
433 42 50       247 if ( $smtp->{'has_mail_from'} ) {
434 0         0 $self->logerror( "Out of Order SMTP command: $command" );
435 0         0 print $socket "503 5.5.1 Nested MAIL Command\r\n";
436 0         0 return;
437             }
438              
439             # Do connect callback here, because of XFORWARD
440 42   33     262 my $host = $smtp->{'fwd_connect_host'} || $smtp->{'connect_host'};
441 42   33     207 my $ip = $smtp->{'fwd_connect_ip'} || $smtp->{'connect_ip'};
442 42   33     177 my $helo = $smtp->{'fwd_helo_host'} || $smtp->{'helo_host'};
443              
444 42 50       279 if ( substr( $ip, 0, 5 ) eq 'IPv6:' ) {
445 0         0 $ip = substr( $ip, 5 );
446             }
447              
448             # Do connection remapping first
449 42         1081 $handler->remap_connect_callback( $host, Net::IP->new( $ip ) );
450 42         419 $handler->remap_helo_callback( $helo );
451              
452 42         246 $self->logdebug( "Inbound IP Address " . $handler->{'ip_object'}->ip() );
453 42         509 $returncode = $handler->top_connect_callback( $host, $handler->{'ip_object'} );
454 42 50       251 if ( $returncode == SMFIS_CONTINUE ) {
    0          
    0          
455 42         291 $returncode = $handler->top_helo_callback( $handler->{'helo_name'} );
456 42 50       253 if ( $returncode == SMFIS_CONTINUE ) {
    0          
    0          
457 42         292 my $envfrom = command_param( $command,10 );
458 42         207 $smtp->{'mail_from'} = $envfrom;
459 42         197 $returncode = $handler->top_envfrom_callback( split_command_params( $envfrom ) );
460 42 50       246 if ( $returncode == SMFIS_CONTINUE ) {
    0          
    0          
461 42         153 $smtp->{'has_mail_from'} = 1;
462 42         3324 print $socket "250 2.0.0 Ok\r\n";
463             }
464             elsif ( my $reject_reason = $handler->get_reject_mail() ) {
465 0         0 $handler->clear_reject_mail();
466 0         0 $self->loginfo ( "SMTPReject: $reject_reason" );
467 0         0 print $socket $reject_reason . "\r\n";
468             }
469             elsif ( my $defer_reason = $handler->get_defer_mail() ) {
470 0         0 $handler->clear_defer_mail();
471 0         0 $self->loginfo ( "SMTPDefer: $defer_reason" );
472 0         0 print $socket $defer_reason . "\r\n";
473             }
474             else {
475 0         0 print $socket "451 4.0.0 MAIL - That's not right\r\n";
476             }
477             }
478             elsif ( my $reject_reason = $handler->get_reject_mail() ) {
479 0         0 $handler->clear_reject_mail();
480 0         0 $self->loginfo ( "SMTPReject: $reject_reason" );
481 0         0 print $socket $reject_reason . "\r\n";
482             }
483             elsif ( my $defer_reason = $handler->get_defer_mail() ) {
484 0         0 $handler->clear_defer_mail();
485 0         0 $self->loginfo ( "SMTPDefer: $defer_reason" );
486 0         0 print $socket $defer_reason . "\r\n";
487             }
488             else {
489 0         0 print $socket "451 4.0.1 HELO - That's not right\r\n";
490             }
491             }
492             elsif ( my $reject_reason = $handler->get_reject_mail() ) {
493 0         0 $handler->clear_reject_mail();
494 0         0 $self->loginfo ( "SMTPReject: $reject_reason" );
495 0         0 print $socket $reject_reason . "\r\n";
496             }
497             elsif ( my $defer_reason = $handler->get_defer_mail() ) {
498 0         0 $handler->clear_defer_mail();
499 0         0 $self->loginfo ( "SMTPDefer: $defer_reason" );
500 0         0 print $socket $defer_reason . "\r\n";
501             }
502             else {
503 0         0 print $socket "451 4.0.2 Connection - That's not right\r\n";
504             }
505             }
506              
507             sub smtp_command_rcptto {
508 42     42 1 255 my ( $self, $command ) = @_;
509 42         152 my $smtp = $self->{'smtp'};
510 42         172 my $socket = $self->{'socket'};
511 42         168 my $handler = $self->{'handler'}->{'_Handler'};
512 42         260 $self->smtp_status('smtp.i.rcptto');
513              
514 42 50       402 if ( $smtp->{'has_data'} ) {
515 0         0 $self->logerror( "Out of Order SMTP command: $command" );
516 0         0 print $socket "503 5.5.2 Out of Order\r\n";
517 0         0 return;
518             }
519 42         216 my $envrcpt = command_param( $command,8 );
520 42         107 push @{ $smtp->{'rcpt_to'} }, $envrcpt;
  42         242  
521 42         178 my $returncode = $handler->top_envrcpt_callback( split_command_params( $envrcpt ) );
522 42 50       282 if ( $returncode == SMFIS_CONTINUE ) {
    0          
    0          
523 42         130 push @{ $smtp->{'lmtp_rcpt'} }, $envrcpt;
  42         216  
524 42         2720 print $socket "250 2.0.0 Ok\r\n";
525             }
526             elsif ( my $reject_reason = $handler->get_reject_mail() ) {
527 0         0 $handler->clear_reject_mail();
528 0         0 $self->loginfo ( "SMTPReject: $reject_reason" );
529 0         0 print $socket $reject_reason . "\r\n";
530             }
531             elsif ( my $defer_reason = $handler->get_defer_mail() ) {
532 0         0 $handler->clear_defer_mail();
533 0         0 $self->loginfo ( "SMTPDefer: $defer_reason" );
534 0         0 print $socket $defer_reason . "\r\n";
535             }
536             else {
537 0         0 print $socket "451 4.0.3 That's not right\r\n";
538             }
539             }
540              
541             sub smtp_command_data {
542 42     42 1 231 my ( $self, $command ) = @_;
543 42         118 my $smtp = $self->{'smtp'};
544 42         102 my $socket = $self->{'socket'};
545 42         132 my $handler = $self->{'handler'}->{'_Handler'};
546 42         222 $self->smtp_status('smtp.i.data');
547              
548 42         366 my $body = q{};
549 42         142 my $done = 0;
550 42         140 my $fail = 0;
551 42         135 my @header_split;
552             my $returncode;
553              
554 42 50       227 if ( $smtp->{'has_data'} ) {
555 0         0 $self->logerror( "Repeated SMTP DATA command: $command" );
556 0         0 print $socket "503 5.5.2 One at a time please\r\n";
557 0         0 return;
558             }
559 42         1897 print $socket "354 2.0.0 Send body\r\n";
560              
561 42     0   1235 local $SIG{'ALRM'} = sub{ die "Timeout\n" };
  0         0  
562 42         280 eval{
563 42         391 alarm( $smtp->{'smtp_timeout_in'} );
564             HEADERS:
565 42         10474 while ( my $dataline = <$socket> ) {
566 1518         8340 $self->extra_debugging( "RAW DEBUG: ". $dataline );
567 1518         7719 alarm( 0 );
568 1518         9362 $dataline =~ s/\r?\n$//;
569 1518 50       4293 if ( $dataline eq '.' ) {
570 0         0 $done = 1;
571 0         0 last HEADERS;
572             }
573             # Handle transparency
574 1518 50       3383 if ( $dataline =~ /^\./ ) {
575 0         0 $dataline = substr( $dataline, 1 );
576             }
577 1518 100       3410 if ( $dataline eq q{} ) {
578 42         256 last HEADERS;
579             }
580 1476         3167 push @header_split, $dataline;
581 1476         12389 alarm( $smtp->{'smtp_timeout_in'} );
582             }
583             };
584 42 50       229 if ( my $error = $@ ) {
585 0         0 $self->logerror( "Read Error: $error" );
586 0         0 $done = 1;
587 0         0 $fail = 1;
588             }
589 42         280 alarm( 0 );
590              
591 42         261 $self->smtp_status('smtp.i.data.process');
592 42         289 my $value = q{};
593 42         331 foreach my $header_line ( @header_split ) {
594 1476 100       5156 if ( $header_line =~ /^\s/ ) {
595 985         2612 $value .= "\r\n" . $header_line;
596             }
597             else {
598 491 100       1168 if ( $value ) {
599 449         785 push @{ $smtp->{'headers'} } , $value;
  449         1497  
600 449         2104 my ( $hkey, $hvalue ) = split ( ':', $value, 2 );
601 449         2413 $hvalue =~ s/^\s+//;
602 449         1179 $hkey =~ s/^\s+//;
603 449         1369 $hkey =~ s/\s+$//;
604 449 50       1195 if ( ! $fail ) {
605 449         1513 my $returncode = $handler->top_header_callback( $hkey, $hvalue, $value );
606 449 50       1548 if ( $returncode != SMFIS_CONTINUE ) {
607 0         0 $fail = 1;
608             }
609             }
610             }
611 491         1501 $value = $header_line;
612             }
613             }
614 42 50       285 if ( $value ) {
615 42         180 push @{ $smtp->{'headers'} } , $value;
  42         157  
616 42         306 my ( $hkey, $hvalue ) = split ( ':', $value, 2 );
617 42         524 $hvalue =~ s/^\s+//;
618 42         177 $hkey =~ s/^\s+//;
619 42         212 $hkey =~ s/\s+$//;
620 42 50       184 if ( ! $fail ) {
621 42         231 my $returncode = $handler->top_header_callback( $hkey, $hvalue, $value );
622 42 50       403 if ( $returncode != SMFIS_CONTINUE ) {
623 0         0 $fail = 1;
624             }
625             }
626             }
627 42 50       188 if ( ! $fail ) {
628 42         334 $returncode = $handler->top_eoh_callback();
629 42 50       338 if ( $returncode != SMFIS_CONTINUE ) {
630 0         0 $fail = 1;
631             }
632             }
633              
634 42         277 my $smtp_conf = $self->get_smtp_config();
635              
636 42         143 my $chunk_limit = 1048576; # Process in chunks no larger than...
637 42 50       209 if ( exists ( $smtp_conf->{ 'chunk_limit' } ) ) {
638 0         0 $chunk_limit = $smtp_conf->{ 'chunk_limit' };
639             }
640              
641 42         125 my $temp_file;
642 42 50       183 if ( exists ( $smtp_conf->{ 'temp_dir' } ) ) {
643             # TODO use spool_dir to create this
644             # add config option to en/disable this
645             # allow dir to be overridden
646 0         0 $temp_file = File::Temp->new( DIR => $smtp_conf->{ 'temp_dir' } );
647             }
648              
649 42         217 $self->smtp_status('smtp.i.body');
650 42         178 my $body_chunk = q{};
651 42 50       229 if ( ! $done ) {
652 42         141 eval {
653 42         395 alarm( $smtp->{'smtp_timeout_in'} );
654             DATA:
655 42         538 while ( my $dataline = <$socket> ) {
656 716         3994 $self->extra_debugging( "RAW DEBUG: ". $dataline );
657 716         3806 alarm( 0 );
658 716 100       2690 last DATA if $dataline =~ /^\.\r\n/;
659             # Handle transparency
660 674 100       1530 if ( $dataline =~ /^\./ ) {
661 16         49 $dataline = substr( $dataline, 1 );
662             }
663              
664 674 50       1419 if ( $temp_file ) {
665 0         0 print $temp_file $dataline;
666             }
667             else {
668 674         1483 $body .= $dataline;
669             }
670              
671 674 50       1539 if ( length( $body_chunk ) + length( $dataline ) > $chunk_limit ) {
672 0         0 $returncode = $handler->top_body_callback( $body_chunk );
673 0 0       0 if ( $returncode != SMFIS_CONTINUE ) {
674 0         0 $fail = 1;
675             }
676 0         0 $body_chunk = q{};
677             }
678              
679 674         1201 $body_chunk .= $dataline;
680              
681 674         6885 alarm( $smtp->{'smtp_timeout_in'} );
682             }
683 42 50       254 if ( ! $fail ) {
684 42         304 $returncode = $handler->top_body_callback( $body_chunk );
685 42 50       383 if ( $returncode != SMFIS_CONTINUE ) {
686 0         0 $fail = 1;
687             }
688 42         165 $body_chunk = q{};
689             }
690             };
691 42 50       190 if ( my $error = $@ ) {
692 0         0 $self->logerror( "Read Error: $error" );
693 0         0 $done = 1;
694 0         0 $fail = 1;
695             }
696 42         290 alarm( 0 );
697             }
698              
699 42 50       275 if ( ! $fail ) {
700 42         267 $returncode = $handler->top_eom_callback();
701 42 100       266 if ( $returncode != SMFIS_CONTINUE ) {
702 1         5 $fail = 1;
703             }
704             }
705              
706 42         265 $self->smtp_status('smtp.i.data.received');
707              
708 42 100       264 if ( ! $fail ) {
    50          
    0          
709              
710 41 50       150 if ( $temp_file ) {
711 0         0 $smtp->{'spool'} = $temp_file;
712             }
713             else {
714 41         229 $smtp->{'body'} = $body;
715             }
716              
717 41 50       451 if ( $self->smtp_forward_to_destination() ) {
718              
719 41 100       323 if ( my $reject_reason = $handler->get_quarantine_mail() ) {
720 2         21 $handler->metric_count( 'mail_processed_total', { 'result' => 'quarantined' } );
721             }
722             else {
723 39         359 $handler->metric_count( 'mail_processed_total', { 'result' => 'accepted' } );
724             }
725              
726 41         404 $handler->dbgout( 'Accept string', $smtp->{'string'}, LOG_INFO );
727 41         298 $smtp->{'has_data'} = 1;
728              
729 41 50       230 if ( $smtp->{'using_lmtp'} ) {
730 0         0 foreach my $rcpt_to ( @{ $smtp->{'lmtp_rcpt'} } ) {
  0         0  
731 0         0 print $socket "250 2.0.0 Queued as " . $self->smtp_queue_id() . "\r\n";
732             }
733             }
734             else {
735 41         231 print $socket "250 2.0.0 Queued as " . $self->smtp_queue_id() . "\r\n";
736             }
737             }
738             else {
739 0         0 $self->logerror( "SMTP Mail Rejected" );
740 0         0 my $error = '451 4.0.4 That\'s not right';
741 0         0 my $upstream_error = $smtp->{'string'};
742 0 0       0 if ( $upstream_error =~ /^4\d\d / ) {
    0          
743 0         0 $handler->metric_count( 'mail_processed_total', { 'result' => 'deferred' } );
744 0         0 $error = $upstream_error;
745             }
746             elsif ( $upstream_error =~ /^5\d\d / ) {
747             # Also pass back rejects
748 0         0 $handler->metric_count( 'mail_processed_total', { 'result' => 'rejected' } );
749 0         0 $error = $upstream_error;
750             }
751             else {
752 0         0 $handler->metric_count( 'mail_processed_total', { 'result' => 'deferred_error' } );
753 0         0 $error .= ': ' . $upstream_error;
754             }
755 0 0       0 if ( $smtp->{'using_lmtp'} ) {
756 0         0 foreach my $rcpt_to ( @{ $smtp->{'lmtp_rcpt'} } ) {
  0         0  
757 0         0 print $socket "$error\r\n";
758             }
759             }
760             else {
761 0         0 print $socket "$error\r\n";
762             }
763             }
764             }
765             elsif ( my $reject_reason = $handler->get_reject_mail() ) {
766 1         9 $handler->metric_count( 'mail_processed_total', { 'result' => 'rejected' } );
767 1         14 $handler->clear_reject_mail();
768 1 50       8 if ( $smtp->{'using_lmtp'} ) {
769 0         0 foreach my $rcpt_to ( @{ $smtp->{'lmtp_rcpt'} } ) {
  0         0  
770 0         0 $self->loginfo ( "SMTPReject: $reject_reason" );
771 0         0 print $socket $reject_reason . "\r\n";
772             }
773             }
774             else {
775 1         10 $self->loginfo ( "SMTPReject: $reject_reason" );
776 1         727 print $socket $reject_reason . "\r\n";
777             }
778             }
779             elsif ( my $defer_reason = $handler->get_defer_mail() ) {
780 0         0 $handler->metric_count( 'mail_processed_total', { 'result' => 'defered' } );
781 0         0 $handler->clear_defer_mail();
782 0 0       0 if ( $smtp->{'using_lmtp'} ) {
783 0         0 foreach my $rcpt_to ( @{ $smtp->{'lmtp_rcpt'} } ) {
  0         0  
784 0         0 $self->loginfo ( "SMTPDefer: $defer_reason" );
785 0         0 print $socket $defer_reason . "\r\n";
786             }
787             }
788             else {
789 0         0 $self->loginfo ( "SMTPDefer: $defer_reason" );
790 0         0 print $socket $defer_reason . "\r\n";
791             }
792             }
793             else {
794 0         0 $handler->metric_count( 'mail_processed_total', { 'result' => 'deferred_error' } );
795 0 0       0 if ( $smtp->{'using_lmtp'} ) {
796 0         0 foreach my $rcpt_to ( @{ $smtp->{'lmtp_rcpt'} } ) {
  0         0  
797 0         0 print $socket "451 4.0.5 That's not right\r\n";
798             }
799             }
800             else {
801 0         0 print $socket "451 4.0.6 That's not right\r\n";
802             }
803             }
804 42         465 $self->smtp_status('smtp.i.data.done');
805              
806             # Reset
807 42         234 $smtp->{'mail_from'} = q{};
808 42         212 $smtp->{'rcpt_to'} = [];
809 42         247 $smtp->{'headers'} = [];
810 42         150 $smtp->{'body'} = q{};
811 42         122 $smtp->{'has_data'} = 0;
812 42         126 $smtp->{'fwd_connect_host'} = undef;
813 42         130 $smtp->{'fwd_connect_ip'} = undef;
814 42         110 $smtp->{'fwd_helo_host'} = undef;
815 42         164 $smtp->{'lmtp_rcpt'} = [];
816 42         123 $smtp->{'string'} = q{};
817 42         329 $self->{'handler'}->{'_Handler'}->top_close_callback();
818 42         204 $smtp->{'init_required'} = 1;
819              
820 42 50       2276 if ( $handler->{'exit_on_close'} ) {
821 0         0 $smtp->{'last_command'} = 1;
822 0         0 $handler->dbgout( 'EXIT ON CLOSE REQUESTED', 'NO MORE SMTP', LOG_INFO );
823             }
824             }
825              
826             sub smtp_insert_received_header {
827 41     41 1 195 my ( $self ) = @_;
828 41         159 my $smtp = $self->{'smtp'};
829              
830             my $value = join ( q{},
831              
832             'from ',
833             $smtp->{'helo_host'},
834             ' (',
835             $smtp->{'connect_host'}
836             ,
837             ' [',
838             $smtp->{'connect_ip'},
839             '])',
840             "\r\n",
841              
842             ' by ',
843 41         339 $smtp->{'server_name'},
844             ' (Authentication Milter)',
845             ' with ESMTP',
846             "\r\n",
847              
848             ' id ',
849             $self->smtp_queue_id(),
850             ';',
851             "\r\n",
852              
853             ' ',
854             email_date(),
855              
856             );
857              
858 41         7683 splice @{ $smtp->{'headers'} }, 0, 0, 'Received: '. $value;
  41         413  
859             }
860              
861             sub smtp_forward_to_destination {
862 41     41 1 154 my ( $self ) = @_;
863 41         176 $self->smtp_status('smtp.o');
864              
865 41         255 my $smtp = $self->{'smtp'};
866              
867 41         417 $self->smtp_insert_received_header();
868              
869 41         255 my $smtp_conf = $self->get_smtp_config();
870              
871 41         182 my $sock = $smtp->{'destination_sock'};
872              
873 41         111 my $new_sock = 0;
874              
875 41         91 my $line;
876              
877 41 100       178 if ( $sock ) {
878 5 50       48 if ( ! $sock->connected() ) {
879 0         0 $self->logerror( "Outbound SMTP socket was disconnected by remote end" );
880 0         0 undef $sock;
881             }
882             }
883              
884 41 100       265 if ( ! $sock ) {
885 36         80 $new_sock = 1;
886 36         178 $self->smtp_status('smtp.o.open');
887              
888 36 50       431 if ( $smtp_conf->{'sock_type'} eq 'inet' ) {
    50          
889             $sock = IO::Socket::INET->new(
890             'Proto' => 'tcp',
891             'PeerAddr' => $smtp_conf->{'sock_host'},
892 0         0 'PeerPort' => $smtp_conf->{'sock_port'},
893             );
894             }
895             elsif ( $smtp_conf->{'sock_type'} eq 'unix' ) {
896             $sock = IO::Socket::UNIX->new(
897 36         503 'Peer' => $smtp_conf->{'sock_path'},
898             );
899             }
900             else {
901 0         0 $self->logerror( 'Outbound SMTP Socket type unknown or undefined: ' . $smtp_conf->{'sock_type'} );
902 0         0 return 0;
903             }
904              
905 36 50       13079 if ( ! $sock ) {
906 0         0 $self->logerror( "Could not open outbound SMTP socket: $!" );
907 0         0 return 0;
908             }
909 36         155 eval {
910 36         16535 $line = <$sock>;
911             };
912 36 50       353 if ( my $error = $@ ) {
913 0         0 $self->logerror( "Outbound SMTP Read Error: $error" );
914 0         0 return 0;
915             }
916 36         319 alarm( 0 );
917              
918 36 50       209 if ( ! $line =~ /250/ ) {
919 0         0 $self->logerror( "Unexpected SMTP response $line" );
920 0         0 return 0;
921             }
922              
923 36         229 $smtp->{'destination_sock'} = $sock;
924             }
925              
926 41         381 $self->logdebug( 'Sending envelope to destination' );
927              
928 41 100       148 if ( $new_sock ) {
929 36 50       414 $self->send_smtp_packet( $sock, 'EHLO ' . $smtp->{'server_name'}, '250' ) || return;
930             }
931             else {
932 5 50       31 $self->send_smtp_packet( $sock, 'RSET', '250' ) || return;
933             }
934              
935 41 50       287 if ( $smtp->{'fwd_helo_host'} ) {
936 41 50       323 $self->send_smtp_packet( $sock, 'XFORWARD HELO=' . $smtp->{'fwd_helo_host'}, '250' ) || return;
937             }
938 41 50       388 if ( $smtp->{'fwd_connect_ip'} ) {
939 41 50       266 $self->send_smtp_packet( $sock, 'XFORWARD ADDR=' . $smtp->{'fwd_connect_ip'}, '250' ) || return;
940             }
941 41 50       297 if ( $smtp->{'fwd_connect_host'} ) {
942 41 50       313 $self->send_smtp_packet( $sock, 'XFORWARD NAME=' . $smtp->{'fwd_connect_host'}, '250' ) || return;
943             }
944 41 50       349 if ( $smtp->{'fwd_ident'} ) {
945 0 0       0 $self->send_smtp_packet( $sock, 'XFORWARD IDENT=' . $smtp->{'fwd_ident'}, '250' ) || return;
946             }
947              
948 41 50       334 $self->send_smtp_packet( $sock, 'MAIL FROM:' . $smtp->{'mail_from'}, '250' ) || return;
949 41         255 foreach my $rcpt_to ( @{ $smtp->{'rcpt_to'} } ) {
  41         252  
950 41 50       254 $self->send_smtp_packet( $sock, 'RCPT TO:' . $rcpt_to, '250' ) || return;
951             }
952              
953 41         378 $self->logdebug( 'Sending data to destination' );
954 41 50       270 $self->send_smtp_packet( $sock, 'DATA', '354' ) || return;
955              
956 41         479 $self->smtp_status('smtp.o.body');
957 41         213 my $email = q{};
958 41         156 foreach my $header ( @{ $smtp->{'headers'} } ) {
  41         278  
959 597         1614 $email .= "$header\r\n";
960             }
961 41         176 $email .= "\r\n";
962              
963 41         170 my $spool = $smtp->{'spool'};
964 41 50       220 if ( $spool ) {
965              
966             # Handle transparency - should not be any in headers, but for completeness
967 0         0 $email =~ s/\015\012\./\015\012\.\./g;
968              
969 0         0 print $sock $email;
970              
971 0         0 seek( $spool, 0, 0 );
972 0         0 while ( my $line = <$spool> ) {
973 0         0 $line =~ s/\015?\012/\015\012/g;
974 0         0 $line =~ s/^\./\.\./g;
975 0         0 print $sock $line;
976             }
977              
978             }
979             else {
980 41         187 my $body = $smtp->{'body'};
981 41         1088 $body =~ s/\015?\012/\015\012/g;
982 41         259 $email .= $body;
983              
984             # Handle transparency
985 41         327 $email =~ s/\015\012\./\015\012\.\./g;
986              
987 41         1949 print $sock $email;
988             }
989              
990 41         461 $self->logdebug( 'Sending end to destination' );
991 41 50       331 $self->send_smtp_packet( $sock, '.', '250' ) || return;
992 41         385 $self->logdebug( 'Sent to destination' );
993 41         325 $self->smtp_status('smtp.o.done');
994              
995 41         356 return 1;
996             }
997              
998             sub close_destination_socket {
999 37     37 1 163 my ( $self ) = @_;
1000 37         131 my $smtp = $self->{'smtp'};
1001 37         130 my $sock = $smtp->{'destination_sock'};
1002 37 100       161 return if ! $sock;
1003 36 50       178 $self->send_smtp_packet( $sock, 'QUIT', '221' ) || return;
1004 36         414 $sock->close();
1005 36         1953 delete $smtp->{'destination_sock'};
1006             }
1007              
1008             sub send_smtp_packet {
1009 364     364 1 1495 my ( $self, $socket, $send, $expect ) = @_;
1010 364         898 my $smtp = $self->{'smtp'};
1011              
1012 364         1106 my $status = lc $send;
1013 364         2223 $status =~ s/^([^ ]+) .*$/$1/;
1014 364 100       1305 $status = 'dot' if $status eq '.';
1015 364         1507 $self->smtp_status('smtp.o.' . $status);
1016              
1017 364         14465 print $socket "$send\r\n";
1018              
1019 364         2642 $self->smtp_status('smtp.o.' . $status . '.wait');
1020              
1021 364     0   7722 local $SIG{'ALRM'} = sub{ die "Timeout\n" };
  0         0  
1022 364         2861 alarm( $smtp->{'smtp_timeout_out'} );
1023 364         988 my $recv;
1024 364         764 eval {
1025 364         130776 $recv = <$socket>;
1026 364         4225 $self->extra_debugging( "RAW DEBUG: ". $recv );
1027 364         1801 while ( $recv =~ /^\d\d\d\-/ ) {
1028 0         0 $self->smtp_status('smtp.o.' . $status . '.waitext');
1029 0         0 $recv = <$socket>;
1030 0         0 $self->extra_debugging( "RAW DEBUG: ". $recv );
1031             }
1032             };
1033 364 50       1056 if ( my $error = $@ ) {
1034 0         0 $self->logerror( "Outbound SMTP Read Error: $error" );
1035 0         0 $smtp->{'string'} = $error;
1036 0         0 return 0;
1037             }
1038 364         2517 alarm( 0 );
1039 364         1971 $self->smtp_status('smtp.o');
1040              
1041 364   50     1897 $smtp->{'string'} = $recv || q{};
1042 364         2296 $smtp->{'string'} =~ s/\r//g;
1043 364         1242 $smtp->{'string'} =~ s/\n//g;
1044              
1045 364 50       5213 if ( $recv =~ /^$expect/ ) {
1046 364         8076 return 1;
1047             }
1048             else {
1049 0         0 $self->logerror( "SMTP Send expected $expect received $recv when sending $send" );
1050 0         0 return 0;
1051             }
1052             }
1053              
1054             sub add_header {
1055 9     9 1 24 my ( $self, $header, $value ) = @_;
1056 9         32 my $smtp = $self->{'smtp'};
1057 9         109 $value =~ s/\015?\012/\015\012/g;
1058 9         25 push @{ $smtp->{'headers'} } , "$header: $value";
  9         57  
1059             }
1060              
1061             sub change_header {
1062 13     13 1 485 my ( $self, $header, $index, $value ) = @_;
1063 13         35 my $smtp = $self->{'smtp'};
1064              
1065 13         24 my $header_i = 0;
1066 13         25 my $search_i = 0;
1067 13         21 my $result_i;
1068              
1069             HEADER:
1070 13         25 foreach my $header_v ( @{ $smtp->{'headers'} } ) {
  13         41  
1071 137 100       365 if ( substr( lc $header_v, 0, length($header) + 1 ) eq lc "$header:" ) {
1072 49         68 $search_i ++;
1073 49 100       98 if ( $search_i == $index ) {
1074 13         24 $result_i = $header_i;
1075 13         38 last HEADER;
1076             }
1077             }
1078 124         202 $header_i ++;
1079             }
1080              
1081 13 50       33 if ( $result_i ) {
1082 13 50       40 if ( $value eq q{} ) {
1083 13         23 splice @{ $smtp->{'headers'} }, $result_i, 1;
  13         88  
1084             }
1085             else {
1086 0         0 $value =~ s/\015?\012/\015\012/g;
1087 0         0 $smtp->{'headers'}->[ $result_i ] = "$header: $value";
1088             #untested.
1089             }
1090             }
1091             }
1092              
1093             sub insert_header {
1094 319     319 1 1179 my ( $self, $index, $key, $value ) = @_;
1095 319         947 my $smtp = $self->{'smtp'};
1096 319         3980 $value =~ s/\015?\012/\015\012/g;
1097 319         964 splice @{ $smtp->{'headers'} }, $index - 1, 0, "$key: $value";
  319         2993  
1098             }
1099              
1100             1;
1101              
1102             __END__
1103              
1104             =pod
1105              
1106             =encoding UTF-8
1107              
1108             =head1 NAME
1109              
1110             Mail::Milter::Authentication::Protocol::SMTP - SMTP protocol handling
1111              
1112             =head1 VERSION
1113              
1114             version 3.20230911
1115              
1116             =head1 SYNOPSIS
1117              
1118             Subclass of Net::Server::PreFork for bringing up the main server process for authentication_milter.
1119              
1120             Please see Net::Server docs for more detail of the server code.
1121              
1122             =head1 DESCRIPTION
1123              
1124             A Perl implenmetation of email authentication standards rolled up into a single easy to use milter.
1125              
1126             =head1 FUNCTIONS
1127              
1128             =over
1129              
1130             =item I<command_param( $command, $index )>
1131              
1132             Extract parameters from a SMTP command line.
1133              
1134             =back
1135              
1136             =head1 METHODS
1137              
1138             =over
1139              
1140             =item register_metrics
1141              
1142             Return details of the metrics this module exports.
1143              
1144             =item I<protocol_process_request( $command, $buffer )>
1145              
1146             Process the command from the SMTP protocol stream.
1147              
1148             =item I<get_smtp_config()>
1149              
1150             Return the SMTP config for the given connection, or
1151             the default config if no connection specific config
1152             exists.
1153              
1154             =item I<queue_type()>
1155              
1156             Return the smtp queue type, either before or after
1157             A before queue will not have an upstream queue id, an
1158             after queue will.
1159              
1160             =item I<send_smtp_packet( $socket, $send, $expect )>
1161              
1162             Send an SMTP command to the protocol stream.
1163             Expecting a response $expect.
1164              
1165             =item I<smtp_command_data( $command )>
1166              
1167             Process the SMTP DATA command.
1168              
1169             =item I<smtp_command_ehlo( $command )>
1170              
1171             Process the SMTP EHLO command.
1172              
1173             =item I<smtp_command_helo( $command )>
1174              
1175             Process the SMTP HELO command.
1176              
1177             =item I<smtp_command_lhlo( $command )>
1178              
1179             Process the LMTP LHLO command.
1180              
1181             =item I<smtp_command_mailfrom( $command )>
1182              
1183             Process the SMTP MAIL FROM command.
1184              
1185             =item I<smtp_command_rcptto( $command )>
1186              
1187             Process the SMTP RCPT TO command.
1188              
1189             =item I<smtp_command_rset( $command )>
1190              
1191             Process the SMTP RSET command.
1192              
1193             =item I<smtp_command_xforward( $command )>
1194              
1195             Process the SMTP XFORWARD command.
1196              
1197             =item I<smtp_forward_to_destination()>
1198              
1199             Send the received SMTP transaction on to its destination
1200             with authentication results headers (etc) added.
1201              
1202             =item I<close_destination_socket()>
1203              
1204             QUIT and close the destination socket if open.
1205              
1206             =item I<smtp_init()>
1207              
1208             Initialise transaction data as/when required.
1209              
1210             =item I<smtp_insert_received_header()>
1211              
1212             Insert a SMTP Received header into the email.
1213              
1214             =item I<smtp_queue_id()>
1215              
1216             Return a generated Queue ID for the email.
1217             This can include the received ID from XFORWARD.
1218              
1219             =item I<add_header( $header, $value )>
1220              
1221             Add a header
1222              
1223             =item I<change_header( $header, $index, $value )>
1224              
1225             Change a header
1226              
1227             =item I<insert_header( $index, $key, $value )>
1228              
1229             Insert a header
1230              
1231             =item I<smtp_status( $status )>
1232              
1233             Update the process name status line
1234              
1235             =back
1236              
1237             =head1 AUTHOR
1238              
1239             Marc Bradshaw <marc@marcbradshaw.net>
1240              
1241             =head1 COPYRIGHT AND LICENSE
1242              
1243             This software is copyright (c) 2020 by Marc Bradshaw.
1244              
1245             This is free software; you can redistribute it and/or modify it under
1246             the same terms as the Perl 5 programming language system itself.
1247              
1248             =cut