PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/pear/HTML/AJAX.php

https://bitbucket.org/ceu/moodle_demo
PHP | 1076 lines | 699 code | 72 blank | 305 comment | 72 complexity | 7815e053b58475259beb557228f10c7d MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * OO AJAX Implementation for PHP
  4. *
  5. * SVN Rev: $Id: AJAX.php,v 1.1.2.1 2008/10/03 07:09:50 nicolasconnault Exp $
  6. *
  7. * @category HTML
  8. * @package AJAX
  9. * @author Joshua Eichorn <josh@bluga.net>
  10. * @author Arpad Ray <arpad@php.net>
  11. * @author David Coallier <davidc@php.net>
  12. * @author Elizabeth Smith <auroraeosrose@gmail.com>
  13. * @copyright 2005-2008 Joshua Eichorn, Arpad Ray, David Coallier, Elizabeth Smith
  14. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  15. * @version Release: 0.5.6
  16. * @link http://pear.php.net/package/HTML_AJAX
  17. */
  18. /**
  19. * This is a quick hack, loading serializers as needed doesn't work in php5
  20. */
  21. require_once "HTML/AJAX/Serializer/JSON.php";
  22. require_once "HTML/AJAX/Serializer/Null.php";
  23. require_once "HTML/AJAX/Serializer/Error.php";
  24. require_once "HTML/AJAX/Serializer/XML.php";
  25. require_once "HTML/AJAX/Serializer/PHP.php";
  26. require_once 'HTML/AJAX/Debug.php';
  27. /**
  28. * OO AJAX Implementation for PHP
  29. *
  30. * @category HTML
  31. * @package AJAX
  32. * @author Joshua Eichorn <josh@bluga.net>
  33. * @author Arpad Ray <arpad@php.net>
  34. * @author David Coallier <davidc@php.net>
  35. * @author Elizabeth Smith <auroraeosrose@gmail.com>
  36. * @copyright 2005-2008 Joshua Eichorn, Arpad Ray, David Coallier, Elizabeth Smith
  37. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  38. * @version Release: 0.5.6
  39. * @link http://pear.php.net/package/HTML_AJAX
  40. */
  41. class HTML_AJAX
  42. {
  43. /**
  44. * An array holding the instances were exporting
  45. *
  46. * key is the exported name
  47. *
  48. * row format is
  49. * <code>
  50. * array('className'=>'','exportedName'=>'','instance'=>'','exportedMethods=>'')
  51. * </code>
  52. *
  53. * @var object
  54. * @access private
  55. */
  56. var $_exportedInstances = array();
  57. /**
  58. * Set the server url in the generated stubs to this value
  59. * If set to false, serverUrl will not be set
  60. * @var false|string
  61. */
  62. var $serverUrl = false;
  63. /**
  64. * What encoding your going to use for serializing data
  65. * from php being sent to javascript.
  66. *
  67. * @var string JSON|PHP|Null
  68. */
  69. var $serializer = 'JSON';
  70. /**
  71. * What encoding your going to use for unserializing data sent from javascript
  72. * @var string JSON|PHP|Null
  73. */
  74. var $unserializer = 'JSON';
  75. /**
  76. * Option to use loose typing for JSON encoding
  77. * @var bool
  78. * @access public
  79. */
  80. var $jsonLooseType = true;
  81. /**
  82. * Content-type map
  83. *
  84. * Used in to automatically choose serializers as needed
  85. */
  86. var $contentTypeMap = array(
  87. 'JSON' => 'application/json',
  88. 'XML' => 'application/xml',
  89. 'Null' => 'text/plain',
  90. 'Error' => 'application/error',
  91. 'PHP' => 'application/php-serialized',
  92. 'Urlencoded' => 'application/x-www-form-urlencoded'
  93. );
  94. /**
  95. * This is the debug variable that we will be passing the
  96. * HTML_AJAX_Debug instance to.
  97. *
  98. * @param object HTML_AJAX_Debug
  99. */
  100. var $debug;
  101. /**
  102. * This is to tell if debug is enabled or not. If so, then
  103. * debug is called, instantiated then saves the file and such.
  104. */
  105. var $debugEnabled = false;
  106. /**
  107. * This puts the error into a session variable is set to true.
  108. * set to false by default.
  109. *
  110. * @access public
  111. */
  112. var $debugSession = false;
  113. /**
  114. * Boolean telling if the Content-Length header should be sent.
  115. *
  116. * If your using a gzip handler on an output buffer, or run into
  117. * any compatability problems, try disabling this.
  118. *
  119. * @access public
  120. * @var boolean
  121. */
  122. var $sendContentLength = true;
  123. /**
  124. * Make Generated code compatible with php4 by lowercasing all
  125. * class/method names before exporting to JavaScript.
  126. *
  127. * If you have code that works on php4 but not on php5 then setting
  128. * this flag can fix the problem. The recommended solution is too
  129. * specify the class and method names when registering the class
  130. * letting you have function case in php4 as well
  131. *
  132. * @access public
  133. * @var boolean
  134. */
  135. var $php4CompatCase = false;
  136. /**
  137. * Automatically pack all generated JavaScript making it smaller
  138. *
  139. * If your using output compression this might not make sense
  140. */
  141. var $packJavaScript = false;
  142. /**
  143. * Holds current payload info
  144. *
  145. * @access private
  146. * @var string
  147. */
  148. var $_payload;
  149. /**
  150. * Holds iframe id IF this is an iframe xmlhttprequest
  151. *
  152. * @access private
  153. * @var string
  154. */
  155. var $_iframe;
  156. /**
  157. * Holds the list of classes permitted to be unserialized
  158. *
  159. * @access private
  160. * @var array
  161. */
  162. var $_allowedClasses = array();
  163. /**
  164. * Holds serializer instances
  165. */
  166. var $_serializers = array();
  167. /**
  168. * PHP callbacks we're exporting
  169. */
  170. var $_validCallbacks = array();
  171. /**
  172. * Interceptor instance
  173. */
  174. var $_interceptor = false;
  175. /**
  176. * Set a class to handle requests
  177. *
  178. * @param object &$instance An instance to export
  179. * @param mixed $exportedName Name used for the javascript class,
  180. * if false the name of the php class is used
  181. * @param mixed $exportedMethods If false all functions without a _ prefix
  182. * are exported, if an array only the methods
  183. * listed in the array are exported
  184. *
  185. * @return void
  186. */
  187. function registerClass(&$instance, $exportedName = false,
  188. $exportedMethods = false)
  189. {
  190. $className = strtolower(get_class($instance));
  191. if ($exportedName === false) {
  192. $exportedName = get_class($instance);
  193. if ($this->php4CompatCase) {
  194. $exportedName = strtolower($exportedName);
  195. }
  196. }
  197. if ($exportedMethods === false) {
  198. $exportedMethods = $this->_getMethodsToExport($className);
  199. }
  200. $index = strtolower($exportedName);
  201. $this->_exportedInstances[$index] = array();
  202. $this->_exportedInstances[$index]['className'] = $className;
  203. $this->_exportedInstances[$index]['exportedName'] = $exportedName;
  204. $this->_exportedInstances[$index]['instance'] =& $instance;
  205. $this->_exportedInstances[$index]['exportedMethods'] = $exportedMethods;
  206. }
  207. /**
  208. * Get a list of methods in a class to export
  209. *
  210. * This function uses get_class_methods to get a list of callable methods,
  211. * so if you're on PHP5 extending this class with a class you want to export
  212. * should export its protected methods, while normally only its public methods
  213. * would be exported. All methods starting with _ are removed from the export list.
  214. * This covers PHP4 style private by naming as well as magic methods in either PHP4 or PHP5
  215. *
  216. * @param string $className Name of the class
  217. *
  218. * @return array all methods of the class that are public
  219. * @access private
  220. */
  221. function _getMethodsToExport($className)
  222. {
  223. $funcs = get_class_methods($className);
  224. foreach ($funcs as $key => $func) {
  225. if (strtolower($func) === $className || substr($func, 0, 1) === '_') {
  226. unset($funcs[$key]);
  227. } else if ($this->php4CompatCase) {
  228. $funcs[$key] = strtolower($func);
  229. }
  230. }
  231. return $funcs;
  232. }
  233. /**
  234. * Generate the client Javascript code
  235. *
  236. * @return string generated javascript client code
  237. */
  238. function generateJavaScriptClient()
  239. {
  240. $client = '';
  241. $names = array_keys($this->_exportedInstances);
  242. foreach ($names as $name) {
  243. $client .= $this->generateClassStub($name);
  244. }
  245. return $client;
  246. }
  247. /**
  248. * Return the stub for a class
  249. *
  250. * @param string $name name of the class to generated the stub for,
  251. * note that this is the exported name not the php class name
  252. *
  253. * @return string javascript proxy stub code for a single class
  254. */
  255. function generateClassStub($name)
  256. {
  257. if (!isset($this->_exportedInstances[$name])) {
  258. return '';
  259. }
  260. $client = "// Client stub for the {$this->_exportedInstances[$name]['exportedName']} PHP Class\n";
  261. $client .= "function {$this->_exportedInstances[$name]['exportedName']}(callback) {\n";
  262. $client .= "\tmode = 'sync';\n";
  263. $client .= "\tif (callback) { mode = 'async'; }\n";
  264. $client .= "\tthis.className = '{$this->_exportedInstances[$name]['exportedName']}';\n";
  265. if ($this->serverUrl) {
  266. $client .= "\tthis.dispatcher = new HTML_AJAX_Dispatcher(this.className,mode,callback,'{$this->serverUrl}','{$this->unserializer}');\n}\n";
  267. } else {
  268. $client .= "\tthis.dispatcher = new HTML_AJAX_Dispatcher(this.className,mode,callback,false,'{$this->unserializer}');\n}\n";
  269. }
  270. $client .= "{$this->_exportedInstances[$name]['exportedName']}.prototype = {\n";
  271. $client .= "\tSync: function() { this.dispatcher.Sync(); }, \n";
  272. $client .= "\tAsync: function(callback) { this.dispatcher.Async(callback); },\n";
  273. foreach ($this->_exportedInstances[$name]['exportedMethods'] as $method) {
  274. $client .= $this->_generateMethodStub($method);
  275. }
  276. $client = substr($client, 0, (strlen($client)-2))."\n";
  277. $client .= "}\n\n";
  278. if ($this->packJavaScript) {
  279. $client = $this->packJavaScript($client);
  280. }
  281. return $client;
  282. }
  283. /**
  284. * Returns a methods stub
  285. *
  286. * @param string $method the method name
  287. *
  288. * @return string the js code
  289. * @access private
  290. */
  291. function _generateMethodStub($method)
  292. {
  293. $stub = "\t{$method}: function() { return ".
  294. "this.dispatcher.doCall('{$method}',arguments); },\n";
  295. return $stub;
  296. }
  297. /**
  298. * Populates the current payload
  299. *
  300. * @return string the js code
  301. * @access private
  302. */
  303. function populatePayload()
  304. {
  305. if (isset($_REQUEST['Iframe_XHR'])) {
  306. $this->_iframe = $_REQUEST['Iframe_XHR_id'];
  307. if (isset($_REQUEST['Iframe_XHR_headers']) &&
  308. is_array($_REQUEST['Iframe_XHR_headers'])) {
  309. foreach ($_REQUEST['Iframe_XHR_headers'] as $header) {
  310. $array = explode(':', $header);
  311. $array[0] = strip_tags(strtoupper(str_replace('-', '_', $array[0])));
  312. //only content-length and content-type can go in without an
  313. //http_ prefix - security
  314. if (strpos($array[0], 'HTTP_') !== 0
  315. && strcmp('CONTENT_TYPE', $array[0])
  316. && strcmp('CONTENT_LENGTH', $array[0])) {
  317. $array[0] = 'HTTP_' . $array[0];
  318. }
  319. $_SERVER[$array[0]] = strip_tags($array[1]);
  320. }
  321. }
  322. $this->_payload = (isset($_REQUEST['Iframe_XHR_data'])
  323. ? $_REQUEST['Iframe_XHR_data'] : '');
  324. if (isset($_REQUEST['Iframe_XHR_method'])) {
  325. $_GET['m'] = $_REQUEST['Iframe_XHR_method'];
  326. }
  327. if (isset($_REQUEST['Iframe_XHR_class'])) {
  328. $_GET['c'] = $_REQUEST['Iframe_XHR_class'];
  329. }
  330. }
  331. }
  332. /**
  333. * Handle a ajax request if needed
  334. *
  335. * The current check is if GET variables c (class) and m (method) are set,
  336. * more options may be available in the future
  337. *
  338. * @return boolean true if an ajax call was handled, false otherwise
  339. */
  340. function handleRequest()
  341. {
  342. set_error_handler(array(&$this,'_errorHandler'));
  343. if (function_exists('set_exception_handler')) {
  344. set_exception_handler(array(&$this,'_exceptionHandler'));
  345. }
  346. if (isset($_GET['px'])) {
  347. if ($this->_iframeGrabProxy()) {
  348. restore_error_handler();
  349. if (function_exists('restore_exception_handler')) {
  350. restore_exception_handler();
  351. }
  352. return true;
  353. }
  354. }
  355. $class = strtolower($this->_getVar('c'));
  356. $method = $this->_getVar('m');
  357. $phpCallback = $this->_getVar('cb');
  358. if (!empty($class) && !empty($method)) {
  359. if (!isset($this->_exportedInstances[$class])) {
  360. // handle error
  361. trigger_error('Unknown class: '. $class);
  362. }
  363. if (!in_array(($this->php4CompatCase ? strtolower($method) : $method),
  364. $this->_exportedInstances[$class]['exportedMethods'])) {
  365. // handle error
  366. trigger_error('Unknown method: ' . $method);
  367. }
  368. } else if (!empty($phpCallback)) {
  369. if (strpos($phpCallback, '.') !== false) {
  370. $phpCallback = explode('.', $phpCallback);
  371. }
  372. if (!$this->_validatePhpCallback($phpCallback)) {
  373. restore_error_handler();
  374. if (function_exists('restore_exception_handler')) {
  375. restore_exception_handler();
  376. }
  377. return false;
  378. }
  379. } else {
  380. restore_error_handler();
  381. if (function_exists('restore_exception_handler')) {
  382. restore_exception_handler();
  383. }
  384. return false;
  385. }
  386. // auto-detect serializer to use from content-type
  387. $type = $this->unserializer;
  388. $key = array_search($this->_getClientPayloadContentType(),
  389. $this->contentTypeMap);
  390. if ($key) {
  391. $type = $key;
  392. }
  393. $unserializer = $this->_getSerializer($type);
  394. $args = $unserializer->unserialize($this->_getClientPayload(), $this->_allowedClasses);
  395. if (!is_array($args)) {
  396. $args = array($args);
  397. }
  398. if ($this->_interceptor !== false) {
  399. $args = $this->_processInterceptor($class, $method, $phpCallback, $args);
  400. }
  401. if (empty($phpCallback)) {
  402. $ret = call_user_func_array(array(&$this->_exportedInstances[$class]['instance'], $method), $args);
  403. } else {
  404. $ret = call_user_func_array($phpCallback, $args);
  405. }
  406. restore_error_handler();
  407. $this->_sendResponse($ret);
  408. return true;
  409. }
  410. /**
  411. * Determines the content type of the client payload
  412. *
  413. * @return string
  414. * a MIME content type
  415. */
  416. function _getClientPayloadContentType()
  417. {
  418. //OPERA IS STUPID FIX
  419. if (isset($_SERVER['HTTP_X_CONTENT_TYPE'])) {
  420. $type = $this->_getServer('HTTP_X_CONTENT_TYPE');
  421. $pos = strpos($type, ';');
  422. return strtolower($pos ? substr($type, 0, $pos) : $type);
  423. } else if (isset($_SERVER['CONTENT_TYPE'])) {
  424. $type = $this->_getServer('CONTENT_TYPE');
  425. $pos = strpos($type, ';');
  426. return strtolower($pos ? substr($type, 0, $pos) : $type);
  427. }
  428. return 'text/plain';
  429. }
  430. /**
  431. * Send a reponse adding needed headers and serializing content
  432. *
  433. * Note: this method echo's output as well as setting headers to prevent caching
  434. * Iframe Detection: if this has been detected as an iframe response, it has to
  435. * be wrapped in different code and headers changed (quite a mess)
  436. *
  437. * @param mixed $response content to serialize and send
  438. *
  439. * @access private
  440. * @return void
  441. */
  442. function _sendResponse($response)
  443. {
  444. if (is_object($response) && is_a($response, 'HTML_AJAX_Response')) {
  445. $output = $response->getPayload();
  446. $content = $response->getContentType();
  447. } elseif (is_a($response, 'PEAR_Error')) {
  448. $serializer = $this->_getSerializer('Error');
  449. $output = $serializer->serialize(array(
  450. 'message' => $response->getMessage(),
  451. 'userinfo' => $response->getUserInfo(),
  452. 'code' => $response->getCode(),
  453. 'mode' => $response->getMode()
  454. ));
  455. $content = $this->contentTypeMap['Error'];
  456. } else {
  457. $serializer = $this->_getSerializer($this->serializer);
  458. $output = $serializer->serialize($response);
  459. $serializerType = $this->serializer;
  460. // let a serializer change its output type
  461. if (isset($serializer->serializerNewType)) {
  462. $serializerType = $serializer->serializerNewType;
  463. }
  464. if (isset($this->contentTypeMap[$serializerType])) {
  465. $content = $this->contentTypeMap[$serializerType];
  466. }
  467. }
  468. // headers to force things not to be cached:
  469. $headers = array();
  470. //OPERA IS STUPID FIX
  471. if (isset($_SERVER['HTTP_X_CONTENT_TYPE'])) {
  472. $headers['X-Content-Type'] = $content;
  473. $content = 'text/plain';
  474. }
  475. if ($this->_sendContentLength()) {
  476. $headers['Content-Length'] = strlen($output);
  477. }
  478. $headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
  479. $headers['Last-Modified'] = gmdate("D, d M Y H:i:s").'GMT';
  480. $headers['Cache-Control'] = 'no-cache, must-revalidate';
  481. $headers['Pragma'] = 'no-cache';
  482. $headers['Content-Type'] = $content.'; charset=utf-8';
  483. //intercept to wrap iframe return data
  484. if ($this->_iframe) {
  485. $output = $this->_iframeWrapper($this->_iframe,
  486. $output, $headers);
  487. $headers['Content-Type'] = 'text/html; charset=utf-8';
  488. }
  489. $this->_sendHeaders($headers);
  490. echo $output;
  491. }
  492. /**
  493. * Decide if we should send a Content-length header
  494. *
  495. * @return bool true if it's ok to send the header, false otherwise
  496. * @access private
  497. */
  498. function _sendContentLength()
  499. {
  500. if (!$this->sendContentLength) {
  501. return false;
  502. }
  503. $ini_tests = array( "output_handler",
  504. "zlib.output_compression",
  505. "zlib.output_handler");
  506. foreach ($ini_tests as $test) {
  507. if (ini_get($test)) {
  508. return false;
  509. }
  510. }
  511. return (ob_get_level() <= 0);
  512. }
  513. /**
  514. * Actually send a list of headers
  515. *
  516. * @param array $array list of headers to send
  517. *
  518. * @access private
  519. * @return void
  520. */
  521. function _sendHeaders($array)
  522. {
  523. foreach ($array as $header => $value) {
  524. header($header . ': ' . $value);
  525. }
  526. }
  527. /**
  528. * Get an instance of a serializer class
  529. *
  530. * @param string $type Last part of the class name
  531. *
  532. * @access private
  533. * @return HTML_AJAX_Serializer
  534. */
  535. function _getSerializer($type)
  536. {
  537. if (isset($this->_serializers[$type])) {
  538. return $this->_serializers[$type];
  539. }
  540. $class = 'HTML_AJAX_Serializer_'.$type;
  541. if ( (version_compare(phpversion(), 5, '>') && !class_exists($class, false))
  542. || (version_compare(phpversion(), 5, '<') && !class_exists($class)) ) {
  543. // include the class only if it isn't defined
  544. include_once "HTML/AJAX/Serializer/{$type}.php";
  545. }
  546. //handle JSON loose typing option for associative arrays
  547. if ($type == 'JSON') {
  548. $this->_serializers[$type] = new $class($this->jsonLooseType);
  549. } else {
  550. $this->_serializers[$type] = new $class();
  551. }
  552. return $this->_serializers[$type];
  553. }
  554. /**
  555. * Get payload in its submitted form, currently only supports raw post
  556. *
  557. * @access private
  558. * @return string raw post data
  559. */
  560. function _getClientPayload()
  561. {
  562. if (empty($this->_payload)) {
  563. if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
  564. $this->_payload = $GLOBALS['HTTP_RAW_POST_DATA'];
  565. } else if (function_exists('file_get_contents')) {
  566. // both file_get_contents() and php://input require PHP >= 4.3.0
  567. $this->_payload = file_get_contents('php://input');
  568. } else {
  569. $this->_payload = '';
  570. }
  571. }
  572. return $this->_payload;
  573. }
  574. /**
  575. * stub for getting get vars - applies strip_tags
  576. *
  577. * @param string $var variable to get
  578. *
  579. * @access private
  580. * @return string filtered _GET value
  581. */
  582. function _getVar($var)
  583. {
  584. if (!isset($_GET[$var])) {
  585. return null;
  586. } else {
  587. return strip_tags($_GET[$var]);
  588. }
  589. }
  590. /**
  591. * stub for getting server vars - applies strip_tags
  592. *
  593. * @param string $var variable to get
  594. *
  595. * @access private
  596. * @return string filtered _GET value
  597. */
  598. function _getServer($var)
  599. {
  600. if (!isset($_SERVER[$var])) {
  601. return null;
  602. } else {
  603. return strip_tags($_SERVER[$var]);
  604. }
  605. }
  606. /**
  607. * Exception handler, passes them to _errorHandler to do the actual work
  608. *
  609. * @param Exception $ex Exception to be handled
  610. *
  611. * @access private
  612. * @return void
  613. */
  614. function _exceptionHandler($ex)
  615. {
  616. $this->_errorHandler($ex->getCode(), $ex->getMessage(), $ex->getFile(), $ex->getLine());
  617. }
  618. /**
  619. * Error handler that sends it errors to the client side
  620. *
  621. * @param int $errno Error number
  622. * @param string $errstr Error string
  623. * @param string $errfile Error file
  624. * @param string $errline Error line
  625. *
  626. * @access private
  627. * @return void
  628. */
  629. function _errorHandler($errno, $errstr, $errfile, $errline)
  630. {
  631. if ($errno & error_reporting()) {
  632. $e = new stdClass();
  633. $e->errNo = $errno;
  634. $e->errStr = $errstr;
  635. $e->errFile = $errfile;
  636. $e->errLine = $errline;
  637. $this->serializer = 'Error';
  638. $this->_sendResponse($e);
  639. if ($this->debugEnabled) {
  640. $this->debug = new HTML_AJAX_Debug($errstr, $errline, $errno, $errfile);
  641. if ($this->debugSession) {
  642. $this->debug->sessionError();
  643. }
  644. $this->debug->_saveError();
  645. }
  646. die();
  647. }
  648. }
  649. /**
  650. * Creates html to wrap serialized info for iframe xmlhttprequest fakeout
  651. *
  652. * @param string $id iframe instance id
  653. * @param string $data data to pass
  654. * @param string $headers headers to pass
  655. *
  656. * @access private
  657. * @return string html page with iframe passing code
  658. */
  659. function _iframeWrapper($id, $data, $headers = array())
  660. {
  661. $string = '<html><script type="text/javascript">'."\n".
  662. 'var Iframe_XHR_headers = new Object();';
  663. foreach ($headers as $label => $value) {
  664. $string .= 'Iframe_XHR_headers["'.preg_replace("/\r?\n/", "\\n",
  665. addslashes($label)).'"] = "'.preg_replace("/\r?\n/", "\\n",
  666. addslashes($value))."\";\n";
  667. }
  668. $string .= 'var Iframe_XHR_data = "' . preg_replace("/\r?\n/", "\\n",
  669. addslashes($data)) . '";</script>'
  670. . '<body onload="parent.HTML_AJAX_IframeXHR_instances[\''.$id.'\']'
  671. . '.isLoaded(Iframe_XHR_headers, Iframe_XHR_data);"></body></html>';
  672. return $string;
  673. }
  674. /**
  675. * Handles a proxied grab request
  676. *
  677. * @return bool true to end the response, false to continue trying to handle it
  678. * @access private
  679. */
  680. function _iframeGrabProxy()
  681. {
  682. if (!isset($_REQUEST['Iframe_XHR_id'])) {
  683. trigger_error('Invalid iframe ID');
  684. return false;
  685. }
  686. $this->_iframe = $_REQUEST['Iframe_XHR_id'];
  687. $this->_payload = (isset($_REQUEST['Iframe_XHR_data']) ? $_REQUEST['Iframe_XHR_data'] : '');
  688. $url = urldecode($_GET['px']);
  689. $url_parts = parse_url($url);
  690. $urlregex = '#^https?://#i';
  691. if (!preg_match($urlregex, $url) || $url_parts['host'] != $_SERVER['HTTP_HOST']) {
  692. trigger_error('Invalid URL for grab proxy');
  693. return true;
  694. }
  695. $method = (isset($_REQUEST['Iframe_XHR_HTTP_method'])
  696. ? strtoupper($_REQUEST['Iframe_XHR_HTTP_method'])
  697. : 'GET');
  698. // validate method
  699. if ($method != 'GET' && $method != 'POST') {
  700. trigger_error('Invalid grab URL');
  701. return true;
  702. }
  703. // validate headers
  704. $headers = '';
  705. if (isset($_REQUEST['Iframe_XHR_headers'])) {
  706. foreach ($_REQUEST['Iframe_XHR_headers'] as $header) {
  707. if (strpos($header, "\r") !== false
  708. || strpos($header, "\n") !== false) {
  709. trigger_error('Invalid grab header');
  710. return true;
  711. }
  712. $headers .= $header . "\r\n";
  713. }
  714. }
  715. // tries to make request with file_get_contents()
  716. if (ini_get('allow_url_fopen') && version_compare(phpversion(), '5.0.0'. '>=')) {
  717. $opts = array(
  718. $url_parts['scheme'] => array(
  719. 'method' => $method,
  720. 'headers' => $headers,
  721. 'content' => $this->_payload
  722. )
  723. );
  724. $ret = @file_get_contents($url, false, stream_context_create($opts));
  725. if (!empty($ret)) {
  726. $this->_sendResponse($ret);
  727. return true;
  728. }
  729. }
  730. // tries to make request using the curl extension
  731. if (function_exists('curl_setopt')) {
  732. $ch = curl_init();
  733. curl_setopt($ch, CURLOPT_URL, $url);
  734. curl_setopt($ch, CURLOPT_HEADER, $headers);
  735. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  736. $ret = curl_exec($ch);
  737. if ($ret !== false) {
  738. curl_close($ch);
  739. $this->_sendResponse($ret);
  740. return true;
  741. }
  742. }
  743. if (isset($url_parts['port'])) {
  744. $port = $url_parts['port'];
  745. } else {
  746. $port = getservbyname(strtolower($url_parts['scheme']), 'tcp');
  747. if ($port === false) {
  748. trigger_error('Grab proxy: Unknown port or service, defaulting to 80', E_USER_WARNING);
  749. $port = 80;
  750. }
  751. }
  752. if (!isset($url_parts['path'])) {
  753. $url_parts['path'] = '/';
  754. }
  755. if (!empty($url_parts['query'])) {
  756. $url_parts['path'] .= '?' . $url_parts['query'];
  757. }
  758. $request = "$method {$url_parts['path']} HTTP/1.0\r\n"
  759. . "Host: {$url['host']}\r\n"
  760. . "Connection: close\r\n"
  761. . "$headers\r\n";
  762. // tries to make request using the socket functions
  763. $fp = fsockopen($_SERVER['HTTP_HOST'], $port, $errno, $errstr, 4);
  764. if ($fp) {
  765. fputs($fp, $request);
  766. $ret = '';
  767. $done_headers = false;
  768. while (!feof($fp)) {
  769. $ret .= fgets($fp, 2048);
  770. if ($done_headers || ($contentpos = strpos($ret, "\r\n\r\n")) === false) {
  771. continue;
  772. }
  773. $done_headers = true;
  774. $ret = substr($ret, $contentpos + 4);
  775. }
  776. fclose($fp);
  777. $this->_sendResponse($ret);
  778. return true;
  779. }
  780. // tries to make the request using the socket extension
  781. $host = gethostbyname($url['host']);
  782. if (($socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0
  783. || ($connected = socket_connect($socket, $host, $port)) < 0
  784. || ($written = socket_write($socket, $request)) < strlen($request)) {
  785. trigger_error('Grab proxy failed: ' . socket_strerror($socket));
  786. return true;
  787. }
  788. $ret = '';
  789. $done_headers = false;
  790. while ($out = socket_read($socket, 2048)) {
  791. $ret .= $out;
  792. if ($done_headers || ($contentpos = strpos($ret, "\r\n\r\n")) === false) {
  793. continue;
  794. }
  795. $done_headers = true;
  796. $ret = substr($ret, $contentpos + 4);
  797. }
  798. socket_close($socket);
  799. $this->_sendResponse($ret);
  800. return true;
  801. }
  802. /**
  803. * Add a class or classes to those allowed to be unserialized
  804. *
  805. * @param mixed $classes the class or array of classes to add
  806. *
  807. * @access public
  808. * @return void
  809. */
  810. function addAllowedClasses($classes)
  811. {
  812. if (!is_array($classes)) {
  813. $this->_allowedClasses[] = $classes;
  814. } else {
  815. $this->_allowedClasses = array_merge($this->_allowedClasses, $classes);
  816. }
  817. $this->_allowedClasses = array_unique($this->_allowedClasses);
  818. }
  819. /**
  820. * Checks that the given callback is callable and allowed to be called
  821. *
  822. * @param callback $callback the callback to check
  823. *
  824. * @return bool true if the callback is valid, false otherwise
  825. * @access private
  826. */
  827. function _validatePhpCallback($callback)
  828. {
  829. if (!is_callable($callback)) {
  830. return false;
  831. }
  832. $sig = md5(serialize($callback));
  833. return isset($this->_validCallbacks[$sig]);
  834. }
  835. /**
  836. * Register a callback so it may be called from JS
  837. *
  838. * @param callback $callback the callback to register
  839. *
  840. * @access public
  841. * @return void
  842. */
  843. function registerPhpCallback($callback)
  844. {
  845. $this->_validCallbacks[md5(serialize($callback))] = 1;
  846. }
  847. /**
  848. * Make JavaScript code smaller
  849. *
  850. * Currently just strips whitespace and comments, needs to remain fast
  851. * Strips comments only if they are not preceeded by code
  852. * Strips /*-style comments only if they span over more than one line
  853. * Since strings cannot span over multiple lines, it cannot be defeated by a
  854. * string containing /*
  855. *
  856. * @param string $input Javascript to pack
  857. *
  858. * @access public
  859. * @return string packed javascript
  860. */
  861. function packJavaScript($input)
  862. {
  863. $stripPregs = array(
  864. '/^\s*$/',
  865. '/^\s*\/\/.*$/'
  866. );
  867. $blockStart = '/^\s*\/\/\*/';
  868. $blockEnd = '/\*\/\s*(.*)$/';
  869. $inlineComment = '/\/\*.*\*\//';
  870. $out = '';
  871. $lines = explode("\n", $input);
  872. $inblock = false;
  873. foreach ($lines as $line) {
  874. $keep = true;
  875. if ($inblock) {
  876. if (preg_match($blockEnd, $line)) {
  877. $inblock = false;
  878. $line = preg_match($blockEnd, '$1', $line);
  879. $keep = strlen($line) > 0;
  880. }
  881. } elseif (preg_match($inlineComment, $line)) {
  882. $keep = true;
  883. } elseif (preg_match($blockStart, $line)) {
  884. $inblock = true;
  885. $keep = false;
  886. }
  887. if (!$inblock) {
  888. foreach ($stripPregs as $preg) {
  889. if (preg_match($preg, $line)) {
  890. $keep = false;
  891. break;
  892. }
  893. }
  894. }
  895. if ($keep && !$inblock) {
  896. $out .= trim($line)."\n";
  897. }
  898. /* Enable to see what your striping out
  899. else {
  900. echo $line."<br>";
  901. }//*/
  902. }
  903. $out .= "\n";
  904. return $out;
  905. }
  906. /**
  907. * Set an interceptor class
  908. *
  909. * An interceptor class runs during the process of handling a request,
  910. * it allows you to run security checks globally. It also allows you to
  911. * rewrite parameters
  912. *
  913. * You can throw errors and exceptions in your intercptor methods and
  914. * they will be passed to javascript
  915. *
  916. * You can add interceptors are 3 levels
  917. * For a particular class/method, this is done by add a method to you class
  918. * named ClassName_MethodName($params)
  919. * For a particular class, method ClassName($methodName,$params)
  920. * Globally, method intercept($className,$methodName,$params)
  921. *
  922. * Only one match is done, using the most specific interceptor
  923. *
  924. * All methods have to return $params, if you want to empty all of the
  925. * parameters return an empty array
  926. *
  927. * @param Object $instance an instance of you interceptor class
  928. *
  929. * @todo handle php callbacks
  930. * @access public
  931. * @return void
  932. */
  933. function setInterceptor($instance)
  934. {
  935. $this->_interceptor = $instance;
  936. }
  937. /**
  938. * Attempt to intercept a call
  939. *
  940. * @param string $className Class Name
  941. * @param string $methodName Method Name
  942. * @param string $callback Not implemented
  943. * @param array $params Array of parameters to pass to the interceptor
  944. *
  945. * @todo handle php callbacks
  946. * @access private
  947. * @return array Updated params
  948. */
  949. function _processInterceptor($className,$methodName,$callback,$params)
  950. {
  951. $m = $className.'_'.$methodName;
  952. if (method_exists($this->_interceptor, $m)) {
  953. return $this->_interceptor->$m($params);
  954. }
  955. $m = $className;
  956. if (method_exists($this->_interceptor, $m)) {
  957. return $this->_interceptor->$m($methodName, $params);
  958. }
  959. $m = 'intercept';
  960. if (method_exists($this->_interceptor, $m)) {
  961. return $this->_interceptor->$m($className, $methodName, $params);
  962. }
  963. return $params;
  964. }
  965. }
  966. /**
  967. * PHP 4 compat function for interface/class exists
  968. *
  969. * @param string $class Class name
  970. * @param bool $autoload Should the autoloader be called
  971. *
  972. * @access public
  973. * @return bool
  974. */
  975. function HTML_AJAX_Class_exists($class, $autoload)
  976. {
  977. if (function_exists('interface_exists')) {
  978. return class_exists($class, $autoload);
  979. } else {
  980. return class_exists($class);
  981. }
  982. }
  983. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  984. ?>