]> git.ipfire.org Git - thirdparty/public-inbox.git/commitdiff
repobrowse: support raw blob display
authorEric Wong <e@80x24.org>
Thu, 24 Dec 2015 23:14:59 +0000 (23:14 +0000)
committerEric Wong <e@80x24.org>
Tue, 5 Apr 2016 18:58:27 +0000 (18:58 +0000)
This allows displaying blob objects, including trees, as is in
"bit perfect" fashion.

lib/PublicInbox/RepoBrowse.pm
lib/PublicInbox/RepoBrowseBase.pm
lib/PublicInbox/RepoBrowseGitBlob.pm [new file with mode: 0644]

index 8fa1d035c9e5889b47ecfc938bdc19be254ac532..41422193326d733fa41118ceabd703910b062351 100644 (file)
@@ -23,7 +23,7 @@ use warnings;
 use URI::Escape qw(uri_escape_utf8 uri_unescape);
 use PublicInbox::RepoConfig;
 
-my %CMD = map { lc($_) => $_ } qw(Log Commit Tree Patch);
+my %CMD = map { lc($_) => $_ } qw(Log Commit Tree Patch Blob);
 my %VCS = (git => 'Git');
 my %LOADED;
 
index e0ea2854e97858e486badca50b01286c83c564f2..7f69108b42d7c87c827a64f43912c79c2ec92a7d 100644 (file)
@@ -19,4 +19,31 @@ sub call {
        $@ ? [ 500, ['Content-Type'=>'text/plain'], [] ] : $rv;
 }
 
+sub mime_load {
+       my ($self, $file) = @_;
+       my %rv;
+       open my $fh, '<', $file or return \%rv;
+       foreach (<$fh>) {
+               next if /^#/; # no comments
+               my ($type, @ext) = split(/\s+/);
+
+               # XSS protection.  Assume the browser knows what to do
+               # with images/audio/video...
+               if (defined $type && $type =~ m!\A(?:image|audio|video)/!) {
+                       $rv{$_} = $type foreach @ext;
+               }
+       }
+       \%rv;
+}
+
+# returns undef if missing, so users can scan the blob if needed
+sub mime_type {
+       my ($self, $fn) = @_;
+       $fn =~ /\.([^\.]+)\z/ or return;
+       my $ext = $1;
+       my $m = $self->{mime_types} ||= $self->mime_load('/etc/mime.types');
+
+       $m->{$ext};
+}
+
 1;
diff --git a/lib/PublicInbox/RepoBrowseGitBlob.pm b/lib/PublicInbox/RepoBrowseGitBlob.pm
new file mode 100644 (file)
index 0000000..d94f0d9
--- /dev/null
@@ -0,0 +1,66 @@
+# Copyright (C) 2015 all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# Show a blob as-is
+package PublicInbox::RepoBrowseGitBlob;
+use strict;
+use warnings;
+use base qw(PublicInbox::RepoBrowseBase);
+
+sub call_git_blob {
+       my ($self, $req) = @_;
+       my $git = $req->{repo_info}->{git};
+       my $to_read = 8000; # git uses this size to detect binary files
+       my $q = PublicInbox::RepoBrowseQuery->new($req->{cgi});
+       my $id = $q->{id};
+       $id eq '' and $id = 'HEAD';
+
+       if (length(my $expath = $req->{expath})) {
+               $id .= ":$expath";
+       }
+       my ($cat, $hex, $type, $size) = $git->cat_file_begin($id);
+       return unless defined $cat;
+
+       my ($r, $buf);
+       my $left = $size;
+       if ($type eq 'blob') {
+               my $base = $req->{extra}->[-1];
+               $type = $self->mime_type($base) if defined $base;
+               unless ($type) {
+                       $to_read = $left if $to_read > $left;
+                       $r = read($cat, $buf, $to_read);
+                       if (!defined $r || $r <= 0) {
+                               $git->cat_file_finish($left);
+                               return;
+                       }
+                       $left -= $r;
+                       $type = (index($buf, "\0") < 0) ?
+                               'text/plain' :
+                               'application/octet-stream';
+               }
+       } elsif ($type eq 'commit' || $type eq 'tag') {
+               $type = 'text/plain';
+       } else {
+               $type = 'application/octet-stream';
+       }
+
+       sub {
+               my ($res) = @_;
+               eval {
+                       my $fh = $res->([ 200, ['Content-Length' => $size,
+                                               'Content-Type' => $type]]);
+                       $fh->write($buf) if defined $buf;
+                       while ($left > 0) {
+                               $to_read = $left if $to_read > $left;
+                               $r = read($cat, $buf, $to_read);
+                               last if (!defined $r || $r <= 0);
+                               $left -= $r;
+                               $fh->write($buf);
+                       }
+                       $fh->close;
+               };
+               $git->cat_file_finish($left);
+       }
+}
+
+1;