PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

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

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