Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

functions_jabber.php

Zuletzt modifiziert: 09.10.2024, 12:51 - Dateigröße: 21.48 KiB


 001  <?php
 002  /**
 003  *
 004  * This file is part of the phpBB Forum Software package.
 005  *
 006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
 007  * @license GNU General Public License, version 2 (GPL-2.0)
 008  *
 009  * For full copyright and license information, please see
 010  * the docs/CREDITS.txt file.
 011  *
 012  */
 013   
 014  /**
 015  * @ignore
 016  */
 017  if (!defined('IN_PHPBB'))
 018  {
 019      exit;
 020  }
 021   
 022  /**
 023  *
 024  * Jabber class from Flyspray project
 025  *
 026  * @version class.jabber2.php 1595 2008-09-19 (0.9.9)
 027  * @copyright 2006 Flyspray.org
 028  * @author Florian Schmitz (floele)
 029  *
 030  * Only slightly modified by Acyd Burn
 031  */
 032  class jabber
 033  {
 034      var $connection = null;
 035      var $session = array();
 036      var $timeout = 10;
 037   
 038      var $server;
 039      var $connect_server;
 040      var $port;
 041      var $username;
 042      var $password;
 043      var $use_ssl;
 044      var $resource = 'functions_jabber.phpbb.php';
 045   
 046      var $enable_logging;
 047      var $log_array;
 048   
 049      var $features = array();
 050   
 051      /**
 052      */
 053      function jabber($server, $port, $username, $password, $use_ssl = false)
 054      {
 055          $this->connect_server        = ($server) ? $server : 'localhost';
 056          $this->port                    = ($port) ? $port : 5222;
 057   
 058          // Get the server and the username
 059          if (strpos($username, '@') === false)
 060          {
 061              $this->server = $this->connect_server;
 062              $this->username = $username;
 063          }
 064          else
 065          {
 066              $jid = explode('@', $username, 2);
 067   
 068              $this->username = $jid[0];
 069              $this->server = $jid[1];
 070          }
 071   
 072          $this->password                = $password;
 073          $this->use_ssl                = ($use_ssl && self::can_use_ssl()) ? true : false;
 074   
 075          // Change port if we use SSL
 076          if ($this->port == 5222 && $this->use_ssl)
 077          {
 078              $this->port = 5223;
 079          }
 080   
 081          $this->enable_logging        = true;
 082          $this->log_array            = array();
 083      }
 084   
 085      /**
 086      * Able to use the SSL functionality?
 087      */
 088      static public function can_use_ssl()
 089      {
 090          // 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)
 091          return ((version_compare(PHP_VERSION, '5.2.1', '<') || version_compare(PHP_VERSION, '5.2.3RC2', '>=')) && @extension_loaded('openssl')) ? true : false;
 092      }
 093   
 094      /**
 095      * Able to use TLS?
 096      */
 097      static public function can_use_tls()
 098      {
 099          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  }
 873