* everything working

git-svn-id: svn://netflint.net/xmpphp@7 ef36c318-a008-4979-b6e8-6b496270793b
This commit is contained in:
fritzy 2008-04-02 07:09:28 +00:00
parent ce6cae7f4a
commit 962226c573
6 changed files with 202 additions and 46 deletions

30
cli_longrun_example.php Normal file
View File

@ -0,0 +1,30 @@
<?php
include("xmpp.php");
$conn = new XMPP('talk.google.com', 5222, 'user', 'password', 'xmpphp', 'gmail.com', $printlog=True, $loglevel=LOGGING_VERBOSE);
$conn->connect();
while(!$conn->disconnected) {
$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("</end>");
break;
case 'presence':
print "Presence: {$pl['from']} [{$pl['show']}] {$pl['status']}\n";
break;
case 'session_start':
$conn->presence($status="Cheese!");
break;
}
}
}
?>

View File

@ -1,8 +0,0 @@
<?php
include "cjp.php";
$client = new XMPP('talk.google.com', 5222, 'username', 'password', 'ChicXMPP', 'gmail.com');
$client->connect();
$client->process();
?>

36
logging.php Normal file
View File

@ -0,0 +1,36 @@
<?php
define('LOGGING_ERROR', 0);
define('LOGGING_WARNING', 1);
define('LOGGING_INFO', 2);
define('LOGGING_DEBUG', 3);
define('LOGGING_VERBOSE', 4);
class Logging {
var $data = array();
var $names = array();
var $runlevel;
var $printout;
function Logging($printout = False, $runlevel=LOGGING_INFO) {
$this->names = array('ERROR ', 'WARNING', 'INFO ', 'DEBUG ', 'VERBOSE');
$this->runlevel = $runlevel;
$this->printout = $printout;
}
function log($msg, $runlevel=Null) {
if(!$runlevel) $runlevel = LOGGING_INFO;
$data[] = array($this->runlevel, $msg);
if($this->printout and $runlevel <= $this->runlevel) print "{$this->names[$runlevel]}: $msg\n";
}
function printout($clear=True, $runlevel=Null) {
if(!$runlevel) $runlevel = $this->runlevel;
foreach($this->data as $data) {
if($runlevel <= $data[0]) print "{$this->names[$runlevel]}: $data[1]\n";
}
if($clear) $this->data = array();
}
}
?>

9
sendmessage_example.php Normal file
View File

@ -0,0 +1,9 @@
<?php
include("xmpp.php");
$conn = new XMPP('talk.google.com', 5222, 'username', 'password', 'xmpphp', 'gmail.com', $printlog=False, $loglevel=LOGGING_INFO);
$conn->connect();
$conn->processUntil('session_start');
$conn->message('someguy@someserver.net', 'This is a test message!');
$conn->disconnect();
?>

View File

@ -1,5 +1,6 @@
<?php <?php
require_once("xmlobj.php"); require_once("xmlobj.php");
require_once("logging.php");
class XMLStream { class XMLStream {
var $socket; var $socket;
@ -9,7 +10,8 @@ class XMLStream {
var $host; var $host;
var $port; var $port;
var $stream_start = '<stream>'; var $stream_start = '<stream>';
var $disconnect = false; var $stream_end = '</stream';
var $disconnected = false;
var $ns_map = array(); var $ns_map = array();
var $current_ns = array(); var $current_ns = array();
var $xmlobj = Null; var $xmlobj = Null;
@ -21,13 +23,19 @@ class XMLStream {
var $until; var $until;
var $until_happened = False; var $until_happened = False;
var $until_payload = array(); var $until_payload = array();
var $log;
var $reconnect = True;
var $been_reset = False;
function XMLStream($host, $port) { function XMLStream($host, $port, $log=False, $loglevel=Null) {
#$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); #$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$this->reconnect = True;
$this->host = $host; $this->host = $host;
$this->port = $port; $this->port = $port;
#set up the parser #set up the parser
$this->setupParser(); $this->setupParser();
#set up logger
$this->log = new Logging($log, $loglevel);
} }
function getId() { function getId() {
@ -47,42 +55,50 @@ class XMLStream {
$this->eventhanders[] = array($name, $pointer, $obj); $this->eventhanders[] = array($name, $pointer, $obj);
} }
function connect($persistent=False) { function connect($persistent=False, $sendinit=True) {
#if(socket_connect($this->socket, $this->host, $this->port)) { $this->disconnected = False;
# socket_write($this->socket, $this->stream_start);
#}
if($persistent) { if($persistent) {
$conflag = STREAM_CLIENT_PERSISTENT; $conflag = STREAM_CLIENT_PERSISTENT;
} else { } else {
$conflag = STREAM_CLIENT_CONNECT; $conflag = STREAM_CLIENT_CONNECT;
} }
print "connecting to tcp://{$this->host}:{$this->port}\n"; $this->log->log("Connecting to tcp://{$this->host}:{$this->port}");
$this->socket = stream_socket_client("tcp://{$this->host}:{$this->port}", $flags=$conflag); $this->socket = stream_socket_client("tcp://{$this->host}:{$this->port}", $flags=$conflag);
$this->send($this->stream_start); if($sendinit) $this->send($this->stream_start);
}
function apply_socket($socket) {
$this->socket = $socket;
} }
function process() { function process() {
$updated = '';
while(!$this->disconnect) { while(!$this->disconnect) {
#$buff = socket_read($this->socket, 1024); $read = array($this->socket);
$write = NULL;
$except = NULL;
$updated = stream_select($read, $write, $except, 1);
if ($updated > 0) {
$buff = fread($this->socket, 1024); $buff = fread($this->socket, 1024);
print "RECV: $buff\n"; if(!$buff and $this->reconnect) $this->doReconnect();
$this->log->log("RECV: $buff", LOGGING_VERBOSE);
xml_parse($this->parser, $buff, False); xml_parse($this->parser, $buff, False);
# parse whatever we get out of the socket }
} }
} }
function processTime($timeout=-1) { function processTime($timeout=-1) {
$start = time(); $start = time();
$updated = ''; $updated = '';
while($timeout == -1 or time() - $start < $timeout) { while(!$this->disconnected and ($timeout == -1 or time() - $start < $timeout)) {
$timeleft = $timeout - (time() - $start);
$read = array($this->socket); $read = array($this->socket);
$write = NULL; $write = NULL;
$except = NULL; $except = NULL;
$updated = stream_select($read, $write, $except, intval($timeleft)); $updated = stream_select($read, $write, $except, 1);
if ($updated > 0) { if ($updated > 0) {
$buff = fread($this->socket, 1024); $buff = fread($this->socket, 1024);
print "RECV: $buff\n"; if(!$buff and $this->reconnect) $this->doReconnect();
$this->log->log("RECV: $buff", LOGGING_VERBOSE);
xml_parse($this->parser, $buff, False); xml_parse($this->parser, $buff, False);
} }
} }
@ -94,14 +110,15 @@ class XMLStream {
$this->until = $event; $this->until = $event;
$this->until_happened = False; $this->until_happened = False;
$updated = ''; $updated = '';
while(!$this->until_happened and (time() - $start < $timeout or $timeout == -1)) { while(!$this->disconnected and !$this->until_happened and (time() - $start < $timeout or $timeout == -1)) {
$read = array($this->socket); $read = array($this->socket);
$write = NULL; $write = NULL;
$except = NULL; $except = NULL;
$updated = stream_select($read, $write, $except, 1); $updated = stream_select($read, $write, $except, 1);
if ($updated > 0) { if ($updated > 0) {
$buff = fread($this->socket, 1024); $buff = fread($this->socket, 1024);
print "RECV: $buff\n"; if(!$buff and $this->reconnect) $this->doReconnect();
$this->log->log("RECV: $buff", LOGGING_VERBOSE);
xml_parse($this->parser, $buff, False); xml_parse($this->parser, $buff, False);
} }
} }
@ -111,6 +128,10 @@ class XMLStream {
} }
function startXML($parser, $name, $attr) { function startXML($parser, $name, $attr) {
if($this->been_reset) {
$this->been_reset = False;
$this->xml_depth = 0;
}
$this->xml_depth++; $this->xml_depth++;
if(array_key_exists('XMLNS', $attr)) { if(array_key_exists('XMLNS', $attr)) {
$this->current_ns[$this->xml_depth] = $attr['XMLNS']; $this->current_ns[$this->xml_depth] = $attr['XMLNS'];
@ -139,14 +160,15 @@ class XMLStream {
} }
function endXML($parser, $name) { function endXML($parser, $name) {
if($this->been_reset) {
$this->been_reset = False;
$this->xml_depth = 0;
}
$this->xml_depth--; $this->xml_depth--;
print "{$this->xml_depth}: $name\n";
if($this->xml_depth == 1) { if($this->xml_depth == 1) {
#clean-up old objects #clean-up old objects
$found = False; $found = False;
foreach($this->nshandlers as $handler) { foreach($this->nshandlers as $handler) {
print $this->xml_depth;
print "::::{$this->xmlobj[2]->name}:{$this->xmlobj[2]->ns}\n";
if($this->xmlobj[2]->name == $handler[0] and ($this->xmlobj[2]->ns == $handler[1] or (!$handler[1] and $this->xmlobj[2]->ns == $this->default_ns))) { if($this->xmlobj[2]->name == $handler[0] and ($this->xmlobj[2]->ns == $handler[1] or (!$handler[1] and $this->xmlobj[2]->ns == $this->default_ns))) {
if($handler[3] === Null) $handler[3] = $this; if($handler[3] === Null) $handler[3] = $this;
call_user_method($handler[2], $handler[3], $this->xmlobj[2]); call_user_method($handler[2], $handler[3], $this->xmlobj[2]);
@ -166,16 +188,37 @@ class XMLStream {
$this->xmlobj[0]->subs = Null; $this->xmlobj[0]->subs = Null;
} }
} }
if($this->xml_depth == 0 and !$this->been_reset) {
if(!$this->disconnected) {
$this->send($this->stream_end);
$this->disconnected = True;
fclose($this->socket);
if($this->reconnect) {
$this->doReconnect();
}
}
$this->event('end_stream');
}
}
function doReconnect() {
$this->connect(False, False);
$this->reset();
}
function disconnect() {
$this->reconnect = False;
$this->send($this->stream_end);
$this->processUntil('end_stream', 5);
$this->disconnected = True;
} }
function event($name, $payload=Null) { function event($name, $payload=Null) {
print "EVENT: $name\n"; $this->log->log("EVENT: $name", LOGGING_DEBUG);
foreach($this->eventhandlers as $handler) { foreach($this->eventhandlers as $handler) {
print "$name {$handler[0]}\n";
if($name == $handler[0]) { if($name == $handler[0]) {
if($handler[2] === Null) $handler[2] = $this; if($handler[2] === Null) $handler[2] = $this;
call_user_method($handler[1], $handler[2], $payload); call_user_method($handler[1], $handler[2], $payload);
print "Called {$handler[1]}\n";
} }
} }
if(in_array($name, $this->until)) { if(in_array($name, $this->until)) {
@ -185,12 +228,12 @@ class XMLStream {
} }
function charXML($parser, $data) { function charXML($parser, $data) {
$this->xmlobj[$this->xml_depth]->data = $data; $this->xmlobj[$this->xml_depth]->data .= $data;
} }
function send($msg) { function send($msg) {
#socket_write($this->socket, $msg); #socket_write($this->socket, $msg);
print "SENT: $msg \n"; $this->log->log("SENT: $msg", LOGGING_VERBOSE);
fwrite($this->socket, $msg); fwrite($this->socket, $msg);
} }
@ -199,6 +242,7 @@ class XMLStream {
$this->xmlobj = Null; $this->xmlobj = Null;
$this->setupParser(); $this->setupParser();
$this->send($this->stream_start); $this->send($this->stream_start);
$this->been_reset = True;
} }
function setupParser() { function setupParser() {

View File

@ -1,20 +1,23 @@
<?php <?php
require_once("xmlobj.php"); require_once("xmlobj.php");
require_once("xmlstream.php"); require_once("xmlstream.php");
require_once("logging.php");
class XMPP extends XMLStream { class XMPP extends XMLStream {
var $server; var $server;
var $user; var $user;
var $password; var $password;
var $resource; var $resource;
var $fulljid;
function XMPP($host, $port, $user, $password, $resource, $server=Null) { function XMPP($host, $port, $user, $password, $resource, $server=Null, $printlog=False, $loglevel=Null) {
$this->XMLStream($host, $port); $this->XMLStream($host, $port, $loglevel, $loglevel);
$this->user = $user; $this->user = $user;
$this->password = $password; $this->password = $password;
$this->resource = $resource; $this->resource = $resource;
if(!$server) $server = $host; if(!$server) $server = $host;
$this->stream_start = '<stream:stream to="' . $server . '" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0">\n'; $this->stream_start = '<stream:stream to="' . $server . '" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0">\n';
$this->stream_end = '</stream:stream>';
$this->addHandler('features', 'http://etherx.jabber.org/streams', 'features_handler'); $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('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('failure', 'urn:ietf:params:xml:ns:xmpp-sasl', 'sasl_failure_handler');
@ -25,10 +28,51 @@ class XMPP extends XMLStream {
} }
function message_handler($xml) { function message_handler($xml) {
print "Message: {$xml->sub('body')->data}\n"; $payload['type'] = $xml->attrs['type'];
if(!$paytload['type']) $payload['type'] = 'chat';
$payload['from'] = $xml->attrs['from'];
$payload['body'] = $xml->sub('body')->data;
$this->log->log("Message: {$xml->sub('body')->data}", LOGGING_DEBUG);
$this->event('message', $payload);
}
function message($to, $body, $type='chat', $subject=Null) {
$to = htmlentities($to);
$body = htmlentities($body);
$subject = htmlentities($subject);
$out = "<message from='{$this->fulljid}' to='$to' type='$type'>";
if($subject) $out .= "<subject>$subject</subject>";
$out .= "<body>$body</body></message>";
$this->send($out);
}
function presence($status=Null, $show='available', $to=Null) {
$to = htmlentities($to);
$status = htmlentities($status);
if($show == 'unavailable') $type = 'unavailable';
$out = "<presence";
if($to) $out .= " to='$to'";
if($type) $out .= " type='$type'";
if($show == 'available' and !$status) {
$out .= "/>";
} else {
$out .= ">";
if($show != 'available') $out .= "<show>$show</show>";
if($status) $out .= "<status>$status</status>";
$out .= "</presence>";
}
$this->send($out);
} }
function presence_handler($xml) { function presence_handler($xml) {
$payload['type'] = $xml->attrs['type'];
if(!$payload['type']) $payload['type'] = 'available';
$payload['show'] = $xml->sub('show')->data;
if(!$payload['show']) $payload['show'] = $payload['type'];
$payload['from'] = $xml->attrs['from'];
$payload['status'] = $xml->sub('status')->data;
$this->log->log("Presence: {$payload['from']} [{$payload['show']}] {$payload['status']}", LOGGING_DEBUG);
$this->event('presence', $payload);
} }
function features_handler($xml) { function features_handler($xml) {
@ -36,40 +80,41 @@ class XMPP extends XMLStream {
$this->send("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required /></starttls>"); $this->send("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required /></starttls>");
} elseif($xml->hassub('bind')) { } elseif($xml->hassub('bind')) {
$id = $this->getId(); $id = $this->getId();
print "ok, we can bind $id\n";
$this->addIdHandler($id, 'resource_bind_handler'); $this->addIdHandler($id, 'resource_bind_handler');
$this->send("<iq xmlns=\"jabber:client\" type=\"set\" id=\"$id\"><bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"><resource>{$this->resource}</resource></bind></iq>"); $this->send("<iq xmlns=\"jabber:client\" type=\"set\" id=\"$id\"><bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"><resource>{$this->resource}</resource></bind></iq>");
} else { } else {
print "Attempting Auth...\n"; $this->log->log("Attempting Auth...");
$this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" . base64_encode("\x00" . $this->user . "\x00" . $this->password) . "</auth>"); $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" . base64_encode("\x00" . $this->user . "\x00" . $this->password) . "</auth>");
} }
} }
function sasl_success_handler($xml) { function sasl_success_handler($xml) {
print "Auth success!\n"; $this->log->log("Auth success!");
$this->reset(); $this->reset();
} }
function sasl_failure_handler($xml) { function sasl_failure_handler($xml) {
print "Auth failed!\n"; $this->log->log("Auth failed!", LOGGING_ERROR);
} }
function resource_bind_handler($xml) { function resource_bind_handler($xml) {
if($xml->attrs['type'] == 'result') print "Bound to " . $xml->sub('bind')->sub('jid')->data . "\n"; if($xml->attrs['type'] == 'result') {
$this->log->log("Bound to " . $xml->sub('bind')->sub('jid')->data);
$this->fulljid = $xml->sub('bind')->sub('jid')->data;
}
$id = $this->getId(); $id = $this->getId();
$this->addIdHandler($id, 'session_start_handler'); $this->addIdHandler($id, 'session_start_handler');
$this->send("<iq xmlns='jabber:client' type='set' id='$id'><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></iq>"); $this->send("<iq xmlns='jabber:client' type='set' id='$id'><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></iq>");
} }
function session_start_handler($xml) { function session_start_handler($xml) {
print "session started\n"; $this->log->log("Session started");
$this->event('session_start'); $this->event('session_start');
} }
function tls_proceed_handler($xml) { function tls_proceed_handler($xml) {
print "Starting TLS connection\n"; $this->log->log("Starting TLS encryption");
stream_socket_enable_crypto($this->socket, True, STREAM_CRYPTO_METHOD_TLS_CLIENT); stream_socket_enable_crypto($this->socket, True, STREAM_CRYPTO_METHOD_TLS_CLIENT);
print stream_socket_get_name($this->socket, True) . "\n";
$this->reset(); $this->reset();
} }
} }