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