about summary refs log tree commit homepage
path: root/lib/PublicInbox/RepoBrowse.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/PublicInbox/RepoBrowse.pm')
-rw-r--r--lib/PublicInbox/RepoBrowse.pm85
1 files changed, 85 insertions, 0 deletions
diff --git a/lib/PublicInbox/RepoBrowse.pm b/lib/PublicInbox/RepoBrowse.pm
new file mode 100644
index 00000000..5865d65d
--- /dev/null
+++ b/lib/PublicInbox/RepoBrowse.pm
@@ -0,0 +1,85 @@
+# Copyright (C) 2015 all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# 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::RepoConfig;
+
+my %CMD = map { lc($_) => $_ } qw(Log Commit);
+
+sub new {
+        my ($class, $file) = @_;
+        bless { rconfig => PublicInbox::RepoConfig->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 may both contain '/'
+        my $rconfig = $self->{rconfig};
+        my (undef, $repo_path, @extra) = split(m{/+}, $cgi->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,
+                path => \@extra,
+                cgi => $cgi,
+                rconfig => $rconfig,
+        };
+
+        my $cmd = shift @extra;
+        if (defined $cmd && length $cmd) {
+                my $mod = $CMD{$cmd};
+                return r404() unless defined $mod;
+                if (index($mod, ':') < 0) {
+                        $mod = "PublicInbox::RepoBrowse$mod";
+                        eval "require $mod";
+                        $CMD{$cmd} = $mod unless $@;
+                }
+                $req->{relcmd} = '../' x scalar(@extra);
+                my $rv = eval { $mod->new->call($req) };
+                $rv || r404();
+        } else {
+                $req->{relcmd} = defined $cmd ? ''  : './';
+                summary($req);
+        }
+}
+
+sub summary {
+        r404();
+}
+
+sub r404 { r(404, 'Not Found') }
+
+1;