[ Index ] |
PHP Cross Reference of phpBB-3.1.12-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * This file is part of the phpBB Forum Software package. 5 * 6 * @copyright (c) phpBB Limited <https://www.phpbb.com> 7 * @license GNU General Public License, version 2 (GPL-2.0) 8 * 9 * For full copyright and license information, please see 10 * the docs/CREDITS.txt file. 11 * 12 */ 13 14 /** 15 * @ignore 16 */ 17 if (!defined('IN_PHPBB')) 18 { 19 exit; 20 } 21 22 /** 23 * 24 * Jabber class from Flyspray project 25 * 26 * @version class.jabber2.php 1595 2008-09-19 (0.9.9) 27 * @copyright 2006 Flyspray.org 28 * @author Florian Schmitz (floele) 29 * 30 * Only slightly modified by Acyd Burn 31 */ 32 class jabber 33 { 34 var $connection = null; 35 var $session = array(); 36 var $timeout = 10; 37 38 var $server; 39 var $connect_server; 40 var $port; 41 var $username; 42 var $password; 43 var $use_ssl; 44 var $verify_peer; 45 var $verify_peer_name; 46 var $allow_self_signed; 47 var $resource = 'functions_jabber.phpbb.php'; 48 49 var $enable_logging; 50 var $log_array; 51 52 var $features = array(); 53 54 /** 55 * Constructor 56 * 57 * @param string $server Jabber server 58 * @param int $port Jabber server port 59 * @param string $username Jabber username or JID 60 * @param string $password Jabber password 61 * @param boold $use_ssl Use ssl 62 * @param bool $verify_peer Verify SSL certificate 63 * @param bool $verify_peer_name Verify Jabber peer name 64 * @param bool $allow_self_signed Allow self signed certificates 65 */ 66 function __construct($server, $port, $username, $password, $use_ssl = false, $verify_peer = true, $verify_peer_name = true, $allow_self_signed = false) 67 { 68 $this->connect_server = ($server) ? $server : 'localhost'; 69 $this->port = ($port) ? $port : 5222; 70 71 // Get the server and the username 72 if (strpos($username, '@') === false) 73 { 74 $this->server = $this->connect_server; 75 $this->username = $username; 76 } 77 else 78 { 79 $jid = explode('@', $username, 2); 80 81 $this->username = $jid[0]; 82 $this->server = $jid[1]; 83 } 84 85 $this->password = $password; 86 $this->use_ssl = ($use_ssl && self::can_use_ssl()) ? true : false; 87 $this->verify_peer = $verify_peer; 88 $this->verify_peer_name = $verify_peer_name; 89 $this->allow_self_signed = $allow_self_signed; 90 91 // Change port if we use SSL 92 if ($this->port == 5222 && $this->use_ssl) 93 { 94 $this->port = 5223; 95 } 96 97 $this->enable_logging = true; 98 $this->log_array = array(); 99 } 100 101 /** 102 * Able to use the SSL functionality? 103 */ 104 static public function can_use_ssl() 105 { 106 // Will not work with PHP >= 5.2.1 or < 5.2.3RC2 until timeout problem with ssl hasn't been fixed (http://bugs.php.net/41236) 107 return ((version_compare(PHP_VERSION, '5.2.1', '<') || version_compare(PHP_VERSION, '5.2.3RC2', '>=')) && @extension_loaded('openssl')) ? true : false; 108 } 109 110 /** 111 * Able to use TLS? 112 */ 113 static public function can_use_tls() 114 { 115 if (!@extension_loaded('openssl') || !function_exists('stream_socket_enable_crypto') || !function_exists('stream_get_meta_data') || !function_exists('stream_set_blocking') || !function_exists('stream_get_wrappers')) 116 { 117 return false; 118 } 119 120 /** 121 * Make sure the encryption stream is supported 122 * Also seem to work without the crypto stream if correctly compiled 123 124 $streams = stream_get_wrappers(); 125 126 if (!in_array('streams.crypto', $streams)) 127 { 128 return false; 129 } 130 */ 131 132 return true; 133 } 134 135 /** 136 * Sets the resource which is used. No validation is done here, only escaping. 137 * @param string $name 138 * @access public 139 */ 140 function set_resource($name) 141 { 142 $this->resource = $name; 143 } 144 145 /** 146 * Connect 147 */ 148 function connect() 149 { 150 /* if (!$this->check_jid($this->username . '@' . $this->server)) 151 { 152 $this->add_to_log('Error: Jabber ID is not valid: ' . $this->username . '@' . $this->server); 153 return false; 154 }*/ 155 156 $this->session['ssl'] = $this->use_ssl; 157 158 if ($this->open_socket($this->connect_server, $this->port, $this->use_ssl, $this->verify_peer, $this->verify_peer_name, $this->allow_self_signed)) 159 { 160 $this->send("<?xml version='1.0' encoding='UTF-8' ?" . ">\n"); 161 $this->send("<stream:stream to='{$this->server}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n"); 162 } 163 else 164 { 165 $this->add_to_log('Error: connect() #2'); 166 return false; 167 } 168 169 // Now we listen what the server has to say...and give appropriate responses 170 $this->response($this->listen()); 171 return true; 172 } 173 174 /** 175 * Disconnect 176 */ 177 function disconnect() 178 { 179 if ($this->connected()) 180 { 181 // disconnect gracefully 182 if (isset($this->session['sent_presence'])) 183 { 184 $this->send_presence('offline', '', true); 185 } 186 187 $this->send('</stream:stream>'); 188 $this->session = array(); 189 return fclose($this->connection); 190 } 191 192 return false; 193 } 194 195 /** 196 * Connected? 197 */ 198 function connected() 199 { 200 return (is_resource($this->connection) && !feof($this->connection)) ? true : false; 201 } 202 203 204 /** 205 * Initiates login (using data from contructor, after calling connect()) 206 * @access public 207 * @return bool 208 */ 209 function login() 210 { 211 if (!sizeof($this->features)) 212 { 213 $this->add_to_log('Error: No feature information from server available.'); 214 return false; 215 } 216 217 return $this->response($this->features); 218 } 219 220 /** 221 * Send data to the Jabber server 222 * @param string $xml 223 * @access public 224 * @return bool 225 */ 226 function send($xml) 227 { 228 if ($this->connected()) 229 { 230 $xml = trim($xml); 231 $this->add_to_log('SEND: '. $xml); 232 return fwrite($this->connection, $xml); 233 } 234 else 235 { 236 $this->add_to_log('Error: Could not send, connection lost (flood?).'); 237 return false; 238 } 239 } 240 241 /** 242 * OpenSocket 243 * @param string $server host to connect to 244 * @param int $port port number 245 * @param bool $use_ssl use ssl or not 246 * @param bool $verify_peer verify ssl certificate 247 * @param bool $verify_peer_name verify peer name 248 * @param bool $allow_self_signed allow self-signed ssl certificates 249 * @access public 250 * @return bool 251 */ 252 function open_socket($server, $port, $use_ssl, $verify_peer, $verify_peer_name, $allow_self_signed) 253 { 254 if (@function_exists('dns_get_record')) 255 { 256 $record = @dns_get_record("_xmpp-client._tcp.$server", DNS_SRV); 257 if (!empty($record) && !empty($record[0]['target'])) 258 { 259 $server = $record[0]['target']; 260 } 261 } 262 263 $options = array(); 264 265 if ($use_ssl) 266 { 267 $remote_socket = 'ssl://' . $server . ':' . $port; 268 269 // Set ssl context options, see http://php.net/manual/en/context.ssl.php 270 $options['ssl'] = array('verify_peer' => $verify_peer, 'verify_peer_name' => $verify_peer_name, 'allow_self_signed' => $allow_self_signed); 271 } 272 else 273 { 274 $remote_socket = $server . ':' . $port; 275 } 276 277 $socket_context = stream_context_create($options); 278 279 if ($this->connection = @stream_socket_client($remote_socket, $errorno, $errorstr, $this->timeout, STREAM_CLIENT_CONNECT, $socket_context)) 280 { 281 stream_set_blocking($this->connection, 0); 282 stream_set_timeout($this->connection, 60); 283 284 return true; 285 } 286 287 // Apparently an error occurred... 288 $this->add_to_log('Error: open_socket() - ' . $errorstr); 289 return false; 290 } 291 292 /** 293 * Return log 294 */ 295 function get_log() 296 { 297 if ($this->enable_logging && sizeof($this->log_array)) 298 { 299 return implode("<br /><br />", $this->log_array); 300 } 301 302 return ''; 303 } 304 305 /** 306 * Add information to log 307 */ 308 function add_to_log($string) 309 { 310 if ($this->enable_logging) 311 { 312 $this->log_array[] = utf8_htmlspecialchars($string); 313 } 314 } 315 316 /** 317 * Listens to the connection until it gets data or the timeout is reached. 318 * Thus, it should only be called if data is expected to be received. 319 * @access public 320 * @return mixed either false for timeout or an array with the received data 321 */ 322 function listen($timeout = 10, $wait = false) 323 { 324 if (!$this->connected()) 325 { 326 return false; 327 } 328 329 // Wait for a response until timeout is reached 330 $start = time(); 331 $data = ''; 332 333 do 334 { 335 $read = trim(fread($this->connection, 4096)); 336 $data .= $read; 337 } 338 while (time() <= $start + $timeout && !feof($this->connection) && ($wait || $data == '' || $read != '' || (substr(rtrim($data), -1) != '>'))); 339 340 if ($data != '') 341 { 342 $this->add_to_log('RECV: '. $data); 343 return $this->xmlize($data); 344 } 345 else 346 { 347 $this->add_to_log('Timeout, no response from server.'); 348 return false; 349 } 350 } 351 352 /** 353 * Initiates account registration (based on data used for contructor) 354 * @access public 355 * @return bool 356 */ 357 function register() 358 { 359 if (!isset($this->session['id']) || isset($this->session['jid'])) 360 { 361 $this->add_to_log('Error: Cannot initiate registration.'); 362 return false; 363 } 364 365 $this->send("<iq type='get' id='reg_1'><query xmlns='jabber:iq:register'/></iq>"); 366 return $this->response($this->listen()); 367 } 368 369 /** 370 * Sets account presence. No additional info required (default is "online" status) 371 * @param $message online, offline... 372 * @param $type dnd, away, chat, xa or nothing 373 * @param $unavailable set this to true if you want to become unavailable 374 * @access public 375 * @return bool 376 */ 377 function send_presence($message = '', $type = '', $unavailable = false) 378 { 379 if (!isset($this->session['jid'])) 380 { 381 $this->add_to_log('ERROR: send_presence() - Cannot set presence at this point, no jid given.'); 382 return false; 383 } 384 385 $type = strtolower($type); 386 $type = (in_array($type, array('dnd', 'away', 'chat', 'xa'))) ? '<show>'. $type .'</show>' : ''; 387 388 $unavailable = ($unavailable) ? " type='unavailable'" : ''; 389 $message = ($message) ? '<status>' . utf8_htmlspecialchars($message) .'</status>' : ''; 390 391 $this->session['sent_presence'] = !$unavailable; 392 393 return $this->send("<presence$unavailable>" . $type . $message . '</presence>'); 394 } 395 396 /** 397 * This handles all the different XML elements 398 * @param array $xml 399 * @access public 400 * @return bool 401 */ 402 function response($xml) 403 { 404 if (!is_array($xml) || !sizeof($xml)) 405 { 406 return false; 407 } 408 409 // did we get multiple elements? do one after another 410 // array('message' => ..., 'presence' => ...) 411 if (sizeof($xml) > 1) 412 { 413 foreach ($xml as $key => $value) 414 { 415 $this->response(array($key => $value)); 416 } 417 return; 418 } 419 else 420 { 421 // or even multiple elements of the same type? 422 // array('message' => array(0 => ..., 1 => ...)) 423 if (sizeof(reset($xml)) > 1) 424 { 425 foreach (reset($xml) as $value) 426 { 427 $this->response(array(key($xml) => array(0 => $value))); 428 } 429 return; 430 } 431 } 432 433 switch (key($xml)) 434 { 435 case 'stream:stream': 436 // Connection initialised (or after authentication). Not much to do here... 437 438 if (isset($xml['stream:stream'][0]['#']['stream:features'])) 439 { 440 // we already got all info we need 441 $this->features = $xml['stream:stream'][0]['#']; 442 } 443 else 444 { 445 $this->features = $this->listen(); 446 } 447 448 $second_time = isset($this->session['id']); 449 $this->session['id'] = $xml['stream:stream'][0]['@']['id']; 450 451 if ($second_time) 452 { 453 // If we are here for the second time after TLS, we need to continue logging in 454 return $this->login(); 455 } 456 457 // go on with authentication? 458 if (isset($this->features['stream:features'][0]['#']['bind']) || !empty($this->session['tls'])) 459 { 460 return $this->response($this->features); 461 } 462 break; 463 464 case 'stream:features': 465 // Resource binding after successful authentication 466 if (isset($this->session['authenticated'])) 467 { 468 // session required? 469 $this->session['sess_required'] = isset($xml['stream:features'][0]['#']['session']); 470 471 $this->send("<iq type='set' id='bind_1'> 472 <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'> 473 <resource>" . utf8_htmlspecialchars($this->resource) . '</resource> 474 </bind> 475 </iq>'); 476 return $this->response($this->listen()); 477 } 478 479 // Let's use TLS if SSL is not enabled and we can actually use it 480 if (!$this->session['ssl'] && self::can_use_tls() && self::can_use_ssl() && isset($xml['stream:features'][0]['#']['starttls'])) 481 { 482 $this->add_to_log('Switching to TLS.'); 483 $this->send("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n"); 484 return $this->response($this->listen()); 485 } 486 487 // Does the server support SASL authentication? 488 489 // I hope so, because we do (and no other method). 490 if (isset($xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns']) && $xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns'] == 'urn:ietf:params:xml:ns:xmpp-sasl') 491 { 492 // Now decide on method 493 $methods = array(); 494 495 foreach ($xml['stream:features'][0]['#']['mechanisms'][0]['#']['mechanism'] as $value) 496 { 497 $methods[] = $value['#']; 498 } 499 500 // we prefer DIGEST-MD5 501 // we don't want to use plain authentication (neither does the server usually) if no encryption is in place 502 503 // http://www.xmpp.org/extensions/attic/jep-0078-1.7.html 504 // The plaintext mechanism SHOULD NOT be used unless the underlying stream is encrypted (using SSL or TLS) 505 // and the client has verified that the server certificate is signed by a trusted certificate authority. 506 507 if (in_array('DIGEST-MD5', $methods)) 508 { 509 $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>"); 510 } 511 else if (in_array('PLAIN', $methods) && ($this->session['ssl'] || !empty($this->session['tls']))) 512 { 513 // http://www.ietf.org/rfc/rfc4616.txt (PLAIN SASL Mechanism) 514 $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" 515 . base64_encode($this->username . '@' . $this->server . chr(0) . $this->username . chr(0) . $this->password) . 516 '</auth>'); 517 } 518 else if (in_array('ANONYMOUS', $methods)) 519 { 520 $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>"); 521 } 522 else 523 { 524 // not good... 525 $this->add_to_log('Error: No authentication method supported.'); 526 $this->disconnect(); 527 return false; 528 } 529 530 return $this->response($this->listen()); 531 } 532 else 533 { 534 // ok, this is it. bye. 535 $this->add_to_log('Error: Server does not offer SASL authentication.'); 536 $this->disconnect(); 537 return false; 538 } 539 break; 540 541 case 'challenge': 542 // continue with authentication...a challenge literally -_- 543 $decoded = base64_decode($xml['challenge'][0]['#']); 544 $decoded = $this->parse_data($decoded); 545 546 if (!isset($decoded['digest-uri'])) 547 { 548 $decoded['digest-uri'] = 'xmpp/'. $this->server; 549 } 550 551 // better generate a cnonce, maybe it's needed 552 $decoded['cnonce'] = base64_encode(md5(uniqid(mt_rand(), true))); 553 554 // second challenge? 555 if (isset($decoded['rspauth'])) 556 { 557 $this->send("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); 558 } 559 else 560 { 561 // Make sure we only use 'auth' for qop (relevant for $this->encrypt_password()) 562 // If the <response> is choking up on the changed parameter we may need to adjust encrypt_password() directly 563 if (isset($decoded['qop']) && $decoded['qop'] != 'auth' && strpos($decoded['qop'], 'auth') !== false) 564 { 565 $decoded['qop'] = 'auth'; 566 } 567 568 $response = array( 569 'username' => $this->username, 570 'response' => $this->encrypt_password(array_merge($decoded, array('nc' => '00000001'))), 571 'charset' => 'utf-8', 572 'nc' => '00000001', 573 'qop' => 'auth', // only auth being supported 574 ); 575 576 foreach (array('nonce', 'digest-uri', 'realm', 'cnonce') as $key) 577 { 578 if (isset($decoded[$key])) 579 { 580 $response[$key] = $decoded[$key]; 581 } 582 } 583 584 $this->send("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" . base64_encode($this->implode_data($response)) . '</response>'); 585 } 586 587 return $this->response($this->listen()); 588 break; 589 590 case 'failure': 591 $this->add_to_log('Error: Server sent "failure".'); 592 $this->disconnect(); 593 return false; 594 break; 595 596 case 'proceed': 597 // continue switching to TLS 598 $meta = stream_get_meta_data($this->connection); 599 stream_set_blocking($this->connection, 1); 600 601 if (!stream_socket_enable_crypto($this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) 602 { 603 $this->add_to_log('Error: TLS mode change failed.'); 604 return false; 605 } 606 607 stream_set_blocking($this->connection, $meta['blocked']); 608 $this->session['tls'] = true; 609 610 // new stream 611 $this->send("<?xml version='1.0' encoding='UTF-8' ?" . ">\n"); 612 $this->send("<stream:stream to='{$this->server}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n"); 613 614 return $this->response($this->listen()); 615 break; 616 617 case 'success': 618 // Yay, authentication successful. 619 $this->send("<stream:stream to='{$this->server}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n"); 620 $this->session['authenticated'] = true; 621 622 // we have to wait for another response 623 return $this->response($this->listen()); 624 break; 625 626 case 'iq': 627 // we are not interested in IQs we did not expect 628 if (!isset($xml['iq'][0]['@']['id'])) 629 { 630 return false; 631 } 632 633 // multiple possibilities here 634 switch ($xml['iq'][0]['@']['id']) 635 { 636 case 'bind_1': 637 $this->session['jid'] = $xml['iq'][0]['#']['bind'][0]['#']['jid'][0]['#']; 638 639 // and (maybe) yet another request to be able to send messages *finally* 640 if ($this->session['sess_required']) 641 { 642 $this->send("<iq to='{$this->server}' type='set' id='sess_1'> 643 <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/> 644 </iq>"); 645 return $this->response($this->listen()); 646 } 647 648 return true; 649 break; 650 651 case 'sess_1': 652 return true; 653 break; 654 655 case 'reg_1': 656 $this->send("<iq type='set' id='reg_2'> 657 <query xmlns='jabber:iq:register'> 658 <username>" . utf8_htmlspecialchars($this->username) . "</username> 659 <password>" . utf8_htmlspecialchars($this->password) . "</password> 660 </query> 661 </iq>"); 662 return $this->response($this->listen()); 663 break; 664 665 case 'reg_2': 666 // registration end 667 if (isset($xml['iq'][0]['#']['error'])) 668 { 669 $this->add_to_log('Warning: Registration failed.'); 670 return false; 671 } 672 return true; 673 break; 674 675 case 'unreg_1': 676 return true; 677 break; 678 679 default: 680 $this->add_to_log('Notice: Received unexpected IQ.'); 681 return false; 682 break; 683 } 684 break; 685 686 case 'message': 687 // we are only interested in content... 688 if (!isset($xml['message'][0]['#']['body'])) 689 { 690 return false; 691 } 692 693 $message['body'] = $xml['message'][0]['#']['body'][0]['#']; 694 $message['from'] = $xml['message'][0]['@']['from']; 695 696 if (isset($xml['message'][0]['#']['subject'])) 697 { 698 $message['subject'] = $xml['message'][0]['#']['subject'][0]['#']; 699 } 700 $this->session['messages'][] = $message; 701 break; 702 703 default: 704 // hm...don't know this response 705 $this->add_to_log('Notice: Unknown server response (' . key($xml) . ')'); 706 return false; 707 break; 708 } 709 } 710 711 function send_message($to, $text, $subject = '', $type = 'normal') 712 { 713 if (!isset($this->session['jid'])) 714 { 715 return false; 716 } 717 718 if (!in_array($type, array('chat', 'normal', 'error', 'groupchat', 'headline'))) 719 { 720 $type = 'normal'; 721 } 722 723 return $this->send("<message from='" . utf8_htmlspecialchars($this->session['jid']) . "' to='" . utf8_htmlspecialchars($to) . "' type='$type' id='" . uniqid('msg') . "'> 724 <subject>" . utf8_htmlspecialchars($subject) . "</subject> 725 <body>" . utf8_htmlspecialchars($text) . "</body> 726 </message>" 727 ); 728 } 729 730 /** 731 * Encrypts a password as in RFC 2831 732 * @param array $data Needs data from the client-server connection 733 * @access public 734 * @return string 735 */ 736 function encrypt_password($data) 737 { 738 // let's me think about <challenge> again... 739 foreach (array('realm', 'cnonce', 'digest-uri') as $key) 740 { 741 if (!isset($data[$key])) 742 { 743 $data[$key] = ''; 744 } 745 } 746 747 $pack = md5($this->username . ':' . $data['realm'] . ':' . $this->password); 748 749 if (isset($data['authzid'])) 750 { 751 $a1 = pack('H32', $pack) . sprintf(':%s:%s:%s', $data['nonce'], $data['cnonce'], $data['authzid']); 752 } 753 else 754 { 755 $a1 = pack('H32', $pack) . sprintf(':%s:%s', $data['nonce'], $data['cnonce']); 756 } 757 758 // should be: qop = auth 759 $a2 = 'AUTHENTICATE:'. $data['digest-uri']; 760 761 return md5(sprintf('%s:%s:%s:%s:%s:%s', md5($a1), $data['nonce'], $data['nc'], $data['cnonce'], $data['qop'], md5($a2))); 762 } 763 764 /** 765 * parse_data like a="b",c="d",... or like a="a, b", c, d="e", f=g,... 766 * @param string $data 767 * @access public 768 * @return array a => b ... 769 */ 770 function parse_data($data) 771 { 772 $data = explode(',', $data); 773 $pairs = array(); 774 $key = false; 775 776 foreach ($data as $pair) 777 { 778 $dd = strpos($pair, '='); 779 780 if ($dd) 781 { 782 $key = trim(substr($pair, 0, $dd)); 783 $pairs[$key] = trim(trim(substr($pair, $dd + 1)), '"'); 784 } 785 else if (strpos(strrev(trim($pair)), '"') === 0 && $key) 786 { 787 // We are actually having something left from "a, b" values, add it to the last one we handled. 788 $pairs[$key] .= ',' . trim(trim($pair), '"'); 789 continue; 790 } 791 } 792 793 return $pairs; 794 } 795 796 /** 797 * opposite of jabber::parse_data() 798 * @param array $data 799 * @access public 800 * @return string 801 */ 802 function implode_data($data) 803 { 804 $return = array(); 805 foreach ($data as $key => $value) 806 { 807 $return[] = $key . '="' . $value . '"'; 808 } 809 return implode(',', $return); 810 } 811 812 /** 813 * xmlize() 814 * @author Hans Anderson 815 * @copyright Hans Anderson / http://www.hansanderson.com/php/xml/ 816 */ 817 function xmlize($data, $skip_white = 1, $encoding = 'UTF-8') 818 { 819 $data = trim($data); 820 821 if (substr($data, 0, 5) != '<?xml') 822 { 823 // mod 824 $data = '<root>'. $data . '</root>'; 825 } 826 827 $vals = $index = $array = array(); 828 $parser = xml_parser_create($encoding); 829 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); 830 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, $skip_white); 831 xml_parse_into_struct($parser, $data, $vals, $index); 832 xml_parser_free($parser); 833 834 $i = 0; 835 $tagname = $vals[$i]['tag']; 836 837 $array[$tagname][0]['@'] = (isset($vals[$i]['attributes'])) ? $vals[$i]['attributes'] : array(); 838 $array[$tagname][0]['#'] = $this->_xml_depth($vals, $i); 839 840 if (substr($data, 0, 5) != '<?xml') 841 { 842 $array = $array['root'][0]['#']; 843 } 844 845 return $array; 846 } 847 848 /** 849 * _xml_depth() 850 * @author Hans Anderson 851 * @copyright Hans Anderson / http://www.hansanderson.com/php/xml/ 852 */ 853 function _xml_depth($vals, &$i) 854 { 855 $children = array(); 856 857 if (isset($vals[$i]['value'])) 858 { 859 array_push($children, $vals[$i]['value']); 860 } 861 862 while (++$i < sizeof($vals)) 863 { 864 switch ($vals[$i]['type']) 865 { 866 case 'open': 867 868 $tagname = (isset($vals[$i]['tag'])) ? $vals[$i]['tag'] : ''; 869 $size = (isset($children[$tagname])) ? sizeof($children[$tagname]) : 0; 870 871 if (isset($vals[$i]['attributes'])) 872 { 873 $children[$tagname][$size]['@'] = $vals[$i]['attributes']; 874 } 875 876 $children[$tagname][$size]['#'] = $this->_xml_depth($vals, $i); 877 878 break; 879 880 case 'cdata': 881 array_push($children, $vals[$i]['value']); 882 break; 883 884 case 'complete': 885 886 $tagname = $vals[$i]['tag']; 887 $size = (isset($children[$tagname])) ? sizeof($children[$tagname]) : 0; 888 $children[$tagname][$size]['#'] = (isset($vals[$i]['value'])) ? $vals[$i]['value'] : array(); 889 890 if (isset($vals[$i]['attributes'])) 891 { 892 $children[$tagname][$size]['@'] = $vals[$i]['attributes']; 893 } 894 895 break; 896 897 case 'close': 898 return $children; 899 break; 900 } 901 } 902 903 return $children; 904 } 905 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 00:25:41 2018 | Cross-referenced by PHPXref 0.7.1 |