xaizek / rocketgit (License: AGPLv3+) (since 2018-12-09)
Light and fast Git hosting solution suitable to serve both as a hub or as a personal code storage with its tickets, pull requests, API and much more.
<root> / scripts / remote.php (d8c882245aa84263775fce6a84871e721bb69a4a) (3,878B) (mode 100644) [raw]
<?php
// This is called by a remote client that does push or fetch
error_reporting(E_ALL);
ini_set("track_errors", "On");

$_start = microtime(TRUE);

require_once("/etc/rocketgit/config.php");

$INC = dirname(__FILE__) . "/../inc";
require_once($INC . "/util.inc.php");
require_once($INC . "/log.inc.php");
require_once($INC . "/sql.inc.php");
require_once($INC . "/repo.inc.php");

rg_log_set_file("/var/log/rocketgit/remote.log");

function fatal($str)
{
	global $access_type;

	rg_log("Sending error: " . $str);
	$str2 = "RocketGit: FATAL ERROR: " . $str . "\n";
	if ($access_type == 2) { // git
		$str3 = "\n" . $str2;
		$len = strlen($str3) + 4;
		$str4 = sprintf("%04x", $len) . $str3;
		fwrite(STDERR, $str4);
	} else { // ssh
		fwrite(STDERR, $str2);
	}
	exit(1);
}

rg_log("Start...");
rg_log("_SERVER: " . print_r($_SERVER, TRUE));

umask(0022);

if (isset($_SERVER['SSH_CONNECTION'])) {
	rg_log("SSH connection: " . @$_SERVER['SSH_CONNECTION']);
	$access_type = 1;

	// we do not have host info
	$host = "";

	// first parameter must be uid of the user
	$uid = @$_SERVER['argv'][1];
	if (empty($uid))
		fatal("uid not provided!");
	rg_log("\tuid is $uid.");

	$cmd_repo = trim(@$_SERVER['SSH_ORIGINAL_COMMAND']);
	if (empty($cmd_repo))
		fatal("No SSH_ORIGINAL_COMMAND provided!");
} else {
	rg_log("git-daemon connection...");
	$access_type = 2;

	// we have no client info
	$uid = 0;

	$f = @fopen("php://stdin", "r");
	if ($f === FALSE)
		fatal("\tCannot open stdin!");
	$line = @fread($f, 8000);
	if ($line === FALSE)
		fatal("\tCannot read!");
	fclose($f);
	$line_len = strlen($line);

	if ($line_len < 4)
		fatal("\tLine 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
	$line = substr($line, 4);
	$v = explode("\0", $line);
	$cmd_repo = trim($v[0]);
	$host = trim(substr($v[1], 5));
}

// extract command and compute permissions
if (strncmp($cmd_repo, "git-upload-pack", 15) == 0) {
	$cmd = "git-upload-pack";
	$needed_rights = "F";
	$push = 0;
} else if (strncmp($cmd_repo, "git-receive-pack", 16) == 0) {
	$cmd = "git-receive-pack";
	$needed_rights = "";
	$push = 1;
} else {
	fatal("Unknown command [$cmd_repo]!");
}

// extract repository name
$repo = substr($cmd_repo, strlen($cmd));
$repo = trim($repo, "' ");
$repo = ltrim($repo, "/");
$repo = preg_replace('/\.git$/' , '', $repo);

rg_log("host=[$host] cmd=[$cmd] repo=[$repo].");

// validity/security checks
if (rg_repo_ok($repo) !== TRUE)
	fatal("Repo [$repo] is invalid (" . rg_repo_error() . ")");

$db = rg_sql_open($rg_sql);
if ($db === FALSE)
	fatal("Internal error (db)!");

// load info about the repository
$ri = rg_repo_info($db, 0, $repo);
if ($ri['ok'] != 1)
	fatal("Temporary error!");
if ($ri['exists'] != 1)
	fatal("Repo does not exists!");
if ($ri['deleted'] == 1)
	fatal("Repo was deleted!");

$ret = rg_repo_rights_get($db, $ri, $uid, 0);
if ($ret['ok'] !== 1)
	fatal("Internal error (rights_get)");
$rights = $ret['rights'];

if (rg_rights_allow($rights, $needed_rights) === FALSE)
	fatal("Repo does not exists.");

// TODO: limit per connection

// TODO: limit time and/or cpu

$repo_base = rg_repo_name2base($repo);
$repo_path = $repo_base . $repo . ".git";
rg_log("repo_path=$repo_path.");

if (($push == 1) && rg_repo_over_limit($ri))
	fatal("Cannot push: repo is over limit"
		. " (" . $ri['disk_used_mb']. "MiB >= "
		. $ri['disk_quota_mb'] . "MiB)");

// Put in environment all we need
putenv("ROCKETGIT_UID=" . $uid);
putenv("ROCKETGIT_RIGHTS=" . $rights);

$run = "git-shell -c \"" . $cmd . " '" . escapeshellcmd($repo_path) . "'\"";
rg_log("Running [$run]...");
passthru($run, $ret);
rg_log("[$run] returned $ret.");

$diff = sprintf("%u", (microtime(TRUE) - $_start) * 1000);
rg_log("Took " . $diff . "ms.");
?>
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/rocketgit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/rocketgit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master