2 // by Edd Dumbill (C) 1999-2002
7 // Copyright (c) 1999,2000,2002 Edd Dumbill.
8 // All rights reserved.
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions
14 // * Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
17 // * Redistributions in binary form must reproduce the above
18 // copyright notice, this list of conditions and the following
19 // disclaimer in the documentation and/or other materials provided
20 // with the distribution.
22 // * Neither the name of the "XML-RPC for PHP" nor the names of its
23 // contributors may be used to endorse or promote products derived
24 // from this software without specific prior written permission.
26 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 // OF THE POSSIBILITY OF SUCH DAMAGE.
39 if (!function_exists('xml_parser_create'))
41 // Win 32 fix. From: 'Leo West' <lwest@imaginet.fr>
53 define("XMLRPCI4", "i4");
55 define("XMLRPCINT", "int");
57 define("XMLRPCBOOLEAN", "boolean");
58 $xmlrpcBoolean=XMLRPCBOOLEAN;
59 define("XMLRPCDOUBLE", "double");
60 $xmlrpcDouble=XMLRPCDOUBLE;
61 define("XMLRPCSTRING", "string");
62 $xmlrpcString=XMLRPCSTRING;
63 define("XMLRPCDATETIME", "dateTime.iso8601");
64 $xmlrpcDateTime=XMLRPCDATETIME;
65 define("XMLRPCBASE64", "base64");
66 $xmlrpcBase64=XMLRPCBASE64;
67 define("XMLRPCARRAY", "array");
68 $xmlrpcArray=XMLRPCARRAY;
69 define("XMLRPCSTRUCT", "struct");
70 $xmlrpcStruct=XMLRPCSTRUCT;
92 $xmlrpcerr['unknown_method']=1;
93 $xmlrpcstr['unknown_method']='Unknown method';
94 $xmlrpcerr['invalid_return']=2;
95 $xmlrpcstr['invalid_return']='Invalid return payload: enabling debugging to examine incoming payload';
96 $xmlrpcerr['incorrect_params']=3;
97 $xmlrpcstr['incorrect_params']='Incorrect parameters passed to method';
98 $xmlrpcerr['introspect_unknown']=4;
99 $xmlrpcstr['introspect_unknown']="Can't introspect: method unknown";
100 $xmlrpcerr['http_error']=5;
101 $xmlrpcstr['http_error']="Didn't receive 200 OK from remote server.";
102 $xmlrpcerr['no_data']=6;
103 $xmlrpcstr['no_data']='No data received from server.';
104 $xmlrpcerr['no_ssl']=7;
105 $xmlrpcstr['no_ssl']='No SSL support compiled in.';
106 $xmlrpcerr['curl_fail']=8;
107 $xmlrpcstr['curl_fail']='CURL error';
110 $xmlrpcerr['multicall_notstruct'] = 9;
111 $xmlrpcstr['multicall_notstruct'] = 'system.multicall expected struct';
112 $xmlrpcerr['multicall_nomethod'] = 10;
113 $xmlrpcstr['multicall_nomethod'] = 'missing methodName';
114 $xmlrpcerr['multicall_notstring'] = 11;
115 $xmlrpcstr['multicall_notstring'] = 'methodName is not a string';
116 $xmlrpcerr['multicall_recursion'] = 12;
117 $xmlrpcstr['multicall_recursion'] = 'recursive system.multicall forbidden';
118 $xmlrpcerr['multicall_noparams'] = 13;
119 $xmlrpcstr['multicall_noparams'] = 'missing params';
120 $xmlrpcerr['multicall_notarray'] = 14;
121 $xmlrpcstr['multicall_notarray'] = 'params is not an array';
123 $xmlrpc_defencoding='UTF-8';
125 $xmlrpcName='XML-RPC for PHP';
126 $xmlrpcVersion='1.0.99';
128 // let user errors start at 800
130 // let XML parse errors start at 100
133 // formulate backslashes for escaping regexp
134 $xmlrpc_backslash=chr(92).chr(92);
136 // used to store state during parsing
137 // quick explanation of components:
138 // st - used to build up a string for evaluation
139 // ac - used to accumulate values
140 // qt - used to decide if quotes are needed for evaluation
141 // cm - used to denote struct or array (comma needed)
142 // isf - used to indicate a fault
143 // lv - used to indicate "looking for a value": implements
144 // the logic to allow values with no types to be strings
145 // params - used to store parameters in method calls
146 // method - used to store method name
150 function xmlrpc_entity_decode($string)
152 $top=split('&', $string);
155 while($i<sizeof($top))
157 if (ereg("^([#a-zA-Z0-9]+);", $top[$i], $regs))
159 $op.=ereg_replace("^[#a-zA-Z0-9]+;",
160 xmlrpc_lookup_entity($regs[1]),
179 function xmlrpc_lookup_entity($ent)
183 if (isset($xmlEntities[strtolower($ent)]))
185 return $xmlEntities[strtolower($ent)];
187 if (ereg("^#([0-9]+)$", $ent, $regs))
189 return chr($regs[1]);
194 function xmlrpc_se($parser, $name, $attrs)
196 global $_xh, $xmlrpcDateTime, $xmlrpcString;
202 $_xh[$parser]['st'].='array(';
203 $_xh[$parser]['cm']++;
204 // this last line turns quoting off
205 // this means if we get an empty array we'll
206 // simply get a bit of whitespace in the eval
207 $_xh[$parser]['qt']=0;
210 $_xh[$parser]['st'].="'"; $_xh[$parser]['ac']='';
213 $_xh[$parser]['isf']=1;
216 $_xh[$parser]['st']='';
219 $_xh[$parser]['st'].='new xmlrpcval(';
220 $_xh[$parser]['vt']=$xmlrpcString;
221 $_xh[$parser]['ac']='';
222 $_xh[$parser]['qt']=0;
223 $_xh[$parser]['lv']=1;
224 // look for a value: if this is still 1 by the
225 // time we reach the first data segment then the type is string
226 // by implication and we need to add in a quote
233 case 'DATETIME.ISO8601':
235 $_xh[$parser]['ac']=''; // reset the accumulator
237 if ($name=='DATETIME.ISO8601' || $name=='STRING')
239 $_xh[$parser]['qt']=1;
240 if ($name=='DATETIME.ISO8601')
242 $_xh[$parser]['vt']=$xmlrpcDateTime;
245 elseif ($name=='BASE64')
247 $_xh[$parser]['qt']=2;
251 // No quoting is required here -- but
252 // at the end of the element we must check
253 // for data format errors.
254 $_xh[$parser]['qt']=0;
258 $_xh[$parser]['ac']='';
266 $_xh[$parser]['lv']=0;
270 function xmlrpc_ee($parser, $name)
272 global $_xh,$xmlrpcTypes,$xmlrpcString;
278 if ($_xh[$parser]['cm'] && substr($_xh[$parser]['st'], -1) ==',')
280 $_xh[$parser]['st']=substr($_xh[$parser]['st'],0,-1);
282 $_xh[$parser]['st'].=')';
283 $_xh[$parser]['vt']=strtolower($name);
284 $_xh[$parser]['cm']--;
287 $_xh[$parser]['st'].= $_xh[$parser]['ac'] . "' => ";
290 // special case here: we translate boolean 1 or 0 into PHP
291 // constants true or false
292 if ($_xh[$parser]['ac']=='1')
294 $_xh[$parser]['ac']='true';
298 $_xh[$parser]['ac']='false';
299 $_xh[$parser]['vt']=strtolower($name);
300 // Drop through intentionally.
306 case 'DATETIME.ISO8601':
308 if ($_xh[$parser]['qt']==1)
310 // we use double quotes rather than single so backslashification works OK
311 $_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"';
313 elseif ($_xh[$parser]['qt']==2)
315 $_xh[$parser]['st'].="base64_decode('". $_xh[$parser]['ac'] . "')";
317 elseif ($name=='BOOLEAN')
319 $_xh[$parser]['st'].=$_xh[$parser]['ac'];
323 // we have an I4, INT or a DOUBLE
324 // we must check that only 0123456789-.<space> are characters here
325 if (!ereg("^\-?[0123456789 \t\.]+$", $_xh[$parser]['ac']))
327 // TODO: find a better way of throwing an error
329 error_log('XML-RPC: non numeric value received in INT or DOUBLE');
330 $_xh[$parser]['st'].='ERROR_NON_NUMERIC_FOUND';
334 // it's ok, add it on
335 $_xh[$parser]['st'].=$_xh[$parser]['ac'];
338 $_xh[$parser]['ac']=''; $_xh[$parser]['qt']=0;
339 $_xh[$parser]['lv']=3; // indicate we've found a value
342 // deal with a string value
343 if (strlen($_xh[$parser]['ac'])>0 &&
344 $_xh[$parser]['vt']==$xmlrpcString)
346 $_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"';
348 // This if() detects if no scalar was inside <VALUE></VALUE>
349 // and pads an empty ''.
350 if($_xh[$parser]['st'][strlen($_xh[$parser]['st'])-1] == '(')
352 $_xh[$parser]['st'].= '""';
354 $_xh[$parser]['st'].=", '" . $_xh[$parser]['vt'] . "')";
355 if ($_xh[$parser]['cm'])
357 $_xh[$parser]['st'].=',';
361 $_xh[$parser]['ac']=''; $_xh[$parser]['qt']=0;
364 $_xh[$parser]['ac']=''; $_xh[$parser]['qt']=0;
367 $_xh[$parser]['params'][]=$_xh[$parser]['st'];
370 $_xh[$parser]['method']=ereg_replace("^[\n\r\t ]+", '', $_xh[$parser]['ac']);
373 // special case here: we translate boolean 1 or 0 into PHP
374 // constants true or false
375 if ($_xh[$parser]['ac']=='1')
377 $_xh[$parser]['ac']='true';
381 $_xh[$parser]['ac']='false';
382 $_xh[$parser]['vt']=strtolower($name);
388 // if it's a valid type name, set the type
389 if (isset($xmlrpcTypes[strtolower($name)]))
391 $_xh[$parser]['vt']=strtolower($name);
395 function xmlrpc_cd($parser, $data)
397 global $_xh, $xmlrpc_backslash;
399 //if (ereg("^[\n\r \t]+$", $data)) return;
400 // print "adding [${data}]\n";
402 if ($_xh[$parser]['lv']!=3)
404 // "lookforvalue==3" means that we've found an entire value
405 // and should discard any further character data
406 if ($_xh[$parser]['lv']==1)
408 // if we've found text and we're just in a <value> then
409 // turn quoting on, as this will be a string
410 $_xh[$parser]['qt']=1;
411 // and say we've found a value
412 $_xh[$parser]['lv']=2;
414 if(!@isset($_xh[$parser]['ac']))
416 $_xh[$parser]['ac'] = '';
418 $_xh[$parser]['ac'].=str_replace('$', '\$', str_replace('"', '\"', str_replace(chr(92),$xmlrpc_backslash, $data)));
422 function xmlrpc_dh($parser, $data)
425 if (substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
427 if ($_xh[$parser]['lv']==1)
429 $_xh[$parser]['qt']=1;
430 $_xh[$parser]['lv']=2;
432 $_xh[$parser]['ac'].=str_replace('$', '\$', str_replace('"', '\"', str_replace(chr(92),$xmlrpc_backslash, $data)));
450 var $no_multicall=false;
452 function xmlrpc_client($path, $server, $port=0)
454 $this->port=$port; $this->server=$server; $this->path=$path;
457 function setDebug($in)
469 function setCredentials($u, $p)
475 function setCertificate($cert, $certpass)
478 $this->certpass = $certpass;
481 function setSSLVerifyPeer($i)
483 $this->verifypeer = $i;
486 function setSSLVerifyHost($i)
488 $this->verifyhost = $i;
491 function send($msg, $timeout=0, $method='http')
495 // $msg is an array of xmlrpcmsg's
496 return $this->multicall($msg, $timeout, $method);
499 // where msg is an xmlrpcmsg
500 $msg->debug=$this->debug;
502 if ($method == 'https')
504 return $this->sendPayloadHTTPS($msg,
506 $this->port, $timeout,
507 $this->username, $this->password,
513 return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
514 $timeout, $this->username,
519 function sendPayloadHTTP10($msg, $server, $port, $timeout=0,$username='', $password='')
521 global $xmlrpcerr, $xmlrpcstr;
528 $fp=fsockopen($server, $port,$this->errno, $this->errstr, $timeout);
529 stream_set_timeout($fp,360);
534 $fp=fsockopen($server, $port,$this->errno, $this->errstr);
535 stream_set_timeout($fp,360);
539 $this->errstr='Connect error';
540 $r=new xmlrpcresp(0, $xmlrpcerr['http_error'],$xmlrpcstr['http_error']);
543 // Only create the payload if it was not created previously
544 if(empty($msg->payload))
546 $msg->createPayload();
549 // thanks to Grant Rauscher <grant7@firstworld.net>
554 $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
557 $op= "POST " . $this->path. " HTTP/1.0\r\nUser-Agent: PHP XMLRPC 1.0\r\n" .
558 "Host: ". $this->server . "\r\n" .
560 "Content-Type: text/xml\r\nContent-Length: " .
561 strlen($msg->payload) . "\r\n\r\n" .
564 if (!fputs($fp, $op, strlen($op)))
566 $this->errstr='Write error';
567 $r=new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']);
570 $resp=$msg->parseResponseFile($fp);
575 // contributed by Justin Miller <justin@voxel.net>
576 // requires curl to be built into PHP
577 function sendPayloadHTTPS($msg, $server, $port, $timeout=240,$username='', $password='', $cert='',$certpass='')
579 global $xmlrpcerr, $xmlrpcstr;
585 // Only create the payload if it was not created previously
586 if(empty($msg->payload))
588 $msg->createPayload();
591 if (!function_exists('curl_init'))
593 $this->errstr='SSL unavailable on this install';
594 $r=new xmlrpcresp(0, $xmlrpcerr['no_ssl'], $xmlrpcstr['no_ssl']);
598 $curl = curl_init('https://' . $server . ':' . $port . $this->path);
600 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
601 // results into variable
604 curl_setopt($curl, CURLOPT_VERBOSE, 1);
606 curl_setopt($curl, CURLOPT_USERAGENT, 'PHP XMLRPC 1.0');
607 // required for XMLRPC
608 curl_setopt($curl, CURLOPT_POST, 1);
610 curl_setopt($curl, CURLOPT_POSTFIELDS, $msg->payload);
612 curl_setopt($curl, CURLOPT_HEADER, 1);
613 // return the header too
614 curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
615 // whether to verify remote host's cert
616 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
617 // 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
618 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
619 // required for XMLRPC
622 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
625 if ($username && $password)
627 curl_setopt($curl, CURLOPT_USERPWD,"$username:$password");
632 curl_setopt($curl, CURLOPT_SSLCERT, $cert);
637 curl_setopt($curl, CURLOPT_SSLCERTPASSWD,$certpass);
641 $result = curl_exec($curl);
645 $this->errstr='no response';
646 $resp=new xmlrpcresp(0, $xmlrpcerr['curl_fail'], $xmlrpcstr['curl_fail']. ': '. curl_error($curl));
650 $resp = $msg->parseResponse($result);
656 function multicall($msgs, $timeout=240, $method='http')
660 if (! $this->no_multicall)
662 $results = $this->_try_multicall($msgs, $timeout, $method);
663 /* TODO - this is not php3-friendly */
664 // if($results !== false)
665 if($results != false)
667 // Either the system.multicall succeeded, or the send
668 // failed (e.g. due to HTTP timeout). In either case,
669 // we're done for now.
674 // system.multicall unsupported by server,
675 // don't try it next time...
676 $this->no_multicall = true;
680 // system.multicall is unupported by server:
681 // Emulate multicall via multiple requests
683 //foreach($msgs as $msg)
685 while(list(,$msg) = @each($msgs))
687 $results[] = $this->send($msg, $timeout, $method);
692 // Attempt to boxcar $msgs via system.multicall.
693 function _try_multicall($msgs, $timeout, $method)
695 // Construct multicall message
697 //foreach($msgs as $msg)
699 while(list(,$msg) = @each($msgs))
701 $call['methodName'] = new xmlrpcval($msg->method(),'string');
702 $numParams = $msg->getNumParams();
704 for ($i = 0; $i < $numParams; $i++)
706 $params[$i] = $msg->getParam($i);
708 $call['params'] = new xmlrpcval($params, 'array');
709 $calls[] = new xmlrpcval($call, 'struct');
711 $multicall = new xmlrpcmsg('system.multicall');
712 $multicall->addParam(new xmlrpcval($calls, 'array'));
715 $result = $this->send($multicall, $timeout, $method);
716 if (!is_object($result))
717 return ($result || 0); // transport failed
719 if ($result->faultCode() != 0)
720 return false; // system.multicall failed
723 $rets = $result->value();
724 if ($rets->kindOf() != 'array')
725 return false; // bad return type from system.multicall
726 $numRets = $rets->arraysize();
727 if ($numRets != count($msgs))
728 return false; // wrong number of return values.
731 for ($i = 0; $i < $numRets; $i++)
733 $val = $rets->arraymem($i);
734 switch ($val->kindOf())
737 if ($val->arraysize() != 1)
738 return false; // Bad value
739 // Normal return value
740 $response[$i] = new xmlrpcresp($val->arraymem(0));
743 $code = $val->structmem('faultCode');
744 if ($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
746 $str = $val->structmem('faultString');
747 if ($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
749 $response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
757 } // end class xmlrpc_client
766 function xmlrpcresp($val, $fcode = 0, $fstr = '')
771 $this->errno = $fcode;
772 $this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
774 else if (!is_object($val))
777 error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to xmlrpcresp. Defaulting to empty value.");
778 $this->val = new xmlrpcval();
792 function faultString()
794 return $this->errstr;
804 $result = "<methodResponse>\n";
811 <name>faultCode</name>
812 <value><int>' . $this->errno . '</int></value>
815 <name>faultString</name>
816 <value><string>' . $this->errstr . '</string></value>
824 $result .= "<params>\n<param>\n" .
825 $this->val->serialize() .
826 "</param>\n</params>";
828 $result .= "\n</methodResponse>";
840 function xmlrpcmsg($meth, $pars=0)
842 $this->methodname=$meth;
843 if (is_array($pars) && sizeof($pars)>0)
845 for($i=0; $i<sizeof($pars); $i++)
847 $this->addParam($pars[$i]);
852 function xml_header()
854 return "<?xml version=\"1.0\"?>\n<methodCall>\n";
857 function xml_footer()
859 return "</methodCall>\n";
862 function createPayload()
864 $this->payload=$this->xml_header();
865 $this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
866 // if (sizeof($this->params)) {
867 $this->payload.="<params>\n";
868 for($i=0; $i<sizeof($this->params); $i++)
870 $p=$this->params[$i];
871 $this->payload.="<param>\n" . $p->serialize() .
874 $this->payload.="</params>\n";
876 $this->payload.=$this->xml_footer();
877 $this->payload=str_replace("\n", "\r\n", $this->payload);
880 function method($meth='')
884 $this->methodname=$meth;
886 return $this->methodname;
891 $this->createPayload();
892 return $this->payload;
895 function addParam($par) { $this->params[]=$par; }
896 function getParam($i) { return $this->params[$i]; }
897 function getNumParams() { return sizeof($this->params); }
899 function parseResponseFile($fp)
902 while($data=fread($fp, 32768))
906 return $this->parseResponse($ipd);
909 function parseResponse($data='')
911 global $_xh,$xmlrpcerr,$xmlrpcstr;
912 global $xmlrpc_defencoding;
914 $parser = xml_parser_create($xmlrpc_defencoding);
916 $_xh[$parser]=array();
918 $_xh[$parser]['st']='';
919 $_xh[$parser]['cm']=0;
920 $_xh[$parser]['isf']=0;
921 $_xh[$parser]['ac']='';
922 $_xh[$parser]['qt']='';
923 $_xh[$parser]['headers'] = array();
925 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
926 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
927 xml_set_character_data_handler($parser, 'xmlrpc_cd');
928 xml_set_default_handler($parser, 'xmlrpc_dh');
929 $xmlrpc_value=new xmlrpcval;
934 //by maHo, replaced htmlspecialchars with htmlentities
935 print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
940 error_log('No response received from server.');
941 $r = new xmlrpcresp(0, $xmlrpcerr['no_data'], $xmlrpcstr['no_data']);
942 xml_parser_free($parser);
945 // see if we got an HTTP 200 OK, else bomb
946 // but only do this if we're using the HTTP protocol.
947 if(ereg("^HTTP",$data) && !ereg("^HTTP/[0-9\.]+ 200 ", $data))
949 $errstr= substr($data, 0, strpos($data, "\n")-1);
950 error_log('HTTP error, got response: ' .$errstr);
951 $r=new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']. ' (' . $errstr . ')');
952 xml_parser_free($parser);
956 // separate HTTP headers from data
957 if (ereg("^HTTP", $data))
959 $ar = split("\r\n", $data);
960 while (($line = array_shift($ar)))
962 if (strlen($line) < 1)
966 $_xh[$parser]['headers'][] = $line;
968 $data = join("\r\n", $ar);
971 if ($this->debug && count($_xh[$parser]['headers']))
974 foreach ($_xh[$parser]['headers'] as $header)
976 print "HEADER: $header\n";
981 if (!xml_parse($parser, $data, sizeof($data)))
983 // thanks to Peter Kocks <peter.kocks@baygate.com>
984 if((xml_get_current_line_number($parser)) == 1)
986 $errstr = 'XML error at line 1, check URL';
990 $errstr = sprintf('XML error: %s at line %d',
991 xml_error_string(xml_get_error_code($parser)),
992 xml_get_current_line_number($parser));
994 $r=new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return']);
995 xml_parser_free($parser);
1000 xml_parser_free($parser);
1003 print "<PRE>---EVALING---[" .
1004 strlen($_xh[$parser]['st']) . " chars]---\n" .
1005 htmlspecialchars($_xh[$parser]['st']) . ";\n---END---</PRE>";
1007 if (strlen($_xh[$parser]['st'])==0)
1009 // then something odd has happened
1010 // and it's time to generate a client side error
1011 // indicating something odd went on
1012 $r=new xmlrpcresp(0, $xmlrpcerr['invalid_return'],
1013 $xmlrpcstr['invalid_return']);
1017 eval('$v=' . $_xh[$parser]['st'] . '; $allOK=1;');
1018 if ($_xh[$parser]['isf'])
1020 $errno_v = $v->structmem('faultCode');
1021 $errstr_v = $v->structmem('faultString');
1022 $errno = $errno_v->scalarval();
1026 // FAULT returned, errno needs to reflect that
1030 $r = new xmlrpcresp($v, $errno, $errstr_v->scalarval());
1034 $r=new xmlrpcresp($v);
1038 $r->hdrs = $_xh[$parser]['headers'];
1048 function xmlrpcval($val=-1, $type='')
1056 XMLRPCDATETIME => 1,
1067 if (gettype($val) == "string") {
1069 $this->addScalar($val,$type);
1071 elseif (gettype($val) == "int") {
1073 $this->addScalar($val,$type);
1075 elseif (gettype($val) == "boolean") {
1077 $this->addScalar($val,$type);
1079 elseif (is_array($val)) {
1081 $this->addArray($val);
1085 $this->addScalar($val,$type);
1088 else if ($type == "i4") {
1089 $val = intval($val);
1090 $this->addScalar($val,$type);
1092 else if ($val!=-1 || $type!='')
1095 if ($xmlrpcTypes[$type]==1)
1097 $this->addScalar($val,$type);
1099 elseif ($xmlrpcTypes[$type]==2)
1102 $this->addArray($val);
1104 elseif ($xmlrpcTypes[$type]==3)
1106 $this->addStruct($val);
1111 function addScalar($val, $type='string')
1113 global $xmlrpcTypes, $xmlrpcBoolean;
1115 if ($this->mytype==1)
1117 echo '<B>xmlrpcval</B>: scalar can have only one value<BR>';
1120 $typeof=$xmlrpcTypes[$type];
1123 echo '<B>xmlrpcval</B>: not a scalar type (${typeof})<BR>';
1127 if ($type==$xmlrpcBoolean)
1129 if (strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
1139 if ($this->mytype==2)
1141 // we're adding to an array here
1142 $ar=$this->me['array'];
1143 $ar[]=new xmlrpcval($val, $type);
1144 $this->me['array']=$ar;
1148 // a scalar, so set the value and remember we're scalar
1149 $this->me[$type]=$val;
1150 $this->mytype=$typeof;
1155 function addArray($vals)
1157 global $xmlrpcTypes;
1158 if ($this->mytype!=0)
1160 echo '<B>xmlrpcval</B>: already initialized as a [' . $this->kindOf() . ']<BR>';
1164 $this->mytype=$xmlrpcTypes['array'];
1165 $this->me['array']=$vals;
1169 function addStruct($vals)
1172 global $xmlrpcTypes;
1180 XMLRPCDATETIME => 1,
1185 if ($this->mytype!=0)
1187 echo '<B>xmlrpcval</B>: already initialized as a [' . $this->kindOf() . ']<BR>';
1190 $this->mytype=$xmlrpcTypes['struct'];
1191 $this->me['struct']=$vals;
1198 while ( list( $key, $val ) = each( $ar ) )
1200 echo "$key => $val<br>";
1201 if ($key == 'array')
1203 while ( list( $key2, $val2 ) = each( $val ) )
1205 echo "-- $key2 => $val2<br>";
1213 switch($this->mytype)
1229 function serializedata($typ, $val)
1232 global $xmlrpcTypes, $xmlrpcBase64, $xmlrpcString,
1234 switch($xmlrpcTypes[$typ])
1240 while(list($key2, $val2)=each($val))
1242 $rs.="<member><name>${key2}</name>\n";
1243 $rs.=$this->serializeval($val2);
1250 $rs.="<array>\n<data>\n";
1251 for($i=0; $i<sizeof($val); $i++) {
1252 $keys = array_keys($val);
1253 $rs.=$this->serializeval($val[$keys[$i]]);
1255 $rs.="</data>\n</array>";
1261 $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
1263 case $xmlrpcBoolean:
1264 $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
1267 $rs.="<${typ}>" . @htmlspecialchars($val). "</${typ}>";
1270 $rs.="<${typ}>${val}</${typ}>";
1279 function serialize()
1281 return $this->serializeval($this);
1284 function serializeval($o)
1286 global $xmlrpcTypes;
1288 if (is_object($o)) {
1290 list($typ,$val) = each($ar);
1294 if (is_array($ar)) {
1296 $ak = array_keys($ar);
1297 if (gettype($ak[0]) == "string" ) {
1312 $rs.=$this->serializedata($typ, $val);
1317 function structmem($m)
1319 $nv=$this->me['struct'][$m];
1323 function structreset()
1325 reset($this->me['struct']);
1328 function structeach()
1330 return each($this->me['struct']);
1336 global $xmlrpcBoolean, $xmlrpcBase64;
1338 list($a,$b)=each($this->me);
1339 // contributed by I Sofer, 2001-03-24
1340 // add support for nested arrays to scalarval
1341 // i've created a new method here, so as to
1342 // preserve back compatibility
1347 while(list($id,$cont) = @each($b))
1349 if (is_object($cont) && get_class($cont) == "xmlrpcval") {
1350 $b[$id] = $cont->getval();
1353 $b[$id] = $cont->scalarval();
1358 // add support for structures directly encoding php objects
1361 $t = get_object_vars($b);
1363 while(list($id,$cont) = @each($t))
1365 if (get_class($b) == "xmlrpcval") {
1366 $t[$id] = $cont->getval();
1369 $t[$id] = $cont->scalarval();
1373 while(list($id,$cont) = @each($t))
1375 eval('$b->'.$id.' = $cont;');
1382 function scalarval()
1384 global $xmlrpcBoolean, $xmlrpcBase64;
1386 list($a,$b)=each($this->me);
1390 function scalartyp()
1392 global $xmlrpcI4, $xmlrpcInt;
1394 list($a,$b)=each($this->me);
1402 function arraymem($m)
1404 $nv=$this->me['array'][$m];
1408 function arraysize()
1411 list($a,$b)=each($this->me);
1417 function iso8601_encode($timet, $utc=0)
1419 // return an ISO8601 encoded string
1420 // really, timezones ought to be supported
1421 // but the XML-RPC spec says:
1423 // "Don't assume a timezone. It should be specified by the server in its
1424 // documentation what assumptions it makes about timezones."
1426 // these routines always assume localtime unless
1427 // $utc is set to 1, in which case UTC is assumed
1428 // and an adjustment for locale is made when encoding
1431 $t=strftime("%Y%m%dT%H:%M:%S", $timet);
1435 if (function_exists('gmstrftime'))
1437 // gmstrftime doesn't exist in some versions
1439 $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
1443 $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
1449 function iso8601_decode($idate, $utc=0)
1451 // return a timet in the localtime, or UTC
1453 if (ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})", $idate, $regs))
1457 $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1461 $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1467 /****************************************************************
1468 * xmlrpc_decode takes a message in PHP xmlrpc object format and *
1469 * tranlates it into native PHP types. *
1471 * author: Dan Libby (dan@libby.com) *
1472 ****************************************************************/
1473 function xmlrpc_decode_php($xmlrpc_val)
1475 $kind = $xmlrpc_val->kindOf();
1477 if($kind == 'scalar')
1479 return $xmlrpc_val->scalarval();
1481 elseif($kind == 'array')
1483 $size = $xmlrpc_val->arraysize();
1486 for($i = 0; $i < $size; $i++)
1488 $arr[]=xmlrpc_decode_php($xmlrpc_val->arraymem($i));
1492 elseif($kind == 'struct')
1494 $xmlrpc_val->structreset();
1497 while(list($key,$value)=$xmlrpc_val->structeach())
1499 $arr[$key] = xmlrpc_decode_php($value);
1505 /****************************************************************
1506 * xmlrpc_encode takes native php types and encodes them into *
1507 * xmlrpc PHP object format. *
1508 * BUG: All sequential arrays are turned into structs. I don't *
1509 * know of a good way to determine if an array is sequential *
1512 * feature creep -- could support more types via optional type *
1515 * author: Dan Libby (dan@libby.com) *
1516 ****************************************************************/
1517 function xmlrpc_encode_php($php_val)
1520 global $xmlrpcDouble;
1521 global $xmlrpcString;
1522 global $xmlrpcArray;
1523 global $xmlrpcStruct;
1524 global $xmlrpcBoolean;
1526 $type = gettype($php_val);
1527 $xmlrpc_val = new xmlrpcval;
1534 while (list($k,$v) = each($php_val))
1536 $arr[$k] = xmlrpc_encode_php($v);
1539 $xmlrpc_val->addStruct($arr);
1542 $xmlrpc_val->addScalar($php_val, $xmlrpcInt);
1545 $xmlrpc_val->addScalar($php_val, $xmlrpcDouble);
1548 $xmlrpc_val->addScalar($php_val, $xmlrpcString);
1550 // <G_Giunta_2001-02-29>
1551 // Add support for encoding/decoding of booleans, since they are supported in PHP
1553 $xmlrpc_val->addScalar($php_val, $xmlrpcBoolean);
1555 // </G_Giunta_2001-02-29>
1556 case 'unknown type':
1558 // giancarlo pinerolo <ping@alt.it>
1560 // an empty object in case (which is already
1561 // at this point), not a boolean.