| [ Index ] |
PHP Cross Reference of Nucleus CMS 3.32 |
[Summary view] [Print] [Text view]
1 <?php 2 // by Edd Dumbill (C) 1999-2002 3 // <edd@usefulinc.com> 4 // $Original: xmlrpc.inc,v 1.158 2007/03/01 21:21:02 ggiunta Exp $ 5 // $Id: xmlrpc.inc.php 1199 2007-09-07 07:03:41Z kimitake $ 6 7 8 // Copyright (c) 1999,2000,2002 Edd Dumbill. 9 // All rights reserved. 10 // 11 // Redistribution and use in source and binary forms, with or without 12 // modification, are permitted provided that the following conditions 13 // are met: 14 // 15 // * Redistributions of source code must retain the above copyright 16 // notice, this list of conditions and the following disclaimer. 17 // 18 // * Redistributions in binary form must reproduce the above 19 // copyright notice, this list of conditions and the following 20 // disclaimer in the documentation and/or other materials provided 21 // with the distribution. 22 // 23 // * Neither the name of the "XML-RPC for PHP" nor the names of its 24 // contributors may be used to endorse or promote products derived 25 // from this software without specific prior written permission. 26 // 27 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 28 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 29 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 30 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 31 // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 32 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 33 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 34 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 36 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 38 // OF THE POSSIBILITY OF SUCH DAMAGE. 39 40 if(!function_exists('xml_parser_create')) 41 { 42 // For PHP 4 onward, XML functionality is always compiled-in on windows: 43 // no more need to dl-open it. It might have been compiled out on *nix... 44 if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN')) 45 { 46 dl('xml.so'); 47 } 48 } 49 50 // Try to be backward compat with php < 4.2 (are we not being nice ?) 51 $phpversion = phpversion(); 52 if($phpversion[0] == '4' && $phpversion[2] < 2) 53 { 54 // give an opportunity to user to specify where to include other files from 55 if(!defined('PHP_XMLRPC_COMPAT_DIR')) 56 { 57 define('PHP_XMLRPC_COMPAT_DIR',dirname(__FILE__).'/compat/'); 58 } 59 if($phpversion[2] == '0') 60 { 61 if($phpversion[4] < 6) 62 { 63 include(PHP_XMLRPC_COMPAT_DIR.'is_callable.php'); 64 } 65 include(PHP_XMLRPC_COMPAT_DIR.'is_scalar.php'); 66 include(PHP_XMLRPC_COMPAT_DIR.'array_key_exists.php'); 67 include(PHP_XMLRPC_COMPAT_DIR.'version_compare.php'); 68 } 69 include(PHP_XMLRPC_COMPAT_DIR.'var_export.php'); 70 include(PHP_XMLRPC_COMPAT_DIR.'is_a.php'); 71 } 72 73 // G. Giunta 2005/01/29: declare global these variables, 74 // so that xmlrpc.inc will work even if included from within a function 75 // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used. 76 $GLOBALS['xmlrpcI4']='i4'; 77 $GLOBALS['xmlrpcInt']='int'; 78 $GLOBALS['xmlrpcBoolean']='boolean'; 79 $GLOBALS['xmlrpcDouble']='double'; 80 $GLOBALS['xmlrpcString']='string'; 81 $GLOBALS['xmlrpcDateTime']='dateTime.iso8601'; 82 $GLOBALS['xmlrpcBase64']='base64'; 83 $GLOBALS['xmlrpcArray']='array'; 84 $GLOBALS['xmlrpcStruct']='struct'; 85 $GLOBALS['xmlrpcValue']='undefined'; 86 87 $GLOBALS['xmlrpcTypes']=array( 88 $GLOBALS['xmlrpcI4'] => 1, 89 $GLOBALS['xmlrpcInt'] => 1, 90 $GLOBALS['xmlrpcBoolean'] => 1, 91 $GLOBALS['xmlrpcString'] => 1, 92 $GLOBALS['xmlrpcDouble'] => 1, 93 $GLOBALS['xmlrpcDateTime'] => 1, 94 $GLOBALS['xmlrpcBase64'] => 1, 95 $GLOBALS['xmlrpcArray'] => 2, 96 $GLOBALS['xmlrpcStruct'] => 3 97 ); 98 99 $GLOBALS['xmlrpc_valid_parents'] = array( 100 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'), 101 'BOOLEAN' => array('VALUE'), 102 'I4' => array('VALUE'), 103 'INT' => array('VALUE'), 104 'STRING' => array('VALUE'), 105 'DOUBLE' => array('VALUE'), 106 'DATETIME.ISO8601' => array('VALUE'), 107 'BASE64' => array('VALUE'), 108 'MEMBER' => array('STRUCT'), 109 'NAME' => array('MEMBER'), 110 'DATA' => array('ARRAY'), 111 'ARRAY' => array('VALUE'), 112 'STRUCT' => array('VALUE'), 113 'PARAM' => array('PARAMS'), 114 'METHODNAME' => array('METHODCALL'), 115 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'), 116 'FAULT' => array('METHODRESPONSE'), 117 'NIL' => array('VALUE') // only used when extension activated 118 ); 119 120 // define extra types for supporting NULL (useful for json or <NIL/>) 121 $GLOBALS['xmlrpcNull']='null'; 122 $GLOBALS['xmlrpcTypes']['null']=1; 123 124 // Not in use anymore since 2.0. Shall we remove it? 125 /// @deprecated 126 $GLOBALS['xmlEntities']=array( 127 'amp' => '&', 128 'quot' => '"', 129 'lt' => '<', 130 'gt' => '>', 131 'apos' => "'" 132 ); 133 134 // tables used for transcoding different charsets into us-ascii xml 135 136 $GLOBALS['xml_iso88591_Entities']=array(); 137 $GLOBALS['xml_iso88591_Entities']['in'] = array(); 138 $GLOBALS['xml_iso88591_Entities']['out'] = array(); 139 for ($i = 0; $i < 32; $i++) 140 { 141 $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i); 142 $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';'; 143 } 144 for ($i = 160; $i < 256; $i++) 145 { 146 $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i); 147 $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';'; 148 } 149 150 /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159. 151 /// These will NOT be present in true ISO-8859-1, but will save the unwary 152 /// windows user from sending junk. 153 /* 154 $cp1252_to_xmlent = 155 array( 156 '\x80'=>'€', '\x81'=>'?', '\x82'=>'‚', '\x83'=>'ƒ', 157 '\x84'=>'„', '\x85'=>'…', '\x86'=>'†', \x87'=>'‡', 158 '\x88'=>'ˆ', '\x89'=>'‰', '\x8A'=>'Š', '\x8B'=>'‹', 159 '\x8C'=>'Œ', '\x8D'=>'?', '\x8E'=>'Ž', '\x8F'=>'?', 160 '\x90'=>'?', '\x91'=>'‘', '\x92'=>'’', '\x93'=>'“', 161 '\x94'=>'”', '\x95'=>'•', '\x96'=>'–', '\x97'=>'—', 162 '\x98'=>'˜', '\x99'=>'™', '\x9A'=>'š', '\x9B'=>'›', 163 '\x9C'=>'œ', '\x9D'=>'?', '\x9E'=>'ž', '\x9F'=>'Ÿ' 164 ); 165 */ 166 167 $GLOBALS['xmlrpcerr']['unknown_method']=1; 168 $GLOBALS['xmlrpcstr']['unknown_method']='Unknown method'; 169 $GLOBALS['xmlrpcerr']['invalid_return']=2; 170 $GLOBALS['xmlrpcstr']['invalid_return']='Invalid return payload: enable debugging to examine incoming payload'; 171 $GLOBALS['xmlrpcerr']['incorrect_params']=3; 172 $GLOBALS['xmlrpcstr']['incorrect_params']='Incorrect parameters passed to method'; 173 $GLOBALS['xmlrpcerr']['introspect_unknown']=4; 174 $GLOBALS['xmlrpcstr']['introspect_unknown']="Can't introspect: method unknown"; 175 $GLOBALS['xmlrpcerr']['http_error']=5; 176 $GLOBALS['xmlrpcstr']['http_error']="Didn't receive 200 OK from remote server."; 177 $GLOBALS['xmlrpcerr']['no_data']=6; 178 $GLOBALS['xmlrpcstr']['no_data']='No data received from server.'; 179 $GLOBALS['xmlrpcerr']['no_ssl']=7; 180 $GLOBALS['xmlrpcstr']['no_ssl']='No SSL support compiled in.'; 181 $GLOBALS['xmlrpcerr']['curl_fail']=8; 182 $GLOBALS['xmlrpcstr']['curl_fail']='CURL error'; 183 $GLOBALS['xmlrpcerr']['invalid_request']=15; 184 $GLOBALS['xmlrpcstr']['invalid_request']='Invalid request payload'; 185 $GLOBALS['xmlrpcerr']['no_curl']=16; 186 $GLOBALS['xmlrpcstr']['no_curl']='No CURL support compiled in.'; 187 $GLOBALS['xmlrpcerr']['server_error']=17; 188 $GLOBALS['xmlrpcstr']['server_error']='Internal server error'; 189 $GLOBALS['xmlrpcerr']['multicall_error']=18; 190 $GLOBALS['xmlrpcstr']['multicall_error']='Received from server invalid multicall response'; 191 192 $GLOBALS['xmlrpcerr']['multicall_notstruct'] = 9; 193 $GLOBALS['xmlrpcstr']['multicall_notstruct'] = 'system.multicall expected struct'; 194 $GLOBALS['xmlrpcerr']['multicall_nomethod'] = 10; 195 $GLOBALS['xmlrpcstr']['multicall_nomethod'] = 'missing methodName'; 196 $GLOBALS['xmlrpcerr']['multicall_notstring'] = 11; 197 $GLOBALS['xmlrpcstr']['multicall_notstring'] = 'methodName is not a string'; 198 $GLOBALS['xmlrpcerr']['multicall_recursion'] = 12; 199 $GLOBALS['xmlrpcstr']['multicall_recursion'] = 'recursive system.multicall forbidden'; 200 $GLOBALS['xmlrpcerr']['multicall_noparams'] = 13; 201 $GLOBALS['xmlrpcstr']['multicall_noparams'] = 'missing params'; 202 $GLOBALS['xmlrpcerr']['multicall_notarray'] = 14; 203 $GLOBALS['xmlrpcstr']['multicall_notarray'] = 'params is not an array'; 204 205 $GLOBALS['xmlrpcerr']['cannot_decompress']=103; 206 $GLOBALS['xmlrpcstr']['cannot_decompress']='Received from server compressed HTTP and cannot decompress'; 207 $GLOBALS['xmlrpcerr']['decompress_fail']=104; 208 $GLOBALS['xmlrpcstr']['decompress_fail']='Received from server invalid compressed HTTP'; 209 $GLOBALS['xmlrpcerr']['dechunk_fail']=105; 210 $GLOBALS['xmlrpcstr']['dechunk_fail']='Received from server invalid chunked HTTP'; 211 $GLOBALS['xmlrpcerr']['server_cannot_decompress']=106; 212 $GLOBALS['xmlrpcstr']['server_cannot_decompress']='Received from client compressed HTTP request and cannot decompress'; 213 $GLOBALS['xmlrpcerr']['server_decompress_fail']=107; 214 $GLOBALS['xmlrpcstr']['server_decompress_fail']='Received from client invalid compressed HTTP request'; 215 216 // The charset encoding used by the server for received messages and 217 // by the client for received responses when received charset cannot be determined 218 // or is not supported 219 $GLOBALS['xmlrpc_defencoding']='UTF-8'; 220 221 // The encoding used internally by PHP. 222 // String values received as xml will be converted to this, and php strings will be converted to xml 223 // as if having been coded with this 224 $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1'; 225 226 $GLOBALS['xmlrpcName']='XML-RPC for PHP'; 227 $GLOBALS['xmlrpcVersion']='2.2'; 228 229 // let user errors start at 800 230 $GLOBALS['xmlrpcerruser']=800; 231 // let XML parse errors start at 100 232 $GLOBALS['xmlrpcerrxml']=100; 233 234 // formulate backslashes for escaping regexp 235 // Not in use anymore since 2.0. Shall we remove it? 236 /// @deprecated 237 $GLOBALS['xmlrpc_backslash']=chr(92).chr(92); 238 239 // set to TRUE to enable correct decoding of <NIL/> values 240 $GLOBALS['xmlrpc_null_extension']=false; 241 242 // used to store state during parsing 243 // quick explanation of components: 244 // ac - used to accumulate values 245 // isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1) 246 // isf_reason - used for storing xmlrpcresp fault string 247 // lv - used to indicate "looking for a value": implements 248 // the logic to allow values with no types to be strings 249 // params - used to store parameters in method calls 250 // method - used to store method name 251 // stack - array with genealogy of xml elements names: 252 // used to validate nesting of xmlrpc elements 253 $GLOBALS['_xh']=null; 254 255 /** 256 * Convert a string to the correct XML representation in a target charset 257 * To help correct communication of non-ascii chars inside strings, regardless 258 * of the charset used when sending requests, parsing them, sending responses 259 * and parsing responses, an option is to convert all non-ascii chars present in the message 260 * into their equivalent 'charset entity'. Charset entities enumerated this way 261 * are independent of the charset encoding used to transmit them, and all XML 262 * parsers are bound to understand them. 263 * Note that in the std case we are not sending a charset encoding mime type 264 * along with http headers, so we are bound by RFC 3023 to emit strict us-ascii. 265 * 266 * @todo do a bit of basic benchmarking (strtr vs. str_replace) 267 * @todo make usage of iconv() or recode_string() or mb_string() where available 268 */ 269 function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='') 270 { 271 if ($src_encoding == '') 272 { 273 // lame, but we know no better... 274 $src_encoding = $GLOBALS['xmlrpc_internalencoding']; 275 } 276 277 switch(strtoupper($src_encoding.'_'.$dest_encoding)) 278 { 279 case 'ISO-8859-1_': 280 case 'ISO-8859-1_US-ASCII': 281 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 282 $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data); 283 break; 284 case 'ISO-8859-1_UTF-8': 285 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 286 $escaped_data = utf8_encode($escaped_data); 287 break; 288 case 'ISO-8859-1_ISO-8859-1': 289 case 'US-ASCII_US-ASCII': 290 case 'US-ASCII_UTF-8': 291 case 'US-ASCII_': 292 case 'US-ASCII_ISO-8859-1': 293 case 'UTF-8_UTF-8': 294 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 295 break; 296 case 'UTF-8_': 297 case 'UTF-8_US-ASCII': 298 case 'UTF-8_ISO-8859-1': 299 // NB: this will choke on invalid UTF-8, going most likely beyond EOF 300 $escaped_data = ''; 301 // be kind to users creating string xmlrpcvals out of different php types 302 $data = (string) $data; 303 $ns = strlen ($data); 304 for ($nn = 0; $nn < $ns; $nn++) 305 { 306 $ch = $data[$nn]; 307 $ii = ord($ch); 308 //1 7 0bbbbbbb (127) 309 if ($ii < 128) 310 { 311 /// @todo shall we replace this with a (supposedly) faster str_replace? 312 switch($ii){ 313 case 34: 314 $escaped_data .= '"'; 315 break; 316 case 38: 317 $escaped_data .= '&'; 318 break; 319 case 39: 320 $escaped_data .= '''; 321 break; 322 case 60: 323 $escaped_data .= '<'; 324 break; 325 case 62: 326 $escaped_data .= '>'; 327 break; 328 default: 329 $escaped_data .= $ch; 330 } // switch 331 } 332 //2 11 110bbbbb 10bbbbbb (2047) 333 else if ($ii>>5 == 6) 334 { 335 $b1 = ($ii & 31); 336 $ii = ord($data[$nn+1]); 337 $b2 = ($ii & 63); 338 $ii = ($b1 * 64) + $b2; 339 $ent = sprintf ('&#%d;', $ii); 340 $escaped_data .= $ent; 341 $nn += 1; 342 } 343 //3 16 1110bbbb 10bbbbbb 10bbbbbb 344 else if ($ii>>4 == 14) 345 { 346 $b1 = ($ii & 31); 347 $ii = ord($data[$nn+1]); 348 $b2 = ($ii & 63); 349 $ii = ord($data[$nn+2]); 350 $b3 = ($ii & 63); 351 $ii = ((($b1 * 64) + $b2) * 64) + $b3; 352 $ent = sprintf ('&#%d;', $ii); 353 $escaped_data .= $ent; 354 $nn += 2; 355 } 356 //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb 357 else if ($ii>>3 == 30) 358 { 359 $b1 = ($ii & 31); 360 $ii = ord($data[$nn+1]); 361 $b2 = ($ii & 63); 362 $ii = ord($data[$nn+2]); 363 $b3 = ($ii & 63); 364 $ii = ord($data[$nn+3]); 365 $b4 = ($ii & 63); 366 $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4; 367 $ent = sprintf ('&#%d;', $ii); 368 $escaped_data .= $ent; 369 $nn += 3; 370 } 371 } 372 break; 373 default: 374 $escaped_data = ''; 375 error_log("Converting from $src_encoding to $dest_encoding: not supported..."); 376 } 377 return $escaped_data; 378 } 379 380 /// xml parser handler function for opening element tags 381 function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false) 382 { 383 // if invalid xmlrpc already detected, skip all processing 384 if ($GLOBALS['_xh']['isf'] < 2) 385 { 386 // check for correct element nesting 387 // top level element can only be of 2 types 388 /// @todo optimization creep: save this check into a bool variable, instead of using count() every time: 389 /// there is only a single top level element in xml anyway 390 if (count($GLOBALS['_xh']['stack']) == 0) 391 { 392 if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && ( 393 $name != 'VALUE' && !$accept_single_vals)) 394 { 395 $GLOBALS['_xh']['isf'] = 2; 396 $GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element'; 397 return; 398 } 399 else 400 { 401 $GLOBALS['_xh']['rt'] = strtolower($name); 402 } 403 } 404 else 405 { 406 // not top level element: see if parent is OK 407 $parent = end($GLOBALS['_xh']['stack']); 408 if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name])) 409 { 410 $GLOBALS['_xh']['isf'] = 2; 411 $GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent"; 412 return; 413 } 414 } 415 416 switch($name) 417 { 418 // optimize for speed switch cases: most common cases first 419 case 'VALUE': 420 /// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element 421 $GLOBALS['_xh']['vt']='value'; // indicator: no value found yet 422 $GLOBALS['_xh']['ac']=''; 423 $GLOBALS['_xh']['lv']=1; 424 $GLOBALS['_xh']['php_class']=null; 425 break; 426 case 'I4': 427 case 'INT': 428 case 'STRING': 429 case 'BOOLEAN': 430 case 'DOUBLE': 431 case 'DATETIME.ISO8601': 432 case 'BASE64': 433 if ($GLOBALS['_xh']['vt']!='value') 434 { 435 //two data elements inside a value: an error occurred! 436 $GLOBALS['_xh']['isf'] = 2; 437 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value"; 438 return; 439 } 440 $GLOBALS['_xh']['ac']=''; // reset the accumulator 441 break; 442 case 'STRUCT': 443 case 'ARRAY': 444 if ($GLOBALS['_xh']['vt']!='value') 445 { 446 //two data elements inside a value: an error occurred! 447 $GLOBALS['_xh']['isf'] = 2; 448 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value"; 449 return; 450 } 451 // create an empty array to hold child values, and push it onto appropriate stack 452 $cur_val = array(); 453 $cur_val['values'] = array(); 454 $cur_val['type'] = $name; 455 // check for out-of-band information to rebuild php objs 456 // and in case it is found, save it 457 if (@isset($attrs['PHP_CLASS'])) 458 { 459 $cur_val['php_class'] = $attrs['PHP_CLASS']; 460 } 461 $GLOBALS['_xh']['valuestack'][] = $cur_val; 462 $GLOBALS['_xh']['vt']='data'; // be prepared for a data element next 463 break; 464 case 'DATA': 465 if ($GLOBALS['_xh']['vt']!='data') 466 { 467 //two data elements inside a value: an error occurred! 468 $GLOBALS['_xh']['isf'] = 2; 469 $GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element"; 470 return; 471 } 472 case 'METHODCALL': 473 case 'METHODRESPONSE': 474 case 'PARAMS': 475 // valid elements that add little to processing 476 break; 477 case 'METHODNAME': 478 case 'NAME': 479 /// @todo we could check for 2 NAME elements inside a MEMBER element 480 $GLOBALS['_xh']['ac']=''; 481 break; 482 case 'FAULT': 483 $GLOBALS['_xh']['isf']=1; 484 break; 485 case 'MEMBER': 486 $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name']=''; // set member name to null, in case we do not find in the xml later on 487 //$GLOBALS['_xh']['ac']=''; 488 // Drop trough intentionally 489 case 'PARAM': 490 // clear value type, so we can check later if no value has been passed for this param/member 491 $GLOBALS['_xh']['vt']=null; 492 break; 493 case 'NIL': 494 if ($GLOBALS['xmlrpc_null_extension']) 495 { 496 if ($GLOBALS['_xh']['vt']!='value') 497 { 498 //two data elements inside a value: an error occurred! 499 $GLOBALS['_xh']['isf'] = 2; 500 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value"; 501 return; 502 } 503 $GLOBALS['_xh']['ac']=''; // reset the accumulator 504 break; 505 } 506 // we do not support the <NIL/> extension, so 507 // drop through intentionally 508 default: 509 /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!! 510 $GLOBALS['_xh']['isf'] = 2; 511 $GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name"; 512 break; 513 } 514 515 // Save current element name to stack, to validate nesting 516 $GLOBALS['_xh']['stack'][] = $name; 517 518 /// @todo optimization creep: move this inside the big switch() above 519 if($name!='VALUE') 520 { 521 $GLOBALS['_xh']['lv']=0; 522 } 523 } 524 } 525 526 /// Used in decoding xml chunks that might represent single xmlrpc values 527 function xmlrpc_se_any($parser, $name, $attrs) 528 { 529 xmlrpc_se($parser, $name, $attrs, true); 530 } 531 532 /// xml parser handler function for close element tags 533 function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true) 534 { 535 if ($GLOBALS['_xh']['isf'] < 2) 536 { 537 // push this element name from stack 538 // NB: if XML validates, correct opening/closing is guaranteed and 539 // we do not have to check for $name == $curr_elem. 540 // we also checked for proper nesting at start of elements... 541 $curr_elem = array_pop($GLOBALS['_xh']['stack']); 542 543 switch($name) 544 { 545 case 'VALUE': 546 // This if() detects if no scalar was inside <VALUE></VALUE> 547 if ($GLOBALS['_xh']['vt']=='value') 548 { 549 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac']; 550 $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString']; 551 } 552 553 if ($rebuild_xmlrpcvals) 554 { 555 // build the xmlrpc val out of the data received, and substitute it 556 $temp =& new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']); 557 // in case we got info about underlying php class, save it 558 // in the object we're rebuilding 559 if (isset($GLOBALS['_xh']['php_class'])) 560 $temp->_php_class = $GLOBALS['_xh']['php_class']; 561 // check if we are inside an array or struct: 562 // if value just built is inside an array, let's move it into array on the stack 563 $vscount = count($GLOBALS['_xh']['valuestack']); 564 if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY') 565 { 566 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp; 567 } 568 else 569 { 570 $GLOBALS['_xh']['value'] = $temp; 571 } 572 } 573 else 574 { 575 /// @todo this needs to treat correctly php-serialized objects, 576 /// since std deserializing is done by php_xmlrpc_decode, 577 /// which we will not be calling... 578 if (isset($GLOBALS['_xh']['php_class'])) 579 { 580 } 581 582 // check if we are inside an array or struct: 583 // if value just built is inside an array, let's move it into array on the stack 584 $vscount = count($GLOBALS['_xh']['valuestack']); 585 if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY') 586 { 587 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value']; 588 } 589 } 590 break; 591 case 'BOOLEAN': 592 case 'I4': 593 case 'INT': 594 case 'STRING': 595 case 'DOUBLE': 596 case 'DATETIME.ISO8601': 597 case 'BASE64': 598 $GLOBALS['_xh']['vt']=strtolower($name); 599 /// @todo: optimization creep - remove the if/elseif cycle below 600 /// since the case() in which we are already did that 601 if ($name=='STRING') 602 { 603 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac']; 604 } 605 elseif ($name=='DATETIME.ISO8601') 606 { 607 if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac'])) 608 { 609 error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']); 610 } 611 $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime']; 612 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac']; 613 } 614 elseif ($name=='BASE64') 615 { 616 /// @todo check for failure of base64 decoding / catch warnings 617 $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']); 618 } 619 elseif ($name=='BOOLEAN') 620 { 621 // special case here: we translate boolean 1 or 0 into PHP 622 // constants true or false. 623 // Strings 'true' and 'false' are accepted, even though the 624 // spec never mentions them (see eg. Blogger api docs) 625 // NB: this simple checks helps a lot sanitizing input, ie no 626 // security problems around here 627 if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0) 628 { 629 $GLOBALS['_xh']['value']=true; 630 } 631 else 632 { 633 // log if receiveing something strange, even though we set the value to false anyway