diff --git a/XMPPHP/BOSH.php b/XMPPHP/BOSH.php new file mode 100644 index 0000000..1c8a3a9 --- /dev/null +++ b/XMPPHP/BOSH.php @@ -0,0 +1,176 @@ + + * @author Stephan Wentz + * @copyright 2008 Nathanael C. Fritz + */ + +/** XMPPHP_XMLStream */ +require_once "XMPP.php"; + +/** + * XMPPHP Main Class + * + * @category xmpphp + * @package XMPPHP + * @author Nathanael C. Fritz + * @author Stephan Wentz + * @copyright 2008 Nathanael C. Fritz + * @version $Id$ + */ +class XMPPHP_BOSH extends XMPPHP_XMPP { + + protected $rid; + protected $sid; + protected $http_server; + protected $http_buffer = Array(); + protected $session = false; + + public function connect($server, $wait='1', $session=false) { + $this->http_server = $server; + $this->use_encryption = false; + $this->session = $session; + + $this->rid = 0; + $this->sid = null; + if($session) + { + print "loading session..."; + $this->loadSession(); + } + if(!$this->sid) { + print "loading..."; + $body = $this->__buildBody(); + $body->addAttribute('hold','1'); + $body->addAttribute('to', $this->host); + $body->addAttribute('route', "xmpp:{$this->host}:{$this->port}"); + $body->addAttribute('secure','true'); + $body->addAttribute('version','1.6'); + $body->addAttribute('wait', strval($wait)); + $body->addAttribute('ack','1'); + $body->addAttribute('xmlns:xmpp','urn:xmpp:xbosh'); + $buff = ""; + xml_parse($this->parser, $buff, false); + $response = $this->__sendBody($body); + $rxml = new SimpleXMLElement($response); + $this->sid = $rxml['sid']; + } + } + + public function __sendBody($body=null, $recv=true) { + if(!$body) { + $body = $this->__buildBody(); + } + $ch = curl_init($this->http_server); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt ($ch, CURLOPT_HTTPHEADER, Array("Content-Type: text/xml")); + curl_setopt ($ch, CURLOPT_POSTFIELDS, Array('upfile' => $body->asXML())); + $output = ''; + if($recv) { + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $output = curl_exec($ch); + $this->http_buffer[] = $output; + } + curl_close($ch); + return $output; + } + + public function __buildBody($sub=null) { + $xml = new SimpleXMLElement(""); + $xml->addAttribute('content', 'text/xml; charset=utf-8'); + $xml->addAttribute('rid', $this->rid); + $this->rid += 1; + if($this->sid) $xml->addAttribute('sid', $this->sid); + #if($this->sid) $xml->addAttribute('xmlns', 'http://jabber.org/protocol/httpbind'); + $xml->addAttribute('xml:lang', 'en'); + if($sub) { // ok, so simplexml is lame + $p = dom_import_simplexml($xml); + $c = dom_import_simplexml($sub); + $cn = $p->ownerDocument->importNode($c, true); + $p->appendChild($cn); + $xml = simplexml_import_dom($p); + } + return $xml; + } + + public function __process() { + if($this->http_buffer) { + $this->__parseBuffer(); + } else { + $this->__sendBody(); + $this->__parseBuffer(); + } + } + + public function __parseBuffer() { + while ($this->http_buffer) { + $idx = key($this->http_buffer); + $buffer = $this->http_buffer[$idx]; + unset($this->http_buffer[$idx]); + $xml = new SimpleXMLElement($buffer); + $children = $xml->xpath('child::node()'); + foreach ($children as $child) { + $buff = $child->asXML(); + $this->log->log("RECV: $buff", XMPPHP_Log::LEVEL_VERBOSE); + xml_parse($this->parser, $buff, false); + } + } + } + + public function send($msg) { + $this->log->log("SEND: $msg", XMPPHP_Log::LEVEL_VERBOSE); + $msg = new SimpleXMLElement($msg); + #$msg->addAttribute('xmlns', 'jabber:client'); + $this->__sendBody($this->__buildBody($msg), true); + #$this->__parseBuffer(); + } + + public function reset() { + $this->xml_depth = 0; + unset($this->xmlobj); + $this->xmlobj = array(); + $this->setupParser(); + #$this->send($this->stream_start); + $body = $this->__buildBody(); + $body->addAttribute('to', $this->host); + $body->addAttribute('xmpp:restart', 'true', 'urn:xmpp:xbosh'); + $buff = ""; + $response = $this->__sendBody($body); + $this->been_reset = true; + xml_parse($this->parser, $buff, false); + } + + public function loadSession() { + foreach($_SESSION['XMPPHP_BOSH'] as $key => $value) { + $this[$key] = $value; + } + } + + public function saveSession() { + $_SESSION['XMPPHP_BOSH'] = Array(); + foreach ($this as $key => $value) { + $_SESSION['XMPPHP_BOSH'][$key] = $value; + } + } +} diff --git a/XMPPHP/XMLStream.php b/XMPPHP/XMLStream.php index 4de23ef..47452f1 100644 --- a/XMPPHP/XMLStream.php +++ b/XMPPHP/XMLStream.php @@ -121,6 +121,10 @@ class XMPPHP_XMLStream { * @var string */ protected $until = ''; + /** + * @var string + */ + protected $until_count = ''; /** * @var array */ @@ -310,7 +314,7 @@ class XMPPHP_XMLStream { return $this->disconnected; } - private function __process() { + public function __process() { $read = array($this->socket); $write = null; $except = null; @@ -370,8 +374,9 @@ class XMPPHP_XMLStream { end($this->until); $event_key = key($this->until); reset($this->until); + $until_count[$event_key] = 0; $updated = ''; - while(!$this->disconnected and $this->until[$event_key] and (time() - $start < $timeout or $timeout == -1)) { + while(!$this->disconnected and $this->until_count[$event_key] < 1 and (time() - $start < $timeout or $timeout == -1)) { $this->__process(); } if(array_key_exists($event_key, $this->until_payload)) { @@ -380,6 +385,8 @@ class XMPPHP_XMLStream { $payload = array(); } unset($this->until_payload[$event_key]); + unset($this->until_count[$event_key]); + unset($this->until[$event_key]); return $payload; } @@ -528,7 +535,8 @@ class XMPPHP_XMLStream { if(is_array($until)) { if(in_array($name, $until)) { $this->until_payload[$key][] = array($name, $payload); - $this->until[$key] = false; + $this->until_count[$key] += 1; + #$this->until[$key] = false; } } } diff --git a/XMPPHP/XMPP.php b/XMPPHP/XMPP.php index b96fe25..82eadcc 100644 --- a/XMPPHP/XMPP.php +++ b/XMPPHP/XMPP.php @@ -73,6 +73,7 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream { * @var boolean */ protected $authed = false; + protected $session_started = false; /** * @var boolean @@ -219,7 +220,10 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream { $payload['status'] = (isset($xml->sub('status')->data)) ? $xml->sub('status')->data : ''; $this->log->log("Presence: {$payload['from']} [{$payload['show']}] {$payload['status']}", XMPPHP_Log::LEVEL_DEBUG); if(array_key_exists('type', $xml->attrs) and $xml->attrs['type'] == 'subscribe') { - if($this->auto_subscribe) $this->send(""); + if($this->auto_subscribe) { + $this->send(""); + $this->send(""); + } $this->event('subscription_requested', $payload); } elseif(array_key_exists('type', $xml->attrs) and $xml->attrs['type'] == 'subscribed') { $this->event('subscription_accepted', $payload); @@ -310,6 +314,7 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream { */ protected function session_start_handler($xml) { $this->log->log("Session started"); + $this->session_started = true; $this->event('session_start'); } diff --git a/cli_longrun_example_bosh.php b/cli_longrun_example_bosh.php new file mode 100644 index 0000000..21b63f1 --- /dev/null +++ b/cli_longrun_example_bosh.php @@ -0,0 +1,43 @@ +autoSubscribe(); + +try { + $conn->connect('http://server.tld:5280/xmpp-httpbind'); + while(!$conn->isDisconnected()) { + $payloads = $conn->processUntil(array('message', 'presence', 'end_stream', 'session_start')); + foreach($payloads as $event) { + $pl = $event[1]; + switch($event[0]) { + case 'message': + print "---------------------------------------------------------------------------------\n"; + print "Message from: {$pl['from']}\n"; + if($pl['subject']) print "Subject: {$pl['subject']}\n"; + print $pl['body'] . "\n"; + print "---------------------------------------------------------------------------------\n"; + $conn->message($pl['from'], $body="Thanks for sending me \"{$pl['body']}\".", $type=$pl['type']); + if($pl['body'] == 'quit') $conn->disconnect(); + if($pl['body'] == 'break') $conn->send(""); + break; + case 'presence': + print "Presence: {$pl['from']} [{$pl['show']}] {$pl['status']}\n"; + break; + case 'session_start': + print "Session Start\n"; + $conn->getRoster(); + $conn->presence($status="Cheese!"); + break; + } + } + } +} catch(XMPPHP_Exception $e) { + die($e->getMessage()); +}