diff --git a/phpBB/includes/functions_jabber.php b/phpBB/includes/functions_jabber.php index ec7d090257..282e6105a2 100644 --- a/phpBB/includes/functions_jabber.php +++ b/phpBB/includes/functions_jabber.php @@ -31,65 +31,134 @@ if (!defined('IN_PHPBB')) */ class jabber { - var $connection = null; - var $session = array(); - var $timeout = 10; + /** @var string */ + protected $connect_server; - var $server; - var $connect_server; - var $port; - var $username; - var $password; - var $use_ssl; - var $verify_peer; - var $verify_peer_name; - var $allow_self_signed; - var $resource = 'functions_jabber.phpbb.php'; + /** @var resource */ + protected $connection = null; - var $enable_logging; - var $log_array; + /** @var bool */ + protected $enable_logging = true; - var $features = array(); + /** @var array */ + protected $features = []; + + /** @var array */ + protected $jid = []; + + /** @var array */ + protected $log_array = []; + + /** @var string */ + protected $password; + + /** @var string */ + 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 bool */ + protected $use_ssl = false; + + /** @var string */ + protected $username; /** @var string Stream close handshake */ private const STREAM_CLOSE_HANDSHAKE = ''; /** - * Constructor - * - * @param string $server Jabber server - * @param int $port Jabber server port - * @param string $username Jabber username or JID - * @param string $password Jabber password - * @param bool $use_ssl Use ssl - * @param bool $verify_peer Verify SSL certificate - * @param bool $verify_peer_name Verify Jabber peer name - * @param bool $allow_self_signed Allow self signed certificates - */ - function __construct($server, $port, $username, $password, $use_ssl = false, $verify_peer = true, $verify_peer_name = true, $allow_self_signed = false) + * Jabber class constructor + * + * Use (username() call should go before server() + * and ssl() call should go before port() and stream_options()): + * + * new jabber() + * -> username($username) + * -> password($password) + * -> ssl($use_ssl) + * -> server($server) + * -> port($port) + * -> stream_options( + * 'verify_peer' => true, + * 'verify_peer_name' => true, + * 'allow_self_signed' => false, + * ); + */ + function __construct() { - $this->connect_server = ($server) ? $server : 'localhost'; - $this->port = ($port) ? $port : 5222; + } - // Get the server and the username - if (strpos($username, '@') === false) + /** + * Set ssl context options + * See http://php.net/manual/en/context.ssl.php + * + * @param array $options SSL context options array + * @return $this + */ + public function stream_options($options = []) + { + if ($this->use_ssl) { - $this->server = $this->connect_server; - $this->username = $username; - } - else - { - $jid = explode('@', $username, 2); - - $this->username = $jid[0]; - $this->server = $jid[1]; + // Set default stream options and change it if needed + $this->stream_options['ssl'] = array_merge([ + 'verify_peer' => true, + 'verify_peer_name' => true, + 'allow_self_signed' => false, + ], $options); } - $this->password = $password; - $this->use_ssl = ($use_ssl && self::can_use_ssl()) ? true : false; - $this->verify_peer = $verify_peer; - $this->verify_peer_name = $verify_peer_name; - $this->allow_self_signed = $allow_self_signed; + return $this; + } + + /** + * Set password to connect to server + * + * @param string $password Password to connect to server + * @return $this + */ + public function password($password = '') + { + $this->password = $password; + + return $this; + } + + /** + * Set use of ssl to connect to server + * + * @param bool $use_ssl Flag indicating use of ssl to connect to server + * @return $this + */ + public function ssl($use_ssl = false) + { + $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 string $port Port to connect to server + * @return $this + */ + public function port($port = '') + { + $this->port = ($port) ? $port : 5222; // Change port if we use SSL if ($this->port == 5222 && $this->use_ssl) @@ -97,21 +166,56 @@ class jabber $this->port = 5223; } - $this->enable_logging = true; - $this->log_array = array(); + return $this; } /** - * Able to use the SSL functionality? - */ + * Set username to connect to server + * + * @param string $username Username to connect to server + * @return $this + */ + public function username($username = '') + { + if (strpos($username, '@') === false) + { + $this->username = $username; + } + else + { + $this->jid = explode('@', $username, 2); + $this->username = $this->jid[0]; + } + + return $this; + } + + /** + * Set server to connect + * Username should be set first + * + * @param string $server Server to connect + * @return $this + */ + public function server($server = '') + { + $this->connect_server = ($server) ? $server : 'localhost'; + $this->server = $this->jid[1] ?? $this->connect_server; + + return $this; + } + + /** + * Able to use the SSL functionality? + */ public static function can_use_ssl() { return @extension_loaded('openssl'); } /** - * Able to use TLS? - */ + * Able to use TLS? + */ public static function can_use_tls() { 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')) @@ -135,19 +239,19 @@ class jabber } /** - * Sets the resource which is used. No validation is done here, only escaping. - * @param string $name - * @access public - */ - function set_resource($name) + * Sets the resource which is used. No validation is done here, only escaping. + * @param string $name + * @access public + */ + public function set_resource($name) { $this->resource = $name; } /** - * Connect - */ - function connect() + * Connect + */ + public function connect() { /* if (!$this->check_jid($this->username . '@' . $this->server)) { @@ -157,7 +261,7 @@ class jabber $this->session['ssl'] = $this->use_ssl; - if ($this->open_socket($this->connect_server, $this->port, $this->use_ssl, $this->verify_peer, $this->verify_peer_name, $this->allow_self_signed)) + if ($this->open_socket($this->connect_server, $this->port)) { $this->send("\n"); $this->send("\n"); @@ -174,9 +278,9 @@ class jabber } /** - * Disconnect - */ - function disconnect() + * Disconnect + */ + public function disconnect() { if ($this->connected()) { @@ -195,7 +299,7 @@ class jabber $this->add_to_log("Error: Unexpected stream close handshake reply ”{$stream_close_reply}”"); } - $this->session = array(); + $this->session = []; return fclose($this->connection); } @@ -203,20 +307,20 @@ class jabber } /** - * Connected? - */ - function connected() + * Connected? + */ + public function connected() { - return (is_resource($this->connection) && !feof($this->connection)) ? true : false; + return is_resource($this->connection) && !feof($this->connection); } /** - * Initiates login (using data from contructor, after calling connect()) - * @access public - * @return bool - */ - function login() + * Initiates login (using data from contructor, after calling connect()) + * + * @return bool + */ + public function login() { if (empty($this->features)) { @@ -228,12 +332,13 @@ class jabber } /** - * Send data to the Jabber server - * @param string $xml - * @access public - * @return bool - */ - function send($xml) + * Send data to the Jabber server + * + * @param string $xml + * + * @return bool + */ + public function send($xml) { if ($this->connected()) { @@ -248,17 +353,14 @@ class jabber } /** - * OpenSocket - * @param string $server host to connect to - * @param int $port port number - * @param bool $use_ssl use ssl or not - * @param bool $verify_peer verify ssl certificate - * @param bool $verify_peer_name verify peer name - * @param bool $allow_self_signed allow self-signed ssl certificates - * @access public - * @return bool - */ - function open_socket($server, $port, $use_ssl, $verify_peer, $verify_peer_name, $allow_self_signed) + * OpenSocket + * + * @param string $server host to connect to + * @param int $port port number + * + * @return bool + */ + public function open_socket($server, $port) { if (@function_exists('dns_get_record')) { @@ -269,21 +371,8 @@ class jabber } } - $options = array(); - - if ($use_ssl) - { - $remote_socket = 'ssl://' . $server . ':' . $port; - - // Set ssl context options, see http://php.net/manual/en/context.ssl.php - $options['ssl'] = array('verify_peer' => $verify_peer, 'verify_peer_name' => $verify_peer_name, 'allow_self_signed' => $allow_self_signed); - } - else - { - $remote_socket = $server . ':' . $port; - } - - $socket_context = stream_context_create($options); + $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)) { @@ -299,9 +388,9 @@ class jabber } /** - * Return log - */ - function get_log() + * Return log + */ + public function get_log() { if ($this->enable_logging && count($this->log_array)) { @@ -312,9 +401,9 @@ class jabber } /** - * Add information to log - */ - function add_to_log($string) + * Add information to log + */ + protected function add_to_log($string) { if ($this->enable_logging) { @@ -323,12 +412,12 @@ class jabber } /** - * 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. - * @access public - * @return mixed either false for timeout or an array with the received data - */ - function listen($timeout = 10, $wait = false) + * 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. + * + * @return mixed either false for timeout or an array with the received data + */ + public function listen($timeout = 10, $wait = false) { if (!$this->connected()) { @@ -358,11 +447,11 @@ class jabber } /** - * Initiates account registration (based on data used for contructor) - * @access public - * @return bool - */ - function register() + * Initiates account registration (based on data used for contructor) + * + * @return bool + */ + public function register() { if (!isset($this->session['id']) || isset($this->session['jid'])) { @@ -375,13 +464,14 @@ class jabber } /** - * Sets account presence. No additional info required (default is "online" status) - * @param $message online, offline... - * @param $type dnd, away, chat, xa or nothing - * @param $unavailable set this to true if you want to become unavailable - * @access public - * @return bool - */ + * Sets account presence. No additional info required (default is "online" status) + * + * @param string $message online, offline... + * @param string $type dnd, away, chat, xa or nothing + * @param bool $unavailable set this to true if you want to become unavailable + * + * @return bool + */ function send_presence($message = '', $type = '', $unavailable = false) { if (!isset($this->session['jid'])) @@ -402,11 +492,12 @@ class jabber } /** - * This handles all the different XML elements - * @param array $xml - * @access public - * @return bool - */ + * This handles all the different XML elements + * + * @param array $xml + * + * @return bool + */ function response($xml) { if (!is_array($xml) || !count($xml)) @@ -716,7 +807,17 @@ class jabber } } - function send_message($to, $text, $subject = '', $type = 'normal') + /** + * Send Jabber message + * + * @param string $to Recepient usermane + * @param string $text Message text + * @param string $subject Message subject + * @param string $type Message type + * + * @return string + */ + public function send_message($to, $text, $subject = '', $type = 'normal') { if (!isset($this->session['jid'])) { @@ -736,12 +837,13 @@ class jabber } /** - * Encrypts a password as in RFC 2831 - * @param array $data Needs data from the client-server connection - * @access public - * @return string - */ - function encrypt_password($data) + * Encrypts a password as in RFC 2831 + * + * @param array $data Needs data from the client-server connection + * + * @return string + */ + public function encrypt_password($data) { // let's me think about again... foreach (array('realm', 'cnonce', 'digest-uri') as $key) @@ -770,12 +872,12 @@ class jabber } /** - * parse_data like a="b",c="d",... or like a="a, b", c, d="e", f=g,... - * @param string $data - * @access public - * @return array a => b ... - */ - function parse_data($data) + * parse_data 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($data) { $data = explode(',', $data); $pairs = array(); @@ -802,12 +904,13 @@ class jabber } /** - * opposite of jabber::parse_data() - * @param array $data - * @access public - * @return string - */ - function implode_data($data) + * opposite of jabber::parse_data() + * + * @param array $data + * + * @return string + */ + public function implode_data($data) { $return = array(); foreach ($data as $key => $value) @@ -818,10 +921,10 @@ class jabber } /** - * xmlize() - * @author Hans Anderson - * @copyright Hans Anderson / http://www.hansanderson.com/php/xml/ - */ + * xmlize() + * @author Hans Anderson + * @copyright Hans Anderson / http://www.hansanderson.com/php/xml/ + */ function xmlize($data, $skip_white = 1, $encoding = 'UTF-8') { $data = trim($data); @@ -854,10 +957,10 @@ class jabber } /** - * _xml_depth() - * @author Hans Anderson - * @copyright Hans Anderson / http://www.hansanderson.com/php/xml/ - */ + * _xml_depth() + * @author Hans Anderson + * @copyright Hans Anderson / http://www.hansanderson.com/php/xml/ + */ function _xml_depth($vals, &$i) { $children = array(); diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index 6a32349d4f..323ab31999 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -54,9 +54,18 @@ class messenger 'return-path' => PathHeader::class, ]; + /** @var array */ + protected $additional_headers = []; + /** @var array */ protected $addresses = []; + /** @var \phpbb\config\config */ + protected $config; + + /** @var \phpbb\event\dispatcher */ + protected $dispatcher; + /** * @var string * @@ -67,6 +76,15 @@ class messenger /** @var string */ protected $from; + /** @var Symfony\Component\Mime\Header\Headers */ + protected $headers; + + /** @var \phpbb\language\language */ + protected $language; + + /** @var \phpbb\log\log_interface */ + protected $log; + /** * @var int * @@ -82,9 +100,21 @@ class messenger /** @var string */ protected $msg; + /** @var string */ + protected $php_ext; + + /** @var queue */ + protected $queue; + /** @var string */ protected $replyto; + /** @var \phpbb\request\request */ + protected $request; + + /** @var string */ + protected $root_path; + /** @var string */ protected $subject; @@ -97,6 +127,9 @@ class messenger /** @var bool */ protected $use_queue = true; + /** @var phpbb\user */ + protected $user; + /** * Messenger class constructor * @@ -144,7 +177,7 @@ class messenger { if (isset($user['user_email']) && $user['user_email']) { - $this->email->to(new Address($user['user_email'], $user['username'] ?: '')); + $this->to($user['user_email'], $user['username'] ?: ''); } if (isset($user['user_jabber']) && $user['user_jabber']) @@ -273,9 +306,17 @@ class messenger * @param string $header_value Email header body * @return void */ - public function headers($header_name, $header_value) + public function header($header_name, $header_value) { - $this->headers->addTextHeader(trim($header_name), trim($header_value)); + $header_name = trim($header_name); + $header_value = trim($header_value); + + // addMailboxListHeader() requires value to be array + if ($this->get_header_method($header_name) == 'addMailboxListHeader') + { + $header_value = [$header_value]; + } + $this->headers->addHeader($header_name, $header_value); } /** @@ -287,10 +328,10 @@ class messenger */ public function anti_abuse_headers($config, $user) { - $this->headers->addTextHeader('X-AntiAbuse', 'Board servername - ' . $config['server_name']); - $this->headers->addTextHeader('X-AntiAbuse', 'User_id - ' . $user->data['user_id']); - $this->headers->addTextHeader('X-AntiAbuse', 'Username - ' . $user->data['username']); - $this->headers->addTextHeader('X-AntiAbuse', 'User IP - ' . $user->ip); + $this->header('X-AntiAbuse', 'Board servername - ' . $config['server_name']); + $this->header('X-AntiAbuse', 'User_id - ' . $user->data['user_id']); + $this->header('X-AntiAbuse', 'Username - ' . $user->data['username']); + $this->header('X-AntiAbuse', 'User IP - ' . $user->ip); } /** @@ -415,6 +456,13 @@ class messenger $this->template->assign_vars($vars); } + /** + * Assign block of variables to email template + * + * @param string $blockname Template block name + * @param array $vars Array of VAR => VALUE to assign to email template block + * @return void + */ public function assign_block_vars($blockname, $vars) { $this->setup_template(); @@ -423,14 +471,11 @@ class messenger } /** - * Send the mail out to the recipients + * Prepare message before sending out to the recipients * - * @param int $method User notification method NOTIFY_EMAIL|NOTIFY_IM|NOTIFY_BOTH - * @param bool $break Flag indicating if the function only formats the subject - * and the message without sending it - * @return bool + * @return void */ - public function send($method = NOTIFY_EMAIL, $break = false) + public function prepare_message() { // We add some standard variables we always use, no need to specify them always $this->assign_vars([ @@ -446,16 +491,13 @@ class messenger * Event to modify the template before parsing * * @event core.modify_notification_template - * @var bool break Flag indicating if the function only formats the subject - * and the message without sending it * @var Symfony\Component\Mime\Email email The Symfony Email object - * @var int method User notification method NOTIFY_EMAIL|NOTIFY_IM|NOTIFY_BOTH * @var string subject The message subject * @var \phpbb\template\template template The (readonly) template object * @since 3.2.4-RC1 - * @changed 4.0.0-a1 Added vars: email. + * @changed 4.0.0-a1 Added vars: email. Removed vars: method, break. */ - $vars = ['break', 'email', 'method', 'subject', 'template']; + $vars = ['email', 'subject', 'template']; extract($this->dispatcher->trigger_event('core.modify_notification_template', compact($vars))); // Parse message through template @@ -465,16 +507,13 @@ class messenger * Event to modify notification message text after parsing * * @event core.modify_notification_message - * @var bool break Flag indicating if the function only formats the subject - * and the message without sending it * @var Symfony\Component\Mime\Email email The Symfony Email object * @var string message The message text - * @var int method User notification method NOTIFY_EMAIL|NOTIFY_IM|NOTIFY_BOTH * @var string subject The message subject * @since 3.1.11-RC1 - * @changed 4.0.0-a1 Added vars: email. + * @changed 4.0.0-a1 Added vars: email. Removed vars: method, break. */ - $vars = ['break', 'email', 'message', 'method', 'subject']; + $vars = ['email', 'message', 'subject']; extract($this->dispatcher->trigger_event('core.modify_notification_message', compact($vars))); $this->email = $email; @@ -489,30 +528,39 @@ class messenger // do this here because the subject may contain a variable $drop_header = ''; $match = []; - if (preg_match('#^(Subject:(.*?))$#m', $this->msg, $match)) + if (preg_match('#^(Subject):(.*?)$#m', $this->msg, $match)) { $this->subject = (trim($match[2]) != '') ? trim($match[2]) : (($this->subject != '') ? $this->subject : $this->user->lang['NO_EMAIL_SUBJECT']); - $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#'); + $drop_header .= '[\r\n]*?' . preg_quote($match[0], '#'); } else { $this->subject = (($this->subject != '') ? $this->subject : $this->user->lang['NO_EMAIL_SUBJECT']); } - if (preg_match('#^(List-Unsubscribe:(.*?))$#m', $this->msg, $match)) + if (preg_match('#^(List-Unsubscribe):(.*?)$#m', $this->msg, $match)) { - $this->headers->addTextHeader('List-Unsubscribe', trim($match[2])); - $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#'); + $drop_header .= '[\r\n]*?' . preg_quote($match[0], '#'); + $this->additional_headers[$match[1]] = trim($match[2]); } if ($drop_header) { $this->msg = trim(preg_replace('#' . $drop_header . '#s', '', $this->msg)); } + } - $this->email->subject($this->subject); - $this->email->text($this->msg); - + /** + * Send the mail out to the recipients + * + * @param int $method User notification method NOTIFY_EMAIL|NOTIFY_IM|NOTIFY_BOTH + * @param bool $break Flag indicating if the function only formats the subject + * and the message without sending it + * @return bool + */ + public function send($method = NOTIFY_EMAIL, $break = false) + { + $this->prepare_message(); if ($break) { return true; @@ -617,13 +665,13 @@ class messenger $board_contact = $this->config['board_contact']; if (empty($this->email->getReplyTo())) { - $this->replyto = $board_contact; + $this->replyto($board_contact); $headers['Reply-To'] = $this->replyto; } if (empty($this->email->getFrom())) { - $this->from = $board_contact; + $this->from($board_contact); $headers['From'] = $this->from; } @@ -647,12 +695,7 @@ class messenger foreach ($headers as $header => $value) { - // addMailboxListHeader() requires value to be array - if ($this->get_header_method($header) == 'addMailboxListHeader') - { - $value = [$value]; - } - $this->headers->addHeader($header, $value); + $this->header($header, $value); } return true; @@ -719,15 +762,10 @@ class messenger if ($this->config['smtp_delivery'] && !in_array($this->dsn, ['null://null', 'sendmail://default'])) { // Set ssl context options, see http://php.net/manual/en/context.ssl.php - $verify_peer = (bool) $this->config['smtp_verify_peer']; - $verify_peer_name = (bool) $this->config['smtp_verify_peer_name']; - $allow_self_signed = (bool) $this->config['smtp_allow_self_signed']; - $options = [ - 'ssl' => [ - 'verify_peer' => $verify_peer, - 'verify_peer_name' => $verify_peer_name, - 'allow_self_signed' => $allow_self_signed, - ] + $options['ssl'] = [ + 'verify_peer' => (bool) $this->config['smtp_verify_peer'], + 'verify_peer_name' => (bool) $this->config['smtp_verify_peer_name'], + 'allow_self_signed' => (bool) $this->config['smtp_allow_self_signed'], ]; $this->transport->getStream()->setStreamOptions($options); } @@ -776,6 +814,9 @@ class messenger $contact_name = html_entity_decode($this->config['board_contact_name'], ENT_COMPAT); $board_contact = $this->config['board_contact']; + $this->email->subject($this->subject); + $this->email->text($this->msg); + $break = false; $subject = $this->subject; $msg = $this->msg; @@ -811,15 +852,19 @@ class messenger if (empty($this->email->getReplyto())) { - $this->email->replyTo(new Address($board_contact)); + $this->replyto($board_contact); } if (empty($this->email->getFrom())) { - $this->email->from(new Address($board_contact)); + $this->from($board_contact); } // Build header + foreach ($this->additional_headers as $header_name => $header_value) + { + $this->header($header_name, $header_value); + } $this->build_header(); // Send message ...