668 lines
13 KiB
PHP
668 lines
13 KiB
PHP
<?php
|
|
/*
|
|
**************************************************************************
|
|
*
|
|
* OpenSRS-PHP
|
|
*
|
|
* Copyright (C) 2000, 2001, 2002, 2003 Colin Viebrock
|
|
* and easyDNS Technologies Inc.
|
|
*
|
|
**************************************************************************
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
**************************************************************************
|
|
*
|
|
* vim: set expandtab tabstop=4 shiftwidth=4:
|
|
* $Id: OPS.php,v 1.1 2004/09/30 09:25:23 Tony Exp $
|
|
*
|
|
**************************************************************************
|
|
*/
|
|
|
|
|
|
require_once 'PEAR.php';
|
|
|
|
class OPS extends PEAR {
|
|
|
|
var $_OPS_VERSION = '0.9';
|
|
var $_OPT = '';
|
|
var $_SPACER = ' '; /* indent character */
|
|
var $_CRLF = "\n";
|
|
var $_MSGTYPE_STD = 'standard';
|
|
var $_SESSID;
|
|
var $_MSGCNT;
|
|
|
|
var $CRLF = "\r\n";
|
|
|
|
var $_log = array();
|
|
|
|
var $_data;
|
|
var $_pointers;
|
|
var $_last_was_data_block;
|
|
|
|
|
|
|
|
/**
|
|
* Class constructor
|
|
*
|
|
* Initialize variables, logs, etc.
|
|
*
|
|
* @param array allows for setting various options (right now, just whether
|
|
* to use compression or not on the generated XML)
|
|
*/
|
|
|
|
function OPS($args=false)
|
|
{
|
|
|
|
$this->PEAR();
|
|
|
|
if (is_array($args)) {
|
|
if ($args['option']=='compress') {
|
|
$this->_OPT = 'compress';
|
|
$this->_SPACER = '';
|
|
$this->_CRLF = '';
|
|
}
|
|
}
|
|
|
|
$this->_SESSID = getmypid();
|
|
$this->_MSGCNT = 0;
|
|
|
|
$this->_log('raw','i','OPS Raw Log:');
|
|
$this->_log('raw','i','Initialized '.date('r') );
|
|
|
|
$this->_log('xml','i','OPS XML Log:');
|
|
$this->_log('xml','i','Initialized '.date('r') );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Writes a message to a socket (buffered IO)
|
|
*
|
|
* @param int socket handle
|
|
*
|
|
* @param string message to write
|
|
*
|
|
*/
|
|
|
|
function writeData(&$fh,$msg)
|
|
{
|
|
$len = strlen($msg);
|
|
fputs($fh, 'Content-Length: ' . $len . $this->CRLF . $this->CRLF);
|
|
fputs($fh, $msg, $len );
|
|
|
|
$this->_log('raw', 'w', $msg, $len);
|
|
}
|
|
|
|
|
|
/**
|
|
* Encodes and writes a message to a socket
|
|
*
|
|
* @param int socket handle
|
|
*
|
|
* @param string message to encode and write
|
|
*
|
|
*/
|
|
|
|
function writeMessage(&$fh, $hr )
|
|
{
|
|
$msg = $this->encode( $hr );
|
|
$this->writeData($fh, $msg );
|
|
}
|
|
|
|
|
|
/**
|
|
* Reads data from a socket
|
|
*
|
|
* @param int socket handle
|
|
*
|
|
* @param int timeout for read
|
|
*
|
|
* @return mixed buffer with data, or an error for a short read
|
|
*
|
|
*/
|
|
|
|
function readData(&$fh, $timeout=5)
|
|
{
|
|
$len = 0;
|
|
|
|
/* PHP doesn't have timeout for fread ... we just set the timeout for the socket */
|
|
|
|
socket_set_timeout($fh, $timeout);
|
|
|
|
$line = fgets($fh, 4000);
|
|
|
|
if ($this->socketStatus($fh)) {
|
|
return false;
|
|
}
|
|
|
|
if (!$len && preg_match('/^\s*Content-Length:\s+(\d+)\s*\r\n/i', $line, $matches ) ) {
|
|
$len = (int)$matches[1];
|
|
} else {
|
|
$this->_log('raw', 'e', 'UNEXPECTED READ: No Content-Length' );
|
|
$this->_log('raw', 'r', $line);
|
|
return false;
|
|
}
|
|
|
|
/* read the empty line */
|
|
|
|
$line = fread($fh, 2);
|
|
if ($this->socketStatus($fh)) {
|
|
return false;
|
|
}
|
|
|
|
if ($line!=$this->CRLF) {
|
|
$this->_log('raw', 'e', 'UNEXPECTED READ: No CRLF');
|
|
$this->_log('raw', 'r', $line);
|
|
return false;
|
|
}
|
|
|
|
$line = '';
|
|
while (strlen($line) < $len) {
|
|
$line .= fread($fh, $len);
|
|
if ($this->socketStatus($fh)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ($line) {
|
|
$buf = $line;
|
|
$this->_log('raw', 'r', $line);
|
|
} else {
|
|
$buf = false;
|
|
$this->_log('raw', 'e', 'NEXT LINE SHORT READ (should be '.$len.')' );
|
|
$this->_log('raw', 'r', $line);
|
|
}
|
|
|
|
return $buf;
|
|
}
|
|
|
|
|
|
/**
|
|
* Reads and decodes data from a socket
|
|
*
|
|
* @param int socket handle
|
|
*
|
|
* @param int timeout for read
|
|
*
|
|
* @return mixed associative array of data, or an error
|
|
*
|
|
*/
|
|
function readMessage(&$fh, $timeout=5)
|
|
{
|
|
$buf = $this->readData($fh, $timeout);
|
|
return ( $buf ? $this->decode($buf) : false );
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Checks a socket for timeout or EOF
|
|
*
|
|
* @param int socket handle
|
|
*
|
|
* @return boolean true if the socket has timed out or is EOF
|
|
*
|
|
*/
|
|
|
|
function socketStatus(&$fh)
|
|
{
|
|
$return = false;
|
|
if (is_resource($fh)) {
|
|
$temp = socket_get_status($fh);
|
|
if ($temp['timed_out']) {
|
|
$this->_log('raw', 'e', 'SOCKET TIMED OUT');
|
|
$return = true;
|
|
}
|
|
if ($temp['eof']) {
|
|
$this->_log('raw', 'e', 'SOCKET EOF');
|
|
$return = true;
|
|
}
|
|
unset($temp);
|
|
}
|
|
return $return;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Internal method to generate error codes hashes
|
|
*
|
|
* @param int error code
|
|
*
|
|
* @param string error message
|
|
*
|
|
* @return array error hash
|
|
*
|
|
*/
|
|
|
|
function _opsError($err_code,$err_text)
|
|
{
|
|
return array(
|
|
'response_code' => $err_code,
|
|
'response_text' => $err_text,
|
|
'is_success' => 0
|
|
);
|
|
}
|
|
|
|
|
|
|
|
#
|
|
# DECODING METHODS
|
|
# Converts XML OPS messages into PHP data
|
|
#
|
|
|
|
|
|
/**
|
|
* Accepts an OPS protocol message or an file handle
|
|
* and decodes the data into a PHP array
|
|
*
|
|
* @param string OPS message
|
|
*
|
|
* @return mixed PHP array, or error
|
|
*
|
|
*/
|
|
|
|
function decode($in)
|
|
{
|
|
|
|
$ops_msg = '';
|
|
|
|
/* determine if we were passed a string or file handle */
|
|
|
|
if (is_resource($in)) {
|
|
# read the file into a string, then process as usual
|
|
while (!feof($in)) {
|
|
$ops_msg .= fgets($in, 400);
|
|
}
|
|
} else {
|
|
$ops_msg = $in;
|
|
}
|
|
|
|
|
|
/* log it first */
|
|
|
|
$this->_log('xml', 'r', $ops_msg);
|
|
|
|
|
|
/* decode and return */
|
|
|
|
return $this->XML2PHP($ops_msg);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* XML Parser that converts an OPS protocol message into a PHP array
|
|
*
|
|
* @param string OPS message
|
|
*
|
|
* @return mixed PHP array, or error
|
|
*
|
|
*/
|
|
|
|
function XML2PHP($msg) {
|
|
|
|
$this->_data = NULL;
|
|
|
|
$xp = xml_parser_create();
|
|
xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
|
|
xml_parser_set_option($xp, XML_OPTION_SKIP_WHITE, true);
|
|
xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING, 'ISO-8859-1');
|
|
|
|
if (!xml_parse_into_struct($xp,$msg,$vals,$index)) {
|
|
$error = sprintf('XML error: %s at line %d',
|
|
xml_error_string(xml_get_error_code($xp)),
|
|
xml_get_current_line_number($xp)
|
|
);
|
|
xml_parser_free($xp);
|
|
return $this->raiseError($error);
|
|
}
|
|
|
|
xml_parser_free($xp);
|
|
|
|
$temp = $depth = array();
|
|
|
|
foreach($vals as $value) {
|
|
|
|
switch ($value['tag']) {
|
|
case 'OPS_envelope':
|
|
case 'header':
|
|
case 'body':
|
|
case 'data_block':
|
|
break;
|
|
|
|
case 'version':
|
|
case 'msg_id':
|
|
case 'msg_type':
|
|
$key = '_OPS_' . $value['tag'];
|
|
$temp[$key] = $value['value'];
|
|
break;
|
|
|
|
case 'item':
|
|
$key = $value['attributes']['key'];
|
|
|
|
switch ($value['type']) {
|
|
case 'open':
|
|
array_push($depth, $key);
|
|
break;
|
|
|
|
case 'complete':
|
|
array_push($depth, $key);
|
|
$p = join('::',$depth);
|
|
$temp[$p] = $value['value'];
|
|
array_pop($depth);
|
|
break;
|
|
|
|
case 'close':
|
|
array_pop($depth);
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'dt_assoc':
|
|
case 'dt_array':
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
foreach ($temp as $key=>$value) {
|
|
|
|
$levels = explode('::',$key);
|
|
$num_levels = count($levels);
|
|
|
|
if ($num_levels==1) {
|
|
$this->_data[$levels[0]] = $value;
|
|
} else {
|
|
$pointer = &$this->_data;
|
|
for ($i=0; $i<$num_levels; $i++) {
|
|
if ( !isset( $pointer[$levels[$i]] ) ) {
|
|
$pointer[$levels[$i]] = array();
|
|
}
|
|
$pointer = &$pointer[$levels[$i]];
|
|
}
|
|
$pointer = $value;
|
|
}
|
|
|
|
}
|
|
|
|
return ($this->_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
# ENCODING METHODS
|
|
# Converts PHP data into XML OPS messages
|
|
#
|
|
|
|
|
|
/**
|
|
* Converts a PHP array into an OPS message
|
|
*
|
|
* @param array PHP array
|
|
*
|
|
* @return string OPS XML message
|
|
*
|
|
*/
|
|
|
|
function encode($array)
|
|
{
|
|
|
|
$this->_MSGCNT++;
|
|
$msg_id = $this->_SESSID + $this->_MSGCNT; /* addition removes the leading zero */
|
|
$msg_type = $this->_MSGTYPE_STD;
|
|
|
|
|
|
if ($array['protocol']) {
|
|
$array['protocol'] = strtoupper($array['protocol']);
|
|
}
|
|
if ($array['action']) {
|
|
$array['action'] = strtoupper($array['action']);
|
|
}
|
|
if ($array['object']) {
|
|
$array['object'] = strtoupper($array['object']);
|
|
}
|
|
|
|
$xml_data_block = $this->PHP2XML($array);
|
|
|
|
$ops_msg = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>' . $this->_CRLF .
|
|
'<!DOCTYPE OPS_envelope SYSTEM "ops.dtd">' . $this->_CRLF .
|
|
'<OPS_envelope>' . $this->_CRLF .
|
|
$this->_SPACER . '<header>' . $this->_CRLF .
|
|
$this->_SPACER . $this->_SPACER . '<version>' . $this->_OPS_VERSION . '</version>' . $this->_CRLF .
|
|
$this->_SPACER . $this->_SPACER . '<msg_id>' . $msg_id . '</msg_id>' . $this->_CRLF .
|
|
$this->_SPACER . $this->_SPACER . '<msg_type>' . $msg_type . '</msg_type>' . $this->_CRLF .
|
|
$this->_SPACER . '</header>' . $this->_CRLF .
|
|
$this->_SPACER . '<body>' . $this->_CRLF .
|
|
$xml_data_block . $this->_CRLF .
|
|
$this->_SPACER . '</body>' . $this->_CRLF .
|
|
'</OPS_envelope>';
|
|
|
|
# log it
|
|
|
|
$this->_log('xml', 'w', $ops_msg);
|
|
|
|
return $ops_msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Converts a PHP array into an OPS data_block tag
|
|
*
|
|
* @param array PHP array
|
|
*
|
|
* @return string OPS data_block tag
|
|
*
|
|
*/
|
|
|
|
function PHP2XML($data)
|
|
{
|
|
return str_repeat($this->_SPACER,2) . '<data_block>' .
|
|
$this->_convertData($data, 3) .
|
|
$this->_CRLF . str_repeat($this->_SPACER,2) . '</data_block>';
|
|
}
|
|
|
|
|
|
/**
|
|
* Recursivly converts PHP data into XML
|
|
*
|
|
* @param mixed PHP array or data
|
|
*
|
|
* @param int ident level
|
|
*
|
|
* @return string XML string
|
|
*
|
|
*/
|
|
|
|
function _convertData(&$array, $indent=0)
|
|
{
|
|
$string = '';
|
|
$IND = str_repeat($this->_SPACER,$indent);
|
|
|
|
if (is_array($array)) {
|
|
|
|
if ($this->_is_assoc($array)) { # HASH REFERENCE
|
|
$string .= $this->_CRLF . $IND . '<dt_assoc>';
|
|
$end = '</dt_assoc>';
|
|
} else { # ARRAY REFERENCE
|
|
$string .= $this->_CRLF . $IND . '<dt_array>';
|
|
$end = '</dt_array>';
|
|
}
|
|
|
|
foreach ($array as $k=>$v) {
|
|
|
|
$indent++;
|
|
|
|
/* don't encode some types of stuff */
|
|
if ((gettype($v)=='resource') || (gettype($v)=='user function') || (gettype($v)=='unknown type')) {
|
|
continue;
|
|
}
|
|
|
|
$string .= $this->_CRLF . $IND . '<item key="' . $k . '"';
|
|
|
|
if (gettype($v)=='object' && get_class($v)) {
|
|
$string .= ' class="' . get_class($v) . '"';
|
|
}
|
|
|
|
$string .= '>';
|
|
|
|
if (is_array($v) || is_object($v)) {
|
|
$string .= $this->_convertData($v, $indent+1);
|
|
$string .= $this->_CRLF . $IND . '</item>';
|
|
} else {
|
|
$string .= $this->_quoteXMLChars($v) . '</item>';
|
|
}
|
|
|
|
$indent--;
|
|
}
|
|
|
|
$string .= $this->_CRLF . $IND . $end;
|
|
|
|
} else { # SCALAR
|
|
|
|
$string .= $this->_CRLF . $IND . '<dt_scalar>' .
|
|
$this->_quoteXMLChars($array) . '</dt_scalar>';
|
|
}
|
|
|
|
return $string;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Quotes special XML characters
|
|
*
|
|
* @param string string to quote
|
|
*
|
|
* @return string quoted string
|
|
*
|
|
*/
|
|
|
|
function _quoteXMLChars($string)
|
|
{
|
|
$search = array ('&', '<', '>', "'", '"');
|
|
$replace = array ('&', '<', '>', ''', '"');
|
|
$string = str_replace($search, $replace, $string);
|
|
$string = utf8_encode($string);
|
|
return $string;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Determines if an array is associative or not, since PHP
|
|
* doesn't really distinguish between the two, but Perl/OPS does
|
|
*
|
|
* @param array array to check
|
|
*
|
|
* @return boolean true if the array is associative
|
|
*
|
|
*/
|
|
|
|
function _is_assoc(&$array)
|
|
{
|
|
if (is_array($array)) {
|
|
foreach ($array as $k=>$v) {
|
|
if (!is_int($k)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Internal loggging method
|
|
*
|
|
* @param string which log to log to
|
|
*
|
|
* @param string type of log message ('r'ead, 'w'rite, 'i'nfo or 'e'rror)
|
|
*
|
|
* @param int message
|
|
*
|
|
*/
|
|
|
|
function _log($log, $type, $msg)
|
|
{
|
|
$types = array(
|
|
'r' => 'read',
|
|
'w' => 'write',
|
|
'e' => 'error',
|
|
'i' => 'info'
|
|
);
|
|
|
|
if ($log=='xml') {
|
|
$this->log[$log][] = sprintf("[% 6s:%06d] %s\n",
|
|
strtoupper($types[$type]),
|
|
($type=='e' || $type=='i') ? 0 : strlen($msg),
|
|
$msg
|
|
);
|
|
} else {
|
|
$this->log[$log][] = sprintf("[% 6s:%06d] %s\n",
|
|
strtoupper($types[$type]),
|
|
($type=='e' || $type=='i') ? 0 : strlen($msg),
|
|
($type=='e' || $type=='i') ? $msg : bin2hex($msg)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Show internal log
|
|
*
|
|
* @param string which log to log show, 'raw' or 'xml'
|
|
*
|
|
* @param string format to display: 'html' (default) or 'raw'
|
|
*
|
|
*/
|
|
|
|
function showLog($log, $format='html')
|
|
{
|
|
echo '<PRE>';
|
|
foreach ($this->log[$log] as $line) {
|
|
switch ($format) {
|
|
case 'raw':
|
|
echo $line . "\n";
|
|
break;
|
|
case 'html':
|
|
default:
|
|
echo htmlEntities($line) . "\n";
|
|
break;
|
|
}
|
|
}
|
|
echo '</PRE>';
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|