<?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"); require_once($INC . "/mail.inc.php"); require_once($INC . "/events.inc.php"); require_once($INC . "/webhooks.inc.php"); $rg_repo_refs_rights = array( "F" => "Fetch", "P" => "Push", "H" => "Anonymous push", "S" => "Create 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" ); $rg_repo_path_rights = array( "P" => "Push", "W" => "Bad whitespace" ); $rg_repo_rights = array( "A" => "Access repo", "E" => "Create/edit repo", "D" => "Delete repo", 'K' => 'Lock repo', "G" => "Grant rights", "a" => "Access bug tracker", "B" => "Add bugs", "r" => "Reopen bugs", "d" => "Delete bugs", "C" => "Close bugs", 'W' => 'Manage hooks' ); // TODO: default rights should go into conf file? // TODO: better move all config to database (modulo db conn info)? rg_rights_register("repo_refs", $rg_repo_refs_rights, "FMH", "rg_repo_compare_refs", "rg_repo_rights_inject"); rg_rights_register("repo_path", $rg_repo_path_rights, "P", "rg_repo_compare_paths", "rg_repo_rights_inject"); rg_rights_register("repo", $rg_repo_rights, "AaB", FALSE, "rg_repo_rights_inject"); /* * Returns info about a repo * If you want to lookup by repo_id or uid/repo_name */ function rg_repo_info($db, $repo_id, $uid, $repo_name) { rg_prof_start("repo_info"); rg_log_enter("repo_info: repo_id=$repo_id uid=$uid repo_name=$repo_name."); $ret['ok'] = 0; $ret['exists'] = 0; while(1) { $params = array("uid" => $uid, "repo_id" => $repo_id, "repo_name" => $repo_name); if ($repo_id > 0) { $c = rg_cache_get('repo_by_id' . '::' . $repo_id); if (($c !== FALSE) && isset($c['repo_id'])) { $ret = $c; rg_repo_cosmetic($db, $ret); break; } $sql = "SELECT * FROM repos WHERE repo_id = @@repo_id@@"; } else if (!empty($repo_name)) { $x_repo_id = rg_cache_get("repo_by_name::$uid::$repo_name"); if ($x_repo_id !== FALSE) { $ret = rg_repo_info($db, $x_repo_id, $uid, ""); break; } $sql = "SELECT * FROM repos WHERE uid = @@uid@@" . " AND name = @@repo_name@@"; } else { rg_repo_set_error("no repo_id or user/repo specified!"); break; } $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_repo_set_error("cannot query (" . rg_sql_error() . ")"); break; } $rows = rg_sql_num_rows($res); if ($rows > 0) $ret = rg_sql_fetch_array($res); rg_sql_free_result($res); if (($rows == 0) && ($repo_id == 0)) { // Repo not found, maybe it was renamed $_repo_id = rg_repo_lookup_by_old_name($db, $uid, $repo_name); if ($_repo_id === FALSE) break; if ($_repo_id > 0) { $ret = rg_repo_info($db, $_repo_id, 0, ""); break; } } $ret['ok'] = 1; if ($rows > 0) { $ret['exists'] = 1; } else { $ret['exists'] = 0; } //rg_log_ml("CHECK: ret=" . print_r($ret, TRUE)); if ($ret['exists'] == 1) { rg_cache_set('repo_by_id' . '::' . $ret['repo_id'], $ret, RG_SOCKET_NO_WAIT); rg_cache_set("repo_by_name::$uid::" . $ret['name'], $ret['repo_id'], RG_SOCKET_NO_WAIT); } if ($rows > 0) rg_repo_cosmetic($db, $ret); break; } rg_log_exit(); rg_prof_end("repo_info"); return $ret; } /* * Function used to inject rights for a obj_id/type combination * It will be called from rg_rights_get * @out - the output array where we want to put rights */ function rg_repo_rights_inject($db, $obj_id, $type, $owner, $uid) { rg_log_enter("repo_rights_inject: obj_id=$obj_id type=$type" . " owner=$owner uid=$uid"); $ret = array(); while (1) { $a = array(); $a['type'] = $type; $a['obj_id'] = $obj_id; $a['uid'] = 0; // TODO: not clear here what to put! $a['itime'] = 0; $a['misc'] = ""; $a['prio'] = 0; $a['who'] = $owner; // TODO: not clear if correct/good $a['right_id'] = 0; $a['ip'] = ""; $a['can_be_deleted'] = 0; $a['description'] = "Autogenerated"; if ($uid > 0) { $ui = rg_user_info($db, $uid, "", ""); if ($ui['exists'] != 1) break; if ($ui['is_admin'] == 1) { $a['uid'] = $owner; $a['rights'] = rg_rights_all($type); $a['description'] = 'Autogenerated (you are admin)'; $ret[] = $a; } } $ri = rg_repo_info($db, $obj_id, 0, ""); if ($ri['exists'] != 1) { rg_log("repo $obj_id does not exists"); break; } // Here, rights to inject, even if repo is not public if (strcmp($type, "repo_path") == 0) { $a['uid'] = 0; $a['prio'] = 30001; $a['rights'] = "PW"; // push + whitespace $a['description'] = 'Autogenerated (sane default)'; $ret[] = $a; } // No rights to inject if is not public if ($ri['public'] != 1) break; if (strcmp($type, "repo") == 0) { $a['uid'] = 0; $a['rights'] = "AaB"; // access repo + access bug tracker + add bugs $a['rights'] = "Aa"; // access repo + access bug tracker + add bugs $a['description'] = 'Autogenerated (repo is public)'; $ret[] = $a; } else if (strcmp($type, "repo_refs") == 0) { $a['uid'] = 0; $a['prio'] = 1; $a['rights'] = "F"; // Fetch $a['description'] = 'Autogenerated (repo is public)'; $ret[] = $a; $a['uid'] = 0; $a['prio'] = 30001; $a['rights'] = "H"; // push + whitespace $a['description'] = 'Autogenerated (sane default)'; $ret[] = $a; } else if (strcmp($type, "repo_path") == 0) { } else { rg_log("type $type not used. bug?"); break; } break; } rg_log_exit(); return $ret; } /* * Returns TRUE if the login user has @rights rights */ function rg_repo_has_rights($db, $rg, $rights) { $x = array(); $x['obj_id'] = $rg['ri']['repo_id']; $x['type'] = 'repo'; $x['owner'] = $rg['ri']['uid']; $x['uid'] = $rg['login_ui']['uid']; $x['username'] = $rg['login_ui']['username']; $x['needed_rights'] = $rights; $x['ip'] = $rg['ip']; $x['misc'] = ''; return rg_rights_allow($db, $x); } /* * Bring a ref to a canonical format (refs/x/name) * TODO: move to git.inc? */ function rg_repo_ref_canon($ref) { if (strncmp($ref, "refs/", 5) == 0) { // do nothing } else if (strncmp($ref, "/refs/", 6) == 0) { $ref = substr($ref, 1); } else { $ref = "refs/heads/" . $ref; } return $ref; } /* * Compare function for refs */ function rg_repo_compare_refs($misc, $ref) { rg_prof_start("repo_compare_refs"); $misc = rg_repo_ref_canon($misc); $ref = rg_repo_ref_canon($ref); $qmisc = preg_quote($misc, '/'); $ret = preg_match('/^' . $qmisc . '/uD', $ref); rg_log("repo_compare_refs: misc=$misc ref=$ref => " . ($ret === 1 ? "match" : "no match")); rg_prof_end("repo_compare_refs"); return $ret === 1; } /* * Compare function for paths */ function rg_repo_compare_paths($misc, $path) { rg_prof_start("repo_compare_paths"); $qmisc = preg_quote($misc, '/'); $ret = preg_match('/' . $qmisc . '/uD', $path); rg_log("repo_compare_paths: misc=$misc path=$path => " . ($ret === 1 ? "T" : "F")); rg_prof_end("repo_compare_paths"); return $ret === 1; } /* * Remove "refs/heads/" when showing rights on web * TODO: don't know where to call it. Maybe in rg_rights_load? * TODO: also, register this kind of function. */ function rg_repo_ref_nice($ref) { if (strncmp($ref, "refs/heads/", 11) == 0) $ref = substr($ref, 11); return $ref; } // Repo history categories define('REPO_CAT_CREATE', 1); define('REPO_CAT_CLONED', 2); define('REPO_CAT_PUSH', 3); define('REPO_CAT_RENAME', 4); define('REPO_CAT_UPDATE', 5); define('REPO_CAT_BUG_ADDED', 10); define('REPO_CAT_BUG_CLOSED', 11); define('REPO_CAT_GIT_ATAG_CREATE', 20); define('REPO_CAT_GIT_ATAG_DELETE', 21); define('REPO_CAT_GIT_ATAG_UPDATE', 22); define('REPO_CAT_GIT_UTAG_CREATE', 30); define('REPO_CAT_GIT_UTAG_DELETE', 31); define('REPO_CAT_GIT_UTAG_UPDATE', 32); define('REPO_CAT_GIT_BRANCH_DELETE', 40); define('REPO_CAT_GIT_BRANCH_UPDATE', 41); define('REPO_CAT_GIT_BRANCH_CREATE', 42); define('REPO_CAT_GIT_BRANCH_ANON_PUSH', 43); define('REPO_CAT_LOCK', 50); define('REPO_CAT_UNLOCK', 51); $rg_repo_error = ""; function rg_repo_set_error($str) { global $rg_repo_error; $rg_repo_error = $str; rg_log($str); } function rg_repo_error() { global $rg_repo_error; return $rg_repo_error; } /* * Events functions */ $rg_repo_functions = array( 3000 => "rg_repo_event_new", 3001 => "rg_repo_event_del", 3002 => "rg_repo_event_update", 3003 => "rg_repo_event_notify_user", 3004 => "rg_repo_event_symlink_by_name", 3005 => "rg_repo_event_storage_create", 3006 => "rg_repo_history_insert" ); rg_event_register_functions($rg_repo_functions); /* * Event for adding a new repo */ function rg_repo_event_new($db, $event) { $ret = array(); $event['op'] = "new"; // Create git dir $x = $event; $x['category'] = 3005; $x['prio'] = 20; $x['notification'] .= "-git"; $ret[] = $x; // make symlink by name $x = $event; $x['category'] = 3004; $x['prio'] = 200; $x['notification'] .= "-symlink"; $ret[] = $x; // notify user $x = $event; $x['category'] = 3003; $x['prio'] = 100; $x['notification'] .= "-notify"; $ret[] = $x; // webhook $x = $event; $x['category'] = 10000; $x['prio'] = 50; $x['wh_event'] = 'C'; // see rg_wh_events array $ri = &$event['ri']; $x['wh_data'] = array( 'name' => $ri['name'], 'public' => $ri['public'], 'description' => $ri['description'], 'itime' => $ri['itime'], 'license' => $ri['license'] ); $ret[] = $x; // add a history entry $x = $event; $x['category'] = 3006; $x['prio'] = 50; $ret[] = $x; // TODO: notify watchers of the user return $ret; } /* * Event for deleting repo */ function rg_repo_event_del($db, $event) { $ret = array(); $event['op'] = "del"; // notify user $ret[] = array_merge($event, array("category" => 3003, "prio" => 100)); // TODO: notify watchers return $ret; } /* * Make a symlink by name (by_name/name -> ../by_id/xx/xx/xx/xx/xxxxxxxx.git) * TODO: why return may be an array?! */ function rg_repo_event_symlink_by_name($db, $e) { global $php_errormsg; rg_prof_start("repo_event_symlink_by_name"); $id_path = rg_repo_path_by_id($e['ui']['uid'], $e['ri']['repo_id']); $id_path_rel = rg_repo_path_by_id_rel($e['ui']['uid'], $e['ri']['repo_id']); $new_path = rg_repo_path_by_name($e['ui']['uid'], $e['ri']['name']); $ret = FALSE; while (1) { // Check if we already did the rename if (file_exists($new_path)) { if (!is_link($new_path)) { rg_internal_error("$new_path is not a link"); break; } $v = readlink($new_path); if ($v === FALSE) { rg_internal_error("cannot read link $new_path"); break; } rg_log("new_path points to [$v]"); if (strcmp($id_path_rel, $v) == 0) { // Link already done $ret = array(); break; } // Seems that new_path points to other place $r = rename($new_path, $new_path . ".BOGUS." . time()); if ($r !== TRUE) { rg_internal_error("cannot rename bogus"); break; } } // Create links' parent $new_path_parent = dirname($new_path); if (!is_dir($new_path_parent)) { $r = mkdir($new_path_parent, 0755, TRUE); if ($r === FALSE) { rg_repo_set_error("cannot create links' parent"); break; } } // Now, the new name is free, do the link $r = symlink($id_path_rel, $new_path); if ($r !== TRUE) { rg_internal_error("cannot symlink $id_path -> $new_path ($php_errormsg)!"); break; } $ret = array(); break; } rg_prof_end("repo_event_symlink_by_name"); return $ret; } /* * Creates git dir storage */ function rg_repo_event_storage_create($db, $e) { rg_prof_start("repo_event_storage_create"); rg_log_enter("repo_event_storage_create: e=" . rg_array2string($e)); $ret = FALSE; while (1) { $by_id_path = rg_repo_path_by_id($e['ui']['uid'], $e['ri']['repo_id']); if (!is_dir($by_id_path)) { if (mkdir($by_id_path, 0755, TRUE) === FALSE) { rg_repo_set_error("could not create folder $dst"); break; } } if ($e['ri']['master'] == 0) { $r = rg_git_init($by_id_path); if ($r === FALSE) { rg_repo_set_error("cannot init master" . " (" . rg_git_error() . ")"); break; } } else { $mi = rg_repo_info($db, $e['ri']['master'], 0, ""); if ($mi['exists'] != 1) { rg_repo_set_error("cannot find master" . " (" . rg_repo_error() . ")"); break; } $master_by_id_path = rg_repo_path_by_id($mi['uid'], $mi['repo_id']); $r = rg_git_clone($master_by_id_path, $by_id_path); if ($r === FALSE) { rg_repo_set_error("could not create repo" . " (" . rg_git_error() . ")"); break; } } $r = rg_repo_event_symlink_by_name($db, $e); if ($r === FALSE) break; $r = rg_repo_git_done($db, $e['ri']['repo_id']); if ($r !== TRUE) break; $ret = array(); break; } rg_log_exit(); rg_prof_end("repo_event_storage_create"); return $ret; } /* * Event for renaming a repo */ function rg_repo_event_update($db, $event) { $ret = array(); $event['op'] = "update"; // make symlink by name $ret[] = array_merge($event, array("category" => 3004, "prio" => 200)); // notify user $ret[] = array_merge($event, array("category" => 3003, "prio" => 100)); // TODO: notify watchers return $ret; } /* * User notification */ function rg_repo_event_notify_user($db, $event) { rg_prof_start("repo_event_notify_user"); $ret = FALSE; while (1) { $r = rg_mail_template("mail/user/repo/" . $event['op'], $event); if ($r === FALSE) break; $ret = array(); break; } rg_prof_end("repo_event_notify_user"); return $ret; } /* * Inserts an event into repo_history table */ function rg_repo_history_insert($db, $event) { rg_prof_start("repo_history_insert"); rg_log_enter("repo_history_insert: event=" . rg_array2string($event)); if (!isset($event['ui']['uid'])) $event['ui']['uid'] = 0; $ret = FALSE; while (1) { $now = time(); $params = array("now" => $now, "repo_id" => $event['ri']['repo_id'], "uid" => $event['ui']['uid'], "cat" => $event['history_category'], "mess" => $event['history_message']); $sql = "INSERT INTO repo_history_" . gmdate("Y_m", $now) . " (itime, uid, repo_id, category, message)" . " VALUES (@@now@@, @@uid@@, @@repo_id@@, @@cat@@, @@mess@@)"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) break; rg_sql_free_result($res); $ret = array(); break; } rg_log_exit(); rg_prof_end("repo_history_insert"); return $ret; } /* * Returns last events from repo_history * @category: 0 for any * @number: number of entries * @max_seconds: limit the search to less than max_seconds in the past */ function rg_repo_history_load($db, $repo_id, $category, $number, $max_seconds) { rg_prof_start("repo_history_load"); rg_log_enter("repo_history_load: repo_id=$repo_id, category=$category" . ", number=$number max_seconds=$max_seconds"); $ret = FALSE; while (1) { $now = time(); $category_sql = ""; if ($category > 0) $category_sql = " AND category = " . $category; $limit_sql = ""; if ($number > 0) $limit_sql = " LIMIT " . $number; else $limit_sql = " LIMIT 30"; $time_sql = ""; if ($max_seconds > 0) $time_sql = " AND itime >= " . ($now - $max_seconds); $sql = "SELECT * FROM repo_history" . " WHERE repo_id = $repo_id" . $category_sql . $time_sql . " ORDER BY itime DESC" . $limit_sql; $res = rg_sql_query($db, $sql); if ($res === FALSE) break; $ret = array(); while (($row = rg_sql_fetch_array($res))) { $row['username'] = 'n/a'; if ($row['uid'] > 0) { $ui = rg_user_info($db, $row['uid'], '', ''); if ($ui['exists'] == 1) $row['username'] = $ui['username']; } if ($row['itime'] == 0) $row['itime_text'] = "N/A"; else $row['itime_text'] = gmdate("Y-m-d H:i", $row['itime']); $ret[] = $row; } rg_sql_free_result($res); break; } rg_log_exit(); rg_prof_end("repo_history_load"); return $ret; } /* * 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, $invalid) !== TRUE) { rg_repo_set_error("invalid repository name" . " (invalid chars: '$invalid')"); return FALSE; } if (preg_match('/\.\./', $repo) !== 0) { rg_repo_set_error("invalid repository name (..)"); return FALSE; } $len = strlen($repo); if ($len < $rg_repo_min_len) { rg_repo_set_error("repository name is too short" . " (minimum $rg_repo_min_len < $len)"); return FALSE; } if ($len > $rg_repo_max_len) { rg_repo_set_error("repository name is too long" . " (maximum $rg_repo_max_len > $len)"); return FALSE; } return TRUE; } /* * Returns the relative path to a repository based on id */ function rg_repo_path_by_id_rel($uid, $repo_id) { return "../by_id/" . $repo_id . ".git"; } /* * Returns the path to a repository based on id */ function rg_repo_path_by_id($uid, $repo_id) { return rg_user_path_by_id($uid) . "/repos/by_id/" . $repo_id . ".git"; } /* * Returns the path to a repository based on name */ function rg_repo_path_by_name($uid, $repo_name) { return rg_user_path_by_id($uid) . "/repos/by_name/" . $repo_name . ".git"; } /* * Improve repo info */ function rg_repo_cosmetic($db, &$row) { if (strlen(substr($row['description'], 0, 1)) == 1) { $_a = rg_xss_safe(trim($row['description'])); $row['HTML:description_nlbr'] = nl2br($_a); } else { $row['HTML:description_nlbr'] = 'n/a'; } if (isset($row['itime'])) $row['HTML:itime_nice'] = gmdate('Y-m-d', $row['itime']); $_ui = rg_user_info($db, $row['uid'], '', ''); if ($_ui['exists'] == 1) { $row['owner'] = $_ui['username']; $row['url_user'] = rg_base_url() . rg_re_userpage($_ui); $row['url_repo'] = rg_base_url() . rg_re_repopage($_ui, $row['name']); } $master_repo = '-'; if ($row['master'] > 0) { $master_repo = '?'; $_mi = rg_repo_info($db, $row['master'], 0, ''); if ($_mi['exists'] = 1) $master_repo = $_mi['name']; } $row['clone_of'] = $master_repo; $row['creation'] = gmdate("Y-m-d", $row['itime']); $row['disk_used'] = rg_1024($row['disk_used_mb'] * 1024 * 1024); } /* * Delete a repo */ function rg_repo_delete($db, $repo_id, $ui) { rg_prof_start("repo_delete"); rg_log_enter("repo_delete: uid=" . $ui['uid'] . ", repo_id=$repo_id"); $ret = FALSE; while (1) { // TODO: Transaction? $ri = rg_repo_info($db, $repo_id, 0, ""); if ($ri['ok'] != 1) break; if ($ri['exists'] != 1) { rg_repo_set_error("Repository does not exists."); break; } // Only mark it as such, deletion will happen in background $params = array("repo_id" => $repo_id); $sql = "UPDATE repos SET deleted = 1" . " WHERE repo_id = @@repo_id@@"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_repo_set_error("Cannot delete (" . rg_sql_error() . ")"); break; } rg_sql_free_result($res); $event = array("category" => 3001, "prio" => 50, 'ui' => array( 'email' => $ui['confirmed'] > 0 ? $ui['email'] : "", 'uid' => $ui['uid']), 'ri' => array( 'name' => $ri['name'], 'repo_id' => $repo_id ) ); $r = rg_event_add($db, $event); if ($r !== TRUE) { rg_repo_set_error("cannot add event" . " (" . rg_event_error() . ")"); break; } rg_event_signal_daemon("", 0); rg_cache_unset('repo_by_id::' . $repo_id, RG_SOCKET_NO_WAIT); rg_cache_unset('repo_by_name::' . $ui['uid'] . '::' . $ri['name'], RG_SOCKET_NO_WAIT); rg_cache_unset('user' . '::' . $ui['uid'] . '::' . 'has_repos', RG_SOCKET_NO_WAIT); $ret = TRUE; break; } rg_log_exit(); rg_prof_end("repo_delete"); return $ret; } /* * Lookup in db the old name, so we can redirect the user to the new name */ function rg_repo_lookup_by_old_name($db, $uid, $old_name) { rg_prof_start("repo_lookup_by_old_name"); rg_log_enter("repo_lookup_by_old_name: uid=$uid old_name=$old_name"); $ret = FALSE; while (1) { $c = rg_cache_get("repo_by_old_name::$uid::$old_name"); if ($c !== FALSE) { $ret = $c; break; } $params = array("uid" => $uid, "old_name" => $old_name); $sql = "SELECT repo_id FROM repos_renames" . " WHERE uid = @@uid@@" . " AND old_name = @@old_name@@"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_repo_set_error("cannot lookup old name (" . rg_sql_error() . ")"); break; } $rows = rg_sql_num_rows($res); if ($rows > 0) $row = rg_sql_fetch_array($res); rg_sql_free_result($res); if ($rows == 0) $ret = 0; else $ret = $row['repo_id']; rg_cache_set("repo_by_old_name::$uid::$old_name", $ret, RG_SOCKET_NO_WAIT); break; } rg_log_exit(); rg_prof_end("repo_lookup_by_old_name"); return $ret; } /* * Add a rename to the database */ function rg_repo_insert_rename($db, $uid, $repo_id, $old_name) { rg_prof_start("repo_insert_rename"); rg_log_enter("repo_insert_rename: uid=$uid repo_id=$repo_id old_name=$old_name"); $ret = FALSE; while (1) { // Check if already exists in the database $r = rg_repo_lookup_by_old_name($db, $uid, $old_name); if ($r === FALSE) break; $params = array("repo_id" => $repo_id, "uid" => $uid, "old_name" => $old_name, "now" => time()); if ($r > 0) { $sql = "UPDATE repos_renames" . " SET repo_id = @@repo_id@@" . " WHERE uid = @@uid@@" . " AND old_name = @@old_name@@"; } else { $sql = "INSERT INTO repos_renames (uid, old_name" . ", repo_id, itime)" . " VALUES (@@uid@@, @@old_name@@, @@repo_id@@" . ", @@now@@)"; } $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_repo_set_error("cannot link with old_name in db" . " (" . rg_sql_error() . ")"); break; } rg_cache_set("repo_by_name::$uid::$old_name", $repo_id, RG_SOCKET_NO_WAIT); $ret = TRUE; break; } rg_log_exit(); rg_prof_end("repo_insert_rename"); return $ret; } /* * Creates/updates a repository * @login_ui - info of the user doing the update. * TODO: Warning, it may not be the owner. * TODO: where do we validate if the user has enough public/private slots? */ function rg_repo_edit($db, $login_ui, &$new) { rg_prof_start("repo_edit"); rg_log_enter("repo_edit: login_uid=" . $login_ui['uid'] . " new=" . rg_array2string($new)); // TODO: test if user is allowed to add a repository // TODO: test if user did not cross the limit for number of repos $ret = FALSE; while (1) { if (rg_repo_ok($new['name']) !== TRUE) break; if ($new['repo_id'] == 0) { // Check if name is already taken $ri = rg_repo_info($db, 0, $login_ui['uid'], $new['name']); if ($ri['ok'] != 1) break; if ($ri['exists'] == 1) { rg_repo_set_error('name already taken;' . ' choose a different one'); break; } } else { // Test if repo_id is valid // TODO: also here we must test if we have a dup name! $ri = rg_repo_info($db, $new['repo_id'], $login_ui['uid'], ""); if ($ri['ok'] != 1) break; if ($ri['exists'] != 1) { rg_repo_set_error('repo ' . $new['repo_id'] . ' does not exists.'); break; } } $renamed = 0; if ($new['repo_id'] > 0) { // Check if the user renamed the repo if (strcmp($new['name'], $ri['name']) != 0) { $renamed = 1; $r = rg_repo_insert_rename($db, $login_ui['uid'], $new['repo_id'], $ri['name']); if ($r !== TRUE) break; } } //TODO: master may be not accessible to this user. check. // Small fixes $new['description'] = trim($new['description']); $new['itime'] = time(); $new['uid'] = $login_ui['uid']; if ($new['repo_id'] == 0) { $new['deleted'] = 0; $new['disk_used_mb'] = 0; $new['git_dir_done'] = 0; $new['last_bug_id'] = 0; $sql = "INSERT INTO repos (uid, master, name" . ", itime, max_commit_size, description" . ", git_dir_done, public, license)" . " VALUES (@@uid@@, @@master@@, @@name@@" . ", @@itime@@, @@max_commit_size@@" . ", @@description@@, 0, @@public@@" . ", @@license@@)" . " RETURNING repo_id"; } else { $sql = "UPDATE repos SET name = @@name@@" . ", max_commit_size = @@max_commit_size@@" . ", description = @@description@@" . ", public = @@public@@" . ", license = @@license@@" . " WHERE repo_id = @@repo_id@@"; } $res = rg_sql_query_params($db, $sql, $new); if ($res === FALSE) { rg_repo_set_error("cannot update: " . rg_sql_error()); break; } if ($new['repo_id'] == 0) { $row = rg_sql_fetch_array($res); if ($row === FALSE) { rg_repo_set_error('cannot fetch row'); break; } } rg_sql_free_result($res); if ($new['repo_id'] == 0) { $cat = 3000; $hcat = REPO_CAT_CREATE; $hmess = "Repository has been created"; $notification = "repo_create-" . $login_ui['uid'] . "-" . $row['repo_id']; $old_description = ""; $new['repo_id'] = $row['repo_id']; } else { $cat = 3002; $hcat = REPO_CAT_UPDATE; $hmess = "Repository has been updated"; $notification = ""; $old_description = $ri['description']; // TODO: we should log what changed } $event = array('category' => $cat, 'prio' => 50, 'notification' => $notification, 'ui' => array( 'uid' => $login_ui['uid'], 'email' => $login_ui['confirmed'] > 0 ? $login_ui['email'] : "" ), 'history_category' => $hcat, 'history_message' => $hmess); $event = rg_array_merge($event, 'ri_old', $ri); $new['url'] = rg_base_url() . rg_re_repopage($login_ui, $new['name']); $event = rg_array_merge($event, 'ri', $new); $event['ri_old']['description_md5'] = md5($old_description); $event['ri']['description_md5'] = md5($new['description']); $r = rg_event_add($db, $event); if ($r !== TRUE) { rg_repo_set_error("cannot add event" . " (" . rg_event_error() . ")"); break; } $new['ok'] = 1; $new['exists'] = 1; rg_cache_merge("repo_by_id::" . $new['repo_id'], $new, RG_SOCKET_NO_WAIT); rg_cache_set("repo_by_name::" . $login_ui['uid'] . "::" . $new['name'], $new['repo_id'], RG_SOCKET_NO_WAIT); rg_event_signal_daemon('', 0); $ret = TRUE; break; } rg_log_exit(); rg_prof_end("repo_edit"); return $ret; } /* * Returns 1 if user has at least one repo * Used to go directly to 'create' page instead to 'list' */ function rg_repo_have($db, $uid) { rg_prof_start('repo_have'); $ret = 1; while (1) { $key = 'user' . '::' . $uid . '::' . 'has_repos'; $c = rg_cache_get($key); if ($c !== FALSE) { $ret = $c; break; } $params = array('uid' => $uid); $sql = 'SELECT 1 FROM repos WHERE uid = @@uid@@ LIMIT 1'; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) break; $rows = rg_sql_num_rows($res); rg_sql_free_result($res); $ret = $rows; rg_cache_set($key, $ret, RG_SOCKET_NO_WAIT); break; } rg_prof_end('repo_have'); return $ret; } /* * List repositories */ function rg_repo_list_query($db, $url, $sql, $params) { rg_prof_start("repo_list_query"); rg_log("repo_list_query: url=$url, sql=$sql..."); $res = rg_sql_query_params($db, $sql, $params); 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_log("uid " . $row['uid'] . " associated with this repo not found"); continue; } rg_repo_cosmetic($db, $_line); $d[] = $_line; } rg_sql_free_result($res); rg_prof_end("repo_list_query"); return rg_template_table("repo/list", $d, array()); } /* * List repos of page user 'ui'. * @uid - owner of the to be listed repos */ function rg_repo_list($db, $rg, $url, $uid) { rg_log("repo_list: url=$url uid=" . $uid . " login_uid=" . $rg['login_ui']['uid']); $params = array( "uid" => $uid, "login_uid" => $rg['login_ui']['uid']); if ($uid > 0) $add = ' AND uid = @@uid@@'; else $add = ' AND uid != @@login_uid@@'; // TODO: also admin must be able to see them? if (($rg['login_ui']['uid'] == 0) || ($rg['login_ui']['uid'] != $uid)) $add .= " AND public = 1"; $sql = "SELECT * FROM repos" . " WHERE deleted = 0" . $add . " ORDER BY name"; return rg_repo_list_query($db, $url, $sql, $params); } /* * Search in all repositories owned by 'login_ui' or public * We need to exclude private repositories. */ function rg_repo_search($db, $login_ui, $q) { rg_prof_start("repo_search"); rg_log("repo_search: q=[$q]"); $admin = 0; if (isset($login_ui['admin']) && ($login_ui['admin'] == 1)) $admin = 1; $params = array("q" => "%" . $q . "%", "uid" => $login_ui['uid']); $sql = "SELECT * FROM repos" . " WHERE deleted = 0" . " AND (uid = @@uid@@ OR public = 1 OR " . $admin . " = 1)" . " AND (name ILIKE @@q@@ OR description ILIKE @@q@@)" . " ORDER BY master, name" . " LIMIT 20"; $r = rg_repo_list_query($db, "", $sql, $params); rg_prof_end("repo_search"); return $r; } /* * Computes the size of a repository * @all - if TRUE, take in account all files. If FALSE, ignore the files with * many links (a clone). */ function rg_repo_size($path, $all) { //rg_log("repo_disk_mb: path=$path"); $ret = FALSE; while (1) { $dir = @opendir($path); if ($dir === FALSE) { rg_repo_set_error("Cannot open $path!"); break; } $total = 0; $error = FALSE; while (($f = readdir($dir)) !== FALSE) { if (strcmp($f, ".") == 0) continue; if (strcmp($f, "..") == 0) continue; if (is_dir($path . "/" . $f)) { $r = rg_repo_size($path . "/" . $f, $all); if ($r === FALSE) { $error = TRUE; break; } $total += $r; } if (is_file($path . "/" . $f)) { $s = stat($path . "/" . $f); if ($s === FALSE) { $error = TRUE; break; } $r = $s['size']; if ($all === FALSE) { if ($s['nlink'] > 1) $r = 0; } $total += $r; } } closedir($dir); if ($error === FALSE) $ret = $total; break; } return $ret; } /* * Mark a git repo as done */ function rg_repo_git_done($db, $repo_id) { rg_prof_start("repo_git_done"); rg_log_enter("repo_git_done: repo_id=$repo_id..."); $ret = FALSE; while (1) { $params = array("repo_id" => $repo_id); rg_cache_merge('repo_by_id::' . $repo_id, array('git_dir_done' => 1), RG_SOCKET_NO_WAIT); $sql = "UPDATE repos SET git_dir_done = 1" . " WHERE repo_id = @@repo_id@@"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_repo_set_error("Cannot query (" . rg_sql_error() . ")"); break; } rg_sql_free_result($res); $ret = TRUE; break; } rg_log_exit(); rg_prof_end("repo_git_done"); return $ret; } /* * Returns the status of a repo */ function rg_repo_lock_status($db, $repo_id) { rg_prof_start('repo_lock_status'); rg_log_enter('repo_lock_status'); $ret = array(); $ret['ok'] = 0; while (1) { $key = 'locks' . '::' . 'repos' . '::' . $repo_id; $c = rg_cache_get($key); if ($c !== FALSE) { $ret = $c; $ret['ok'] = 1; break; } $params = array('repo_id' => $repo_id); $sql = 'SELECT * FROM repo_locks' . ' WHERE repo_id = @@repo_id@@'; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_repo_set_error('cannot get lock status'); break; } $rows = rg_sql_num_rows($res); if ($rows == 0) { $ret['status'] = 0; } else { $ret = rg_sql_fetch_array($res); $ret['status'] = 1; } rg_sql_free_result($res); rg_cache_set($key, $ret, RG_SOCKET_NO_WAIT); $ret['ok'] = 1; break; } rg_log_exit(); rg_prof_end('repo_lock_status'); return $ret; } /* * Locks/unlocks a repo * @lock: 1 = lock, 0 = unlock */ function rg_repo_lock($db, $repo_id, $uid, $lock, $reason) { rg_prof_start('repo_lock'); rg_log_enter('repo_lock'); $ret = FALSE; while (1) { $si = rg_repo_lock_status($db, $repo_id); if ($si['ok'] != 1) break; // If we already are in the same state as the passed one if ($si['status'] == $lock) { $ret = TRUE; break; } $params = array( 'itime' => time(), 'repo_id' => $repo_id, 'uid' => $uid, 'reason' => $reason); if ($lock == 1) $sql = 'INSERT INTO repo_locks (repo_id, uid, itime)' . ' VALUES (@@repo_id@@, @@uid@@, @@itime@@)'; else $sql = 'DELETE FROM repo_locks' . ' WHERE repo_id = @@repo_id@@'; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_repo_set_error('db lock/unlock error'); break; } $key = 'locks' . '::' . 'repos' . '::' . $repo_id; if ($lock == 1) { $params['status'] = 1; rg_cache_set($key, $params, RG_SOCKET_NO_WAIT); } else { rg_cache_unset($key, RG_SOCKET_NO_WAIT); } $h = array('ri' => array(), 'ui' => array()); $h['ri']['repo_id'] = $repo_id; $h['ui']['uid'] = $uid; $h['history_category'] = $lock == 1 ? REPO_CAT_LOCK : REPO_CAT_UNLOCK; $h['history_message'] = 'Repository ' . ($lock == 1 ? 'locked' : 'unlocked') . ': ' . $reason; rg_repo_history_insert($db, $h); $ret = TRUE; break; } rg_log_exit(); rg_prof_end('repo_lock'); return $ret; } /* * High level function for Repo -> Admin -> Rights -> Repo/Refs/Path rights menu. */ function rg_repo_admin_rights($db, $rg, $type) { rg_log("rg_repo_admin_rights type=$type"); /* 'repo' is correct here, we test for granting rights on repo */ $x = array(); $x['obj_id'] = $rg['ri']['repo_id']; $x['type'] = 'repo'; $x['owner'] = $rg['ri']['uid']; $x['uid'] = $rg['login_ui']['uid']; $x['username'] = $rg['login_ui']['username']; $x['needed_rights'] = 'G'; $x['ip'] = $rg['ip']; $x['misc'] = ""; if (rg_rights_allow($db, $x) !== TRUE) return rg_template("user/repo/rights/deny.html", $rg, TRUE /* xss */); $ret = ""; $a = array(); $a['type'] = $type; $a['right_id'] = rg_var_uint("right_id"); $a['edit_id'] = rg_var_uint("edit_id"); $a['username'] = rg_var_str("username"); $a['rights'] = rg_rights_a2s(rg_var_str("rights")); $a['misc'] = rg_var_str("misc"); $a['ip'] = rg_var_str("ip"); $a['prio'] = rg_var_uint("prio"); $a['description'] = trim(rg_var_str("description")); //rg_log_ml("CHECK: a(POST)=" . print_r($a, TRUE)); $errmsg = array(); $list_errmsg = array(); $load_defaults = 1; $delete = rg_var_bool("delete"); while ($delete == 1) { if (!rg_valid_referer()) { $errmsg[] = "invalid referer; try again"; break; } if (!rg_token_valid($db, $rg, 'repo_admin_rights', FALSE)) { $errmsg[] = "invalid token; try again"; break; } $list = rg_var_uint("rights_delete_ids"); if (empty($list)) { $list_errmsg[] = "please select at least one item"; break; } $my_list = array(); foreach ($list as $k => $junk) $my_list[] = $k; $r = rg_rights_delete_list($db, $type, $rg['ri']['repo_id'], $my_list); if ($r !== TRUE) { $list_errmsg[] = "cannot delete rights: " . rg_rights_error(); break; } $ret .= rg_template("user/repo/rights/delete_ok.html", $rg, TRUE /* xss */); break; } // edit while ($a['edit_id'] > 0) { $owner = $rg['ri']['uid']; $r = rg_rights_get($db, $rg['ri']['repo_id'], $type, $owner, -1, $a['edit_id']); if ($r['ok'] != 1) { $list_errmsg[] = "cannot load rights: " . rg_rights_error(); break; } if (empty($r['list'])) { $list_errmsg[] = "right not found"; break; } // Only one right is returned when we edit by right_id $a = $r['list'][0]; $load_defaults = 0; break; } $grant = rg_var_bool("grant"); while ($grant == 1) { $load_defaults = 0; if (!rg_valid_referer()) { $errmsg[] = "invalid referer; try again"; break; } if (!rg_token_valid($db, $rg, 'repo_admin_rights', FALSE)) { $errmsg[] = "invalid token; try again"; break; } if (strcmp($a['username'], '*') == 0) { $a['uid'] = 0; } else { $_ui = rg_user_info($db, 0, $a['username'], ""); if ($_ui['exists'] == 1) { $a['uid'] = $_ui['uid']; } else { $errmsg[] = rg_user_error(); break; } } $a['obj_id'] = $rg['ri']['repo_id']; $a['who'] = $rg['login_ui']['uid']; $r = rg_rights_set($db, $type, $a); if ($r !== TRUE) { $errmsg[] = rg_rights_error(); break; } $ret .= rg_template("user/repo/rights/grant_ok.html", $rg, TRUE /* xss */); $load_defaults = 1; break; } if ($load_defaults == 1) { $rg['right_id'] = 0; $rg['username'] = ""; $rg['rights'] = rg_rights_default($type); $rg['description'] = ""; $rg['misc'] = ""; $rg['ip'] = ""; $rg['prio'] = 100; } else { $rg = rg_array_merge($rg, '', $a); } $rg['rg_form_token'] = rg_token_get($db, $rg, 'repo_admin_rights'); $rg['HTML:errmsg'] = rg_template_errmsg($errmsg); $rg['HTML:list_errmsg'] = rg_template_errmsg($list_errmsg); $rg['HTML:rights_checkboxes'] = rg_rights_checkboxes($type, "rights", $rg['rights']); // list rights $r = rg_rights_get($db, $rg['ri']['repo_id'], $type, $rg['ri']['uid'], -1, 0); if ($r['ok'] != 1) $ret .= rg_warning("Cannot load rights. Try later."); else $ret .= rg_template_table("user/repo/rights/list_" . $type, $r['list'], $rg); $ret .= rg_template("user/repo/rights/form_" . $type . ".html", $rg, TRUE /* xss */); // hints $hints = array(); $hints[]['HTML:hint'] = rg_template("hints/repo/edit_rights.html", $rg, TRUE /* xss */); $hints[]['HTML:hint'] = rg_template("hints/repo/edit_" . $type . "_rights.html", $rg, TRUE /* xss */); $ret .= rg_template_table("hints/list", $hints, $rg); return $ret; } /* * High level function for repo deletion */ function rg_repo_admin_delete($db, $rg) { $ret = ""; $x = array(); $x['obj_id'] = $rg['ri']['repo_id']; $x['type'] = 'repo'; $x['owner'] = $rg['ri']['uid']; $x['uid'] = $rg['login_ui']['uid']; $x['username'] = $rg['login_ui']['username']; $x['needed_rights'] = 'D'; $x['ip'] = $rg['ip']; $x['misc'] = ""; if (rg_rights_allow($db, $x) !== TRUE) return rg_template("user/repo/delete/deny.html", $rg, TRUE /* xss */); $are_you_sure = rg_var_uint("are_you_sure"); $errmsg = array(); $show_form = 1; while (1) { if ($rg['doit'] != 1) break; if ($are_you_sure == 0) { $ret .= rg_template("user/repo/delete/no.html", $rg, TRUE /* xss */); $show_form = 0; break; } if (!rg_valid_referer()) { $errmsg[] = "invalid referer; try again"; break; } if (!rg_token_valid($db, $rg, 'repo_admin_delete', FALSE)) { $errmsg[] = "invalid token; try again"; break; } rg_log_ml("CHECK: rg: " . print_r($rg, TRUE)); $r = rg_repo_delete($db, $rg['ri']['repo_id'], $rg['login_ui']); if ($r === FALSE) { $errmsg[] = rg_repo_error(); break; } $ret .= rg_template("user/repo/delete/done.html", $rg, TRUE /* xss */); $show_form = 0; // TODO: shouldn't we invalidate the cache? break; } if ($show_form == 1) { $rg['HTML:errmsg'] = rg_template_errmsg($errmsg); $rg['rg_form_token'] = rg_token_get($db, $rg, 'repo_admin_delete'); $ret .= rg_template("user/repo/delete/sure.html", $rg, TRUE /* xss */); } return $ret; } /* * High level function creating/editing a repo */ function rg_repo_edit_high_level($db, &$rg) { rg_log_enter("rg_repo_edit_high_level"); $ret = ""; $errmsg = array(); $load_form = TRUE; while (1) { if ($rg['ri']['repo_id'] > 0) $edit = TRUE; else $edit = FALSE; // User is not logged in? if (!$edit && ($rg['login_ui']['uid'] == 0)) { $ret .= rg_template("user/repo/deny_create.html", $rg, TRUE /* xss */); $load_form = FALSE; break; } if ($edit) { $x = array(); $x['obj_id'] = $rg['ri']['repo_id']; $x['type'] = 'repo'; $x['owner'] = $rg['ri']['uid']; $x['uid'] = $rg['login_ui']['uid']; $x['username'] = $rg['login_ui']['username']; $x['needed_rights'] = 'E'; $x['ip'] = $rg['ip']; $x['misc'] = ""; if (rg_rights_allow($db, $x) !== TRUE) { $ret .= rg_template("user/repo/deny_edit.html", $rg, TRUE /* xss */); $load_form = FALSE; break; } } if ($rg['doit'] != 1) { if (!$edit) { // Defaults $rg['ri'] = array(); $rg['ri']['repo_id'] = "0"; $rg['ri']['master'] = "0"; $rg['ri']['name'] = ""; $rg['ri']['max_commit_size'] = "0"; $rg['ri']['description'] = ""; $rg['ri']['public'] = "1"; $rg['ri']['license'] = ""; } break; } $rg['ri']['repo_id'] = rg_var_uint('repo_id'); $rg['ri']['master'] = rg_var_uint('master'); $rg['ri']['name'] = rg_var_str('name'); // TODO: filter name! $rg['ri']['max_commit_size'] = rg_var_uint('max_commit_size'); $rg['ri']['description'] = trim(rg_var_str('description')); $rg['ri']['public'] = rg_var_bool('public'); $rg['ri']['license'] = trim(rg_var_str('license')); rg_repo_cosmetic($db, $rg['ri']); //rg_log_ml("CHECK: after repo edit: rg[ri]=" . print_r($rg['ri'], TRUE)); if (!rg_valid_referer()) { $errmsg[] = "invalid referer; try again"; break; } if (!rg_token_valid($db, $rg, 'repo_edit_hl', FALSE)) { // TODO: replace all of these with a template $errmsg[] = "invalid token; try again."; break; } $r = rg_repo_edit($db, $rg['login_ui'], $rg['ri']); if ($r === FALSE) { $errmsg[] = rg_repo_error(); break; } $rg['ri']['home'] = rg_re_repopage($rg['login_ui'], $rg['ri']['name']); if ($edit) { $ret .= rg_template("repo/edit_ok.html", $rg, TRUE /*xss*/); } else { $ret .= rg_template("repo/create_ok.html", $rg, TRUE /*xss*/); } $load_form = FALSE; break; } if ($load_form) { if ($rg['ri']['master'] > 0) { $rg['ri']['master_name'] = $rg['ri']['master']; $_mi = rg_repo_info($db, $rg['ri']['master'], ""); if ($_mi['exists'] == 1) $rg['ri']['master_name'] = $_mi['name']; } else { $rg['ri']['master_name'] = ""; } $rg['HTML:errmsg'] = rg_template_errmsg($errmsg); $rg['rg_form_token'] = rg_token_get($db, $rg, 'repo_edit_hl'); $hints = array(); $hints[]['HTML:hint'] = rg_template("hints/repo/create_repo.html", $rg, TRUE /* xss */); $rg['HTML:repo_edit_hints'] = rg_template_table("hints/list", $hints, $rg); $ret .= rg_template("repo/add_edit.html", $rg, TRUE /* xss */); } rg_log_exit(); return $ret; } /* * High level function for locking a repo */ function rg_repo_lock_high_level($db, &$rg) { rg_log_enter("rg_repo_lock_high_level"); $ret = ""; $errmsg = array(); $show_form = TRUE; while (1) { $x = array(); $x['obj_id'] = $rg['ri']['repo_id']; $x['type'] = 'repo'; $x['owner'] = $rg['ri']['uid']; $x['uid'] = $rg['login_ui']['uid']; $x['username'] = $rg['login_ui']['username']; $x['needed_rights'] = 'K'; $x['ip'] = $rg['ip']; $x['misc'] = ''; if (rg_rights_allow($db, $x) !== TRUE) { $ret .= rg_template("user/repo/deny_lock.html", $rg, TRUE /*xss*/); $show_form = FALSE; break; } $ls = rg_repo_lock_status($db, $rg['ri']['repo_id']); if ($ls['ok'] != 1) break; $rg['ri']['next_status'] = 1 - $ls['status']; $rg['ri']['reason'] = rg_var_str('ri::reason'); $lock = rg_var_int('ri::lock'); rg_log('ZZZ: status=' . $ls['status']); rg_log('ZZZ: lock=' . $lock); if ($rg['doit'] != 1) break; if (!rg_valid_referer()) { $errmsg[] = "invalid referer; try again"; break; } if (!rg_token_valid($db, $rg, 'repo_lock_hl', FALSE)) { // TODO: replace all of these with a template $errmsg[] = "invalid token; try again."; break; } $r = rg_repo_lock($db, $rg['ri']['repo_id'], $rg['login_ui']['uid'], $lock, $rg['ri']['reason']); if ($r === FALSE) { $errmsg[] = rg_repo_error(); break; } if ($lock == 1) $t = 'repo/lock_ok.html'; else $t = 'repo/unlock_ok.html'; $ret .= rg_template($t, $rg, TRUE /*xss*/); // clean form $rg['ri']['reason'] = ''; $rg['ri']['next_status'] = 1 - $lock; break; } if ($show_form) { $rg['HTML:errmsg'] = rg_template_errmsg($errmsg); $hints = array(); $hints[]['HTML:hint'] = rg_template("hints/repo/lock_repo.html", $rg, TRUE /*xss*/); $rg['HTML:repo_lock_hints'] = rg_template_table("hints/list", $hints, $rg); $rg['rg_form_token'] = rg_token_get($db, $rg, 'repo_lock_hl'); $ret .= rg_template("repo/lock.html", $rg, TRUE /*xss*/); } rg_log_exit(); return $ret; } /* * High level function for 'Repo -> Admin' menu */ function rg_repo_admin($db, &$rg, $paras) { rg_log("rg_repo_admin paras=" . rg_array2string($paras)); $ret = ""; $_op = empty($paras) ? "edit" : array_shift($paras); $rg['allow_edit_repo'] = 0; $rg['allow_lock_repo'] = 0; $rg['allow_grant_rights'] = 0; $rg['allow_delete_repo'] = 0; $x = array(); $x['obj_id'] = $rg['ri']['repo_id']; $x['type'] = 'repo'; $x['owner'] = $rg['ri']['uid']; $x['uid'] = $rg['login_ui']['uid']; $x['username'] = $rg['login_ui']['username']; $x['ip'] = $rg['ip']; $x['misc'] = ''; $x['needed_rights'] = 'E'; if (rg_rights_allow($db, $x) === TRUE) $rg['allow_edit_repo'] = 1; $x['needed_rights'] = 'K'; if (rg_rights_allow($db, $x) === TRUE) $rg['allow_lock_repo'] = 1; $x['needed_rights'] = 'G'; if (rg_rights_allow($db, $x) === TRUE) $rg['allow_grant_rights'] = 1; $x['needed_rights'] = 'D'; if (rg_rights_allow($db, $x) === TRUE) $rg['allow_delete_repo'] = 1; $rg['menu']['repo'][$_op] = 1; $ret .= rg_template("user/repo/menu.html", $rg, TRUE /* xss */); switch ($_op) { case 'repo_rights': $ret .= rg_repo_admin_rights($db, $rg, "repo"); break; case 'refs_rights': $ret .= rg_repo_admin_rights($db, $rg, "repo_refs"); break; case 'path_rights': $ret .= rg_repo_admin_rights($db, $rg, "repo_path"); break; case 'delete': $ret .= rg_repo_admin_delete($db, $rg); break; case 'lock': $rg['form_url'] = $rg['ri']['url_repo'] . "/admin/lock"; $ret .= rg_repo_lock_high_level($db, $rg); break; default: $rg['form_url'] = $rg['ri']['url_repo'] . "/admin"; $ret .= rg_repo_edit_high_level($db, $rg); break; } return $ret; } /* * HL function for repo search */ function rg_repo_search_high_level($db, $rg, $ui, $url) { $q = rg_var_str('q'); $ret = ''; $errmsg = array(); while ($rg['doit'] == 1) { $_t = rg_repo_search($db, $ui, $q); if ($_t === FALSE) { $errmsg[] = rg_repo_error(); break; } $ret .= $_t; break; } $rg['q'] = $q; $rg['HTML:errmsg'] = rg_template_errmsg($errmsg); $rg['search_url'] = $url; $ret .= rg_template("repo/search.html", $rg, TRUE /* xss */); return $ret; } /* * Discover top menu * @ui - act like login_ui */ function rg_repo_discover($db, $op, $rg, $ui) { rg_log("repo_discover: op=$op"); $ret = ''; if (empty($op)) $op = 'list'; switch ($op) { case 'search': $x = rg_repo_search_high_level($db, $rg, $ui, '/op/discover'); break; default: $x = rg_repo_list($db, $rg, '', $ui['uid']); break; } $rg['discover_menu_' . $op] = 1; $ret .= rg_template('repo/discover.html', $rg, TRUE /* xss */); $ret .= $x; return $ret; } /* * Show some stats about a repo */ function rg_repo_stats($rg) { rg_log_enter('repo_start'); while (1) { if ($rg['ri']['git_dir_done'] == 0) { $ret = rg_template('repo/no_git_dir.html', $rg, TRUE /*xss*/); break; } $path = rg_repo_path_by_id($rg['ri']['uid'], $rg['ri']['repo_id']); $log = rg_git_log($path, 0 /*max*/, '', '', FALSE); if ($log === FALSE) { $ret = rg_template('internal_err.html', $rg, TRUE /*xss*/); break; } $s = rg_git_stats($log); //rg_log_ml('DEBUG: stats: ' . print_r($s, TRUE)); $rg['ri']['stats'] = $s['global']; $rg['ri']['HTML:authors'] = rg_template_table('repo/stats/authors', $s['authors'], $rg); $ret = rg_template('repo/stats/stats.html', $rg, TRUE /*xss*/); break; } rg_log_exit(); return $ret; } ?>