user/dev discussion of public-inbox itself
 help / color / mirror / code / Atom feed
From: Eric Wong <e@80x24.org>
To: meta@public-inbox.org
Subject: [PATCH] lei: handle a single IMAP message in most places
Date: Thu, 27 May 2021 10:49:49 +0000	[thread overview]
Message-ID: <20210527104949.3003-1-e@80x24.org> (raw)

"lei import" can now import a single IMAP message via
<imaps://example.com/MAILBOX/;UID=$UID>

Likewise, "lei inspect" can show the blob information for UID
URLs and "lei lcat" can display the blob without network access
if imported.

"lei lcat" also gets rid of some unused code and supports
"blob:$OIDHEX" syntax as described in the comments (and used by
our "text" output format).
---
 lib/PublicInbox/LeiInspect.pm  | 24 +++++++++++++++++++++--
 lib/PublicInbox/LeiLcat.pm     | 35 +++++++++++++++++++++-------------
 lib/PublicInbox/LeiMailSync.pm | 23 ++++++++++++++++++++++
 lib/PublicInbox/LeiToMail.pm   |  8 +++++++-
 lib/PublicInbox/NetReader.pm   |  9 +++++++--
 t/lei-import-imap.t            | 14 ++++++++++++++
 6 files changed, 95 insertions(+), 18 deletions(-)

diff --git a/lib/PublicInbox/LeiInspect.pm b/lib/PublicInbox/LeiInspect.pm
index 46b9197f..7205979e 100644
--- a/lib/PublicInbox/LeiInspect.pm
+++ b/lib/PublicInbox/LeiInspect.pm
@@ -24,6 +24,19 @@ sub inspect_blob ($$) {
 	$ent;
 }
 
+sub inspect_imap_uid ($$) {
+	my ($lei, $uid_uri) = @_;
+	my $ent = {};
+	my $lse = $lei->{lse} or return $ent;
+	my $lms = $lse->lms or return $ent;
+	my $oidhex = $lms->imap_oid($lei, $uid_uri);
+	if (ref(my $err = $oidhex)) { # art2folder error
+		$lei->qerr(@{$err->{qerr}}) if $err->{qerr};
+	}
+	$ent->{$$uid_uri} = $oidhex;
+	$ent;
+}
+
 sub inspect_sync_folder ($$) {
 	my ($lei, $folder) = @_;
 	my $ent = {};
@@ -49,8 +62,15 @@ sub inspect1 ($$$) {
 	my $ent;
 	if ($item =~ /\Ablob:(.+)/) {
 		$ent = inspect_blob($lei, $1);
-	} elsif ($item =~ m!\Aimaps?://!i ||
-			$item =~ m!\A(?:maildir|mh):!i || -d $item) {
+	} elsif ($item =~ m!\Aimaps?://!i) {
+		require PublicInbox::URIimap;
+		my $uri = PublicInbox::URIimap->new($item);
+		if (defined($uri->uid)) {
+			$ent = inspect_imap_uid($lei, $uri);
+		} else {
+			$ent = inspect_sync_folder($lei, $item);
+		}
+	} elsif ($item =~ m!\A(?:maildir|mh):!i || -d $item) {
 		$ent = inspect_sync_folder($lei, $item);
 	} else { # TODO: more things
 		return $lei->fail("$item not understood");
diff --git a/lib/PublicInbox/LeiLcat.pm b/lib/PublicInbox/LeiLcat.pm
index 87729acf..e5d8e805 100644
--- a/lib/PublicInbox/LeiLcat.pm
+++ b/lib/PublicInbox/LeiLcat.pm
@@ -9,27 +9,33 @@ use strict;
 use v5.10.1;
 use PublicInbox::LeiViewText;
 use URI::Escape qw(uri_unescape);
-use URI;
 use PublicInbox::MID qw($MID_EXTRACT);
 
-sub lcat_redispatch {
-	my ($lei, $out, $op_p) = @_;
-	my $l = bless { %$lei }, ref($lei);
-	delete $l->{sock};
-	$l->{''} = $op_p; # daemon only
-	eval {
-		$l->qerr("# updating $out");
-		up1($l, $out);
-		$l->qerr("# $out done");
-	};
-	$l->err($@) if $@;
+sub lcat_imap_uid_uri ($$) {
+	my ($lei, $uid_uri) = @_;
+	my $lms = $lei->{lse}->lms or return;
+	my $oidhex = $lms->imap_oid($lei, $uid_uri);
+	if (ref(my $err = $oidhex)) { # art2folder error
+		$lei->qerr(@{$err->{qerr}}) if $err->{qerr};
+	}
+	push @{$lei->{lcat_blob}}, $oidhex; # cf. LeiToMail->wq_atexit_child
+	'""'; # blank query
 }
 
 sub extract_1 ($$) {
 	my ($lei, $x) = @_;
-	if ($x =~ m!\b([a-z]+?://\S+)!i) {
+	if ($x =~ m!\b(imaps?://[^>]+)!i) {
+		my $u = $1;
+		require PublicInbox::URIimap;
+		$u = PublicInbox::URIimap->new($u);
+		if (!defined($u->uid)) {
+			$lei->qerr("# no UID= in $u");
+		}
+		lcat_imap_uid_uri($lei, $u);
+	} elsif ($x =~ m!\b([a-z]+?://\S+)!i) {
 		my $u = $1;
 		$u =~ s/[\>\]\)\,\.\;]+\z//;
+		require URI;
 		$u = URI->new($u);
 		my $p = $u->path;
 		my $term;
@@ -57,6 +63,9 @@ sub extract_1 ($$) {
 		$1;
 	} elsif ($x =~ /\bid:(\S+)/) { # notmuch convention
 		"mid:$1";
+	} elsif ($x =~ /\bblob:([0-9a-f]{7,})\b/) {
+		push @{$lei->{lcat_blob}}, $1; # cf. LeiToMail->wq_atexit_child
+		'""'; # blank query
 	} else {
 		undef;
 	}
diff --git a/lib/PublicInbox/LeiMailSync.pm b/lib/PublicInbox/LeiMailSync.pm
index b3e5e035..eb2b0c0f 100644
--- a/lib/PublicInbox/LeiMailSync.pm
+++ b/lib/PublicInbox/LeiMailSync.pm
@@ -356,6 +356,29 @@ sub forget_folder {
 	$dbh->do('DELETE FROM folders WHERE fid = ?', undef, $fid);
 }
 
+sub imap_oid {
+	my ($self, $lei, $uid_uri) = @_;
+	my $mailbox_uri = $uid_uri->clone;
+	$mailbox_uri->uid(undef);
+	warn "$uid_uri  $mailbox_uri";
+	my $folders = [ $$mailbox_uri ];
+	if (my $err = $self->arg2folder($lei, $folders)) {
+		if ($err->{fail}) {
+			$lei->qerr("# no sync information for $mailbox_uri");
+			return;
+		}
+		$lei->qerr(@{$err->{qerr}}) if $err->{qerr};
+	}
+	my $fid = $self->{fmap}->{$folders->[0]} //=
+		_fid_for($self, $folders->[0]) // return;
+	my $sth = $self->{dbh}->prepare_cached(<<EOM, undef, 1);
+SELECT oidbin FROM blob2num WHERE fid = ? AND uid = ?
+EOM
+	$sth->execute($fid, $uid_uri->uid);
+	my ($oidbin) = $sth->fetchrow_array;
+	$oidbin ? unpack('H*', $oidbin) : undef;
+}
+
 sub DESTROY {
 	my ($self) = @_;
 	my $dbh = $self->{dbh} or return;
diff --git a/lib/PublicInbox/LeiToMail.pm b/lib/PublicInbox/LeiToMail.pm
index ad6b9439..b3aec50b 100644
--- a/lib/PublicInbox/LeiToMail.pm
+++ b/lib/PublicInbox/LeiToMail.pm
@@ -702,8 +702,14 @@ sub write_mail { # via ->wq_io_do
 
 sub wq_atexit_child {
 	my ($self) = @_;
-	delete $self->{wcb};
 	my $lei = $self->{lei};
+	if (!$self->{-wq_worker_nr} && $lei->{lcat_blob}) {
+		for my $oid (@{$lei->{lcat_blob}}) {
+			my $smsg = { blob => $oid, pct => 100 };
+			write_mail($self, $smsg);
+		}
+	}
+	delete $self->{wcb};
 	$lei->{ale}->git->async_wait_all;
 	my $nr = delete($lei->{-nr_write}) or return;
 	return if $lei->{early_mua} || !$lei->{-progress} || !$lei->{pkt_op_p};
diff --git a/lib/PublicInbox/NetReader.pm b/lib/PublicInbox/NetReader.pm
index 73b8b1cd..76d2fe62 100644
--- a/lib/PublicInbox/NetReader.pm
+++ b/lib/PublicInbox/NetReader.pm
@@ -469,7 +469,10 @@ E: $orig_uri UIDVALIDITY mismatch (got $r_uidval)
 EOF
 
 	my $uri = $orig_uri->clone;
+	my $single_uid = $uri->uid;
 	my ($itrk, $l_uid, $l_uidval) = itrk_last($self, $uri, $r_uidval, $mic);
+	$itrk = $l_uid = undef if defined($single_uid);
+
 	return <<EOF if $l_uidval != $r_uidval;
 E: $uri UIDVALIDITY mismatch
 E: local=$l_uidval != remote=$r_uidval
@@ -499,7 +502,9 @@ EOF
 		# I wish "UID FETCH $START:*" could work, but:
 		# 1) servers do not need to return results in any order
 		# 2) Mail::IMAPClient doesn't offer a streaming API
-		unless ($uids = $mic->search("UID $l_uid:*")) {
+		if (defined $single_uid) {
+			$uids = [ $single_uid ];
+		} elsif (!($uids = $mic->search("UID $l_uid:*"))) {
 			return if $!{EINTR} && $self->{quit};
 			return "E: $uri UID SEARCH $l_uid:* error: $!";
 		}
@@ -541,7 +546,7 @@ EOF
 		}
 		run_commit_cb($self);
 		$itrk->update_last($r_uidval, $last_uid) if $itrk;
-	} until ($err || $self->{quit});
+	} until ($err || $self->{quit} || defined($single_uid));
 	$err;
 }
 
diff --git a/t/lei-import-imap.t b/t/lei-import-imap.t
index 5283cc23..2f928a2e 100644
--- a/t/lei-import-imap.t
+++ b/t/lei-import-imap.t
@@ -75,5 +75,19 @@ test_lei({ tmpdir => $tmpdir }, sub {
 	lei_ok 'forget-mail-sync', $url;
 	lei_ok 'ls-mail-sync';
 	unlike($lei_out, qr!\Q$host_port\E!, 'sync info gone after forget');
+	my $uid_url = "$url/;UID=".$stats->{'uid.max'};
+	lei_ok 'import', $uid_url;
+	lei_ok 'inspect', $uid_url;
+	$lei_out =~ /([a-f0-9]{40,})/ or
+		xbail 'inspect missed blob with UID URL';
+	my $blob = $1;
+	lei_ok 'lcat', $uid_url;
+	like $lei_out, qr/^Subject: /sm,
+		'lcat shows mail text with UID URL';
+	like $lei_out, qr/\bblob:$blob\b/, 'lcat showed blob';
+	my $orig = $lei_out;
+	lei_ok 'lcat', "blob:$blob";
+	is($lei_out, $orig, 'lcat understands blob:...');
 });
+
 done_testing;

             reply	other threads:[~2021-05-27 10:49 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-27 10:49 Eric Wong [this message]
2021-05-28  0:07 ` [PATCH 0/6] lei: odds and ends and a resend Eric Wong
2021-05-28  0:07   ` [PATCH 1/6] viewdiff: make $UNSAFE a variable Eric Wong
2021-05-28  0:07   ` [PATCH 2/6] viewdiff: escape '{' and '}' for regexp Eric Wong
2021-05-28  0:07   ` [PATCH 3/6] lei_mail_sync: debug code for uncommitted txn Eric Wong
2021-05-28  9:35     ` Eric Wong
2021-05-28  0:07   ` [PATCH 4/6] lei: mark reorder-and-rewrite-local-history as a TODO item Eric Wong
2021-05-28  0:07   ` [PATCH 5/6] lei: handle a single IMAP message in most places Eric Wong
2021-05-28  9:37     ` Eric Wong
2021-05-28  0:07   ` [PATCH 6/6] lei: add TODO item for FUSE mount Eric Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://public-inbox.org/README

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210527104949.3003-1-e@80x24.org \
    --to=e@80x24.org \
    --cc=meta@public-inbox.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/public-inbox.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).