PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/registrar/_opensrs-php/OPS.php

https://github.com/axxtel/agilebill
PHP | 667 lines | 341 code | 148 blank | 178 comment | 49 complexity | e22f0f80a6fc2a3568edf9fb281e360b MD5 | raw file
  1. <?php
  2. /*
  3. **************************************************************************
  4. *
  5. * OpenSRS-PHP
  6. *
  7. * Copyright (C) 2000, 2001, 2002, 2003 Colin Viebrock
  8. * and easyDNS Technologies Inc.
  9. *
  10. **************************************************************************
  11. *
  12. * This library is free software; you can redistribute it and/or
  13. * modify it under the terms of the GNU Lesser General Public
  14. * License as published by the Free Software Foundation; either
  15. * version 2.1 of the License, or (at your option) any later version.
  16. *
  17. * This library is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. * Lesser General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Lesser General Public
  23. * License along with this library; if not, write to the Free Software
  24. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  25. *
  26. **************************************************************************
  27. *
  28. * vim: set expandtab tabstop=4 shiftwidth=4:
  29. * $Id: OPS.php,v 1.1 2004/09/30 09:25:23 Tony Exp $
  30. *
  31. **************************************************************************
  32. */
  33. require_once 'PEAR.php';
  34. class OPS extends PEAR {
  35. var $_OPS_VERSION = '0.9';
  36. var $_OPT = '';
  37. var $_SPACER = ' '; /* indent character */
  38. var $_CRLF = "\n";
  39. var $_MSGTYPE_STD = 'standard';
  40. var $_SESSID;
  41. var $_MSGCNT;
  42. var $CRLF = "\r\n";
  43. var $_log = array();
  44. var $_data;
  45. var $_pointers;
  46. var $_last_was_data_block;
  47. /**
  48. * Class constructor
  49. *
  50. * Initialize variables, logs, etc.
  51. *
  52. * @param array allows for setting various options (right now, just whether
  53. * to use compression or not on the generated XML)
  54. */
  55. function OPS($args=false)
  56. {
  57. $this->PEAR();
  58. if (is_array($args)) {
  59. if ($args['option']=='compress') {
  60. $this->_OPT = 'compress';
  61. $this->_SPACER = '';
  62. $this->_CRLF = '';
  63. }
  64. }
  65. $this->_SESSID = getmypid();
  66. $this->_MSGCNT = 0;
  67. $this->_log('raw','i','OPS Raw Log:');
  68. $this->_log('raw','i','Initialized '.date('r') );
  69. $this->_log('xml','i','OPS XML Log:');
  70. $this->_log('xml','i','Initialized '.date('r') );
  71. }
  72. /**
  73. * Writes a message to a socket (buffered IO)
  74. *
  75. * @param int socket handle
  76. *
  77. * @param string message to write
  78. *
  79. */
  80. function writeData(&$fh,$msg)
  81. {
  82. $len = strlen($msg);
  83. fputs($fh, 'Content-Length: ' . $len . $this->CRLF . $this->CRLF);
  84. fputs($fh, $msg, $len );
  85. $this->_log('raw', 'w', $msg, $len);
  86. }
  87. /**
  88. * Encodes and writes a message to a socket
  89. *
  90. * @param int socket handle
  91. *
  92. * @param string message to encode and write
  93. *
  94. */
  95. function writeMessage(&$fh, $hr )
  96. {
  97. $msg = $this->encode( $hr );
  98. $this->writeData($fh, $msg );
  99. }
  100. /**
  101. * Reads data from a socket
  102. *
  103. * @param int socket handle
  104. *
  105. * @param int timeout for read
  106. *
  107. * @return mixed buffer with data, or an error for a short read
  108. *
  109. */
  110. function readData(&$fh, $timeout=5)
  111. {
  112. $len = 0;
  113. /* PHP doesn't have timeout for fread ... we just set the timeout for the socket */
  114. socket_set_timeout($fh, $timeout);
  115. $line = fgets($fh, 4000);
  116. if ($this->socketStatus($fh)) {
  117. return false;
  118. }
  119. if (!$len && preg_match('/^\s*Content-Length:\s+(\d+)\s*\r\n/i', $line, $matches ) ) {
  120. $len = (int)$matches[1];
  121. } else {
  122. $this->_log('raw', 'e', 'UNEXPECTED READ: No Content-Length' );
  123. $this->_log('raw', 'r', $line);
  124. return false;
  125. }
  126. /* read the empty line */
  127. $line = fread($fh, 2);
  128. if ($this->socketStatus($fh)) {
  129. return false;
  130. }
  131. if ($line!=$this->CRLF) {
  132. $this->_log('raw', 'e', 'UNEXPECTED READ: No CRLF');
  133. $this->_log('raw', 'r', $line);
  134. return false;
  135. }
  136. $line = '';
  137. while (strlen($line) < $len) {
  138. $line .= fread($fh, $len);
  139. if ($this->socketStatus($fh)) {
  140. return false;
  141. }
  142. }
  143. if ($line) {
  144. $buf = $line;
  145. $this->_log('raw', 'r', $line);
  146. } else {
  147. $buf = false;
  148. $this->_log('raw', 'e', 'NEXT LINE SHORT READ (should be '.$len.')' );
  149. $this->_log('raw', 'r', $line);
  150. }
  151. return $buf;
  152. }
  153. /**
  154. * Reads and decodes data from a socket
  155. *
  156. * @param int socket handle
  157. *
  158. * @param int timeout for read
  159. *
  160. * @return mixed associative array of data, or an error
  161. *
  162. */
  163. function readMessage(&$fh, $timeout=5)
  164. {
  165. $buf = $this->readData($fh, $timeout);
  166. return ( $buf ? $this->decode($buf) : false );
  167. }
  168. /**
  169. * Checks a socket for timeout or EOF
  170. *
  171. * @param int socket handle
  172. *
  173. * @return boolean true if the socket has timed out or is EOF
  174. *
  175. */
  176. function socketStatus(&$fh)
  177. {
  178. $return = false;
  179. if (is_resource($fh)) {
  180. $temp = socket_get_status($fh);
  181. if ($temp['timed_out']) {
  182. $this->_log('raw', 'e', 'SOCKET TIMED OUT');
  183. $return = true;
  184. }
  185. if ($temp['eof']) {
  186. $this->_log('raw', 'e', 'SOCKET EOF');
  187. $return = true;
  188. }
  189. unset($temp);
  190. }
  191. return $return;
  192. }
  193. /**
  194. * Internal method to generate error codes hashes
  195. *
  196. * @param int error code
  197. *
  198. * @param string error message
  199. *
  200. * @return array error hash
  201. *
  202. */
  203. function _opsError($err_code,$err_text)
  204. {
  205. return array(
  206. 'response_code' => $err_code,
  207. 'response_text' => $err_text,
  208. 'is_success' => 0
  209. );
  210. }
  211. #
  212. # DECODING METHODS
  213. # Converts XML OPS messages into PHP data
  214. #
  215. /**
  216. * Accepts an OPS protocol message or an file handle
  217. * and decodes the data into a PHP array
  218. *
  219. * @param string OPS message
  220. *
  221. * @return mixed PHP array, or error
  222. *
  223. */
  224. function decode($in)
  225. {
  226. $ops_msg = '';
  227. /* determine if we were passed a string or file handle */
  228. if (is_resource($in)) {
  229. # read the file into a string, then process as usual
  230. while (!feof($in)) {
  231. $ops_msg .= fgets($in, 400);
  232. }
  233. } else {
  234. $ops_msg = $in;
  235. }
  236. /* log it first */
  237. $this->_log('xml', 'r', $ops_msg);
  238. /* decode and return */
  239. return $this->XML2PHP($ops_msg);
  240. }
  241. /**
  242. * XML Parser that converts an OPS protocol message into a PHP array
  243. *
  244. * @param string OPS message
  245. *
  246. * @return mixed PHP array, or error
  247. *
  248. */
  249. function XML2PHP($msg) {
  250. $this->_data = NULL;
  251. $xp = xml_parser_create();
  252. xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
  253. xml_parser_set_option($xp, XML_OPTION_SKIP_WHITE, true);
  254. xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING, 'ISO-8859-1');
  255. if (!xml_parse_into_struct($xp,$msg,$vals,$index)) {
  256. $error = sprintf('XML error: %s at line %d',
  257. xml_error_string(xml_get_error_code($xp)),
  258. xml_get_current_line_number($xp)
  259. );
  260. xml_parser_free($xp);
  261. return $this->raiseError($error);
  262. }
  263. xml_parser_free($xp);
  264. $temp = $depth = array();
  265. foreach($vals as $value) {
  266. switch ($value['tag']) {
  267. case 'OPS_envelope':
  268. case 'header':
  269. case 'body':
  270. case 'data_block':
  271. break;
  272. case 'version':
  273. case 'msg_id':
  274. case 'msg_type':
  275. $key = '_OPS_' . $value['tag'];
  276. $temp[$key] = $value['value'];
  277. break;
  278. case 'item':
  279. $key = $value['attributes']['key'];
  280. switch ($value['type']) {
  281. case 'open':
  282. array_push($depth, $key);
  283. break;
  284. case 'complete':
  285. array_push($depth, $key);
  286. $p = join('::',$depth);
  287. $temp[$p] = $value['value'];
  288. array_pop($depth);
  289. break;
  290. case 'close':
  291. array_pop($depth);
  292. break;
  293. }
  294. break;
  295. case 'dt_assoc':
  296. case 'dt_array':
  297. break;
  298. }
  299. }
  300. foreach ($temp as $key=>$value) {
  301. $levels = explode('::',$key);
  302. $num_levels = count($levels);
  303. if ($num_levels==1) {
  304. $this->_data[$levels[0]] = $value;
  305. } else {
  306. $pointer = &$this->_data;
  307. for ($i=0; $i<$num_levels; $i++) {
  308. if ( !isset( $pointer[$levels[$i]] ) ) {
  309. $pointer[$levels[$i]] = array();
  310. }
  311. $pointer = &$pointer[$levels[$i]];
  312. }
  313. $pointer = $value;
  314. }
  315. }
  316. return ($this->_data);
  317. }
  318. #
  319. # ENCODING METHODS
  320. # Converts PHP data into XML OPS messages
  321. #
  322. /**
  323. * Converts a PHP array into an OPS message
  324. *
  325. * @param array PHP array
  326. *
  327. * @return string OPS XML message
  328. *
  329. */
  330. function encode($array)
  331. {
  332. $this->_MSGCNT++;
  333. $msg_id = $this->_SESSID + $this->_MSGCNT; /* addition removes the leading zero */
  334. $msg_type = $this->_MSGTYPE_STD;
  335. if ($array['protocol']) {
  336. $array['protocol'] = strtoupper($array['protocol']);
  337. }
  338. if ($array['action']) {
  339. $array['action'] = strtoupper($array['action']);
  340. }
  341. if ($array['object']) {
  342. $array['object'] = strtoupper($array['object']);
  343. }
  344. $xml_data_block = $this->PHP2XML($array);
  345. $ops_msg = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>' . $this->_CRLF .
  346. '<!DOCTYPE OPS_envelope SYSTEM "ops.dtd">' . $this->_CRLF .
  347. '<OPS_envelope>' . $this->_CRLF .
  348. $this->_SPACER . '<header>' . $this->_CRLF .
  349. $this->_SPACER . $this->_SPACER . '<version>' . $this->_OPS_VERSION . '</version>' . $this->_CRLF .
  350. $this->_SPACER . $this->_SPACER . '<msg_id>' . $msg_id . '</msg_id>' . $this->_CRLF .
  351. $this->_SPACER . $this->_SPACER . '<msg_type>' . $msg_type . '</msg_type>' . $this->_CRLF .
  352. $this->_SPACER . '</header>' . $this->_CRLF .
  353. $this->_SPACER . '<body>' . $this->_CRLF .
  354. $xml_data_block . $this->_CRLF .
  355. $this->_SPACER . '</body>' . $this->_CRLF .
  356. '</OPS_envelope>';
  357. # log it
  358. $this->_log('xml', 'w', $ops_msg);
  359. return $ops_msg;
  360. }
  361. /**
  362. * Converts a PHP array into an OPS data_block tag
  363. *
  364. * @param array PHP array
  365. *
  366. * @return string OPS data_block tag
  367. *
  368. */
  369. function PHP2XML($data)
  370. {
  371. return str_repeat($this->_SPACER,2) . '<data_block>' .
  372. $this->_convertData($data, 3) .
  373. $this->_CRLF . str_repeat($this->_SPACER,2) . '</data_block>';
  374. }
  375. /**
  376. * Recursivly converts PHP data into XML
  377. *
  378. * @param mixed PHP array or data
  379. *
  380. * @param int ident level
  381. *
  382. * @return string XML string
  383. *
  384. */
  385. function _convertData(&$array, $indent=0)
  386. {
  387. $string = '';
  388. $IND = str_repeat($this->_SPACER,$indent);
  389. if (is_array($array)) {
  390. if ($this->_is_assoc($array)) { # HASH REFERENCE
  391. $string .= $this->_CRLF . $IND . '<dt_assoc>';
  392. $end = '</dt_assoc>';
  393. } else { # ARRAY REFERENCE
  394. $string .= $this->_CRLF . $IND . '<dt_array>';
  395. $end = '</dt_array>';
  396. }
  397. foreach ($array as $k=>$v) {
  398. $indent++;
  399. /* don't encode some types of stuff */
  400. if ((gettype($v)=='resource') || (gettype($v)=='user function') || (gettype($v)=='unknown type')) {
  401. continue;
  402. }
  403. $string .= $this->_CRLF . $IND . '<item key="' . $k . '"';
  404. if (gettype($v)=='object' && get_class($v)) {
  405. $string .= ' class="' . get_class($v) . '"';
  406. }
  407. $string .= '>';
  408. if (is_array($v) || is_object($v)) {
  409. $string .= $this->_convertData($v, $indent+1);
  410. $string .= $this->_CRLF . $IND . '</item>';
  411. } else {
  412. $string .= $this->_quoteXMLChars($v) . '</item>';
  413. }
  414. $indent--;
  415. }
  416. $string .= $this->_CRLF . $IND . $end;
  417. } else { # SCALAR
  418. $string .= $this->_CRLF . $IND . '<dt_scalar>' .
  419. $this->_quoteXMLChars($array) . '</dt_scalar>';
  420. }
  421. return $string;
  422. }
  423. /**
  424. * Quotes special XML characters
  425. *
  426. * @param string string to quote
  427. *
  428. * @return string quoted string
  429. *
  430. */
  431. function _quoteXMLChars($string)
  432. {
  433. $search = array ('&', '<', '>', "'", '"');
  434. $replace = array ('&amp;', '&lt;', '&gt;', '&apos;', '&quot;');
  435. $string = str_replace($search, $replace, $string);
  436. $string = utf8_encode($string);
  437. return $string;
  438. }
  439. /**
  440. * Determines if an array is associative or not, since PHP
  441. * doesn't really distinguish between the two, but Perl/OPS does
  442. *
  443. * @param array array to check
  444. *
  445. * @return boolean true if the array is associative
  446. *
  447. */
  448. function _is_assoc(&$array)
  449. {
  450. if (is_array($array)) {
  451. foreach ($array as $k=>$v) {
  452. if (!is_int($k)) {
  453. return true;
  454. }
  455. }
  456. }
  457. return false;
  458. }
  459. /**
  460. * Internal loggging method
  461. *
  462. * @param string which log to log to
  463. *
  464. * @param string type of log message ('r'ead, 'w'rite, 'i'nfo or 'e'rror)
  465. *
  466. * @param int message
  467. *
  468. */
  469. function _log($log, $type, $msg)
  470. {
  471. $types = array(
  472. 'r' => 'read',
  473. 'w' => 'write',
  474. 'e' => 'error',
  475. 'i' => 'info'
  476. );
  477. if ($log=='xml') {
  478. $this->log[$log][] = sprintf("[% 6s:%06d] %s\n",
  479. strtoupper($types[$type]),
  480. ($type=='e' || $type=='i') ? 0 : strlen($msg),
  481. $msg
  482. );
  483. } else {
  484. $this->log[$log][] = sprintf("[% 6s:%06d] %s\n",
  485. strtoupper($types[$type]),
  486. ($type=='e' || $type=='i') ? 0 : strlen($msg),
  487. ($type=='e' || $type=='i') ? $msg : bin2hex($msg)
  488. );
  489. }
  490. }
  491. /**
  492. * Show internal log
  493. *
  494. * @param string which log to log show, 'raw' or 'xml'
  495. *
  496. * @param string format to display: 'html' (default) or 'raw'
  497. *
  498. */
  499. function showLog($log, $format='html')
  500. {
  501. echo '<PRE>';
  502. foreach ($this->log[$log] as $line) {
  503. switch ($format) {
  504. case 'raw':
  505. echo $line . "\n";
  506. break;
  507. case 'html':
  508. default:
  509. echo htmlEntities($line) . "\n";
  510. break;
  511. }
  512. }
  513. echo '</PRE>';
  514. }
  515. }