[ Index ]

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


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1