]> Sergey Matveev's repositories - public-inbox.git/commitdiff
lei: IMAP .onion support via --proxy=s switch
authorEric Wong <e@80x24.org>
Fri, 30 Apr 2021 09:24:36 +0000 (09:24 +0000)
committerEric Wong <e@80x24.org>
Fri, 30 Apr 2021 19:59:46 +0000 (19:59 +0000)
Mail::IMAPClient provides the ability to pass a pre-connected
Socket to it.  We can rely on this functionality to use
IO::Socket::Socks in place whatever socket class
Mail::IMAPClient chooses to use.

The --proxy=s is shared with curl(1), though we only support
socks5h:// at the moment.  Is there any need for SOCKS4 or SOCKS5
without name resolution?  Tor .onions require socks5h:// for
name resolution and to prevent data leakage.

lib/PublicInbox/LEI.pm
lib/PublicInbox/LeiInput.pm
lib/PublicInbox/LeiToMail.pm
lib/PublicInbox/NetReader.pm

index 6a82d497f81a1e62266db5d2d01510bc662dbc35..bb67fc0bed8330ce112f7f1a7b95c2265344eb61 100644 (file)
@@ -188,7 +188,8 @@ our %CMD = ( # sorted in order of importance/use:
        qw(stdin| threads|t from|f=s mid=s oid=s), @c_opt ],
 'tag' => [ 'KEYWORDS...',
        'set/unset keywords and/or labels on message(s)',
-       qw(stdin| in-format|F=s input|i=s@ oid=s@ mid=s@), @c_opt,
+       qw(stdin| in-format|F=s input|i=s@ oid=s@ mid=s@),
+       qw(no-torsocks torsocks=s), PublicInbox::LeiQuery::curl_opt(), @c_opt,
        pass_through('-kw:foo for delete') ],
 'forget' => [ '[--stdin|--oid=OID|--by-mid=MID]',
        "exclude message(s) on stdin from `q' search results",
@@ -211,11 +212,12 @@ our %CMD = ( # sorted in order of importance/use:
 'import' => [ 'LOCATION...|--stdin',
        'one-time import/update from URL or filesystem',
        qw(stdin| offset=i recursive|r exclude=s include|I=s
-       lock=s@ in-format|F=s kw! verbose|v+ incremental! mail-sync!), @c_opt ],
+       lock=s@ in-format|F=s kw! verbose|v+ incremental! mail-sync!),
+       qw(no-torsocks torsocks=s), PublicInbox::LeiQuery::curl_opt(), @c_opt ],
 'convert' => [ 'LOCATION...|--stdin',
        'one-time conversion from URL or filesystem to another format',
-       qw(stdin| in-format|F=s out-format|f=s output|mfolder|o=s
-       lock=s@ kw!), @c_opt ],
+       qw(stdin| in-format|F=s out-format|f=s output|mfolder|o=s lock=s@ kw!),
+       qw(no-torsocks torsocks=s), PublicInbox::LeiQuery::curl_opt(), @c_opt ],
 'p2q' => [ 'FILE|COMMIT_OID|--stdin',
        "use a patch to generate a query for `lei q --stdin'",
        qw(stdin| want|w=s@ uri debug), @c_opt ],
@@ -277,6 +279,8 @@ my %OPTDESC = (
 'path-a|a=s' => 'pre-image pathname associated with OID',
 'path-b|b=s' => 'post-image pathname associated with OID',
 'git-dir=s@' => 'additional git repository to scan',
+'proxy=s' => [ 'PROTO://HOST[:PORT]', # shared with curl(1)
+       "proxy for (e.g. `socks5h://0:9050')" ],
 'torsocks=s' => ['VAL|auto|no|yes',
                'whether or not to wrap git and curl commands with torsocks'],
 'no-torsocks' => 'alias for --torsocks=no',
index 277ad88d99dd06b777a8469e28f42966b25c9c76..86f300c3a572438ac01e475ecaca2ad58c419011 100644 (file)
@@ -294,7 +294,7 @@ $input is `eml', not --in-format=$in_fmt
        }
        if ($net) {
                $net->{-can_die} = 1;
-               if (my $err = $net->errors) {
+               if (my $err = $net->errors($lei)) {
                        return $lei->fail($err);
                }
                $net->{quiet} = $lei->{opt}->{quiet};
index fa3af71038dc1c7a222d68d38db4be7d1d6b9cd6..eda4701c595d28962660d0d39719e68001523886 100644 (file)
@@ -351,14 +351,14 @@ sub new {
                require PublicInbox::MboxReader;
                $self->can("eml2$fmt") or die "bad mbox format: $fmt\n";
                $self->{base_type} = 'mbox';
-       } elsif ($fmt =~ /\Aimaps?\z/) { # TODO .onion support
+       } elsif ($fmt =~ /\Aimaps?\z/) {
                require PublicInbox::NetWriter;
                require PublicInbox::URIimap;
                my $net = PublicInbox::NetWriter->new;
                $net->{quiet} = $lei->{opt}->{quiet};
                my $uri = PublicInbox::URIimap->new($dst)->canonical;
                $net->add_url($uri);
-               my $err = $net->errors;
+               my $err = $net->errors($lei);
                return $lei->fail($err) if $err;
                $uri->mailbox or return $lei->fail("No mailbox: $dst");
                $self->{uri} = $uri;
index b9365c05e37ee9126e39f5e96f3fafe37463477d..ac23e701944a0e683f83c8b9adc736d65e8d8edf 100644 (file)
@@ -7,6 +7,7 @@ use strict;
 use v5.10.1;
 use parent qw(Exporter PublicInbox::IPC);
 use PublicInbox::Eml;
+use PublicInbox::Config;
 our %IMAPflags2kw = map {; "\\\u$_" => $_ } qw(seen answered flagged draft);
 $IMAPflags2kw{'$Forwarded'} = 'forwarded';  # RFC 5550
 
@@ -51,7 +52,16 @@ sub mic_for ($$$$) { # mic = Mail::IMAPClient
                %$common, # may set Starttls, Compress, Debug ....
        };
        require PublicInbox::IMAPClient;
-       my $mic = PublicInbox::IMAPClient->new(%$mic_arg) or
+       my %socks;
+       if ($lei && $lei->{socks5h}) {
+               my %opt = %{$lei->{socks5h}};
+               $opt{ConnectAddr} = delete $mic_arg->{Server};
+               $opt{ConnectPort} = delete $mic_arg->{Port};
+               $socks{Socket} = IO::Socket::Socks->new(%opt) or die
+                       "E: <$url> ".eval('$IO::Socket::Socks::SOCKS_ERROR');
+               $self->{mic_socks5h} = \%opt;
+       }
+       my $mic = PublicInbox::IMAPClient->new(%$mic_arg, %socks) or
                die "E: <$url> new: $@\n";
 
        # default to using STARTTLS if it's available, but allow
@@ -331,7 +341,7 @@ sub add_url {
 }
 
 sub errors {
-       my ($self) = @_;
+       my ($self, $lei) = @_;
        if (my $u = $self->{unsupported_url}) {
                return "Unsupported URL(s): @$u";
        }
@@ -343,6 +353,16 @@ sub errors {
                eval { require Net::NNTP } or
                        die "Net::NNTP is required for NNTP:\n$@\n";
        }
+       if ($lei && (($lei->{opt}->{proxy}//'') =~ m!\Asocks5h://
+                               (?: \[ ([^\]]+) \] | ([^:/]+) )
+                               (?::([0-9]+))?/?(?:,|\z)!ix)) {
+               my ($h, $p) = ($1 // $2, $3 + 0);
+               $h = '127.0.0.1' if $h eq '0';
+               eval { require IO::Socket::Socks } or die <<EOM;
+IO::Socket::Socks missing for socks5h://$h:$p
+EOM
+               $lei->{socks5h} = { ProxyAddr => $h, ProxyPort => $p };
+       }
        undef;
 }
 
@@ -507,7 +527,12 @@ sub mic_get {
                        $mic_arg->{Authcallback} = $self->can($cb_name);
                }
        }
-       my $mic = PublicInbox::IMAPClient->new(%$mic_arg);
+       my %socks;
+       if (my $s5h = $self->{mic_socks5h}) {
+               $socks{Socket} = IO::Socket::Socks->new(%$s5h) or die
+                       "E: <$$uri> ".eval('$IO::Socket::Socks::SOCKS_ERROR');
+       }
+       my $mic = PublicInbox::IMAPClient->new(%$mic_arg, %socks);
        $cached //= {}; # invalid placeholder if no cache enabled
        $mic && $mic->IsConnected ? ($cached->{$sec} = $mic) : undef;
 }