RPC.php 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * PHP implementation of the XML-RPC protocol
  5. *
  6. * This is a PEAR-ified version of Useful inc's XML-RPC for PHP.
  7. * It has support for HTTP transport, proxies and authentication.
  8. *
  9. * PHP versions 4 and 5
  10. *
  11. * LICENSE: License is granted to use or modify this software
  12. * ("XML-RPC for PHP") for commercial or non-commercial use provided the
  13. * copyright of the author is preserved in any distributed or derivative work.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESSED OR
  16. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  17. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  18. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  19. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  20. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  24. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. *
  26. * @category Web Services
  27. * @package XML_RPC
  28. * @author Edd Dumbill <edd@usefulinc.com>
  29. * @author Stig Bakken <stig@php.net>
  30. * @author Martin Jansen <mj@php.net>
  31. * @author Daniel Convissor <danielc@php.net>
  32. * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group
  33. * @version CVS: $Id: RPC.php,v 1.83 2005/08/14 20:25:35 danielc Exp $
  34. * @link http://pear.php.net/package/XML_RPC
  35. */
  36. if (!function_exists('xml_parser_create')) {
  37. PEAR::loadExtension('xml');
  38. }
  39. /**#@+
  40. * Error constants
  41. */
  42. /**
  43. * Parameter values don't match parameter types
  44. */
  45. define('XML_RPC_ERROR_INVALID_TYPE', 101);
  46. /**
  47. * Parameter declared to be numeric but the values are not
  48. */
  49. define('XML_RPC_ERROR_NON_NUMERIC_FOUND', 102);
  50. /**
  51. * Communication error
  52. */
  53. define('XML_RPC_ERROR_CONNECTION_FAILED', 103);
  54. /**
  55. * The array or struct has already been started
  56. */
  57. define('XML_RPC_ERROR_ALREADY_INITIALIZED', 104);
  58. /**
  59. * Incorrect parameters submitted
  60. */
  61. define('XML_RPC_ERROR_INCORRECT_PARAMS', 105);
  62. /**
  63. * Programming error by developer
  64. */
  65. define('XML_RPC_ERROR_PROGRAMMING', 106);
  66. /**#@-*/
  67. /**
  68. * Data types
  69. * @global string $GLOBALS['XML_RPC_I4']
  70. */
  71. $GLOBALS['XML_RPC_I4'] = 'i4';
  72. /**
  73. * Data types
  74. * @global string $GLOBALS['XML_RPC_Int']
  75. */
  76. $GLOBALS['XML_RPC_Int'] = 'int';
  77. /**
  78. * Data types
  79. * @global string $GLOBALS['XML_RPC_Boolean']
  80. */
  81. $GLOBALS['XML_RPC_Boolean'] = 'boolean';
  82. /**
  83. * Data types
  84. * @global string $GLOBALS['XML_RPC_Double']
  85. */
  86. $GLOBALS['XML_RPC_Double'] = 'double';
  87. /**
  88. * Data types
  89. * @global string $GLOBALS['XML_RPC_String']
  90. */
  91. $GLOBALS['XML_RPC_String'] = 'string';
  92. /**
  93. * Data types
  94. * @global string $GLOBALS['XML_RPC_DateTime']
  95. */
  96. $GLOBALS['XML_RPC_DateTime'] = 'dateTime.iso8601';
  97. /**
  98. * Data types
  99. * @global string $GLOBALS['XML_RPC_Base64']
  100. */
  101. $GLOBALS['XML_RPC_Base64'] = 'base64';
  102. /**
  103. * Data types
  104. * @global string $GLOBALS['XML_RPC_Array']
  105. */
  106. $GLOBALS['XML_RPC_Array'] = 'array';
  107. /**
  108. * Data types
  109. * @global string $GLOBALS['XML_RPC_Struct']
  110. */
  111. $GLOBALS['XML_RPC_Struct'] = 'struct';
  112. /**
  113. * Data type meta-types
  114. * @global array $GLOBALS['XML_RPC_Types']
  115. */
  116. $GLOBALS['XML_RPC_Types'] = array(
  117. $GLOBALS['XML_RPC_I4'] => 1,
  118. $GLOBALS['XML_RPC_Int'] => 1,
  119. $GLOBALS['XML_RPC_Boolean'] => 1,
  120. $GLOBALS['XML_RPC_String'] => 1,
  121. $GLOBALS['XML_RPC_Double'] => 1,
  122. $GLOBALS['XML_RPC_DateTime'] => 1,
  123. $GLOBALS['XML_RPC_Base64'] => 1,
  124. $GLOBALS['XML_RPC_Array'] => 2,
  125. $GLOBALS['XML_RPC_Struct'] => 3,
  126. );
  127. /**
  128. * Error message numbers
  129. * @global array $GLOBALS['XML_RPC_err']
  130. */
  131. $GLOBALS['XML_RPC_err'] = array(
  132. 'unknown_method' => 1,
  133. 'invalid_return' => 2,
  134. 'incorrect_params' => 3,
  135. 'introspect_unknown' => 4,
  136. 'http_error' => 5,
  137. 'not_response_object' => 6,
  138. 'invalid_request' => 7,
  139. );
  140. /**
  141. * Error message strings
  142. * @global array $GLOBALS['XML_RPC_str']
  143. */
  144. $GLOBALS['XML_RPC_str'] = array(
  145. 'unknown_method' => 'Unknown method',
  146. 'invalid_return' => 'Invalid return payload: enable debugging to examine incoming payload',
  147. 'incorrect_params' => 'Incorrect parameters passed to method',
  148. 'introspect_unknown' => 'Can\'t introspect: method unknown',
  149. 'http_error' => 'Didn\'t receive 200 OK from remote server.',
  150. 'not_response_object' => 'The requested method didn\'t return an XML_RPC_Response object.',
  151. 'invalid_request' => 'Invalid request payload',
  152. );
  153. /**
  154. * Default XML encoding (ISO-8859-1, UTF-8 or US-ASCII)
  155. * @global string $GLOBALS['XML_RPC_defencoding']
  156. */
  157. $GLOBALS['XML_RPC_defencoding'] = 'UTF-8';
  158. /**
  159. * User error codes start at 800
  160. * @global int $GLOBALS['XML_RPC_erruser']
  161. */
  162. $GLOBALS['XML_RPC_erruser'] = 800;
  163. /**
  164. * XML parse error codes start at 100
  165. * @global int $GLOBALS['XML_RPC_errxml']
  166. */
  167. $GLOBALS['XML_RPC_errxml'] = 100;
  168. /**
  169. * Compose backslashes for escaping regexp
  170. * @global string $GLOBALS['XML_RPC_backslash']
  171. */
  172. $GLOBALS['XML_RPC_backslash'] = chr(92) . chr(92);
  173. /**
  174. * Valid parents of XML elements
  175. * @global array $GLOBALS['XML_RPC_valid_parents']
  176. */
  177. $GLOBALS['XML_RPC_valid_parents'] = array(
  178. 'BOOLEAN' => array('VALUE'),
  179. 'I4' => array('VALUE'),
  180. 'INT' => array('VALUE'),
  181. 'STRING' => array('VALUE'),
  182. 'DOUBLE' => array('VALUE'),
  183. 'DATETIME.ISO8601' => array('VALUE'),
  184. 'BASE64' => array('VALUE'),
  185. 'ARRAY' => array('VALUE'),
  186. 'STRUCT' => array('VALUE'),
  187. 'PARAM' => array('PARAMS'),
  188. 'METHODNAME' => array('METHODCALL'),
  189. 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
  190. 'MEMBER' => array('STRUCT'),
  191. 'NAME' => array('MEMBER'),
  192. 'DATA' => array('ARRAY'),
  193. 'FAULT' => array('METHODRESPONSE'),
  194. 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
  195. );
  196. /**
  197. * Stores state during parsing
  198. *
  199. * quick explanation of components:
  200. * + ac = accumulates values
  201. * + qt = decides if quotes are needed for evaluation
  202. * + cm = denotes struct or array (comma needed)
  203. * + isf = indicates a fault
  204. * + lv = indicates "looking for a value": implements the logic
  205. * to allow values with no types to be strings
  206. * + params = stores parameters in method calls
  207. * + method = stores method name
  208. *
  209. * @global array $GLOBALS['XML_RPC_xh']
  210. */
  211. $GLOBALS['XML_RPC_xh'] = array();
  212. /**
  213. * Start element handler for the XML parser
  214. *
  215. * @return void
  216. */
  217. function XML_RPC_se($parser_resource, $name, $attrs)
  218. {
  219. global $XML_RPC_xh, $XML_RPC_DateTime, $XML_RPC_String, $XML_RPC_valid_parents;
  220. $parser = (int) $parser_resource;
  221. // if invalid xmlrpc already detected, skip all processing
  222. if ($XML_RPC_xh[$parser]['isf'] >= 2) {
  223. return;
  224. }
  225. // check for correct element nesting
  226. // top level element can only be of 2 types
  227. if (count($XML_RPC_xh[$parser]['stack']) == 0) {
  228. if ($name != 'METHODRESPONSE' && $name != 'METHODCALL') {
  229. $XML_RPC_xh[$parser]['isf'] = 2;
  230. $XML_RPC_xh[$parser]['isf_reason'] = 'missing top level xmlrpc element';
  231. return;
  232. }
  233. } else {
  234. // not top level element: see if parent is OK
  235. if (!in_array($XML_RPC_xh[$parser]['stack'][0], $XML_RPC_valid_parents[$name])) {
  236. $name = preg_replace('[^a-zA-Z0-9._-]', '', $name);
  237. $XML_RPC_xh[$parser]['isf'] = 2;
  238. $XML_RPC_xh[$parser]['isf_reason'] = "xmlrpc element $name cannot be child of {$XML_RPC_xh[$parser]['stack'][0]}";
  239. return;
  240. }
  241. }
  242. switch ($name) {
  243. case 'STRUCT':
  244. $XML_RPC_xh[$parser]['cm']++;
  245. // turn quoting off
  246. $XML_RPC_xh[$parser]['qt'] = 0;
  247. $cur_val = array();
  248. $cur_val['value'] = array();
  249. $cur_val['members'] = 1;
  250. array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
  251. break;
  252. case 'ARRAY':
  253. $XML_RPC_xh[$parser]['cm']++;
  254. // turn quoting off
  255. $XML_RPC_xh[$parser]['qt'] = 0;
  256. $cur_val = array();
  257. $cur_val['value'] = array();
  258. $cur_val['members'] = 0;
  259. array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
  260. break;
  261. case 'NAME':
  262. $XML_RPC_xh[$parser]['ac'] = '';
  263. break;
  264. case 'FAULT':
  265. $XML_RPC_xh[$parser]['isf'] = 1;
  266. break;
  267. case 'PARAM':
  268. $XML_RPC_xh[$parser]['valuestack'] = array();
  269. break;
  270. case 'VALUE':
  271. $XML_RPC_xh[$parser]['lv'] = 1;
  272. $XML_RPC_xh[$parser]['vt'] = $XML_RPC_String;
  273. $XML_RPC_xh[$parser]['ac'] = '';
  274. $XML_RPC_xh[$parser]['qt'] = 0;
  275. // look for a value: if this is still 1 by the
  276. // time we reach the first data segment then the type is string
  277. // by implication and we need to add in a quote
  278. break;
  279. case 'I4':
  280. case 'INT':
  281. case 'STRING':
  282. case 'BOOLEAN':
  283. case 'DOUBLE':
  284. case 'DATETIME.ISO8601':
  285. case 'BASE64':
  286. $XML_RPC_xh[$parser]['ac'] = ''; // reset the accumulator
  287. if ($name == 'DATETIME.ISO8601' || $name == 'STRING') {
  288. $XML_RPC_xh[$parser]['qt'] = 1;
  289. if ($name == 'DATETIME.ISO8601') {
  290. $XML_RPC_xh[$parser]['vt'] = $XML_RPC_DateTime;
  291. }
  292. } elseif ($name == 'BASE64') {
  293. $XML_RPC_xh[$parser]['qt'] = 2;
  294. } else {
  295. // No quoting is required here -- but
  296. // at the end of the element we must check
  297. // for data format errors.
  298. $XML_RPC_xh[$parser]['qt'] = 0;
  299. }
  300. break;
  301. case 'MEMBER':
  302. $XML_RPC_xh[$parser]['ac'] = '';
  303. break;
  304. case 'DATA':
  305. case 'METHODCALL':
  306. case 'METHODNAME':
  307. case 'METHODRESPONSE':
  308. case 'PARAMS':
  309. // valid elements that add little to processing
  310. break;
  311. }
  312. // Save current element to stack
  313. array_unshift($XML_RPC_xh[$parser]['stack'], $name);
  314. if ($name != 'VALUE') {
  315. $XML_RPC_xh[$parser]['lv'] = 0;
  316. }
  317. }
  318. /**
  319. * End element handler for the XML parser
  320. *
  321. * @return void
  322. */
  323. function XML_RPC_ee($parser_resource, $name)
  324. {
  325. global $XML_RPC_xh, $XML_RPC_Types, $XML_RPC_String;
  326. $parser = (int) $parser_resource;
  327. if ($XML_RPC_xh[$parser]['isf'] >= 2) {
  328. return;
  329. }
  330. // push this element from stack
  331. // NB: if XML validates, correct opening/closing is guaranteed and
  332. // we do not have to check for $name == $curr_elem.
  333. // we also checked for proper nesting at start of elements...
  334. $curr_elem = array_shift($XML_RPC_xh[$parser]['stack']);
  335. switch ($name) {
  336. case 'STRUCT':
  337. case 'ARRAY':
  338. $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']);
  339. $XML_RPC_xh[$parser]['value'] = $cur_val['value'];
  340. $XML_RPC_xh[$parser]['vt'] = strtolower($name);
  341. $XML_RPC_xh[$parser]['cm']--;
  342. break;
  343. case 'NAME':
  344. $XML_RPC_xh[$parser]['valuestack'][0]['name'] = $XML_RPC_xh[$parser]['ac'];
  345. break;
  346. case 'BOOLEAN':
  347. // special case here: we translate boolean 1 or 0 into PHP
  348. // constants true or false
  349. if ($XML_RPC_xh[$parser]['ac'] == '1') {
  350. $XML_RPC_xh[$parser]['ac'] = 'true';
  351. } else {
  352. $XML_RPC_xh[$parser]['ac'] = 'false';
  353. }
  354. $XML_RPC_xh[$parser]['vt'] = strtolower($name);
  355. // Drop through intentionally.
  356. case 'I4':
  357. case 'INT':
  358. case 'STRING':
  359. case 'DOUBLE':
  360. case 'DATETIME.ISO8601':
  361. case 'BASE64':
  362. if ($XML_RPC_xh[$parser]['qt'] == 1) {
  363. // we use double quotes rather than single so backslashification works OK
  364. $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
  365. } elseif ($XML_RPC_xh[$parser]['qt'] == 2) {
  366. $XML_RPC_xh[$parser]['value'] = base64_decode($XML_RPC_xh[$parser]['ac']);
  367. } elseif ($name == 'BOOLEAN') {
  368. $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
  369. } else {
  370. // we have an I4, INT or a DOUBLE
  371. // we must check that only 0123456789-.<space> are characters here
  372. if (!ereg("^[+-]?[0123456789 \t\.]+$", $XML_RPC_xh[$parser]['ac'])) {
  373. XML_RPC_Base::raiseError('Non-numeric value received in INT or DOUBLE',
  374. XML_RPC_ERROR_NON_NUMERIC_FOUND);
  375. $XML_RPC_xh[$parser]['value'] = XML_RPC_ERROR_NON_NUMERIC_FOUND;
  376. } else {
  377. // it's ok, add it on
  378. $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
  379. }
  380. }
  381. $XML_RPC_xh[$parser]['ac'] = '';
  382. $XML_RPC_xh[$parser]['qt'] = 0;
  383. $XML_RPC_xh[$parser]['lv'] = 3; // indicate we've found a value
  384. break;
  385. case 'VALUE':
  386. // deal with a string value
  387. if (strlen($XML_RPC_xh[$parser]['ac']) > 0 &&
  388. $XML_RPC_xh[$parser]['vt'] == $XML_RPC_String) {
  389. $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
  390. }
  391. $temp = new XML_RPC_Value($XML_RPC_xh[$parser]['value'], $XML_RPC_xh[$parser]['vt']);
  392. $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']);
  393. if (is_array($cur_val)) {
  394. if ($cur_val['members']==0) {
  395. $cur_val['value'][] = $temp;
  396. } else {
  397. $XML_RPC_xh[$parser]['value'] = $temp;
  398. }
  399. array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
  400. } else {
  401. $XML_RPC_xh[$parser]['value'] = $temp;
  402. }
  403. break;
  404. case 'MEMBER':
  405. $XML_RPC_xh[$parser]['ac'] = '';
  406. $XML_RPC_xh[$parser]['qt'] = 0;
  407. $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']);
  408. if (is_array($cur_val)) {
  409. if ($cur_val['members']==1) {
  410. $cur_val['value'][$cur_val['name']] = $XML_RPC_xh[$parser]['value'];
  411. }
  412. array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
  413. }
  414. break;
  415. case 'DATA':
  416. $XML_RPC_xh[$parser]['ac'] = '';
  417. $XML_RPC_xh[$parser]['qt'] = 0;
  418. break;
  419. case 'PARAM':
  420. $XML_RPC_xh[$parser]['params'][] = $XML_RPC_xh[$parser]['value'];
  421. break;
  422. case 'METHODNAME':
  423. case 'RPCMETHODNAME':
  424. $XML_RPC_xh[$parser]['method'] = ereg_replace("^[\n\r\t ]+", '',
  425. $XML_RPC_xh[$parser]['ac']);
  426. break;
  427. }
  428. // if it's a valid type name, set the type
  429. if (isset($XML_RPC_Types[strtolower($name)])) {
  430. $XML_RPC_xh[$parser]['vt'] = strtolower($name);
  431. }
  432. }
  433. /**
  434. * Character data handler for the XML parser
  435. *
  436. * @return void
  437. */
  438. function XML_RPC_cd($parser_resource, $data)
  439. {
  440. global $XML_RPC_xh, $XML_RPC_backslash;
  441. $parser = (int) $parser_resource;
  442. if ($XML_RPC_xh[$parser]['lv'] != 3) {
  443. // "lookforvalue==3" means that we've found an entire value
  444. // and should discard any further character data
  445. if ($XML_RPC_xh[$parser]['lv'] == 1) {
  446. // if we've found text and we're just in a <value> then
  447. // turn quoting on, as this will be a string
  448. $XML_RPC_xh[$parser]['qt'] = 1;
  449. // and say we've found a value
  450. $XML_RPC_xh[$parser]['lv'] = 2;
  451. }
  452. // replace characters that eval would
  453. // do special things with
  454. if (!isset($XML_RPC_xh[$parser]['ac'])) {
  455. $XML_RPC_xh[$parser]['ac'] = '';
  456. }
  457. $XML_RPC_xh[$parser]['ac'] .= $data;
  458. }
  459. }
  460. /**
  461. * The common methods and properties for all of the XML_RPC classes
  462. *
  463. * @category Web Services
  464. * @package XML_RPC
  465. * @author Edd Dumbill <edd@usefulinc.com>
  466. * @author Stig Bakken <stig@php.net>
  467. * @author Martin Jansen <mj@php.net>
  468. * @author Daniel Convissor <danielc@php.net>
  469. * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group
  470. * @version Release: 1.4.0
  471. * @link http://pear.php.net/package/XML_RPC
  472. */
  473. class XML_RPC_Base {
  474. /**
  475. * PEAR Error handling
  476. *
  477. * @return object PEAR_Error object
  478. */
  479. function raiseError($msg, $code)
  480. {
  481. include_once 'PEAR.php';
  482. if (is_object(@$this)) {
  483. return PEAR::raiseError(get_class($this) . ': ' . $msg, $code);
  484. } else {
  485. return PEAR::raiseError('XML_RPC: ' . $msg, $code);
  486. }
  487. }
  488. /**
  489. * Tell whether something is a PEAR_Error object
  490. *
  491. * @param mixed $value the item to check
  492. *
  493. * @return bool whether $value is a PEAR_Error object or not
  494. *
  495. * @access public
  496. */
  497. function isError($value)
  498. {
  499. return is_a($value, 'PEAR_Error');
  500. }
  501. }
  502. /**
  503. * The methods and properties for submitting XML RPC requests
  504. *
  505. * @category Web Services
  506. * @package XML_RPC
  507. * @author Edd Dumbill <edd@usefulinc.com>
  508. * @author Stig Bakken <stig@php.net>
  509. * @author Martin Jansen <mj@php.net>
  510. * @author Daniel Convissor <danielc@php.net>
  511. * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group
  512. * @version Release: 1.4.0
  513. * @link http://pear.php.net/package/XML_RPC
  514. */
  515. class XML_RPC_Client extends XML_RPC_Base {
  516. /**
  517. * The path and name of the RPC server script you want the request to go to
  518. * @var string
  519. */
  520. var $path = '';
  521. /**
  522. * The name of the remote server to connect to
  523. * @var string
  524. */
  525. var $server = '';
  526. /**
  527. * The protocol to use in contacting the remote server
  528. * @var string
  529. */
  530. var $protocol = 'http://';
  531. /**
  532. * The port for connecting to the remote server
  533. *
  534. * The default is 80 for http:// connections
  535. * and 443 for https:// and ssl:// connections.
  536. *
  537. * @var integer
  538. */
  539. var $port = 80;
  540. /**
  541. * A user name for accessing the RPC server
  542. * @var string
  543. * @see XML_RPC_Client::setCredentials()
  544. */
  545. var $username = '';
  546. /**
  547. * A password for accessing the RPC server
  548. * @var string
  549. * @see XML_RPC_Client::setCredentials()
  550. */
  551. var $password = '';
  552. /**
  553. * The name of the proxy server to use, if any
  554. * @var string
  555. */
  556. var $proxy = '';
  557. /**
  558. * The protocol to use in contacting the proxy server, if any
  559. * @var string
  560. */
  561. var $proxy_protocol = 'http://';
  562. /**
  563. * The port for connecting to the proxy server
  564. *
  565. * The default is 8080 for http:// connections
  566. * and 443 for https:// and ssl:// connections.
  567. *
  568. * @var integer
  569. */
  570. var $proxy_port = 8080;
  571. /**
  572. * A user name for accessing the proxy server
  573. * @var string
  574. */
  575. var $proxy_user = '';
  576. /**
  577. * A password for accessing the proxy server
  578. * @var string
  579. */
  580. var $proxy_pass = '';
  581. /**
  582. * The error number, if any
  583. * @var integer
  584. */
  585. var $errno = 0;
  586. /**
  587. * The error message, if any
  588. * @var string
  589. */
  590. var $errstr = '';
  591. /**
  592. * The current debug mode (1 = on, 0 = off)
  593. * @var integer
  594. */
  595. var $debug = 0;
  596. /**
  597. * The HTTP headers for the current request.
  598. * @var string
  599. */
  600. var $headers = '';
  601. /**
  602. * Sets the object's properties
  603. *
  604. * @param string $path the path and name of the RPC server script
  605. * you want the request to go to
  606. * @param string $server the URL of the remote server to connect to.
  607. * If this parameter doesn't specify a
  608. * protocol and $port is 443, ssl:// is
  609. * assumed.
  610. * @param integer $port a port for connecting to the remote server.
  611. * Defaults to 80 for http:// connections and
  612. * 443 for https:// and ssl:// connections.
  613. * @param string $proxy the URL of the proxy server to use, if any.
  614. * If this parameter doesn't specify a
  615. * protocol and $port is 443, ssl:// is
  616. * assumed.
  617. * @param integer $proxy_port a port for connecting to the remote server.
  618. * Defaults to 8080 for http:// connections and
  619. * 443 for https:// and ssl:// connections.
  620. * @param string $proxy_user a user name for accessing the proxy server
  621. * @param string $proxy_pass a password for accessing the proxy server
  622. *
  623. * @return void
  624. */
  625. function XML_RPC_Client($path, $server, $port = 0,
  626. $proxy = '', $proxy_port = 0,
  627. $proxy_user = '', $proxy_pass = '')
  628. {
  629. $this->path = $path;
  630. $this->proxy_user = $proxy_user;
  631. $this->proxy_pass = $proxy_pass;
  632. preg_match('@^(http://|https://|ssl://)?(.*)$@', $server, $match);
  633. if ($match[1] == '') {
  634. if ($port == 443) {
  635. $this->server = $match[2];
  636. $this->protocol = 'ssl://';
  637. $this->port = 443;
  638. } else {
  639. $this->server = $match[2];
  640. if ($port) {
  641. $this->port = $port;
  642. }
  643. }
  644. } elseif ($match[1] == 'http://') {
  645. $this->server = $match[2];
  646. if ($port) {
  647. $this->port = $port;
  648. }
  649. } else {
  650. $this->server = $match[2];
  651. $this->protocol = 'ssl://';
  652. if ($port) {
  653. $this->port = $port;
  654. } else {
  655. $this->port = 443;
  656. }
  657. }
  658. if ($proxy) {
  659. preg_match('@^(http://|https://|ssl://)?(.*)$@', $proxy, $match);
  660. if ($match[1] == '') {
  661. if ($proxy_port == 443) {
  662. $this->proxy = $match[2];
  663. $this->proxy_protocol = 'ssl://';
  664. $this->proxy_port = 443;
  665. } else {
  666. $this->proxy = $match[2];
  667. if ($proxy_port) {
  668. $this->proxy_port = $proxy_port;
  669. }
  670. }
  671. } elseif ($match[1] == 'http://') {
  672. $this->proxy = $match[2];
  673. if ($proxy_port) {
  674. $this->proxy_port = $proxy_port;
  675. }
  676. } else {
  677. $this->proxy = $match[2];
  678. $this->proxy_protocol = 'ssl://';
  679. if ($proxy_port) {
  680. $this->proxy_port = $proxy_port;
  681. } else {
  682. $this->proxy_port = 443;
  683. }
  684. }
  685. }
  686. }
  687. /**
  688. * Change the current debug mode
  689. *
  690. * @param int $in where 1 = on, 0 = off
  691. *
  692. * @return void
  693. */
  694. function setDebug($in)
  695. {
  696. if ($in) {
  697. $this->debug = 1;
  698. } else {
  699. $this->debug = 0;
  700. }
  701. }
  702. /**
  703. * Set username and password properties for connecting to the RPC server
  704. *
  705. * @param string $u the user name
  706. * @param string $p the password
  707. *
  708. * @return void
  709. *
  710. * @see XML_RPC_Client::$username, XML_RPC_Client::$password
  711. */
  712. function setCredentials($u, $p)
  713. {
  714. $this->username = $u;
  715. $this->password = $p;
  716. }
  717. /**
  718. * Transmit the RPC request via HTTP 1.0 protocol
  719. *
  720. * @param object $msg the XML_RPC_Message object
  721. * @param int $timeout how many seconds to wait for the request
  722. *
  723. * @return object an XML_RPC_Response object. 0 is returned if any
  724. * problems happen.
  725. *
  726. * @see XML_RPC_Message, XML_RPC_Client::XML_RPC_Client(),
  727. * XML_RPC_Client::setCredentials()
  728. */
  729. function send($msg, $timeout = 0)
  730. {
  731. if (strtolower(get_class($msg)) != 'xml_rpc_message') {
  732. $this->errstr = 'send()\'s $msg parameter must be an'
  733. . ' XML_RPC_Message object.';
  734. $this->raiseError($this->errstr, XML_RPC_ERROR_PROGRAMMING);
  735. return 0;
  736. }
  737. $msg->debug = $this->debug;
  738. return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
  739. $timeout, $this->username,
  740. $this->password);
  741. }
  742. /**
  743. * Transmit the RPC request via HTTP 1.0 protocol
  744. *
  745. * Requests should be sent using XML_RPC_Client send() rather than
  746. * calling this method directly.
  747. *
  748. * @param object $msg the XML_RPC_Message object
  749. * @param string $server the server to send the request to
  750. * @param int $port the server port send the request to
  751. * @param int $timeout how many seconds to wait for the request
  752. * before giving up
  753. * @param string $username a user name for accessing the RPC server
  754. * @param string $password a password for accessing the RPC server
  755. *
  756. * @return object an XML_RPC_Response object. 0 is returned if any
  757. * problems happen.
  758. *
  759. * @access protected
  760. * @see XML_RPC_Client::send()
  761. */
  762. function sendPayloadHTTP10($msg, $server, $port, $timeout = 0,
  763. $username = '', $password = '')
  764. {
  765. /*
  766. * If we're using a proxy open a socket to the proxy server
  767. * instead to the xml-rpc server
  768. */
  769. if ($this->proxy) {
  770. if ($this->proxy_protocol == 'http://') {
  771. $protocol = '';
  772. } else {
  773. $protocol = $this->proxy_protocol;
  774. }
  775. if ($timeout > 0) {
  776. $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
  777. $this->errno, $this->errstr, $timeout);
  778. } else {
  779. $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
  780. $this->errno, $this->errstr);
  781. }
  782. } else {
  783. if ($this->protocol == 'http://') {
  784. $protocol = '';
  785. } else {
  786. $protocol = $this->protocol;
  787. }
  788. if ($timeout > 0) {
  789. $fp = @fsockopen($protocol . $server, $port,
  790. $this->errno, $this->errstr, $timeout);
  791. } else {
  792. $fp = @fsockopen($protocol . $server, $port,
  793. $this->errno, $this->errstr);
  794. }
  795. }
  796. /*
  797. * Just raising the error without returning it is strange,
  798. * but keep it here for backwards compatibility.
  799. */
  800. if (!$fp && $this->proxy) {
  801. $this->raiseError('Connection to proxy server '
  802. . $this->proxy . ':' . $this->proxy_port
  803. . ' failed. ' . $this->errstr,
  804. XML_RPC_ERROR_CONNECTION_FAILED);
  805. return 0;
  806. } elseif (!$fp) {
  807. $this->raiseError('Connection to RPC server '
  808. . $server . ':' . $port
  809. . ' failed. ' . $this->errstr,
  810. XML_RPC_ERROR_CONNECTION_FAILED);
  811. return 0;
  812. }
  813. if ($timeout) {
  814. /*
  815. * Using socket_set_timeout() because stream_set_timeout()
  816. * was introduced in 4.3.0, but we need to support 4.2.0.
  817. */
  818. socket_set_timeout($fp, $timeout);
  819. }
  820. // Pre-emptive BC hacks for fools calling sendPayloadHTTP10() directly
  821. if ($username != $this->username) {
  822. $this->setCredentials($username, $password);
  823. }
  824. // Only create the payload if it was not created previously
  825. if (empty($msg->payload)) {
  826. $msg->createPayload();
  827. }
  828. $this->createHeaders($msg);
  829. $op = $this->headers . "\r\n\r\n";
  830. $op .= $msg->payload;
  831. if (!fputs($fp, $op, strlen($op))) {
  832. $this->errstr = 'Write error';
  833. return 0;
  834. }
  835. $resp = $msg->parseResponseFile($fp);
  836. $meta = socket_get_status($fp);
  837. if ($meta['timed_out']) {
  838. fclose($fp);
  839. $this->errstr = 'RPC server did not send response before timeout.';
  840. $this->raiseError($this->errstr, XML_RPC_ERROR_CONNECTION_FAILED);
  841. return 0;
  842. }
  843. fclose($fp);
  844. return $resp;
  845. }
  846. /**
  847. * Determines the HTTP headers and puts it in the $headers property
  848. *
  849. * @param object $msg the XML_RPC_Message object
  850. *
  851. * @return boolean TRUE if okay, FALSE if the message payload isn't set.
  852. *
  853. * @access protected
  854. */
  855. function createHeaders($msg)
  856. {
  857. if (empty($msg->payload)) {
  858. return false;
  859. }
  860. if ($this->proxy) {
  861. $this->headers = 'POST ' . $this->protocol . $this->server;
  862. if ($this->proxy_port) {
  863. $this->headers .= ':' . $this->port;
  864. }
  865. } else {
  866. $this->headers = 'POST ';
  867. }
  868. $this->headers .= $this->path. " HTTP/1.0\r\n";
  869. $this->headers .= "User-Agent: PEAR XML_RPC\r\n";
  870. $this->headers .= 'Host: ' . $this->server . "\r\n";
  871. if ($this->proxy && $this->proxy_user) {
  872. $this->headers .= 'Proxy-Authorization: Basic '
  873. . base64_encode("$this->proxy_user:$this->proxy_pass")
  874. . "\r\n";
  875. }
  876. // thanks to Grant Rauscher <grant7@firstworld.net> for this
  877. if ($this->username) {
  878. $this->headers .= 'Authorization: Basic '
  879. . base64_encode("$this->username:$this->password")
  880. . "\r\n";
  881. }
  882. $this->headers .= "Content-Type: text/xml\r\n";
  883. $this->headers .= 'Content-Length: ' . strlen($msg->payload);
  884. return true;
  885. }
  886. }
  887. /**
  888. * The methods and properties for interpreting responses to XML RPC requests
  889. *
  890. * @category Web Services
  891. * @package XML_RPC
  892. * @author Edd Dumbill <edd@usefulinc.com>
  893. * @author Stig Bakken <stig@php.net>
  894. * @author Martin Jansen <mj@php.net>
  895. * @author Daniel Convissor <danielc@php.net>
  896. * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group
  897. * @version Release: 1.4.0
  898. * @link http://pear.php.net/package/XML_RPC
  899. */
  900. class XML_RPC_Response extends XML_RPC_Base
  901. {
  902. var $xv;
  903. var $fn;
  904. var $fs;
  905. var $hdrs;
  906. /**
  907. * @return void
  908. */
  909. function XML_RPC_Response($val, $fcode = 0, $fstr = '')
  910. {
  911. if ($fcode != 0) {
  912. $this->fn = $fcode;
  913. $this->fs = htmlspecialchars($fstr);
  914. } else {
  915. $this->xv = $val;
  916. }
  917. }
  918. /**
  919. * @return int the error code
  920. */
  921. function faultCode()
  922. {
  923. if (isset($this->fn)) {
  924. return $this->fn;
  925. } else {
  926. return 0;
  927. }
  928. }
  929. /**
  930. * @return string the error string
  931. */
  932. function faultString()
  933. {
  934. return $this->fs;
  935. }
  936. /**
  937. * @return mixed the value
  938. */
  939. function value()
  940. {
  941. return $this->xv;
  942. }
  943. /**
  944. * @return string the error message in XML format
  945. */
  946. function serialize()
  947. {
  948. $rs = "<methodResponse>\n";
  949. if ($this->fn) {
  950. $rs .= "<fault>
  951. <value>
  952. <struct>
  953. <member>
  954. <name>faultCode</name>
  955. <value><int>" . $this->fn . "</int></value>
  956. </member>
  957. <member>
  958. <name>faultString</name>
  959. <value><string>" . $this->fs . "</string></value>
  960. </member>
  961. </struct>
  962. </value>
  963. </fault>";
  964. } else {
  965. $rs .= "<params>\n<param>\n" . $this->xv->serialize() .
  966. "</param>\n</params>";
  967. }
  968. $rs .= "\n</methodResponse>";
  969. return $rs;
  970. }
  971. }
  972. /**
  973. * The methods and properties for composing XML RPC messages
  974. *
  975. * @category Web Services
  976. * @package XML_RPC
  977. * @author Edd Dumbill <edd@usefulinc.com>
  978. * @author Stig Bakken <stig@php.net>
  979. * @author Martin Jansen <mj@php.net>
  980. * @author Daniel Convissor <danielc@php.net>
  981. * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group
  982. * @version Release: 1.4.0
  983. * @link http://pear.php.net/package/XML_RPC
  984. */
  985. class XML_RPC_Message extends XML_RPC_Base
  986. {
  987. /**
  988. * The current debug mode (1 = on, 0 = off)
  989. * @var integer
  990. */
  991. var $debug = 0;
  992. /**
  993. * The encoding to be used for outgoing messages
  994. *
  995. * Defaults to the value of <var>$GLOBALS['XML_RPC_defencoding']</var>
  996. *
  997. * @var string
  998. * @see XML_RPC_Message::setSendEncoding(),
  999. * $GLOBALS['XML_RPC_defencoding'], XML_RPC_Message::xml_header()
  1000. */
  1001. var $send_encoding = '';
  1002. /**
  1003. * The method presently being evaluated
  1004. * @var string
  1005. */
  1006. var $methodname = '';
  1007. /**
  1008. * @var array
  1009. */
  1010. var $params = array();
  1011. /**
  1012. * The XML message being generated
  1013. * @var string
  1014. */
  1015. var $payload = '';
  1016. /**
  1017. * @return void
  1018. */
  1019. function XML_RPC_Message($meth, $pars = 0)
  1020. {
  1021. $this->methodname = $meth;
  1022. if (is_array($pars) && sizeof($pars) > 0) {
  1023. for ($i = 0; $i < sizeof($pars); $i++) {
  1024. $this->addParam($pars[$i]);
  1025. }
  1026. }
  1027. }
  1028. /**
  1029. * Produces the XML declaration including the encoding attribute
  1030. *
  1031. * The encoding is determined by this class' <var>$send_encoding</var>
  1032. * property. If the <var>$send_encoding</var> property is not set, use
  1033. * <var>$GLOBALS['XML_RPC_defencoding']</var>.
  1034. *
  1035. * @return string the XML declaration and <methodCall> element
  1036. *
  1037. * @see XML_RPC_Message::setSendEncoding(),
  1038. * XML_RPC_Message::$send_encoding, $GLOBALS['XML_RPC_defencoding']
  1039. */
  1040. function xml_header()
  1041. {
  1042. global $XML_RPC_defencoding;
  1043. if (!$this->send_encoding) {
  1044. $this->send_encoding = $XML_RPC_defencoding;
  1045. }
  1046. return '<?xml version="1.0" encoding="' . $this->send_encoding . '"?>'
  1047. . "\n<methodCall>\n";
  1048. }
  1049. /**
  1050. * @return string the closing </methodCall> tag
  1051. */
  1052. function xml_footer()
  1053. {
  1054. return "</methodCall>\n";
  1055. }
  1056. /**
  1057. * @return void
  1058. *
  1059. * @uses XML_RPC_Message::xml_header(), XML_RPC_Message::xml_footer()
  1060. */
  1061. function createPayload()
  1062. {
  1063. $this->payload = $this->xml_header();
  1064. $this->payload .= '<methodName>' . $this->methodname . "</methodName>\n";
  1065. $this->payload .= "<params>\n";
  1066. for ($i = 0; $i < sizeof($this->params); $i++) {
  1067. $p = $this->params[$i];
  1068. $this->payload .= "<param>\n" . $p->serialize() . "</param>\n";
  1069. }
  1070. $this->payload .= "</params>\n";
  1071. $this->payload .= $this->xml_footer();
  1072. $this->payload = ereg_replace("[\r\n]+", "\r\n", $this->payload);
  1073. }
  1074. /**
  1075. * @return string the name of the method
  1076. */
  1077. function method($meth = '')
  1078. {
  1079. if ($meth != '') {
  1080. $this->methodname = $meth;
  1081. }
  1082. return $this->methodname;
  1083. }
  1084. /**
  1085. * @return string the payload
  1086. */
  1087. function serialize()
  1088. {
  1089. $this->createPayload();
  1090. return $this->payload;
  1091. }
  1092. /**
  1093. * @return void
  1094. */
  1095. function addParam($par)
  1096. {
  1097. $this->params[] = $par;
  1098. }
  1099. /**
  1100. * Obtains an XML_RPC_Value object for the given parameter
  1101. *
  1102. * @param int $i the index number of the parameter to obtain
  1103. *
  1104. * @return object the XML_RPC_Value object.
  1105. * If the parameter doesn't exist, an XML_RPC_Response object.
  1106. *
  1107. * @since Returns XML_RPC_Response object on error since Release 1.3.0
  1108. */
  1109. function getParam($i)
  1110. {
  1111. global $XML_RPC_err, $XML_RPC_str;
  1112. if (isset($this->params[$i])) {
  1113. return $this->params[$i];
  1114. } else {
  1115. $this->raiseError('The submitted request did not contain this parameter',
  1116. XML_RPC_ERROR_INCORRECT_PARAMS);
  1117. return new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
  1118. $XML_RPC_str['incorrect_params']);
  1119. }
  1120. }
  1121. /**
  1122. * @return int the number of parameters
  1123. */
  1124. function getNumParams()
  1125. {
  1126. return sizeof($this->params);
  1127. }
  1128. /**
  1129. * Sets the XML declaration's encoding attribute
  1130. *
  1131. * @param string $type the encoding type (ISO-8859-1, UTF-8 or US-ASCII)
  1132. *
  1133. * @return void
  1134. *
  1135. * @see XML_RPC_Message::$send_encoding, XML_RPC_Message::xml_header()
  1136. * @since Method available since Release 1.2.0
  1137. */
  1138. function setSendEncoding($type)
  1139. {
  1140. $this->send_encoding = $type;
  1141. }
  1142. /**
  1143. * Determine the XML's encoding via the encoding attribute
  1144. * in the XML declaration
  1145. *
  1146. * If the encoding parameter is not set or is not ISO-8859-1, UTF-8
  1147. * or US-ASCII, $XML_RPC_defencoding will be returned.
  1148. *
  1149. * @param string $data the XML that will be parsed
  1150. *
  1151. * @return string the encoding to be used
  1152. *
  1153. * @link http://php.net/xml_parser_create
  1154. * @since Method available since Release 1.2.0
  1155. */
  1156. function getEncoding($data)
  1157. {
  1158. global $XML_RPC_defencoding;
  1159. if (preg_match('/<\?xml[^>]*\s*encoding\s*=\s*[\'"]([^"\']*)[\'"]/i',
  1160. $data, $match))
  1161. {
  1162. $match[1] = trim(strtoupper($match[1]));
  1163. switch ($match[1]) {
  1164. case 'ISO-8859-1':
  1165. case 'UTF-8':
  1166. case 'US-ASCII':
  1167. return $match[1];
  1168. break;
  1169. default:
  1170. return $XML_RPC_defencoding;
  1171. }
  1172. } else {
  1173. return $XML_RPC_defencoding;
  1174. }
  1175. }
  1176. /**
  1177. * @return object a new XML_RPC_Response object
  1178. */
  1179. function parseResponseFile($fp)
  1180. {
  1181. $ipd = '';
  1182. while ($data = @fread($fp, 8192)) {
  1183. $ipd .= $data;
  1184. }
  1185. return $this->parseResponse($ipd);
  1186. }
  1187. /**
  1188. * @return object a new XML_RPC_Response object
  1189. */
  1190. function parseResponse($data = '')
  1191. {
  1192. global $XML_RPC_xh, $XML_RPC_err, $XML_RPC_str, $XML_RPC_defencoding;
  1193. $encoding = $this->getEncoding($data);
  1194. $parser_resource = xml_parser_create($encoding);
  1195. $parser = (int) $parser_resource;
  1196. $XML_RPC_xh = array();
  1197. $XML_RPC_xh[$parser] = array();
  1198. $XML_RPC_xh[$parser]['cm'] = 0;
  1199. $XML_RPC_xh[$parser]['isf'] = 0;
  1200. $XML_RPC_xh[$parser]['ac'] = '';
  1201. $XML_RPC_xh[$parser]['qt'] = '';
  1202. $XML_RPC_xh[$parser]['stack'] = array();
  1203. $XML_RPC_xh[$parser]['valuestack'] = array();
  1204. xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
  1205. xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
  1206. xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
  1207. $hdrfnd = 0;
  1208. if ($this->debug) {
  1209. print "\n<pre>---GOT---\n";
  1210. print isset($_SERVER['SERVER_PROTOCOL']) ? htmlspecialchars($data) : $data;
  1211. print "\n---END---</pre>\n";
  1212. }
  1213. // See if response is a 200 or a 100 then a 200, else raise error.
  1214. // But only do this if we're using the HTTP protocol.
  1215. if (ereg('^HTTP', $data) &&
  1216. !ereg('^HTTP/[0-9\.]+ 200 ', $data) &&
  1217. !preg_match('@^HTTP/[0-9\.]+ 10[0-9]([A-Za-z ]+)?[\r\n]+HTTP/[0-9\.]+ 200@', $data))
  1218. {
  1219. $errstr = substr($data, 0, strpos($data, "\n") - 1);
  1220. if(defined("DEBUG") && DEBUG) {error_log('HTTP error, got response: ' . $errstr);}
  1221. $r = new XML_RPC_Response(0, $XML_RPC_err['http_error'],
  1222. $XML_RPC_str['http_error'] . ' (' .
  1223. $errstr . ')');
  1224. xml_parser_free($parser_resource);
  1225. return $r;
  1226. }
  1227. // gotta get rid of headers here
  1228. if (!$hdrfnd && ($brpos = strpos($data,"\r\n\r\n"))) {
  1229. $XML_RPC_xh[$parser]['ha'] = substr($data, 0, $brpos);
  1230. $data = substr($data, $brpos + 4);
  1231. $hdrfnd = 1;
  1232. }
  1233. /*
  1234. * be tolerant of junk after methodResponse
  1235. * (e.g. javascript automatically inserted by free hosts)
  1236. * thanks to Luca Mariano <luca.mariano@email.it>
  1237. */
  1238. $data = substr($data, 0, strpos($data, "</methodResponse>") + 17);
  1239. if (!xml_parse($parser_resource, $data, sizeof($data))) {
  1240. // thanks to Peter Kocks <peter.kocks@baygate.com>
  1241. if (xml_get_current_line_number($parser_resource) == 1) {
  1242. $errstr = 'XML error at line 1, check URL';
  1243. } else {
  1244. $errstr = sprintf('XML error: %s at line %d',
  1245. xml_error_string(xml_get_error_code($parser_resource)),
  1246. xml_get_current_line_number($parser_resource));
  1247. }
  1248. if(defined("DEBUG") && DEBUG) {error_log($errstr);}
  1249. $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
  1250. $XML_RPC_str['invalid_return']);
  1251. xml_parser_free($parser_resource);
  1252. return $r;
  1253. }
  1254. xml_parser_free($parser_resource);
  1255. if ($this->debug) {
  1256. print "\n<pre>---PARSED---\n";
  1257. var_dump($XML_RPC_xh[$parser]['value']);
  1258. print "---END---</pre>\n";
  1259. }
  1260. if ($XML_RPC_xh[$parser]['isf'] > 1) {
  1261. $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
  1262. $XML_RPC_str['invalid_return'].' '.$XML_RPC_xh[$parser]['isf_reason']);
  1263. } elseif (!is_object($XML_RPC_xh[$parser]['value'])) {
  1264. // then something odd has happened
  1265. // and it's time to generate a client side error
  1266. // indicating something odd went on
  1267. $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
  1268. $XML_RPC_str['invalid_return']);
  1269. } else {
  1270. $v = $XML_RPC_xh[$parser]['value'];
  1271. $allOK=1;
  1272. if ($XML_RPC_xh[$parser]['isf']) {
  1273. $f = $v->structmem('faultCode');
  1274. $fs = $v->structmem('faultString');
  1275. $r = new XML_RPC_Response($v, $f->scalarval(),
  1276. $fs->scalarval());
  1277. } else {
  1278. $r = new XML_RPC_Response($v);
  1279. }
  1280. }
  1281. $r->hdrs = split("\r?\n", $XML_RPC_xh[$parser]['ha'][1]);
  1282. return $r;
  1283. }
  1284. }
  1285. /**
  1286. * The methods and properties that represent data in XML RPC format
  1287. *
  1288. * @category Web Services
  1289. * @package XML_RPC
  1290. * @author Edd Dumbill <edd@usefulinc.com>
  1291. * @author Stig Bakken <stig@php.net>
  1292. * @author Martin Jansen <mj@php.net>
  1293. * @author Daniel Convissor <danielc@php.net>
  1294. * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group
  1295. * @version Release: 1.4.0
  1296. * @link http://pear.php.net/package/XML_RPC
  1297. */
  1298. class XML_RPC_Value extends XML_RPC_Base
  1299. {
  1300. var $me = array();
  1301. var $mytype = 0;
  1302. /**
  1303. * @return void
  1304. */
  1305. function XML_RPC_Value($val = -1, $type = '')
  1306. {
  1307. global $XML_RPC_Types;
  1308. $this->me = array();
  1309. $this->mytype = 0;
  1310. if ($val != -1 || $type != '') {
  1311. if ($type == '') {
  1312. $type = 'string';
  1313. }
  1314. if (!array_key_exists($type, $XML_RPC_Types)) {
  1315. // XXX
  1316. // need some way to report this error
  1317. } elseif ($XML_RPC_Types[$type] == 1) {
  1318. $this->addScalar($val, $type);
  1319. } elseif ($XML_RPC_Types[$type] == 2) {
  1320. $this->addArray($val);
  1321. } elseif ($XML_RPC_Types[$type] == 3) {
  1322. $this->addStruct($val);
  1323. }
  1324. }
  1325. }
  1326. /**
  1327. * @return int returns 1 if successful or 0 if there are problems
  1328. */
  1329. function addScalar($val, $type = 'string')
  1330. {
  1331. global $XML_RPC_Types, $XML_RPC_Boolean;
  1332. if ($this->mytype == 1) {
  1333. $this->raiseError('Scalar can have only one value',
  1334. XML_RPC_ERROR_INVALID_TYPE);
  1335. return 0;
  1336. }
  1337. $typeof = $XML_RPC_Types[$type];
  1338. if ($typeof != 1) {
  1339. $this->raiseError("Not a scalar type (${typeof})",
  1340. XML_RPC_ERROR_INVALID_TYPE);
  1341. return 0;
  1342. }
  1343. if ($type == $XML_RPC_Boolean) {
  1344. if (strcasecmp($val, 'true') == 0
  1345. || $val == 1
  1346. || ($val == true && strcasecmp($val, 'false')))
  1347. {
  1348. $val = 1;
  1349. } else {
  1350. $val = 0;
  1351. }
  1352. }
  1353. if ($this->mytype == 2) {
  1354. // we're adding to an array here
  1355. $ar = $this->me['array'];
  1356. $ar[] = new XML_RPC_Value($val, $type);
  1357. $this->me['array'] = $ar;
  1358. } else {
  1359. // a scalar, so set the value and remember we're scalar
  1360. $this->me[$type] = $val;
  1361. $this->mytype = $typeof;
  1362. }
  1363. return 1;
  1364. }
  1365. /**
  1366. * @return int returns 1 if successful or 0 if there are problems
  1367. */
  1368. function addArray($vals)
  1369. {
  1370. global $XML_RPC_Types;
  1371. if ($this->mytype != 0) {
  1372. $this->raiseError(
  1373. 'Already initialized as a [' . $this->kindOf() . ']',
  1374. XML_RPC_ERROR_ALREADY_INITIALIZED);
  1375. return 0;
  1376. }
  1377. $this->mytype = $XML_RPC_Types['array'];
  1378. $this->me['array'] = $vals;
  1379. return 1;
  1380. }
  1381. /**
  1382. * @return int returns 1 if successful or 0 if there are problems
  1383. */
  1384. function addStruct($vals)
  1385. {
  1386. global $XML_RPC_Types;
  1387. if ($this->mytype != 0) {
  1388. $this->raiseError(
  1389. 'Already initialized as a [' . $this->kindOf() . ']',
  1390. XML_RPC_ERROR_ALREADY_INITIALIZED);
  1391. return 0;
  1392. }
  1393. $this->mytype = $XML_RPC_Types['struct'];
  1394. $this->me['struct'] = $vals;
  1395. return 1;
  1396. }
  1397. /**
  1398. * @return void
  1399. */
  1400. function dump($ar)
  1401. {
  1402. reset($ar);
  1403. foreach ($ar as $key => $val) {
  1404. echo "$key => $val<br />";
  1405. if ($key == 'array') {
  1406. foreach ($val as $key2 => $val2) {
  1407. echo "-- $key2 => $val2<br />";
  1408. }
  1409. }
  1410. }
  1411. }
  1412. /**
  1413. * @return string the data type of the current value
  1414. */
  1415. function kindOf()
  1416. {
  1417. switch ($this->mytype) {
  1418. case 3:
  1419. return 'struct';
  1420. case 2:
  1421. return 'array';
  1422. case 1:
  1423. return 'scalar';
  1424. default:
  1425. return 'undef';
  1426. }
  1427. }
  1428. /**
  1429. * @return string the data in XML format
  1430. */
  1431. function serializedata($typ, $val)
  1432. {
  1433. $rs = '';
  1434. global $XML_RPC_Types, $XML_RPC_Base64, $XML_RPC_String, $XML_RPC_Boolean;
  1435. if (!array_key_exists($typ, $XML_RPC_Types)) {
  1436. // XXX
  1437. // need some way to report this error
  1438. return;
  1439. }
  1440. switch ($XML_RPC_Types[$typ]) {
  1441. case 3:
  1442. // struct
  1443. $rs .= "<struct>\n";
  1444. reset($val);
  1445. foreach ($val as $key2 => $val2) {
  1446. $rs .= "<member><name>${key2}</name>\n";
  1447. $rs .= $this->serializeval($val2);
  1448. $rs .= "</member>\n";
  1449. }
  1450. $rs .= '</struct>';
  1451. break;
  1452. case 2:
  1453. // array
  1454. $rs .= "<array>\n<data>\n";
  1455. for ($i = 0; $i < sizeof($val); $i++) {
  1456. $rs .= $this->serializeval($val[$i]);
  1457. }
  1458. $rs .= "</data>\n</array>";
  1459. break;
  1460. case 1:
  1461. switch ($typ) {
  1462. case $XML_RPC_Base64:
  1463. $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>";
  1464. break;
  1465. case $XML_RPC_Boolean:
  1466. $rs .= "<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
  1467. break;
  1468. case $XML_RPC_String:
  1469. $rs .= "<${typ}>" . htmlspecialchars($val). "</${typ}>";
  1470. break;
  1471. default:
  1472. $rs .= "<${typ}>${val}</${typ}>";
  1473. }
  1474. }
  1475. return $rs;
  1476. }
  1477. /**
  1478. * @return string the data in XML format
  1479. */
  1480. function serialize()
  1481. {
  1482. return $this->serializeval($this);
  1483. }
  1484. /**
  1485. * @return string the data in XML format
  1486. */
  1487. function serializeval($o)
  1488. {
  1489. if (!is_object($o) || empty($o->me) || !is_array($o->me)) {
  1490. return '';
  1491. }
  1492. $ar = $o->me;
  1493. reset($ar);
  1494. list($typ, $val) = each($ar);
  1495. return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
  1496. }
  1497. /**
  1498. * @return mixed the contents of the element requested
  1499. */
  1500. function structmem($m)
  1501. {
  1502. return $this->me['struct'][$m];
  1503. }
  1504. /**
  1505. * @return void
  1506. */
  1507. function structreset()
  1508. {
  1509. reset($this->me['struct']);
  1510. }
  1511. /**
  1512. * @return the key/value pair of the struct's current element
  1513. */
  1514. function structeach()
  1515. {
  1516. return each($this->me['struct']);
  1517. }
  1518. /**
  1519. * @return mixed the current value
  1520. */
  1521. function getval()
  1522. {
  1523. // UNSTABLE
  1524. global $XML_RPC_BOOLEAN, $XML_RPC_Base64;
  1525. reset($this->me);
  1526. $b = current($this->me);
  1527. // contributed by I Sofer, 2001-03-24
  1528. // add support for nested arrays to scalarval
  1529. // i've created a new method here, so as to
  1530. // preserve back compatibility
  1531. if (is_array($b)) {
  1532. foreach ($b as $id => $cont) {
  1533. $b[$id] = $cont->scalarval();
  1534. }
  1535. }
  1536. // add support for structures directly encoding php objects
  1537. if (is_object($b)) {
  1538. $t = get_object_vars($b);
  1539. foreach ($t as $id => $cont) {
  1540. $t[$id] = $cont->scalarval();
  1541. }
  1542. foreach ($t as $id => $cont) {
  1543. $b->$id = $cont;
  1544. }
  1545. }
  1546. // end contrib
  1547. return $b;
  1548. }
  1549. /**
  1550. * @return mixed
  1551. */
  1552. function scalarval()
  1553. {
  1554. global $XML_RPC_Boolean, $XML_RPC_Base64;
  1555. reset($this->me);
  1556. return current($this->me);
  1557. }
  1558. /**
  1559. * @return string
  1560. */
  1561. function scalartyp()
  1562. {
  1563. global $XML_RPC_I4, $XML_RPC_Int;
  1564. reset($this->me);
  1565. $a = key($this->me);
  1566. if ($a == $XML_RPC_I4) {
  1567. $a = $XML_RPC_Int;
  1568. }
  1569. return $a;
  1570. }
  1571. /**
  1572. * @return mixed the struct's current element
  1573. */
  1574. function arraymem($m)
  1575. {
  1576. return $this->me['array'][$m];
  1577. }
  1578. /**
  1579. * @return int the number of elements in the array
  1580. */
  1581. function arraysize()
  1582. {
  1583. reset($this->me);
  1584. list($a, $b) = each($this->me);
  1585. return sizeof($b);
  1586. }
  1587. /**
  1588. * Determines if the item submitted is an XML_RPC_Value object
  1589. *
  1590. * @param mixed $val the variable to be evaluated
  1591. *
  1592. * @return bool TRUE if the item is an XML_RPC_Value object
  1593. *
  1594. * @static
  1595. * @since Method available since Release 1.3.0
  1596. */
  1597. function isValue($val)
  1598. {
  1599. return (strtolower(get_class($val)) == 'xml_rpc_value');
  1600. }
  1601. }
  1602. /**
  1603. * Return an ISO8601 encoded string
  1604. *
  1605. * While timezones ought to be supported, the XML-RPC spec says:
  1606. *
  1607. * "Don't assume a timezone. It should be specified by the server in its
  1608. * documentation what assumptions it makes about timezones."
  1609. *
  1610. * This routine always assumes localtime unless $utc is set to 1, in which
  1611. * case UTC is assumed and an adjustment for locale is made when encoding.
  1612. *
  1613. * @return string the formatted date
  1614. */
  1615. function XML_RPC_iso8601_encode($timet, $utc = 0)
  1616. {
  1617. if (!$utc) {
  1618. $t = strftime('%Y%m%dT%H:%M:%S', $timet);
  1619. } else {
  1620. if (function_exists('gmstrftime')) {
  1621. // gmstrftime doesn't exist in some versions
  1622. // of PHP
  1623. $t = gmstrftime('%Y%m%dT%H:%M:%S', $timet);
  1624. } else {
  1625. $t = strftime('%Y%m%dT%H:%M:%S', $timet - date('Z'));
  1626. }
  1627. }
  1628. return $t;
  1629. }
  1630. /**
  1631. * Convert a datetime string into a Unix timestamp
  1632. *
  1633. * While timezones ought to be supported, the XML-RPC spec says:
  1634. *
  1635. * "Don't assume a timezone. It should be specified by the server in its
  1636. * documentation what assumptions it makes about timezones."
  1637. *
  1638. * This routine always assumes localtime unless $utc is set to 1, in which
  1639. * case UTC is assumed and an adjustment for locale is made when encoding.
  1640. *
  1641. * @return int the unix timestamp of the date submitted
  1642. */
  1643. function XML_RPC_iso8601_decode($idate, $utc = 0)
  1644. {
  1645. $t = 0;
  1646. if (ereg('([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})', $idate, $regs)) {
  1647. if ($utc) {
  1648. $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
  1649. } else {
  1650. $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
  1651. }
  1652. }
  1653. return $t;
  1654. }
  1655. /**
  1656. * Converts an XML_RPC_Value object into native PHP types
  1657. *
  1658. * @param object $XML_RPC_val the XML_RPC_Value object to decode
  1659. *
  1660. * @return mixed the PHP values
  1661. */
  1662. function XML_RPC_decode($XML_RPC_val)
  1663. {
  1664. $kind = $XML_RPC_val->kindOf();
  1665. if ($kind == 'scalar') {
  1666. return $XML_RPC_val->scalarval();
  1667. } elseif ($kind == 'array') {
  1668. $size = $XML_RPC_val->arraysize();
  1669. $arr = array();
  1670. for ($i = 0; $i < $size; $i++) {
  1671. $arr[] = XML_RPC_decode($XML_RPC_val->arraymem($i));
  1672. }
  1673. return $arr;
  1674. } elseif ($kind == 'struct') {
  1675. $XML_RPC_val->structreset();
  1676. $arr = array();
  1677. while (list($key, $value) = $XML_RPC_val->structeach()) {
  1678. $arr[$key] = XML_RPC_decode($value);
  1679. }
  1680. return $arr;
  1681. }
  1682. }
  1683. /**
  1684. * Converts native PHP types into an XML_RPC_Value object
  1685. *
  1686. * @param mixed $php_val the PHP value or variable you want encoded
  1687. *
  1688. * @return object the XML_RPC_Value object
  1689. */
  1690. function XML_RPC_encode($php_val)
  1691. {
  1692. global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double, $XML_RPC_String,
  1693. $XML_RPC_Array, $XML_RPC_Struct;
  1694. $type = gettype($php_val);
  1695. $XML_RPC_val = new XML_RPC_Value;
  1696. switch ($type) {
  1697. case 'array':
  1698. if (empty($php_val)) {
  1699. $XML_RPC_val->addArray($php_val);
  1700. break;
  1701. }
  1702. $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
  1703. if (empty($tmp)) {
  1704. $arr = array();
  1705. foreach ($php_val as $k => $v) {
  1706. $arr[$k] = XML_RPC_encode($v);
  1707. }
  1708. $XML_RPC_val->addArray($arr);
  1709. break;
  1710. }
  1711. // fall though if it's not an enumerated array
  1712. case 'object':
  1713. $arr = array();
  1714. foreach ($php_val as $k => $v) {
  1715. $arr[$k] = XML_RPC_encode($v);
  1716. }
  1717. $XML_RPC_val->addStruct($arr);
  1718. break;
  1719. case 'integer':
  1720. $XML_RPC_val->addScalar($php_val, $XML_RPC_Int);
  1721. break;
  1722. case 'double':
  1723. $XML_RPC_val->addScalar($php_val, $XML_RPC_Double);
  1724. break;
  1725. case 'string':
  1726. case 'NULL':
  1727. $XML_RPC_val->addScalar($php_val, $XML_RPC_String);
  1728. break;
  1729. case 'boolean':
  1730. // Add support for encoding/decoding of booleans, since they
  1731. // are supported in PHP
  1732. // by <G_Giunta_2001-02-29>
  1733. $XML_RPC_val->addScalar($php_val, $XML_RPC_Boolean);
  1734. break;
  1735. case 'unknown type':
  1736. default:
  1737. $XML_RPC_val = false;
  1738. }
  1739. return $XML_RPC_val;
  1740. }
  1741. /*
  1742. * Local variables:
  1743. * tab-width: 4
  1744. * c-basic-offset: 4
  1745. * c-hanging-comment-ender-p: nil
  1746. * End:
  1747. */
  1748. ?>