]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/NetReader.pm
net_reader: Net::NNTP --proxy=socks5h:// support
[public-inbox.git] / lib / PublicInbox / NetReader.pm
index 3fc37b10f8b22efa52ca6ad223b3aa65a009f76a..b2c4fee25756d75d2f4c0148e38af986ac678fc5 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
 
@@ -27,7 +28,7 @@ sub uri_section ($) {
 sub auth_anon_cb { '' }; # for Mail::IMAPClient::Authcallback
 
 # mic_for may prompt the user and store auth info, prepares mic_get
-sub mic_for { # mic = Mail::IMAPClient
+sub mic_for ($$$$) { # mic = Mail::IMAPClient
        my ($self, $url, $mic_args, $lei) = @_;
        require PublicInbox::URIimap;
        my $uri = PublicInbox::URIimap->new($url);
@@ -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
@@ -106,7 +116,13 @@ sub try_starttls ($) {
 
 sub nn_new ($$$) {
        my ($nn_arg, $nntp_opt, $uri) = @_;
-       my $nn = Net::NNTP->new(%$nn_arg) or die "E: <$uri> new: $!\n";
+       my $nn;
+       if (defined $nn_arg->{ProxyAddr}) {
+               eval { $nn = PublicInbox::NetNNTPSocks->new_socks(%$nn_arg) };
+               die "E: <$uri> $@\n" if $@;
+       } else {
+               $nn = Net::NNTP->new(%$nn_arg) or die "E: <$uri> new: $!\n";
+       }
 
        # default to using STARTTLS if it's available, but allow
        # it to be disabled for localhost/VPN users
@@ -133,7 +149,7 @@ E: <$uri> STARTTLS requested and failed
        $nn;
 }
 
-sub nn_for ($$$;$) { # nn = Net::NNTP
+sub nn_for ($$$$) { # nn = Net::NNTP
        my ($self, $uri, $nn_args, $lei) = @_;
        my $sec = uri_section($uri);
        my $nntp_opt = $self->{nntp_opt}->{$sec} //= {};
@@ -160,6 +176,10 @@ sub nn_for ($$$;$) { # nn = Net::NNTP
                SSL => $uri->secure, # snews == nntps
                %$common, # may Debug ....
        };
+       if ($lei && $lei->{socks5h}) {
+               require PublicInbox::NetNNTPSocks;
+               %$nn_arg = (%$nn_arg, %{$lei->{socks5h}});
+       }
        my $nn = nn_new($nn_arg, $nntp_opt, $uri);
        if ($cred) {
                $cred->fill($lei); # may prompt user here
@@ -331,7 +351,7 @@ sub add_url {
 }
 
 sub errors {
-       my ($self) = @_;
+       my ($self, $lei) = @_;
        if (my $u = $self->{unsupported_url}) {
                return "Unsupported URL(s): @$u";
        }
@@ -343,6 +363,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 +537,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;
 }