View source
<?php
function restws_get_resource_info() {
$info =& drupal_static(__FUNCTION__);
if (!isset($info)) {
$info = module_invoke_all('restws_resource_info');
drupal_alter('restws_resource_info', $info);
}
return $info;
}
function restws_get_format_info() {
$info =& drupal_static(__FUNCTION__);
if (!isset($info)) {
$info = module_invoke_all('restws_format_info');
drupal_alter('restws_format_info', $info);
}
return $info;
}
function restws_restws_resource_info() {
foreach (entity_get_info() as $entity_type => $info) {
$result[$entity_type] = array(
'label' => $info['label'],
'class' => 'RestWSEntityResourceController',
);
}
return $result;
}
function restws_resource_controller($name) {
$static =& drupal_static(__FUNCTION__);
if (!isset($static[$name])) {
$info = restws_get_resource_info();
$static[$name] = isset($info[$name]) ? new $info[$name]['class']($name, $info[$name]) : FALSE;
}
return $static[$name];
}
function restws_restws_format_info() {
$result = array(
'json' => array(
'label' => t('JSON'),
'class' => 'RestWSFormatJSON',
'mime type' => 'application/json',
),
'xml' => array(
'label' => t('XML'),
'class' => 'RestWSFormatXML',
'mime type' => 'application/xml',
),
);
if (module_exists('rdf')) {
$result['rdf'] = array(
'label' => t('RDF'),
'class' => 'RestWSFormatRDF',
'mime type' => 'application/rdf+xml',
);
}
return $result;
}
function restws_format($name) {
$static =& drupal_static(__FUNCTION__);
if (!isset($static[$name])) {
$info = restws_get_format_info();
$static[$name] = isset($info[$name]) ? new $info[$name]['class']($name, $info[$name]) : FALSE;
}
return $static[$name];
}
function restws_handle_request($op, $format, $resource_name, $id = NULL, $payload = NULL) {
if ($resource = restws_resource_controller($resource_name)) {
$request = array(
'op' => &$op,
'format' => &$format,
'resource' => &$resource,
'id' => &$id,
'payload' => &$payload,
);
drupal_alter('restws_request', $request);
if (user_access('access resource ' . $resource_name) && $resource
->access($op, $id)) {
try {
$method = $op . 'Resource';
if ($op == 'create') {
print $format
->{$method}($resource, $payload);
drupal_add_http_header('Status', '201 Created');
}
else {
print $format
->{$method}($resource, $id, $payload);
}
drupal_add_http_header('Content-Type', $format
->mimeType());
} catch (RestWSException $e) {
echo check_plain($e
->getHTTPError()) . ': ' . check_plain($e
->getMessage());
drupal_add_http_header('Status', $e
->getHTTPError());
}
}
else {
echo '403 Forbidden';
drupal_add_http_header('Status', '403 Forbidden');
watchdog('access denied', check_plain($_GET['q']), NULL, WATCHDOG_WARNING);
}
}
else {
echo '404 Not Found';
drupal_add_http_header('Status', '404 Not Found');
}
drupal_page_footer();
exit;
}
class RestWSException extends Exception {
public function getHTTPError() {
$code = $this
->getCode();
switch ($code) {
case 403:
return '403 Forbidden';
case 404:
return '404 Not Found';
case 406:
return '406 Not Acceptable';
case 422:
return '422 Unprocessable Entity';
default:
return '500 Internal Server Error';
}
}
}
function restws_menu_alter(&$items) {
foreach (restws_get_resource_info() as $resource => $info) {
$menu_path = isset($info['menu_path']) ? $info['menu_path'] . '/%' : $resource . '/%';
if (isset($items[$menu_path])) {
array_unshift($items[$menu_path]['page arguments'], $resource, $items[$menu_path]['page callback']);
$items[$menu_path]['page callback'] = 'restws_page_callback';
}
elseif (isset($items[$menu_path . $resource])) {
$menu_path = $menu_path . $resource;
array_unshift($items[$menu_path]['page arguments'], $resource, $items[$menu_path]['page callback']);
$items[$menu_path]['page callback'] = 'restws_page_callback';
}
else {
$items[$menu_path] = array(
'page callback' => 'restws_page_callback',
'page arguments' => array(
$resource,
'drupal_not_found',
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
}
if (!isset($info['menu_path'])) {
if (isset($items[$resource])) {
if (!isset($items[$resource]['page arguments'])) {
$items[$resource]['page arguments'] = array();
}
array_unshift($items[$resource]['page arguments'], $resource, $items[$resource]['page callback']);
$items[$resource]['page callback'] = 'restws_page_callback';
}
else {
$items[$resource] = array(
'page callback' => 'restws_page_callback',
'page arguments' => array(
$resource,
'drupal_not_found',
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
}
}
}
}
function restws_page_callback($resource, $page_callback) {
$id_arg = arg(1);
$format = FALSE;
if (($pos = strpos($id_arg, '.')) && ($format_name = substr($id_arg, $pos + 1))) {
$id = substr($id_arg, 0, $pos);
$format = restws_format($format_name);
}
else {
$id = $id_arg;
switch ($_SERVER['REQUEST_METHOD']) {
case 'PUT':
case 'POST':
$parts = explode(';', $_SERVER['CONTENT_TYPE'], 2);
$format = restws_format_mimetype($parts[0]);
break;
case 'DELETE':
if (isset($_SERVER['HTTP_ACCEPT'])) {
$parts = explode(',', $_SERVER['HTTP_ACCEPT'], 2);
$format = restws_format_mimetype($parts[0]);
}
if (!$format) {
$format = restws_format('json');
}
break;
default:
if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'html') === FALSE) {
$parts = explode(',', $_SERVER['HTTP_ACCEPT'], 2);
$format = restws_format_mimetype($parts[0]);
}
if ($format && !isset($_COOKIE[session_name()]) && variable_get('cache')) {
drupal_goto($_GET['q'] . '.' . $format
->getName(), array(), 301);
}
}
}
if ($format) {
switch ($_SERVER['REQUEST_METHOD']) {
case 'PUT':
$op = 'create';
break;
case 'POST':
$op = 'update';
break;
case 'DELETE':
$op = 'delete';
break;
default:
$op = 'view';
}
if (!in_array($_SERVER['REQUEST_METHOD'], array(
'GET',
'HEAD',
'OPTIONS',
'TRACE',
)) && !restws_csrf_validation()) {
echo '403 Access Denied: CSRF validation failed';
drupal_add_http_header('Status', '403 Forbidden');
drupal_page_footer();
exit;
}
$payload = file_get_contents('php://input');
if ($file = variable_get('restws_debug_log')) {
$log = date(DATE_ISO8601) . "\n";
$log .= 'Resource: ' . $resource . "\n";
$log .= 'Operation: ' . $op . "\n";
$log .= 'Format: ' . $format
->mimeType() . "\n";
$log .= 'Id: ' . $id . "\n";
$log .= 'Payload: ' . $payload . "\n";
$log .= "----------------------------------------------------------------\n";
file_put_contents($file, $log, FILE_APPEND);
}
restws_handle_request($op, $format, $resource, $id, $payload);
}
$args = func_get_args();
return call_user_func_array($page_callback, array_slice($args, 2));
}
function restws_csrf_validation() {
if (user_is_logged_in() && !empty($_COOKIE[session_name()])) {
return isset($_SERVER['HTTP_X_CSRF_TOKEN']) && drupal_valid_token($_SERVER['HTTP_X_CSRF_TOKEN'], 'restws');
}
return TRUE;
}
function restws_resource_uri($resource, $id) {
return url($resource . '/' . $id, array(
'absolute' => TRUE,
'alias' => TRUE,
));
}
function restws_format_mimetype($mime) {
foreach (restws_get_format_info() as $format_name => $info) {
if ($info['mime type'] == $mime) {
return restws_format($format_name);
}
}
return FALSE;
}
function restws_permission() {
$permissions = array();
foreach (restws_get_resource_info() as $type => $info) {
$permissions['access resource ' . $type] = array(
'title' => t('Access the resource %resource', array(
'%resource' => $type,
)),
);
}
return $permissions;
}
function restws_module_implements_alter(&$implementations, $hook) {
if ($hook == 'menu_alter' || $hook == 'entity_info_alter') {
$group = $implementations['restws'];
unset($implementations['restws']);
$implementations['restws'] = $group;
}
}
function restws_menu() {
$items['restws/session/token'] = array(
'page callback' => 'restws_session_token',
'access callback' => 'user_is_logged_in',
'type' => MENU_CALLBACK,
);
return $items;
}
function restws_session_token() {
drupal_add_http_header('Content-Type', 'text/plain');
print drupal_get_token('restws');
drupal_exit();
}
function restws_entity_node_access($op, $node = NULL, $account = NULL) {
if (isset($node)) {
if ($op == 'update' && empty($node->nid)) {
$op = 'create';
}
if ($op == 'create') {
if (isset($node->type)) {
return node_access($op, $node->type, $account);
}
else {
throw new EntityMalformedException('Permission to create a node was requested but no node type was given.');
}
}
$default_revision = node_load($node->nid);
if ($node->vid !== $default_revision->vid) {
return _node_revision_access($node, $op, $account);
}
else {
return node_access($op, $node, $account);
}
}
if (user_access('bypass node access', $account)) {
return TRUE;
}
if (!user_access('access content', $account)) {
return FALSE;
}
if ($op == 'view' && node_access_view_all_nodes($account)) {
return TRUE;
}
return FALSE;
}
function restws_entity_info_alter(&$info) {
$info['node']['access callback'] = 'restws_entity_node_access';
}