diff options
96 files changed, 2513 insertions, 1624 deletions
| diff --git a/.travis.yml b/.travis.yml index 346d067240..82ddd01158 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,12 +31,14 @@ services:    - redis-server    - postgresql    - mysql +  - memcached  install:    - travis/setup-phpbb.sh $DB $TRAVIS_PHP_VERSION $NOTESTS  before_script:    - travis/setup-database.sh $DB $TRAVIS_PHP_VERSION $NOTESTS +  - travis/setup-ldap.sh $SLOWTESTS    - phantomjs --webdriver=8910 > /dev/null &  script: @@ -31,9 +31,11 @@ Read our [Vagrant documentation](phpBB/docs/vagrant.md) to find out how to use V  We have unit and functional tests in order to prevent regressions. You can view the bamboo continuous integration [here](https://bamboo.phpbb.com) or check our travis builds below: -* [](http://travis-ci.org/phpbb/phpbb)[](https://ci.appveyor.com/project/phpBB/phpbb/branch/master) **master** - Latest development version -* [](http://travis-ci.org/phpbb/phpbb)[](https://ci.appveyor.com/project/phpBB/phpbb/branch/3.3.x) **3.3.x** - Development of version 3.3.x -* [](http://travis-ci.org/phpbb/phpbb)[](https://ci.appveyor.com/project/phpBB/phpbb/branch/3.2.x) **3.2.x** - Development of version 3.2.x +Travis CI  | AppVeyor | Branch  | Description +---------- | -------- | ------- | ----------- +[](http://travis-ci.org/phpbb/phpbb) | [](https://ci.appveyor.com/project/phpBB/phpbb/branch/master) | **master** | Latest development version +[](http://travis-ci.org/phpbb/phpbb) | [](https://ci.appveyor.com/project/phpBB/phpbb/branch/3.3.x) | **3.3.x** | Development of version 3.3.x +[](http://travis-ci.org/phpbb/phpbb) | [](https://ci.appveyor.com/project/phpBB/phpbb/branch/3.2.x) | **3.2.x** | Development of version 3.2.x  ## LICENSE diff --git a/build/build.xml b/build/build.xml index 9722063e2d..d016ba51ed 100644 --- a/build/build.xml +++ b/build/build.xml @@ -2,9 +2,9 @@  <project name="phpBB" description="The phpBB forum software" default="all" basedir="../">  	<!-- a few settings for the build --> -	<property name="newversion" value="3.3.0-b2-dev" /> -	<property name="prevversion" value="3.3.0-b1" /> -	<property name="olderversions" value="3.1.0, 3.1.1, 3.1.2, 3.1.3, 3.1.4, 3.1.5, 3.1.6, 3.1.7, 3.1.7-pl1, 3.1.8, 3.1.9, 3.1.10, 3.1.11, 3.1.12, 3.2.0-a1, 3.2.0-a2, 3.2.0-b1, 3.2.0-b2, 3.2.0-RC1, 3.2.0-RC2, 3.2.0, 3.2.1, 3.2.2, 3.2.3, 3.2.4, 3.2.5, 3.2.6, 3.2.7, 3.2.8" /> +	<property name="newversion" value="3.3.0-RC1-dev" /> +	<property name="prevversion" value="3.2.8" /> +	<property name="olderversions" value="3.1.0, 3.1.1, 3.1.2, 3.1.3, 3.1.4, 3.1.5, 3.1.6, 3.1.7, 3.1.7-pl1, 3.1.8, 3.1.9, 3.1.10, 3.1.11, 3.1.12, 3.2.0-a1, 3.2.0-a2, 3.2.0-b1, 3.2.0-b2, 3.2.0-RC1, 3.2.0-RC2, 3.2.0, 3.2.1, 3.2.2, 3.2.3, 3.2.4, 3.2.5, 3.2.6, 3.2.7, 3.3.0-b1, 3.3.0-b2" />  	<!-- no configuration should be needed beyond this point -->  	<property name="oldversions" value="${olderversions}, ${prevversion}" /> diff --git a/phpBB/.htaccess b/phpBB/.htaccess index 53bce762ea..0be28ab670 100644 --- a/phpBB/.htaccess +++ b/phpBB/.htaccess @@ -36,6 +36,13 @@ RewriteRule ^(.*)$ app.php [QSA,L]  #Options +FollowSymLinks  </IfModule> +# Apache content negotation tries to interpret non-existent paths as files if +# MultiViews is enabled. This will however cause issues with paths containg +# dots, e.g. for the cron tasks +<IfModule mod_negotiation.c> +	Options -MultiViews +</IfModule> +  # With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from  # module mod_authz_host to a new module called mod_access_compat (which may be  # disabled) and a new "Require" syntax has been introduced to mod_authz_host. diff --git a/phpBB/assets/javascript/core.js b/phpBB/assets/javascript/core.js index 4a61490615..bedbd23532 100644 --- a/phpBB/assets/javascript/core.js +++ b/phpBB/assets/javascript/core.js @@ -740,6 +740,8 @@ phpbb.search.closeResults = function($input, $container) {  phpbb.search.navigateResults = function($input, $container, $resultContainer) {  	// Add a namespace to the event (.phpbb.search),  	// so it can be unbound specifically later on. +	// Rebind it, to ensure the event is 'dynamic'. +	$input.off('.phpbb.search');  	$input.on('keydown.phpbb.search', function(event) {  		var key = event.keyCode || event.which,  			$active = $resultContainer.children('.active'); @@ -1585,7 +1587,7 @@ phpbb.colorPalette = function(dir, width, height) {  * @param {jQuery} el jQuery object for the palette container.  */  phpbb.registerPalette = function(el) { -	var	orientation	= el.attr('data-color-palette'), +	var	orientation	= el.attr('data-color-palette') || el.attr('data-orientation'), // data-orientation kept for backwards compat.  		height		= el.attr('data-height'),  		width		= el.attr('data-width'),  		target		= el.attr('data-target'), @@ -1799,7 +1801,7 @@ $(function() {  	phpbb.registerPageDropdowns(); -	$('[data-color-palette]').each(function() { +	$('[data-color-palette], [data-orientation]').each(function() {  		phpbb.registerPalette($(this));  	}); diff --git a/phpBB/config/default/container/services_auth.yml b/phpBB/config/default/container/services_auth.yml index ed8dc90a74..1540bea97f 100644 --- a/phpBB/config/default/container/services_auth.yml +++ b/phpBB/config/default/container/services_auth.yml @@ -15,12 +15,12 @@ services:      auth.provider.db:          class: phpbb\auth\provider\db          arguments: -            - '@dbal.conn' +            - '@captcha.factory'              - '@config' +            - '@dbal.conn'              - '@passwords.manager'              - '@request'              - '@user' -            - '@service_container'              - '%core.root_path%'              - '%core.php_ext%'          tags: @@ -29,9 +29,9 @@ services:      auth.provider.apache:          class: phpbb\auth\provider\apache          arguments: -            - '@dbal.conn'              - '@config' -            - '@passwords.manager' +            - '@dbal.conn' +            - '@language'              - '@request'              - '@user'              - '%core.root_path%' @@ -42,9 +42,9 @@ services:      auth.provider.ldap:          class: phpbb\auth\provider\ldap          arguments: -            - '@dbal.conn'              - '@config' -            - '@passwords.manager' +            - '@dbal.conn' +            - '@language'              - '@user'          tags:              - { name: auth.provider } @@ -52,18 +52,18 @@ services:      auth.provider.oauth:          class: phpbb\auth\provider\oauth\oauth          arguments: -            - '@dbal.conn'              - '@config' -            - '@passwords.manager' +            - '@dbal.conn' +            - '@auth.provider.db' +            - '@dispatcher' +            - '@language'              - '@request' +            - '@auth.provider.oauth.service_collection'              - '@user'              - '%tables.auth_provider_oauth_token_storage%'              - '%tables.auth_provider_oauth_states%'              - '%tables.auth_provider_oauth_account_assoc%' -            - '@auth.provider.oauth.service_collection'              - '%tables.users%' -            - '@service_container' -            - '@dispatcher'              - '%core.root_path%'              - '%core.php_ext%'          tags: diff --git a/phpBB/config/default/container/services_console.yml b/phpBB/config/default/container/services_console.yml index 05e467ff8d..b662102b35 100644 --- a/phpBB/config/default/container/services_console.yml +++ b/phpBB/config/default/container/services_console.yml @@ -158,14 +158,6 @@ services:          tags:              - { name: console.command } -    console.command.fixup.recalculate_email_hash: -        class: phpbb\console\command\fixup\recalculate_email_hash -        arguments: -            - '@user' -            - '@dbal.conn' -        tags: -            - { name: console.command } -      console.command.fixup.update_hashes:          class: phpbb\console\command\fixup\update_hashes          arguments: diff --git a/phpBB/develop/calc_email_hash.php b/phpBB/develop/calc_email_hash.php deleted file mode 100644 index 740f9158cf..0000000000 --- a/phpBB/develop/calc_email_hash.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -/** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ - -// -// Security message: -// -// This script is potentially dangerous. -// Remove or comment the next line (die(".... ) to enable this script. -// Do NOT FORGET to either remove this script or disable it after you have used it. -// -die("Please read the first lines of this script for instructions on how to enable it"); -@set_time_limit(300); - -$db = $dbhost = $dbuser = $dbpasswd = $dbport = $dbname = ''; - -define('IN_PHPBB', 1); -define('ANONYMOUS', 1); -$phpEx = substr(strrchr(__FILE__, '.'), 1); -$phpbb_root_path='./../'; -include($phpbb_root_path . 'config.'.$phpEx); -require($phpbb_root_path . 'includes/acm/acm_' . $acm_type . '.'.$phpEx); -require($phpbb_root_path . 'includes/db/' . $dbms . '.'.$phpEx); -include($phpbb_root_path . 'includes/functions.'.$phpEx); - -$cache		= new acm(); -$db			= new sql_db(); - -// Connect to DB -$db->sql_connect($dbhost, $dbuser, $dbpasswd, $dbname, $dbport, false); - -$start = 0; -do -{ -	// Batch query for group members, call group_user_del -	$sql = "SELECT user_id, user_email  -		FROM  {$table_prefix}users -		LIMIT $start, 100"; -	$result = $db->sql_query($sql); - -	if ($row = $db->sql_fetchrow($result)) -	{ -		do -		{ -			$sql = "UPDATE {$table_prefix}users  -				SET user_email_hash = " . (crc32(strtolower($row['user_email'])) . strlen($row['user_email'])) . ' -				WHERE user_id = ' . $row['user_id']; -			$db->sql_query($sql); - -			$start++; -		} -		while ($row = $db->sql_fetchrow($result)); - -		echo "<br />Batch -> $start\n"; -		flush(); -	} -	else -	{ -		$start = 0; -	} -	$db->sql_freeresult($result); -} -while ($start); - -echo "<p><b>Done</b></p>\n"; diff --git a/phpBB/develop/update_email_hash.php b/phpBB/develop/update_email_hash.php deleted file mode 100644 index c149900d64..0000000000 --- a/phpBB/develop/update_email_hash.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** -* Corrects user_email_hash values if DB moved from 32-bit system to 64-bit system or vice versa. -* The CRC32 function in PHP generates different results for both systems. -* @PHP dev team: no, a hexdec() applied to it does not solve the issue. And please document it. -* -*/ -die("Please read the first lines of this script for instructions on how to enable it"); - -set_time_limit(0); - -define('IN_PHPBB', true); -$phpbb_root_path = './../'; -$phpEx = substr(strrchr(__FILE__, '.'), 1); -include($phpbb_root_path . 'common.' . $phpEx); - -// Start session management -$user->session_begin(); -$auth->acl($user->data); -$user->setup(); - -$start = $request->variable('start', 0); -$num_items = 1000; - -echo '<br />Updating user email hashes' . "\n"; - -$sql = 'SELECT user_id, user_email -	FROM ' . USERS_TABLE . ' -	ORDER BY user_id ASC'; -$result = $db->sql_query($sql); - -$echos = 0; -while ($row = $db->sql_fetchrow($result)) -{ -	$echos++; - -	$sql = 'UPDATE ' . USERS_TABLE . " -		SET user_email_hash = '" . $db->sql_escape(phpbb_email_hash($row['user_email'])) . "' -		WHERE user_id = " . (int) $row['user_id']; -	$db->sql_query($sql); - -	if ($echos == 200) -	{ -		echo '<br />'; -		$echos = 0; -	} - -	echo '.'; -	flush(); -} -$db->sql_freeresult($result); - -echo 'FINISHED'; - -// Done -$db->sql_close(); diff --git a/phpBB/docs/CHANGELOG.html b/phpBB/docs/CHANGELOG.html index 08804e5ce5..d229b91bd6 100644 --- a/phpBB/docs/CHANGELOG.html +++ b/phpBB/docs/CHANGELOG.html @@ -50,6 +50,7 @@  <ol>  	<li><a href="#changelog">Changelog</a>  	<ul> +		<li><a href="#v330b1">Changes since 3.3.0-b1</a></li>  		<li><a href="#v32x">Changes since 3.2.x</a></li>  		<li><a href="#v328rc1">Changes since 3.2.8-RC1</a></li>  		<li><a href="#v327">Changes since 3.2.7</a></li> @@ -141,6 +142,30 @@  		<div class="inner">  		<div class="content"> +			<a name="v330b1"></a><h3>Changes since 3.3.0-b1</h3> +			<h4>Bug</h4> +			<ul> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16008">PHPBB3-16008</a>] - oAuth does not respect custom server settings</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16167">PHPBB3-16167</a>] - phpbb_email_hash creates false duplicates</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16188">PHPBB3-16188</a>] - Statistics Panel in ACP</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16192">PHPBB3-16192</a>] - Installing Extensions Via CLI Broken</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16205">PHPBB3-16205</a>] - Undefined variable 'zebra' in search.php</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16210">PHPBB3-16210</a>] - Terms of use should not be skippable</li> +			</ul> +			<h4>Improvement</h4> +			<ul> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-12539">PHPBB3-12539</a>] - Live Member Search Improvements</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-12559">PHPBB3-12559</a>] - Add forum setting to limit subforums legend to direct children only</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-12574">PHPBB3-12574</a>] - Don't require the passwords_manager in the constructor of the auth plugins</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-15958">PHPBB3-15958</a>] - Created forums and default forum created during install have diferent options</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16187">PHPBB3-16187</a>] - Correctly display registration using external services</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16189">PHPBB3-16189</a>] - Deprecate inet_ntop and inet_pton wrappers</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16190">PHPBB3-16190</a>] - Deprecate phpbb's checkdnsrr wrapper</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16195">PHPBB3-16195</a>] - Copy forum permissions missing paragraph</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16196">PHPBB3-16196</a>] - Remove random_compat</li> +				<li>[<a href="http://tracker.phpbb.com/browse/PHPBB3-16206">PHPBB3-16206</a>] - Remove offsetExists reimplementation in service_collection</li> +			</ul> +  			<a name="v32x"></a><h3>Changes since 3.2.x</h3>  			<h4>Bug</h4>  			<ul> diff --git a/phpBB/docs/INSTALL.html b/phpBB/docs/INSTALL.html index 33609837eb..fbc701a2ca 100644 --- a/phpBB/docs/INSTALL.html +++ b/phpBB/docs/INSTALL.html @@ -147,7 +147,7 @@  			<li>Oracle</li>  		</ul>  		</li> -		<li><strong>PHP 7.1.0+</strong> but less than <strong>PHP 7.4</strong> with support for the database you intend to use.</li> +		<li><strong>PHP 7.1.0+</strong> up to and including <strong>PHP 7.4</strong> with support for the database you intend to use.</li>  		<li>The following PHP modules are required:  		<ul>  			<li>json</li> diff --git a/phpBB/docs/coding-guidelines.html b/phpBB/docs/coding-guidelines.html index 6cf647c55f..ffa2112771 100644 --- a/phpBB/docs/coding-guidelines.html +++ b/phpBB/docs/coding-guidelines.html @@ -234,9 +234,9 @@ PHPBB_USE_BOARD_URL_PATH   (use generate_board_url() for image paths instead of  PHPBB_DISABLE_ACP_EDITOR   (disable ACP style editor for templates)  PHPBB_DISABLE_CONFIG_CHECK (disable ACP config.php writeable check) -PHPBB_ACM_MEMCACHE_PORT     (overwrite memcached port, default is 11211) -PHPBB_ACM_MEMCACHE_COMPRESS (overwrite memcached compress setting, default is disabled) -PHPBB_ACM_MEMCACHE_HOST     (overwrite memcached host name, default is localhost) +PHPBB_ACM_MEMCACHED_PORT     (overwrite memcached port, default is 11211) +PHPBB_ACM_MEMCACHED_COMPRESS (overwrite memcached compress setting, default is disabled) +PHPBB_ACM_MEMCACHED_HOST     (overwrite memcached host name, default is localhost)  PHPBB_ACM_REDIS_HOST        (overwrite redis host name, default is localhost)  PHPBB_ACM_REDIS_PORT        (overwrite redis port, default is 6379) diff --git a/phpBB/docs/nginx.sample.conf b/phpBB/docs/nginx.sample.conf index 55c01a1fc9..848998cfeb 100644 --- a/phpBB/docs/nginx.sample.conf +++ b/phpBB/docs/nginx.sample.conf @@ -93,7 +93,7 @@ http {          # Correctly pass scripts for installer          location /install/ {              # phpBB uses index.htm -            try_files $uri $uri/ @rewrite_installapp; +            try_files $uri $uri/ @rewrite_installapp =404;              # Pass the php scripts to fastcgi server specified in upstream declaration.              location ~ \.php(/|$) { @@ -104,7 +104,7 @@ http {                  fastcgi_param PATH_INFO $fastcgi_path_info;                  fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;                  fastcgi_param DOCUMENT_ROOT $realpath_root; -                try_files $uri $uri/ /install/app.php$is_args$args; +                try_files $uri $uri/ /install/app.php$is_args$args =404;                  fastcgi_pass php;              }          } diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php index 05871e4157..2441a37edc 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -101,6 +101,7 @@ class acp_board  						'allow_bookmarks'				=> array('lang' => 'ALLOW_BOOKMARKS',				'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'allow_birthdays'				=> array('lang' => 'ALLOW_BIRTHDAYS',				'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'display_last_subject'			=> array('lang' => 'DISPLAY_LAST_SUBJECT',			'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), +						'display_unapproved_posts'		=> array('lang' => 'DISPLAY_UNAPPROVED_POSTS',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'allow_quick_reply'				=> array('lang' => 'ALLOW_QUICK_REPLY',				'validate' => 'bool',	'type' => 'custom', 'method' => 'quick_reply', 'explain' => true),  						'legend2'							=> 'ACP_SUBMIT_CHANGES', diff --git a/phpBB/includes/acp/acp_users.php b/phpBB/includes/acp/acp_users.php index 1b66943490..6993c86279 100644 --- a/phpBB/includes/acp/acp_users.php +++ b/phpBB/includes/acp/acp_users.php @@ -966,10 +966,7 @@ class acp_users  						if ($update_email !== false)  						{ -							$sql_ary += array( -								'user_email'		=> $update_email, -								'user_email_hash'	=> phpbb_email_hash($update_email), -							); +							$sql_ary += ['user_email'		=> $update_email];  							$phpbb_log->add('user', $user->data['user_id'], $user->ip, 'LOG_USER_UPDATE_EMAIL', false, array(  								'reportee_id' => $user_id, diff --git a/phpBB/includes/constants.php b/phpBB/includes/constants.php index 759bc94520..493399bd71 100644 --- a/phpBB/includes/constants.php +++ b/phpBB/includes/constants.php @@ -28,7 +28,7 @@ if (!defined('IN_PHPBB'))  */  // phpBB Version -@define('PHPBB_VERSION', '3.3.0-b2-dev'); +@define('PHPBB_VERSION', '3.3.0-RC1-dev');  // QA-related  // define('PHPBB_QA', 1); diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index d2d5b503a2..994323531a 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -264,18 +264,6 @@ function still_on_time($extra_time = 15)  }  /** -* Hashes an email address to a big integer -* -* @param string $email		Email address -* -* @return string			Unsigned Big Integer -*/ -function phpbb_email_hash($email) -{ -	return sprintf('%u', crc32(strtolower($email))) . strlen($email); -} - -/**  * Wrapper for version_compare() that allows using uppercase A and B  * for alpha and beta releases.  * @@ -2276,6 +2264,7 @@ function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = fa  	$err = '';  	$form_name = 'login'; +	$username = $autologin = false;  	// Make sure user->setup() has been called  	if (!$user->is_setup()) diff --git a/phpBB/includes/functions_compatibility.php b/phpBB/includes/functions_compatibility.php index 2578290875..92e24c055c 100644 --- a/phpBB/includes/functions_compatibility.php +++ b/phpBB/includes/functions_compatibility.php @@ -659,3 +659,17 @@ function phpbb_inet_pton($address)  {  	return inet_pton($address);  } + +/** + * Hashes an email address to a big integer + * + * @param string $email		Email address + * + * @return string			Unsigned Big Integer + * + * @deprecated 3.3.0-b2 (To be removed: 4.0.0) + */ +function phpbb_email_hash($email) +{ +	return sprintf('%u', crc32(strtolower($email))) . strlen($email); +} diff --git a/phpBB/includes/functions_convert.php b/phpBB/includes/functions_convert.php index 13e01afe51..df4c9b1875 100644 --- a/phpBB/includes/functions_convert.php +++ b/phpBB/includes/functions_convert.php @@ -207,16 +207,6 @@ function get_group_id($group_name)  }  /** -* Generate the email hash stored in the users table -* -* Note: Deprecated, calls should directly go to phpbb_email_hash() -*/ -function gen_email_hash($email) -{ -	return phpbb_email_hash($email); -} - -/**  * Convert a boolean into the appropriate phpBB constant indicating whether the topic is locked  */  function is_topic_locked($bool) diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index 7f8238e1bf..ec297b536a 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -1893,14 +1893,21 @@ function mail_encode($str, $eol = "\r\n")  }  /** -* Wrapper for sending out emails with the PHP's mail function -*/ + * Wrapper for sending out emails with the PHP's mail function + */  function phpbb_mail($to, $subject, $msg, $headers, $eol, &$err_msg)  {  	global $config, $phpbb_root_path, $phpEx; -	// We use the EOL character for the OS here because the PHP mail function does not correctly transform line endings. On Windows SMTP is used (SMTP is \r\n), on UNIX a command is used... -	// Reference: http://bugs.php.net/bug.php?id=15841 +	// Convert Numeric Character References to UTF-8 chars (ie. Emojis) +	$subject = utf8_decode_ncr($subject); +	$msg = utf8_decode_ncr($msg); + +	/** +	 * We use the EOL character for the OS here because the PHP mail function does not correctly transform line endings. +	 * On Windows SMTP is used (SMTP is \r\n), on UNIX a command is used... +	 * Reference: http://bugs.php.net/bug.php?id=15841 +	 */  	$headers = implode($eol, $headers);  	if (!class_exists('\phpbb\error_collector')) @@ -1911,10 +1918,14 @@ function phpbb_mail($to, $subject, $msg, $headers, $eol, &$err_msg)  	$collector = new \phpbb\error_collector;  	$collector->install(); -	// On some PHP Versions mail() *may* fail if there are newlines within the subject. -	// Newlines are used as a delimiter for lines in mail_encode() according to RFC 2045 section 6.8. -	// Because PHP can't decide what is wanted we revert back to the non-RFC-compliant way of separating by one space (Use '' as parameter to mail_encode() results in SPACE used) +	/** +	 * On some PHP Versions mail() *may* fail if there are newlines within the subject. +	 * Newlines are used as a delimiter for lines in mail_encode() according to RFC 2045 section 6.8. +	 * Because PHP can't decide what is wanted we revert back to the non-RFC-compliant way of separating by one space +	 * (Use '' as parameter to mail_encode() results in SPACE used) +	 */  	$additional_parameters = $config['email_force_sender'] ? '-f' . $config['board_email'] : ''; +  	$result = mail($to, mail_encode($subject, ''), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $headers, $additional_parameters);  	$collector->uninstall(); diff --git a/phpBB/includes/functions_user.php b/phpBB/includes/functions_user.php index 5c94a90d9d..dc6e09268a 100644 --- a/phpBB/includes/functions_user.php +++ b/phpBB/includes/functions_user.php @@ -204,7 +204,6 @@ function user_add($user_row, $cp_data = false, $notifications_data = null)  		'username_clean'	=> $username_clean,  		'user_password'		=> (isset($user_row['user_password'])) ? $user_row['user_password'] : '',  		'user_email'		=> strtolower($user_row['user_email']), -		'user_email_hash'	=> phpbb_email_hash($user_row['user_email']),  		'group_id'			=> $user_row['group_id'],  		'user_type'			=> $user_row['user_type'],  	); @@ -1948,9 +1947,9 @@ function validate_user_email($email, $allowed_email = false)  	if (!$config['allow_emailreuse'])  	{ -		$sql = 'SELECT user_email_hash +		$sql = 'SELECT user_email  			FROM ' . USERS_TABLE . " -			WHERE user_email_hash = " . $db->sql_escape(phpbb_email_hash($email)); +			WHERE user_email = '" . $db->sql_escape($email) . "'";  		$result = $db->sql_query($sql);  		$row = $db->sql_fetchrow($result);  		$db->sql_freeresult($result); diff --git a/phpBB/includes/ucp/ucp_pm_compose.php b/phpBB/includes/ucp/ucp_pm_compose.php index 06baa279a5..87a8c91fd2 100644 --- a/phpBB/includes/ucp/ucp_pm_compose.php +++ b/phpBB/includes/ucp/ucp_pm_compose.php @@ -999,7 +999,10 @@ function compose_pm($id, $mode, $action, $user_folders = array())  		{  			$quote_attributes['post_id'] = $post['msg_id'];  		} - +		if ($action === 'quote') +		{ +			$quote_attributes['msg_id'] = $post['msg_id']; +		}  		/** @var \phpbb\language\language $language */  		$language = $phpbb_container->get('language');  		/** @var \phpbb\textformatter\utils_interface $text_formatter_utils */ diff --git a/phpBB/includes/ucp/ucp_profile.php b/phpBB/includes/ucp/ucp_profile.php index 6d98362e08..dca7e7eeb7 100644 --- a/phpBB/includes/ucp/ucp_profile.php +++ b/phpBB/includes/ucp/ucp_profile.php @@ -131,7 +131,6 @@ class ucp_profile  							'username'			=> ($auth->acl_get('u_chgname') && $config['allow_namechange']) ? $data['username'] : $user->data['username'],  							'username_clean'	=> ($auth->acl_get('u_chgname') && $config['allow_namechange']) ? utf8_clean_string($data['username']) : $user->data['username_clean'],  							'user_email'		=> ($auth->acl_get('u_chgemail')) ? $data['email'] : $user->data['user_email'], -							'user_email_hash'	=> ($auth->acl_get('u_chgemail')) ? phpbb_email_hash($data['email']) : $user->data['user_email_hash'],  							'user_password'		=> ($auth->acl_get('u_chgpasswd') && $data['new_password']) ? $passwords_manager->hash($data['new_password']) : $user->data['user_password'],  						); diff --git a/phpBB/includes/ucp/ucp_register.php b/phpBB/includes/ucp/ucp_register.php index 97d2631224..00fa8034f9 100644 --- a/phpBB/includes/ucp/ucp_register.php +++ b/phpBB/includes/ucp/ucp_register.php @@ -39,12 +39,23 @@ class ucp_register  			trigger_error('UCP_REGISTER_DISABLE');  		} -		$coppa			= $request->is_set('coppa') ? (int) $request->variable('coppa', false) : false; +		$coppa			= $request->is_set('coppa_yes') ? 1 : ($request->is_set('coppa_no') ? 0 : false); +		$coppa			= $request->is_set('coppa') ? $request->variable('coppa', 0) : $coppa;  		$agreed			= $request->variable('agreed', false);  		$submit			= $request->is_set_post('submit');  		$change_lang	= $request->variable('change_lang', '');  		$user_lang		= $request->variable('lang', $user->lang_name); +		if ($agreed && !check_form_key('ucp_register')) +		{ +			$agreed = false; +		} + +		if ($coppa !== false && !check_form_key('ucp_register')) +		{ +			$coppa = false; +		} +  		/**  		* Add UCP register data before they are assigned to the template or submitted  		* @@ -67,14 +78,7 @@ class ucp_register  		);  		extract($phpbb_dispatcher->trigger_event('core.ucp_register_requests_after', compact($vars))); -		if ($agreed) -		{ -			add_form_key('ucp_register'); -		} -		else -		{ -			add_form_key('ucp_register_terms'); -		} +		add_form_key('ucp_register');  		if ($change_lang || $user_lang != $config['default_lang'])  		{ @@ -168,11 +172,8 @@ class ucp_register  				$template_vars = array(  					'S_LANG_OPTIONS'	=> (count($lang_row) > 1) ? language_select($user_lang) : '', -					'L_COPPA_NO'		=> sprintf($user->lang['UCP_COPPA_BEFORE'], $coppa_birthday), -					'L_COPPA_YES'		=> sprintf($user->lang['UCP_COPPA_ON_AFTER'], $coppa_birthday), - -					'U_COPPA_NO'		=> append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register&coppa=0'), -					'U_COPPA_YES'		=> append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register&coppa=1'), +					'L_COPPA_NO'		=> $user->lang('UCP_COPPA_BEFORE', $coppa_birthday), +					'L_COPPA_YES'		=> $user->lang('UCP_COPPA_ON_AFTER', $coppa_birthday),  					'S_SHOW_COPPA'		=> true,  					'S_HIDDEN_FIELDS'	=> build_hidden_fields($s_hidden_fields), diff --git a/phpBB/includes/ucp/ucp_resend.php b/phpBB/includes/ucp/ucp_resend.php index 44c54100cd..55923668d4 100644 --- a/phpBB/includes/ucp/ucp_resend.php +++ b/phpBB/includes/ucp/ucp_resend.php @@ -47,7 +47,7 @@ class ucp_resend  			$sql = 'SELECT user_id, group_id, username, user_email, user_type, user_lang, user_actkey, user_inactive_reason  				FROM ' . USERS_TABLE . " -				WHERE user_email_hash = '" . $db->sql_escape(phpbb_email_hash($email)) . "' +				WHERE user_email = '" . $db->sql_escape($email) . "'  					AND username_clean = '" . $db->sql_escape(utf8_clean_string($username)) . "'";  			$result = $db->sql_query($sql);  			$user_row = $db->sql_fetchrow($result); diff --git a/phpBB/install/convertors/convert_phpbb20.php b/phpBB/install/convertors/convert_phpbb20.php index c4d2be5a28..cefb33fe3c 100644 --- a/phpBB/install/convertors/convert_phpbb20.php +++ b/phpBB/install/convertors/convert_phpbb20.php @@ -38,7 +38,7 @@ $dbms = $phpbb_config_php_file->convert_30_dbms_to_31($dbms);  $convertor_data = array(  	'forum_name'	=> 'phpBB 2.0.x',  	'version'		=> '1.0.3', -	'phpbb_version'	=> '3.3.0', +	'phpbb_version'	=> '3.3.0-b2',  	'author'		=> '<a href="https://www.phpbb.com/">phpBB Limited</a>',  	'dbms'			=> $dbms,  	'dbhost'		=> $dbhost, @@ -899,7 +899,6 @@ if (!$get_info)  				array('user_password',			'users.user_password',				'phpbb_convert_password_hash'),  				array('user_posts',				'users.user_posts',					'intval'),  				array('user_email',				'users.user_email',					'strtolower'), -				array('user_email_hash',		'users.user_email',					'gen_email_hash'),  				array('user_birthday',			((defined('MOD_BIRTHDAY')) ? 'users.user_birthday' : ''),	'phpbb_get_birthday'),  				array('user_lastvisit',			'users.user_lastvisit',				'intval'),  				array('user_lastmark',			'users.user_lastvisit',				'intval'), diff --git a/phpBB/install/phpbbcli.php b/phpBB/install/phpbbcli.php index b7a07488ac..6ac261aa13 100755 --- a/phpBB/install/phpbbcli.php +++ b/phpBB/install/phpbbcli.php @@ -23,7 +23,7 @@ if (php_sapi_name() !== 'cli')  define('IN_PHPBB', true);  define('IN_INSTALL', true);  define('PHPBB_ENVIRONMENT', 'production'); -define('PHPBB_VERSION', '3.3.0-b2-dev'); +define('PHPBB_VERSION', '3.3.0-b2');  $phpbb_root_path = __DIR__ . '/../';  $phpEx = substr(strrchr(__FILE__, '.'), 1); diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql index 97fc23f0bf..8e7b26d60c 100644 --- a/phpBB/install/schemas/schema_data.sql +++ b/phpBB/install/schemas/schema_data.sql @@ -278,7 +278,7 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('tpl_allow_php', '0  INSERT INTO phpbb_config (config_name, config_value) VALUES ('upload_icons_path', 'images/upload_icons');  INSERT INTO phpbb_config (config_name, config_value) VALUES ('upload_path', 'files');  INSERT INTO phpbb_config (config_name, config_value) VALUES ('use_system_cron', '0'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('version', '3.3.0-b2-dev'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('version', '3.3.0-RC1-dev');  INSERT INTO phpbb_config (config_name, config_value) VALUES ('warnings_expire_days', '90');  INSERT INTO phpbb_config (config_name, config_value) VALUES ('warnings_gc', '14400'); diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index fdc02d9ae8..cb9013805d 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -52,6 +52,8 @@ $lang = array_merge($lang, array(  	'DISABLE_BOARD_EXPLAIN'			=> 'This will make the board unavailable to users who are neither administrators nor moderators. You can also enter a short (255 character) message to display if you wish.',  	'DISPLAY_LAST_SUBJECT'			=> 'Display subject of last added post on forum list',  	'DISPLAY_LAST_SUBJECT_EXPLAIN'	=> 'The subject of the last added post will be displayed in the forum list with a hyperlink to the post. Subjects from password protected forums and forums in which user doesn’t have read access are not shown.', +	'DISPLAY_UNAPPROVED_POSTS'		=> 'Display unapproved posts to the author', +	'DISPLAY_UNAPPROVED_POSTS_EXPLAIN'	=> 'Unapproved posts can be viewed by the author. Does not apply to Guest posts.',  	'GUEST_STYLE'					=> 'Guest style',  	'GUEST_STYLE_EXPLAIN'			=> 'The board style for guests.',  	'OVERRIDE_STYLE'				=> 'Override user style', diff --git a/phpBB/language/en/cli.php b/phpBB/language/en/cli.php index 505d12e8ff..122010d2cf 100644 --- a/phpBB/language/en/cli.php +++ b/phpBB/language/en/cli.php @@ -78,8 +78,6 @@ $lang = array_merge($lang, array(  	'CLI_DESCRIPTION_REPARSER_REPARSE_OPT_RANGE_SIZE'	=> 'Approximate number of records to process at a time',  	'CLI_DESCRIPTION_REPARSER_REPARSE_OPT_RESUME'		=> 'Start reparsing where the last execution stopped', -	'CLI_DESCRIPTION_RECALCULATE_EMAIL_HASH'			=> 'Recalculates the user_email_hash column of the users table.', -  	'CLI_DESCRIPTION_SET_ATOMIC_CONFIG'					=> 'Sets a configuration option’s value only if the old matches the current value',  	'CLI_DESCRIPTION_SET_CONFIG'						=> 'Sets a configuration option’s value', @@ -130,7 +128,6 @@ $lang = array_merge($lang, array(  	'CLI_EXTENSIONS_ENABLED'			=> 'Enabled',  	'CLI_FIXUP_FIX_LEFT_RIGHT_IDS_SUCCESS'		=> 'Successfully repaired the tree structure of the forums and modules.', -	'CLI_FIXUP_RECALCULATE_EMAIL_HASH_SUCCESS'	=> 'Successfully recalculated all email hashes.',  	'CLI_FIXUP_UPDATE_HASH_BCRYPT_SUCCESS'		=> 'Successfully updated outdated password hashes to bcrypt.',  	'CLI_MIGRATION_NAME'					=> 'Migration name, including the namespace (use forward slashes instead of backslashes to avoid problems).', diff --git a/phpBB/language/en/common.php b/phpBB/language/en/common.php index d050c1b109..609ae4fe53 100644 --- a/phpBB/language/en/common.php +++ b/phpBB/language/en/common.php @@ -94,6 +94,7 @@ $lang = array_merge($lang, array(  	'AUTH_PROVIDER_OAUTH_ERROR_ALREADY_LINKED'				=> 'This external service is already associated with another board account.',  	'AUTH_PROVIDER_OAUTH_ERROR_INVALID_ENTRY'				=> 'Invalid database entry.',  	'AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE'		=> 'Invalid service type provided to OAuth service handler.', +	'AUTH_PROVIDER_OAUTH_ERROR_REQUEST'						=> 'Something went wrong when processing your OAuth request.',  	'AUTH_PROVIDER_OAUTH_ERROR_SERVICE_NOT_CREATED'			=> 'OAuth service not created',  	'AUTH_PROVIDER_OAUTH_SERVICE_BITLY'						=> 'Bitly',  	'AUTH_PROVIDER_OAUTH_SERVICE_FACEBOOK'					=> 'Facebook', @@ -615,6 +616,7 @@ $lang = array_merge($lang, array(  	'POST_TOPIC'			=> 'Post a new topic',  	'POST_UNAPPROVED_ACTION'	=> 'Post awaiting approval:',  	'POST_UNAPPROVED'		=> 'This post has not been approved.', +	'POST_UNAPPROVED_EXPLAIN'	=> 'This post is not visible to other users until it has been approved by a moderator.',  	'POWERED_BY'			=> 'Powered by %s',  	'PREVIEW'				=> 'Preview',  	'PREVIOUS'				=> 'Previous',		// Used in pagination diff --git a/phpBB/phpbb/auth/provider/apache.php b/phpBB/phpbb/auth/provider/apache.php index aa5bf64335..a713674657 100644 --- a/phpBB/phpbb/auth/provider/apache.php +++ b/phpBB/phpbb/auth/provider/apache.php @@ -13,34 +13,55 @@  namespace phpbb\auth\provider; +use phpbb\config\config; +use phpbb\db\driver\driver_interface; +use phpbb\language\language; +use phpbb\request\request_interface; +use phpbb\request\type_cast_helper; +use phpbb\user; +  /**  * Apache authentication provider for phpBB3  */ -class apache extends \phpbb\auth\provider\base +class apache extends base  { -	/** -	* phpBB passwords manager -	* -	* @var \phpbb\passwords\manager -	*/ -	protected $passwords_manager; +	/** @var config phpBB config */ +	protected $config; + +	/** @var driver_interface Database object */ +	protected $db; + +	/** @var language Language object */ +	protected $language; + +	/** @var request_interface Request object */ +	protected $request; + +	/** @var user User object */ +	protected $user; + +	/** @var string Relative path to phpBB root */ +	protected $phpbb_root_path; + +	/** @var string PHP file extension */ +	protected $php_ext;  	/**  	 * Apache Authentication Constructor  	 * -	 * @param	\phpbb\db\driver\driver_interface 	$db		Database object -	 * @param	\phpbb\config\config 		$config		Config object -	 * @param	\phpbb\passwords\manager	$passwords_manager		Passwords Manager object -	 * @param	\phpbb\request\request 		$request		Request object -	 * @param	\phpbb\user 			$user		User object +	 * @param	config 				$config		Config object +	 * @param	driver_interface 	$db		Database object +	 * @param	language			$language Language object +	 * @param	request_interface 	$request		Request object +	 * @param	user 				$user		User object  	 * @param	string 				$phpbb_root_path		Relative path to phpBB root  	 * @param	string 				$php_ext		PHP file extension  	 */ -	public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request $request, \phpbb\user $user, $phpbb_root_path, $php_ext) +	public function __construct(config $config, driver_interface $db, language $language, request_interface $request, user $user, $phpbb_root_path, $php_ext)  	{ -		$this->db = $db;  		$this->config = $config; -		$this->passwords_manager = $passwords_manager; +		$this->db = $db; +		$this->language = $language;  		$this->request = $request;  		$this->user = $user;  		$this->phpbb_root_path = $phpbb_root_path; @@ -52,9 +73,9 @@ class apache extends \phpbb\auth\provider\base  	 */  	public function init()  	{ -		if (!$this->request->is_set('PHP_AUTH_USER', \phpbb\request\request_interface::SERVER) || $this->user->data['username'] !== htmlspecialchars_decode($this->request->server('PHP_AUTH_USER'))) +		if (!$this->request->is_set('PHP_AUTH_USER', request_interface::SERVER) || $this->user->data['username'] !== htmlspecialchars_decode($this->request->server('PHP_AUTH_USER')))  		{ -			return $this->user->lang['APACHE_SETUP_BEFORE_USE']; +			return $this->language->lang('APACHE_SETUP_BEFORE_USE');  		}  		return false;  	} @@ -83,7 +104,7 @@ class apache extends \phpbb\auth\provider\base  			);  		} -		if (!$this->request->is_set('PHP_AUTH_USER', \phpbb\request\request_interface::SERVER)) +		if (!$this->request->is_set('PHP_AUTH_USER', request_interface::SERVER))  		{  			return array(  				'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, @@ -137,7 +158,7 @@ class apache extends \phpbb\auth\provider\base  			return array(  				'status'		=> LOGIN_SUCCESS_CREATE_PROFILE,  				'error_msg'		=> false, -				'user_row'		=> $this->user_row($php_auth_user, $php_auth_pw), +				'user_row'		=> $this->user_row($php_auth_user),  			);  		} @@ -154,7 +175,7 @@ class apache extends \phpbb\auth\provider\base  	 */  	public function autologin()  	{ -		if (!$this->request->is_set('PHP_AUTH_USER', \phpbb\request\request_interface::SERVER)) +		if (!$this->request->is_set('PHP_AUTH_USER', request_interface::SERVER))  		{  			return array();  		} @@ -164,8 +185,8 @@ class apache extends \phpbb\auth\provider\base  		if (!empty($php_auth_user) && !empty($php_auth_pw))  		{ -			set_var($php_auth_user, $php_auth_user, 'string', true); -			set_var($php_auth_pw, $php_auth_pw, 'string', true); +			$type_cast_helper = new type_cast_helper(); +			$type_cast_helper->set_var($php_auth_user, $php_auth_user, 'string', true);  			$sql = 'SELECT *  				FROM ' . USERS_TABLE . " @@ -185,7 +206,7 @@ class apache extends \phpbb\auth\provider\base  			}  			// create the user if he does not exist yet -			user_add($this->user_row($php_auth_user, $php_auth_pw)); +			user_add($this->user_row($php_auth_user));  			$sql = 'SELECT *  				FROM ' . USERS_TABLE . " @@ -208,11 +229,11 @@ class apache extends \phpbb\auth\provider\base  	 * function in order to create a user  	 *  	 * @param 	string	$username 	The username of the new user. -	 * @param 	string	$password 	The password of the new user. +	 *  	 * @return 	array 				Contains data that can be passed directly to  	 *								the user_add function.  	 */ -	private function user_row($username, $password) +	private function user_row($username)  	{  		// first retrieve default group id  		$sql = 'SELECT group_id @@ -231,7 +252,7 @@ class apache extends \phpbb\auth\provider\base  		// generate user account data  		return array(  			'username'		=> $username, -			'user_password'	=> $this->passwords_manager->hash($password), +			'user_password'	=> '',  			'user_email'	=> '',  			'group_id'		=> (int) $row['group_id'],  			'user_type'		=> USER_NORMAL, @@ -246,7 +267,7 @@ class apache extends \phpbb\auth\provider\base  	public function validate_session($user)  	{  		// Check if PHP_AUTH_USER is set and handle this case -		if ($this->request->is_set('PHP_AUTH_USER', \phpbb\request\request_interface::SERVER)) +		if ($this->request->is_set('PHP_AUTH_USER', request_interface::SERVER))  		{  			$php_auth_user = $this->request->server('PHP_AUTH_USER'); diff --git a/phpBB/phpbb/auth/provider/base.php b/phpBB/phpbb/auth/provider/base.php index dea27ccc25..30e0a0fe2d 100644 --- a/phpBB/phpbb/auth/provider/base.php +++ b/phpBB/phpbb/auth/provider/base.php @@ -16,7 +16,7 @@ namespace phpbb\auth\provider;  /**  * Base authentication provider class that all other providers should implement  */ -abstract class base implements \phpbb\auth\provider\provider_interface +abstract class base implements provider_interface  {  	/**  	* {@inheritdoc} diff --git a/phpBB/phpbb/auth/provider/db.php b/phpBB/phpbb/auth/provider/db.php index 1adf85ee05..a70734fcbe 100644 --- a/phpBB/phpbb/auth/provider/db.php +++ b/phpBB/phpbb/auth/provider/db.php @@ -13,48 +13,69 @@  namespace phpbb\auth\provider; +use phpbb\captcha\factory; +use phpbb\config\config; +use phpbb\db\driver\driver_interface; +use phpbb\passwords\manager; +use phpbb\request\request_interface; +use phpbb\user; +  /**   * Database authentication provider for phpBB3   * This is for authentication via the integrated user table   */ -class db extends \phpbb\auth\provider\base +class db extends base  { +	/** @var factory CAPTCHA factory */ +	protected $captcha_factory; + +	/** @var config phpBB config */ +	protected $config; + +	/** @var driver_interface DBAL driver instance */ +	protected $db; + +	/** @var request_interface Request object */ +	protected $request; + +	/** @var user User object */ +	protected $user; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; + +	/** @var string PHP file extension */ +	protected $php_ext; +  	/**  	* phpBB passwords manager  	* -	* @var \phpbb\passwords\manager +	* @var manager  	*/  	protected $passwords_manager;  	/** -	* DI container -	* -	* @var \Symfony\Component\DependencyInjection\ContainerInterface -	*/ -	protected $phpbb_container; - -	/**  	 * Database Authentication Constructor  	 * -	 * @param	\phpbb\db\driver\driver_interface		$db -	 * @param	\phpbb\config\config 		$config -	 * @param	\phpbb\passwords\manager	$passwords_manager -	 * @param	\phpbb\request\request		$request -	 * @param	\phpbb\user			$user -	 * @param	\Symfony\Component\DependencyInjection\ContainerInterface $phpbb_container DI container +	 * @param factory $captcha_factory +	 * @param	config 		$config +	 * @param	driver_interface		$db +	 * @param	manager	$passwords_manager +	 * @param	request_interface		$request +	 * @param	user			$user  	 * @param	string				$phpbb_root_path  	 * @param	string				$php_ext  	 */ -	public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request $request, \phpbb\user $user, \Symfony\Component\DependencyInjection\ContainerInterface $phpbb_container, $phpbb_root_path, $php_ext) +	public function __construct(factory $captcha_factory, config $config, driver_interface $db, manager $passwords_manager, request_interface $request, user $user, $phpbb_root_path, $php_ext)  	{ -		$this->db = $db; +		$this->captcha_factory = $captcha_factory;  		$this->config = $config; +		$this->db = $db;  		$this->passwords_manager = $passwords_manager;  		$this->request = $request;  		$this->user = $user;  		$this->phpbb_root_path = $phpbb_root_path;  		$this->php_ext = $php_ext; -		$this->phpbb_container = $phpbb_container;  	}  	/** @@ -155,9 +176,7 @@ class db extends \phpbb\auth\provider\base  		// Every auth module is able to define what to do by itself...  		if ($show_captcha)  		{ -			/* @var $captcha_factory \phpbb\captcha\factory */ -			$captcha_factory = $this->phpbb_container->get('captcha.factory'); -			$captcha = $captcha_factory->get_instance($this->config['captcha_plugin']); +			$captcha = $this->captcha_factory->get_instance($this->config['captcha_plugin']);  			$captcha->init(CONFIRM_LOGIN);  			$vc_response = $captcha->validate($row);  			if ($vc_response) diff --git a/phpBB/phpbb/auth/provider/ldap.php b/phpBB/phpbb/auth/provider/ldap.php index 0789a6234d..6a78136e5f 100644 --- a/phpBB/phpbb/auth/provider/ldap.php +++ b/phpBB/phpbb/auth/provider/ldap.php @@ -1,4 +1,5 @@  <?php +  /**  *  * This file is part of the phpBB Forum Software package. @@ -13,32 +14,42 @@  namespace phpbb\auth\provider; +use phpbb\config\config; +use phpbb\db\driver\driver_interface; +use phpbb\language\language; +use phpbb\user; +  /**   * Database authentication provider for phpBB3   * This is for authentication via the integrated user table   */ -class ldap extends \phpbb\auth\provider\base +class ldap extends base  { -	/** -	* phpBB passwords manager -	* -	* @var \phpbb\passwords\manager -	*/ -	protected $passwords_manager; +	/** @var config phpBB config */ +	protected $config; + +	/** @var driver_interface DBAL driver interface */ +	protected $db; + +	/** @var language phpBB language class */ +	protected $language; + +	/** @var user phpBB user */ +	protected $user;  	/**  	 * LDAP Authentication Constructor  	 * -	 * @param	\phpbb\db\driver\driver_interface		$db		Database object -	 * @param	\phpbb\config\config		$config		Config object -	 * @param	\phpbb\passwords\manager	$passwords_manager		Passwords manager object -	 * @param	\phpbb\user			$user		User object +	 * @param	driver_interface	$db			DBAL driver interface +	 * @param	config				$config		Config object +	 * @param	language			$language	Language object +	 * @param	user				$user		User object  	 */ -	public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\user $user) +	public function __construct(config $config, driver_interface $db, language $language, user $user)  	{ -		$this->db = $db;  		$this->config = $config; -		$this->passwords_manager = $passwords_manager; +		$this->db = $db; +		$this->language = $language;  		$this->user = $user;  	} @@ -49,7 +60,7 @@ class ldap extends \phpbb\auth\provider\base  	{  		if (!@extension_loaded('ldap'))  		{ -			return $this->user->lang['LDAP_NO_LDAP_EXTENSION']; +			return $this->language->lang('LDAP_NO_LDAP_EXTENSION');  		}  		$this->config['ldap_port'] = (int) $this->config['ldap_port']; @@ -64,7 +75,7 @@ class ldap extends \phpbb\auth\provider\base  		if (!$ldap)  		{ -			return $this->user->lang['LDAP_NO_SERVER_CONNECTION']; +			return $this->language->lang('LDAP_NO_SERVER_CONNECTION');  		}  		@ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3); @@ -74,7 +85,7 @@ class ldap extends \phpbb\auth\provider\base  		{  			if (!@ldap_bind($ldap, htmlspecialchars_decode($this->config['ldap_user']), htmlspecialchars_decode($this->config['ldap_password'])))  			{ -				return $this->user->lang['LDAP_INCORRECT_USER_PASSWORD']; +				return $this->language->lang('LDAP_INCORRECT_USER_PASSWORD');  			}  		} @@ -92,7 +103,7 @@ class ldap extends \phpbb\auth\provider\base  		if ($search === false)  		{ -			return $this->user->lang['LDAP_SEARCH_FAILED']; +			return $this->language->lang('LDAP_SEARCH_FAILED');  		}  		$result = @ldap_get_entries($ldap, $search); @@ -101,12 +112,12 @@ class ldap extends \phpbb\auth\provider\base  		if (!is_array($result) || count($result) < 2)  		{ -			return sprintf($this->user->lang['LDAP_NO_IDENTITY'], $this->user->data['username']); +			return $this->language->lang('LDAP_NO_IDENTITY', $this->user->data['username']);  		}  		if (!empty($this->config['ldap_email']) && !isset($result[0][htmlspecialchars_decode($this->config['ldap_email'])]))  		{ -			return $this->user->lang['LDAP_NO_EMAIL']; +			return $this->language->lang('LDAP_NO_EMAIL');  		}  		return false; @@ -245,7 +256,7 @@ class ldap extends \phpbb\auth\provider\base  					// generate user account data  					$ldap_user_row = array(  						'username'		=> $username, -						'user_password'	=> $this->passwords_manager->hash($password), +						'user_password'	=> '',  						'user_email'	=> (!empty($this->config['ldap_email'])) ? utf8_htmlspecialchars($ldap_result[0][htmlspecialchars_decode($this->config['ldap_email'])][0]) : '',  						'group_id'		=> (int) $row['group_id'],  						'user_type'		=> USER_NORMAL, diff --git a/phpBB/phpbb/auth/provider/oauth/oauth.php b/phpBB/phpbb/auth/provider/oauth/oauth.php index e3f8394bba..29ffe6d591 100644 --- a/phpBB/phpbb/auth/provider/oauth/oauth.php +++ b/phpBB/phpbb/auth/provider/oauth/oauth.php @@ -1,169 +1,137 @@  <?php  /** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @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\auth\provider\oauth; +use OAuth\Common\Http\Exception\TokenResponseException; +use OAuth\ServiceFactory;  use OAuth\Common\Consumer\Credentials; +use OAuth\Common\Service\ServiceInterface; +use OAuth\OAuth1\Service\AbstractService as OAuth1Service; +use OAuth\OAuth2\Service\AbstractService as OAuth2Service; +use phpbb\auth\provider\base; +use phpbb\auth\provider\db; +use phpbb\auth\provider\oauth\service\exception; +use phpbb\config\config; +use phpbb\db\driver\driver_interface; +use phpbb\di\service_collection; +use phpbb\event\dispatcher; +use phpbb\language\language; +use phpbb\request\request_interface; +use phpbb\user;  /** -* OAuth authentication provider for phpBB3 -*/ -class oauth extends \phpbb\auth\provider\base + * OAuth authentication provider for phpBB3 + */ +class oauth extends base  { -	/** -	* Database driver -	* -	* @var \phpbb\db\driver\driver_interface -	*/ -	protected $db; - -	/** -	* phpBB config -	* -	* @var \phpbb\config\config -	*/ +	/** @var config */  	protected $config; -	/** -	* phpBB passwords manager -	* -	* @var \phpbb\passwords\manager -	*/ -	protected $passwords_manager; - -	/** -	* phpBB request object -	* -	* @var \phpbb\request\request_interface -	*/ -	protected $request; +	/** @var driver_interface */ +	protected $db; -	/** -	* phpBB user -	* -	* @var \phpbb\user -	*/ -	protected $user; +	/** @var db */ +	protected $db_auth; -	/** -	* OAuth token table -	* -	* @var string -	*/ -	protected $auth_provider_oauth_token_storage_table; +	/** @var dispatcher */ +	protected $dispatcher; -	/** -	* OAuth state table -	* -	* @var string -	*/ -	protected $auth_provider_oauth_state_table; +	/** @var language */ +	protected $language; -	/** -	* OAuth account association table -	* -	* @var string -	*/ -	protected $auth_provider_oauth_token_account_assoc; +	/** @var request_interface */ +	protected $request; -	/** -	* All OAuth service providers -	* -	* @var \phpbb\di\service_collection Contains \phpbb\auth\provider\oauth\service_interface -	*/ +	/** @var service_collection */  	protected $service_providers; -	/** -	* Users table -	* -	* @var string -	*/ -	protected $users_table; +	/** @var user */ +	protected $user; -	/** -	* Cached current uri object -	* -	* @var \OAuth\Common\Http\Uri\UriInterface|null -	*/ -	protected $current_uri; +	/** @var string OAuth table: token storage */ +	protected $oauth_token_table; -	/** -	* DI container -	* -	* @var \Symfony\Component\DependencyInjection\ContainerInterface -	*/ -	protected $phpbb_container; +	/** @var string OAuth table: state */ +	protected $oauth_state_table; -	/** -	* phpBB event dispatcher -	* -	* @var \phpbb\event\dispatcher_interface -	*/ -	protected $dispatcher; +	/** @var string OAuth table: account association */ +	protected $oauth_account_table; -	/** -	* phpBB root path -	* -	* @var string -	*/ -	protected $phpbb_root_path; +	/** @var string Users table */ +	protected $users_table; -	/** -	* PHP file extension -	* -	* @var string -	*/ +	/** @var string phpBB root path */ +	protected $root_path; + +	/** @var string php File extension */  	protected $php_ext;  	/** -	* OAuth Authentication Constructor -	* -	* @param	\phpbb\db\driver\driver_interface	$db -	* @param	\phpbb\config\config	$config -	* @param	\phpbb\passwords\manager	$passwords_manager -	* @param	\phpbb\request\request_interface	$request -	* @param	\phpbb\user		$user -	* @param	string			$auth_provider_oauth_token_storage_table -	* @param	string			$auth_provider_oauth_state_table -	* @param	string			$auth_provider_oauth_token_account_assoc -	* @param	\phpbb\di\service_collection	$service_providers Contains \phpbb\auth\provider\oauth\service_interface -	* @param	string			$users_table -	* @param	\Symfony\Component\DependencyInjection\ContainerInterface $phpbb_container DI container -	* @param	\phpbb\event\dispatcher_interface $dispatcher phpBB event dispatcher -	* @param	string			$phpbb_root_path -	* @param	string			$php_ext -	*/ -	public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request_interface $request, \phpbb\user $user, $auth_provider_oauth_token_storage_table, $auth_provider_oauth_state_table, $auth_provider_oauth_token_account_assoc, \phpbb\di\service_collection $service_providers, $users_table, \Symfony\Component\DependencyInjection\ContainerInterface $phpbb_container, \phpbb\event\dispatcher_interface $dispatcher, $phpbb_root_path, $php_ext) +	 * Constructor. +	 * +	 * @param config				$config					Config object +	 * @param driver_interface	$db						Database object +	 * @param db			$db_auth				DB auth provider +	 * @param dispatcher			$dispatcher				Event dispatcher object +	 * @param language			$language				Language object +	 * @param request_interface	$request				Request object +	 * @param service_collection		$service_providers		OAuth providers service collection +	 * @param user						$user					User object +	 * @param string							$oauth_token_table		OAuth table: token storage +	 * @param string							$oauth_state_table		OAuth table: state +	 * @param string							$oauth_account_table	OAuth table: account association +	 * @param string							$users_table			User table +	 * @param string							$root_path				phpBB root path +	 * @param string							$php_ext				php File extension +	 */ +	public function __construct( +		config $config, +		driver_interface $db, +		db $db_auth, +		dispatcher $dispatcher, +		language $language, +		request_interface $request, +		service_collection $service_providers, +		user $user, +		$oauth_token_table, +		$oauth_state_table, +		$oauth_account_table, +		$users_table, +		$root_path, +		$php_ext +	)  	{ -		$this->db = $db; -		$this->config = $config; -		$this->passwords_manager = $passwords_manager; -		$this->request = $request; -		$this->user = $user; -		$this->auth_provider_oauth_token_storage_table = $auth_provider_oauth_token_storage_table; -		$this->auth_provider_oauth_state_table = $auth_provider_oauth_state_table; -		$this->auth_provider_oauth_token_account_assoc = $auth_provider_oauth_token_account_assoc; -		$this->service_providers = $service_providers; -		$this->users_table = $users_table; -		$this->phpbb_container = $phpbb_container; -		$this->dispatcher = $dispatcher; -		$this->phpbb_root_path = $phpbb_root_path; -		$this->php_ext = $php_ext; +		$this->config				= $config; +		$this->db					= $db; +		$this->db_auth				= $db_auth; +		$this->dispatcher			= $dispatcher; +		$this->language				= $language; +		$this->service_providers	= $service_providers; +		$this->request				= $request; +		$this->user					= $user; + +		$this->oauth_token_table	= $oauth_token_table; +		$this->oauth_state_table	= $oauth_state_table; +		$this->oauth_account_table	= $oauth_account_table; +		$this->users_table			= $users_table; +		$this->root_path			= $root_path; +		$this->php_ext				= $php_ext;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function init()  	{  		// This does not test whether or not the key and secret provided are valid. @@ -173,61 +141,85 @@ class oauth extends \phpbb\auth\provider\base  			if (($credentials['key'] && !$credentials['secret']) || (!$credentials['key'] && $credentials['secret']))  			{ -				return $this->user->lang['AUTH_PROVIDER_OAUTH_ERROR_ELEMENT_MISSING']; +				return $this->language->lang('AUTH_PROVIDER_OAUTH_ERROR_ELEMENT_MISSING');  			}  		} +  		return false;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function login($username, $password)  	{  		// Temporary workaround for only having one authentication provider available  		if (!$this->request->is_set('oauth_service'))  		{ -			$provider = new \phpbb\auth\provider\db($this->db, $this->config, $this->passwords_manager, $this->request, $this->user, $this->phpbb_container, $this->phpbb_root_path, $this->php_ext); -			return $provider->login($username, $password); +			return $this->db_auth->login($username, $password);  		}  		// Request the name of the OAuth service -		$service_name_original = $this->request->variable('oauth_service', '', false); -		$service_name = 'auth.provider.oauth.service.' . strtolower($service_name_original); -		if ($service_name_original === '' || !array_key_exists($service_name, $this->service_providers)) +		$provider = $this->request->variable('oauth_service', '', false); +		$service_name = $this->get_service_name($provider); + +		if ($provider === '' || !array_key_exists($service_name, $this->service_providers))  		{ -			return array( +			return [  				'status'		=> LOGIN_ERROR_EXTERNAL_AUTH,  				'error_msg'		=> 'LOGIN_ERROR_OAUTH_SERVICE_DOES_NOT_EXIST', -				'user_row'		=> array('user_id' => ANONYMOUS), -			); +				'user_row'		=> ['user_id' => ANONYMOUS], +			];  		}  		// Get the service credentials for the given service -		$service_credentials = $this->service_providers[$service_name]->get_service_credentials(); +		$storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table); +		$query = 'mode=login&login=external&oauth_service=' . $provider; -		$storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table); -		$query = 'mode=login&login=external&oauth_service=' . $service_name_original; -		$service = $this->get_service($service_name_original, $storage, $service_credentials, $query, $this->service_providers[$service_name]->get_auth_scope()); +		try +		{ +			/** @var OAuth1Service|OAuth2Service $service */ +			$service = $this->get_service($provider, $storage, $query); +		} +		catch (\Exception $e) +		{ +			return [ +				'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, +				'error_msg'		=> $e->getMessage(), +				'user_row'		=> ['user_id' => ANONYMOUS], +			]; +		} -		if (($service::OAUTH_VERSION === 2 && $this->request->is_set('code', \phpbb\request\request_interface::GET)) -			|| ($service::OAUTH_VERSION === 1 && $this->request->is_set('oauth_token', \phpbb\request\request_interface::GET))) +		if ($this->is_set_code($service))  		{  			$this->service_providers[$service_name]->set_external_service_provider($service); -			$unique_id = $this->service_providers[$service_name]->perform_auth_login(); + +			try +			{ +				$unique_id = $this->service_providers[$service_name]->perform_auth_login(); +			} +			catch (exception $e) +			{ +				return [ +					'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, +					'error_msg'		=> $e->getMessage(), +					'user_row'		=> ['user_id' => ANONYMOUS], +				]; +			}  			/**  			 * Check to see if this provider is already associated with an account.  			 * -			 * Enforcing a data type to make data contains strings and not integers, +			 * Enforcing a data type to make sure it are strings and not integers,  			 * so values are quoted in the SQL WHERE statement.  			 */ -			$data = array( -				'provider'			=> (string) $service_name_original, +			$data = [ +				'provider'			=> (string) utf8_strtolower($provider),  				'oauth_provider_id'	=> (string) $unique_id -			); +			]; -			$sql = 'SELECT user_id FROM ' . $this->auth_provider_oauth_token_account_assoc . ' +			$sql = 'SELECT user_id  +				FROM ' . $this->oauth_account_table . '  				WHERE ' . $this->db->sql_build_array('SELECT', $data);  			$result = $this->db->sql_query($sql);  			$row = $this->db->sql_fetchrow($result); @@ -235,204 +227,134 @@ class oauth extends \phpbb\auth\provider\base  			$redirect_data = array(  				'auth_provider'				=> 'oauth', -				'login_link_oauth_service'	=> $service_name_original, +				'login_link_oauth_service'	=> $provider,  			);  			/** -			* Event is triggered before check if provider is already associated with an account -			* -			* @event core.oauth_login_after_check_if_provider_id_has_match -			* @var	array									row				User row -			* @var	array									data			Provider data -			* @var	array									redirect_data	Data to be appended to the redirect url -			* @var	\OAuth\Common\Service\ServiceInterface	service			OAuth service -			* @since 3.2.3-RC1 -			* @changed 3.2.6-RC1 Added redirect_data -			*/ -			$vars = array( +			 * Event is triggered before check if provider is already associated with an account +			 * +			 * @event core.oauth_login_after_check_if_provider_id_has_match +			 * @var array				row				User row +			 * @var array				data			Provider data +			 * @var	array				redirect_data	Data to be appended to the redirect url +			 * @var ServiceInterface	service			OAuth service +			 * @since 3.2.3-RC1 +			 * @changed 3.2.6-RC1						Added redirect_data +			 */ +			$vars = [  				'row',  				'data',  				'redirect_data',  				'service', -			); +			];  			extract($this->dispatcher->trigger_event('core.oauth_login_after_check_if_provider_id_has_match', compact($vars)));  			if (!$row)  			{  				// The user does not yet exist, ask to link or create profile -				return array( +				return [  					'status'		=> LOGIN_SUCCESS_LINK_PROFILE,  					'error_msg'		=> 'LOGIN_OAUTH_ACCOUNT_NOT_LINKED', -					'user_row'		=> array(), +					'user_row'		=> [],  					'redirect_data'	=> $redirect_data, -				); +				];  			}  			// Retrieve the user's account  			$sql = 'SELECT user_id, username, user_password, user_passchg, user_email, user_ip, user_type, user_login_attempts  				FROM ' . $this->users_table . ' -					WHERE user_id = ' . (int) $row['user_id']; +				WHERE user_id = ' . (int) $row['user_id'];  			$result = $this->db->sql_query($sql);  			$row = $this->db->sql_fetchrow($result);  			$this->db->sql_freeresult($result);  			if (!$row)  			{ -				throw new \Exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_ENTRY'); +				return [ +					'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, +					'error_msg'		=> 'AUTH_PROVIDER_OAUTH_ERROR_INVALID_ENTRY', +					'user_row'		=> ['user_id' => ANONYMOUS], +				];  			}  			/**  			 * Check if the user is banned. -			 * The fourth parameter, return, has to be true, -			 * otherwise the OAuth login is still called and -			 * an uncaught exception is thrown as there is no -			 * token stored in the database. +			 * The fourth parameter (return) has to be true, otherwise the OAuth login is still called and +			 * an uncaught exception is thrown as there is no token stored in the database.  			 */  			$ban = $this->user->check_ban($row['user_id'], $row['user_ip'], $row['user_email'], true); +  			if (!empty($ban))  			{  				$till_date = !empty($ban['ban_end']) ? $this->user->format_date($ban['ban_end']) : '';  				$message = !empty($ban['ban_end']) ? 'BOARD_BAN_TIME' : 'BOARD_BAN_PERM'; -				$contact_link = phpbb_get_board_contact_link($this->config, $this->phpbb_root_path, $this->php_ext); -				$message = $this->user->lang($message, $till_date, '<a href="' . $contact_link . '">', '</a>'); -				$message .= !empty($ban['ban_give_reason']) ? '<br /><br />' . $this->user->lang('BOARD_BAN_REASON', $ban['ban_give_reason']) : ''; -				$message .= !empty($ban['ban_triggered_by']) ? '<br /><br /><em>' . $this->user->lang('BAN_TRIGGERED_BY_' . strtoupper($ban['ban_triggered_by'])) . '</em>' : ''; +				$contact_link = phpbb_get_board_contact_link($this->config, $this->root_path, $this->php_ext); + +				$message = $this->language->lang($message, $till_date, '<a href="' . $contact_link . '">', '</a>'); +				$message .= !empty($ban['ban_give_reason']) ? '<br /><br />' . $this->language->lang('BOARD_BAN_REASON', $ban['ban_give_reason']) : ''; +				$message .= !empty($ban['ban_triggered_by']) ? '<br /><br /><em>' . $this->language->lang('BAN_TRIGGERED_BY_' . utf8_strtoupper($ban['ban_triggered_by'])) . '</em>' : ''; -				return array( +				return [  					'status'	=> LOGIN_BREAK,  					'error_msg'	=> $message,  					'user_row'	=> $row, -				); +				];  			}  			// Update token storage to store the user_id  			$storage->set_user_id($row['user_id']);  			/** -			* Event is triggered after user is successfully logged in via OAuth. -			* -			* @event core.auth_oauth_login_after -			* @var    array    row    User row -			* @since 3.1.11-RC1 -			*/ -			$vars = array( +			 * Event is triggered after user is successfully logged in via OAuth. +			 * +			 * @event core.auth_oauth_login_after +			 * @var array	row		User row +			 * @since 3.1.11-RC1 +			 */ +			$vars = [  				'row', -			); +			];  			extract($this->dispatcher->trigger_event('core.auth_oauth_login_after', compact($vars)));  			// The user is now authenticated and can be logged in -			return array( +			return [  				'status'		=> LOGIN_SUCCESS,  				'error_msg'		=> false,  				'user_row'		=> $row, -			); +			];  		}  		else  		{ -			if ($service::OAUTH_VERSION === 1) -			{ -				$token = $service->requestRequestToken(); -				$url = $service->getAuthorizationUri(array('oauth_token' => $token->getRequestToken())); -			} -			else -			{ -				$url = $service->getAuthorizationUri(); -			} -			header('Location: ' . $url); -		} -	} - -	/** -	* Returns the cached current_uri object or creates and caches it if it is -	* not already created. In each case the query string is updated based on -	* the $query parameter. -	* -	* @param	string	$service_name	The name of the service -	* @param	string	$query			The query string of the current_uri -	*									used in redirects -	* @return	\OAuth\Common\Http\Uri\UriInterface -	*/ -	protected function get_current_uri($service_name, $query) -	{ -		if ($this->current_uri) -		{ -			$this->current_uri->setQuery($query); -			return $this->current_uri; -		} - -		$uri_factory = new \OAuth\Common\Http\Uri\UriFactory(); -		$super_globals = $this->request->get_super_global(\phpbb\request\request_interface::SERVER); -		if (!empty($super_globals['HTTP_X_FORWARDED_PROTO']) && $super_globals['HTTP_X_FORWARDED_PROTO'] === 'https') -		{ -			$super_globals['HTTPS'] = 'on'; -			$super_globals['SERVER_PORT'] = 443; -		} -		$current_uri = $uri_factory->createFromSuperGlobalArray($super_globals); -		$current_uri->setQuery($query); - -		$this->current_uri = $current_uri; -		return $current_uri; -	} - -	/** -	* Returns a new service object -	* -	* @param	string	$service_name			The name of the service -	* @param	\phpbb\auth\provider\oauth\token_storage $storage -	* @param	array	$service_credentials	{@see \phpbb\auth\provider\oauth\oauth::get_service_credentials} -	* @param	string	$query					The query string of the -	*											current_uri used in redirection -	* @param	array	$scopes					The scope of the request against -	*											the api. -	* @return	\OAuth\Common\Service\ServiceInterface -	* @throws	\Exception -	*/ -	protected function get_service($service_name, \phpbb\auth\provider\oauth\token_storage $storage, array $service_credentials, $query, array $scopes = array()) -	{ -		$current_uri = $this->get_current_uri($service_name, $query); - -		// Setup the credentials for the requests -		$credentials = new Credentials( -			$service_credentials['key'], -			$service_credentials['secret'], -			$current_uri->getAbsoluteUri() -		); - -		$service_factory = new \OAuth\ServiceFactory(); -		$service = $service_factory->createService($service_name, $credentials, $storage, $scopes); - -		if (!$service) -		{ -			throw new \Exception('AUTH_PROVIDER_OAUTH_ERROR_SERVICE_NOT_CREATED'); +			return $this->set_redirect($service);  		} - -		return $service;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function get_login_data()  	{ -		$login_data = array( +		$login_data = [  			'TEMPLATE_FILE'		=> 'login_body_oauth.html',  			'BLOCK_VAR_NAME'	=> 'oauth', -			'BLOCK_VARS'		=> array(), -		); +			'BLOCK_VARS'		=> [], +		];  		foreach ($this->service_providers as $service_name => $service_provider)  		{  			// Only include data if the credentials are set  			$credentials = $service_provider->get_service_credentials(); +  			if ($credentials['key'] && $credentials['secret'])  			{ -				$actual_name = str_replace('auth.provider.oauth.service.', '', $service_name); -				$redirect_url = generate_board_url() . '/ucp.' . $this->php_ext . '?mode=login&login=external&oauth_service=' . $actual_name; -				$login_data['BLOCK_VARS'][$service_name] = array( +				$provider = $this->get_provider($service_name); +				$redirect_url = generate_board_url() . '/ucp.' . $this->php_ext . '?mode=login&login=external&oauth_service=' . $provider; + +				$login_data['BLOCK_VARS'][$service_name] = [  					'REDIRECT_URL'	=> redirect($redirect_url, true), -					'SERVICE_NAME'	=> $this->user->lang['AUTH_PROVIDER_OAUTH_SERVICE_' . strtoupper($actual_name)], -				); +					'SERVICE_NAME'	=> $this->get_provider_title($provider), +				];  			}  		} @@ -440,51 +362,55 @@ class oauth extends \phpbb\auth\provider\base  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function acp()  	{ -		$ret = array(); +		$ret = [];  		foreach ($this->service_providers as $service_name => $service_provider)  		{ -			$actual_name = str_replace('auth.provider.oauth.service.', '', $service_name); -			$ret[] = 'auth_oauth_' . $actual_name . '_key'; -			$ret[] = 'auth_oauth_' . $actual_name . '_secret'; +			$provider = $this->get_provider($service_name); + +			$provider = utf8_strtolower($provider); + +			$ret[] = 'auth_oauth_' . $provider . '_key'; +			$ret[] = 'auth_oauth_' . $provider . '_secret';  		}  		return $ret;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function get_acp_template($new_config)  	{ -		$ret = array( +		$ret = [  			'BLOCK_VAR_NAME'	=> 'oauth_services', -			'BLOCK_VARS'		=> array(), +			'BLOCK_VARS'		=> [],  			'TEMPLATE_FILE'		=> 'auth_provider_oauth.html', -			'TEMPLATE_VARS'		=> array(), -		); +			'TEMPLATE_VARS'		=> [], +		];  		foreach ($this->service_providers as $service_name => $service_provider)  		{ -			$actual_name = str_replace('auth.provider.oauth.service.', '', $service_name); -			$ret['BLOCK_VARS'][$actual_name] = array( -				'ACTUAL_NAME'	=> $this->user->lang['AUTH_PROVIDER_OAUTH_SERVICE_' . strtoupper($actual_name)], -				'KEY'			=> $new_config['auth_oauth_' . $actual_name . '_key'], -				'NAME'			=> $actual_name, -				'SECRET'		=> $new_config['auth_oauth_' . $actual_name . '_secret'], -			); +			$provider = $this->get_provider($service_name); + +			$ret['BLOCK_VARS'][$provider] = [ +				'NAME'			=> $provider, +				'ACTUAL_NAME'	=> $this->get_provider_title($provider), +				'KEY'			=> $new_config['auth_oauth_' . utf8_strtolower($provider) . '_key'], +				'SECRET'		=> $new_config['auth_oauth_' . utf8_strtolower($provider) . '_secret'], +			];  		}  		return $ret;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function login_link_has_necessary_data($login_link_data)  	{  		if (empty($login_link_data)) @@ -502,16 +428,13 @@ class oauth extends \phpbb\auth\provider\base  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function link_account(array $link_data)  	{  		// Check for a valid link method (auth_link or login_link)  		if (!array_key_exists('link_method', $link_data) || -			!in_array($link_data['link_method'], array( -				'auth_link', -				'login_link', -			))) +			!in_array($link_data['link_method'], ['auth_link', 'login_link']))  		{  			return 'LOGIN_LINK_MISSING_DATA';  		} @@ -527,7 +450,8 @@ class oauth extends \phpbb\auth\provider\base  			}  		} -		$service_name = 'auth.provider.oauth.service.' . strtolower($link_data['oauth_service']); +		$service_name = $this->get_service_name($link_data['oauth_service']); +  		if (!array_key_exists($service_name, $this->service_providers))  		{  			return 'LOGIN_ERROR_OAUTH_SERVICE_DOES_NOT_EXIST'; @@ -539,21 +463,109 @@ class oauth extends \phpbb\auth\provider\base  				return $this->link_account_auth_link($link_data, $service_name);  			case 'login_link':  				return $this->link_account_login_link($link_data, $service_name); +			default: +				return 'LOGIN_LINK_MISSING_DATA'; +		} +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function logout($data, $new_session) +	{ +		// Clear all tokens belonging to the user +		$storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table); +		$storage->clearAllTokens(); + +		return; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function get_auth_link_data($user_id = 0) +	{ +		$user_ids	= []; +		$block_vars	= []; + +		$sql = 'SELECT oauth_provider_id, provider + 			FROM ' . $this->oauth_account_table . ' +			WHERE user_id = ' . ($user_id > 0 ? (int) $user_id : (int) $this->user->data['user_id']); +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$user_ids[$row['provider']] = $row['oauth_provider_id']; +		} +		$this->db->sql_freeresult($result); + +		foreach ($this->service_providers as $service_name => $service_provider) +		{ +			// Only include data if the credentials are set +			$credentials = $service_provider->get_service_credentials(); + +			if ($credentials['key'] && $credentials['secret']) +			{ +				$provider = $this->get_provider($service_name); + +				$block_vars[$service_name] = [ +					'SERVICE_NAME'	=> $this->get_provider_title($provider), +					'UNIQUE_ID'		=> isset($user_ids[$provider]) ? $user_ids[$provider] : null, +					'HIDDEN_FIELDS'	=> [ +						'link'			=> !isset($user_ids[$provider]), +						'oauth_service' => $provider, +					], +				]; +			} +		} + +		return [ +			'BLOCK_VAR_NAME'	=> 'oauth', +			'BLOCK_VARS'		=> $block_vars, + +			'TEMPLATE_FILE'		=> 'ucp_auth_link_oauth.html', +		]; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function unlink_account(array $link_data) +	{ +		if (!array_key_exists('oauth_service', $link_data) || !$link_data['oauth_service']) +		{ +			return 'LOGIN_LINK_MISSING_DATA';  		} + +		// Remove user specified in $link_data if possible +		$user_id = isset($link_data['user_id']) ? $link_data['user_id'] : $this->user->data['user_id']; + +		// Remove the link +		$sql = 'DELETE FROM ' . $this->oauth_account_table . " +			WHERE provider = '" . $this->db->sql_escape($link_data['oauth_service']) . "' +				AND user_id = " . (int) $user_id; +		$this->db->sql_query($sql); + +		$service_name = $this->get_service_name($link_data['oauth_service']); + +		// Clear all tokens belonging to the user on this service +		$storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table); +		$storage->clearToken($service_name); + +		return false;  	}  	/** -	* Performs the account linking for login_link -	* -	* @param	array	$link_data		The same variable given to {@see \phpbb\auth\provider\provider_interface::link_account} -	* @param	string	$service_name	The name of the service being used in -	*									linking. -	* @return	string|null	Returns a language constant (string) if an error is -	*						encountered, or null on success. -	*/ +	 * Performs the account linking for login_link. +	 * +	 * @param array		$link_data		The same variable given to +	 * 									{@see \phpbb\auth\provider\provider_interface::link_account} +	 * @param string	$service_name	The name of the service being used in linking. +	 * @return string|false				Returns a language key (string) if an error is encountered, +	 * 									or false on success. +	 */  	protected function link_account_login_link(array $link_data, $service_name)  	{ -		$storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table); +		$storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table);  		// Check for an access token, they should have one  		if (!$storage->has_access_token_by_session($service_name)) @@ -561,87 +573,109 @@ class oauth extends \phpbb\auth\provider\base  			return 'LOGIN_LINK_ERROR_OAUTH_NO_ACCESS_TOKEN';  		} -		// Prepare the query string -		$query = 'mode=login_link&login_link_oauth_service=' . strtolower($link_data['oauth_service']); -  		// Prepare for an authentication request -		$service_credentials = $this->service_providers[$service_name]->get_service_credentials(); -		$scopes = $this->service_providers[$service_name]->get_auth_scope(); -		$service = $this->get_service(strtolower($link_data['oauth_service']), $storage, $service_credentials, $query, $scopes); +		$query = 'mode=login_link&login_link_oauth_service=' . $link_data['oauth_service']; + +		try +		{ +			$service = $this->get_service($link_data['oauth_service'], $storage, $query); +		} +		catch (\Exception $e) +		{ +			return $e->getMessage(); +		} +  		$this->service_providers[$service_name]->set_external_service_provider($service); -		// The user has already authenticated successfully, request to authenticate again -		$unique_id = $this->service_providers[$service_name]->perform_token_auth(); +		try +		{ +			// The user has already authenticated successfully, request to authenticate again +			$unique_id = $this->service_providers[$service_name]->perform_token_auth(); +		} +		catch (exception $e) +		{ +			return $e->getMessage(); +		}  		// Insert into table, they will be able to log in after this -		$data = array( +		$data = [  			'user_id'			=> $link_data['user_id'], -			'provider'			=> strtolower($link_data['oauth_service']), +			'provider'			=> utf8_strtolower($link_data['oauth_service']),  			'oauth_provider_id'	=> $unique_id, -		); +		];  		$this->link_account_perform_link($data); +  		// Update token storage to store the user_id  		$storage->set_user_id($link_data['user_id']); + +		return false;  	}  	/** -	* Performs the account linking for auth_link -	* -	* @param	array	$link_data		The same variable given to {@see \phpbb\auth\provider\provider_interface::link_account} -	* @param	string	$service_name	The name of the service being used in -	*									linking. -	* @return	string|null	Returns a language constant (string) if an error is -	*						encountered, or null on success. -	*/ +	 * Performs the account linking for auth_link. +	 * +	 * @param array		$link_data		The same variable given to +	 * 									{@see \phpbb\auth\provider\provider_interface::link_account} +	 * @param string	$service_name	The name of the service being used in linking. +	 * @return string|false				Returns a language constant (string) if an error is encountered, +	 * 									or false on success. +	 */  	protected function link_account_auth_link(array $link_data, $service_name)  	{ -		$storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table); -		$query = 'i=ucp_auth_link&mode=auth_link&link=1&oauth_service=' . strtolower($link_data['oauth_service']); -		$service_credentials = $this->service_providers[$service_name]->get_service_credentials(); -		$scopes = $this->service_providers[$service_name]->get_auth_scope(); -		$service = $this->get_service(strtolower($link_data['oauth_service']), $storage, $service_credentials, $query, $scopes); +		$storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table); +		$query = 'i=ucp_auth_link&mode=auth_link&link=1&oauth_service=' . $link_data['oauth_service']; -		if (($service::OAUTH_VERSION === 2 && $this->request->is_set('code', \phpbb\request\request_interface::GET)) -			|| ($service::OAUTH_VERSION === 1 && $this->request->is_set('oauth_token', \phpbb\request\request_interface::GET))) +		try +		{ +			/** @var OAuth1Service|OAuth2Service $service */ +			$service = $this->get_service($link_data['oauth_service'], $storage, $query); +		} +		catch (\Exception $e) +		{ +			return $e->getMessage(); +		} + +		if ($this->is_set_code($service))  		{  			$this->service_providers[$service_name]->set_external_service_provider($service); -			$unique_id = $this->service_providers[$service_name]->perform_auth_login(); + +			try +			{ +				$unique_id = $this->service_providers[$service_name]->perform_auth_login(); +			} +			catch (exception $e) +			{ +				return $e->getMessage(); +			}  			// Insert into table, they will be able to log in after this -			$data = array( +			$data = [  				'user_id'			=> $this->user->data['user_id'], -				'provider'			=> strtolower($link_data['oauth_service']), +				'provider'			=> utf8_strtolower($link_data['oauth_service']),  				'oauth_provider_id'	=> $unique_id, -			); +			];  			$this->link_account_perform_link($data); + +			return false;  		}  		else  		{ -			if ($service::OAUTH_VERSION === 1) -			{ -				$token = $service->requestRequestToken(); -				$url = $service->getAuthorizationUri(array('oauth_token' => $token->getRequestToken())); -			} -			else -			{ -				$url = $service->getAuthorizationUri(); -			} -			header('Location: ' . $url); +			return $this->set_redirect($service);  		}  	}  	/** -	* Performs the query that inserts an account link -	* -	* @param	array	$data	This array is passed to db->sql_build_array -	*/ +	 * Performs the query that inserts an account link +	 * +	 * @param	array	$data	This array is passed to db->sql_build_array +	 */  	protected function link_account_perform_link(array $data)  	{  		// Check if the external account is already associated with other user  		$sql = 'SELECT user_id -			FROM ' . $this->auth_provider_oauth_token_account_assoc . " +			FROM ' . $this->oauth_account_table . "  			WHERE provider = '" . $this->db->sql_escape($data['provider']) . "'  				AND oauth_provider_id = '" . $this->db->sql_escape($data['oauth_provider_id']) . "'";  		$result = $this->db->sql_query($sql); @@ -654,114 +688,172 @@ class oauth extends \phpbb\auth\provider\base  		}  		// Link account -		$sql = 'INSERT INTO ' . $this->auth_provider_oauth_token_account_assoc . ' -			' . $this->db->sql_build_array('INSERT', $data); +		$sql = 'INSERT INTO ' . $this->oauth_account_table . ' ' . $this->db->sql_build_array('INSERT', $data);  		$this->db->sql_query($sql);  		/**  		 * Event is triggered after user links account.  		 *  		 * @event core.auth_oauth_link_after -		 * @var    array    data    User row +		 * @var array	data	User row  		 * @since 3.1.11-RC1  		 */ -		$vars = array( +		$vars = [  			'data', -		); +		];  		extract($this->dispatcher->trigger_event('core.auth_oauth_link_after', compact($vars)));  	}  	/** -	* {@inheritdoc} -	*/ -	public function logout($data, $new_session) +	 * Returns a new service object. +	 * +	 * @param string			$provider		The name of the provider +	 * @param token_storage		$storage		Token storage object +	 * @param string			$query			The query string used for the redirect uri +	 * @return ServiceInterface +	 * @throws exception						When OAuth service was not created +	 */ +	protected function get_service($provider, token_storage $storage, $query)  	{ -		// Clear all tokens belonging to the user -		$storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table); -		$storage->clearAllTokens(); +		$service_name = $this->get_service_name($provider); -		return; -	} +		/** @see \phpbb\auth\provider\oauth\service\service_interface::get_service_credentials */ +		$service_credentials = $this->service_providers[$service_name]->get_service_credentials(); -	/** -	* {@inheritdoc} -	*/ -	public function get_auth_link_data($user_id = 0) -	{ -		$block_vars = array(); +		/** @see \phpbb\auth\provider\oauth\service\service_interface::get_auth_scope */ +		$scopes = $this->service_providers[$service_name]->get_auth_scope(); -		// Get all external accounts tied to the current user -		$data = array( -			'user_id' => ($user_id <= 0) ? (int) $this->user->data['user_id'] : (int) $user_id, +		$callback = generate_board_url() . "/ucp.{$this->php_ext}?{$query}"; + +		// Setup the credentials for the requests +		$credentials = new Credentials( +			$service_credentials['key'], +			$service_credentials['secret'], +			$callback  		); -		$sql = 'SELECT oauth_provider_id, provider FROM ' . $this->auth_provider_oauth_token_account_assoc . ' -			WHERE ' . $this->db->sql_build_array('SELECT', $data); -		$result = $this->db->sql_query($sql); -		$rows = $this->db->sql_fetchrowset($result); -		$this->db->sql_freeresult($result); -		$oauth_user_ids = array(); +		$service_factory = new ServiceFactory; -		if ($rows !== false && count($rows)) +		// Allow providers to register a custom class or override the provider name +		if ($class = $this->service_providers[$service_name]->get_external_service_class())  		{ -			foreach ($rows as $row) +			if (class_exists($class)) +			{ +				try +				{ +					$service_factory->registerService($provider, $class); +				} +				catch (\OAuth\Common\Exception\Exception $e) +				{ +					throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE'); +				} +			} +			else  			{ -				$oauth_user_ids[$row['provider']] = $row['oauth_provider_id']; +				$provider = $class;  			}  		} -		unset($rows); -		foreach ($this->service_providers as $service_name => $service_provider) +		$service = $service_factory->createService($provider, $credentials, $storage, $scopes); + +		if (!$service)  		{ -			// Only include data if the credentials are set -			$credentials = $service_provider->get_service_credentials(); -			if ($credentials['key'] && $credentials['secret']) -			{ -				$actual_name = str_replace('auth.provider.oauth.service.', '', $service_name); - -				$block_vars[$service_name] = array( -					'HIDDEN_FIELDS'	=> array( -						'link'			=> (!isset($oauth_user_ids[$actual_name])), -						'oauth_service' => $actual_name, -					), - -					'SERVICE_ID'	=> $actual_name, -					'SERVICE_NAME'	=> $this->user->lang['AUTH_PROVIDER_OAUTH_SERVICE_' . strtoupper($actual_name)], -					'UNIQUE_ID'		=> (isset($oauth_user_ids[$actual_name])) ? $oauth_user_ids[$actual_name] : null, -				); -			} +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_SERVICE_NOT_CREATED');  		} -		return array( -			'BLOCK_VAR_NAME'	=> 'oauth', -			'BLOCK_VARS'		=> $block_vars, +		return $service; +	} -			'TEMPLATE_FILE'	=> 'ucp_auth_link_oauth.html', -		); +	/** +	 * Returns the service name for an OAuth provider name. +	 * +	 * @param string	$provider		The OAuth provider name +	 * @return string					The service name +	 */ +	protected function get_service_name($provider) +	{ +		if (strpos($provider, 'auth.provider.oauth.service.') !== 0) +		{ +			$provider = 'auth.provider.oauth.service.' . utf8_strtolower($provider); +		} + +		return $provider;  	}  	/** -	* {@inheritdoc} -	*/ -	public function unlink_account(array $link_data) +	 * Returns the OAuth provider name from a service name. +	 * +	 * @param string	$service_name	The service name +	 * @return string					The OAuth provider name +	 */ +	protected function get_provider($service_name)  	{ -		if (!array_key_exists('oauth_service', $link_data) || !$link_data['oauth_service']) +		return str_replace('auth.provider.oauth.service.', '', $service_name); +	} + +	/** +	 * Returns the localized title for the OAuth provider. +	 * +	 * @param string	$provider		The OAuth provider name +	 * @return string					The OAuth provider title +	 */ +	protected function get_provider_title($provider) +	{ +		return $this->language->lang('AUTH_PROVIDER_OAUTH_SERVICE_' . utf8_strtoupper($provider)); +	} + +	/** +	 * Returns whether or not the authorization code is set. +	 * +	 * @param OAuth1Service|OAuth2Service	$service	The external OAuth service +	 * @return bool										Whether or not the authorization code is set in the URL +	 *                       							for the respective OAuth service's version +	 */ +	protected function is_set_code($service) +	{ +		switch ($service::OAUTH_VERSION)  		{ -			return 'LOGIN_LINK_MISSING_DATA'; +			case 1: +				return $this->request->is_set('oauth_token', request_interface::GET); + +			case 2: +				return $this->request->is_set('code', request_interface::GET); + +			default: +				return false;  		} +	} -		// Remove user specified in $link_data if possible -		$user_id = isset($link_data['user_id']) ? $link_data['user_id'] : $this->user->data['user_id']; +	/** +	 * Sets a redirect to the authorization uri. +	 * +	 * @param OAuth1Service|OAuth2Service $service		The external OAuth service +	 * @return array|false								Array if an error occurred, +	 *                            						false on success +	 */ +	protected function set_redirect($service) +	{ +		$parameters = []; -		// Remove the link -		$sql = 'DELETE FROM ' . $this->auth_provider_oauth_token_account_assoc . " -			WHERE provider = '" . $this->db->sql_escape($link_data['oauth_service']) . "' -				AND user_id = " . (int) $user_id; -		$this->db->sql_query($sql); +		if ($service::OAUTH_VERSION === 1) +		{ +			try +			{ +				$token		= $service->requestRequestToken(); +				$parameters	= ['oauth_token' => $token->getRequestToken()]; +			} +			catch (TokenResponseException $e) +			{ +				return [ +					'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, +					'error_msg'		=> $e->getMessage(), +					'user_row'		=> ['user_id' => ANONYMOUS], +				]; +			} +		} -		// Clear all tokens belonging to the user on this service -		$service_name = 'auth.provider.oauth.service.' . strtolower($link_data['oauth_service']); -		$storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table); -		$storage->clearToken($service_name); +		redirect($service->getAuthorizationUri($parameters), false, true); + +		return false;  	}  } diff --git a/phpBB/phpbb/auth/provider/oauth/service/base.php b/phpBB/phpbb/auth/provider/oauth/service/base.php index 6adf64aa30..5ab426a0aa 100644 --- a/phpBB/phpbb/auth/provider/oauth/service/base.php +++ b/phpBB/phpbb/auth/provider/oauth/service/base.php @@ -1,49 +1,57 @@  <?php  /** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @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\auth\provider\oauth\service;  /** -* Base OAuth abstract class that all OAuth services should implement -*/ -abstract class base implements \phpbb\auth\provider\oauth\service\service_interface + * Base OAuth abstract class that all OAuth services should implement + */ +abstract class base implements service_interface  {  	/** -	* External OAuth service provider -	* -	* @var \OAuth\Common\Service\ServiceInterface -	*/ +	 * External OAuth service provider +	 * +	 * @var \OAuth\Common\Service\ServiceInterface +	 */  	protected $service_provider;  	/** -	* {@inheritdoc} -	*/ -	public function get_external_service_provider() +	 * {@inheritdoc} +	 */ +	public function get_auth_scope()  	{ -		return $this->service_provider; +		return [];  	}  	/** -	* {@inheritdoc} -	*/ -	public function get_auth_scope() +	 * {@inheritdoc} +	 */ +	public function get_external_service_class() +	{ +		return ''; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function get_external_service_provider()  	{ -		return array(); +		return $this->service_provider;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function set_external_service_provider(\OAuth\Common\Service\ServiceInterface $service_provider)  	{  		$this->service_provider = $service_provider; diff --git a/phpBB/phpbb/auth/provider/oauth/service/bitly.php b/phpBB/phpbb/auth/provider/oauth/service/bitly.php index 25e731a02c..ca131b2019 100644 --- a/phpBB/phpbb/auth/provider/oauth/service/bitly.php +++ b/phpBB/phpbb/auth/provider/oauth/service/bitly.php @@ -1,94 +1,107 @@  <?php  /** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @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\auth\provider\oauth\service;  /** -* Bitly OAuth service -*/ -class bitly extends \phpbb\auth\provider\oauth\service\base + * Bitly OAuth service + */ +class bitly extends base  { -	/** -	* phpBB config -	* -	* @var \phpbb\config\config -	*/ +	/** @var \phpbb\config\config */  	protected $config; -	/** -	* phpBB request -	* -	* @var \phpbb\request\request_interface -	*/ +	/** @var \phpbb\request\request_interface */  	protected $request;  	/** -	* Constructor -	* -	* @param	\phpbb\config\config				$config -	* @param	\phpbb\request\request_interface	$request -	*/ +	 * Constructor. +	 * +	 * @param \phpbb\config\config				$config		Config object +	 * @param \phpbb\request\request_interface	$request	Request object +	 */  	public function __construct(\phpbb\config\config $config, \phpbb\request\request_interface $request)  	{ -		$this->config = $config; -		$this->request = $request; +		$this->config	= $config; +		$this->request	= $request;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function get_service_credentials()  	{ -		return array( +		return [  			'key'		=> $this->config['auth_oauth_bitly_key'],  			'secret'	=> $this->config['auth_oauth_bitly_secret'], -		); +		];  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function perform_auth_login()  	{  		if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Bitly))  		{ -			throw new \phpbb\auth\provider\oauth\service\exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE'); +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');  		} -		// This was a callback request from bitly, get the token -		$this->service_provider->requestAccessToken($this->request->variable('code', '')); +		try +		{ +			// This was a callback request, get the token +			$this->service_provider->requestAccessToken($this->request->variable('code', '')); +		} +		catch (\OAuth\Common\Http\Exception\TokenResponseException $e) +		{ +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST'); +		} -		// Send a request with it -		$result = json_decode($this->service_provider->request('user/info'), true); +		try +		{ +			// Send a request with it +			$result = (array) json_decode($this->service_provider->request('user/info'), true); +		} +		catch (\OAuth\Common\Exception\Exception $e) +		{ +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST'); +		}  		// Return the unique identifier returned from bitly  		return $result['data']['login'];  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function perform_token_auth()  	{  		if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Bitly))  		{ -			throw new \phpbb\auth\provider\oauth\service\exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE'); +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');  		} -		// Send a request with it -		$result = json_decode($this->service_provider->request('user/info'), true); +		try +		{ +			// Send a request with it +			$result = (array) json_decode($this->service_provider->request('user/info'), true); +		} +		catch (\OAuth\Common\Exception\Exception $e) +		{ +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST'); +		} -		// Return the unique identifier returned from bitly +		// Return the unique identifier  		return $result['data']['login'];  	}  } diff --git a/phpBB/phpbb/auth/provider/oauth/service/facebook.php b/phpBB/phpbb/auth/provider/oauth/service/facebook.php index bb98835e07..f7dbe307eb 100644 --- a/phpBB/phpbb/auth/provider/oauth/service/facebook.php +++ b/phpBB/phpbb/auth/provider/oauth/service/facebook.php @@ -1,63 +1,55 @@  <?php  /** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @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\auth\provider\oauth\service;  /** -* Facebook OAuth service -*/ + * Facebook OAuth service + */  class facebook extends base  { -	/** -	* phpBB config -	* -	* @var \phpbb\config\config -	*/ +	/** @var \phpbb\config\config */  	protected $config; -	/** -	* phpBB request -	* -	* @var \phpbb\request\request_interface -	*/ +	/** @var \phpbb\request\request_interface */  	protected $request;  	/** -	* Constructor -	* -	* @param	\phpbb\config\config				$config -	* @param	\phpbb\request\request_interface 	$request -	*/ +	 * Constructor. +	 * +	 * @param \phpbb\config\config				$config		Config object +	 * @param \phpbb\request\request_interface	$request	Request object +	 */  	public function __construct(\phpbb\config\config $config, \phpbb\request\request_interface $request)  	{ -		$this->config = $config; -		$this->request = $request; +		$this->config	= $config; +		$this->request	= $request;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function get_service_credentials()  	{ -		return array( +		return [  			'key'		=> $this->config['auth_oauth_facebook_key'],  			'secret'	=> $this->config['auth_oauth_facebook_secret'], -		); +		];  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function perform_auth_login()  	{  		if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Facebook)) @@ -65,19 +57,33 @@ class facebook extends base  			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');  		} -		// This was a callback request, get the token -		$this->service_provider->requestAccessToken($this->request->variable('code', '')); +		try +		{ +			// This was a callback request, get the token +			$this->service_provider->requestAccessToken($this->request->variable('code', '')); +		} +		catch (\OAuth\Common\Http\Exception\TokenResponseException $e) +		{ +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST'); +		} -		// Send a request with it -		$result = json_decode($this->service_provider->request('/me'), true); +		try +		{ +			// Send a request with it +			$result = (array) json_decode($this->service_provider->request('/me'), true); +		} +		catch (\OAuth\Common\Exception\Exception $e) +		{ +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST'); +		}  		// Return the unique identifier  		return $result['id'];  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function perform_token_auth()  	{  		if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Facebook)) @@ -85,8 +91,15 @@ class facebook extends base  			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');  		} -		// Send a request with it -		$result = json_decode($this->service_provider->request('/me'), true); +		try +		{ +			// Send a request with it +			$result = (array) json_decode($this->service_provider->request('/me'), true); +		} +		catch (\OAuth\Common\Exception\Exception $e) +		{ +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST'); +		}  		// Return the unique identifier  		return $result['id']; diff --git a/phpBB/phpbb/auth/provider/oauth/service/google.php b/phpBB/phpbb/auth/provider/oauth/service/google.php index cb9f83a94f..6e671ab13e 100644 --- a/phpBB/phpbb/auth/provider/oauth/service/google.php +++ b/phpBB/phpbb/auth/provider/oauth/service/google.php @@ -1,74 +1,66 @@  <?php  /** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @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\auth\provider\oauth\service;  /** -* Google OAuth service -*/ + * Google OAuth service + */  class google extends base  { -	/** -	* phpBB config -	* -	* @var \phpbb\config\config -	*/ +	/** @var \phpbb\config\config */  	protected $config; -	/** -	* phpBB request -	* -	* @var \phpbb\request\request_interface -	*/ +	/** @var \phpbb\request\request_interface */  	protected $request;  	/** -	* Constructor -	* -	* @param	\phpbb\config\config				$config -	* @param	\phpbb\request\request_interface 	$request -	*/ +	 * Constructor. +	 * +	 * @param \phpbb\config\config				$config		Config object +	 * @param \phpbb\request\request_interface	$request	Request object +	 */  	public function __construct(\phpbb\config\config $config, \phpbb\request\request_interface $request)  	{ -		$this->config = $config; -		$this->request = $request; +		$this->config	= $config; +		$this->request	= $request;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function get_auth_scope()  	{ -		return array( +		return [  			'userinfo_email',  			'userinfo_profile', -		); +		];  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function get_service_credentials()  	{ -		return array( +		return [  			'key'		=> $this->config['auth_oauth_google_key'],  			'secret'	=> $this->config['auth_oauth_google_secret'], -		); +		];  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function perform_auth_login()  	{  		if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Google)) @@ -76,19 +68,33 @@ class google extends base  			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');  		} -		// This was a callback request, get the token -		$this->service_provider->requestAccessToken($this->request->variable('code', '')); +		try +		{ +			// This was a callback request, get the token +			$this->service_provider->requestAccessToken($this->request->variable('code', '')); +		} +		catch (\OAuth\Common\Http\Exception\TokenResponseException $e) +		{ +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST'); +		} -		// Send a request with it -		$result = json_decode($this->service_provider->request('https://www.googleapis.com/oauth2/v1/userinfo'), true); +		try +		{ +			// Send a request with it +			$result = (array) json_decode($this->service_provider->request('https://www.googleapis.com/oauth2/v1/userinfo'), true); +		} +		catch (\OAuth\Common\Exception\Exception $e) +		{ +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST'); +		}  		// Return the unique identifier  		return $result['id'];  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function perform_token_auth()  	{  		if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Google)) @@ -96,8 +102,15 @@ class google extends base  			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');  		} -		// Send a request with it -		$result = json_decode($this->service_provider->request('https://www.googleapis.com/oauth2/v1/userinfo'), true); +		try +		{ +			// Send a request with it +			$result = (array) json_decode($this->service_provider->request('https://www.googleapis.com/oauth2/v1/userinfo'), true); +		} +		catch (\OAuth\Common\Exception\Exception $e) +		{ +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST'); +		}  		// Return the unique identifier  		return $result['id']; diff --git a/phpBB/phpbb/auth/provider/oauth/service/service_interface.php b/phpBB/phpbb/auth/provider/oauth/service/service_interface.php index e84eb247b6..ea9ef43788 100644 --- a/phpBB/phpbb/auth/provider/oauth/service/service_interface.php +++ b/phpBB/phpbb/auth/provider/oauth/service/service_interface.php @@ -1,73 +1,87 @@  <?php  /** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @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\auth\provider\oauth\service;  /** -* OAuth service interface -*/ + * OAuth service interface + */  interface service_interface  {  	/** -	* Returns an array of the scopes necessary for auth -	* -	* @return	array	An array of the required scopes -	*/ +	 * Returns an array of the scopes necessary for auth +	 * +	 * @return array	An array of the required scopes +	 */  	public function get_auth_scope();  	/** -	* Returns the external library service provider once it has been set -	* -	* @param	\OAuth\Common\Service\ServiceInterface|null -	*/ -	public function get_external_service_provider(); - -	/** -	* Returns an array containing the service credentials belonging to requested -	* service. -	* -	* @return	array	An array containing the 'key' and the 'secret' of the -	*					service in the form: -	*						array( -	*							'key'		=> string -	*							'secret'	=> string -	*						) -	*/ +	 * Returns an array containing the service credentials belonging to requested +	 * service. +	 * +	 * @return array	An array containing the 'key' and the 'secret' of the +	 *					service in the form: +	 *						array( +	 *							'key'		=> string +	 *							'secret'	=> string +	 *						) +	 */  	public function get_service_credentials();  	/** -	* Returns the results of the authentication in json format -	* -	* @throws	\phpbb\auth\provider\oauth\service\exception -	* @return	string	The unique identifier returned by the service provider -	*					that is used to authenticate the user with phpBB. -	*/ +	 * Returns the results of the authentication in json format +	 * +	 * @throws \phpbb\auth\provider\oauth\service\exception +	 * @return string	The unique identifier returned by the service provider +	 *					that is used to authenticate the user with phpBB. +	 */  	public function perform_auth_login();  	/** -	* Returns the results of the authentication in json format -	* Use this function when the user already has an access token -	* -	* @throws	\phpbb\auth\provider\oauth\service\exception -	* @return	string	The unique identifier returned by the service provider -	*					that is used to authenticate the user with phpBB. -	*/ +	 * Returns the results of the authentication in json format +	 * Use this function when the user already has an access token +	 * +	 * @throws \phpbb\auth\provider\oauth\service\exception +	 * @return string	The unique identifier returned by the service provider +	 *					that is used to authenticate the user with phpBB. +	 */  	public function perform_token_auth();  	/** -	* Sets the external library service provider -	* -	* @param	\OAuth\Common\Service\ServiceInterface	$service_provider -	*/ +	 * Returns the class of external library service provider that has to be used. +	 * +	 * @return string	If the string is a class, it will register the provided string as a class, +	 *						which later will be generated as the OAuth external service provider. +	 * 					If the string is not a class, it will use this string, +	 * 						trying to generate a service for the version 2 and 1 respectively: +	 * 						\OAuth\OAuth2\Service\<string> +	 * 					If the string is empty, it will default to OAuth's standard service classes, +	 * 						trying to generate a service for the version 2 and 1 respectively: +	 * 						\OAuth\OAuth2\Service\Facebook +	 */ +	public function get_external_service_class(); + +	/** +	 * Returns the external library service provider once it has been set +	 * +	 * @param \OAuth\Common\Service\ServiceInterface|null +	 */ +	public function get_external_service_provider(); + +	/** +	 * Sets the external library service provider +	 * +	 * @param \OAuth\Common\Service\ServiceInterface	$service_provider +	 */  	public function set_external_service_provider(\OAuth\Common\Service\ServiceInterface $service_provider);  } diff --git a/phpBB/phpbb/auth/provider/oauth/service/twitter.php b/phpBB/phpbb/auth/provider/oauth/service/twitter.php index 06beac51e2..35cbc9e4f7 100644 --- a/phpBB/phpbb/auth/provider/oauth/service/twitter.php +++ b/phpBB/phpbb/auth/provider/oauth/service/twitter.php @@ -1,102 +1,111 @@  <?php  /** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @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\auth\provider\oauth\service;  /** -* Twitter OAuth service -*/ -class twitter extends \phpbb\auth\provider\oauth\service\base + * Twitter OAuth service + */ +class twitter extends base  { -	/** -	* phpBB config -	* -	* @var \phpbb\config\config -	*/ +	/** @var \phpbb\config\config */  	protected $config; -	/** -	* phpBB request -	* -	* @var \phpbb\request\request_interface -	*/ +	/** @var \phpbb\request\request_interface */  	protected $request;  	/** -	* Constructor -	* -	* @param	\phpbb\config\config				$config -	* @param	\phpbb\request\request_interface	$request -	*/ +	 * Constructor. +	 * +	 * @param \phpbb\config\config				$config		Config object +	 * @param \phpbb\request\request_interface	$request	Request object +	 */  	public function __construct(\phpbb\config\config $config, \phpbb\request\request_interface $request)  	{ -		$this->config = $config; -		$this->request = $request; +		$this->config	= $config; +		$this->request	= $request;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function get_service_credentials()  	{ -		return array( +		return [  			'key'		=> $this->config['auth_oauth_twitter_key'],  			'secret'	=> $this->config['auth_oauth_twitter_secret'], -		); +		];  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function perform_auth_login()  	{  		if (!($this->service_provider instanceof \OAuth\OAuth1\Service\Twitter))  		{ -			throw new \phpbb\auth\provider\oauth\service\exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE'); +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');  		}  		$storage = $this->service_provider->getStorage(); -		$token = $storage->retrieveAccessToken('Twitter'); -		$tokensecret = $token->getRequestTokenSecret(); -		// This was a callback request from twitter, get the token -		$this->service_provider->requestAccessToken( -			$this->request->variable('oauth_token', ''), -			$this->request->variable('oauth_verifier', ''), -			$tokensecret -		); +		try +		{ +			/** @var \OAuth\OAuth1\Token\TokenInterface $token */ +			$token = $storage->retrieveAccessToken('Twitter'); +		} +		catch (\OAuth\Common\Storage\Exception\TokenNotFoundException $e) +		{ +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST'); +		} + +		$secret = $token->getRequestTokenSecret(); + +		try +		{ +			// This was a callback request, get the token +			$this->service_provider->requestAccessToken( +				$this->request->variable('oauth_token', ''), +				$this->request->variable('oauth_verifier', ''), +				$secret +			); +		} +		catch (\OAuth\Common\Http\Exception\TokenResponseException $e) +		{ +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST'); +		}  		// Send a request with it -		$result = json_decode($this->service_provider->request('account/verify_credentials.json'), true); +		$result = (array) json_decode($this->service_provider->request('account/verify_credentials.json'), true); -		// Return the unique identifier returned from twitter +		// Return the unique identifier  		return $result['id'];  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function perform_token_auth()  	{  		if (!($this->service_provider instanceof \OAuth\OAuth1\Service\Twitter))  		{ -			throw new \phpbb\auth\provider\oauth\service\exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE'); +			throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');  		}  		// Send a request with it -		$result = json_decode($this->service_provider->request('account/verify_credentials.json'), true); +		$result = (array) json_decode($this->service_provider->request('account/verify_credentials.json'), true); -		// Return the unique identifier returned from twitter +		// Return the unique identifier  		return $result['id'];  	}  } diff --git a/phpBB/phpbb/auth/provider/oauth/token_storage.php b/phpBB/phpbb/auth/provider/oauth/token_storage.php index b0c2fd0d62..c0f585d7bb 100644 --- a/phpBB/phpbb/auth/provider/oauth/token_storage.php +++ b/phpBB/phpbb/auth/provider/oauth/token_storage.php @@ -1,15 +1,15 @@  <?php  /** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @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\auth\provider\oauth; @@ -20,67 +20,48 @@ use OAuth\Common\Storage\Exception\TokenNotFoundException;  use OAuth\Common\Storage\Exception\AuthorizationStateNotFoundException;  /** -* OAuth storage wrapper for phpbb's cache -*/ + * OAuth storage wrapper for phpBB's cache + */  class token_storage implements TokenStorageInterface  { -	/** -	* Cache driver. -	* -	* @var \phpbb\db\driver\driver_interface -	*/ +	/** @var \phpbb\db\driver\driver_interface */  	protected $db; -	/** -	* phpBB user -	* -	* @var \phpbb\user -	*/ +	/** @var \phpbb\user */  	protected $user; -	/** -	* OAuth token table -	* -	* @var string -	*/ +	/** @var string OAuth table: token storage */  	protected $oauth_token_table; -	/** -	* OAuth state table -	* -	* @var string -	*/ +	/** @var string OAuth table: state */  	protected $oauth_state_table; -	/** -	* @var object|TokenInterface -	*/ +	/** @var TokenInterface OAuth token */  	protected $cachedToken; -	/** -	* @var string -	*/ +	/** @var string OAuth state */  	protected $cachedState;  	/** -	* Creates token storage for phpBB. -	* -	* @param	\phpbb\db\driver\driver_interface	$db -	* @param	\phpbb\user		$user -	* @param	string			$oauth_token_table -	* @param	string			$oauth_state_table -	*/ +	 * Constructor. +	 * +	 * @param \phpbb\db\driver\driver_interface	$db					Database object +	 * @param \phpbb\user						$user				User object +	 * @param string							$oauth_token_table	OAuth table: token storage +	 * @param string							$oauth_state_table	OAuth table: state +	 */  	public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\user $user, $oauth_token_table, $oauth_state_table)  	{ -		$this->db = $db; -		$this->user = $user; +		$this->db	= $db; +		$this->user	= $user; +  		$this->oauth_token_table = $oauth_token_table;  		$this->oauth_state_table = $oauth_state_table;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function retrieveAccessToken($service)  	{  		$service = $this->get_service_name_for_db($service); @@ -90,10 +71,10 @@ class token_storage implements TokenStorageInterface  			return $this->cachedToken;  		} -		$data = array( +		$data = [  			'user_id'	=> (int) $this->user->data['user_id'],  			'provider'	=> $service, -		); +		];  		if ((int) $this->user->data['user_id'] === ANONYMOUS)  		{ @@ -104,33 +85,38 @@ class token_storage implements TokenStorageInterface  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function storeAccessToken($service, TokenInterface $token)  	{  		$service = $this->get_service_name_for_db($service);  		$this->cachedToken = $token; -		$data = array( +		$data = [  			'oauth_token'	=> $this->json_encode_token($token), -		); +		];  		$sql = 'UPDATE ' . $this->oauth_token_table . ' -				SET ' . $this->db->sql_build_array('UPDATE', $data) . ' -				WHERE user_id = ' . (int) $this->user->data['user_id'] . ' -					' . ((int) $this->user->data['user_id'] === ANONYMOUS ? "AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'" : '') . " -					AND provider = '" . $this->db->sql_escape($service) . "'"; +			SET ' . $this->db->sql_build_array('UPDATE', $data) . ' +			WHERE user_id = ' . (int) $this->user->data['user_id'] . " +				AND provider = '" . $this->db->sql_escape($service) . "'"; + +		if ((int) $this->user->data['user_id'] === ANONYMOUS) +		{ +			$sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'"; +		} +  		$this->db->sql_query($sql);  		if (!$this->db->sql_affectedrows())  		{ -			$data = array( +			$data = [  				'user_id'		=> (int) $this->user->data['user_id'],  				'provider'		=> $service,  				'oauth_token'	=> $this->json_encode_token($token),  				'session_id'	=> $this->user->data['session_id'], -			); +			];  			$sql = 'INSERT INTO ' . $this->oauth_token_table . $this->db->sql_build_array('INSERT', $data); @@ -141,8 +127,8 @@ class token_storage implements TokenStorageInterface  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function hasAccessToken($service)  	{  		$service = $this->get_service_name_for_db($service); @@ -152,22 +138,22 @@ class token_storage implements TokenStorageInterface  			return true;  		} -		$data = array( +		$data = [  			'user_id'	=> (int) $this->user->data['user_id'],  			'provider'	=> $service, -		); +		];  		if ((int) $this->user->data['user_id'] === ANONYMOUS)  		{  			$data['session_id']	= $this->user->data['session_id'];  		} -		return $this->_has_acess_token($data); +		return $this->has_access_token($data);  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function clearToken($service)  	{  		$service = $this->get_service_name_for_db($service); @@ -189,13 +175,13 @@ class token_storage implements TokenStorageInterface  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function clearAllTokens()  	{  		$this->cachedToken = null; -		$sql = 'DELETE FROM ' . $this->oauth_token_table . ' +		$sql = 'DELETE FROM ' . $this->oauth_token_table . '   			WHERE user_id = ' . (int) $this->user->data['user_id'];  		if ((int) $this->user->data['user_id'] === ANONYMOUS) @@ -209,31 +195,30 @@ class token_storage implements TokenStorageInterface  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function storeAuthorizationState($service, $state)  	{  		$service = $this->get_service_name_for_db($service);  		$this->cachedState = $state; -		$data = array( +		$data = [  			'user_id'		=> (int) $this->user->data['user_id'],  			'provider'		=> $service,  			'oauth_state'	=> $state,  			'session_id'	=> $this->user->data['session_id'], -		); +		]; -		$sql = 'INSERT INTO ' . $this->oauth_state_table . ' -			' . $this->db->sql_build_array('INSERT', $data); +		$sql = 'INSERT INTO ' . $this->oauth_state_table . ' ' . $this->db->sql_build_array('INSERT', $data);  		$this->db->sql_query($sql);  		return $this;  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function hasAuthorizationState($service)  	{  		$service = $this->get_service_name_for_db($service); @@ -243,10 +228,10 @@ class token_storage implements TokenStorageInterface  			return true;  		} -		$data = array( +		$data = [  			'user_id'	=> (int) $this->user->data['user_id'],  			'provider'	=> $service, -		); +		];  		if ((int) $this->user->data['user_id'] === ANONYMOUS)  		{ @@ -257,8 +242,8 @@ class token_storage implements TokenStorageInterface  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function retrieveAuthorizationState($service)  	{  		$service = $this->get_service_name_for_db($service); @@ -268,10 +253,10 @@ class token_storage implements TokenStorageInterface  			return $this->cachedState;  		} -		$data = array( +		$data = [  			'user_id'	=> (int) $this->user->data['user_id'],  			'provider'	=> $service, -		); +		];  		if ((int) $this->user->data['user_id'] === ANONYMOUS)  		{ @@ -282,8 +267,8 @@ class token_storage implements TokenStorageInterface  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function clearAuthorizationState($service)  	{  		$service = $this->get_service_name_for_db($service); @@ -305,8 +290,8 @@ class token_storage implements TokenStorageInterface  	}  	/** -	* {@inheritdoc} -	*/ +	 * {@inheritdoc} +	 */  	public function clearAllAuthorizationStates()  	{  		$this->cachedState = null; @@ -325,10 +310,11 @@ class token_storage implements TokenStorageInterface  	}  	/** -	* Updates the user_id field in the database assosciated with the token -	* -	* @param	int	$user_id -	*/ +	 * Updates the user_id field in the database associated with the token. +	 * +	 * @param int		$user_id	The user identifier +	 * @return void +	 */  	public function set_user_id($user_id)  	{  		if (!$this->cachedToken) @@ -336,21 +322,24 @@ class token_storage implements TokenStorageInterface  			return;  		} +		$data = [ +			'user_id' => (int) $user_id, +		]; +  		$sql = 'UPDATE ' . $this->oauth_token_table . ' -			SET ' . $this->db->sql_build_array('UPDATE', array( -					'user_id' => (int) $user_id -				)) . ' -				WHERE user_id = ' . (int) $this->user->data['user_id'] . " -					AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'"; +			SET ' . $this->db->sql_build_array('UPDATE', $data) . ' +			WHERE user_id = ' . (int) $this->user->data['user_id'] . " +				AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'";  		$this->db->sql_query($sql);  	}  	/** -	* Checks to see if an access token exists solely by the session_id of the user -	* -	* @param	string	$service	The name of the OAuth service -	* @return	bool	true if they have token, false if they don't -	*/ +	 * Checks to see if an access token exists solely by the session_id of the user. +	 * +	 * @param string	$service	The OAuth service name +	 * @return bool					true if the user's access token exists, +	 * 								false if the user's access token does not exist +	 */  	public function has_access_token_by_session($service)  	{  		$service = $this->get_service_name_for_db($service); @@ -360,20 +349,21 @@ class token_storage implements TokenStorageInterface  			return true;  		} -		$data = array( +		$data = [  			'session_id'	=> $this->user->data['session_id'],  			'provider'		=> $service, -		); +		]; -		return $this->_has_acess_token($data); +		return $this->has_access_token($data);  	}  	/** -	* Checks to see if a state exists solely by the session_id of the user -	* -	* @param	string	$service	The name of the OAuth service -	* @return	bool	true if they have state, false if they don't -	*/ +	 * Checks to see if a state exists solely by the session_id of the user. +	 * +	 * @param string	$service	The OAuth service name +	 * @return bool					true if the user's state exists, +	 * 								false if the user's state does not exist +	 */  	public function has_state_by_session($service)  	{  		$service = $this->get_service_name_for_db($service); @@ -383,25 +373,34 @@ class token_storage implements TokenStorageInterface  			return true;  		} -		$data = array( +		$data = [  			'session_id'	=> $this->user->data['session_id'],  			'provider'		=> $service, -		); +		];  		return (bool) $this->get_state_row($data);  	}  	/** -	* A helper function that performs the query for has access token functions -	* -	* @param	array	$data -	* @return	bool -	*/ -	protected function _has_acess_token($data) +	 * A helper function that performs the query for has access token functions. +	 * +	 * @param array		$data		The SQL WHERE data +	 * @return bool					true if the user's access token exists, +	 * 								false if the user's access token does not exist +	 */ +	protected function has_access_token($data)  	{  		return (bool) $this->get_access_token_row($data);  	} +	/** +	 * A helper function that performs the query for retrieving access token functions by session. +	 * Also checks if the token is a valid token. +	 * +	 * @param string	$service	The OAuth service provider name +	 * @return TokenInterface +	 * @throws TokenNotFoundException +	 */  	public function retrieve_access_token_by_session($service)  	{  		$service = $this->get_service_name_for_db($service); @@ -411,14 +410,21 @@ class token_storage implements TokenStorageInterface  			return $this->cachedToken;  		} -		$data = array( +		$data = [  			'session_id'	=> $this->user->data['session_id'], -			'provider'	=> $service, -		); +			'provider'		=> $service, +		];  		return $this->_retrieve_access_token($data);  	} +	/** +	 * A helper function that performs the query for retrieving state functions by session. +	 * +	 * @param string	$service	The OAuth service provider name +	 * @return string				The OAuth state +	 * @throws AuthorizationStateNotFoundException +	 */  	public function retrieve_state_by_session($service)  	{  		$service = $this->get_service_name_for_db($service); @@ -428,22 +434,22 @@ class token_storage implements TokenStorageInterface  			return $this->cachedState;  		} -		$data = array( +		$data = [  			'session_id'	=> $this->user->data['session_id'], -			'provider'	=> $service, -		); +			'provider'		=> $service, +		];  		return $this->_retrieve_state($data);  	}  	/** -	* A helper function that performs the query for retrieve access token functions -	* Also checks if the token is a valid token -	* -	* @param	array	$data -	* @return	mixed -	* @throws \OAuth\Common\Storage\Exception\TokenNotFoundException -	*/ +	 * A helper function that performs the query for retrieve access token functions. +	 * Also checks if the token is a valid token. +	 * +	 * @param array		$data		The SQL WHERE data +	 * @return TokenInterface +	 * @throws TokenNotFoundException +	 */  	protected function _retrieve_access_token($data)  	{  		$row = $this->get_access_token_row($data); @@ -459,19 +465,21 @@ class token_storage implements TokenStorageInterface  		if (!($token instanceof TokenInterface))  		{  			$this->clearToken($data['provider']); +  			throw new TokenNotFoundException('AUTH_PROVIDER_OAUTH_TOKEN_ERROR_INCORRECTLY_STORED');  		}  		$this->cachedToken = $token; +  		return $token;  	}  	/** -	 * A helper function that performs the query for retrieve state functions +	 * A helper function that performs the query for retrieve state functions.  	 * -	 * @param	array	$data -	 * @return	mixed -	 * @throws \OAuth\Common\Storage\Exception\AuthorizationStateNotFoundException +	 * @param array		$data		The SQL WHERE data +	 * @return string				The OAuth state +	 * @throws AuthorizationStateNotFoundException  	 */  	protected function _retrieve_state($data)  	{ @@ -483,18 +491,21 @@ class token_storage implements TokenStorageInterface  		}  		$this->cachedState = $row['oauth_state']; +  		return $this->cachedState;  	}  	/** -	* A helper function that performs the query for retrieving an access token -	* -	* @param	array	$data -	* @return	mixed -	*/ +	 * A helper function that performs the query for retrieving an access token. +	 * +	 * @param array		$data		The SQL WHERE data +	 * @return array|false			array with the OAuth token row, +	 *                       		false if the token does not exist +	 */  	protected function get_access_token_row($data)  	{ -		$sql = 'SELECT oauth_token FROM ' . $this->oauth_token_table . ' +		$sql = 'SELECT oauth_token  +			FROM ' . $this->oauth_token_table . '  			WHERE ' . $this->db->sql_build_array('SELECT', $data);  		$result = $this->db->sql_query($sql);  		$row = $this->db->sql_fetchrow($result); @@ -504,14 +515,16 @@ class token_storage implements TokenStorageInterface  	}  	/** -	 * A helper function that performs the query for retrieving a state +	 * A helper function that performs the query for retrieving a state.  	 * -	 * @param	array	$data -	 * @return	mixed +	 * @param array		$data		The SQL WHERE data +	 * @return array|false			array with the OAuth state row, +	 *                       		false if the state does not exist  	 */  	protected function get_state_row($data)  	{ -		$sql = 'SELECT oauth_state FROM ' . $this->oauth_state_table . ' +		$sql = 'SELECT oauth_state  +			FROM ' . $this->oauth_state_table . '  			WHERE ' . $this->db->sql_build_array('SELECT', $data);  		$result = $this->db->sql_query($sql);  		$row = $this->db->sql_fetchrow($result); @@ -520,16 +533,22 @@ class token_storage implements TokenStorageInterface  		return $row;  	} +	/** +	 * A helper function that JSON encodes a TokenInterface's data. +	 * +	 * @param TokenInterface	$token +	 * @return string					The json encoded TokenInterface's data +	 */  	public function json_encode_token(TokenInterface $token)  	{ -		$members = array( +		$members = [  			'accessToken'	=> $token->getAccessToken(),  			'endOfLife'		=> $token->getEndOfLife(),  			'extraParams'	=> $token->getExtraParams(),  			'refreshToken'	=> $token->getRefreshToken(),  			'token_class'	=> get_class($token), -		); +		];  		// Handle additional data needed for OAuth1 tokens  		if ($token instanceof StdOAuth1Token) @@ -542,6 +561,13 @@ class token_storage implements TokenStorageInterface  		return json_encode($members);  	} +	/** +	 * A helper function that JSON decodes a data string and creates a TokenInterface. +	 * +	 * @param string	$json			The json encoded TokenInterface's data +	 * @return TokenInterface +	 * @throws TokenNotFoundException +	 */  	public function json_decode_token($json)  	{  		$token_data = json_decode($json, true); @@ -557,7 +583,10 @@ class token_storage implements TokenStorageInterface  		$endOfLife		= $token_data['endOfLife'];  		$extra_params	= $token_data['extraParams']; -		// Create the token +		/** +		 * Create the token +		 * @var TokenInterface	$token +		 */  		$token = new $token_class($access_token, $refresh_token, TokenInterface::EOL_NEVER_EXPIRES, $extra_params);  		$token->setEndOfLife($endOfLife); @@ -573,20 +602,19 @@ class token_storage implements TokenStorageInterface  	}  	/** -	* Returns the name of the service as it must be stored in the database. -	* -	* @param	string	$service	The name of the OAuth service -	* @return	string	The name of the OAuth service as it needs to be stored -	*					in the database. -	*/ -	protected function get_service_name_for_db($service) +	 * Returns the service name as it must be stored in the database. +	 * +	 * @param string	$provider	The OAuth provider name +	 * @return string				The OAuth service name +	 */ +	protected function get_service_name_for_db($provider)  	{  		// Enforce the naming convention for oauth services -		if (strpos($service, 'auth.provider.oauth.service.') !== 0) +		if (strpos($provider, 'auth.provider.oauth.service.') !== 0)  		{ -			$service = 'auth.provider.oauth.service.' . strtolower($service); +			$provider = 'auth.provider.oauth.service.' . strtolower($provider);  		} -		return $service; +		return $provider;  	}  } diff --git a/phpBB/phpbb/auth/provider/provider_interface.php b/phpBB/phpbb/auth/provider/provider_interface.php index 463324ff46..21c73a33c5 100644 --- a/phpBB/phpbb/auth/provider/provider_interface.php +++ b/phpBB/phpbb/auth/provider/provider_interface.php @@ -53,7 +53,7 @@ interface provider_interface  	 * Autologin function  	 *  	 * @return 	array|null	containing the user row, empty if no auto login -	 * 						should take place, or null if not impletmented. +	 * 						should take place, or null if not implemented.  	 */  	public function autologin(); @@ -68,7 +68,7 @@ interface provider_interface  	/**  	 * This function updates the template with variables related to the acp -	 * options with whatever configuraton values are passed to it as an array. +	 * options with whatever configuration values are passed to it as an array.  	 * It then returns the name of the acp file related to this authentication  	 * provider.  	 * diff --git a/phpBB/phpbb/cache/driver/memcache.php b/phpBB/phpbb/cache/driver/memcache.php deleted file mode 100644 index 57f138f574..0000000000 --- a/phpBB/phpbb/cache/driver/memcache.php +++ /dev/null @@ -1,122 +0,0 @@ -<?php -/** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @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\cache\driver; - -if (!defined('PHPBB_ACM_MEMCACHE_PORT')) -{ -	define('PHPBB_ACM_MEMCACHE_PORT', 11211); -} - -if (!defined('PHPBB_ACM_MEMCACHE_COMPRESS')) -{ -	define('PHPBB_ACM_MEMCACHE_COMPRESS', false); -} - -if (!defined('PHPBB_ACM_MEMCACHE_HOST')) -{ -	define('PHPBB_ACM_MEMCACHE_HOST', 'localhost'); -} - -if (!defined('PHPBB_ACM_MEMCACHE')) -{ -	//can define multiple servers with host1/port1,host2/port2 format -	define('PHPBB_ACM_MEMCACHE', PHPBB_ACM_MEMCACHE_HOST . '/' . PHPBB_ACM_MEMCACHE_PORT); -} - -/** -* ACM for Memcached -*/ -class memcache extends \phpbb\cache\driver\memory -{ -	var $extension = 'memcache'; - -	var $memcache; -	var $flags = 0; - -	function __construct() -	{ -		// Call the parent constructor -		parent::__construct(); - -		$this->memcache = new \Memcache; -		foreach (explode(',', PHPBB_ACM_MEMCACHE) as $u) -		{ -			preg_match('#(.*)/(\d+)#', $u, $parts); -			$this->memcache->addServer(trim($parts[1]), (int) trim($parts[2])); -		} -		$this->flags = (PHPBB_ACM_MEMCACHE_COMPRESS) ? MEMCACHE_COMPRESSED : 0; -	} - -	/** -	* {@inheritDoc} -	*/ -	function unload() -	{ -		parent::unload(); - -		$this->memcache->close(); -	} - -	/** -	* {@inheritDoc} -	*/ -	function purge() -	{ -		$this->memcache->flush(); - -		parent::purge(); -	} - -	/** -	* Fetch an item from the cache -	* -	* @access protected -	* @param string $var Cache key -	* @return mixed Cached data -	*/ -	function _read($var) -	{ -		return $this->memcache->get($this->key_prefix . $var); -	} - -	/** -	* Store data in the cache -	* -	* @access protected -	* @param string $var Cache key -	* @param mixed $data Data to store -	* @param int $ttl Time-to-live of cached data -	* @return bool True if the operation succeeded -	*/ -	function _write($var, $data, $ttl = 2592000) -	{ -		if (!$this->memcache->replace($this->key_prefix . $var, $data, $this->flags, $ttl)) -		{ -			return $this->memcache->set($this->key_prefix . $var, $data, $this->flags, $ttl); -		} -		return true; -	} - -	/** -	* Remove an item from the cache -	* -	* @access protected -	* @param string $var Cache key -	* @return bool True if the operation succeeded -	*/ -	function _delete($var) -	{ -		return $this->memcache->delete($this->key_prefix . $var); -	} -} diff --git a/phpBB/phpbb/cache/driver/memcached.php b/phpBB/phpbb/cache/driver/memcached.php index 7d66759ec2..fbb587a369 100644 --- a/phpBB/phpbb/cache/driver/memcached.php +++ b/phpBB/phpbb/cache/driver/memcached.php @@ -50,12 +50,16 @@ class memcached extends \phpbb\cache\driver\memory  	/**  	 * Memcached constructor +	 * +	 * @param string $memcached_servers Memcached servers string (optional)  	 */ -	public function __construct() +	public function __construct($memcached_servers = '')  	{  		// Call the parent constructor  		parent::__construct(); +		$memcached_servers = $memcached_servers ?: PHPBB_ACM_MEMCACHED; +  		$this->memcached = new \Memcached();  		$this->memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);  		// Memcached defaults to using compression, disable if we don't want @@ -65,10 +69,20 @@ class memcached extends \phpbb\cache\driver\memory  			$this->memcached->setOption(\Memcached::OPT_COMPRESSION, false);  		} -		foreach (explode(',', PHPBB_ACM_MEMCACHED) as $u) +		$server_list = []; +		foreach (explode(',', $memcached_servers) as $u) +		{ +			if (preg_match('#(.*)/(\d+)#', $u, $parts)) +			{ +				$server_list[] = [trim($parts[1]), (int) trim($parts[2])]; +			} +		} + +		$this->memcached->addServers($server_list); + +		if (empty($server_list) || empty($this->memcached->getStats()))  		{ -			preg_match('#(.*)/(\d+)#', $u, $parts); -			$this->memcached->addServer(trim($parts[1]), (int) trim($parts[2])); +			trigger_error('Could not connect to memcached server(s).');  		}  	} diff --git a/phpBB/phpbb/console/command/fixup/recalculate_email_hash.php b/phpBB/phpbb/console/command/fixup/recalculate_email_hash.php deleted file mode 100644 index 6f7096296d..0000000000 --- a/phpBB/phpbb/console/command/fixup/recalculate_email_hash.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php -/** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @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\console\command\fixup; - -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; - -class recalculate_email_hash extends \phpbb\console\command\command -{ -	/** @var \phpbb\db\driver\driver_interface */ -	protected $db; - -	public function __construct(\phpbb\user $user, \phpbb\db\driver\driver_interface $db) -	{ -		$this->db = $db; - -		parent::__construct($user); -	} - -	protected function configure() -	{ -		$this -			->setName('fixup:recalculate-email-hash') -			->setDescription($this->user->lang('CLI_DESCRIPTION_RECALCULATE_EMAIL_HASH')) -		; -	} - -	protected function execute(InputInterface $input, OutputInterface $output) -	{ -		$io = new SymfonyStyle($input, $output); - -		$sql = 'SELECT user_id, user_email, user_email_hash -			FROM ' . USERS_TABLE . ' -			WHERE user_type <> ' . USER_IGNORE . " -				AND user_email <> ''"; -		$result = $this->db->sql_query($sql); - -		while ($row = $this->db->sql_fetchrow($result)) -		{ -			$user_email_hash = phpbb_email_hash($row['user_email']); -			if ($user_email_hash !== $row['user_email_hash']) -			{ -				$sql_ary = array( -					'user_email_hash'	=> $user_email_hash, -				); - -				$sql = 'UPDATE ' . USERS_TABLE . ' -					SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' -					WHERE user_id = ' . (int) $row['user_id']; -				$this->db->sql_query($sql); - -				if ($output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) -				{ -					$io->table( -						array('user_id', 'user_email', 'user_email_hash'), -						array(array($row['user_id'], $row['user_email'], $user_email_hash)) -					); -				} -			} -		} -		$this->db->sql_freeresult($result); - -		$io->success($this->user->lang('CLI_FIXUP_RECALCULATE_EMAIL_HASH_SUCCESS')); -	} -} diff --git a/phpBB/phpbb/content_visibility.php b/phpBB/phpbb/content_visibility.php index 704ec6badb..fbc56f3db2 100644 --- a/phpBB/phpbb/content_visibility.php +++ b/phpBB/phpbb/content_visibility.php @@ -144,7 +144,14 @@ class content_visibility  	*/  	public function is_visible($mode, $forum_id, $data)  	{ -		$is_visible = $this->auth->acl_get('m_approve', $forum_id) || $data[$mode . '_visibility'] == ITEM_APPROVED; +		$visibility = $data[$mode . '_visibility']; +		$poster_key = ($mode === 'topic') ? 'topic_poster' : 'poster_id'; +		$is_visible = ($visibility == ITEM_APPROVED) || +			($this->config['display_unapproved_posts'] && +				($this->user->data['user_id'] != ANONYMOUS) && +				($visibility == ITEM_UNAPPROVED || $visibility == ITEM_REAPPROVE) && +				($this->user->data['user_id'] == $data[$poster_key])) || +			 $this->auth->acl_get('m_approve', $forum_id);  		/**  		* Allow changing the result of calling is_visible @@ -216,9 +223,16 @@ class content_visibility  		}  		else  		{ -			$where_sql .= $table_alias . $mode . '_visibility = ' . ITEM_APPROVED; -		} +			$visibility_query = $table_alias . $mode . '_visibility = '; +			$where_sql .= '(' . $visibility_query . ITEM_APPROVED . ')'; +			if ($this->config['display_unapproved_posts'] && ($this->user->data['user_id'] != ANONYMOUS)) +			{ +				$poster_key = ($mode === 'topic') ? 'topic_poster' : 'poster_id'; +				$where_sql .= ' OR ((' . $visibility_query . ITEM_UNAPPROVED . ' OR ' . $visibility_query . ITEM_REAPPROVE .')'; +				$where_sql .= ' AND ' . $table_alias . $poster_key . ' = ' . ((int) $this->user->data['user_id']) . ')'; +			} +		}  		return '(' . $where_sql . ')';  	} diff --git a/phpBB/phpbb/db/migration/data/v330/add_display_unapproved_posts_config.php b/phpBB/phpbb/db/migration/data/v330/add_display_unapproved_posts_config.php new file mode 100644 index 0000000000..b429270827 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v330/add_display_unapproved_posts_config.php @@ -0,0 +1,24 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @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\db\migration\data\v330; + +class add_display_unapproved_posts_config extends \phpbb\db\migration\migration +{ +	public function update_data() +	{ +		return [ +			['config.add', ['display_unapproved_posts', 1]], +		]; +	} +} diff --git a/phpBB/phpbb/db/migration/data/v330/remove_email_hash.php b/phpBB/phpbb/db/migration/data/v330/remove_email_hash.php new file mode 100644 index 0000000000..dc43678625 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v330/remove_email_hash.php @@ -0,0 +1,57 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @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\db\migration\data\v330; + +class remove_email_hash extends \phpbb\db\migration\migration +{ +	public function update_schema() +	{ +		return [ +			'add_index' => [ +				$this->table_prefix . 'users' => [ +					'user_email' => ['user_email'], +				], +			], +			'drop_keys' => [ +				$this->table_prefix . 'users' => [ +					'user_email_hash', +				], +			], +			'drop_columns' => [ +				$this->table_prefix . 'users' => ['user_email_hash'], +			], +		]; +	} + +	public function revert_schema() +	{ +		return [ +			'add_columns' => [ +				$this->table_prefix . 'users' => [ +					'user_email_hash' => ['BINT', 0], +				], +			], +			'add_index' => [ +				$this->table_prefix . 'users' => [ +					'user_email_hash', +				], +			], +			'drop_keys' => [ +				$this->table_prefix . 'users' => [ +					'user_email' => ['user_email'], +				], +			], +		]; +	} +} diff --git a/phpBB/phpbb/db/migration/data/v330/v330b2.php b/phpBB/phpbb/db/migration/data/v330/v330b2.php new file mode 100644 index 0000000000..1badc1387a --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v330/v330b2.php @@ -0,0 +1,38 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @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\db\migration\data\v330; + +class v330b2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.3.0-b2', '>='); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v330\add_display_unapproved_posts_config', +			'\phpbb\db\migration\data\v330\forums_legend_limit', +			'\phpbb\db\migration\data\v330\remove_email_hash', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.3.0-b2')), +		); +	} +} diff --git a/phpBB/phpbb/di/service_collection.php b/phpBB/phpbb/di/service_collection.php index 8c1c172e36..6298670c42 100644 --- a/phpBB/phpbb/di/service_collection.php +++ b/phpBB/phpbb/di/service_collection.php @@ -49,21 +49,6 @@ class service_collection extends \ArrayObject  		return new service_collection_iterator($this);  	} -	// Because of a PHP issue we have to redefine offsetExists -	// (even with a call to the parent): -	// 		https://bugs.php.net/bug.php?id=66834 -	// 		https://bugs.php.net/bug.php?id=67067 -	// But it triggers a sniffer issue that we have to skip -	// @codingStandardsIgnoreStart -	/** -	* {@inheritdoc} -	*/ -	public function offsetExists($index) -	{ -		return parent::offsetExists($index); -	} -	// @codingStandardsIgnoreEnd -  	/**  	* {@inheritdoc}  	*/ @@ -76,11 +61,11 @@ class service_collection extends \ArrayObject  	* Add a service to the collection  	*  	* @param string $name The service name -	* @return null +	* @return void  	*/  	public function add($name)  	{ -		$this->offsetSet($name, null); +		$this->offsetSet($name, false);  	}  	/** diff --git a/phpBB/phpbb/install/module/install_database/task/add_config_settings.php b/phpBB/phpbb/install/module/install_database/task/add_config_settings.php index ba439609ff..91d7884aa4 100644 --- a/phpBB/phpbb/install/module/install_database/task/add_config_settings.php +++ b/phpBB/phpbb/install/module/install_database/task/add_config_settings.php @@ -245,7 +245,6 @@ class add_config_settings extends \phpbb\install\task_base  					user_lang = '" . $this->db->sql_escape($this->install_config->get('user_language', 'en')) . "',  					user_email='" . $this->db->sql_escape($this->install_config->get('board_email')) . "',  					user_dateformat='" . $this->db->sql_escape($this->language->lang('default_dateformat')) . "', -					user_email_hash = " . $this->db->sql_escape(phpbb_email_hash($this->install_config->get('board_email'))) . ",  					username_clean = '" . $this->db->sql_escape(utf8_clean_string($this->install_config->get('admin_name'))) . "'  				WHERE username = 'Admin'", diff --git a/phpBB/phpbb/textformatter/s9e/bbcode_merger.php b/phpBB/phpbb/textformatter/s9e/bbcode_merger.php index af644192d8..d1bedb0b72 100644 --- a/phpBB/phpbb/textformatter/s9e/bbcode_merger.php +++ b/phpBB/phpbb/textformatter/s9e/bbcode_merger.php @@ -50,7 +50,7 @@ class bbcode_merger  		$with    = $this->create_bbcode($with);  		// Select the appropriate strategy for merging this BBCode -		if ($this->is_content_bbcode($without, $with)) +		if (!$this->is_optional_bbcode($without, $with) && $this->is_content_bbcode($without, $with))  		{  			$merged = $this->merge_content_bbcode($without, $with);  		} @@ -107,12 +107,12 @@ class bbcode_merger  	/**  	* Test whether the two definitions form a "content"-style BBCode  	* -	* Such BBCodes include the [URL] BBCode, which uses its text content as +	* Such BBCodes include the [url] BBCode, which uses its text content as  	* attribute if none is provided  	*  	* @param  array $without BBCode definition without an attribute  	* @param  array $with    BBCode definition with an attribute -	* @return array          Merged definition +	* @return bool  	*/  	protected function is_content_bbcode(array $without, array $with)  	{ @@ -123,6 +123,22 @@ class bbcode_merger  	}  	/** +	* Test whether the two definitions form BBCode with an optional attribute +	* +	* @param  array $without BBCode definition without an attribute +	* @param  array $with    BBCode definition with an attribute +	* @return bool +	*/ +	protected function is_optional_bbcode(array $without, array $with) +	{ +		// Remove the default attribute from the definition +		$with['usage'] = preg_replace('(=[^\\]]++)', '', $with['usage']); + +		// Test whether both definitions are the same, regardless of case +		return strcasecmp($without['usage'], $with['usage']) === 0; +	} + +	/**  	* Merge the two BBCode definitions of a "content"-style BBCode  	*  	* @param  array $without BBCode definition without an attribute @@ -131,7 +147,7 @@ class bbcode_merger  	*/  	protected function merge_content_bbcode(array $without, array $with)  	{ -		// Convert [X={X}] into [X={X;useContent}] +		// Convert [x={X}] into [x={X;useContent}]  		$usage = preg_replace('(\\})', ';useContent}', $with['usage'], 1);  		// Use the template from the definition that uses an attribute @@ -143,7 +159,7 @@ class bbcode_merger  	/**  	* Merge the two BBCode definitions of a BBCode with an optional argument  	* -	* Such BBCodes include the [QUOTE] BBCode, which takes an optional argument +	* Such BBCodes include the [quote] BBCode, which takes an optional argument  	* but otherwise does not behave differently  	*  	* @param  array $without BBCode definition without an attribute diff --git a/phpBB/phpbb/textformatter/s9e/factory.php b/phpBB/phpbb/textformatter/s9e/factory.php index dca1c78d40..f82c7b0771 100644 --- a/phpBB/phpbb/textformatter/s9e/factory.php +++ b/phpBB/phpbb/textformatter/s9e/factory.php @@ -89,6 +89,8 @@ class factory implements \phpbb\textformatter\cache_interface  				author={TEXT1;optional}  				post_id={UINT;optional}  				post_url={URL;optional;postFilter=#false} +				msg_id={UINT;optional} +				msg_url={URL;optional;postFilter=#false}  				profile_url={URL;optional;postFilter=#false}  				time={UINT;optional}  				url={URL;optional} diff --git a/phpBB/phpbb/textformatter/s9e/link_helper.php b/phpBB/phpbb/textformatter/s9e/link_helper.php index 483794a83e..1cd5dd2fa7 100644 --- a/phpBB/phpbb/textformatter/s9e/link_helper.php +++ b/phpBB/phpbb/textformatter/s9e/link_helper.php @@ -61,7 +61,7 @@ class link_helper  		$text   = substr($parser->getText(), $start, $length);  		// Create a tag that consumes the link's text and make it depends on this tag -		$link_text_tag = $parser->addSelfClosingTag('LINK_TEXT', $start, $length); +		$link_text_tag = $parser->addSelfClosingTag('LINK_TEXT', $start, $length, 10);  		$link_text_tag->setAttribute('text', $text);  		$tag->cascadeInvalidationTo($link_text_tag);  	} diff --git a/phpBB/phpbb/textformatter/s9e/parser.php b/phpBB/phpbb/textformatter/s9e/parser.php index a36fc63141..f7e4668980 100644 --- a/phpBB/phpbb/textformatter/s9e/parser.php +++ b/phpBB/phpbb/textformatter/s9e/parser.php @@ -15,6 +15,7 @@ namespace phpbb\textformatter\s9e;  use s9e\TextFormatter\Parser\AttributeFilters\UrlFilter;  use s9e\TextFormatter\Parser\Logger; +use s9e\TextFormatter\Parser\Tag;  /**  * s9e\TextFormatter\Parser adapter @@ -219,7 +220,7 @@ class parser implements \phpbb\textformatter\parser_interface  			{  				$errors[] = array($msg, $context['max_' . strtolower($m[1])]);  			} -			else if ($msg === 'Tag is disabled') +			else if ($msg === 'Tag is disabled' && $this->is_a_bbcode($context['tag']))  			{  				$name = strtolower($context['tag']->getName());  				$errors[] = array('UNAUTHORISED_BBCODE', '[' . $name . ']'); @@ -396,4 +397,21 @@ class parser implements \phpbb\textformatter\parser_interface  		return $url;  	} + +	/** +	* Test whether given tag consumes text that looks like BBCode-styled markup +	* +	* @param  Tag  $tag Original tag +	* @return bool +	*/ +	protected function is_a_bbcode(Tag $tag) +	{ +		if ($tag->getLen() < 3) +		{ +			return false; +		} +		$markup = substr($this->parser->getText(), $tag->getPos(), $tag->getLen()); + +		return (bool) preg_match('(^\\[\\w++.*?\\]$)s', $markup); +	}  } diff --git a/phpBB/phpbb/textformatter/s9e/quote_helper.php b/phpBB/phpbb/textformatter/s9e/quote_helper.php index 86c33c7591..3011ec88dc 100644 --- a/phpBB/phpbb/textformatter/s9e/quote_helper.php +++ b/phpBB/phpbb/textformatter/s9e/quote_helper.php @@ -21,6 +21,11 @@ class quote_helper  	protected $post_url;  	/** +	* @var string Base URL for a private message link, uses {MSG_ID} as placeholder +	*/ +	protected $msg_url; + +	/**  	* @var string Base URL for a profile link, uses {USER_ID} as placeholder  	*/  	protected $profile_url; @@ -40,6 +45,7 @@ class quote_helper  	public function __construct(\phpbb\user $user, $root_path, $php_ext)  	{  		$this->post_url = append_sid($root_path . 'viewtopic.' . $php_ext, 'p={POST_ID}#p{POST_ID}', false); +		$this->msg_url = append_sid($root_path . 'ucp.' . $php_ext, 'i=pm&mode=view&p={MSG_ID}', false);  		$this->profile_url = append_sid($root_path . 'memberlist.' . $php_ext, 'mode=viewprofile&u={USER_ID}', false);  		$this->user = $user;  	} @@ -52,26 +58,26 @@ class quote_helper  	*/  	public function inject_metadata($xml)  	{ -		$post_url = $this->post_url; -		$profile_url = $this->profile_url; -		$user = $this->user; -  		return \s9e\TextFormatter\Utils::replaceAttributes(  			$xml,  			'QUOTE', -			function ($attributes) use ($post_url, $profile_url, $user) +			function ($attributes)  			{  				if (isset($attributes['post_id']))  				{ -					$attributes['post_url'] = str_replace('{POST_ID}', $attributes['post_id'], $post_url); +					$attributes['post_url'] = str_replace('{POST_ID}', $attributes['post_id'], $this->post_url); +				} +				if (isset($attributes['msg_id'])) +				{ +					$attributes['msg_url'] = str_replace('{MSG_ID}', $attributes['msg_id'], $this->msg_url);  				}  				if (isset($attributes['time']))  				{ -					$attributes['date'] = $user->format_date($attributes['time']); +					$attributes['date'] = $this->user->format_date($attributes['time']);  				}  				if (isset($attributes['user_id']))  				{ -					$attributes['profile_url'] = str_replace('{USER_ID}', $attributes['user_id'], $profile_url); +					$attributes['profile_url'] = str_replace('{USER_ID}', $attributes['user_id'], $this->profile_url);  				}  				return $attributes; diff --git a/phpBB/phpbb/ucp/controller/reset_password.php b/phpBB/phpbb/ucp/controller/reset_password.php index 7bd1b20cb3..5c27c4f414 100644 --- a/phpBB/phpbb/ucp/controller/reset_password.php +++ b/phpBB/phpbb/ucp/controller/reset_password.php @@ -173,7 +173,7 @@ class reset_password  				'SELECT'	=> 'user_id, username, user_permissions, user_email, user_jabber, user_notify_type, user_type,'  								. ' user_lang, user_inactive_reason, reset_token, reset_token_expiration',  				'FROM'		=> [$this->users_table => 'u'], -				'WHERE'		=> "user_email_hash = '" . $this->db->sql_escape(phpbb_email_hash($email)) . "'" . +				'WHERE'		=> "user_email = '" . $this->db->sql_escape($email) . "'" .  					(!empty($username) ? " AND username_clean = '" . $this->db->sql_escape(utf8_clean_string($username)) . "'" : ''),  			]; diff --git a/phpBB/search.php b/phpBB/search.php index 8dde46f999..bd8025dae5 100644 --- a/phpBB/search.php +++ b/phpBB/search.php @@ -720,6 +720,8 @@ if ($keywords || $author || $author_id || $search_id || $submit)  	if ($sql_where)  	{ +		$zebra = []; +  		if ($show_results == 'posts')  		{  			// @todo Joining this query to the one below? @@ -728,7 +730,6 @@ if ($keywords || $author || $author_id || $search_id || $submit)  				WHERE user_id = ' . $user->data['user_id'];  			$result = $db->sql_query($sql); -			$zebra = array();  			while ($row = $db->sql_fetchrow($result))  			{  				$zebra[($row['friend']) ? 'friend' : 'foe'][] = $row['zebra_id']; diff --git a/phpBB/styles/prosilver/style.cfg b/phpBB/styles/prosilver/style.cfg index cdf67d5a0c..9b9a02c1fb 100644 --- a/phpBB/styles/prosilver/style.cfg +++ b/phpBB/styles/prosilver/style.cfg @@ -21,8 +21,8 @@  # General Information about this style  name = prosilver  copyright = Ā© phpBB Limited, 2007 -style_version = 3.3.0-b1 -phpbb_version = 3.3.0-b1 +style_version = 3.3.0-b2 +phpbb_version = 3.3.0-b2  # Defining a different template bitfield  # template_bitfield = //g= diff --git a/phpBB/styles/prosilver/template/bbcode.html b/phpBB/styles/prosilver/template/bbcode.html index 940c0ace29..b37ba238d2 100644 --- a/phpBB/styles/prosilver/template/bbcode.html +++ b/phpBB/styles/prosilver/template/bbcode.html @@ -37,6 +37,10 @@  					<xsl:text> </xsl:text>  					<a href="{@post_url}" data-post-id="{@post_id}" onclick="if(document.getElementById(hash.substr(1)))href=hash">↑</a>  				</xsl:if> +				<xsl:if test="@msg_url"> +					<xsl:text> </xsl:text> +					<a href="{@msg_url}" data-msg-id="{@msg_id}">↑</a> +				</xsl:if>  				<xsl:if test="@date">  					<div class="responsive-hide"><xsl:value-of select="@date"/></div>  				</xsl:if> diff --git a/phpBB/styles/prosilver/template/ucp_agreement.html b/phpBB/styles/prosilver/template/ucp_agreement.html index d4fef9f0a5..7959925d30 100644 --- a/phpBB/styles/prosilver/template/ucp_agreement.html +++ b/phpBB/styles/prosilver/template/ucp_agreement.html @@ -43,7 +43,8 @@  		<div class="inner">  		<fieldset class="submit-buttons">  			<!-- IF S_SHOW_COPPA --> -			<strong><a href="{U_COPPA_NO}" class="button1">{L_COPPA_NO}</a></strong>  <a href="{U_COPPA_YES}" class="button2">{L_COPPA_YES}</a> +			<input type="submit" name="coppa_no" id="coppa_no" value="{{ L_COPPA_NO }}" class="button1" /> +			<input type="submit" name="coppa_yes" id="coppa_yes" value="{{ L_COPPA_YES }}" class="button2" />  			<!-- ELSE -->  			<input type="submit" name="agreed" id="agreed" value="{L_AGREE}" class="button1" />   			<input type="submit" name="not_agreed" value="{L_NOT_AGREE}" class="button2" /> diff --git a/phpBB/styles/prosilver/template/viewtopic_body.html b/phpBB/styles/prosilver/template/viewtopic_body.html index 9bfa07e52b..6af33f2f87 100644 --- a/phpBB/styles/prosilver/template/viewtopic_body.html +++ b/phpBB/styles/prosilver/template/viewtopic_body.html @@ -294,6 +294,7 @@  			<!-- EVENT viewtopic_body_postrow_post_details_after -->  			<!-- IF postrow.S_POST_UNAPPROVED --> +			<!-- IF postrow.S_CAN_APPROVE -->  			<form method="post" class="mcp_approve" action="{postrow.U_APPROVE_ACTION}">  				<p class="post-notice unapproved">  					<span><i class="icon fa-question icon-red fa-fw" aria-hidden="true"></i></span> @@ -304,6 +305,12 @@  					{S_FORM_TOKEN}  				</p>  			</form> +			<!-- ELSE --> +				<p class="post-notice unapproved"> +					<span><i class="icon fa-exclamation icon-red fa-fw" aria-hidden="true"></i></span> +					<strong>{L_POST_UNAPPROVED_EXPLAIN}</strong> +				</p> +			<!-- ENDIF -->  			<!-- ELSEIF postrow.S_POST_DELETED -->  			<form method="post" class="mcp_approve" action="{postrow.U_APPROVE_ACTION}">  				<p class="post-notice deleted"> diff --git a/phpBB/viewforum.php b/phpBB/viewforum.php index 4691512cbd..eb6b37ada8 100644 --- a/phpBB/viewforum.php +++ b/phpBB/viewforum.php @@ -899,6 +899,11 @@ if (count($topic_list))  		// Replies  		$replies = $phpbb_content_visibility->get_count('topic_posts', $row, $topic_forum_id) - 1; +		// Correction for case of unapproved topic visible to poster +		if ($replies < 0) +		{ +			$replies = 0; +		}  		if ($row['topic_status'] == ITEM_MOVED)  		{ diff --git a/phpBB/viewtopic.php b/phpBB/viewtopic.php index 4e502538c8..df241a5e8b 100644 --- a/phpBB/viewtopic.php +++ b/phpBB/viewtopic.php @@ -2092,6 +2092,7 @@ for ($i = 0, $end = count($post_list); $i < $end; ++$i)  		'S_HAS_ATTACHMENTS'	=> (!empty($attachments[$row['post_id']])) ? true : false,  		'S_MULTIPLE_ATTACHMENTS'	=> !empty($attachments[$row['post_id']]) && count($attachments[$row['post_id']]) > 1,  		'S_POST_UNAPPROVED'	=> ($row['post_visibility'] == ITEM_UNAPPROVED || $row['post_visibility'] == ITEM_REAPPROVE) ? true : false, +		'S_CAN_APPROVE'		=> $auth->acl_get('m_approve', $forum_id),  		'S_POST_DELETED'	=> ($row['post_visibility'] == ITEM_DELETED) ? true : false,  		'L_POST_DELETED_MESSAGE'	=> $l_deleted_message,  		'S_POST_REPORTED'	=> ($row['post_reported'] && $auth->acl_get('m_report', $forum_id)) ? true : false, diff --git a/tests/RUNNING_TESTS.md b/tests/RUNNING_TESTS.md index 516541151c..54db823b4a 100644 --- a/tests/RUNNING_TESTS.md +++ b/tests/RUNNING_TESTS.md @@ -109,6 +109,21 @@ Or via environment variables as follows:      $ PHPBB_TEST_REDIS_HOST=localhost PHPBB_TEST_REDIS_PORT=6379 phpunit +Memcached +--------- + +In order to run tests for the memcached cache driver, at least one of memcached +host or port must be specified in the test configuration. This can be done via +test_config.php as follows: + +    <?php +    $phpbb_memcached_host = 'localhost'; +    $phpbb_memcached_port = '11211'; + +Or via environment variables as follows: + +    $ PHPBB_TEST_MEMCACHED_HOST=localhost PHPBB_TEST_MEMCACHED_PORT=11211 phpunit +  Running  ======= diff --git a/tests/auth/fixtures/user.xml b/tests/auth/fixtures/user.xml index 1e0eb6ee49..33f69a9067 100644 --- a/tests/auth/fixtures/user.xml +++ b/tests/auth/fixtures/user.xml @@ -1,5 +1,17 @@  <?xml version="1.0" encoding="UTF-8" ?>  <dataset> +	<table name="phpbb_groups"> +		<column>group_id</column> +		<column>group_name</column> +		<column>group_type</column> +		<column>group_desc</column> +		<row> +			<value>1</value> +			<value>REGISTERED</value> +			<value>3</value> +			<value>foobar</value> +		</row> +	</table>  	<table name="phpbb_users">  		<column>user_id</column>  		<column>username</column> diff --git a/tests/auth/fixtures/user_533.xml b/tests/auth/fixtures/user_533.xml deleted file mode 100644 index 9731e4db4a..0000000000 --- a/tests/auth/fixtures/user_533.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<dataset> -	<table name="phpbb_users"> -		<column>user_id</column> -		<column>username</column> -		<column>username_clean</column> -		<column>user_password</column> -		<column>user_passchg</column> -		<column>user_email</column> -		<column>user_type</column> -		<column>user_login_attempts</column> -		<column>user_permissions</column> -		<column>user_sig</column> -		<row> -			<value>1</value> -			<value>foobar</value> -			<value>foobar</value> -			<value>$2a$10$e01Syh9PbJjUkio66eFuUu4FhCE2nRgG7QPc1JACalsPXcIuG2bbi</value> -			<value>0</value> -			<value>example@example.com</value> -			<value>0</value> -			<value>0</value> -			<value></value> -			<value></value> -		</row> -		<row> -			<value>2</value> -			<value>foobar2</value> -			<value>foobar2</value> -			<value>$H$9E45lK6J8nLTSm9oJE5aNCSTFK9wqa/</value> -			<value>0</value> -			<value>example@example.com</value> -			<value>0</value> -			<value>0</value> -			<value></value> -			<value></value> -		</row> -	</table> -</dataset> diff --git a/tests/auth/provider_apache_test.php b/tests/auth/provider_apache_test.php index 58d6354228..ebc97c204a 100644 --- a/tests/auth/provider_apache_test.php +++ b/tests/auth/provider_apache_test.php @@ -28,41 +28,14 @@ class phpbb_auth_provider_apache_test extends phpbb_database_test_case  		$lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx);  		$lang = new \phpbb\language\language($lang_loader);  		$this->request = $this->createMock('\phpbb\request\request'); -		$this->user = new \phpbb\user($lang, '\phpbb\datetime'); -		$driver_helper = new \phpbb\passwords\driver\helper($config); -		$passwords_drivers = array( -			'passwords.driver.bcrypt_2y'	=> new \phpbb\passwords\driver\bcrypt_2y($config, $driver_helper), -			'passwords.driver.bcrypt'		=> new \phpbb\passwords\driver\bcrypt($config, $driver_helper), -			'passwords.driver.salted_md5'	=> new \phpbb\passwords\driver\salted_md5($config, $driver_helper), -			'passwords.driver.phpass'		=> new \phpbb\passwords\driver\phpass($config, $driver_helper), -		); - -		$passwords_helper = new \phpbb\passwords\helper; -		// Set up passwords manager -		$passwords_manager = new \phpbb\passwords\manager($config, $passwords_drivers, $passwords_helper, array_keys($passwords_drivers)); - -		if (version_compare(PHP_VERSION, '5.3.7', '<')) -		{ -			$this->password_hash = '$2a$10$e01Syh9PbJjUkio66eFuUu4FhCE2nRgG7QPc1JACalsPXcIuG2bbi'; -		} -		else -		{ -			$this->password_hash = '$2y$10$4RmpyVu2y8Yf/lP3.yQBquKvE54TCUuEDEBJYY6FDDFN3LcbCGz9i'; -		} +		$this->user = new \phpbb\user($lang, '\phpbb\datetime');; -		$this->provider = new \phpbb\auth\provider\apache($db, $config, $passwords_manager, $this->request, $this->user, $phpbb_root_path, $phpEx); +		$this->provider = new \phpbb\auth\provider\apache($config, $db, $lang, $this->request, $this->user, $phpbb_root_path, $phpEx);  	}  	public function getDataSet()  	{ -		if ((version_compare(PHP_VERSION, '5.3.7', '<'))) -		{ -			return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user_533.xml'); -		} -		else -		{ -			return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user.xml'); -		} +		return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user.xml');  	}  	/** @@ -109,7 +82,7 @@ class phpbb_auth_provider_apache_test extends phpbb_database_test_case  			'user_row'		=> array(  				'user_id' 				=> '1',  				'username' 				=> 'foobar', -				'user_password'			=> $this->password_hash, +				'user_password'			=> '$2y$10$4RmpyVu2y8Yf/lP3.yQBquKvE54TCUuEDEBJYY6FDDFN3LcbCGz9i',  				'user_passchg' 			=> '0',  				'user_email' 			=> 'example@example.com',  				'user_type' 			=> '0', @@ -145,10 +118,9 @@ class phpbb_auth_provider_apache_test extends phpbb_database_test_case  			'user_regdate' => '0',  			'username' => 'foobar',  			'username_clean' => 'foobar', -			'user_password' => $this->password_hash, +			'user_password' => '$2y$10$4RmpyVu2y8Yf/lP3.yQBquKvE54TCUuEDEBJYY6FDDFN3LcbCGz9i',  			'user_passchg' => '0',  			'user_email' => 'example@example.com', -			'user_email_hash' => '0',  			'user_birthday' => '',  			'user_lastvisit' => '0',  			'user_lastmark' => '0', diff --git a/tests/auth/provider_db_test.php b/tests/auth/provider_db_test.php index b7d94ed046..8305e7caa4 100644 --- a/tests/auth/provider_db_test.php +++ b/tests/auth/provider_db_test.php @@ -15,14 +15,7 @@ class phpbb_auth_provider_db_test extends phpbb_database_test_case  {  	public function getDataSet()  	{ -		if ((version_compare(PHP_VERSION, '5.3.7', '<'))) -		{ -			return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user_533.xml'); -		} -		else -		{ -			return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user.xml'); -		} +		return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user.xml');  	}  	public function test_login() @@ -52,16 +45,21 @@ class phpbb_auth_provider_db_test extends phpbb_database_test_case  		$passwords_manager = new \phpbb\passwords\manager($config, $passwords_drivers, $passwords_helper, array_keys($passwords_drivers));  		$phpbb_container = new phpbb_mock_container_builder(); +		$plugins = new \phpbb\di\service_collection($phpbb_container); +		$plugins->add('core.captcha.plugins.nogd'); +		$phpbb_container->set( +			'captcha.factory', +			new \phpbb\captcha\factory($phpbb_container, $plugins) +		); +		$phpbb_container->set( +			'core.captcha.plugins.nogd', +			new \phpbb\captcha\plugins\nogd() +		); +		/** @var \phpbb\captcha\factory $captcha_factory */ +		$captcha_factory = $phpbb_container->get('captcha.factory'); -		$provider = new \phpbb\auth\provider\db($db, $config, $passwords_manager, $request, $user, $phpbb_container, $phpbb_root_path, $phpEx); -		if (version_compare(PHP_VERSION, '5.3.7', '<')) -		{ -			$password_hash = '$2a$10$e01Syh9PbJjUkio66eFuUu4FhCE2nRgG7QPc1JACalsPXcIuG2bbi'; -		} -		else -		{ -			$password_hash = '$2y$10$4RmpyVu2y8Yf/lP3.yQBquKvE54TCUuEDEBJYY6FDDFN3LcbCGz9i'; -		} +		$provider = new \phpbb\auth\provider\db($captcha_factory, $config, $db, $passwords_manager, $request, $user, $phpbb_root_path, $phpEx); +		$password_hash = '$2y$10$4RmpyVu2y8Yf/lP3.yQBquKvE54TCUuEDEBJYY6FDDFN3LcbCGz9i';  		$expected = array(  			'status'		=> LOGIN_SUCCESS, @@ -88,7 +86,6 @@ class phpbb_auth_provider_db_test extends phpbb_database_test_case  		// Check if convert works  		$login_return = $provider->login('foobar2', 'example'); -		$password_start = (version_compare(PHP_VERSION, '5.3.7', '<')) ? '$2a$10$' : '$2y$10$'; -		$this->assertStringStartsWith($password_start, $login_return['user_row']['user_password']); +		$this->assertStringStartsWith('$2y$10$', $login_return['user_row']['user_password']);  	}  } diff --git a/tests/auth/provider_ldap_test.php b/tests/auth/provider_ldap_test.php new file mode 100644 index 0000000000..0bc9961f52 --- /dev/null +++ b/tests/auth/provider_ldap_test.php @@ -0,0 +1,94 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +/** + * @group slow + */ +class phpbb_auth_provider_ldap_test extends phpbb_database_test_case +{ +	/** @var \phpbb\auth\provider\ldap */ +	protected $provider; + +	protected $user; + +	protected function setup() : void +	{ +		parent::setUp(); + +		global $phpbb_root_path, $phpEx; + +		$db = $this->new_dbal(); +		$config = new \phpbb\config\config([ +			'ldap_server'	=> 'localhost', +			'ldap_port'		=> 3389, +			'ldap_base_dn'	=> 'dc=example,dc=com', +			'ldap_uid'		=> 'uid', +			'ldap_email'	=> 'mail', +		]); +		$lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx); +		$lang = new \phpbb\language\language($lang_loader); +		$this->user = new \phpbb\user($lang, '\phpbb\datetime'); +		$this->user->data['username'] = 'admin'; + +		$this->provider = new \phpbb\auth\provider\ldap($config, $db, $lang, $this->user); +	} + +	public function getDataSet() +	{ +		return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user.xml'); +	} + +	/** +	 * Test to see if a user is identified to Apache. Expects false if they are. +	 */ +	public function test_init() +	{ +		$this->assertFalse($this->provider->init()); +	} + +	public function test_login() +	{ +		$username = 'admin'; +		$password = 'adminadmin'; + +		$expected = array( +			'status'		=> LOGIN_SUCCESS_CREATE_PROFILE, // successful login and user created +			'error_msg'		=> false, +			'user_row'		=> array( +				'username' 				=> 'admin', +				'user_password'			=> '', +				'user_email' 			=> 'admin@example.com', +				'user_type' 			=> 0, +				'group_id'				=> 1, +				'user_new'				=> 0, +				'user_ip'				=> '', +			), +		); + +		$this->assertEquals($expected, $this->provider->login($username, $password)); +	} + +	public function test_autologin() +	{ +		$this->assertNull($this->provider->autologin()); +	} + +	public function test_validate_session() +	{ +		$user = array( +			'username'	=> 'admin', +		); + +		$this->assertNull($this->provider->validate_session($user)); +	} +} diff --git a/tests/cache/memcached_test.php b/tests/cache/memcached_test.php new file mode 100644 index 0000000000..650b72ea18 --- /dev/null +++ b/tests/cache/memcached_test.php @@ -0,0 +1,65 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +require_once dirname(__FILE__) . '/common_test_case.php'; + +class phpbb_cache_memcached_driver_test extends \phpbb_cache_common_test_case +{ +	protected static $config; + +	public function getDataSet() +	{ +		return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/config.xml'); +	} + +	static public function setUpBeforeClass() +	{ +		if (!extension_loaded('memcached')) +		{ +			self::markTestSkipped('memcached extension is not loaded'); +		} + +		$config = phpbb_test_case_helpers::get_test_config(); +		if (isset($config['memcached_host']) || isset($config['memcached_port'])) +		{ +			$host = isset($config['memcached_host']) ? $config['memcached_host'] : 'localhost'; +			$port = isset($config['memcached_port']) ? $config['memcached_port'] : 11211; +			self::$config = array('host' => $host, 'port' => $port); +		} +		else +		{ +			self::markTestSkipped('Test memcached host/port is not specified'); +		} + +		$memcached = new \Memcached(); +		$memcached->addServer(self::$config['host'], self::$config['port']); +		if (empty($memcached->getStats())) +		{ +			self::markTestSkipped('Test memcached server is not available'); +		} + +		parent::setUpBeforeClass(); +	} + +	protected function setUp(): void +	{ +		global $phpbb_root_path, $phpbb_container; + +		parent::setUp(); + +		$phpbb_container = new phpbb_mock_container_builder(); +		$phpbb_container->setParameter('core.cache_dir', $phpbb_root_path . 'cache/' . PHPBB_ENVIRONMENT . '/'); +		$this->driver = new \phpbb\cache\driver\memcached(self::$config['host'] . '/' . self::$config['port']); +		$this->driver->purge(); +	} +} diff --git a/tests/content_visibility/fixtures/get_visibility_sql.xml b/tests/content_visibility/fixtures/get_visibility_sql.xml index 146244263e..2128064986 100644 --- a/tests/content_visibility/fixtures/get_visibility_sql.xml +++ b/tests/content_visibility/fixtures/get_visibility_sql.xml @@ -3,17 +3,20 @@  	<table name="phpbb_topics">  		<column>topic_id</column>  		<column>forum_id</column> +		<column>topic_poster</column>  		<column>topic_visibility</column>  		<column>topic_title</column>  		<row>  			<value>1</value>  			<value>1</value>  			<value>0</value> +			<value>0</value>  			<value>Unapproved</value>  		</row>  		<row>  			<value>2</value>  			<value>1</value> +			<value>0</value>  			<value>1</value>  			<value>Approved</value>  		</row> @@ -21,13 +24,22 @@  			<value>3</value>  			<value>1</value>  			<value>2</value> +			<value>0</value>  			<value>Softdeleted</value>  		</row> +		<row> +			<value>4</value> +			<value>1</value> +			<value>1</value> +			<value>0</value> +			<value>Unapproved</value> +		</row>  	</table>  	<table name="phpbb_posts">  		<column>post_id</column>  		<column>topic_id</column>  		<column>forum_id</column> +		<column>poster_id</column>  		<column>post_visibility</column>  		<column>post_text</column>  		<row> @@ -35,12 +47,14 @@  			<value>1</value>  			<value>1</value>  			<value>0</value> +			<value>0</value>  			<value>Unapproved</value>  		</row>  		<row>  			<value>2</value>  			<value>2</value>  			<value>1</value> +			<value>0</value>  			<value>1</value>  			<value>Approved</value>  		</row> @@ -48,8 +62,17 @@  			<value>3</value>  			<value>3</value>  			<value>1</value> +			<value>0</value>  			<value>2</value>  			<value>Softdeleted</value>  		</row> +		<row> +			<value>4</value> +			<value>4</value> +			<value>1</value> +			<value>1</value> +			<value>0</value> +			<value>Unapproved</value> +		</row>  	</table>  </dataset> diff --git a/tests/content_visibility/get_visibility_sql_test.php b/tests/content_visibility/get_visibility_sql_test.php index 18802fadbc..6026778487 100644 --- a/tests/content_visibility/get_visibility_sql_test.php +++ b/tests/content_visibility/get_visibility_sql_test.php @@ -21,8 +21,11 @@ class phpbb_content_visibility_get_visibility_sql_test extends phpbb_database_te  	public function get_visibility_sql_data()  	{  		return array( +			// data set 0: display_unapproved_posts=false, moderator, can see all posts  			array(  				'phpbb_posts', +				0, +				false,  				'post', 1, '',  				array(  					array('m_approve', 1, true), @@ -31,10 +34,14 @@ class phpbb_content_visibility_get_visibility_sql_test extends phpbb_database_te  					array('post_id' => 1),  					array('post_id' => 2),  					array('post_id' => 3), +					array('post_id' => 4),  				),  			), +			// data set 1: display_unapproved_posts=false, normal user, cannot see any unapproved posts  			array(  				'phpbb_posts', +				0, +				false,  				'post', 1, '',  				array(  				), @@ -42,8 +49,11 @@ class phpbb_content_visibility_get_visibility_sql_test extends phpbb_database_te  					array('post_id' => 2),  				),  			), +			// data set 2: display_unapproved_posts=false, moderator, can see all topics  			array(  				'phpbb_topics', +				0, +				false,  				'topic', 1, '',  				array(  					array('m_approve', 1, true), @@ -52,23 +62,74 @@ class phpbb_content_visibility_get_visibility_sql_test extends phpbb_database_te  					array('topic_id' => 1),  					array('topic_id' => 2),  					array('topic_id' => 3), +					array('topic_id' => 4),  				),  			), +			// data set 3: display_unapproved_posts=false, normal user, cannot see unapproved posts topic  			array(  				'phpbb_topics', +				0, +				false,  				'topic', 1, '',  				array(),  				array(  					array('topic_id' => 2),  				),  			), +			// data set 4: display_unapproved_posts=true, guest user, cannot see unapproved posts +			array( +				'phpbb_posts', +				1, +				true, +				'post', 1, '', +				array( +				), +				array( +					array('post_id' => 2), +				), +			), +			// data set 5: display_unapproved_posts=true, guest user, cannot see unapproved posts topic +			array( +				'phpbb_topics', +				1, +				true, +				'topic', 1, '', +				array(), +				array( +					array('topic_id' => 2), +				), +			), +			// data set 6: display_unapproved_posts=true, normal user, can see own unapproved posts +			array( +				'phpbb_posts', +				0, +				true, +				'post', 1, '', +				array(), +				array( +					array('post_id' => 1), +					array('post_id' => 2), +				), +			), +			// data set 7: display_unapproved_posts=true, normal user, can see own unapproved posts topic +			array( +				'phpbb_topics', +				0, +				true, +				'topic', 1, '', +				array(), +				array( +					array('topic_id' => 1), +					array('topic_id' => 2), +				), +			),  		);  	}  	/**  	* @dataProvider get_visibility_sql_data  	*/ -	public function test_get_visibility_sql($table, $mode, $forum_id, $table_alias, $permissions, $expected) +	public function test_get_visibility_sql($table, $user_id, $display_unapproved, $mode, $forum_id, $table_alias, $permissions, $expected)  	{  		global $cache, $db, $auth, $phpbb_root_path, $phpEx; @@ -84,15 +145,20 @@ class phpbb_content_visibility_get_visibility_sql_test extends phpbb_database_te  		$lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx);  		$lang = new \phpbb\language\language($lang_loader);  		$user = new \phpbb\user($lang, '\phpbb\datetime'); -		$config = new phpbb\config\config(array()); +		$user->data['user_id'] = $user_id; +		$config = $this->config = new \phpbb\config\config(array( +			'display_unapproved_posts'			=> $display_unapproved, +		));  		$phpbb_dispatcher = new phpbb_mock_event_dispatcher();  		$content_visibility = new \phpbb\content_visibility($auth, $config, $phpbb_dispatcher, $db, $user, $phpbb_root_path, $phpEx, FORUMS_TABLE, POSTS_TABLE, TOPICS_TABLE, USERS_TABLE); -		$result = $db->sql_query('SELECT ' . $mode . '_id +		$sql = 'SELECT ' . $mode . '_id  			FROM ' . $table . '  			WHERE ' . $content_visibility->get_visibility_sql($mode, $forum_id, $table_alias) . ' -			ORDER BY ' . $mode . '_id ASC'); +			ORDER BY ' . $mode . '_id ASC'; +		$result = $db->sql_query($sql);  		$this->assertEquals($expected, $db->sql_fetchrowset($result)); +		$db->sql_freeresult($result);  	}  } diff --git a/tests/functional/private_messages_test.php b/tests/functional/private_messages_test.php index 7fda26fb49..ce709524a9 100644 --- a/tests/functional/private_messages_test.php +++ b/tests/functional/private_messages_test.php @@ -85,7 +85,7 @@ class phpbb_functional_private_messages_test extends phpbb_functional_test_case  	public function test_quote_pm()  	{  		$text     = 'This is a test private message sent by the testing framework.'; -		$expected = "(\\[quote=admin time=\\d+ user_id=2\\]\n" . $text . "\n\\[/quote\\])"; +		$expected = "(\\[quote=admin msg_id=\\d+ time=\\d+ user_id=2\\]\n" . $text . "\n\\[/quote\\])";  		$this->login();  		$message_id = $this->create_private_message('Test', $text, array(2)); diff --git a/tests/functional/registration_test.php b/tests/functional/registration_test.php index 690f4ae9f2..48982edc8c 100644 --- a/tests/functional/registration_test.php +++ b/tests/functional/registration_test.php @@ -36,6 +36,10 @@ class phpbb_functional_registration_test extends phpbb_functional_test_case  	{  		$this->add_lang('ucp'); +		// Check that we can't skip +		self::request('GET', 'ucp.php?mode=register&agreed=1'); +		$this->assertContainsLang('AGREE', $this->get_content()); +  		$crawler = self::request('GET', 'ucp.php?mode=register');  		$this->assertContainsLang('REGISTRATION', $crawler->filter('div.content h2')->text()); @@ -64,4 +68,54 @@ class phpbb_functional_registration_test extends phpbb_functional_test_case  		$this->assert_checkbox_is_checked($crawler, 'notification.type.post_notification.method.email');  		$this->assert_checkbox_is_checked($crawler, 'notification.type.topic_notification.method.email');  	} + +	/** +	 * @depends test_disable_captcha_on_registration +	 */ +	public function test_register_coppa_account() +	{ +		$this->login(); +		$this->admin_login(); + +		$crawler = self::request('GET', "adm/index.php?i=acp_board&mode=registration&sid={$this->sid}"); +		$form = $crawler->selectButton('Submit')->form(); +		$form['config[coppa_enable]']->setValue('1'); +		$crawler = self::submit($form); + +		$this->assertContainsLang('CONFIG_UPDATED', $crawler->filter('#main .successbox')->text()); +		$this->logout(); + +		$this->add_lang('ucp'); + +		// Check that we can't skip +		$crawler = self::request('GET', 'ucp.php?mode=register&coppa=1'); +		$this->assertContainsLang('COPPA_BIRTHDAY', $crawler->html()); + +		$form = $crawler->selectButton('coppa_yes')->form(); +		$crawler = self::submit($form); + +		$this->assertContainsLang('REGISTRATION', $crawler->filter('div.content h2')->text()); + +		$form = $crawler->selectButton('I agree to these terms')->form(); +		$crawler = self::submit($form); + +		$form = $crawler->selectButton('Submit')->form(array( +			'username'			=> 'user-coppa-test', +			'email'				=> 'user-coppa-test@phpbb.com', +			'new_password'		=> 'user-coppa-testuser-coppa-test', +			'password_confirm'	=> 'user-coppa-testuser-coppa-test', +		)); +		$form['tz']->select('Europe/Berlin'); +		$crawler = self::submit($form); + +		$this->assertContainsLang('ACCOUNT_COPPA', $crawler->filter('#message')->text()); + +		$this->login(); +		$this->admin_login(); + +		$crawler = self::request('GET', "adm/index.php?i=acp_board&mode=registration&sid={$this->sid}"); +		$form = $crawler->selectButton('Submit')->form(); +		$form['config[coppa_enable]']->setValue('0'); +		$crawler = self::submit($form); +	}  } diff --git a/tests/functional/visibility_unapproved_posts_test.php b/tests/functional/visibility_unapproved_posts_test.php new file mode 100644 index 0000000000..9f6491d1d8 --- /dev/null +++ b/tests/functional/visibility_unapproved_posts_test.php @@ -0,0 +1,356 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +/** +* @group functional +*/ +class phpbb_functional_visibility_unapproved_test extends phpbb_functional_test_case +{ +	protected $data = []; + +	public function test_setup_forums() +	{ +		$this->login(); +		$this->admin_login(); + +		$crawler = self::request('GET', "adm/index.php?i=acp_forums&mode=manage&sid={$this->sid}"); +		$form = $crawler->selectButton('addforum')->form([ +			'forum_name'	=> 'Unapproved Posts Test #1', +		]); +		$crawler = self::submit($form); +		$form = $crawler->selectButton('update')->form([ +			'forum_perm_from'	=> 2, +		]); +		$crawler = self::submit($form); + +		// Set flood interval to 0 +		$this->set_flood_interval(0); +	} + +	public function test_create_posts() +	{ +		$this->login(); +		$this->load_ids([ +			'forums' => [ +				'Unapproved Posts Test #1', +			], +		]); + +		$this->assert_forum_details($this->data['forums']['Unapproved Posts Test #1'], [ +			'forum_posts_approved'		=> 0, +			'forum_posts_unapproved'	=> 0, +			'forum_posts_softdeleted'	=> 0, +			'forum_topics_approved'		=> 0, +			'forum_topics_unapproved'	=> 0, +			'forum_topics_softdeleted'	=> 0, +			'forum_last_post_id'		=> 0, +		], 'initial comparison'); + +		// Test creating topic #1 +		$post = $this->create_topic($this->data['forums']['Unapproved Posts Test #1'], 'Unapproved Posts Test Topic #1', 'This is a test topic posted by the testing framework.'); +		$crawler = self::request('GET', "viewtopic.php?t={$post['topic_id']}&sid={$this->sid}"); + +		$this->assertContains('Unapproved Posts Test Topic #1', $crawler->filter('h2')->text()); +		$this->data['topics']['Unapproved Posts Test Topic #1'] = (int) $post['topic_id']; +		$this->data['posts']['Unapproved Posts Test Topic #1'] = (int) $this->get_parameter_from_link($crawler->filter('.post')->selectLink($this->lang('POST', '', ''))->link()->getUri(), 'p'); + +		$this->assert_forum_details($this->data['forums']['Unapproved Posts Test #1'], [ +			'forum_posts_approved'		=> 1, +			'forum_posts_unapproved'	=> 0, +			'forum_posts_softdeleted'	=> 0, +			'forum_topics_approved'		=> 1, +			'forum_topics_unapproved'	=> 0, +			'forum_topics_softdeleted'	=> 0, +			'forum_last_post_id'		=> $this->data['posts']['Unapproved Posts Test Topic #1'], +		], 'after creating topic #1'); + +		$this->logout(); +		$this->create_user('unapproved_posts_test_user#1'); +		$this->add_user_group('NEWLY_REGISTERED', ['unapproved_posts_test_user#1']); +		$this->login('unapproved_posts_test_user#1'); + +		// Test creating a reply +		$post2 = $this->create_post($this->data['forums']['Unapproved Posts Test #1'], $post['topic_id'], 'Re: Unapproved Posts Test Topic #1-#2', 'This is a test post posted by the testing framework.', [], 'POST_STORED_MOD'); + +		$crawler = self::request('GET', "viewtopic.php?t={$this->data['topics']['Unapproved Posts Test Topic #1']}&sid={$this->sid}"); +		$this->assertNotContains('Re: Unapproved Posts Test Topic #1-#2', $crawler->filter('#page-body')->text()); + +		$this->assert_forum_details($this->data['forums']['Unapproved Posts Test #1'], [ +			'forum_posts_approved'		=> 1, +			'forum_posts_unapproved'	=> 1, +			'forum_posts_softdeleted'	=> 0, +			'forum_topics_approved'		=> 1, +			'forum_topics_unapproved'	=> 0, +			'forum_topics_softdeleted'	=> 0, +			'forum_last_post_id'		=> $this->data['posts']['Unapproved Posts Test Topic #1'], +		], 'after replying'); + +		// Test creating topic #2 +		$post = $this->create_topic($this->data['forums']['Unapproved Posts Test #1'], 'Unapproved Posts Test Topic #2', 'This is a test topic posted by the testing framework.', [], 'POST_STORED_MOD'); +		$crawler = self::request('GET', "viewforum.php?f={$this->data['forums']['Unapproved Posts Test #1']}&sid={$this->sid}"); + +		$this->assertNotContains('Unapproved Posts Test Topic #2', $crawler->filter('html')->text()); + +		$this->assert_forum_details($this->data['forums']['Unapproved Posts Test #1'], [ +			'forum_posts_approved'		=> 1, +			'forum_posts_unapproved'	=> 2, +			'forum_posts_softdeleted'	=> 0, +			'forum_topics_approved'		=> 1, +			'forum_topics_unapproved'	=> 1, +			'forum_topics_softdeleted'	=> 0, +			'forum_last_post_id'		=> $this->data['posts']['Unapproved Posts Test Topic #1'], +		], 'after creating topic #2'); + +		$this->logout(); +	} + +	public function test_view_unapproved_post_disabled() +	{ +		// user who created post +		$this->login('unapproved_posts_test_user#1'); +		$this->load_ids([ +			'forums' => [ +				'Unapproved Posts Test #1', +			], +			'topics' => [ +				'Unapproved Posts Test Topic #1', +				'Unapproved Posts Test Topic #2', +			], +			'posts' => [ +				'Unapproved Posts Test Topic #1', +				'Re: Unapproved Posts Test Topic #1-#2', +				'Unapproved Posts Test Topic #2', +			], +		]); + +		$this->assert_forum_details($this->data['forums']['Unapproved Posts Test #1'], [ +			'forum_posts_approved'		=> 1, +			'forum_posts_unapproved'	=> 2, +			'forum_posts_softdeleted'	=> 0, +			'forum_topics_approved'		=> 1, +			'forum_topics_unapproved'	=> 1, +			'forum_topics_softdeleted'	=> 0, +			'forum_last_post_id'		=> $this->data['posts']['Unapproved Posts Test Topic #1'], +		], 'before approving post'); + +		$this->add_lang('posting'); +		$this->add_lang('viewtopic'); +		$this->add_lang('mcp'); + +		// should be able to see topic 1 but not unapproved post +		$crawler = self::request('GET', "viewtopic.php?t={$this->data['topics']['Unapproved Posts Test Topic #1']}&sid={$this->sid}"); +		$this->assertContains('Unapproved Posts Test Topic #1', $crawler->filter('h2')->text()); +		$this->assertNotContains('Re: Unapproved Posts Test Topic #1-#2', $crawler->filter('#page-body')->text()); +		$this->assertNotContains('This post is not visible to other users until it has been approved', $crawler->filter('#page-body')->text()); + +		// should not be able to see topic 2 +		$crawler = self::request('GET', "viewforum.php?f={$this->data['forums']['Unapproved Posts Test #1']}&sid={$this->sid}"); +		$this->assertNotContains('Unapproved Posts Test Topic #2', $crawler->filter('html')->text()); +		$this->logout(); + +		// another user +		$this->create_user('unapproved_posts_test_user#2'); +		$this->login('unapproved_posts_test_user#2'); + +		$this->add_lang('posting', 'viewtopic', 'mcp'); + +		// should be able to see topic 1 but not unapproved post +		$crawler = self::request('GET', "viewtopic.php?t={$this->data['topics']['Unapproved Posts Test Topic #1']}&sid={$this->sid}"); +		$this->assertContains('Unapproved Posts Test Topic #1', $crawler->filter('h2')->text()); +		$this->assertNotContains('Re: Unapproved Posts Test Topic #1-#2', $crawler->filter('#page-body')->text()); +		$this->assertNotContains('This post is not visible to other users until it has been approved', $crawler->filter('#page-body')->text()); + +		// should not be able to see topic 2 +		$crawler = self::request('GET', "viewforum.php?f={$this->data['forums']['Unapproved Posts Test #1']}&sid={$this->sid}"); +		$this->assertNotContains('Unapproved Posts Test Topic #2', $crawler->filter('html')->text()); +	} + +	public function test_view_unapproved_post_enabled() +	{ +		$this->config_display_unapproved_posts_state(true); + +		// user who created post +		$this->login('unapproved_posts_test_user#1'); +		$this->load_ids([ +			'forums' => [ +				'Unapproved Posts Test #1', +			], +			'topics' => [ +				'Unapproved Posts Test Topic #1', +				'Unapproved Posts Test Topic #2', +			], +			'posts' => [ +				'Unapproved Posts Test Topic #1', +				'Re: Unapproved Posts Test Topic #1-#2', +				'Unapproved Posts Test Topic #2', +			], +		]); + +		$this->assert_forum_details($this->data['forums']['Unapproved Posts Test #1'], [ +			'forum_posts_approved'		=> 1, +			'forum_posts_unapproved'	=> 2, +			'forum_posts_softdeleted'	=> 0, +			'forum_topics_approved'		=> 1, +			'forum_topics_unapproved'	=> 1, +			'forum_topics_softdeleted'	=> 0, +			'forum_last_post_id'		=> $this->data['posts']['Unapproved Posts Test Topic #1'], +		], 'before approving post'); + +		$this->add_lang('posting'); +		$this->add_lang('viewtopic'); +		$this->add_lang('mcp'); + +		// should be able to see topic 1 and unapproved post +		$crawler = self::request('GET', "viewtopic.php?t={$this->data['topics']['Unapproved Posts Test Topic #1']}&sid={$this->sid}"); +		$this->assertContains('Unapproved Posts Test Topic #1', $crawler->filter('h2')->text()); +		$this->assertContains('Re: Unapproved Posts Test Topic #1-#2', $crawler->filter('#page-body')->text()); +		$this->assertContains('This post is not visible to other users until it has been approved', $crawler->filter('#page-body')->text()); + +		// should be able to see topic 2 +		$crawler = self::request('GET', "viewforum.php?f={$this->data['forums']['Unapproved Posts Test #1']}&sid={$this->sid}"); +		$this->assertContains('Unapproved Posts Test Topic #2', $crawler->filter('html')->text()); + +		// should be able to see post in topic 2 +		$crawler = self::request('GET', "viewtopic.php?t={$this->data['topics']['Unapproved Posts Test Topic #2']}&sid={$this->sid}"); +		$this->assertContains('Unapproved Posts Test Topic #2', $crawler->filter('#page-body')->text()); +		$this->assertContains('This post is not visible to other users until it has been approved', $crawler->filter('#page-body')->text()); +		$this->logout(); + +		// another user +		$this->login('unapproved_posts_test_user#2'); + +		$this->add_lang('posting'); +		$this->add_lang('viewtopic'); +		$this->add_lang('mcp'); + +		// should be able to see topic 1 but not unapproved post +		$crawler = self::request('GET', "viewtopic.php?t={$this->data['topics']['Unapproved Posts Test Topic #1']}&sid={$this->sid}"); +		$this->assertContains('Unapproved Posts Test Topic #1', $crawler->filter('h2')->text()); +		$this->assertNotContains('Re: Unapproved Posts Test Topic #1-#2', $crawler->filter('#page-body')->text()); +		$this->assertNotContains('This post is not visible to other users until it has been approved', $crawler->filter('#page-body')->text()); + +		// should not be able to see topic 2 +		$crawler = self::request('GET', "viewforum.php?f={$this->data['forums']['Unapproved Posts Test #1']}&sid={$this->sid}"); +		$this->assertNotContains('Unapproved Posts Test Topic #2', $crawler->filter('html')->text()); +		$this->logout(); +	} + +	public function test_reset_flood_interval() +	{ +		$this->login(); +		$this->admin_login(); + +		// Set flood interval back to 15 +		$this->set_flood_interval(15); +	} + +	protected function assert_forum_details($forum_id, $details, $additional_error_message = '') +	{ +		$this->db = $this->get_db(); + +		$sql = 'SELECT ' . implode(', ', array_keys($details)) . ' +			FROM phpbb_forums +			WHERE forum_id = ' . (int) $forum_id; +		$result = $this->db->sql_query($sql); +		$data = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		$this->assertEquals($details, $data, "Forum {$forum_id} does not match expected {$additional_error_message}"); +	} + +	protected function set_flood_interval($flood_interval) +	{ +		$crawler = self::request('GET', "adm/index.php?sid={$this->sid}&i=acp_board&mode=post"); + +		$form = $crawler->selectButton('Submit')->form(); +		$values = $form->getValues(); + +		$values['config[flood_interval]'] = $flood_interval; +		$form->setValues($values); +		$crawler = self::submit($form); +		$this->assertGreaterThan(0, $crawler->filter('.successbox')->count()); +	} + +	protected function load_ids($data) +	{ +		$this->db = $this->get_db(); + +		if (!empty($data['forums'])) +		{ +			$sql = 'SELECT forum_id, forum_name +				FROM phpbb_forums +				WHERE ' . $this->db->sql_in_set('forum_name', $data['forums']); +			$result = $this->db->sql_query($sql); +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				if (in_array($row['forum_name'], $data['forums'])) +				{ +					$this->data['forums'][$row['forum_name']] = (int) $row['forum_id']; +				} +			} +			$this->db->sql_freeresult($result); +		} + +		if (!empty($data['topics'])) +		{ +			$sql = 'SELECT * +				FROM phpbb_topics +				WHERE ' . $this->db->sql_in_set('topic_title', $data['topics']); +			$result = $this->db->sql_query($sql); +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				if (in_array($row['topic_title'], $data['topics'])) +				{ +					$this->data['topics'][$row['topic_title']] = (int) $row['topic_id']; +				} +			} +			$this->db->sql_freeresult($result); +		} + +		if (!empty($data['posts'])) +		{ +			$sql = 'SELECT * +				FROM phpbb_posts +				WHERE ' . $this->db->sql_in_set('post_subject', $data['posts']); +			$result = $this->db->sql_query($sql); +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				if (in_array($row['post_subject'], $data['posts'])) +				{ +					$this->data['posts'][$row['post_subject']] = (int) $row['post_id']; +				} +			} +			$this->db->sql_freeresult($result); +		} +	} + +	protected function config_display_unapproved_posts_state($state) +	{ +		$this->login(); +		$this->admin_login(); + +		$crawler = self::request('GET', "adm/index.php?sid={$this->sid}&i=acp_board&mode=features"); + +		$form = $crawler->selectButton('Submit')->form(); +		$values = $form->getValues(); + +		// Enable display of unapproved posts to posters +		$values['config[display_unapproved_posts]'] = $state; + +		$form->setValues($values); + +		$crawler = self::submit($form); +		self::assertContainsLang('CONFIG_UPDATED', $crawler->filter('.successbox')->text()); +		$this->logout(); +	} +} diff --git a/tests/functions/fixtures/validate_email.xml b/tests/functions/fixtures/validate_email.xml index fa139f6f18..985050cedc 100644 --- a/tests/functions/fixtures/validate_email.xml +++ b/tests/functions/fixtures/validate_email.xml @@ -30,14 +30,14 @@  		<column>username_clean</column>  		<column>user_permissions</column>  		<column>user_sig</column> -		<column>user_email_hash</column> +		<column>user_email</column>  		<row>  			<value>1</value>  			<value>admin</value>  			<value>admin</value>  			<value></value>  			<value></value> -			<value>143317126117</value> +			<value>admin@example.com</value>  		</row>  	</table>  </dataset> diff --git a/tests/functions/user_delete_test.php b/tests/functions/user_delete_test.php index 89aecdefb9..f4ea5696b9 100644 --- a/tests/functions/user_delete_test.php +++ b/tests/functions/user_delete_test.php @@ -60,21 +60,43 @@ class phpbb_functions_user_delete_test extends phpbb_database_test_case  		// Set up passwords manager  		$passwords_manager = new \phpbb\passwords\manager($config, $passwords_drivers, $passwords_helper, array_keys($passwords_drivers)); -		$oauth_provider = new \phpbb\auth\provider\oauth\oauth( -			$db, +		$plugins = new \phpbb\di\service_collection($phpbb_container); +		$plugins->add('core.captcha.plugins.nogd'); +		$phpbb_container->set( +			'captcha.factory', +			new \phpbb\captcha\factory($phpbb_container, $plugins) +		); +		$phpbb_container->set( +			'core.captcha.plugins.nogd', +			new \phpbb\captcha\plugins\nogd() +		); +		// Set up passwords manager +		$db_auth_provider = new \phpbb\auth\provider\db( +			new \phpbb\captcha\factory($phpbb_container, $plugins),  			$config, +			$db,  			$passwords_manager,  			$request,  			$user, +			$phpbb_root_path, +			$phpEx +		); + +		$oauth_provider = new \phpbb\auth\provider\oauth\oauth( +			$config, +			$db, +			$db_auth_provider, +			$phpbb_dispatcher, +			$lang, +			$request, +			$oauth_provider_collection, +			$user,  			'phpbb_oauth_tokens',  			'phpbb_oauth_states',  			'phpbb_oauth_accounts', -			$oauth_provider_collection,  			'phpbb_users', -			$phpbb_container, -			$phpbb_dispatcher, -			$this->phpbb_root_path, -			$this->php_ext +			$phpbb_root_path, +			$phpEx  		);  		$provider_collection->offsetSet('auth.provider.oauth', $oauth_provider); diff --git a/tests/passwords/manager_test.php b/tests/passwords/manager_test.php index dc5c539316..90dbb47f46 100644 --- a/tests/passwords/manager_test.php +++ b/tests/passwords/manager_test.php @@ -51,26 +51,13 @@ class phpbb_passwords_manager_test extends \phpbb_test_case  	public function hash_password_data()  	{ -		if (version_compare(PHP_VERSION, '5.3.7', '<')) -		{ -			return array( -				array('', '2a', 60), -				array('passwords.driver.bcrypt_2y', '2a', 60), -				array('passwords.driver.bcrypt', '2a', 60), -				array('passwords.driver.salted_md5', 'H', 34), -				array('passwords.driver.foobar', '', false), -			); -		} -		else -		{ -			return array( -				array('', '2y', 60), -				array('passwords.driver.bcrypt_2y', '2y', 60), -				array('passwords.driver.bcrypt', '2a', 60), -				array('passwords.driver.salted_md5', 'H', 34), -				array('passwords.driver.foobar', '', false), -			); -		} +		return array( +			array('', '2y', 60), +			array('passwords.driver.bcrypt_2y', '2y', 60), +			array('passwords.driver.bcrypt', '2a', 60), +			array('passwords.driver.salted_md5', 'H', 34), +			array('passwords.driver.foobar', '', false), +		);  	}  	/** @@ -100,23 +87,12 @@ class phpbb_passwords_manager_test extends \phpbb_test_case  	public function check_password_data()  	{ -		if (version_compare(PHP_VERSION, '5.3.7', '<')) -		{ -			return array( -				array('passwords.driver.bcrypt'), -				array('passwords.driver.salted_md5'), -				array('passwords.driver.phpass'), -			); -		} -		else -		{ -			return array( -				array('passwords.driver.bcrypt_2y'), -				array('passwords.driver.bcrypt'), -				array('passwords.driver.salted_md5'), -				array('passwords.driver.phpass'), -			); -		} +		return array( +			array('passwords.driver.bcrypt_2y'), +			array('passwords.driver.bcrypt'), +			array('passwords.driver.salted_md5'), +			array('passwords.driver.phpass'), +		);  	}  	/** @@ -136,7 +112,7 @@ class phpbb_passwords_manager_test extends \phpbb_test_case  		}  		// Check if convert_flag is correctly set -		$default_type = (version_compare(PHP_VERSION, '5.3.7', '<')) ? 'passwords.driver.bcrypt' : 'passwords.driver.bcrypt_2y'; +		$default_type = 'passwords.driver.bcrypt_2y';  		$this->assertEquals(($hash_type !== $default_type), $this->manager->convert_flag);  	} @@ -200,79 +176,43 @@ class phpbb_passwords_manager_test extends \phpbb_test_case  	public function test_hash_password_8bit_bcrypt()  	{  		$this->assertEquals(false, $this->manager->hash('foobar𝄞', 'passwords.driver.bcrypt')); -		if (version_compare(PHP_VERSION, '5.3.7', '<')) -		{ -			$this->assertEquals(false, $this->manager->hash('foobar𝄞', 'passwords.driver.bcrypt_2y')); -		} -		else -		{ -			$this->assertNotEquals(false, $this->manager->hash('foobar𝄞', 'passwords.driver.bcrypt_2y')); -		} +		$this->assertNotEquals(false, $this->manager->hash('foobar𝄞', 'passwords.driver.bcrypt_2y'));  	}  	public function combined_hash_data()  	{ -		if (version_compare(PHP_VERSION, '5.3.7', '<')) -		{ -			return array( -				array( -					'passwords.driver.salted_md5', -					array('passwords.driver.bcrypt'), -				), -				array( -					'passwords.driver.phpass', -					array('passwords.driver.salted_md5'), -				), -				array( -					'passwords.driver.salted_md5', -					array('passwords.driver.phpass', 'passwords.driver.bcrypt'), -				), -				array( -					'passwords.driver.salted_md5', -					array('passwords.driver.salted_md5'), -					false, -				), -				array( -					'$H$', -					array('$2a$'), -				), -			); -		} -		else -		{ -			return array( -				array( -					'passwords.driver.salted_md5', -					array('passwords.driver.bcrypt_2y'), -				), -				array( -					'passwords.driver.salted_md5', -					array('passwords.driver.bcrypt'), -				), -				array( -					'passwords.driver.phpass', -					array('passwords.driver.salted_md5'), -				), -				array( -					'passwords.driver.salted_md5', -					array('passwords.driver.bcrypt_2y', 'passwords.driver.bcrypt'), -				), -				array( -					'passwords.driver.salted_md5', -					array('passwords.driver.salted_md5'), -					false, -				), -				array( -					'passwords.driver.bcrypt_2y', -					array('passwords.driver.salted_md4'), -					false, -				), -				array( -					'$H$', -					array('$2y$'), -				), -			); -		} +		return array( +			array( +				'passwords.driver.salted_md5', +				array('passwords.driver.bcrypt_2y'), +			), +			array( +				'passwords.driver.salted_md5', +				array('passwords.driver.bcrypt'), +			), +			array( +				'passwords.driver.phpass', +				array('passwords.driver.salted_md5'), +			), +			array( +				'passwords.driver.salted_md5', +				array('passwords.driver.bcrypt_2y', 'passwords.driver.bcrypt'), +			), +			array( +				'passwords.driver.salted_md5', +				array('passwords.driver.salted_md5'), +				false, +			), +			array( +				'passwords.driver.bcrypt_2y', +				array('passwords.driver.salted_md4'), +				false, +			), +			array( +				'$H$', +				array('$2y$'), +			), +		);  	}  	/** diff --git a/tests/test_framework/phpbb_test_case_helpers.php b/tests/test_framework/phpbb_test_case_helpers.php index 807a64d810..9a2ea275d0 100644 --- a/tests/test_framework/phpbb_test_case_helpers.php +++ b/tests/test_framework/phpbb_test_case_helpers.php @@ -173,6 +173,16 @@ class phpbb_test_case_helpers  			{  				$config['fulltext_sphinx_id'] = $fulltext_sphinx_id;  			} + +			if (isset($phpbb_memcached_host)) +			{ +				$config['memcached_host'] = $phpbb_memcached_host; +			} + +			if (isset($phpbb_memcached_port)) +			{ +				$config['memcached_port'] = $phpbb_memcached_port; +			}  		}  		if (isset($_SERVER['PHPBB_TEST_DBMS'])) @@ -205,6 +215,16 @@ class phpbb_test_case_helpers  			$config['redis_port'] = $_SERVER['PHPBB_TEST_REDIS_PORT'];  		} +		if (isset($_SERVER['PHPBB_TEST_MEMCACHED_HOST'])) +		{ +			$config['memcached_host'] = $_SERVER['PHPBB_TEST_MEMCACHED_HOST']; +		} + +		if (isset($_SERVER['PHPBB_TEST_MEMCACHED_PORT'])) +		{ +			$config['memcached_port'] = $_SERVER['PHPBB_TEST_MEMCACHED_PORT']; +		} +  		return $config;  	} diff --git a/tests/test_framework/phpbb_ui_test_case.php b/tests/test_framework/phpbb_ui_test_case.php index fcac3594d0..243db39d69 100644 --- a/tests/test_framework/phpbb_ui_test_case.php +++ b/tests/test_framework/phpbb_ui_test_case.php @@ -473,7 +473,8 @@ class phpbb_ui_test_case extends phpbb_test_case  		$config = new \phpbb\config\config(array());  		$db = $this->get_db(); -		$db_tools = new \phpbb\db\tools($db); +		$factory = new \phpbb\db\tools\factory(); +		$db_tools = $factory->get($this->db);  		$container = new phpbb_mock_container_builder();  		$migrator = new \phpbb\db\migrator( diff --git a/tests/text_formatter/s9e/bbcode_merger_test.php b/tests/text_formatter/s9e/bbcode_merger_test.php index 815539056b..5ec0c91971 100644 --- a/tests/text_formatter/s9e/bbcode_merger_test.php +++ b/tests/text_formatter/s9e/bbcode_merger_test.php @@ -275,6 +275,22 @@ class phpbb_textformatter_s9e_bbcode_merger_test extends phpbb_test_case  				</table>  				<p>Ā </p>'  			], +			[ +				// https://www.phpbb.com/community/viewtopic.php?f=438&t=2530451 +				'[issue]{NUMBER}[/issue]', +				'<a href="/default/issues/{NUMBER}"> Issue #{NUMBER}</a>', + +				'[issue={SIMPLETEXT}]{NUMBER}[/issue]', +				'<a href="/{SIMPLETEXT}/issues/{NUMBER}"> Issue #{NUMBER} ({SIMPLETEXT})</a>', + +				'[issue={SIMPLETEXT?}]{NUMBER}[/issue]', +				'<a> +					<xsl:choose> +						<xsl:when test="@issue"><xsl:attribute name="href">/<xsl:value-of select="@issue"/>/issues/<xsl:value-of select="@content"/></xsl:attribute> Issue #<xsl:value-of select="@content"/> (<xsl:value-of select="@issue"/>)</xsl:when> +						<xsl:otherwise><xsl:attribute name="href">/default/issues/<xsl:value-of select="@content"/></xsl:attribute> Issue #<xsl:value-of select="@content"/></xsl:otherwise> +					</xsl:choose> +				</a>' +			],  		];  	}  } diff --git a/tests/text_formatter/s9e/link_helper_test.php b/tests/text_formatter/s9e/link_helper_test.php new file mode 100644 index 0000000000..762d67f883 --- /dev/null +++ b/tests/text_formatter/s9e/link_helper_test.php @@ -0,0 +1,35 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @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_textformatter_s9e_link_helper_test extends phpbb_test_case +{ +	public function test_does_not_override_autoimage() +	{ +		$container    = $this->get_test_case_helpers()->set_s9e_services(); +		$configurator = $container->get('text_formatter.s9e.factory')->get_configurator(); + +		$configurator->Autoimage; +		extract($configurator->finalize()); + +		$original = 'http://localhost/path_to_long_image_filename_0123456789.png'; +		$expected = '<r> +			<URL url="http://localhost/path_to_long_image_filename_0123456789.png"> +				<IMG src="http://localhost/path_to_long_image_filename_0123456789.png"> +					<LINK_TEXT text="http://localhost/path_to_long_image_fil ... 456789.png">http://localhost/path_to_long_image_filename_0123456789.png</LINK_TEXT> +				</IMG> +			</URL> +		</r>'; + +		$this->assertXmlStringEqualsXmlString($expected, $parser->parse($original)); +	} +} diff --git a/phpBB/phpbb/db/tools.php b/tests/text_processing/tickets_data/PHPBB3-16252.after.php index 4d1b91f7b4..c2f57c171e 100644 --- a/phpBB/phpbb/db/tools.php +++ b/tests/text_processing/tickets_data/PHPBB3-16252.after.php @@ -11,11 +11,8 @@  *  */ -namespace phpbb\db; - -/** - * @deprecated	3.2.0-dev	(To be removed 3.3.0) use \phpbb\db\tools\tools instead - */ -class tools extends \phpbb\db\tools\tools +function after_assert_phpbb3_16252($vars)  { +	extract($vars); +	$test->assertEmpty($parser->get_errors());  } diff --git a/tests/text_processing/tickets_data/PHPBB3-16252.before.php b/tests/text_processing/tickets_data/PHPBB3-16252.before.php new file mode 100644 index 0000000000..94c59d9602 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-16252.before.php @@ -0,0 +1,17 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +function before_assert_phpbb3_16252($vars) +{ +	$vars['parser']->disable_bbcode('url'); +} diff --git a/tests/text_processing/tickets_data/PHPBB3-16252.html b/tests/text_processing/tickets_data/PHPBB3-16252.html new file mode 100644 index 0000000000..5b14ab0e7a --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-16252.html @@ -0,0 +1 @@ +http://localhost/
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-16252.txt b/tests/text_processing/tickets_data/PHPBB3-16252.txt new file mode 100644 index 0000000000..5b14ab0e7a --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-16252.txt @@ -0,0 +1 @@ +http://localhost/
\ No newline at end of file diff --git a/travis/ldap/base.ldif b/travis/ldap/base.ldif new file mode 100644 index 0000000000..09fe7cecc6 --- /dev/null +++ b/travis/ldap/base.ldif @@ -0,0 +1,41 @@ +dn: dc=example,dc=com +objectClass: top +objectClass: dcObject +objectClass: organization +o: example +dc: example + +dn: ou=foo,dc=example,dc=com +objectClass: organizationalUnit +ou: foo + +dn: cn=admin,dc=example,dc=com +objectClass: simpleSecurityObject +objectClass: organizationalRole +cn: admin +description: LDAP administrator +userPassword:: e1NTSEF9NytMR2gveUxTMzdsc3RRd1V1dENZSVA0TWdYdm9SdDY= + +dn: ou=group,dc=example,dc=com +objectClass: organizationalUnit +ou: group + +dn: cn=admin,ou=foo,dc=example,dc=com +objectClass: posixAccount +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +loginShell: /bin/bash +homeDirectory: /home/admin +uid: admin +cn: admin +uidNumber: 10000 +gidNumber: 10000 +sn: admin +mail: admin@example.com +userPassword:: e1NTSEF9WHpueGZURHZZc21JSkl6czdMVXBjdCtWYTA1dlMzVlQ= + +dn: cn=admin,ou=group,dc=example,dc=com +objectClass: posixGroup +gidNumber: 10000 +cn: admin diff --git a/travis/ldap/slapd.conf b/travis/ldap/slapd.conf new file mode 100644 index 0000000000..5fce95cee2 --- /dev/null +++ b/travis/ldap/slapd.conf @@ -0,0 +1,17 @@ +# See slapd.conf(5) for details on configuration options. +include   /etc/ldap/schema/core.schema +include   /etc/ldap/schema/cosine.schema +include   /etc/ldap/schema/inetorgperson.schema +include   /etc/ldap/schema/nis.schema + +pidfile         /tmp/slapd/slapd.pid +argsfile        /tmp/slapd/slapd.args + +modulepath     /usr/lib/openldap + +database  ldif +directory /tmp/slapd + +suffix    "dc=example,dc=com" +rootdn    "cn=admin,dc=example,dc=com" +rootpw    adminadmin diff --git a/travis/phpunit-mysqli-travis.xml b/travis/phpunit-mysqli-travis.xml index d2284086b9..0f064ab996 100644 --- a/travis/phpunit-mysqli-travis.xml +++ b/travis/phpunit-mysqli-travis.xml @@ -38,6 +38,7 @@  		<server name="PHPBB_TEST_DBUSER" value="root" />  		<server name="PHPBB_TEST_DBPASSWD" value="" />  		<server name="PHPBB_TEST_REDIS_HOST" value="localhost" /> +		<server name="PHPBB_TEST_MEMCACHED_HOST" value="localhost" />  		<server name="PHPBB_TEST_TABLE_PREFIX" value="phpbb_"/>  		<server name="PHPBB_FUNCTIONAL_URL" value="http://localhost/" />  	</php> diff --git a/travis/phpunit-postgres-travis.xml b/travis/phpunit-postgres-travis.xml index 6faab4d61a..f9b8a6f595 100644 --- a/travis/phpunit-postgres-travis.xml +++ b/travis/phpunit-postgres-travis.xml @@ -38,6 +38,7 @@  		<server name="PHPBB_TEST_DBUSER" value="postgres" />  		<server name="PHPBB_TEST_DBPASSWD" value="" />  		<server name="PHPBB_TEST_REDIS_HOST" value="localhost" /> +		<server name="PHPBB_TEST_MEMCACHED_HOST" value="localhost" />  		<server name="PHPBB_TEST_TABLE_PREFIX" value="phpbb_"/>  		<server name="PHPBB_FUNCTIONAL_URL" value="http://localhost/" />  	</php> diff --git a/travis/phpunit-sqlite3-travis.xml b/travis/phpunit-sqlite3-travis.xml index 633963c9fb..1b1fa24e7d 100644 --- a/travis/phpunit-sqlite3-travis.xml +++ b/travis/phpunit-sqlite3-travis.xml @@ -38,6 +38,7 @@  		<!--server name="PHPBB_TEST_DBUSER" value="" /-->  		<!--server name="PHPBB_TEST_DBPASSWD" value="" /-->  		<server name="PHPBB_TEST_REDIS_HOST" value="localhost" /> +		<server name="PHPBB_TEST_MEMCACHED_HOST" value="localhost" />  		<server name="PHPBB_TEST_TABLE_PREFIX" value="phpbb_"/>  		<server name="PHPBB_FUNCTIONAL_URL" value="http://localhost/" />  	</php> diff --git a/travis/setup-ldap.sh b/travis/setup-ldap.sh new file mode 100755 index 0000000000..9be816d77d --- /dev/null +++ b/travis/setup-ldap.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# +# This file is part of the phpBB Forum Software package. +# +# @copyright (c) phpBB Limited <https://www.phpbb.com> +# @license GNU General Public License, version 2 (GPL-2.0) +# +# For full copyright and license information, please see +# the docs/CREDITS.txt file. +# +set -e +set -x + +SLOWTESTS=$1 + +if [ "$SLOWTESTS" == '1' ] +then +	sudo apt-get -y install ldap-utils slapd php-ldap +	mkdir /tmp/slapd +	slapd -f travis/ldap/slapd.conf -h ldap://localhost:3389 & +	sleep 3 +	ldapadd -h localhost:3389 -D "cn=admin,dc=example,dc=com" -w adminadmin -f travis/ldap/base.ldif +fi diff --git a/travis/setup-php-extensions.sh b/travis/setup-php-extensions.sh index 7d1400c3b3..0fcddb044f 100755 --- a/travis/setup-php-extensions.sh +++ b/travis/setup-php-extensions.sh @@ -42,10 +42,6 @@ function install_php_extension  php_ini_file=$(find_php_ini) -# Disable opcache for testing -echo 'Disabling Opcache' -echo 'opcache.enable=0' >> "$php_ini_file" -  # APCu  if [ `php -r "echo (int) (version_compare(PHP_VERSION, '7.0.0-dev', '>=') && version_compare(PHP_VERSION, '7.3.0-dev', '<'));"` == "1" ]  then @@ -58,6 +54,11 @@ then  	fi  fi +# Disable xdebug on travis +phpenv config-rm xdebug.ini || true + +# memcached +register_php_extension memcached "$php_ini_file"  # redis  # Disabled redis for now as it causes travis to fail | 
