import
[web.mtrack] / bin / codeshell
1 #!/usr/bin/perl
2 # vim:ts=2:sw=2:et:
3 # For licensing and copyright terms, see the file named LICENSE
4 use strict;
5 use IO::File;
6
7 # We are invoked by sshd as the repo serving user in place of the actual
8 # command they requested.
9 # Our purpose is to interpose and ensure that the underlying tool looks
10 # only at the appropriate location
11
12 # Our parameters are:
13 # $1: path to the mtrack config.ini file
14 # $2: the mtrack username
15 # However, at least on OS/X, we get invoked as "-c '$1 $2'", so we need
16 # to check for that.
17
18 my ($inifile, $username, $mtrack) = @ARGV;
19
20 if ($inifile eq '-c') {
21   require 'shellwords.pl';
22   @ARGV = &shellwords($username);
23   shift @ARGV;
24   ($inifile, $username, $mtrack) = @ARGV;
25 }
26 $ENV{MTRACK_CONFIG_FILE} = $inifile;
27
28 # The command requested by the remote user is stored in this envvar.
29 my $cmd = $ENV{SSH_ORIGINAL_COMMAND};
30
31 sub validate_reponame {
32   my ($name) = @_;
33
34   if ($name =~ m/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/) {
35     if ($name !~ m/\.\./) {
36       my $base = get_cfg('repos', 'basedir');
37       if (! -d "$base/$name") {
38         print STDERR "Non-existant repo $name\n";
39         exit(1);
40       }
41
42       # Sanity check that we at least have checkout access
43       my $php = get_tool('php');
44       if (system($php, "$mtrack/bin/acl-check.php", $username, '--repo',
45           $name, 'checkout')) {
46         print STDERR "$username does not have checkout permission on $name\n";
47         exit(1);
48       }
49
50       return "$base/$name";
51     }
52   }
53   print STDERR "Invalid repo name $name\n";
54   exit(1);
55 }
56
57 my %CFG;
58
59 sub read_config_file {
60   my $f = IO::File->new($inifile);
61   if (!$f) {
62     print STDERR "Unable to open ini file $inifile: $!\n";
63     exit(1);
64   }
65   my $sect = undef;
66   while (<$f>) {
67     my $line = $_;
68     $line =~ s/;.*$//;
69     $line =~ s/\s+$//;
70     if ($line =~ m/^\[(.*)\]$/) {
71       $sect = $1;
72       next;
73     }
74     if ($line =~ m/^(\S+)\s*=\s*"(.*)"$/) {
75       $CFG{$sect}{$1} = $2;
76       next;
77     }
78     if ($line =~ m/^(\S+)\s*=\s*(.*)$/) {
79       $CFG{$sect}{$1} = $2;
80       next;
81     }
82   }
83 }
84
85 sub get_cfg {
86   my ($sect, $name) = @_;
87   my $val;
88
89   if (not exists $CFG{$sect}) {
90     return undef;
91   }
92   if (not exists $CFG{$sect}{$name}) {
93     return undef;
94   }
95   $val = $CFG{$sect}{$name};
96
97   while ($val =~ m/\@\{(\S+):(\S+)\}/) {
98     my ($s, $k) = ($1, $2);
99
100     my $r = '';
101     if (exists $CFG{$s} and exists $CFG{$s}{$k}) {
102       $r = $CFG{$s}{$k};
103     }
104     $val =~ s/\@\{$s:$k\}/$r/g;
105   }
106   return $val;
107 }
108
109 read_config_file();
110
111 sub get_tool {
112   my ($name) = @_;
113   my $tool = get_cfg('tools', $name);
114   if (-x $tool) {
115     return $tool;
116   }
117   print STDERR "tool $name is not configured\n";
118   exit(1);
119 }
120
121 $ENV{LOGNAME} = $username;
122 if (0) {
123   open LOG, ">>/var/tmp/mtrack.ssh.session.log";
124   print LOG "$username $cmd\n";
125   close LOG;
126 }
127
128 if ($cmd =~ m/^hg -R (\S+) serve --stdio$/) {
129   my $name = validate_reponame($1);
130
131   my $hg = get_tool('hg');
132
133   exec($hg, '-R', $name, 'serve', '--stdio');
134 }
135
136 if ($cmd =~ m/^git-(\S+)\s+'(\S+)'$/) {
137   my ($verb, $name) = ($1, $2);
138   $name = validate_reponame($name);
139   my $git = get_tool('git');
140
141   exec($git, 'shell', '-c', "git-$verb '$name'");
142 }
143
144 if ($cmd eq 'svnserve -t') {
145   my $base = get_cfg('repos', 'basedir');
146   if (! -d $base) {
147     print STDERR "basedir $base does not exist\n";
148     exit(1);
149   }
150   my $svnserve = get_tool('svnserve');
151   exec($svnserve, '-r', $base, '-t', "--tunnel-user=$username");
152 }
153
154 print STDERR "Unsupported command:\n$cmd\n";