params[] = $pars[$i];
}
}
}
//-------------------------------------
// Create Payload to Send
//-------------------------------------
function createPayload()
{
$this->payload = "\r\n\r\n";
$this->payload .= '' . $this->method_name . "\r\n";
$this->payload .= "\r\n";
for($i=0; $iparams); $i++)
{
// $p = XML_RPC_Values
$p = $this->params[$i];
$this->payload .= "\r\n".$p->serialize_class()."\r\n";
}
$this->payload .= "\r\n\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 "";
echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
echo "
";
}
//-------------------------------------
// 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 "";
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---
";
}
//-------------------------------------
// 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 to nothing to prevent errors later if no 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-. 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- 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
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 || $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 'XML_RPC_Values: scalar can have only one value
';
return 0;
}
if ($typeof != 1)
{
echo 'XML_RPC_Values: not a scalar type (${typeof})
';
return 0;
}
if ($type == $this->xmlrpcBoolean)
{
if (strcasecmp($val,'true')==0 || $val==1 || ($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 'XML_RPC_Values: already initialized as a [' . $this->kindOf() . ']
';
return 0;
}
$this->mytype = $this->xmlrpcTypes['array'];
$this->me['array'] = $vals;
return 1;
}
function addStruct($vals)
{
if ($this->mytype != 0)
{
echo 'XML_RPC_Values: already initialized as a [' . $this->kindOf() . ']
';
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 .= "\n";
reset($val);
while(list($key2, $val2) = each($val))
{
$rs .= "\n{$key2}\n";
$rs .= $this->serializeval($val2);
$rs .= "\n";
}
$rs .= '';
break;
case 2:
// array
$rs .= "\n\n";
for($i=0; $i < sizeof($val); $i++)
{
$rs .= $this->serializeval($val[$i]);
}
$rs.="\n\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 = "\n".$this->serializedata($typ, $val)."\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