File Coverage

blib/lib/Mail/Sendmail.pm
Criterion Covered Total %
statement 153 274 55.8
branch 40 170 23.5
condition 15 48 31.2
subroutine 11 17 64.7
pod 2 6 33.3
total 221 515 42.9


line stmt bran cond sub pod time code
1             package Mail::Sendmail;
2             # Mail::Sendmail by Milivoj Ivkovic
3             # see embedded POD documentation after __END__
4             # or http://alma.ch/perl/mail.html
5              
6             =head1 NAME
7              
8             Mail::Sendmail v. 0.79_16 - Simple platform independent mailer
9              
10             =cut
11              
12             $VERSION = substr q$Revision: 0.79_16 $, 10;
13              
14             # *************** Configuration you may want to change *******************
15             # You probably want to set your SMTP server here (unless you specify it in
16             # every script), and leave the rest as is. See pod documentation for details
17              
18             %mailcfg = (
19             # List of SMTP servers:
20             'smtp' => [ qw( localhost ) ],
21             #'smtp' => [ qw( mail.mydomain.com ) ], # example
22              
23             'from' => '', # default sender e-mail, used when no From header in mail
24              
25             'mime' => 1, # use MIME encoding by default
26              
27             'retries' => 1, # number of retries on smtp connect failure
28             'delay' => 1, # delay in seconds between retries
29              
30             'tz' => '', # only to override automatic detection
31             'port' => 25, # change it if you always use a non-standard port
32             'debug' => 0 # prints stuff to STDERR
33             );
34              
35             # *******************************************************************
36              
37             require Exporter;
38 1     1   1001 use strict;
  1         2  
  1         71  
39 1         191 use vars qw(
40             $VERSION
41             @ISA
42             @EXPORT
43             @EXPORT_OK
44             %mailcfg
45             $address_rx
46             $debug
47             $log
48             $error
49             $retry_delay
50             $connect_retries
51             $auth_support
52 1     1   7 );
  1         2  
53              
54 1     1   1402 use Socket;
  1         5478  
  1         555  
55 1     1   1777 use Time::Local; # for automatic time zone detection
  1         1964  
  1         64  
56 1     1   858 use Sys::Hostname; # for use of hostname in HELO
  1         1350  
  1         1125  
57              
58             #use Digest::HMAC_MD5 qw(hmac_md5 hmac_md5_hex);
59              
60             $auth_support = 'DIGEST-MD5 CRAM-MD5 PLAIN LOGIN';
61              
62             # use MIME::QuotedPrint if available and configured in %mailcfg
63 1     1   999 eval("use MIME::QuotedPrint");
  1         2029  
  1         48  
64             $mailcfg{'mime'} &&= (!$@);
65              
66             @ISA = qw(Exporter);
67             @EXPORT = qw(&sendmail);
68             @EXPORT_OK = qw(
69             %mailcfg
70             time_to_date
71             $address_rx
72             $debug
73             $log
74             $error
75             );
76              
77             # regex for e-mail addresses where full=$1, user=$2, domain=$3
78             # see pod documentation about this regex
79              
80             my $word_rx = '[\x21\x23-\x27\x2A-\x2B\x2D\x2F\w\x3D\x3F]+';
81             my $user_rx = $word_rx # valid chars
82             .'(?:\.' . $word_rx . ')*' # possibly more words preceded by a dot
83             ;
84             my $dom_rx = '\w[-\w]*(?:\.\w[-\w]*)*'; # less valid chars in domain names
85             my $ip_rx = '\[\d{1,3}(?:\.\d{1,3}){3}\]';
86              
87             $address_rx = '((' . $user_rx . ')\@(' . $dom_rx . '|' . $ip_rx . '))';
88             ; # v. 0.61
89              
90             sub _require_md5 {
91 0     0   0 eval { require Digest::MD5; Digest::MD5->import(qw(md5 md5_hex)); };
  0         0  
  0         0  
92 0 0       0 $error .= $@ if $@;
93 0 0       0 return ($@ ? undef : 1);
94             }
95              
96             sub _require_base64 {
97 0     0   0 eval {
98 0         0 require MIME::Base64; MIME::Base64->import(qw(encode_base64 decode_base64));
  0         0  
99             };
100 0 0       0 $error .= $@ if $@;
101 0 0       0 return ($@ ? undef : 1);
102             }
103              
104             sub _hmac_md5 {
105 0     0   0 my ($pass, $ckey) = @_;
106 0         0 my $size = 64;
107 0 0       0 $pass = md5($pass) if length($pass) > $size;
108 0         0 my $ipad = $pass ^ (chr(0x36) x $size);
109 0         0 my $opad = $pass ^ (chr(0x5c) x $size);
110 0         0 return md5_hex($opad, md5($ipad, $ckey));
111             }
112              
113             sub _digest_md5 {
114 0     0   0 my ($user, $pass, $challenge, $realm) = @_;
115              
116 0         0 my %ckey = map { /^([^=]+)="?(.+?)"?$/ } split(/,/, $challenge);
  0         0  
117 0   0     0 $realm ||= $ckey{realm}; #($user =~ s/\@(.+)$//o) ? $1 : $server;
118 0         0 my $nonce = $ckey{nonce};
119 0         0 my $cnonce = &make_cnonce;
120 0   0     0 my $uri = join('/', 'smtp', hostname()||'localhost', $ckey{realm});
121 0         0 my $qop = 'auth';
122 0         0 my $nc = '00000001';
123 0         0 my($hv, $a1, $a2);
124 0         0 $hv = md5("$user:$realm:$pass");
125 0         0 $a1 = md5_hex("$hv:$nonce:$cnonce");
126 0         0 $a2 = md5_hex("AUTHENTICATE:$uri");
127 0         0 $hv = md5_hex("$a1:$nonce:$nc:$cnonce:$qop:$a2");
128 0         0 return qq(username="$user",realm="$ckey{realm}",nonce="$nonce",nc=$nc,cnonce="$cnonce",digest-uri="$uri",response=$hv,qop=$qop);
129             }
130              
131             sub make_cnonce {
132 0     0 0 0 my $s = '' ;
133 0         0 for(1..16) { $s .= chr(rand 256) }
  0         0  
134 0         0 $s = encode_base64($s, "");
135 0         0 $s =~ s/\W/X/go;
136 0         0 return substr($s, 0, 16);
137             }
138              
139             sub time_to_date {
140             # convert a time() value to a date-time string according to RFC 822
141              
142 2   33 2 1 151 my $time = $_[0] || time(); # default to now if no argument
143              
144 2         11 my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
145 2         5 my @wdays = qw(Sun Mon Tue Wed Thu Fri Sat);
146              
147 2         66 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)
148             = localtime($time);
149              
150 2         8 my $TZ = $mailcfg{'tz'};
151 2 50       7 if ( $TZ eq "" ) {
152             # offset in hours
153 2         40 my $offset = sprintf "%.1f", (timegm(localtime) - time) / 3600;
154 2         93 my $minutes = sprintf "%02d", abs( $offset - int($offset) ) * 60;
155 2         6 $TZ = sprintf("%+03d", int($offset)) . $minutes;
156             }
157 2         20 return join(" ",
158             ($wdays[$wday] . ','),
159             $mday,
160             $months[$mon],
161             $year+1900,
162             sprintf("%02d:%02d:%02d", $hour, $min, $sec),
163             $TZ
164             );
165             } # end sub time_to_date
166              
167             sub sendmail {
168              
169 1     1 1 37 $error = '';
170 1         26 $log = "Mail::Sendmail v. $VERSION - " . scalar(localtime()) . "\n";
171              
172 1         4 my $CRLF = "\015\012";
173 1         5 local $/ = $CRLF;
174 1         4 local $\ = ''; # to protect us from outside settings
175 1         2 local $_;
176              
177 1         2 my (%mail, $k,
178             $smtp, $server, $port, $connected, $localhost,
179             $fromaddr, $recip, @recipients, $to, $header,
180             %esmtp, @wanted_methods,
181             );
182 1     1   8 use vars qw($server_reply);
  1         2  
  1         4258  
183             # -------- a few internal subs ----------
184             sub fail {
185             # things to do before returning a sendmail failure
186 0     0 0 0 $error .= join(" ", @_) . "\n";
187 0 0       0 if ($server_reply) {
188 0         0 $error .= "Server said: $server_reply\n";
189 0 0       0 print STDERR "Server said: $server_reply\n" if $^W;
190             }
191 0         0 close S;
192 0         0 return 0;
193             }
194              
195             sub socket_write {
196 13     13 0 23 my $i;
197 13         44 for $i (0..$#_) {
198             # accept references, so we don't copy potentially big data
199 15 100       51 my $data = ref($_[$i]) ? $_[$i] : \$_[$i];
200 15 50       63 if ($mailcfg{'debug'} > 5) {
201 0 0       0 if (length($$data) < 500) {
202 0         0 print ">", $$data;
203             }
204             else {
205 0         0 print "> [...", length($$data), " bytes sent ...]\n";
206             }
207             }
208 15 50       645 print(S $$data) || return 0;
209             }
210 13         66 1;
211             }
212              
213             sub socket_read {
214 7     7 0 19 $server_reply = "";
215 7         15 do {
216 16         259537 $_ = ;
217 16         54 $server_reply .= $_;
218             #chomp $_;
219 16 50       84 print "<$_" if $mailcfg{'debug'} > 5;
220 16 50 33     241 if (/^[45]/ or !$_) {
221 0         0 chomp $server_reply;
222 0         0 return; # return false
223             }
224             } while (/^[\d]+-/);
225 7         25 chomp $server_reply;
226 7         66 return $server_reply;
227             }
228             # -------- end of internal subs ----------
229              
230             # all config keys to lowercase, to prevent typo errors
231 1         6 foreach $k (keys %mailcfg) {
232 8 50       19 if ($k =~ /[A-Z]/) {
233 0         0 $mailcfg{lc($k)} = $mailcfg{$k};
234             }
235             }
236              
237             # redo mail hash, arranging keys case etc...
238 1         4 while (@_) {
239 5         9 $k = shift @_;
240 5 0 33     13 if (!$k and $^W) {
241 0         0 warn "Received false mail hash key: \'$k\'. Did you forget to put it in quotes?\n";
242             }
243              
244             # arrange keys case
245 5         12 $k = ucfirst lc($k);
246              
247 5         11 $k =~ s/\s*:\s*$//o; # kill colon (and possible spaces) at end, we add it later.
248             # uppercase also after "-", so people don't complain that headers case is different
249             # than in Outlook.
250 5         7 $k =~ s/-(.)/"-" . uc($1)/ge;
  0         0  
251 5         10 $mail{$k} = shift @_;
252 5 100       27 if ($k !~ /^(Message|Body|Text)$/i) {
253             # normalize possible line endings in headers
254 4         7 $mail{$k} =~ s/\015\012?/\012/go;
255 4         12 $mail{$k} =~ s/\012/$CRLF/go;
256             }
257             }
258              
259 1   33     7 $smtp = $mail{'Smtp'} || $mail{'Server'};
260 1 50 33     10 unshift @{$mailcfg{'smtp'}}, $smtp if ($smtp and $mailcfg{'smtp'}->[0] ne $smtp);
  1         3  
261              
262             # delete non-header keys, so we don't send them later as mail headers
263             # I like this syntax, but it doesn't seem to work with AS port 5.003_07:
264             # delete @mail{'Smtp', 'Server'};
265             # so instead:
266 1         4 delete $mail{'Smtp'}; delete $mail{'Server'};
  1         2  
267              
268 1   50     32 $mailcfg{'port'} = $mail{'Port'} || $mailcfg{'port'} || 25;
269 1         3 delete $mail{'Port'};
270              
271 1         3 my $auth = $mail{'Auth'};
272 1         2 delete $mail{'Auth'};
273              
274              
275             { # don't warn for undefined values below
276 1         2 local $^W = 0;
  1         5  
277 1         5 $mail{'Message'} = join("", $mail{'Message'}, $mail{'Body'}, $mail{'Text'});
278             }
279              
280             # delete @mail{'Body', 'Text'};
281 1         3 delete $mail{'Body'}; delete $mail{'Text'};
  1         2  
282              
283             # Extract 'From:' e-mail address to use as envelope sender
284              
285 1   33     7 $fromaddr = $mail{'Sender'} || $mail{'From'} || $mailcfg{'from'};
286             #delete $mail{'Sender'};
287 1 50       199 unless ($fromaddr =~ /$address_rx/) {
288 0         0 return fail("Bad or missing From address: \'$fromaddr\'");
289             }
290 1         4 $fromaddr = $1;
291              
292             # add Date header if needed
293 1   33     7 $mail{Date} ||= time_to_date() ;
294 1         4 $log .= "Date: $mail{Date}\n";
295              
296             # cleanup message, and encode if needed
297 1         3 $mail{'Message'} =~ s/\r\n/\n/go; # normalize line endings, step 1 of 2 (next step after MIME encoding)
298              
299 1   50     7 $mail{'Mime-Version'} ||= '1.0';
300 1   50     5 $mail{'Content-Type'} ||= 'text/plain; charset="iso-8859-1"';
301              
302 1 50 33     9 unless ( $mail{'Content-Transfer-Encoding'}
303             || $mail{'Content-Type'} =~ /multipart/io )
304             {
305 1 50       4 if ($mailcfg{'mime'}) {
306 1         3 $mail{'Content-Transfer-Encoding'} = 'quoted-printable';
307 1         14 $mail{'Message'} = encode_qp($mail{'Message'});
308             }
309             else {
310 0         0 $mail{'Content-Transfer-Encoding'} = '8bit';
311 0 0       0 if ($mail{'Message'} =~ /[\x80-\xFF]/o) {
312 0         0 $error .= "MIME::QuotedPrint not present!\nSending 8bit characters, hoping it will come across OK.\n";
313 0 0       0 warn "MIME::QuotedPrint not present!\n",
314             "Sending 8bit characters without encoding, hoping it will come across OK.\n"
315             if $^W;
316             }
317             }
318             }
319              
320 1         3 $mail{'Message'} =~ s/^\./\.\./gom; # handle . as first character
321 1         5 $mail{'Message'} =~ s/\n/$CRLF/go; # normalize line endings, step 2.
322              
323             # Get recipients
324             { # don't warn for undefined values below
325 1         2 local $^W = 0;
  1         3  
326 1         4 $recip = join(", ", $mail{To}, $mail{Cc}, $mail{Bcc});
327             }
328              
329 1         2 delete $mail{'Bcc'};
330              
331 1         2 @recipients = ();
332 1         139 while ($recip =~ /$address_rx/go) {
333 1         7 push @recipients, $1;
334             }
335 1 50       3 unless (@recipients) {
336 0         0 return fail("No recipient!")
337             }
338              
339             # get local hostname for polite HELO
340 1   50     5 $localhost = hostname() || 'localhost';
341              
342 1         17 foreach $server ( @{$mailcfg{'smtp'}} ) {
  1         4  
343             # open socket needs to be inside this foreach loop on Linux,
344             # otherwise all servers fail if 1st one fails !??! why?
345 1 50       1016 unless ( socket S, AF_INET, SOCK_STREAM, scalar(getprotobyname 'tcp') ) {
346 0         0 return fail("socket failed ($!)")
347             }
348              
349 1 50       6 print "- trying $server\n" if $mailcfg{'debug'} > 1;
350              
351 1         3 $server =~ s/\s+//go; # remove spaces just in case of a typo
352             # extract port if server name like "mail.domain.com:2525"
353 1 50       8 $port = ($server =~ s/:(\d+)$//o) ? $1 : $mailcfg{'port'};
354 1         2 $smtp = $server; # save $server for use outside foreach loop
355              
356 1         191830 my $smtpaddr = inet_aton $server;
357 1 50       8 unless ($smtpaddr) {
358 0         0 $error .= "$server not found\n";
359 0         0 next; # next server
360             }
361              
362 1         4 my $retried = 0; # reset retries for each server
363 1   33     26893 while ( ( not $connected = connect S, pack_sockaddr_in($port, $smtpaddr) )
364             and ( $retried < $mailcfg{'retries'} )
365             ) {
366 0         0 $retried++;
367 0         0 $error .= "connect to $server failed ($!)\n";
368 0 0       0 print "- connect to $server failed ($!)\n" if $mailcfg{'debug'} > 1;
369 0 0       0 print "retrying in $mailcfg{'delay'} seconds...\n" if $mailcfg{'debug'} > 1;
370 0         0 sleep $mailcfg{'delay'};
371             }
372              
373 1 50       13 if ( $connected ) {
374 1 50       48 print "- connected to $server\n" if $mailcfg{'debug'} > 3;
375 1         9 last;
376             }
377             else {
378 0         0 $error .= "connect to $server failed\n";
379 0 0       0 print "- connect to $server failed, next server...\n" if $mailcfg{'debug'} > 1;
380 0         0 next; # next server
381             }
382             }
383              
384 1 50       8 unless ( $connected ) {
385 0         0 return fail("connect to $smtp failed ($!) no (more) retries!")
386             };
387              
388             {
389 1         4 local $^W = 0; # don't warn on undefined variables
  1         14  
390             # Add info to log variable
391 1         21 $log .= "Server: $smtp Port: $port\n"
392             . "From: $fromaddr\n"
393             . "Subject: $mail{Subject}\n"
394             ;
395             }
396              
397 1         11 my($oldfh) = select(S); $| = 1; select($oldfh);
  1         9  
  1         11  
398              
399 1 50       11 socket_read()
400             || return fail("Connection error from $smtp on port $port ($_)");
401 1 50       12 socket_write("EHLO $localhost$CRLF")
402             || return fail("send EHLO error (lost connection?)");
403 1         4 my $ehlo = socket_read();
404 1 50       11 if ($ehlo) {
405             # parse EHLO response
406 10         206 map {
407 1         13 s/^\d+[- ]//;
408 10         29 my ($k, $v) = split /\s+/, $_, 2;
409 10 50 100     86 $esmtp{$k} = $v || 1 if $k;
410             } split(/\n/, $ehlo);
411             }
412             else {
413             # try plain HELO instead
414 0 0       0 socket_write("HELO $localhost$CRLF")
415             || return fail("send HELO error (lost connection?)");
416             }
417              
418 1 50       8 if ($auth) {
419 0 0       0 warn "AUTH requested\n" if ($mailcfg{debug} > 4);
420             # reduce wanted methods to those supported
421 0         0 my @methods = grep {$esmtp{'AUTH'}=~/(^|\s)$_(\s|$)/i}
  0         0  
422 0         0 grep {$auth_support =~ /(^|\s)$_(\s|$)/i}
423             grep /\S/, split(/\s+/, $auth->{method});
424              
425 0 0       0 if (@methods) {
426             # try to authenticate
427              
428 0 0       0 if (exists $auth->{pass}) {
429 0         0 $auth->{password} = $auth->{pass};
430             }
431              
432 0         0 my $method = uc $methods[0];
433 0 0       0 _require_base64() || fail("Could not use MIME::Base64 module required for authentication");
434 0 0       0 if ($method eq "LOGIN") {
    0          
    0          
    0          
435 0 0       0 print STDERR "Trying AUTH LOGIN\n" if ($mailcfg{debug} > 9);
436 0 0       0 socket_write("AUTH LOGIN$CRLF")
437             || return fail("send AUTH LOGIN failed (lost connection?)");
438 0 0       0 socket_read()
439             || return fail("AUTH LOGIN failed: $server_reply");
440 0 0       0 socket_write(encode_base64($auth->{user},$CRLF))
441             || return fail("send LOGIN username failed (lost connection?)");
442 0 0       0 socket_read()
443             || return fail("LOGIN username failed: $server_reply");
444 0 0       0 socket_write(encode_base64($auth->{password},$CRLF))
445             || return fail("send LOGIN password failed (lost connection?)");
446 0 0       0 socket_read()
447             || return fail("LOGIN password failed: $server_reply");
448             }
449             elsif ($method eq "PLAIN") {
450 0 0       0 warn "Trying AUTH PLAIN\n" if ($mailcfg{debug} > 9);
451 0 0       0 socket_write(
452             "AUTH PLAIN "
453             . encode_base64(join("\0", $auth->{user}, $auth->{user}, $auth->{password}), $CRLF)
454             ) || return fail("send AUTH PLAIN failed (lost connection?)");
455 0 0       0 socket_read()
456             || return fail("AUTH PLAIN failed: $server_reply");
457             }
458             elsif ($method eq "CRAM-MD5") {
459 0 0       0 _require_md5() || fail("Could not use Digest::MD5 module required for authentication");
460 0 0       0 warn "Trying AUTH CRAM-MD5\n" if ($mailcfg{debug} > 9);
461 0 0       0 socket_write("AUTH CRAM-MD5$CRLF")
462             || return fail("send CRAM-MD5 failed (lost connection?)");
463 0   0     0 my $challenge = socket_read()
464             || return fail("AUTH CRAM-MD5 failed: $server_reply");
465 0         0 $challenge =~ s/^\d+\s+//;
466 0         0 my $response = _hmac_md5($auth->{password}, decode_base64($challenge));
467 0 0       0 socket_write(encode_base64("$auth->{user} $response", $CRLF))
468             || return fail("AUTH CRAM-MD5 failed: $server_reply");
469 0 0       0 socket_read()
470             || return fail("AUTH CRAM-MD5 failed: $server_reply");
471             }
472             elsif ($method eq "DIGEST-MD5") {
473 0 0       0 _require_md5() || fail("Could not use Digest::MD5 module required for authentication");
474 0 0       0 warn "Trying AUTH DIGEST-MD5\n" if ($mailcfg{debug} > 9);
475 0 0       0 socket_write("AUTH DIGEST-MD5$CRLF")
476             || return fail("send CRAM-MD5 failed (lost connection?)");
477 0   0     0 my $challenge = socket_read()
478             || return fail("AUTH DIGEST-MD5 failed: $server_reply");
479 0         0 $challenge =~ s/^\d+\s+//; $challenge =~ s/[\r\n]+$//;
  0         0  
480 0 0       0 warn "\nCHALLENGE=", decode_base64($challenge), "\n" if ($mailcfg{debug} > 10);
481 0         0 my $response = _digest_md5($auth->{user}, $auth->{password}, decode_base64($challenge), $auth->{realm});
482 0 0       0 warn "\nRESPONSE=$response\n" if ($mailcfg{debug} > 10);
483 0 0       0 socket_write(encode_base64($response, ""), $CRLF)
484             || return fail("AUTH DIGEST-MD5 failed: $server_reply");
485 0   0     0 my $status = socket_read()
486             || return fail("AUTH DIGEST-MD5 failed: $server_reply");
487 0 0       0 if ($status =~ /^3/) {
488 0 0       0 socket_write($CRLF)
489             || return fail("AUTH DIGEST-MD5 failed: $server_reply");
490 0 0       0 socket_read()
491             || return fail("AUTH DIGEST-MD5 failed: $server_reply");
492             }
493             }
494             else {
495 0         0 return fail("$method not supported (and wrongly advertised as supported by this silly module)\n");
496             }
497 0         0 $log .= "AUTH $method succeeded as user $auth->{user}\n";
498             }
499             else {
500 0         0 $esmtp{'AUTH'} =~ s/(^\s+|\s+$)//g; # cleanup for printig it below
501 0 0       0 if ($auth->{required}) {
502 0         0 return fail("Required AUTH method '$auth->{method}' not supported. "
503             ."(Server supports '$esmtp{'AUTH'}'. Module supports: '$auth_support')");
504             }
505             else {
506 0         0 warn "No common authentication method! Requested: '$auth->{method}'. Server supports '$esmtp{'AUTH'}'. Module supports: '$auth_support'. Skipping authentication\n";
507             }
508             }
509             }
510 1 50       30 socket_write("MAIL FROM:<$fromaddr>$CRLF")
511             || return fail("send MAIL FROM: error");
512 1 50       5 socket_read()
513             || return fail("MAIL FROM: error ($_)");
514              
515 1         6 my $to_ok = 0;
516 1         4 foreach $to (@recipients) {
517 1 50       10 socket_write("RCPT TO:<$to>$CRLF")
518             || return fail("send RCPT TO: error");
519 1 50       5 if (socket_read()) {
520 1         6 $log .= "To: $to\n";
521 1         5 $to_ok++;
522             } else {
523 0         0 $log .= "FAILED To: $to ($server_reply)";
524 0         0 $error .= "Bad recipient <$to>: $server_reply\n";
525             }
526             }
527 1 50       4 unless ($to_ok) {
528 0         0 return fail("No valid recipient");
529             }
530              
531             # start data part
532              
533 1 50       10 socket_write("DATA$CRLF")
534             || return fail("send DATA error");
535 1 50       5 socket_read()
536             || return fail("DATA error ($_)");
537              
538             # print headers
539 1         10 foreach $header (keys %mail) {
540 8 100       18 next if $header eq "Message";
541 7         24 $mail{$header} =~ s/\s+$//o; # kill possible trailing garbage
542 7 50       39 socket_write("$header: $mail{$header}$CRLF")
543             || return fail("send $header: error");
544             };
545              
546             #- test diconnecting from network here, to see what happens
547             #- print STDERR "DISCONNECT NOW!\n";
548             #- sleep 4;
549             #- print STDERR "trying to continue, expecting an error... \n";
550              
551             # send message body (passed as a reference, in case it's big)
552 1 50       7 socket_write($CRLF, \$mail{'Message'}, "$CRLF.$CRLF")
553             || return fail("send message error");
554 1 50       5 socket_read()
555             || return fail("message transmission error ($_)");
556 1         7 $log .= "\nResult: $_";
557              
558             # finish
559 1 50       7 socket_write("QUIT$CRLF")
560             || return fail("send QUIT error");
561 1         19 socket_read();
562 1         123 close S;
563              
564 1         30 return 1;
565             } # end sub sendmail
566              
567             1;
568             __END__