| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  | <?php | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  |  * StatusNet - the distributed open-source microblogging tool | 
					
						
							|  |  |  |  * Copyright (C) 2008, 2009, StatusNet, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU Affero General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * XMPP background connection manager for XMPP-using queue handlers, | 
					
						
							|  |  |  |  * allowing them to send outgoing messages on the right connection. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Input is handled during socket select loop, keepalive pings during idle. | 
					
						
							|  |  |  |  * Any incoming messages will be handled. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * In a multi-site queuedaemon.php run, one connection will be instantiated | 
					
						
							|  |  |  |  * for each site being handled by the current process that has XMPP enabled. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | class XmppManager extends ImManager | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     protected $lastping = null; | 
					
						
							|  |  |  |     protected $pingid = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public $conn = null; | 
					
						
							| 
									
										
										
										
											2011-06-15 13:20:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |     const PING_INTERVAL = 120; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Initialize connection to server. | 
					
						
							|  |  |  |      * @return boolean true on success | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function start($master) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if(parent::start($master)) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             $this->connect(); | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         }else{ | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function send_raw_message($data) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->connect(); | 
					
						
							|  |  |  |         if (!$this->conn || $this->conn->isDisconnected()) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $this->conn->send($data); | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Message pump is triggered on socket input, so we only need an idle() | 
					
						
							|  |  |  |      * call often enough to trigger our outgoing pings. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function timeout() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return self::PING_INTERVAL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Process XMPP events that have come in over the wire. | 
					
						
							|  |  |  |      * @fixme may kill process on XMPP error | 
					
						
							|  |  |  |      * @param resource $socket | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function handleInput($socket) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2011-03-22 11:54:23 -04:00
										 |  |  |         // Process the queue for as long as needed
 | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |         try { | 
					
						
							|  |  |  |             common_log(LOG_DEBUG, "Servicing the XMPP queue."); | 
					
						
							|  |  |  |             $this->stats('xmpp_process'); | 
					
						
							|  |  |  |             $this->conn->processTime(0); | 
					
						
							|  |  |  |         } catch (XMPPHP_Exception $e) { | 
					
						
							|  |  |  |             common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); | 
					
						
							|  |  |  |             die($e->getMessage()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Lists the IM connection socket to allow i/o master to wake | 
					
						
							|  |  |  |      * when input comes in here as well as from the queue source. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return array of resources | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function getSockets() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->connect(); | 
					
						
							|  |  |  |         if($this->conn){ | 
					
						
							|  |  |  |             return array($this->conn->getSocket()); | 
					
						
							|  |  |  |         }else{ | 
					
						
							|  |  |  |             return array(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Idle processing for io manager's execution loop. | 
					
						
							|  |  |  |      * Send keepalive pings to server. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Side effect: kills process on exception from XMPP library. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2011-06-15 13:20:23 +02:00
										 |  |  |      * @todo FIXME: non-dying error handling | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function idle($timeout=0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $now = time(); | 
					
						
							|  |  |  |         if (empty($this->lastping) || $now - $this->lastping > self::PING_INTERVAL) { | 
					
						
							|  |  |  |             try { | 
					
						
							|  |  |  |                 $this->send_ping(); | 
					
						
							|  |  |  |             } catch (XMPPHP_Exception $e) { | 
					
						
							|  |  |  |                 common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); | 
					
						
							|  |  |  |                 die($e->getMessage()); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function connect() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!$this->conn || $this->conn->isDisconnected()) { | 
					
						
							|  |  |  |             $resource = 'queue' . posix_getpid(); | 
					
						
							|  |  |  |             $this->conn = new Sharing_XMPP($this->plugin->host ? | 
					
						
							|  |  |  |                                     $this->plugin->host : | 
					
						
							|  |  |  |                                     $this->plugin->server, | 
					
						
							|  |  |  |                                     $this->plugin->port, | 
					
						
							|  |  |  |                                     $this->plugin->user, | 
					
						
							|  |  |  |                                     $this->plugin->password, | 
					
						
							|  |  |  |                                     $this->plugin->resource, | 
					
						
							|  |  |  |                                     $this->plugin->server, | 
					
						
							|  |  |  |                                     $this->plugin->debug ? | 
					
						
							|  |  |  |                                     true : false, | 
					
						
							|  |  |  |                                     $this->plugin->debug ? | 
					
						
							|  |  |  |                                     XMPPHP_Log::LEVEL_VERBOSE :  null | 
					
						
							|  |  |  |                                     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!$this->conn) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $this->conn->addEventHandler('message', 'handle_xmpp_message', $this); | 
					
						
							|  |  |  |             $this->conn->addEventHandler('reconnect', 'handle_xmpp_reconnect', $this); | 
					
						
							|  |  |  |             $this->conn->setReconnectTimeout(600); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $this->conn->autoSubscribe(); | 
					
						
							|  |  |  |             $this->conn->useEncryption($this->plugin->encryption); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             try { | 
					
						
							|  |  |  |                 $this->conn->connect(true); // true = persistent connection
 | 
					
						
							|  |  |  |             } catch (XMPPHP_Exception $e) { | 
					
						
							|  |  |  |                 common_log(LOG_ERR, $e->getMessage()); | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $this->conn->processUntil('session_start'); | 
					
						
							| 
									
										
										
										
											2011-06-15 13:20:23 +02:00
										 |  |  |             // TRANS: Presence announcement for XMPP.
 | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |             $this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return $this->conn; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function send_ping() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->connect(); | 
					
						
							|  |  |  |         if (!$this->conn || $this->conn->isDisconnected()) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $now = time(); | 
					
						
							|  |  |  |         if (!isset($this->pingid)) { | 
					
						
							|  |  |  |             $this->pingid = 0; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $this->pingid++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         common_log(LOG_DEBUG, "Sending ping #{$this->pingid}"); | 
					
						
							| 
									
										
										
										
											2010-08-31 00:04:16 -04:00
										 |  |  | 		$this->conn->send("<iq from='{" . $this->plugin->daemonScreenname() . "}' to='{$this->plugin->server}' id='ping_{$this->pingid}' type='get'><ping xmlns='urn:xmpp:ping'/></iq>"); | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |         $this->lastping = $now; | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function handle_xmpp_message(&$pl) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2010-08-31 00:05:40 -04:00
										 |  |  |         $this->plugin->enqueueIncomingRaw($pl); | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Callback for Jabber reconnect event | 
					
						
							|  |  |  |      * @param $pl | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function handle_xmpp_reconnect(&$pl) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         common_log(LOG_NOTICE, 'XMPP reconnected'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->conn->processUntil('session_start'); | 
					
						
							| 
									
										
										
										
											2011-06-15 13:20:23 +02:00
										 |  |  |         // TRANS: Message for XMPP reconnect.
 | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |         $this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * sends a presence stanza on the XMPP network | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $status   current status, free-form string | 
					
						
							|  |  |  |      * @param string $show     structured status value | 
					
						
							|  |  |  |      * @param string $to       recipient of presence, null for general | 
					
						
							|  |  |  |      * @param string $type     type of status message, related to $show | 
					
						
							|  |  |  |      * @param int    $priority priority of the presence | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return boolean success value | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function send_presence($status, $show='available', $to=null, | 
					
						
							|  |  |  |                                   $type = 'available', $priority=null) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->connect(); | 
					
						
							|  |  |  |         if (!$this->conn || $this->conn->isDisconnected()) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $this->conn->presence($status, $show, $to, $type, $priority); | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * sends a "special" presence stanza on the XMPP network | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $type   Type of presence | 
					
						
							|  |  |  |      * @param string $to     JID to send presence to | 
					
						
							|  |  |  |      * @param string $show   show value for presence | 
					
						
							|  |  |  |      * @param string $status status value for presence | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return boolean success flag | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @see send_presence() | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function special_presence($type, $to=null, $show=null, $status=null) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2011-06-17 15:41:45 +02:00
										 |  |  |         // @todo FIXME: why use this instead of send_presence()?
 | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |         $this->connect(); | 
					
						
							|  |  |  |         if (!$this->conn || $this->conn->isDisconnected()) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $to     = htmlspecialchars($to); | 
					
						
							|  |  |  |         $status = htmlspecialchars($status); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $out = "<presence"; | 
					
						
							|  |  |  |         if ($to) { | 
					
						
							|  |  |  |             $out .= " to='$to'"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if ($type) { | 
					
						
							|  |  |  |             $out .= " type='$type'"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if ($show == 'available' and !$status) { | 
					
						
							|  |  |  |             $out .= "/>"; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $out .= ">"; | 
					
						
							|  |  |  |             if ($show && ($show != 'available')) { | 
					
						
							|  |  |  |                 $out .= "<show>$show</show>"; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if ($status) { | 
					
						
							|  |  |  |                 $out .= "<status>$status</status>"; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $out .= "</presence>"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $this->conn->send($out); | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |