aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes/functions_jabber.php
diff options
context:
space:
mode:
authorMeik Sievertsen <acydburn@phpbb.com>2007-05-26 16:38:33 +0000
committerMeik Sievertsen <acydburn@phpbb.com>2007-05-26 16:38:33 +0000
commit1b32236b1ebbf046e84a435c97c7ed6bc9edd5f9 (patch)
treefb368f22ddc4ca85ba81b455cc05c756f533f6a1 /phpBB/includes/functions_jabber.php
parentfda482ce2921bdaa5c5c61a1e288461701cea6b7 (diff)
downloadforums-1b32236b1ebbf046e84a435c97c7ed6bc9edd5f9.tar
forums-1b32236b1ebbf046e84a435c97c7ed6bc9edd5f9.tar.gz
forums-1b32236b1ebbf046e84a435c97c7ed6bc9edd5f9.tar.bz2
forums-1b32236b1ebbf046e84a435c97c7ed6bc9edd5f9.tar.xz
forums-1b32236b1ebbf046e84a435c97c7ed6bc9edd5f9.zip
hopefully not too late in the game. Checked in new jabber class (the class done by the flyspray project). It would be nice if this could be tested with more servers - jabber.org seems to work fine...
- other fixes git-svn-id: file:///svn/phpbb/trunk@7687 89ea8834-ac86-4346-8a33-228a782c2dd0
Diffstat (limited to 'phpBB/includes/functions_jabber.php')
-rw-r--r--phpBB/includes/functions_jabber.php1635
1 files changed, 468 insertions, 1167 deletions
diff --git a/phpBB/includes/functions_jabber.php b/phpBB/includes/functions_jabber.php
index 3872346bbc..3ad96df928 100644
--- a/phpBB/includes/functions_jabber.php
+++ b/phpBB/includes/functions_jabber.php
@@ -10,665 +10,212 @@
/**
*
-* Class.Jabber.PHP v0.4.2
-* (c) 2004 Nathan "Fritzy" Fritz
-* http://cjphp.netflint.net *** fritzy@netflint.net
+* Jabber class from Flyspray project
+* @version class.jabber2.php 1209 2007-05-12 13:39:10Z floele
+* @copyright 2006 Flyspray.org
+* @author: Florian Schmitz (floele)
*
-* This is a bugfix version, specifically for those who can't get
-* 0.4 to work on Jabberd2 servers.
-*
-* last modified: 24.03.2004 13:01:53
-*
-* Modified by phpBB Development Team
-* version: v0.4.3
+* Modified by Acyd Burn
*
* @package phpBB3
*/
class jabber
{
+ var $connection = null;
+ var $session = array();
+ var $timeout = 10;
+
var $server;
var $port;
var $username;
var $password;
- var $resource;
- var $jid;
-
- var $connection;
- var $delay_disconnect;
-
- var $stream_id;
+ var $use_ssl;
var $enable_logging;
var $log_array;
- var $iq_sleep_timer;
- var $last_ping_time;
-
- var $packet_queue;
-
- var $iq_version_name;
- var $iq_version_os;
- var $iq_version_version;
-
- var $error_codes;
-
- var $connected;
- var $keep_alive_id;
- var $returned_keep_alive;
- var $txnid;
-
- var $connector;
+ var $features = array();
- var $version;
- var $show_version;
-
- /**
- * Constructor
- */
- function jabber($server, $port, $username, $password, $resource)
+ function jabber($server, $port, $username, $password, $use_ssl = false)
{
$this->server = ($server) ? $server : 'localhost';
- $this->port = ($port) ? $port : '5222';
+ $this->port = ($port) ? $port : 5222;
$this->username = $username;
$this->password = $password;
- $this->resource = ($resource) ? $resource : NULL;
-
- $this->enable_logging = true;
- $this->log_array = array();
+ $this->use_ssl = ($use_ssl && $this->can_use_ssl) ? true : false;
- $this->version = '1.0';
- $this->show_version = false;
-
- $this->packet_queue = array();
- $this->iq_sleep_timer = $this->delay_disconnect = 1;
-
- $this->returned_keep_alive = true;
- $this->txnid = 0;
-
- $this->iq_version_name = "Class.Jabber.PHP -- http://cjphp.netflint.net -- by Nathan 'Fritzy' Fritz, fritz@netflint.net";
- $this->iq_version_version = '0.4';
- $this->iq_version_os = $_SERVER['SERVER_SOFTWARE'];
-
- $this->error_codes = array(
- 400 => 'Bad Request',
- 401 => 'Unauthorised',
- 402 => 'Payment Required',
- 403 => 'Forbidden',
- 404 => 'Not Found',
- 405 => 'Not Allowed',
- 406 => 'Not Acceptable',
- 407 => 'Registration Required',
- 408 => 'Request Timeout',
- 409 => 'Conflict',
- 500 => 'Internal Server Error',
- 501 => 'Not Implemented',
- 502 => 'Remove Server Error',
- 503 => 'Service Unavailable',
- 504 => 'Remove Server Timeout',
- 510 => 'Disconnected'
- );
- }
-
- /**
- * Connect
- */
- function connect()
- {
- $this->connector = new cjp_standard_connector;
-
- if ($this->connector->open_socket($this->server, $this->port))
+ // Change port if we use SSL
+ if ($this->port == 5222 && $this->use_ssl)
{
- $this->send_packet("<?xml version='1.0' encoding='UTF-8' ?" . ">\n");
- $this->send_packet("<stream:stream to='{$this->server}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'" . (($this->show_version) ? " version='{$this->version}'" : '') . ">\n");
-
- sleep(2);
-
- if ($this->_check_connected())
- {
- $this->connected = true; // Nathan Fritz
- return true;
- }
- else
- {
- $this->add_to_log('ERROR: connect() #1');
- return false;
- }
+ $this->port = 5223;
}
- else
- {
- $this->add_to_log('ERROR: connect() #2');
- return false;
- }
- }
-
- /**
- * Disconnect
- */
- function disconnect()
- {
- if (is_int($this->delay_disconnect))
- {
- sleep($this->delay_disconnect);
- }
-
- $this->send_packet('</stream:stream>');
- $this->connector->close_socket();
- }
-
- /**
- * Send authentication request
- */
- function send_auth()
- {
- $this->auth_id = 'auth_' . md5(time() . $_SERVER['REMOTE_ADDR']);
- $this->resource = ($this->resource != NULL) ? $this->resource : ('Class.Jabber.PHP ' . md5($this->auth_id));
- $this->jid = "{$this->username}@{$this->server}/{$this->resource}";
-
- // request available authentication methods
- $payload = "<username>{$this->username}</username>";
- $packet = $this->send_iq(NULL, 'get', $this->auth_id, 'jabber:iq:auth', $payload);
-
- // was a result returned?
- if ($this->get_info_from_iq_type($packet) == 'result' && $this->get_info_from_iq_id($packet) == $this->auth_id)
- {
- // yes, now check for auth method availability in descending order (best to worst)
- if (isset($packet['iq']['#']['query'][0]['#']['sequence'][0]['#']) && isset($packet['iq']['#']['query'][0]['#']['token'][0]['#']))
- {
- // auth_0k
- return $this->_sendauth_ok($packet['iq']['#']['query'][0]['#']['token'][0]['#'], $packet['iq']['#']['query'][0]['#']['sequence'][0]['#']);
- }
- else if (isset($packet['iq']['#']['query'][0]['#']['digest']))
- {
- // digest
- return $this->_sendauth_digest();
- }
- else if ($packet['iq']['#']['query'][0]['#']['password'])
- {
- // plain text
- return $this->_sendauth_plaintext();
- }
- else
- {
- $this->add_to_log('ERROR: send_auth() #2 - No auth method available!');
- return false;
- }
- }
- else
- {
- // no result returned
- $this->add_to_log('ERROR: send_auth() #1');
- return false;
- }
- }
-
- /**
- * Register account
- */
- function account_registration($reg_email = NULL, $reg_name = NULL)
- {
- $packet = $this->send_iq($this->server, 'get', 'reg_01', 'jabber:iq:register');
-
- if ($packet)
- {
- // just in case a key was passed back from the server
- $key = $this->get_info_from_iq_key($packet);
- unset($packet);
-
- $payload = "<username>{$this->username}</username>
- <password>{$this->password}</password>
- <email>$reg_email</email>
- <name>$reg_name</name>\n";
-
- $payload .= ($key) ? "<key>$key</key>\n" : '';
- $packet = $this->send_iq($this->server, 'set', 'reg_01', 'jabber:iq:register', $payload);
-
- if ($this->get_info_from_iq_type($packet) == 'result')
- {
- $return_code = (isset($packet['iq']['#']['query'][0]['#']['registered'][0]['#'])) ? 1 : 2;
- $this->jid = ($this->resource) ? "{$this->username}@{$this->server}/{$this->resource}" : "{$this->username}@{$this->server}";
- }
- else if ($this->get_info_from_iq_type($packet) == 'error' && isset($packet['iq']['#']['error'][0]['#']))
- {
- // "conflict" error, i.e. already registered
- if ($packet['iq']['#']['error'][0]['@']['code'] == '409')
- {
- $return_code = 1;
- }
- else
- {
- $return_code = 'Error ' . $packet['iq']['#']['error'][0]['@']['code'] . ': ' . $packet['iq']['#']['error'][0]['#'];
- }
- }
-
- return $return_code;
- }
- else
- {
- return 3;
- }
+ $this->enable_logging = true;
+ $this->log_array = array();
}
/**
- * Change password
+ * Able to use the SSL functionality?
*/
- function change_password($new_password)
+ function can_use_ssl()
{
- $packet = $this->send_iq($this->server, 'get', 'A0', 'jabber:iq:register');
-
- if ($packet)
- {
- // just in case a key was passed back from the server
- $key = $this->get_info_from_iq_key($packet);
- unset($packet);
-
- $payload = "<username>{$this->username}</username>
- <password>{$new_password}</password>\n";
- $payload .= ($key) ? "<key>$key</key>\n" : '';
-
- $packet = $this->send_iq($this->server, 'set', 'A0', 'jabber:iq:register', $payload);
-
- if ($this->get_info_from_iq_type($packet) == 'result')
- {
- $return_code = (isset($packet['iq']['#']['query'][0]['#']['registered'][0]['#'])) ? 1 : 2;
- }
- else if ($this->get_info_from_iq_type($packet) == 'error' && isset($packet['iq']['#']['error'][0]['#']))
- {
- // "conflict" error, i.e. already registered
- if ($packet['iq']['#']['error'][0]['@']['code'] == '409')
- {
- $return_code = 1;
- }
- else
- {
- $return_code = 'Error ' . $packet['iq']['#']['error'][0]['@']['code'] . ': ' . $packet['iq']['#']['error'][0]['#'];
- }
- }
-
- return $return_code;
- }
- else
- {
- return 3;
- }
+ // Will not work with PHP >= 5.2.1 until timeout problem with ssl hasn't been fixed (http://bugs.php.net/41236)
+ return (version_compare(PHP_VERSION, '5.2.1', '<') && @extension_loaded('openssl')) ? true : false;
}
/**
- * Send packet
+ * Able to use TLS?
*/
- function send_packet($xml)
+ function can_use_tls()
{
- $xml = trim($xml);
-
- if ($this->connector->write_to_socket($xml))
+ 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'))
{
- $this->add_to_log('SEND: ' . $xml);
- return true;
- }
- else
- {
- $this->add_to_log('ERROR: send_packet() #1');
return false;
}
- }
-
- /**
- * Listen to socket
- */
- function listen()
- {
- $incoming = '';
-
- while ($line = $this->connector->read_from_socket(4096))
- {
- $incoming .= $line;
- }
- $incoming = trim($incoming);
+ // Make sure the encryption stream is supported
+ $streams = stream_get_wrappers();
- if ($incoming != '')
+ if (!in_array('streams.crypto', $streams))
{
- $this->add_to_log('RECV: ' . $incoming);
- $temp = $this->_split_incoming($incoming);
-
- for ($i = 0, $size = sizeof($temp); $i < $size; $i++)
- {
- $this->packet_queue[] = $this->xmlize($temp[$i]);
- }
+ return false;
}
return true;
}
/**
- * Strip jid
- */
- function strip_jid($jid = NULL)
- {
- preg_match('#(.*)\/(.*)#Ui', $jid, $temp);
- return ($temp[1] != '') ? $temp[1] : $jid;
- }
-
- /**
- * Send a message
+ * Connect
*/
- function send_message($to, $type = 'normal', $id = NULL, $content = NULL, $payload = NULL)
+ function connect()
{
- if ($to && is_array($content))
- {
- if (!$id)
- {
- $id = $type . '_' . time();
- }
-
- $this->_array_xmlspecialchars($content);
-
- $xml = "<message to='$to' type='$type' id='$id'>\n";
-
- if (!empty($content['subject']))
- {
- $xml .= '<subject>' . $content['subject'] . "</subject>\n";
- }
-
- if (!empty($content['thread']))
- {
- $xml .= '<thread>' . $content['thread'] . "</thread>\n";
- }
-
- $xml .= '<body>' . $content['body'] . "</body>\n";
- $xml .= $payload;
- $xml .= "</message>\n";
-
- if ($this->send_packet($xml))
- {
- return true;
- }
- else
- {
- $this->add_to_log('ERROR: send_message() #1');
- }
- }
- else
+/* if (!$this->check_jid($this->username . '@' . $this->server))
{
- $this->add_to_log('ERROR: send_message() #2');
+ $this->add_to_log('Error: Jabber ID is not valid: ' . $this->username . '@' . $this->server);
return false;
- }
- }
-
- /**
- * Send presence
- */
- function send_presence($type = NULL, $to = NULL, $status = NULL, $show = NULL, $priority = NULL)
- {
- $xml = '<presence';
- $xml .= ($to) ? " to='$to'" : '';
- $xml .= ($type) ? " type='$type'" : '';
- $xml .= ($status || $show || $priority) ? ">\n" : " />\n";
-
- $xml .= ($status) ? " <status>$status</status>\n" : '';
- $xml .= ($show) ? " <show>$show</show>\n" : '';
- $xml .= ($priority) ? " <priority>$priority</priority>\n" : '';
+ }*/
- $xml .= ($status || $show || $priority) ? "</presence>\n" : '';
+ $this->session['ssl'] = $this->use_ssl;
- if ($this->send_packet($xml))
+ if ($this->open_socket($this->server, $this->port, $this->use_ssl))
{
- return true;
+ $this->send("<?xml version='1.0' encoding='UTF-8' ?" . ">\n");
+ $this->send("<stream:stream to='{$this->server}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n");
}
else
{
- $this->add_to_log('ERROR: send_presence() #1');
+ $this->add_to_log('Error: connect() #2');
return false;
}
- }
-
- /**
- * Send error
- */
- function send_error($to, $id = NULL, $error_number, $error_message = NULL)
- {
- $xml = "<iq type='error' to='$to'";
- $xml .= ($id) ? " id='$id'" : '';
- $xml .= ">\n";
- $xml .= " <error code='$error_number'>";
- $xml .= ($error_message) ? $error_message : $this->error_codes[$error_number];
- $xml .= "</error>\n";
- $xml .= '</iq>';
-
- $this->send_packet($xml);
- }
- /**
- * Get first from queue
- */
- function get_first_from_queue()
- {
- return array_shift($this->packet_queue);
+ // Now we listen what the server has to say...and give appropriate responses
+ $this->response($this->listen());
+ return true;
}
/**
- * Get from queue by id
+ * Disconnect
*/
- function get_from_queue_by_id($packet_type, $id)
+ function disconnect()
{
- $found_message = false;
-
- foreach ($this->packet_queue as $key => $value)
+ if ($this->connected())
{
- if ($value[$packet_type]['@']['id'] == $id)
+ // disconnect gracefully
+ if (isset($this->session['sent_presence']))
{
- $found_message = $value;
- unset($this->packet_queue[$key]);
-
- break;
+ $this->presence('offline', '', true);
}
+
+ $this->send('</stream:stream>');
+ $this->session = array();
+ return fclose($this->connection);
}
- return (is_array($found_message)) ? $found_message : false;
+ return false;
}
/**
- * Call handler
+ * Connected?
*/
- function call_handler($packet = NULL)
+ function connected()
{
- $packet_type = $this->_get_packet_type($packet);
-
- if ($packet_type == 'message')
- {
- $type = $packet['message']['@']['type'];
- $type = ($type != '') ? $type : 'normal';
- $funcmeth = "handler_message_$type";
- }
- else if ($packet_type == 'iq')
- {
- $namespace = $packet['iq']['#']['query'][0]['@']['xmlns'];
- $namespace = str_replace(':', '_', $namespace);
- $funcmeth = "handler_iq_$namespace";
- }
- else if ($packet_type == 'presence')
- {
- $type = $packet['presence']['@']['type'];
- $type = ($type != '') ? $type : 'available';
- $funcmeth = "handler_presence_$type";
- }
-
- if ($funcmeth != '')
- {
- if (function_exists($funcmeth))
- {
- call_user_func($funcmeth, $packet);
- }
- else if (method_exists($this, $funcmeth))
- {
- call_user_func(array(&$this, $funcmeth), $packet);
- }
- else
- {
- $this->handler_not_implemented($packet);
- $this->add_to_log("ERROR: call_handler() #1 - neither method nor function $funcmeth() available");
- }
- }
+ return (is_resource($this->connection) && !feof($this->connection)) ? true : false;
}
+
/**
- * Cruise Control
+ * Initiates login (using data from contructor, after calling connect())
+ * @access public
+ * @return bool
*/
- function cruise_control($seconds = -1)
+ function login()
{
- $count = 0;
-
- while ($count != $seconds)
+ if (!sizeof($this->features))
{
- $this->listen();
-
- do
- {
- $packet = $this->get_first_from_queue();
-
- if ($packet)
- {
- $this->call_handler($packet);
- }
- }
- while (sizeof($this->packet_queue) > 1);
-
- $count += 0.25;
- usleep(250000);
-
- if (($this->last_ping_time + 180) < time())
- {
- // Modified by Nathan Fritz
- if ($this->returned_keep_alive == false)
- {
- $this->connected = false;
- $this->add_to_log('EVENT: Disconnected');
- }
-
- if ($this->returned_keep_alive == true)
- {
- $this->connected = true;
- }
-
- $this->returned_keep_alive = false;
-
- $this->keep_alive_id = 'keep_alive_' . time();
- // $this->send_packet("<iq id='{$this->keep_alive_id}'/>", 'cruise_control');
- $this->send_packet("<iq type='get' from='{$this->username}@{$this->server}/{$this->resource}' to='{$this->server}' id='{$this->keep_alive_id}'><query xmlns='jabber:iq:time' /></iq>");
- $this->last_ping_time = time();
- }
+ $this->add_to_log('Error: No feature information from server available.');
+ return false;
}
- return true;
+ return $this->response($this->features);
}
/**
- * Send iq
+ * Send data to the Jabber server
+ * @param string $xml
+ * @access public
+ * @return bool
*/
- function send_iq($to = NULL, $type = 'get', $id = NULL, $xmlns = NULL, $payload = NULL, $from = NULL)
+ function send($xml)
{
- if (!preg_match('#^(get|set|result|error)$#', $type))
+ if ($this->connected())
{
- unset($type);
-
- $this->add_to_log("ERROR: send_iq() #2 - type must be 'get', 'set', 'result' or 'error'");
- return false;
- }
- else if ($id && $xmlns)
- {
- $xml = "<iq type='$type' id='$id'";
- $xml .= ($to) ? " to='" . htmlspecialchars($to) . "'" : '';
- $xml .= ($from) ? " from='$from'" : '';
- $xml .= ">
- <query xmlns='$xmlns'>
- $payload
- </query>
- </iq>";
-
- $this->send_packet($xml);
- sleep($this->iq_sleep_timer);
- $this->listen();
-
- return (preg_match('#^(get|set)$#', $type)) ? $this->get_from_queue_by_id('iq', $id) : true;
+ $xml = trim($xml);
+ $this->add_to_log('SEND: '. $xml);
+ return fwrite($this->connection, $xml);
}
else
{
- $this->add_to_log('ERROR: send_iq() #1 - to, id and xmlns are mandatory');
+ $this->add_to_log('Error: Could not send, connection lost (flood?).');
return false;
}
}
/**
- * get the transport registration fields
- * method written by Steve Blinch, http://www.blitzaffe.com
+ * OpenSocket
+ * @param string $server host to connect to
+ * @param int $port port number
+ * @param bool $use_ssl use ssl or not
+ * @access public
+ * @return bool
*/
- function transport_registration_details($transport)
+ function open_socket($server, $port, $use_ssl = false)
{
- $this->txnid++;
- $packet = $this->send_iq($transport, 'get', "reg_{$this->txnid}", 'jabber:iq:register', NULL, $this->jid);
-
- if ($packet)
+ if (@function_exists('dns_get_record'))
{
- $res = array();
-
- foreach ($packet['iq']['#']['query'][0]['#'] as $element => $data)
+ $record = dns_get_record("_xmpp-client._tcp.$server", DNS_SRV);
+ if (!empty($record))
{
- if ($element != 'instructions' && $element != 'key')
- {
- $res[] = $element;
- }
+ $server = $record[0]['target'];
}
-
- return $res;
}
else
{
- return 3;
+ $this->add_to_log('Warning: dns_get_record function not found. GTalk will not work.');
}
- }
- /**
- * register with the transport
- * method written by Steve Blinch, http://www.blitzaffe.com
- */
- function transport_registration($transport, $details)
- {
- $this->txnid++;
- $packet = $this->send_iq($transport, 'get', "reg_{$this->txnid}", 'jabber:iq:register', NULL, $this->jid);
+ $server = $use_ssl ? 'ssl://' . $server : $server;
- if ($packet)
+ if ($this->connection = @fsockopen($server, $port, $errorno, $errorstr, $this->timeout))
{
- // just in case a key was passed back from the server
- $key = $this->get_info_from_iq_key($packet);
- unset($packet);
-
- $payload = ($key) ? "<key>$key</key>\n" : '';
- foreach ($details as $element => $value)
- {
- $payload .= "<$element>$value</$element>\n";
- }
-
- $packet = $this->send_iq($transport, 'set', "reg_{$this->txnid}", 'jabber:iq:register', $payload);
-
- if ($this->get_info_from_iq_type($packet) == 'result')
- {
- $return_code = (isset($packet['iq']['#']['query'][0]['#']['registered'][0]['#'])) ? 1 : 2;
- }
- else if ($this->get_info_from_iq_type($packet) == 'error')
- {
- if (isset($packet['iq']['#']['error'][0]['#']))
- {
- $return_code = 'Error ' . $packet['iq']['#']['error'][0]['@']['code'] . ': ' . $packet['iq']['#']['error'][0]['#'];
- $this->add_to_log('ERROR: transport_registration()');
- }
- }
+ socket_set_blocking($this->connection, 0);
+ socket_set_timeout($this->connection, 60);
- return $return_code;
- }
- else
- {
- return 3;
+ return true;
}
+
+ // Apparently an error occured...
+ $this->add_to_log('Error: open_socket() - ' . $errorstr);
+ return false;
}
/**
@@ -678,7 +225,7 @@ class jabber
{
if ($this->enable_logging && sizeof($this->log_array))
{
- return implode("<br /><br />", $this->log_array);
+ return '<br /><br />' . implode("<br /><br />", $this->log_array);
}
return '';
@@ -691,640 +238,483 @@ class jabber
{
if ($this->enable_logging)
{
- $this->log_array[] = htmlspecialchars($string);
+ $this->log_array[] = utf8_htmlspecialchars($string);
}
}
-
- // ======================================================================
- // private methods
- // ======================================================================
-
/**
- * Send auth
- * @access private
+ * Listens to the connection until it gets data or the timeout is reached.
+ * Thus, it should only be called if data is expected to be received.
+ * @access public
+ * @return mixed either false for timeout or an array with the received data
*/
- function _sendauth_ok($zerok_token, $zerok_sequence)
+ function listen($timeout = 10, $wait = false)
{
- // initial hash of password
- $zerok_hash = sha1($this->password);
-
- // sequence 0: hash of hashed-password and token
- $zerok_hash = sha1($zerok_hash . $zerok_token);
-
- // repeat as often as needed
- for ($i = 0; $i < $zerok_sequence; $i++)
+ if (!$this->connected())
{
- $zerok_hash = sha1($zerok_hash);
+ return false;
}
- $payload = "<username>{$this->username}</username>
- <hash>$zerok_hash</hash>
- <resource>{$this->resource}</resource>";
-
- $packet = $this->send_iq(NULL, 'set', $this->auth_id, 'jabber:iq:auth', $payload);
+ // Wait for a response until timeout is reached
+ $start = time();
+ $data = '';
- // was a result returned?
- if ($this->get_info_from_iq_type($packet) == 'result' && $this->get_info_from_iq_id($packet) == $this->auth_id)
+ do
{
- return true;
+ $read = trim(fread($this->connection, 4096));
+ $data .= $read;
}
- else
- {
- $this->add_to_log('ERROR: _sendauth_ok() #1');
- return false;
- }
- }
+ while (time() <= $start + $timeout && ($wait || $data == '' || $read != '' || (substr(rtrim($data), -1) != '>')));
- /**
- * Send auth digest
- * @access private
- */
- function _sendauth_digest()
- {
- $payload = "<username>{$this->username}</username>
- <resource>{$this->resource}</resource>
- <digest>" . sha1($this->stream_id . $this->password) . "</digest>";
-
- $packet = $this->send_iq(NULL, 'set', $this->auth_id, 'jabber:iq:auth', $payload);
-
- // was a result returned?
- if ($this->get_info_from_iq_type($packet) == 'result' && $this->get_info_from_iq_id($packet) == $this->auth_id)
+ if ($data != '')
{
- return true;
+ $this->add_to_log('RECV: '. $data);
+ return $this->xmlize($data);
}
else
{
- $this->add_to_log('ERROR: _sendauth_digest() #1');
+ $this->add_to_log('Timeout, no response from server.');
return false;
}
}
/**
- * Send auth plain
- * @access private
+ * Initiates account registration (based on data used for contructor)
+ * @access public
+ * @return bool
*/
- function _sendauth_plaintext()
+ function register()
{
- $payload = "<username>{$this->username}</username>
- <password>{$this->password}</password>
- <resource>{$this->resource}</resource>";
-
- $packet = $this->send_iq(NULL, 'set', $this->auth_id, 'jabber:iq:auth', $payload);
-
- // was a result returned?
- if ($this->get_info_from_iq_type($packet) == 'result' && $this->get_info_from_iq_id($packet) == $this->auth_id)
- {
- return true;
- }
- else
+ if (!isset($this->session['id']) || isset($this->session['jid']))
{
- $this->add_to_log('ERROR: _sendauth_plaintext() #1');
+ $this->add_to_log('Error: Cannot initiate registration.');
return false;
}
+
+ $this->send("<iq type='get' id='reg_1'><query xmlns='jabber:iq:register'/></iq>");
+ return $this->response($this->listen());
}
/**
- * Listen on socket
- * @access private
+ * Sets account presence. No additional info required (default is "online" status)
+ * @param $message online, offline...
+ * @param $type dnd, away, chat, xa or nothing
+ * @access public
+ * @return bool
*/
- function _listen_incoming()
+ function send_presence($message = '', $type = '', $unavailable = false)
{
- $incoming = '';
-
- while ($line = $this->connector->read_from_socket(4096))
+ if (!isset($this->session['jid']))
{
- $incoming .= $line;
- }
-
- $incoming = trim($incoming);
-
- if ($incoming != '')
- {
- $this->add_to_log('RECV: ' . $incoming);
+ $this->add_to_log('ERROR: send_presence() - Cannot set presence at this point, no jid given.');
+ return false;
}
- return $this->xmlize($incoming);
- }
+ $type = strtolower($type);
+ $type = (in_array($type, array('dnd', 'away', 'chat', 'xa'))) ? '<show>'. $type .'</show>' : '';
- /**
- * Check if connected
- * @access private
- */
- function _check_connected($in_tls = false)
- {
- $incoming_array = $this->_listen_incoming();
-
- if (is_array($incoming_array))
- {
- if ($incoming_array['stream:stream']['@']['from'] == $this->server && $incoming_array['stream:stream']['@']['xmlns'] == 'jabber:client' && $incoming_array['stream:stream']['@']['xmlns:stream'] == 'http://etherx.jabber.org/streams')
- {
- $this->stream_id = $incoming_array['stream:stream']['@']['id'];
+ $unavailable = ($unavailable) ? " type='unavailable'" : '';
+ $message = ($message) ? '<status>' . utf8_htmlspecialchars($message) .'</status>' : '';
- // We only start TLS authentication if not called within TLS authentication itself, which may produce a never ending loop...
- if (!$in_tls)
- {
- if (!empty($incoming_array['stream:stream']['#']['stream:features'][0]['#']['starttls'][0]['@']['xmlns']) && $incoming_array['stream:stream']['#']['stream:features'][0]['#']['starttls'][0]['@']['xmlns'] == 'urn:ietf:params:xml:ns:xmpp-tls')
- {
- return $this->_starttls();
- }
- }
+ $this->session['sent_presence'] = !$unavailable;
- return true;
- }
- else
- {
- $this->add_to_log('ERROR: _check_connected() #1');
- return false;
- }
- }
- else
- {
- $this->add_to_log('ERROR: _check_connected() #2');
- return false;
- }
+ return $this->send("<presence$unavailable>" . $type . $message . '</presence>');
}
/**
- * Start TLS/SSL session if supported (PHP5.1)
- * @access private
+ * This handles all the different XML elements
+ * @param array $xml
+ * @access public
+ * @return bool
*/
- function _starttls()
+ function response($xml)
{
- if (!function_exists('stream_socket_enable_crypto') || !function_exists('stream_get_meta_data') || !function_exists('socket_set_blocking') || !function_exists('stream_get_wrappers'))
+ if (!is_array($xml) || !sizeof($xml))
{
- $this->add_to_log('WARNING: TLS is not available');
- return true;
- }
-
- // Make sure the encryption stream is supported
- $streams = stream_get_wrappers();
-
- if (!in_array('streams.crypto', $streams))
- {
- $this->add_to_log('WARNING: SSL/crypto stream not supported');
- return true;
- }
-
- $this->send_packet("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n");
- sleep(2);
- $incoming_array = $this->_listen_incoming();
-
- if (!is_array($incoming_array))
- {
- $this->add_to_log('ERROR: _starttls() #1');
- return false;
- }
-
- if ($incoming_array['proceed']['@']['xmlns'] != 'urn:ietf:params:xml:ns:xmpp-tls')
- {
- $this->add_to_log('ERROR: _starttls() #2');
return false;
}
- $meta = stream_get_meta_data($this->connector->active_socket);
- socket_set_blocking($this->connector->active_socket, 1);
-
- $result = @stream_socket_enable_crypto($this->connector->active_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
- if (!$result)
+ // did we get multiple elements? do one after another
+ // array('message' => ..., 'presence' => ...)
+ if (sizeof($xml) > 1)
{
- socket_set_blocking($this->connector->active_socket, $meta['blocked']);
- $this->add_to_log('ERROR: _starttls() #3');
- return false;
+ foreach ($xml as $key => $value)
+ {
+ $this->response(array($key => $value));
+ }
+ return;
}
-
- socket_set_blocking($this->connector->active_socket, $meta['blocked']);
-
- $this->send_packet("<?xml version='1.0' encoding='UTF-8' ?" . ">\n");
- $this->send_packet("<stream:stream to='{$this->server}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'" . (($this->show_version) ? " version='{$this->version}'" : '') . ">\n");
- sleep(2);
-
- if (!$this->_check_connected(true))
+ else
{
- $this->add_to_log('ERROR: _starttls() #4');
- return false;
+ // or even multiple elements of the same type?
+ // array('message' => array(0 => ..., 1 => ...))
+ if (sizeof(reset($xml)) > 1)
+ {
+ foreach (reset($xml) as $value)
+ {
+ $this->response(array(key($xml) => array(0 => $value)));
+ }
+ return;
+ }
}
- return true;
- }
-
- /**
- * Get packet type
- * @access private
- */
- function _get_packet_type($packet = NULL)
- {
- if (is_array($packet))
+ switch (key($xml))
{
- reset($packet);
- $packet_type = key($packet);
- }
-
- return ($packet_type) ? $packet_type : false;
- }
+ case 'stream:stream':
+ // Connection initialised (or after authentication). Not much to do here...
+ $this->session['id'] = $xml['stream:stream'][0]['@']['id'];
- /**
- * Split incoming packet
- * @access private
- */
- function _split_incoming($incoming)
- {
- $temp = preg_split('#<(message|iq|presence|stream)#', $incoming, -1, PREG_SPLIT_DELIM_CAPTURE);
- $array = array();
-
- for ($i = 1, $size = sizeof($temp); $i < $size; $i += 2)
- {
- $array[] = '<' . $temp[$i] . $temp[($i + 1)];
- }
-
- return $array;
- }
-
- /**
- * Recursively prepares the strings in an array to be used in XML data.
- * @access private
- */
- function _array_xmlspecialchars(&$array)
- {
- if (is_array($array))
- {
- foreach ($array as $k => $v)
- {
- if (is_array($v))
+ if (isset($xml['stream:stream'][0]['#']['stream:features']))
{
- $this->_array_xmlspecialchars($array[$k]);
+ // we already got all info we need
+ $this->features = $xml['stream:stream'][0]['#'];
}
else
{
- $this->_xmlspecialchars($array[$k]);
+ $this->features = $this->listen();
}
- }
- }
- }
-
- /**
- * Prepares a string for usage in XML data.
- * @access private
- */
- function _xmlspecialchars(&$string)
- {
- // we only have a few entities in xml
- $string = str_replace(array('&', '>', '<', '"', '\''), array('&amp;', '&gt;', '&lt;', '&quot;', '&apos;'), $string);
- }
-
- // ======================================================================
- // <message/> parsers
- // ======================================================================
-
- /**
- * Get info from message (from)
- */
- function get_info_from_message_from($packet = NULL)
- {
- return (is_array($packet)) ? $packet['message']['@']['from'] : false;
- }
-
- /**
- * Get info from message (type)
- */
- function get_info_from_message_type($packet = NULL)
- {
- return (is_array($packet)) ? $packet['message']['@']['type'] : false;
- }
-
- /**
- * Get info from message (id)
- */
- function get_info_from_message_id($packet = NULL)
- {
- return (is_array($packet)) ? $packet['message']['@']['id'] : false;
- }
-
- /**
- * Get info from message (thread)
- */
- function get_info_from_message_thread($packet = NULL)
- {
- return (is_array($packet)) ? $packet['message']['#']['thread'][0]['#'] : false;
- }
- /**
- * Get info from message (subject)
- */
- function get_info_from_message_subject($packet = NULL)
- {
- return (is_array($packet)) ? $packet['message']['#']['subject'][0]['#'] : false;
- }
-
- /**
- * Get info from message (body)
- */
- function get_info_from_message_body($packet = NULL)
- {
- return (is_array($packet)) ? $packet['message']['#']['body'][0]['#'] : false;
- }
-
- /**
- * Get info from message (xmlns)
- */
- function get_info_from_message_xmlns($packet = NULL)
- {
- return (is_array($packet)) ? $packet['message']['#']['x'] : false;
- }
-
- /**
- * Get info from message (error)
- */
- function get_info_from_message_error($packet = NULL)
- {
- $error = preg_replace('#^\/$#', '', ($packet['message']['#']['error'][0]['@']['code'] . '/' . $packet['message']['#']['error'][0]['#']));
- return (is_array($packet)) ? $error : false;
- }
+ // go on with authentication?
+ if (isset($this->features['stream:features'][0]['#']['bind']))
+ {
+ return $this->response($this->features);
+ }
+ break;
- // ======================================================================
- // <iq/> parsers
- // ======================================================================
+ case 'stream:features':
+ // Resource binding after successful authentication
+ if (isset($this->session['authenticated']))
+ {
+ // session required?
+ $this->session['sess_required'] = isset($xml['stream:features'][0]['#']['session']);
+
+ $this->send("<iq type='set' id='bind_1'>
+ <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
+ <resource>functions_jabber.phpbb.php</resource>
+ </bind>
+ </iq>");
+ return $this->response($this->listen());
+ }
- /**
- * Get info from iq (from)
- */
- function get_info_from_iq_from($packet = NULL)
- {
- return (is_array($packet)) ? $packet['iq']['@']['from'] : false;
- }
+ // Let's use TLS if SSL is not enabled and we can actually use it
+ if (!$this->session['ssl'] && $this->can_use_tls() && isset($xml['stream:features'][0]['#']['starttls']))
+ {
+ $this->add_to_log('Switching to TLS.');
+ $this->send("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n");
+ return $this->response($this->listen());
+ }
- /**
- * Get info from iq (type)
- */
- function get_info_from_iq_type($packet = NULL)
- {
- return (is_array($packet)) ? $packet['iq']['@']['type'] : false;
- }
+ // Does the server support SASL authentication?
+ // I hope so, because we do (and no other method).
+ if (isset($xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns']) && $xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns'] == 'urn:ietf:params:xml:ns:xmpp-sasl')
+ {
+ // Now decide on method
+ $methods = array();
- /**
- * Get info from iq (id)
- */
- function get_info_from_iq_id($packet = NULL)
- {
- return (is_array($packet)) ? $packet['iq']['@']['id'] : false;
- }
+ foreach ($xml['stream:features'][0]['#']['mechanisms'][0]['#']['mechanism'] as $value)
+ {
+ $methods[] = $value['#'];
+ }
- /**
- * Get info from iq (key)
- */
- function get_info_from_iq_key($packet = NULL)
- {
- return (is_array($packet) && isset($packet['iq']['#']['query'][0]['#']['key'][0]['#'])) ? $packet['iq']['#']['query'][0]['#']['key'][0]['#'] : false;
- }
+ // we prefer this one
+ if (in_array('DIGEST-MD5', $methods))
+ {
+ $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>");
+ }
+ else if (in_array('PLAIN', $methods) && ($this->session['ssl'] || $this->session['tls']))
+ {
+ // we don't want to use this (neither does the server usually) if no encryption is in place
+ $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>"
+ . base64_encode(chr(0) . $this->username . '@' . $this->server . chr(0) . $this->password) .
+ '</auth>');
+ }
+ else
+ {
+ // not good...
+ $this->add_to_log('Error: No authentication method supported.');
+ $this->disconnect();
+ return false;
+ }
- /**
- * Get info from iq (error)
- */
- function get_info_from_iq_error($packet = NULL)
- {
- $error = preg_replace('#^\/$#', '', ($packet['iq']['#']['error'][0]['@']['code'] . '/' . $packet['iq']['#']['error'][0]['#']));
- return (is_array($packet)) ? $error : false;
- }
+ return $this->response($this->listen());
+ }
+ else
+ {
+ // ok, this is it. bye.
+ $this->add_to_log('Error: Server does not offer SASL authentication.');
+ $this->disconnect();
+ return false;
+ }
+ break;
- // ======================================================================
- // <message/> handlers
- // ======================================================================
+ case 'challenge':
+ // continue with authentication...a challenge literally -_-
+ $decoded = base64_decode($xml['challenge'][0]['#']);
+ $decoded = $this->parse_data($decoded);
- /**
- * Message type normal
- */
- function handler_message_normal($packet)
- {
- $from = $packet['message']['@']['from'];
- $this->add_to_log("EVENT: Message (type normal) from $from");
- }
+ if (!isset($decoded['digest-uri']))
+ {
+ $decoded['digest-uri'] = 'xmpp/'. $this->server;
+ }
- /**
- * Message type chat
- */
- function handler_message_chat($packet)
- {
- $from = $packet['message']['@']['from'];
- $this->add_to_log("EVENT: Message (type chat) from $from");
- }
+ // better generate a cnonce, maybe it's needed
+ $str = '';
+ mt_srand((double)microtime()*10000000);
- /**
- * Message type groupchat
- */
- function handler_message_groupchat($packet)
- {
- $from = $packet['message']['@']['from'];
- $this->add_to_log("EVENT: Message (type groupchat) from $from");
- }
+ for ($i = 0; $i < 32; $i++)
+ {
+ $str .= chr(mt_rand(0, 255));
+ }
+ $decoded['cnonce'] = base64_encode($str);
- /**
- * Message type headline
- */
- function handler_message_headline($packet)
- {
- $from = $packet['message']['@']['from'];
- $this->add_to_log("EVENT: Message (type headline) from $from");
- }
+ // second challenge?
+ if (isset($decoded['rspauth']))
+ {
+ $this->send("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
+ }
+ else
+ {
+ $response = array(
+ 'username' => $this->username,
+ 'response' => $this->encrypt_password(array_merge($decoded, array('nc' => '00000001'))),
+ 'charset' => 'utf-8',
+ 'nc' => '00000001',
+ );
+
+ foreach (array('nonce', 'qop', 'digest-uri', 'realm', 'cnonce') as $key)
+ {
+ if (isset($decoded[$key]))
+ {
+ $response[$key] = $decoded[$key];
+ }
+ }
- /**
- * Message type error
- */
- function handler_message_error($packet)
- {
- $from = $packet['message']['@']['from'];
- $this->add_to_log("EVENT: Message (type error) from $from");
- }
+ $this->send("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" . base64_encode($this->implode_data($response)) . '</response>');
+ }
- // ======================================================================
- // <iq/> handlers
- // ======================================================================
+ return $this->response($this->listen());
+ break;
- /**
- * application version updates
- */
- function handler_iq_jabber_iq_autoupdate($packet)
- {
- $from = $this->get_info_from_iq_from($packet);
- $id = $this->get_info_from_iq_id($packet);
+ case 'failure':
+ $this->add_to_log('Error: Server sent "failure".');
+ $this->disconnect();
+ return false;
+ break;
- $this->send_error($from, $id, 501);
- $this->add_to_log("EVENT: jabber:iq:autoupdate from $from");
- }
+ case 'proceed':
+ // continue switching to TLS
+ $meta = stream_get_meta_data($this->connection);
+ socket_set_blocking($this->connection, 1);
- /**
- * interactive server component properties
- */
- function handler_iq_jabber_iq_agent($packet)
- {
- $from = $this->get_info_from_iq_from($packet);
- $id = $this->get_info_from_iq_id($packet);
+ if (!stream_socket_enable_crypto($this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT))
+ {
+ $this->add_to_log('Error: TLS mode change failed.');
+ return false;
+ }
- $this->send_error($from, $id, 501);
- $this->add_to_log("EVENT: jabber:iq:agent from $from");
- }
+ socket_set_blocking($this->connection, $meta['blocked']);
+ $this->session['tls'] = true;
- /**
- * method to query interactive server components
- */
- function handler_iq_jabber_iq_agents($packet)
- {
- $from = $this->get_info_from_iq_from($packet);
- $id = $this->get_info_from_iq_id($packet);
+ // new stream
+ $this->send("<?xml version='1.0' encoding='UTF-8' ?" . ">\n");
+ $this->send("<stream:stream to='{$this->server}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n");
- $this->send_error($from, $id, 501);
- $this->add_to_log("EVENT: jabber:iq:agents from $from");
- }
+ return $this->response($this->listen());
+ break;
- /**
- * simple client authentication
- */
- function handler_iq_jabber_iq_auth($packet)
- {
- $from = $this->get_info_from_iq_from($packet);
- $id = $this->get_info_from_iq_id($packet);
+ case 'success':
+ // Yay, authentication successful.
+ $this->send("<stream:stream to='{$this->server}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n");
+ $this->session['authenticated'] = true;
- $this->send_error($from, $id, 501);
- $this->add_to_log("EVENT: jabber:iq:auth from $from");
- }
+ // we have to wait for another response
+ return $this->response($this->listen());
+ break;
- /**
- * out of band data
- */
- function handler_iq_jabber_iq_oob($packet)
- {
- $from = $this->get_info_from_iq_from($packet);
- $id = $this->get_info_from_iq_id($packet);
+ case 'iq':
+ // we are not interested in IQs we did not expect
+ if (!isset($xml['iq'][0]['@']['id']))
+ {
+ return false;
+ }
- $this->send_error($from, $id, 501);
- $this->add_to_log("EVENT: jabber:iq:oob from $from");
- }
+ // multiple possibilities here
+ switch ($xml['iq'][0]['@']['id'])
+ {
+ case 'bind_1':
+ $this->session['jid'] = $xml['iq'][0]['#']['bind'][0]['#']['jid'][0]['#'];
+
+ // and (maybe) yet another request to be able to send messages *finally*
+ if ($this->session['sess_required'])
+ {
+ $this->send("<iq to='{$this->server}' type='set' id='sess_1'>
+ <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
+ </iq>");
+ return $this->response($this->listen());
+ }
+
+ return true;
+ break;
+
+ case 'sess_1':
+ return true;
+ break;
+
+ case 'reg_1':
+ // more than instructions, username and password?
+ if (sizeof($xml['iq'][0]['#']['query'][0]['#']) > 3)
+ {
+ $this->add_to_log('Server requires too much data for registration.');
+ return false;
+ }
+
+ $this->send("<iq type='set' id='reg_2'>
+ <query xmlns='jabber:iq:register'>
+ <username>" . utf8_htmlspecialchars($this->username) . "</username>
+ <password>" . utf8_htmlspecialchars($this->password) . "</password>
+ </query>
+ </iq>");
+ return $this->response($this->listen());
+ break;
+
+ case 'reg_2':
+ // registration end
+ if (isset($xml['iq'][0]['#']['error']))
+ {
+ $this->add_to_log('Warning: Registration failed.');
+ return false;
+ }
+ return true;
+ break;
+
+ case 'unreg_1':
+ return true;
+ break;
+
+ default:
+ $this->add_to_log('Notice: Received unexpected IQ.');
+ return false;
+ break;
+ }
+ break;
- /**
- * method to store private data on the server
- */
- function handler_iq_jabber_iq_private($packet)
- {
- $from = $this->get_info_from_iq_from($packet);
- $id = $this->get_info_from_iq_id($packet);
+ case 'message':
+ // we are only interested in content...
+ if (!isset($xml['message'][0]['#']['body']))
+ {
+ return false;
+ }
- $this->send_error($from, $id, 501);
- $this->add_to_log("EVENT: jabber:iq:private from $from");
- }
+ $message['body'] = $xml['message'][0]['#']['body'][0]['#'];
+ $message['from'] = $xml['message'][0]['@']['from'];
- /**
- * method for interactive registration
- */
- function handler_iq_jabber_iq_register($packet)
- {
- $from = $this->get_info_from_iq_from($packet);
- $id = $this->get_info_from_iq_id($packet);
+ if (isset($xml['message'][0]['#']['subject']))
+ {
+ $message['subject'] = $xml['message'][0]['#']['subject'][0]['#'];
+ }
+ $this->session['messages'][] = $message;
+ break;
- $this->send_error($from, $id, 501);
- $this->add_to_log("EVENT: jabber:iq:register from $from");
+ default:
+ // hm...don't know this response
+ $this->add_to_log('Notice: Unknown server response (' . key($xml) . ')');
+ return false;
+ break;
+ }
}
- /**
- * client roster management
- */
- function handler_iq_jabber_iq_roster($packet)
+ function send_message($to, $text, $subject = '', $type = 'normal')
{
- $from = $this->get_info_from_iq_from($packet);
- $id = $this->get_info_from_iq_id($packet);
-
- $this->send_error($from, $id, 501);
- $this->add_to_log("EVENT: jabber:iq:roster from $from");
- }
+ if (!isset($this->session['jid']))
+ {
+ return false;
+ }
- /**
- * method for searching a user database
- */
- function handler_iq_jabber_iq_search($packet)
- {
- $from = $this->get_info_from_iq_from($packet);
- $id = $this->get_info_from_iq_id($packet);
+ if (!in_array($type, array('chat', 'normal', 'error', 'groupchat', 'headline')))
+ {
+ $type = 'normal';
+ }
- $this->send_error($from, $id, 501);
- $this->add_to_log("EVENT: jabber:iq:search from $from");
+ return $this->send("<message from='" . utf8_htmlspecialchars($this->session['jid']) . "' to='" . utf8_htmlspecialchars($to) . "' type='$type' id='" . uniqid('msg') . "'>
+ <subject>" . utf8_htmlspecialchars($subject) . "</subject>
+ <body>" . utf8_htmlspecialchars($text) . "</body>
+ </message>"
+ );
}
/**
- * method for requesting the current time
+ * Encrypts a password as in RFC 2831
+ * @param array $data Needs data from the client-server connection
+ * @access public
+ * @return string
*/
- function handler_iq_jabber_iq_time($packet)
+ function encrypt_password($data)
{
- if ($this->keep_alive_id == $this->get_info_from_iq_id($packet))
+ // let's me think about <challenge> again...
+ foreach (array('realm', 'cnonce', 'digest-uri') as $key)
{
- $this->returned_keep_alive = true;
- $this->connected = true;
-
- $this->add_to_log('EVENT: Keep-Alive returned, connection alive.');
+ if (!isset($data[$key]))
+ {
+ $data[$key] = '';
+ }
}
- $type = $this->get_info_from_iq_type($packet);
- $from = $this->get_info_from_iq_from($packet);
- $id = $this->get_info_from_iq_id($packet);
- $id = ($id != '') ? $id : 'time_' . time();
+ $pack = md5($this->username . ':' . $data['realm'] . ':' . $this->password);
- if ($type == 'get')
+ if (isset($data['authzid']))
{
- $payload = '<utc>' . gmdate("Ydm\TH:i:s") . '</utc><tz>' . date('T') . '</tz><display>' . date("Y/d/m h:i:s A") . '</display>';
- $this->send_iq($from, 'result', $id, 'jabber:iq:time', $payload);
+ $a1 = pack('H32', $pack) . sprintf(':%s:%s:%s', $data['nonce'], $data['cnonce'], $data['authzid']);
+ }
+ else
+ {
+ $a1 = pack('H32', $pack) . sprintf(':%s:%s', $data['nonce'], $data['cnonce']);
}
- $this->add_to_log("EVENT: jabber:iq:time (type $type) from $from");
- }
+ // should be: qop = auth
+ $a2 = 'AUTHENTICATE:'. $data['digest-uri'];
- /**
- */
- function handler_iq_error($packet)
- {
- // We'll do something with these later. This is a placeholder so that errors don't bounce back and forth.
+ return md5(sprintf('%s:%s:%s:%s:%s:%s', md5($a1), $data['nonce'], $data['nc'], $data['cnonce'], $data['qop'], md5($a2)));
}
/**
- * method for requesting version
+ * parse_data like a="b",c="d",...
+ * @param string $data
+ * @access public
+ * @return array a => b ...
*/
- function handler_iq_jabber_iq_version($packet)
+ function parse_data($data)
{
- $type = $this->get_info_from_iq_type($packet);
- $from = $this->get_info_from_iq_from($packet);
- $id = $this->get_info_from_iq_id($packet);
- $id = ($id != '') ? $id : 'version_' . time();
+ // super basic, but should suffice
+ $data = explode(',', $data);
+ $pairs = array();
- if ($type == 'get')
+ foreach ($data as $pair)
{
- $payload = "<name>{$this->iq_version_name}</name>
- <os>{$this->iq_version_os}</os>
- <version>{$this->iq_version_version}</version>";
-
- //$this->SendIq($from, 'result', $id, "jabber:iq:version", $payload);
+ $dd = strpos($pair, '=');
+ if ($dd)
+ {
+ $pairs[substr($pair, 0, $dd)] = trim(substr($pair, $dd + 1), '"');
+ }
}
-
- $this->add_to_log("EVENT: jabber:iq:version (type $type) from $from -- DISABLED");
+ return $pairs;
}
- // ======================================================================
- // Generic handlers
- // ======================================================================
-
/**
- * Generic handler for unsupported requests
+ * opposite of jabber::parse_data()
+ * @param array $data
+ * @access public
+ * @return string
*/
- function handler_not_implemented($packet)
+ function implode_data($data)
{
- $packet_type = $this->_get_packet_type($packet);
- $from = call_user_func(array(&$this, 'get_info_from_' . strtolower($packet_type) . '_from'), $packet);
- $id = call_user_func(array(&$this, 'get_info_from_' . strtolower($packet_type) . '_id'), $packet);
-
- $this->send_error($from, $id, 501);
- $this->add_to_log("EVENT: Unrecognized <$packet_type/> from $from");
+ $return = array();
+ foreach ($data as $key => $value)
+ {
+ $return[] = $key . '="' . $value . '"';
+ }
+ return implode(',', $return);
}
- // ======================================================================
- // Third party code
- // m@d pr0ps to the coders ;)
- // ======================================================================
-
/**
* xmlize()
* @author Hans Anderson
@@ -1334,6 +724,12 @@ class jabber
{
$data = trim($data);
+ if (substr($data, 0, 5) != '<?xml')
+ {
+ // mod
+ $data = '<root>'. $data . '</root>';
+ }
+
$vals = $index = $array = array();
$parser = xml_parser_create($encoding);
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
@@ -1344,8 +740,13 @@ class jabber
$i = 0;
$tagname = $vals[$i]['tag'];
- $array[$tagname]['@'] = (isset($vals[$i]['attributes'])) ? $vals[$i]['attributes'] : array();
- $array[$tagname]['#'] = $this->_xml_depth($vals, $i);
+ $array[$tagname][0]['@'] = (isset($vals[$i]['attributes'])) ? $vals[$i]['attributes'] : array();
+ $array[$tagname][0]['#'] = $this->_xml_depth($vals, $i);
+
+ if (substr($data, 0, 5) != '<?xml')
+ {
+ $array = $array['root'][0]['#'];
+ }
return $array;
}
@@ -1407,106 +808,6 @@ class jabber
return $children;
}
-
- /**
- * TraverseXMLize()
- * @author acebone@f2s.com
- * @copyright acebone@f2s.com, a HUGE help!
- */
- function traverse_xmlize($array, $arr_name = 'array', $level = 0)
- {
- if ($level == 0)
- {
- echo '<pre>';
- }
-
- foreach ($array as $key => $val)
- {
- if (is_array($val))
- {
- $this->traverse_xmlize($val, $arr_name . '[' . $key . ']', $level + 1);
- }
- else
- {
- $GLOBALS['traverse_array'][] = '$' . $arr_name . '[' . $key . '] = "' . $val . "\"\n";
- }
- }
-
- if ($level == 0)
- {
- echo '</pre>';
- }
-
- return 1;
- }
-}
-
-/**
-* Jabber Connector
-* @package phpBB3
-*/
-class cjp_standard_connector
-{
- var $active_socket;
-
- /**
- * Open socket
- */
- function open_socket($server, $port)
- {
- if (function_exists('dns_get_record'))
- {
- $record = dns_get_record("_xmpp-client._tcp.$server", DNS_SRV);
-
- if (!empty($record))
- {
- $server = $record[0]['target'];
- $port = $record[0]['port'];
- }
- }
-
- $errno = 0;
- $errstr = '';
-
- if ($this->active_socket = @fsockopen($server, $port, $errno, $errstr, 5))
- {
- @socket_set_blocking($this->active_socket, 0);
- @socket_set_timeout($this->active_socket, 31536000);
-
- return true;
- }
- else
- {
- return false;
- }
- }
-
- /**
- * Close socket
- */
- function close_socket()
- {
- return @fclose($this->active_socket);
- }
-
- /**
- * Write to socket
- */
- function write_to_socket($data)
- {
- return @fwrite($this->active_socket, $data);
- }
-
- /**
- * Read from socket
- */
- function read_from_socket($chunksize)
- {
- $buffer = @fread($this->active_socket, $chunksize);
- $buffer = (STRIP) ? stripslashes($buffer) : $buffer;
-
- return $buffer;
- }
}
?> \ No newline at end of file