PageRenderTime 33ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/core/model/modx/rest/modrestserver.class.php

http://github.com/modxcms/revolution
PHP | 379 lines | 226 code | 30 blank | 123 comment | 33 complexity | e606c7bf02786b875ced961c55d6402e 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. /**
  11. * An extendable class for handling REST requests.
  12. *
  13. * @deprecated To be removed in 2.3. See modRestService instead.
  14. *
  15. * @package modx
  16. * @subpackage rest
  17. */
  18. class modRestServer {
  19. const OPT_AUTH = 'authenticate';
  20. const OPT_AUTH_GET = 'authenticateGet';
  21. const OPT_AUTH_USER_VAR = 'authUserVar';
  22. const OPT_AUTH_PASS_VAR = 'authPassVar';
  23. const OPT_ENCODING = 'encoding';
  24. const OPT_ERROR_DATA_NODE = 'error_data_node';
  25. const OPT_ERROR_NODE = 'error_node';
  26. const OPT_ERROR_MESSAGE_NODE = 'error_message_node';
  27. const OPT_FORMAT = 'format';
  28. const OPT_PROCESSORS_PATH = 'processors_path';
  29. const OPT_REQUEST_PATH = 'request_path';
  30. const OPT_REQUEST_VAR = 'requestVar';
  31. const OPT_REALM = 'realm';
  32. const OPT_RENDERERS = 'renderers';
  33. /**
  34. * @var $error The current error message
  35. * @access protected
  36. */
  37. protected $error = false;
  38. /**
  39. * @param modX $modx A reference to the modX object
  40. * @param array $config An array of configuration options
  41. */
  42. function __construct(modX &$modx,array $config = array()) {
  43. $this->modx =& $modx;
  44. $this->config = array_merge(array(
  45. modRestServer::OPT_AUTH => true,
  46. modRestServer::OPT_AUTH_GET => false,
  47. modRestServer::OPT_AUTH_USER_VAR => 'user',
  48. modRestServer::OPT_AUTH_PASS_VAR => 'password',
  49. modRestServer::OPT_ENCODING => 'UTF-8',
  50. modRestServer::OPT_FORMAT => 'xml',
  51. modRestServer::OPT_PROCESSORS_PATH => '',
  52. modRestServer::OPT_REQUEST_VAR => 'p',
  53. modRestServer::OPT_REALM => 'MODX',
  54. modRestServer::OPT_RENDERERS => 'renderers',
  55. modRestServer::OPT_ERROR_DATA_NODE => 'data',
  56. modRestServer::OPT_ERROR_NODE => 'error',
  57. modRestServer::OPT_ERROR_MESSAGE_NODE => 'message',
  58. ),$config);
  59. $this->modx->deprecated('2.3.0', 'Use the modRestService classes instead.');
  60. }
  61. /**
  62. * Handles the REST request and loads the correct processor. Checks for
  63. * authentication should it be a type not equal to GET if authenticate is
  64. * set to true, or always if authenticateGet is set to true.
  65. *
  66. * @access public
  67. * @return string
  68. */
  69. public function handle() {
  70. $scriptProperties = array();
  71. $scriptProperties[modRestServer::OPT_REQUEST_PATH] = $this->computePath();
  72. $output = '';
  73. if (file_exists($scriptProperties[modRestServer::OPT_REQUEST_PATH])) {
  74. if ($_SERVER['REQUEST_METHOD'] == 'GET') {
  75. if ($this->config[modRestServer::OPT_AUTH_GET]) {
  76. $result = $this->authenticate();
  77. if ($result !== true) {
  78. return $result;
  79. }
  80. }
  81. $scriptProperties = array_merge($scriptProperties,$_GET);
  82. } else {
  83. if ($this->config[modRestServer::OPT_AUTH]) {
  84. $result = $this->authenticate();
  85. if ($result !== true) {
  86. return $result;
  87. }
  88. }
  89. }
  90. $modx =& $this->modx;
  91. $output = include $scriptProperties[modRestServer::OPT_REQUEST_PATH];
  92. } else {
  93. return $this->error('404 Not Found',$scriptProperties);
  94. }
  95. return $output;
  96. }
  97. /**
  98. * Computes the path for the REST request
  99. *
  100. * @access public
  101. * @return string The absolute path to the processor to load
  102. */
  103. public function computePath() {
  104. $path = $this->config[modRestServer::OPT_PROCESSORS_PATH];
  105. $path .= trim($_REQUEST[$this->config[modRestServer::OPT_REQUEST_VAR]],'/').'/';
  106. $path .= strtolower($_SERVER['REQUEST_METHOD']).'.php';
  107. return $path;
  108. }
  109. /**
  110. * Handles basic authentication for the server
  111. *
  112. * @todo Add an optional usergroup check
  113. *
  114. * @return boolean True if successful.
  115. */
  116. public function authenticate() {
  117. $this->modx->getService('lexicon','modLexicon');
  118. $this->modx->lexicon->load('user');
  119. if (empty($_REQUEST[$this->config[modRestServer::OPT_AUTH_USER_VAR]])) {
  120. return $this->deny($this->modx->lexicon('user_err_ns'));
  121. }
  122. if (empty($_REQUEST[$this->config[modRestServer::OPT_AUTH_PASS_VAR]])) {
  123. return $this->deny($this->modx->lexicon('user_err_not_specified_password'));
  124. }
  125. $user = $this->modx->getObject('modUser',array(
  126. 'username' => $_REQUEST[$this->config[modRestServer::OPT_AUTH_USER_VAR]],
  127. ));
  128. if (empty($user)) return $this->deny($this->modx->lexicon('user_err_nf'));
  129. if (!$user->passwordMatches($_REQUEST[$this->config[modRestServer::OPT_AUTH_PASS_VAR]])) {
  130. return $this->deny($this->modx->lexicon('user_err_password'));
  131. }
  132. return true;
  133. }
  134. /**
  135. * Deny access and send a 401.
  136. *
  137. * @param string $message
  138. * @param array $data
  139. * @return string
  140. */
  141. public function deny($message,array $data = array()) {
  142. return $this->error($message,$data,'401');
  143. }
  144. /**
  145. * Handles success messages
  146. *
  147. * @param array|xPDOObject $data The data to pass and encode
  148. * @param string $root
  149. * @return string The encoded message
  150. */
  151. public function success($data,$root = '') {
  152. header($_SERVER['SERVER_PROTOCOL'].' 200 OK');
  153. return $this->encode($data,$root);
  154. }
  155. /**
  156. * Handles error messages
  157. *
  158. * @access public
  159. * @param string $message An error message
  160. * @param array|xPDOObject $data Any additional data
  161. * @param string $type The type of the error message
  162. * @return string
  163. */
  164. public function error($message = '',$data = array(),$type = '404') {
  165. $this->error = true;
  166. if (method_exists($this,'_err'.$type)) {
  167. $errType = '_err'.$type;
  168. $this->$errType();
  169. } else {
  170. $this->_err404();
  171. }
  172. return $this->encode(array(
  173. $this->config[modRestServer::OPT_ERROR_MESSAGE_NODE] => $message,
  174. $this->config[modRestServer::OPT_ERROR_DATA_NODE] => $data,
  175. ),'<'.$this->config[modRestServer::OPT_ERROR_NODE].'>');
  176. }
  177. /**
  178. * Encodes the data to the specified format. Defaults to XML.
  179. *
  180. * @access public
  181. * @param array|xPDOObject $data
  182. * @param string $root
  183. * @return string The encoded message
  184. */
  185. public function encode($data,$root = '') {
  186. $output = '';
  187. $format = $this->modx->getOption(modRestServer::OPT_FORMAT,$_REQUEST,$this->config[modRestServer::OPT_FORMAT]);
  188. switch ($format) {
  189. case 'json':
  190. header('Content-Type: application/javascript');
  191. if (is_array($data)) {
  192. $list = array();
  193. foreach ($data as $k => $v) {
  194. $list[$k.'s'] = $v;
  195. }
  196. } else {
  197. $list = array($root => $data->toArray());
  198. }
  199. $output = $this->modx->toJSON($list);
  200. break;
  201. case 'xml':
  202. default:
  203. header('Content-Type: text/xml');
  204. if (is_array($data)) {
  205. $list = $data;
  206. } else if ($data instanceof xPDOObject) {
  207. $list = $data->toArray();
  208. }
  209. $output = '<?xml version="1.0" encoding="'.$this->config['encoding'].'"?>'.
  210. "\n{$root}\n";
  211. $output .= $this->array2xml($list);
  212. $endRootTag = '</'.substr($root,1,strpos($root,' ')-1).'>';
  213. $output .= "{$endRootTag}\n";
  214. break;
  215. }
  216. return $output;
  217. }
  218. /**
  219. * Sets HTTP 204 response headers
  220. * @param string $output The outputted response to send
  221. * @return string
  222. */
  223. private function _err204($output = '') {
  224. header($_SERVER['SERVER_PROTOCOL'].' 204 No Content');
  225. return $output;
  226. }
  227. /**
  228. * Sets HTTP 400 response headers
  229. * @param string $output The outputted response to send
  230. * @return string
  231. */
  232. private function _err400($output = '') {
  233. header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request');
  234. return '';
  235. }
  236. /**
  237. * Sets HTTP 401 response headers
  238. * @return string
  239. */
  240. private function _err401() {
  241. header('WWW-Authenticate: Basic realm="'.$this->config['realm'].'"');
  242. header($_SERVER['SERVER_PROTOCOL'].' 401 Unauthorized');
  243. return '';
  244. }
  245. /**
  246. * Sets HTTP 404 response headers
  247. * @param array $scriptProperties An array of properties
  248. * @return string
  249. */
  250. private function _err404($scriptProperties = array()) {
  251. header("{$_SERVER['SERVER_PROTOCOL']} 404 Not Found");
  252. $output = '<h2>404 Not Found</h2>';
  253. if (!empty($scriptProperties)) {
  254. $output .= '<p>'.$scriptProperties['called'].' is not a valid request.</p>';
  255. }
  256. return $output;
  257. }
  258. /**
  259. * Sets HTTP 405 response headers
  260. * @param string $allowed A comma-separated list of allowed protocols
  261. * @return string
  262. */
  263. private function _err405($allowed = 'GET, HEAD') {
  264. header($_SERVER['SERVER_PROTOCOL'].' 405 Method Not Allowed');
  265. header('Allow: '.$allowed);
  266. return '';
  267. }
  268. /**
  269. * Sets HTTP 406 response headers
  270. * @param string $output The outputted response to send
  271. * @return string
  272. */
  273. private function _err406($output = '') {
  274. header($_SERVER['SERVER_PROTOCOL'].' 406 Not Acceptable');
  275. $output = join(', ', array_keys($this->config[modRestServer::OPT_RENDERERS]));
  276. return $output;
  277. }
  278. /**
  279. * Sets HTTP 411 response headers
  280. * @param string $output The outputted response to send
  281. * @return string
  282. */
  283. private function _err411($output = '') {
  284. header($_SERVER['SERVER_PROTOCOL'].' 411 Length Required');
  285. return '';
  286. }
  287. /**
  288. * Sets HTTP 500 response headers
  289. * @param string $output The outputted response to send
  290. * @return string
  291. */
  292. private function _err500($output = '') {
  293. header($_SERVER['SERVER_PROTOCOL'].' 500 Internal Server Error');
  294. return '';
  295. }
  296. /**
  297. * Converts an array to xml
  298. *
  299. * @access protected
  300. * @param array $array
  301. * @param integer $level
  302. * @return string
  303. */
  304. protected function array2xml($array,$level=1) {
  305. $xml = '';
  306. foreach ($array as $key=>$value) {
  307. $key = strtolower($key);
  308. if (is_array($value)) {
  309. $multi_tags = false;
  310. foreach($value as $key2=>$value2) {
  311. if (is_array($value2)) {
  312. $xml .= str_repeat("\t",$level)."<$key>\n";
  313. $xml .= $this->array2xml($value2,$level+1);
  314. $xml .= str_repeat("\t",$level)."</$key>\n";
  315. $multi_tags = true;
  316. } else {
  317. if (trim($value2)!='') {
  318. if (htmlspecialchars($value2)!=$value2) {
  319. $xml .= str_repeat("\t",$level).
  320. "<$key><![CDATA[$value2]]>".
  321. "</$key>\n";
  322. } else {
  323. $xml .= str_repeat("\t",$level).
  324. "<$key>$value2</$key>\n";
  325. }
  326. }
  327. $multi_tags = true;
  328. }
  329. }
  330. if (!$multi_tags and count($value)>0) {
  331. $xml .= str_repeat("\t",$level)."<$key>\n";
  332. $xml .= $this->array2xml($value,$level+1);
  333. $xml .= str_repeat("\t",$level)."</$key>\n";
  334. }
  335. } else {
  336. if (trim($value)!='') {
  337. if (htmlspecialchars($value)!=$value) {
  338. $xml .= str_repeat("\t",$level)."<$key>".
  339. "<![CDATA[$value]]></$key>\n";
  340. } else {
  341. $xml .= str_repeat("\t",$level).
  342. "<$key>$value</$key>\n";
  343. }
  344. }
  345. }
  346. }
  347. return $xml;
  348. }
  349. }