public-inbox.git  about / heads / tags
an "archives first" approach to mailing lists
blob 49d5103330e1f668268fa00b42937d563f054e29 3378 bytes (raw)
$ git show repobrowse:lib/PublicInbox/RepoGitSnapshot.pm	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
 
# Copyright (C) 2016 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>

# shows the /snapshot/ endpoint for git repositories
# Mainly for compatibility reasons with cgit, I'm unsure if
# showing this in a repository viewer is a good idea.

package PublicInbox::RepoGitSnapshot;
use strict;
use warnings;
use base qw(PublicInbox::RepoBase);
use PublicInbox::Git;
use PublicInbox::Qspawn;
our $SUFFIX;
BEGIN {
	# as described in git-archive(1), users may add support for
	# other compression schemes such as xz or bz2 via git-config(1):
	#	git config tar.tar.xz.command "xz -c"
	#	git config tar.tar.bz2.command "bzip2 -c"
	chomp(my @l = `git archive --list`);
	$SUFFIX = join('|', map { quotemeta $_ } @l);
}

# Not using standard mime types since the compressed tarballs are
# special or do not match my /etc/mime.types.  Choose what gitweb
# and cgit agree on for compatibility.
our %FMT_TYPES = (
	'tar' => 'application/x-tar',
	'tar.bz2' => 'application/x-bzip2',
	'tar.gz' => 'application/x-gzip',
	'tar.xz' => 'application/x-xz',
	'tgz' => 'application/x-gzip',
	'zip' => 'application/x-zip',
);

sub call_git_snapshot ($$) { # invoked by PublicInbox::RepoBase::call
	my ($self, $req) = @_;

	my $ref = $req->{tip} || $req->{-repo}->tip;
	my $orig_fn = $ref;

	# just in case git changes refname rules, don't allow wonky filenames
	# to break the Content-Disposition header, either.
	return $self->r(404) if $orig_fn =~ /["\s]/s;
	return $self->r(404) unless ($ref =~ s/\.($SUFFIX)\z//o);
	my $fmt = $1;
	my $env = $req->{env};
	my $repo = $req->{-repo};

	# support disabling certain snapshots types entirely to twart
	# URL guessing since it could burn server resources.
	return $self->r(404) if $repo->{snapshots_disabled}->{$fmt};

	# strip optional basename (may not exist)
	$ref =~ s/$repo->{snapshot_re}//;

	# don't allow option/command injection, git refs do not start with '-'
	return $self->r(404) if $ref =~ /\A-/;

	my $git = $repo->{git};
	my $tree = '';
	my $last_cb = sub {
		delete $env->{'repobrowse.tree_cb'};
		delete $env->{'qspawn.quiet'};
		my $pfx = "$repo->{snapshot_pfx}-$ref/";
		my $cmd = $git->cmd('archive',
				"--prefix=$pfx", "--format=$fmt", $tree);
		my $rdr = { 2 => $git->err_begin };
		my $qsp = PublicInbox::Qspawn->new($cmd, undef, $rdr);
		$qsp->psgi_return($env, undef, sub {
			my $r = $_[0];
			return $self->r(500) unless $r;
			[ 200, [ 'Content-Type',
				$FMT_TYPES{$fmt} || 'application/octet-stream',
				'Content-Disposition',
					qq(inline; filename="$orig_fn"),
				'ETag', qq("$tree") ] ];
		});
	};

	my $cmd = $git->cmd(qw(rev-parse --verify --revs-only));
	# try prefixing "v" or "V" for tag names to get the tree
	my @refs = ("V$ref", "v$ref", $ref);
	$env->{'qspawn.quiet'} = 1;
	my $tree_cb = $env->{'repobrowse.tree_cb'} = sub {
		my ($ref) = @_;
		if (defined $ref) {
			$tree = $$ref;
			chomp $tree;
		}
		return $last_cb->() if $tree ne '';
		unless (scalar(@refs)) {
			my $res = delete $env->{'qspawn.response'};
			return $res->($self->r(404));
		}
		my $rdr = { 2 => $git->err_begin };
		my $r = pop @refs;
		my $qsp = PublicInbox::Qspawn->new([@$cmd, $r], undef, $rdr);
		$qsp->psgi_qx($env, undef, $env->{'repobrowse.tree_cb'});
	};
	sub {
		$env->{'qspawn.response'} = $_[0];
		# kick off the "loop" foreach @refs
		$tree_cb->(undef);
	}
}

1;

git clone https://public-inbox.org/public-inbox.git
git clone http://7fh6tueqddpjyxjmgtdiueylzoqt6pt7hec3pukyptlmohoowvhde4yd.onion/public-inbox.git