OSDN Git Service

This commit was generated by cvs2svn to compensate for changes in r4,
[nucleus-jp/nucleus-jp-ancient.git] / euc / nucleus / libs / xmlrpc.inc.php
1 <?php                                   // -*-c++-*-
2 // by Edd Dumbill (C) 1999-2001
3 // <edd@usefulinc.com>
4 // $Id: xmlrpc.inc.php,v 1.1.1.1 2005-02-28 07:14:15 kimitake Exp $
5
6 /*
7         Modifications made for use with Nucleus:
8         
9         renamed methods:
10         xmlrpc_encode -> _xmlrpc_encode
11         xmlrpc_decode -> _xmlrpc_decode
12 */
13
14
15 // Copyright (c) 1999,2000,2001 Edd Dumbill.
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions
20 // are met:
21 //
22 //    * Redistributions of source code must retain the above copyright
23 //      notice, this list of conditions and the following disclaimer.
24 //
25 //    * Redistributions in binary form must reproduce the above
26 //      copyright notice, this list of conditions and the following
27 //      disclaimer in the documentation and/or other materials provided
28 //      with the distribution.
29 //
30 //    * Neither the name of the "XML-RPC for PHP" nor the names of its
31 //      contributors may be used to endorse or promote products derived
32 //      from this software without specific prior written permission.
33 //
34 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
35 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
36 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
37 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
38 // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
39 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
41 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
43 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
45 // OF THE POSSIBILITY OF SUCH DAMAGE.
46
47 if (!function_exists('xml_parser_create')) {
48 // Win 32 fix. From: "Leo West" <lwest@imaginet.fr>
49         if($WINDIR) {
50                 dl("php3_xml.dll");
51         } else {
52                 dl("xml.so");
53         }
54 }
55
56 $xmlrpcI4="i4";
57 $xmlrpcInt="int";
58 $xmlrpcBoolean="boolean";
59 $xmlrpcDouble="double";
60 $xmlrpcString="string";
61 $xmlrpcDateTime="dateTime.iso8601";
62 $xmlrpcBase64="base64";
63 $xmlrpcArray="array";
64 $xmlrpcStruct="struct";
65
66
67 $xmlrpcTypes=array($xmlrpcI4 => 1,
68                                    $xmlrpcInt => 1,
69                                    $xmlrpcBoolean => 1,
70                                    $xmlrpcString => 1,
71                                    $xmlrpcDouble => 1,
72                                    $xmlrpcDateTime => 1,
73                                    $xmlrpcBase64 => 1,
74                                    $xmlrpcArray => 2,
75                                    $xmlrpcStruct => 3);
76
77 $xmlEntities=array(      "amp" => "&",
78                                                                          "quot" => '"',
79                                                                          "lt" => "<",
80                                                                          "gt" => ">",
81                                                                          "apos" => "'");
82
83 $xmlrpcerr["unknown_method"]=1;
84 $xmlrpcstr["unknown_method"]="Unknown method";
85 $xmlrpcerr["invalid_return"]=2;
86 $xmlrpcstr["invalid_return"]="Invalid return payload: enabling debugging to examine incoming payload";
87 $xmlrpcerr["incorrect_params"]=3;
88 $xmlrpcstr["incorrect_params"]="Incorrect parameters passed to method";
89 $xmlrpcerr["introspect_unknown"]=4;
90 $xmlrpcstr["introspect_unknown"]="Can't introspect: method unknown";
91 $xmlrpcerr["http_error"]=5;
92 $xmlrpcstr["http_error"]="Didn't receive 200 OK from remote server.";
93 $xmlrpcerr["no_data"]=6;
94 $xmlrpcstr["no_data"]="No data received from server.";
95 $xmlrpcerr["no_ssl"]=7;
96 $xmlrpcstr["no_ssl"]="No SSL support compiled in.";
97 $xmlrpcerr["curl_fail"]=8;
98 $xmlrpcstr["curl_fail"]="CURL error";
99
100 $xmlrpc_defencoding="UTF-8";
101
102 $xmlrpcName="XML-RPC for PHP";
103 $xmlrpcVersion="1.02";
104
105 // let user errors start at 800
106 $xmlrpcerruser=800; 
107 // let XML parse errors start at 100
108 $xmlrpcerrxml=100;
109
110 // formulate backslashes for escaping regexp
111 $xmlrpc_backslash=chr(92).chr(92);
112
113 // used to store state during parsing
114 // quick explanation of components:
115 //   st - used to build up a string for evaluation
116 //   ac - used to accumulate values
117 //   qt - used to decide if quotes are needed for evaluation
118 //   cm - used to denote struct or array (comma needed)
119 //   isf - used to indicate a fault
120 //   lv - used to indicate "looking for a value": implements
121 //        the logic to allow values with no types to be strings
122 //   params - used to store parameters in method calls
123 //   method - used to store method name
124
125 $_xh=array();
126
127 function xmlrpc_entity_decode($string) {
128   $top=split("&", $string);
129   $op="";
130   $i=0; 
131   while($i<sizeof($top)) {
132         if (ereg("^([#a-zA-Z0-9]+);", $top[$i], $regs)) {
133           $op.=ereg_replace("^[#a-zA-Z0-9]+;",
134                                                 xmlrpc_lookup_entity($regs[1]),
135                                                                                         $top[$i]);
136         } else {
137           if ($i==0) 
138                 $op=$top[$i]; 
139           else
140                 $op.="&" . $top[$i];
141         }
142         $i++;
143   }
144   return $op;
145 }
146
147 function xmlrpc_lookup_entity($ent) {
148   global $xmlEntities;
149   
150   if (isset($xmlEntities[strtolower($ent)]))
151         return $xmlEntities[strtolower($ent)];
152   if (ereg("^#([0-9]+)$", $ent, $regs))
153         return chr($regs[1]);
154   return "?";
155 }
156
157 function xmlrpc_se($parser, $name, $attrs) {
158         global $_xh, $xmlrpcDateTime, $xmlrpcString;
159         
160         switch($name) {
161         case "STRUCT":
162         case "ARRAY":
163           $_xh[$parser]['st'].="array(";
164           $_xh[$parser]['cm']++;
165                 // this last line turns quoting off
166                 // this means if we get an empty array we'll 
167                 // simply get a bit of whitespace in the eval
168                 $_xh[$parser]['qt']=0;
169           break;
170         case "NAME":
171           $_xh[$parser]['st'].="'"; $_xh[$parser]['ac']="";
172           break;
173         case "FAULT":
174           $_xh[$parser]['isf']=1;
175           break;
176         case "PARAM":
177           $_xh[$parser]['st']="";
178           break;
179         case "VALUE":
180           $_xh[$parser]['st'].="new xmlrpcval("; 
181                 $_xh[$parser]['vt']=$xmlrpcString;
182                 $_xh[$parser]['ac']="";
183                 $_xh[$parser]['qt']=0;
184           $_xh[$parser]['lv']=1;
185           // look for a value: if this is still 1 by the
186           // time we reach the first data segment then the type is string
187           // by implication and we need to add in a quote
188                 break;
189
190         case "I4":
191         case "INT":
192         case "STRING":
193         case "BOOLEAN":
194         case "DOUBLE":
195         case "DATETIME.ISO8601":
196         case "BASE64":
197           $_xh[$parser]['ac']=""; // reset the accumulator
198
199           if ($name=="DATETIME.ISO8601" || $name=="STRING") {
200                         $_xh[$parser]['qt']=1; 
201                         if ($name=="DATETIME.ISO8601")
202                                 $_xh[$parser]['vt']=$xmlrpcDateTime;
203           } else if ($name=="BASE64") {
204                         $_xh[$parser]['qt']=2;
205                 } else {
206                         // No quoting is required here -- but
207                         // at the end of the element we must check
208                         // for data format errors.
209                         $_xh[$parser]['qt']=0;
210           }
211                 break;
212         case "MEMBER":
213                 $_xh[$parser]['ac']="";
214           break;
215         default:
216                 break;
217         }
218
219         if ($name!="VALUE") $_xh[$parser]['lv']=0;
220 }
221
222 function xmlrpc_ee($parser, $name) {
223         global $_xh,$xmlrpcTypes,$xmlrpcString;
224
225         switch($name) {
226         case "STRUCT":
227         case "ARRAY":
228           if ($_xh[$parser]['cm'] && substr($_xh[$parser]['st'], -1) ==',') {
229                 $_xh[$parser]['st']=substr($_xh[$parser]['st'],0,-1);
230           }
231           $_xh[$parser]['st'].=")";     
232           $_xh[$parser]['vt']=strtolower($name);
233           $_xh[$parser]['cm']--;
234           break;
235         case "NAME":
236           $_xh[$parser]['st'].= $_xh[$parser]['ac'] . "' => ";
237           break;
238         case "BOOLEAN":
239                 // special case here: we translate boolean 1 or 0 into PHP
240                 // constants true or false
241                 if ($_xh[$parser]['ac']=='1') 
242                         $_xh[$parser]['ac']="true";
243                 else 
244                         $_xh[$parser]['ac']="false";
245                 $_xh[$parser]['vt']=strtolower($name);
246                 // Drop through intentionally.
247         case "I4":
248         case "INT":
249         case "STRING":
250         case "DOUBLE":
251         case "DATETIME.ISO8601":
252         case "BASE64":
253           if ($_xh[$parser]['qt']==1) {
254                         // we use double quotes rather than single so backslashification works OK
255                         $_xh[$parser]['st'].="\"". $_xh[$parser]['ac'] . "\""; 
256                 } else if ($_xh[$parser]['qt']==2) {
257                         $_xh[$parser]['st'].="base64_decode('". $_xh[$parser]['ac'] . "')"; 
258                 } else if ($name=="BOOLEAN") {
259                         $_xh[$parser]['st'].=$_xh[$parser]['ac'];
260                 } else {
261                         // we have an I4, INT or a DOUBLE
262                         // we must check that only 0123456789-.<space> are characters here
263                         if (!ereg("^\-?[0123456789 \t\.]+$", $_xh[$parser]['ac'])) {
264                                 // TODO: find a better way of throwing an error
265                                 // than this!
266                                 error_log("XML-RPC: non numeric value received in INT or DOUBLE");
267                                 $_xh[$parser]['st'].="ERROR_NON_NUMERIC_FOUND";
268                         } else {
269                                 // it's ok, add it on
270                                 $_xh[$parser]['st'].=$_xh[$parser]['ac'];
271                         }
272                 }
273                 $_xh[$parser]['ac']=""; $_xh[$parser]['qt']=0;
274                 $_xh[$parser]['lv']=3; // indicate we've found a value
275           break;
276         case "VALUE":
277                 // deal with a string value
278                 if (strlen($_xh[$parser]['ac'])>0 &&
279                                 $_xh[$parser]['vt']==$xmlrpcString) {
280                         $_xh[$parser]['st'].="\"". $_xh[$parser]['ac'] . "\""; 
281                 }
282                 // This if() detects if no scalar was inside <VALUE></VALUE>
283                 // and pads an empty "".
284                 if($_xh[$parser]['st'][strlen($_xh[$parser]['st'])-1] == '(') {
285                         $_xh[$parser]['st'].= '""';
286                 }
287                 $_xh[$parser]['st'].=", '" . $_xh[$parser]['vt'] . "')";
288                 if ($_xh[$parser]['cm']) $_xh[$parser]['st'].=",";
289                 break;
290         case "MEMBER":
291           $_xh[$parser]['ac']=""; $_xh[$parser]['qt']=0;
292          break;
293         case "DATA":
294           $_xh[$parser]['ac']=""; $_xh[$parser]['qt']=0;
295           break;
296         case "PARAM":
297           $_xh[$parser]['params'][]=$_xh[$parser]['st'];
298           break;
299         case "METHODNAME":
300           $_xh[$parser]['method']=ereg_replace("^[\n\r\t ]+", "", 
301                                                                                                                                                                  $_xh[$parser]['ac']);
302                 break;
303         case "BOOLEAN":
304                 // special case here: we translate boolean 1 or 0 into PHP
305                 // constants true or false
306                 if ($_xh[$parser]['ac']=='1') 
307                         $_xh[$parser]['ac']="true";
308                 else 
309                         $_xh[$parser]['ac']="false";
310                 $_xh[$parser]['vt']=strtolower($name);
311                 break;
312         default:
313                 break;
314         }
315         // if it's a valid type name, set the type
316         if (isset($xmlrpcTypes[strtolower($name)])) {
317                 $_xh[$parser]['vt']=strtolower($name);
318         }
319         
320 }
321
322 function xmlrpc_cd($parser, $data)
323 {       
324   global $_xh, $xmlrpc_backslash;
325
326   //if (ereg("^[\n\r \t]+$", $data)) return;
327   // print "adding [${data}]\n";
328
329         if ($_xh[$parser]['lv']!=3) {
330                 // "lookforvalue==3" means that we've found an entire value
331                 // and should discard any further character data
332                 if ($_xh[$parser]['lv']==1) {  
333                         // if we've found text and we're just in a <value> then
334                         // turn quoting on, as this will be a string
335                         $_xh[$parser]['qt']=1; 
336                         // and say we've found a value
337                         $_xh[$parser]['lv']=2; 
338                 }
339         // replace characters that eval would
340         // do special things with
341         $_xh[$parser]['ac'].=str_replace('$', '\$',
342                 str_replace('"', '\"', str_replace(chr(92),
343                         $xmlrpc_backslash, $data)));
344         }
345 }
346
347 function xmlrpc_dh($parser, $data)
348 {
349   global $_xh;
350   if (substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") {
351                 if ($_xh[$parser]['lv']==1) {  
352                         $_xh[$parser]['qt']=1; 
353                         $_xh[$parser]['lv']=2; 
354                 }
355                 $_xh[$parser]['ac'].=str_replace('$', '\$',
356                                 str_replace('"', '\"', str_replace(chr(92),
357                                         $xmlrpc_backslash, $data)));
358   }
359 }
360
361 class xmlrpc_client {
362   var $path;
363   var $server;
364   var $port;
365   var $errno;
366   var $errstring;
367   var $debug=0;
368   var $username="";
369   var $password="";
370   var $cert="";
371   var $certpass="";
372   
373   function xmlrpc_client($path, $server, $port=0) {
374     $this->port=$port; $this->server=$server; $this->path=$path;
375   }
376
377   function setDebug($in) {
378                 if ($in) { 
379                         $this->debug=1;
380                 } else {
381                         $this->debug=0;
382                 }
383   }
384
385   function setCredentials($u, $p) {
386     $this->username=$u;
387     $this->password=$p;
388   }
389
390   function setCertificate($cert, $certpass) {
391     $this->cert = $cert;
392     $this->certpass = $certpass;
393   }
394
395   function send($msg, $timeout=0, $method='http') {
396     // where msg is an xmlrpcmsg
397     $msg->debug=$this->debug;
398  
399     if ($method == 'https') {
400       return $this->sendPayloadHTTPS($msg,
401                                      $this->server,
402                                      $this->port, $timeout,
403                                      $this->username, $this->password,
404                                      $this->cert,
405                                      $this->certpass);
406     } else {
407       return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
408                                       $timeout, $this->username, 
409                                       $this->password);
410     }
411   }
412
413   function sendPayloadHTTP10($msg, $server, $port, $timeout=0,
414                              $username="", $password="") {
415     if ($port==0) $port=80;
416     if($timeout>0)
417       $fp=@fsockopen($server, $port,
418                     $this->errno, $this->errstr, $timeout);
419     else
420       $fp=@fsockopen($server, $port,
421                     $this->errno, $this->errstr);
422     if (!$fp) {   
423       return 0;
424     }
425     // Only create the payload if it was not created previously
426     if(empty($msg->payload)) $msg->createPayload();
427     
428     // thanks to Grant Rauscher <grant7@firstworld.net>
429     // for this
430     $credentials="";
431     if ($username!="") {
432       $credentials="Authorization: Basic " .
433         base64_encode($username . ":" . $password) . "\r\n";
434     }
435     
436     $op= "POST " . $this->path. " HTTP/1.0\r\nUser-Agent: PHP XMLRPC 1.0\r\n" .
437       "Host: ". $this->server  . "\r\n" .
438       $credentials . 
439       "Content-Type: text/xml\r\nContent-Length: " .
440       strlen($msg->payload) . "\r\n\r\n" .
441       $msg->payload;
442     
443     if (!fputs($fp, $op, strlen($op))) {
444       $this->errstr="Write error";
445       return 0;
446     }
447     $resp=$msg->parseResponseFile($fp);
448     fclose($fp);
449     return $resp;
450   }
451
452   // contributed by Justin Miller <justin@voxel.net>
453   // requires curl to be built into PHP
454   function sendPayloadHTTPS($msg, $server, $port, $timeout=0,
455                             $username="", $password="", $cert="",
456                             $certpass="") {
457     global $xmlrpcerr, $xmlrpcstr;
458     if ($port == 0) $port = 443;
459     
460     // Only create the payload if it was not created previously
461     if(empty($msg->payload)) $msg->createPayload();
462     
463     if (!function_exists("curl_init")) {
464       $r=new xmlrpcresp(0, $xmlrpcerr["no_ssl"],
465                         $xmlrpcstr["no_ssl"]);
466       return $r;
467     }
468
469     $curl = curl_init("https://" . $server . ':' . $port .
470                       $this->path);
471     
472     curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
473     // results into variable
474     if ($this->debug) {
475       curl_setopt($curl, CURLOPT_VERBOSE, 1);
476     }
477     curl_setopt($curl, CURLOPT_USERAGENT, 'PHP XMLRPC 1.0');
478     // required for XMLRPC
479     curl_setopt($curl, CURLOPT_POST, 1);
480     // post the data
481     curl_setopt($curl, CURLOPT_POSTFIELDS, $msg->payload);
482     // the data
483     curl_setopt($curl, CURLOPT_HEADER, 1);
484     // return the header too
485     curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
486     // required for XMLRPC
487     if ($timeout) curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 :
488                               $timeout - 1);
489     // timeout is borked
490     if ($username && $password) curl_setopt($curl, CURLOPT_USERPWD,
491                                             "$username:$password"); 
492     // set auth stuff
493     if ($cert) curl_setopt($curl, CURLOPT_SSLCERT, $cert);
494     // set cert file
495     if ($certpass) curl_setopt($curl, CURLOPT_SSLCERTPASSWD,
496                        $certpass);                    
497     // set cert password
498     
499     $result = curl_exec($curl);
500     
501     if (!$result) {
502       $resp=new xmlrpcresp(0, 
503                            $xmlrpcerr["curl_fail"],
504                            $xmlrpcstr["curl_fail"]. ": ". 
505                            curl_error($curl));
506     } else {
507       $resp = $msg->parseResponse($result);
508     }
509     curl_close($curl);
510     return $resp;
511   }
512
513 } // end class xmlrpc_client
514
515 class xmlrpcresp {
516   var $xv;
517   var $fn;
518   var $fs;
519   var $hdrs;
520
521   function xmlrpcresp($val, $fcode=0, $fstr="") {
522     if ($fcode!=0) {
523       $this->xv=0;
524       $this->fn=$fcode;
525       $this->fs=htmlspecialchars($fstr);
526     } else {
527       $this->xv=$val;
528       $this->fn=0;
529     }
530   }
531
532   function faultCode() { 
533                 if (isset($this->fn)) 
534                         return $this->fn;
535                 else
536                         return 0; 
537         }
538
539   function faultString() { return $this->fs; }
540   function value() { return $this->xv; }
541
542   function serialize() { 
543         $rs="<methodResponse>\n";
544         if ($this->fn) {
545           $rs.="<fault>
546   <value>
547     <struct>
548       <member>
549         <name>faultCode</name>
550         <value><int>" . $this->fn . "</int></value>
551       </member>
552       <member>
553         <name>faultString</name>
554         <value><string>" . $this->fs . "</string></value>
555       </member>
556     </struct>
557   </value>
558 </fault>";
559         } else {
560           $rs.="<params>\n<param>\n" . $this->xv->serialize() . 
561                 "</param>\n</params>";
562         }
563         $rs.="\n</methodResponse>";
564         return $rs;
565   }
566 }
567
568 class xmlrpcmsg {
569   var $payload;
570   var $methodname;
571   var $params=array();
572   var $debug=0;
573
574   function xmlrpcmsg($meth, $pars=0) {
575                 $this->methodname=$meth;
576                 if (is_array($pars) && sizeof($pars)>0) {
577                         for($i=0; $i<sizeof($pars); $i++) 
578                                 $this->addParam($pars[$i]);
579                 }
580   }
581
582   function xml_header() {
583         return "<" . "?xml version=\"1.0\"?".">\n<methodCall>\n";
584   }
585
586   function xml_footer() {
587         return "</methodCall>\n";
588   }
589
590   function createPayload() {
591         $this->payload=$this->xml_header();
592         $this->payload.="<methodName>" . $this->methodname . "</methodName>\n";
593         //      if (sizeof($this->params)) {
594           $this->payload.="<params>\n";
595           for($i=0; $i<sizeof($this->params); $i++) {
596                 $p=$this->params[$i];
597                 $this->payload.="<param>\n" . $p->serialize() .
598                   "</param>\n";
599           }
600           $this->payload.="</params>\n";
601         // }
602         $this->payload.=$this->xml_footer();
603         $this->payload=str_replace("\n", "\r\n", $this->payload);
604   }
605
606   function method($meth="") {
607         if ($meth!="") {
608           $this->methodname=$meth;
609         }
610         return $this->methodname;
611   }
612
613   function serialize() {
614                 $this->createPayload();
615                 return $this->payload;
616   }
617
618   function addParam($par) { $this->params[]=$par; }
619   function getParam($i) { return $this->params[$i]; }
620   function getNumParams() { return sizeof($this->params); }
621
622   function parseResponseFile($fp) {
623         $ipd="";
624
625         while($data=fread($fp, 32768)) {
626           $ipd.=$data;
627         }
628         return $this->parseResponse($ipd);
629   }
630
631   function parseResponse($data="") {
632         global $_xh,$xmlrpcerr,$xmlrpcstr;
633         global $xmlrpc_defencoding;
634
635         
636         $parser = xml_parser_create($xmlrpc_defencoding);
637
638         $_xh[$parser]=array();
639
640         $_xh[$parser]['st']=""; 
641         $_xh[$parser]['cm']=0; 
642         $_xh[$parser]['isf']=0; 
643         $_xh[$parser]['ac']="";
644         $_xh[$parser]['qt']="";
645         $_xh[$parser]['ha']="";
646         $_xh[$parser]['ac']="";
647
648         xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
649         xml_set_element_handler($parser, "xmlrpc_se", "xmlrpc_ee");
650         xml_set_character_data_handler($parser, "xmlrpc_cd");
651         xml_set_default_handler($parser, "xmlrpc_dh");
652         $xmlrpc_value=new xmlrpcval;
653
654         if ($this->debug)
655           print "<PRE>---GOT---\n" . htmlspecialchars($data) . 
656                 "\n---END---\n</PRE>";
657         if ($data=="") {
658           error_log("No response received from server.");
659           $r=new xmlrpcresp(0, $xmlrpcerr["no_data"],
660                             $xmlrpcstr["no_data"]);
661           xml_parser_free($parser);
662           return $r;
663         }
664         // see if we got an HTTP 200 OK, else bomb
665         // but only do this if we're using the HTTP protocol.
666         if (ereg("^HTTP",$data) && 
667                         !ereg("^HTTP/[0-9\.]+ 200 ", $data)) {
668                 $errstr= substr($data, 0, strpos($data, "\n")-1);
669                 error_log("HTTP error, got response: " .$errstr);
670                 $r=new xmlrpcresp(0, $xmlrpcerr["http_error"],
671                                   $xmlrpcstr["http_error"]. " (" . $errstr . ")");
672                 xml_parser_free($parser);
673                 return $r;
674         }
675
676         // if using HTTP, then gotta get rid of HTTP headers here
677         // and we store them in the 'ha' bit of our data array
678         if (ereg("^HTTP", $data)) {
679                 $ar=explode("\r\n", $data);
680                 $newdata="";
681                 $hdrfnd=0;
682                 for ($i=0; $i<sizeof($ar); $i++) {
683                         if (!$hdrfnd) {
684                                 if (strlen($ar[$i])>0) {
685                                         $_xh[$parser]['ha'].=$ar[$i]. "\r\n";
686                                 } else {
687                                         $hdrfnd=1; 
688                                 }
689                         } else {
690                                 $newdata.=$ar[$i] . "\r\n";
691                         }
692                 }
693                 $data=$newdata;
694         }
695         
696         if (!xml_parse($parser, $data, sizeof($data))) {
697                 // thanks to Peter Kocks <peter.kocks@baygate.com>
698                 if((xml_get_current_line_number($parser)) == 1)   
699                         $errstr = "XML error at line 1, check URL";
700                 else
701                         $errstr = sprintf("XML error: %s at line %d",
702                                                                                                 xml_error_string(xml_get_error_code($parser)),
703                                                                                                 xml_get_current_line_number($parser));
704                 error_log($errstr);
705                 $r=new xmlrpcresp(0, $xmlrpcerr["invalid_return"],
706                                                                                         $xmlrpcstr["invalid_return"]);
707                 xml_parser_free($parser);
708                 return $r;
709         }
710         xml_parser_free($parser);
711         if ($this->debug) {
712           print "<PRE>---EVALING---[" . 
713                 strlen($_xh[$parser]['st']) . " chars]---\n" . 
714                 htmlspecialchars($_xh[$parser]['st']) . ";\n---END---</PRE>";
715         }
716         if (strlen($_xh[$parser]['st'])==0) {
717           // then something odd has happened
718           // and it's time to generate a client side error
719           // indicating something odd went on
720           $r=new xmlrpcresp(0, $xmlrpcerr["invalid_return"],
721                                                 $xmlrpcstr["invalid_return"]);
722         } else {
723           eval('$v=' . $_xh[$parser]['st'] . '; $allOK=1;');
724           if ($_xh[$parser]['isf']) {
725                 $f=$v->structmem("faultCode");
726                 $fs=$v->structmem("faultString");
727                 $r=new xmlrpcresp($v, $f->scalarval(), 
728                                                   $fs->scalarval());
729           } else {
730                 $r=new xmlrpcresp($v);
731           }
732         }
733         $r->hdrs=split("\r?\n", $_xh[$parser]['ha']);
734         return $r;
735   }
736
737 }
738
739 class xmlrpcval {
740   var $me=array();
741   var $mytype=0;
742
743   function xmlrpcval($val=-1, $type="") {
744                 global $xmlrpcTypes;
745                 $this->me=array();
746                 $this->mytype=0;
747                 if ($val!=-1 || $type!="") {
748                         if ($type=="") $type="string";
749                         if ($xmlrpcTypes[$type]==1) {
750                                 $this->addScalar($val,$type);
751                         }
752           else if ($xmlrpcTypes[$type]==2)
753                         $this->addArray($val);
754                         else if ($xmlrpcTypes[$type]==3)
755                                 $this->addStruct($val);
756                 }
757   }
758
759   function addScalar($val, $type="string") {
760                 global $xmlrpcTypes, $xmlrpcBoolean;
761
762                 if ($this->mytype==1) {
763                         echo "<B>xmlrpcval</B>: scalar can have only one value<BR>";
764                         return 0;
765                 }
766                 $typeof=$xmlrpcTypes[$type];
767                 if ($typeof!=1) {
768                         echo "<B>xmlrpcval</B>: not a scalar type (${typeof})<BR>";
769                         return 0;
770                 }
771                 
772                 if ($type==$xmlrpcBoolean) {
773                         if (strcasecmp($val,"true")==0 || 
774                                         $val==1 || ($val==true &&
775                                                                                         strcasecmp($val,"false"))) {
776                                 $val=1;
777                         } else {
778                                 $val=0;
779                         }
780                 }
781                 
782                 if ($this->mytype==2) {
783                         // we're adding to an array here
784                         $ar=$this->me["array"];
785                         $ar[]=new xmlrpcval($val, $type);
786                         $this->me["array"]=$ar;
787                 } else {
788                         // a scalar, so set the value and remember we're scalar
789                         $this->me[$type]=$val;
790                         $this->mytype=$typeof;
791                 }
792                 return 1;
793   }
794
795   function addArray($vals) {
796                 global $xmlrpcTypes;
797                 if ($this->mytype!=0) {
798                         echo "<B>xmlrpcval</B>: already initialized as a [" . 
799                                 $this->kindOf() . "]<BR>";
800                         return 0;
801                 }
802
803                 $this->mytype=$xmlrpcTypes["array"];
804                 $this->me["array"]=$vals;
805                 return 1;
806   }
807
808   function addStruct($vals) {
809         global $xmlrpcTypes;
810         if ($this->mytype!=0) {
811           echo "<B>xmlrpcval</B>: already initialized as a [" . 
812                 $this->kindOf() . "]<BR>";
813           return 0;
814         }
815         $this->mytype=$xmlrpcTypes["struct"];
816         $this->me["struct"]=$vals;
817         return 1;
818   }
819
820   function dump($ar) {
821         reset($ar);
822         while ( list( $key, $val ) = each( $ar ) ) {
823           echo "$key => $val<br>";
824           if ($key == 'array')
825                 while ( list( $key2, $val2 ) = each( $val ) ) {
826                   echo "-- $key2 => $val2<br>";
827                 }
828         }
829   }
830
831   function kindOf() {
832         switch($this->mytype) {
833         case 3:
834           return "struct";
835           break;
836         case 2:
837           return "array";
838           break;
839         case 1:
840           return "scalar";
841           break;
842         default:
843           return "undef";
844         }
845   }
846
847   function serializedata($typ, $val) {
848                 $rs="";
849                 global $xmlrpcTypes, $xmlrpcBase64, $xmlrpcString,
850                         $xmlrpcBoolean;
851                 switch($xmlrpcTypes[$typ]) {
852                 case 3:
853                         // struct
854                         $rs.="<struct>\n";
855                         reset($val);
856                         while(list($key2, $val2)=each($val)) {
857                                 $rs.="<member><name>${key2}</name>\n";
858                                 $rs.=$this->serializeval($val2);
859                                 $rs.="</member>\n";
860                         }
861                         $rs.="</struct>";
862                         break;
863                 case 2:
864                         // array
865                         $rs.="<array>\n<data>\n";
866                         for($i=0; $i<sizeof($val); $i++) {
867                                 $rs.=$this->serializeval($val[$i]);
868                         }
869                         $rs.="</data>\n</array>";
870                         break;
871                 case 1:
872                         switch ($typ) {
873                         case $xmlrpcBase64:
874                                 $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
875                                 break;
876                         case $xmlrpcBoolean:
877                                 $rs.="<${typ}>" . ($val ? "1" : "0") . "</${typ}>";
878                                         break;
879                         case $xmlrpcString:
880                                 $rs.="<${typ}>" . htmlspecialchars($val). "</${typ}>";
881                                 break;
882                         default:
883                                 $rs.="<${typ}>${val}</${typ}>";
884                         }
885                         break;
886                 default:
887                         break;
888                 }
889                 return $rs;
890   }
891
892   function serialize() {
893         return $this->serializeval($this);
894   }
895
896   function serializeval($o) {
897                 global $xmlrpcTypes;
898                 $rs="";
899                 $ar=$o->me;
900                 reset($ar);
901                 list($typ, $val) = each($ar);
902                 $rs.="<value>";
903                 $rs.=$this->serializedata($typ, $val);
904                 $rs.="</value>\n";
905                 return $rs;
906   }
907
908   function structmem($m) {
909                 $nv=$this->me["struct"][$m];
910                 return $nv;
911   }
912
913         function structreset() {
914                 reset($this->me["struct"]);
915         }
916         
917         function structeach() {
918                 return each($this->me["struct"]);
919         }
920
921   function getval() {
922                 // UNSTABLE
923                 global $xmlrpcBoolean, $xmlrpcBase64;
924                 reset($this->me);
925                 list($a,$b)=each($this->me);
926                 // contributed by I Sofer, 2001-03-24
927                 // add support for nested arrays to scalarval
928                 // i've created a new method here, so as to
929                 // preserve back compatibility
930
931                 if (is_array($b))    {
932                         foreach ($b as $id => $cont) {
933                                 $b[$id] = $cont->scalarval();
934                         }
935                 }
936
937                 // add support for structures directly encoding php objects
938                 if (is_object($b))  {
939                         $t = get_object_vars($b);
940                         foreach ($t as $id => $cont) {
941                                 $t[$id] = $cont->scalarval();
942                         }
943                         foreach ($t as $id => $cont) {
944                                 eval('$b->'.$id.' = $cont;');
945                         }
946                 }
947                 // end contrib
948                 return $b;
949   }
950
951   function scalarval() {
952                 global $xmlrpcBoolean, $xmlrpcBase64;
953                 reset($this->me);
954                 list($a,$b)=each($this->me);
955                 return $b;
956   }
957
958   function scalartyp() {
959                 global $xmlrpcI4, $xmlrpcInt;
960                 reset($this->me);
961                 list($a,$b)=each($this->me);
962                 if ($a==$xmlrpcI4) 
963                         $a=$xmlrpcInt;
964                 return $a;
965   }
966
967   function arraymem($m) {
968                 $nv=$this->me["array"][$m];
969                 return $nv;
970   }
971
972   function arraysize() {
973                 reset($this->me);
974                 list($a,$b)=each($this->me);
975                 return sizeof($b);
976   }
977 }
978
979 // date helpers
980 function iso8601_encode($timet, $utc=0) {
981         // return an ISO8601 encoded string
982         // really, timezones ought to be supported
983         // but the XML-RPC spec says:
984         //
985         // "Don't assume a timezone. It should be specified by the server in its
986   // documentation what assumptions it makes about timezones."
987         // 
988         // these routines always assume localtime unless 
989         // $utc is set to 1, in which case UTC is assumed
990         // and an adjustment for locale is made when encoding
991         if (!$utc) {
992                 $t=strftime("%Y%m%dT%H:%M:%S", $timet);
993         } else {
994                 if (function_exists("gmstrftime")) 
995                         // gmstrftime doesn't exist in some versions
996                         // of PHP
997                         $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
998                 else {
999                         $t=strftime("%Y%m%dT%H:%M:%S", $timet-date("Z"));
1000                 }
1001         }
1002         return $t;
1003 }
1004
1005 function iso8601_decode($idate, $utc=0) {
1006         // return a timet in the localtime, or UTC
1007         $t=0;
1008         if (ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})",
1009                                          $idate, $regs)) {
1010                 if ($utc) {
1011                         $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1012                 } else {
1013                         $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1014                 }
1015         } 
1016         return $t;
1017 }
1018
1019 /****************************************************************
1020 * xmlrpc_decode takes a message in PHP xmlrpc object format and *
1021 * tranlates it into native PHP types.                           *
1022 *                                                               *
1023 * author: Dan Libby (dan@libby.com)                             *
1024 ****************************************************************/
1025 function _xmlrpc_decode($xmlrpc_val) {
1026    $kind = $xmlrpc_val->kindOf();
1027
1028    if($kind == "scalar") {
1029       return $xmlrpc_val->scalarval();
1030    }
1031    else if($kind == "array") {
1032       $size = $xmlrpc_val->arraysize();
1033       $arr = array();
1034
1035       for($i = 0; $i < $size; $i++) {
1036          $arr[]=_xmlrpc_decode($xmlrpc_val->arraymem($i));
1037       }
1038       return $arr; 
1039    }
1040    else if($kind == "struct") {
1041       $xmlrpc_val->structreset();
1042       $arr = array();
1043
1044       while(list($key,$value)=$xmlrpc_val->structeach()) {
1045          $arr[$key] = _xmlrpc_decode($value);
1046       }
1047       return $arr;
1048    }
1049 }
1050
1051 /****************************************************************
1052 * xmlrpc_encode takes native php types and encodes them into    *
1053 * xmlrpc PHP object format.                                     *
1054 * BUG: All sequential arrays are turned into structs.  I don't  *
1055 * know of a good way to determine if an array is sequential     *
1056 * only.                                                         *
1057 *                                                               *
1058 * feature creep -- could support more types via optional type   *
1059 * argument.                                                     *
1060 *                                                               *
1061 * author: Dan Libby (dan@libby.com)                             *
1062 ****************************************************************/
1063 function _xmlrpc_encode($php_val) {
1064    global $xmlrpcInt;
1065    global $xmlrpcDouble;
1066    global $xmlrpcString;
1067    global $xmlrpcArray;
1068    global $xmlrpcStruct;
1069    global $xmlrpcBoolean;
1070
1071    $type = gettype($php_val);
1072    $xmlrpc_val = new xmlrpcval;
1073
1074    switch($type) {
1075       case "array":
1076       case "object":
1077          $arr = array();
1078          while (list($k,$v) = each($php_val)) {
1079             $arr[$k] = _xmlrpc_encode($v);
1080          }
1081          $xmlrpc_val->addStruct($arr);
1082          break;
1083       case "integer":
1084          $xmlrpc_val->addScalar($php_val, $xmlrpcInt);
1085          break;
1086       case "double":
1087          $xmlrpc_val->addScalar($php_val, $xmlrpcDouble);
1088          break;
1089       case "string":
1090          $xmlrpc_val->addScalar($php_val, $xmlrpcString);
1091          break;
1092 // <G_Giunta_2001-02-29>
1093 // Add support for encoding/decoding of booleans, since they are supported in PHP
1094       case "boolean":
1095          $xmlrpc_val->addScalar($php_val, $xmlrpcBoolean);
1096          break;
1097 // </G_Giunta_2001-02-29>
1098       case "unknown type":
1099       default:
1100         // giancarlo pinerolo <ping@alt.it>
1101         // it has to return 
1102         // an empty object in case (which is already
1103         // at this point), not a boolean. 
1104         break;
1105    }
1106    return $xmlrpc_val;
1107 }
1108
1109 ?>