File Coverage

blib/lib/Net/SMTPS.pm
Criterion Covered Total %
statement 15 98 15.3
branch 0 46 0.0
condition 0 35 0.0
subroutine 5 8 62.5
pod 2 3 66.6
total 22 190 11.5


line stmt bran cond sub pod time code
1             # ====
2             # SSL/STARTTLS extention for G.Barr's Net::SMTP.
3             # plus, enable arbitrary SMTP auth mechanism selection.
4             # IO::Socket::SSL (also Net::SSLeay openssl),
5             # Authen::SASL, MIME::Base64 should be installed.
6             #
7             package Net::SMTPS;
8              
9 1     1   31024 use vars qw ( $VERSION @ISA );
  1         2  
  1         92  
10              
11             $VERSION = "0.05";
12              
13 1     1   6 use base qw ( Net::SMTP );
  1         2  
  1         827  
14 1     1   75198 use Net::Cmd; # import CMD_OK, CMD_MORE, ...
  1         6  
  1         52  
15 1     1   4 use Net::Config;
  1         1  
  1         119  
16              
17             eval {
18             require IO::Socket::INET6
19             and unshift @ISA, 'IO::Socket::INET6';
20             } or do {
21             require IO::Socket::INET
22             and unshift @ISA, 'IO::Socket::INET';
23             };
24              
25 1     1   4 use strict;
  1         1  
  1         645  
26              
27             # Override to support SSL/TLS.
28             sub new {
29 0     0 1   my $self = shift;
30 0   0       my $type = ref($self) || $self;
31 0           my ($host, %arg);
32 0 0         if (@_ % 2) {
33 0           $host = shift;
34 0           %arg = @_;
35             }
36             else {
37 0           %arg = @_;
38 0           $host = delete $arg{Host};
39             }
40 0           my $ssl = delete $arg{doSSL};
41 0 0         $ssl = 'ssl' if delete $arg{SSL};
42              
43 0 0         my $hosts = defined $host ? $host : $NetConfig{smtp_hosts};
44 0           my $obj;
45              
46             # eliminate IO::Socket::SSL from @ISA for multiple call of new.
47 0           @ISA = grep { !/IO::Socket::SSL/ } @ISA;
  0            
48              
49 0           my %_args = map { +"$_" => $arg{$_} } grep {! /^SSL/} keys %arg;
  0            
  0            
50              
51 0           my $h;
52 0   0       $_args{PeerPort} = $_args{Port} || 'smtp(25)';
53 0           $_args{Proto} = 'tcp';
54 0 0         $_args{Timeout} = defined $_args{Timeout} ? $_args{Timeout} : 120;
55              
56 0 0         foreach $h (@{ref($hosts) ? $hosts : [$hosts]}) {
  0            
57 0           $_args{PeerAddr} = ($host = $h);
58              
59             #if ($_args{Debug}) {
60             # foreach my $i (keys %_args) {
61             # print STDERR "$type>>> arg $i: $_args{$i}\n";
62             # }
63             #}
64              
65 0 0         $obj = $type->SUPER::new(
66             %_args
67             )
68             and last;
69             }
70              
71             return undef
72 0 0         unless defined $obj;
73              
74 0           $obj->autoflush(1);
75              
76 0 0         $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
77              
78             # common in SSL
79 0           my %ssl_args;
80 0 0         if ($ssl) {
81             eval {
82 0           require IO::Socket::SSL;
83 0 0         } or do {
84 0           $obj->set_status(500, ["Need working IO::Socket::SSL"]);
85 0           $obj->close;
86 0           return undef;
87             };
88 0           %ssl_args = map { +"$_" => $arg{$_} } grep {/^SSL/} keys %arg;
  0            
  0            
89 0 0         $IO::Socket::SSL::DEBUG = (exists $arg{Debug} ? $arg{Debug} : undef);
90             }
91              
92             # OverSSL
93 0 0 0       if (defined($ssl) && $ssl =~ /ssl/i) {
94             $obj->ssl_start(\%ssl_args)
95 0 0         or do {
96 0           $obj->set_status(500, ["Cannot start SSL"]);
97 0           $obj->close;
98 0           return undef;
99             };
100             }
101              
102 0 0         unless ($obj->response() == CMD_OK) {
103 0           $obj->close();
104 0           return undef;
105             }
106              
107 0           ${*$obj}{'net_smtp_exact_addr'} = $arg{ExactAddresses};
  0            
108 0           ${*$obj}{'net_smtp_host'} = $host;
  0            
109              
110 0           (${*$obj}{'net_smtp_banner'}) = $obj->message;
  0            
111 0           (${*$obj}{'net_smtp_domain'}) = $obj->message =~ /\A\s*(\S+)/;
  0            
112              
113 0 0 0       unless ($obj->hello($arg{Hello} || "")) {
114 0           $obj->close();
115 0           return undef;
116             }
117              
118             # STARTTLS
119 0 0 0       if (defined($ssl) && $ssl =~ /starttls/i && $obj->supports('STARTTLS') ) {
      0        
120             (($obj->command('STARTTLS')->response() == CMD_OK)
121             and $obj->ssl_start(\%ssl_args)
122             and $obj->hello($arg{Hello} || ""))
123 0 0 0       or do {
      0        
      0        
124 0           $obj->set_status(500, ["Cannot start SSL session"]);
125 0           $obj->close();
126 0           return undef;
127             };
128             }
129              
130 0           $obj;
131             }
132              
133             sub ssl_start {
134 0     0 0   my ($self, $args) = @_;
135 0           my $type = ref($self);
136              
137 0 0 0       (unshift @ISA, 'IO::Socket::SSL'
      0        
      0        
138             and IO::Socket::SSL->start_SSL($self, %$args)
139             and $self->isa('IO::Socket::SSL')
140             and bless $self, $type # re-bless 'cause IO::Socket::SSL blesses himself.
141             ) or return undef;
142             }
143              
144             # Override to specify a certain auth mechanism.
145             sub auth {
146 0     0 1   my ($self, $username, $password, $mech) = @_;
147              
148 0 0         if ($mech) {
149 0 0         $self->debug_print(1, "my favorite: ". $mech . "\n") if $self->debug;
150              
151 0           my @cl_mech = split /\s+/, $mech;
152 0           my @matched = ();
153 0 0         if (exists ${*$self}{'net_smtp_esmtp'}->{'AUTH'}) {
  0            
154 0           my $sv = ${*$self}{'net_smtp_esmtp'}->{'AUTH'};
  0            
155 0           foreach my $i (@cl_mech) {
156 0 0 0       if (index($sv, $i) >= 0 && grep(/$i/i, @matched) == () ) {
157 0           push @matched, uc($i);
158             }
159             }
160             }
161 0 0         if (@matched) {
162             ## override AUTH mech as specified.
163             ## if multiple mechs are specified, priority is still up to Authen::SASL module.
164 0           ${*$self}{'net_smtp_esmtp'}->{'AUTH'} = join " ", @matched;
  0            
165             }
166             }
167 0           $self->SUPER::auth($username, $password);
168             }
169              
170             1;
171              
172             __END__