[ Index ]

PHP Cross Reference of phpBB-3.1.10-deutsch

title

Body

[close]

/includes/ -> functions_jabber.php (source)

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


Generated: Sun Feb 19 19:52:41 2017 Cross-referenced by PHPXref 0.7.1