2 // by Edd Dumbill (C) 1999-2002
4 // $Id: xmlrpc.inc,v 1.4 2005-07-08 05:36:12 rurban Exp $
6 // Copyright (c) 1999,2000,2002 Edd Dumbill.
7 // All rights reserved.
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions
13 // * Redistributions of source code must retain the above copyright
14 // notice, this list of conditions and the following disclaimer.
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.
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.
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.
38 if (!function_exists('xml_parser_create')) {
39 // Win 32 fix. From: 'Leo West' <lwest@imaginet.fr>
48 // G. Giunta 2005/01/29: declare global these variables,
49 // so that xmlrpc.inc will work even if included from within a function
50 // NB: it will give warnings in PHP3, so we comment it out
51 // Milosch: Next round, maybe we should explicitly request these via $GLOBALS where used.
52 if (phpversion() >= '4') {
57 global $xmlrpcDateTime;
66 global $xmlrpc_defencoding;
67 global $xmlrpc_internalencoding;
69 global $xmlrpcVersion;
70 global $xmlrpcerruser;
72 global $xmlrpc_backslash;
77 $xmlrpcBoolean='boolean';
78 $xmlrpcDouble='double';
79 $xmlrpcString='string';
80 $xmlrpcDateTime='dateTime.iso8601';
81 $xmlrpcBase64='base64';
83 $xmlrpcStruct='struct';
105 $xmlrpcerr['unknown_method']=1;
106 $xmlrpcstr['unknown_method']='Unknown method';
107 $xmlrpcerr['invalid_return']=2;
108 $xmlrpcstr['invalid_return']='Invalid return payload: enable debugging to examine incoming payload';
109 $xmlrpcerr['incorrect_params']=3;
110 $xmlrpcstr['incorrect_params']='Incorrect parameters passed to method';
111 $xmlrpcerr['introspect_unknown']=4;
112 $xmlrpcstr['introspect_unknown']="Can't introspect: method unknown";
113 $xmlrpcerr['http_error']=5;
114 $xmlrpcstr['http_error']="Didn't receive 200 OK from remote server.";
115 $xmlrpcerr['no_data']=6;
116 $xmlrpcstr['no_data']='No data received from server.';
117 $xmlrpcerr['no_ssl']=7;
118 $xmlrpcstr['no_ssl']='No SSL support compiled in.';
119 $xmlrpcerr['curl_fail']=8;
120 $xmlrpcstr['curl_fail']='CURL error';
123 $xmlrpcerr['multicall_notstruct'] = 9;
124 $xmlrpcstr['multicall_notstruct'] = 'system.multicall expected struct';
125 $xmlrpcerr['multicall_nomethod'] = 10;
126 $xmlrpcstr['multicall_nomethod'] = 'missing methodName';
127 $xmlrpcerr['multicall_notstring'] = 11;
128 $xmlrpcstr['multicall_notstring'] = 'methodName is not a string';
129 $xmlrpcerr['multicall_recursion'] = 12;
130 $xmlrpcstr['multicall_recursion'] = 'recursive system.multicall forbidden';
131 $xmlrpcerr['multicall_noparams'] = 13;
132 $xmlrpcstr['multicall_noparams'] = 'missing params';
133 $xmlrpcerr['multicall_notarray'] = 14;
134 $xmlrpcstr['multicall_notarray'] = 'params is not an array';
136 // The charset encoding expected by the server for received messages and
137 // by the client for received responses
138 $xmlrpc_defencoding='UTF-8';
139 // The encoding used by PHP.
140 // String values received will be converted to this.
141 $xmlrpc_internalencoding='ISO-8859-1';
143 $xmlrpcName='XML-RPC for PHP';
144 $xmlrpcVersion='1.1.1';
146 // let user errors start at 800
148 // let XML parse errors start at 100
151 // formulate backslashes for escaping regexp
152 $xmlrpc_backslash=chr(92).chr(92);
154 // used to store state during parsing
155 // quick explanation of components:
156 // st - used to build up a string for evaluation
157 // ac - used to accumulate values
158 // qt - used to decide if quotes are needed for evaluation
159 // cm - used to denote struct or array (comma needed)
160 // isf - used to indicate a fault
161 // lv - used to indicate "looking for a value": implements
162 // the logic to allow values with no types to be strings
163 // params - used to store parameters in method calls
164 // method - used to store method name
169 * To help correct communication of non-ascii chars inside strings, regardless
170 * of the charset used when sending requests, parsing them, sending responses
171 * and parsing responses, convert all non-ascii chars present in the message
172 * into their equivalent 'charset entity'. Charset entities enumerated this way
173 * are independent of the charset encoding used to transmit them, and all XML
174 * parsers are bound to understand them.
176 function xmlrpc_entity_decode($string)
178 $top=split('&', $string);
181 while($i<sizeof($top))
183 if (ereg("^([#a-zA-Z0-9]+);", $top[$i], $regs))
185 $op.=ereg_replace("^[#a-zA-Z0-9]+;",
186 xmlrpc_lookup_entity($regs[1]),
205 function xmlrpc_lookup_entity($ent)
209 if (isset($xmlEntities[strtolower($ent)]))
211 return $xmlEntities[strtolower($ent)];
213 if (ereg("^#([0-9]+)$", $ent, $regs))
215 return chr($regs[1]);
221 * These entities originate from HTML specs (1.1, proposed 2.0, etc),
222 * and are taken directly from php-4.3.1/ext/mbstring/html_entities.c.
223 * Until php provides functionality to translate these entities in its
224 * core library, use this function.
226 function xmlrpc_html_entity_xlate($data = '')
229 " " => " ",
230 "¡" => "¡",
231 "¢" => "¢",
232 "£" => "£",
233 "¤" => "¤",
235 "¦" => "¦",
236 "§" => "§",
238 "©" => "©",
239 "ª" => "ª",
240 "«" => "«",
244 "¯" => "¯",
246 "±" => "±",
247 "²" => "²",
248 "³" => "³",
249 "´" => "´",
250 "µ" => "µ",
251 "¶" => "¶",
252 "·" => "·",
253 "¸" => "¸",
254 "¹" => "¹",
255 "º" => "º",
256 "»" => "»",
257 "¼" => "¼",
258 "½" => "½",
259 "¾" => "¾",
260 "¿" => "¿",
261 "À" => "À",
262 "Á" => "Á",
263 "Â" => "Â",
264 "Ã" => "Ã",
265 "Ä" => "Ä",
266 "Å" => "Å",
267 "Æ" => "Æ",
268 "Ç" => "Ç",
269 "È" => "È",
270 "É" => "É",
271 "Ê" => "Ê",
272 "Ë" => "Ë",
273 "Ì" => "Ì",
274 "Í" => "Í",
275 "Î" => "Î",
276 "Ï" => "Ï",
278 "Ñ" => "Ñ",
279 "Ò" => "Ò",
280 "Ó" => "Ó",
281 "Ô" => "Ô",
282 "Õ" => "Õ",
283 "Ö" => "Ö",
284 "×" => "×",
285 "Ø" => "Ø",
286 "Ù" => "Ù",
287 "Ú" => "Ú",
288 "Û" => "Û",
289 "Ü" => "Ü",
290 "Ý" => "Ý",
291 "Þ" => "Þ",
292 "ß" => "ß",
293 "à" => "à",
294 "á" => "á",
295 "â" => "â",
296 "ã" => "ã",
297 "ä" => "ä",
298 "å" => "å",
299 "æ" => "æ",
300 "ç" => "ç",
301 "è" => "è",
302 "é" => "é",
303 "ê" => "ê",
304 "ë" => "ë",
305 "ì" => "ì",
306 "í" => "í",
307 "î" => "î",
308 "ï" => "ï",
310 "ñ" => "ñ",
311 "ò" => "ò",
312 "ó" => "ó",
313 "ô" => "ô",
314 "õ" => "õ",
315 "ö" => "ö",
316 "÷" => "÷",
317 "ø" => "ø",
318 "ù" => "ù",
319 "ú" => "ú",
320 "û" => "û",
321 "ü" => "ü",
322 "ý" => "ý",
323 "þ" => "þ",
324 "ÿ" => "ÿ",
325 "Œ" => "Œ",
326 "œ" => "œ",
327 "Š" => "Š",
328 "š" => "š",
329 "Ÿ" => "Ÿ",
330 "ƒ" => "ƒ",
331 "ˆ" => "ˆ",
332 "˜" => "˜",
333 "Α" => "Α",
334 "Β" => "Β",
335 "Γ" => "Γ",
336 "Δ" => "Δ",
337 "Ε" => "Ε",
338 "Ζ" => "Ζ",
340 "Θ" => "Θ",
341 "Ι" => "Ι",
342 "Κ" => "Κ",
343 "Λ" => "Λ",
347 "Ο" => "Ο",
350 "Σ" => "Σ",
352 "Υ" => "Υ",
356 "Ω" => "Ω",
357 "β" => "β",
358 "γ" => "γ",
359 "δ" => "δ",
360 "ε" => "ε",
361 "ζ" => "ζ",
363 "θ" => "θ",
364 "ι" => "ι",
365 "κ" => "κ",
366 "λ" => "λ",
370 "ο" => "ο",
373 "ς" => "ς",
374 "σ" => "σ",
376 "υ" => "υ",
380 "ω" => "ω",
381 "ϑ" => "ϑ",
382 "ϒ" => "ϒ",
384 " " => " ",
385 " " => " ",
386 " " => " ",
387 "‌" => "‌",
388 "‍" => "‍",
389 "‎" => "‎",
390 "‏" => "‏",
391 "–" => "–",
392 "—" => "—",
393 "‘" => "‘",
394 "’" => "’",
395 "‚" => "‚",
396 "“" => "“",
397 "”" => "”",
398 "„" => "„",
399 "†" => "†",
400 "‡" => "‡",
401 "•" => "•",
402 "…" => "…",
403 "‰" => "‰",
404 "′" => "′",
405 "″" => "″",
406 "‹" => "‹",
407 "›" => "›",
408 "‾" => "‾",
409 "⁄" => "⁄",
410 "€" => "€",
411 "℘" => "℘",
412 "ℑ" => "ℑ",
413 "ℜ" => "ℜ",
414 "™" => "™",
415 "ℵ" => "ℵ",
416 "←" => "←",
417 "↑" => "↑",
418 "→" => "→",
419 "↓" => "↓",
420 "↔" => "↔",
421 "↵" => "↵",
422 "⇐" => "⇐",
423 "⇑" => "⇑",
424 "⇒" => "⇒",
425 "⇓" => "⇓",
426 "⇔" => "⇔",
427 "∀" => "∀",
428 "∂" => "∂",
429 "∃" => "∃",
430 "∅" => "∅",
431 "∇" => "∇",
432 "∈" => "∈",
433 "∉" => "∉",
435 "∏" => "∏",
436 "∑" => "∑",
437 "−" => "−",
438 "∗" => "∗",
439 "√" => "√",
440 "∝" => "∝",
441 "∞" => "∞",
442 "∠" => "∠",
443 "∧" => "∧",
445 "∩" => "∩",
446 "∪" => "∪",
447 "∫" => "∫",
448 "∴" => "∴",
449 "∼" => "∼",
450 "≅" => "≅",
451 "≈" => "≈",
453 "≡" => "≡",
456 "⊂" => "⊂",
457 "⊃" => "⊃",
458 "⊄" => "⊄",
459 "⊆" => "⊆",
460 "⊇" => "⊇",
461 "⊕" => "⊕",
462 "⊗" => "⊗",
463 "⊥" => "⊥",
464 "⋅" => "⋅",
465 "⌈" => "⌈",
466 "⌉" => "⌉",
467 "⌊" => "⌊",
468 "⌋" => "⌋",
469 "⟨" => "〈",
470 "⟩" => "〉",
471 "◊" => "◊",
472 "♠" => "♠",
473 "♣" => "♣",
474 "♥" => "♥",
475 "♦" => "♦");
476 return strtr($data, $entities);
479 function xmlrpc_encode_entitites($data)
481 $length = strlen($data);
483 for($position = 0; $position < $length; $position++)
485 $character = substr($data, $position, 1);
486 $code = Ord($character);
489 $character = """;
492 $character = "&";
495 $character = "'";
504 if ($code < 32 || $code > 159)
505 $character = ("&#".strval($code).";");
508 $escapeddata .= $character;
513 function xmlrpc_se($parser, $name, $attrs)
515 global $_xh, $xmlrpcDateTime, $xmlrpcString;
521 $_xh[$parser]['st'].='array(';
522 $_xh[$parser]['cm']++;
523 // this last line turns quoting off
524 // this means if we get an empty array we'll
525 // simply get a bit of whitespace in the eval
526 $_xh[$parser]['qt']=0;
529 $_xh[$parser]['st'].='"';
530 $_xh[$parser]['ac']='';
533 $_xh[$parser]['isf']=1;
536 $_xh[$parser]['st']='';
539 $_xh[$parser]['st'].='new xmlrpcval(';
540 $_xh[$parser]['vt']=$xmlrpcString;
541 $_xh[$parser]['ac']='';
542 $_xh[$parser]['qt']=0;
543 $_xh[$parser]['lv']=1;
544 // look for a value: if this is still 1 by the
545 // time we reach the first data segment then the type is string
546 // by implication and we need to add in a quote
553 case 'DATETIME.ISO8601':
555 $_xh[$parser]['ac']=''; // reset the accumulator
557 if ($name=='DATETIME.ISO8601' || $name=='STRING')
559 $_xh[$parser]['qt']=1;
560 if ($name=='DATETIME.ISO8601')
562 $_xh[$parser]['vt']=$xmlrpcDateTime;
565 elseif ($name=='BASE64')
567 $_xh[$parser]['qt']=2;
571 // No quoting is required here -- but
572 // at the end of the element we must check
573 // for data format errors.
574 $_xh[$parser]['qt']=0;
578 $_xh[$parser]['ac']='';
586 $_xh[$parser]['lv']=0;
590 function xmlrpc_ee($parser, $name)
592 global $_xh,$xmlrpcTypes,$xmlrpcString;
598 if ($_xh[$parser]['cm'] && substr($_xh[$parser]['st'], -1) ==',')
600 $_xh[$parser]['st']=substr($_xh[$parser]['st'],0,-1);
602 $_xh[$parser]['st'].=')';
603 $_xh[$parser]['vt']=strtolower($name);
604 $_xh[$parser]['cm']--;
607 $_xh[$parser]['st'].= $_xh[$parser]['ac'] . '" => ';
610 // special case here: we translate boolean 1 or 0 into PHP
611 // constants true or false
612 // NB: this simple checks helps a lot sanitizing input, ie no
613 // security problems around here
614 if ($_xh[$parser]['ac']=='1')
616 $_xh[$parser]['ac']='true';
620 $_xh[$parser]['ac']='false';
622 $_xh[$parser]['vt']=strtolower($name);
623 // Drop through intentionally.
628 case 'DATETIME.ISO8601':
630 if ($_xh[$parser]['qt']==1)
632 // we use double quotes rather than single so backslashification works OK
633 $_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"';
635 elseif ($_xh[$parser]['qt']==2)
637 $_xh[$parser]['st'].='base64_decode("'. $_xh[$parser]['ac'] . '")';
639 elseif ($name=='BOOLEAN')
641 $_xh[$parser]['st'].=$_xh[$parser]['ac'];
643 elseif ($name=='DOUBLE')
646 // we must check that only 0123456789-.<space> are characters here
647 if (!ereg("^[+-]?[eE0123456789 \\t\\.]+$", $_xh[$parser]['ac']))
649 // TODO: find a better way of throwing an error
651 error_log('XML-RPC: non numeric value received in DOUBLE: '.$_xh[$parser]['ac']);
652 $_xh[$parser]['st'].="'ERROR_NON_NUMERIC_FOUND'";
656 // it's ok, add it on
657 $_xh[$parser]['st'].=(double)$_xh[$parser]['ac'];
663 // we must check that only 0123456789-<space> are characters here
664 if (!ereg("^[+-]?[0123456789 \\t]+$", $_xh[$parser]['ac']))
666 // TODO: find a better way of throwing an error
668 error_log('XML-RPC: non numeric value received in INT: '.$_xh[$parser]['ac']);
669 $_xh[$parser]['st'].="'ERROR_NON_NUMERIC_FOUND'";
673 // it's ok, add it on
674 $_xh[$parser]['st'].=(int)$_xh[$parser]['ac'];
677 $_xh[$parser]['ac']='';
678 $_xh[$parser]['qt']=0;
679 $_xh[$parser]['lv']=3; // indicate we've found a value
682 // deal with a string value
683 if (strlen($_xh[$parser]['ac'])>0 &&
684 $_xh[$parser]['vt']==$xmlrpcString)
686 $_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"';
688 // This if() detects if no scalar was inside <VALUE></VALUE>
689 // and pads an empty ''.
690 if($_xh[$parser]['st'][strlen($_xh[$parser]['st'])-1] == '(')
692 $_xh[$parser]['st'].= '""';
694 // G. Giunta 2005/03/12 save some chars in the reconstruction of string vals...
695 if ($_xh[$parser]['vt'] != $xmlrpcString)
696 $_xh[$parser]['st'].=", '" . $_xh[$parser]['vt'] . "')";
698 $_xh[$parser]['st'].=")";
699 if ($_xh[$parser]['cm'])
701 $_xh[$parser]['st'].=',';
705 $_xh[$parser]['ac']='';
706 $_xh[$parser]['qt']=0;
709 $_xh[$parser]['ac']='';
710 $_xh[$parser]['qt']=0;
713 $_xh[$parser]['params'][]=$_xh[$parser]['st'];
716 $_xh[$parser]['method']=ereg_replace("^[\n\r\t ]+", '', $_xh[$parser]['ac']);
718 // BOOLEAN HAS BEEN ENUMERATED ABOVE!
720 // special case here: we translate boolean 1 or 0 into PHP
721 // constants true or false
722 if ($_xh[$parser]['ac']=='1')
724 $_xh[$parser]['ac']='true';
728 $_xh[$parser]['ac']='false';
729 $_xh[$parser]['vt']=strtolower($name);
735 // if it's a valid type name, set the type
736 if (isset($xmlrpcTypes[strtolower($name)]))
738 $_xh[$parser]['vt']=strtolower($name);
742 function xmlrpc_cd($parser, $data)
744 global $_xh, $xmlrpc_backslash;
746 //if (ereg("^[\n\r \t]+$", $data)) return;
747 // print "adding [${data}]\n";
749 if ($_xh[$parser]['lv']!=3)
751 // "lookforvalue==3" means that we've found an entire value
752 // and should discard any further character data
753 if ($_xh[$parser]['lv']==1)
755 // if we've found text and we're just in a <value> then
756 // turn quoting on, as this will be a string
757 $_xh[$parser]['qt']=1;
758 // and say we've found a value
759 $_xh[$parser]['lv']=2;
761 if(!@isset($_xh[$parser]['ac']))
763 $_xh[$parser]['ac'] = '';
765 $_xh[$parser]['ac'].=str_replace('$', '\$', str_replace('"', '\"', str_replace(chr(92),$xmlrpc_backslash, $data)));
769 function xmlrpc_dh($parser, $data)
771 global $_xh, $xmlrpc_backslash;
772 if (substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
774 if ($_xh[$parser]['lv']==1)
776 $_xh[$parser]['qt']=1;
777 $_xh[$parser]['lv']=2;
779 $_xh[$parser]['ac'].=str_replace('$', '\$', str_replace('"', '\"', str_replace(chr(92),$xmlrpc_backslash, $data)));
797 var $no_multicall=false;
799 function xmlrpc_client($path, $server, $port=0)
801 $this->port=$port; $this->server=$server; $this->path=$path;
804 function setDebug($in)
816 function setCredentials($u, $p)
822 function setCertificate($cert, $certpass)
825 $this->certpass = $certpass;
828 function setSSLVerifyPeer($i)
830 $this->verifypeer = $i;
833 function setSSLVerifyHost($i)
835 $this->verifyhost = $i;
838 function send($msg, $timeout=0, $method='http')
842 // $msg is an array of xmlrpcmsg's
843 return $this->multicall($msg, $timeout, $method);
846 // where msg is an xmlrpcmsg
847 $msg->debug=$this->debug;
849 if ($method == 'https')
851 return $this->sendPayloadHTTPS($msg,
853 $this->port, $timeout,
854 $this->username, $this->password,
860 return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
861 $timeout, $this->username,
866 function sendPayloadHTTP10($msg, $server, $port, $timeout=0,$username='', $password='')
868 global $xmlrpcerr, $xmlrpcstr, $xmlrpcName, $xmlrpcVersion, $xmlrpc_defencoding;
875 $fp=@fsockopen($server, $port,$this->errno, $this->errstr, $timeout);
879 $fp=@fsockopen($server, $port,$this->errno, $this->errstr);
883 if ($timeout>0 && function_exists('stream_set_timeout'))
884 stream_set_timeout($fp, $timeout);
888 $this->errstr='Connect error';
889 $r=new xmlrpcresp(0, $xmlrpcerr['http_error'],$xmlrpcstr['http_error']);
892 // Only create the payload if it was not created previously
893 if(empty($msg->payload))
895 $msg->createPayload();
898 // thanks to Grant Rauscher <grant7@firstworld.net>
903 $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
906 $op= "POST " . $this->path. " HTTP/1.0\r\n" .
907 "User-Agent: " . $xmlrpcName . " " . $xmlrpcVersion . "\r\n" .
908 "Host: ". $server . "\r\n" .
910 "Accept-Charset: " . $xmlrpc_defencoding . "\r\n" .
911 "Content-Type: text/xml\r\nContent-Length: " .
912 strlen($msg->payload) . "\r\n\r\n" .
915 if (!fputs($fp, $op, strlen($op)))
917 $this->errstr='Write error';
918 $r=new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']);
921 $resp=$msg->parseResponseFile($fp);
926 // contributed by Justin Miller <justin@voxel.net>
927 // requires curl to be built into PHP
928 function sendPayloadHTTPS($msg, $server, $port, $timeout=0,$username='', $password='', $cert='',$certpass='')
930 global $xmlrpcerr, $xmlrpcstr, $xmlrpcVersion, $xmlrpc_internalencoding;
936 // Only create the payload if it was not created previously
937 if(empty($msg->payload))
939 $msg->createPayload();
942 if (!function_exists('curl_init'))
944 $this->errstr='SSL unavailable on this install';
945 $r=new xmlrpcresp(0, $xmlrpcerr['no_ssl'], $xmlrpcstr['no_ssl']);
949 $curl = curl_init('https://' . $server . ':' . $port . $this->path);
951 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
952 // results into variable
955 curl_setopt($curl, CURLOPT_VERBOSE, 1);
957 curl_setopt($curl, CURLOPT_USERAGENT, 'PHP XMLRPC '.$xmlrpcVersion);
958 // required for XMLRPC
959 curl_setopt($curl, CURLOPT_POST, 1);
961 curl_setopt($curl, CURLOPT_POSTFIELDS, $msg->payload);
963 curl_setopt($curl, CURLOPT_HEADER, 1);
964 // return the header too
965 curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: text/xml', 'Accept-Charset: '.$xmlrpc_internalencoding));
966 // whether to verify remote host's cert
967 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
968 // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
969 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
970 // required for XMLRPC
973 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
976 if ($username && $password)
978 curl_setopt($curl, CURLOPT_USERPWD,"$username:$password");
983 curl_setopt($curl, CURLOPT_SSLCERT, $cert);
988 curl_setopt($curl, CURLOPT_SSLCERTPASSWD,$certpass);
992 $result = curl_exec($curl);
996 $this->errstr='no response';
997 $resp=new xmlrpcresp(0, $xmlrpcerr['curl_fail'], $xmlrpcstr['curl_fail']. ': '. curl_error($curl));
1003 $resp = $msg->parseResponse($result);
1008 function multicall($msgs, $timeout=0, $method='http')
1012 if (! $this->no_multicall)
1014 $results = $this->_try_multicall($msgs, $timeout, $method);
1015 /* TODO - this is not php3-friendly */
1016 // if($results !== false)
1017 if(is_array($results))
1019 // Either the system.multicall succeeded, or the send
1020 // failed (e.g. due to HTTP timeout). In either case,
1021 // we're done for now.
1026 // system.multicall unsupported by server,
1027 // don't try it next time...
1028 $this->no_multicall = true;
1032 // system.multicall is unupported by server:
1033 // Emulate multicall via multiple requests
1035 //foreach($msgs as $msg)
1037 while(list(,$msg) = @each($msgs))
1039 $results[] = $this->send($msg, $timeout, $method);
1044 // Attempt to boxcar $msgs via system.multicall.
1045 function _try_multicall($msgs, $timeout, $method)
1047 // Construct multicall message
1049 //foreach($msgs as $msg)
1051 while(list(,$msg) = @each($msgs))
1053 $call['methodName'] = new xmlrpcval($msg->method(),'string');
1054 $numParams = $msg->getNumParams();
1056 for ($i = 0; $i < $numParams; $i++)
1058 $params[$i] = $msg->getParam($i);
1060 $call['params'] = new xmlrpcval($params, 'array');
1061 $calls[] = new xmlrpcval($call, 'struct');
1063 $multicall = new xmlrpcmsg('system.multicall');
1064 $multicall->addParam(new xmlrpcval($calls, 'array'));
1067 $result = $this->send($multicall, $timeout, $method);
1068 if(!is_object($result))
1070 return ($result || 0); // transport failed
1073 if($result->faultCode() != 0)
1075 return false; // system.multicall failed
1078 // Unpack responses.
1079 $rets = $result->value();
1080 if($rets->kindOf() != 'array')
1082 return false; // bad return type from system.multicall
1084 $numRets = $rets->arraysize();
1085 if($numRets != count($msgs))
1087 return false; // wrong number of return values.
1090 $response = array();
1091 for ($i = 0; $i < $numRets; $i++)
1093 $val = $rets->arraymem($i);
1094 switch ($val->kindOf())
1097 if($val->arraysize() != 1)
1099 return false; // Bad value
1101 // Normal return value
1102 $response[$i] = new xmlrpcresp($val->arraymem(0));
1105 $code = $val->structmem('faultCode');
1106 if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
1110 $str = $val->structmem('faultString');
1111 if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
1115 $response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1123 } // end class xmlrpc_client
1130 var $hdrs = array();
1132 function xmlrpcresp($val, $fcode = 0, $fstr = '')
1137 $this->errno = $fcode;
1138 $this->errstr = $fstr;
1139 //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
1141 elseif (!is_object($val))
1144 error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to xmlrpcresp. Defaulting to empty value.");
1145 $this->val = new xmlrpcval();
1154 function faultCode()
1156 return $this->errno;
1159 function faultString()
1161 return $this->errstr;
1169 function serialize()
1171 $result = "<methodResponse>\n";
1174 // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
1179 <name>faultCode</name>
1180 <value><int>' . $this->errno . '</int></value>
1183 <name>faultString</name>
1184 <value><string>' . xmlrpc_encode_entitites($this->errstr) . '</string></value>
1192 $result .= "<params>\n<param>\n" .
1193 $this->val->serialize() .
1194 "</param>\n</params>";
1196 $result .= "\n</methodResponse>";
1205 var $params=array();
1208 function xmlrpcmsg($meth, $pars=0)
1210 $this->methodname=$meth;
1211 if (is_array($pars) && sizeof($pars)>0)
1213 for($i=0; $i<sizeof($pars); $i++)
1215 $this->addParam($pars[$i]);
1220 function xml_header()
1222 return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
1225 function xml_footer()
1227 return "</methodCall>\n";
1230 function createPayload()
1232 $this->payload=$this->xml_header();
1233 $this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
1234 // if (sizeof($this->params)) {
1235 $this->payload.="<params>\n";
1236 for($i=0; $i<sizeof($this->params); $i++)
1238 $p=$this->params[$i];
1239 $this->payload.="<param>\n" . $p->serialize() .
1242 $this->payload.="</params>\n";
1244 $this->payload.=$this->xml_footer();
1245 //$this->payload=str_replace("\n", "\r\n", $this->payload);
1248 function method($meth='')
1252 $this->methodname=$meth;
1254 return $this->methodname;
1257 function serialize()
1259 $this->createPayload();
1260 return $this->payload;
1263 function addParam($par) { $this->params[]=$par; }
1264 function getParam($i) { return $this->params[$i]; }
1265 function getNumParams() { return sizeof($this->params); }
1267 function parseResponseFile($fp)
1270 while($data=fread($fp, 32768))
1274 return $this->parseResponse($ipd);
1277 function parseResponse($data='')
1279 global $_xh,$xmlrpcerr,$xmlrpcstr;
1280 global $xmlrpc_defencoding, $xmlrpc_internalencoding;
1285 //by maHo, replaced htmlspecialchars with htmlentities
1286 print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
1291 error_log('No response received from server.');
1292 $r = new xmlrpcresp(0, $xmlrpcerr['no_data'], $xmlrpcstr['no_data']);
1295 // see if we got an HTTP 200 OK, else bomb
1296 // but only do this if we're using the HTTP protocol.
1297 if(ereg("^HTTP",$data))
1299 // Strip HTTP 1.1 100 Continue header if present
1300 while (ereg('^HTTP/1.1 1[0-9]{2}', $data))
1302 $pos = strpos($data, 'HTTP', 12);
1303 // server sent a Continue header without any (valid) content following...
1304 // give the client a chance to know it
1305 if (!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
1307 $data = substr($data, $pos);
1309 if (!ereg("^HTTP/[0-9\\.]+ 200 ", $data))
1311 $errstr= substr($data, 0, strpos($data, "\n")-1);
1312 error_log('HTTP error, got response: ' .$errstr);
1313 $r=new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']. ' (' . $errstr . ')');
1317 $parser = xml_parser_create($xmlrpc_defencoding);
1319 // G. Giunta 2004/04/06
1320 // Clean up the accumulator, or it will grow indefinitely long
1321 // if making xmlrpc calls for a while
1323 $_xh[$parser]=array();
1324 $_xh[$parser]['headers'] = array();
1326 // separate HTTP headers from data
1327 if (ereg("^HTTP", $data))
1329 // be tolerant to usage of \n instead of \r\n to separate headers and data
1330 // (even though it is not valid http)
1331 $pos = strpos($data,"\r\n\r\n");
1332 if($pos || is_int($pos))
1336 $pos = strpos($data,"\n\n");
1337 if($pos || is_int($pos))
1341 // No separation between response headers and body: fault?
1345 // be tolerant to line endings, and extra empty lines
1346 $ar = split("\r?\n", trim(substr($data, 0, $pos)));
1347 while (list(,$line) = @each($ar))
1349 // take care of multi-line headers
1350 $arr = explode(':',$line);
1353 $header_name = trim($arr[0]);
1354 // TO DO: some headers (the ones that allow a CSV list of values)
1355 // do allow many values to be passed using multiple header lines.
1356 // We should add content to $_xh[$parser]['headers'][$header_name]
1357 // instead of replacing it for those...
1358 $_xh[$parser]['headers'][$header_name] = $arr[1];
1359 for ($i = 2; $i < count($arr); $i++)
1361 $_xh[$parser]['headers'][$header_name] .= ':'.$arr[$i];
1363 $_xh[$parser]['headers'][$header_name] = trim($_xh[$parser]['headers'][$header_name]);
1364 } else if (isset($header_name))
1366 $_xh[$parser]['headers'][$header_name] .= ' ' . trim($line);
1369 $data = substr($data, $bd);
1371 if ($this->debug && count($_xh[$parser]['headers']))
1374 //foreach ($_xh[$parser]['headers'] as $header)
1375 @reset($_xh[$parser]['headers']);
1376 while(list($header, $value) = @each($_xh[$parser]['headers']))
1378 print "HEADER: $header: $value\n";
1384 // be tolerant of extra whitespace in response body
1385 $data = trim($data);
1387 // be tolerant of junk after methodResponse (e.g. javascript automatically inserted by free hosts)
1388 // idea from Luca Mariano <luca.mariano@email.it> originally in PEARified version of the lib
1390 $pos = strpos($data, "</methodResponse>");
1391 while ($pos || is_int($pos))
1394 $pos = strpos($data, "</methodResponse>", $bd);
1397 $data = substr($data, 0, $bd);
1399 $_xh[$parser]['st']='';
1400 $_xh[$parser]['cm']=0;
1401 $_xh[$parser]['isf']=0;
1402 $_xh[$parser]['ac']='';
1403 $_xh[$parser]['qt']='';
1405 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
1406 // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
1407 // the xml parser to give us back data in the expected charset
1408 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $xmlrpc_internalencoding);
1410 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
1411 xml_set_character_data_handler($parser, 'xmlrpc_cd');
1412 xml_set_default_handler($parser, 'xmlrpc_dh');
1413 //$xmlrpc_value=new xmlrpcval;
1415 if (!xml_parse($parser, $data, sizeof($data)))
1417 // thanks to Peter Kocks <peter.kocks@baygate.com>
1418 if((xml_get_current_line_number($parser)) == 1)
1420 $errstr = 'XML error at line 1, check URL';
1424 $errstr = sprintf('XML error: %s at line %d',
1425 xml_error_string(xml_get_error_code($parser)),
1426 xml_get_current_line_number($parser));
1429 $r=new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return'].' ('.$errstr.')');
1430 xml_parser_free($parser);
1433 $r->hdrs = $_xh[$parser]['headers'];
1436 xml_parser_free($parser);
1439 print "<PRE>---EVALING---[" .
1440 strlen($_xh[$parser]['st']) . " chars]---\n" .
1441 htmlspecialchars($_xh[$parser]['st']) . ";\n---END---</PRE>";
1443 if (strlen($_xh[$parser]['st'])==0)
1445 // then something odd has happened
1446 // and it's time to generate a client side error
1447 // indicating something odd went on
1448 $r=new xmlrpcresp(0, $xmlrpcerr['invalid_return'],
1449 $xmlrpcstr['invalid_return']);
1454 @eval('$v=' . $_xh[$parser]['st'] . '; $allOK=1;');
1457 $r = new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return']);
1460 if ($_xh[$parser]['isf'])
1462 $errno_v = $v->structmem('faultCode');
1463 $errstr_v = $v->structmem('faultString');
1464 $errno = $errno_v->scalarval();
1468 // FAULT returned, errno needs to reflect that
1472 $r = new xmlrpcresp($v, $errno, $errstr_v->scalarval());
1476 $r=new xmlrpcresp($v);
1480 $r->hdrs = $_xh[$parser]['headers'];
1490 function xmlrpcval($val=-1, $type='')
1492 global $xmlrpcTypes;
1495 if ($val!=-1 || !is_int($val) || $type!='')
1501 if ($xmlrpcTypes[$type]==1)
1503 $this->addScalar($val,$type);
1505 elseif ($xmlrpcTypes[$type]==2)
1507 $this->addArray($val);
1509 elseif ($xmlrpcTypes[$type]==3)
1511 $this->addStruct($val);
1516 function addScalar($val, $type='string')
1518 global $xmlrpcTypes, $xmlrpcBoolean;
1520 if ($this->mytype==1)
1522 echo '<B>xmlrpcval</B>: scalar can have only one value<BR>';
1525 $typeof=$xmlrpcTypes[$type];
1528 echo '<B>xmlrpcval</B>: not a scalar type (${typeof})<BR>';
1532 if ($type==$xmlrpcBoolean)
1534 if (strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
1544 if ($this->mytype==2)
1546 // we're adding to an array here
1547 $ar=$this->me['array'];
1548 $ar[]=new xmlrpcval($val, $type);
1549 $this->me['array']=$ar;
1553 // a scalar, so set the value and remember we're scalar
1554 $this->me[$type]=$val;
1555 $this->mytype=$typeof;
1560 function addArray($vals)
1562 global $xmlrpcTypes;
1563 if ($this->mytype!=0)
1565 echo '<B>xmlrpcval</B>: already initialized as a [' . $this->kindOf() . ']<BR>';
1569 $this->mytype=$xmlrpcTypes['array'];
1570 $this->me['array']=$vals;
1574 function addStruct($vals)
1576 global $xmlrpcTypes;
1577 if ($this->mytype!=0)
1579 echo '<B>xmlrpcval</B>: already initialized as a [' . $this->kindOf() . ']<BR>';
1582 $this->mytype=$xmlrpcTypes['struct'];
1583 $this->me['struct']=$vals;
1590 while ( list( $key, $val ) = each( $ar ) )
1592 echo "$key => $val<br>";
1593 if ($key == 'array')
1595 while ( list( $key2, $val2 ) = each( $val ) )
1597 echo "-- $key2 => $val2<br>";
1605 switch($this->mytype)
1621 function serializedata($typ, $val)
1624 global $xmlrpcTypes, $xmlrpcBase64, $xmlrpcString,
1626 switch(@$xmlrpcTypes[$typ])
1632 while(list($key2, $val2)=each($val))
1634 $rs.="<member><name>${key2}</name>\n";
1635 $rs.=$this->serializeval($val2);
1642 $rs.="<array>\n<data>\n";
1643 for($i=0; $i<sizeof($val); $i++)
1645 $rs.=$this->serializeval($val[$i]);
1647 $rs.="</data>\n</array>";
1653 $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
1655 case $xmlrpcBoolean:
1656 $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
1659 // G. Giunta 2005/2/13: do NOT use htmlentities, since
1660 // it will produce named html entities, which are invalid xml
1661 $rs.="<${typ}>" . xmlrpc_encode_entitites($val). "</${typ}>";
1662 // $rs.="<${typ}>" . htmlentities($val). "</${typ}>";
1665 $rs.="<${typ}>${val}</${typ}>";
1674 function serialize()
1676 return $this->serializeval($this);
1679 function serializeval($o)
1681 //global $xmlrpcTypes;
1685 list($typ, $val) = each($ar);
1687 $rs.=$this->serializedata($typ, $val);
1692 function structmem($m)
1694 $nv=$this->me['struct'][$m];
1698 function structreset()
1700 reset($this->me['struct']);
1703 function structeach()
1705 return each($this->me['struct']);
1711 global $xmlrpcBoolean, $xmlrpcBase64;
1713 list($a,$b)=each($this->me);
1714 // contributed by I Sofer, 2001-03-24
1715 // add support for nested arrays to scalarval
1716 // i've created a new method here, so as to
1717 // preserve back compatibility
1722 while(list($id,$cont) = @each($b))
1724 $b[$id] = $cont->scalarval();
1728 // add support for structures directly encoding php objects
1731 $t = get_object_vars($b);
1733 while(list($id,$cont) = @each($t))
1735 $t[$id] = $cont->scalarval();
1738 while(list($id,$cont) = @each($t))
1740 eval('$b->'.$id.' = $cont;');
1747 function scalarval()
1749 //global $xmlrpcBoolean, $xmlrpcBase64;
1751 list($a,$b)=each($this->me);
1755 function scalartyp()
1757 global $xmlrpcI4, $xmlrpcInt;
1759 list($a,$b)=each($this->me);
1767 function arraymem($m)
1769 $nv=$this->me['array'][$m];
1773 function arraysize()
1776 list($a,$b)=each($this->me);
1782 function iso8601_encode($timet, $utc=0)
1784 // return an ISO8601 encoded string
1785 // really, timezones ought to be supported
1786 // but the XML-RPC spec says:
1788 // "Don't assume a timezone. It should be specified by the server in its
1789 // documentation what assumptions it makes about timezones."
1791 // these routines always assume localtime unless
1792 // $utc is set to 1, in which case UTC is assumed
1793 // and an adjustment for locale is made when encoding
1796 $t=strftime("%Y%m%dT%H:%M:%S", $timet);
1800 if (function_exists('gmstrftime'))
1802 // gmstrftime doesn't exist in some versions
1804 $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
1808 $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
1814 function iso8601_decode($idate, $utc=0)
1816 // return a timet in the localtime, or UTC
1818 if (ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})", $idate, $regs))
1822 $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1826 $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1832 /****************************************************************
1833 * xmlrpc_decode takes a message in PHP xmlrpc object format and *
1834 * tranlates it into native PHP types. *
1836 * author: Dan Libby (dan@libby.com) *
1837 ****************************************************************/
1838 function php_xmlrpc_decode($xmlrpc_val)
1840 $kind = $xmlrpc_val->kindOf();
1842 if($kind == 'scalar')
1844 return $xmlrpc_val->scalarval();
1846 elseif($kind == 'array')
1848 $size = $xmlrpc_val->arraysize();
1851 for($i = 0; $i < $size; $i++)
1853 $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i));
1857 elseif($kind == 'struct')
1859 $xmlrpc_val->structreset();
1862 while(list($key,$value)=$xmlrpc_val->structeach())
1864 $arr[$key] = php_xmlrpc_decode($value);
1870 if(function_exists('xmlrpc_decode'))
1872 define('XMLRPC_EPI_ENABLED','1');
1876 define('XMLRPC_EPI_ENABLED','0');
1877 function xmlrpc_decode($xmlrpc_val)
1879 $kind = $xmlrpc_val->kindOf();
1881 if($kind == 'scalar')
1883 return $xmlrpc_val->scalarval();
1885 elseif($kind == 'array')
1887 $size = $xmlrpc_val->arraysize();
1890 for($i = 0; $i < $size; $i++)
1892 $arr[]=xmlrpc_decode($xmlrpc_val->arraymem($i));
1896 elseif($kind == 'struct')
1898 $xmlrpc_val->structreset();
1901 while(list($key,$value)=$xmlrpc_val->structeach())
1903 $arr[$key] = xmlrpc_decode($value);
1910 /****************************************************************
1911 * xmlrpc_encode takes native php types and encodes them into *
1912 * xmlrpc PHP object format. *
1913 * BUG: All sequential arrays are turned into structs. I don't *
1914 * know of a good way to determine if an array is sequential *
1917 * feature creep -- could support more types via optional type *
1920 * author: Dan Libby (dan@libby.com) *
1921 ****************************************************************/
1922 function php_xmlrpc_encode($php_val)
1925 global $xmlrpcDouble;
1926 global $xmlrpcString;
1927 global $xmlrpcArray;
1928 global $xmlrpcStruct;
1929 global $xmlrpcBoolean;
1931 $type = gettype($php_val);
1932 $xmlrpc_val = new xmlrpcval;
1939 while (list($k,$v) = each($php_val))
1941 $arr[$k] = php_xmlrpc_encode($v);
1943 $xmlrpc_val->addStruct($arr);
1946 $xmlrpc_val->addScalar($php_val, $xmlrpcInt);
1949 $xmlrpc_val->addScalar($php_val, $xmlrpcDouble);
1952 $xmlrpc_val->addScalar($php_val, $xmlrpcString);
1954 // <G_Giunta_2001-02-29>
1955 // Add support for encoding/decoding of booleans, since they are supported in PHP
1957 $xmlrpc_val->addScalar($php_val, $xmlrpcBoolean);
1959 // </G_Giunta_2001-02-29>
1960 // catch "resource", "NULL", "user function", "unknown type"
1961 //case 'unknown type':
1963 // giancarlo pinerolo <ping@alt.it>
1965 // an empty object in case (which is already
1966 // at this point), not a boolean.
1972 if(XMLRPC_EPI_ENABLED == '0')
1974 function xmlrpc_encode($php_val)
1977 global $xmlrpcDouble;
1978 global $xmlrpcString;
1979 global $xmlrpcArray;
1980 global $xmlrpcStruct;
1981 global $xmlrpcBoolean;
1983 $type = gettype($php_val);
1984 $xmlrpc_val = new xmlrpcval;
1991 while (list($k,$v) = each($php_val))
1993 $arr[$k] = xmlrpc_encode($v);
1995 $xmlrpc_val->addStruct($arr);
1998 $xmlrpc_val->addScalar($php_val, $xmlrpcInt);
2001 $xmlrpc_val->addScalar($php_val, $xmlrpcDouble);
2004 $xmlrpc_val->addScalar($php_val, $xmlrpcString);
2006 // <G_Giunta_2001-02-29>
2007 // Add support for encoding/decoding of booleans, since they are supported in PHP
2009 $xmlrpc_val->addScalar($php_val, $xmlrpcBoolean);
2011 // </G_Giunta_2001-02-29>
2012 //case 'unknown type':
2014 // giancarlo pinerolo <ping@alt.it>
2016 // an empty object in case (which is already
2017 // at this point), not a boolean.