Changeset View
Changeset View
Standalone View
Standalone View
externals/JAXL/xmpp/xmpp_stream.php
- This file was added.
| <?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. | |||||
| * | |||||
| */ | |||||
| require_once JAXL_CWD.'/core/jaxl_fsm.php'; | |||||
| require_once JAXL_CWD.'/core/jaxl_xml.php'; | |||||
| require_once JAXL_CWD.'/core/jaxl_xml_stream.php'; | |||||
| require_once JAXL_CWD.'/core/jaxl_util.php'; | |||||
| require_once JAXL_CWD.'/core/jaxl_socket_client.php'; | |||||
| require_once JAXL_CWD.'/xmpp/xmpp_nss.php'; | |||||
| require_once JAXL_CWD.'/xmpp/xmpp_jid.php'; | |||||
| require_once JAXL_CWD.'/xmpp/xmpp_msg.php'; | |||||
| require_once JAXL_CWD.'/xmpp/xmpp_pres.php'; | |||||
| require_once JAXL_CWD.'/xmpp/xmpp_iq.php'; | |||||
| /** | |||||
| * | |||||
| * Enter description here ... | |||||
| * @author abhinavsingh | |||||
| * | |||||
| */ | |||||
| abstract class XMPPStream extends JAXLFsm { | |||||
| // jid with binding resource value | |||||
| public $full_jid = null; | |||||
| // input parameters | |||||
| public $jid = null; | |||||
| public $pass = null; | |||||
| public $resource = null; | |||||
| public $force_tls = false; | |||||
| // underlying socket/bosh and xml stream ref | |||||
| protected $trans = null; | |||||
| protected $xml = null; | |||||
| // stanza id | |||||
| protected $last_id = 0; | |||||
| // | |||||
| // abstract methods | |||||
| // | |||||
| abstract public function handle_stream_start($stanza); | |||||
| abstract public function handle_auth_mechs($stanza, $mechs); | |||||
| abstract public function handle_auth_success(); | |||||
| abstract public function handle_auth_failure($reason); | |||||
| abstract public function handle_iq($stanza); | |||||
| abstract public function handle_presence($stanza); | |||||
| abstract public function handle_message($stanza); | |||||
| abstract public function handle_other($event, $args); | |||||
| // | |||||
| // public api | |||||
| // | |||||
| public function __construct($transport, $jid, $pass=null, $resource=null, $force_tls=false) { | |||||
| $this->jid = $jid; | |||||
| $this->pass = $pass; | |||||
| $this->resource = $resource ? $resource : md5(time()); | |||||
| $this->force_tls = $force_tls; | |||||
| $this->trans = $transport; | |||||
| $this->xml = new JAXLXmlStream(); | |||||
| $this->trans->set_callback(array(&$this->xml, "parse")); | |||||
| $this->xml->set_callback(array(&$this, "start_cb"), array(&$this, "end_cb"), array(&$this, "stanza_cb")); | |||||
| parent::__construct("setup"); | |||||
| } | |||||
| public function __destruct() { | |||||
| //_debug("cleaning up xmpp stream..."); | |||||
| } | |||||
| public function handle_invalid_state($r) { | |||||
| _error("got invalid return value from state handler '".$this->state."', sending end stream..."); | |||||
| $this->send_end_stream(); | |||||
| $this->state = "logged_out"; | |||||
| _notice("state handler '".$this->state."' returned ".serialize($r).", kindly report this to developers"); | |||||
| } | |||||
| public function send($stanza) { | |||||
| $this->trans->send($stanza->to_string()); | |||||
| } | |||||
| public function send_raw($data) { | |||||
| $this->trans->send($data); | |||||
| } | |||||
| // | |||||
| // pkt creation utilities | |||||
| // | |||||
| public function get_start_stream($jid) { | |||||
| $xml = '<stream:stream xmlns:stream="'.NS_XMPP.'" version="1.0" '; | |||||
| //if(isset($jid->bare)) $xml .= 'from="'.$jid->bare.'" '; | |||||
| if(isset($jid->domain)) $xml .= 'to="'.$jid->domain.'" '; | |||||
| $xml .= 'xmlns="'.NS_JABBER_CLIENT.'" xml:lang="en" xmlns:xml="'.NS_XML.'">'; | |||||
| return $xml; | |||||
| } | |||||
| public function get_end_stream() { | |||||
| return '</stream:stream>'; | |||||
| } | |||||
| public function get_starttls_pkt() { | |||||
| $stanza = new JAXLXml('starttls', NS_TLS); | |||||
| return $stanza; | |||||
| } | |||||
| public function get_compress_pkt($method) { | |||||
| $stanza = new JAXLXml('compress', NS_COMPRESSION_PROTOCOL); | |||||
| $stanza->c('method')->t($method); | |||||
| return $stanza; | |||||
| } | |||||
| // someday this all needs to go inside jaxl_sasl_auth | |||||
| public function get_auth_pkt($mechanism, $user, $pass) { | |||||
| $stanza = new JAXLXml('auth', NS_SASL, array('mechanism'=>$mechanism)); | |||||
| switch($mechanism) { | |||||
| case 'PLAIN': | |||||
| case 'X-OAUTH2': | |||||
| $stanza->t(base64_encode("\x00".$user."\x00".$pass)); | |||||
| break; | |||||
| case 'DIGEST-MD5': | |||||
| break; | |||||
| case 'CRAM-MD5': | |||||
| break; | |||||
| case 'SCRAM-SHA-1': | |||||
| // client first message always starts with n, y or p for GS2 extensibility | |||||
| $stanza->t(base64_encode("n,,n=".$user.",r=".JAXLUtil::get_nonce(false))); | |||||
| break; | |||||
| case 'ANONYMOUS': | |||||
| break; | |||||
| case 'EXTERNAL': | |||||
| // If no password, then we are probably doing certificate auth, so follow RFC 6120 form and pass '='. | |||||
| if(strlen($pass) == 0) | |||||
| $stanza->t('='); | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| return $stanza; | |||||
| } | |||||
| public function get_challenge_response_pkt($challenge) { | |||||
| $stanza = new JAXLXml('response', NS_SASL); | |||||
| $decoded = $this->explode_data(base64_decode($challenge)); | |||||
| if(!isset($decoded['rspauth'])) { | |||||
| _debug("calculating response to challenge"); | |||||
| $stanza->t($this->get_challenge_response($decoded)); | |||||
| } | |||||
| return $stanza; | |||||
| } | |||||
| public function get_challenge_response($decoded) { | |||||
| $response = array(); | |||||
| $nc = '00000001'; | |||||
| if(!isset($decoded['digest-uri'])) | |||||
| $decoded['digest-uri'] = 'xmpp/'.$this->jid->domain; | |||||
| $decoded['cnonce'] = base64_encode(JAXLUtil::get_nonce()); | |||||
| if(isset($decoded['qop']) && $decoded['qop'] != 'auth' && strpos($decoded['qop'], 'auth') !== false) | |||||
| $decoded['qop'] = 'auth'; | |||||
| $data = array_merge($decoded, array('nc'=>$nc)); | |||||
| $response = array( | |||||
| 'username'=> $this->jid->node, | |||||
| 'response' => $this->encrypt_password($data, $this->jid->node, $this->pass), | |||||
| 'charset' => 'utf-8', | |||||
| 'nc' => $nc, | |||||
| 'qop' => 'auth' | |||||
| ); | |||||
| foreach(array('nonce', 'digest-uri', 'realm', 'cnonce') as $key) | |||||
| if(isset($decoded[$key])) | |||||
| $response[$key] = $decoded[$key]; | |||||
| return base64_encode($this->implode_data($response)); | |||||
| } | |||||
| public function get_bind_pkt($resource) { | |||||
| $stanza = new JAXLXml('bind', NS_BIND); | |||||
| $stanza->c('resource')->t($resource); | |||||
| return $this->get_iq_pkt(array( | |||||
| 'type' => 'set' | |||||
| ), $stanza); | |||||
| } | |||||
| public function get_session_pkt() { | |||||
| $stanza = new JAXLXml('session', NS_SESSION); | |||||
| return $this->get_iq_pkt(array( | |||||
| 'type' => 'set' | |||||
| ), $stanza); | |||||
| } | |||||
| public function get_msg_pkt($attrs, $body=null, $thread=null, $subject=null, $payload=null) { | |||||
| $msg = new XMPPMsg($attrs, $body, $thread, $subject); | |||||
| if(!$msg->id) $msg->id = $this->get_id(); | |||||
| if($payload) $msg->cnode($payload); | |||||
| return $msg; | |||||
| } | |||||
| public function get_pres_pkt($attrs, $status=null, $show=null, $priority=null, $payload=null) { | |||||
| $pres = new XMPPPres($attrs, $status, $show, $priority); | |||||
| if(!$pres->id) $pres->id = $this->get_id(); | |||||
| if($payload) $pres->cnode($payload); | |||||
| return $pres; | |||||
| } | |||||
| public function get_iq_pkt($attrs, $payload) { | |||||
| $iq = new XMPPIq($attrs); | |||||
| if(!$iq->id) $iq->id = $this->get_id(); | |||||
| if($payload) $iq->cnode($payload); | |||||
| return $iq; | |||||
| } | |||||
| public function get_id() { | |||||
| ++$this->last_id; | |||||
| return dechex($this->last_id); | |||||
| } | |||||
| public function explode_data($data) { | |||||
| $data = explode(',', $data); | |||||
| $pairs = array(); | |||||
| $key = false; | |||||
| foreach($data as $pair) { | |||||
| $dd = strpos($pair, '='); | |||||
| if($dd) { | |||||
| $key = trim(substr($pair, 0, $dd)); | |||||
| $pairs[$key] = trim(trim(substr($pair, $dd + 1)), '"'); | |||||
| } | |||||
| else if(strpos(strrev(trim($pair)), '"') === 0 && $key) { | |||||
| $pairs[$key] .= ',' . trim(trim($pair), '"'); | |||||
| continue; | |||||
| } | |||||
| } | |||||
| return $pairs; | |||||
| } | |||||
| public function implode_data($data) { | |||||
| $return = array(); | |||||
| foreach($data as $key => $value) $return[] = $key . '="' . $value . '"'; | |||||
| return implode(',', $return); | |||||
| } | |||||
| public function encrypt_password($data, $user, $pass) { | |||||
| foreach(array('realm', 'cnonce', 'digest-uri') as $key) | |||||
| if(!isset($data[$key])) | |||||
| $data[$key] = ''; | |||||
| $pack = md5($user.':'.$data['realm'].':'.$pass); | |||||
| if(isset($data['authzid'])) | |||||
| $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']); | |||||
| $a2 = 'AUTHENTICATE:'.$data['digest-uri']; | |||||
| return md5(sprintf('%s:%s:%s:%s:%s:%s', md5($a1), $data['nonce'], $data['nc'], $data['cnonce'], $data['qop'], md5($a2))); | |||||
| } | |||||
| // | |||||
| // socket senders | |||||
| // | |||||
| protected function send_start_stream($jid) { | |||||
| $this->send_raw($this->get_start_stream($jid)); | |||||
| } | |||||
| public function send_end_stream() { | |||||
| $this->send_raw($this->get_end_stream()); | |||||
| } | |||||
| protected function send_auth_pkt($type, $user, $pass) { | |||||
| $this->send($this->get_auth_pkt($type, $user, $pass)); | |||||
| } | |||||
| protected function send_starttls_pkt() { | |||||
| $this->send($this->get_starttls_pkt()); | |||||
| } | |||||
| protected function send_compress_pkt($method) { | |||||
| $this->send($this->get_compress_pkt($method)); | |||||
| } | |||||
| protected function send_challenge_response($challenge) { | |||||
| $this->send($this->get_challenge_response_pkt($challenge)); | |||||
| } | |||||
| protected function send_bind_pkt($resource) { | |||||
| $this->send($this->get_bind_pkt($resource)); | |||||
| } | |||||
| protected function send_session_pkt() { | |||||
| $this->send($this->get_session_pkt()); | |||||
| } | |||||
| private function do_connect($args) { | |||||
| $socket_path = @$args[0]; | |||||
| if($this->trans->connect($socket_path)) { | |||||
| return array("connected", 1); | |||||
| } | |||||
| else { | |||||
| return array("disconnected", 0); | |||||
| } | |||||
| } | |||||
| // | |||||
| // fsm States | |||||
| // | |||||
| public function setup($event, $args) { | |||||
| switch($event) { | |||||
| case "connect": | |||||
| return $this->do_connect($args); | |||||
| break; | |||||
| // someone else already started the stream | |||||
| // even before "connect" was called must be bosh | |||||
| case "start_cb": | |||||
| $stanza = $args[0]; | |||||
| return $this->handle_stream_start($stanza); | |||||
| break; | |||||
| default: | |||||
| _debug("uncatched $event"); | |||||
| //print_r($args); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("setup", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| public function connected($event, $args) { | |||||
| switch($event) { | |||||
| case "start_stream": | |||||
| $this->send_start_stream($this->jid); | |||||
| return array("wait_for_stream_start", 1); | |||||
| break; | |||||
| // someone else already started the stream before us | |||||
| // even before "start_stream" was called | |||||
| // must be component | |||||
| case "start_cb": | |||||
| $stanza = $args[0]; | |||||
| return $this->handle_stream_start($stanza); | |||||
| break; | |||||
| default: | |||||
| _debug("uncatched $event"); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("connected", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| public function disconnected($event, $args) { | |||||
| switch($event) { | |||||
| case "connect": | |||||
| return $this->do_connect($args); | |||||
| break; | |||||
| case "end_stream": | |||||
| $this->send_end_stream(); | |||||
| return "logged_out"; | |||||
| break; | |||||
| default: | |||||
| _debug("uncatched $event"); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("disconnected", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| public function wait_for_stream_start($event, $args) { | |||||
| switch($event) { | |||||
| case "start_cb": | |||||
| // TODO: save stream id and other meta info | |||||
| //_debug("stream started"); | |||||
| return "wait_for_stream_features"; | |||||
| break; | |||||
| default: | |||||
| _debug("uncatched $event"); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("wait_for_stream_start", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| // XEP-0170: Recommended Order of Stream Feature Negotiation | |||||
| public function wait_for_stream_features($event, $args) { | |||||
| switch($event) { | |||||
| case "stanza_cb": | |||||
| $stanza = $args[0]; | |||||
| // get starttls requirements | |||||
| $starttls = $stanza->exists('starttls', NS_TLS); | |||||
| $required = $starttls ? ($this->force_tls ? true : ($starttls->exists('required') ? true : false)) : false; | |||||
| if($starttls && $required) { | |||||
| $this->send_starttls_pkt(); | |||||
| return "wait_for_tls_result"; | |||||
| } | |||||
| // handle auth mech | |||||
| $mechs = $stanza->exists('mechanisms', NS_SASL); | |||||
| if($mechs) { | |||||
| $new_state = $this->handle_auth_mechs($stanza, $mechs); | |||||
| return $new_state ? $new_state : "wait_for_sasl_response"; | |||||
| } | |||||
| // post auth | |||||
| $bind = $stanza->exists('bind', NS_BIND) ? true : false; | |||||
| $sess = $stanza->exists('session', NS_SESSION) ? true : false; | |||||
| $comp = $stanza->exists('compression', NS_COMPRESSION_FEATURE) ? true : false; | |||||
| if($bind) { | |||||
| $this->send_bind_pkt($this->resource); | |||||
| return "wait_for_bind_response"; | |||||
| } | |||||
| /*// compression not supported due to bug in php stream filters | |||||
| else if($comp) { | |||||
| $this->send_compress_pkt("zlib"); | |||||
| return "wait_for_compression_result"; | |||||
| }*/ | |||||
| else { | |||||
| _debug("no catch"); | |||||
| } | |||||
| break; | |||||
| default: | |||||
| _debug("uncatched $event"); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("wait_for_stream_features", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| public function wait_for_tls_result($event, $args) { | |||||
| switch($event) { | |||||
| case "stanza_cb": | |||||
| $stanza = $args[0]; | |||||
| if($stanza->name == 'proceed' && $stanza->ns == NS_TLS) { | |||||
| if($this->trans->crypt()) { | |||||
| $this->xml->reset_parser(); | |||||
| $this->send_start_stream($this->jid); | |||||
| return "wait_for_stream_start"; | |||||
| } | |||||
| else { | |||||
| $this->handle_auth_failure("tls-negotiation-failed"); | |||||
| return "logged_out"; | |||||
| } | |||||
| } | |||||
| else { | |||||
| // FIXME: here | |||||
| } | |||||
| break; | |||||
| default: | |||||
| _debug("uncatched $event"); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("wait_for_tls_result", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| public function wait_for_compression_result($event, $args) { | |||||
| switch($event) { | |||||
| case "stanza_cb": | |||||
| $stanza = $args[0]; | |||||
| if($stanza->name == 'compressed' && $stanza->ns == NS_COMPRESSION_PROTOCOL) { | |||||
| $this->xml->reset_parser(); | |||||
| $this->trans->compress(); | |||||
| $this->send_start_stream($this->jid); | |||||
| return "wait_for_stream_start"; | |||||
| } | |||||
| break; | |||||
| default: | |||||
| _debug("uncatched $event"); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("wait_for_compression_result", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| public function wait_for_sasl_response($event, $args) { | |||||
| switch($event) { | |||||
| case "stanza_cb": | |||||
| $stanza = $args[0]; | |||||
| if($stanza->name == 'failure' && $stanza->ns == NS_SASL) { | |||||
| $reason = $stanza->childrens[0]->name; | |||||
| //_debug("sasl failed with reason ".$reason.""); | |||||
| $this->handle_auth_failure($reason); | |||||
| return "logged_out"; | |||||
| } | |||||
| else if($stanza->name == 'challenge' && $stanza->ns == NS_SASL) { | |||||
| $challenge = $stanza->text; | |||||
| $this->send_challenge_response($challenge); | |||||
| return "wait_for_sasl_response"; | |||||
| } | |||||
| else if($stanza->name == 'success' && $stanza->ns == NS_SASL) { | |||||
| $this->xml->reset_parser(); | |||||
| $this->send_start_stream(@$this->jid); | |||||
| return "wait_for_stream_start"; | |||||
| } | |||||
| else { | |||||
| _debug("got unhandled sasl response"); | |||||
| } | |||||
| return "wait_for_sasl_response"; | |||||
| break; | |||||
| default: | |||||
| _debug("uncatched $event"); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("wait_for_sasl_response", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| public function wait_for_bind_response($event, $args) { | |||||
| switch($event) { | |||||
| case "stanza_cb": | |||||
| $stanza = $args[0]; | |||||
| // TODO: chk on id | |||||
| if($stanza->name == 'iq' && $stanza->attrs['type'] == 'result' | |||||
| && ($jid = $stanza->exists('bind', NS_BIND)->exists('jid'))) { | |||||
| $this->full_jid = new XMPPJid($jid->text); | |||||
| $this->send_session_pkt(); | |||||
| return "wait_for_session_response"; | |||||
| } | |||||
| else { | |||||
| // FIXME: | |||||
| } | |||||
| break; | |||||
| default: | |||||
| _debug("uncatched $event"); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("wait_for_bind_response", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| public function wait_for_session_response($event, $args) { | |||||
| switch($event) { | |||||
| case "stanza_cb": | |||||
| $this->handle_auth_success(); | |||||
| return "logged_in"; | |||||
| break; | |||||
| default: | |||||
| _debug("uncatched $event"); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("wait_for_session_response", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| public function logged_in($event, $args) { | |||||
| switch($event) { | |||||
| case "stanza_cb": | |||||
| $stanza = $args[0]; | |||||
| // call abstract | |||||
| if($stanza->name == 'message') { | |||||
| $this->handle_message($stanza); | |||||
| } | |||||
| else if($stanza->name == 'presence') { | |||||
| $this->handle_presence($stanza); | |||||
| } | |||||
| else if($stanza->name == 'iq') { | |||||
| $this->handle_iq($stanza); | |||||
| } | |||||
| else { | |||||
| $this->handle_other($event, $args); | |||||
| } | |||||
| return "logged_in"; | |||||
| break; | |||||
| case "end_cb": | |||||
| $this->send_end_stream(); | |||||
| return "logged_out"; | |||||
| break; | |||||
| case "end_stream": | |||||
| $this->send_end_stream(); | |||||
| return "logged_out"; | |||||
| break; | |||||
| case "disconnect": | |||||
| $this->trans->disconnect(); | |||||
| return "disconnected"; | |||||
| break; | |||||
| default: | |||||
| _debug("uncatched $event"); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("logged_in", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| public function logged_out($event, $args) { | |||||
| switch($event) { | |||||
| case "end_cb": | |||||
| $this->trans->disconnect(); | |||||
| return "disconnected"; | |||||
| break; | |||||
| case "end_stream": | |||||
| return "disconnected"; | |||||
| break; | |||||
| case "disconnect": | |||||
| $this->trans->disconnect(); | |||||
| return "disconnected"; | |||||
| break; | |||||
| case "connect": | |||||
| return $this->do_connect($args); | |||||
| break; | |||||
| default: | |||||
| // exit for any other event in logged_out state | |||||
| _debug("uncatched $event"); | |||||
| return $this->handle_other($event, $args); | |||||
| //return array("logged_out", 0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| ?> | |||||