From: Eric Wong <e@80x24.org>
Date: Thu, 16 Sep 2021 00:26:52 +0000 (+0000)
Subject: www: support publicinbox.imapserver
X-Git-Tag: v1.7.0~368
X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=3690f0625fbfb75dcc73986d78abba738e9113a4;p=public-inbox.git

www: support publicinbox.imapserver

This allows PublicInbox::WWW hosts to advertise the existence of
IMAP servers in addition to NNTP servers.
---

diff --git a/Documentation/public-inbox-config.pod b/Documentation/public-inbox-config.pod
index 5b86ef6c..a5bc67fd 100644
--- a/Documentation/public-inbox-config.pod
+++ b/Documentation/public-inbox-config.pod
@@ -62,13 +62,15 @@ Default: none, optional
 
 =item publicinbox.<name>.newsgroup
 
-The NNTP group name for use with L<public-inbox-nntpd(8)>.  This
+The NNTP group name for use with L<public-inbox-nntpd(1)>.  This
 may be any newsgroup name with hierarchies delimited by C<.>.
 For example, the newsgroup for L<mailto:meta@public-inbox.org>
 is: C<inbox.comp.mail.public-inbox.meta>
 
-Omitting this for the given inbox will prevent the group from
-being read by L<public-inbox-nntpd(1)>
+It also configures the folder hierarchy used by L<public-inbox-imapd(1)>.
+
+Omitting this for a given inbox will prevent the inbox from
+being served by L<public-inbox-nntpd(1)> and/or L<public-inbox-imapd(1)>.
 
 Default: none, optional
 
@@ -98,6 +100,12 @@ needs to match.
 
 Default: none
 
+=item publicinbox.<name>.imapmirror
+
+This may be the full IMAP URL of an independently-run IMAP mirror.
+
+Default: none
+
 =item publicinbox.<name>.nntpmirror
 
 This may be the full NNTP URL of an independently-run mirror.
@@ -203,14 +211,18 @@ See L<public-inbox-watch(1)>
 
 See L<public-inbox-watch(1)>
 
-=item publicinbox.nntpserver
+=item publicinbox.imapserver
 
-Set this to point to the hostname of the L<public-inbox-nntpd(1)>
-instance.  This is used to advertise the existence of the NNTP
+Set this to point to the hostname(s) of the L<public-inbox-imapd(1)>
+instance.  This is used to advertise the existence of the IMAP
 endpoint in the L<PublicInbox::WWW> HTML interface.
 
-Multiple values are allowed for instances with multiple hostnames
-or mirrors.
+Default: none
+
+=item publicinbox.nntpserver
+
+Same as C<publicinbox.imapserver>, but for the hostname(s) of the
+L<public-inbox-nntpd(1)> instance.
 
 Default: none
 
diff --git a/lib/PublicInbox/Config.pm b/lib/PublicInbox/Config.pm
index 5e7a9f2b..b3bf5d12 100644
--- a/lib/PublicInbox/Config.pm
+++ b/lib/PublicInbox/Config.pm
@@ -450,8 +450,9 @@ sub _fill_ibx {
 	}
 	# TODO: more arrays, we should support multi-value for
 	# more things to encourage decentralization
-	for my $k (qw(address altid nntpmirror coderepo hide listid url
-			infourl watchheader nntpserver)) {
+	for my $k (qw(address altid nntpmirror imapmirror
+			coderepo hide listid url
+			infourl watchheader nntpserver imapserver)) {
 		my $v = $self->{"$pfx.$k"} // next;
 		$ibx->{$k} = _array($v);
 	}
diff --git a/lib/PublicInbox/Inbox.pm b/lib/PublicInbox/Inbox.pm
index f234b96f..6cd20ec0 100644
--- a/lib/PublicInbox/Inbox.pm
+++ b/lib/PublicInbox/Inbox.pm
@@ -258,50 +258,52 @@ sub base_url {
 	};
 }
 
-sub nntp_url {
-	my ($self, $ctx) = @_;
-	$self->{-nntp_url} ||= do {
-		# no checking for nntp_usable here, we can point entirely
-		# to non-local servers or users run by a different user
-		my $ns = $self->{nntpserver} //
-		       $ctx->{www}->{pi_cfg}->get_all('publicinbox.nntpserver');
-		my $group = $self->{newsgroup};
-		my @urls;
-		if ($ns && $group) {
-			@urls = map {
-				my $u = m!\Anntps?://! ? $_ : "nntp://$_";
-				$u .= '/' if $u !~ m!/\z!;
-				$u.$group;
-			} @$ns;
-		}
-		my $mirrors = $self->{nntpmirror};
-		if ($mirrors) {
-			my @m;
-			foreach (@$mirrors) {
-				my $u = m!\Anntps?://! ? $_ : "nntp://$_";
-				if ($u =~ m!\Anntps?://[^/]+/?\z!) {
-					if ($group) {
-						$u .= '/' if $u !~ m!/\z!;
-						$u .= $group;
-					} else {
-						warn
-"publicinbox.$self->{name}.nntpmirror=$_ missing newsgroup name\n";
-					}
+sub _x_url ($$$) {
+	my ($self, $x, $ctx) = @_; # $x is "nntp" or "imap"
+	# no checking for nntp_usable here, we can point entirely
+	# to non-local servers or users run by a different user
+	my $ns = $self->{"${x}server"} //
+	       $ctx->{www}->{pi_cfg}->get_all("publicinbox.${x}server");
+	my $group = $self->{newsgroup};
+	my @urls;
+	if ($ns && $group) {
+		@urls = map {
+			my $u = m!\A${x}s?://! ? $_ : "$x://$_";
+			$u .= '/' if $u !~ m!/\z!;
+			$u.$group;
+		} @$ns;
+	}
+	if (my $mirrors = $self->{"${x}mirror"}) {
+		my @m;
+		for (@$mirrors) {
+			my $u = m!\A${x}s?://! ? $_ : "$x://$_";
+			if ($u =~ m!\A${x}s?://[^/]+/?\z!) {
+				if ($group) {
+					$u .= '/' if $u !~ m!/\z!;
+					$u .= $group;
+				} else { # n.b. IMAP uses "newsgroup"
+					warn <<EOM;
+publicinbox.$self->{name}.${x}mirror=$_ missing newsgroup name
+EOM
 				}
-				# else: allow full URLs like:
-				# nntp://news.example.com/alt.example
-				push @m, $u;
 			}
-
-			# List::Util::uniq requires Perl 5.26+, maybe we
-			# can use it by 2030 or so
-			my %seen;
-			@urls = grep { !$seen{$_}++ } (@urls, @m);
+			# else: allow full URLs like:
+			# nntp://news.example.com/alt.example
+			push @m, $u;
 		}
-		\@urls;
-	};
+
+		# List::Util::uniq requires Perl 5.26+, maybe we
+		# can use it by 2030 or so
+		my %seen;
+		@urls = grep { !$seen{$_}++ } (@urls, @m);
+	}
+	\@urls;
 }
 
+# my ($self, $ctx) = @_;
+sub nntp_url { $_[0]->{-nntp_url} //= _x_url($_[0], 'nntp', $_[1]) }
+sub imap_url { $_[0]->{-imap_url} //= _x_url($_[0], 'imap', $_[1]) }
+
 sub nntp_usable {
 	my ($self) = @_;
 	my $ret = mm($self) && over($self);
diff --git a/lib/PublicInbox/WwwText.pm b/lib/PublicInbox/WwwText.pm
index 177d55e4..bb9a0a0f 100644
--- a/lib/PublicInbox/WwwText.pm
+++ b/lib/PublicInbox/WwwText.pm
@@ -211,6 +211,7 @@ EOF
 		$$txt .= "\t$k = $v\n";
 	}
 	$$txt .= "\tnntpmirror = $_\n" for (@{$ibx->nntp_url($ctx)});
+	$$txt .= "\timapmirror = $_\n" for (@{$ibx->imap_url($ctx)});
 	_coderepo_config($ctx, $txt);
 	1;
 }
@@ -342,6 +343,17 @@ EOM
 
 Example config snippet for mirrors: $cfg_link
 EOF
+	if ($ibx->can('imap_url')) {
+		my $imap = $ibx->imap_url($ctx);
+		if (@$imap) {
+			$$txt .= "\n";
+			$$txt .= 'IMAP subfolder(s) available under:';
+			$$txt .= "\n\t" . join("\n\t", @$imap) . "\n";
+			$$txt .= <<EOM
+	# each subfolder (starting with `0') holds 50K messages at most
+EOM
+		}
+	}
 	if ($ibx->can('nntp_url')) {
 		my $nntp = $ibx->nntp_url($ctx);
 		if (scalar @$nntp) {