/ext/php_xmlrpc/xmlrpc.inc
PHP | 2143 lines | 1520 code | 109 blank | 514 comment | 262 complexity | 621dba3017b7a068d0755e2bf315e0df MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1<?php 2// by Edd Dumbill (C) 1999-2002 3// <edd@usefulinc.com> 4// $Id: xmlrpc.inc,v 1.174 2009/03/16 19:36:38 ggiunta Exp $ 5 6// Copyright (c) 1999,2000,2002 Edd Dumbill. 7// All rights reserved. 8// 9// Redistribution and use in source and binary forms, with or without 10// modification, are permitted provided that the following conditions 11// are met: 12// 13// * Redistributions of source code must retain the above copyright 14// notice, this list of conditions and the following disclaimer. 15// 16// * Redistributions in binary form must reproduce the above 17// copyright notice, this list of conditions and the following 18// disclaimer in the documentation and/or other materials provided 19// with the distribution. 20// 21// * Neither the name of the "XML-RPC for PHP" nor the names of its 22// contributors may be used to endorse or promote products derived 23// from this software without specific prior written permission. 24// 25// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 28// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 29// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 30// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 31// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 32// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 34// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 36// OF THE POSSIBILITY OF SUCH DAMAGE. 37 38 if(!function_exists('xml_parser_create')) 39 { 40 // For PHP 4 onward, XML functionality is always compiled-in on windows: 41 // no more need to dl-open it. It might have been compiled out on *nix... 42 if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN')) 43 { 44 dl('xml.so'); 45 } 46 } 47 48 // Try to be backward compat with php < 4.2 (are we not being nice ?) 49 $phpversion = phpversion(); 50 if($phpversion[0] == '4' && $phpversion[2] < 2) 51 { 52 // give an opportunity to user to specify where to include other files from 53 if(!defined('PHP_XMLRPC_COMPAT_DIR')) 54 { 55 define('PHP_XMLRPC_COMPAT_DIR',dirname(__FILE__).'/compat/'); 56 } 57 if($phpversion[2] == '0') 58 { 59 if($phpversion[4] < 6) 60 { 61 include(PHP_XMLRPC_COMPAT_DIR.'is_callable.php'); 62 } 63 include(PHP_XMLRPC_COMPAT_DIR.'is_scalar.php'); 64 include(PHP_XMLRPC_COMPAT_DIR.'array_key_exists.php'); 65 include(PHP_XMLRPC_COMPAT_DIR.'version_compare.php'); 66 } 67 include(PHP_XMLRPC_COMPAT_DIR.'var_export.php'); 68 include(PHP_XMLRPC_COMPAT_DIR.'is_a.php'); 69 } 70 71 // G. Giunta 2005/01/29: declare global these variables, 72 // so that xmlrpc.inc will work even if included from within a function 73 // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used. 74 $GLOBALS['xmlrpcI4']='i4'; 75 $GLOBALS['xmlrpcInt']='int'; 76 $GLOBALS['xmlrpcBoolean']='boolean'; 77 $GLOBALS['xmlrpcDouble']='double'; 78 $GLOBALS['xmlrpcString']='string'; 79 $GLOBALS['xmlrpcDateTime']='dateTime.iso8601'; 80 $GLOBALS['xmlrpcBase64']='base64'; 81 $GLOBALS['xmlrpcArray']='array'; 82 $GLOBALS['xmlrpcStruct']='struct'; 83 $GLOBALS['xmlrpcValue']='undefined'; 84 85 $GLOBALS['xmlrpcTypes']=array( 86 $GLOBALS['xmlrpcI4'] => 1, 87 $GLOBALS['xmlrpcInt'] => 1, 88 $GLOBALS['xmlrpcBoolean'] => 1, 89 $GLOBALS['xmlrpcString'] => 1, 90 $GLOBALS['xmlrpcDouble'] => 1, 91 $GLOBALS['xmlrpcDateTime'] => 1, 92 $GLOBALS['xmlrpcBase64'] => 1, 93 $GLOBALS['xmlrpcArray'] => 2, 94 $GLOBALS['xmlrpcStruct'] => 3 95 ); 96 97 $GLOBALS['xmlrpc_valid_parents'] = array( 98 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'), 99 'BOOLEAN' => array('VALUE'), 100 'I4' => array('VALUE'), 101 'INT' => array('VALUE'), 102 'STRING' => array('VALUE'), 103 'DOUBLE' => array('VALUE'), 104 'DATETIME.ISO8601' => array('VALUE'), 105 'BASE64' => array('VALUE'), 106 'MEMBER' => array('STRUCT'), 107 'NAME' => array('MEMBER'), 108 'DATA' => array('ARRAY'), 109 'ARRAY' => array('VALUE'), 110 'STRUCT' => array('VALUE'), 111 'PARAM' => array('PARAMS'), 112 'METHODNAME' => array('METHODCALL'), 113 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'), 114 'FAULT' => array('METHODRESPONSE'), 115 'NIL' => array('VALUE') // only used when extension activated 116 ); 117 118 // define extra types for supporting NULL (useful for json or <NIL/>) 119 $GLOBALS['xmlrpcNull']='null'; 120 $GLOBALS['xmlrpcTypes']['null']=1; 121 122 // Not in use anymore since 2.0. Shall we remove it? 123 /// @deprecated 124 $GLOBALS['xmlEntities']=array( 125 'amp' => '&', 126 'quot' => '"', 127 'lt' => '<', 128 'gt' => '>', 129 'apos' => "'" 130 ); 131 132 // tables used for transcoding different charsets into us-ascii xml 133 134 $GLOBALS['xml_iso88591_Entities']=array(); 135 $GLOBALS['xml_iso88591_Entities']['in'] = array(); 136 $GLOBALS['xml_iso88591_Entities']['out'] = array(); 137 for ($i = 0; $i < 32; $i++) 138 { 139 $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i); 140 $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';'; 141 } 142 for ($i = 160; $i < 256; $i++) 143 { 144 $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i); 145 $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';'; 146 } 147 148 /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159? 149 /// These will NOT be present in true ISO-8859-1, but will save the unwary 150 /// windows user from sending junk (though no luck when reciving them...) 151 /* 152 $GLOBALS['xml_cp1252_Entities']=array(); 153 for ($i = 128; $i < 160; $i++) 154 { 155 $GLOBALS['xml_cp1252_Entities']['in'][] = chr($i); 156 } 157 $GLOBALS['xml_cp1252_Entities']['out'] = array( 158 '€', '?', '‚', 'ƒ', 159 '„', '…', '†', '‡', 160 'ˆ', '‰', 'Š', '‹', 161 'Œ', '?', 'Ž', '?', 162 '?', '‘', '’', '“', 163 '”', '•', '–', '—', 164 '˜', '™', 'š', '›', 165 'œ', '?', 'ž', 'Ÿ' 166 ); 167 */ 168 169 $GLOBALS['xmlrpcerr'] = array( 170 'unknown_method'=>1, 171 'invalid_return'=>2, 172 'incorrect_params'=>3, 173 'introspect_unknown'=>4, 174 'http_error'=>5, 175 'no_data'=>6, 176 'no_ssl'=>7, 177 'curl_fail'=>8, 178 'invalid_request'=>15, 179 'no_curl'=>16, 180 'server_error'=>17, 181 'multicall_error'=>18, 182 'multicall_notstruct'=>9, 183 'multicall_nomethod'=>10, 184 'multicall_notstring'=>11, 185 'multicall_recursion'=>12, 186 'multicall_noparams'=>13, 187 'multicall_notarray'=>14, 188 189 'cannot_decompress'=>103, 190 'decompress_fail'=>104, 191 'dechunk_fail'=>105, 192 'server_cannot_decompress'=>106, 193 'server_decompress_fail'=>107 194 ); 195 196 $GLOBALS['xmlrpcstr'] = array( 197 'unknown_method'=>'Unknown method', 198 'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload', 199 'incorrect_params'=>'Incorrect parameters passed to method', 200 'introspect_unknown'=>"Can't introspect: method unknown", 201 'http_error'=>"Didn't receive 200 OK from remote server.", 202 'no_data'=>'No data received from server.', 203 'no_ssl'=>'No SSL support compiled in.', 204 'curl_fail'=>'CURL error', 205 'invalid_request'=>'Invalid request payload', 206 'no_curl'=>'No CURL support compiled in.', 207 'server_error'=>'Internal server error', 208 'multicall_error'=>'Received from server invalid multicall response', 209 'multicall_notstruct'=>'system.multicall expected struct', 210 'multicall_nomethod'=>'missing methodName', 211 'multicall_notstring'=>'methodName is not a string', 212 'multicall_recursion'=>'recursive system.multicall forbidden', 213 'multicall_noparams'=>'missing params', 214 'multicall_notarray'=>'params is not an array', 215 216 'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress', 217 'decompress_fail'=>'Received from server invalid compressed HTTP', 218 'dechunk_fail'=>'Received from server invalid chunked HTTP', 219 'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress', 220 'server_decompress_fail'=>'Received from client invalid compressed HTTP request' 221 ); 222 223 // The charset encoding used by the server for received messages and 224 // by the client for received responses when received charset cannot be determined 225 // or is not supported 226 $GLOBALS['xmlrpc_defencoding']='UTF-8'; 227 228 // The encoding used internally by PHP. 229 // String values received as xml will be converted to this, and php strings will be converted to xml 230 // as if having been coded with this 231 $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1'; 232 233 $GLOBALS['xmlrpcName']='XML-RPC for PHP'; 234 $GLOBALS['xmlrpcVersion']='2.2.2'; 235 236 // let user errors start at 800 237 $GLOBALS['xmlrpcerruser']=800; 238 // let XML parse errors start at 100 239 $GLOBALS['xmlrpcerrxml']=100; 240 241 // formulate backslashes for escaping regexp 242 // Not in use anymore since 2.0. Shall we remove it? 243 /// @deprecated 244 $GLOBALS['xmlrpc_backslash']=chr(92).chr(92); 245 246 // set to TRUE to enable correct decoding of <NIL/> values 247 $GLOBALS['xmlrpc_null_extension']=false; 248 249 // used to store state during parsing 250 // quick explanation of components: 251 // ac - used to accumulate values 252 // isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1) 253 // isf_reason - used for storing xmlrpcresp fault string 254 // lv - used to indicate "looking for a value": implements 255 // the logic to allow values with no types to be strings 256 // params - used to store parameters in method calls 257 // method - used to store method name 258 // stack - array with genealogy of xml elements names: 259 // used to validate nesting of xmlrpc elements 260 $GLOBALS['_xh']=null; 261 262 /** 263 * Convert a string to the correct XML representation in a target charset 264 * To help correct communication of non-ascii chars inside strings, regardless 265 * of the charset used when sending requests, parsing them, sending responses 266 * and parsing responses, an option is to convert all non-ascii chars present in the message 267 * into their equivalent 'charset entity'. Charset entities enumerated this way 268 * are independent of the charset encoding used to transmit them, and all XML 269 * parsers are bound to understand them. 270 * Note that in the std case we are not sending a charset encoding mime type 271 * along with http headers, so we are bound by RFC 3023 to emit strict us-ascii. 272 * 273 * @todo do a bit of basic benchmarking (strtr vs. str_replace) 274 * @todo make usage of iconv() or recode_string() or mb_string() where available 275 */ 276 function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='') 277 { 278 if ($src_encoding == '') 279 { 280 // lame, but we know no better... 281 $src_encoding = $GLOBALS['xmlrpc_internalencoding']; 282 } 283 284 switch(strtoupper($src_encoding.'_'.$dest_encoding)) 285 { 286 case 'ISO-8859-1_': 287 case 'ISO-8859-1_US-ASCII': 288 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 289 $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data); 290 break; 291 case 'ISO-8859-1_UTF-8': 292 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 293 $escaped_data = utf8_encode($escaped_data); 294 break; 295 case 'ISO-8859-1_ISO-8859-1': 296 case 'US-ASCII_US-ASCII': 297 case 'US-ASCII_UTF-8': 298 case 'US-ASCII_': 299 case 'US-ASCII_ISO-8859-1': 300 case 'UTF-8_UTF-8': 301 //case 'CP1252_CP1252': 302 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 303 break; 304 case 'UTF-8_': 305 case 'UTF-8_US-ASCII': 306 case 'UTF-8_ISO-8859-1': 307 // NB: this will choke on invalid UTF-8, going most likely beyond EOF 308 $escaped_data = ''; 309 // be kind to users creating string xmlrpcvals out of different php types 310 $data = (string) $data; 311 $ns = strlen ($data); 312 for ($nn = 0; $nn < $ns; $nn++) 313 { 314 $ch = $data[$nn]; 315 $ii = ord($ch); 316 //1 7 0bbbbbbb (127) 317 if ($ii < 128) 318 { 319 /// @todo shall we replace this with a (supposedly) faster str_replace? 320 switch($ii){ 321 case 34: 322 $escaped_data .= '"'; 323 break; 324 case 38: 325 $escaped_data .= '&'; 326 break; 327 case 39: 328 $escaped_data .= '''; 329 break; 330 case 60: 331 $escaped_data .= '<'; 332 break; 333 case 62: 334 $escaped_data .= '>'; 335 break; 336 default: 337 $escaped_data .= $ch; 338 } // switch 339 } 340 //2 11 110bbbbb 10bbbbbb (2047) 341 else if ($ii>>5 == 6) 342 { 343 $b1 = ($ii & 31); 344 $ii = ord($data[$nn+1]); 345 $b2 = ($ii & 63); 346 $ii = ($b1 * 64) + $b2; 347 $ent = sprintf ('&#%d;', $ii); 348 $escaped_data .= $ent; 349 $nn += 1; 350 } 351 //3 16 1110bbbb 10bbbbbb 10bbbbbb 352 else if ($ii>>4 == 14) 353 { 354 $b1 = ($ii & 15); 355 $ii = ord($data[$nn+1]); 356 $b2 = ($ii & 63); 357 $ii = ord($data[$nn+2]); 358 $b3 = ($ii & 63); 359 $ii = ((($b1 * 64) + $b2) * 64) + $b3; 360 $ent = sprintf ('&#%d;', $ii); 361 $escaped_data .= $ent; 362 $nn += 2; 363 } 364 //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb 365 else if ($ii>>3 == 30) 366 { 367 $b1 = ($ii & 7); 368 $ii = ord($data[$nn+1]); 369 $b2 = ($ii & 63); 370 $ii = ord($data[$nn+2]); 371 $b3 = ($ii & 63); 372 $ii = ord($data[$nn+3]); 373 $b4 = ($ii & 63); 374 $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4; 375 $ent = sprintf ('&#%d;', $ii); 376 $escaped_data .= $ent; 377 $nn += 3; 378 } 379 } 380 break; 381/* 382 case 'CP1252_': 383 case 'CP1252_US-ASCII': 384 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 385 $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data); 386 $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data); 387 break; 388 case 'CP1252_UTF-8': 389 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 390 /// @todo we could use real UTF8 chars here instead of xml entities... (note that utf_8 encode all allone will NOT convert them) 391 $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data); 392 $escaped_data = utf8_encode($escaped_data); 393 break; 394 case 'CP1252_ISO-8859-1': 395 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 396 // we might as well replave all funky chars with a '?' here, but we are kind and leave it to the receiving application layer to decide what to do with these weird entities... 397 $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data); 398 break; 399*/ 400 default: 401 $escaped_data = ''; 402 error_log("Converting from $src_encoding to $dest_encoding: not supported..."); 403 } 404 return $escaped_data; 405 } 406 407 /// xml parser handler function for opening element tags 408 function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false) 409 { 410 // if invalid xmlrpc already detected, skip all processing 411 if ($GLOBALS['_xh']['isf'] < 2) 412 { 413 // check for correct element nesting 414 // top level element can only be of 2 types 415 /// @todo optimization creep: save this check into a bool variable, instead of using count() every time: 416 /// there is only a single top level element in xml anyway 417 if (count($GLOBALS['_xh']['stack']) == 0) 418 { 419 if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && ( 420 $name != 'VALUE' && !$accept_single_vals)) 421 { 422 $GLOBALS['_xh']['isf'] = 2; 423 $GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element'; 424 return; 425 } 426 else 427 { 428 $GLOBALS['_xh']['rt'] = strtolower($name); 429 } 430 } 431 else 432 { 433 // not top level element: see if parent is OK 434 $parent = end($GLOBALS['_xh']['stack']); 435 if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name])) 436 { 437 $GLOBALS['_xh']['isf'] = 2; 438 $GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent"; 439 return; 440 } 441 } 442 443 switch($name) 444 { 445 // optimize for speed switch cases: most common cases first 446 case 'VALUE': 447 /// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element 448 $GLOBALS['_xh']['vt']='value'; // indicator: no value found yet 449 $GLOBALS['_xh']['ac']=''; 450 $GLOBALS['_xh']['lv']=1; 451 $GLOBALS['_xh']['php_class']=null; 452 break; 453 case 'I4': 454 case 'INT': 455 case 'STRING': 456 case 'BOOLEAN': 457 case 'DOUBLE': 458 case 'DATETIME.ISO8601': 459 case 'BASE64': 460 if ($GLOBALS['_xh']['vt']!='value') 461 { 462 //two data elements inside a value: an error occurred! 463 $GLOBALS['_xh']['isf'] = 2; 464 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value"; 465 return; 466 } 467 $GLOBALS['_xh']['ac']=''; // reset the accumulator 468 break; 469 case 'STRUCT': 470 case 'ARRAY': 471 if ($GLOBALS['_xh']['vt']!='value') 472 { 473 //two data elements inside a value: an error occurred! 474 $GLOBALS['_xh']['isf'] = 2; 475 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value"; 476 return; 477 } 478 // create an empty array to hold child values, and push it onto appropriate stack 479 $cur_val = array(); 480 $cur_val['values'] = array(); 481 $cur_val['type'] = $name; 482 // check for out-of-band information to rebuild php objs 483 // and in case it is found, save it 484 if (@isset($attrs['PHP_CLASS'])) 485 { 486 $cur_val['php_class'] = $attrs['PHP_CLASS']; 487 } 488 $GLOBALS['_xh']['valuestack'][] = $cur_val; 489 $GLOBALS['_xh']['vt']='data'; // be prepared for a data element next 490 break; 491 case 'DATA': 492 if ($GLOBALS['_xh']['vt']!='data') 493 { 494 //two data elements inside a value: an error occurred! 495 $GLOBALS['_xh']['isf'] = 2; 496 $GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element"; 497 return; 498 } 499 case 'METHODCALL': 500 case 'METHODRESPONSE': 501 case 'PARAMS': 502 // valid elements that add little to processing 503 break; 504 case 'METHODNAME': 505 case 'NAME': 506 /// @todo we could check for 2 NAME elements inside a MEMBER element 507 $GLOBALS['_xh']['ac']=''; 508 break; 509 case 'FAULT': 510 $GLOBALS['_xh']['isf']=1; 511 break; 512 case 'MEMBER': 513 $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 514 //$GLOBALS['_xh']['ac']=''; 515 // Drop trough intentionally 516 case 'PARAM': 517 // clear value type, so we can check later if no value has been passed for this param/member 518 $GLOBALS['_xh']['vt']=null; 519 break; 520 case 'NIL': 521 if ($GLOBALS['xmlrpc_null_extension']) 522 { 523 if ($GLOBALS['_xh']['vt']!='value') 524 { 525 //two data elements inside a value: an error occurred! 526 $GLOBALS['_xh']['isf'] = 2; 527 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value"; 528 return; 529 } 530 $GLOBALS['_xh']['ac']=''; // reset the accumulator 531 break; 532 } 533 // we do not support the <NIL/> extension, so 534 // drop through intentionally 535 default: 536 /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!! 537 $GLOBALS['_xh']['isf'] = 2; 538 $GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name"; 539 break; 540 } 541 542 // Save current element name to stack, to validate nesting 543 $GLOBALS['_xh']['stack'][] = $name; 544 545 /// @todo optimization creep: move this inside the big switch() above 546 if($name!='VALUE') 547 { 548 $GLOBALS['_xh']['lv']=0; 549 } 550 } 551 } 552 553 /// Used in decoding xml chunks that might represent single xmlrpc values 554 function xmlrpc_se_any($parser, $name, $attrs) 555 { 556 xmlrpc_se($parser, $name, $attrs, true); 557 } 558 559 /// xml parser handler function for close element tags 560 function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true) 561 { 562 if ($GLOBALS['_xh']['isf'] < 2) 563 { 564 // push this element name from stack 565 // NB: if XML validates, correct opening/closing is guaranteed and 566 // we do not have to check for $name == $curr_elem. 567 // we also checked for proper nesting at start of elements... 568 $curr_elem = array_pop($GLOBALS['_xh']['stack']); 569 570 switch($name) 571 { 572 case 'VALUE': 573 // This if() detects if no scalar was inside <VALUE></VALUE> 574 if ($GLOBALS['_xh']['vt']=='value') 575 { 576 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac']; 577 $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString']; 578 } 579 580 if ($rebuild_xmlrpcvals) 581 { 582 // build the xmlrpc val out of the data received, and substitute it 583 $temp =& new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']); 584 // in case we got info about underlying php class, save it 585 // in the object we're rebuilding 586 if (isset($GLOBALS['_xh']['php_class'])) 587 $temp->_php_class = $GLOBALS['_xh']['php_class']; 588 // check if we are inside an array or struct: 589 // if value just built is inside an array, let's move it into array on the stack 590 $vscount = count($GLOBALS['_xh']['valuestack']); 591 if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY') 592 { 593 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp; 594 } 595 else 596 { 597 $GLOBALS['_xh']['value'] = $temp; 598 } 599 } 600 else 601 { 602 /// @todo this needs to treat correctly php-serialized objects, 603 /// since std deserializing is done by php_xmlrpc_decode, 604 /// which we will not be calling... 605 if (isset($GLOBALS['_xh']['php_class'])) 606 { 607 } 608 609 // check if we are inside an array or struct: 610 // if value just built is inside an array, let's move it into array on the stack 611 $vscount = count($GLOBALS['_xh']['valuestack']); 612 if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY') 613 { 614 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value']; 615 } 616 } 617 break; 618 case 'BOOLEAN': 619 case 'I4': 620 case 'INT': 621 case 'STRING': 622 case 'DOUBLE': 623 case 'DATETIME.ISO8601': 624 case 'BASE64': 625 $GLOBALS['_xh']['vt']=strtolower($name); 626 /// @todo: optimization creep - remove the if/elseif cycle below 627 /// since the case() in which we are already did that 628 if ($name=='STRING') 629 { 630 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac']; 631 } 632 elseif ($name=='DATETIME.ISO8601') 633 { 634 if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac'])) 635 { 636 error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']); 637 } 638 $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime']; 639 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac']; 640 } 641 elseif ($name=='BASE64') 642 { 643 /// @todo check for failure of base64 decoding / catch warnings 644 $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']); 645 } 646 elseif ($name=='BOOLEAN') 647 { 648 // special case here: we translate boolean 1 or 0 into PHP 649 // constants true or false. 650 // Strings 'true' and 'false' are accepted, even though the 651 // spec never mentions them (see eg. Blogger api docs) 652 // NB: this simple checks helps a lot sanitizing input, ie no 653 // security problems around here 654 if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0) 655 { 656 $GLOBALS['_xh']['value']=true; 657 } 658 else 659 { 660 // log if receiveing something strange, even though we set the value to false anyway 661 if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($GLOBALS['_xh']['ac'], 'false') != 0) 662 error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']); 663 $GLOBALS['_xh']['value']=false; 664 } 665 } 666 elseif ($name=='DOUBLE') 667 { 668 // we have a DOUBLE 669 // we must check that only 0123456789-.<space> are characters here 670 // NOTE: regexp could be much stricter than this... 671 if (!preg_match('/^[+-eE0123456789 \t.]+$/', $GLOBALS['_xh']['ac'])) 672 { 673 /// @todo: find a better way of throwing an error than this! 674 error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']); 675 $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND'; 676 } 677 else 678 { 679 // it's ok, add it on 680 $GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac']; 681 } 682 } 683 else 684 { 685 // we have an I4/INT 686 // we must check that only 0123456789-<space> are characters here 687 if (!preg_match('/^[+-]?[0123456789 \t]+$/', $GLOBALS['_xh']['ac'])) 688 { 689 /// @todo find a better way of throwing an error than this! 690 error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']); 691 $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND'; 692 } 693 else 694 { 695 // it's ok, add it on 696 $GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac']; 697 } 698 } 699 //$GLOBALS['_xh']['ac']=''; // is this necessary? 700 $GLOBALS['_xh']['lv']=3; // indicate we've found a value 701 break; 702 case 'NAME': 703 $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac']; 704 break; 705 case 'MEMBER': 706 //$GLOBALS['_xh']['ac']=''; // is this necessary? 707 // add to array in the stack the last element built, 708 // unless no VALUE was found 709 if ($GLOBALS['_xh']['vt']) 710 { 711 $vscount = count($GLOBALS['_xh']['valuestack']); 712 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value']; 713 } else 714 error_log('XML-RPC: missing VALUE inside STRUCT in received xml'); 715 break; 716 case 'DATA': 717 //$GLOBALS['_xh']['ac']=''; // is this necessary? 718 $GLOBALS['_xh']['vt']=null; // reset this to check for 2 data elements in a row - even if they're empty 719 break; 720 case 'STRUCT': 721 case 'ARRAY': 722 // fetch out of stack array of values, and promote it to current value 723 $curr_val = array_pop($GLOBALS['_xh']['valuestack']); 724 $GLOBALS['_xh']['value'] = $curr_val['values']; 725 $GLOBALS['_xh']['vt']=strtolower($name); 726 if (isset($curr_val['php_class'])) 727 { 728 $GLOBALS['_xh']['php_class'] = $curr_val['php_class']; 729 } 730 break; 731 case 'PARAM': 732 // add to array of params the current value, 733 // unless no VALUE was found 734 if ($GLOBALS['_xh']['vt']) 735 { 736 $GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value']; 737 $GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt']; 738 } 739 else 740 error_log('XML-RPC: missing VALUE inside PARAM in received xml'); 741 break; 742 case 'METHODNAME': 743 $GLOBALS['_xh']['method']=preg_replace('/^[\n\r\t ]+/', '', $GLOBALS['_xh']['ac']); 744 break; 745 case 'NIL': 746 if ($GLOBALS['xmlrpc_null_extension']) 747 { 748 $GLOBALS['_xh']['vt']='null'; 749 $GLOBALS['_xh']['value']=null; 750 $GLOBALS['_xh']['lv']=3; 751 break; 752 } 753 // drop through intentionally if nil extension not enabled 754 case 'PARAMS': 755 case 'FAULT': 756 case 'METHODCALL': 757 case 'METHORESPONSE': 758 break; 759 default: 760 // End of INVALID ELEMENT! 761 // shall we add an assert here for unreachable code??? 762 break; 763 } 764 } 765 } 766 767 /// Used in decoding xmlrpc requests/responses without rebuilding xmlrpc values 768 function xmlrpc_ee_fast($parser, $name) 769 { 770 xmlrpc_ee($parser, $name, false); 771 } 772 773 /// xml parser handler function for character data 774 function xmlrpc_cd($parser, $data) 775 { 776 // skip processing if xml fault already detected 777 if ($GLOBALS['_xh']['isf'] < 2) 778 { 779 // "lookforvalue==3" means that we've found an entire value 780 // and should discard any further character data 781 if($GLOBALS['_xh']['lv']!=3) 782 { 783 // G. Giunta 2006-08-23: useless change of 'lv' from 1 to 2 784 //if($GLOBALS['_xh']['lv']==1) 785 //{ 786 // if we've found text and we're just in a <value> then 787 // say we've found a value 788 //$GLOBALS['_xh']['lv']=2; 789 //} 790 // we always initialize the accumulator before starting parsing, anyway... 791 //if(!@isset($GLOBALS['_xh']['ac'])) 792 //{ 793 // $GLOBALS['_xh']['ac'] = ''; 794 //} 795 $GLOBALS['_xh']['ac'].=$data; 796 } 797 } 798 } 799 800 /// xml parser handler function for 'other stuff', ie. not char data or 801 /// element start/end tag. In fact it only gets called on unknown entities... 802 function xmlrpc_dh($parser, $data) 803 { 804 // skip processing if xml fault already detected 805 if ($GLOBALS['_xh']['isf'] < 2) 806 { 807 if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';') 808 { 809 // G. Giunta 2006-08-25: useless change of 'lv' from 1 to 2 810 //if($GLOBALS['_xh']['lv']==1) 811 //{ 812 // $GLOBALS['_xh']['lv']=2; 813 //} 814 $GLOBALS['_xh']['ac'].=$data; 815 } 816 } 817 return true; 818 } 819 820 class xmlrpc_client 821 { 822 var $path; 823 var $server; 824 var $port=0; 825 var $method='http'; 826 var $errno; 827 var $errstr; 828 var $debug=0; 829 var $username=''; 830 var $password=''; 831 var $authtype=1; 832 var $cert=''; 833 var $certpass=''; 834 var $cacert=''; 835 var $cacertdir=''; 836 var $key=''; 837 var $keypass=''; 838 var $verifypeer=true; 839 var $verifyhost=1; 840 var $no_multicall=false; 841 var $proxy=''; 842 var $proxyport=0; 843 var $proxy_user=''; 844 var $proxy_pass=''; 845 var $proxy_authtype=1; 846 var $cookies=array(); 847 /** 848 * List of http compression methods accepted by the client for responses. 849 * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib 850 * 851 * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since 852 * in those cases it will be up to CURL to decide the compression methods 853 * it supports. You might check for the presence of 'zlib' in the output of 854 * curl_version() to determine wheter compression is supported or not 855 */ 856 var $accepted_compression = array(); 857 /** 858 * Name of compression scheme to be used for sending requests. 859 * Either null, gzip or deflate 860 */ 861 var $request_compression = ''; 862 /** 863 * CURL handle: used for keep-alive connections (PHP 4.3.8 up, see: 864 * http://curl.haxx.se/docs/faq.html#7.3) 865 */ 866 var $xmlrpc_curl_handle = null; 867 /// Wheter to use persistent connections for http 1.1 and https 868 var $keepalive = false; 869 /// Charset encodings that can be decoded without problems by the client 870 var $accepted_charset_encodings = array(); 871 /// Charset encoding to be used in serializing request. NULL = use ASCII 872 var $request_charset_encoding = ''; 873 /** 874 * Decides the content of xmlrpcresp objects returned by calls to send() 875 * valid strings are 'xmlrpcvals', 'phpvals' or 'xml' 876 */ 877 var $return_type = 'xmlrpcvals'; 878 879 /** 880 * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php 881 * @param string $server the server name / ip address 882 * @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used 883 * @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed 884 */ 885 function xmlrpc_client($path, $server='', $port='', $method='') 886 { 887 // allow user to specify all params in $path 888 if($server == '' and $port == '' and $method == '') 889 { 890 $parts = parse_url($path); 891 $server = $parts['host']; 892 $path = isset($parts['path']) ? $parts['path'] : ''; 893 if(isset($parts['query'])) 894 { 895 $path .= '?'.$parts['query']; 896 } 897 if(isset($parts['fragment'])) 898 { 899 $path .= '#'.$parts['fragment']; 900 } 901 if(isset($parts['port'])) 902 { 903 $port = $parts['port']; 904 } 905 if(isset($parts['scheme'])) 906 { 907 $method = $parts['scheme']; 908 } 909 if(isset($parts['user'])) 910 { 911 $this->username = $parts['user']; 912 } 913 if(isset($parts['pass'])) 914 { 915 $this->password = $parts['pass']; 916 } 917 } 918 if($path == '' || $path[0] != '/') 919 { 920 $this->path='/'.$path; 921 } 922 else 923 { 924 $this->path=$path; 925 } 926 $this->server=$server; 927 if($port != '') 928 { 929 $this->port=$port; 930 } 931 if($method != '') 932 { 933 $this->method=$method; 934 } 935 936 // if ZLIB is enabled, let the client by default accept compressed responses 937 if(function_exists('gzinflate') || ( 938 function_exists('curl_init') && (($info = curl_version()) && 939 ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version']))) 940 )) 941 { 942 $this->accepted_compression = array('gzip', 'deflate'); 943 } 944 945 // keepalives: enabled by default ONLY for PHP >= 4.3.8 946 // (see http://curl.haxx.se/docs/faq.html#7.3) 947 if(version_compare(phpversion(), '4.3.8') >= 0) 948 { 949 $this->keepalive = true; 950 } 951 952 // by default the xml parser can support these 3 charset encodings 953 $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII'); 954 } 955 956 /** 957 * Enables/disables the echoing to screen of the xmlrpc responses received 958 * @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, before received response) 959 * @access public 960 */ 961 function setDebug($in) 962 { 963 $this->debug=$in; 964 } 965 966 /** 967 * Add some http BASIC AUTH credentials, used by the client to authenticate 968 * @param string $u username 969 * @param string $p password 970 * @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth) 971 * @access public 972 */ 973 function setCredentials($u, $p, $t=1) 974 { 975 $this->username=$u; 976 $this->password=$p; 977 $this->authtype=$t; 978 } 979 980 /** 981 * Add a client-side https certificate 982 * @param string $cert 983 * @param string $certpass 984 * @access public 985 */ 986 function setCertificate($cert, $certpass) 987 { 988 $this->cert = $cert; 989 $this->certpass = $certpass; 990 } 991 992 /** 993 * Add a CA certificate to verify server with (see man page about 994 * CURLOPT_CAINFO for more details 995 * @param string $cacert certificate file name (or dir holding certificates) 996 * @param bool $is_dir set to true to indicate cacert is a dir. defaults to false 997 * @access public 998 */ 999 function setCaCertificate($cacert, $is_dir=false) 1000 { 1001 if ($is_dir) 1002 { 1003 $this->cacertdir = $cacert; 1004 } 1005 else 1006 { 1007 $this->cacert = $cacert; 1008 } 1009 } 1010 1011 /** 1012 * Set attributes for SSL communication: private SSL key 1013 * NB: does not work in older php/curl installs 1014 * Thanks to Daniel Convissor 1015 * @param string $key The name of a file containing a private SSL key 1016 * @param string $keypass The secret password needed to use the private SSL key 1017 * @access public 1018 */ 1019 function setKey($key, $keypass) 1020 { 1021 $this->key = $key; 1022 $this->keypass = $keypass; 1023 } 1024 1025 /** 1026 * Set attributes for SSL communication: verify server certificate 1027 * @param bool $i enable/disable verification of peer certificate 1028 * @access public 1029 */ 1030 function setSSLVerifyPeer($i) 1031 { 1032 $this->verifypeer = $i; 1033 } 1034 1035 /** 1036 * Set attributes for SSL communication: verify match of server cert w. hostname 1037 * @param int $i 1038 * @access public 1039 */ 1040 function setSSLVerifyHost($i) 1041 { 1042 $this->verifyhost = $i; 1043 } 1044 1045 /** 1046 * Set proxy info 1047 * @param string $proxyhost 1048 * @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS 1049 * @param string $proxyusername Leave blank if proxy has public access 1050 * @param string $proxypassword Leave blank if proxy has public access 1051 * @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy 1052 * @access public 1053 */ 1054 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1) 1055 { 1056 $this->proxy = $proxyhost; 1057 $this->proxyport = $proxyport; 1058 $this->proxy_user = $proxyusername; 1059 $this->proxy_pass = $proxypassword; 1060 $this->proxy_authtype = $proxyauthtype; 1061 } 1062 1063 /** 1064 * Enables/disables reception of compressed xmlrpc responses. 1065 * Note that enabling reception of compressed responses merely adds some standard 1066 * http headers to xmlrpc requests. It is up to the xmlrpc server to return 1067 * compressed responses when receiving such requests. 1068 * @param string $compmethod either 'gzip', 'deflate', 'any' or '' 1069 * @access public 1070 */ 1071 function setAcceptedCompression($compmethod) 1072 { 1073 if ($compmethod == 'any') 1074 $this->accepted_compression = array('gzip', 'deflate'); 1075 else 1076 $this->accepted_compression = array($compmethod); 1077 } 1078 1079 /** 1080 * Enables/disables http compression of xmlrpc request. 1081 * Take care when sending compressed requests: servers might not support them 1082 * (and automatic fallback to uncompressed requests is not yet implemented) 1083 * @param string $compmethod either 'gzip', 'deflate' or '' 1084 * @access public 1085 */ 1086 function setRequestCompression($compmethod) 1087 { 1088 $this->request_compression = $compmethod; 1089 } 1090 1091 /** 1092 * Adds a cookie to list of cookies that will be sent to server. 1093 * NB: setting any param but name and value will turn the cookie into a 'version 1' cookie: 1094 * do not do it unless you know what you are doing 1095 * @param string $name 1096 * @param string $value 1097 * @param string $path 1098 * @param string $domain 1099 * @param int $port 1100 * @access public 1101 * 1102 * @todo check correctness of urlencoding cookie value (copied from php way of doing it...) 1103 */ 1104 function setCookie($name, $value='', $path='', $domain='', $port=null) 1105 { 1106 $this->cookies[$name]['value'] = urlencode($value); 1107 if ($path || $domain || $port) 1108 { 1109 $this->cookies[$name]['path'] = $path; 1110 $this->cookies[$name]['domain'] = $domain; 1111 $this->cookies[$name]['port'] = $port; 1112 $this->cookies[$name]['version'] = 1; 1113 } 1114 else 1115 { 1116 $this->cookies[$name]['version'] = 0; 1117 } 1118 } 1119 1120 /** 1121 * Send an xmlrpc request 1122 * @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request 1123 * @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply 1124 * @param string $method if left unspecified, the http protocol chosen during creation of the object will be used 1125 * @return xmlrpcresp 1126 * @access public 1127 */ 1128 function& send($msg, $timeout=0, $method='') 1129 { 1130 // if user deos not specify http protocol, use native method of this client 1131 // (i.e. method set during call to constructor) 1132 if($method == '') 1133 { 1134 $method = $this->method; 1135 } 1136 1137 if(is_array($msg)) 1138 { 1139 // $msg is an array of xmlrpcmsg's 1140 $r = $this->multicall($msg, $timeout, $method); 1141 return $r; 1142 } 1143 elseif(is_string($msg)) 1144 { 1145 $n =& new xmlrpcmsg(''); 1146 $n->payload = $msg; 1147 $msg = $n; 1148 } 1149 1150 // where msg is an xmlrpcmsg 1151 $msg->debug=$this->debug; 1152 1153 if($method == 'https') 1154 { 1155 $r =& $this->sendPayloadHTTPS( 1156 $msg, 1157 $this->server, 1158 $this->port, 1159 $timeout, 1160 $this->username, 1161 $this->password, 1162 $this->authtype, 1163 $this->cert, 1164 $this->certpass, 1165 $this->cacert, 1166 $this->cacertdir, 1167 $this->proxy, 1168 $this->proxyport, 1169 $this->proxy_user, 1170 $this->proxy_pass, 1171 $this->proxy_authtype, 1172 $this->keepalive, 1173 $this->key, 1174 $this->keypass 1175 ); 1176 } 1177 elseif($method == 'http11') 1178 { 1179 $r =& $this->sendPayloadCURL( 1180 $msg, 1181 $this->server, 1182 $this->port, 1183 $timeout, 1184 $this->username, 1185 $this->password, 1186 $this->authtype, 1187 null, 1188 null, 1189 null, 1190 null, 1191 $this->proxy, 1192 $this->proxyport, 1193 $this->proxy_user, 1194 $this->proxy_pass, 1195 $this->proxy_authtype, 1196 'http', 1197 $this->keepalive 1198 ); 1199 } 1200 else 1201 { 1202 $r =& $this->sendPayloadHTTP10( 1203 $msg, 1204 $this->server, 1205 $this->port, 1206 $timeout, 1207 $this->username, 1208 $this->password, 1209 $this->authtype, 1210 $this->proxy, 1211 $this->proxyport, 1212 $this->proxy_user, 1213 $this->proxy_pass, 1214 $this->proxy_authtype 1215 ); 1216 } 1217 1218 return $r; 1219 } 1220 1221 /** 1222 * @access private 1223 */ 1224 function &sendPayloadHTTP10($msg, $server, $port, $timeout=0, 1225 $username='', $password='', $authtype=1, $proxyhost='', 1226 $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1) 1227 { 1228 if($port==0) 1229 { 1230 $port=80; 1231 } 1232 1233 // Only create the payload if it was not created previously 1234 if(empty($msg->payload)) 1235 { 1236 $msg->createPayload($this->request_charset_encoding); 1237 } 1238 1239 $payload = $msg->payload; 1240 // Deflate request body and set appropriate request headers 1241 if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate')) 1242 { 1243 if($this->request_compression == 'gzip') 1244 { 1245 $a = @gzencode($payload); 1246 if($a) 1247 { 1248 $payload = $a; 1249 $encoding_hdr = "Content-Encoding: gzip\r\n"; 1250 } 1251 } 1252 else 1253 { 1254 $a = @gzcompress($payload); 1255 if($a) 1256 { 1257 $payload = $a; 1258 $encoding_hdr = "Content-Encoding: deflate\r\n"; 1259 } 1260 } 1261 } 1262 else 1263 { 1264 $encoding_hdr = ''; 1265 } 1266 1267 // thanks to Grant Rauscher <grant7@firstworld.net> for this 1268 $credentials=''; 1269 if($username!='') 1270 { 1271 $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n"; 1272 if ($authtype != 1) 1273 { 1274 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported with HTTP 1.0'); 1275 } 1276 } 1277 1278 $accepted_encoding = ''; 1279 if(is_array($this->accepted_compression) && count($this->accepted_compression)) 1280 { 1281 $accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n"; 1282 } 1283 1284 $proxy_credentials = ''; 1285 if($proxyhost) 1286 { 1287 if($proxyport == 0) 1288 { 1289 $proxyport = 8080; 1290 } 1291 $connectserver = $proxyhost; 1292 $connectport = $proxyport; 1293 $uri = 'http://'.$server.':'.$port.$this->path; 1294 if($proxyusername != '') 1295 { 1296 if ($proxyauthtype != 1) 1297 { 1298 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported with HTTP 1.0'); 1299 } 1300 $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n"; 1301 } 1302 } 1303 else 1304 { 1305 $connectserver = $server; 1306 $connectport = $port; 1307 $uri = $this->path; 1308 } 1309 1310 // Cookie generation, as per rfc2965 (version 1 cookies) or 1311 // netscape's rules (version 0 cookies) 1312 $cookieheader=''; 1313 if (count($this->cookies)) 1314 { 1315 $version = ''; 1316 foreach ($this->cookies as $name => $cookie) 1317 { 1318 if ($cookie['version']) 1319 { 1320 $version = ' $Version="' . $cookie['version'] . '";'; 1321 $cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";'; 1322 if ($cookie['path']) 1323 $cookieheader .= ' $Path="' . $cookie['path'] . '";'; 1324 if ($cookie['domain']) 1325 $cookieheader .= ' $Domain="' . $cookie['domain'] . '";'; 1326 if ($cookie['port']) 1327 $cookieheader .= ' $Port="' . $cookie['port'] . '";'; 1328 } 1329 else 1330 { 1331 $cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";"; 1332 } 1333 } 1334 $cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n"; 1335 } 1336 1337 $op= 'POST ' . $uri. " HTTP/1.0\r\n" . 1338 'User-Agent: ' . $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'] . "\r\n" . 1339 'Host: '. $server . ':' . $port . "\r\n" . 1340 $credentials . 1341 $proxy_credentials . 1342 $accepted_encoding . 1343 $encoding_hdr . 1344 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" . 1345 $cookieheader . 1346 'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " . 1347 strlen($payload) . "\r\n\r\n" . 1348 $payload; 1349 1350 if($this->debug > 1) 1351 { 1352 print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>"; 1353 // let the client see this now in case http times out... 1354 flush(); 1355 } 1356 1357 if($timeout>0) 1358 { 1359 $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout); 1360 } 1361 else 1362 { 1363 $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr); 1364 } 1365 if($fp) 1366 { 1367 if($timeout>0 && function_exists('stream_set_timeout')) 1368 { 1369 stream_set_timeout($fp, $timeout); 1370 } 1371 } 1372 else 1373 { 1374 $this->errstr='Connect error: '.$this->errstr; 1375 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')'); 1376 return $r; 1377 } 1378 1379 if(!fputs($fp, $op, strlen($op))) 1380 { 1381 fclose($fp); 1382 $this->errstr='Write error'; 1383 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr); 1384 return $r; 1385 } 1386 else 1387 { 1388 // reset errno and errstr on succesful socket connection 1389 $this->errstr = ''; 1390 } 1391 // G. Giunta 2005/10/24: close socket before parsing. 1392 // should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects) 1393 $ipd=''; 1394 do 1395 { 1396 // shall we check for $data === FALSE? 1397 // as per the manual, it signals an error 1398 $ipd.=fread($fp, 32768); 1399 } while(!feof($fp)); 1400 fclose($fp); 1401 $r =& $msg->parseResponse($ipd, false, $this->return_type); 1402 return $r; 1403 1404 } 1405 1406 /** 1407 * @access private 1408 */ 1409 function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='', 1410 $password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='', 1411 $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, 1412 $keepalive=false, $key='', $keypass='') 1413 { 1414 $r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username, 1415 $password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport, 1416 $proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass); 1417 return $r; 1418 } 1419 1420 /** 1421 * Contributed by Justin Miller <justin@voxel.net> 1422 * Requires curl to be built into PHP 1423 * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers! 1424 * @access private 1425 */ 1426 function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='', 1427 $password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='', 1428 $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https', 1429 $keepalive=false, $key='', $keypass='') 1430 { 1431 if(!function_exists('curl_init')) 1432 { 1433 $this->errstr='CURL unavailable on this install'; 1434 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']); 1435 return $r; 1436 } 1437 if($method == 'https') 1438 { 1439 if(($info = curl_version()) && 1440 ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version'])))) 1441 { 1442 $this->errstr='SSL unavailable on this install'; 1443 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']); 1444 return $r; 1445 } 1446 } 1447 1448 if($port == 0) 1449 { 1450 if($method == 'http') 1451 { 1452 $port = 80; 1453 } 1454 else 1455 { 1456 $port = 443; 1457 } 1458 } 1459 1460 // Only create the payload if it was not created previously 1461 if(empty($msg->payload)) 1462 { 1463 $msg->createPayload($this->request_charset_encoding); 1464 } 1465 1466 // Deflate request body and set appropriate request headers 1467 $payload = $msg->payload; 1468 if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate')) 1469 { 1470 if($this->request_compression == 'gzip') 1471 { 1472 $a = @gzencode($payload); 1473 if($a) 1474 { 1475 $payload = $a; 1476 $encoding_hdr = 'Content-Encoding: gzip'; 1477 } 1478 } 1479 else 1480 { 1481 $a = @gzcompress($payload); 1482 if($a) 1483 { 1484 $payload = $a; 1485 $encoding_hdr = 'Content-Encoding: deflate'; 1486 } 1487 } 1488 } 1489 else 1490 { 1491 $encoding_hdr = ''; 1492 } 1493 1494 if($this->debug > 1) 1495 { 1496 print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>"; 1497 // let the client see this now in case http times out... 1498 flush(); 1499 } 1500 1501 if(!$keepalive || !$this->xmlrpc_curl_handle) 1502 { 1503 $curl = curl_init($method . '://' . $server . ':' . $port . $this->path); 1504 if($keepalive) 1505 { 1506 $this->xmlrpc_curl_handle = $curl; 1507 } 1508 } 1509 else 1510 { 1511 $curl = $this->xmlrpc_curl_handle; 1512 } 1513 1514 // results into variable 1515 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 1516 1517 if($this->debug) 1518 { 1519 curl_setopt($curl, CURLOPT_VERBOSE, 1); 1520 } 1521 curl_setopt($curl, CURLOPT_USERAGENT, $GLOBALS['xmlrpcName'].' '.$GLOBALS['xmlrpcVersion']); 1522 // required for XMLRPC: post the data 1523 curl_setopt($curl, CURLOPT_POST, 1); 1524 // the data 1525 curl_setopt($curl, CURLOPT_POSTFIELDS, $payload); 1526 1527 // return the header too 1528 curl_setopt($curl, CURLOPT_HEADER, 1); 1529 1530 // will only work with PHP >= 5.0 1531 // NB: if we set an empty string, CURL will add http header indicating 1532 // ALL methods it is supporting. This is possibly a better option than 1533 // letting the user tell what curl can / cannot do... 1534 if(is_array($this->accepted_compression) && count($this->accepted_compression)) 1535 { 1536 //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression)); 1537 // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?) 1538 if (count($this->accepted_compression) == 1) 1539 { 1540 curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]); 1541 } 1542 else 1543 curl_setopt($curl, CURLOPT_ENCODING, ''); 1544 } 1545 // extra headers 1546 $headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings)); 1547 // if no keepalive is wanted, let the server know it in advance 1548 if(!$keepalive) 1549 { 1550 $headers[] = 'Connection: close'; 1551 } 1552 // request compression header 1553 if($encoding_hdr) 1554 { 1555 $headers[] = $encoding_hdr; 1556 } 1557 1558 curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); 1559 // timeout is borked 1560 if($timeout) 1561 { 1562 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1); 1563 } 1564 1565 if($username && $password) 1566 { 1567 curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password); 1568 if (defined('CURLOPT_HTTPAUTH')) 1569 { 1570 curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype); 1571 } 1572 else if ($authtype != 1) 1573 { 1574 error_log('XML-RPC: xmlrpc_client::send: warn…
Large files files are truncated, but you can click here to view the full file