From 610fa67928a0ada7afbff728bddd1fc5b3fb7cae Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Mon, 14 Apr 2025 20:33:13 +0200 Subject: [PATCH] [ticket/17493] Remove jabber as messenger method PHPBB-17493 --- .../default/container/services_messenger.yml | 9 - phpBB/phpbb/message/user_form.php | 2 +- phpBB/phpbb/messenger/method/jabber.php | 1236 ----------------- tests/messenger/method_jabber_test.php | 311 ----- tests/messenger/queue_test.php | 17 - tests/notification/base.php | 1 - .../notification_method_email_test.php | 1 - tests/notification/submit_post_base.php | 1 - 8 files changed, 1 insertion(+), 1577 deletions(-) delete mode 100644 phpBB/phpbb/messenger/method/jabber.php delete mode 100644 tests/messenger/method_jabber_test.php diff --git a/phpBB/config/default/container/services_messenger.yml b/phpBB/config/default/container/services_messenger.yml index 8645d2f17c..14bdf18df2 100644 --- a/phpBB/config/default/container/services_messenger.yml +++ b/phpBB/config/default/container/services_messenger.yml @@ -38,15 +38,6 @@ services: tags: - { name: messenger.method } - messenger.method.jabber: - class: phpbb\messenger\method\jabber - shared: false - parent: messenger.method.base - calls: - - [init, []] - tags: - - { name: messenger.method } - messenger.queue: class: phpbb\messenger\queue shared: false diff --git a/phpBB/phpbb/message/user_form.php b/phpBB/phpbb/message/user_form.php index 46a012297d..f52eabc7e9 100644 --- a/phpBB/phpbb/message/user_form.php +++ b/phpBB/phpbb/message/user_form.php @@ -34,7 +34,7 @@ class user_form extends form */ protected function get_user_row($user_id) { - $sql = 'SELECT user_id, username, user_colour, user_email, user_allow_viewemail, user_lang, user_jabber, user_notify_type + $sql = 'SELECT user_id, username, user_colour, user_email, user_allow_viewemail, user_lang, user_notify_type FROM ' . USERS_TABLE . ' WHERE user_id = ' . (int) $user_id . ' AND user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')'; diff --git a/phpBB/phpbb/messenger/method/jabber.php b/phpBB/phpbb/messenger/method/jabber.php deleted file mode 100644 index a4a2c07f61..0000000000 --- a/phpBB/phpbb/messenger/method/jabber.php +++ /dev/null @@ -1,1236 +0,0 @@ - - * @license GNU General Public License, version 2 (GPL-2.0) - * - * For full copyright and license information, please see - * the docs/CREDITS.txt file. - * - */ - -namespace phpbb\messenger\method; - -/** - * - * Based on Jabber class from Flyspray project - * - * @version class.jabber2.php 1595 2008-09-19 (0.9.9) - * @copyright 2006 Flyspray.org - * @author Florian Schmitz (floele) - * - * Slightly modified by Acyd Burn (2006) - * Refactored to a service (2024) - */ -class jabber extends base -{ - /** @var string */ - protected $connect_server; - - /** @var resource|null */ - protected $connection = null; - - /** @var bool */ - protected $enable_logging = true; - - /** @var array */ - protected $features = []; - - /** @var array */ - protected $jid = []; - - /** @var array */ - protected $log_array = []; - - /** @var string */ - protected $password; - - /** @var int */ - protected $port; - - /** @var string */ - protected $resource = 'functions_jabber.phpbb.php'; - - /** @var string */ - protected $server; - - /** @var array */ - protected $session = []; - - /** @var array */ - protected $stream_options = []; - - /** @var int */ - protected $timeout = 10; - - /** @var array */ - protected $to = []; - - /** @var bool */ - protected $use_ssl = false; - - /** @var string */ - protected $username; - - /** @var string Stream close handshake */ - private const STREAM_CLOSE_HANDSHAKE = ''; - - /** - * Set initial parameter values - * To init correctly, username() call should go before server() - * and ssl() call should go before port() and stream_options() calls. - * - * Example: - * $this->username($username) - * ->password($password) - * ->ssl($use_ssl) - * ->server($server) - * ->port($port) - * ->stream_options( - * 'verify_peer' => true, - * 'verify_peer_name' => true, - * 'allow_self_signed' => false, - * ); - * - * @return void - */ - public function init(): void - { - $this->username($this->config['jab_username']) - ->password($this->config['jab_password']) - ->ssl((bool) $this->config['jab_use_ssl']) - ->server($this->config['jab_host']) - ->port($this->config['jab_port']) - ->stream_options['ssl'] = [ - 'verify_peer' => $this->config['jab_verify_peer'], - 'verify_peer_name' => $this->config['jab_verify_peer_name'], - 'allow_self_signed' => $this->config['jab_allow_self_signed'], - ]; - } - - /** - * {@inheritDoc} - */ - public function get_id(): int - { - return self::NOTIFY_IM; - } - - /** - * {@inheritDoc} - */ - public function get_queue_object_name(): string - { - return 'jabber'; - } - - /** - * {@inheritDoc} - */ - public function is_enabled(): bool - { - return - !empty($this->config['jab_enable']) && - !empty($this->config['jab_host']) && - !empty($this->config['jab_username']) && - !empty($this->config['jab_password']); - } - - /** - * Set ssl context options - * See http://php.net/manual/en/context.ssl.php - * - * @param array $options SSL context options array - * @return self - */ - public function stream_options(array $options = []): self - { - if ($this->use_ssl) - { - // Change default stream options if needed - $this->stream_options['ssl'] = array_merge($this->stream_options['ssl'], $options); - } - - return $this; - } - - /** - * Set password to connect to server - * - * @param string $password Password to connect to server - * @return self - */ - public function password(string $password = ''): self - { - $this->password = html_entity_decode($password, ENT_COMPAT); - - return $this; - } - - /** - * Set use of ssl to connect to server - * - * @param bool $use_ssl Flag indicating use of ssl to connect to server - * @return self - */ - public function ssl(bool $use_ssl = false): self - { - $this->use_ssl = $use_ssl && self::can_use_ssl(); - - return $this; - } - - /** - * Set port to connect to server - * use_ssl flag should be set first - * - * @param int $port Port to connect to server - * @return self - */ - public function port(int $port = 5222): self - { - $this->port = ($port > 0) ? $port : 5222; - - // Change port if we use SSL - if ($this->port == 5222 && $this->use_ssl) - { - $this->port = 5223; - } - - return $this; - } - - /** - * Set username to connect to server - * - * @param string $username Username to connect to server - * @return self - */ - public function username(string $username = ''): self - { - if (str_contains($username, '@')) - { - $this->jid = explode('@', $username, 2); - $this->username = $this->jid[0]; - } - else - { - $this->username = $username; - } - - return $this; - } - - /** - * Set server to connect - * Username should be set first - * - * @param string $server Server to connect - * @return self - */ - public function server(string $server = ''): self - { - $this->connect_server = $server ?: 'localhost'; - $this->server = $this->jid[1] ?? $this->connect_server; - - return $this; - } - - /** - * Check if it's possible to use the SSL functionality - * - * @return bool - */ - public static function can_use_ssl(): bool - { - return @extension_loaded('openssl'); - } - - /** - * Check if it's possible to use TLS functionality - * - * @return bool - */ - public static function can_use_tls(): bool - { - if (!@extension_loaded('openssl') || !function_exists('stream_socket_enable_crypto') || !function_exists('stream_get_meta_data') || !function_exists('stream_set_blocking') || !function_exists('stream_get_wrappers')) - { - return false; - } - - /** - * Make sure the encryption stream is supported - * Also seem to work without the crypto stream if correctly compiled - - $streams = stream_get_wrappers(); - - if (!in_array('streams.crypto', $streams)) - { - return false; - } - */ - - return true; - } - - /** - * Sets the resource which is used. No validation is done here, only escaping - * - * @param string $name - * @return void - */ - public function set_resource(string $name): void - { - $this->resource = $name; - } - - /** - * Connect to the server - * - * @return bool - */ - public function connect(): bool - { -/* if (!$this->check_jid($this->username . '@' . $this->server)) - { - $this->add_to_log('Error: Jabber ID is not valid: ' . $this->username . '@' . $this->server); - return false; - }*/ - - $this->session['ssl'] = $this->use_ssl; - - if ($this->open_socket($this->connect_server, $this->port)) - { - $this->send_xml("\n"); - $this->send_xml("\n"); - } - else - { - $this->add_to_log('Error: connect() #2'); - return false; - } - - // Now we listen what the server has to say...and give appropriate responses - $this->response($this->listen()); - return true; - } - - /** - * Disconnect from the server - * - * @return bool - */ - public function disconnect(): bool - { - if ($this->connected()) - { - // disconnect gracefully - if (isset($this->session['sent_presence'])) - { - $this->send_presence('offline', '', true); - } - - $this->send(self::STREAM_CLOSE_HANDSHAKE); - // Check stream close handshake reply - $stream_close_reply = $this->listen(); - - if ($stream_close_reply != self::STREAM_CLOSE_HANDSHAKE) - { - $this->add_to_log("Error: Unexpected stream close handshake reply ”{$stream_close_reply}”"); - } - - $this->session = []; - /** @psalm-suppress InvalidPropertyAssignmentValue */ - return fclose($this->connection); - } - - return false; - } - - /** - * Check if it's still connected to the server - * - * @return bool - */ - public function connected(): bool - { - return is_resource($this->connection) && !feof($this->connection); - } - - /** - * Initiates login (using data from contructor, after calling connect()) - * - * @return bool - */ - public function login(): bool - { - if (empty($this->features)) - { - $this->add_to_log('Error: No feature information from server available.'); - return false; - } - - return $this->response($this->features); - } - - /** - * {@inheritDoc} - */ - public function set_addresses(array $user_row): void - { - if (isset($user_row['user_jabber']) && $user_row['user_jabber']) - { - $this->to($user_row['user_jabber'], (isset($user_row['username']) ? $user_row['username'] : '')); - } - } - - /** - * Sets jabber contact to send message to - * - * @param string $address Jabber "To" recipient address - * @param string $realname Jabber "To" recipient name - * @return void - */ - public function to(string $address, string $realname = ''): void - { - // IM-Addresses could be empty - if (!trim($address)) - { - return; - } - - $pos = !empty($this->to) ? count($this->to) : 0; - $this->to[$pos]['uid'] = trim($address); - $this->to[$pos]['name'] = trim($realname); - } - - /** - * Resets all the data (address, template file, etc) to default - */ - public function reset(): void - { - $this->subject = $this->msg = ''; - $this->additional_headers = $this->to = []; - $this->use_queue = true; - unset($this->template); - } - - /** - * {@inheritDoc} - */ - public function set_use_queue(bool $use_queue = true): void - { - $this->use_queue = !$this->config['jab_package_size'] ? false : $use_queue; - } - - /** - * {@inheritDoc} - */ - public function process_queue(array &$queue_data): void - { - $queue_object_name = $this->get_queue_object_name(); - $messages_count = count($queue_data[$queue_object_name]['data']); - - if (!$this->is_enabled() || !$messages_count) - { - unset($queue_data[$queue_object_name]); - return; - } - - @set_time_limit(0); - - $package_size = $queue_data[$queue_object_name]['package_size'] ?? 0; - $num_items = (!$package_size || $messages_count < $package_size) ? $messages_count : $package_size; - - for ($i = 0; $i < $num_items; $i++) - { - // Make variables available... - extract(array_shift($queue_data[$queue_object_name]['data'])); - - if (!$this->connect()) - { - $this->error($this->user->lang['ERR_JAB_CONNECT'] . '
' . $this->get_log()); - return; - } - - if (!$this->login()) - { - $this->error($this->user->lang['ERR_JAB_AUTH'] . '
' . $this->get_log()); - return; - } - - foreach ($addresses as $address) - { - if ($this->send_message($address, $msg, $subject) === false) - { - $this->error($this->get_log()); - continue; - } - } - } - - // No more data for this object? Unset it - if (!count($queue_data[$queue_object_name]['data'])) - { - unset($queue_data[$queue_object_name]); - } - - $this->disconnect(); - } - - /** - * {@inheritDoc} - */ - public function send(): bool - { - $this->prepare_message(); - - if (empty($this->to)) - { - $this->add_to_log('Error: Could not send, recepient addresses undefined.'); - return false; - } - - $addresses = []; - foreach ($this->to as $uid_ary) - { - $addresses[] = $uid_ary['uid']; - } - $addresses = array_unique($addresses); - - if (!$this->use_queue) - { - if (!$this->connect()) - { - $this->error($this->user->lang['ERR_JAB_CONNECT'] . '
' . $this->get_log()); - return false; - } - - if (!$this->login()) - { - $this->error($this->user->lang['ERR_JAB_AUTH'] . '
' . $this->get_log()); - return false; - } - - foreach ($addresses as $address) - { - if ($this->send_message($address, $this->msg, $this->subject) === false) - { - $this->error($this->get_log()); - continue; - } - } - - $this->disconnect(); - } - else - { - $this->queue->init('jabber', $this->config['jab_package_size']); - $this->queue->put('jabber', [ - 'addresses' => $addresses, - 'subject' => $this->subject, - 'msg' => $this->msg, - ]); - } - unset($addresses); - - $this->reset(); - - return true; - } - - /** - * Send data to the Jabber server - * - * @param string $xml - * @return int|bool - */ - public function send_xml(string $xml): int|bool - { - if ($this->connected()) - { - $xml = trim($xml); - return fwrite($this->connection, $xml); - } - else - { - $this->add_to_log('Error: Could not send, connection lost (flood?).'); - return false; - } - } - - /** - * Open socket - * - * @param string $server Host to connect to - * @param int $port Port number - * - * @return bool - */ - public function open_socket(string $server, int $port): bool - { - if (@function_exists('dns_get_record')) - { - $record = @dns_get_record("_xmpp-client._tcp.$server", DNS_SRV); - if (!empty($record) && !empty($record[0]['target'])) - { - $server = $record[0]['target']; - } - } - - $remote_socket = $this->use_ssl ? 'ssl://' . $server . ':' . $port : $server . ':' . $port; - $socket_context = stream_context_create($this->stream_options); - - if ($this->connection = @stream_socket_client($remote_socket, $errorno, $errorstr, $this->timeout, STREAM_CLIENT_CONNECT, $socket_context)) - { - stream_set_blocking($this->connection, 0); - stream_set_timeout($this->connection, 60); - - return true; - } - - // Apparently an error occurred... - $this->add_to_log('Error: open_socket() - ' . $errorstr); - return false; - } - - /** - * Get connection log - * - * @return string - */ - public function get_log(): string - { - if ($this->enable_logging && count($this->log_array)) - { - return implode("

", $this->log_array); - } - - return ''; - } - - /** - * Add information to log - * - * @param string $string Log entry - * @return void - */ - protected function add_to_log(string $string): void - { - if ($this->enable_logging) - { - $this->log_array[] = utf8_htmlspecialchars($string); - } - } - - /** - * Listens to the connection until it gets data or the timeout is reached. - * Thus, it should only be called if data is expected to be received. - * - * @param int $timeout Connection timeout - * @param bool $wait Flag indicating if it should wait for the responce until timeout - * @return bool|array Either false for timeout or an array with the received data - */ - public function listen(int $timeout = 10, bool $wait = false): bool|array - { - if (!$this->connected()) - { - return false; - } - - // Wait for a response until timeout is reached - $start = time(); - $data = ''; - - do - { - $read = trim(fread($this->connection, 4096)); - $data .= $read; - } - while (time() <= $start + $timeout && !feof($this->connection) && ($wait || $data == '' || $read != '' || (substr(rtrim($data), -1) != '>'))); - - if ($data != '') - { - return $this->xmlize($data); - } - else - { - $this->add_to_log('Timeout, no response from server.'); - return false; - } - } - - /** - * Initiates account registration (based on data used for constructor) - * - * @return bool|null - */ - public function register(): bool|null - { - if (!isset($this->session['id']) || isset($this->session['jid'])) - { - $this->add_to_log('Error: Cannot initiate registration.'); - return false; - } - - $this->send_xml(""); - return $this->response($this->listen()); - } - - /** - * Sets account presence. No additional info required (default is "online" status) - * - * @param string $message Account status (online, offline) - * @param string $type Status type (dnd, away, chat, xa or nothing) - * @param bool $unavailable Set to true to make unavailable status - * @return int|bool - */ - function send_presence(string $message = '', string $type = '', bool $unavailable = false): int|bool - { - if (!isset($this->session['jid'])) - { - $this->add_to_log('ERROR: send_presence() - Cannot set presence at this point, no jid given.'); - return false; - } - - $type = strtolower($type); - $type = (in_array($type, array('dnd', 'away', 'chat', 'xa'))) ? ''. $type .'' : ''; - - $unavailable = ($unavailable) ? " type='unavailable'" : ''; - $message = ($message) ? '' . utf8_htmlspecialchars($message) .'' : ''; - - $this->session['sent_presence'] = !$unavailable; - - return $this->send_xml("" . $type . $message . ''); - } - - /** - * This handles all the different XML elements - * - * @param array $xml - * @return bool - */ - function response(array $xml): bool - { - if (!count($xml)) - { - return false; - } - - // did we get multiple elements? do one after another - // array('message' => ..., 'presence' => ...) - if (count($xml) > 1) - { - foreach ($xml as $key => $value) - { - $this->response(array($key => $value)); - } - return true; - } - else if (is_array(reset($xml)) && count(reset($xml)) > 1) - { - // or even multiple elements of the same type? - // array('message' => array(0 => ..., 1 => ...)) - foreach (reset($xml) as $value) - { - $this->response(array(key($xml) => array(0 => $value))); - } - return true; - } - - switch (key($xml)) - { - case 'stream:stream': - // Connection initialised (or after authentication). Not much to do here... - - if (isset($xml['stream:stream'][0]['#']['stream:features'])) - { - // we already got all info we need - $this->features = $xml['stream:stream'][0]['#']; - } - else - { - $this->features = $this->listen(); - } - - $second_time = isset($this->session['id']); - $this->session['id'] = isset($xml['stream:stream'][0]['@']['id']) ? $xml['stream:stream'][0]['@']['id'] : ''; - - if ($second_time) - { - // If we are here for the second time after TLS, we need to continue logging in - return $this->login(); - } - - // go on with authentication? - if (isset($this->features['stream:features'][0]['#']['bind']) || !empty($this->session['tls'])) - { - return $this->response($this->features); - } - return false; - break; - - case 'stream:features': - // Resource binding after successful authentication - if (isset($this->session['authenticated'])) - { - // session required? - $this->session['sess_required'] = isset($xml['stream:features'][0]['#']['session']); - - $this->send_xml(" - - " . utf8_htmlspecialchars($this->resource) . ' - - '); - return $this->response($this->listen()); - } - - // Let's use TLS if SSL is not enabled and we can actually use it - if (!$this->session['ssl'] && self::can_use_tls() && self::can_use_ssl() && isset($xml['stream:features'][0]['#']['starttls'])) - { - $this->add_to_log('Switching to TLS.'); - $this->send_xml("\n"); - return $this->response($this->listen()); - } - - // Does the server support SASL authentication? - - // I hope so, because we do (and no other method). - if (isset($xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns']) && $xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns'] == 'urn:ietf:params:xml:ns:xmpp-sasl') - { - // Now decide on method - $methods = array(); - - foreach ($xml['stream:features'][0]['#']['mechanisms'][0]['#']['mechanism'] as $value) - { - $methods[] = $value['#']; - } - - // we prefer DIGEST-MD5 - // we don't want to use plain authentication (neither does the server usually) if no encryption is in place - - // http://www.xmpp.org/extensions/attic/jep-0078-1.7.html - // The plaintext mechanism SHOULD NOT be used unless the underlying stream is encrypted (using SSL or TLS) - // and the client has verified that the server certificate is signed by a trusted certificate authority. - - if (in_array('DIGEST-MD5', $methods)) - { - $this->send_xml(""); - } - else if (in_array('PLAIN', $methods) && ($this->session['ssl'] || !empty($this->session['tls']))) - { - // http://www.ietf.org/rfc/rfc4616.txt (PLAIN SASL Mechanism) - $this->send_xml("" - . base64_encode($this->username . '@' . $this->server . chr(0) . $this->username . chr(0) . $this->password) . - ''); - } - else if (in_array('ANONYMOUS', $methods)) - { - $this->send_xml(""); - } - else - { - // not good... - $this->add_to_log('Error: No authentication method supported.'); - $this->disconnect(); - return false; - } - - return $this->response($this->listen()); - } - else - { - // ok, this is it. bye. - $this->add_to_log('Error: Server does not offer SASL authentication.'); - $this->disconnect(); - return false; - } - break; - - case 'challenge': - // continue with authentication...a challenge literally -_- - $decoded = base64_decode($xml['challenge'][0]['#']); - $decoded = $this->parse_data($decoded); - - if (!isset($decoded['digest-uri'])) - { - $decoded['digest-uri'] = 'xmpp/'. $this->server; - } - - // better generate a cnonce, maybe it's needed - $decoded['cnonce'] = base64_encode(md5(uniqid(mt_rand(), true))); - - // second challenge? - if (isset($decoded['rspauth'])) - { - $this->send_xml(""); - } - else - { - // Make sure we only use 'auth' for qop (relevant for $this->encrypt_password()) - // If the is choking up on the changed parameter we may need to adjust encrypt_password() directly - if (isset($decoded['qop']) && $decoded['qop'] != 'auth' && strpos($decoded['qop'], 'auth') !== false) - { - $decoded['qop'] = 'auth'; - } - - $response = array( - 'username' => $this->username, - 'response' => $this->encrypt_password(array_merge($decoded, array('nc' => '00000001'))), - 'charset' => 'utf-8', - 'nc' => '00000001', - 'qop' => 'auth', // only auth being supported - ); - - foreach (array('nonce', 'digest-uri', 'realm', 'cnonce') as $key) - { - if (isset($decoded[$key])) - { - $response[$key] = $decoded[$key]; - } - } - - $this->send_xml("" . base64_encode($this->implode_data($response)) . ''); - } - - return $this->response($this->listen()); - break; - - case 'failure': - $this->add_to_log('Error: Server sent "failure".'); - $this->disconnect(); - return false; - break; - - case 'proceed': - // continue switching to TLS - $meta = stream_get_meta_data($this->connection); - stream_set_blocking($this->connection, 1); - - if (!stream_socket_enable_crypto($this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) - { - $this->add_to_log('Error: TLS mode change failed.'); - return false; - } - - stream_set_blocking($this->connection, $meta['blocked']); - $this->session['tls'] = true; - - // new stream - $this->send_xml("\n"); - $this->send_xml("\n"); - - return $this->response($this->listen()); - break; - - case 'success': - // Yay, authentication successful. - $this->send_xml("\n"); - $this->session['authenticated'] = true; - - // we have to wait for another response - return $this->response($this->listen()); - break; - - case 'iq': - // we are not interested in IQs we did not expect - if (!isset($xml['iq'][0]['@']['id'])) - { - return false; - } - - // multiple possibilities here - switch ($xml['iq'][0]['@']['id']) - { - case 'bind_1': - $this->session['jid'] = $xml['iq'][0]['#']['bind'][0]['#']['jid'][0]['#']; - - // and (maybe) yet another request to be able to send messages *finally* - if ($this->session['sess_required']) - { - $this->send_xml(" - - "); - return $this->response($this->listen()); - } - - return true; - break; - - case 'sess_1': - return true; - break; - - case 'reg_1': - $this->send_xml(" - - " . utf8_htmlspecialchars($this->username) . " - " . utf8_htmlspecialchars($this->password) . " - - "); - return $this->response($this->listen()); - break; - - case 'reg_2': - // registration end - if (isset($xml['iq'][0]['#']['error'])) - { - $this->add_to_log('Warning: Registration failed.'); - return false; - } - return true; - break; - - case 'unreg_1': - return true; - break; - - default: - $this->add_to_log('Notice: Received unexpected IQ.'); - return false; - break; - } - break; - - case 'message': - // we are only interested in content... - if (!isset($xml['message'][0]['#']['body'])) - { - return false; - } - - $message['body'] = $xml['message'][0]['#']['body'][0]['#']; - $message['from'] = $xml['message'][0]['@']['from']; - - if (isset($xml['message'][0]['#']['subject'])) - { - $message['subject'] = $xml['message'][0]['#']['subject'][0]['#']; - } - $this->session['messages'][] = $message; - return true; - break; - - default: - // hm...don't know this response - $this->add_to_log('Notice: Unknown server response'); - return false; - break; - } - } - - /** - * Send Jabber message - * - * @param string $to Recepient usermane - * @param string $text Message text - * @param string $subject Message subject - * @param string $type Message type - * - * @return int|bool - */ - public function send_message(string $to, string $text, string $subject = '', string $type = 'normal'): int|bool - { - if (!isset($this->session['jid'])) - { - return false; - } - - if (!in_array($type, array('chat', 'normal', 'error', 'groupchat', 'headline'))) - { - $type = 'normal'; - } - - return $this->send_xml(" - " . utf8_htmlspecialchars($subject) . " - " . utf8_htmlspecialchars($text) . " - " - ); - } - - /** - * Encrypts a password as in RFC 2831 - * - * @param array $data Needs data from the client-server connection - * @return string - */ - public function encrypt_password(array $data): string - { - // let's me think about again... - foreach (array('realm', 'cnonce', 'digest-uri') as $key) - { - if (!isset($data[$key])) - { - $data[$key] = ''; - } - } - - $pack = md5($this->username . ':' . $data['realm'] . ':' . $this->password); - - if (isset($data['authzid'])) - { - $a1 = pack('H32', $pack) . sprintf(':%s:%s:%s', $data['nonce'], $data['cnonce'], $data['authzid']); - } - else - { - $a1 = pack('H32', $pack) . sprintf(':%s:%s', $data['nonce'], $data['cnonce']); - } - - // should be: qop = auth - $a2 = 'AUTHENTICATE:'. $data['digest-uri']; - - return md5(sprintf('%s:%s:%s:%s:%s:%s', md5($a1), $data['nonce'], $data['nc'], $data['cnonce'], $data['qop'], md5($a2))); - } - - /** - * Parse data string like a="b",c="d",... or like a="a, b", c, d="e", f=g,... - * - * @param string $data - * @return array a => b ... - */ - public function parse_data(string $data): array - { - $data = explode(',', $data); - $pairs = array(); - $key = false; - - foreach ($data as $pair) - { - $dd = strpos($pair, '='); - - if ($dd) - { - $key = trim(substr($pair, 0, $dd)); - $pairs[$key] = trim(trim(substr($pair, $dd + 1)), '"'); - } - else if (strpos(strrev(trim($pair)), '"') === 0 && $key) - { - // We are actually having something left from "a, b" values, add it to the last one we handled. - $pairs[$key] .= ',' . trim(trim($pair), '"'); - continue; - } - } - - return $pairs; - } - - /** - * The opposite of jabber::parse_data() - * - * @param array $data Data array - * @return string - */ - public function implode_data(array $data): string - { - $return = array(); - foreach ($data as $key => $value) - { - $return[] = $key . '="' . $value . '"'; - } - return implode(',', $return); - } - - /** - * xmlize() - * @author Hans Anderson - * @copyright Hans Anderson / http://www.hansanderson.com/php/xml/ - * - * @param string $data Data string - * @param string|int|bool $skip_white New XML parser option value - * @param string $encoding Encoding value - * @return array - */ - function xmlize(string $data, string|int|bool $skip_white = 1, string $encoding = 'UTF-8'): array - { - $data = trim($data); - - if (substr($data, 0, 5) != ''. $data . ''; - } - - $vals = $index = $array = array(); - $parser = xml_parser_create($encoding); - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); - xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, $skip_white); - xml_parse_into_struct($parser, $data, $vals, $index); - xml_parser_free($parser); - - $i = 0; - $tagname = $vals[$i]['tag']; - - $array[$tagname][0]['@'] = (isset($vals[$i]['attributes'])) ? $vals[$i]['attributes'] : array(); - $array[$tagname][0]['#'] = $this->_xml_depth($vals, $i); - - if (substr($data, 0, 5) != '_xml_depth($vals, $i); - - break; - - case 'cdata': - array_push($children, $vals[$i]['value']); - break; - - case 'complete': - - $tagname = $vals[$i]['tag']; - $size = (isset($children[$tagname])) ? count($children[$tagname]) : 0; - $children[$tagname][$size]['#'] = (isset($vals[$i]['value'])) ? $vals[$i]['value'] : array(); - - if (isset($vals[$i]['attributes'])) - { - $children[$tagname][$size]['@'] = $vals[$i]['attributes']; - } - - break; - - case 'close': - return $children; - break; - } - } - - return $children; - } -} diff --git a/tests/messenger/method_jabber_test.php b/tests/messenger/method_jabber_test.php deleted file mode 100644 index d61cd9b1b1..0000000000 --- a/tests/messenger/method_jabber_test.php +++ /dev/null @@ -1,311 +0,0 @@ - - * @license GNU General Public License, version 2 (GPL-2.0) - * - * For full copyright and license information, please see - * the docs/CREDITS.txt file. - * - */ - -class phpbb_messenger_method_jabber_test extends \phpbb_test_case -{ - protected $assets_bag; - protected $cache_path; - protected config $config; - protected $dispatcher; - protected $extension_manager; - protected jabber $method_jabber; - protected $method_base; - protected $language; - protected $log; - protected $path_helper; - protected queue $queue; - protected $request; - protected $twig_extensions_collection; - protected $twig_lexer; - protected $user; - - public function setUp(): void - { - global $config, $request, $symfony_request, $user, $phpbb_root_path, $phpEx; - - $this->assets_bag = new assets_bag(); - $this->cache_path = $phpbb_root_path . 'cache/' . PHPBB_ENVIRONMENT . '/twig'; - $this->config = new config([ - 'force_server_vars' => false, - 'jab_username' => 'test', - 'jab_password' => 'password', - 'jab_use_ssl' => false, - 'jab_host' => 'localhost', - 'jab_port' => 5222, - 'jab_verify_peer' => true, - 'jab_verify_peer_name' => true, - 'jab_allow_self_signed' => false, - ]); - $config = $this->config; - $this->dispatcher = $this->getMockBuilder('\phpbb\event\dispatcher') - ->disableOriginalConstructor() - ->getMock(); - $this->filesystem = new \phpbb\filesystem\filesystem(); - $this->language = new language(new language_file_loader($phpbb_root_path, $phpEx)); - $this->queue = $this->createMock(queue::class); - $this->request = new phpbb_mock_request(); - $request = $this->request; - $this->symfony_request = new symfony_request(new phpbb_mock_request()); - $symfony_request = $this->symfony_request; - $this->user = $this->getMockBuilder('\phpbb\user') - ->setConstructorArgs([$this->language, '\phpbb\datetime']) - ->getMock(); - $user = $this->user; - $user->page['root_script_path'] = 'phpbb/'; - $this->user->host = 'yourdomain.com'; - $this->path_helper = new path_helper( - $this->symfony_request, - $this->request, - $phpbb_root_path, - $phpEx - ); - $phpbb_container = new phpbb_mock_container_builder; - $this->twig_extensions_collection = new \phpbb\di\service_collection($phpbb_container); - $twig = new \phpbb\template\twig\environment( - $this->assets_bag, - $this->config, - $this->filesystem, - $this->path_helper, - $this->cache_path, - null, - new \phpbb\template\twig\loader(''), - $this->dispatcher, - array( - 'cache' => false, - 'debug' => false, - 'auto_reload' => true, - 'autoescape' => false, - ) - ); - $this->twig_lexer = new \phpbb\template\twig\lexer($twig); - $this->extension_manager = new phpbb_mock_extension_manager( - __DIR__ . '/', - array( - 'vendor2/foo' => array( - 'ext_name' => 'vendor2/foo', - 'ext_active' => '1', - 'ext_path' => 'ext/vendor2/foo/', - ), - ) - ); - $this->log = $this->createMock(\phpbb\log\log_interface::class); - - $this->method_jabber = new jabber( - $this->assets_bag, - $this->config, - $this->dispatcher, - $this->language, - $this->queue, - $this->path_helper, - $this->request, - $this->twig_extensions_collection, - $this->twig_lexer, - $this->user, - $phpbb_root_path, - $this->cache_path, - $this->extension_manager, - $this->log - ); - } - - public function test_miscellaneous() - { - $this->method_jabber->init(); - $this->assertEquals(messenger_interface::NOTIFY_IM, $this->method_jabber->get_id()); - $this->assertEquals('jabber', $this->method_jabber->get_queue_object_name()); - $this->assertFalse($this->method_jabber->is_enabled()); - $this->config->set('jab_enable', true); - $this->assertTrue($this->method_jabber->is_enabled()); - $this->assertEquals(@extension_loaded('openssl'), $this->method_jabber->can_use_ssl()); - } - - public function test_stream_options() - { - $this->method_jabber->init(); - $this->assertEquals($this->method_jabber, $this->method_jabber->stream_options([ - 'allow_self_signed' => true, - ])); - - $stream_options_reflection = new \ReflectionProperty($this->method_jabber, 'stream_options'); - $stream_options = $stream_options_reflection->getValue($this->method_jabber); - $this->assertEquals([ - 'ssl' => [ - 'allow_self_signed' => false, - 'verify_peer' => true, - 'verify_peer_name' => true, - ], - ], $stream_options); - - $this->method_jabber->ssl(true); - - $this->assertEquals($this->method_jabber, $this->method_jabber->stream_options([ - 'allow_self_signed' => true, - ])); - $stream_options = $stream_options_reflection->getValue($this->method_jabber); - $this->assertEquals([ - 'ssl' => [ - 'allow_self_signed' => true, - 'verify_peer' => true, - 'verify_peer_name' => true, - ], - ], $stream_options); - } - - public function test_port_ssl_switch() - { - $port_reflection = new \ReflectionProperty($this->method_jabber, 'port'); - - $this->method_jabber->port(); - $this->assertEquals(5222, $port_reflection->getValue($this->method_jabber)); - - $this->method_jabber->ssl(true) - ->port(); - $this->assertEquals(5223, $port_reflection->getValue($this->method_jabber)); - } - - public function test_username() - { - $jabber_reflection = new \ReflectionClass($this->method_jabber); - $username_reflection = $jabber_reflection->getProperty('username'); - $jid_reflection = $jabber_reflection->getProperty('jid'); - - $this->method_jabber->username('foo@bar'); - $this->assertEquals(['foo', 'bar'], $jid_reflection->getValue($this->method_jabber)); - $this->assertEquals('foo', $username_reflection->getValue($this->method_jabber)); - - $this->method_jabber->username('bar@baz@qux'); - $this->assertEquals(['bar', 'baz@qux'], $jid_reflection->getValue($this->method_jabber)); - $this->assertEquals('bar', $username_reflection->getValue($this->method_jabber)); - } - - public function test_server() - { - $jabber_reflection = new \ReflectionClass($this->method_jabber); - $connect_server_reflection = $jabber_reflection->getProperty('connect_server'); - $server_reflection = $jabber_reflection->getProperty('server'); - - $this->method_jabber->server(); - $this->assertEquals('localhost', $connect_server_reflection->getValue($this->method_jabber)); - $this->assertEquals('localhost', $server_reflection->getValue($this->method_jabber)); - - $this->method_jabber->server('foobar.com'); - $this->assertEquals('foobar.com', $connect_server_reflection->getValue($this->method_jabber)); - $this->assertEquals('foobar.com', $server_reflection->getValue($this->method_jabber)); - - $this->method_jabber->username('foo@bar.com'); - $this->method_jabber->server('foobar.com'); - $this->assertEquals('foobar.com', $connect_server_reflection->getValue($this->method_jabber)); - $this->assertEquals('bar.com', $server_reflection->getValue($this->method_jabber)); - } - - public function test_encrypt_password() - { - $this->method_jabber->init(); - $this->method_jabber->password('password'); - $data = [ - 'realm' => 'example.com', - 'nonce' => '12345', - 'cnonce' => 'abcde', - 'digest-uri' => 'xmpp/example.com', - 'nc' => '00000001', - 'qop' => 'auth', - ]; - - $expected = md5(sprintf( - '%s:%s:%s:%s:%s:%s', - md5(pack('H32', md5('test:example.com:password')) . ':12345:abcde'), - $data['nonce'], - $data['nc'], - $data['cnonce'], - $data['qop'], - md5('AUTHENTICATE:xmpp/example.com') - )); - $this->assertEquals($expected, $this->method_jabber->encrypt_password($data)); - } - - public function test_parse_data() - { - $data = 'key1="value1",key2="value2",key3="value3"'; - $expected = [ - 'key1' => 'value1', - 'key2' => 'value2', - 'key3' => 'value3', - ]; - - $this->assertEquals($expected, $this->method_jabber->parse_data($data)); - } - - public function test_implode_data() - { - $data = [ - 'key1' => 'value1', - 'key2' => 'value2', - 'key3' => 'value3', - ]; - $expected = 'key1="value1",key2="value2",key3="value3"'; - - $this->assertEquals($expected, $this->method_jabber->implode_data($data)); - } - - public function test_xmlize() - { - $xml = 'content'; - $result = $this->method_jabber->xmlize($xml); - - $this->assertArrayHasKey('root', $result); - $this->assertArrayHasKey('child', $result['root'][0]['#']); - $this->assertEquals('content', $result['root'][0]['#']['child'][0]['#']); - $this->assertEquals(['key' => 'value'], $result['root'][0]['#']['child'][0]['@']); - } - - public function test_send_xml() - { - $jabber_mock = $this->getMockBuilder(jabber::class) - ->setConstructorArgs([ - $this->assets_bag, - $this->config, - $this->dispatcher, - $this->language, - $this->queue, - $this->path_helper, - $this->request, - $this->twig_extensions_collection, - $this->twig_lexer, - $this->user, - '', - '', - $this->extension_manager, - $this->log, - ]) - ->onlyMethods(['send_xml']) - ->getMock(); - - $jabber_mock->expects($this->once()) - ->method('send_xml') - ->with('Test') - ->willReturn(true); - - $this->assertTrue($jabber_mock->send_xml('Test')); - } -} diff --git a/tests/messenger/queue_test.php b/tests/messenger/queue_test.php index e99893d577..cb058cdc75 100644 --- a/tests/messenger/queue_test.php +++ b/tests/messenger/queue_test.php @@ -239,27 +239,10 @@ class phpbb_messenger_queue_test extends phpbb_test_case ], $queue_data['email']); unset($queue_data['email']); }); - $jabber_method = $this->getMockBuilder('phpbb\messenger\method\jabber') - ->disableOriginalConstructor() - ->onlyMethods(['get_queue_object_name', 'process_queue']) - ->getMock(); - $jabber_method->method('get_queue_object_name') - ->willReturn('jabber'); - $jabber_method->method('process_queue') - ->willReturnCallback(function(array &$queue_data) { - $this->assertEquals([ - 'package_size' => 10, - 'data' => [ - ['data2'], - ], - ], $queue_data['jabber']); - unset($queue_data['jabber']); - }); $this->service_collection->method('getIterator') ->willReturn(new \ArrayIterator([ 'email' => $email_method, - 'jabber' => $jabber_method, ])); // Process the queue diff --git a/tests/notification/base.php b/tests/notification/base.php index 07fef3a1c5..ec6319c420 100644 --- a/tests/notification/base.php +++ b/tests/notification/base.php @@ -148,7 +148,6 @@ abstract class phpbb_tests_notification_base extends phpbb_database_test_case $messenger_method_collection = new \phpbb\di\service_collection($phpbb_container); $messenger_method_collection->add('messenger.method.email'); - $messenger_method_collection->add('messenger.method.jabber'); $phpbb_container->set('messenger.method_collection', $messenger_method_collection); $phpbb_container->compile(); diff --git a/tests/notification/notification_method_email_test.php b/tests/notification/notification_method_email_test.php index 15811cc968..6400c8bb1d 100644 --- a/tests/notification/notification_method_email_test.php +++ b/tests/notification/notification_method_email_test.php @@ -107,7 +107,6 @@ class notification_method_email_test extends phpbb_tests_notification_base $messenger_method_collection = new \phpbb\di\service_collection($phpbb_container); $messenger_method_collection->add('messenger.method.email'); - $messenger_method_collection->add('messenger.method.jabber'); $phpbb_container->set('messenger.method_collection', $messenger_method_collection); $this->notification_method_email = $this->getMockBuilder('\phpbb\notification\method\email') diff --git a/tests/notification/submit_post_base.php b/tests/notification/submit_post_base.php index 57b1242509..08505667d7 100644 --- a/tests/notification/submit_post_base.php +++ b/tests/notification/submit_post_base.php @@ -161,7 +161,6 @@ abstract class phpbb_notification_submit_post_base extends phpbb_database_test_c $messenger_method_collection = new \phpbb\di\service_collection($phpbb_container); $messenger_method_collection->add('messenger.method.email'); - $messenger_method_collection->add('messenger.method.jabber'); $phpbb_container->set('messenger.method_collection', $messenger_method_collection); $phpbb_container->addCompilerPass(new phpbb\di\pass\markpublic_pass());