-# avoid exposing deprecated "snews" to users.
-my %SCHEME_MAP = ('snews' => 'nntps');
-
-sub uri_scheme ($) {
- my ($uri) = @_;
- my $scheme = $uri->scheme;
- $SCHEME_MAP{$scheme} // $scheme;
-}
-
-# returns the git config section name, e.g [imap "imaps://user@example.com"]
-# without the mailbox, so we can share connections between different inboxes
-sub uri_section ($) {
- my ($uri) = @_;
- uri_scheme($uri) . '://' . $uri->authority;
-}
-
-sub cfg_intvl ($$$) {
- my ($cfg, $key, $url) = @_;
- my $v = $cfg->urlmatch($key, $url) // return;
- $v =~ /\A[0-9]+(?:\.[0-9]+)?\z/s and return $v + 0;
- if (ref($v) eq 'ARRAY') {
- $v = join(', ', @$v);
- warn "W: $key has multiple values: $v\nW: $key ignored\n";
- } else {
- warn "W: $key=$v is not a numeric value in seconds\n";
- }
-}
-
-sub cfg_bool ($$$) {
- my ($cfg, $key, $url) = @_;
- my $orig = $cfg->urlmatch($key, $url) // return;
- my $bool = $cfg->git_bool($orig);
- warn "W: $key=$orig for $url is not boolean\n" unless defined($bool);
- $bool;
-}
-
-# flesh out common IMAP-specific data structures
-sub imap_common_init ($) {
- my ($self) = @_;
- my $cfg = $self->{pi_cfg};
- my $mic_args = {}; # scheme://authority => Mail:IMAPClient arg
- for my $url (sort keys %{$self->{imap}}) {
- my $uri = PublicInbox::URIimap->new($url);
- my $sec = uri_section($uri);
- for my $k (qw(Starttls Debug Compress)) {
- my $bool = cfg_bool($cfg, "imap.$k", $url) // next;
- $mic_args->{$sec}->{$k} = $bool;
- }
- my $to = cfg_intvl($cfg, 'imap.timeout', $url);
- $mic_args->{$sec}->{Timeout} = $to if $to;
- for my $k (qw(pollInterval idleInterval)) {
- $to = cfg_intvl($cfg, "imap.$k", $url) // next;
- $self->{imap_opt}->{$sec}->{$k} = $to;
- }
- my $k = 'imap.fetchBatchSize';
- my $bs = $cfg->urlmatch($k, $url) // next;
- if ($bs =~ /\A([0-9]+)\z/) {
- $self->{imap_opt}->{$sec}->{batch_size} = $bs;
- } else {
- warn "$k=$bs is not an integer\n";
- }
- }
- $mic_args;
-}
-
-sub auth_anon_cb { '' }; # for Mail::IMAPClient::Authcallback
-
-sub mic_for ($$$) { # mic = Mail::IMAPClient
- my ($self, $url, $mic_args) = @_;
- my $uri = PublicInbox::URIimap->new($url);
- require PublicInbox::GitCredential;
- my $cred = bless {
- url => $url,
- protocol => $uri->scheme,
- host => $uri->host,
- username => $uri->user,
- password => $uri->password,
- }, 'PublicInbox::GitCredential';
- my $common = $mic_args->{uri_section($uri)} // {};
- # IMAPClient and Net::Netrc both mishandles `0', so we pass `127.0.0.1'
- my $host = $cred->{host};
- $host = '127.0.0.1' if $host eq '0';
- my $mic_arg = {
- Port => $uri->port,
- Server => $host,
- Ssl => $uri->scheme eq 'imaps',
- Keepalive => 1, # SO_KEEPALIVE
- %$common, # may set Starttls, Compress, Debug ....
- };
- my $mic = PublicInbox::IMAPClient->new(%$mic_arg) or
- die "E: <$url> new: $@\n";
-
- # default to using STARTTLS if it's available, but allow
- # it to be disabled since I usually connect to localhost
- if (!$mic_arg->{Ssl} && !defined($mic_arg->{Starttls}) &&
- $mic->has_capability('STARTTLS') &&
- $mic->can('starttls')) {
- $mic->starttls or die "E: <$url> STARTTLS: $@\n";
- }
-
- # do we even need credentials?
- if (!defined($cred->{username}) &&
- $mic->has_capability('AUTH=ANONYMOUS')) {
- $cred = undef;
- }
- if ($cred) {
- $cred->check_netrc unless defined $cred->{password};
- $cred->fill; # may prompt user here
- $mic->User($mic_arg->{User} = $cred->{username});
- $mic->Password($mic_arg->{Password} = $cred->{password});
- } else { # AUTH=ANONYMOUS
- $mic->Authmechanism($mic_arg->{Authmechanism} = 'ANONYMOUS');
- $mic->Authcallback($mic_arg->{Authcallback} = \&auth_anon_cb);
- }
- if ($mic->login && $mic->IsAuthenticated) {
- # success! keep IMAPClient->new arg in case we get disconnected
- $self->{mic_arg}->{uri_section($uri)} = $mic_arg;
- } else {
- warn "E: <$url> LOGIN: $@\n";
- $mic = undef;
- }
- $cred->run($mic ? 'approve' : 'reject') if $cred;
- $mic;
-}
-