| 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 |  | 91556 | use vars qw ( $VERSION @ISA ); | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 156 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | $VERSION = '0.10'; | 
| 12 |  |  |  |  |  |  |  | 
| 13 | 1 |  |  | 1 |  | 11 | use strict; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 43 |  | 
| 14 | 1 |  |  | 1 |  | 10 | use base qw ( Net::POP3 ); | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 586 |  | 
| 15 | 1 |  |  | 1 |  | 103810 | use Net::Cmd;  # import CMD_OK, CMD_MORE, ... | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 59 |  | 
| 16 | 1 |  |  | 1 |  | 5 | use Net::Config; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 1026 |  | 
| 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 |  |  |  |  |  |  | # Fix #121006 no timeout issue. | 
| 187 |  |  |  |  |  |  | sub getline { | 
| 188 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 189 | 0 |  |  |  |  |  | $self->Net::Cmd::getline(@_); | 
| 190 |  |  |  |  |  |  | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | 1; | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | __END__ |