<?php
// This files deals with keys used to call the API (over HTTPS)
require_once($INC . '/sql.inc.php');
require_once($INC . '/state.inc.php');
require_once($INC . '/prof.inc.php');
require_once($INC . '/events.inc.php');
require_once($INC . '/cache.inc.php');
$rg_ak_error = '';
function rg_ak_set_error($str)
{
global $rg_ak_error;
$rg_ak_error = $str;
rg_log($str);
}
function rg_ak_error()
{
global $rg_ak_error;
return $rg_ak_error;
}
/*
* Events functions
*/
$rg_ak_functions = array(
'ak_event_new' => 'rg_ak_event_new',
'ak_event_del' => 'rg_ak_event_del',
'ak_event_notify_user' => 'rg_ak_event_notify_user'
);
rg_event_register_functions($rg_ak_functions);
/*
* Event for adding a new api key
*/
function rg_ak_event_new($db, $event)
{
$ret = array();
$event['op'] = 'new';
// notify user
$a = array('category' => 'ak_event_notify_user', 'prio' => 100);
$ret[] = array_merge($event, $a);
return $ret;
}
/*
* Event for deleting a key
*/
function rg_ak_event_del($db, $event)
{
$ret = array();
$event['op'] = 'del';
// notify user
$a = array('category' => 'ak_event_notify_user', 'prio' => 100);
$ret[] = array_merge($event, $a);
return $ret;
}
/*
* Notify user that a new key was added to the keyring
*/
function rg_ak_event_notify_user($db, $event)
{
rg_prof_start('ak_event_notify_user');
rg_log('ak_event_notify_user: event=' . rg_array2string($event));
$ret = FALSE;
while (1) {
$r = rg_mail_template('mail/user/ak/' . $event['op'], $event);
if ($r === FALSE)
break;
$ret = array();
break;
}
rg_prof_end('ak_event_notify_user');
return $ret;
}
/*
* Remove api keys from database for user 'ui'
*/
function rg_ak_remove($db, $ui, $list)
{
rg_prof_start('ak_remove');
rg_log_enter('ak_remove: list=' . rg_array2string($list));
$ret = FALSE;
while (1) {
$my_list = array();
foreach ($list as $key_id => $junk)
$my_list[] = sprintf('%u', $key_id);
$params = array('uid' => $ui['uid']);
$sql_list = implode(', ', $my_list);
$sql = 'DELETE FROM apikeys'
. ' WHERE uid = @@uid@@'
. ' AND key_id IN (' . $sql_list . ')';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_ak_set_error('cannot delete api keys');
break;
}
rg_sql_free_result($res);
$event = array(
'category' => 'ak_event_del',
'prio' => 50,
'ui' => $ui,
'keys' => implode(',', $my_list));
$r = rg_event_add($db, $event);
if ($r !== TRUE) {
rg_ak_set_error('cannot add event'
. ' (' . rg_event_error() . ')');
break;
}
$key = 'user' . '::' . $ui['uid'] . '::' . 'apikeys'
. '::' . 'list';
foreach ($my_list as $_key_id)
rg_cache_unset($key . '::' . $_key_id,
RG_SOCKET_NO_WAIT);
rg_event_signal_daemon('', 0);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end('ak_remove');
return $ret;
}
/*
* Adds an apikey
* Returns the key_id of the key.
*/
function rg_ak_add($db, $ui, $key, $name)
{
rg_prof_start('ak_add');
rg_log_enter('ak_add: key=' . $key . ' name=' . $name);
$ret = FALSE;
$do_rollback = 0;
while (1) {
$itime = time();
$r = rg_sql_begin($db);
if ($r !== TRUE) {
rg_ak_set_error('cannot start transaction');
break;
}
$do_rollback = 1;
$params = array(
'itime' => $itime,
'uid' => $ui['uid'],
'key' => $key,
'name' => $name,
'count' => 0,
'first_use' => 0,
'last_use' => 0,
'last_ip' => '',
'last_cmd' => '');
$sql = 'INSERT INTO apikeys (itime, uid, key, name)'
. ' VALUES (@@itime@@, @@uid@@, @@key@@, @@name@@)'
. ' RETURNING key_id';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_ak_set_error('cannot insert key');
break;
}
$row = rg_sql_fetch_array($res);
$key_id = $row['key_id'];
rg_sql_free_result($res);
$event = array(
'category' => 'ak_event_new',
'prio' => 50,
'ui' => $ui,
'key' => $key,
'name' => $name,
'key_id' => $key_id);
$r = rg_event_add($db, $event);
if ($r !== TRUE) {
rg_ak_set_error('cannot add event'
. ' (' . rg_event_error() . ')');
break;
}
$r = rg_sql_commit($db);
if ($r !== TRUE) {
rg_ak_set_error('cannot commit transaction');
break;
}
$do_rollback = 0;
$_key = 'user' . '::' . $ui['uid'] . '::'
. 'apikeys' . '::' . 'list' . '::' . $key_id;
rg_cache_merge($_key, $params, RG_SOCKET_NO_WAIT);
rg_event_signal_daemon('', 0);
$ret = $key_id;
break;
}
if ($do_rollback == 1)
rg_sql_rollback($db);
rg_log_exit();
rg_prof_end('ak_add');
return $ret;
}
/*
* Update first_use, last_use, last_ip and count
*/
function rg_ak_update_use($db, $uid, $key_id, $ip, $cmd)
{
rg_prof_start('ak_update_use');
rg_log_enter('ak_update_use: uid=' . $uid . ' key_id=' . $key_id
. ', ip=' . $ip . ', cmd=' . $cmd);
$ret = FALSE;
while (1) {
$now = time();
$update_first_use = TRUE;
$update_last_use = TRUE;
$key = 'user' . '::' . $uid . '::' . 'apikeys'
. ' ::' . 'list' . '::' . $key_id;
$c = rg_cache_get($key);
if ($c !== FALSE) {
if (isset($c['first_use']) && ($c['first_use'] > 0))
$update_first_use = FALSE;
// We will not update the field if is too soon
if (isset($c['last_use'])
&& (strcmp($ip, $c['last_ip']) == 0)
&& (strcmp($cmd, $c['last_cmd']) == 0)
&& ($now - $c['last_use'] < 60))
$update_last_use = FALSE;
}
$params = array(
'now' => $now,
'key_id' => $key_id,
'last_ip' => $ip,
'last_cmd' => $cmd);
if ($update_first_use) {
$sql = 'UPDATE apikeys SET first_use = @@now@@'
. ' WHERE first_use = 0'
. ' AND key_id = @@key_id@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_ak_set_error('cannot update apikey\'s first use');
break;
}
rg_sql_free_result($res);
rg_cache_set($key . '::' . 'first_use', $now,
RG_SOCKET_NO_WAIT);
}
if ($update_last_use) {
$sql = 'UPDATE apikeys SET last_use = @@now@@'
. ', last_ip = @@last_ip@@'
. ', last_cmd = @@last_cmd@@'
. ', count = count + 1'
. ' WHERE key_id = @@key_id@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_ak_set_error('cannot update key');
break;
}
rg_sql_free_result($res);
$a = array(
'last_use' => $now,
'last_ip' => $ip,
'last_cmd' => $cmd);
rg_cache_merge($key, $a, RG_SOCKET_NO_WAIT);
}
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end('ak_update_use');
return $ret;
}
/*
* Cosmetic changes for a key
*/
function rg_ak_cosmetic(&$row)
{
if ($row['itime'] == 0)
$row['itime_nice'] = 'N/A';
else
$row['itime_nice'] = gmdate('Y-m-d H:i', $row['itime']);
if ($row['first_use'] == 0)
$row['first_use_nice'] = 'N/A';
else
$row['first_use_nice'] = gmdate('Y-m-d H:i', $row['first_use']);
if (empty($row['last_ip']))
$row['last_ip'] = 'N/A';
if ($row['last_use'] == 0)
$row['last_use_nice'] = 'N/A';
else
$row['last_use_nice'] = gmdate('Y-m-d H:i', $row['last_use']);
if (empty($row['last_cmd']))
$row['last_cmd'] = 'N/A';
}
/*
* List keys
*/
function rg_ak_list($db, $uid)
{
rg_prof_start('ak_list');
rg_log_enter('ak_list: uid=' . $uid);
$ret = FALSE;
while (1) {
$key = 'user' . '::' . $uid . '::' . 'apikeys';
$c = rg_cache_get($key);
//rg_log_ml('DEBUG: cache: ' . print_r($c, TRUE));
if (($c !== FALSE) && isset($c['LOADED'])) {
$ret = $c['list'];
foreach ($ret as $key_id => &$i)
rg_ak_cosmetic($i);
break;
}
$params = array('uid' => $uid);
$sql = 'SELECT * FROM apikeys WHERE uid = @@uid@@'
. ' ORDER BY itime DESC';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_ak_set_error('cannot query');
break;
}
$ret = array();
while (($row = rg_sql_fetch_array($res))) {
$key_id = $row['key_id'];
$ret[$key_id] = $row;
}
rg_sql_free_result($res);
$a = array('LOADED' => 1, 'list' => $ret);
rg_cache_set($key, $a, RG_SOCKET_NO_WAIT);
foreach ($ret as $key_id => &$i)
rg_ak_cosmetic($i);
break;
}
rg_log_exit();
rg_prof_end('ak_list');
return $ret;
}
/*
* Validates a api keys
* Returns the key_id of the matching key or FALSE
*/
function rg_ak_valid($db, $uid, $apikey)
{
rg_prof_start('ak_valid');
rg_log_enter('ak_valid');
$ret = FALSE;
while (1) {
$key = 'user' . '::' . $uid . '::' . 'apikeys';
$c = rg_cache_get($key);
if (($c === FALSE) || !isset($c['LOADED'])) {
$params = array('uid' => $uid);
$sql = 'SELECT * FROM apikeys WHERE uid = @@uid@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_ak_set_error('canno load list');
break;
}
$list = array();
while (($row = rg_sql_fetch_array($res))) {
$key_id = intval($row['key_id']);
unset($row['key_id']);
$list[$key_id] = $row;
}
rg_sql_free_result($res);
$a = array('LOADED' => 1, 'list' => $list);
rg_cache_set($key, $a, RG_SOCKET_NO_WAIT);
} else {
$list = $c['list'];
}
foreach ($list as $key_id => $info) {
if (strcmp($info['key'], $apikey) == 0) {
$ret = $key_id;
break;
}
}
break;
}
rg_log_exit();
rg_prof_end('ak_valid');
return $ret;
}
/*
* High-level function for listing api keys
*/
function rg_ak_list_high_level($db, $rg, $paras)
{
rg_prof_start('ak_list_high_level');
rg_log_enter('ak_list_high_level');
$ret = '';
$errmsg = array();
$rg['HTML:status'] = '';
$doit = rg_var_uint('doit');
while ($doit == 1) {
if (!rg_valid_referer()) {
$errmsg[] = 'invalid referer; try again';
break;
}
if (!rg_token_valid($db, $rg, 'ak_list', FALSE)) {
$errmsg[] = 'invalid token; try again.';
break;
}
$list = rg_var_str('key_delete_ids');
$r = rg_ak_remove($db, $rg['login_ui'], $list);
if ($r !== TRUE) {
$errmsg[] = 'cannot delete: ' . rg_ak_error();
break;
}
$rg['HTML:status'] = rg_template(
'user/settings/apikeys/delete_ok.html',
$rg, TRUE /*xss*/);
break;
}
$r = rg_ak_list($db, $rg['login_ui']['uid']);
if ($r === FALSE) {
$rg['errmsg'] = rg_ak_error();
$ret .= rg_template('user/settings/apikeys/list_err.html',
$rg, TRUE /*xss*/);
} else {
$rg['rg_form_token'] = rg_token_get($db, $rg, 'ak_list');
$rg['HTML:errmsg'] = rg_template_errmsg($errmsg);
$ret .= rg_template_table('user/settings/apikeys/list', $r, $rg);
}
rg_log_exit();
rg_prof_end('ak_list_high_level');
return $ret;
}
/*
* Enroll function for adding a api key
*/
function rg_ak_add_high_level($db, $rg, $paras)
{
rg_prof_start('ak_add_high_level');
rg_log_enter('ak_add_high_level');
$ret = '';
$errmsg = array();
$rg['ak'] = array();
$rg['ak']['name'] = '';
$rg['ak']['key'] = rg_id(32);;
$doit = rg_var_uint('doit');
rg_log('DEBUG: doit=' . $doit);
while ($doit == 1) {
$rg['ak']['name'] = rg_var_str('ak::name');
$rg['ak']['key'] = rg_var_str('ak::key');
if (strlen($rg['ak']['name']) == 0) {
$errmsg[] = 'invalid name';
break;
}
if (!rg_valid_referer()) {
$errmsg[] = 'invalid referer; try again';
break;
}
if (!rg_token_valid($db, $rg, 'ak_add', FALSE)) {
$errmsg[] = 'invalid token; try again';
break;
}
$r = rg_ak_add($db, $rg['login_ui'], $rg['ak']['key'],
$rg['ak']['name']);
if ($r === FALSE) {
$errmsg[] = rg_ak_error();
break;
}
$ret .= rg_template('user/settings/apikeys/add_ok.html',
$rg, TRUE /*xss*/);
$rg['ak']['name'] = '';
$rg['ak']['key'] = rg_id(32);
break;
}
$rg['HTML:errmsg'] = rg_template_errmsg($errmsg);
$rg['rg_form_token'] = rg_token_get($db, $rg, 'ak_add');
$ret .= rg_template('user/settings/apikeys/add.html', $rg, TRUE /*xss*/);
rg_log_exit();
rg_prof_end('ak_add_high_level');
return $ret;
}
/*
* Main HL function for api keys
*/
function rg_ak_high_level($db, &$rg, $paras)
{
rg_prof_start('ak_high_level');
rg_log_enter('ak_high_level paras: ' . rg_array2string($paras));
$ret = '';
$op = empty($paras) ? 'list' : array_shift($paras);
$rg['menu']['ak'][$op] = 1;
$rg['HTML:menu_level2'] =
rg_template('user/settings/apikeys/menu.html', $rg, TRUE /*xss*/);
switch ($op) {
case 'add':
$ret .= rg_ak_add_high_level($db, $rg, $paras);
break;
default:
$ret .= rg_ak_list_high_level($db, $rg, $paras);
break;
}
$hints = array();
$hints[]['HTML:hint'] = rg_template('user/settings/apikeys/hints.html',
$rg, TRUE /*xss*/);
$ret .= rg_template_table('hints/list', $hints, $rg);
rg_log_exit();
rg_prof_end('ak_high_level');
return $ret;
}
?>