Thursday, June 11, 2009

Secure HTTP/HTTPS capable XmlRpc library for PHP

IXR - The Inutio XML-RPC Library is one of my favorite Xmlrpc library for PHP. I found it very straight forward,simple and powerful xmlrpc client. Unfortunately, it doesn't have capability to connect using secure HTTP protocol / HTTPS. So, I decided to give more "security" juice to it. The trick is to use fopen instead of fsockopen function to send HTTP POST method to the server. For your notice, this trick is for PHP ver 5 or above only. Replace the function query() in file IXR_Library.inc, with this modification:

/*
* Sends a form-encoded HTTP(S) POST to a given URL.
* Returns a string containing the response.
*
* If response code is not 200, throws an error msg.
*/
function SendHttpPost($url,$content)
{


$context_options = array(
'http'=>array(
'method'=>"POST",
'header'=> "User-Agent: {$this->useragent}\r\n".
"Content-type: text/xml\r\n".
"Content-length: " . strlen($content) . "\r\n"
,
'content'=> $content
)
);
// Make sure the HTTPS options are the same as HTTP
$context_options['https'] = $context_options['http'];
$context = stream_context_create($context_options);
echo 'context:'.var_export($context_options,true);
$fp = fopen($url, 'r', false, $context);
// Get the response...
$response = '';
/* For HTTPS, IIS closes the SSL socket improperly hence
* generating a warning. We want to suppress this, as some
* servers will be doing this.
*
* see http://uk.php.net/manual/en/wrappers.http.php
*/
$old_error_reporting = error_reporting();
error_reporting($old_error_reporting & ~E_WARNING);
// Get the headers
$metadata = stream_get_meta_data($fp);
foreach($metadata['wrapper_data'] as $responseheader) {
// Look for the status header...
if (substr($responseheader,0,5) == 'HTTP/') {
$bits = split(' ', $responseheader);
$httpstatus = (int) $bits[1];
$httpstatusmessage = $responseheader;
}
}
// There is no need to check the HTTP status, as fopen wrapper
// that for us anyway.
// Read the the response data
while (!feof($fp)) {
$response .= fread($fp, 8192);
}
fclose($fp);
error_reporting($old_error_reporting);
return $response;
}


function query() {
$args = func_get_args();
$method = array_shift($args);
$request = new IXR_Request($method, $args);
$length = $request->getLength();
$xml = $request->getXml();
// Modification from Original ------------------
// $r = "\r\n";
// $request = "POST {$this->path} HTTP/1.0$r";
// $request .= "Host: {$this->server}$r";
// $request .= "Content-Type: text/xml$r";
// $request .= "User-Agent: {$this->useragent}$r";
// $request .= "Content-length: {$length}$r$r";
// $request .= $xml;
// // Now send the request
// if ($this->debug) {
// echo '
'.htmlspecialchars($request)."\n
\n\n";
// }
// $fp = @fsockopen($this->server, $this->port);
// if (!$fp) {
// $this->error = new IXR_Error(-32300, 'transport error - could not open socket');
// return false;
// }
// fputs($fp, $request);
// $contents = '';
// $gotFirstLine = false;
// $gettingHeaders = true;
// while (!feof($fp)) {
// $line = fgets($fp, 4096);
// if (!$gotFirstLine) {
// // Check line for '200'
// if (strstr($line, '200') === false) {
// $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200');
// return false;
// }
// $gotFirstLine = true;
// }
// if (trim($line) == '') {
// $gettingHeaders = false;
// }
// if (!$gettingHeaders) {
// $contents .= trim($line)."\n";
// }
// }


// end of Modification from Original ------------------

if ($this->debug) {
echo 'server:'.$this->server.'
'.htmlspecialchars( $xml)."\n
\n\n";
}
// Add new function to send HTTP Post
$contents=$this->SendHttpPost($this->server,$xml);
if ($this->debug) {
echo '
'.htmlspecialchars($contents)."\n
\n\n";
}
// Now parse what we've got back
$this->message = new IXR_Message($contents);
if (!$this->message->parse()) {
// XML error
$this->error = new IXR_Error(-32700, 'parse error. not well formed');
return false;
}
// Is the message a fault?
if ($this->message->messageType == 'fault') {
$this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
return false;
}
// Message must be OK
return true;
}

No comments:

Post a Comment