Xmlrpc.php

Go to the documentation of this file.
00001 <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
00002 /**
00003  * CodeIgniter
00004  *
00005  * An open source application development framework for PHP 4.3.2 or newer
00006  *
00007  * @package             CodeIgniter
00008  * @author              ExpressionEngine Dev Team
00009  * @copyright   Copyright (c) 2008, EllisLab, Inc.
00010  * @license             http://codeigniter.com/user_guide/license.html
00011  * @link                http://codeigniter.com
00012  * @since               Version 1.0
00013  * @filesource
00014  */
00015 
00016 if ( ! function_exists('xml_parser_create'))
00017 {       
00018         show_error('Your PHP installation does not support XML');
00019 }
00020 
00021 
00022 // ------------------------------------------------------------------------
00023 
00024 /**
00025  * XML-RPC request handler class
00026  *
00027  * @package             CodeIgniter
00028  * @subpackage  Libraries
00029  * @category    XML-RPC
00030  * @author              ExpressionEngine Dev Team
00031  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
00032  */
00033 class CI_Xmlrpc {
00034 
00035         var $debug                      = FALSE;        // Debugging on or off  
00036         var $xmlrpcI4           = 'i4';
00037         var $xmlrpcInt          = 'int';
00038         var $xmlrpcBoolean      = 'boolean';
00039         var $xmlrpcDouble       = 'double';     
00040         var $xmlrpcString       = 'string';
00041         var $xmlrpcDateTime     = 'datetime.iso8601';
00042         var $xmlrpcBase64       = 'base64';
00043         var $xmlrpcArray        = 'array';
00044         var $xmlrpcStruct       = 'struct';
00045         
00046         var $xmlrpcTypes        = array();
00047         var $valid_parents      = array();
00048         var $xmlrpcerr          = array();      // Response numbers
00049         var $xmlrpcstr          = array();  // Response strings
00050         
00051         var $xmlrpc_defencoding = 'UTF-8';
00052         var $xmlrpcName                 = 'XML-RPC for CodeIgniter';
00053         var $xmlrpcVersion              = '1.1';
00054         var $xmlrpcerruser              = 800; // Start of user errors
00055         var $xmlrpcerrxml               = 100; // Start of XML Parse errors
00056         var $xmlrpc_backslash   = ''; // formulate backslashes for escaping regexp
00057         
00058         var $client;
00059         var $method;
00060         var $data;
00061         var $message                    = '';
00062         var $error                              = '';           // Error string for request
00063         var $result;
00064         var $response                   = array();  // Response from remote server
00065 
00066 
00067         //-------------------------------------
00068         //  VALUES THAT MULTIPLE CLASSES NEED
00069         //-------------------------------------
00070 
00071         function CI_Xmlrpc ($config = array())
00072         {
00073                 $this->xmlrpcName               = $this->xmlrpcName;
00074                 $this->xmlrpc_backslash = chr(92).chr(92);
00075                 
00076                 // Types for info sent back and forth
00077                 $this->xmlrpcTypes = array(
00078                         $this->xmlrpcI4    => '1',
00079                         $this->xmlrpcInt          => '1',
00080                         $this->xmlrpcBoolean  => '1',
00081                         $this->xmlrpcString   => '1',
00082                         $this->xmlrpcDouble   => '1',
00083                         $this->xmlrpcDateTime => '1',
00084                         $this->xmlrpcBase64   => '1',
00085                         $this->xmlrpcArray      => '2',
00086                         $this->xmlrpcStruct   => '3'
00087                         );
00088                         
00089                 // Array of Valid Parents for Various XML-RPC elements
00090                 $this->valid_parents = array('BOOLEAN'                  => array('VALUE'),
00091                                                                          'I4'                           => array('VALUE'),
00092                                                                          'INT'                          => array('VALUE'),
00093                                                                          'STRING'                       => array('VALUE'),
00094                                                                          'DOUBLE'                       => array('VALUE'),
00095                                                                          'DATETIME.ISO8601'     => array('VALUE'),
00096                                                                          'BASE64'                       => array('VALUE'),
00097                                                                          'ARRAY'                        => array('VALUE'),
00098                                                                          'STRUCT'                       => array('VALUE'),
00099                                                                          'PARAM'                        => array('PARAMS'),
00100                                                                          'METHODNAME'           => array('METHODCALL'),
00101                                                                          'PARAMS'                       => array('METHODCALL', 'METHODRESPONSE'),
00102                                                                          'MEMBER'                       => array('STRUCT'),
00103                                                                          'NAME'                         => array('MEMBER'),
00104                                                                          'DATA'                         => array('ARRAY'),
00105                                                                          'FAULT'                        => array('METHODRESPONSE'),
00106                                                                          'VALUE'                        => array('MEMBER', 'DATA', 'PARAM', 'FAULT')
00107                                                                          );
00108                         
00109                         
00110                 // XML-RPC Responses
00111                 $this->xmlrpcerr['unknown_method'] = '1';
00112                 $this->xmlrpcstr['unknown_method'] = 'This is not a known method for this XML-RPC Server';
00113                 $this->xmlrpcerr['invalid_return'] = '2';
00114                 $this->xmlrpcstr['invalid_return'] = 'The XML data receieved was either invalid or not in the correct form for XML-RPC.  Turn on debugging to examine the XML data further.';
00115                 $this->xmlrpcerr['incorrect_params'] = '3';
00116                 $this->xmlrpcstr['incorrect_params'] = 'Incorrect parameters were passed to method';
00117                 $this->xmlrpcerr['introspect_unknown'] = '4';
00118                 $this->xmlrpcstr['introspect_unknown'] = "Cannot inspect signature for request: method unknown";
00119                 $this->xmlrpcerr['http_error'] = '5';
00120                 $this->xmlrpcstr['http_error'] = "Did not receive a '200 OK' response from remote server.";
00121                 $this->xmlrpcerr['no_data'] = '6';
00122                 $this->xmlrpcstr['no_data'] ='No data received from server.';
00123                 
00124                 $this->initialize($config);
00125                 
00126                 log_message('debug', "XML-RPC Class Initialized");
00127         }
00128         
00129         
00130         //-------------------------------------
00131         //  Initialize Prefs
00132         //-------------------------------------
00133 
00134         function initialize($config = array())
00135         {
00136                 if (sizeof($config) > 0)
00137                 {
00138                         foreach ($config as $key => $val)
00139                         {
00140                                 if (isset($this->$key))
00141                                 {
00142                                         $this->$key = $val;                     
00143                                 }
00144                         }
00145                 }
00146         }
00147         // END
00148         
00149         //-------------------------------------
00150         //  Take URL and parse it
00151         //-------------------------------------
00152 
00153         function server($url, $port=80)
00154         {
00155                 if (substr($url, 0, 4) != "http")
00156                 {
00157                         $url = "http://".$url;
00158                 }
00159                 
00160                 $parts = parse_url($url);
00161                 
00162                 $path = ( ! isset($parts['path'])) ? '/' : $parts['path'];
00163                 
00164                 if (isset($parts['query']) && $parts['query'] != '')
00165                 {
00166                         $path .= '?'.$parts['query'];
00167                 }       
00168                 
00169                 $this->client = new XML_RPC_Client($path, $parts['host'], $port);
00170         }
00171         // END
00172         
00173         //-------------------------------------
00174         //  Set Timeout
00175         //-------------------------------------
00176 
00177         function timeout($seconds=5)
00178         {
00179                 if ( ! is_null($this->client) && is_int($seconds))
00180                 {
00181                         $this->client->timeout = $seconds;
00182                 }
00183         }
00184         // END
00185         
00186         //-------------------------------------
00187         //  Set Methods
00188         //-------------------------------------
00189 
00190         function method($function)
00191         {
00192                 $this->method = $function;
00193         }
00194         // END
00195         
00196         //-------------------------------------
00197         //  Take Array of Data and Create Objects
00198         //-------------------------------------
00199 
00200         function request($incoming)
00201         {
00202                 if ( ! is_array($incoming))
00203                 {
00204                         // Send Error
00205                 }
00206                 
00207                 $this->data = array();
00208                 
00209                 foreach($incoming as $key => $value)
00210                 {
00211                         $this->data[$key] = $this->values_parsing($value);
00212                 }
00213         }
00214         // END
00215         
00216         
00217         //-------------------------------------
00218         //  Set Debug
00219         //-------------------------------------
00220 
00221         function set_debug($flag = TRUE)
00222         {
00223                 $this->debug = ($flag == TRUE) ? TRUE : FALSE;
00224         }
00225         
00226         //-------------------------------------
00227         //  Values Parsing
00228         //-------------------------------------
00229 
00230         function values_parsing($value, $return = FALSE)
00231         {
00232                 if (is_array($value) && isset($value['0']))
00233                 {
00234                         if ( ! isset($value['1']) OR ! isset($this->xmlrpcTypes[strtolower($value['1'])]))
00235                         {
00236                                 if (is_array($value[0]))
00237                                 {
00238                                         $temp = new XML_RPC_Values($value['0'], 'array');
00239                                 }
00240                                 else
00241                                 {
00242                                         $temp = new XML_RPC_Values($value['0'], 'string');
00243                                 }
00244                         }
00245                         elseif(is_array($value['0']) && ($value['1'] == 'struct' OR $value['1'] == 'array'))
00246                         {
00247                                 while (list($k) = each($value['0']))
00248                                 {
00249                                         $value['0'][$k] = $this->values_parsing($value['0'][$k], TRUE);
00250                                 }
00251                                 
00252                                 $temp = new XML_RPC_Values($value['0'], $value['1']);
00253                         }
00254                         else
00255                         {
00256                                 $temp = new XML_RPC_Values($value['0'], $value['1']);
00257                         }
00258                 }
00259                 else
00260                 {
00261                         $temp = new XML_RPC_Values($value, 'string');
00262                 }
00263 
00264                 return $temp;
00265         }
00266         // END
00267 
00268 
00269         //-------------------------------------
00270         //  Sends XML-RPC Request
00271         //-------------------------------------
00272 
00273         function send_request()
00274         {
00275                 $this->message = new XML_RPC_Message($this->method,$this->data);
00276                 $this->message->debug = $this->debug;
00277         
00278                 if ( ! $this->result = $this->client->send($this->message))
00279                 {
00280                         $this->error = $this->result->errstr;
00281                         return FALSE;
00282                 }
00283                 elseif( ! is_object($this->result->val))
00284                 {
00285                         $this->error = $this->result->errstr;
00286                         return FALSE;
00287                 }
00288                 
00289                 $this->response = $this->result->decode();
00290                 
00291                 return TRUE;
00292         }
00293         // END
00294         
00295         //-------------------------------------
00296         //  Returns Error
00297         //-------------------------------------
00298 
00299         function display_error()
00300         {
00301                 return $this->error;
00302         }
00303         // END
00304         
00305         //-------------------------------------
00306         //  Returns Remote Server Response
00307         //-------------------------------------
00308 
00309         function display_response()
00310         {
00311                 return $this->response;
00312         }
00313         // END
00314         
00315         //-------------------------------------
00316         //  Sends an Error Message for Server Request
00317         //-------------------------------------
00318         
00319         function send_error_message($number, $message)
00320         {
00321                 return new XML_RPC_Response('0',$number, $message);
00322         }
00323         // END
00324         
00325         
00326         //-------------------------------------
00327         //  Send Response for Server Request
00328         //-------------------------------------
00329         
00330         function send_response($response)
00331         {
00332                 // $response should be array of values, which will be parsed
00333                 // based on their data and type into a valid group of XML-RPC values
00334                 
00335                 $response = $this->values_parsing($response);
00336         
00337                 return new XML_RPC_Response($response);
00338         }
00339         // END
00340         
00341 } // END XML_RPC Class
00342 
00343         
00344         
00345 /**
00346  * XML-RPC Client class
00347  *
00348  * @category    XML-RPC
00349  * @author              ExpressionEngine Dev Team
00350  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
00351  */
00352 class XML_RPC_Client extends CI_Xmlrpc
00353 {
00354         var $path                       = '';
00355         var $server                     = '';
00356         var $port                       = 80;
00357         var $errno                      = '';
00358         var $errstring          = '';
00359         var $timeout            = 5;
00360         var $no_multicall       = false;
00361 
00362         function XML_RPC_Client($path, $server, $port=80)
00363         {
00364                 parent::CI_Xmlrpc();
00365                 
00366                 $this->port = $port;
00367                 $this->server = $server;
00368                 $this->path = $path;
00369         }
00370         
00371         function send($msg)
00372         {
00373                 if (is_array($msg))
00374                 {
00375                         // Multi-call disabled
00376                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'],$this->xmlrpcstr['multicall_recursion']);
00377                         return $r;
00378                 }
00379 
00380                 return $this->sendPayload($msg);
00381         }
00382 
00383         function sendPayload($msg)
00384         {       
00385                 $fp = @fsockopen($this->server, $this->port,$this->errno, $this->errstr, $this->timeout);
00386                 
00387                 if ( ! is_resource($fp))
00388                 {
00389                         error_log($this->xmlrpcstr['http_error']);
00390                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'],$this->xmlrpcstr['http_error']);
00391                         return $r;
00392                 }
00393                 
00394                 if(empty($msg->payload))
00395                 {
00396                         // $msg = XML_RPC_Messages
00397                         $msg->createPayload();
00398                 }
00399                 
00400                 $r = "\r\n";
00401                 $op  = "POST {$this->path} HTTP/1.0$r";
00402                 $op .= "Host: {$this->server}$r";
00403                 $op .= "Content-Type: text/xml$r";
00404                 $op .= "User-Agent: {$this->xmlrpcName}$r";
00405                 $op .= "Content-Length: ".strlen($msg->payload). "$r$r";
00406                 $op .= $msg->payload;
00407                 
00408 
00409                 if ( ! fputs($fp, $op, strlen($op)))
00410                 {
00411                         error_log($this->xmlrpcstr['http_error']);
00412                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
00413                         return $r;
00414                 }
00415                 $resp = $msg->parseResponse($fp);
00416                 fclose($fp);
00417                 return $resp;
00418         }
00419 
00420 } // end class XML_RPC_Client
00421 
00422 
00423 /**
00424  * XML-RPC Response class
00425  *
00426  * @category    XML-RPC
00427  * @author              ExpressionEngine Dev Team
00428  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
00429  */
00430 class XML_RPC_Response
00431 {
00432         var $val = 0;
00433         var $errno = 0;
00434         var $errstr = '';
00435         var $headers = array();
00436 
00437         function XML_RPC_Response($val, $code = 0, $fstr = '')
00438         {       
00439                 if ($code != 0)
00440                 {
00441                         // error
00442                         $this->errno = $code;
00443                         $this->errstr = htmlentities($fstr);
00444                 }
00445                 else if ( ! is_object($val))
00446                 {
00447                         // programmer error, not an object
00448                         error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to XML_RPC_Response.  Defaulting to empty value.");
00449                         $this->val = new XML_RPC_Values();
00450                 }
00451                 else
00452                 {
00453                         $this->val = $val;
00454                 }
00455         }
00456 
00457         function faultCode()
00458         {
00459                 return $this->errno;
00460         }
00461 
00462         function faultString()
00463         {
00464                 return $this->errstr;
00465         }
00466 
00467         function value()
00468         {
00469                 return $this->val;
00470         }
00471         
00472         function prepare_response()
00473         {
00474                 $result = "<methodResponse>\n";
00475                 if ($this->errno)
00476                 {
00477                         $result .= '<fault>
00478         <value>
00479                 <struct>
00480                         <member>
00481                                 <name>faultCode</name>
00482                                 <value><int>' . $this->errno . '</int></value>
00483                         </member>
00484                         <member>
00485                                 <name>faultString</name>
00486                                 <value><string>' . $this->errstr . '</string></value>
00487                         </member>
00488                 </struct>
00489         </value>
00490 </fault>';
00491                 }
00492                 else
00493                 {
00494                         $result .= "<params>\n<param>\n" .
00495                                         $this->val->serialize_class() .
00496                                         "</param>\n</params>";
00497                 }
00498                 $result .= "\n</methodResponse>";
00499                 return $result;
00500         }
00501         
00502         function decode($array=FALSE)
00503         {
00504                 $CI =& get_instance();
00505 
00506                 if ($array !== FALSE && is_array($array))
00507                 {
00508                         while (list($key) = each($array))
00509                         {
00510                                 if (is_array($array[$key]))
00511                                 {
00512                                         $array[$key] = $this->decode($array[$key]);
00513                                 }
00514                                 else
00515                                 {
00516                                         $array[$key] = $CI->input->xss_clean($array[$key]);
00517                                 }
00518                         }
00519                         
00520                         $result = $array;
00521                 }
00522                 else
00523                 {
00524                         $result = $this->xmlrpc_decoder($this->val);
00525                         
00526                         if (is_array($result))
00527                         {
00528                                 $result = $this->decode($result);
00529                         }
00530                         else
00531                         {
00532                                 $result = $CI->input->xss_clean($result);
00533                         }
00534                 }
00535                 
00536                 return $result;
00537         }
00538 
00539         
00540         
00541         //-------------------------------------
00542         //  XML-RPC Object to PHP Types
00543         //-------------------------------------
00544 
00545         function xmlrpc_decoder($xmlrpc_val)
00546         {
00547                 $kind = $xmlrpc_val->kindOf();
00548 
00549                 if($kind == 'scalar')
00550                 {
00551                         return $xmlrpc_val->scalarval();
00552                 }
00553                 elseif($kind == 'array')
00554                 {
00555                         reset($xmlrpc_val->me);
00556                         list($a,$b) = each($xmlrpc_val->me);
00557                         $size = sizeof($b);
00558                         
00559                         $arr = array();
00560 
00561                         for($i = 0; $i < $size; $i++)
00562                         {
00563                                 $arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]);
00564                         }
00565                         return $arr;
00566                 }
00567                 elseif($kind == 'struct')
00568                 {
00569                         reset($xmlrpc_val->me['struct']);
00570                         $arr = array();
00571 
00572                         while(list($key,$value) = each($xmlrpc_val->me['struct']))
00573                         {
00574                                 $arr[$key] = $this->xmlrpc_decoder($value);
00575                         }
00576                         return $arr;
00577                 }
00578         }
00579         
00580         
00581         //-------------------------------------
00582         //  ISO-8601 time to server or UTC time
00583         //-------------------------------------
00584 
00585         function iso8601_decode($time, $utc=0)
00586         {
00587                 // return a timet in the localtime, or UTC
00588                 $t = 0;
00589                 if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs))
00590                 {
00591                         if ($utc == 1)
00592                                 $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
00593                         else
00594                                 $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
00595                 }
00596                 return $t;
00597         }
00598         
00599 } // End Response Class
00600 
00601 
00602 
00603 /**
00604  * XML-RPC Message class
00605  *
00606  * @category    XML-RPC
00607  * @author              ExpressionEngine Dev Team
00608  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
00609  */
00610 class XML_RPC_Message extends CI_Xmlrpc
00611 {
00612         var $payload;
00613         var $method_name;
00614         var $params                     = array();
00615         var $xh                         = array();
00616 
00617         function XML_RPC_Message($method, $pars=0)
00618         {
00619                 parent::CI_Xmlrpc();
00620                 
00621                 $this->method_name = $method;
00622                 if (is_array($pars) && sizeof($pars) > 0)
00623                 {
00624                         for($i=0; $i<sizeof($pars); $i++)
00625                         {
00626                                 // $pars[$i] = XML_RPC_Values
00627                                 $this->params[] = $pars[$i];
00628                         }
00629                 }
00630         }
00631         
00632         //-------------------------------------
00633         //  Create Payload to Send
00634         //-------------------------------------
00635         
00636         function createPayload()
00637         {
00638                 $this->payload = "<?xml version=\"1.0\"?".">\r\n<methodCall>\r\n";
00639                 $this->payload .= '<methodName>' . $this->method_name . "</methodName>\r\n";
00640                 $this->payload .= "<params>\r\n";
00641                 
00642                 for($i=0; $i<sizeof($this->params); $i++)
00643                 {
00644                         // $p = XML_RPC_Values
00645                         $p = $this->params[$i];
00646                         $this->payload .= "<param>\r\n".$p->serialize_class()."</param>\r\n";
00647                 }
00648                 
00649                 $this->payload .= "</params>\r\n</methodCall>\r\n";
00650         }
00651         
00652         //-------------------------------------
00653         //  Parse External XML-RPC Server's Response
00654         //-------------------------------------
00655         
00656         function parseResponse($fp)
00657         {
00658                 $data = '';
00659                 
00660                 while($datum = fread($fp, 4096))
00661                 {
00662                         $data .= $datum;
00663                 }
00664                 
00665                 //-------------------------------------
00666                 //  DISPLAY HTTP CONTENT for DEBUGGING
00667                 //-------------------------------------
00668                 
00669                 if ($this->debug === TRUE)
00670                 {
00671                         echo "<pre>";
00672                         echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
00673                         echo "</pre>";
00674                 }
00675                 
00676                 //-------------------------------------
00677                 //  Check for data
00678                 //-------------------------------------
00679 
00680                 if($data == "")
00681                 {
00682                         error_log($this->xmlrpcstr['no_data']);
00683                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']);
00684                         return $r;
00685                 }
00686                 
00687                 
00688                 //-------------------------------------
00689                 //  Check for HTTP 200 Response
00690                 //-------------------------------------
00691                 
00692                 if (strncmp($data, 'HTTP', 4) == 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data))
00693                 {
00694                         $errstr= substr($data, 0, strpos($data, "\n")-1);
00695                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']. ' (' . $errstr . ')');
00696                         return $r;
00697                 }
00698                 
00699                 //-------------------------------------
00700                 //  Create and Set Up XML Parser
00701                 //-------------------------------------
00702         
00703                 $parser = xml_parser_create($this->xmlrpc_defencoding);
00704 
00705                 $this->xh[$parser]                               = array();
00706                 $this->xh[$parser]['isf']                = 0;
00707                 $this->xh[$parser]['ac']                 = '';
00708                 $this->xh[$parser]['headers']    = array();
00709                 $this->xh[$parser]['stack']              = array();
00710                 $this->xh[$parser]['valuestack'] = array();
00711                 $this->xh[$parser]['isf_reason'] = 0;
00712 
00713                 xml_set_object($parser, $this);
00714                 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
00715                 xml_set_element_handler($parser, 'open_tag', 'closing_tag');
00716                 xml_set_character_data_handler($parser, 'character_data');
00717                 //xml_set_default_handler($parser, 'default_handler');
00718 
00719 
00720                 //-------------------------------------
00721                 //  GET HEADERS
00722                 //-------------------------------------
00723                 
00724                 $lines = explode("\r\n", $data);
00725                 while (($line = array_shift($lines)))
00726                 {
00727                         if (strlen($line) < 1)
00728                         {
00729                                 break;
00730                         }
00731                         $this->xh[$parser]['headers'][] = $line;
00732                 }
00733                 $data = implode("\r\n", $lines);
00734                 
00735                 
00736                 //-------------------------------------
00737                 //  PARSE XML DATA
00738                 //-------------------------------------         
00739 
00740                 if ( ! xml_parse($parser, $data, sizeof($data)))
00741                 {
00742                         $errstr = sprintf('XML error: %s at line %d',
00743                                         xml_error_string(xml_get_error_code($parser)),
00744                                         xml_get_current_line_number($parser));
00745                         //error_log($errstr);
00746                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
00747                         xml_parser_free($parser);
00748                         return $r;
00749                 }
00750                 xml_parser_free($parser);
00751                 
00752                 // ---------------------------------------
00753                 //  Got Ourselves Some Badness, It Seems
00754                 // ---------------------------------------
00755                 
00756                 if ($this->xh[$parser]['isf'] > 1)
00757                 {
00758                         if ($this->debug === TRUE)
00759                         {
00760                                 echo "---Invalid Return---\n";
00761                                 echo $this->xh[$parser]['isf_reason'];
00762                                 echo "---Invalid Return---\n\n";
00763                         }
00764                                 
00765                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
00766                         return $r;
00767                 }
00768                 elseif ( ! is_object($this->xh[$parser]['value']))
00769                 {
00770                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
00771                         return $r;
00772                 }
00773                 
00774                 //-------------------------------------
00775                 //  DISPLAY XML CONTENT for DEBUGGING
00776                 //-------------------------------------         
00777                 
00778                 if ($this->debug === TRUE)
00779                 {
00780                         echo "<pre>";
00781                         
00782                         if (count($this->xh[$parser]['headers'] > 0))
00783                         {
00784                                 echo "---HEADERS---\n";
00785                                 foreach ($this->xh[$parser]['headers'] as $header)
00786                                 {
00787                                         echo "$header\n";
00788                                 }
00789                                 echo "---END HEADERS---\n\n";
00790                         }
00791                         
00792                         echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
00793                         
00794                         echo "---PARSED---\n" ;
00795                         var_dump($this->xh[$parser]['value']);
00796                         echo "\n---END PARSED---</pre>";
00797                 }
00798                 
00799                 //-------------------------------------
00800                 //  SEND RESPONSE
00801                 //-------------------------------------
00802                 
00803                 $v = $this->xh[$parser]['value'];
00804                         
00805                 if ($this->xh[$parser]['isf'])
00806                 {
00807                         $errno_v = $v->me['struct']['faultCode'];
00808                         $errstr_v = $v->me['struct']['faultString'];
00809                         $errno = $errno_v->scalarval();
00810 
00811                         if ($errno == 0)
00812                         {
00813                                 // FAULT returned, errno needs to reflect that
00814                                 $errno = -1;
00815                         }
00816 
00817                         $r = new XML_RPC_Response($v, $errno, $errstr_v->scalarval());
00818                 }
00819                 else
00820                 {
00821                         $r = new XML_RPC_Response($v);
00822                 }
00823 
00824                 $r->headers = $this->xh[$parser]['headers'];
00825                 return $r;
00826         }
00827         
00828         // ------------------------------------
00829         //  Begin Return Message Parsing section
00830         // ------------------------------------
00831         
00832         // quick explanation of components:
00833         //   ac - used to accumulate values
00834         //   isf - used to indicate a fault
00835         //   lv - used to indicate "looking for a value": implements
00836         //              the logic to allow values with no types to be strings
00837         //   params - used to store parameters in method calls
00838         //   method - used to store method name
00839         //       stack - array with parent tree of the xml element,
00840         //                       used to validate the nesting of elements
00841 
00842         //-------------------------------------
00843         //  Start Element Handler
00844         //-------------------------------------
00845 
00846         function open_tag($the_parser, $name, $attrs)
00847         {
00848                 // If invalid nesting, then return
00849                 if ($this->xh[$the_parser]['isf'] > 1) return;
00850                 
00851                 // Evaluate and check for correct nesting of XML elements
00852                 
00853                 if (count($this->xh[$the_parser]['stack']) == 0)
00854                 {
00855                         if ($name != 'METHODRESPONSE' && $name != 'METHODCALL')
00856                         {
00857                                 $this->xh[$the_parser]['isf'] = 2;
00858                                 $this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing';
00859                                 return;
00860                         }
00861                 }
00862                 else
00863                 {
00864                         // not top level element: see if parent is OK
00865                         if ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE))
00866                         {
00867                                 $this->xh[$the_parser]['isf'] = 2;
00868                                 $this->xh[$the_parser]['isf_reason'] = "XML-RPC element $name cannot be child of ".$this->xh[$the_parser]['stack'][0];
00869                                 return;
00870                         }
00871                 }
00872                 
00873                 switch($name)
00874                 {
00875                         case 'STRUCT':
00876                         case 'ARRAY':
00877                                 // Creates array for child elements
00878                                 
00879                                 $cur_val = array('value' => array(),
00880                                                                  'type'  => $name);
00881                                                                 
00882                                 array_unshift($this->xh[$the_parser]['valuestack'], $cur_val);
00883                         break;
00884                         case 'METHODNAME':
00885                         case 'NAME':
00886                                 $this->xh[$the_parser]['ac'] = '';
00887                         break;
00888                         case 'FAULT':
00889                                 $this->xh[$the_parser]['isf'] = 1;
00890                         break;
00891                         case 'PARAM':
00892                                 $this->xh[$the_parser]['value'] = null;
00893                         break;
00894                         case 'VALUE':
00895                                 $this->xh[$the_parser]['vt'] = 'value';
00896                                 $this->xh[$the_parser]['ac'] = '';
00897                                 $this->xh[$the_parser]['lv'] = 1;
00898                         break;
00899                         case 'I4':
00900                         case 'INT':
00901                         case 'STRING':
00902                         case 'BOOLEAN':
00903                         case 'DOUBLE':
00904                         case 'DATETIME.ISO8601':
00905                         case 'BASE64':
00906                                 if ($this->xh[$the_parser]['vt'] != 'value')
00907                                 {
00908                                         //two data elements inside a value: an error occurred!
00909                                         $this->xh[$the_parser]['isf'] = 2;
00910                                         $this->xh[$the_parser]['isf_reason'] = "'Twas a $name element following a ".$this->xh[$the_parser]['vt']." element inside a single value";
00911                                         return;
00912                                 }
00913                                 
00914                                 $this->xh[$the_parser]['ac'] = '';
00915                         break;
00916                         case 'MEMBER':
00917                                 // Set name of <member> to nothing to prevent errors later if no <name> is found
00918                                 $this->xh[$the_parser]['valuestack'][0]['name'] = '';
00919                                 
00920                                 // Set NULL value to check to see if value passed for this param/member
00921                                 $this->xh[$the_parser]['value'] = null;
00922                         break;
00923                         case 'DATA':
00924                         case 'METHODCALL':
00925                         case 'METHODRESPONSE':
00926                         case 'PARAMS':
00927                                 // valid elements that add little to processing
00928                         break;
00929                         default:
00930                                 /// An Invalid Element is Found, so we have trouble
00931                                 $this->xh[$the_parser]['isf'] = 2;
00932                                 $this->xh[$the_parser]['isf_reason'] = "Invalid XML-RPC element found: $name";
00933                         break;
00934                 }
00935                 
00936                 // Add current element name to stack, to allow validation of nesting
00937                 array_unshift($this->xh[$the_parser]['stack'], $name);
00938 
00939                 if ($name != 'VALUE') $this->xh[$the_parser]['lv'] = 0;
00940         }
00941         // END
00942 
00943 
00944         //-------------------------------------
00945         //  End Element Handler
00946         //-------------------------------------
00947 
00948         function closing_tag($the_parser, $name)
00949         {
00950                 if ($this->xh[$the_parser]['isf'] > 1) return;
00951                 
00952                 // Remove current element from stack and set variable
00953                 // NOTE: If the XML validates, then we do not have to worry about
00954                 // the opening and closing of elements.  Nesting is checked on the opening
00955                 // tag so we be safe there as well.
00956                 
00957                 $curr_elem = array_shift($this->xh[$the_parser]['stack']);
00958         
00959                 switch($name)
00960                 {
00961                         case 'STRUCT':
00962                         case 'ARRAY':
00963                                 $cur_val = array_shift($this->xh[$the_parser]['valuestack']);
00964                                 $this->xh[$the_parser]['value'] = ( ! isset($cur_val['values'])) ? array() : $cur_val['values'];
00965                                 $this->xh[$the_parser]['vt']    = strtolower($name);
00966                         break;
00967                         case 'NAME':
00968                                 $this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac'];
00969                         break;
00970                         case 'BOOLEAN':
00971                         case 'I4':
00972                         case 'INT':
00973                         case 'STRING':
00974                         case 'DOUBLE':
00975                         case 'DATETIME.ISO8601':
00976                         case 'BASE64':
00977                                 $this->xh[$the_parser]['vt'] = strtolower($name);
00978                                 
00979                                 if ($name == 'STRING')
00980                                 {
00981                                         $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
00982                                 }
00983                                 elseif ($name=='DATETIME.ISO8601')
00984                                 {
00985                                         $this->xh[$the_parser]['vt']    = $this->xmlrpcDateTime;
00986                                         $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
00987                                 }
00988                                 elseif ($name=='BASE64')
00989                                 {
00990                                         $this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']);
00991                                 }
00992                                 elseif ($name=='BOOLEAN')
00993                                 {
00994                                         // Translated BOOLEAN values to TRUE AND FALSE
00995                                         if ($this->xh[$the_parser]['ac'] == '1')
00996                                         {
00997                                                 $this->xh[$the_parser]['value'] = TRUE;
00998                                         }
00999                                         else
01000                                         {
01001                                                 $this->xh[$the_parser]['value'] = FALSE;
01002                                         }
01003                                 }
01004                                 elseif ($name=='DOUBLE')
01005                                 {
01006                                         // we have a DOUBLE
01007                                         // we must check that only 0123456789-.<space> are characters here
01008                                         if ( ! preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh[$the_parser]['ac']))
01009                                         {
01010                                                 $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
01011                                         }
01012                                         else
01013                                         {
01014                                                 $this->xh[$the_parser]['value'] = (double)$this->xh[$the_parser]['ac'];
01015                                         }
01016                                 }
01017                                 else
01018                                 {
01019                                         // we have an I4/INT
01020                                         // we must check that only 0123456789-<space> are characters here
01021                                         if ( ! preg_match('/^[+-]?[0-9\t ]+$/', $this->xh[$the_parser]['ac']))
01022                                         {
01023                                                 $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
01024                                         }
01025                                         else
01026                                         {
01027                                                 $this->xh[$the_parser]['value'] = (int)$this->xh[$the_parser]['ac'];
01028                                         }
01029                                 }
01030                                 $this->xh[$the_parser]['ac'] = '';
01031                                 $this->xh[$the_parser]['lv'] = 3; // indicate we've found a value
01032                         break;
01033                         case 'VALUE':
01034                                 // This if() detects if no scalar was inside <VALUE></VALUE>
01035                                 if ($this->xh[$the_parser]['vt']=='value')
01036                                 {
01037                                         $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
01038                                         $this->xh[$the_parser]['vt']    = $this->xmlrpcString;
01039                                 }
01040                                 
01041                                 // build the XML-RPC value out of the data received, and substitute it
01042                                 $temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']);
01043                                 
01044                                 if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] == 'ARRAY')
01045                                 {
01046                                         // Array
01047                                         $this->xh[$the_parser]['valuestack'][0]['values'][] = $temp;
01048                                 }
01049                                 else
01050                                 {
01051                                         // Struct
01052                                         $this->xh[$the_parser]['value'] = $temp;
01053                                 }
01054                         break;
01055                         case 'MEMBER':
01056                                 $this->xh[$the_parser]['ac']='';
01057                                 
01058                                 // If value add to array in the stack for the last element built
01059                                 if ($this->xh[$the_parser]['value'])
01060                                 {
01061                                         $this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value'];
01062                                 }
01063                         break;
01064                         case 'DATA':
01065                                 $this->xh[$the_parser]['ac']='';
01066                         break;
01067                         case 'PARAM':
01068                                 if ($this->xh[$the_parser]['value'])
01069                                 {
01070                                         $this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value'];
01071                                 }
01072                         break;
01073                         case 'METHODNAME':
01074                                 $this->xh[$the_parser]['method'] = ltrim($this->xh[$the_parser]['ac']);
01075                         break;
01076                         case 'PARAMS':
01077                         case 'FAULT':
01078                         case 'METHODCALL':
01079                         case 'METHORESPONSE':
01080                                 // We're all good kids with nuthin' to do
01081                         break;
01082                         default:
01083                                 // End of an Invalid Element.  Taken care of during the opening tag though
01084                         break;
01085                 }
01086         }
01087 
01088         //-------------------------------------
01089         //  Parses Character Data
01090         //-------------------------------------
01091 
01092         function character_data($the_parser, $data)
01093         {
01094                 if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already
01095                 
01096                 // If a value has not been found
01097                 if ($this->xh[$the_parser]['lv'] != 3)
01098                 {
01099                         if ($this->xh[$the_parser]['lv'] == 1)
01100                         {
01101                                 $this->xh[$the_parser]['lv'] = 2; // Found a value
01102                         }
01103                                 
01104                         if( ! @isset($this->xh[$the_parser]['ac']))
01105                         {
01106                                 $this->xh[$the_parser]['ac'] = '';
01107                         }
01108                                 
01109                         $this->xh[$the_parser]['ac'] .= $data;
01110                 }
01111         }
01112         
01113         
01114         function addParam($par) { $this->params[]=$par; }
01115         
01116         function output_parameters($array=FALSE)
01117         {
01118                 $CI =& get_instance();  
01119 
01120                 if ($array !== FALSE && is_array($array))
01121                 {
01122                         while (list($key) = each($array))
01123                         {
01124                                 if (is_array($array[$key]))
01125                                 {
01126                                         $array[$key] = $this->output_parameters($array[$key]);
01127                                 }
01128                                 else
01129                                 {
01130                                         $array[$key] = $CI->input->xss_clean($array[$key]);
01131                                 }
01132                         }
01133                         
01134                         $parameters = $array;
01135                 }
01136                 else
01137                 {
01138                         $parameters = array();
01139                 
01140                         for ($i = 0; $i < sizeof($this->params); $i++)
01141                         {
01142                                 $a_param = $this->decode_message($this->params[$i]);
01143                                 
01144                                 if (is_array($a_param))
01145                                 {
01146                                         $parameters[] = $this->output_parameters($a_param);
01147                                 }
01148                                 else
01149                                 {
01150                                         $parameters[] = $CI->input->xss_clean($a_param);
01151                                 }
01152                         }       
01153                 }
01154                 
01155                 return $parameters;
01156         }
01157         
01158         
01159         function decode_message($param)
01160         {
01161                 $kind = $param->kindOf();
01162 
01163                 if($kind == 'scalar')
01164                 {
01165                         return $param->scalarval();
01166                 }
01167                 elseif($kind == 'array')
01168                 {
01169                         reset($param->me);
01170                         list($a,$b) = each($param->me);
01171                         
01172                         $arr = array();
01173 
01174                         for($i = 0; $i < sizeof($b); $i++)
01175                         {
01176                                 $arr[] = $this->decode_message($param->me['array'][$i]);
01177                         }
01178                         
01179                         return $arr;
01180                 }
01181                 elseif($kind == 'struct')
01182                 {
01183                         reset($param->me['struct']);
01184                         
01185                         $arr = array();
01186 
01187                         while(list($key,$value) = each($param->me['struct']))
01188                         {
01189                                 $arr[$key] = $this->decode_message($value);
01190                         }
01191                         
01192                         return $arr;
01193                 }
01194         }
01195         
01196 } // End XML_RPC_Messages class
01197 
01198 
01199 
01200 /**
01201  * XML-RPC Values class
01202  *
01203  * @category    XML-RPC
01204  * @author              ExpressionEngine Dev Team
01205  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
01206  */
01207 class XML_RPC_Values extends CI_Xmlrpc
01208 {
01209         var $me         = array();
01210         var $mytype     = 0;
01211 
01212         function XML_RPC_Values($val=-1, $type='')
01213         {       
01214                 parent::CI_Xmlrpc();
01215                 
01216                 if ($val != -1 OR $type != '')
01217                 {
01218                         $type = $type == '' ? 'string' : $type;
01219                         
01220                         if ($this->xmlrpcTypes[$type] == 1)
01221                         {
01222                                 $this->addScalar($val,$type);
01223                         }
01224                         elseif ($this->xmlrpcTypes[$type] == 2)
01225                         {
01226                                 $this->addArray($val);
01227                         }
01228                         elseif ($this->xmlrpcTypes[$type] == 3)
01229                         {
01230                                 $this->addStruct($val);
01231                         }
01232                 }
01233         }
01234 
01235         function addScalar($val, $type='string')
01236         {
01237                 $typeof = $this->xmlrpcTypes[$type];
01238                 
01239                 if ($this->mytype==1)
01240                 {
01241                         echo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />';
01242                         return 0;
01243                 }
01244                 
01245                 if ($typeof != 1)
01246                 {
01247                         echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />';
01248                         return 0;
01249                 }
01250 
01251                 if ($type == $this->xmlrpcBoolean)
01252                 {
01253                         if (strcasecmp($val,'true')==0 OR $val==1 OR ($val==true && strcasecmp($val,'false')))
01254                         {
01255                                 $val = 1;
01256                         }
01257                         else
01258                         {
01259                                 $val=0;
01260                         }
01261                 }
01262 
01263                 if ($this->mytype == 2)
01264                 {
01265                         // adding to an array here
01266                         $ar = $this->me['array'];
01267                         $ar[] = new XML_RPC_Values($val, $type);
01268                         $this->me['array'] = $ar;
01269                 }
01270                 else
01271                 {
01272                         // a scalar, so set the value and remember we're scalar
01273                         $this->me[$type] = $val;
01274                         $this->mytype = $typeof;
01275                 }
01276                 return 1;
01277         }
01278 
01279         function addArray($vals)
01280         {
01281                 if ($this->mytype != 0)
01282                 {
01283                         echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
01284                         return 0;
01285                 }
01286 
01287                 $this->mytype = $this->xmlrpcTypes['array'];
01288                 $this->me['array'] = $vals;
01289                 return 1;
01290         }
01291 
01292         function addStruct($vals)
01293         {
01294                 if ($this->mytype != 0)
01295                 {
01296                         echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
01297                         return 0;
01298                 }
01299                 $this->mytype = $this->xmlrpcTypes['struct'];
01300                 $this->me['struct'] = $vals;
01301                 return 1;
01302         }
01303 
01304         function kindOf()
01305         {
01306                 switch($this->mytype)
01307                 {
01308                         case 3:
01309                                 return 'struct';
01310                                 break;
01311                         case 2:
01312                                 return 'array';
01313                                 break;
01314                         case 1:
01315                                 return 'scalar';
01316                                 break;
01317                         default:
01318                                 return 'undef';
01319                 }
01320         }
01321 
01322         function serializedata($typ, $val)
01323         {
01324                 $rs = '';
01325                 
01326                 switch($this->xmlrpcTypes[$typ])
01327                 {
01328                         case 3:
01329                                 // struct
01330                                 $rs .= "<struct>\n";
01331                                 reset($val);
01332                                 while(list($key2, $val2) = each($val))
01333                                 {
01334                                         $rs .= "<member>\n<name>{$key2}</name>\n";
01335                                         $rs .= $this->serializeval($val2);
01336                                         $rs .= "</member>\n";
01337                                 }
01338                                 $rs .= '</struct>';
01339                         break;
01340                         case 2:
01341                                 // array
01342                                 $rs .= "<array>\n<data>\n";
01343                                 for($i=0; $i < sizeof($val); $i++)
01344                                 {
01345                                         $rs .= $this->serializeval($val[$i]);
01346                                 }
01347                                 $rs.="</data>\n</array>\n";
01348                                 break;
01349                         case 1:
01350                                 // others
01351                                 switch ($typ)
01352                                 {
01353                                         case $this->xmlrpcBase64:
01354                                                 $rs .= "<{$typ}>" . base64_encode($val) . "</{$typ}>\n";
01355                                         break;
01356                                         case $this->xmlrpcBoolean:
01357                                                 $rs .= "<{$typ}>" . ($val ? '1' : '0') . "</{$typ}>\n";
01358                                         break;
01359                                         case $this->xmlrpcString:
01360                                                 $rs .= "<{$typ}>" . htmlspecialchars($val). "</{$typ}>\n";
01361                                         break;
01362                                         default:
01363                                                 $rs .= "<{$typ}>{$val}</{$typ}>\n";
01364                                         break;
01365                                 }
01366                         default:
01367                         break;
01368                 }
01369                 return $rs;
01370         }
01371 
01372         function serialize_class()
01373         {
01374                 return $this->serializeval($this);
01375         }
01376 
01377         function serializeval($o)
01378         {
01379                 $ar = $o->me;
01380                 reset($ar);
01381                 
01382                 list($typ, $val) = each($ar);
01383                 $rs = "<value>\n".$this->serializedata($typ, $val)."</value>\n";
01384                 return $rs;
01385         }
01386         
01387         function scalarval()
01388         {
01389                 reset($this->me);
01390                 list($a,$b) = each($this->me);
01391                 return $b;
01392         }
01393 
01394 
01395         //-------------------------------------
01396         // Encode time in ISO-8601 form.
01397         //-------------------------------------
01398         
01399         // Useful for sending time in XML-RPC
01400 
01401         function iso8601_encode($time, $utc=0)
01402         {       
01403                 if ($utc == 1)
01404                 {
01405                         $t = strftime("%Y%m%dT%H:%M:%S", $time);
01406                 }
01407                 else
01408                 {
01409                         if (function_exists('gmstrftime'))
01410                                 $t = gmstrftime("%Y%m%dT%H:%M:%S", $time);
01411                         else
01412                                 $t = strftime("%Y%m%dT%H:%M:%S", $time - date('Z'));
01413                 }
01414                 return $t;
01415         }
01416         
01417 }
01418 // END XML_RPC_Values Class
01419 
01420 /* End of file Xmlrpc.php */
01421 /* Location: ./system/libraries/Xmlrpc.php */