Subversion Repositories Applications.papyrus

Rev

Blame | Last modification | View Log | RSS feed

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 4.3.2 or newer
 *
 * @package             CodeIgniter
 * @author              ExpressionEngine Dev Team
 * @copyright   Copyright (c) 2008, EllisLab, Inc.
 * @license             http://codeigniter.com/user_guide/license.html
 * @link                http://codeigniter.com
 * @since               Version 1.0
 * @filesource
 */

if ( ! function_exists('xml_parser_create'))
{       
        show_error('Your PHP installation does not support XML');
}


// ------------------------------------------------------------------------

/**
 * XML-RPC request handler class
 *
 * @package             CodeIgniter
 * @subpackage  Libraries
 * @category    XML-RPC
 * @author              ExpressionEngine Dev Team
 * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
 */
class CI_Xmlrpc {

        var $debug                      = FALSE;        // Debugging on or off  
        var $xmlrpcI4           = 'i4';
        var $xmlrpcInt          = 'int';
        var $xmlrpcBoolean      = 'boolean';
        var $xmlrpcDouble       = 'double';     
        var $xmlrpcString       = 'string';
        var $xmlrpcDateTime     = 'datetime.iso8601';
        var $xmlrpcBase64       = 'base64';
        var $xmlrpcArray        = 'array';
        var $xmlrpcStruct       = 'struct';
        
        var $xmlrpcTypes        = array();
        var $valid_parents      = array();
        var $xmlrpcerr          = array();      // Response numbers
        var $xmlrpcstr          = array();  // Response strings
        
        var $xmlrpc_defencoding = 'UTF-8';
        var $xmlrpcName                 = 'XML-RPC for CodeIgniter';
        var $xmlrpcVersion              = '1.1';
        var $xmlrpcerruser              = 800; // Start of user errors
        var $xmlrpcerrxml               = 100; // Start of XML Parse errors
        var $xmlrpc_backslash   = ''; // formulate backslashes for escaping regexp
        
        var $client;
        var $method;
        var $data;
        var $message                    = '';
        var $error                              = '';           // Error string for request
        var $result;
        var $response                   = array();  // Response from remote server


        //-------------------------------------
        //  VALUES THAT MULTIPLE CLASSES NEED
        //-------------------------------------

        function CI_Xmlrpc ($config = array())
        {
                $this->xmlrpcName               = $this->xmlrpcName;
                $this->xmlrpc_backslash = chr(92).chr(92);
                
                // Types for info sent back and forth
                $this->xmlrpcTypes = array(
                        $this->xmlrpcI4    => '1',
                        $this->xmlrpcInt          => '1',
                        $this->xmlrpcBoolean  => '1',
                        $this->xmlrpcString   => '1',
                        $this->xmlrpcDouble   => '1',
                        $this->xmlrpcDateTime => '1',
                        $this->xmlrpcBase64   => '1',
                        $this->xmlrpcArray      => '2',
                        $this->xmlrpcStruct   => '3'
                        );
                        
                // Array of Valid Parents for Various XML-RPC elements
                $this->valid_parents = array('BOOLEAN'                  => array('VALUE'),
                                                                         'I4'                           => array('VALUE'),
                                                                         'INT'                          => array('VALUE'),
                                                                         'STRING'                       => array('VALUE'),
                                                                         'DOUBLE'                       => array('VALUE'),
                                                                         'DATETIME.ISO8601'     => array('VALUE'),
                                                                         'BASE64'                       => array('VALUE'),
                                                                         'ARRAY'                        => array('VALUE'),
                                                                         'STRUCT'                       => array('VALUE'),
                                                                         'PARAM'                        => array('PARAMS'),
                                                                         'METHODNAME'           => array('METHODCALL'),
                                                                         'PARAMS'                       => array('METHODCALL', 'METHODRESPONSE'),
                                                                         'MEMBER'                       => array('STRUCT'),
                                                                         'NAME'                         => array('MEMBER'),
                                                                         'DATA'                         => array('ARRAY'),
                                                                         'FAULT'                        => array('METHODRESPONSE'),
                                                                         'VALUE'                        => array('MEMBER', 'DATA', 'PARAM', 'FAULT')
                                                                         );
                        
                        
                // XML-RPC Responses
                $this->xmlrpcerr['unknown_method'] = '1';
                $this->xmlrpcstr['unknown_method'] = 'This is not a known method for this XML-RPC Server';
                $this->xmlrpcerr['invalid_return'] = '2';
                $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.';
                $this->xmlrpcerr['incorrect_params'] = '3';
                $this->xmlrpcstr['incorrect_params'] = 'Incorrect parameters were passed to method';
                $this->xmlrpcerr['introspect_unknown'] = '4';
                $this->xmlrpcstr['introspect_unknown'] = "Cannot inspect signature for request: method unknown";
                $this->xmlrpcerr['http_error'] = '5';
                $this->xmlrpcstr['http_error'] = "Did not receive a '200 OK' response from remote server.";
                $this->xmlrpcerr['no_data'] = '6';
                $this->xmlrpcstr['no_data'] ='No data received from server.';
                
                $this->initialize($config);
                
                log_message('debug', "XML-RPC Class Initialized");
        }
        
        
        //-------------------------------------
        //  Initialize Prefs
        //-------------------------------------

        function initialize($config = array())
        {
                if (sizeof($config) > 0)
                {
                        foreach ($config as $key => $val)
                        {
                                if (isset($this->$key))
                                {
                                        $this->$key = $val;                     
                                }
                        }
                }
        }
        // END
        
        //-------------------------------------
        //  Take URL and parse it
        //-------------------------------------

        function server($url, $port=80)
        {
                if (substr($url, 0, 4) != "http")
                {
                        $url = "http://".$url;
                }
                
                $parts = parse_url($url);
                
                $path = ( ! isset($parts['path'])) ? '/' : $parts['path'];
                
                if (isset($parts['query']) && $parts['query'] != '')
                {
                        $path .= '?'.$parts['query'];
                }       
                
                $this->client = new XML_RPC_Client($path, $parts['host'], $port);
        }
        // END
        
        //-------------------------------------
        //  Set Timeout
        //-------------------------------------

        function timeout($seconds=5)
        {
                if ( ! is_null($this->client) && is_int($seconds))
                {
                        $this->client->timeout = $seconds;
                }
        }
        // END
        
        //-------------------------------------
        //  Set Methods
        //-------------------------------------

        function method($function)
        {
                $this->method = $function;
        }
        // END
        
        //-------------------------------------
        //  Take Array of Data and Create Objects
        //-------------------------------------

        function request($incoming)
        {
                if ( ! is_array($incoming))
                {
                        // Send Error
                }
                
                $this->data = array();
                
                foreach($incoming as $key => $value)
                {
                        $this->data[$key] = $this->values_parsing($value);
                }
        }
        // END
        
        
        //-------------------------------------
        //  Set Debug
        //-------------------------------------

        function set_debug($flag = TRUE)
        {
                $this->debug = ($flag == TRUE) ? TRUE : FALSE;
        }
        
        //-------------------------------------
        //  Values Parsing
        //-------------------------------------

        function values_parsing($value, $return = FALSE)
        {
                if (is_array($value) && isset($value['0']))
                {
                        if ( ! isset($value['1']) OR ! isset($this->xmlrpcTypes[strtolower($value['1'])]))
                        {
                                if (is_array($value[0]))
                                {
                                        $temp = new XML_RPC_Values($value['0'], 'array');
                                }
                                else
                                {
                                        $temp = new XML_RPC_Values($value['0'], 'string');
                                }
                        }
                        elseif(is_array($value['0']) && ($value['1'] == 'struct' OR $value['1'] == 'array'))
                        {
                                while (list($k) = each($value['0']))
                                {
                                        $value['0'][$k] = $this->values_parsing($value['0'][$k], TRUE);
                                }
                                
                                $temp = new XML_RPC_Values($value['0'], $value['1']);
                        }
                        else
                        {
                                $temp = new XML_RPC_Values($value['0'], $value['1']);
                        }
                }
                else
                {
                        $temp = new XML_RPC_Values($value, 'string');
                }

                return $temp;
        }
        // END


        //-------------------------------------
        //  Sends XML-RPC Request
        //-------------------------------------

        function send_request()
        {
                $this->message = new XML_RPC_Message($this->method,$this->data);
                $this->message->debug = $this->debug;
        
                if ( ! $this->result = $this->client->send($this->message))
                {
                        $this->error = $this->result->errstr;
                        return FALSE;
                }
                elseif( ! is_object($this->result->val))
                {
                        $this->error = $this->result->errstr;
                        return FALSE;
                }
                
                $this->response = $this->result->decode();
                
                return TRUE;
        }
        // END
        
        //-------------------------------------
        //  Returns Error
        //-------------------------------------

        function display_error()
        {
                return $this->error;
        }
        // END
        
        //-------------------------------------
        //  Returns Remote Server Response
        //-------------------------------------

        function display_response()
        {
                return $this->response;
        }
        // END
        
        //-------------------------------------
        //  Sends an Error Message for Server Request
        //-------------------------------------
        
        function send_error_message($number, $message)
        {
                return new XML_RPC_Response('0',$number, $message);
        }
        // END
        
        
        //-------------------------------------
        //  Send Response for Server Request
        //-------------------------------------
        
        function send_response($response)
        {
                // $response should be array of values, which will be parsed
                // based on their data and type into a valid group of XML-RPC values
                
                $response = $this->values_parsing($response);
        
                return new XML_RPC_Response($response);
        }
        // END
        
} // END XML_RPC Class

        
        
/**
 * XML-RPC Client class
 *
 * @category    XML-RPC
 * @author              ExpressionEngine Dev Team
 * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
 */
class XML_RPC_Client extends CI_Xmlrpc
{
        var $path                       = '';
        var $server                     = '';
        var $port                       = 80;
        var $errno                      = '';
        var $errstring          = '';
        var $timeout            = 5;
        var $no_multicall       = false;

        function XML_RPC_Client($path, $server, $port=80)
        {
                parent::CI_Xmlrpc();
                
                $this->port = $port;
                $this->server = $server;
                $this->path = $path;
        }
        
        function send($msg)
        {
                if (is_array($msg))
                {
                        // Multi-call disabled
                        $r = new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'],$this->xmlrpcstr['multicall_recursion']);
                        return $r;
                }

                return $this->sendPayload($msg);
        }

        function sendPayload($msg)
        {       
                $fp = @fsockopen($this->server, $this->port,$this->errno, $this->errstr, $this->timeout);
                
                if ( ! is_resource($fp))
                {
                        error_log($this->xmlrpcstr['http_error']);
                        $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'],$this->xmlrpcstr['http_error']);
                        return $r;
                }
                
                if(empty($msg->payload))
                {
                        // $msg = XML_RPC_Messages
                        $msg->createPayload();
                }
                
                $r = "\r\n";
                $op  = "POST {$this->path} HTTP/1.0$r";
                $op .= "Host: {$this->server}$r";
                $op .= "Content-Type: text/xml$r";
                $op .= "User-Agent: {$this->xmlrpcName}$r";
                $op .= "Content-Length: ".strlen($msg->payload). "$r$r";
                $op .= $msg->payload;
                

                if ( ! fputs($fp, $op, strlen($op)))
                {
                        error_log($this->xmlrpcstr['http_error']);
                        $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
                        return $r;
                }
                $resp = $msg->parseResponse($fp);
                fclose($fp);
                return $resp;
        }

} // end class XML_RPC_Client


/**
 * XML-RPC Response class
 *
 * @category    XML-RPC
 * @author              ExpressionEngine Dev Team
 * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
 */
class XML_RPC_Response
{
        var $val = 0;
        var $errno = 0;
        var $errstr = '';
        var $headers = array();

        function XML_RPC_Response($val, $code = 0, $fstr = '')
        {       
                if ($code != 0)
                {
                        // error
                        $this->errno = $code;
                        $this->errstr = htmlentities($fstr);
                }
                else if ( ! is_object($val))
                {
                        // programmer error, not an object
                        error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to XML_RPC_Response.  Defaulting to empty value.");
                        $this->val = new XML_RPC_Values();
                }
                else
                {
                        $this->val = $val;
                }
        }

        function faultCode()
        {
                return $this->errno;
        }

        function faultString()
        {
                return $this->errstr;
        }

        function value()
        {
                return $this->val;
        }
        
        function prepare_response()
        {
                $result = "<methodResponse>\n";
                if ($this->errno)
                {
                        $result .= '<fault>
        <value>
                <struct>
                        <member>
                                <name>faultCode</name>
                                <value><int>' . $this->errno . '</int></value>
                        </member>
                        <member>
                                <name>faultString</name>
                                <value><string>' . $this->errstr . '</string></value>
                        </member>
                </struct>
        </value>
</fault>';
                }
                else
                {
                        $result .= "<params>\n<param>\n" .
                                        $this->val->serialize_class() .
                                        "</param>\n</params>";
                }
                $result .= "\n</methodResponse>";
                return $result;
        }
        
        function decode($array=FALSE)
        {
                $CI =& get_instance();

                if ($array !== FALSE && is_array($array))
                {
                        while (list($key) = each($array))
                        {
                                if (is_array($array[$key]))
                                {
                                        $array[$key] = $this->decode($array[$key]);
                                }
                                else
                                {
                                        $array[$key] = $CI->input->xss_clean($array[$key]);
                                }
                        }
                        
                        $result = $array;
                }
                else
                {
                        $result = $this->xmlrpc_decoder($this->val);
                        
                        if (is_array($result))
                        {
                                $result = $this->decode($result);
                        }
                        else
                        {
                                $result = $CI->input->xss_clean($result);
                        }
                }
                
                return $result;
        }

        
        
        //-------------------------------------
        //  XML-RPC Object to PHP Types
        //-------------------------------------

        function xmlrpc_decoder($xmlrpc_val)
        {
                $kind = $xmlrpc_val->kindOf();

                if($kind == 'scalar')
                {
                        return $xmlrpc_val->scalarval();
                }
                elseif($kind == 'array')
                {
                        reset($xmlrpc_val->me);
                        list($a,$b) = each($xmlrpc_val->me);
                        $size = sizeof($b);
                        
                        $arr = array();

                        for($i = 0; $i < $size; $i++)
                        {
                                $arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]);
                        }
                        return $arr;
                }
                elseif($kind == 'struct')
                {
                        reset($xmlrpc_val->me['struct']);
                        $arr = array();

                        while(list($key,$value) = each($xmlrpc_val->me['struct']))
                        {
                                $arr[$key] = $this->xmlrpc_decoder($value);
                        }
                        return $arr;
                }
        }
        
        
        //-------------------------------------
        //  ISO-8601 time to server or UTC time
        //-------------------------------------

        function iso8601_decode($time, $utc=0)
        {
                // return a timet in the localtime, or UTC
                $t = 0;
                if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs))
                {
                        if ($utc == 1)
                                $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
                        else
                                $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
                }
                return $t;
        }
        
} // End Response Class



/**
 * XML-RPC Message class
 *
 * @category    XML-RPC
 * @author              ExpressionEngine Dev Team
 * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
 */
class XML_RPC_Message extends CI_Xmlrpc
{
        var $payload;
        var $method_name;
        var $params                     = array();
        var $xh                         = array();

        function XML_RPC_Message($method, $pars=0)
        {
                parent::CI_Xmlrpc();
                
                $this->method_name = $method;
                if (is_array($pars) && sizeof($pars) > 0)
                {
                        for($i=0; $i<sizeof($pars); $i++)
                        {
                                // $pars[$i] = XML_RPC_Values
                                $this->params[] = $pars[$i];
                        }
                }
        }
        
        //-------------------------------------
        //  Create Payload to Send
        //-------------------------------------
        
        function createPayload()
        {
                $this->payload = "<?xml version=\"1.0\"?".">\r\n<methodCall>\r\n";
                $this->payload .= '<methodName>' . $this->method_name . "</methodName>\r\n";
                $this->payload .= "<params>\r\n";
                
                for($i=0; $i<sizeof($this->params); $i++)
                {
                        // $p = XML_RPC_Values
                        $p = $this->params[$i];
                        $this->payload .= "<param>\r\n".$p->serialize_class()."</param>\r\n";
                }
                
                $this->payload .= "</params>\r\n</methodCall>\r\n";
        }
        
        //-------------------------------------
        //  Parse External XML-RPC Server's Response
        //-------------------------------------
        
        function parseResponse($fp)
        {
                $data = '';
                
                while($datum = fread($fp, 4096))
                {
                        $data .= $datum;
                }
                
                //-------------------------------------
                //  DISPLAY HTTP CONTENT for DEBUGGING
                //-------------------------------------
                
                if ($this->debug === TRUE)
                {
                        echo "<pre>";
                        echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
                        echo "</pre>";
                }
                
                //-------------------------------------
                //  Check for data
                //-------------------------------------

                if($data == "")
                {
                        error_log($this->xmlrpcstr['no_data']);
                        $r = new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']);
                        return $r;
                }
                
                
                //-------------------------------------
                //  Check for HTTP 200 Response
                //-------------------------------------
                
                if (strncmp($data, 'HTTP', 4) == 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data))
                {
                        $errstr= substr($data, 0, strpos($data, "\n")-1);
                        $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']. ' (' . $errstr . ')');
                        return $r;
                }
                
                //-------------------------------------
                //  Create and Set Up XML Parser
                //-------------------------------------
        
                $parser = xml_parser_create($this->xmlrpc_defencoding);

                $this->xh[$parser]                               = array();
                $this->xh[$parser]['isf']                = 0;
                $this->xh[$parser]['ac']                 = '';
                $this->xh[$parser]['headers']    = array();
                $this->xh[$parser]['stack']              = array();
                $this->xh[$parser]['valuestack'] = array();
                $this->xh[$parser]['isf_reason'] = 0;

                xml_set_object($parser, $this);
                xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
                xml_set_element_handler($parser, 'open_tag', 'closing_tag');
                xml_set_character_data_handler($parser, 'character_data');
                //xml_set_default_handler($parser, 'default_handler');


                //-------------------------------------
                //  GET HEADERS
                //-------------------------------------
                
                $lines = explode("\r\n", $data);
                while (($line = array_shift($lines)))
                {
                        if (strlen($line) < 1)
                        {
                                break;
                        }
                        $this->xh[$parser]['headers'][] = $line;
                }
                $data = implode("\r\n", $lines);
                
                
                //-------------------------------------
                //  PARSE XML DATA
                //-------------------------------------         

                if ( ! xml_parse($parser, $data, sizeof($data)))
                {
                        $errstr = sprintf('XML error: %s at line %d',
                                        xml_error_string(xml_get_error_code($parser)),
                                        xml_get_current_line_number($parser));
                        //error_log($errstr);
                        $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
                        xml_parser_free($parser);
                        return $r;
                }
                xml_parser_free($parser);
                
                // ---------------------------------------
                //  Got Ourselves Some Badness, It Seems
                // ---------------------------------------
                
                if ($this->xh[$parser]['isf'] > 1)
                {
                        if ($this->debug === TRUE)
                        {
                                echo "---Invalid Return---\n";
                                echo $this->xh[$parser]['isf_reason'];
                                echo "---Invalid Return---\n\n";
                        }
                                
                        $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
                        return $r;
                }
                elseif ( ! is_object($this->xh[$parser]['value']))
                {
                        $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
                        return $r;
                }
                
                //-------------------------------------
                //  DISPLAY XML CONTENT for DEBUGGING
                //-------------------------------------         
                
                if ($this->debug === TRUE)
                {
                        echo "<pre>";
                        
                        if (count($this->xh[$parser]['headers'] > 0))
                        {
                                echo "---HEADERS---\n";
                                foreach ($this->xh[$parser]['headers'] as $header)
                                {
                                        echo "$header\n";
                                }
                                echo "---END HEADERS---\n\n";
                        }
                        
                        echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
                        
                        echo "---PARSED---\n" ;
                        var_dump($this->xh[$parser]['value']);
                        echo "\n---END PARSED---</pre>";
                }
                
                //-------------------------------------
                //  SEND RESPONSE
                //-------------------------------------
                
                $v = $this->xh[$parser]['value'];
                        
                if ($this->xh[$parser]['isf'])
                {
                        $errno_v = $v->me['struct']['faultCode'];
                        $errstr_v = $v->me['struct']['faultString'];
                        $errno = $errno_v->scalarval();

                        if ($errno == 0)
                        {
                                // FAULT returned, errno needs to reflect that
                                $errno = -1;
                        }

                        $r = new XML_RPC_Response($v, $errno, $errstr_v->scalarval());
                }
                else
                {
                        $r = new XML_RPC_Response($v);
                }

                $r->headers = $this->xh[$parser]['headers'];
                return $r;
        }
        
        // ------------------------------------
        //  Begin Return Message Parsing section
        // ------------------------------------
        
        // quick explanation of components:
        //   ac - used to accumulate values
        //   isf - used to indicate a fault
        //   lv - used to indicate "looking for a value": implements
        //              the logic to allow values with no types to be strings
        //   params - used to store parameters in method calls
        //   method - used to store method name
        //       stack - array with parent tree of the xml element,
        //                       used to validate the nesting of elements

        //-------------------------------------
        //  Start Element Handler
        //-------------------------------------

        function open_tag($the_parser, $name, $attrs)
        {
                // If invalid nesting, then return
                if ($this->xh[$the_parser]['isf'] > 1) return;
                
                // Evaluate and check for correct nesting of XML elements
                
                if (count($this->xh[$the_parser]['stack']) == 0)
                {
                        if ($name != 'METHODRESPONSE' && $name != 'METHODCALL')
                        {
                                $this->xh[$the_parser]['isf'] = 2;
                                $this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing';
                                return;
                        }
                }
                else
                {
                        // not top level element: see if parent is OK
                        if ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE))
                        {
                                $this->xh[$the_parser]['isf'] = 2;
                                $this->xh[$the_parser]['isf_reason'] = "XML-RPC element $name cannot be child of ".$this->xh[$the_parser]['stack'][0];
                                return;
                        }
                }
                
                switch($name)
                {
                        case 'STRUCT':
                        case 'ARRAY':
                                // Creates array for child elements
                                
                                $cur_val = array('value' => array(),
                                                                 'type'  => $name);
                                                                
                                array_unshift($this->xh[$the_parser]['valuestack'], $cur_val);
                        break;
                        case 'METHODNAME':
                        case 'NAME':
                                $this->xh[$the_parser]['ac'] = '';
                        break;
                        case 'FAULT':
                                $this->xh[$the_parser]['isf'] = 1;
                        break;
                        case 'PARAM':
                                $this->xh[$the_parser]['value'] = null;
                        break;
                        case 'VALUE':
                                $this->xh[$the_parser]['vt'] = 'value';
                                $this->xh[$the_parser]['ac'] = '';
                                $this->xh[$the_parser]['lv'] = 1;
                        break;
                        case 'I4':
                        case 'INT':
                        case 'STRING':
                        case 'BOOLEAN':
                        case 'DOUBLE':
                        case 'DATETIME.ISO8601':
                        case 'BASE64':
                                if ($this->xh[$the_parser]['vt'] != 'value')
                                {
                                        //two data elements inside a value: an error occurred!
                                        $this->xh[$the_parser]['isf'] = 2;
                                        $this->xh[$the_parser]['isf_reason'] = "'Twas a $name element following a ".$this->xh[$the_parser]['vt']." element inside a single value";
                                        return;
                                }
                                
                                $this->xh[$the_parser]['ac'] = '';
                        break;
                        case 'MEMBER':
                                // Set name of <member> to nothing to prevent errors later if no <name> is found
                                $this->xh[$the_parser]['valuestack'][0]['name'] = '';
                                
                                // Set NULL value to check to see if value passed for this param/member
                                $this->xh[$the_parser]['value'] = null;
                        break;
                        case 'DATA':
                        case 'METHODCALL':
                        case 'METHODRESPONSE':
                        case 'PARAMS':
                                // valid elements that add little to processing
                        break;
                        default:
                                /// An Invalid Element is Found, so we have trouble
                                $this->xh[$the_parser]['isf'] = 2;
                                $this->xh[$the_parser]['isf_reason'] = "Invalid XML-RPC element found: $name";
                        break;
                }
                
                // Add current element name to stack, to allow validation of nesting
                array_unshift($this->xh[$the_parser]['stack'], $name);

                if ($name != 'VALUE') $this->xh[$the_parser]['lv'] = 0;
        }
        // END


        //-------------------------------------
        //  End Element Handler
        //-------------------------------------

        function closing_tag($the_parser, $name)
        {
                if ($this->xh[$the_parser]['isf'] > 1) return;
                
                // Remove current element from stack and set variable
                // NOTE: If the XML validates, then we do not have to worry about
                // the opening and closing of elements.  Nesting is checked on the opening
                // tag so we be safe there as well.
                
                $curr_elem = array_shift($this->xh[$the_parser]['stack']);
        
                switch($name)
                {
                        case 'STRUCT':
                        case 'ARRAY':
                                $cur_val = array_shift($this->xh[$the_parser]['valuestack']);
                                $this->xh[$the_parser]['value'] = ( ! isset($cur_val['values'])) ? array() : $cur_val['values'];
                                $this->xh[$the_parser]['vt']    = strtolower($name);
                        break;
                        case 'NAME':
                                $this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac'];
                        break;
                        case 'BOOLEAN':
                        case 'I4':
                        case 'INT':
                        case 'STRING':
                        case 'DOUBLE':
                        case 'DATETIME.ISO8601':
                        case 'BASE64':
                                $this->xh[$the_parser]['vt'] = strtolower($name);
                                
                                if ($name == 'STRING')
                                {
                                        $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
                                }
                                elseif ($name=='DATETIME.ISO8601')
                                {
                                        $this->xh[$the_parser]['vt']    = $this->xmlrpcDateTime;
                                        $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
                                }
                                elseif ($name=='BASE64')
                                {
                                        $this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']);
                                }
                                elseif ($name=='BOOLEAN')
                                {
                                        // Translated BOOLEAN values to TRUE AND FALSE
                                        if ($this->xh[$the_parser]['ac'] == '1')
                                        {
                                                $this->xh[$the_parser]['value'] = TRUE;
                                        }
                                        else
                                        {
                                                $this->xh[$the_parser]['value'] = FALSE;
                                        }
                                }
                                elseif ($name=='DOUBLE')
                                {
                                        // we have a DOUBLE
                                        // we must check that only 0123456789-.<space> are characters here
                                        if ( ! preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh[$the_parser]['ac']))
                                        {
                                                $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
                                        }
                                        else
                                        {
                                                $this->xh[$the_parser]['value'] = (double)$this->xh[$the_parser]['ac'];
                                        }
                                }
                                else
                                {
                                        // we have an I4/INT
                                        // we must check that only 0123456789-<space> are characters here
                                        if ( ! preg_match('/^[+-]?[0-9\t ]+$/', $this->xh[$the_parser]['ac']))
                                        {
                                                $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
                                        }
                                        else
                                        {
                                                $this->xh[$the_parser]['value'] = (int)$this->xh[$the_parser]['ac'];
                                        }
                                }
                                $this->xh[$the_parser]['ac'] = '';
                                $this->xh[$the_parser]['lv'] = 3; // indicate we've found a value
                        break;
                        case 'VALUE':
                                // This if() detects if no scalar was inside <VALUE></VALUE>
                                if ($this->xh[$the_parser]['vt']=='value')
                                {
                                        $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
                                        $this->xh[$the_parser]['vt']    = $this->xmlrpcString;
                                }
                                
                                // build the XML-RPC value out of the data received, and substitute it
                                $temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']);
                                
                                if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] == 'ARRAY')
                                {
                                        // Array
                                        $this->xh[$the_parser]['valuestack'][0]['values'][] = $temp;
                                }
                                else
                                {
                                        // Struct
                                        $this->xh[$the_parser]['value'] = $temp;
                                }
                        break;
                        case 'MEMBER':
                                $this->xh[$the_parser]['ac']='';
                                
                                // If value add to array in the stack for the last element built
                                if ($this->xh[$the_parser]['value'])
                                {
                                        $this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value'];
                                }
                        break;
                        case 'DATA':
                                $this->xh[$the_parser]['ac']='';
                        break;
                        case 'PARAM':
                                if ($this->xh[$the_parser]['value'])
                                {
                                        $this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value'];
                                }
                        break;
                        case 'METHODNAME':
                                $this->xh[$the_parser]['method'] = ltrim($this->xh[$the_parser]['ac']);
                        break;
                        case 'PARAMS':
                        case 'FAULT':
                        case 'METHODCALL':
                        case 'METHORESPONSE':
                                // We're all good kids with nuthin' to do
                        break;
                        default:
                                // End of an Invalid Element.  Taken care of during the opening tag though
                        break;
                }
        }

        //-------------------------------------
        //  Parses Character Data
        //-------------------------------------

        function character_data($the_parser, $data)
        {
                if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already
                
                // If a value has not been found
                if ($this->xh[$the_parser]['lv'] != 3)
                {
                        if ($this->xh[$the_parser]['lv'] == 1)
                        {
                                $this->xh[$the_parser]['lv'] = 2; // Found a value
                        }
                                
                        if( ! @isset($this->xh[$the_parser]['ac']))
                        {
                                $this->xh[$the_parser]['ac'] = '';
                        }
                                
                        $this->xh[$the_parser]['ac'] .= $data;
                }
        }
        
        
        function addParam($par) { $this->params[]=$par; }
        
        function output_parameters($array=FALSE)
        {
                $CI =& get_instance();  

                if ($array !== FALSE && is_array($array))
                {
                        while (list($key) = each($array))
                        {
                                if (is_array($array[$key]))
                                {
                                        $array[$key] = $this->output_parameters($array[$key]);
                                }
                                else
                                {
                                        $array[$key] = $CI->input->xss_clean($array[$key]);
                                }
                        }
                        
                        $parameters = $array;
                }
                else
                {
                        $parameters = array();
                
                        for ($i = 0; $i < sizeof($this->params); $i++)
                        {
                                $a_param = $this->decode_message($this->params[$i]);
                                
                                if (is_array($a_param))
                                {
                                        $parameters[] = $this->output_parameters($a_param);
                                }
                                else
                                {
                                        $parameters[] = $CI->input->xss_clean($a_param);
                                }
                        }       
                }
                
                return $parameters;
        }
        
        
        function decode_message($param)
        {
                $kind = $param->kindOf();

                if($kind == 'scalar')
                {
                        return $param->scalarval();
                }
                elseif($kind == 'array')
                {
                        reset($param->me);
                        list($a,$b) = each($param->me);
                        
                        $arr = array();

                        for($i = 0; $i < sizeof($b); $i++)
                        {
                                $arr[] = $this->decode_message($param->me['array'][$i]);
                        }
                        
                        return $arr;
                }
                elseif($kind == 'struct')
                {
                        reset($param->me['struct']);
                        
                        $arr = array();

                        while(list($key,$value) = each($param->me['struct']))
                        {
                                $arr[$key] = $this->decode_message($value);
                        }
                        
                        return $arr;
                }
        }
        
} // End XML_RPC_Messages class



/**
 * XML-RPC Values class
 *
 * @category    XML-RPC
 * @author              ExpressionEngine Dev Team
 * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
 */
class XML_RPC_Values extends CI_Xmlrpc
{
        var $me         = array();
        var $mytype     = 0;

        function XML_RPC_Values($val=-1, $type='')
        {       
                parent::CI_Xmlrpc();
                
                if ($val != -1 OR $type != '')
                {
                        $type = $type == '' ? 'string' : $type;
                        
                        if ($this->xmlrpcTypes[$type] == 1)
                        {
                                $this->addScalar($val,$type);
                        }
                        elseif ($this->xmlrpcTypes[$type] == 2)
                        {
                                $this->addArray($val);
                        }
                        elseif ($this->xmlrpcTypes[$type] == 3)
                        {
                                $this->addStruct($val);
                        }
                }
        }

        function addScalar($val, $type='string')
        {
                $typeof = $this->xmlrpcTypes[$type];
                
                if ($this->mytype==1)
                {
                        echo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />';
                        return 0;
                }
                
                if ($typeof != 1)
                {
                        echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />';
                        return 0;
                }

                if ($type == $this->xmlrpcBoolean)
                {
                        if (strcasecmp($val,'true')==0 OR $val==1 OR ($val==true && strcasecmp($val,'false')))
                        {
                                $val = 1;
                        }
                        else
                        {
                                $val=0;
                        }
                }

                if ($this->mytype == 2)
                {
                        // adding to an array here
                        $ar = $this->me['array'];
                        $ar[] = new XML_RPC_Values($val, $type);
                        $this->me['array'] = $ar;
                }
                else
                {
                        // a scalar, so set the value and remember we're scalar
                        $this->me[$type] = $val;
                        $this->mytype = $typeof;
                }
                return 1;
        }

        function addArray($vals)
        {
                if ($this->mytype != 0)
                {
                        echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
                        return 0;
                }

                $this->mytype = $this->xmlrpcTypes['array'];
                $this->me['array'] = $vals;
                return 1;
        }

        function addStruct($vals)
        {
                if ($this->mytype != 0)
                {
                        echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
                        return 0;
                }
                $this->mytype = $this->xmlrpcTypes['struct'];
                $this->me['struct'] = $vals;
                return 1;
        }

        function kindOf()
        {
                switch($this->mytype)
                {
                        case 3:
                                return 'struct';
                                break;
                        case 2:
                                return 'array';
                                break;
                        case 1:
                                return 'scalar';
                                break;
                        default:
                                return 'undef';
                }
        }

        function serializedata($typ, $val)
        {
                $rs = '';
                
                switch($this->xmlrpcTypes[$typ])
                {
                        case 3:
                                // struct
                                $rs .= "<struct>\n";
                                reset($val);
                                while(list($key2, $val2) = each($val))
                                {
                                        $rs .= "<member>\n<name>{$key2}</name>\n";
                                        $rs .= $this->serializeval($val2);
                                        $rs .= "</member>\n";
                                }
                                $rs .= '</struct>';
                        break;
                        case 2:
                                // array
                                $rs .= "<array>\n<data>\n";
                                for($i=0; $i < sizeof($val); $i++)
                                {
                                        $rs .= $this->serializeval($val[$i]);
                                }
                                $rs.="</data>\n</array>\n";
                                break;
                        case 1:
                                // others
                                switch ($typ)
                                {
                                        case $this->xmlrpcBase64:
                                                $rs .= "<{$typ}>" . base64_encode($val) . "</{$typ}>\n";
                                        break;
                                        case $this->xmlrpcBoolean:
                                                $rs .= "<{$typ}>" . ($val ? '1' : '0') . "</{$typ}>\n";
                                        break;
                                        case $this->xmlrpcString:
                                                $rs .= "<{$typ}>" . htmlspecialchars($val). "</{$typ}>\n";
                                        break;
                                        default:
                                                $rs .= "<{$typ}>{$val}</{$typ}>\n";
                                        break;
                                }
                        default:
                        break;
                }
                return $rs;
        }

        function serialize_class()
        {
                return $this->serializeval($this);
        }

        function serializeval($o)
        {
                $ar = $o->me;
                reset($ar);
                
                list($typ, $val) = each($ar);
                $rs = "<value>\n".$this->serializedata($typ, $val)."</value>\n";
                return $rs;
        }
        
        function scalarval()
        {
                reset($this->me);
                list($a,$b) = each($this->me);
                return $b;
        }


        //-------------------------------------
        // Encode time in ISO-8601 form.
        //-------------------------------------
        
        // Useful for sending time in XML-RPC

        function iso8601_encode($time, $utc=0)
        {       
                if ($utc == 1)
                {
                        $t = strftime("%Y%m%dT%H:%M:%S", $time);
                }
                else
                {
                        if (function_exists('gmstrftime'))
                                $t = gmstrftime("%Y%m%dT%H:%M:%S", $time);
                        else
                                $t = strftime("%Y%m%dT%H:%M:%S", $time - date('Z'));
                }
                return $t;
        }
        
}
// END XML_RPC_Values Class

/* End of file Xmlrpc.php */
/* Location: ./system/libraries/Xmlrpc.php */