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.
32 class XmppManager extends ImManager
34 protected $lastping = null;
35 protected $pingid = null;
39 const PING_INTERVAL = 120;
42 * Initialize connection to server.
43 * @return boolean true on success
45 public function start($master)
47 if(parent::start($master))
56 function send_raw_message($data)
59 if (!$this->conn || $this->conn->isDisconnected()) {
62 $this->conn->send($data);
67 * Message pump is triggered on socket input, so we only need an idle()
68 * call often enough to trigger our outgoing pings.
72 return self::PING_INTERVAL;
76 * Process XMPP events that have come in over the wire.
77 * @fixme may kill process on XMPP error
78 * @param resource $socket
80 public function handleInput($socket)
82 // Process the queue for as long as needed
84 common_log(LOG_DEBUG, "Servicing the XMPP queue.");
85 $this->stats('xmpp_process');
86 $this->conn->processTime(0);
87 } catch (XMPPHP_Exception $e) {
88 common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
89 die($e->getMessage());
94 * Lists the IM connection socket to allow i/o master to wake
95 * when input comes in here as well as from the queue source.
97 * @return array of resources
99 public function getSockets()
103 return array($this->conn->getSocket());
110 * Idle processing for io manager's execution loop.
111 * Send keepalive pings to server.
113 * Side effect: kills process on exception from XMPP library.
115 * @todo FIXME: non-dying error handling
117 public function idle($timeout=0)
120 if (empty($this->lastping) || $now - $this->lastping > self::PING_INTERVAL) {
123 } catch (XMPPHP_Exception $e) {
124 common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
125 die($e->getMessage());
132 if (!$this->conn || $this->conn->isDisconnected()) {
133 $resource = 'queue' . posix_getpid();
134 $this->conn = new SharingXMPP($this->plugin->host ?
135 $this->plugin->host :
136 $this->plugin->server,
139 $this->plugin->password,
140 $this->plugin->resource,
141 $this->plugin->server,
142 $this->plugin->debug ?
144 $this->plugin->debug ?
145 XMPPHP_Log::LEVEL_VERBOSE : null
151 $this->conn->addEventHandler('message', 'handle_xmpp_message', $this);
152 $this->conn->addEventHandler('reconnect', 'handle_xmpp_reconnect', $this);
153 $this->conn->setReconnectTimeout(600);
155 $this->conn->autoSubscribe();
156 $this->conn->useEncryption($this->plugin->encryption);
159 $this->conn->connect(true); // true = persistent connection
160 } catch (XMPPHP_Exception $e) {
161 common_log(LOG_ERR, $e->getMessage());
165 $this->conn->processUntil('session_start');
166 // TRANS: Presence announcement for XMPP.
167 $this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100);
175 if (!$this->conn || $this->conn->isDisconnected()) {
179 if (!isset($this->pingid)) {
185 common_log(LOG_DEBUG, "Sending ping #{$this->pingid}");
186 $this->conn->send("<iq from='{$this->plugin->daemonScreenname()}' to='{$this->plugin->server}' id='ping_{$this->pingid}' type='get'><ping xmlns='urn:xmpp:ping'/></iq>");
187 $this->lastping = $now;
191 function handle_xmpp_message(&$pl)
193 $this->plugin->enqueueIncomingRaw($pl);
198 * Callback for Jabber reconnect event
201 function handle_xmpp_reconnect(&$pl)
203 common_log(LOG_NOTICE, 'XMPP reconnected');
205 $this->conn->processUntil('session_start');
206 // TRANS: Message for XMPP reconnect.
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 // @todo 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);