Copyright (c) 2009-2012, Abhinav Singh .
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.
public function __call($event, $args) { if ($this->state) { // call state method JAXLLogger::debug(sprintf("calling state handler '%s' for incoming event '%s'", is_array($this->state) ? $this->state[1] : $this->state, $event)); if (is_callable($this->state)) { $call = $this->state; } else { $call = method_exists($this, $this->state) ? array(&$this, $this->state) : $this->state; } $r = call_user_func($call, $event, $args); // 4 cases of possible return value if (is_callable($r)) { $this->state = $r; } elseif (is_array($r) && count($r) == 2) { list($this->state, $ret) = $r; } elseif (is_array($r) && count($r) == 1) { $this->state = $r[0]; } elseif (is_string($r)) { $this->state = $r; } else { $this->handle_invalid_state($r); } JAXLLogger::debug("current state '" . (is_array($this->state) ? $this->state[1] : $this->state) . "'"); // return case if (!is_callable($r) && is_array($r) && count($r) == 2) { return $ret; } } else { JAXLLogger::debug("invalid state found, nothing called for event " . $event . ""); } }
public function logged_out($stanza) { if ($stanza->name == "error" && $stanza->ns == XMPP::NS_XMPP) { $reason = $stanza->children[0]->name; $this->jaxl->handle_auth_failure($reason); $this->jaxl->send_end_stream(); return array("logged_out", 0); } else { JAXLLogger::debug("uncatched stanza received in logged_out"); } }
public function dispatch($request) { foreach ($this->rules as $rule) { //JAXLLogger::debug("matching $request->path with pattern $rule->pattern"); if (($matches = $rule->match($request->path, $request->method)) !== false) { JAXLLogger::debug("matching rule found, dispatching"); $params = array($request); // TODO: a bad way to restrict on 'pk', fix me for generalization if (isset($matches['pk'])) { $params[] = $matches['pk']; } call_user_func_array($rule->cb, $params); return true; } } return false; }
public static function shutdown_handler() { try { JAXLLogger::debug("got shutdown handler"); if (null !== ($error = error_get_last())) { throw new JAXLException($error['message'], $error['type'], $error['file'], $error['line']); } } catch (Exception $e) { JAXLLogger::debug("shutdown handler catched with exception " . json_encode($e)); } }
public function done($event, $data) { JAXLLogger::warning("got unhandled event {$event} with data {$data['0']}"); return array('done', false); }
* 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. * */ if ($argc < 2) { echo "Usage: {$argv['0']} url\n"; exit; } require_once 'jaxl.php'; JAXLLogger::$level = JAXL_DEBUG; require_once JAXL_CWD . '/http/http_client.php'; $request = new HTTPClient($argv[1]); $request->start();
* * 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 dirname(__FILE__) . '/_bootstrap.php'; // configure logger JAXLLogger::$level = JAXLLogger::INFO; // print usage notice and parse addr/port parameters if passed JAXLLogger::cliLog("Usage: {$argv['0']} port (default: 9699)", JAXLLogger::NOTICE); $port = $argc == 2 ? $argv[1] : 9699; // initialize http server $http = new HTTPServer($port); // catch all incoming requests here function on_request($request) { $body = json_encode($request); $request->ok($body, array('Content-Type' => 'application/json')); } // start http server $http->start('on_request');
function on_auth_failure_callback($reason) { global $client; $client->send_end_stream(); JAXLLogger::info("got on_auth_failure cb with reason {$reason}"); }
function delete_event($request, $pk) { JAXLLogger::info("got event delete request for {$pk}"); $request->close(); }
public function on_client_write_ready($client) { $client_id = (int) $client; JAXLLogger::debug("client#{$client_id} is write ready"); try { // send in chunks $total = $this->clients[$client_id]['obuffer']; $written = @fwrite($client, substr($total, 0, $this->send_chunk_size)); if ($written === false) { // fwrite failed JAXLLogger::warning("====> fwrite failed"); $this->clients[$client_id]['obuffer'] = $total; } elseif ($written == strlen($total) || $written == $this->send_chunk_size) { // full chunk written //JAXLLogger::debug("full chunk written"); $this->clients[$client_id]['obuffer'] = substr($total, $this->send_chunk_size); } else { // partial chunk written //JAXLLogger::debug("partial chunk $written written"); $this->clients[$client_id]['obuffer'] = substr($total, $written); } // if no more stuff to write, remove write handler if (strlen($this->clients[$client_id]['obuffer']) === 0) { $this->del_write_cb($client_id); // if scheduled for close and not closed do it and clean up if ($this->clients[$client_id]['close'] && !$this->clients[$client_id]['closed']) { if (is_resource($client)) { fclose($client); } $this->clients[$client_id]['closed'] = true; unset($this->clients[$client_id]); JAXLLogger::debug("closed client#" . $client_id); } } } catch (JAXLException $e) { JAXLLogger::debug("====> got fwrite exception"); } }
protected function handle_start_tag($parser, $name, array $attrs) { $name = $this->explode($name); //echo "start of tag ".$name[1]." with ns ".$name[0].PHP_EOL; // replace ns with prefix foreach ($attrs as $key => $v) { $k = $this->explode($key); // no ns specified if ($k[0] == null) { $attrs[$k[1]] = $v; } elseif ($k[0] == XMPP::NS_XML) { // xml ns unset($attrs[$key]); $attrs['xml:' . $k[1]] = $v; } else { JAXLLogger::error("==================> unhandled ns prefix on attribute"); // remove attribute else will cause error with bad stanza format // report to developer if above error message is ever encountered unset($attrs[$key]); } } if ($this->depth <= 0) { $this->depth = 0; $this->ns = $name[1]; if ($this->start_cb) { $stanza = new JAXLXml($name[1], $name[0], $attrs); call_user_func($this->start_cb, $stanza); } } else { if (!$this->stanza) { $stanza = new JAXLXml($name[1], $name[0], $attrs); $this->stanza =& $stanza; } else { $this->stanza->c($name[1], $name[0], $attrs); } } ++$this->depth; }
public function on_write_ready($fd) { //JAXLLogger::debug("on write ready called"); $total = strlen($this->obuffer); $bytes = @fwrite($fd, $this->obuffer); $this->send_bytes += $bytes; JAXLLogger::debug("sent " . $bytes . "/" . $this->send_bytes . " of data"); JAXLLogger::debug(substr($this->obuffer, 0, $bytes)); $this->obuffer = substr($this->obuffer, $bytes, $total - $bytes); // unwatch for write if obuffer is empty if (strlen($this->obuffer) === 0) { JAXLLoop::unwatch($fd, array('write' => true)); $this->writing = false; } //JAXLLogger::debug("current obuffer size: ".strlen($this->obuffer).""); }
* 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. * */ if ($argc < 2) { echo "Usage: {$argv['0']} /path/to/server.sock\n"; exit; } require_once 'jaxl.php'; JAXLLogger::$level = JAXL_INFO; $server = null; function on_request($client, $raw) { global $server; $server->send($client, $raw); _info("got client callback " . $raw); } @unlink($argv[1]); $server = new JAXLSocketServer('unix://' . $argv[1], 'on_request'); JAXLLoop::run(); echo "done\n";
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 JAXLLogger::debug("uncatched {$event}"); return $this->handle_other($event, $args); //return array("logged_out", 0); break; } }
function read_event_callback($data) { global $pipe; JAXLLogger::info("read " . trim($data) . " from pipe"); }
public function __destruct() { JAXLLogger::info("shutting down clock server..."); }
protected function send_response($code, array $headers = array(), $body = null) { // send out response line $this->send_line($code); // set content length of body exists and is not already set if ($body && !isset($headers['Content-Length'])) { $headers['Content-Length'] = strlen($body); } // send out headers $this->send_headers($code, $headers); // send body // prefixed with an empty line JAXLLogger::debug("sending out HTTP_CRLF prefixed body"); if ($body) { $this->send_body(HTTPServer::HTTP_CRLF . $body); } }
function on_disconnect($client) { JAXLLogger::info("got on_disconnect cb"); }
function on_request($client, $raw) { global $server; $server->send($client, $raw); JAXLLogger::info("got client callback " . $raw); }
public function handle_other($event, $args) { $stanza = isset($args[0]) ? $args[0] : null; $stanza = new XMPPStanza($stanza); $ev = 'on_' . $stanza->name . '_stanza'; if ($this->ev->exists($ev)) { return $this->ev->emit($ev, array($stanza)); } else { JAXLLogger::warning("event '" . $event . "' catched in handle_other with stanza name " . $stanza->name); } }
function on_disconnect_callback() { JAXLLogger::info("got on_disconnect cb"); }
function on_disconnect_callback() { global $form; JAXLLogger::info("registration " . ($form['type'] == 'result' ? 'succeeded' : 'failed')); }
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 on_response($raw) { JAXLLogger::debug($raw); }
function on_auth_success_callback() { global $xmpp; JAXLLogger::info("got on_auth_success cb, jid " . $xmpp->full_jid->to_string()); }
public function disconnect() { JAXLLogger::debug("disconnecting"); }
function _colorize($msg, $verbosity) { error_log(JAXLLogger::colorize($msg, $verbosity)); }
public function on_request($sock, $raw) { JAXLLogger::debug("on_request for client#{$sock}"); $request = $this->requests[$sock]; // 'wait_for_body' state is reached when ever // application calls recv_body() method // on received $request object if ($request->state() == 'wait_for_body') { $request->body($raw); } else { // break on crlf $lines = explode(HTTP_CRLF, $raw); // parse request line if ($request->state() == 'wait_for_request_line') { list($method, $resource, $version) = explode(" ", $lines[0]); $request->line($method, $resource, $version); unset($lines[0]); JAXLLogger::info($request->ip . " " . $request->method . " " . $request->resource . " " . $request->version); } // parse headers foreach ($lines as $line) { $line_parts = explode(":", $line); if (count($line_parts) > 1) { if (strlen($line_parts[0]) > 0) { $k = $line_parts[0]; unset($line_parts[0]); $v = implode(":", $line_parts); $request->set_header($k, $v); } } elseif (strlen(trim($line_parts[0])) == 0) { $request->empty_line(); } else { // if exploded line array size is 1 // and there is something in $line_parts[0] // must be request body $request->body($line); } } } // if request has reached 'headers_received' state? if ($request->state() == 'headers_received') { // dispatch to any matching rule found JAXLLogger::debug("delegating to dispatcher for further routing"); $dispatched = $this->dispatcher->dispatch($request); // if no dispatch rule matched call generic callback if (!$dispatched && $this->cb) { JAXLLogger::debug("no dispatch rule matched, sending to generic callback"); call_user_func($this->cb, $request); } elseif (!$dispatched) { // elseif not dispatched and not generic callbacked // send 404 not_found // TODO: send 404 if no callback is registered for this request JAXLLogger::debug("dropping request since no matching dispatch rule or generic callback was specified"); $request->not_found('404 Not Found'); } } else { // if state is not 'headers_received' // reactivate client socket for read event $this->server->read($sock); } }
public static function printHelp() { global $exe; JAXLLogger::cliLog("Usage: {$exe} command [options...]" . PHP_EOL, JAXLLogger::INFO); JAXLLogger::cliLog("Commands:", JAXLLogger::NOTICE); JAXLLogger::cliLog(" help This help text", JAXLLogger::DEBUG); JAXLLogger::cliLog(" debug Attach a debug console to a running JAXL daemon", JAXLLogger::DEBUG); JAXLLogger::cliLog(" shell Open up Jaxl shell emulator", JAXLLogger::DEBUG); echo PHP_EOL; }
public static function select() { $read = self::$read_fds; $write = self::$write_fds; $except = null; $changed = @stream_select($read, $write, $except, self::$secs, self::$usecs); if ($changed === false) { JAXLLogger::error("error in the event loop, shutting down..."); /*foreach (self::$read_fds as $fd) { if (is_resource($fd)) { print_r(stream_get_meta_data($fd)); } }*/ exit; } elseif ($changed > 0) { // read callback foreach ($read as $r) { $fdid = array_search($r, self::$read_fds); if (isset(self::$read_fds[$fdid])) { call_user_func(self::$read_cbs[$fdid], self::$read_fds[$fdid]); } } // write callback foreach ($write as $w) { $fdid = array_search($w, self::$write_fds); if (isset(self::$write_fds[$fdid])) { call_user_func(self::$write_cbs[$fdid], self::$write_fds[$fdid]); } } self::$clock->tick(); } elseif ($changed === 0) { //JAXLLogger::debug("nothing changed while selecting for read"); self::$clock->tick(self::$secs * pow(10, 6) + self::$usecs); } }