3 * StatusNet - the distributed open-source microblogging tool
4 * Copyright (C) 2008, 2009, StatusNet, Inc.
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
23 * XMPP background connection manager for XMPP-using queue handlers,
24 * allowing them to send outgoing messages on the right connection.
26 * Input is handled during socket select loop, keepalive pings during idle.
27 * Any incoming messages will be handled.
29 * In a multi-site queuedaemon.php run, one connection will be instantiated
30 * for each site being handled by the current process that has XMPP enabled.
33 class XmppManager extends ImManager
35 protected $lastping = null;
36 protected $pingid = null;
40 const PING_INTERVAL = 120;
44 * Initialize connection to server.
45 * @return boolean true on success
47 public function start($master)
49 if(parent::start($master))
58 function send_raw_message($data)
61 if (!$this->conn || $this->conn->isDisconnected()) {
64 $this->conn->send($data);
69 * Message pump is triggered on socket input, so we only need an idle()
70 * call often enough to trigger our outgoing pings.
74 return self::PING_INTERVAL;
78 * Process XMPP events that have come in over the wire.
79 * @fixme may kill process on XMPP error
80 * @param resource $socket
82 public function handleInput($socket)
84 # Process the queue for as long as needed
86 common_log(LOG_DEBUG, "Servicing the XMPP queue.");
87 $this->stats('xmpp_process');
88 $this->conn->processTime(0);
89 } catch (XMPPHP_Exception $e) {
90 common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
91 die($e->getMessage());
96 * Lists the IM connection socket to allow i/o master to wake
97 * when input comes in here as well as from the queue source.
99 * @return array of resources
101 public function getSockets()
105 return array($this->conn->getSocket());
112 * Idle processing for io manager's execution loop.
113 * Send keepalive pings to server.
115 * Side effect: kills process on exception from XMPP library.
117 * @fixme non-dying error handling
119 public function idle($timeout=0)
122 if (empty($this->lastping) || $now - $this->lastping > self::PING_INTERVAL) {
125 } catch (XMPPHP_Exception $e) {
126 common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
127 die($e->getMessage());
134 if (!$this->conn || $this->conn->isDisconnected()) {
135 $resource = 'queue' . posix_getpid();
136 $this->conn = new Sharing_XMPP($this->plugin->host ?
137 $this->plugin->host :
138 $this->plugin->server,
141 $this->plugin->password,
142 $this->plugin->resource,
143 $this->plugin->server,
144 $this->plugin->debug ?
146 $this->plugin->debug ?
147 XMPPHP_Log::LEVEL_VERBOSE : null
153 $this->conn->addEventHandler('message', 'handle_xmpp_message', $this);
154 $this->conn->addEventHandler('reconnect', 'handle_xmpp_reconnect', $this);
155 $this->conn->setReconnectTimeout(600);
157 $this->conn->autoSubscribe();
158 $this->conn->useEncryption($this->plugin->encryption);
161 $this->conn->connect(true); // true = persistent connection
162 } catch (XMPPHP_Exception $e) {
163 common_log(LOG_ERR, $e->getMessage());
167 $this->conn->processUntil('session_start');
168 $this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100);
176 if (!$this->conn || $this->conn->isDisconnected()) {
180 if (!isset($this->pingid)) {
186 common_log(LOG_DEBUG, "Sending ping #{$this->pingid}");
187 $this->conn->send("<iq from='{" . $this->plugin->daemon_screenname() . "}' to='{$this->plugin->server}' id='ping_{$this->pingid}' type='get'><ping xmlns='urn:xmpp:ping'/></iq>");
188 $this->lastping = $now;
192 function handle_xmpp_message(&$pl)
194 $this->plugin->enqueue_incoming_raw($pl);
199 * Callback for Jabber reconnect event
202 function handle_xmpp_reconnect(&$pl)
204 common_log(LOG_NOTICE, 'XMPP reconnected');
206 $this->conn->processUntil('session_start');
207 $this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100);
211 * sends a presence stanza on the XMPP network
213 * @param string $status current status, free-form string
214 * @param string $show structured status value
215 * @param string $to recipient of presence, null for general
216 * @param string $type type of status message, related to $show
217 * @param int $priority priority of the presence
219 * @return boolean success value
222 function send_presence($status, $show='available', $to=null,
223 $type = 'available', $priority=null)
226 if (!$this->conn || $this->conn->isDisconnected()) {
229 $this->conn->presence($status, $show, $to, $type, $priority);
234 * sends a "special" presence stanza on the XMPP network
236 * @param string $type Type of presence
237 * @param string $to JID to send presence to
238 * @param string $show show value for presence
239 * @param string $status status value for presence
241 * @return boolean success flag
243 * @see send_presence()
246 function special_presence($type, $to=null, $show=null, $status=null)
248 // FIXME: why use this instead of send_presence()?
250 if (!$this->conn || $this->conn->isDisconnected()) {
254 $to = htmlspecialchars($to);
255 $status = htmlspecialchars($status);
262 $out .= " type='$type'";
264 if ($show == 'available' and !$status) {
268 if ($show && ($show != 'available')) {
269 $out .= "<show>$show</show>";
272 $out .= "<status>$status</status>";
274 $out .= "</presence>";
276 $this->conn->send($out);