<?php
/**
* Jaxl (Jabber XMPP Library)
*
* Copyright (c) 2009-2010, Abhinav Singh <hide@address.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Abhinav Singh nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package jaxl
* @subpackage core
* @author Abhinav Singh <hide@address.com>
* @copyright Abhinav Singh
* @link http://code.google.com/p/jaxl
*/
declare(ticks=1);
// Set JAXL_BASE_PATH if not already defined by application code
if(!@constant('JAXL_BASE_PATH'))
define('JAXL_BASE_PATH', dirname(dirname(__FILE__)));
/**
* Autoload method for Jaxl library and it's applications
*
* @param string|array $classNames core class name required in PHP environment
* @param object $jaxl Jaxl object which require these classes. Optional but always required while including implemented XEP's in PHP environment
*/
function jaxl_require($classNames, $jaxl=false) {
static $included = array();
$tagMap = array(
// core classes
'JAXLCron' => '/core/jaxl.cron.php',
'JAXLHTTPd' => '/core/jaxl.httpd.php',
'JAXLog' => '/core/jaxl.logger.php',
'JAXLXml' => '/core/jaxl.parser.php',
'JAXLPlugin' => '/core/jaxl.plugin.php',
'JAXLUtil' => '/core/jaxl.util.php',
'JAXLException' => '/core/jaxl.exception.php',
'XML' => '/core/jaxl.xml.php',
'S5B' => '/core/jaxl.s5b.php',
// xmpp classes
'XMPP' => '/xmpp/xmpp.class.php',
'XMPPGet' => '/xmpp/xmpp.get.php',
'XMPPSend' => '/xmpp/xmpp.send.php',
'XMPPAuth' => '/xmpp/xmpp.auth.php'
);
if(!is_array($classNames)) $classNames = array('0'=>$classNames);
foreach($classNames as $key => $className) {
$xep = substr($className, 4, 4);
if(substr($className, 0, 4) == 'JAXL'
&& is_numeric($xep)
) { // is XEP
if(!isset($included[$className])) {
require_once JAXL_BASE_PATH.'/xep/jaxl.'.$xep.'.php';
$included[$className] = true;
}
call_user_func(array('JAXL'.$xep, 'init'), $jaxl);
} // is Core file
else if(isset($tagMap[$className])) {
require_once JAXL_BASE_PATH.$tagMap[$className];
$included[$className] = true;
}
}
return;
}
// cnt of connected instances
global $jaxl_instance_cnt;
$jaxl_instance_cnt = 0;
// Include core classes and xmpp base
jaxl_require(array(
'JAXLog',
'JAXLUtil',
'JAXLPlugin',
'JAXLCron',
'JAXLException',
'XML',
'XMPP',
));
/**
* Jaxl class extending base XMPP class
*
* Jaxl library core is like any of your desktop Instant Messaging (IM) clients.
* Include Jaxl core in you application and start connecting and managing multiple XMPP accounts
* Packaged library is custom configured for running <b>single instance</b> Jaxl applications
*
* For connecting <b>multiple instance</b> XMPP accounts inside your application see documentation for
* <b>addCore()</b> method
*/
class JAXL extends XMPP {
/**
* Client version of the connected Jaxl instance
*/
const version = '2.1.2';
/**
* Client name of the connected Jaxl instance
*/
const name = 'Jaxl :: Jabber XMPP Client Library';
/**
* Custom config passed to Jaxl constructor
*
* @var array
*/
var $config = array();
/**
* Username of connecting Jaxl instance
*
* @var string
*/
var $user = false;
/**
* Password of connecting Jaxl instance
*
* @var string
*/
var $pass = false;
/**
* Hostname for the connecting Jaxl instance
*
* @var string
*/
var $host = 'localhost';
/**
* Port for the connecting Jaxl instance
*
* @var integer
*/
var $port = 5222;
/**
* Full JID of the connected Jaxl instance
*
* @var string
*/
var $jid = false;
/**
* Bare JID of the connected Jaxl instance
*
* @var string
*/
var $bareJid = false;
/**
* Domain for the connecting Jaxl instance
*
* @var string
*/
var $domain = 'localhost';
/**
* Resource for the connecting Jaxl instance
*
* @var string
*/
var $resource = false;
/**
* Local cache of roster list and related info maintained by Jaxl instance
*/
var $roster = array();
/**
* Default status of connected Jaxl instance
*/
var $status = 'Online using Jaxl library http://code.google.com/p/jaxl';
/**
* Jaxl will track presence stanza's and update local $roster cache
*/
var $trackPresence = true;
/**
* Configure Jaxl instance to auto-accept subscription requests
*/
var $autoSubscribe = false;
/**
* Log Level of the connected Jaxl instance
*
* @var integer
*/
var $logLevel = 1;
/**
* Enable/Disable automatic log rotation for this Jaxl instance
*
* @var bool|integer
*/
var $logRotate = false;
/**
* Absolute path of log file for this Jaxl instance
*/
var $logPath = '/var/log/jaxl.log';
/**
* Absolute path of pid file for this Jaxl instance
*/
var $pidPath = '/var/run/jaxl.pid';
/**
* Enable/Disable shutdown callback on SIGH terms
*
* @var bool
*/
var $sigh = true;
/**
* Process Id of the connected Jaxl instance
*
* @var bool|integer
*/
var $pid = false;
/**
* Mode of the connected Jaxl instance (cgi or cli)
*
* @var bool|string
*/
var $mode = false;
/**
* Jabber auth mechanism performed by this Jaxl instance
*
* @var false|string
*/
var $authType = false;
/**
* Jaxl instance dumps usage statistics periodically. Disabled if set to "false"
*
* @var bool|integer
*/
var $dumpStat = 300;
/**
* List of XMPP feature supported by this Jaxl instance
*
* @var array
*/
var $features = array();
/**
* XMPP entity category for the connected Jaxl instance
*
* @var string
*/
var $category = 'client';
/**
* XMPP entity type for the connected Jaxl instance
*
* @var string
*/
var $type = 'bot';
/**
* Default language of the connected Jaxl instance
*/
var $lang = 'en';
/**
* Location to system temporary folder
*/
var $tmpPath = null;
/**
* IP address of the host Jaxl instance is running upon.
* To be used by STUN client implementations.
*/
var $ip = null;
/**
* PHP OpenSSL module info
*/
var $openSSL = false;
var $instances = false;
/**
* @return string $name Returns name of this Jaxl client
*/
function getName() {
return JAXL::name;
}
/**
* @return float $version Returns version of this Jaxl client
*/
function getVersion() {
return JAXL::version;
}
/**
* Discover items
*/
function discoItems($jid, $callback, $node=false) {
$this->JAXL0030('discoItems', $jid, $this->jid, $callback, $node);
}
/**
* Discover info
*/
function discoInfo($jid, $callback, $node=false) {
$this->JAXL0030('discoInfo', $jid, $this->jid, $callback, $node);
}
/**
* Shutdown Jaxl instance cleanly
*
* shutdown method is auto invoked when Jaxl instance receives a SIGH term.
* Before cleanly shutting down this method callbacks registered using <b>jaxl_pre_shutdown</b> hook.
*
* @param mixed $signal This is passed as paramater to callbacks registered using <b>jaxl_pre_shutdown</b> hook
*/
function shutdown($signal=false) {
$this->log("[[JAXL]] Shutting down ...");
$this->executePlugin('jaxl_pre_shutdown', $signal);
if($this->stream) $this->endStream();
$this->stream = false;
}
/**
* Perform authentication for connecting Jaxl instance
*
* @param string $type Authentication mechanism to proceed with. Supported auth types are:
* - DIGEST-MD5
* - PLAIN
* - X-FACEBOOK-PLATFORM
* - ANONYMOUS
*/
function auth($type) {
$this->authType = $type;
return XMPPSend::startAuth($this);
}
/**
* Set status of the connected Jaxl instance
*
* @param bool|string $status
* @param bool|string $show
* @param bool|integer $priority
* @param bool $caps
*/
function setStatus($status=false, $show=false, $priority=false, $caps=false, $vcard=false) {
$child = array();
$child['status'] = ($status === false ? $this->status : $status);
$child['show'] = ($show === false ? 'chat' : $show);
$child['priority'] = ($priority === false ? 1 : $priority);
if($caps) $child['payload'] = $this->JAXL0115('getCaps', $this->features);
if($vcard) $child['payload'] .= $this->JAXL0153('getUpdateData', false);
return XMPPSend::presence($this, false, false, $child, false);
}
/**
* Send authorization request to $toJid
*
* @param string $toJid JID whom Jaxl instance wants to send authorization request
*/
function subscribe($toJid) {
return XMPPSend::presence($this, $toJid, false, false, 'subscribe');
}
/**
* Accept authorization request from $toJid
*
* @param string $toJid JID who's authorization request Jaxl instance wants to accept
*/
function subscribed($toJid) {
return XMPPSend::presence($this, $toJid, false, false, 'subscribed');
}
/**
* Send cancel authorization request to $toJid
*
* @param string $toJid JID whom Jaxl instance wants to send cancel authorization request
*/
function unsubscribe($toJid) {
return XMPPSend::presence($this, $toJid, false, false, 'unsubscribe');
}
/**
* Accept cancel authorization request from $toJid
*
* @param string $toJid JID who's cancel authorization request Jaxl instance wants to accept
*/
function unsubscribed($toJid) {
return XMPPSend::presence($this, $toJid, false, false, 'unsubscribed');
}
/**
* Retrieve connected Jaxl instance roster list from the server
*
* @param mixed $callback Method to be callback'd when roster list is received from the server
*/
function getRosterList($callback=false) {
$payload = '<query xmlns="jabber:iq:roster"/>';
if($callback === false) $callback = array($this, '_handleRosterList');
return XMPPSend::iq($this, "get", $payload, false, $this->jid, $callback);
}
/**
* Add a new jabber account in connected Jaxl instance roster list
*
* @param string $jid JID to be added in the roster list
* @param string $group Group name
* @param bool|string $name Name of JID to be added
*/
function addRoster($jid, $group, $name=false) {
$payload = '<query xmlns="jabber:iq:roster">';
$payload .= '<item jid="'.$jid.'"';
if($name) $payload .= ' name="'.$name.'"';
$payload .= '>';
$payload .= '<group>'.$group.'</group>';
$payload .= '</item>';
$payload .= '</query>';
return XMPPSend::iq($this, "set", $payload, false, $this->jid, false);
}
/**
* Update subscription of a jabber account in roster list
*
* @param string $jid JID to be updated inside roster list
* @param string $group Updated group name
* @param bool|string $subscription Updated subscription type
*/
function updateRoster($jid, $group, $name=false, $subscription=false) {
$payload = '<query xmlns="jabber:iq:roster">';
$payload .= '<item jid="'.$jid.'"';
if($name) $payload .= ' name="'.$name.'"';
if($subscription) $payload .= ' subscription="'.$subscription.'"';
$payload .= '>';
$payload .= '<group>'.$group.'</group>';
$payload .= '</item>';
$payload .= '</query>';
return XMPPSend::iq($this, "set", $payload, false, $this->jid, false);
}
/**
* Delete a jabber account from roster list
*
* @param string $jid JID to be removed from the roster list
*/
function deleteRoster($jid) {
$payload = '<query xmlns="jabber:iq:roster">';
$payload .= '<item jid="'.$jid.'" subscription="remove">';
$payload .= '</item>';
$payload .= '</query>';
return XMPPSend::iq($this, "set", $payload, false, $this->jid, false);
}
/**
* Send an XMPP message
*
* @param string $to JID to whom message is sent
* @param string $message Message to be sent
* @param string $from (Optional) JID from whom this message should be sent
* @param string $type (Optional) Type of message stanza to be delivered
* @param integer $id (Optional) Add an id attribute to transmitted stanza (omitted if not provided)
*/
function sendMessage($to, $message, $from=false, $type='chat', $id=false) {
$child = array();
$child['body'] = $message;
return XMPPSend::message($this, $to, $from, $child, $type, $id);
}
/**
* Send multiple XMPP messages in one go
*
* @param array $to array of JID's to whom this presence stanza should be send
* @param array $from (Optional) array of JID from whom this presence stanza should originate
* @param array $child (Optional) array of arrays specifying child objects of the stanza
* @param array $type (Optional) array of type of presence stanza to send
* @param integer $id (Optional) Add an id attribute to transmitted stanza (omitted if not provided)
*/
function sendMessages($to, $from=false, $child=false, $type='chat', $id=false) {
return XMPPSend::message($this, $to, $from, $child, $type);
}
/**
* Send an XMPP presence stanza
*
* @param string $to (Optional) JID to whom this presence stanza should be send
* @param string $from (Optional) JID from whom this presence stanza should originate
* @param array $child (Optional) array specifying child objects of the stanza
* @param string $type (Optional) Type of presence stanza to send
* @param integer $id (Optional) Add an id attribute to transmitted stanza (omitted if not provided)
*/
function sendPresence($to=false, $from=false, $child=false, $type=false, $id=false) {
return XMPPSend::presence($this, $to, $from, $child, $type, $id);
}
/**
* Send an XMPP iq stanza
*
* @param string $type Type of iq stanza to send
* @param string $payload (Optional) XML string to be transmitted
* @param string $to (Optional) JID to whom this iq stanza should be send
* @param string $from (Optional) JID from whom this presence stanza should originate
* @param string|array $callback (Optional) Callback method which will handle "result" type stanza rcved
* @param integer $id (Optional) Add an id attribute to transmitted stanza (auto-generated if not provided)
*/
function sendIQ($type, $payload=false, $to=false, $from=false, $callback=false, $id=false) {
return XMPPSend::iq($this, $type, $payload, $to, $from, $callback, $id);
}
/**
* Logs library core and application debug data into the log file
*
* @param string $log Datum to be logged
* @param integer $level Log level for passed datum
*/
function log($log, $level=1) {
JAXLog::log($log, $level, $this);
}
/**
* Instead of using jaxl_require method applications can use $jaxl->requires to include XEP's in PHP environment
*
* @param string $class Class name of the XEP to be included e.g. JAXL0045 for including XEP-0045 a.k.a. Multi-user chat
*/
function requires($class) {
jaxl_require($class, $this);
}
/**
* Use this method instead of JAXLPlugin::add to register a callback for connected instance only
*/
function addPlugin($hook, $callback, $priority=10) {
JAXLPlugin::add($hook, $callback, $priority, $this->uid);
}
/**
* Use this method instead of JAXLPlugin::remove to remove a callback for connected instance only
*/
function removePlugin($hook, $callback, $priority=10) {
JAXLPlugin::remove($hook, $callback, $priority, $this->uid);
}
/**
* Use this method instead of JAXLPlugin::remove to remove a callback for connected instance only
*/
function executePlugin($hook, $payload) {
return JAXLPlugin::execute($hook, $payload, $this);
}
/**
* Add another jaxl instance in a running core
*/
function addCore($jaxl) {
$jaxl->addPlugin('jaxl_post_connect', array($jaxl, 'startStream'));
$jaxl->connect();
$this->instances['xmpp'][] = $jaxl;
}
/**
* Starts Jaxl Core
*
* This method should be called after Jaxl initialization and hook registration inside your application code
* Optionally, you can pass 'jaxl_post_connect' and 'jaxl_get_auth_mech' response type directly to this method
* In that case application code SHOULD NOT register callbacks to above mentioned hooks
*
* @param string $arg[0] Optionally application code can pre-choose what Jaxl core should do after establishing socket connection.
* Following are the valid options:
* a) startStream
* b) startComponent
* c) startBosh
*/
function startCore(/* $mode, $param1, $param2, ... */) {
$argv = func_get_args();
$mode = $argv[0];
if($mode) {
switch($mode) {
case 'stream':
$this->addPlugin('jaxl_post_connect', array($this, 'startStream'));
break;
case 'component':
$this->addPlugin('jaxl_post_connect', array($this, 'startComponent'));
break;
case 'bosh':
$this->startBosh();
break;
default:
break;
}
}
if($this->mode == 'cli') {
try {
if($this->connect()) {
while($this->stream) {
$this->getXML();
}
}
}
catch(JAXLException $e) {
die($e->getMessage());
}
/* Exit Jaxl after end of loop */
exit;
}
}
/**
* Start instance in bosh mode
*/
function startBosh() {
$this->JAXL0206('startStream');
}
/**
* Start instance in component mode
*/
function startComponent($payload, $jaxl) {
$this->JAXL0114('startStream', $payload);
}
/**
* Jaxl core constructor
*
* Jaxl instance configures itself using the constants inside your application jaxl.ini.
* However, passed array of configuration options overrides the value inside jaxl.ini.
* If no configuration are found or passed for a particular value,
* Jaxl falls back to default config options.
*
* @param $config Array of configuration options
* @todo Use DNS SRV lookup to set $jaxl->host from provided domain info
*/
function __construct($config=array()) {
global $jaxl_instance_cnt;
parent::__construct($config);
$this->uid = ++$jaxl_instance_cnt;
$this->ip = gethostbyname(php_uname('n'));
$this->config = $config;
$this->pid = getmypid();
/* Mandatory params to be supplied either by jaxl.ini constants or constructor $config array */
$this->user = $this->getConfigByPriority(@$config['user'], "JAXL_USER_NAME", $this->user);
$this->pass = $this->getConfigByPriority(@$config['pass'], "JAXL_USER_PASS", $this->pass);
$this->domain = $this->getConfigByPriority(@$config['domain'], "JAXL_HOST_DOMAIN", $this->domain);
$this->bareJid = $this->user."@".$this->domain;
/* Optional params if not configured using jaxl.ini or $config take default values */
$this->host = $this->getConfigByPriority(@$config['host'], "JAXL_HOST_NAME", $this->domain);
$this->port = $this->getConfigByPriority(@$config['port'], "JAXL_HOST_PORT", $this->port);
$this->resource = $this->getConfigByPriority(@$config['resource'], "JAXL_USER_RESC", "jaxl.".$this->uid.".".$this->clocked);
$this->status = $this->getConfigByPriority(@$config['status'], "JAXL_USER_STATUS", $this->status);
$this->logLevel = $this->getConfigByPriority(@$config['logLevel'], "JAXL_LOG_LEVEL", $this->logLevel);
$this->logRotate = $this->getConfigByPriority(@$config['logRotate'], "JAXL_LOG_ROTATE", $this->logRotate);
$this->logPath = $this->getConfigByPriority(@$config['logPath'], "JAXL_LOG_PATH", $this->logPath);
if(!file_exists($this->logPath) && !touch($this->logPath)) throw new JAXLException("Log file ".$this->logPath." doesn't exists");
$this->pidPath = $this->getConfigByPriority(@$config['pidPath'], "JAXL_PID_PATH", $this->pidPath);
$this->mode = $this->getConfigByPriority(@$config['mode'], "JAXL_MODE", (PHP_SAPI == "cli") ? PHP_SAPI : "cgi");
if($this->mode == "cli" && !file_exists($this->pidPath) && !touch($this->pidPath)) throw new JAXLException("Pid file ".$this->pidPath." doesn't exists");
/* Resolve temporary folder path */
if(function_exists('sys_get_temp_dir')) $this->tmpPath = sys_get_temp_dir();
$this->tmpPath = $this->getConfigByPriority(@$config['tmpPath'], "JAXL_TMP_PATH", $this->tmpPath);
if($this->tmpPath && !file_exists($this->tmpPath)) throw new JAXLException("Tmp directory ".$this->tmpPath." doesn't exists");
/* Handle pre-choosen auth type mechanism */
$this->authType = $this->getConfigByPriority(@$config['authType'], "JAXL_AUTH_TYPE", $this->authType);
if($this->authType) $this->addPlugin('jaxl_get_auth_mech', array($this, 'doAuth'));
/* Presence handling */
$this->trackPresence = isset($config['trackPresence']) ? $config['trackPresence'] : true;
$this->autoSubscribe = isset($config['autoSubscribe']) ? $config['autoSubscribe'] : false;
$this->addPlugin('jaxl_get_presence', array($this, '_handlePresence'), 0);
/* Optional params which can be configured only via constructor $config */
$this->sigh = isset($config['sigh']) ? $config['sigh'] : true;
$this->dumpStat = isset($config['dumpStat']) ? $config['dumpStat'] : 300;
/* Configure instance for platforms */
$this->configure($config);
/* Initialize xml to array class (will deprecate in future) */
$this->xml = new XML();
/* Initialize JAXLCron and register core jobs */
JAXLCron::init($this);
if($this->dumpStat) JAXLCron::add(array($this, 'dumpStat'), $this->dumpStat);
if($this->logRotate) JAXLCron::add(array('JAXLog', 'logRotate'), $this->logRotate);
/* include recommended XEP's for every XMPP entity */
$this->requires(array(
'JAXL0030', // service discovery
'JAXL0128' // entity capabilities
));
/* initialize multi-core instance holder */
if($jaxl_instance_cnt == 1) $this->instances = array('xmpp'=>array());
$this->instances['xmpp'][] = $this;
}
/**
* Return Jaxl instance config param depending upon user choice and default values
*/
function getConfigByPriority($config, $constant, $default) {
return ($config === null) ? (@constant($constant) === null ? $default : constant($constant)) : $config;
}
/**
* Configures Jaxl instance to run across various platforms (*nix/windows)
*
* Configure method tunes connecting Jaxl instance for
* OS compatibility, SSL support and dependencies over PHP methods
*/
protected function configure() {
// register shutdown function
if(!JAXLUtil::isWin() && JAXLUtil::pcntlEnabled() && $this->sigh) {
pcntl_signal(SIGTERM, array($this, "shutdown"));
pcntl_signal(SIGINT, array($this, "shutdown"));
$this->log("[[JAXL]] Registering shutdown callbacks", 5);
}
else {
$this->log("[[JAXL]] No callbacks for shutdown", 5);
}
// check Jaxl dependency on PHP extension in cli mode
if($this->mode == "cli") {
if(($this->openSSL = JAXLUtil::sslEnabled()))
$this->log("[[JAXL]] OpenSSL extension is loaded.", 5);
else
$this->log("[[JAXL]] OpenSSL extension not loaded.", 5);
if(!function_exists('fsockopen'))
throw new JAXLException("[[JAXL]] Requires fsockopen method");
if(@is_writable($this->pidPath))
file_put_contents($this->pidPath, $this->pid);
}
// check Jaxl dependency on PHP extension in cgi mode
if($this->mode == "cgi") {
if(!function_exists('curl_init'))
throw new JAXLException("[[JAXL]] Requires CURL PHP extension");
if(!function_exists('json_encode'))
throw new JAXLException("[[JAXL]] Requires JSON PHP extension.");
}
}
/**
* Dumps Jaxl instance usage statistics
*
* Jaxl instance periodically calls this methods every JAXL::$dumpStat seconds.
*/
function dumpStat() {
$stat = "[[JAXL]] Memory:".round(memory_get_usage()/pow(1024,2), 2)."Mb";
if(function_exists('memory_get_peak_usage')) $stat .= ", PeakMemory:".round(memory_get_peak_usage()/pow(1024,2), 2)."Mb";
$stat .= ", obuffer: ".strlen($this->obuffer);
$stat .= ", buffer: ".strlen($this->buffer);
$stat .= ", RcvdRate: ".$this->totalRcvdSize/$this->clock."Kb";
$stat .= ", SentRate: ".$this->totalSentSize/$this->clock."Kb";
$this->log($stat, 1);
}
/**
* Magic method for calling XEP's included by the JAXL instance
*
* Application <b>should never</b> call an XEP method directly using <code>JAXL0045::joinRoom()</code>
* instead use <code>$jaxl->JAXL0045('joinRoom', $param1, ..)</code> to call methods provided by included XEP's
*
* @param string $xep XMPP extension (XEP) class name e.g. JAXL0045 for XEP-0045 a.k.a. Multi-User chat extension
* @param array $param Array of parameters where $param[0]='methodName' is the method called inside JAXL0045 class
*
* @return mixed $return Return value of called XEP method
*/
function __call($xep, $param) {
$method = array_shift($param);
array_unshift($param, $this);
if(substr($xep, 0, 4) == 'JAXL') {
$xep = substr($xep, 4, 4);
if(is_numeric($xep)
&& class_exists('JAXL'.$xep)
) {
$this->log("[[JAXL]] Calling JAXL$xep method ".$method, 5);
return call_user_func_array(array('JAXL'.$xep, $method), $param);
}
else { $this->log("[[JAXL]] JAXL$xep Doesn't exists in the environment"); }
}
else {
$this->log("[[JAXL]] Call to an unidentified XEP");
}
}
/**
* Perform pre-choosen auth type for the Jaxl instance
*/
function doAuth($mechanism) {
return XMPPSend::startAuth($this);
}
/**
* Adds a node (if doesn't exists) for $jid inside local $roster cache
*/
function _addRosterNode($jid, $inRoster=true) {
if(!isset($this->roster[$jid]))
$this->roster[$jid] = array(
'groups'=>array(),
'name'=>'',
'subscription'=>'',
'presence'=>array(),
'inRoster'=>$inRoster
);
return;
}
/**
* Core method that accepts retrieved roster list and manage local cache
*/
function _handleRosterList($payload, $jaxl) {
if(@is_array($payload['queryItemJid'])) {
foreach($payload['queryItemJid'] as $key=>$jid) {
$this->_addRosterNode($jid);
$this->roster[$jid]['groups'] = @$payload['queryItemGrp'][$key];
$this->roster[$jid]['name'] = @$payload['queryItemName'][$key];
$this->roster[$jid]['subscription'] = @$payload['queryItemSub'][$key];
}
}
else {
$jid = @$payload['queryItemJid'];
$this->_addRosterNode($jid);
$this->roster[$jid]['groups'] = @$payload['queryItemGrp'];
$this->roster[$jid]['name'] = @$payload['queryItemName'];
$this->roster[$jid]['subscription'] = @$payload['queryItemSub'];
}
$this->executePlugin('jaxl_post_roster_update', $payload);
return $payload;
}
/**
* Tracks all incoming presence stanza's
*/
function _handlePresence($payloads, $jaxl) {
foreach($payloads as $payload) {
if($this->trackPresence) {
// update local $roster cache
$jid = JAXLUtil::getBareJid($payload['from']);
$this->_addRosterNode($jid, false);
if(!isset($this->roster[$jid]['presence'][$payload['from']])) $this->roster[$jid]['presence'][$payload['from']] = array();
$this->roster[$jid]['presence'][$payload['from']]['type'] = $payload['type'] == '' ? 'available' : $payload['type'];
$this->roster[$jid]['presence'][$payload['from']]['status'] = $payload['status'];
$this->roster[$jid]['presence'][$payload['from']]['show'] = $payload['show'];
$this->roster[$jid]['presence'][$payload['from']]['priority'] = $payload['priority'];
}
if($payload['type'] == 'subscribe'
&& $this->autoSubscribe
) {
$this->subscribed($payload['from']);
$this->subscribe($payload['from']);
$this->executePlugin('jaxl_post_subscription_request', $payload);
}
else if($payload['type'] == 'subscribed') {
$this->executePlugin('jaxl_post_subscription_accept', $payload);
}
}
return $payloads;
}
}
?>