/jaxl.php
PHP | 816 lines | 532 code | 118 blank | 166 comment | 110 complexity | 2e317515cab0a33382bce21dd97287c2 MD5 | raw file
Possible License(s): BSD-3-Clause
- <?php
- /**
- * Jaxl (Jabber XMPP Library)
- *
- * Copyright (c) 2009-2012, Abhinav Singh <me@abhinavsingh.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.
- *
- */
- date_default_timezone_set("UTC");
- declare(ticks = 1);
- define('JAXL_CWD', dirname(__FILE__));
- require_once JAXL_CWD.'/core/jaxl_exception.php';
- require_once JAXL_CWD.'/core/jaxl_cli.php';
- require_once JAXL_CWD.'/core/jaxl_loop.php';
- require_once JAXL_CWD.'/xmpp/xmpp_stream.php';
- require_once JAXL_CWD.'/core/jaxl_event.php';
- require_once JAXL_CWD.'/core/jaxl_logger.php';
- require_once JAXL_CWD.'/core/jaxl_socket_server.php';
- /**
- *
- * @author abhinavsingh
- */
- class RosterItem {
-
- public $jid = null;
- public $subscription = null;
- public $groups = array();
- public $resources = array();
- public $vcard = null;
-
- public function __construct($jid, $subscription, $groups) {
- $this->jid = $jid;
- $this->subscription = $subscription;
- $this->groups = $groups;
- }
-
- }
- /**
- * Jaxl class extends base XMPPStream class with following functionalities:
- * 1) Adds an event based wrapper over xmpp stream lifecycle
- * 2) Provides restart strategy and signal handling to ensure connectivity of xmpp stream
- * 3) Roster management as specified in XMPP-IM
- * 4) Management of XEP's inside xmpp stream lifecycle
- * 5) Adds a logging facility
- * 6) Adds a cron job facility in sync with connected xmpp stream timeline
- *
- * @author abhinavsingh
- *
- */
- class JAXL extends XMPPStream {
-
- // lib meta info
- const version = '3.0.0-alpha-1';
- const name = 'JAXL :: Jabber XMPP Library';
-
- // cached init config array
- public $cfg = array();
-
- // event callback engine for xmpp stream lifecycle
- protected $ev = null;
-
- // reference to various xep instance objects
- public $xeps = array();
-
- // local cache of roster list
- public $roster = array();
-
- // whether jaxl must also populate local roster cache with
- // received presence information about the contacts
- public $manage_roster = true;
-
- // what to do with presence sub requests
- // "none" | "accept" | "mutual"
- public $manage_subscribe = "none";
-
- // path variables
- public $log_level = JAXL_INFO;
- public $priv_dir;
- public $tmp_dir;
- public $log_dir;
- public $pid_dir;
- public $sock_dir;
-
- // ipc utils
- private $sock;
- private $cli;
-
- // env
- public $local_ip;
- public $pid;
- public $mode;
-
- // current status message
- public $status;
-
- // identity
- public $features = array();
- public $category = 'client';
- public $type = 'bot';
- public $lang = 'en';
-
- // after cth failed attempt
- // retry connect after k * $retry_interval seconds
- // where k is a random number between 0 and 2^c - 1.
- public $retry = true;
- private $retry_interval = 1;
- private $retry_attempt = 0;
- private $retry_max_interval = 32; // 2^5 seconds (means 5 max tries)
-
- public function __construct($config) {
- // env
- $this->cfg = $config;
- $strict = isset($this->cfg['strict']) ? $this->cfg['strict'] : TRUE;
- if($strict) $this->add_exception_handlers();
- $this->mode = PHP_SAPI;
- $this->local_ip = gethostbyname(php_uname('n'));
- $this->pid = getmypid();
-
- // initialize core modules
- $this->ev = new JAXLEvent();
-
- // jid object
- $jid = @$this->cfg['jid'] ? new XMPPJid($this->cfg['jid']) : null;
-
- // handle signals
- if(extension_loaded('pcntl')) {
- pcntl_signal(SIGHUP, array($this, 'signal_handler'));
- pcntl_signal(SIGINT, array($this, 'signal_handler'));
- pcntl_signal(SIGTERM, array($this, 'signal_handler'));
- }
-
- // create .jaxl directory in JAXL_CWD
- // for our /tmp, /run and /log folders
- // overwrite these using jaxl config array
- $this->priv_dir = @$this->cfg['priv_dir'] ? $this->cfg['priv_dir'] : JAXL_CWD."/.jaxl";
- $this->tmp_dir = $this->priv_dir."/tmp";
- $this->pid_dir = $this->priv_dir."/run";
- $this->log_dir = $this->priv_dir."/log";
- $this->sock_dir = $this->priv_dir."/sock";
- if(!is_dir($this->priv_dir)) mkdir($this->priv_dir);
- if(!is_dir($this->tmp_dir)) mkdir($this->tmp_dir);
- if(!is_dir($this->pid_dir)) mkdir($this->pid_dir);
- if(!is_dir($this->log_dir)) mkdir($this->log_dir);
- if(!is_dir($this->sock_dir)) mkdir($this->sock_dir);
-
- // setup logger
- if(isset($this->cfg['log_path'])) JAXLLogger::$path = $this->cfg['log_path'];
- //else JAXLLogger::$path = $this->log_dir."/jaxl.log";
- if(isset($this->cfg['log_level'])) JAXLLogger::$level = $this->log_level = $this->cfg['log_level'];
- else JAXLLogger::$level = $this->log_level;
-
- // touch pid file
- if($this->mode == "cli") {
- touch($this->get_pid_file_path());
- _info("created pid file ".$this->get_pid_file_path());
- }
-
- // include mandatory xmpp xeps
- // service discovery and entity caps
- // are recommended for every xmpp entity
- $this->require_xep(array('0030', '0115'));
-
- // do dns lookup, update $cfg['host'] and $cfg['port'] if not already specified
- $host = @$this->cfg['host']; $port = @$this->cfg['port'];
- if(!$host && !$port && $jid) {
- // this dns lookup is blocking
- _info("dns srv lookup for ".$jid->domain);
- list($host, $port) = JAXLUtil::get_dns_srv($jid->domain);
- }
- $this->cfg['host'] = $host; $this->cfg['port'] = $port;
-
- // choose appropriate transport
- // if 'bosh_url' cfg is defined include 0206
- if(@$this->cfg['bosh_url']) {
- _debug("including bosh xep");
- $this->require_xep('0206');
- $transport = $this->xeps['0206'];
- }
- else {
- list($host, $port) = JAXLUtil::get_dns_srv($jid->domain);
- $stream_context = @$this->cfg['stream_context'];
- $transport = new JAXLSocketClient($stream_context);
- }
-
- // initialize xmpp stream with configured transport
- parent::__construct(
- $transport,
- $jid,
- @$this->cfg['pass'],
- @$this->cfg['resource'] ? 'jaxl#'.$this->cfg['resource'] : 'jaxl#'.md5(time()),
- @$this->cfg['force_tls']
- );
- }
-
- public function __destruct() {
- // delete pid file
- _info("cleaning up pid and unix sock files");
- @unlink($this->get_pid_file_path());
- @unlink($this->get_sock_file_path());
-
- parent::__destruct();
- }
-
- public function add_exception_handlers() {
- _info("strict mode enabled, adding exception handlers. Set 'strict'=>TRUE inside JAXL config to disable this");
- set_error_handler(array('JAXLException', 'error_handler'));
- set_exception_handler(array('JAXLException', 'exception_handler'));
- register_shutdown_function(array('JAXLException', 'shutdown_handler'));
- }
-
- public function get_pid_file_path() {
- return $this->pid_dir."/jaxl_".$this->pid.".pid";
- }
-
- public function get_sock_file_path() {
- return $this->sock_dir."/jaxl_".$this->pid.".sock";
- }
-
- public function require_xep($xeps) {
- if(!is_array($xeps))
- $xeps = array($xeps);
-
- foreach($xeps as $xep) {
- $filename = 'xep_'.$xep.'.php';
- $classname = 'XEP_'.$xep;
-
- // include xep
- require_once JAXL_CWD.'/xep/'.$filename;
- $this->xeps[$xep] = new $classname($this);
-
- // add necessary requested callback on events
- foreach($this->xeps[$xep]->init() as $ev=>$cb) {
- $this->add_cb($ev, array($this->xeps[$xep], $cb));
- }
- }
- }
-
- public function add_cb($ev, $cb, $pri=1) {
- return $this->ev->add($ev, $cb, $pri);
- }
-
- public function del_cb($ref) {
- $this->ev->del($ref);
- }
-
- public function set_status($status, $show='chat', $priority=10) {
- $this->send($this->get_pres_pkt(
- array(),
- $status,
- $show,
- $priority
- ));
- }
-
- public function send_chat_msg($to, $body, $thread=null, $subject=null) {
- $msg = new XMPPMsg(
- array(
- 'type'=>'chat',
- 'to'=>$to,
- 'from'=>$this->full_jid->to_string()
- ),
- $body,
- $thread,
- $subject
- );
- $this->send($msg);
- }
-
- public function get_vcard($jid=null, $cb=null) {
- $attrs = array(
- 'type'=>'get',
- 'from'=>$this->full_jid->to_string()
- );
-
- if($jid) {
- $jid = new XMPPJid($jid);
- $attrs['to'] = $jid->node."@".$jid->domain;
- }
-
- $pkt = $this->get_iq_pkt(
- $attrs,
- new JAXLXml('vCard', 'vcard-temp')
- );
- if($cb) $this->add_cb('on_stanza_id_'.$pkt->id, $cb);
- $this->send($pkt);
- }
-
- public function get_roster($cb=null) {
- $pkt = $this->get_iq_pkt(
- array(
- 'type'=>'get',
- 'from'=>$this->full_jid->to_string()
- ),
- new JAXLXml('query', 'jabber:iq:roster')
- );
- if($cb) $this->add_cb('on_stanza_id_'.$pkt->id, $cb);
- $this->send($pkt);
- }
-
- public function subscribe($to) {
- $this->send($this->get_pres_pkt(
- array('to'=>$to, 'type'=>'subscribe')
- ));
- }
-
- public function subscribed($to) {
- $this->send($this->get_pres_pkt(
- array('to'=>$to, 'type'=>'subscribed')
- ));
- }
-
- public function unsubscribe($to) {
- $this->send($this->get_pres_pkt(
- array('to'=>$to, 'type'=>'unsubscribe')
- ));
- }
-
- public function unsubscribed($to) {
- $this->send($this->get_pres_pkt(
- array('to'=>$to, 'type'=>'unsubscribed')
- ));
- }
-
- public function get_socket_path() {
- return ($this->cfg['port'] == 5223 ? "ssl" : "tcp")."://".$this->cfg['host'].":".$this->cfg['port'];
- }
-
- public function start($opts=array()) {
- // is bosh bot?
- if(@$this->cfg['bosh_url']) {
- $this->trans->session_start();
-
- for(;;) {
- // while any of the curl request is pending
- // keep receiving response
- while(sizeof($this->trans->chs) != 0) {
- $this->trans->recv();
- }
-
- // if no request in queue, ping bosh end point
- // and repeat recv
- $this->trans->ping();
- }
-
- $this->trans->session_end();
- return;
- }
-
- // is xmpp client or component?
- // if on_connect event have no callbacks
- // set default on_connect callback to $this->start_stream()
- // i.e. xmpp client mode
- if(!$this->ev->exists('on_connect'))
- $this->add_cb('on_connect', array($this, 'start_stream'));
-
- // connect to the destination host/port
- if($this->connect($this->get_socket_path())) {
- // emit
- $this->ev->emit('on_connect');
-
- // parse opts
- if(@$opts['--with-debug-shell']) $this->enable_debug_shell();
- if(@$opts['--with-unix-sock']) $this->enable_unix_sock();
-
- // run main loop
- JAXLLoop::run();
-
- // emit
- $this->ev->emit('on_disconnect');
- }
- // if connection to the destination fails
- else {
- if($this->trans->errno == 61
- || $this->trans->errno == 110
- || $this->trans->errno == 111
- ) {
- $retry_after = pow(2, $this->retry_attempt) * $this->retry_interval;
- $this->retry_attempt++;
- _debug("unable to connect with errno ".$this->trans->errno." (".$this->trans->errstr."), will try again in ".$retry_after." seconds");
- // TODO: use sigalrm instead
- // they usually doesn't gel well inside select loop
- sleep($retry_after);
- $this->start();
- }
- else {
- $this->ev->emit('on_connect_error', array(
- $this->trans->errno,
- $this->trans->errstr
- ));
- }
- }
- }
-
- //
- // callback methods
- //
-
- // signals callback handler
- // not for public api consumption
- public function signal_handler($sig) {
- $this->end_stream();
- $this->disconnect();
- $this->ev->emit('on_disconnect');
-
- switch($sig) {
- // terminal line hangup
- case SIGHUP:
- _debug("got sighup");
- break;
- // interrupt program
- case SIGINT:
- _debug("got sigint");
- break;
- // software termination signal
- case SIGTERM:
- _debug("got sigterm");
- break;
- }
-
- exit;
- }
-
- // called internally for ipc
- // not for public consumption
- public function on_unix_sock_accept($_c, $addr) {
- $this->sock->read($_c);
- }
-
- // this currently simply evals the incoming raw string
- // know what you are doing while in production
- public function on_unix_sock_request($_c, $_raw) {
- _debug("evaling raw string rcvd over unix sock: ".$_raw);
- $this->sock->send($_c, serialize(eval($_raw)));
- $this->sock->read($_c);
- }
-
- public function enable_unix_sock() {
- $this->sock = new JAXLSocketServer(
- 'unix://'.$this->get_sock_file_path(),
- array(&$this, 'on_unix_sock_accept'),
- array(&$this, 'on_unix_sock_request')
- );
- }
-
- // this simply eval the incoming raw data
- // inside current jaxl environment
- // security is all upto you, no checks made here
- public function handle_debug_shell($_raw) {
- print_r(eval($_raw));
- echo PHP_EOL;
- JAXLCli::prompt();
- }
-
- protected function enable_debug_shell() {
- $this->cli = new JAXLCli(array(&$this, 'handle_debug_shell'));
- JAXLCli::prompt();
- }
-
- //
- // abstract method implementation
- //
-
- protected function send_fb_challenge_response($challenge) {
- $this->send($this->get_fb_challenge_response_pkt($challenge));
- }
-
- // refer https://developers.facebook.com/docs/chat/#jabber
- public function get_fb_challenge_response_pkt($challenge) {
- $stanza = new JAXLXml('response', NS_SASL);
-
- $challenge = base64_decode($challenge);
- $challenge = urldecode($challenge);
- parse_str($challenge, $challenge_arr);
-
- $response = http_build_query(array(
- 'method' => $challenge_arr['method'],
- 'nonce' => $challenge_arr['nonce'],
- 'access_token' => $this->cfg['fb_access_token'],
- 'api_key' => $this->cfg['fb_app_key'],
- 'call_id' => 0,
- 'v' => '1.0'
- ));
-
- $stanza->t(base64_encode($response));
- return $stanza;
- }
-
- public function wait_for_fb_sasl_response($event, $args) {
- switch($event) {
- case "stanza_cb":
- $stanza = $args[0];
-
- if($stanza->name == 'challenge' && $stanza->ns == NS_SASL) {
- $challenge = $stanza->text;
- $this->send_fb_challenge_response($challenge);
- return "wait_for_sasl_response";
- }
- else {
- _debug("got unhandled sasl response, should never happen here");
- exit;
- }
- break;
- default:
- _debug("not catched $event, should never happen here");
- exit;
- break;
- }
- }
-
- // someday this needs to go inside xmpp stream
- public function wait_for_cram_md5_response($event, $args) {
- switch($event) {
- case "stanza_cb":
- $stanza = $args[0];
-
- if($stanza->name == 'challenge' && $stanza->ns == NS_SASL) {
- $challenge = base64_decode($stanza->text);
- $resp = new JAXLXml('response', NS_SASL);
- $resp->t(base64_encode($this->jid->to_string().' '.hash_hmac('md5', $challenge, $this->pass)));
- $this->send($resp);
- return "wait_for_sasl_response";
- }
- else {
- _debug("got unhandled sasl response, should never happen here");
- exit;
- }
- break;
- default:
- _debug("not catched $event, should never happen here");
- exit;
- break;
- }
- }
-
- // http://tools.ietf.org/html/rfc5802#section-5
- public function get_scram_sha1_response($pass, $challenge) {
- // it contains users iteration count i and the user salt
- // also server will append it's own nonce to the one we specified
- $decoded = $this->explode_data(base64_decode($challenge));
-
- // r=,s=,i=
- $nonce = $decoded['r'];
- $salt = base64_decode($decoded['s']);
- $iteration = intval($decoded['i']);
-
- // SaltedPassword := Hi(Normalize(password), salt, i)
- $salted = JAXLUtil::pbkdf2($this->pass, $salt, $iteration);
- // ClientKey := HMAC(SaltedPassword, "Client Key")
- $client_key = hash_hmac('sha1', $salted, "Client Key", true);
- // StoredKey := H(ClientKey)
- $stored_key = hash('sha1', $client_key, true);
- // AuthMessage := client-first-message-bare + "," + server-first-message + "," + client-final-message-without-proof
- $auth_message = '';
- // ClientSignature := HMAC(StoredKey, AuthMessage)
- $signature = hash_hmac('sha1', $stored_key, $auth_message, true);
- // ClientProof := ClientKey XOR ClientSignature
- $client_proof = $client_key ^ $signature;
-
- $proof = 'c=biws,r='.$nonce.',p='.base64_encode($client_proof);
- return base64_encode($proof);
- }
-
- public function wait_for_scram_sha1_response($event, $args) {
- switch($event) {
- case "stanza_cb":
- $stanza = $args[0];
-
- if($stanza->name == 'challenge' && $stanza->ns == NS_SASL) {
- $challenge = $stanza->text;
-
- $resp = new JAXLXml('response', NS_SASL);
- $resp->t($this->get_scram_sha1_response($this->pass, $challenge));
- $this->send($resp);
-
- return "wait_for_sasl_response";
- }
- else {
- _debug("got unhandled sasl response, should never happen here");
- exit;
- }
- break;
- default:
- _debug("not catched $event, should never happen here");
- exit;
- break;
- }
- }
-
- public function handle_auth_mechs($stanza, $mechanisms) {
- if($this->ev->exists('on_stream_features')) {
- return $this->ev->emit('on_stream_features', array($stanza));
- }
-
- // extract available mechanisms
- $mechs = array();
- if($mechanisms) foreach($mechanisms->childrens as $mechanism) $mechs[$mechanism->text] = true;
-
- // check if preferred auth type exists in available mechanisms
- $pref_auth = @$this->cfg['auth_type'] ? $this->cfg['auth_type'] : 'PLAIN';
- $pref_auth_exists = isset($mechs[$pref_auth]) ? true : false;
- _debug("pref_auth ".$pref_auth." ".($pref_auth_exists ? "exists" : "doesn't exists"));
-
- // if pref auth exists, try it
- if($pref_auth_exists) {
- $mech = $pref_auth;
- }
- // if pref auth doesn't exists, choose one from available mechanisms
- else {
- foreach($mechs as $mech=>$any) {
- // choose X-FACEBOOK-PLATFORM only if fb_access_token config value is available
- if($mech == 'X-FACEBOOK-PLATFORM') {
- if(@$this->cfg['fb_access_token']) {
- break;
- }
- }
- // else try first of the available methods
- else {
- break;
- }
- }
- _error("preferred auth type not supported, trying $mech");
- }
-
- $this->send_auth_pkt($mech, @$this->jid ? $this->jid->to_string() : null, @$this->pass);
-
- if($pref_auth == 'X-FACEBOOK-PLATFORM') {
- return "wait_for_fb_sasl_response";
- }
- else if($pref_auth == 'CRAM-MD5') {
- return "wait_for_cram_md5_response";
- }
- else if($pref_auth == 'SCRAM-SHA-1') {
- return "wait_for_scram_sha1_response";
- }
- }
-
- public function handle_auth_success() {
- // if not a component
- /*if(!@$this->xeps['0114']) {
- $this->xeps['0030']->get_info($this->full_jid->domain, array(&$this, 'handle_domain_info'));
- $this->xeps['0030']->get_items($this->full_jid->domain, array(&$this, 'handle_domain_items'));
- }*/
-
- $this->ev->emit('on_auth_success');
- }
-
- public function handle_auth_failure($reason) {
- $this->ev->emit('on_auth_failure', array(
- $reason
- ));
- }
-
- public function handle_stream_start($stanza) {
- $stanza = new XMPPStanza($stanza);
-
- $this->ev->emit('on_stream_start', array($stanza));
- return array(@$this->cfg['bosh_url'] ? 'wait_for_stream_features' : 'connected', 1);
- }
-
- public function handle_iq($stanza) {
- $stanza = new XMPPStanza($stanza);
-
- // emit callback registered on stanza id's
- $emited = false;
- if($stanza->id && $this->ev->exists('on_stanza_id_'.$stanza->id)) {
- //_debug("on stanza id callbackd");
- $emited = true;
- $this->ev->emit('on_stanza_id_'.$stanza->id, array($stanza));
- }
-
- // catch roster list
- if($stanza->type == 'result' && ($query = $stanza->exists('query', 'jabber:iq:roster'))) {
- foreach($query->childrens as $child) {
- if($child->name == 'item') {
- $jid = $child->attrs['jid'];
- $subscription = $child->attrs['subscription'];
-
- $groups = array();
- foreach($child->childrens as $group) {
- if($group->name == 'group') {
- $groups[] = $group->text;
- }
- }
-
- $this->roster[$jid] = new RosterItem($jid, $subscription, $groups);
- }
- }
-
- // emit this event if not emited above
- if(!$emited)
- $this->ev->emit('on_roster_update');
- }
-
- // if managing roster
- // catch contact vcard results
- if($this->manage_roster && $stanza->type == 'result' && ($query = $stanza->exists('vCard', 'vcard-temp'))) {
- if(@$this->roster[$stanza->from])
- $this->roster[$stanza->from]->vcard = $query;
- }
-
- // on_get_iq, on_result_iq, and other events are only
- // emitted if on_stanza_id_{id} wasn't emitted above
- // TODO: can we add more checks here before calling back
- // e.g. checks on existence of an attribute, check on 1st level child ns and so on
- if(!$emited)
- $this->ev->emit('on_'.$stanza->type.'_iq', array($stanza));
- }
-
- public function handle_presence($stanza) {
- $stanza = new XMPPStanza($stanza);
-
- // if managing roster
- // catch available/unavailable type stanza
- if($this->manage_roster) {
- $type = ($stanza->type ? $stanza->type : "available");
- $jid = new XMPPJid($stanza->from);
-
- if($type == 'available') {
- $this->roster[$jid->bare]->resources[$jid->resource] = $stanza;
- }
- else if($type == 'unavailable') {
- if(@$this->roster[$jid->bare] && @$this->roster[$jid->bare]->resources[$jid->resource])
- unset($this->roster[$jid->bare]->resources[$jid->resource]);
- }
- }
-
- // if managing subscription requests
- // we need to automate stuff here
- if($stanza->type == "subscribe" && $this->manage_subscribe != "none") {
- $this->subscribed($stanza->from);
- if($this->manage_subscribe == "mutual")
- $this->subscribe($stanza->from);
- }
-
- $this->ev->emit('on_presence_stanza', array($stanza));
- }
-
- public function handle_message($stanza) {
- $stanza = new XMPPStanza($stanza);
- $this->ev->emit('on_'.$stanza->type.'_message', array($stanza));
- }
-
- // unhandled event and arguments bubbled up
- // TODO: in a lot of cases this will be called, need more checks
- public function handle_other($event, $args) {
- $stanza = $args[0];
- $stanza = new XMPPStanza($stanza);
- $ev = 'on_'.$stanza->name.'_stanza';
- if($this->ev->exists($ev)) {
- return $this->ev->emit($ev, array($stanza));
- }
- else {
- _warning("event '".$event."' catched in handle_other with stanza name ".$stanza->name);
- }
- }
-
- public function handle_domain_info($stanza) {
- $query = $stanza->exists('query', NS_DISCO_INFO);
- foreach($query->childrens as $k=>$child) {
- if($child->name == 'identity') {
- //echo 'identity category:'.@$child->attrs['category'].', type:'.@$child->attrs['type'].', name:'.@$child->attrs['name'].PHP_EOL;
- }
- else if($child->name == 'x') {
- //echo 'x ns:'.$child->ns.PHP_EOL;
- }
- else if($child->name == 'feature') {
- //echo 'feature var:'.$child->attrs['var'].PHP_EOL;
- }
- }
- }
-
- public function handle_domain_items($stanza) {
- $query = $stanza->exists('query', NS_DISCO_ITEMS);
- foreach($query->childrens as $k=>$child) {
- if($child->name == 'item') {
- //echo 'item jid:'.@$child->attrs['jid'].', name:'.@$child->attrs['name'].', node:'.@$child->attrs['node'].PHP_EOL;
- }
- }
- }
-
- }
- ?>