<?php
// It is called by a remote client that does a push/fetch by git/ssh.
error_reporting(E_ALL);
ini_set("track_errors", "On");
require_once("/etc/rocketgit/config.php");
$INC = dirname(__FILE__) . "/../inc";
require_once($INC . "/init.inc.php");
require_once($INC . "/util.inc.php");
require_once($INC . "/log.inc.php");
require_once($INC . "/sql.inc.php");
require_once($INC . "/struct.inc.php");
require_once($INC . "/repo.inc.php");
require_once($INC . "/prof.inc.php");
require_once($INC . "/ssh.inc.php");
require_once($INC . "/keys.inc.php");
require_once($INC . "/fixes.inc.php");
require_once($INC . "/plan.inc.php");
require_once($INC . "/ver.php");
rg_prof_start("remote.php");
rg_log_set_file($rg_log_dir . "/remote.log");
function info($str)
{
rg_log("Sending: " . $str);
$str2 = "RocketGit: Info: " . $str . "\n";
if (isset($_SERVER['SSH_CONNECTION'])) {
// ssh
fwrite(STDERR, $str2);
} else {
// Keep in mind we did not negotiated yet the side-band feat.
// So, seems we cannot send nice messages to the user.
}
}
function fatal($str)
{
rg_log("Sending: " . $str);
$str2 = "RocketGit: Error: " . $str . "\n";
if (isset($_SERVER['SSH_CONNECTION'])) {
// ssh
fwrite(STDERR, $str2);
} else {
// See above for 'info' function
// But, keep in mind that at least git archive --remote
// expects a 4 byte len!
//echo "\n" . $str2;
echo rg_git_pack('ERR ' . $str2);
}
exit(1);
}
@umask(0022);
// Without next lines the STDOUT/STDERR are mixed
@stream_set_write_buffer(STDOUT, 0);
@stream_set_write_buffer(STDERR, 0);
rg_log("Start ($rocketgit_version)...");
// DEBUG SELinux
$label = @file_get_contents("/proc/self/attr/current");
rg_log("SELINUX: " . $label);
rg_sql_app("rg-remote");
$db = rg_sql_open($rg_sql);
if ($db === FALSE)
fatal("Internal error (db)!");
// Force ste state loading, for sure we will need it
rg_cache_get('state');
if (rg_struct_ok($db) === FALSE)
fatal("We are in a short maintenance window. Try again later.");
$hostname = rg_state_get($db, 'hostname');
$http_allow = rg_state_get($db, 'http_allow');
$https_allow = rg_state_get($db, 'https_allow');
rg_base_url_build($hostname, $http_allow, $https_allow);
rg_log('DEBUG: base_url=' . rg_base_url());
$login_ui = array('uid' => 0,
'username' => 'anonymous user',
'organization' => 0);
if (isset($_SERVER['SSH_CONNECTION'])) {
rg_log("SSH connection: " . $_SERVER['SSH_CONNECTION']);
// we do not have host info
$host = '';
// first parameter must be uid of the user
$login_uid = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : 0;
rg_log("uid is $login_uid.");
// second parameter must be the ssh key id
$key_id = isset($_SERVER['argv'][2]) ? $_SERVER['argv'][2] : 0;
// TODO: because of build system, 0 may be valid.
//if ($key_id == 0)
// fatal("key_id not provided!");
rg_log("key_id is $key_id.");
// Third para is 'flags'
$flags = isset($_SERVER['argv'][3]) ? $_SERVER['argv'][3] : '';
rg_log('flags=' . $flags);
if (!isset($_SERVER['SSH_ORIGINAL_COMMAND']))
$cmd_repo = "";
else
$cmd_repo = trim($_SERVER['SSH_ORIGINAL_COMMAND']);
$ssh_client = getenv("SSH_CLIENT");
$_t = explode(" ", $ssh_client);
$ip = rg_fix_ip($_t[0]);
info('== Welcome to RocketGit! ==');
info('you are connecting from IP ' . $ip . '.');
info('date/time: ' . gmdate('Y-m-d H:i:s')
. ', debug id ' . $rg_log_sid . '.');
if (strstr($flags, 'N')) {
$login_ui = rg_user_info($db, $login_uid, '', '');
if ($login_ui['exists'] != 1)
fatal("User does not exists (conn).");
info('you are connecting as user \''
. $login_ui['username'] . '\'.');
putenv('ROCKETGIT_SHOW_INFO=0');
}
// Only normal keys can execute some commands
if (strstr($flags, 'N'))
$must_exit = rg_ssh_dispatch($db, $ip, $login_uid, $cmd_repo);
else
$must_exit = FALSE;
// Only normal keys can update stats
if (strstr($flags, 'N')) {
// We do this operation after dispatch to not impact the latency.
// TODO: This should be put in a queue for performance reasons
// At the same time, update stats? events?
$_r = rg_keys_update_use($db, $login_ui['uid'], $key_id, $ip,
$cmd_repo);
if ($_r !== TRUE)
rg_internal_error("Cannot update key last_use!");
}
if ($must_exit) {
rg_prof_end("remote.php");
rg_prof_log();
exit(0);
}
} else {
rg_log("git-daemon connection...");
// we have no client info
$login_uid = 0;
$key_id = 0;
$flags = '';
$line = @fread(STDIN, 8000);
$line_len = strlen($line);
rg_log("line=[$line]");
if ($line_len < 4)
fatal("Line is too short!");
$len = @hexdec(substr($line, 0, 4));
if ($line_len < $len)
fatal("Too less data ($line_len/$len) received!");
// parse something like: 002bgit-upload-pack /aa.git[0x00]host=localhost:9418[0x00]
$line = substr($line, 4); // skip length
$v = explode("\0", $line);
$cmd_repo = trim($v[0]);
$host_port = isset($v[1]) ? trim(substr($v[1], 5)) : '';
$v = explode(':', $host_port);
$host = $v[0];
$ip = rg_fix_ip(getenv("REMOTE_HOST"));
}
if (strncasecmp($cmd_repo, 'git-upload-pack', 15) == 0) {
$cmd = 'git-upload-pack';
} else if (strncasecmp($cmd_repo, 'git-receive-pack', 16) == 0) {
$cmd = 'git-receive-pack';
} else {
$cmd = $cmd_repo;
}
// extract repository name
$_t = substr($cmd_repo, strlen($cmd)); // skip cmd
$_t = trim($_t, "' ");
$_t = ltrim($_t, "/");
$_t = preg_replace('/\.git$/' , '', $_t);
$_t = explode("/", $_t);
if (strcmp($_t[0], "user") == 0) {
$prefix = "/user";
$user = isset($_t[1]) ? $_t[1] : "";
$repo = isset($_t[2]) ? $_t[2] : "";
} else {
$prefix = "";
$user = isset($_t[0]) ? $_t[0] : "";
$repo = isset($_t[1]) ? $_t[1] : "";
}
rg_log("host=[$host] cmd=[$cmd] prefix=[$prefix] user=[$user] repo=[$repo].");
if (strstr($flags, 'W')) {
// We are a worker, the command may be only for cloning!
if (strcmp($cmd, 'git-upload-pack') != 0)
fatal('A worker can only clone!');
}
$r = rg_repo_fetch_push_helper($db, $host, $ip, $login_ui, $prefix, $user,
$repo, $cmd);
rg_log_ml('DEBUG: repo_fetch_push_helper: ' . print_r($r, TRUE));
if (($r['ok'] !== 1) || ($r['allow'] !== 1))
fatal($r['errmsg']);
$run = "git-shell -c \"" . $cmd . " " . escapeshellarg($r['repo_path']) . "\"";
//$run = $cmd . ' ' . escapeshellarg($r['repo_path']);
rg_log("Running [$run]...");
rg_prof_start($cmd);
// TODO: shouldn't we use rg_exec to capture stderr?
passthru($run, $ret);
rg_prof_end($cmd);
rg_log("[$run] returned $ret.");
rg_prof_end("remote.php");
rg_prof_log();
?>