View source  
  <?php
include_once __DIR__ . '/restful.entity.inc';
function restful_ctools_plugin_directory($module, $plugin) {
  if ($module == 'restful') {
    return 'plugins/' . $plugin;
  }
}
function restful_plugin_process(&$plugin, $info) {
  
  $plugin += array(
    'description' => '',
  );
  
  $function = array(
    '\\RestfulManager',
    'pluginProcess' . preg_replace('/ /', '', ucwords(preg_replace('/_/', ' ', $info['type']))),
  );
  if (is_callable($function)) {
    $plugin = call_user_func_array($function, array(
      $plugin,
      $info,
    ));
  }
}
function restful_ctools_plugin_type() {
  $plugins['authentication'] = $plugins['restful'] = $plugins['rate_limit'] = $plugins['formatter'] = array(
    'classes' => array(
      'class',
    ),
    'process' => 'restful_plugin_process',
  );
  $plugins['restful']['child plugins'] = TRUE;
  return $plugins;
}
function restful_get_restful_plugins() {
  ctools_include('plugins');
  return ctools_get_plugins('restful', 'restful');
}
function restful_get_authentication_plugins() {
  ctools_include('plugins');
  return ctools_get_plugins('restful', 'authentication');
}
function restful_get_rate_limit_plugins() {
  ctools_include('plugins');
  return ctools_get_plugins('restful', 'rate_limit');
}
function restful_get_formatter_plugins() {
  ctools_include('plugins');
  return ctools_get_plugins('restful', 'formatter');
}
function restful_get_authentication_plugin($plugin_name) {
  ctools_include('plugins');
  return ctools_get_plugins('restful', 'authentication', $plugin_name);
}
function restful_get_formatter_plugin($plugin_name) {
  ctools_include('plugins');
  return ctools_get_plugins('restful', 'formatter', $plugin_name);
}
function restful_get_rate_limit_plugin($plugin_name) {
  ctools_include('plugins');
  return ctools_get_plugins('restful', 'rate_limit', $plugin_name);
}
function restful_menu() {
  $base_path = variable_get('restful_hook_menu_base_path', 'api');
  $items = array();
  foreach (restful_get_restful_plugins() as $plugin) {
    if (!$plugin['hook_menu']) {
      
      continue;
    }
    $item = array(
      'title' => $plugin['name'],
      'access callback' => 'restful_menu_access_callback',
      'access arguments' => array(
        $plugin['resource'],
      ),
      'page callback' => 'restful_menu_process_callback',
      'page arguments' => array(
        $plugin['resource'],
      ),
      'delivery callback' => 'restful_formatted_delivery',
      'type' => MENU_CALLBACK,
    );
    
    if ($plugin['hook_menu'] && empty($plugin['menu_item'])) {
      
      $item['access arguments'][] = 1;
      $item['page arguments'][] = 1;
      
      $items[$base_path . '/v' . $plugin['major_version'] . '.' . $plugin['minor_version'] . '/' . $plugin['resource']] = $item;
      
      $items[$base_path . '/v' . $plugin['major_version'] . '/' . $plugin['resource']] = $item;
      
      $item['access arguments'] = $item['page arguments'] = array(
        1,
      );
      $items[$base_path . '/' . $plugin['resource']] = $item;
    }
    else {
      $items[$plugin['menu_item']] = $item;
    }
  }
  
  if (!empty($items[$base_path . '/session/token'])) {
    $items[$base_path . '/session/token']['delivery callback'] = 'restful_unprepared_delivery';
  }
  
  if (!empty($items[$base_path . '/login'])) {
    $items[$base_path . '/login']['access callback'] = 'user_is_anonymous';
  }
  
  $items['admin/config/services/restful'] = array(
    'title' => 'RESTful',
    'description' => 'Administer the RESTful module.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'restful_admin_settings',
    ),
    'access arguments' => array(
      'administer restful',
    ),
    'file' => 'restful.admin.inc',
  );
  $items['admin/config/services/restful/restful'] = $items['admin/config/services/restful'];
  $items['admin/config/services/restful/restful']['type'] = MENU_DEFAULT_LOCAL_TASK;
  return $items;
}
function restful_permission() {
  return array(
    'administer restful' => array(
      'title' => t('Administer the RESTful module'),
      'description' => t('Access the administration pages for the RESTful module.'),
    ),
  );
}
function restful_help($path, $arg) {
  switch ($path) {
    case 'admin/structure/block':
    case 'admin/help#restful':
      return '<p>' . t('This module is managed in GitHub. Please make sure to read the docs in the !link page for more help.', array(
        '!link' => l('README.md', 'https://github.com/Gizra/restful/blob/7.x-1.x/README.md'),
      )) . '</p>';
  }
}
function restful_get_restful_handler($resource_name, $major_version = 1, $minor_version = 0) {
  $cache =& drupal_static(__FUNCTION__);
  $identifier = implode(':', array(
    $major_version,
    $resource_name,
    $minor_version,
  ));
  if (isset($cache[$identifier])) {
    return $cache[$identifier];
  }
  $cache[$identifier] = NULL;
  
  $valid_plugins = array();
  foreach (restful_get_restful_plugins() as $plugin) {
    if ($plugin['major_version'] != $major_version) {
      continue;
    }
    if ($plugin['resource'] != $resource_name) {
      continue;
    }
    if ($minor_version == $plugin['minor_version']) {
      
      $valid_plugins[$plugin['minor_version']] = $plugin;
      break;
    }
    if ($plugin['minor_version'] > $minor_version) {
      
      continue;
    }
    $valid_plugins[$plugin['minor_version']] = $plugin;
  }
  if (!$valid_plugins) {
    return;
  }
  
  ksort($valid_plugins);
  $plugin = end($valid_plugins);
  $cache[$identifier] = restful_get_restful_handler_by_name($plugin['name']);
  return $cache[$identifier];
}
function restful_get_restful_handler_by_name($plugin_name) {
  ctools_include('plugins');
  $plugin = ctools_get_plugins('restful', 'restful', $plugin_name);
  if (!($class = ctools_plugin_load_class('restful', 'restful', $plugin_name, 'class'))) {
    throw new \RestfulServiceUnavailable(format_string('Restful plugin class (@plugin) was not found.', array(
      '@plugin' => $plugin_name,
    )));
  }
  $handler = new $class($plugin);
  
  $auth_plugins = $plugin['authentication_types'] === TRUE ? array_keys(restful_get_authentication_plugins()) : $plugin['authentication_types'];
  
  foreach ($auth_plugins as $auth_plugin_name) {
    $auth_handler = restful_get_authentication_handler($auth_plugin_name);
    $handler
      ->getAuthenticationManager()
      ->addAuthenticationProvider($auth_handler);
  }
  
  $handler
    ->getAuthenticationManager()
    ->setIsOptional($plugin['authentication_optional']);
  return $handler;
}
function restful_get_authentication_handler($auth_plugin_name) {
  $auth_plugin = restful_get_authentication_plugin($auth_plugin_name);
  if (!($auth_class = ctools_plugin_get_class($auth_plugin, 'class'))) {
    throw new \RestfulServiceUnavailable(format_string('Authentication plugin class (@plugin) was not found.', array(
      '@plugin' => $auth_plugin_name,
    )));
  }
  return new $auth_class($auth_plugin);
}
function restful_get_formatter_handler($formatter_plugin_name, $restful_handler) {
  $formatter_plugin = restful_get_formatter_plugin($formatter_plugin_name);
  if (!($formatter_class = ctools_plugin_get_class($formatter_plugin, 'class'))) {
    throw new \RestfulServiceUnavailable(format_string('Formatter plugin class (@plugin) was not found.', array(
      '@plugin' => $formatter_plugin_name,
    )));
  }
  return new $formatter_class($formatter_plugin, $restful_handler);
}
function restful_get_restful_handler_for_path($path = NULL) {
  $handlers =& drupal_static(__FUNCTION__);
  $path = is_null($path) ? $_GET['q'] : $path;
  if (isset($handlers[$path])) {
    return $handlers[$path];
  }
  $router_item = \RestfulBase::getMenuItem($path);
  
  if ($router_item['page_callback'] != 'restful_menu_process_callback') {
    $handlers[$path] = FALSE;
    return;
  }
  list($resource, ) = \RestfulBase::getPageArguments($path);
  list($major_version, $minor_version) = \RestfulBase::getVersionFromRequest($path);
  $handlers[$path] = restful_get_restful_handler($resource, $major_version, $minor_version);
  return $handlers[$path];
}
function restful_menu_access_callback($resource_name, $version = NULL) {
  if (!($versions = \RestfulBase::getVersionFromRequest())) {
    
    return;
  }
  if (!($handler = restful_get_restful_handler($resource_name, $versions[0], $versions[1]))) {
    return;
  }
  if (!\RestfulBase::isValidMethod($_SERVER['REQUEST_METHOD'], FALSE)) {
    return;
  }
  $method = strtoupper($_SERVER['REQUEST_METHOD']);
  if ($method == \RestfulInterface::POST && \RestfulManager::getRequestHttpHeader('X-HTTP-Method-Override')) {
    $method = strtoupper(\RestfulManager::getRequestHttpHeader('X-HTTP-Method-Override'));
  }
  if (!\RestfulBase::isValidMethod($method, FALSE)) {
    
    return;
  }
  
  $path = func_get_args();
  array_shift($path);
  if (preg_match('/^v\\d+(\\.\\d+)?$/', $version)) {
    array_shift($path);
  }
  $path = implode('/', $path);
  $request = restful_parse_request();
  $handler
    ->setPath($path);
  $handler
    ->setMethod($method);
  $handler
    ->setRequest($request);
  try {
    $access = $handler
      ->access();
    return $access;
  } catch (Exception $e) {
    return FALSE;
  }
}
function restful_menu_process_callback($resource_name, $version = NULL) {
  list($major_version, $minor_version) = \RestfulBase::getVersionFromRequest();
  $handler = restful_get_restful_handler($resource_name, $major_version, $minor_version);
  if (!$handler instanceof \RestfulDataProviderInterface) {
    throw new \RestfulServiceUnavailable(format_string('The selected plugin (@plugin) does not implement \\RestfulDataProviderInterface.', array(
      '@plugin' => $resource_name . ' v' . $major_version . '.' . $minor_version,
    )));
  }
  
  $headers = $handler
    ->getHttpHeaders();
  $additional_variations = empty($headers['Vary']) ? array(
    'Accept',
  ) : array(
    $headers['Vary'],
    'Accept',
  );
  if (\RestfulManager::getRequestHttpHeader('X-API-Version')) {
    $additional_variations[] = 'X-API-Version';
  }
  if ($additional_variations) {
    $handler
      ->setHttpHeaders('Vary', implode(',', $additional_variations));
  }
  
  if ($allowed_origin = $handler
    ->getPluginKey('allow_origin')) {
    $handler
      ->setHttpHeaders('Access-Control-Allow-Origin', $allowed_origin);
  }
  $method = strtoupper($_SERVER['REQUEST_METHOD']);
  if ($method == \RestfulInterface::POST && \RestfulManager::getRequestHttpHeader('X-HTTP-Method-Override')) {
    $method = \RestfulManager::getRequestHttpHeader('X-HTTP-Method-Override');
  }
  $method = strtolower($method);
  $path = func_get_args();
  array_shift($path);
  if (preg_match('/^v\\d+(\\.\\d+)?$/', $version)) {
    array_shift($path);
  }
  $path = implode('/', $path);
  $request = restful_parse_request();
  try {
    if (!\RestfulBase::isValidMethod($method, FALSE)) {
      throw new RestfulBadRequestException(format_string('Unsupported method @method.', array(
        '@method' => $method,
      )));
    }
    return $handler
      ->{$method}($path, $request);
  } catch (RestfulException $e) {
    $result = array(
      'type' => $e
        ->getType(),
      'title' => $e
        ->getMessage(),
      'status' => $e
        ->getCode(),
      'detail' => $e
        ->getDescription(),
    );
    if ($instance = $e
      ->getInstance()) {
      $result['instance'] = $instance;
    }
    if ($errors = $e
      ->getFieldErrors()) {
      $result['errors'] = $errors;
    }
    foreach ($e
      ->getHeaders() as $header_name => $header_value) {
      drupal_add_http_header($header_name, $header_value);
    }
    if ($e
      ->getCode() < 500) {
      
      watchdog('restful', $e
        ->getMessage());
    }
    else {
      
      watchdog_exception('restful', $e);
    }
  } catch (Exception $e) {
    $result = array(
      'type' => 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1',
      'title' => $e
        ->getMessage(),
      'status' => 500,
    );
    watchdog_exception('restful', $e);
  }
  
  drupal_add_http_header('Status', $result['status']);
  drupal_add_http_header('Content-Type', 'application/problem+json; charset=utf-8');
  return $result;
}
function restful_parse_request() {
  $request =& drupal_static(__FUNCTION__, NULL);
  if ($request) {
    return $request;
  }
  $method = strtoupper($_SERVER['REQUEST_METHOD']);
  if ($method == \RestfulInterface::GET) {
    $request = $_GET;
  }
  elseif ($method == \RestfulInterface::POST) {
    $request = $_POST;
  }
  if (!$request && ($query_string = _restful_get_query_string_from_php_input())) {
    
    if ($decoded_json = drupal_json_decode($query_string)) {
      $request = $decoded_json;
    }
    else {
      parse_str($query_string, $request);
    }
  }
  
  $request['__application'] = array(
    'rest_call' => TRUE,
    'csrf_token' => \RestfulManager::getRequestHttpHeader('X-CSRF-Token'),
  );
  
  drupal_alter('restful_parse_request', $request);
  return $request;
}
function restful_delivery($var = NULL, $method = 'format') {
  if ($restful_handler = restful_get_restful_handler_for_path()) {
    
    foreach ($restful_handler
      ->getHttpHeaders() as $key => $value) {
      drupal_add_http_header($key, $value);
    }
  }
  if (!isset($var)) {
    return;
  }
  if (is_int($var)) {
    _restful_get_json_from_menu_status($var);
    
    drupal_add_http_header('Status', $var['status']);
    drupal_add_http_header('Content-Type', 'application/problem+json; charset=utf-8');
  }
  
  if ($restful_handler && $restful_handler
    ->getMethod() == \RestfulInterface::OPTIONS) {
    $method = 'render';
  }
  
  try {
    $formatter_handler = \RestfulManager::outputFormat($restful_handler);
    $output = $formatter_handler
      ->{$method}($var);
    
    drupal_add_http_header('Content-Type', $formatter_handler
      ->getContentTypeHeader());
  } catch (\RestfulException $e) {
    
    drupal_add_http_header('Status', $e
      ->getCode());
    echo $e
      ->getMessage();
    return;
  }
  print $output;
  \RestfulManager::pageFooter();
}
function restful_formatted_delivery($var = NULL) {
  restful_delivery($var, 'format');
}
function restful_unprepared_delivery($var = NULL) {
  restful_delivery($var, 'render');
}
function _restful_get_json_from_menu_status(&$var) {
  switch ($var) {
    case MENU_NOT_FOUND:
      $class_name = 'RestfulNotFoundException';
      $message = 'Invalid URL path.';
      break;
    case MENU_ACCESS_DENIED:
      $class_name = 'RestfulForbiddenException';
      $message = 'Access denied.';
      break;
    case MENU_SITE_OFFLINE:
      $class_name = 'RestfulServiceUnavailable';
      $message = 'Site is offline.';
      break;
  }
  $e = new $class_name($message);
  $var = array(
    'type' => $e
      ->getType(),
    'title' => $e
      ->getMessage(),
    'status' => $e
      ->getCode(),
    'detail' => $e
      ->getDescription(),
  );
  if ($instance = $e
    ->getInstance()) {
    $var['instance'] = $instance;
  }
  if ($errors = $e
    ->getFieldErrors()) {
    $var['errors'] = $errors;
  }
}
function restful_page_delivery_callback_alter(&$callback) {
  if (!variable_get('restful_hijack_api_pages', TRUE)) {
    return;
  }
  $base_path = variable_get('restful_hook_menu_base_path', 'api');
  if (strpos($_GET['q'], $base_path . '/') !== 0 && $_GET['q'] != $base_path) {
    
    return;
  }
  if (menu_get_item()) {
    
    return;
  }
  $callback = 'restful_deliver_menu_not_found';
}
function restful_deliver_menu_not_found($page_callback_result) {
  restful_delivery(MENU_NOT_FOUND);
}
function restful_cron() {
  \RestfulRateLimitManager::deleteExpired();
}
function restful_csrf_session_token() {
  return array(
    'X-CSRF-Token' => drupal_get_token(\RestfulInterface::TOKEN_VALUE),
  );
}
function restful_date_time_format_element_validate($element, &$form_state) {
  $value = $element['#value'];
  try {
    new \DateInterval($value);
  } catch (\Exception $e) {
    form_error($element, t('%name must be compatible with the !link.', array(
      '%name' => $element['#title'],
      '!link' => l(t('\\DateInterval format'), 'http://php.net/manual/en/class.dateinterval.php'),
    )));
  }
}
function restful_is_user_switched() {
  $restful_switch_user =& drupal_static(__FUNCTION__, FALSE);
  if (!$restful_switch_user) {
    $restful_switch_user = TRUE;
    return FALSE;
  }
  return TRUE;
}
function _restful_get_query_string_from_php_input() {
  $query_string =& drupal_static(__FUNCTION__);
  if (!isset($query_string)) {
    
    $query_string = file_get_contents('php://input');
  }
  return $query_string;
}