name = strtolower($name); $this->ns = $ns; if(is_array($attrs)) { foreach($attrs as $key => $value) { $this->attrs[strtolower($key)] = $value; } } $this->data = $data; } function printobj($depth=0) { print str_repeat("\t", $depth) . $this->name . " " . $this->ns . ' ' . $this->data; print "\n"; foreach($this->subs as $sub) { $sub->printobj($depth + 1); } } function hassub($name) { foreach($this->subs as $sub) { if($sub->name == $name) return True; } return False; } function sub($name, $attrs=Null, $ns=Null) { foreach($this->subs as $sub) { if($sub->name == $name) return $sub; } } } class XMLStream { var $socket; var $parser; var $buffer; var $xml_depth = 0; var $host; var $port; var $stream_start = ''; var $disconnect = false; var $ns_map = array(); var $current_ns; var $xmlobj = Null; var $nshandlers = array(); var $idhandlers = array(); var $lastid = 0; function XMLStream($host, $port) { #$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); $this->host = $host; $this->port = $port; #set up the parser $this->setupParser(); } function getId() { $this->lastid++; return $this->lastid; } function addIdHandler($id, $pointer, $obj=Null) { $this->idhandlers[$id] = array($pointer, $obj); } function addHandler($name, $ns, $pointer, $obj=Null) { $this->nshandlers[] = array($name,$ns,$pointer,$obj); } function connect() { #if(socket_connect($this->socket, $this->host, $this->port)) { # socket_write($this->socket, $this->stream_start); #} print "connecting to tcp://{$this->host}:{$this->port}\n"; $this->socket = stream_socket_client("tcp://{$this->host}:{$this->port}"); $this->send($this->stream_start); } function process() { while(!$this->disconnect) { #$buff = socket_read($this->socket, 1024); $buff = fread($this->socket, 1024); print "RECV: '$buff'\n"; xml_parse($this->parser, $buff, False); sleep(1); # parse whatever we get out of the socket } } function processTime($timeout=-1) { $start = time(); $updated = 'hi'; while($timeout == -1 or time() - $start < $timeout) { $timeleft = $timeout - (time() - $start); $read = array($this->socket); $write = NULL; $except = NULL; $updated = stream_select($read, $write, $except, intval($timeleft)); if ($updated > 0) { $buff = fread($this->socket, 1024); xml_parse($this->parser, $buff, False); } } } function processUntil($mask) { } function startXML($parser, $name, $attr) { $this->xml_depth++; if(array_key_exists('XMLNS', $attr)) { $this->current_ns = $attr['XMLNS']; } $ns = $this->current_ns; foreach($attr as $key => $value) { if(strstr($key, ":")) { $key = explode(':', $key); $key = $key[1]; $this->ns_map[$key] = $value; } } if(!strstr($name, ":") === False) { $name = explode(':', $name); $ns = $this->ns_map[$name[0]]; $name = $name[1]; } $obj = new XMLObj($name, $ns, $attr); if($this->xml_depth > 1) $this->xmlobj[$this->xml_depth - 1]->subs[] = $obj; $this->xmlobj[$this->xml_depth] = $obj; } function endXML($parser, $name) { $this->xml_depth--; if($this->xml_depth == 1) { #clean-up old objects $found = False; foreach($this->nshandlers as $handler) { if($this->xmlobj[2]->name == $handler[0] and $this->xmlobj[2]->ns == $handler[1]) { if($handler[3] === Null) $handler[3] = $this; call_user_method($handler[2], $handler[3], $this->xmlobj[2]); break; } } foreach($this->idhandlers as $id => $handler) { if($this->xmlobj[2]->attrs['id'] == $id) { if($handler[1] === Null) $handler[1] = $this; call_user_method($handler[0], $handler[1], $this->xmlobj[2]); #id handlers are only used once unset($this->idhandlers[$id]); break; } } if(is_array($this->xmlobj)) { $this->xmlobj = array_slice($this->xmlobj, 0, 1); $this->xmlobj[0]->subs = Null; } } } function charXML($parser, $data) { $this->xmlobj[$this->xml_depth]->data = $data; } function send($msg) { #socket_write($this->socket, $msg); print "SENT: $msg \n"; fwrite($this->socket, $msg); } function reset() { $this->xml_depth = 0; $this->xmlobj = Null; $this->setupParser(); $this->send($this->stream_start); } function setupParser() { $this->parser = xml_parser_create(); xml_set_object($this->parser, $this); xml_set_element_handler($this->parser, 'startXML', 'endXML'); xml_set_character_data_handler($this->parser, 'charXML'); } } class XMPP extends XMLStream { var $server; var $user; var $password; var $resource; function XMPP($host, $port, $user, $password, $resource, $server=Null) { $this->XMLStream($host, $port); $this->user = $user; $this->password = $password; $this->resource = $resource; if(!$server) $server = $host; $this->stream_start = '\n'; $this->addHandler('features', 'http://etherx.jabber.org/streams', 'features_handler'); $this->addHandler('success', 'urn:ietf:params:xml:ns:xmpp-sasl', 'sasl_success_handler'); $this->addHandler('failure', 'urn:ietf:params:xml:ns:xmpp-sasl', 'sasl_failure_handler'); $this->addHandler('proceed', 'urn:ietf:params:xml:ns:xmpp-tls', 'tls_proceed_handler'); } function features_handler($xml) { if($xml->hassub('starttls')) { $this->send(""); } elseif($xml->hassub('bind')) { $id = $this->getId(); print "ok, we can bind $id\n"; $this->addIdHandler($id, 'resource_bind_handler'); $this->send("{$this->resource}"); } else { print "Attempting Auth...\n"; $this->send("" . base64_encode("\x00" . $this->user . "\x00" . $this->password) . ""); } } function sasl_success_handler($xml) { print "Auth success!\n"; $this->reset(); } function sasl_failure_handler($xml) { print "Auth failed!\n"; } function resource_bind_handler($xml) { if($xml->attrs['type'] == 'result') print "Bound to " . $xml->sub('bind')->sub('jid')->data . "\n"; $id = $this->getId(); $this->addIdHandler($id, 'session_start_handler'); $this->send(""); } function session_start_handler($xml) { print "session started\n"; } function tls_proceed_handler($xml) { print "Starting TLS connection\n"; stream_socket_enable_crypto($this->socket, True, STREAM_CRYPTO_METHOD_TLS_CLIENT); print stream_socket_get_name($this->socket, True) . "\n"; $this->reset(); } }