From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-3.0 required=3.0 tests=ALL_TRUSTED,AWL,BAYES_00, URIBL_BLOCKED shortcircuit=no autolearn=unavailable version=3.3.2 X-Original-To: meta@public-inbox.org Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 3EA4D1FD2E for ; Tue, 1 Sep 2015 08:55:30 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 09/11] completely revamp URL structure to shorten permalinks Date: Tue, 1 Sep 2015 08:55:26 +0000 Message-Id: <1441097728-31950-9-git-send-email-e@80x24.org> In-Reply-To: <1441097728-31950-1-git-send-email-e@80x24.org> References: <1441097728-31950-1-git-send-email-e@80x24.org> List-Id: This allows common /m/ links to be used without a prefix, saving 2 precious bytes for permalinks and raw messages. Old URLs continue to redirect. --- Documentation/design_www.txt | 37 +++++---- lib/PublicInbox/Feed.pm | 19 +++-- lib/PublicInbox/View.pm | 48 ++++++------ lib/PublicInbox/WWW.pm | 177 +++++++++++++++++++++++-------------------- t/cgi.t | 20 ++--- t/feed.t | 2 +- t/plack.t | 32 +++++--- t/view.t | 6 +- 8 files changed, 179 insertions(+), 162 deletions(-) diff --git a/Documentation/design_www.txt b/Documentation/design_www.txt index a11c389..b73a798 100644 --- a/Documentation/design_www.txt +++ b/Documentation/design_www.txt @@ -2,29 +2,28 @@ URL naming ---------- ### Unstable endpoints -/$LISTNAME/?r=$GIT_COMMIT -> HTML only -/$LISTNAME/new.atom -> Atom feed +/$LISTNAME/?r=$GIT_COMMIT -> HTML only +/$LISTNAME/new.atom -> Atom feed #### Optional, relies on Search::Xapian -/$LISTNAME/t/$MESSAGE_ID/ -> HTML content of thread -/$LISTNAME/t/$MESSAGE_ID/atom -> Atom feed for thread -/$LISTNAME/t/$MESSAGE_ID/mbox.gz -> gzipped mbox of thread +/$LISTNAME/$MESSAGE_ID/t/ -> HTML content of thread +/$LISTNAME/$MESSAGE_ID/t.atom -> Atom feed for thread +/$LISTNAME/$MESSAGE_ID/t.mbox.gz -> gzipped mbox of thread ### Stable endpoints -/$LISTNAME/m/$MESSAGE_ID/ -> HTML content (short quotes) -/$LISTNAME/m/$MESSAGE_ID -> 301 to above -/$LISTNAME/m/$MESSAGE_ID/raw -> raw mbox -/$LISTNAME/f/$MESSAGE_ID/ -> HTML content (full quotes) -/$LISTNAME/f/$MESSAGE_ID -> 301 to above -/$LISTNAME/f/$MESSAGE_ID/raw [1] -> 301 to ../m/$MESSAGE_ID/raw - -### Legacy endpoints (may be ambiguous given Message-IDs with similar suffixes) -/$LISTNAME/m/$MESSAGE_ID.html -> 301 to $MESSAGE_ID/ -/$LISTNAME/m/$MESSAGE_ID.txt -> 301 to $MESSAGE_ID/raw -/$LISTNAME/f/$MESSAGE_ID.html -> 301 to $MESSAGE_ID/ -/$LISTNAME/f/$MESSAGE_ID.txt [1] -> 301 to ../m/$MESSAGE_ID/raw - -/$LISTNAME/atom.xml [2] -> identical to /$LISTNAME/new.atom +/$LISTNAME/$MESSAGE_ID/ -> HTML content (short quotes) +/$LISTNAME/$MESSAGE_ID -> 301 to /$LISTNAME/$MESSAGE_ID +/$LISTNAME/$MESSAGE_ID/raw -> raw mbox +/$LISTNAME/$MESSAGE_ID/f/ -> HTML content (full quotes) + +### Legacy endpoints (may be ambiguous given Message-IDs with similar suffies) +/$LISTNAME/m/$MESSAGE_ID/ -> 301 to /$LISTNAME/$MESSAGE_ID/ +/$LISTNAME/m/$MESSAGE_ID.html -> 301 to /$LISTNAME/$MESSAGE_ID/ +/$LISTNAME/m/$MESSAGE_ID.txt -> 301 to /$LISTNAME/$MESSAGE_ID/raw +/$LISTNAME/f/$MESSAGE_ID.html -> 301 to /$LISTNAME/$MESSAGE_ID/f/ +/$LISTNAME/f/$MESSAGE_ID.txt [1] -> 301 to /$LISTNAME/$MESSAGE_ID/raw + +/$LISTNAME/atom.xml [2] -> identical to /$LISTNAME/new.atom FIXME: we must refactor/cleanup/add tests for most of our CGI before adding more endpoints and features. diff --git a/lib/PublicInbox/Feed.pm b/lib/PublicInbox/Feed.pm index 9d58193..4420fde 100644 --- a/lib/PublicInbox/Feed.pm +++ b/lib/PublicInbox/Feed.pm @@ -101,7 +101,7 @@ sub emit_atom_thread { my $feed_opts = get_feedopts($ctx); my $html_url = $feed_opts->{atomurl} = $ctx->{self_url}; - $html_url =~ s!/atom\z!/!; + $html_url =~ s!/t\.atom\z!/!; $feed_opts->{url} = $html_url; $feed_opts->{emit_header} = 1; @@ -285,7 +285,7 @@ sub get_feedopts { } $url_base = "$base/$listname"; if (my $mid = $ctx->{mid}) { # per-thread feed: - $rv{atomurl} = "$url_base/t/$mid/atom"; + $rv{atomurl} = "$url_base/$mid/t.atom"; } else { $rv{atomurl} = "$url_base/new.atom"; } @@ -294,8 +294,7 @@ sub get_feedopts { $rv{atomurl} = "$url_base/new.atom"; } $rv{url} ||= "$url_base/"; - $rv{midurl} = "$url_base/m/"; - $rv{fullurl} = "$url_base/f/"; + $rv{midurl} = "$url_base/"; \%rv; } @@ -317,14 +316,15 @@ sub add_to_feed { my ($feed_opts, $fh, $add, $git) = @_; my $mime = do_cat_mail($git, $add) or return 0; - my $fullurl = $feed_opts->{fullurl} || 'http://example.com/f/'; + my $url = $feed_opts->{url}; + my $midurl = $feed_opts->{midurl}; my $header_obj = $mime->header_obj; my $mid = $header_obj->header('Message-ID'); defined $mid or return 0; $mid = PublicInbox::Hval->new_msgid($mid); - my $href = $mid->as_href . '/'; - my $content = PublicInbox::View->feed_entry($mime, $fullurl . $href); + my $href = $mid->as_href; + my $content = PublicInbox::View->feed_entry($mime, "$midurl$href/f/"); defined($content) or return 0; $mime = undef; @@ -355,8 +355,7 @@ sub add_to_feed { my $h = '[a-f0-9]'; my (@uuid5) = ($add =~ m!\A($h{8})($h{4})($h{4})($h{4})($h{12})!o); my $id = 'urn:uuid:' . join('-', @uuid5); - my $midurl = $feed_opts->{midurl}; - $fh->write(qq{}. + $fh->write(qq!!. "$id"); 1; } @@ -414,7 +413,7 @@ sub dump_topics { $mid = PublicInbox::Hval->new($mid)->as_href; $subj = PublicInbox::Hval->new($subj)->as_html; $u = PublicInbox::Hval->new($u)->as_html; - $dst .= "\n$subj\n- "; + $dst .= "\n$subj\n- "; $ts = strftime('%Y-%m-%d %H:%M', gmtime($ts)); if ($n == 1) { $dst .= "created by $u @ $ts UTC\n" diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm index a30bf70..2be16b4 100644 --- a/lib/PublicInbox/View.pm +++ b/lib/PublicInbox/View.pm @@ -80,7 +80,7 @@ sub index_entry { $anchor = $seen->{$anchor_idx}; } if ($srch) { - $subj = "$subj"; + $subj = "$subj"; } if ($root_anchor && $root_anchor eq $id) { $subj = "$subj"; @@ -101,9 +101,9 @@ sub index_entry { $fh->write($rv .= "\n\n"); my ($fhref, $more_ref); - my $mhref = "${path}m/$href/"; + my $mhref = "${path}$href/"; if ($level > 0) { - $fhref = "${path}f/$href/"; + $fhref = "${path}$href/f/"; $more_ref = \$more; } # scan through all parts, looking for displayable text @@ -112,7 +112,7 @@ sub index_entry { }); $mime->body_set(''); - my $txt = "${path}m/$href/raw"; + my $txt = "${path}$href/raw"; $rv = "\n$more raw "; $rv .= html_footer($mime, 0, undef, $ctx); @@ -120,7 +120,7 @@ sub index_entry { unless (defined $anchor) { my $v = PublicInbox::Hval->new_msgid($irt); $v = $v->as_href; - $anchor = "${path}m/$v/"; + $anchor = "${path}$v/"; $seen->{$anchor_idx} = $anchor; } $rv .= " parent"; @@ -160,8 +160,8 @@ sub emit_thread_html { my $next = ""; $next .= $final_anchor == 1 ? 'only message in' : 'end of'; $next .= " thread, back to index\n"; - $next .= "download: mbox.gz"; - $next .= " / Atom feed\n\n"; + $next .= "download: mbox.gz"; + $next .= " / Atom feed\n\n"; $fh->write("
" . PRE_WRAP . $next . $foot . ""); $fh->close; @@ -349,8 +349,8 @@ sub headers_to_html_header { } elsif ($h eq 'Subject') { $title[0] = $v->as_html; if ($srch) { - $rv .= "$h: "; + my $p = $full_pfx ? '' : '../'; + $rv .= "$h: "; $rv .= $v->as_html . "\n"; next; } @@ -359,7 +359,7 @@ sub headers_to_html_header { } $rv .= 'Message-ID: <' . $mid->as_html . '> '; - my $raw_ref = $full_pfx ? 'raw' : "../../m/$mid_href/raw"; + my $raw_ref = $full_pfx ? 'raw' : '../raw'; $rv .= "(raw)\n"; if ($srch) { $rv .= "References: [see below]\n"; @@ -373,7 +373,7 @@ sub headers_to_html_header { } sub thread_inline { - my ($dst, $ctx, $cur) = @_; + my ($dst, $ctx, $cur, $full_pfx) = @_; my $srch = $ctx->{srch}; my $mid = mid_compress(mid_clean($cur->header('Message-ID'))); my $res = $srch->get_thread($mid); @@ -383,9 +383,10 @@ sub thread_inline { $$dst .= "\n[no followups, yet]\n"; return; } + my $upfx = $full_pfx ? '' : '../'; $$dst .= "\n\n~$nr messages in thread: ". - "(expand)\n"; + "(expand)\n"; my $subj = $srch->subject_path($cur->header('Subject')); my $state = { seen => { $subj => 1 }, @@ -393,7 +394,7 @@ sub thread_inline { cur => $mid, }; for (thread_results(load_results($res))->rootset) { - inline_dump($dst, $state, $_, 0); + inline_dump($dst, $state, $upfx, $_, 0); } $state->{next_msg}; } @@ -461,19 +462,20 @@ sub html_footer { my $href = "mailto:$to?In-Reply-To=$irt&Cc=${cc}&Subject=$subj"; my $srch = $ctx->{srch} if $ctx; - my $idx = $standalone ? " index" : ''; + my $upfx = $full_pfx ? '../' : '../../'; + my $idx = $standalone ? "index" : ''; if ($idx && $srch) { - my $next = thread_inline(\$idx, $ctx, $mime); + my $next = thread_inline(\$idx, $ctx, $mime, $full_pfx); $irt = $mime->header('In-Reply-To'); if (defined $irt) { $irt = PublicInbox::Hval->new_msgid($irt); $irt = $irt->as_href; - $irt = "parent "; + $irt = "parent "; } else { $irt = ' ' x length('parent '); } if ($next) { - $irt .= "next "; + $irt .= "next "; } else { $irt .= ' '; } @@ -564,7 +566,7 @@ sub _msg_date { } sub _inline_header { - my ($dst, $state, $mime, $level) = @_; + my ($dst, $state, $upfx, $mime, $level) = @_; my $pfx = ' ' x $level; my $cur = $state->{cur}; @@ -601,7 +603,7 @@ sub _inline_header { $s = $s->as_html; } my $m = PublicInbox::Hval->new_msgid($mid); - $m = '../' . $m->as_href . '/'; + $m = $upfx . '../' . $m->as_href . '/'; if (defined $s) { $$dst .= "$pfx` $s\n" . "$pfx $f @ $d\n"; @@ -611,14 +613,14 @@ sub _inline_header { } sub inline_dump { - my ($dst, $state, $node, $level) = @_; + my ($dst, $state, $upfx, $node, $level) = @_; return unless $node; return if $state->{stopped}; if (my $mime = $node->message) { - _inline_header($dst, $state, $mime, $level); + _inline_header($dst, $state, $upfx, $mime, $level); } - inline_dump($dst, $state, $node->child, $level+1); - inline_dump($dst, $state, $node->next, $level); + inline_dump($dst, $state, $upfx, $node->child, $level+1); + inline_dump($dst, $state, $upfx, $node->next, $level); } 1; diff --git a/lib/PublicInbox/WWW.pm b/lib/PublicInbox/WWW.pm index a9cb6d7..d666a1b 100644 --- a/lib/PublicInbox/WWW.pm +++ b/lib/PublicInbox/WWW.pm @@ -16,6 +16,7 @@ use URI::Escape qw(uri_escape_utf8 uri_unescape); use constant SSOMA_URL => 'http://ssoma.public-inbox.org/'; use constant PI_URL => 'http://public-inbox.org/'; our $LISTNAME_RE = qr!\A/([\w\.\-]+)!; +our $MID_RE = qr!([^/]+)!; our $pi_config; sub run { @@ -31,56 +32,37 @@ sub run { if ($path_info eq '/') { r404(); } elsif ($path_info =~ m!$LISTNAME_RE\z!o) { - invalid_list(\%ctx, $1) || redirect_list_index($cgi); + invalid_list(\%ctx, $1) || r301(\%ctx, $1); } elsif ($path_info =~ m!$LISTNAME_RE(?:/|/index\.html)?\z!o) { invalid_list(\%ctx, $1) || get_index(\%ctx); } elsif ($path_info =~ m!$LISTNAME_RE/(?:atom\.xml|new\.atom)\z!o) { invalid_list(\%ctx, $1) || get_atom(\%ctx); + # thread display + } elsif ($path_info =~ m!$LISTNAME_RE/$MID_RE/t/\z!o) { + invalid_list_mid(\%ctx, $1, $2) || get_thread(\%ctx); + } elsif ($path_info =~ m!$LISTNAME_RE/$MID_RE/t\.mbox(\.gz)?\z!o) { + my $sfx = $3; + invalid_list_mid(\%ctx, $1, $2) || get_thread_mbox(\%ctx, $sfx); + } elsif ($path_info =~ m!$LISTNAME_RE/$MID_RE/t\.atom\z!o) { + invalid_list_mid(\%ctx, $1, $2) || get_thread_atom(\%ctx); + # single-message pages - } elsif ($path_info =~ m!$LISTNAME_RE/m/(\S+)/\z!o) { + } elsif ($path_info =~ m!$LISTNAME_RE/$MID_RE/\z!o) { invalid_list_mid(\%ctx, $1, $2) || get_mid_html(\%ctx); - } elsif ($path_info =~ m!$LISTNAME_RE/m/(\S+)/raw\z!o) { + } elsif ($path_info =~ m!$LISTNAME_RE/$MID_RE/raw\z!o) { invalid_list_mid(\%ctx, $1, $2) || get_mid_txt(\%ctx); # full-message page - } elsif ($path_info =~ m!$LISTNAME_RE/f/(\S+)/\z!o) { + } elsif ($path_info =~ m!$LISTNAME_RE/$MID_RE/f/\z!o) { invalid_list_mid(\%ctx, $1, $2) || get_full_html(\%ctx); - # thread display - } elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)/\z!o) { - invalid_list_mid(\%ctx, $1, $2) || get_thread(\%ctx); - - } elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)/mbox(\.gz)?\z!o) { - my $sfx = $3; - invalid_list_mid(\%ctx, $1, $2) || - get_thread_mbox(\%ctx, $sfx); - - } elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)/atom\z!o) { - invalid_list_mid(\%ctx, $1, $2) || get_thread_atom(\%ctx); - - # legacy redirects - } elsif ($path_info =~ m!$LISTNAME_RE/(t|m|f)/(\S+)\.html\z!o) { - my $pfx = $2; - invalid_list_mid(\%ctx, $1, $3) || - redirect_mid(\%ctx, $pfx, qr/\.html\z/, '/'); - } elsif ($path_info =~ m!$LISTNAME_RE/(m|f)/(\S+)\.txt\z!o) { - my $pfx = $2; - invalid_list_mid(\%ctx, $1, $3) || - redirect_mid(\%ctx, $pfx, qr/\.txt\z/, '/raw'); - } elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)(\.mbox(?:\.gz)?)\z!o) { - my $end = $3; - invalid_list_mid(\%ctx, $1, $2) || - redirect_mid(\%ctx, 't', $end, '/mbox.gz'); - - # convenience redirects, order matters - } elsif ($path_info =~ m!$LISTNAME_RE/(m|f|t|s)/(\S+)\z!o) { - my $pfx = $2; - invalid_list_mid(\%ctx, $1, $3) || - redirect_mid(\%ctx, $pfx, qr/\z/, '/'); + # convenience redirects order matters + } elsif ($path_info =~ m!$LISTNAME_RE/([^/]{2,})\z!o) { + r301(\%ctx, $1, $2); } else { - r404(); + legacy_redirects(\%ctx, $path_info); } } @@ -163,7 +145,7 @@ sub mid2blob { } } -# /$LISTNAME/m/$MESSAGE_ID.txt -> raw mbox +# /$LISTNAME/$MESSAGE_ID/raw -> raw mbox sub get_mid_txt { my ($ctx) = @_; my $x = mid2blob($ctx) or return r404(); @@ -171,22 +153,21 @@ sub get_mid_txt { PublicInbox::Mbox::emit1($x); } -# /$LISTNAME/m/$MESSAGE_ID.html -> HTML content (short quotes) +# /$LISTNAME/$MESSAGE_ID/ -> HTML content (short quotes) sub get_mid_html { my ($ctx) = @_; my $x = mid2blob($ctx) or return r404(); require PublicInbox::View; - my $pfx = msg_pfx($ctx); my $foot = footer($ctx); require Email::MIME; my $mime = Email::MIME->new($x); searcher($ctx); [ 200, [ 'Content-Type' => 'text/html; charset=UTF-8' ], - [ PublicInbox::View::msg_html($ctx, $mime, $pfx, $foot) ] ]; + [ PublicInbox::View::msg_html($ctx, $mime, 'f/', $foot) ] ]; } -# /$LISTNAME/f/$MESSAGE_ID.html -> HTML content (fullquotes) +# /$LISTNAME/$MESSAGE_ID/f/ -> HTML content (fullquotes) sub get_full_html { my ($ctx) = @_; my $x = mid2blob($ctx) or return r404(); @@ -200,7 +181,7 @@ sub get_full_html { [ PublicInbox::View::msg_html($ctx, $mime, undef, $foot)] ]; } -# /$LISTNAME/t/$MESSAGE_ID.html +# /$LISTNAME/$MESSAGE_ID/t/ sub get_thread { my ($ctx) = @_; my $srch = searcher($ctx) or return need_search($ctx); @@ -214,39 +195,6 @@ sub self_url { ref($cgi) eq 'CGI' ? $cgi->self_url : $cgi->uri->as_string; } -sub redirect_list_index { - my ($cgi) = @_; - do_redirect(self_url($cgi) . "/"); -} - -sub redirect_mid { - my ($ctx, $pfx, $old, $sfx) = @_; - my $url = self_url($ctx->{cgi}); - my $anchor = ''; - if (lc($pfx) eq 't' && $sfx eq '/') { - $anchor = '#u'; # is used to highlight in View.pm - } - $url =~ s/$old/$sfx/; - do_redirect($url . $anchor); -} - -# only hit when somebody tries to guess URLs manually: -sub redirect_mid_txt { - my ($ctx, $pfx) = @_; - my $listname = $ctx->{listname}; - my $url = self_url($ctx->{cgi}); - $url =~ s!/$listname/f/(\S+\.txt)\z!/$listname/m/$1!; - do_redirect($url); -} - -sub do_redirect { - my ($url) = @_; - [ 301, - [ Location => $url, 'Content-Type' => 'text/plain' ], - [ "Redirecting to $url\n" ] - ] -} - sub ctx_get { my ($ctx, $key) = @_; my $val = $ctx->{$key}; @@ -333,14 +281,8 @@ EOF [ 501, [ 'Content-Type' => 'text/html; charset=UTF-8' ], [ $msg ] ]; } -sub msg_pfx { - my ($ctx) = @_; - my $href = PublicInbox::Hval::ascii_html(uri_escape_utf8($ctx->{mid})); - "../../f/$href/"; -} - -# /$LISTNAME/t/$MESSAGE_ID/mbox -> thread as mbox -# /$LISTNAME/t/$MESSAGE_ID/mbox.gz -> thread as gzipped mbox +# /$LISTNAME/$MESSAGE_ID/t.mbox -> thread as mbox +# /$LISTNAME/$MESSAGE_ID/t.mbox.gz -> thread as gzipped mbox # note: I'm not a big fan of other compression formats since they're # significantly more expensive on CPU than gzip and less-widely available, # especially on older systems. Stick to zlib since that's what git uses. @@ -352,7 +294,7 @@ sub get_thread_mbox { } -# /$LISTNAME/t/$MESSAGE_ID/atom -> thread as Atom feed +# /$LISTNAME/$MESSAGE_ID/t.atom -> thread as Atom feed sub get_thread_atom { my ($ctx) = @_; searcher($ctx) or return need_search($ctx); @@ -361,4 +303,71 @@ sub get_thread_atom { PublicInbox::Feed::generate_thread_atom($ctx); } +sub legacy_redirects { + my ($ctx, $path_info) = @_; + + # single-message pages + if ($path_info =~ m!$LISTNAME_RE/m/(\S+)/\z!o) { + r301($ctx, $1, $2); + } elsif ($path_info =~ m!$LISTNAME_RE/m/(\S+)/raw\z!o) { + r301($ctx, $1, $2, 'raw'); + + } elsif ($path_info =~ m!$LISTNAME_RE/f/(\S+)/\z!o) { + r301($ctx, $1, $2, 'f/'); + + # thread display + } elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)/\z!o) { + r301($ctx, $1, $2, 't/#u'); + + } elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)/mbox(\.gz)?\z!o) { + r301($ctx, $1, $2, "t.mbox$3"); + + # even older legacy redirects + } elsif ($path_info =~ m!$LISTNAME_RE/m/(\S+)\.html\z!o) { + r301($ctx, $1, $2); + + } elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)\.html\z!o) { + r301($ctx, $1, $2, 't/#u'); + + } elsif ($path_info =~ m!$LISTNAME_RE/f/(\S+)\.html\z!o) { + r301($ctx, $1, $2, 'f/'); + + } elsif ($path_info =~ m!$LISTNAME_RE/(?:m|f)/(\S+)\.txt\z!o) { + r301($ctx, $1, $2, 'raw'); + + } elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)(\.mbox(?:\.gz)?)\z!o) { + r301($ctx, $1, $2, "t$3"); + + # legacy convenience redirects, order still matters + } elsif ($path_info =~ m!$LISTNAME_RE/m/(\S+)\z!o) { + r301($ctx, $1, $2); + } elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)\z!o) { + r301($ctx, $1, $2, 't/#u'); + } elsif ($path_info =~ m!$LISTNAME_RE/f/(\S+)\z!o) { + r301($ctx, $1, $2, 'f/'); + + } else { + r404(); + } +} + +sub r301 { + my ($ctx, $listname, $mid, $suffix) = @_; + my $cgi = $ctx->{cgi}; + my $url; + if (ref($cgi) eq 'CGI') { + $url = $cgi->url(-base) . '/'; + } else { + $url = $cgi->base->as_string; + } + + $url .= $listname . '/'; + $url .= (uri_escape_utf8($mid) . '/') if (defined $mid); + $url .= $suffix if (defined $suffix); + + [ 301, + [ Location => $url, 'Content-Type' => 'text/plain' ], + [ "Redirecting to $url\n" ] ] +} + 1; diff --git a/t/cgi.t b/t/cgi.t index d84e634..a6600c2 100644 --- a/t/cgi.t +++ b/t/cgi.t @@ -109,7 +109,7 @@ EOF like($res->{body}, qr/test for public-inbox/, "set title in XML feed"); like($res->{body}, - qr!http://test\.example\.com/test/m/blah%40example\.com!, + qr!http://test\.example\.com/test/blah%40example\.com/!, "link id set"); like($res->{body}, qr/what\?/, "reply included"); } @@ -152,26 +152,26 @@ EOF } local $ENV{GIT_DIR} = $maindir; - my $res = cgi_run("/test/m/slashy%2fasdf%40example.com/raw"); + my $res = cgi_run("/test/slashy%2fasdf%40example.com/raw"); like($res->{body}, qr/Message-Id: <\Q$slashy_mid\E>/, "slashy mid raw hit"); - $res = cgi_run("/test/m/blahblah\@example.com/raw"); + $res = cgi_run("/test/blahblah\@example.com/raw"); like($res->{body}, qr/Message-Id: <blahblah\@example\.com>/, "mid raw hit"); - $res = cgi_run("/test/m/blahblah\@example.con/raw"); + $res = cgi_run("/test/blahblah\@example.con/raw"); like($res->{head}, qr/Status: 404 Not Found/, "mid raw miss"); - $res = cgi_run("/test/m/blahblah\@example.com/"); + $res = cgi_run("/test/blahblah\@example.com/"); like($res->{body}, qr/\A<html>/, "mid html hit"); like($res->{head}, qr/Status: 200 OK/, "200 response"); - $res = cgi_run("/test/m/blahblah\@example.con/"); + $res = cgi_run("/test/blahblah\@example.con/"); like($res->{head}, qr/Status: 404 Not Found/, "mid html miss"); - $res = cgi_run("/test/f/blahblah\@example.com/"); + $res = cgi_run("/test/blahblah\@example.com/f/"); like($res->{body}, qr/\A<html>/, "mid html"); like($res->{head}, qr/Status: 200 OK/, "200 response"); - $res = cgi_run("/test/f/blahblah\@example.con/"); + $res = cgi_run("/test/blahblah\@example.con/f/"); like($res->{head}, qr/Status: 404 Not Found/, "mid html miss"); $res = cgi_run("/test/"); @@ -183,7 +183,7 @@ EOF { local $ENV{HOME} = $home; local $ENV{PATH} = $main_path; - my $path = "/test/t/blahblah%40example.com/mbox.gz"; + my $path = "/test/blahblah%40example.com/t.mbox.gz"; my $res = cgi_run($path); like($res->{head}, qr/^Status: 501 /, "search not-yet-enabled"); my $indexed = system($index, $maindir) == 0; @@ -203,7 +203,7 @@ EOF my $have_xml_feed = eval { require XML::Feed; 1 } if $indexed; if ($have_xml_feed) { - $path = "/test/t/blahblah%40example.com/atom"; + $path = "/test/blahblah%40example.com/t.atom"; $res = cgi_run($path); like($res->{head}, qr/^Status: 200 /, "atom returned 200"); like($res->{head}, qr!^Content-Type: application/xml!m, diff --git a/t/feed.t b/t/feed.t index a9955f0..e4ec752 100644 --- a/t/feed.t +++ b/t/feed.t @@ -77,7 +77,7 @@ EOF } unlike($feed, qr/drop me/, "long quoted text dropped"); - like($feed, qr!/f/\d%40example\.com/#q!, + like($feed, qr!/\d%40example\.com/f/#q!, "/f/ url generated for long quoted text"); like($feed, qr/inline me here/, "short quoted text kept"); like($feed, qr/keep me/, "unquoted text saved"); diff --git a/t/plack.t b/t/plack.t index 50c9e60..067a593 100644 --- a/t/plack.t +++ b/t/plack.t @@ -88,7 +88,7 @@ EOF is(200, $res->code, 'success response received'); like($res->content, qr!href="\Q$atomurl\E"!, 'atom URL generated'); - like($res->content, qr!href="m/blah%40example\.com/"!, + like($res->content, qr!href="blah%40example\.com/"!, 'index generated'); }); @@ -98,14 +98,14 @@ EOF my $res = $cb->(GET($pfx . '/atom.xml')); is(200, $res->code, 'success response received for atom'); like($res->content, - qr!link\s+href="\Q$pfx\E/m/blah%40example\.com/"!s, + qr!link\s+href="\Q$pfx\E/blah%40example\.com/"!s, 'atom feed generated correct URL'); }); - foreach my $t (qw(f m)) { + foreach my $t (('', 'f/')) { test_psgi($app, sub { my ($cb) = @_; - my $path = "/$t/blah%40example.com/"; + my $path = "/blah%40example.com/$t"; my $res = $cb->(GET($pfx . $path)); is(200, $res->code, "success for $path"); like($res->content, qr!<title>hihi - Me!, @@ -114,8 +114,8 @@ EOF } test_psgi($app, sub { my ($cb) = @_; - my $res = $cb->(GET($pfx . '/m/blah%40example.com/raw')); - is(200, $res->code, 'success response received for /m/*/raw'); + my $res = $cb->(GET($pfx . '/blah%40example.com/raw')); + is(200, $res->code, 'success response received for /*/raw'); like($res->content, qr!\AFrom !, "mbox returned"); }); @@ -126,18 +126,25 @@ EOF my $res = $cb->(GET($pfx . "/$t/blah%40example.com.txt")); is(301, $res->code, "redirect for old $t .txt link"); my $location = $res->header('Location'); - like($location, qr!/$t/blah%40example\.com/raw\z!, + like($location, qr!/blah%40example\.com/raw\z!, ".txt redirected to /raw"); }); } - foreach my $t (qw(m f t)) { + + my %umap = ( + 'm' => '', + 'f' => 'f/', + 't' => 't/', + ); + while (my ($t, $e) = each %umap) { test_psgi($app, sub { my ($cb) = @_; my $res = $cb->(GET($pfx . "/$t/blah%40example.com.html")); is(301, $res->code, "redirect for old $t .html link"); my $location = $res->header('Location'); - like($location, qr!/$t/blah%40example\.com/(?:#u)?\z!, - ".html redirected to /raw"); + like($location, + qr!/blah%40example\.com/$e(?:#u)?\z!, + ".html redirected to new location"); }); } foreach my $sfx (qw(mbox mbox.gz)) { @@ -146,8 +153,9 @@ EOF my $res = $cb->(GET($pfx . "/t/blah%40example.com.$sfx")); is(301, $res->code, 'redirect for old thread link'); my $location = $res->header('Location'); - like($location, qr!/t/blah%40example\.com/mbox\.gz\z!, - "$sfx redirected to /mbox.gz"); + like($location, + qr!/blah%40example\.com/t\.mbox(?:\.gz)?\z!, + "$sfx redirected to /mbox.gz"); }); } } diff --git a/t/view.t b/t/view.t index 77cf3a3..83823d8 100644 --- a/t/view.t +++ b/t/view.t @@ -44,17 +44,17 @@ EOF my $html = PublicInbox::View::msg_html(undef, $mime); # ghetto tests - like($html, qr!]+><\/a>> Long and wordy/, "long quoted text is anchored"); # short page - my $pfx = "../../f/hello%40example.com/"; + my $pfx = "../hello%40example.com/f/"; $mime = Email::MIME->new($s); my $short = PublicInbox::View::msg_html(undef, $mime, $pfx); - like($short, qr!