File Coverage

blib/lib/Net/POP3S.pm
Criterion Covered Total %
statement 15 107 14.0
branch 0 48 0.0
condition 0 35 0.0
subroutine 5 11 45.4
pod 4 5 80.0
total 24 206 11.6


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