PageRenderTime 44ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/core/model/modx/modconnectorresponse.class.php

http://github.com/modxcms/revolution
PHP | 278 lines | 167 code | 14 blank | 97 comment | 49 complexity | f49a23070dcf4b945c51df92acae3200 MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /*
  3. * This file is part of MODX Revolution.
  4. *
  5. * Copyright (c) MODX, LLC. All Rights Reserved.
  6. *
  7. * For complete copyright and license information, see the COPYRIGHT and LICENSE
  8. * files found in the top-level directory of this distribution.
  9. */
  10. require_once MODX_CORE_PATH . 'model/modx/modresponse.class.php';
  11. /**
  12. * Encapsulates an HTTP response from the MODX manager.
  13. *
  14. * {@inheritdoc}
  15. *
  16. * @package modx
  17. * @extends modResponse
  18. */
  19. class modConnectorResponse extends modResponse {
  20. /**
  21. * The base location of the processors called by the connectors.
  22. *
  23. * @var string
  24. * @access private
  25. */
  26. protected $_directory;
  27. public $responseCode = 200;
  28. protected $_responseCodes = array(
  29. 100 => 'Continue',
  30. 101 => 'Switching Protocols',
  31. 200 => 'OK',
  32. 201 => 'Created',
  33. 202 => 'Accepted',
  34. 203 => 'Non-Authoritative Information',
  35. 204 => 'No Content',
  36. 205 => 'Reset Content',
  37. 206 => 'Partial Content',
  38. 300 => 'Multiple Choices',
  39. 301 => 'Moved Permanently',
  40. 302 => 'Found',
  41. 303 => 'See Other',
  42. 304 => 'Not Modified',
  43. 305 => 'Use Proxy',
  44. 306 => '(Unused)',
  45. 307 => 'Temporary Redirect',
  46. 400 => 'Bad Request',
  47. 401 => 'Unauthorized',
  48. 402 => 'Payment Required',
  49. 403 => 'Forbidden',
  50. 404 => 'Not Found',
  51. 405 => 'Method Not Allowed',
  52. 406 => 'Not Acceptable',
  53. 407 => 'Proxy Authentication Required',
  54. 408 => 'Request Timeout',
  55. 409 => 'Conflict',
  56. 410 => 'Gone',
  57. 411 => 'Length Required',
  58. 412 => 'Precondition Failed',
  59. 413 => 'Request Entity Too Large',
  60. 414 => 'Request-URI Too Long',
  61. 415 => 'Unsupported Media Type',
  62. 416 => 'Requested Range Not Satisfiable',
  63. 417 => 'Expectation Failed',
  64. 500 => 'Internal Server Error',
  65. 501 => 'Not Implemented',
  66. 502 => 'Bad Gateway',
  67. 503 => 'Service Unavailable',
  68. 504 => 'Gateway Timeout',
  69. 505 => 'HTTP Version Not Supported'
  70. );
  71. /**
  72. * Creates a modConnectorResponse object.
  73. *
  74. * {@inheritdoc}
  75. */
  76. function __construct(modX & $modx) {
  77. parent :: __construct($modx);
  78. $this->setDirectory();
  79. }
  80. /**
  81. * Overrides modResponse::outputContent to provide connector-specific
  82. * processing.
  83. *
  84. * {@inheritdoc}
  85. */
  86. public function outputContent(array $options = array()) {
  87. /* variable pointer for easier access */
  88. $modx =& $this->modx;
  89. /* backwards compat */
  90. $error =& $this->modx->error;
  91. /* prevent browsing of subdirectories for security */
  92. $target = preg_replace('/[\.]{2,}/', '', htmlspecialchars($options['action']));
  93. $siteId = $this->modx->user->getUserToken($this->modx->context->get('key'));
  94. $isLogin = $target == 'login' || $target == 'security/login';
  95. /* Block the user if there's no user token for the current context, and permissions are in fact required */
  96. if (empty($siteId) && (!defined('MODX_REQP') || MODX_REQP === TRUE)) {
  97. $this->responseCode = 401;
  98. $this->body = $modx->error->failure($modx->lexicon('access_denied'),array('code' => 401));
  99. }
  100. /* Make sure we've got a token */
  101. elseif (!$isLogin && !isset($_SERVER['HTTP_MODAUTH']) && (!isset($_REQUEST['HTTP_MODAUTH']) || empty($_REQUEST['HTTP_MODAUTH']))) {
  102. $this->responseCode = 401;
  103. $this->body = $modx->error->failure($modx->lexicon('access_denied'),array('code' => 401));
  104. }
  105. /* If the token was passed as a request header (like in the manager), check if it's right */
  106. else if (!$isLogin && isset($_SERVER['HTTP_MODAUTH']) && $_SERVER['HTTP_MODAUTH'] != $siteId) {
  107. $this->responseCode = 401;
  108. $this->body = $modx->error->failure($modx->lexicon('access_denied'),array('code' => 401));
  109. }
  110. /* If the token was passed a request variable, check if it's right */
  111. else if (!$isLogin && isset($_REQUEST['HTTP_MODAUTH']) && $_REQUEST['HTTP_MODAUTH'] != $siteId) {
  112. $this->responseCode = 401;
  113. $this->body = $modx->error->failure($modx->lexicon('access_denied'), array('code' => 401));
  114. }
  115. /* verify the location and action */
  116. /*else if (!isset($options['location']) || !isset($options['action'])) {
  117. $this->responseCode = 404;
  118. $this->body = $this->modx->error->failure($modx->lexicon('action_err_ns'),array('code' => 404));
  119. }*/
  120. /* If we don't have an action, 404 out */
  121. else if (empty($options['action'])) {
  122. $this->responseCode = 404;
  123. $this->body = $this->modx->error->failure($modx->lexicon('action_err_ns'), array('code' => 404));
  124. }
  125. /* execute a processor and format the response */
  126. else {
  127. /* create scriptProperties array from HTTP GPC vars */
  128. if (!isset($_POST)) $_POST = array();
  129. if (!isset($_GET) || $isLogin) $_GET = array();
  130. $scriptProperties = array_merge($_GET,$_POST);
  131. if (isset($_FILES) && !empty($_FILES)) {
  132. $scriptProperties = array_merge($scriptProperties,$_FILES);
  133. }
  134. /* run processor */
  135. $this->response = $this->modx->runProcessor($target,$scriptProperties,$options);
  136. if (!$this->response) {
  137. $this->responseCode = 404;
  138. $this->body = $this->modx->error->failure($this->modx->lexicon('processor_err_nf',array(
  139. 'target' => $target,
  140. )));
  141. } else {
  142. $this->body = $this->response->getResponse();
  143. }
  144. }
  145. /* if files sent, this means that the browser needs it in text/plain,
  146. * so ignore text/json header type
  147. */
  148. if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
  149. header("Content-Type: application/json; charset=UTF-8");
  150. $message = 'OK';
  151. if (array_key_exists($this->responseCode,$this->_responseCodes)) {
  152. $message = $this->_responseCodes[$this->responseCode];
  153. }
  154. header('Status: '.$this->responseCode.' '.$message);
  155. header('Version: ' . $_SERVER['SERVER_PROTOCOL']);
  156. }
  157. if (is_array($this->header)) {
  158. foreach ($this->header as $header) header($header);
  159. }
  160. if (is_array($this->body)) {
  161. @session_write_close();
  162. $json = $this->modx->toJSON(array(
  163. 'success' => isset($this->body['success']) ? $this->body['success'] : 0,
  164. 'message' => isset($this->body['message']) ? $this->body['message'] : $this->modx->lexicon('error'),
  165. 'total' => (isset($this->body['total']) && $this->body['total'] > 0)
  166. ? intval($this->body['total'])
  167. : (isset($this->body['errors'])
  168. ? count($this->body['errors'])
  169. : 1),
  170. 'data' => isset($this->body['errors']) ? $this->body['errors'] : array(),
  171. 'object' => isset($this->body['object']) ? $this->body['object'] : array(),
  172. ));
  173. if (!empty($_GET['callback'])) {
  174. $json = $modx->stripTags($_GET['callback']) . '(' . $json . ')';
  175. }
  176. die($json);
  177. } else {
  178. @session_write_close();
  179. die($this->body);
  180. }
  181. }
  182. /**
  183. * Return arrays of objects (with count) converted to JSON.
  184. *
  185. * The JSON result includes two main elements, total and results. This format is used for list
  186. * results.
  187. *
  188. * @access public
  189. * @param array $array An array of data objects.
  190. * @param mixed $count The total number of objects. Used for pagination.
  191. * @return string The JSON output.
  192. */
  193. public function outputArray(array $array,$count = false) {
  194. if (!is_array($array)) return false;
  195. if ($count === false) { $count = count($array); }
  196. return '({"total":"'.$count.'","results":'.$this->modx->toJSON($array).'})';
  197. }
  198. /**
  199. * Set the physical location of the processor directory for the response handler.
  200. *
  201. * This allows for dynamic processor locations.
  202. *
  203. * @access public
  204. * @param string $dir The directory to set as the processors directory.
  205. */
  206. public function setDirectory($dir = '') {
  207. if ($dir == '') {
  208. $this->_directory = $this->modx->getOption('processors_path');
  209. } else {
  210. $this->_directory = $dir;
  211. }
  212. }
  213. /**
  214. * Converts PHP to JSON with JavaScript literals left in-tact.
  215. *
  216. * JSON does not allow JavaScript literals, but this function encodes certain identifiable
  217. * literals and decodes them back into literals after modX::toJSON() formats the data.
  218. *
  219. * @access public
  220. * @param mixed $data The PHP data to be converted.
  221. * @return string The extended JSON-encoded string.
  222. */
  223. public function toJSON($data) {
  224. if (is_array($data)) {
  225. array_walk_recursive($data, array(&$this, '_encodeLiterals'));
  226. }
  227. return $this->_decodeLiterals($this->modx->toJSON($data));
  228. }
  229. /**
  230. * Encodes certain JavaScript literal strings for later decoding.
  231. *
  232. * @access protected
  233. * @param mixed &$value A reference to the value to be encoded if it is identified as a literal.
  234. * @param integer|string $key The array key corresponding to the value.
  235. */
  236. protected function _encodeLiterals(&$value, $key) {
  237. if (is_string($value)) {
  238. /* properly handle common literal structures */
  239. if (strpos($value, 'function(') === 0
  240. || strpos($value, 'this.') === 0
  241. || strpos($value, 'new Function(') === 0
  242. || strpos($value, 'Ext.') === 0) {
  243. $value = '@@' . base64_encode($value) . '@@';
  244. }
  245. }
  246. }
  247. /**
  248. * Decodes strings encoded by _encodeLiterals to restore JavaScript literals.
  249. *
  250. * @access protected
  251. * @param string $string The JSON-encoded string with encoded literals.
  252. * @return string The JSON-encoded string with literals restored.
  253. */
  254. protected function _decodeLiterals($string) {
  255. $pattern = '/"@@(.*?)@@"/';
  256. $string = preg_replace_callback(
  257. $pattern,
  258. function ($matches) { return base64_decode($matches[1]); },
  259. $string
  260. );
  261. return $string;
  262. }
  263. }