]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/ExtMsg.pm
extmsg: fixup comparison for unknown message types
[public-inbox.git] / lib / PublicInbox / ExtMsg.pm
1 # Copyright (C) 2015 all contributors <meta@public-inbox.org>
2 # License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
3 #
4 # Used by the web interface to link to messages outside of the our
5 # public-inboxes.  Mail threads may cross projects/threads; so
6 # we should ensure users can find more easily find them on other
7 # sites.
8 package PublicInbox::ExtMsg;
9 use strict;
10 use warnings;
11 use URI::Escape qw(uri_escape_utf8);
12 use PublicInbox::Hval;
13 use PublicInbox::MID qw/mid2path/;
14
15 # TODO: user-configurable
16 our @EXT_URL = (
17         'http://mid.gmane.org/%s',
18         'https://lists.debian.org/msgid-search/%s',
19         'http://mid.mail-archive.com/%s',
20         'http://marc.info/?i=%s',
21 );
22
23 sub ext_msg {
24         my ($ctx) = @_;
25         my $pi_config = $ctx->{pi_config};
26         my $listname = $ctx->{listname};
27         my $mid = $ctx->{mid};
28
29         eval { require PublicInbox::Search };
30         my $have_xap = $@ ? 0 : 1;
31         my (@nox, @pfx);
32
33         foreach my $k (keys %$pi_config) {
34                 $k =~ /\Apublicinbox\.([A-Z0-9a-z-]+)\.url\z/ or next;
35                 my $list = $1;
36                 next if $list eq $listname;
37
38                 my $git_dir = $pi_config->{"publicinbox.$list.mainrepo"};
39                 defined $git_dir or next;
40
41                 my $url = $pi_config->{"publicinbox.$list.url"};
42                 defined $url or next;
43
44                 $url =~ s!/+\z!!;
45
46                 # try to find the URL with Xapian to avoid forking
47                 if ($have_xap) {
48                         my $s;
49                         my $doc_id = eval {
50                                 $s = PublicInbox::Search->new($git_dir);
51                                 $s->find_unique_doc_id('mid', $mid);
52                         };
53                         if ($@) {
54                                 # xapian not configured for this repo
55                         } else {
56                                 # maybe we found it!
57                                 return r302($url, $mid) if (defined $doc_id);
58
59                                 # no point in trying the fork fallback if we
60                                 # know Xapian is up-to-date but missing the
61                                 # message in the current repo
62                                 push @pfx, { git_dir => $git_dir, url => $url };
63                                 next;
64                         }
65                 }
66
67                 # queue up for forking after we've tried Xapian on all of them
68                 push @nox, { git_dir => $git_dir, url => $url };
69         }
70
71         # Xapian not installed or configured for some repos
72         my $path = "HEAD:" . mid2path($mid);
73
74         foreach my $n (@nox) {
75                 # TODO: reuse existing PublicInbox::Git objects to save forks
76                 my $git = PublicInbox::Git->new($n->{git_dir});
77                 my (undef, $type, undef) = $git->check($path);
78                 return r302($n->{url}, $mid) if ($type && $type eq 'blob');
79         }
80
81         # fall back to partial MID matching
82         my $n_partial = 0;
83         my @partial;
84
85         eval { require PublicInbox::Msgmap };
86         my $have_mm = $@ ? 0 : 1;
87         if ($have_mm) {
88                 my $tmp_mid = $mid;
89 again:
90                 my $cgi = $ctx->{cgi};
91                 my $url = ref($cgi) eq 'CGI' ? $cgi->url(-base) . '/'
92                                         : $cgi->base->as_string;
93                 $url .= $listname;
94                 unshift @pfx, { git_dir => $ctx->{git_dir}, url => $url };
95                 foreach my $pfx (@pfx) {
96                         my $git_dir = delete $pfx->{git_dir} or next;
97                         my $mm = eval { PublicInbox::Msgmap->new($git_dir) };
98
99                         $mm or next;
100                         if (my $res = $mm->mid_prefixes($tmp_mid)) {
101                                 $n_partial += scalar(@$res);
102                                 $pfx->{res} = $res;
103                                 push @partial, $pfx;
104                         }
105                 }
106                 # fixup common errors:
107                 if (!$n_partial && $tmp_mid =~ s,/[tTf],,) {
108                         goto again;
109                 }
110         }
111
112         my $code = 404;
113         my $h = PublicInbox::Hval->new_msgid($mid, 1);
114         my $href = $h->as_href;
115         my $html = $h->as_html;
116         my $title = "Message-ID &lt;$html&gt; not found";
117         my $s = "<html><head><title>$title</title>" .
118                 "</head><body><pre><b>$title</b>\n";
119
120         if ($n_partial) {
121                 $code = 300;
122                 my $es = $n_partial == 1 ? '' : 'es';
123                 $s.= "\n$n_partial partial match$es found:\n\n";
124                 foreach my $pfx (@partial) {
125                         my $u = $pfx->{url};
126                         foreach my $m (@{$pfx->{res}}) {
127                                 my $p = PublicInbox::Hval->new($m);
128                                 my $r = $p->as_href;
129                                 my $t = $p->as_html;
130                                 $s .= qq{<a\nhref="$u/$r/">$u/$t/</a>\n};
131                         }
132                 }
133         }
134
135         # Fall back to external repos if configured
136         if (@EXT_URL && index($mid, '@') >= 0) {
137                 $code = 300;
138                 $s .= "\nPerhaps try an external site:\n\n";
139                 foreach my $u (@EXT_URL) {
140                         my $r = sprintf($u, $href);
141                         my $t = sprintf($u, $html);
142                         $s .= qq{<a\nhref="$r">$t</a>\n};
143                 }
144         }
145         $s .= '</pre></body></html>';
146
147         [300, ['Content-Type'=>'text/html; charset=UTF-8'], [$s]];
148 }
149
150 # Redirect to another public-inbox which is mapped by $pi_config
151 sub r302 {
152         my ($url, $mid) = @_;
153         $url .= '/' . uri_escape_utf8($mid) . '/';
154         [ 302,
155           [ 'Location' => $url, 'Content-Type' => 'text/plain' ],
156           [ "Redirecting to\n$url\n" ] ]
157 }
158
159 1;