<?php
$INC = isset($INC) ? $INC : dirname(__FILE__);
require_once($INC . "/util.inc.php");
require_once($INC . "/log.inc.php");
require_once($INC . "/sql.inc.php");
require_once($INC . "/sess.inc.php");
require_once($INC . "/rights.inc.php");
require_once($INC . "/events.inc.php");
require_once($INC . "/cache.inc.php");
require_once($INC . "/mail.inc.php");
require_once($INC . "/plan.inc.php");
require_once($INC . "/totp.inc.php");
$rg_user_rights = array(
"C" => "Create repository",
"A" => "Create user",
"E" => "Edit user",
"R" => "Remove user",
"S" => "Suspend user",
"G" => "Grant rights",
"M" => "Give admin rights"
);
/* We do not have defaults rights and no compare function for misc */
rg_rights_register("user", $rg_user_rights, "", FALSE, FALSE);
$rg_user_error = "";
function rg_user_set_error($str)
{
global $rg_user_error;
$rg_user_error = $str;
rg_log($str);
}
function rg_user_error()
{
global $rg_user_error;
return $rg_user_error;
}
/*
* Event functions
*/
$rg_user_functions = array(
2000 => "rg_user_event_new",
2001 => "rg_user_event_login",
2002 => "rg_user_event_notify_user",
2005 => "rg_user_event_rename",
2006 => "rg_user_link_by_name",
'ask-email-confirmation' => 'rg_user_event_ask_email_confirmation'
);
rg_event_register_functions($rg_user_functions);
/*
* Ask for e-mail confirmation
*/
function rg_user_event_ask_email_confirmation($db, $ev)
{
rg_log('rg_user_event_ask_email_confirmation: ev='
. rg_array2string($ev));
if (!isset($ev['ui']['email'])) {
$ev['ui'] = rg_user_info($db, $ev['ui']['uid'], '', '');
if ($ev['ui']['exists'] != 1)
return FALSE;
}
rg_mail_template('mail/user/econf', $ev);
return array();
}
/*
* Event for adding a new user
*/
function rg_user_event_new($db, $event)
{
$ret = array();
//rg_log_ml('DEBUG: user_event_new: event: ' . print_r($event, TRUE));
$event['op'] = 'new';
// create link by name
$ret[] = array_merge($event, array("category" => 2006, "prio" => 500));
// notify user
$ret[] = array_merge($event, array("category" => 2002, "prio" => 200));
return $ret;
}
/*
* Event for login
*/
function rg_user_event_login($db, $event)
{
$ret = FALSE;
while (1) {
$r = rg_user_set_last_seen($db, $event['ui']['uid'], $event['itime'],
$event['ip']);
if ($r !== TRUE)
break;
$ret = array();
break;
}
return $ret;
}
/*
* Notify user: welcome
*/
function rg_user_event_notify_user($db, $event)
{
rg_log("user_event_notify_user: event=" . rg_array2string($event));
if (strcmp($event['op'], "rename") == 0) {
$r = rg_mail_template("mail/user/rename", $event);
} else {
$r = rg_mail_template("mail/user/welcome", $event);
}
// TODO: we may want to return here an error?
return array();
}
/*
* Event for renaming an user
*/
function rg_user_event_rename($db, $event)
{
$ret = array();
$event['op'] = "rename";
// notify user
$ret[] = array_merge($event, array("category" => 2002, "prio" => 100));
// TODO: notify watchers
return $ret;
}
/*
* Empty user data
*/
function rg_user_empty()
{
return array(
'ok' => 0,
'uid' => 0,
'is_admin' => 0,
'rights' => '',
'homepage' => '',
'username' => '',
'exists' => 0,
'organization' => 0,
'confirmed' => 0
);
}
/*
* Cosmetic function for 'user' info
*/
function rg_user_cosmetic(&$row)
{
if (isset($row['itime']))
$row['itime_nice'] = gmdate('Y-m-d', $row['itime']);
$row['email_md5'] = md5(strtolower(trim($row['email'])));
$row['homepage'] = rg_re_userpage($row);
$row['HTML:gravatar'] = '<img class="gravatar" src="https://www.gravatar.com/avatar/'
. $row['email_md5']
. '?s=64&r=g" alt="avatar" width="64" height="64" />';
}
/*
* Event for creating a link by name to the uid
*/
function rg_user_link_by_name($db, $event)
{
global $php_errormsg;
rg_log("user_link_by_name: event=" . rg_array2string($event));
$by_id = rg_user_path_by_id($event['ui']['uid']);
if (!is_dir($by_id) && (mkdir($by_id, 0700, TRUE) === FALSE)) {
rg_user_set_error("cannot mkdir by_id=$by_id ($php_errormsg)");
return FALSE;
}
$by_name = rg_user_path_by_name($event['ui']['username']);
$by_name_parent = dirname($by_name);
if (!is_dir($by_name_parent)
&& (mkdir($by_name_parent, 0700, TRUE) === FALSE)) {
rg_user_set_error("cannot mkdir by_name_parent=$by_name_parent ($php_errmsg)");
return FALSE;
}
$by_id_rel = rg_user_path_by_id_rel($event['ui']['uid']);
if (is_link($by_name))
unlink($by_name);
if (symlink($by_id_rel, $by_name) === FALSE) {
rg_user_set_error("cannot symlink $by_id_rel <- $by_name ($php_errormsg)!");
return FALSE;
}
return array();
}
/*
* Returns the relative path to user home based on uid
*/
function rg_user_path_by_id_rel($uid)
{
$xuid = sprintf("%08X", $uid);
$uid_path = array();
for ($i = 0; $i < 8; $i += 2)
$uid_path[] = $xuid[$i] . $xuid[$i + 1];
$uid_path = implode("/", $uid_path);
return "../../../../by_id/" . $uid_path . "/" . $xuid;
}
/*
* Returns the path to user home based on uid
*/
function rg_user_path_by_id($uid)
{
global $rg_repos;
$xuid = sprintf("%08X", $uid);
$uid_path = array();
for ($i = 0; $i < 8; $i += 2)
$uid_path[] = $xuid[$i] . $xuid[$i + 1];
$uid_path = implode("/", $uid_path);
return $rg_repos . "/by_id/" . $uid_path . "/" . $xuid;
}
/*
* Returns the path to user home based on name
*/
function rg_user_path_by_name($name)
{
global $rg_repos;
$x = $name . "__";
return $rg_repos . "/by_name/"
. $x[0] . "/" . $x[1] . "/" . $x[2] . "/" . $name;
}
/*
* Returns the URL to a user home based on $ui
*/
function rg_user_url($ui)
{
if ($ui['organization'] == 0)
$prefix = "/user";
else
$prefix = "";
return $prefix . "/" . $ui['username'];
}
/*
* Computes password hash
*/
function rg_user_pass($salt, $pass)
{
return sha512($salt . "===" . $pass);
}
/*
* Validates a password
*/
function rg_user_pass_ok($pass)
{
if (strlen($pass) < 5) {
rg_user_set_error("password is too short (less than 5 chars)");
return FALSE;
}
return TRUE;
}
/*
* Returns true if the user is ok
*/
function rg_user_ok($user)
{
global $rg_user_allow;
global $rg_user_min_len;
global $rg_user_max_len;
if (rg_chars_allow($user, $rg_user_allow, $invalid) !== TRUE) {
rg_user_set_error("invalid user name (invalid chars: '$invalid')");
return FALSE;
}
if (strlen($user) < $rg_user_min_len) {
rg_user_set_error("user name too short (shorter than $rg_user_min_len)");
return FALSE;
}
if (strlen($user) > $rg_user_max_len) {
rg_user_set_error("user name too long (longer than $rg_user_max_len)");
return FALSE;
}
return TRUE;
}
/*
* Lookup in db the old names, so we can redirect the user to the new name
*/
function rg_user_lookup_by_old_name($db, $old_name)
{
rg_prof_start("user_lookup_by_old_name");
rg_log_enter("user_lookup_by_old_name: old_name=$old_name");
$ret = FALSE;
while (1) {
$x = rg_cache_get("old_name::" . $old_name);
if ($x !== FALSE) {
$ret = $x;
break;
}
$params = array("old_name" => $old_name);
$sql = "SELECT uid FROM users_renames"
. " WHERE old_name = @@old_name@@";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_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['uid'];
rg_cache_set("old_name::" . $old_name, $ret, RG_SOCKET_NO_WAIT);
break;
}
rg_log_exit();
rg_prof_end("user_lookup_by_old_name");
return $ret;
}
/*
* Add a rename to the database
*/
function rg_user_insert_rename($db, $uid, $old_name)
{
rg_prof_start("user_insert_rename");
rg_log_enter("user_insert_rename: uid=$uid old_name=$old_name");
$ret = FALSE;
while(1) {
// Check if already exists in the database
$r = rg_user_lookup_by_old_name($db, $old_name);
if ($r === FALSE)
break;
$params = array("uid" => $uid,
"old_name" => $old_name,
"now" => time());
if ($r > 0) {
$sql = "UPDATE users_renames"
. " SET uid = @@uid@@"
. " WHERE old_name = @@old_name@@";
} else {
$sql = "INSERT INTO users_renames (uid, old_name"
. ", itime)"
. " VALUES (@@uid@@, @@old_name@@, @@now@@)";
}
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error("cannot link with old_name in db"
. " (" . rg_sql_error() . ")");
break;
}
rg_cache_set("old_name::" . $old_name, $uid, RG_SOCKET_NO_WAIT);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end("user_insert_rename");
return $ret;
}
/*
* Rename a user
* We keep the old name around, so the clients do not break.
*/
function rg_user_rename($db, $ui, $new_name)
{
global $php_errormsg;
rg_prof_start("user_rename");
rg_log_enter("user_rename: from=[" . $ui['username'] . "]"
. " to=[" . $new_name . "]...");
$user_path = rg_user_path_by_id($ui['uid']);
$old_path = rg_user_path_by_id_rel($ui['uid']);
$new_path = rg_user_path_by_name($new_name);
rg_log("old_path=$old_path new_path=$new_path");
$ret = FALSE;
while (1) {
$do_link = TRUE;
// 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($old_path, $v) == 0) {
// Link already done
$do_link = FALSE;
} else {
// 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;
}
}
}
if ($do_link === TRUE) {
// Now, the new name is free, do the link
$r = symlink($old_path, $new_path);
if ($r !== TRUE) {
rg_internal_error("Cannot symlink $old_path -> $new_path ($php_errormsg)!");
break;
}
}
// TOOD: transaction?
$r = rg_user_insert_rename($db, $ui['uid'], $new_name);
if ($r !== TRUE)
break;
// TODO: Check if all parameters are used.
$event = array(
'category' => 2005,
'prio' => 50,
'ui' => $ui,
'rename_from' => $ui['username'],
'rename_to' => $new_name
);
$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);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end("user_rename");
return $ret;
}
/*
* Add/edit a user
* If uid > 0 - edit, else, add
*/
function rg_user_edit($db, $d)
{
rg_prof_start("user_edit");
rg_log_enter("user_edit: d: " . rg_array2string($d));
$ret = FALSE;
while (1) {
$add = $d['uid'] == 0;
if (rg_user_ok($d['username']) !== TRUE)
break;
// TODO: check rights
// TODO - check if user is allowed to give passed rights
if ($d['ask_for_email_confirmation'] == 1)
$d['confirmed'] = 0;
$update_pass = !empty($d['pass']);
if ($update_pass) {
if (strcmp($d['pass'], $d['pass2']) != 0) {
rg_user_set_error("passwords are not the same");
break;
}
$d['salt'] = rg_id(40);
$d['pass_crypted'] = rg_user_pass($d['salt'], $d['pass']);
}
$d['itime'] = time();
if ($add) {
if (rg_user_pass_ok($d['pass']) !== TRUE)
break;
$d['suspended'] = 0;
$d['deleted'] = 0;
$d['last_seen'] = 0;
$d['disk_used_mb'] = 0;
$sql = "INSERT INTO users (username, realname, salt"
. ", pass, email, itime"
. ", is_admin, rights, session_time"
. ", confirmed, confirm_token, plan_id"
. ", suspended, last_seen, disk_used_mb"
. ", deleted)"
. " VALUES (@@username@@, @@realname@@, @@salt@@"
. ", @@pass_crypted@@, @@email@@, @@itime@@"
. ", @@is_admin@@, @@rights@@, @@session_time@@"
. ", @@confirmed@@, @@confirm_token@@, @@plan_id@@"
. ", @@suspended@@, @@last_seen@@"
. ", @@disk_used_mb@@, @@deleted@@)"
. " RETURNING uid";
} else { // edit
$salt_pass_add = "";
if ($update_pass)
$salt_pass_add = ", pass = @@pass_crypted@@"
. ", salt = @@salt@@";
$sql = "UPDATE users"
. " SET username = @@username@@"
. ", realname = @@realname@@"
. ", email = @@email@@"
. ", is_admin = @@is_admin@@"
. ", rights = @@rights@@"
. ", session_time = @@session_time@@"
. ", plan_id = @@plan_id@@"
. ", confirmed = @@confirmed@@"
. ", confirm_token = @@confirm_token@@"
. $salt_pass_add
. " WHERE uid = @@uid@@";
}
$res = rg_sql_query_params($db, $sql, $d);
if ($res === FALSE) {
rg_user_set_error('cannot insert/update user info');
break;
}
if ($add) {
$row = rg_sql_fetch_array($res);
$d['uid'] = $row['uid'];
}
rg_sql_free_result($res);
if ($add) {
rg_cache_set('user' . '::' . $d['uid'] . '::' . 'info',
$d, RG_SOCKET_NO_WAIT);
// we need to be able to send the welcome mail
$d['ignore_confirmed'] = 1;
$event = array(
'category' => 2000,
'prio' => 50,
'ui' => $d,
'url' => rg_base_url()
);
$r = rg_event_add($db, $event);
if ($r === FALSE) {
rg_user_set_error('cannot add event');
break;
}
rg_event_signal_daemon("", 0);
} else { // edit
// else, we will overwrite the pass in cache
if (!$update_pass)
unset($d['pass']);
unset($d['pass2']); // not needed in cache
rg_cache_merge('user' . '::' . $d['uid']
. '::' . 'info', $d, RG_SOCKET_NO_WAIT);
if ($d['ask_for_email_confirmation'] == 1) {
$r = rg_user_ask_for_email_confirmation($db, $d['uid']);
if ($r === FALSE) {
rg_user_set_error('cannot add event');
break;
}
}
}
// TODO: should we cache here the user_by_uid and user_by_name
$ret = $d['uid'];
break;
}
rg_log_exit();
rg_prof_end("user_edit");
return $ret;
}
/*
* Delete a user
* @uid - the uid of the user to be removed
* This function only marks the user to be removed.
*/
function rg_user_remove($db, $rg, $uid)
{
rg_prof_start('user_remove');
rg_log_enter('user_remove: uid=$uid');
$ret = FALSE;
while (1) {
$x = array();
$x['obj_id'] = $uid;
$x['type'] = 'user';
$x['owner'] = $uid;
$x['uid'] = $rg['login_ui']['uid'];
$x['username'] = $rg['login_ui']['username'];
$x['needed_rights'] = 'R';
$x['ip'] = $rg['ip'];
$x['misc'] = '';
if (rg_rights_allow($db, $x) !== TRUE)
break;
$now = time();
$params = array(
'now' => $now,
'uid' => $uid
);
$sql = 'UPDATE users SET deleted = @@now@@'
. ' WHERE uid = @@uid@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error('cannot remove user');
break;
}
rg_sql_free_result($res);
// update cache
rg_cache_set('user' . '::' . $uid . '::' . 'info'
. '::' . 'deleted', $now, RG_SOCKET_NO_WAIT);
// invalidate session
rg_sess_destroy($db, $rg['sid'], $rg['login_ui']);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end('user_remove');
return $ret;
}
/*
* Returns info about a user (by uid, user or e-mail)
*/
function rg_user_info($db, $uid, $user, $email)
{
rg_prof_start("user_info");
rg_log_enter("user_info: uid=$uid user=$user email=$email");
$ret = rg_user_empty();
while (1) {
$params = array("uid" => $uid,
"user" => $user,
"email" => $email);
if ($uid > 0) {
// We will get all info about the user not only 'info'
// to populate the cache with all info.
$c = rg_cache_get('user' . '::' . $uid);
if (($c !== FALSE)
&& (isset($c['info']))
&& (isset($c['info']['itime']))) {
$ret = $c['info'];
$ret['exists'] = 1;
rg_user_cosmetic($ret);
break;
}
$sql = "SELECT * FROM users WHERE uid = @@uid@@";
} else if (!empty($user)) {
if (rg_user_ok($user) !== TRUE) {
// cannot exists
$ret['ok'] = 1;
break;
}
$c = rg_cache_get("username_to_uid::" . $user);
if ($c !== FALSE) {
$uid = $c;
continue;
}
$sql = "SELECT * FROM users WHERE username = @@user@@";
} else if (!empty($email)) {
$c = rg_cache_get("email_to_uid::" . $email);
if ($c !== FALSE) {
$uid = $c;
continue;
}
$sql = "SELECT * FROM users WHERE email = @@email@@";
} else {
$ret['ok'] = 1;
break;
}
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error("cannot get info (" . rg_sql_error() . ")");
break;
}
$ret['ok'] = 1;
$rows = rg_sql_num_rows($res);
if ($rows > 0)
$row = rg_sql_fetch_array($res);
rg_sql_free_result($res);
if ($rows == 0) {
rg_log("user_info: user [$uid/$user/$email] not found");
rg_user_set_error("user not found");
break;
}
$ret = array_merge($ret, $row);
rg_cache_set('user' . '::' . $ret['uid'] . '::' . 'info',
$ret, RG_SOCKET_NO_WAIT);
rg_cache_set('username_to_uid::' . $ret['username'],
$ret['uid'], RG_SOCKET_NO_WAIT);
rg_cache_set('email_to_uid::' . $ret['email'], $ret['uid'],
RG_SOCKET_NO_WAIT);
rg_user_cosmetic($ret);
$ret['exists'] = 1;
break;
}
rg_log_exit();
rg_prof_end("user_info");
return $ret;
}
/*
* Update last_seen field
*/
function rg_user_set_last_seen($db, $uid, $ts, $ip)
{
rg_log_enter("user_set_last_seen: uid=$uid ts=$ts");
$ret = FALSE;
while (1) {
$params = array("last_seen" => $ts,
"last_ip" => $ip,
"uid" => $uid);
$sql = 'UPDATE users SET last_seen = @@last_seen@@'
. ', last_ip = @@last_ip@@'
. ' WHERE uid = @@uid@@'
. ' AND last_seen != @@last_seen@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error("cannot update last seen (" . rg_sql_error() . ")");
break;
}
rg_sql_free_result($res);
rg_cache_merge('user' . '::' . $uid . '::' . 'info',
$params, RG_SOCKET_NO_WAIT);
$ret = TRUE;
break;
}
rg_log_exit();
return $ret;
}
/*
* Loads ui based on sid, if possible
*/
function rg_user_login_by_sid($db, &$rg)
{
rg_prof_start("user_login_by_sid");
rg_log_enter("user_login_by_sid: sid=" . $rg['sid']);
// Make sure it is not passed by client
$rg['login_ui'] = rg_user_empty();
$ret = FALSE;
while (1) {
if (empty($rg['sid']))
break;
// Is a pre login sesison?
if (strncmp($rg['sid'], "X", 1) == 0)
break;
$sess = rg_sess_valid($db, $rg['sid']);
if ($sess == FALSE)
break;
$uid = $sess['uid'];
$rg['login_ui'] = rg_user_info($db, $uid, "", "");
if ($rg['login_ui']['exists'] != 1) {
rg_log("Uid $uid does not exists (" . rg_user_error() . ")!");
rg_user_set_error("invalid uid");
break;
}
rg_sess_update($db, $sess);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end("user_login_by_sid");
return $ret;
}
/*
* Test if a password is valid
*/
function rg_user_pass_valid($db, $uid, $pass)
{
rg_log_enter("user_pass_valid: uid=$uid");
$ret = FALSE;
while (1) {
if (empty($pass)) {
rg_user_set_error("password is empty");
break;
}
$ui = rg_user_info($db, $uid, "", "");
if ($ui['exists'] != 1) {
rg_user_set_error("user does not exists");
break;
}
$pass_hash = rg_user_pass($ui['salt'], $pass);
if (strcmp($pass_hash, $ui['pass']) != 0) {
rg_user_set_error("password is not ok");
break;
}
rg_log("Pass is valid.");
$ret = TRUE;
break;
}
rg_log_exit();
return TRUE;
}
/*
* Set session cookie
*/
function rg_user_set_session_cookie($db, $uid, $sess_time, $lock_ip)
{
rg_log_enter("user_set_session_cookie: uid=$uid");
$secure = FALSE;
if (isset($_SERVER['HTTPS']))
$secure = TRUE;
$sid = rg_id(40);
if ($uid > 0)
rg_sess_add($db, $uid, $sid, $sess_time, $lock_ip);
else
$sid = "X" . $sid;
if (isset($_SERVER['SERVER_NAME']))
setcookie('sid', $sid, 0, '/', $_SERVER['SERVER_NAME'],
$secure, TRUE /*httponly*/);
rg_log_exit();
return $sid;
}
/*
* Auto login the user
*/
function rg_user_auto_login($db, $uid, $lock_ip, &$ui)
{
rg_prof_start("user_auto_login");
rg_log_enter("user_auto_login: uid=$uid lock_ip=$lock_ip");
$ret = FALSE;
while (1) {
$ui = rg_user_info($db, $uid, "", "");
if ($ui['ok'] != 1)
break;
if ($ui['exists'] != 1) {
rg_log("user does not exists");
break;
}
rg_user_set_session_cookie($db, $uid, $ui['session_time'], $lock_ip);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end("user_auto_login");
return $ret;
}
/*
* Test if login is OK
*/
function rg_user_login_by_user_pass($db, $user, $pass, $login_token, $lock_ip,
&$ui)
{
global $rg_account_email_confirm;
rg_prof_start("user_login_by_user_pass");
rg_log_enter("user_login_by_user_pass: user=$user"
. " login_token=$login_token lock_ip=$lock_ip");
$ui = rg_user_empty();
$ret = FALSE;
while (1) {
if (empty($user) || empty($pass)) {
rg_user_set_error("invalid user, pass or login token");
rg_log("user or pass are empty");
break;
}
$ui0 = rg_user_info($db, 0, $user, "");
if ($ui0['ok'] != 1)
break;
if ($ui0['exists'] != 1) {
rg_user_set_error("invalid user, pass or login token");
rg_log("user doesn't exists");
break;
}
if ($ui0['deleted'] > 0) {
rg_user_set_error('invalid user, pass or login token');
rg_log('account is deleted');
break;
}
if ($ui0['suspended'] > 0) {
rg_user_set_error("invalid user, pass or login token");
rg_log("account is suspended");
break;
}
if ($rg_account_email_confirm && ($ui0['confirmed'] == 0)) {
rg_user_set_error("invalid user, pass or login token");
rg_log("account is not confirmed");
break;
}
$pass_hash = rg_user_pass($ui0['salt'], $pass);
if (strcmp($pass_hash, $ui0['pass']) != 0) {
rg_user_set_error("invalid user, pass or login token");
rg_log("pass mismatch db:" . $ui0['pass'] . " computed=$pass_hash");
break;
}
$vi = rg_totp_verify_any($db, $ui0['uid'], $login_token);
if ($vi['ok'] != 1) {
rg_user_set_error('login token error: ' . rg_totp_error());
break;
}
//rg_log_ml('DEBUG: vi: ' . print_r($vi, TRUE));
if (($vi['enrolled'] == 1) && ($vi['token_valid'] != 1)) {
rg_user_set_error('invalid user, pass or login token');
rg_log('invalid token');
break;
}
$event = array(
'category' => 2001,
'prio' => 100,
'ui' => $ui0,
'itime' => time());
$r = rg_event_add($db, $event);
if ($r !== TRUE) {
rg_user_set_error('internal error; try again later');
break;
}
rg_event_signal_daemon('', 0);
$ui = $ui0;
rg_user_auto_login($db, $ui['uid'], $lock_ip, $ui);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_start("user_login_by_user_pass");
return $ret;
}
/*
* Suspend an account
* 1=suspend, 0=unsuspend
*/
function rg_user_suspend($db, $rg, $uid, $op)
{
rg_log_enter("user_suspend: uid=$uid, op=$op");
$ret = FALSE;
while (1) {
$x = array();
$x['obj_id'] = $uid;
$x['type'] = 'user';
$x['owner'] = $uid;
$x['uid'] = $rg['login_ui']['uid'];
$x['username'] = $rg['login_ui']['username'];
$x['needed_rights'] = 'S';
$x['ip'] = $rg['ip'];
$x['misc'] = "";
if (rg_rights_allow($db, $x) !== TRUE)
break;
$now = time();
if ($op == 1)
$v = $now;
else
$v = 0;
$params = array("suspeneded" => $v,
"uid" => $uid);
$sql = "UPDATE users SET suspended = @@suspended@@"
. " WHERE uid = @@uid@@";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error("cannot suspend (" . rg_sql_error() . ")");
break;
}
rg_sql_free_result($res);
// update cache
// TODO: what if we cannot update?
rg_cache_set('user' . '::' . $uid . '::' . 'info'
. '::' . 'suspended', $v, RG_SOCKET_NO_WAIT);
break;
}
rg_log_exit();
return $ret;
}
/*
* Make/remove admin
* @op: 1=make, 0=remove
*/
function rg_user_make_admin($db, $rg, $uid, $op)
{
rg_prof_start("user_make_admin");
rg_log_enter("user_make_admin: uid=$uid, op=$op");
$ret = FALSE;
while (1) {
$x = array();
$x['obj_id'] = $uid;
$x['type'] = 'user';
$x['owner'] = $uid;
$x['uid'] = $rg['login_ui']['uid'];
$x['username'] = $rg['login_ui']['username'];
$x['needed_rights'] = 'M';
$x['ip'] = $rg['ip'];
$x['misc'] = "";
if (rg_rights_allow($db, $x) !== TRUE)
break;
$params = array("op" => $op, "uid" => $uid);
$sql = "UPDATE users SET is_admin = @@op@@"
. " WHERE uid = @@uid@@";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error("cannot make admin (" . rg_sql_error() . ")");
break;
}
rg_sql_free_result($res);
rg_cache_set('user' . '::' . $uid . '::' . 'info'
. '::' . 'is_admin', 1, RG_SOCKET_NO_WAIT);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end("user_make_admin");
return $ret;
}
/*
* List users
* TODO: switch to templates.
*/
function rg_user_list($db)
{
rg_prof_start("user_list");
rg_log_enter("user_list");
$ret = FALSE;
while (1) {
$sql = 'SELECT * FROM users'
. ' WHERE deleted = 0'
. ' ORDER BY username';
$res = rg_sql_query($db, $sql);
if ($res === FALSE) {
rg_user_set_error("cannot get info (" . rg_sql_error() . ")!");
break;
}
$ret = "";
$ret .= "<table>\n";
$ret .= "<tr>\n";
$ret .= " <th>User name</th>\n";
$ret .= " <th>Name</th>\n";
$ret .= " <th>E-mail</th>\n";
$ret .= " <th>Admin?</th>\n";
$ret .= " <th>Creation date (UTC)</th>\n";
$ret .= " <th>Plan</th>\n";
$ret .= " <th>Suspended?</th>\n";
$ret .= " <th>Confirmed?</th>\n";
$ret .= " <th>Session time</th>\n";
$ret .= " <th>Last seen (UTC)</th>\n";
$ret .= " <th>Last IP</th>\n";
$ret .= " <th>Rights</th>\n";
$ret .= " <th>Operations</th>\n";
$ret .= "</tr>\n";
while (($row = rg_sql_fetch_array($res))) {
$ret .= "<tr>\n";
$ret .= " <td>" . $row['username'] . "</td>\n";
$ret .= " <td>" . $row['realname'] . "</td>\n";
$ret .= " <td>" . $row['email'] . "</td>\n";
$ret .= " <td>" . ($row['is_admin'] == 1 ? "Yes" : "No") . "</td>\n";
$ret .= " <td>" . gmdate("Y-m-d", $row['itime']) . "</td>\n";
$pi = rg_plan_info($db, $row['plan_id']);
$plan = "Unlimited";
if ($pi['exists'] == 1)
$plan = $pi['name'];
$ret .= " <td>" . $plan . "</td>\n";
$ret .= " <td>" . ($row['suspended'] == 0 ? "No" : "Yes") . "</th>\n";
$ret .= " <td>" . ($row['confirmed'] == 0 ? "No" : gmdate("Y-m-d", $row['confirmed'])) . "</th>\n";
$ret .= " <td>" . $row['session_time'] . "s</td>\n";
$v = $row['last_seen'] == 0 ? "-" : gmdate("Y-m-d", $row['last_seen']);
$ret .= " <td>" . $v . "</td>\n";
$ret .= " <td>" . $row['last_ip'] . "</td>\n";
$v = implode(", ", rg_rights_text("user", $row['rights']));
$ret .= " <td>" . $v . "</td>\n";
// operations
$url = "/op/admin/users";
$url2 = $row['username'];
$ret .= " <td>";
// edit
$ret .= "[<a href=\"$url/edit/$url2\">Edit</a>]";
// suspend
$v = "suspend"; $t = "Suspend";
if ($row['suspended'] > 0) {
$t = "Unsuspend";
$v = "unsuspend";
}
$ret .= "[<a href=\"$url/$v/$url2\">$t</a>]";
// admin
$v = "make_admin"; $t = "Make admin";
if ($row['is_admin'] == 1) {
$t = "Remove admin";
$v = "remove_admin";
}
$ret .= "[<a href=\"$url/$v/$url2\">$t</a>]";
// remove
if ($row['suspended'] > 0)
$ret .= "[<a href=\"$url/remove/$url2\">Remove!</a>]";
$ret .= " </td>";
$ret .= "</tr>\n";
}
$ret .= "</table>\n";
rg_sql_free_result($res);
break;
}
rg_log_exit();
rg_prof_end("user_list");
return $ret;
}
/*
* Returns uid by token, if not expired
*/
function rg_user_forgot_pass_uid($db, $token)
{
rg_prof_start("user_forgot_pass_uid");
rg_log_enter("user_forgot_pass_uid: token=$token");
if (strlen($token) > 20)
$token = substr($token, 0, 20);
$ret = array();
$ret['ok'] = 0;
$ret['uid'] = 0;
while (1) {
$now = time();
$params = array("token" => $token, "now" => $now);
$sql = "SELECT uid FROM forgot_pass"
. " WHERE token = @@token@@"
. " AND expire > @@now@@";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error('cannot lookup token');
break;
}
$ret['ok'] = 1;
$rows = rg_sql_num_rows($res);
if ($rows > 0)
$row = rg_sql_fetch_array($res);
rg_sql_free_result($res);
if ($rows == 0) {
rg_user_set_error('invalid token');
break;
}
$ret['uid'] = $row['uid'];
break;
}
rg_log_exit();
rg_prof_end("user_forgot_pass_uid");
return $ret;
}
/*
* Reset password function (send mail) - helper
*/
function rg_user_forgot_pass_mail_prepare($db, $email)
{
rg_prof_start('user_forgot_pass_mail_prepare');
rg_log_enter("user_forgot_pass_mail_prepare: email=$email");
$ret = array();
$ret['ok'] = 0;
$ret['exists'] = 0;
$expire = time() + 24 * 3600;
$token = rg_id(20);
while (1) {
$r = rg_user_info($db, 0, "", $email);
if ($r['ok'] == 0) {
rg_log("Internal error.");
break;
}
if ($r['exists'] == 0) {
rg_log("User does not exists.");
$ret['ok'] = 1;
break;
}
$uid = $r['uid'];
// store token in database
$params = array("token" => $token,
"uid" => $uid,
"expire" => $expire);
$sql = "INSERT INTO forgot_pass (token, uid, expire)"
. " VALUES (@@token@@, @@uid@@, @@expire@@)";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error("cannot query (" . rg_sql_error() . ")");
break;
}
rg_sql_free_result($res);
$ret['ok'] = 1;
$ret['exists'] = 1;
$ret['token'] = $token;
break;
}
rg_log("DEBUG: user_forgot_pass_mail_prepare: ret=" . rg_array2string($ret));
rg_log_exit();
rg_prof_end('user_forgot_pass_mail_prepare');
return $ret;
}
/*
* Reset password function (send mail)
*/
function rg_user_forgot_pass_mail($db, $rg, $email)
{
global $php_errormsg;
global $rg_admin_name;
rg_prof_start('user_forgot_pass_mail');
rg_log_enter("user_forgot_pass_mail: email=$email");
$ret = array();
$ret['ok'] = 0;
$ret['exists'] = 0;
while (1) {
$r = rg_user_forgot_pass_mail_prepare($db, $email);
if ($r['exists'] != 1) {
rg_log("User does not exists.");
$ret = $r;
break;
}
$ret['exists'] = 1;
$rg['ui'] = $rg['login_ui'];
$rg['ui']['email'] = $email;
$rg['ui']['ignore_confirmed'] = 1;
$rg['forgot_token'] = $r['token'];
$r = rg_mail_template('mail/user/forgot/recover', $rg);
if ($r === FALSE) {
rg_user_set_error(rg_mail_error());
break;
}
$ret['ok'] = 1;
break;
}
//rg_log("DEBUG: user_forgot_pass_mail: ret=" . rg_array2string($ret) . ".");
rg_log_exit();
rg_prof_end('user_forgot_pass_mail');
return $ret;
}
/*
* After reseting the pass, we have to destroy all 'reset pass' requests
*/
function rg_user_forgot_pass_destroy($db, $uid)
{
rg_prof_start('user_forgot_pass_destroy');
rg_log_enter("user_forgot_pass_destroy: uid=$uid");
$ret = FALSE;
while (1) {
$params = array("uid" => $uid);
$sql = "DELETE FROM forgot_pass WHERE uid = @@uid@@";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error("cannot query (" . rg_sql_error() . ")");
break;
}
rg_sql_free_result($res);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end('user_forgot_pass_destroy');
return $ret;
}
/*
* Change the password of a user
*/
function rg_user_set_pass($db, $uid, $pass)
{
rg_prof_start("user_set_pass");
rg_log_enter("user_set_pass: uid=$uid");
$ret = FALSE;
while (1) {
$salt = rg_id(40);
$pass = rg_user_pass($salt, $pass);
$params = array("salt" => $salt,
"pass" => $pass,
"uid" => $uid);
$sql = "UPDATE users SET"
. " salt = @@salt@@"
. ", pass = @@pass@@"
. " WHERE uid = @@uid@@";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error("cannot update pass (" . rg_sql_error() . ")");
break;
}
rg_sql_free_result($res);
unset($params['uid']);
rg_cache_merge('user' . '::' . $uid . '::' . 'info',
$params, RG_SOCKET_NO_WAIT);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end("user_set_pass");
return $ret;
}
/*
* Confirm account creation
*/
function rg_user_confirm($db, $token)
{
rg_prof_start("user_confirm");
rg_log_enter("user_confirm: token=$token");
$now = time();
$token = preg_replace("/[^A-Za-z0-9]/", '', $token);
$ret = FALSE;
while (1) {
if (empty($token)) {
rg_user_set_error("invalid token");
break;
}
$params = array("token" => $token);
$sql = 'SELECT uid, confirmed FROM users'
. ' WHERE confirm_token = @@token@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error('cannot search for token');
break;
}
$rows = rg_sql_num_rows($res);
if ($rows == 1)
$row = rg_sql_fetch_array($res);
rg_sql_free_result($res);
if ($rows != 1) {
rg_user_set_error("cannot find token");
break;
}
$uid = $row['uid'];
if ($row['confirmed'] == 0) {
// "< 2" because we mark with "1" if "no need to confirm"
$params = array("confirmed" => $now, "uid" => $uid);
$sql = 'UPDATE users SET confirmed = @@confirmed@@'
. ' WHERE uid = @@uid@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error('cannot set confirmed');
break;
}
rg_sql_free_result($res);
rg_cache_set('user' . '::' . $uid . '::' . 'info'
. '::' . 'confirmed', $now, RG_SOCKET_NO_WAIT);
}
$ret = $uid;
break;
}
rg_log_exit();
rg_prof_end("user_confirm");
return $ret;
}
/*
* Add a suggestion to database
*/
function rg_user_suggestion($db, $uid, $suggestion)
{
rg_prof_start("user_suggestion");
rg_log_enter("user_suggestion: uid=$uid suggestion=$suggestion");
$ret = FALSE;
while (1) {
$params = array('uid' => $uid,
'itime' => time(),
'sug' => $suggestion);
$sql = "INSERT INTO suggestions (uid, itime, suggestion)"
. " VALUES (@@uid@@, @@itime@@, @@sug@@)";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_user_set_error("cannot add suggestion (" . rg_sql_error() . ")");
break;
}
rg_sql_free_result($res);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end("user_suggestion");
return $ret;
}
/*
* Checks if a user is over limit.
* Returns maximum allowed in @max.
*/
function rg_user_over_limit($db, $ui, &$max)
{
$pi = rg_plan_info($db, $ui['plan_id']);
if ($pi['exists'] != 1) {
rg_internal_error("Invalid plan.");
return FALSE;
}
$max = $pi['disk_mb'];
if ($max == 0)
return FALSE;
if ($ui['disk_used_mb'] >= $max)
return TRUE;
return FALSE;
}
/*
* High level functions
*/
/*
* High-level function for editing a user
*/
function rg_user_edit_high_level($db, &$rg)
{
global $rg_session_time;
rg_log("user_edit_high_level");
rg_log_ml("user_edit_high_level:rg:" . print_r($rg, TRUE));
$ret = "";
if (($rg['target_ui']['uid'] == 0)
&& ($rg['rg_account_allow_creation'] != 1)) {
$ret .= rg_template("user/create_na.html", $rg, TRUE /* xss */);
return $ret;
}
$owner = $rg['target_ui']['uid'];
if ($owner > 0) {
$x = array();
$x['obj_id'] = $rg['target_ui']['uid'];
$x['type'] = 'user';
$x['owner'] = $owner;
$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("access_denied.html", $rg, TRUE /* xss */);
return $ret;
}
}
if ($rg['target_ui']['uid'] > 0)
$rg['create_mode'] = 0;
else
$rg['create_mode'] = 1;
if ($rg['doit'] == 0) {
if ($rg['target_ui']['uid'] > 0) {
// TODO: check also access rights?
$ui = $rg['target_ui'];
} else {
// Defaults
$ui = array();
$ui['uid'] = 0;
$ui['username'] = "";
$ui['realname'] = "";
$ui['email'] = "";
$ui['pass'] = "";
$ui['pass2'] = "";
$ui['is_admin'] = "0";
$ui['rights'] = rg_rights_checkboxes("user", "rights", "C"); // TODO
$ui['plan_id'] = 0;
$ui['session_time'] = $rg_session_time;
$ui['tos'] = 1;
}
}
$errmsg = array();
$load_form = TRUE;
while (1) {
if ($rg['doit'] == 0)
break;
$ui = array();
$ui['uid'] = $rg['target_ui']['uid'];
$ui['username'] = rg_var_str("username");
$ui['realname'] = rg_var_str("realname");
$ui['email'] = rg_var_str("email");
$ui['pass'] = rg_var_str("pass");
$ui['pass2'] = rg_var_str("pass2");
$ui['is_admin'] = rg_var_bool("is_admin");
$ui['rights'] = "C"; // TODO
$ui['plan_id'] = rg_var_uint("plan_id");
$ui['session_time'] = rg_var_uint("session_time");
$ui['confirmed'] = 0;
if ($rg['no_tos'] == 1)
$ui['tos'] = 1;
else
$ui['tos'] = rg_var_uint('tos');
$ui['ask_for_email_confirmation'] = 0;
$ui['confirm_token'] = rg_id(20);
if ($rg['target_ui']['uid'] > 0) {
if (strcasecmp($rg['target_ui']['email'], $ui['email']) != 0) {
$ui['ask_for_email_confirmation'] = 1;
} else {
// reuse
$ui['confirm_token'] = $rg['target_ui']['confirm_token'];
}
}
if ($ui['tos'] != 1) {
$errmsg[] = rg_template('user/tos_deny.html', $rg, TRUE /*xss*/);
break;
}
if (!rg_valid_referer()) {
$errmsg[] = "invalid referer; try again";
break;
}
if (!rg_token_valid($db, $rg, 'user_edit_hl', FALSE)) {
$errmsg[] = "invalid token; try again";
break;
}
if (($rg['login_ui']['is_admin'] != 1) && ($ui['is_admin'] != 0)) {
$errmsg[] = "you are not admin, you cannot give admin rights";
break;
}
if ($ui['uid'] == 0) {
$_ui = rg_user_info($db, 0, $ui['username'], '');
if ($_ui['ok'] != 1) {
$errmsg[] = rg_user_error();
break;
}
if ($_ui['exists'] == 1) {
$errmsg[] = "user already exists";
break;
}
}
// Security warning - a user can pass "pass" var to me avoiding
// being asked for old pass
if ($ui['uid'] > 0) {
if (!empty($ui['pass']))
rg_security_violation_no_exit("User tried to"
. " change pass using 'edit info' page.");
$ui['pass'] = '';
}
$r = rg_user_edit($db, $ui);
if ($r === FALSE) {
$errmsg[] = rg_user_error();
break;
}
// TODO: should we just redirect to login page?
// TODO: or to user page if there is no need to confirm the account?
if ($ui['uid'] == 0)
$ret = rg_template("user/create_ok.html", $rg, TRUE /* xss */);
else
$ret = rg_template("user/edit_ok.html", $rg, TRUE /* xss */);
$load_form = FALSE;
break;
}
if ($load_form) {
$rg = array_merge($rg, $ui);
$rg['HTML:select_plan'] = rg_plan_select($db, $ui['plan_id']);
$rg['HTML:checkbox_rights'] = rg_rights_checkboxes("user",
"rights", $ui['rights']);
$rg['HTML:errmsg'] = rg_template_errmsg($errmsg);
$rg['rg_form_token'] = rg_token_get($db, $rg, 'user_edit_hl');
$ret .= rg_template("user/add_edit.html", $rg, TRUE /* xss */);
}
return $ret;
}
/*
* API dispatch function
*/
function rg_user_api($db, $a)
{
rg_prof_start('user_api');
rg_log_enter('user_api');
$cmd = $a['cmd'];
$ret = array();
while (1) {
if (isset($a['user'])) {
$ui = rg_user_info($db, 0, $a['user'], '');
} else if (isset($a['uid'])) {
$ui = rg_user_info($db, $a['uid'], '', '');
} else {
$ret['error'] = 'no uid=/user= specified';
break;
}
if ($ui['exists'] != 1) {
$ret['error'] = 'invalid user or no rights';
break;
}
// TODO: allow also a master user to access user info
if (($a['cui']['is_admin'] != 1)
&& ($ui['uid'] != $a['cui']['uid'])) {
$ret['error'] = 'invalid user or no rights';
rg_log('user has no rights');
break;
}
if (strcmp($cmd, 'user_info') == 0) {
$ret = $ui;
break;
}
if (strcmp($cmd, 'user_ssh_keys_list') == 0) {
$ret = rg_keys_list($db, $a['cui']);
break;
}
if (strcmp($cmd, 'user_rights_list') == 0) {
$params = array('who' => $ui['uid']);
$sql = 'SELECT * FROM rights'
. ' WHERE who = @@who@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
$ret['error'] = 'internal error';
break;
}
$ret = array();
while (($row = rg_sql_fetch_array($res))) {
rg_rights_cosmetic($db, $row);
$ret[] = $row;
}
rg_sql_free_result($res);
break;
}
if (strcmp($cmd, 'user_wh_list') == 0) {
$r = rg_wh_list($db, $ui['uid']);
if ($r['ok'] !== 1) {
$ret['error'] = rg_wh_error();
break;
}
rg_wh_cosmetic($r['list']);
// list is indexed by hook_id, remove this
$ret = array();
foreach ($r['list'] as $index => $d)
$ret[] = $d;
break;
}
$ret['error'] = 'invalid command';
break;
}
rg_log_exit();
rg_prof_end('user_api');
return $ret;
}
/*
* Add an ask-for-email-confirmation event to queue
*/
function rg_user_ask_for_email_confirmation($db, $uid)
{
$ui = rg_user_info($db, $uid, '', '');
if ($ui['exists'] != 1)
return FALSE;
$ev = array(
'category' => 'ask-email-confirmation',
'prio' => 200,
'ui' => $ui,
'base_url' => rg_base_url()
);
$r = rg_event_add($db, $ev);
if ($r !== TRUE)
return FALSE;
rg_event_signal_daemon('', 0);
return TRUE;
}
/*
* Transforms a list of uids into a list with full user info
*/
function rg_user_list_to_full_info($db, $list)
{
$ret = array();
foreach ($list as $index => $uid) {
$ret['ui'] = rg_user_info($db, $uid, '', '');
if ($ret['ui']['ok'] != 1)
return FALSE;
if ($ret['ui']['exists'] != 1)
continue;
}
return $ret;
}
/*
* This function deals with incoming compressed input.
* Please note that if the webserver was configured without an input filter
* (like apache's SetInputFilter DEFLATE), this function will
* take care of it.
* Returns the uncompressed stream.
*/
function rg_process_gzip($content_length, $content_encoding)
{
rg_log('DEBUG: process_gzip: cl=' . $content_length
. ' content_encoding=' . $content_encoding);
$data_in = @file_get_contents('php://input');
if ($data_in === FALSE) {
rg_log('Cannot get in stream (1)!');
return FALSE;
}
rg_log('DEBUG: data_in: ' . substr($data_in, 0, 32));
if (strcmp($content_encoding, 'gzip') == 0) {
$data_in = @gzdecode($data_in);
if ($data_in === FALSE) {
rg_log('Cannot decompress!');
return FALSE;
}
rg_log('DEBUG: after decompress: ' . substr($data_in, 0, 32));
}
return $data_in;
}
/*
* Deals with push/fetch by HTTP(S)
*/
function rg_user_http_git($db, $rg, $paras)
{
rg_prof_start('user_http_git');
rg_log_enter('user_http_git');
if (strcasecmp($rg['ct'], 'application/x-git-upload-pack-request') == 0)
$service = 'git-upload-pack';
else if (strcasecmp($rg['ct'], 'application/x-git-receive-pack-request') == 0)
$service = 'git-receive-pack';
else
$service = isset($_REQUEST['service']) ? $_REQUEST['service'] : '';
rg_log('DEBUG: service=' . $service);
$protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
$ret = FALSE;
while (1) {
if (empty($service))
break;
$ret = TRUE;
set_time_limit(3600);
rg_log_ml('paras: ' . print_r($paras, TRUE));
if (count($paras) < 2) {
//TODO
}
$prefix = array_shift($paras);
if (strcmp($prefix, 'user') != 0) {
$user = $prefix;
$prefix = '';
} else {
$user = array_shift($paras);
}
$repo = array_shift($paras);
$file = implode('/', $paras);
rg_log('DEBUG: file=' . $file);
header('Expires: Fri, 01 Jan 1980 00:00:00 GMT');
header('Pragma: no-cache');
header('Cache-Control: no-cache, max-age=0, must-revalidate');
// if user is valid, retry auth
// if user is not valid, consider anonymous
$authd = FALSE;
$u = '';
$empty_user = TRUE;
$auth_ui = rg_user_empty();
while (isset($_SERVER['PHP_AUTH_USER'])
&& isset($_SERVER['PHP_AUTH_PW'])) {
$u = $_SERVER['PHP_AUTH_USER'];
$p = $_SERVER['PHP_AUTH_PW'];
$empty_user = FALSE;
rg_log('DEBUG: HTTP auth: u=' . $u);
if (empty($u))
break;
$auth_ui = rg_user_info($db, 0, $u, '');
if ($auth_ui['ok'] !== 1) {
rg_log('DEBUG: set errror (500): ' . rg_user_error());
header('X-Rocketgit-Error: ' . rg_user_error());
header($protocol . ' 500 Internal server error');
break;
}
$authd = rg_user_login_by_user_pass($db, $u, $p,
'' /*login_token*/, TRUE /*lock IP*/,
$rg['login_ui']);
break;
}
$host = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
$r = rg_repo_fetch_push_helper($db, $host, $rg['ip'],
$rg['login_ui'], $prefix, $user, $repo, $service);
rg_log_ml('DEBUG: repo_fetch_push_helper: ' . print_r($r, TRUE));
if ($r['ok'] !== 1) {
rg_log('DEBUG: set errror: ' . $r['error']);
header('X-Rocketgit-Error: ' . $r['error']);
if (isset($r['owner_ui'])) {
if ($r['owner_ui']['ok'] != 1) {
header($protocol . ' 500 Internal server error');
break;
}
if ($r['owner_ui']['exists'] != 1) {
header($protocol . ' 404 Not found');
break;
}
}
if (isset($r['ri'])) {
if ($r['ri']['ok'] != 1) {
header($protocol . ' 500 Internal server error');
break;
}
if (($r['ri']['exists'] != 1)
|| ($r['ri']['deleted'] > 0)) {
header($protocol . ' 404 Not found');
break;
}
}
header($protocol . ' 500 Internal server error');
break;
}
$repo_path = $r['repo_path'];
if ($r['allow'] !== 1) {
rg_log('DEBUG: allow != 1 => 401');
// Connecting user has no rights to push, not even anon.
// The user be authed at this point, but may try another
// user/pass combination.
header($protocol . ' 401 Unauthorized status');
header('WWW-Authenticate: Basic'
. ' realm="Use empty user if you have no account"');
break;
}
rg_log('DEBUG: push_allowed=' . $r['push_allowed']
. ' empty_user=' . ($empty_user ? 'yes' : 'no')
. ' authd=' . ($authd === TRUE ? 'yes' : 'no')
. ' exists=' . $auth_ui['exists']
. ' push=' . $r['push']);
if (($r['push'] === 1) && ($r['push_allowed'] !== 1)) {
// We have only anon push rights at this point.
// If user is correct, but password not, we will ask
// the user to try again. If user is not correct,
// we will go on with anon push access.
if ($empty_user
|| (($authd === FALSE) && ($auth_ui['exists'] == 1))) {
rg_log('DEBUG: send 401');
header($protocol . ' 401 Unauthorized status');
header('WWW-Authenticate: Basic'
. ' realm="Use empty user if you have no account"');
break;
}
} else if ($r['push'] === 1) {
rg_log('DEBUG: user has full push rights');
} else {
rg_log('DEBUG: it is a fetch');
}
$content_length = rg_var_str('CONTENT_LENGTH');
$content_encoding = rg_var_str('HTTP_CONTENT_ENCODING');
rg_log('DEBUG: cl=' . $content_length . ' ce=' . $content_encoding);
if (strcmp($file, 'info/refs') == 0) {
rg_log('DEBUG: info/refs');
// TODO: we should allow this only if the connecting user has
// fetch rights!
header('Content-Type: application/x-'
. $service . '-advertisement');
echo rg_git_pack('# service=' . $service . "\n");
echo rg_git_flush();
$run = '/usr/libexec/git-core/' . $service
. ' --stateless-rpc --advertise-refs'
. ' ' . escapeshellarg($repo_path);
$e = rg_exec($run, '', 'rg_echo', 'rg_git_band_2');
if ($e['code'] != 0)
rg_log('Error executing command: ' . $e['errmsg']);
} else if (strcasecmp($rg['ct'], 'application/x-git-upload-pack-request') == 0) {
rg_log('DEBUG: git-upload-pack...');
header('Content-Type: application/x-git-upload-pack-result');
// TODO: seems I cannot do this here -
// seems remote receive-pack expects ACK/NAK first:
// fatal: git fetch_pack: expected ACK/NAK, got '...
/*
rg_git_info_pack("\x02", '== Welcome to RocketGit! ==');
rg_git_info_pack("\x02", 'you are connecting from IP '
. $rg['ip'] . '.');
// If user does not correct to the correct URL, correct them
if (!empty($host) && (strcasecmp($host, $rg['rg_http_host_no_port']) != 0))
rg_git_info_pack("\x02", 'Please use ' . $rg['rg_http_host_no_port']
. ' instead of ' . $host . '.');
putenv('ROCKETGIT_SHOW_INFO=0');
*/
$run = '/usr/libexec/git-core/git-upload-pack'
. ' --stateless-rpc'
. ' ' . escapeshellarg($repo_path);
$data_in = rg_process_gzip($content_length, $content_encoding);
if ($data_in === FALSE)
break;
$e = rg_exec($run, $data_in, 'rg_echo', 'rg_git_band_2');
if ($e['code'] != 0)
rg_log('Error executing command: ' . $e['errmsg']);
} else if (strcasecmp($rg['ct'], 'application/x-git-receive-pack-request') == 0) {
rg_log('DEBUG: git-receive-pack...');
header('Content-Type: application/x-git-receive-pack-result');
rg_git_info_pack("\x02", '== Welcome to RocketGit! ==');
rg_git_info_pack("\x02", 'you are connecting from IP '
. $rg['ip'] . '.');
// If user does not connect to the correct URL, correct them
if (!empty($host) && (strcasecmp($host, $rg['rg_http_host_no_port']) != 0))
rg_git_info_pack("\x02", 'Please use ' . $rg['rg_http_host_no_port']
. ' instead of ' . $host . '.');
putenv('ROCKETGIT_SHOW_INFO=0');
$run = '/usr/libexec/git-core/git-receive-pack'
. ' --stateless-rpc'
. ' ' . escapeshellarg($repo_path);
$data_in = rg_process_gzip($content_length, $content_encoding);
if ($data_in === FALSE)
break;
$e = rg_exec($run, $data_in, 'rg_echo', 'rg_echo');
if ($e['code'] != 0)
rg_log('Error executing command: ' . $e['errmsg']);
} else {
rg_log('Unknown service!');
// TODO: send some errors, also above
}
break;
}
rg_log_exit();
rg_prof_end('user_http_git');
return $ret;
}
/*
* Delete account high level function
*/
function rg_user_delete_account_high_level($db, $rg, $paras)
{
$ret = '';
$are_you_sure = rg_var_uint('are_you_sure');
$errmsg = array();
$show_form = TRUE;
while (1) {
if ($rg['doit'] != 1)
break;
if ($are_you_sure == 0) {
$ret .= rg_template('user/settings/delete/no.html',
$rg, TRUE/*xss*/);
$show_form = FALSE;
break;
}
if (!rg_valid_referer()) {
$errmsg[] = 'invalid referer; try again';
break;
}
if (!rg_token_valid($db, $rg, 'delete_account', FALSE)) {
$errmsg[] = 'invalid token; try again';
break;
}
$r = rg_user_remove($db, $rg, $rg['login_ui']['uid']);
if ($r !== TRUE) {
$errmsg[] = rg_user_error();
break;
}
$ret .= rg_template('user/settings/delete/done.html',
$rg, TRUE/*xss*/);
$show_form = FALSE;
break;
}
if ($show_form) {
// hints
$hints = array();
$hints[]['HTML:hint'] = rg_template('hints/user/delete_account.html',
$rg, TRUE /*xss*/);
$rg['HTML:hints'] = rg_template_table('hints/list', $hints, $rg);
$rg['HTML:errmsg'] = rg_template_errmsg($errmsg);
$rg['rg_form_token'] = rg_token_get($db, $rg, 'delete_account');
$ret .= rg_template('user/settings/delete/sure.html',
$rg, TRUE/*xss*/);
}
return $ret;
}
?>