From 1e7a2bbd2c7b0c1d5f989c0e225d22276055eff1 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 12 Jan 2016 21:32:33 +0000 Subject: repobrowse: change Perl capitalization to "Repobrowse" We mainly call it "repobrowse" (all lowercase), so do not imply it is two separate words by capitalizing "Browse". --- lib/PublicInbox/Repobrowse.pm | 106 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 lib/PublicInbox/Repobrowse.pm (limited to 'lib/PublicInbox/Repobrowse.pm') diff --git a/lib/PublicInbox/Repobrowse.pm b/lib/PublicInbox/Repobrowse.pm new file mode 100644 index 00000000..75dee72f --- /dev/null +++ b/lib/PublicInbox/Repobrowse.pm @@ -0,0 +1,106 @@ +# Copyright (C) 2015 all contributors +# License: AGPL-3.0+ + +# Version control system (VCS) repository viewer like cgit or gitweb, +# but with optional public-inbox archive integration. +# This uses cgit-compatible PATH_INFO URLs. +# This may be expanded to support other Free Software VCSes such as +# Subversion and Mercurial, so not just git +# +# Same web design principles as PublicInbox::WWW for supporting the +# lowest common denominators (see bottom of Documentation/design_www.txt) +# +# This allows an M:N relationship between "normal" repos for project +# and public-inbox (ssoma) git repositories where N may be zero. +# In other words, repobrowse must work for repositories without +# any public-inbox at all; or with multiple public-inboxes. +# And the rest of public-inbox will always work without a "normal" +# code repo for the project. + +package PublicInbox::Repobrowse; +use strict; +use warnings; +use URI::Escape qw(uri_escape_utf8 uri_unescape); +use PublicInbox::RepobrowseConfig; + +my %CMD = map { lc($_) => $_ } qw(Log Commit Tree Patch Blob Plain Tag); +my %VCS = (git => 'Git'); +my %LOADED; + +sub new { + my ($class, $file) = @_; + bless { rconfig => PublicInbox::RepobrowseConfig->new($file) }, $class; +} + +# simple response for errors +sub r { [ $_[0], ['Content-Type' => 'text/plain'], [ join(' ', @_, "\n") ] ] } + +sub run { + my ($self, $cgi, $method) = @_; + return r(405, 'Method Not Allowed') if ($method !~ /\AGET|HEAD\z/); + + # URL syntax: / repo [ / cmd [ / path ] ] + # cmd: log | commit | diff | tree | view | blob | snapshot + # repo and path (@extra) may both contain '/' + my $rconfig = $self->{rconfig}; + my $path_info = uri_unescape($cgi->path_info); + my (undef, $repo_path, @extra) = split(m{/+}, $path_info, -1); + + return r404() unless $repo_path; + my $repo_info; + until ($repo_info = $rconfig->lookup($repo_path)) { + my $p = shift @extra or last; + $repo_path .= "/$p"; + } + return r404() unless $repo_info; + + my $req = { + repo_info => $repo_info, + extra => \@extra, # path + cgi => $cgi, + rconfig => $rconfig, + tslash => 0, + }; + + my $cmd = shift @extra; + if (defined $cmd && length $cmd) { + my $vcs_lc = $repo_info->{vcs}; + my $vcs = $VCS{$vcs_lc} or return r404(); + my $mod = $CMD{$cmd}; + unless ($mod) { + unshift @extra, $cmd; + $mod = 'Fallback'; + } + $mod = load_once("PublicInbox::Repobrowse$vcs$mod"); + $vcs = load_once("PublicInbox::$vcs"); + $repo_info->{$vcs_lc} ||= $vcs->new($repo_info->{path}); + $req->{relcmd} = '../' x scalar(@extra); + while (@extra && $extra[-1] eq '') { + pop @extra; + ++$req->{tslash}; + } + $req->{expath} = join('/', @extra); + my $rv = eval { $mod->new->call($cmd, $req) }; + $rv || r404(); + } else { + $req->{relcmd} = defined $cmd ? '' : './'; + summary($req); + } +} + +sub summary { + r404(); +} + +sub r404 { r(404, 'Not Found') } + +sub load_once { + my ($mod) = @_; + + return $mod if $LOADED{$mod}; + eval "require $mod"; + $LOADED{$mod} = 1 unless $@; + $mod; +} + +1; -- cgit v1.2.3-24-ge0c7