From 4e69a31a6b40b99194559afe111dfa95af9cc0fc Mon Sep 17 00:00:00 2001 From: Jamie Cameron Date: Sun, 24 May 2020 14:26:38 -0700 Subject: [PATCH] Support SSL mode imap --- mailboxes/folders-lib.pl | 105 ++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/mailboxes/folders-lib.pl b/mailboxes/folders-lib.pl index 0c66c1660..44a18ed42 100755 --- a/mailboxes/folders-lib.pl +++ b/mailboxes/folders-lib.pl @@ -2009,31 +2009,46 @@ foreach $f (keys %imap_login_handle) { sub imap_login { local ($folder) = @_; -local $key = join("/", $folder->{'server'}, $folder->{'port'}, - $folder->{'user'}); +local $defport = $folder->{'ssl'} ? 993 : 143; +local $port = $folder->{'port'} || $defport; +local $key = join("/", $folder->{'server'}, $port, $folder->{'user'}); local $h = $imap_login_handle{$key}; local @rv; if (!$h) { # Need to open socket - $h = "IMAP".time().++$imap_login_count; + $h = ($folder->{'ssl'} ? "SSL" : "")."IMAP".time().++$imap_login_count; local $error; - print DEBUG "Connecting to IMAP server $folder->{'server'}:$folder->{'port'}\n"; - &open_socket($folder->{'server'}, $folder->{'port'} || $imap_port, - $h, \$error); + print DEBUG "Connecting to IMAP server $folder->{'server'}:$port\n"; + &open_socket($folder->{'server'}, $port, $h, \$error); print DEBUG "IMAP error=$error\n" if ($error); return (0, $error) if ($error); local $os = select($h); $| = 1; select($os); + if ($folder->{'ssl'}) { + # Switch to SSL mode + eval "use Net::SSLeay"; + $@ && return (0, "Net::SSLeay module is not installed"); + eval "Net::SSLeay::SSLeay_add_ssl_algorithms()"; + eval "Net::SSLeay::load_error_strings()"; + my $ssl_ctx = Net::SSLeay::CTX_new() || + return (0, "Failed to create SSL context"); + my $ssl_con = Net::SSLeay::new($ssl_ctx) || + return (0, "Failed to create SSL connection"); + Net::SSLeay::set_fd($ssl_con, fileno($h)); + Net::SSLeay::connect($ssl_con) || + return (0, "SSL connect() failed"); + $imap_login_ssl{$h} = $ssl_con; + } # Login normally @rv = &imap_command($h); - return (0, $rv[3]) if (!$rv[0]); + return (0, $rv[3] || "No response") if (!$rv[0]); local $user = $folder->{'user'} eq '*' ? $remote_user : $folder->{'user'}; local $pass = $folder->{'pass'}; $pass =~ s/\\/\\\\/g; $pass =~ s/"/\\"/g; @rv = &imap_command($h,"login \"$user\" \"$pass\""); - return (2, $rv[3]) if (!$rv[0]); + return (2, $rv[3] || "No response") if (!$rv[0]); $imap_login_handle{$key} = $h; } @@ -2052,36 +2067,61 @@ return (1, $h, $count, $uidnext); # all of the results joined together, and the stuff after OK/BAD sub imap_command { -local ($h, $c) = @_; +my ($h, $c) = @_; if (!$h) { - local $err = "Invalid IMAP handle"; + my $err = "Invalid IMAP handle"; return (0, [ $err ], $err, $err); } -local @rv; +my $ssl_con = $imap_login_ssl{$h}; +my @rv; # Send the command, and read lines until a non-* one is found -local $id = $$."-".$imap_command_count++; -local ($first, $rest) = split(/\r?\n/, $c, 2); +my $id = $$."-".$imap_command_count++; +my ($first, $rest) = split(/\r?\n/, $c, 2); if ($rest) { # Multi-line - send first line, then wait for continuation, then rest - print $h "$id $first\r\n"; print DEBUG "imap command $id $first\n"; - local $l = <$h>; - print DEBUG "imap line $l"; - if ($l =~ /^\+/) { - print $h $rest."\r\n"; + my $l; + if ($ssl_con) { + Net::SSLeay::write($ssl_con, "$id $first\r\n"); + $l = Net::SSLeay::ssl_read_until($ssl_con); } else { - local $err = "Server did not ask for continuation : $l"; + print $h "$id $first\r\n"; + $l = <$h>; + } + print DEBUG "imap line $l"; + if ($l =~ /^\+/) { + if ($ssl_con) { + Net::SSLeay::write($ssl_con, $rest."\r\n"); + } + else { + print $h $rest."\r\n"; + } + } + else { + my $err = "Server did not ask for continuation : $l"; return (0, [ $err ], $err, $err); } } elsif ($c) { - print $h "$id $c\r\n"; + # Single line command + if ($ssl_con) { + Net::SSLeay::write($ssl_con, "$id $c\r\n"); + } + else { + print $h "$id $c\r\n"; + } print DEBUG "imap command $id $c\n"; } while(1) { - local $l = <$h>; + my $l; + if ($ssl_con) { + $l = Net::SSLeay::ssl_read_until($ssl_con); + } + else { + $l = <$h>; + } print DEBUG "imap line $l"; last if (!$l); if ($l =~ /^(\*|\+)/) { @@ -2091,12 +2131,19 @@ while(1) { last if (!$c); if ($l =~ /\{(\d+)\}\s*$/) { # Start of multi-line text .. read the specified size - local $size = $1; - local $got; - local $err = "Error reading email"; + my $size = $1; + my $got; + my $err = "Error reading email"; while($got < $size) { - local $buf; - local $r = read($h, $buf, $size-$got); + my $buf; + my $r; + if ($ssl_con) { + $buf = Net::SSLeay::read($ssl_con, $size-$got); + $r = length($buf); + } + else { + $r = read($h, $buf, $size-$got); + } return (0, [ $err ], $err, $err) if ($r <= 0); $rv[$#rv] .= $buf; $got += $r; @@ -2111,15 +2158,15 @@ while(1) { else { # Part of last response if (!@rv) { - local $err = "Got unknown line $l"; + my $err = "Got unknown line $l"; return (0, [ $err ], $err, $err); } $rv[$#rv] .= $l; } } -local $j = join("", @rv); +my $j = join("", @rv); print DEBUG "imap response $j\n"; -local $lline = $rv[$#rv]; +my $lline = $rv[$#rv]; if ($lline =~ /^(\S+)\s+OK\s*(.*)/) { # Looks like the command worked return (1, \@rv, $j, $2);