diff options
Diffstat (limited to 'phpBB/phpbb/captcha/plugins')
| -rw-r--r-- | phpBB/phpbb/captcha/plugins/captcha_abstract.php | 390 | ||||
| -rw-r--r-- | phpBB/phpbb/captcha/plugins/gd.php | 130 | ||||
| -rw-r--r-- | phpBB/phpbb/captcha/plugins/gd_wave.php | 42 | ||||
| -rw-r--r-- | phpBB/phpbb/captcha/plugins/nogd.php | 42 | ||||
| -rw-r--r-- | phpBB/phpbb/captcha/plugins/qa.php | 1009 | ||||
| -rw-r--r-- | phpBB/phpbb/captcha/plugins/recaptcha.php | 330 | 
6 files changed, 1943 insertions, 0 deletions
| diff --git a/phpBB/phpbb/captcha/plugins/captcha_abstract.php b/phpBB/phpbb/captcha/plugins/captcha_abstract.php new file mode 100644 index 0000000000..24ed7f939d --- /dev/null +++ b/phpBB/phpbb/captcha/plugins/captcha_abstract.php @@ -0,0 +1,390 @@ +<?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\captcha\plugins; + +/** +* This class holds the code shared by the two default 3.0.x CAPTCHAs. +*/ +abstract class captcha_abstract +{ +	var $confirm_id; +	var $confirm_code; +	var $code; +	var $seed; +	var $attempts = 0; +	var $type; +	var $solved = 0; +	var $captcha_vars = false; + +	/** +	* @var string name of the service. +	*/ +	protected $service_name; + +	function init($type) +	{ +		global $config, $db, $user; + +		// read input +		$this->confirm_id = request_var('confirm_id', ''); +		$this->confirm_code = request_var('confirm_code', ''); +		$refresh = request_var('refresh_vc', false) && $config['confirm_refresh']; + +		$this->type = (int) $type; + +		if (!strlen($this->confirm_id) || !$this->load_code()) +		{ +			// we have no confirm ID, better get ready to display something +			$this->generate_code(); +		} +		else if ($refresh) +		{ +			$this->regenerate_code(); +		} +	} + +	function execute_demo() +	{ +		global $user; + +		$this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS)); +		$this->seed = hexdec(substr(unique_id(), 4, 10)); + +		// compute $seed % 0x7fffffff +		$this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff); + +		$generator = $this->get_generator_class(); +		$captcha = new $generator(); +		define('IMAGE_OUTPUT', 1); +		$captcha->execute($this->code, $this->seed); +	} + +	function execute() +	{ +		if (empty($this->code)) +		{ +			if (!$this->load_code()) +			{ +				// invalid request, bail out +				return false; +			} +		} +		$generator = $this->get_generator_class(); +		$captcha = new $generator(); +		define('IMAGE_OUTPUT', 1); +		$captcha->execute($this->code, $this->seed); +	} + +	function get_template() +	{ +		global $config, $user, $template, $phpEx, $phpbb_root_path; + +		if ($this->is_solved()) +		{ +			return false; +		} +		else +		{ +			$link = append_sid($phpbb_root_path . 'ucp.' . $phpEx,  'mode=confirm&confirm_id=' . $this->confirm_id . '&type=' . $this->type); +			$contact_link = phpbb_get_board_contact_link($config, $phpbb_root_path, $phpEx); +			$explain = $user->lang(($this->type != CONFIRM_POST) ? 'CONFIRM_EXPLAIN' : 'POST_CONFIRM_EXPLAIN', '<a href="' . $contact_link . '">', '</a>'); + +			$template->assign_vars(array( +				'CONFIRM_IMAGE_LINK'		=> $link, +				'CONFIRM_IMAGE'				=> '<img src="' . $link . '" />', +				'CONFIRM_IMG'				=> '<img src="' . $link . '" />', +				'CONFIRM_ID'				=> $this->confirm_id, +				'S_CONFIRM_CODE'			=> true, +				'S_TYPE'					=> $this->type, +				'S_CONFIRM_REFRESH'			=> ($config['enable_confirm'] && $config['confirm_refresh'] && $this->type == CONFIRM_REG) ? true : false, +				'L_CONFIRM_EXPLAIN'			=> $explain, +			)); + +			return 'captcha_default.html'; +		} +	} + +	function get_demo_template($id) +	{ +		global $config, $user, $template, $phpbb_admin_path, $phpEx; + +		$variables = ''; + +		if (is_array($this->captcha_vars)) +		{ +			foreach ($this->captcha_vars as $captcha_var => $template_var) +			{ +				$variables .= '&' . rawurlencode($captcha_var) . '=' . request_var($captcha_var, (int) $config[$captcha_var]); +			} +		} + +		// acp_captcha has a delivery function; let's use it +		$template->assign_vars(array( +			'CONFIRM_IMAGE'		=> append_sid($phpbb_admin_path . 'index.' . $phpEx, 'captcha_demo=1&mode=visual&i=' . $id . '&select_captcha=' . $this->get_service_name()) . $variables, +			'CONFIRM_ID'		=> $this->confirm_id, +		)); + +		return 'captcha_default_acp_demo.html'; +	} + +	function get_hidden_fields() +	{ +		$hidden_fields = array(); + +		// this is required for posting.php - otherwise we would forget about the captcha being already solved +		if ($this->solved) +		{ +			$hidden_fields['confirm_code'] = $this->confirm_code; +		} +		$hidden_fields['confirm_id'] = $this->confirm_id; +		return $hidden_fields; +	} + +	function garbage_collect($type) +	{ +		global $db, $config; + +		$sql = 'SELECT DISTINCT c.session_id +			FROM ' . CONFIRM_TABLE . ' c +			LEFT JOIN ' . SESSIONS_TABLE . ' s ON (c.session_id = s.session_id) +			WHERE s.session_id IS NULL' . +				((empty($type)) ? '' : ' AND c.confirm_type = ' . (int) $type); +		$result = $db->sql_query($sql); + +		if ($row = $db->sql_fetchrow($result)) +		{ +			$sql_in = array(); +			do +			{ +				$sql_in[] = (string) $row['session_id']; +			} +			while ($row = $db->sql_fetchrow($result)); + +			if (sizeof($sql_in)) +			{ +				$sql = 'DELETE FROM ' . CONFIRM_TABLE . ' +					WHERE ' . $db->sql_in_set('session_id', $sql_in); +				$db->sql_query($sql); +			} +		} +		$db->sql_freeresult($result); +	} + +	function uninstall() +	{ +		$this->garbage_collect(0); +	} + +	function install() +	{ +		return; +	} + +	function validate() +	{ +		global $config, $db, $user; + +		if (empty($user->lang)) +		{ +			$user->setup(); +		} + +		$error = ''; +		if (!$this->confirm_id) +		{ +			$error = $user->lang['CONFIRM_CODE_WRONG']; +		} +		else +		{ +			if ($this->check_code()) +			{ +				$this->solved = true; +			} +			else +			{ +				$error = $user->lang['CONFIRM_CODE_WRONG']; +			} +		} + +		if (strlen($error)) +		{ +			// okay, incorrect answer. Let's ask a new question. +			$this->new_attempt(); +			return $error; +		} +		else +		{ +			return false; +		} +	} + +	/** +	* The old way to generate code, suitable for GD and non-GD. Resets the internal state. +	*/ +	function generate_code() +	{ +		global $db, $user; + +		$this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS)); +		$this->confirm_id = md5(unique_id($user->ip)); +		$this->seed = hexdec(substr(unique_id(), 4, 10)); +		$this->solved = 0; +		// compute $seed % 0x7fffffff +		$this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff); + +		$sql = 'INSERT INTO ' . CONFIRM_TABLE . ' ' . $db->sql_build_array('INSERT', array( +				'confirm_id'	=> (string) $this->confirm_id, +				'session_id'	=> (string) $user->session_id, +				'confirm_type'	=> (int) $this->type, +				'code'			=> (string) $this->code, +				'seed'			=> (int) $this->seed) +		); +		$db->sql_query($sql); +	} + +	/** +	* New Question, if desired. +	*/ +	function regenerate_code() +	{ +		global $db, $user; + +		$this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS)); +		$this->seed = hexdec(substr(unique_id(), 4, 10)); +		$this->solved = 0; +		// compute $seed % 0x7fffffff +		$this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff); + +		$sql = 'UPDATE ' . CONFIRM_TABLE . ' SET ' . $db->sql_build_array('UPDATE', array( +				'code'			=> (string) $this->code, +				'seed'			=> (int) $this->seed)) . ' +				WHERE +				confirm_id = \'' . $db->sql_escape($this->confirm_id) . '\' +					AND session_id = \'' . $db->sql_escape($user->session_id) . '\''; +		$db->sql_query($sql); +	} + +	/** +	* New Question, if desired. +	*/ +	function new_attempt() +	{ +		global $db, $user; + +		$this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS)); +		$this->seed = hexdec(substr(unique_id(), 4, 10)); +		$this->solved = 0; +		// compute $seed % 0x7fffffff +		$this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff); + +		$sql = 'UPDATE ' . CONFIRM_TABLE . ' SET ' . $db->sql_build_array('UPDATE', array( +				'code'			=> (string) $this->code, +				'seed'			=> (int) $this->seed)) . ' +				, attempts = attempts + 1 +				WHERE +				confirm_id = \'' . $db->sql_escape($this->confirm_id) . '\' +					AND session_id = \'' . $db->sql_escape($user->session_id) . '\''; +		$db->sql_query($sql); +	} + +	/** +	* Look up everything we need for painting&checking. +	*/ +	function load_code() +	{ +		global $db, $user; + +		$sql = 'SELECT code, seed, attempts +			FROM ' . CONFIRM_TABLE . " +			WHERE confirm_id = '" . $db->sql_escape($this->confirm_id) . "' +				AND session_id = '" . $db->sql_escape($user->session_id) . "' +				AND confirm_type = " . $this->type; +		$result = $db->sql_query($sql); +		$row = $db->sql_fetchrow($result); +		$db->sql_freeresult($result); + +		if ($row) +		{ +			$this->code = $row['code']; +			$this->seed = $row['seed']; +			$this->attempts = $row['attempts']; +			return true; +		} + +		return false; +	} + +	function check_code() +	{ +		return (strcasecmp($this->code, $this->confirm_code) === 0); +	} + +	function get_attempt_count() +	{ +		return $this->attempts; +	} + +	function reset() +	{ +		global $db, $user; + +		$sql = 'DELETE FROM ' . CONFIRM_TABLE . " +			WHERE session_id = '" . $db->sql_escape($user->session_id) . "' +				AND confirm_type = " . (int) $this->type; +		$db->sql_query($sql); + +		// we leave the class usable by generating a new question +		$this->generate_code(); +	} + +	function is_solved() +	{ +		if (request_var('confirm_code', false) && $this->solved === 0) +		{ +			$this->validate(); +		} +		return (bool) $this->solved; +	} + +	/** +	*  API function +	*/ +	function has_config() +	{ +		return false; +	} + +	/** +	* @return string the name of the service corresponding to the plugin +	*/ +	function get_service_name() +	{ +		return $this->service_name; +	} + +	/** +	* Set the name of the plugin +	* +	* @param string $name +	*/ +	public function set_name($name) +	{ +		$this->service_name = $name; +	} + +	/** +	* @return string the name of the class used to generate the captcha +	*/ +	abstract function get_generator_class(); +} diff --git a/phpBB/phpbb/captcha/plugins/gd.php b/phpBB/phpbb/captcha/plugins/gd.php new file mode 100644 index 0000000000..f6200b5b2f --- /dev/null +++ b/phpBB/phpbb/captcha/plugins/gd.php @@ -0,0 +1,130 @@ +<?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\captcha\plugins; + +class gd extends captcha_abstract +{ +	var $captcha_vars = array( +		'captcha_gd_x_grid'				=> 'CAPTCHA_GD_X_GRID', +		'captcha_gd_y_grid'				=> 'CAPTCHA_GD_Y_GRID', +		'captcha_gd_foreground_noise'	=> 'CAPTCHA_GD_FOREGROUND_NOISE', +//		'captcha_gd'					=> 'CAPTCHA_GD_PREVIEWED', +		'captcha_gd_wave'				=> 'CAPTCHA_GD_WAVE', +		'captcha_gd_3d_noise'			=> 'CAPTCHA_GD_3D_NOISE', +		'captcha_gd_fonts'				=> 'CAPTCHA_GD_FONTS', +	); + +	public function is_available() +	{ +		return @extension_loaded('gd'); +	} + +	/** +	* @return string the name of the class used to generate the captcha +	*/ +	function get_generator_class() +	{ +		return '\\phpbb\\captcha\\gd'; +	} + +	/** +	*  API function +	*/ +	function has_config() +	{ +		return true; +	} + +	public function get_name() +	{ +		return 'CAPTCHA_GD'; +	} + +	function acp_page($id, &$module) +	{ +		global $db, $user, $auth, $template; +		global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx; + +		$user->add_lang('acp/board'); + +		$config_vars = array( +			'enable_confirm'		=> 'REG_ENABLE', +			'enable_post_confirm'	=> 'POST_ENABLE', +			'confirm_refresh'		=> 'CONFIRM_REFRESH', +			'captcha_gd'			=> 'CAPTCHA_GD', +		); + +		$module->tpl_name = 'captcha_gd_acp'; +		$module->page_title = 'ACP_VC_SETTINGS'; +		$form_key = 'acp_captcha'; +		add_form_key($form_key); + +		$submit = request_var('submit', ''); + +		if ($submit && check_form_key($form_key)) +		{ +			$captcha_vars = array_keys($this->captcha_vars); +			foreach ($captcha_vars as $captcha_var) +			{ +				$value = request_var($captcha_var, 0); +				if ($value >= 0) +				{ +					set_config($captcha_var, $value); +				} +			} + +			add_log('admin', 'LOG_CONFIG_VISUAL'); +			trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($module->u_action)); +		} +		else if ($submit) +		{ +			trigger_error($user->lang['FORM_INVALID'] . adm_back_link($module->u_action)); +		} +		else +		{ +			foreach ($this->captcha_vars as $captcha_var => $template_var) +			{ +				$var = (isset($_REQUEST[$captcha_var])) ? request_var($captcha_var, 0) : $config[$captcha_var]; +				$template->assign_var($template_var, $var); +			} + +			$template->assign_vars(array( +				'CAPTCHA_PREVIEW'	=> $this->get_demo_template($id), +				'CAPTCHA_NAME'		=> $this->get_service_name(), +				'U_ACTION'			=> $module->u_action, +			)); +		} +	} + +	function execute_demo() +	{ +		global $config; + +		$config_old = $config; + +		$config = new \phpbb\config\config(array()); +		foreach ($config_old as $key => $value) +		{ +			$config->set($key, $value); +		} + +		foreach ($this->captcha_vars as $captcha_var => $template_var) +		{ +			$config->set($captcha_var, request_var($captcha_var, (int) $config[$captcha_var])); +		} +		parent::execute_demo(); +		$config = $config_old; +	} + +} diff --git a/phpBB/phpbb/captcha/plugins/gd_wave.php b/phpBB/phpbb/captcha/plugins/gd_wave.php new file mode 100644 index 0000000000..e1d44df778 --- /dev/null +++ b/phpBB/phpbb/captcha/plugins/gd_wave.php @@ -0,0 +1,42 @@ +<?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\captcha\plugins; + +class gd_wave extends captcha_abstract +{ +	public function is_available() +	{ +		return @extension_loaded('gd'); +	} + +	public function get_name() +	{ +		return 'CAPTCHA_GD_3D'; +	} + +	/** +	* @return string the name of the class used to generate the captcha +	*/ +	function get_generator_class() +	{ +		return '\\phpbb\\captcha\\gd_wave'; +	} + +	function acp_page($id, &$module) +	{ +		global $config, $db, $template, $user; + +		trigger_error($user->lang['CAPTCHA_NO_OPTIONS'] . adm_back_link($module->u_action)); +	} +} diff --git a/phpBB/phpbb/captcha/plugins/nogd.php b/phpBB/phpbb/captcha/plugins/nogd.php new file mode 100644 index 0000000000..6845e5935c --- /dev/null +++ b/phpBB/phpbb/captcha/plugins/nogd.php @@ -0,0 +1,42 @@ +<?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\captcha\plugins; + +class nogd extends captcha_abstract +{ +	public function is_available() +	{ +		return true; +	} + +	public function get_name() +	{ +		return 'CAPTCHA_NO_GD'; +	} + +	/** +	* @return string the name of the class used to generate the captcha +	*/ +	function get_generator_class() +	{ +		return '\\phpbb\\captcha\\non_gd'; +	} + +	function acp_page($id, &$module) +	{ +		global $user; + +		trigger_error($user->lang['CAPTCHA_NO_OPTIONS'] . adm_back_link($module->u_action)); +	} +} diff --git a/phpBB/phpbb/captcha/plugins/qa.php b/phpBB/phpbb/captcha/plugins/qa.php new file mode 100644 index 0000000000..a7ba994cc3 --- /dev/null +++ b/phpBB/phpbb/captcha/plugins/qa.php @@ -0,0 +1,1009 @@ +<?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\captcha\plugins; + +/** +* And now to something completely different. Let's make a captcha without extending the abstract class. +* QA CAPTCHA sample implementation +*/ +class qa +{ +	var $confirm_id; +	var $answer; +	var $question_ids; +	var $question_text; +	var $question_lang; +	var $question_strict; +	var $attempts = 0; +	var $type; +	// dirty trick: 0 is false, but can still encode that the captcha is not yet validated +	var $solved = 0; + +	protected $table_captcha_questions; +	protected $table_captcha_answers; +	protected $table_qa_confirm; + +	/** +	* @var string name of the service. +	*/ +	protected $service_name; + +	/** +	* Constructor +	* +	* @param string $table_captcha_questions +	* @param string $table_captcha_answers +	* @param string $table_qa_confirm +	*/ +	function __construct($table_captcha_questions, $table_captcha_answers, $table_qa_confirm) +	{ +		$this->table_captcha_questions = $table_captcha_questions; +		$this->table_captcha_answers = $table_captcha_answers; +		$this->table_qa_confirm = $table_qa_confirm; +	} + +	/** +	* @param int $type  as per the CAPTCHA API docs, the type +	*/ +	function init($type) +	{ +		global $config, $db, $user; + +		// load our language file +		$user->add_lang('captcha_qa'); + +		// read input +		$this->confirm_id = request_var('qa_confirm_id', ''); +		$this->answer = utf8_normalize_nfc(request_var('qa_answer', '', true)); + +		$this->type = (int) $type; +		$this->question_lang = $user->lang_name; + +		// we need all defined questions - shouldn't be too many, so we can just grab them +		// try the user's lang first +		$sql = 'SELECT question_id +			FROM ' . $this->table_captcha_questions . " +			WHERE lang_iso = '" . $db->sql_escape($user->lang_name) . "'"; +		$result = $db->sql_query($sql, 3600); + +		while ($row = $db->sql_fetchrow($result)) +		{ +			$this->question_ids[$row['question_id']] = $row['question_id']; +		} +		$db->sql_freeresult($result); + +		// fallback to the board default lang +		if (!sizeof($this->question_ids)) +		{ +			$this->question_lang = $config['default_lang']; + +			$sql = 'SELECT question_id +				FROM ' . $this->table_captcha_questions . " +				WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'"; +			$result = $db->sql_query($sql, 7200); + +			while ($row = $db->sql_fetchrow($result)) +			{ +				$this->question_ids[$row['question_id']] = $row['question_id']; +			} +			$db->sql_freeresult($result); +		} + +		// okay, if there is a confirm_id, we try to load that confirm's state. If not, we try to find one +		if (!$this->load_answer() && (!$this->load_confirm_id() || !$this->load_answer())) +		{ +			// we have no valid confirm ID, better get ready to ask something +			$this->select_question(); +		} +	} + +	/** +	* See if the captcha has created its tables. +	*/ +	public function is_installed() +	{ +		global $db; + +		$db_tool = new \phpbb\db\tools($db); + +		return $db_tool->sql_table_exists($this->table_captcha_questions); +	} + +	/** +	*  API function - for the captcha to be available, it must have installed itself and there has to be at least one question in the board's default lang +	*/ +	public function is_available() +	{ +		global $config, $db, $phpbb_root_path, $phpEx, $user; + +		// load language file for pretty display in the ACP dropdown +		$user->add_lang('captcha_qa'); + +		if (!$this->is_installed()) +		{ +			return false; +		} + +		$sql = 'SELECT COUNT(question_id) AS question_count +			FROM ' . $this->table_captcha_questions . " +			WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'"; +		$result = $db->sql_query($sql); +		$row = $db->sql_fetchrow($result); +		$db->sql_freeresult($result); + +		return ((bool) $row['question_count']); +	} + +	/** +	*  API function +	*/ +	function has_config() +	{ +		return true; +	} + +	/** +	*  API function +	*/ +	static public function get_name() +	{ +		return 'CAPTCHA_QA'; +	} + +	/** +	* @return string the name of the service corresponding to the plugin +	*/ +	function get_service_name() +	{ +		return $this->service_name; +	} + +	/** +	* Set the name of the plugin +	* +	* @param string $name +	*/ +	public function set_name($name) +	{ +		$this->service_name = $name; +	} + +	/** +	*  API function - not needed as we don't display an image +	*/ +	function execute_demo() +	{ +	} + +	/** +	*  API function - not needed as we don't display an image +	*/ +	function execute() +	{ +	} + +	/** +	*  API function - send the question to the template +	*/ +	function get_template() +	{ +		global $template; + +		if ($this->is_solved()) +		{ +			return false; +		} +		else +		{ +			$template->assign_vars(array( +				'QA_CONFIRM_QUESTION'		=> $this->question_text, +				'QA_CONFIRM_ID'				=> $this->confirm_id, +				'S_CONFIRM_CODE'			=> true, +				'S_TYPE'					=> $this->type, +			)); + +			return 'captcha_qa.html'; +		} +	} + +	/** +	*  API function - we just display a mockup so that the captcha doesn't need to be installed +	*/ +	function get_demo_template() +	{ +		global $config, $db, $template; + +		if ($this->is_available()) +		{ +			$sql = 'SELECT question_text +				FROM ' . $this->table_captcha_questions . " +				WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'"; +			$result = $db->sql_query_limit($sql, 1); +			if ($row = $db->sql_fetchrow($result)) +			{ +				$template->assign_vars(array( +					'QA_CONFIRM_QUESTION'		=> $row['question_text'], +				)); +			} +			$db->sql_freeresult($result); +		} +		return 'captcha_qa_acp_demo.html'; +	} + +	/** +	*  API function +	*/ +	function get_hidden_fields() +	{ +		$hidden_fields = array(); + +		// this is required - otherwise we would forget about the captcha being already solved +		if ($this->solved) +		{ +			$hidden_fields['qa_answer'] = $this->answer; +		} +		$hidden_fields['qa_confirm_id'] = $this->confirm_id; + +		return $hidden_fields; +	} + +	/** +	*  API function +	*/ +	function garbage_collect($type = 0) +	{ +		global $db, $config; + +		$sql = 'SELECT c.confirm_id +			FROM ' . $this->table_qa_confirm . ' c +			LEFT JOIN ' . SESSIONS_TABLE . ' s +				ON (c.session_id = s.session_id) +			WHERE s.session_id IS NULL' . +				((empty($type)) ? '' : ' AND c.confirm_type = ' . (int) $type); +		$result = $db->sql_query($sql); + +		if ($row = $db->sql_fetchrow($result)) +		{ +			$sql_in = array(); + +			do +			{ +				$sql_in[] = (string) $row['confirm_id']; +			} +			while ($row = $db->sql_fetchrow($result)); + +			if (sizeof($sql_in)) +			{ +				$sql = 'DELETE FROM ' . $this->table_qa_confirm . ' +					WHERE ' . $db->sql_in_set('confirm_id', $sql_in); +				$db->sql_query($sql); +			} +		} +		$db->sql_freeresult($result); +	} + +	/** +	*  API function - we don't drop the tables here, as that would cause the loss of all entered questions. +	*/ +	function uninstall() +	{ +		$this->garbage_collect(0); +	} + +	/** +	*  API function - set up shop +	*/ +	function install() +	{ +		global $db; + +		$db_tool = new \phpbb\db\tools($db); + +		$tables = array($this->table_captcha_questions, $this->table_captcha_answers, $this->table_qa_confirm); + +		$schemas = array( +				$this->table_captcha_questions		=> array ( +					'COLUMNS' => array( +						'question_id'	=> array('UINT', null, 'auto_increment'), +						'strict'		=> array('BOOL', 0), +						'lang_id'		=> array('UINT', 0), +						'lang_iso'		=> array('VCHAR:30', ''), +						'question_text'	=> array('TEXT_UNI', ''), +					), +					'PRIMARY_KEY'		=> 'question_id', +					'KEYS'				=> array( +						'lang'			=> array('INDEX', 'lang_iso'), +					), +				), +				$this->table_captcha_answers		=> array ( +					'COLUMNS' => array( +						'question_id'	=> array('UINT', 0), +						'answer_text'	=> array('STEXT_UNI', ''), +					), +					'KEYS'				=> array( +						'qid'			=> array('INDEX', 'question_id'), +					), +				), +				$this->table_qa_confirm		=> array ( +					'COLUMNS' => array( +						'session_id'	=> array('CHAR:32', ''), +						'confirm_id'	=> array('CHAR:32', ''), +						'lang_iso'		=> array('VCHAR:30', ''), +						'question_id'	=> array('UINT', 0), +						'attempts'		=> array('UINT', 0), +						'confirm_type'	=> array('USINT', 0), +					), +					'KEYS'				=> array( +						'session_id'			=> array('INDEX', 'session_id'), +						'lookup'				=> array('INDEX', array('confirm_id', 'session_id', 'lang_iso')), +					), +					'PRIMARY_KEY'		=> 'confirm_id', +				), +		); + +		foreach($schemas as $table => $schema) +		{ +			if (!$db_tool->sql_table_exists($table)) +			{ +				$db_tool->sql_create_table($table, $schema); +			} +		} +	} + +	/** +	*  API function - see what has to be done to validate +	*/ +	function validate() +	{ +		global $config, $db, $user; + +		$error = ''; + +		if (!sizeof($this->question_ids)) +		{ +			return false; +		} + +		if (!$this->confirm_id) +		{ +			$error = $user->lang['CONFIRM_QUESTION_WRONG']; +		} +		else +		{ +			if ($this->check_answer()) +			{ +				$this->solved = true; +			} +			else +			{ +				$error = $user->lang['CONFIRM_QUESTION_WRONG']; +			} +		} + +		if (strlen($error)) +		{ +			// okay, incorrect answer. Let's ask a new question. +			$this->new_attempt(); +			$this->solved = false; + +			return $error; +		} +		else +		{ +			return false; +		} +	} + +	/** +	*  Select a question +	*/ +	function select_question() +	{ +		global $db, $user; + +		if (!sizeof($this->question_ids)) +		{ +			return false; +		} +		$this->confirm_id = md5(unique_id($user->ip)); +		$this->question = (int) array_rand($this->question_ids); + +		$sql = 'INSERT INTO ' . $this->table_qa_confirm . ' ' . $db->sql_build_array('INSERT', array( +			'confirm_id'	=> (string) $this->confirm_id, +			'session_id'	=> (string) $user->session_id, +			'lang_iso'		=> (string) $this->question_lang, +			'confirm_type'	=> (int) $this->type, +			'question_id'	=> (int) $this->question, +		)); +		$db->sql_query($sql); + +		$this->load_answer(); +	} + +	/** +	* New Question, if desired. +	*/ +	function reselect_question() +	{ +		global $db, $user; + +		if (!sizeof($this->question_ids)) +		{ +			return false; +		} + +		$this->question = (int) array_rand($this->question_ids); +		$this->solved = 0; + +		$sql = 'UPDATE ' . $this->table_qa_confirm . ' +			SET question_id = ' . (int) $this->question . " +			WHERE confirm_id = '" . $db->sql_escape($this->confirm_id) . "' +				AND session_id = '" . $db->sql_escape($user->session_id) . "'"; +		$db->sql_query($sql); + +		$this->load_answer(); +	} + +	/** +	* Wrong answer, so we increase the attempts and use a different question. +	*/ +	function new_attempt() +	{ +		global $db, $user; + +		// yah, I would prefer a stronger rand, but this should work +		$this->question = (int) array_rand($this->question_ids); +		$this->solved = 0; + +		$sql = 'UPDATE ' . $this->table_qa_confirm . ' +			SET question_id = ' . (int) $this->question . ", +				attempts = attempts + 1 +			WHERE confirm_id = '" . $db->sql_escape($this->confirm_id) . "' +				AND session_id = '" . $db->sql_escape($user->session_id) . "'"; +		$db->sql_query($sql); + +		$this->load_answer(); +	} + + +	/** +	* See if there is already an entry for the current session. +	*/ +	function load_confirm_id() +	{ +		global $db, $user; + +		$sql = 'SELECT confirm_id +			FROM ' . $this->table_qa_confirm . " +			WHERE +				session_id = '" . $db->sql_escape($user->session_id) . "' +				AND lang_iso = '" . $db->sql_escape($this->question_lang) . "' +				AND confirm_type = " . $this->type; +		$result = $db->sql_query_limit($sql, 1); +		$row = $db->sql_fetchrow($result); +		$db->sql_freeresult($result); + +		if ($row) +		{ +			$this->confirm_id = $row['confirm_id']; +			return true; +		} +		return false; +	} + +	/** +	* Look up everything we need and populate the instance variables. +	*/ +	function load_answer() +	{ +		global $db, $user; + +		if (!strlen($this->confirm_id) || !sizeof($this->question_ids)) +		{ +			return false; +		} + +		$sql = 'SELECT con.question_id, attempts, question_text, strict +			FROM ' . $this->table_qa_confirm . ' con, ' . $this->table_captcha_questions . " qes +			WHERE con.question_id = qes.question_id +				AND confirm_id = '" . $db->sql_escape($this->confirm_id) . "' +				AND session_id = '" . $db->sql_escape($user->session_id) . "' +				AND qes.lang_iso = '" . $db->sql_escape($this->question_lang) . "' +				AND confirm_type = " . $this->type; +		$result = $db->sql_query($sql); +		$row = $db->sql_fetchrow($result); +		$db->sql_freeresult($result); + +		if ($row) +		{ +			$this->question = $row['question_id']; + +			$this->attempts = $row['attempts']; +			$this->question_strict = $row['strict']; +			$this->question_text = $row['question_text']; + +			return true; +		} + +		return false; +	} + +	/** +	*  The actual validation +	*/ +	function check_answer() +	{ +		global $db; + +		$answer = ($this->question_strict) ? utf8_normalize_nfc(request_var('qa_answer', '', true)) : utf8_clean_string(utf8_normalize_nfc(request_var('qa_answer', '', true))); + +		$sql = 'SELECT answer_text +			FROM ' . $this->table_captcha_answers . ' +			WHERE question_id = ' . (int) $this->question; +		$result = $db->sql_query($sql); + +		while ($row = $db->sql_fetchrow($result)) +		{ +			$solution = ($this->question_strict) ? $row['answer_text'] : utf8_clean_string($row['answer_text']); + +			if ($solution === $answer) +			{ +				$this->solved = true; + +				break; +			} +		} +		$db->sql_freeresult($result); + +		return $this->solved; +	} + +	/** +	*  API function +	*/ +	function get_attempt_count() +	{ +		return $this->attempts; +	} + +	/** +	*  API function +	*/ +	function reset() +	{ +		global $db, $user; + +		$sql = 'DELETE FROM ' . $this->table_qa_confirm . " +			WHERE session_id = '" . $db->sql_escape($user->session_id) . "' +				AND confirm_type = " . (int) $this->type; +		$db->sql_query($sql); + +		// we leave the class usable by generating a new question +		$this->select_question(); +	} + +	/** +	*  API function +	*/ +	function is_solved() +	{ +		if (request_var('qa_answer', false) && $this->solved === 0) +		{ +			$this->validate(); +		} + +		return (bool) $this->solved; +	} + +	/** +	*  API function - The ACP backend, this marks the end of the easy methods +	*/ +	function acp_page($id, &$module) +	{ +		global $db, $user, $auth, $template; +		global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx; + +		$user->add_lang('acp/board'); +		$user->add_lang('captcha_qa'); + +		if (!self::is_installed()) +		{ +			$this->install(); +		} + +		$module->tpl_name = 'captcha_qa_acp'; +		$module->page_title = 'ACP_VC_SETTINGS'; +		$form_key = 'acp_captcha'; +		add_form_key($form_key); + +		$submit = request_var('submit', false); +		$question_id = request_var('question_id', 0); +		$action = request_var('action', ''); + +		// we have two pages, so users might want to navigate from one to the other +		$list_url = $module->u_action . "&configure=1&select_captcha=" . $this->get_service_name(); + +		$template->assign_vars(array( +			'U_ACTION'		=> $module->u_action, +			'QUESTION_ID'	=> $question_id , +			'CLASS'			=> $this->get_service_name(), +		)); + +		// show the list? +		if (!$question_id && $action != 'add') +		{ +			$this->acp_question_list($module); +		} +		else if ($question_id && $action == 'delete') +		{ +			if ($this->get_service_name() !== $config['captcha_plugin'] || !$this->acp_is_last($question_id)) +			{ +				if (confirm_box(true)) +				{ +					$this->acp_delete_question($question_id); + +					trigger_error($user->lang['QUESTION_DELETED'] . adm_back_link($list_url)); +				} +				else +				{ +					confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array( +						'question_id'		=> $question_id, +						'action'			=> $action, +						'configure'			=> 1, +						'select_captcha'	=> $this->get_service_name(), +						)) +					); +				} +			} +			else +			{ +				trigger_error($user->lang['QA_LAST_QUESTION'] . adm_back_link($list_url), E_USER_WARNING); +			} +		} +		else +		{ +			// okay, show the editor +			$error = false; +			$input_question = request_var('question_text', '', true); +			$input_answers = request_var('answers', '', true); +			$input_lang = request_var('lang_iso', '', true); +			$input_strict = request_var('strict', false); +			$langs = $this->get_languages(); + +			foreach ($langs as $lang => $entry) +			{ +				$template->assign_block_vars('langs', array( +					'ISO' => $lang, +					'NAME' => $entry['name'], +				)); +			} + +			$template->assign_vars(array( +				'U_LIST' => $list_url, +			)); + +			if ($question_id) +			{ +				if ($question = $this->acp_get_question_data($question_id)) +				{ +					$answers = (isset($input_answers[$lang])) ? $input_answers[$lang] : implode("\n", $question['answers']); + +					$template->assign_vars(array( +						'QUESTION_TEXT'		=> ($input_question) ? $input_question : $question['question_text'], +						'LANG_ISO'			=> ($input_lang) ? $input_lang : $question['lang_iso'], +						'STRICT'			=> (isset($_REQUEST['strict'])) ? $input_strict : $question['strict'], +						'ANSWERS'			=> $answers, +					)); +				} +				else +				{ +					trigger_error($user->lang['FORM_INVALID'] . adm_back_link($list_url)); +				} +			} +			else +			{ +				$template->assign_vars(array( +					'QUESTION_TEXT'		=> $input_question, +					'LANG_ISO'			=> $input_lang, +					'STRICT'			=> $input_strict, +					'ANSWERS'			=> $input_answers, +				)); +			} + +			if ($submit && check_form_key($form_key)) +			{ +				$data = $this->acp_get_question_input(); + +				if (!$this->validate_input($data)) +				{ +					$template->assign_vars(array( +						'S_ERROR'			=> true, +					)); +				} +				else +				{ +					if ($question_id) +					{ +						$this->acp_update_question($data, $question_id); +					} +					else +					{ +						$this->acp_add_question($data); +					} + +					add_log('admin', 'LOG_CONFIG_VISUAL'); +					trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($list_url)); +				} +			} +			else if ($submit) +			{ +				trigger_error($user->lang['FORM_INVALID'] . adm_back_link($list_url), E_USER_WARNING); +			} +		} +	} + +	/** +	*  This handles the list overview +	*/ +	function acp_question_list(&$module) +	{ +		global $db, $template; + +		$sql = 'SELECT * +			FROM ' . $this->table_captcha_questions; +		$result = $db->sql_query($sql); + +		$template->assign_vars(array( +			'S_LIST'			=> true, +		)); + +		while ($row = $db->sql_fetchrow($result)) +		{ +			$url = $module->u_action . "&question_id={$row['question_id']}&configure=1&select_captcha=" . $this->get_service_name() . '&'; + +			$template->assign_block_vars('questions', array( +				'QUESTION_TEXT'		=> $row['question_text'], +				'QUESTION_ID'		=> $row['question_id'], +				'QUESTION_LANG'		=> $row['lang_iso'], +				'U_DELETE'			=> "{$url}action=delete", +				'U_EDIT'			=> "{$url}action=edit", +			)); +		} +		$db->sql_freeresult($result); +	} + +	/** +	*  Grab a question and bring it into a format the editor understands +	*/ +	function acp_get_question_data($question_id) +	{ +		global $db; + +		if ($question_id) +		{ +			$sql = 'SELECT * +				FROM ' . $this->table_captcha_questions . ' +				WHERE question_id = ' . $question_id; +			$result = $db->sql_query($sql); +			$question = $db->sql_fetchrow($result); +			$db->sql_freeresult($result); + +			if (!$question) +			{ +				return false; +			} + +			$question['answers'] = array(); + +			$sql = 'SELECT * +				FROM ' . $this->table_captcha_answers . ' +				WHERE question_id = ' . $question_id; +			$result = $db->sql_query($sql); + +			while ($row = $db->sql_fetchrow($result)) +			{ +				$question['answers'][] = $row['answer_text']; +			} +			$db->sql_freeresult($result); + +			return $question; +		} +	} + +	/** +	*  Grab a question from input and bring it into a format the editor understands +	*/ +	function acp_get_question_input() +	{ +		$answers = utf8_normalize_nfc(request_var('answers', '', true)); +		$question = array( +			'question_text'	=> request_var('question_text', '', true), +			'strict'		=> request_var('strict', false), +			'lang_iso'		=> request_var('lang_iso', ''), +			'answers'		=> (strlen($answers)) ? explode("\n", $answers) : '', +		); + +		return $question; +	} + +	/** +	*  Update a question. +	* param mixed $data : an array as created from acp_get_question_input or acp_get_question_data +	*/ +	function acp_update_question($data, $question_id) +	{ +		global $db, $cache; + +		// easier to delete all answers than to figure out which to update +		$sql = 'DELETE FROM ' . $this->table_captcha_answers . " WHERE question_id = $question_id"; +		$db->sql_query($sql); + +		$langs = $this->get_languages(); +		$question_ary = $data; +		$question_ary['lang_id'] = $langs[$question_ary['lang_iso']]['id']; +		unset($question_ary['answers']); + +		$sql = 'UPDATE ' . $this->table_captcha_questions . ' +			SET ' . $db->sql_build_array('UPDATE', $question_ary) . " +			WHERE question_id = $question_id"; +		$db->sql_query($sql); + +		$this->acp_insert_answers($data, $question_id); + +		$cache->destroy('sql', $this->table_captcha_questions); +	} + +	/** +	*  Insert a question. +	* param mixed $data : an array as created from acp_get_question_input or acp_get_question_data +	*/ +	function acp_add_question($data) +	{ +		global $db, $cache; + +		$langs = $this->get_languages(); +		$question_ary = $data; + +		$question_ary['lang_id'] = $langs[$data['lang_iso']]['id']; +		unset($question_ary['answers']); + +		$sql = 'INSERT INTO ' . $this->table_captcha_questions . ' ' . $db->sql_build_array('INSERT', $question_ary); +		$db->sql_query($sql); + +		$question_id = $db->sql_nextid(); + +		$this->acp_insert_answers($data, $question_id); + +		$cache->destroy('sql', $this->table_captcha_questions); +	} + +	/** +	*  Insert the answers. +	* param mixed $data : an array as created from acp_get_question_input or acp_get_question_data +	*/ +	function acp_insert_answers($data, $question_id) +	{ +		global $db, $cache; + +		foreach ($data['answers'] as $answer) +		{ +			$answer_ary = array( +				'question_id'	=> $question_id, +				'answer_text'	=> $answer, +			); + +			$sql = 'INSERT INTO ' . $this->table_captcha_answers . ' ' . $db->sql_build_array('INSERT', $answer_ary); +			$db->sql_query($sql); +		} + +		$cache->destroy('sql', $this->table_captcha_answers); +	} + +	/** +	*  Delete a question. +	*/ +	function acp_delete_question($question_id) +	{ +		global $db, $cache; + +		$tables = array($this->table_captcha_questions, $this->table_captcha_answers); + +		foreach ($tables as $table) +		{ +			$sql = "DELETE FROM $table +				WHERE question_id = $question_id"; +			$db->sql_query($sql); +		} + +		$cache->destroy('sql', $tables); +	} + +	/** +	*  Check if the entered data can be inserted/used +	* param mixed $data : an array as created from acp_get_question_input or acp_get_question_data +	*/ +	function validate_input($question_data) +	{ +		$langs = $this->get_languages(); + +		if (!isset($question_data['lang_iso']) || +			!isset($question_data['question_text']) || +			!isset($question_data['strict']) || +			!isset($question_data['answers'])) +		{ +			return false; +		} + +		if (!isset($langs[$question_data['lang_iso']]) || +			!strlen($question_data['question_text']) || +			!sizeof($question_data['answers']) || +			!is_array($question_data['answers'])) +		{ +			return false; +		} + +		return true; +	} + +	/** +	* List the installed language packs +	*/ +	function get_languages() +	{ +		global $db; + +		$sql = 'SELECT * +			FROM ' . LANG_TABLE; +		$result = $db->sql_query($sql); + +		$langs = array(); +		while ($row = $db->sql_fetchrow($result)) +		{ +			$langs[$row['lang_iso']] = array( +				'name'	=> $row['lang_local_name'], +				'id'	=> (int) $row['lang_id'], +			); +		} +		$db->sql_freeresult($result); + +		return $langs; +	} + + + +	/** +	*  See if there is a question other than the one we have +	*/ +	function acp_is_last($question_id) +	{ +		global $config, $db; + +		if ($question_id) +		{ +			$sql = 'SELECT question_id +				FROM ' . $this->table_captcha_questions . " +				WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "' +					AND  question_id <> " .  (int) $question_id; +			$result = $db->sql_query_limit($sql, 1); +			$question = $db->sql_fetchrow($result); +			$db->sql_freeresult($result); + +			if (!$question) +			{ +				return true; +			} +			return false; +		} +	} +} diff --git a/phpBB/phpbb/captcha/plugins/recaptcha.php b/phpBB/phpbb/captcha/plugins/recaptcha.php new file mode 100644 index 0000000000..ea446d7bc3 --- /dev/null +++ b/phpBB/phpbb/captcha/plugins/recaptcha.php @@ -0,0 +1,330 @@ +<?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\captcha\plugins; + +class recaptcha extends captcha_abstract +{ +	var $recaptcha_server = 'http://www.google.com/recaptcha/api'; +	var $recaptcha_server_secure = 'https://www.google.com/recaptcha/api'; // class constants :( + +	// We are opening a socket to port 80 of this host and send +	// the POST request asking for verification to the path specified here. +	var $recaptcha_verify_server = 'www.google.com'; +	var $recaptcha_verify_path = '/recaptcha/api/verify'; + +	var $challenge; +	var $response; + +	// PHP4 Constructor +	function phpbb_recaptcha() +	{ +		global $request; +		$this->recaptcha_server = $request->is_secure() ? $this->recaptcha_server_secure : $this->recaptcha_server; +	} + +	function init($type) +	{ +		global $config, $db, $user; + +		$user->add_lang('captcha_recaptcha'); +		parent::init($type); +		$this->challenge = request_var('recaptcha_challenge_field', ''); +		$this->response = request_var('recaptcha_response_field', ''); +	} + +	public function is_available() +	{ +		global $config, $user; +		$user->add_lang('captcha_recaptcha'); +		return (isset($config['recaptcha_pubkey']) && !empty($config['recaptcha_pubkey'])); +	} + +	/** +	*  API function +	*/ +	function has_config() +	{ +		return true; +	} + +	static public function get_name() +	{ +		return 'CAPTCHA_RECAPTCHA'; +	} + +	/** +	* This function is implemented because required by the upper class, but is never used for reCaptcha. +	*/ +	function get_generator_class() +	{ +		throw new \Exception('No generator class given.'); +	} + +	function acp_page($id, &$module) +	{ +		global $config, $db, $template, $user; + +		$captcha_vars = array( +			'recaptcha_pubkey'				=> 'RECAPTCHA_PUBKEY', +			'recaptcha_privkey'				=> 'RECAPTCHA_PRIVKEY', +		); + +		$module->tpl_name = 'captcha_recaptcha_acp'; +		$module->page_title = 'ACP_VC_SETTINGS'; +		$form_key = 'acp_captcha'; +		add_form_key($form_key); + +		$submit = request_var('submit', ''); + +		if ($submit && check_form_key($form_key)) +		{ +			$captcha_vars = array_keys($captcha_vars); +			foreach ($captcha_vars as $captcha_var) +			{ +				$value = request_var($captcha_var, ''); +				if ($value) +				{ +					set_config($captcha_var, $value); +				} +			} + +			add_log('admin', 'LOG_CONFIG_VISUAL'); +			trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($module->u_action)); +		} +		else if ($submit) +		{ +			trigger_error($user->lang['FORM_INVALID'] . adm_back_link($module->u_action)); +		} +		else +		{ +			foreach ($captcha_vars as $captcha_var => $template_var) +			{ +				$var = (isset($_REQUEST[$captcha_var])) ? request_var($captcha_var, '') : ((isset($config[$captcha_var])) ? $config[$captcha_var] : ''); +				$template->assign_var($template_var, $var); +			} + +			$template->assign_vars(array( +				'CAPTCHA_PREVIEW'	=> $this->get_demo_template($id), +				'CAPTCHA_NAME'		=> $this->get_service_name(), +				'U_ACTION'			=> $module->u_action, +			)); + +		} +	} + +	// not needed +	function execute_demo() +	{ +	} + +	// not needed +	function execute() +	{ +	} + +	function get_template() +	{ +		global $config, $user, $template, $phpbb_root_path, $phpEx; + +		if ($this->is_solved()) +		{ +			return false; +		} +		else +		{ +			$contact_link = phpbb_get_board_contact_link($config, $phpbb_root_path, $phpEx); +			$explain = $user->lang(($this->type != CONFIRM_POST) ? 'CONFIRM_EXPLAIN' : 'POST_CONFIRM_EXPLAIN', '<a href="' . $contact_link . '">', '</a>'); + +			$template->assign_vars(array( +				'RECAPTCHA_SERVER'			=> $this->recaptcha_server, +				'RECAPTCHA_PUBKEY'			=> isset($config['recaptcha_pubkey']) ? $config['recaptcha_pubkey'] : '', +				'RECAPTCHA_ERRORGET'		=> '', +				'S_RECAPTCHA_AVAILABLE'		=> self::is_available(), +				'S_CONFIRM_CODE'			=> true, +				'S_TYPE'					=> $this->type, +				'L_CONFIRM_EXPLAIN'			=> $explain, +			)); + +			return 'captcha_recaptcha.html'; +		} +	} + +	function get_demo_template($id) +	{ +		return $this->get_template(); +	} + +	function get_hidden_fields() +	{ +		$hidden_fields = array(); + +		// this is required for posting.php - otherwise we would forget about the captcha being already solved +		if ($this->solved) +		{ +			$hidden_fields['confirm_code'] = $this->code; +		} +		$hidden_fields['confirm_id'] = $this->confirm_id; +		return $hidden_fields; +	} + +	function uninstall() +	{ +		$this->garbage_collect(0); +	} + +	function install() +	{ +		return; +	} + +	function validate() +	{ +		if (!parent::validate()) +		{ +			return false; +		} +		else +		{ +			return $this->recaptcha_check_answer(); +		} +	} + +// Code from here on is based on recaptchalib.php +/* + * This is a PHP library that handles calling reCAPTCHA. + *	- Documentation and latest version + *		  http://recaptcha.net/plugins/php/ + *	- Get a reCAPTCHA API Key + *		  http://recaptcha.net/api/getkey + *	- Discussion group + *		  http://groups.google.com/group/recaptcha + * + * Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net + * AUTHORS: + *   Mike Crawford + *   Ben Maurer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +	/** +	* Submits an HTTP POST to a reCAPTCHA server +	* @param string $host +	* @param string $path +	* @param array $data +	* @param int port +	* @return array response +	*/ +	function _recaptcha_http_post($host, $path, $data, $port = 80) +	{ +		$req = $this->_recaptcha_qsencode ($data); + +		$http_request  = "POST $path HTTP/1.0\r\n"; +		$http_request .= "Host: $host\r\n"; +		$http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n"; +		$http_request .= "Content-Length: " . strlen($req) . "\r\n"; +		$http_request .= "User-Agent: reCAPTCHA/PHP/phpBB\r\n"; +		$http_request .= "\r\n"; +		$http_request .= $req; + +		$response = ''; +		if (false == ($fs = @fsockopen($host, $port, $errno, $errstr, 10))) +		{ +			trigger_error('RECAPTCHA_SOCKET_ERROR', E_USER_ERROR); +		} + +		fwrite($fs, $http_request); + +		while (!feof($fs)) +		{ +			// One TCP-IP packet +			$response .= fgets($fs, 1160); +		} +		fclose($fs); +		$response = explode("\r\n\r\n", $response, 2); + +		return $response; +	} + +	/** +	* Calls an HTTP POST function to verify if the user's guess was correct +	* @param array $extra_params an array of extra variables to post to the server +	* @return ReCaptchaResponse +	*/ +	function recaptcha_check_answer($extra_params = array()) +	{ +		global $config, $user; + +		//discard spam submissions +		if ($this->challenge == null || strlen($this->challenge) == 0 || $this->response == null || strlen($this->response) == 0) +		{ +			return $user->lang['RECAPTCHA_INCORRECT']; +		} + +		$response = $this->_recaptcha_http_post($this->recaptcha_verify_server, $this->recaptcha_verify_path, +			array( +				'privatekey'	=> $config['recaptcha_privkey'], +				'remoteip'		=> $user->ip, +				'challenge'		=> $this->challenge, +				'response'		=> $this->response +			) + $extra_params +		); + +		$answers = explode("\n", $response[1]); + +		if (trim($answers[0]) === 'true') +		{ +			$this->solved = true; +			return false; +		} +		else +		{ +			return $user->lang['RECAPTCHA_INCORRECT']; +		} +	} + +	/** +	* Encodes the given data into a query string format +	* @param $data - array of string elements to be encoded +	* @return string - encoded request +	*/ +	function _recaptcha_qsencode($data) +	{ +		$req = ''; +		foreach ($data as $key => $value) +		{ +			$req .= $key . '=' . urlencode(stripslashes($value)) . '&'; +		} + +		// Cut the last '&' +		$req = substr($req, 0, strlen($req) - 1); +		return $req; +	} +} | 
