gitolite/src/gitolite-shell
Sitaram Chamarty e9ea674be9 (minor) fixup various TODOs and such
some got junked, some were already done or got done, and some were
converted into actual todo items in the 'todo' file.
2012-03-24 10:30:45 +05:30

140 lines
4.3 KiB
Perl
Executable file

#!/usr/bin/perl
# gitolite shell, invoked from ~/.ssh/authorized_keys
# ----------------------------------------------------------------------
use FindBin;
BEGIN { $ENV{GL_BINDIR} = $FindBin::RealBin; }
use lib $ENV{GL_BINDIR};
use Gitolite::Rc;
use Gitolite::Common;
use Gitolite::Conf::Load;
use strict;
use warnings;
# the main() sub expects ssh-ish things; set them up...
my $id = '';
if ( exists $ENV{G3T_USER} ) {
$id = in_local(); # file:// masquerading as ssh:// for easy testing
} elsif ( exists $ENV{SSH_CONNECTION} ) {
$id = in_ssh();
} elsif ( exists $ENV{REQUEST_URI} ) {
$id = in_http();
} else {
_die "who the *heck* are you?";
}
main($id);
gl_log( '==end==' ) if $$ == $ENV{GL_TID};
exit 0;
# ----------------------------------------------------------------------
sub in_local {
if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /git-\w+-pack/ ) {
print STDERR "TRACE: gsh(", join( ")(", @ARGV ), ")\n";
print STDERR "TRACE: gsh(SOC=$ENV{SSH_ORIGINAL_COMMAND})\n";
}
return 'local';
}
sub in_http {
_die 'http not yet implemented...';
}
sub in_ssh {
$ENV{SSH_ORIGINAL_COMMAND} ||= '';
my $soc = $ENV{SSH_ORIGINAL_COMMAND};
$soc =~ s/[\n\r]+/<<newline>>/g;
_die "I don't like newlines in the command: $soc\n" if $ENV{SSH_ORIGINAL_COMMAND} ne $soc;
my $ip;
($ip = $ENV{SSH_CONNECTION} || '(no-IP)') =~ s/ .*//;
return $ip;
}
# ----------------------------------------------------------------------
# call this once you are sure arg-1 is the username and SSH_ORIGINAL_COMMAND
# has been setup (even if it's not actually coming via ssh).
sub main {
my $id = shift;
gl_log( 'remote', $id, @ARGV, $ENV{SSH_ORIGINAL_COMMAND} );
umask $rc{UMASK};
# set up the user
my $user = $ENV{GL_USER} = shift @ARGV;
# set up the repo and the attempted access
my ( $verb, $repo ) = parse_soc(); # returns only for git commands
sanity($repo);
$ENV{GL_REPO} = $repo;
my $aa = ( $verb =~ 'upload' ? 'R' : 'W' );
# auto-create?
if ( repo_missing($repo) and access( $repo, $user, '^C', 'any' ) !~ /DENIED/ ) {
require Gitolite::Conf::Store;
Gitolite::Conf::Store->import;
new_wild_repo( $repo, $user );
gl_log( 'create', $repo, $user );
}
# a ref of 'any' signifies that this is a pre-git check, where we don't
# yet know the ref that will be eventually pushed (and even that won't
# apply if it's a read operation). See the matching code in access() for
# more information.
my $ret = access( $repo, $user, $aa, 'any' );
trace( 1, "access($repo, $user, $aa, 'any')", "-> $ret" );
gl_log( 'check1', $repo, $user, $aa, 'any', '->', $ret );
trigger( 'ACCESS_CHECK', $repo, $user, $aa, 'any', $ret );
_die $ret . "\n(or you mis-spelled the reponame)" if $ret =~ /DENIED/;
check_repo_write_enabled($repo) if $aa eq 'W';
trigger( 'PRE_GIT', $repo, $user, $aa, 'any', $verb );
my $repodir = "'$rc{GL_REPO_BASE}/$repo.git'";
_system( "git", "shell", "-c", "$verb $repodir" );
trigger( 'POST_GIT', $repo, $user, $aa, 'any', $verb );
}
# ----------------------------------------------------------------------
sub parse_soc {
my $soc = $ENV{SSH_ORIGINAL_COMMAND};
$soc ||= 'info';
my $git_commands = "git-upload-pack|git-receive-pack|git-upload-archive";
if ( $soc =~ m(^($git_commands) '/?(.*?)(?:\.git(\d)?)?'$) ) {
my ( $verb, $repo, $trace_level ) = ( $1, $2, $3 );
$ENV{D} = $trace_level if $trace_level;
_die "invalid repo name: '$repo'" if $repo !~ $REPONAME_PATT;
trace( 2, "git command", $soc );
return ( $verb, $repo );
}
# after this we should not return; caller expects us to handle it all here
# and exit out
_die "suspicious characters loitering about '$soc'" if $soc !~ $REMOTE_COMMAND_PATT;
my @words = split ' ', $soc;
if ( $rc{COMMANDS}{ $words[0] } ) {
trace( 2, "gitolite command", $soc );
_system( "gitolite", @words );
exit 0;
}
_die "unknown git/gitolite command: $soc";
}
sub sanity {
my $repo = shift;
_die "'$repo' contains bad characters" if $repo !~ $REPONAME_PATT;
_die "'$repo' ends with a '/'" if $repo =~ m(/$);
_die "'$repo' contains '..'" if $repo =~ m(\.\.$);
}