/wp-includes/IXR/class-IXR-message.php

  1. <?php 
  2.  
  3. /** 
  4. * IXR_MESSAGE 
  5. * 
  6. * @package IXR 
  7. * @since 1.5.0 
  8. * 
  9. */ 
  10. class IXR_Message 
  11. var $message; 
  12. var $messageType; // methodCall / methodResponse / fault 
  13. var $faultCode; 
  14. var $faultString; 
  15. var $methodName; 
  16. var $params; 
  17.  
  18. // Current variable stacks 
  19. var $_arraystructs = array(); // The stack used to keep track of the current array/struct 
  20. var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array 
  21. var $_currentStructName = array(); // A stack as well 
  22. var $_param; 
  23. var $_value; 
  24. var $_currentTag; 
  25. var $_currentTagContents; 
  26. // The XML parser 
  27. var $_parser; 
  28.  
  29. /** 
  30. * PHP5 constructor. 
  31. */ 
  32. function __construct( $message ) 
  33. $this->message =& $message; 
  34.  
  35. /** 
  36. * PHP4 constructor. 
  37. */ 
  38. public function IXR_Message( $message ) { 
  39. self::__construct( $message ); 
  40.  
  41. function parse() 
  42. if ( ! function_exists( 'xml_parser_create' ) ) { 
  43. trigger_error( __( "PHP's XML extension is not available. Please contact your hosting provider to enable PHP's XML extension." ) ); 
  44. return false; 
  45.  
  46. // first remove the XML declaration 
  47. // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages 
  48. $header = preg_replace( '/<\?xml.*?\?'.'>/s', '', substr( $this->message, 0, 100 ), 1 ); 
  49. $this->message = trim( substr_replace( $this->message, $header, 0, 100 ) ); 
  50. if ( '' == $this->message ) { 
  51. return false; 
  52.  
  53. // Then remove the DOCTYPE 
  54. $header = preg_replace( '/^<!DOCTYPE[^>]*+>/i', '', substr( $this->message, 0, 200 ), 1 ); 
  55. $this->message = trim( substr_replace( $this->message, $header, 0, 200 ) ); 
  56. if ( '' == $this->message ) { 
  57. return false; 
  58.  
  59. // Check that the root tag is valid 
  60. $root_tag = substr( $this->message, 0, strcspn( substr( $this->message, 0, 20 ), "> \t\r\n" ) ); 
  61. if ( '<!DOCTYPE' === strtoupper( $root_tag ) ) { 
  62. return false; 
  63. if ( ! in_array( $root_tag, array( '<methodCall', '<methodResponse', '<fault' ) ) ) { 
  64. return false; 
  65.  
  66. // Bail if there are too many elements to parse 
  67. $element_limit = 30000; 
  68. if ( function_exists( 'apply_filters' ) ) { 
  69. /** 
  70. * Filters the number of elements to parse in an XML-RPC response. 
  71. * 
  72. * @since 4.0.0 
  73. * 
  74. * @param int $element_limit Default elements limit. 
  75. */ 
  76. $element_limit = apply_filters( 'xmlrpc_element_limit', $element_limit ); 
  77. if ( $element_limit && 2 * $element_limit < substr_count( $this->message, '<' ) ) { 
  78. return false; 
  79.  
  80. $this->_parser = xml_parser_create(); 
  81. // Set XML parser to take the case of tags in to account 
  82. xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); 
  83. // Set XML parser callback functions 
  84. xml_set_object($this->_parser, $this); 
  85. xml_set_element_handler($this->_parser, 'tag_open', 'tag_close'); 
  86. xml_set_character_data_handler($this->_parser, 'cdata'); 
  87.  
  88. // 256Kb, parse in chunks to avoid the RAM usage on very large messages 
  89. $chunk_size = 262144; 
  90.  
  91. /** 
  92. * Filters the chunk size that can be used to parse an XML-RPC reponse message. 
  93. * 
  94. * @since 4.4.0 
  95. * 
  96. * @param int $chunk_size Chunk size to parse in bytes. 
  97. */ 
  98. $chunk_size = apply_filters( 'xmlrpc_chunk_parsing_size', $chunk_size ); 
  99.  
  100. $final = false; 
  101. do { 
  102. if (strlen($this->message) <= $chunk_size) { 
  103. $final = true; 
  104. $part = substr($this->message, 0, $chunk_size); 
  105. $this->message = substr($this->message, $chunk_size); 
  106. if (!xml_parse($this->_parser, $part, $final)) { 
  107. return false; 
  108. if ($final) { 
  109. break; 
  110. } while (true); 
  111. xml_parser_free($this->_parser); 
  112.  
  113. // Grab the error messages, if any 
  114. if ($this->messageType == 'fault') { 
  115. $this->faultCode = $this->params[0]['faultCode']; 
  116. $this->faultString = $this->params[0]['faultString']; 
  117. return true; 
  118.  
  119. function tag_open($parser, $tag, $attr) 
  120. $this->_currentTagContents = ''; 
  121. $this->currentTag = $tag; 
  122. switch($tag) { 
  123. case 'methodCall': 
  124. case 'methodResponse': 
  125. case 'fault': 
  126. $this->messageType = $tag; 
  127. break; 
  128. /** Deal with stacks of arrays and structs */ 
  129. case 'data': // data is to all intents and puposes more interesting than array 
  130. $this->_arraystructstypes[] = 'array'; 
  131. $this->_arraystructs[] = array(); 
  132. break; 
  133. case 'struct': 
  134. $this->_arraystructstypes[] = 'struct'; 
  135. $this->_arraystructs[] = array(); 
  136. break; 
  137.  
  138. function cdata($parser, $cdata) 
  139. $this->_currentTagContents .= $cdata; 
  140.  
  141. function tag_close($parser, $tag) 
  142. $valueFlag = false; 
  143. switch($tag) { 
  144. case 'int': 
  145. case 'i4': 
  146. $value = (int)trim($this->_currentTagContents); 
  147. $valueFlag = true; 
  148. break; 
  149. case 'double': 
  150. $value = (double)trim($this->_currentTagContents); 
  151. $valueFlag = true; 
  152. break; 
  153. case 'string': 
  154. $value = (string)trim($this->_currentTagContents); 
  155. $valueFlag = true; 
  156. break; 
  157. case 'dateTime.iso8601': 
  158. $value = new IXR_Date(trim($this->_currentTagContents)); 
  159. $valueFlag = true; 
  160. break; 
  161. case 'value': 
  162. // "If no type is indicated, the type is string." 
  163. if (trim($this->_currentTagContents) != '') { 
  164. $value = (string)$this->_currentTagContents; 
  165. $valueFlag = true; 
  166. break; 
  167. case 'boolean': 
  168. $value = (boolean)trim($this->_currentTagContents); 
  169. $valueFlag = true; 
  170. break; 
  171. case 'base64': 
  172. $value = base64_decode($this->_currentTagContents); 
  173. $valueFlag = true; 
  174. break; 
  175. /** Deal with stacks of arrays and structs */ 
  176. case 'data': 
  177. case 'struct': 
  178. $value = array_pop($this->_arraystructs); 
  179. array_pop($this->_arraystructstypes); 
  180. $valueFlag = true; 
  181. break; 
  182. case 'member': 
  183. array_pop($this->_currentStructName); 
  184. break; 
  185. case 'name': 
  186. $this->_currentStructName[] = trim($this->_currentTagContents); 
  187. break; 
  188. case 'methodName': 
  189. $this->methodName = trim($this->_currentTagContents); 
  190. break; 
  191.  
  192. if ($valueFlag) { 
  193. if (count($this->_arraystructs) > 0) { 
  194. // Add value to struct or array 
  195. if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') { 
  196. // Add to struct 
  197. $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value; 
  198. } else { 
  199. // Add to array 
  200. $this->_arraystructs[count($this->_arraystructs)-1][] = $value; 
  201. } else { 
  202. // Just add as a parameter 
  203. $this->params[] = $value; 
  204. $this->_currentTagContents = ''; 
.