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