+ $ctx->{www}->{pi_cfg}->each_inbox(\&ext_msg_i, $ctx);
+ finalize_exact($ctx);
+ }
+ };
+}
+
+# called via PublicInbox::DS->EventLoop
+sub event_step {
+ my ($ctx, $sync) = @_;
+ # can't find a partial match in current inbox, try the others:
+ my $ibx = shift @{$ctx->{again}} or return finalize_partial($ctx);
+ my $mids = search_partial($ibx, $ctx->{mid}) or
+ return ($sync ? undef : PublicInbox::DS::requeue($ctx));
+ $ctx->{n_partial} += scalar(@$mids);
+ push @{$ctx->{partial}}, [ $ibx, $mids ];
+ $ctx->{n_partial} >= PARTIAL_MAX ? finalize_partial($ctx)
+ : ($sync ? undef : PublicInbox::DS::requeue($ctx));
+}
+
+sub finalize_exact {
+ my ($ctx) = @_;
+
+ return $ctx->{-wcb}->(exact($ctx)) if $ctx->{found};
+
+ # fall back to partial MID matching
+ my $mid = $ctx->{mid};
+ my $cur = $ctx->{ibx};
+ my $mids = search_partial($cur, $mid);
+ if ($mids) {
+ $ctx->{n_partial} = scalar(@$mids);
+ push @{$ctx->{partial}}, [ $cur, $mids ];
+ } elsif ($ctx->{again} && length($mid) >= $MIN_PARTIAL_LEN) {
+ bless $ctx, __PACKAGE__;
+ if ($ctx->{env}->{'pi-httpd.async'}) {
+ $ctx->event_step;
+ return;
+ }
+
+ # synchronous fall-through
+ $ctx->event_step while @{$ctx->{again}};
+ }
+ finalize_partial($ctx);
+}
+
+sub partial_response ($) {
+ my ($ctx) = @_;
+ my $mid = $ctx->{mid};
+ my $code = 404;
+ my $href = mid_href($mid);
+ my $html = ascii_html($mid);
+ my $title = "<$html> not found";
+ my $s = "<pre>Message-ID <$html>\nnot found\n";
+ if (my $n_partial = $ctx->{n_partial}) {
+ $code = 300;
+ my $es = $n_partial == 1 ? '' : 'es';
+ $n_partial .= '+' if ($n_partial == PARTIAL_MAX);
+ $s .= "\n$n_partial partial match$es found:\n\n";
+ my $cur_name = $ctx->{ibx}->{name};
+ foreach my $pair (@{$ctx->{partial}}) {
+ my ($ibx, $res) = @$pair;
+ my $env = $ctx->{env} if $ibx->{name} eq $cur_name;
+ my $u = $ibx->base_url($env) or next;
+ foreach my $m (@$res) {
+ my $href = mid_href($m);
+ my $html = ascii_html($m);
+ $s .= qq{<a\nhref="$u$href/">$u$html/</a>\n};