| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  | <?php | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  | // This file is part of GNU social - https://www.gnu.org/software/social
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GNU social 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.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GNU social 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 GNU social.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |  * @copyright 2008, 2009 StatusNet, Inc. | 
					
						
							|  |  |  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  | defined('GNUSOCIAL') || die(); | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | use XMPPHP\Log; | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * 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 | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |     const PING_INTERVAL = 120; | 
					
						
							|  |  |  |     public $conn = null; | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |     protected $lastping = 0; | 
					
						
							|  |  |  |     protected $pingid = 0; | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Initialize connection to server. | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |      * @param $master | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |      * @return boolean true on success | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function start($master) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |         if (parent::start($master)) { | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |             $this->connect(); | 
					
						
							|  |  |  |             return true; | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |     protected function connect() | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (!$this->conn || $this->conn->isDisconnected()) { | 
					
						
							|  |  |  |             $resource = 'queue' . posix_getpid(); | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |             $this->conn = new SharingXMPP( | 
					
						
							|  |  |  |                 $this->plugin->host ?: $this->plugin->server, | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |                 $this->plugin->port, | 
					
						
							|  |  |  |                 $this->plugin->user, | 
					
						
							|  |  |  |                 $this->plugin->password, | 
					
						
							|  |  |  |                 $this->plugin->resource, | 
					
						
							|  |  |  |                 $this->plugin->server, | 
					
						
							|  |  |  |                 $this->plugin->debug ? | 
					
						
							|  |  |  |                     true : false, | 
					
						
							|  |  |  |                 $this->plugin->debug ? | 
					
						
							|  |  |  |                     Log::LEVEL_VERBOSE : null | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!$this->conn) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |             $this->conn->addEventHandler('message', function (&$pl) { | 
					
						
							|  |  |  |                 $this->handleXmppMessage($pl); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |             $this->conn->addEventHandler('reconnect', function ($pl) { | 
					
						
							|  |  |  |                 $this->handleXmppReconnect(); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |             $this->conn->addXPathHandler( | 
					
						
							|  |  |  |                 'iq/{urn:xmpp:ping}ping', | 
					
						
							|  |  |  |                 function (&$xml) { | 
					
						
							|  |  |  |                     $this->handleXmppPing($xml); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |             $this->conn->setReconnectTimeout(600); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $this->conn->autoSubscribe(); | 
					
						
							|  |  |  |             $this->conn->useEncryption($this->plugin->encryption); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $this->conn->connect(true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $this->conn->processUntil('session_start'); | 
					
						
							|  |  |  |             // TRANS: Presence announcement for XMPP.
 | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |             $this->sendPresence( | 
					
						
							|  |  |  |                 _m('Send me a message to post a notice'), | 
					
						
							|  |  |  |                 'available', | 
					
						
							|  |  |  |                 null, | 
					
						
							|  |  |  |                 'available', | 
					
						
							|  |  |  |                 100 | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         return $this->conn; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * sends a presence stanza on the XMPP network | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |      * @param string|null $status current status, free-form string | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |      * @param string $show structured status value | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |      * @param string|null $to recipient of presence, null for general | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |      * @param string $type type of status message, related to $show | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |      * @param int|null $priority priority of the presence | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |      * @return bool success value | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |     protected function sendPresence( | 
					
						
							|  |  |  |         ?string $status, | 
					
						
							|  |  |  |         string $show = 'available', | 
					
						
							|  |  |  |         ?string $to = null, | 
					
						
							|  |  |  |         string $type = 'available', | 
					
						
							|  |  |  |         ?int $priority = null | 
					
						
							|  |  |  |     ): bool { | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |         $this->connect(); | 
					
						
							|  |  |  |         if (!$this->conn || $this->conn->isDisconnected()) { | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |         $this->conn->presence($status, $show, $to, $type, $priority); | 
					
						
							|  |  |  |         return true; | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |     public function send_raw_message($data) | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |     { | 
					
						
							|  |  |  |         $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. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |     public function timeout() | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |     { | 
					
						
							|  |  |  |         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
 | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |         common_log(LOG_DEBUG, "Servicing the XMPP queue."); | 
					
						
							|  |  |  |         $this->stats('xmpp_process'); | 
					
						
							|  |  |  |         $this->conn->processTime(0); | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * 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(); | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |         if ($this->conn) { | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |             return array($this->conn->getSocket()); | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |             return array(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Idle processing for io manager's execution loop. | 
					
						
							|  |  |  |      * Send keepalive pings to server. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Side effect: kills process on exception from XMPP library. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |      * @param int $timeout | 
					
						
							| 
									
										
										
										
											2011-06-15 13:20:23 +02:00
										 |  |  |      * @todo FIXME: non-dying error handling | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |     public function idle($timeout = 0) | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |         if ( | 
					
						
							|  |  |  |             hrtime(true) - $this->lastping > self::PING_INTERVAL * 1000000000 | 
					
						
							|  |  |  |         ) { | 
					
						
							|  |  |  |             $this->sendPing(); | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |     protected function sendPing(): bool | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |     { | 
					
						
							|  |  |  |         $this->connect(); | 
					
						
							|  |  |  |         if (!$this->conn || $this->conn->isDisconnected()) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |         ++$this->pingid; | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         common_log(LOG_DEBUG, "Sending ping #{$this->pingid}"); | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01: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>"); | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |         $this->lastping = hrtime(true); | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |     protected function handleXmppMessage($pl): void | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2010-08-31 00:05:40 -04:00
										 |  |  |         $this->plugin->enqueueIncomingRaw($pl); | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |      * Callback for the XMPP reconnect event | 
					
						
							|  |  |  |      * @return void | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |     protected function handleXmppReconnect(): void | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |     { | 
					
						
							|  |  |  |         common_log(LOG_NOTICE, 'XMPP reconnected'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->conn->processUntil('session_start'); | 
					
						
							| 
									
										
										
										
											2011-06-15 13:20:23 +02:00
										 |  |  |         // TRANS: Message for XMPP reconnect.
 | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |         $this->sendPresence( | 
					
						
							|  |  |  |             _m('Send me a message to post a notice'), | 
					
						
							|  |  |  |             'available', | 
					
						
							|  |  |  |             null, | 
					
						
							|  |  |  |             'available', | 
					
						
							|  |  |  |             100 | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected function handleXmppPing($xml): void | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if ($xml->attrs['type'] !== 'get') { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->conn->send( | 
					
						
							|  |  |  |             "<iq from=\"{$xml->attrs['to']}\" to=\"{$xml->attrs['from']}\" " | 
					
						
							|  |  |  |             . "id=\"{$xml->attrs['id']}\" type=\"result\" />" | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * sends a "special" presence stanza on the XMPP network | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |      * @param string $type Type of presence | 
					
						
							|  |  |  |      * @param string $to JID to send presence to | 
					
						
							|  |  |  |      * @param string $show show value for presence | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |      * @param string $status status value for presence | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |      * @return bool success flag | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |      * @see sendPresence() | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |      */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-27 00:16:08 +03:00
										 |  |  |     protected function specialPresence( | 
					
						
							|  |  |  |         string $type, | 
					
						
							|  |  |  |         ?string $to = null, | 
					
						
							|  |  |  |         ?string $show = null, | 
					
						
							|  |  |  |         ?string $status = null | 
					
						
							|  |  |  |     ): bool { | 
					
						
							|  |  |  |         // @todo @fixme Why use this instead of sendPresence()?
 | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |         $this->connect(); | 
					
						
							|  |  |  |         if (!$this->conn || $this->conn->isDisconnected()) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-21 01:23:50 +01:00
										 |  |  |         $to = htmlspecialchars($to); | 
					
						
							| 
									
										
										
										
											2010-01-23 01:25:27 -05:00
										 |  |  |         $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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |