View source
<?php
function radioactivity_http_help($section = '') {
$output = '';
switch ($section) {
case "admin/help#radioactivity":
$output = '<p>' . t('Provides HTTP interface for some radioactivity functionality. The functionality is ' . 'exposed as individually configured <em>ports</em>. Please take note of security considerations when ' . 'exposing ports to internet. More info on that in the port configurator.') . '</p>';
break;
}
return $output;
}
function _radioactivity_http_get_ports() {
return variable_get('radioactivity_http_ports', array());
}
function radioactivity_http_menu($may_cache) {
if ($may_cache) {
$items[] = array(
'path' => 'admin/settings/radioactivity/http_ports',
'title' => t('HTTP ports'),
'description' => t('Configure HTTP ports for external access.'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'radioactivity_http_ports_list',
),
'access' => user_access(RADIOACTIVITY_PERM_ADMIN),
'weight' => 30,
'type' => MENU_LOCAL_TASK,
);
$items[] = array(
'path' => 'admin/settings/radioactivity/http_port',
'title' => t('HTTP port editor'),
'description' => t('Configure HTTP port for external access.'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'radioactivity_http_port_edit',
),
'access' => user_access(RADIOACTIVITY_PERM_ADMIN),
'type' => MENU_CALLBACK,
);
$items[] = array(
'path' => 'admin/settings/radioactivity/http_port_delete',
'title' => t('Delete HTTP port'),
'description' => t('Delete HTTP port for external access.'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'radioactivity_http_port_delete',
),
'access' => user_access(RADIOACTIVITY_PERM_ADMIN),
'type' => MENU_CALLBACK,
);
}
else {
foreach (_radioactivity_http_get_ports() as $id => $port) {
$items[] = array(
'path' => $port['path'],
'type' => MENU_CALLBACK,
'callback' => 'radioactivity_http_cb',
'callback arguments' => array(
$id,
),
'access' => TRUE,
);
}
}
return $items;
}
function radioactivity_http_ports_list() {
$form = array();
$form[] = array(
'#value' => '<h2>' . t('Configured HTTP ports') . '</h2>',
);
$rows = array();
$ports = _radioactivity_http_get_ports();
if (count($ports)) {
foreach ($ports as $id => $port) {
$rows[] = array(
check_plain($id),
check_plain($port['path']),
l(t('Edit'), 'admin/settings/radioactivity/http_port/' . $id) . ' ' . l(t('Delete'), 'admin/settings/radioactivity/http_port_delete/' . $id),
);
}
}
else {
$rows[] = array(
array(
'colspan' => 3,
'data' => t('No ports configured'),
),
);
}
$table = theme('table', array(
t('Port id'),
t('Port URL'),
t('Actions'),
), $rows);
$form['ports'] = array(
'#value' => $table,
);
$form['create_port'] = array(
'#value' => l(t('Create new port'), 'admin/settings/radioactivity/http_port/new'),
);
return $form;
}
function radioactivity_http_port_delete($port_id) {
$form = array();
drupal_set_title(t('Confirm delete port %id', array(
'%id' => $port_id,
)));
$form['port_id'] = array(
'#type' => 'hidden',
'#default_value' => $port_id,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
);
$form['cancel'] = array(
'#value' => l(t('Cancel'), 'admin/settings/radioactivity/http_ports'),
);
return $form;
}
function radioactivity_http_port_delete_submit($form_id, $form) {
$port_id = $form['port_id'];
drupal_set_message(t('Deleted port %id', array(
'%id' => $port_id,
)));
$ports = _radioactivity_http_get_ports();
unset($ports[$port_id]);
variable_set('radioactivity_http_ports', $ports);
drupal_goto('admin/settings/radioactivity/http_ports');
}
function radioactivity_http_port_edit($port_id) {
global $base_url;
$form = array();
if ($port_id == 'new') {
$form[] = array(
'#value' => '<h3>' . t('Create new port') . '</h3>',
);
}
else {
$form[] = array(
'#value' => '<h3>' . t('Edit port %id', array(
'%id' => $port_id,
)) . '</h3>',
);
}
$ports = _radioactivity_http_get_ports();
$port = $ports[$port_id];
$form['port_id'] = array(
'#type' => 'hidden',
'#default_value' => $port_id,
);
$form['path'] = array(
'#type' => 'textfield',
'#required' => TRUE,
'#title' => t('Path of the access point'),
'#description' => t('Define the path of the port access point. The exposed methods will be accessed by ' . '<code>' . $base_url . '/<path>/<method_name></code>. ' . 'Do not add trailing slash.'),
'#default_value' => $port['path'],
);
$form['security_scheme'] = array(
'#type' => 'select',
'#title' => t('Security scheme for the port'),
'#description' => t('Choose security scheme for the port. The available schemes are:') . '<dl>' . '<dt>' . t('None') . '</dt>' . '<dd>' . t('No security. Choose this only if you have secured the access some other way.') . '</dd>' . '<dt>' . t('MD5 sign with private key') . '</dt>' . '<dd>' . t('MD5-based private key signing of method access. The signature is created by ' . 'the following formula: ') . "<code>md5('<method_name>,<arg1_name=arg1_value>,...,<argN_name=argN_value>,<pkey>')</code>. " . t('The lowercase signature is the appended to the query as a query parameter <code>s</code>. Note that ' . 'the parameters are listed in <em>signature</em> order which may differ from the actual invocation order.') . '</dl>',
'#default_value' => $port['security_scheme'],
'#options' => array(
'none' => t('None'),
'pkey-md5' => t('MD5 sign with private key'),
),
);
$form['private_key'] = array(
'#type' => 'textfield',
'#title' => t('Private key'),
'#description' => t('The private key is used by some security schemes.'),
'#default_value' => $port['private_key'],
);
$form['access_method'] = array(
'#type' => 'select',
'#title' => t('Access method'),
'#description' => t('Choose accessing method for the operations. The available access methods are:') . '<dl>' . '<dt>' . t('Method + query params') . '</dt>' . '<dd>' . t('Method is invoked by HTTP GET to <code><port_url>/<method_name>?arg1_name=arg1_value&...argN_name=argN_value</code> .') . '</dd>' . '</dl>',
'#default_value' => $port['access_method'],
'#options' => array(
'method-and-query' => t('Method + query params'),
),
);
$form['return'] = array(
'#type' => 'select',
'#title' => t('Return value encoding'),
'#description' => t('Select return value encoding.'),
'#default_value' => $port['return'],
'#options' => array(
'php-serialize' => t('PHP serialize()'),
),
);
$missing_functions = array();
if (function_exists('json_encode')) {
$form['return']['#options']['json'] = t('JSON');
}
else {
$missing_functions[] = 'json_encode';
}
$options = array();
foreach (_radioactivity_http_get_method_signatures() as $method => $signature) {
$options[$method] = $method . '(' . implode(', ', $signature) . ')';
}
$form['exposed_methods'] = array(
'#type' => 'checkboxes',
'#title' => t('Exposed method'),
'#description' => t('Choose methods to expose by this port.'),
'#options' => $options,
'#default_value' => $port['exposed_methods'],
);
$form['missing_functions'] = array(
'#type' => 'item',
'#title' => t('Missing extension functions'),
'#description' => t('Add extensions to provide missing functions if required. They ' . 'bring more configuration options.'),
);
if (!count($missing_functions)) {
$form['missing_functions']['#value'] = t('None');
}
else {
$form['missing_functions']['#value'] = implode(', ', $missing_functions);
}
$form['example'] = array(
'#type' => 'item',
'#title' => t("Example on how to invoke <code>radioactivity_get_energy(oid=2, oclass='node')</code>"),
'#description' => t("The port translates the call to <code>radioactivity_get_energy(2, 'node')</code> as in PHP code. " . 'This retrieves radioactivity info on node/2. This example is not available before the port is saved. ' . 'Also, you must expose <code>radioactivity_get_energy</code> to make this example actually work.'),
);
if ($port_id == 'new') {
$form['example']['#value'] = t('Example unavailable');
}
else {
$query = array();
switch ($port['access_method']) {
case 'method-and-query':
$base = $base_url . '/' . $port['path'] . '/radioactivity_get_energy';
$query['oid'] = 2;
$query['oclass'] = 'node';
break;
}
switch ($port['security_scheme']) {
case 'pkey-md5':
$query['s'] = _radioactivity_http_create_pkey_md5('radioactivity_get_energy', array(
'oid' => 2,
'oclass' => 'node',
), $port['private_key']);
}
$url = $base;
if (count($query)) {
$url .= '?';
$first = TRUE;
foreach ($query as $name => $value) {
if (!$first) {
$url .= '&';
}
$url .= urlencode($name) . '=' . urlencode($value);
$first = FALSE;
}
}
$form['example']['#value'] = '<a href="' . $url . '">' . $url . '</a>';
}
$form['save'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
$form['save_and_edit'] = array(
'#type' => 'submit',
'#value' => t('Save and edit'),
);
$form['cancel'] = array(
'#value' => l(t('Cancel'), 'admin/settings/radioactivity/http_ports'),
);
return $form;
}
function radioactivity_http_port_edit_submit($form_id, $form) {
$ports = _radioactivity_http_get_ports();
$port_id = $form['port_id'];
if ($port_id == 'new') {
if (count($ports)) {
$port_ids = array_keys($ports);
sort($port_ids, SORT_NUMERIC);
$port_id = 1 + $port_ids[count($ports) - 1];
}
else {
$port_id = 1;
}
}
$ports[$port_id] = array(
'path' => $form['path'],
'security_scheme' => $form['security_scheme'],
'private_key' => $form['private_key'],
'access_method' => $form['access_method'],
'exposed_methods' => $form['exposed_methods'],
'return' => $form['return'],
);
drupal_set_message(t('Saved port %id', array(
'%id' => $port_id,
)));
variable_set('radioactivity_http_ports', $ports);
if ($form['op'] == t('Save and edit')) {
drupal_goto('admin/settings/radioactivity/http_port/' . $port_id);
}
else {
drupal_goto('admin/settings/radioactivity/http_ports');
}
}
function _radioactivity_http_get_method_signatures() {
static $signatures = array(
'radioactivity_get_energy' => array(
'oid',
'oclass',
),
'radioactivity_add_energy' => array(
'oid',
'oclass',
'source',
),
'radioactivity_delete_energy' => array(
'oid',
'oclass',
),
);
return $signatures;
}
function _radioactivity_http_get_method_signature($method) {
$signatures = _radioactivity_http_get_method_signatures();
return $signatures[$method];
}
function _radioactivity_http_create_pkey_md5($method, $params, $pkey) {
$param_string = '';
$signature = _radioactivity_http_get_method_signature($method);
$first = TRUE;
foreach ($signature as $name) {
if (!$first) {
$param_string .= ',';
}
$param_string .= $name . '=' . $params[$name];
$first = FALSE;
}
return md5($method . ',' . $param_string . ',' . $pkey);
}
function _radioactivity_http_cb_get_invocation($port) {
switch ($port['access_method']) {
case 'method-and-query':
$method = substr($_GET['q'], strlen($port['path']) + 1);
$params = array();
$signature = _radioactivity_http_get_method_signature($method);
if (!$signature) {
return FALSE;
}
foreach ($signature as $name) {
$params[$name] = $_GET[$name];
}
return array(
'method' => $method,
'params' => $params,
);
}
return FALSE;
}
function _radioactivity_http_cb_process($port) {
$invocation = _radioactivity_http_cb_get_invocation($port);
if (!$invocation) {
print 'Unknown method';
return;
}
if (!$port['exposed_methods'][$invocation['method']]) {
print 'Method not exposed';
return;
}
switch ($port['security_scheme']) {
case 'none':
break;
case 'pkey-md5':
$expected_hash = _radioactivity_http_create_pkey_md5($invocation['method'], $invocation['params'], $port['private_key']);
break;
default:
print 'Unknown security scheme';
}
if (isset($expected_hash)) {
if ($expected_hash != $_GET['s']) {
print 'Hash signature mismatch';
return;
}
}
$ret = call_user_func_array($invocation['method'], $invocation['params']);
switch ($port['return']) {
case 'json':
print json_encode($ret);
break;
case 'php-serialize':
print serialize($ret);
break;
default:
print 'Unknown return encoding: ' . $invocation['return'];
}
}
function radioactivity_http_cb($port_id) {
$ports = _radioactivity_http_get_ports();
$port = $ports[$port_id];
unset($ports);
_radioactivity_http_cb_process($port);
module_invoke_all('exit', $url);
exit;
}