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')) {
27 * XMPP background connection manager for XMPP-using queue handlers,
28 * allowing them to send outgoing messages on the right connection.
30 * Input is handled during socket select loop, keepalive pings during idle.
31 * Any incoming messages will be handled.
33 * In a multi-site queuedaemon.php run, one connection will be instantiated
34 * for each site being handled by the current process that has XMPP enabled.
36 class XmppManager extends ImManager
38 const PING_INTERVAL = 120;
40 protected $lastping = null;
41 protected $pingid = null;
44 * Initialize connection to server.
46 * @return boolean true on success
48 public function start($master)
50 if (parent::start($master)) {
60 if (!$this->conn || $this->conn->isDisconnected()) {
61 $resource = 'queue' . posix_getpid();
62 $this->conn = new SharingXMPP($this->plugin->host ?
64 $this->plugin->server,
67 $this->plugin->password,
68 $this->plugin->resource,
69 $this->plugin->server,
70 $this->plugin->debug ?
72 $this->plugin->debug ?
73 Log::LEVEL_VERBOSE : null
79 $this->conn->addEventHandler('message', 'handle_xmpp_message', $this);
80 $this->conn->addEventHandler('reconnect', 'handle_xmpp_reconnect', $this);
81 $this->conn->setReconnectTimeout(600);
83 $this->conn->autoSubscribe();
84 $this->conn->useEncryption($this->plugin->encryption);
86 $this->conn->connect(true);
88 $this->conn->processUntil('session_start');
89 // TRANS: Presence announcement for XMPP.
90 $this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100);
96 * sends a presence stanza on the XMPP network
98 * @param string $status current status, free-form string
99 * @param string $show structured status value
100 * @param string $to recipient of presence, null for general
101 * @param string $type type of status message, related to $show
102 * @param int $priority priority of the presence
104 * @return boolean success value
107 function send_presence($status, $show = 'available', $to = null,
108 $type = 'available', $priority = null)
111 if (!$this->conn || $this->conn->isDisconnected()) {
114 $this->conn->presence($status, $show, $to, $type, $priority);
118 function send_raw_message($data)
121 if (!$this->conn || $this->conn->isDisconnected()) {
124 $this->conn->send($data);
129 * Message pump is triggered on socket input, so we only need an idle()
130 * call often enough to trigger our outgoing pings.
134 return self::PING_INTERVAL;
138 * Process XMPP events that have come in over the wire.
139 * @fixme may kill process on XMPP error
140 * @param resource $socket
142 public function handleInput($socket)
144 // Process the queue for as long as needed
145 common_log(LOG_DEBUG, "Servicing the XMPP queue.");
146 $this->stats('xmpp_process');
147 $this->conn->processTime(0);
151 * Lists the IM connection socket to allow i/o master to wake
152 * when input comes in here as well as from the queue source.
154 * @return array of resources
156 public function getSockets()
160 return array($this->conn->getSocket());
167 * Idle processing for io manager's execution loop.
168 * Send keepalive pings to server.
170 * Side effect: kills process on exception from XMPP library.
172 * @param int $timeout
173 * @todo FIXME: non-dying error handling
175 public function idle($timeout = 0)
178 if (empty($this->lastping) || $now - $this->lastping > self::PING_INTERVAL) {
186 if (!$this->conn || $this->conn->isDisconnected()) {
190 if (!isset($this->pingid)) {
196 common_log(LOG_DEBUG, "Sending ping #{$this->pingid}");
197 $this->conn->send("<iq from='{$this->plugin->daemonScreenname()}' to='{$this->plugin->server}' id='ping_{$this->pingid}' type='get'><ping xmlns='urn:xmpp:ping'/></iq>");
198 $this->lastping = $now;
202 function handle_xmpp_message(&$pl)
204 $this->plugin->enqueueIncomingRaw($pl);
209 * Callback for Jabber reconnect event
212 function handle_xmpp_reconnect(&$pl)
214 common_log(LOG_NOTICE, 'XMPP reconnected');
216 $this->conn->processUntil('session_start');
217 // TRANS: Message for XMPP reconnect.
218 $this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100);
222 * sends a "special" presence stanza on the XMPP network
224 * @param string $type Type of presence
225 * @param string $to JID to send presence to
226 * @param string $show show value for presence
227 * @param string $status status value for presence
229 * @return boolean success flag
231 * @see send_presence()
234 function special_presence($type, $to = null, $show = null, $status = null)
236 // @todo FIXME: why use this instead of send_presence()?
238 if (!$this->conn || $this->conn->isDisconnected()) {
242 $to = htmlspecialchars($to);
243 $status = htmlspecialchars($status);
250 $out .= " type='$type'";
252 if ($show == 'available' and !$status) {
256 if ($show && ($show != 'available')) {
257 $out .= "<show>$show</show>";
260 $out .= "<status>$status</status>";
262 $out .= "</presence>";
264 $this->conn->send($out);