<?php require_once($INC . "/util.inc.php"); require_once($INC . "/log.inc.php"); require_once($INC . "/sql.inc.php"); require_once($INC . "/user.inc.php"); require_once($INC . "/git.inc.php"); require_once($INC . "/rights.inc.php"); require_once($INC . "/prof.inc.php"); $rg_repo_error = ""; $rg_repo_rights = array( "A" => "Admin", "F" => "Fetch", "P" => "Push", "H" => "Anonymous push", "S" => "Create annotated tag", "N" => "Modify annotated tag", "n" => "Delete annotated tag", "Y" => "Create un-annotated tag", "U" => "Modify un-annotated tag", "u" => "Delete un-annotated tag", "C" => "Create branch", "D" => "Delete branch", "O" => "Non fast-forwards", "M" => "Merge commits", "W" => "Bad whitespace" ); // What rights are on by default $rg_repo_rights_default = "FMH"; rg_rights_register("repo", $rg_repo_rights); function rg_repo_set_error($str) { global $rg_repo_error; $rg_repo_error = $str; } function rg_repo_error() { global $rg_repo_error; return $rg_repo_error; } /* * Enforce name */ function rg_repo_ok($repo) { global $rg_repo_allow; global $rg_repo_min_len; global $rg_repo_max_len; if (empty($repo)) { rg_repo_set_error("Invalid repository name (empty)"); return FALSE; } if (rg_chars_allow($repo, $rg_repo_allow) === FALSE) { rg_repo_set_error("Invalid repository name (invalid chars)"); return FALSE; } if (preg_match('/\.\./', $repo) > 0) { rg_repo_set_error("Invalid repository name (..)"); return FALSE; } if (strlen($repo) < $rg_repo_min_len) { rg_repo_set_error("Repository name is too short" . " (minimum $rg_repo_min_len)."); return FALSE; } if (strlen($repo) > $rg_repo_max_len) { rg_repo_set_error("Repository name is too long" . " (maximum $rg_repo_max_len)."); return FALSE; } return TRUE; } /* * Returns the path to a repository based on name */ function rg_repo_name2base($rr) { return rg_user_name2path($rr) . "/repos/"; } /* * Return info about a repo * @param rr contains data about user and repo */ $rg_repo_info_cache = array(); function rg_repo_info($db, $rr) { global $rg_repo_info_cache; rg_prof_start("repo_info"); $key = implode("__", $rr); if (isset($rg_repo_info_cache[$key])) return $rg_repo_info_cache[$key]; rg_log("repo_info: rr: " . rg_array2string($rr)); $uid = isset($rr['uid']) ? $rr['uid'] : 0; $repo_id = isset($rr['repo_id']) ? $rr['repo_id'] : 0; $user = isset($rr['user']) ? $rr['user'] : ""; $repo = isset($rr['repo']) ? $rr['repo'] : ""; $ret['ok'] = 0; $ret['exists'] = 0; $rg_repo_info_cache[$key] = $ret; if (($uid == 0) && (!empty($user))) { $ui = rg_user_info($db, 0, $user, ""); if ($ui['ok'] != 1) { rg_repo_set_error("invalid repo path (user)"); return $ret; } $uid = $ui['uid']; } if ($repo_id > 0) { $add = " repo_id = $repo_id"; } else if (($uid > 0) && !empty($repo)) { $e_repo = rg_sql_escape($db, $repo); $add = " uid = " . $uid . " AND name = '$e_repo'"; } else { rg_repo_set_error("no repo_id or user/repo specified!"); return $ret; } $sql = "SELECT * FROM repos WHERE " . $add; $res = rg_sql_query($db, $sql); if ($res === FALSE) { rg_repo_set_error("cannot query (" . rg_sql_error() . ")"); return $ret; } $ret['ok'] = 1; $row = rg_sql_fetch_array($res); rg_sql_free_result($res); if (!isset($row['repo_id'])) { rg_log("\tRepo not found!"); return $ret; } $row['exists'] = 1; $row['ok'] = 1; $rg_repo_info_cache[$key] = $row; rg_prof_end("repo_info"); return $row; } /* * Check if a uid has access to repository */ function rg_repo_allow($db, $ri, $rg_ui, $needed_rights) { rg_prof_start("repo_allow"); rg_log("repo_allow: repo_id=" . $ri['repo_id'] . " rg_uid=" . $rg_ui['uid'] . ", needed_rights=$needed_rights..."); if ($rg_ui['is_admin'] == 1) { rg_log("\tUser is admin, allow!"); return TRUE; } if (empty($needed_rights)) { rg_log("\tNo perms passed!"); return FALSE; } // anonymous acess (git://...) if ($rg_ui['uid'] == 0) { $db_rights = $ri['default_rights']; } else { $rr = rg_repo_rights_get($db, $ri, $rg_ui['uid'], 0); if ($rr['ok'] != 1) { rg_repo_set_error("cannot get rights from db"); return FALSE; } $db_rights = $rr['rights']; } rg_log("\tdb rights: " . $db_rights); if (rg_rights_allow($db_rights, $needed_rights) !== TRUE) { rg_repo_set_error("no rights ($needed_rights) vs ($db_rights)"); return FALSE; } rg_log("\tAllow access!"); rg_prof_end("repo_allow"); return TRUE; } /* * Add a repository * @master - makes sense only for clones - who is the master. * TODO: put all fields into an array! */ function rg_repo_create($db, $master, $rg_ui, $name, $max_commit_size, $description, $rights, $max_users) { rg_prof_start("repo_create"); // TODO: reorder parameters - are not logical rg_log("repo_create: rg_uid=" . $rg_ui['uid'] . ", name=[$name], master=$master" . ", max_commit_size=$max_commit_size" . ", description=[$description]" . ", rights=$rights, max_users=$max_users..."); // TODO: test if user is allowed to add a repository $ret = FALSE; do { if (rg_repo_ok($name) === FALSE) break; // First, test if it already exists $rr = array("user" => $rg_ui['username'], "repo" => $name); $ri = rg_repo_info($db, $rr); if ($ri['ok'] != 1) break; if ($ri['exists'] == 1) { rg_repo_set_error("Repository already exists."); break; } $e_name = rg_sql_escape($db, $name); $e_description = rg_sql_escape($db, $description); $itime = time(); $sql = "INSERT INTO repos (uid, master, name" . ", itime, max_commit_size, description, git_dir_done" . ", default_rights, max_users)" . " VALUES (" . $rg_ui['uid'] . ", $master, '$e_name'" . ", $itime, $max_commit_size, '$e_description', 0" . ", '$rights', $max_users)" . " RETURNING repo_id"; $res = rg_sql_query($db, $sql); if ($res === FALSE) { rg_repo_set_error("Cannot insert (" . rg_sql_error() . ")"); break; } $row = rg_sql_fetch_array($res); $ret = $row['repo_id']; rg_sql_free_result($res); } while (0); // git repo creation will be delayed for serialization reasons // and for permission reasons (we are 'apache' user here) rg_prof_end("repo_create"); return $ret; } /* * Delete a repo */ function rg_repo_delete($db, $repo_id, $rg_ui) { rg_prof_start("repo_delete"); rg_log("repo_delete: rg_uid=" . $rg_ui['uid'] . ", repo_id=$repo_id"); // TODO: Check rights // Only mark it as such, deletion will happen in background $sql = "UPDATE repos SET deleted = 1 WHERE repo_id = $repo_id"; $res = rg_sql_query($db, $sql); if ($res === FALSE) { rg_repo_set_error("Cannot delete (" . rg_sql_error() . ")"); return FALSE; } rg_sql_free_result($res); rg_prof_end("repo_delete"); return TRUE; } /* * Update a repository * TODO: check rights - also for create? */ function rg_repo_update($db, $user, &$new) { rg_prof_start("repo_update"); rg_log("repo_update: user=" . $user . " repo_id=" . $new['repo_id'] . " name=[" . $new['name'] . "]" . " max_commit_size=" . $new['max_commit_size'] . " description=[" . $new['description'] . "]" . " default_rights=" . $new['default_rights'] . " max_users=" . $new['max_users']); if (rg_repo_ok($new['name']) !== TRUE) return FALSE; // First, test if it already exists $rr = array("user" => $user, "repo" => $new['name']); $ri = rg_repo_info($db, $rr); if ($ri['ok'] != 1) return FALSE; if (($ri['exists'] == 1) && ($ri['repo_id'] != $new['repo_id'])) { rg_repo_set_error("Name already taken."); return FALSE; } // Second, test if repo_id is valid $rr = array("repo_id" => $new['repo_id']); $ri = rg_repo_info($db, $rr); if ($ri['ok'] != 1) return FALSE; if ($ri['exists'] == 0) { rg_repo_set_error("Repo " . $new['repo_id'] . " does not exists."); return FALSE; } $e_name = rg_sql_escape($db, $new['name']); $e_description = rg_sql_escape($db, $new['description']); $sql = "UPDATE repos SET name = '$e_name'" . ", max_commit_size = " . $new['max_commit_size'] . ", description = '$e_description'" . ", default_rights = '" . $new['default_rights'] . "'" . ", max_users = " . $new['max_users'] . " WHERE repo_id = " . $new['repo_id']; $res = rg_sql_query($db, $sql); if ($res === FALSE) { rg_repo_set_error("Cannot update (" . rg_sql_error() . ")"); return FALSE; } rg_sql_free_result($res); rg_prof_end("repo_update"); return TRUE; } /* * List repositories */ function rg_repo_list_query($db, $url, $sql) { rg_prof_start("repo_list_query"); rg_log("repo_list_query: url=$url, sql=$sql..."); $res = rg_sql_query($db, $sql); if ($res === FALSE) { rg_repo_set_error("cannot list by query (" . rg_sql_error() . ")"); return FALSE; } $d = array(); while (($row = rg_sql_fetch_array($res))) { $_line = array(); foreach ($row as $k => $v) $_line[$k] = $v; $_ui = rg_user_info($db, $row['uid'], "", ""); if ($_ui['exists'] != 1) { rg_repo_set_error("user associated with this repo not found"); return FALSE; } $_line['owner'] = $_ui['username']; $_line['url_repo'] = rg_re_repopage($_ui, $row['name']); $_line['url_user'] = rg_re_userpage($_ui); $_line['HTML:description'] = nl2br($row['description']); $master_repo = "-"; if ($row['master'] > 0) { $master_repo = "?"; $rr = array("repo_id" => $row['master']); $_mi = rg_repo_info($db, $rr); if ($_mi['exists'] = 1) $master_repo = $_mi['name']; } $_line['clone_of'] = $master_repo; $_line['creation'] = gmdate("Y-m-d", $row['itime']); // rights $_line['rights'] = implode(", ", rg_rights_text("repo", $row['default_rights'])); $_max = "unlimited"; if ($row['disk_quota_mb'] > 0) $_max = rg_1024($row['disk_quota_mb'] * 1024 * 1024); $_line['disk_used'] = $row['disk_used_mb'] . "/" . $_max; $_line['max_commit_size'] = "unlimited"; if ($row['max_commit_size'] > 0) $_line['max_commit_size'] = rg_1024($row['max_commit_size']); $_line['max_users'] = "unlimited"; if ($row['max_users'] > 0) $_line['max_users'] = $row['max_users']; $d[] = $_line; } rg_sql_free_result($res); rg_prof_end("repo_list_query"); return rg_template_table("repo/list", $d, array()); } /* * */ function rg_repo_list($db, $url, $rg_ui) { rg_log("repo_list: url=$url, rg_uid=" . $rg_ui['uid']); $add = ""; if ($rg_ui['uid'] > 0) $add = " AND uid = " . $rg_ui['uid']; $sql = "SELECT * FROM repos" . " WHERE deleted = 0" . $add . " ORDER BY name"; return rg_repo_list_query($db, $url, $sql); } /* * */ function rg_repo_search($db, $q, $masters) { rg_log("repo_search: q=$q, masters=$masters..."); $add = ""; if ($masters == 1) $add = " AND master = 0"; $e_q = rg_sql_escape($db, $q); $sql = "SELECT * FROM repos" . " WHERE deleted = 0" . " AND name ILIKE '%$e_q%'" . $add . " ORDER BY name" . " LIMIT 10"; return rg_repo_list_query($db, "", $sql); } /* * Computes the size of a repository */ function rg_repo_disk_mb($path) { rg_log("repo_disk_mb: path=$path..."); // TODO return 10; } /* * Mark a git repo as done */ function rg_repo_git_done($db, $repo_id) { rg_prof_start("repo_git_done"); rg_log("repo_git_done: repo_id=$repo_id..."); $sql = "UPDATE repos SET git_dir_done = 1" . " WHERE repo_id = $repo_id"; $res = rg_sql_query($db, $sql); if ($res === FALSE) { rg_repo_set_error("Cannot query (" . rg_sql_error() . ")"); return FALSE; } rg_sql_free_result($res); rg_prof_end("repo_git_done"); return TRUE; } /* * Get rights for a user */ function rg_repo_rights_get($db, $ri, $uid, $flags) { rg_prof_start("repo_rights_get"); rg_log("rg_repo_rights_get: repo_id=" . $ri['repo_id'] . ", uid=$uid" . " flags=$flags..."); $ret = array(); $ret['ok'] = 0; $ret['rights'] = ""; $repo_id = $ri['repo_id']; // Give all rights to owner if ($ri['uid'] == $uid) { rg_log("\tuid $uid is the owner."); $rights = rg_rights_all("repo"); if (($flags & RG_RIGHTS_FILL_EXISTS) == 0) { rg_log("\tNo need to fill 'exists' field. Return."); $ret['rights'] = $rights; $ret['ok'] = 1; return $ret; } } else { rg_log("\tuid $uid is NOT the owner (" . $ri['uid'] . ");" . " assign default rights."); $rights = $ri['default_rights']; } $r = rg_rights_get($db, "repo", $repo_id, $uid); if ($r['ok'] !== 1) { rg_repo_set_error("cannot get rights (" . rg_rights_error() . ")!"); return FALSE; } $ret['rights'] = rg_rights_combine($rights, $r['rights']); rg_log("\tFinal rights($rights + " . $r['rights'] . ")=" . $ret['rights']); $ret['ok'] = 1; rg_prof_end("repo_rights_get"); return $ret; } /* * Add rights for a repo */ function rg_repo_rights_set($db, $ri, $uid, $rights) { if (!isset($ri['repo_id'])) { rg_internal_error("repo_id is not defined!"); return FALSE; } rg_log("rg_repo_rights_set: repo_id=" . $ri['repo_id'] . ", uid=$uid, rights=$rights..."); $r = rg_rights_set($db, "repo", $ri['repo_id'], $uid, $rights); if ($r !== TRUE) { rg_repo_set_error("cannot alter rights (" . rg_rights_error() . ")!"); return FALSE; } return TRUE; } /* * List rights for a repo */ function rg_repo_rights_list($db, $ri, $url) { rg_log("rg_repo_rights_list: repo_id=" . $ri['repo_id'] . " url=$url"); $r = rg_rights_list($db, "repo", $ri['repo_id'], $url); if ($r === FALSE) { rg_repo_set_error("Cannot list rights (" . rg_rights_error() . ")"); return FALSE; } return $r; } /* * Returns TRUE if a repo is over limit */ function rg_repo_over_limit($ri) { if ($ri['disk_quota_mb'] == 0) return FALSE; if ($ri['disk_used_mb'] >= $ri['disk_quota_mb']) return TRUE; return FALSE; } /* * Add in queue a statistic file */ function rg_repo_stats_push2file($a) { global $php_errormsg; global $rg_state_dir; $q = $rg_state_dir . "/qstats"; if (!is_dir($q)) { $r = @mkdir($q, 0700); if ($r !== TRUE) { rg_internal_error("Cannot create dir $q ($php_errormsg)!"); return FALSE; } } $buf = serialize($a); $file = sha1($buf); $r = file_put_contents($q . "/" . $file, $buf); if ($r === FALSE) { rg_internal_error("Cannot store file in qstats ($php_errormsg)!"); return FALSE; } return $file; } ?>