MDL-65743 lib: update moodle library docs
[moodle.git] / lib / jabber / XMPP / XMLStream.php
CommitLineData
838a8eb1 1<?php
838a8eb1 2
d947f53d 3namespace BirknerAlex\XMPPHP;
838a8eb1 4
d947f53d
SL
5 /**
6 * XMPPHP: The PHP XMPP Library
7 * Copyright (C) 2008 Nathanael C. Fritz
8 * This file is part of SleekXMPP.
9 *
10 * XMPPHP is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * XMPPHP is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with XMPPHP; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 *
24 * @category xmpphp
25 * @package XMPPHP
26 * @author Nathanael C. Fritz <JID: fritzy@netflint.net>
27 * @author Stephan Wentz <JID: stephan@jabber.wentz.it>
28 * @author Michael Garvin <JID: gar@netflint.net>
29 * @author Alexander Birkner (https://github.com/BirknerAlex)
30 * @copyright 2008 Nathanael C. Fritz
31 */
838a8eb1 32
33/**
d947f53d
SL
34 * XMPPHP Main Class
35 *
36 * @category xmpphp
37 * @package XMPPHP
38 * @author Nathanael C. Fritz <JID: fritzy@netflint.net>
39 * @author Stephan Wentz <JID: stephan@jabber.wentz.it>
40 * @author Michael Garvin <JID: gar@netflint.net>
838a8eb1 41 * @copyright 2008 Nathanael C. Fritz
d947f53d 42 * @version $Id$
838a8eb1 43 */
d947f53d 44class XMLStream {
838a8eb1 45 /**
46 * @var resource
47 */
48 protected $socket;
49 /**
50 * @var resource
51 */
52 protected $parser;
53 /**
54 * @var string
55 */
56 protected $buffer;
57 /**
58 * @var integer
59 */
60 protected $xml_depth = 0;
61 /**
62 * @var string
63 */
64 protected $host;
65 /**
66 * @var integer
67 */
68 protected $port;
69 /**
70 * @var string
71 */
72 protected $stream_start = '<stream>';
73 /**
74 * @var string
75 */
76 protected $stream_end = '</stream>';
77 /**
78 * @var boolean
79 */
d947f53d 80 protected $disconnected = true;
838a8eb1 81 /**
82 * @var boolean
83 */
84 protected $sent_disconnect = false;
85 /**
86 * @var array
87 */
88 protected $ns_map = array();
89 /**
90 * @var array
91 */
92 protected $current_ns = array();
93 /**
94 * @var array
95 */
96 protected $xmlobj = null;
97 /**
98 * @var array
99 */
100 protected $nshandlers = array();
d804f9e2
PS
101 /**
102 * @var array
103 */
104 protected $xpathhandlers = array();
838a8eb1 105 /**
106 * @var array
107 */
108 protected $idhandlers = array();
109 /**
110 * @var array
111 */
112 protected $eventhandlers = array();
113 /**
114 * @var integer
115 */
116 protected $lastid = 0;
117 /**
118 * @var string
119 */
120 protected $default_ns;
121 /**
d947f53d 122 * @var string[]
838a8eb1 123 */
d947f53d 124 protected $until = array();
d804f9e2 125 /**
d947f53d 126 * @var int[]
d804f9e2 127 */
d947f53d 128 protected $until_count = array();
838a8eb1 129 /**
130 * @var array
131 */
132 protected $until_happened = false;
133 /**
134 * @var array
135 */
136 protected $until_payload = array();
137 /**
d947f53d 138 * @var Log
838a8eb1 139 */
140 protected $log;
141 /**
142 * @var boolean
143 */
144 protected $reconnect = true;
145 /**
146 * @var boolean
147 */
148 protected $been_reset = false;
149 /**
150 * @var boolean
151 */
152 protected $is_server;
153 /**
154 * @var float
155 */
156 protected $last_send = 0;
d804f9e2
PS
157 /**
158 * @var boolean
159 */
160 protected $use_ssl = false;
161 /**
162 * @var integer
163 */
164 protected $reconnectTimeout = 30;
838a8eb1 165
166 /**
167 * Constructor
168 *
169 * @param string $host
170 * @param string $port
171 * @param boolean $printlog
172 * @param string $loglevel
173 * @param boolean $is_server
174 */
175 public function __construct($host = null, $port = null, $printlog = false, $loglevel = null, $is_server = false) {
176 $this->reconnect = !$is_server;
177 $this->is_server = $is_server;
178 $this->host = $host;
179 $this->port = $port;
180 $this->setupParser();
d947f53d 181 $this->log = new Log($printlog, $loglevel);
838a8eb1 182 }
183
184 /**
185 * Destructor
186 * Cleanup connection
187 */
188 public function __destruct() {
189 if(!$this->disconnected && $this->socket) {
190 $this->disconnect();
191 }
192 }
193
194 /**
195 * Return the log instance
196 *
d947f53d 197 * @return Log
838a8eb1 198 */
199 public function getLog() {
200 return $this->log;
201 }
202
203 /**
204 * Get next ID
205 *
206 * @return integer
207 */
208 public function getId() {
209 $this->lastid++;
210 return $this->lastid;
211 }
212
d804f9e2
PS
213 /**
214 * Set SSL
215 *
216 * @return integer
217 */
218 public function useSSL($use=true) {
219 $this->use_ssl = $use;
220 }
221
838a8eb1 222 /**
223 * Add ID Handler
224 *
225 * @param integer $id
226 * @param string $pointer
227 * @param string $obj
228 */
229 public function addIdHandler($id, $pointer, $obj = null) {
230 $this->idhandlers[$id] = array($pointer, $obj);
231 }
232
233 /**
234 * Add Handler
235 *
d804f9e2 236 * @param string $name
838a8eb1 237 * @param string $ns
238 * @param string $pointer
239 * @param string $obj
240 * @param integer $depth
241 */
242 public function addHandler($name, $ns, $pointer, $obj = null, $depth = 1) {
d804f9e2 243 #TODO deprication warning
838a8eb1 244 $this->nshandlers[] = array($name,$ns,$pointer,$obj, $depth);
245 }
246
247 /**
d804f9e2
PS
248 * Add XPath Handler
249 *
250 * @param string $xpath
251 * @param string $pointer
252 * @param
253 */
254 public function addXPathHandler($xpath, $pointer, $obj = null) {
255 if (preg_match_all("/\(?{[^\}]+}\)?(\/?)[^\/]+/", $xpath, $regs)) {
256 $ns_tags = $regs[0];
257 } else {
258 $ns_tags = array($xpath);
259 }
260 foreach($ns_tags as $ns_tag) {
d947f53d 261 list($l, $r) = explode('}', $ns_tag);
d804f9e2
PS
262 if ($r != null) {
263 $xpart = array(substr($l, 1), $r);
264 } else {
265 $xpart = array(null, $l);
266 }
267 $xpath_array[] = $xpart;
268 }
269 $this->xpathhandlers[] = array($xpath_array, $pointer, $obj);
270 }
271
272 /**
273 * Add Event Handler
838a8eb1 274 *
275 * @param integer $id
276 * @param string $pointer
277 * @param string $obj
278 */
279 public function addEventHandler($name, $pointer, $obj) {
d804f9e2 280 $this->eventhandlers[] = array($name, $pointer, $obj);
838a8eb1 281 }
282
283 /**
284 * Connect to XMPP Host
285 *
d947f53d 286 * @param integer $timeout Timeout in seconds
838a8eb1 287 * @param boolean $persistent
d947f53d
SL
288 * @param boolean $sendinit Send XMPP starting sequence after connect
289 * automatically
290 *
291 * @throws Exception When the connection fails
838a8eb1 292 */
293 public function connect($timeout = 30, $persistent = false, $sendinit = true) {
838a8eb1 294 $this->sent_disconnect = false;
d804f9e2
PS
295 $starttime = time();
296
297 do {
298 $this->disconnected = false;
299 $this->sent_disconnect = false;
300 if($persistent) {
301 $conflag = STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT;
302 } else {
303 $conflag = STREAM_CLIENT_CONNECT;
304 }
305 $conntype = 'tcp';
306 if($this->use_ssl) $conntype = 'ssl';
307 $this->log->log("Connecting to $conntype://{$this->host}:{$this->port}");
308 try {
309 $this->socket = @stream_socket_client("$conntype://{$this->host}:{$this->port}", $errno, $errstr, $timeout, $conflag);
310 } catch (Exception $e) {
d947f53d 311 throw new Exception($e->getMessage());
d804f9e2
PS
312 }
313 if(!$this->socket) {
d947f53d 314 $this->log->log("Could not connect.", Log::LEVEL_ERROR);
d804f9e2
PS
315 $this->disconnected = true;
316 # Take it easy for a few seconds
317 sleep(min($timeout, 5));
318 }
319 } while (!$this->socket && (time() - $starttime) < $timeout);
320
321 if ($this->socket) {
322 stream_set_blocking($this->socket, 1);
323 if($sendinit) $this->send($this->stream_start);
838a8eb1 324 } else {
d947f53d 325 throw new Exception("Could not connect before timeout.");
838a8eb1 326 }
838a8eb1 327 }
328
329 /**
330 * Reconnect XMPP Host
d947f53d
SL
331 *
332 * @throws Exception When the connection fails
333 * @uses $reconnectTimeout
334 * @see setReconnectTimeout()
838a8eb1 335 */
336 public function doReconnect() {
337 if(!$this->is_server) {
d947f53d 338 $this->log->log("Reconnecting ($this->reconnectTimeout)...", Log::LEVEL_WARNING);
d804f9e2 339 $this->connect($this->reconnectTimeout, false, false);
838a8eb1 340 $this->reset();
d804f9e2 341 $this->event('reconnect');
838a8eb1 342 }
343 }
344
d804f9e2
PS
345 public function setReconnectTimeout($timeout) {
346 $this->reconnectTimeout = $timeout;
347 }
348
838a8eb1 349 /**
350 * Disconnect from XMPP Host
351 */
352 public function disconnect() {
d947f53d 353 $this->log->log("Disconnecting...", Log::LEVEL_VERBOSE);
d804f9e2
PS
354 if(false == (bool) $this->socket) {
355 return;
356 }
838a8eb1 357 $this->reconnect = false;
358 $this->send($this->stream_end);
359 $this->sent_disconnect = true;
360 $this->processUntil('end_stream', 5);
361 $this->disconnected = true;
362 }
363
364 /**
365 * Are we are disconnected?
366 *
367 * @return boolean
368 */
369 public function isDisconnected() {
370 return $this->disconnected;
371 }
372
d947f53d
SL
373 /**
374 * Checks if the given string is closed with the same tag as it is
375 * opened. We try to be as fast as possible here.
376 *
377 * @param string $buff Read buffer of __process()
378 *
379 * @return boolean true if the buffer seems to be complete
380 */
381 protected function bufferComplete($buff)
382 {
383 if (substr($buff, -1) != '>') {
384 return false;
385 }
386 //we always have a space since the namespace needs to be
387 //declared. could be a tab, though
388 $start = substr(
389 $buff, 1,
390 min(strpos($buff, '>', 2), strpos($buff, ' ', 2)) - 1
391 );
392 $stop = substr($buff, -strlen($start) - 3);
393
394 if ($start == '?xml') {
395 //starting with an xml tag. this means a stream is being
396 // opened, which is not much of data, so no fear it's
397 // not complete
398 return true;
399 }
400 if (substr($stop, -2) == '/>') {
401 //one tag, i.e. <success />
402 return true;
403 }
404 if ('</' . $start . '>' == $stop) {
405 return true;
406 }
407
408 return false;
409 }
410
d804f9e2
PS
411 /**
412 * Core reading tool
d947f53d
SL
413 *
414 * @param mixed $maximum Limit when to return
415 * - 0: only read if data is immediately ready
416 * - NULL: wait forever and ever
417 * - integer: process for this amount of microseconds
418 * @param boolean $return_when_received Immediately return when data have been
419 * received
420 *
421 * @return boolean True when all goes well, false when something fails
d804f9e2 422 */
d947f53d
SL
423 private function __process($maximum = 5, $return_when_received = false)
424 {
d804f9e2
PS
425 $remaining = $maximum;
426
427 do {
428 $starttime = (microtime(true) * 1000000);
429 $read = array($this->socket);
430 $write = array();
431 $except = array();
432 if (is_null($maximum)) {
433 $secs = NULL;
434 $usecs = NULL;
435 } else if ($maximum == 0) {
436 $secs = 0;
437 $usecs = 0;
438 } else {
439 $usecs = $remaining % 1000000;
440 $secs = floor(($remaining - $usecs) / 1000000);
441 }
442 $updated = @stream_select($read, $write, $except, $secs, $usecs);
443 if ($updated === false) {
d947f53d 444 $this->log->log("Error on stream_select()", Log::LEVEL_VERBOSE);
d804f9e2 445 if ($this->reconnect) {
838a8eb1 446 $this->doReconnect();
447 } else {
448 fclose($this->socket);
d804f9e2 449 $this->socket = NULL;
838a8eb1 450 return false;
451 }
d804f9e2 452 } else if ($updated > 0) {
d947f53d
SL
453 $buff = '';
454 do {
455 if ($buff != '') {
456 //disable blocking for now because fread() will
457 // block until the 4k are full if we already
458 // read a part of the packet
459 stream_set_blocking($this->socket, 0);
d804f9e2 460 }
d947f53d
SL
461 $part = fread($this->socket, 4096);
462 stream_set_blocking($this->socket, 1);
463
464 if (!$part && feof($this->socket)) {
465 if($this->reconnect) {
466 $this->doReconnect();
467 } else {
468 fclose($this->socket);
469 $this->socket = NULL;
470 return false;
471 }
472 }
473 $this->log->log("RECV: $part", Log::LEVEL_VERBOSE);
474 $buff .= $part;
475 } while (!$this->bufferComplete($buff));
476
d804f9e2 477 xml_parse($this->parser, $buff, false);
d947f53d
SL
478 if ($return_when_received) {
479 return true;
480 }
d804f9e2
PS
481 } else {
482 # $updated == 0 means no changes during timeout.
838a8eb1 483 }
d804f9e2
PS
484 $endtime = (microtime(true)*1000000);
485 $time_past = $endtime - $starttime;
486 $remaining = $remaining - $time_past;
487 } while (is_null($maximum) || $remaining > 0);
488 return true;
838a8eb1 489 }
490
491 /**
492 * Process
493 *
494 * @return string
495 */
496 public function process() {
d804f9e2 497 $this->__process(NULL);
838a8eb1 498 }
499
500 /**
501 * Process until a timeout occurs
502 *
d947f53d
SL
503 * @param integer $timeout Time in seconds
504 *
838a8eb1 505 * @return string
d947f53d
SL
506 *
507 * @see __process()
838a8eb1 508 */
d804f9e2
PS
509 public function processTime($timeout=NULL) {
510 if (is_null($timeout)) {
511 return $this->__process(NULL);
512 } else {
513 return $this->__process($timeout * 1000000);
838a8eb1 514 }
515 }
516
517 /**
518 * Process until a specified event or a timeout occurs
519 *
d947f53d
SL
520 * @param string|array $event Event name or array of event names
521 * @param integer $timeout Timeout in seconds
522 *
523 * @return array Payload
838a8eb1 524 */
d947f53d
SL
525 public function processUntil($event, $timeout = -1)
526 {
527 if ($this->disconnected) {
528 throw new Exception('You need to connect first');
529 }
530
838a8eb1 531 $start = time();
d947f53d
SL
532 if (!is_array($event)) {
533 $event = array($event);
534 }
535
838a8eb1 536 $this->until[] = $event;
537 end($this->until);
538 $event_key = key($this->until);
539 reset($this->until);
d947f53d 540
d804f9e2 541 $this->until_count[$event_key] = 0;
838a8eb1 542 $updated = '';
d947f53d
SL
543 while (!$this->disconnected
544 && $this->until_count[$event_key] < 1
545 && ($timeout == -1 || time() - $start < $timeout)
546 ) {
547 $maximum = $timeout == -1
548 ? NULL
549 : ($timeout - (time() - $start)) * 1000000;
550 $ret = $this->__process($maximum, true);
551 if (!$ret) {
552 break;
553 }
838a8eb1 554 }
d947f53d
SL
555
556 if (array_key_exists($event_key, $this->until_payload)) {
838a8eb1 557 $payload = $this->until_payload[$event_key];
d804f9e2
PS
558 unset($this->until_payload[$event_key]);
559 unset($this->until_count[$event_key]);
560 unset($this->until[$event_key]);
838a8eb1 561 } else {
562 $payload = array();
563 }
d947f53d 564
838a8eb1 565 return $payload;
566 }
567
568 /**
569 * Obsolete?
570 */
571 public function Xapply_socket($socket) {
572 $this->socket = $socket;
573 }
574
575 /**
576 * XML start callback
577 *
578 * @see xml_set_element_handler
579 *
580 * @param resource $parser
581 * @param string $name
582 */
583 public function startXML($parser, $name, $attr) {
584 if($this->been_reset) {
585 $this->been_reset = false;
586 $this->xml_depth = 0;
587 }
588 $this->xml_depth++;
589 if(array_key_exists('XMLNS', $attr)) {
590 $this->current_ns[$this->xml_depth] = $attr['XMLNS'];
591 } else {
592 $this->current_ns[$this->xml_depth] = $this->current_ns[$this->xml_depth - 1];
593 if(!$this->current_ns[$this->xml_depth]) $this->current_ns[$this->xml_depth] = $this->default_ns;
594 }
595 $ns = $this->current_ns[$this->xml_depth];
596 foreach($attr as $key => $value) {
597 if(strstr($key, ":")) {
598 $key = explode(':', $key);
599 $key = $key[1];
600 $this->ns_map[$key] = $value;
601 }
602 }
603 if(!strstr($name, ":") === false)
604 {
605 $name = explode(':', $name);
606 $ns = $this->ns_map[$name[0]];
607 $name = $name[1];
608 }
d947f53d 609 $obj = new XMLObj($name, $ns, $attr);
838a8eb1 610 if($this->xml_depth > 1) {
611 $this->xmlobj[$this->xml_depth - 1]->subs[] = $obj;
612 }
613 $this->xmlobj[$this->xml_depth] = $obj;
614 }
615
616 /**
617 * XML end callback
618 *
619 * @see xml_set_element_handler
620 *
621 * @param resource $parser
622 * @param string $name
623 */
624 public function endXML($parser, $name) {
d947f53d 625 #$this->log->log("Ending $name", Log::LEVEL_DEBUG);
838a8eb1 626 #print "$name\n";
627 if($this->been_reset) {
628 $this->been_reset = false;
629 $this->xml_depth = 0;
630 }
631 $this->xml_depth--;
632 if($this->xml_depth == 1) {
633 #clean-up old objects
d804f9e2
PS
634 #$found = false; #FIXME This didn't appear to be in use --Gar
635 foreach($this->xpathhandlers as $handler) {
636 if (is_array($this->xmlobj) && array_key_exists(2, $this->xmlobj)) {
637 $searchxml = $this->xmlobj[2];
638 $nstag = array_shift($handler[0]);
639 if (($nstag[0] == null or $searchxml->ns == $nstag[0]) and ($nstag[1] == "*" or $nstag[1] == $searchxml->name)) {
640 foreach($handler[0] as $nstag) {
641 if ($searchxml !== null and $searchxml->hasSub($nstag[1], $ns=$nstag[0])) {
642 $searchxml = $searchxml->sub($nstag[1], $ns=$nstag[0]);
643 } else {
644 $searchxml = null;
645 break;
646 }
647 }
648 if ($searchxml !== null) {
649 if($handler[2] === null) $handler[2] = $this;
d947f53d 650 $this->log->log("Calling {$handler[1]}", Log::LEVEL_DEBUG);
f9ba0713 651 $handler[2]->{$handler[1]}($this->xmlobj[2]);
d804f9e2
PS
652 }
653 }
654 }
655 }
838a8eb1 656 foreach($this->nshandlers as $handler) {
d804f9e2 657 if($handler[4] != 1 and array_key_exists(2, $this->xmlobj) and $this->xmlobj[2]->hasSub($handler[0])) {
838a8eb1 658 $searchxml = $this->xmlobj[2]->sub($handler[0]);
659 } elseif(is_array($this->xmlobj) and array_key_exists(2, $this->xmlobj)) {
660 $searchxml = $this->xmlobj[2];
661 }
662 if($searchxml !== null and $searchxml->name == $handler[0] and ($searchxml->ns == $handler[1] or (!$handler[1] and $searchxml->ns == $this->default_ns))) {
663 if($handler[3] === null) $handler[3] = $this;
d947f53d 664 $this->log->log("Calling {$handler[2]}", Log::LEVEL_DEBUG);
f9ba0713 665 $handler[3]->{$handler[2]}($this->xmlobj[2]);
838a8eb1 666 }
667 }
668 foreach($this->idhandlers as $id => $handler) {
669 if(array_key_exists('id', $this->xmlobj[2]->attrs) and $this->xmlobj[2]->attrs['id'] == $id) {
670 if($handler[1] === null) $handler[1] = $this;
f9ba0713 671 $handler[1]->{$handler[0]}($this->xmlobj[2]);
838a8eb1 672 #id handlers are only used once
673 unset($this->idhandlers[$id]);
674 break;
675 }
676 }
677 if(is_array($this->xmlobj)) {
678 $this->xmlobj = array_slice($this->xmlobj, 0, 1);
d947f53d 679 if(isset($this->xmlobj[0]) && $this->xmlobj[0] instanceof XMLObj) {
838a8eb1 680 $this->xmlobj[0]->subs = null;
681 }
682 }
683 unset($this->xmlobj[2]);
684 }
685 if($this->xml_depth == 0 and !$this->been_reset) {
686 if(!$this->disconnected) {
687 if(!$this->sent_disconnect) {
688 $this->send($this->stream_end);
689 }
690 $this->disconnected = true;
691 $this->sent_disconnect = true;
692 fclose($this->socket);
693 if($this->reconnect) {
694 $this->doReconnect();
695 }
696 }
697 $this->event('end_stream');
698 }
699 }
700
701 /**
702 * XML character callback
703 * @see xml_set_character_data_handler
704 *
705 * @param resource $parser
706 * @param string $data
707 */
708 public function charXML($parser, $data) {
709 if(array_key_exists($this->xml_depth, $this->xmlobj)) {
710 $this->xmlobj[$this->xml_depth]->data .= $data;
711 }
712 }
713
714 /**
715 * Event?
716 *
717 * @param string $name
718 * @param string $payload
719 */
720 public function event($name, $payload = null) {
d947f53d 721 $this->log->log("EVENT: $name", Log::LEVEL_DEBUG);
838a8eb1 722 foreach($this->eventhandlers as $handler) {
723 if($name == $handler[0]) {
724 if($handler[2] === null) {
725 $handler[2] = $this;
726 }
f9ba0713 727 $handler[2]->{$handler[1]}($payload);
838a8eb1 728 }
729 }
d947f53d 730
838a8eb1 731 foreach($this->until as $key => $until) {
732 if(is_array($until)) {
733 if(in_array($name, $until)) {
734 $this->until_payload[$key][] = array($name, $payload);
d804f9e2
PS
735 if(!isset($this->until_count[$key])) {
736 $this->until_count[$key] = 0;
737 }
738 $this->until_count[$key] += 1;
739 #$this->until[$key] = false;
838a8eb1 740 }
741 }
742 }
743 }
744
745 /**
746 * Read from socket
747 */
748 public function read() {
749 $buff = @fread($this->socket, 1024);
750 if(!$buff) {
751 if($this->reconnect) {
752 $this->doReconnect();
753 } else {
754 fclose($this->socket);
755 return false;
756 }
757 }
d947f53d 758 $this->log->log("RECV: $buff", Log::LEVEL_VERBOSE);
838a8eb1 759 xml_parse($this->parser, $buff, false);
760 }
761
762 /**
763 * Send to socket
764 *
765 * @param string $msg
766 */
d804f9e2
PS
767 public function send($msg, $timeout=NULL) {
768
769 if (is_null($timeout)) {
770 $secs = NULL;
771 $usecs = NULL;
772 } else if ($timeout == 0) {
773 $secs = 0;
774 $usecs = 0;
775 } else {
776 $maximum = $timeout * 1000000;
777 $usecs = $maximum % 1000000;
778 $secs = floor(($maximum - $usecs) / 1000000);
838a8eb1 779 }
d804f9e2
PS
780
781 $read = array();
782 $write = array($this->socket);
783 $except = array();
784
785 $select = @stream_select($read, $write, $except, $secs, $usecs);
786
787 if($select === False) {
788 $this->log->log("ERROR sending message; reconnecting.");
789 $this->doReconnect();
790 # TODO: retry send here
791 return false;
792 } elseif ($select > 0) {
d947f53d 793 $this->log->log("Socket is ready; send it.", Log::LEVEL_VERBOSE);
d804f9e2 794 } else {
d947f53d 795 $this->log->log("Socket is not ready; break.", Log::LEVEL_ERROR);
d804f9e2 796 return false;
838a8eb1 797 }
d804f9e2
PS
798
799 $sentbytes = @fwrite($this->socket, $msg);
d947f53d 800 $this->log->log("SENT: " . mb_substr($msg, 0, $sentbytes, '8bit'), Log::LEVEL_VERBOSE);
838a8eb1 801 if($sentbytes === FALSE) {
d947f53d 802 $this->log->log("ERROR sending message; reconnecting.", Log::LEVEL_ERROR);
838a8eb1 803 $this->doReconnect();
d804f9e2 804 return false;
838a8eb1 805 }
d947f53d 806 $this->log->log("Successfully sent $sentbytes bytes.", Log::LEVEL_VERBOSE);
d804f9e2 807 return $sentbytes;
838a8eb1 808 }
809
810 public function time() {
811 list($usec, $sec) = explode(" ", microtime());
812 return (float)$sec + (float)$usec;
813 }
814
815 /**
816 * Reset connection
817 */
818 public function reset() {
819 $this->xml_depth = 0;
820 unset($this->xmlobj);
821 $this->xmlobj = array();
822 $this->setupParser();
823 if(!$this->is_server) {
824 $this->send($this->stream_start);
825 }
826 $this->been_reset = true;
827 }
828
829 /**
830 * Setup the XML parser
831 */
832 public function setupParser() {
833 $this->parser = xml_parser_create('UTF-8');
834 xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1);
835 xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
836 xml_set_object($this->parser, $this);
837 xml_set_element_handler($this->parser, 'startXML', 'endXML');
838 xml_set_character_data_handler($this->parser, 'charXML');
839 }
d804f9e2
PS
840
841 public function readyToProcess() {
842 $read = array($this->socket);
843 $write = array();
844 $except = array();
845 $updated = @stream_select($read, $write, $except, 0);
846 return (($updated !== false) && ($updated > 0));
847 }
838a8eb1 848}