<?php
require_once(__DIR__ . '/util2.inc.php');
require_once(__DIR__ . '/log.inc.php');
require_once(__DIR__ . '/sql.inc.php');
require_once(__DIR__ . '/sess.inc.php');
require_once(__DIR__ . '/rights.inc.php');
require_once(__DIR__ . '/events.inc.php');
require_once(__DIR__ . '/cache.inc.php');
require_once(__DIR__ . '/mail.inc.php');
require_once(__DIR__ . '/plan.inc.php');
require_once(__DIR__ . '/totp.inc.php');
require_once(__DIR__ . '/stats.inc.php');
require_once(__DIR__ . '/user/pay/main.inc.php');
$rg_user_rights = array(
"C" => "Create repository",
"A" => "Create user",
"E" => "Edit user",
"R" => "Remove user",
"S" => "Suspend user",
"G" => "Grant rights",
'K' => 'Revoke 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;
}
/* Functions for template system */
function rg_user_template_ui_login($param)
{
$r = rg_ui_login();
rg_user_cosmetic($r);
$ret = array();
if (isset($r['HTML:' . $param])) {
$ret['value'] = $r['HTML:' . $param];
$ret['html'] = 1;
return $ret;
}
if (isset($r[$param])) {
$ret['value'] = $r[$param];
$ret['html'] = 0;
return $ret;
}
rg_log_debug('ui_page: param=' . $param . ' not found; r: ' . print_r($r, TRUE));
return '?';
}
rg_template_func('UI_LOGIN', 'rg_user_template_ui_login');
function rg_user_template_ui_page($param)
{
$r = rg_ui_page();
rg_user_cosmetic($r);
$ret = array();
if (isset($r['HTML:' . $param])) {
$ret['value'] = $r['HTML:' . $param];
$ret['html'] = 1;
return $ret;
}
if (isset($r[$param])) {
$ret['value'] = $r[$param];
$ret['html'] = 0;
return $ret;
}
rg_log_debug('ui_page: param=' . $param . ' not found; r: ' . print_r($r, TRUE));
return '?';
}
rg_template_func('UI_PAGE', 'rg_user_template_ui_page');
/*
* Event functions
*/
$rg_user_functions = array(
2000 => "rg_user_event_new",
'user_event_new' => 'rg_user_event_new',
2001 => "rg_user_event_login",
'user_event_login' => "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',
// new new style
'user_event_new' => 'rg_user_event_new',
'user_event_login' => 'rg_user_event_login',
'user_event_notify_user' => 'rg_user_event_notify_user',
'user_event_rename' => 'rg_user_event_rename',
'user_link_by_name' => 'rg_user_link_by_name'
);
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));
$ev['ui_login']['ignore_confirmed'] = 1;
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_debug('user_event_new: event: ' . print_r($event, TRUE));
$event['op'] = 'new';
// create link by name
$ret[] = array_merge($event,
array(
'category' => 'user_link_by_name',
'source' => 'user_event_new',
'prio' => 500)
);
// notify user
$ret[] = array_merge($event,
array('category' => 'user_event_notify_user', 'prio' => 200));
return $ret;
}
/*
* Event for login
*/
function rg_user_event_login($db, $ev)
{
$ret = FALSE;
while (1) {
$r = rg_user_set_last_seen($db, $ev['ui_login']['uid'],
$ev['itime'], $ev['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' => 'user_event_notify_user',
'source' => 'user_event_rename',
'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,
'email' => ''
);
}
/*
* 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)
{
rg_log("user_link_by_name: event=" . rg_array2string($event));
$by_id = rg_user_path_by_id($event['ui_login']['uid']);
if (!is_dir($by_id) && (mkdir($by_id, 0700, TRUE) === FALSE)) {
rg_user_set_error("cannot mkdir by_id=$by_id (" . rg_php_err() . ")");
return FALSE;
}
$by_name = rg_user_path_by_name($event['ui_login']['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 (" . rg_php_err() . ")");
return FALSE;
}
$by_id_rel = rg_user_path_by_id_rel($event['ui_login']['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 (" . rg_php_err() . ")!");
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 (mb_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 (strcmp(substr($user, 0, 1), '-') == 0) {
rg_user_set_error('invalid user name'
. ' (cannot start with minus)');
return FALSE;
}
if (strpos($user, ':') !== FALSE) {
rg_user_set_error('invalid user name'
. ' (cannot contain :)');
return FALSE;
}
if (rg_chars_allow($user, $rg_user_allow, $invalid) !== 1) {
rg_user_set_error("invalid user name (invalid chars: '$invalid')");
return FALSE;
}
$len = mb_strlen($user);
if ($len < $rg_user_min_len) {
rg_user_set_error("user name too short (shorter than $rg_user_min_len)");
return FALSE;
}
if ($len > $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_sql_free_result($res);
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_login, $new_name)
{
rg_prof_start("user_rename");
rg_log_enter("user_rename: from=[" . $ui_login['username'] . "]"
. " to=[" . $new_name . "]...");
$user_path = rg_user_path_by_id($ui_login['uid']);
$old_path = rg_user_path_by_id_rel($ui_login['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 (" . rg_php_err() . ")!");
break;
}
}
// TOOD: transaction?
$r = rg_user_insert_rename($db, $ui_login['uid'], $new_name);
if ($r !== TRUE)
break;
// TODO: Check if all parameters are used.
$event = array(
'category' => 'user_event_rename',
'source' => 'user_rename',
'prio' => 50,
'ui_login' => $ui_login,
'rename_from' => $ui_login['username'],
'rename_to' => $new_name
);
$r = rg_event_add($db, $event);
if ($r !== TRUE) {
rg_user_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;
}
/*
* Helper for rg_user_edit
*/
function rg_user_edit_no_check($db, $d)
{
rg_prof_start('user_edit_no_check');
rg_log_enter('user_edit_no_check');
if (rg_debug() > 0)
rg_log('d: ' . rg_array2string($d));
$ret = array('ok' => 0, 'already_exists' => 0);
while (1) {
$add = $d['uid'] == 0;
$update_pass = $add || !empty($d['pass']);
if ($update_pass) {
if (strcmp($d['pass'], $d['pass2']) != 0) {
$ret['errmsg'] = 'passwords are not the same';
break;
}
// We do not need it anymore
unset($d['pass2']);
if (rg_user_pass_ok($d['pass']) !== TRUE) {
$ret['errmsg'] = rg_user_error();
break;
}
$d['salt'] = rg_id(40);
$d['pass'] = rg_user_pass($d['salt'], $d['pass']);
} else {
// No need to keep them in memory
unset($d['pass']);
unset($d['pass2']);
}
if ($add) {
$d['itime'] = time();
if (!isset($d['suspended']))
$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, last_ip)'
. ' VALUES (@@username@@, @@realname@@, @@salt@@'
. ', @@pass@@, @@email@@, @@itime@@'
. ', @@is_admin@@, @@rights@@, @@session_time@@'
. ', @@confirmed@@, @@confirm_token@@, @@plan_id@@'
. ', @@suspended@@, @@last_seen@@'
. ', @@disk_used_mb@@, @@deleted@@'
. ', @@last_ip@@)'
. ' RETURNING uid';
$ignore = array(RG_SQL_UNIQUE_VIOLATION);
} else { // edit
$salt_pass_add = "";
if ($update_pass)
$salt_pass_add = ', pass = @@pass@@'
. ', 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@@';
$ignore = array();
}
$res = rg_sql_query_params_ignore($db, $sql, $d,
$ignore, $ignore_kicked);
if ($res === FALSE) {
if (!$ignore_kicked) {
$ret['errmsg'] = 'cannot insert/update user info';
break;
}
rg_log_debug('username already present');
$ret['already_exists'] = 1;
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);
$d2 = $d;
// we need to be able to send the welcome mail
$d2['ignore_confirmed'] = 1;
$event = array(
'category' => 'user_event_new',
'source' => 'user_edit_no_check',
'prio' => 50,
'ui_login' => $d2,
'base_url' => rg_base_url($db, '', '')
);
$r = rg_event_add($db, $event);
if ($r === FALSE) {
$ret['errmsg'] = 'cannot add event';
break;
}
rg_event_signal_daemon('', 0);
} else { // edit
rg_cache_merge('user' . '::' . $d['uid']
. '::' . 'info', $d, RG_SOCKET_NO_WAIT);
if (isset($d['ask_for_email_confirmation'])
&& ($d['ask_for_email_confirmation'] == 1)) {
$r = rg_user_ask_for_email_confirmation($db, $d);
if ($r === FALSE) {
$ret['errmsg'] = 'cannot add event';
break;
}
}
}
// TODO: should we cache here the user_by_uid and user_by_name
$ret['ui_login'] = $d;
$ret['ok'] = 1;
break;
}
rg_log_exit();
rg_prof_end('user_edit_no_check');
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) {
if (rg_user_ok($d['username']) !== TRUE)
break;
// TODO: check rights
// TODO - check if user is allowed to give passed rights
if (isset($d['ask_for_email_confirmation'])
&& ($d['ask_for_email_confirmation'] == 1))
$d['confirmed'] = 0;
$r = rg_user_edit_no_check($db, $d);
if ($r['ok'] !== 1) {
rg_user_set_error($r['errmsg']);
break;
}
$ret = $r['ui_login']['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_prof_start('user_remove');
rg_log_enter('user_remove');
$ret = FALSE;
while (1) {
$ui_login = rg_ui_login();
$x = array();
$x['type'] = 'user';
$x['obj_id'] = $ui_login['uid'];
$x['owner'] = $ui_login['uid'];
$x['needed_rights'] = 'R';
if (rg_rights_allow($db, $x) !== TRUE) {
rg_user_set_error('no rights');
break;
}
$now = time();
$params = array(
'now' => $now,
'uid' => $ui_login['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' . '::' . $ui_login['uid'] . '::' . 'info'
. '::' . 'deleted', $now, RG_SOCKET_NO_WAIT);
// invalidate session
rg_sess_destroy($db);
$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');
$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['ok'] = 1;
$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_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_prof_start("user_login_by_sid");
rg_log_enter('user_login_by_sid');
$ret = FALSE;
while (1) {
$sess = rg_sess_valid($db);
if ($sess === FALSE)
break;
$uid = $sess['uid'];
$ui_login = rg_user_info($db, $uid, "", "");
if ($ui_login['exists'] != 1) {
rg_log("Uid $uid does not exists (" . rg_user_error() . ")!");
rg_user_set_error("invalid uid");
break;
}
rg_ui_login_set($ui_login);
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_log_debug('ui: ' . print_r($ui, TRUE));
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;
}
$ret = TRUE;
break;
}
rg_log_exit();
return TRUE;
}
/*
* Set session cookie
*/
function rg_user_set_session_cookie($db, $uid, $sess_time, $lock_ip, $https,
$domain)
{
rg_log_enter("user_set_session_cookie: uid=$uid domain=$domain");
if ($https) {
$secure = TRUE;
$cookie_name = 'sids';
} else {
$secure = FALSE;
$cookie_name = 'sidu';
}
$sid = rg_id(40);
if ($uid > 0)
rg_sess_add($db, $uid, $sid, $sess_time, $lock_ip);
else
$sid = "X" . $sid;
rg_log_debug('setting cookie [' . $cookie_name . '] to [' . $sid . ']');
setcookie($cookie_name, $sid, 0, '/', $domain, $secure,
TRUE /*httponly*/);
rg_log_exit();
return $sid;
}
/*
* Auto login the user
*/
function rg_user_auto_login($db, $uid, $lock_ip, $https, $domain)
{
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_debug('ui: ' . print_r($ui, TRUE));
rg_log("user does not exists");
break;
}
rg_ui_login_set($ui);
rg_user_set_session_cookie($db, $uid, $ui['session_time'],
$lock_ip, $https, $domain);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end("user_auto_login");
return $ret;
}
/*
* Helper for rg_user_login_by_user_pass for db
*/
function rg_user_login_by_user_pass_db($db, $user, $pass, $lock_ip, $https,
$domain)
{
global $rg_account_email_confirm;
rg_prof_start('user_login_by_user_pass_db');
rg_log_enter('user_login_by_user_pass_db: user=' . $user
. ' lock_ip=' . $lock_ip . ' https=' . $https
. ' domain=' . $domain);
$ret = FALSE;
while (1) {
$ui0 = rg_user_info($db, 0, $user, "");
if ($ui0['ok'] != 1)
break;
if ($ui0['exists'] != 1) {
rg_log_debug('ui0: ' . print_r($ui0, TRUE));
rg_user_set_error('invalid user, pass or login token');
rg_log('user does not exists');
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;
}
rg_ui_login_set($ui0);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end('user_login_by_user_pass_db');
return $ret;
}
/*
* Authorize a user
*/
function rg_user_login_by_user_pass_helper($db, $user, $pass, $login_token,
$lock_ip, $https, $domain)
{
global $rg_login_functions;
rg_prof_start('user_login_by_user_pass_helper');
rg_log_enter('user_login_by_user_pass_helper: user=' . $user
. ' login_token=' . $login_token . ' lock_ip=' . $lock_ip
. ' https=' . $https . ' domain=' . $domain);
$ret = array();
$ret['ok'] = 0;
while (1) {
if (empty($user) || empty($pass)) {
rg_log("user or pass are empty");
$ret['errmsg'] = 'invalid user, pass or login token';
break;
}
$ui = rg_user_empty();
while (1) {
$r = rg_user_login_by_user_pass_db($db, $user, $pass,
$lock_ip, $https, $domain);
if ($r === TRUE) {
$ui = rg_ui_login();
break;
}
$ret['errmsg'] = rg_user_error();
// Try external authentication (LDAP etc.)
foreach ($rg_login_functions as $funcs) {
$r = $funcs['login']($db, $user, $pass, $ui);
if ($r['ok'] === 1) {
unset($ret['errmsg']);
$post = $r['post'];
break;
}
}
if (isset($ret['errmsg']))
break;
// We found a good user/pass combination
rg_log_debug('post: ' . print_r($post, TRUE));
rg_log_debug('ui returned by login callback: '
. print_r($ui, TRUE));
$check_for_changes = TRUE;
if ($ui['uid'] == 0) {
// User found, but not in 'users' table.
// We update the table and call the callback
// to set uid into external auth table.
rg_log_debug('user not in \'users\' table (ui[uid] 0), add it');
$ui['last_ip'] = rg_ip();
$r = rg_user_edit_no_check($db, $ui);
if ($r['ok'] !== 1) {
if ($r['already_exists'] == 0) {
$ret['errmsg'] = $r['errmsg'];
break;
}
rg_log_debug('user is already present'
. '; lookup by username');
$ui = rg_user_info($db, 0, $ui['username'], '');
if ($ui['ok'] !== 1) {
$ret['errmsg'] = rg_user_error();
break;
}
} else {
$ui['uid'] = $r['ui_login']['uid'];
$check_for_changes = FALSE;
}
// This will update external auth cache uid.
if (isset($funcs['post_login'])) {
$r = $funcs['post_login']($db,
$ui['uid'], $post);
// We will not return an error here
if ($r['ok'] !== 1)
rg_log('post_login function'
. ' returned error: '
. $r['errmsg']);
}
} else {
// User found, but some fields may have old
// values and we may need to update 'users'.
rg_log_debug('user exists in db (ui[uid]!=0), load info');
$ui = rg_user_info($db, $ui['uid'], '', '');
if ($ui['ok'] !== 1) {
$ret['errmsg'] = rg_user_error();
break;
}
}
// Some fields could change
$do_update = FALSE;
while (1) {
if (!$check_for_changes)
break;
if (strcmp($ui['username'], $post['username']) != 0) {
rg_log_debug('different username: ' . $ui['username'] . ' != ' . $post['username']);
$ui['username'] = $post['username'];
$do_update = TRUE;
break;
}
if (strcmp($ui['email'], $post['mail']) != 0) {
rg_log_debug('different mail');
$ui['email'] = $post['mail'];
$do_update = TRUE;
break;
}
if (strcmp($ui['realname'], $post['realname']) != 0) {
rg_log_debug('different realname');
$ui['realname'] = $post['realname'];
$do_update = TRUE;
break;
}
if (strcmp($ui['pass'], $post['password']) != 0) {
rg_log_debug('different passwords');
$ui['pass'] = $post['password'];
$ui['pass2'] = $post['password'];
$do_update = TRUE;
break;
}
// TODO: expiration?
// TODO: plan_id will change when we will edit
// the ldap server info. Same with is_admin.
// Same with 'prio'.
break;
}
if ($do_update) {
rg_log_debug('desync between sql and ldap');
// If we do not have pass2, we do not want
// to change the password.
if (!isset($ui['pass2']))
unset($ui['pass']);
$r = rg_user_edit_no_check($db, $ui);
if ($r['ok'] !== 1) {
$ret['errmsg'] = $r['errmsg'];
break;
}
}
break;
}
if (isset($ret['errmsg']))
break;
rg_log_debug('ui: ' . print_r($ui, TRUE));
if ($ui['exists'] != 1) {
$ret['errmsg'] = 'invalid user, pass or login token';
rg_log('user does not exists');
break;
}
if ($ui['deleted'] > 0) {
$ret['errmsg'] = 'invalid user, pass or login token';
rg_log('account is deleted');
break;
}
if ($ui['suspended'] > 0) {
$ret['errmsg'] = 'invalid user, pass or login token';
rg_log('account is suspended');
break;
}
$vi = rg_totp_verify_any($db, $ui['uid'], $login_token);
if ($vi['ok'] != 1) {
$ret['errmsg'] = 'login token error: ' . rg_totp_error();
break;
}
rg_log_debug('vi: ' . print_r($vi, TRUE));
if (($vi['enrolled'] == 1) && ($vi['token_valid'] != 1)) {
// It may be possible that the login_token to be appended
// to the pass.
$_list = array();
$_found = FALSE;
foreach ($_list as $_lt) {
$vi = rg_totp_verify_any($db, $ui['uid'], $_lt);
if ($vi['ok'] == 1) {
$_found = TRUE;
break;
}
}
if (!$_found) {
$ret['errmsg'] = 'invalid user, pass or login token';
rg_log('invalid token');
break;
}
}
rg_user_auto_login($db, $ui['uid'], $lock_ip, $https,
$domain, $ui);
$event = array(
'category' => 'user_event_login',
'source' => 'user_login_by_user_pass_helper',
'prio' => 100,
'itime' => time());
$r = rg_event_add($db, $event);
if ($r !== TRUE) {
$ret['errmsg'] = 'internal error; try again later';
break;
}
rg_event_signal_daemon('', 0);
$ret['ok'] = 1;
break;
}
rg_log_exit();
rg_prof_end('user_login_by_user_pass_helper');
return $ret;
}
function rg_user_login_by_user_pass($db, $user, $pass, $login_token,
$lock_ip, $https, $domain)
{
rg_prof_start('user_login_by_user_pass');
rg_log_enter('user_login_by_user_pass');
do {
$ret = rg_user_login_by_user_pass_helper($db, $user, $pass,
$login_token, $lock_ip, $https, $domain);
if ($ret['ok'] == 1)
break;
if (!empty($login_token))
break;
// Password may contain a login_token
$lt = substr($pass, -6);
$pass2 = substr($pass, 0, -6);
$ret = rg_user_login_by_user_pass_helper($db, $user, $pass2,
$lt, $lock_ip, $https, $domain);
if ($ret['ok'] == 1)
break;
// Password may contain a scratch code
$lt = substr($pass, -8);
$pass2 = substr($pass, 0, -8);
$ret = rg_user_login_by_user_pass_helper($db, $user, $pass2,
$lt, $lock_ip, $https, $domain);
if ($ret['ok'] == 1)
break;
rg_log_debug('invalid login: ' . $pass);
} while (0);
rg_log_exit();
rg_prof_end('user_login_by_user_pass');
return $ret;
}
/*
* Suspend an account
* 1=suspend, 0=unsuspend
*/
function rg_user_suspend($db, $uid, $op)
{
rg_log_enter("user_suspend: uid=$uid, op=$op");
$ret = FALSE;
while (1) {
if (rg_rights_allow2($db, 'user', $uid, 'S', '') !== 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, $uid, $op)
{
rg_prof_start("user_make_admin");
rg_log_enter("user_make_admin: uid=$uid, op=$op");
$ret = FALSE;
while (1) {
if (rg_rights_allow2($db, 'user', $uid, 'M', '') !== 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>Disk (MiB)</th>\n";
$ret .= " <th>Git (MiB)</th>\n";
$ret .= " <th>Artifacts (MiB)</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";
$ret .= " <td>" . $row['disk_used_mb'] . "</td>\n";
$ret .= " <td>" . $row['git_mb'] . "</td>\n";
$ret .= " <td>" . $row['artifacts_mb'] . "</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'] != 1) {
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 $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_login'] = rg_ui_login();
$rg['ui_login']['email'] = $email; // TODO: why do I overwrite the e-mail?!
$rg['ui_login']['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@@'
. ', confirm_token = \'\''
. ' 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);
$_a = array('confirmed' => $now, 'confirm_token' => '');
rg_cache_merge('user' . '::' . $uid . '::' . 'info',
$_a, 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 id ' . $ui['plan_id'] . '.');
return FALSE;
}
$max = $pi['disk_mb'];
if ($max == 0)
return FALSE;
if ($ui['disk_used_mb'] >= $max)
return TRUE;
return FALSE;
}
/*
* Always returns a nice text representation of the uid, even if invalid.
*/
function rg_user_nice($db, $uid)
{
if ($uid == 0)
return 'anonymous';
$ui = rg_user_info($db, $uid, '', '');
if ($ui['exists'] == 1)
return $ui['username'];
return 'n/a';
}
/*
* Loads data for graphs
* @unit - interval on which a sum is made
*/
function rg_user_data($db, $type, $start, $end, $unit, $mode)
{
$params = array('start' => $start, 'end' => $end);
switch ($type) {
case 'create_account':
$q = 'SELECT itime FROM users'
. ' WHERE itime >= @@start@@ AND itime <= @@end@@'
. ' AND is_admin = 0';
break;
case 'disk':
$q = 'SELECT disk_used_mb AS value, itime FROM users'
. ' WHERE itime >= @@start@@ AND itime <= @@end@@'
. ' AND is_admin = 0';
break;
default:
rg_internal_error('invalid type');
return FALSE;
}
$ret = rg_graph_query($db, $start, $end, $unit, $mode, $q, $params, '');
if ($ret === FALSE)
rg_user_set_error(rg_graph_error());
return $ret;
}
$__ui_page = rg_user_empty();
function rg_ui_page()
{
global $__ui_page;
return $__ui_page;
}
function rg_ui_page_reset()
{
global $__ui_page;
rg_log_debug('ui_page_reset');
$__ui_page = rg_user_empty();
}
function rg_ui_page_set($a)
{
global $__ui_page;
rg_log_debug('ui_page_set');
$__ui_page = $a;
}
$__ui_login = rg_user_empty();
function rg_ui_login()
{
global $__ui_login;
return $__ui_login;
}
function rg_ui_login_reset()
{
global $__ui_login;
rg_log_debug('ui_login_reset');
$__ui_login = rg_user_empty();
}
function rg_ui_login_set($a)
{
global $__ui_login;
rg_log_debug('ui_login_set');
$__ui_login = $a;
}
/*
* 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) {
$obj_id = $rg['target_ui']['uid'];
if (rg_rights_allow2($db, 'user', $obj_id, 'E', '') !== 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;
}
}
$v = microtime(TRUE);
$gen1 = sprintf("%u", $v);
$gen2 = sprintf("%03u", ($v - intval($v)) * 1000);
$errmsg = array();
$load_form = TRUE;
while (1) {
if ($rg['doit'] == 0)
break;
$ui = array();
$ui['uid'] = $rg['target_ui']['uid'];
$ui['username'] = trim(rg_var_str_nocr("username"));
$ui['realname'] = trim(rg_var_str_nocr("realname"));
$ui['email'] = rg_var_email('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['last_ip'] = rg_ip();
$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'];
}
}
// We try to prevent bots to create accounts
if ($rg['target_ui']['uid'] == 0) {
$gen = rg_var_str('gen');
if (empty($gen)) {
$diff = 0;
} else {
$xgen1 = substr($gen, 3);
$xgen2 = substr($gen, 0, 3);
$diff = ($gen1 - $xgen1) * 1000 + $gen2 - $xgen2;
}
if ($diff < 2000) {
rg_log('Bot tried to create account in ' . $diff . 'ms');
$errmsg[] = 'invalid token; try again';
break;
}
}
if ($ui['tos'] != 1) {
$errmsg[] = rg_template_blind('user/tos_deny.html');
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;
}
$pi = rg_plan_info($db, $ui['plan_id']);
if ($pi['exists'] != 1) {
$errmsg[] = 'invalid plan';
break;
}
$ui_login = rg_ui_login();
if (($ui_login['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*/);
$ret .= rg_pay_needed_high_level($db, $rg);
$load_form = FALSE;
break;
}
if ($load_form) {
$rg = array_merge($rg, $ui);
$rg['HTML:select_plan'] = rg_plan_select($db, 'plan_id',
$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');
$rg['gen'] = $gen2 . $gen1;
$ret .= rg_template("user/add_edit.html", $rg, TRUE /*xss*/);
}
return $ret;
}
/*
* API dispatch function
*/
function rg_user_api($db, $a, $json)
{
rg_prof_start('user_api');
rg_log_enter('user_api');
$cmd = $json['cmd'];
$ret = array();
while (1) {
if ($a['target_ui']['exists'] != 1) {
$ret['error'] = 'invalid user or no rights';
break;
}
$ui_login = rg_ui_login();
// TODO: allow also a master user to access user info
if (($ui_login['is_admin'] != 1)
&& ($a['target_ui']['uid'] != $ui_login['uid'])) {
$ret['error'] = 'invalid user or no rights';
rg_log('user has no rights');
break;
}
if (strcmp($cmd, 'user_info') == 0) {
$ret = $a['target_ui'];
$list = array('pass', 'salt', 'confirm_token');
foreach ($list as $k)
unset($ret[$k]);
break;
}
if (strcmp($cmd, 'user_ssh_keys_list') == 0) {
$ret['list'] = rg_keys_list($db, $a['target_ui']);
break;
}
if (strcmp($cmd, 'user_rights_list') == 0) {
$params = array('who' => $a['target_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['list'] = array();
while (($row = rg_sql_fetch_array($res))) {
rg_rights_cosmetic($db, $row);
$ret['list'][] = $row;
}
rg_sql_free_result($res);
break;
}
if (strcmp($cmd, 'user_wh_list') == 0) {
$r = rg_wh_list($db, $a['target_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['list'] = array();
foreach ($r['list'] as $index => $d)
$ret['list'][] = $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, $ui_login)
{
$ev = array(
'category' => 'ask-email-confirmation',
'source' => 'user_ask_for_email_confirmation',
'prio' => 200,
'ui_login' => $ui_login,
'base_url' => rg_base_url($db, '', '')
);
$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) {
$ui = rg_user_info($db, $uid, '', '');
if ($ui['ok'] != 1)
return FALSE;
if ($ui['exists'] != 1)
continue;
$ret[$uid] = $ui;
}
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.
* TE is alway empty and CL>0 using the current nginx configuration.
* content_encoding may be gzip or empty.
* Returns the uncompressed stream.
*/
function rg_process_input($content_length, $content_encoding, &$err)
{
$te = isset($_SERVER['HTTP_TRANSFER_ENCODING']) ?
$_SERVER['HTTP_TRANSFER_ENCODING'] : '';
rg_log_enter('process_input: content_length=' . $content_length
. ' content_encoding=' . $content_encoding
. ' te=' . $te);
$ret = FALSE;
while (1) {
$max_nice = ini_get('post_max_size');
$max = rg_mega2bytes($max_nice);
$max_nice = rg_1024($max);
if ($content_length > $max) {
$err = 'Your push size (' . rg_1024($content_length) . ')'
. ' is bigger than the max allowed'
. ' (' . $max_nice . ').' . "\n"
. 'You may want to ask the admin to raise'
. ' the PHP limit (post_max_size).';
rg_internal_error($err);
break;
}
// TODO: I have working requests with cl > 0 and te = 'chunked'
if ($content_length == 0) {
if (strcasecmp($te, 'chunked') != 0) {
$err = 'No Content-Length header and'
. ' Transfer-Encoding is not'
. ' "chunked". Please let us know.';
rg_internal_error($err);
break;
}
if (!empty($te)) {
$err = 'Unknown Transfer-Encoding: ' . $te;
rg_internal_error($err);
break;
}
// TODO: Sadly, we are not able to do the streaming!
// It is not available to php-fpm!
rg_git_info_pack("\x02", 'We could not process'
. ' the data (chunked).');
$err = 'You are trying to push chunked encoding data'
. ' but php-fpm does not support it.' . "\n"
. ' Try to run'
. ' \'git config http.postBuffer 200000000\'.' . "\n"
. ' The value must be bigger than what you'
. ' want to push.';
rg_internal_error($err);
break;
} else { // Content-Length > 0
}
if (strcmp($content_encoding, 'gzip') == 0) {
// ok, we are dealing with it
} else if (!empty($content_encoding)) {
$err = 'Unknown Content-Encoding: ' . $content_encoding;
rg_internal_error($err);
break;
}
$ret = TRUE;
break;
}
rg_log_exit();
return $ret;
}
/*
* Helper for rg_user_http_git
*/
function rg_user_http_git_cb_input($index, &$a, $stream)
{
rg_log_enter('user_http_git_cb_input');
switch ($stream) {
case 1: // stdout
echo $a['in_buf'];
$a['in_buf'] = '';
break;
case 2: // stderr
$a['cb_input_stderr_func']($a['err_buf']);
$a['err_buf'] = '';
break;
}
rg_log_exit();
}
/*
* Deals with push/fetch by HTTP(S)
*/
function rg_user_http_git($db, $rg, $paras)
{
global $rg_log_sid;
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'] : '';
if (!empty($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;
rg_stats_conns_set('cmd', $service);
set_time_limit(3600);
rg_log_ml('paras: ' . print_r($paras, TRUE));
if (count($paras) < 2) {
//TODO
}
$prefix = empty($paras) ? '' : array_shift($paras);
if (strcmp($prefix, 'user') != 0) {
$user = $prefix;
$prefix = '';
} else {
$user = empty($paras) ? '' : array_shift($paras);
}
$repo = empty($paras) ? '' : 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');
header('Content-Type: text/plain');
rg_debug() && header('X-RocketGit-skip-etag: 1');
rg_debug() && header('X-RocketGit-skip-compression: 1');
// If user is valid, retry auth.
// If user is not valid, consider anonymous.
$authd = array('ok' => 0);
$u = '';
$no_user_provided = TRUE;
while (isset($_SERVER['PHP_AUTH_USER'])
&& isset($_SERVER['PHP_AUTH_PW'])) {
$u = $_SERVER['PHP_AUTH_USER'];
$p = $_SERVER['PHP_AUTH_PW'];
rg_log_debug('HTTP auth: u=' . $u);
$no_user_provided = FALSE;
if (strcasecmp($u, 'guest') == 0)
$u = '';
if (empty($u))
break;
$r = rg_user_info($db, 0, $u, '');
if ($r['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['https'], $rg['hostname']);
break;
}
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
$helper_flags = '';
$r = rg_repo_fetch_push_helper($db, $host, $prefix, $user,
$repo, $service, $helper_flags);
rg_log_debug('repo_fetch_push_helper returns: ' . print_r($r, TRUE));
if ($r['ok'] !== 1) {
rg_log_debug('set errror: ' . $r['errmsg']);
header('X-Rocketgit-Error: ' . $r['errmsg']);
$ui_page = rg_ui_page();
if ($ui_page['ok'] != 1) {
header($protocol . ' 500 Internal server error');
break;
}
// TODO: what about 'deleted' etc.?
if ($ui_page['exists'] != 1) {
header($protocol . ' 404 Not found');
break;
}
if (isset($r['ri'])) {
if ($r['ri']['ok'] != 1) {
header($protocol . ' 500 Internal server error');
break;
}
// TODO: what about locked?
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'];
// for 'conns' stats
$ui_login = rg_ui_login();
rg_stats_conns_set('login_uid', $ui_login['uid']);
rg_stats_conns_set('uid', $r['ri']['uid']);
rg_stats_conns_set('repo_id', $r['ri']['repo_id']);
// push_allowed is only about the rights, not about auth/login_tokens
rg_log_debug('push=' . $r['push']
. ' push_allowed=' . $r['push_allowed']
. ' no_user_provided=' . ($no_user_provided ? 'yes' : 'no')
. ' authd=' . ($authd['ok'] === 1 ? 'yes' : 'no'));
$send_401 = FALSE;
while (1) {
if ($r['allow'] !== 1) {
rg_log_debug('allow != 1 => 401');
// Connecting user has no rights to push, not even anon.
// The user may be authed at this point, but may try another
// user/pass combination.
$send_401 = TRUE;
break;
}
if ($r['push'] !== 1) {
rg_log_debug('it is a fetch');
break;
}
if ($no_user_provided) {
rg_log_debug('no user provided');
$send_401 = TRUE;
break;
}
$ui_login = rg_ui_login();
if (($authd['ok'] !== 1) && ($ui_login['exists'] == 1)) {
rg_log_debug('user exists but not authenticated');
$send_401 = TRUE;
break;
}
break;
}
if ($send_401) {
rg_log_debug('sending 401');
header($protocol . ' 401 Unauthorized status');
header('WWW-Authenticate: Basic'
. ' realm="Use user \'guest\' if you have no account"');
echo 'RocketGit: Info: == Welcome to RocketGit! ==' . "\n";
echo 'RocketGit: Info: you are connecting from IP '
. rg_ip() . ' by ' . $protocol . '.' . "\n";
echo 'RocketGit: Info: date/time: ' . gmdate('Y-m-d H:i:s')
. ' (UTC), debug id ' . $rg_log_sid . '.' . "\n";
echo 'RocketGit: Info: Use user \'guest\' with any'
. ' password if you want to push anonymously.' . "\n";
echo 'RocketGit: Info: Append the login token or the'
. ' scratch code to the password if you are'
. ' enrolled into 2fa.' . "\n";
break;
}
$content_length = isset($_SERVER['CONTENT_LENGTH']) ?
intval($_SERVER['CONTENT_LENGTH']) : 0;
$content_encoding = isset($_SERVER['HTTP_CONTENT_ENCODING']) ?
$_SERVER['HTTP_CONTENT_ENCODING'] : '';
rg_log_debug('cl=' . $content_length . ' ce=' . $content_encoding . '.');
$input_fd = @fopen('php://input', 'r');
if ($input_fd === FALSE) {
$err = 'cannot open php://input: '. rg_php_err();
rg_internal_error($err);
break;
}
$cmd = array(
'cmds' => array(
'cmd1' => array(
'cb_output' => 'rg_exec2_helper_read_fd',
'cb_output_fd' => $input_fd,
//TODO 'out_buf_done' => 0,
'cb_input' => 'rg_user_http_git_cb_input'
)
)
);
if (strcasecmp($content_encoding, 'gzip') == 0)
$cmd['cmds']['cmd1']['out_buf_helper'] = 'rg_exec2_helper_gzip_in';
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();
$cmd['cmds']['cmd1']['cmd'] = '/usr/libexec/git-core/' . $service
. ' --stateless-rpc --advertise-refs'
. ' ' . escapeshellarg($repo_path);
$cmd['cmds']['cmd1']['cb_input_stderr_func'] = 'rg_git_band_2';
$e = rg_exec2($cmd);
//rg_log_ml('XDEBUG: e: ' . print_r($e, TRUE));
if (!isset($e['code']))
exit(1);
if ($e['ok'] != 1) {
$err = 'error executing command: ' . $e['errmsg'];
rg_internal_error($err);
break;
}
rg_log('Done!');
break;
}
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:
// remote receive-pack expects ACK/NAK first:
// fatal: git fetch_pack: expected ACK/NAK, got '...
// Should I send NAK first?
/*
rg_git_info_pack("\x02", '== Welcome to RocketGit! ==');
rg_git_info_pack("\x02", 'you are connecting from IP '
. rg_ip() . ' by ' . $protocol . '.');
rg_git_info_pack("\x02", 'date/time: ' . gmdate('Y-m-d H:i:s')
. ' (UTC), debug id ' . $rg_log_sid . '.');
// If user does not connect to the correct URL, correct them
if (!empty($host) && (strcasecmp($host, $rg['hostname_port']) != 0))
rg_git_info_pack("\x02", 'Please use ' . $rg['hostname_port']
. ' instead of ' . $host . '.');
putenv('ROCKETGIT_SHOW_INFO=0');
*/
$r = rg_process_input($content_length, $content_encoding, $err);
if ($r === FALSE)
break;
$cmd['cmds']['cmd1']['cmd'] = '/usr/libexec/git-core/git-upload-pack'
. ' --stateless-rpc'
. ' ' . escapeshellarg($repo_path);
$cmd['cmds']['cmd1']['cb_input_stderr_func'] = 'rg_git_band_2';
$e = rg_exec2($cmd);
if ($e['ok'] != 1) {
$err = 'error executing command: ' . $e['errmsg'];
rg_internal_error($err);
break;
}
rg_log('Done!');
} 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() . ' by ' . $protocol . '.');
rg_git_info_pack("\x02", 'date/time: ' . gmdate('Y-m-d H:i:s')
. ' (UTC), debug id ' . $rg_log_sid . '.');
// If user does not connect to the correct URL, correct them
if (!empty($host) && (strcasecmp($host, $rg['hostname_port']) != 0))
rg_git_info_pack("\x02", 'Please use ' . $rg['hostname_port']
. ' instead of ' . $host . '.');
putenv('ROCKETGIT_SHOW_INFO=0');
$r = rg_process_input($content_length, $content_encoding, $err);
if ($r === FALSE) {
// We have to send '200'.
// Else, we cannot send the hints
header($protocol . ' 200 Service unavailable');
rg_git_info_pack("\x02", $err);
echo rg_git_flush();
break;
}
$cmd['cmds']['cmd1']['cmd'] = '/usr/libexec/git-core/git-receive-pack'
. ' --stateless-rpc'
. ' ' . escapeshellarg($repo_path);
$cmd['cmds']['cmd1']['cb_input_stderr_func'] = 'rg_echo';
$e = rg_exec2($cmd);
if ($e['ok'] != 1) {
$err = 'error executing command: ' . $e['errmsg'];
rg_internal_error($err);
break;
}
rg_log('Done!');
} 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);
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;
}