Location: PHPKode > projects > phpBB Smith - Modifications/Hacks > Photo Visual Confirmation/root/includes/captcha/plugins/phpbb_captcha_photo_plugin.php
<?php
/*-----------------------------------------------------------------------------
	Photo Visual Confirmation - A phpBB Add-On
  ----------------------------------------------------------------------------
	phpbb_captcha_photo.php
		Captcha Plugin File
	File Version: 2.0.2
	Begun: May 29, 2007
	Last Modified: April 12, 2010
  ----------------------------------------------------------------------------
	Copyright 2009, 2010 by Jeremy Rogers.
	License: GNU General Public License v2
-----------------------------------------------------------------------------*/

/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
	exit;
}

global $table_prefix;

define('CAPTCHA_PHOTO_TABLE',	$table_prefix . 'captcha_photo');

class phpbb_captcha_photo
{
	var $current_version	= '2.0.2';
	var $confirm_id;
	var $confirm_code;
	var $code;
	var $attempts		= 0;
	var $solved			= 0;
	var $type;
	var $pass_path;
	var $fail_path;

	// Default values are for use with ACP preview before plugin is installed.
	var $width			= 100;
	var $height			= 100;
	var $rows			= 3;
	var $cols			= 3;
	var $total			= 9;

	// Demo Mode is for use with ACP preview before plugin is installed.
	var	$demo_mode		= false;
	var $filters		= true;

	var $vars			= array();
	var $notice			= array();
	var $error			= array();
	var $new_config		= array();
	var $modes			= array();

	// Short form list of image filter options
	var $imgfilter_list	= array('color', 'gblur', 'gray', 'mean', 'blur', 'smooth');

	// Maximum number of pictures allowed on one confirm prompt.
	// This is based on the size of the database field that holds the confirm code.
	// If it holds up to 8 characters (which is phpBB default), we can use up to 32 images.
	var $max		= 32;

	function phpbb_captcha_photo()
	{
		global $phpbb_root_path, $phpEx;

		if (!class_exists('captcha'))
		{
			include($phpbb_root_path . 'includes/captcha/captcha_photo.' . $phpEx);
		}
	}

	function init($type = 0)
	{
		global $config, $db, $user;

		$user->add_lang('mods/captcha_photo');

		if( !$this->is_installed() && defined('IN_ADMIN') && isset($_REQUEST['demo_mode']) )
		{
			// We're using the uninstalled ACP demo
			$this->demo_mode = true;
			if( empty($config['photo_captcha_img_path']) )
			{
				$config['photo_captcha_img_path'] = 'images/captcha/';
			}
			return;
		}

		// Get type settings
		$this->set_type($type);

		// read input
		$this->confirm_id	= request_var('confirm_id', '');
		$refresh			= request_var('refresh_vc', false) && $config['confirm_refresh'];
		$this->request_code();

		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();
		}
	}

/*	set_type
		Sets variables specific to the captcha type.
*/
	function set_type($type = 0)
	{
		global $config;

		if( $this->demo_mode )
		{
			return;
		}

		$old_type	= 0;
		if( empty($type) )
		{
			// Type was omitted, let's try to fetch it from the URL
			$type = request_var('type', 0);
		}

		if( empty($type) || !isset($config['photo_captcha_' . $type . '_rows']) )
		{
			// There is no type or we're missing data for this type.
			// The type might have been added by another modification.
			// Use default type for failsafe operation.
			$old_type			= $type;
			$type				= $config['photo_captcha_default_type'];
		}

		$this->type		= (int) $type;
		$this->width	= $config['photo_captcha_' . $this->type . '_width'];
		$this->height	= $config['photo_captcha_' . $this->type . '_height'];
		$this->rows		= $config['photo_captcha_' . $this->type . '_rows'];
		$this->cols		= $config['photo_captcha_' . $this->type . '_number'];
		$this->total	= $this->rows * $this->cols;

		// Use type from URL, if provided, for interfacing with other mods
		$type = ( !empty($old_type) ) ? $old_type : $type;
	}

	function validate()
	{
		global $user;

		$error	= '';
		if (!$this->confirm_id)
		{
			$error = $user->lang['CONFIRM_CODE_WRONG'];
		}
		else
		{
			if ($this->check_code())
			{
				// $this->delete_code(); commented out to allow posting.php to repeat the question
				$this->solved = true;
			}
			else
			{
				$error = $user->lang['CONFIRM_CODE_WRONG'];
			}
		}

		if (strlen($error))
		{
			// okay, incorrect answer. Let's ask a new question.
			$this->regenerate_code(true);
			return $error;
		}
		else
		{
			return false;
		}
	}

	function check_code()
	{
		return (strcasecmp($this->code, $this->confirm_code) === 0);
	}

// Get the confirm code details from the database
	function load_code()
	{
		global $db, $user;

		$sql = 'SELECT code, attempts, pass_set, fail_set
			FROM ' . CAPTCHA_PHOTO_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			= substr(base_convert($row['code'], 16, 2), 0, $this->total);
			$this->attempts		= $row['attempts'];
			$this->pass_path	= $row['pass_set'];
			$this->fail_path	= $row['fail_set'];
			return true;
		}

		return false;
	}

	function generate_code()
	{
		global $db, $user;

		$this->code			= substr(unique_id(), 0, 8);
		$this->confirm_id	= md5(unique_id($user->ip));
		$this->solved		= 0;
		$this->select_image_sets();

		$sql = 'INSERT INTO ' . CAPTCHA_PHOTO_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,
				'pass_set'		=> (string) $this->pass_path,
				'fail_set'		=> (string) $this->fail_path
		));
		$db->sql_query($sql);
	}

	function regenerate_code($new_attempt = false)
	{
		global $db, $user;

		$this->code = substr(unique_id(), 0, 8);
		$this->solved = 0;
		$this->select_image_sets();

		$sql = 'UPDATE ' . CAPTCHA_PHOTO_TABLE . ' SET ' . $db->sql_build_array('UPDATE', array(
				'code'			=> (string) $this->code,
				'pass_set'		=> (string) $this->pass_path,
				'fail_set'		=> (string) $this->fail_path
		));

		if( $new_attempt )
		{
			$sql .= ', attempts = attempts + 1 ';
		}

		$sql .= ' WHERE
				confirm_id = \'' . $db->sql_escape($this->confirm_id) . '\'
					AND session_id = \'' . $db->sql_escape($user->session_id) . '\'';
		$db->sql_query($sql);
	}

	function prep_path(&$path)
	{
		if( substr($path, -1) != '/' )
		{
			$path .= '/';
		}
	}

	function select_image_sets()
	{
		global $user, $config, $phpbb_root_path;

		$user->add_lang('mods/captcha_photo_sets');

		$base_path = $phpbb_root_path . $config['photo_captcha_img_path'];
		$this->prep_path($base_path);

		$options	= array();

		// Instead of running through the actual directory, let's use the
		// directories defined via the language file!
		foreach( $user->lang['PVC_IMAGE_SETS'] as $k => $v )
		{
			$name	= strtolower(str_replace('PVC_', '', $k));
			if( is_dir($base_path . $name) && $name != '.' && $name != '..' )
			{
				$options[] = $name;
			}
		}

		// TODO: Log error if there are not at least two image paths?

		// Now we need to select two random directories from the list.
		$dirs = array_rand($options, 2);
		// Randomly reverse to increase chances of alternating pass/fail sets
		// when there are a small number of sets
		if( mt_rand(0, 1) )
		{
			$dirs = array_reverse($dirs);
		}
		$this->pass_path	= $options[$dirs[0]];
		$this->fail_path	= $options[$dirs[1]];
	}

	function request_code()
	{
		$this->confirm_code	= request_var('confirm_code', '');

		if( !empty($this->confirm_code) )
		{
			return true;
		}

		// If confirm_code was not passed, we need to look for checked
		// image boxes. Each captcha type may have a different number of
		// boxes. We need to run through the whole set for this type
		// in order to build the code.
		$this->confirm_code	= '';
		for ($box=0; $box < $this->total; $box++)
		{
			if (request_var("captchabox$box", '') == 'on')
			{
				$this->confirm_code	.= '1';
			}
			else
			{
				$this->confirm_code	.= '0';
			}
		}
		return ( empty($this->confirm_code) ) ? false: true;
	}

	function &get_instance()
	{
		$instance =& new phpbb_captcha_photo();
		return $instance;
	}

	function is_available()
	{
		global $phpbb_root_path, $phpEx, $user;

		// Need language file for captcha selection list
		$user->add_lang('mods/captcha_photo');
		$available = true;

		if (!phpbb_captcha_photo::is_installed())
		{
			return false;
		}

		// We need GD
		if ( @!extension_loaded('gd') )
		{
			if (!function_exists('can_load_dll'))
			{
				include($phpbb_root_path . 'includes/functions_install.' . $phpEx);
			}

			if( !can_load_dll('gd') )
			{
				$available = can_load_dll('gd');
			}
		}

		if( !function_exists('imagecreatetruecolor') )
		{
			// We also need GD 2.x or later. All servers that have GD
			// should be using this version by now, but if I don't check
			// for it there will be one that doesn't.
			$available = false;
		}

		return $available;
	}

	function execute_demo()
	{
		global $config;

		$filter			= request_var('filter', '');
		$filter_state	= request_var('filter_on', 0);
		$preview_mode	= request_var('preview_mode', CONFIRM_REG);

		$this->build_var_list();

		foreach($this->vars as $group=>$settings)
		{
			validate_config_vars($settings['vars'], $config, $this->error);
		}

		foreach ($this->vars as $group => $settings)
		{
			if( $group == 'config' )
			{
				continue; // Skip the general configuration settings
			}
			foreach($settings['vars'] as $k=>$v)
			{
				if (strpos($k, 'legend') !== false)
				{
					continue;
				}
				$config[$k]	= $this->get_var($k);
			}
		}

		// We need to have an actual running instance of the captcha for the previews
		// We init down here to ensure we're using passed demo variables
		$this->init($preview_mode);

		if( !$this->is_installed() && defined('IN_ADMIN') && isset($_REQUEST['demo_mode']) )
		{
			$this->pass_path	= request_var('pass', '');
			$this->fail_path	= request_var('fail', '');
		}

		$captcha = new captcha($this->width, $this->height, $this->pass_path, $this->fail_path);
		define('IMAGE_OUTPUT', 1);

		if( !empty($filter) )
		{
			$captcha->execute_filter($filter, $filter_state);
		}
		else
		{
			$this->code		= substr(unique_id(), 0, 8);
			$this->code		= substr(base_convert($this->code, 16, 2), 0, $this->total);
			$captcha->execute($this->code);
		}
	}

	function execute()
	{
		if (empty($this->code))
		{
			if (!$this->load_code())
			{
				// invalid request, bail out
				return false;
			}
		}
		$captcha = new captcha($this->width, $this->height, $this->pass_path, $this->fail_path);
		define('IMAGE_OUTPUT', 1);
		$captcha->execute($this->code);
	}

	function disable_config($name, $prefix = 'photo_captcha_')
	{
		global $config;
		if( !is_array($name) )
		{
			$name = array($name);
		}
		for( $i = 0, $num = count($name) ; $i < $num ; $i++ )
		{
			$varname = $prefix . $name[$i];
			if( $config[$varname] )
			{
				set_config($varname, 0);
			}
		}
	}

	function get_template()
	{
		global $config, $user, $template;

		if ($this->is_solved())
		{
			return false;
		}
		else
		{
			$user->add_lang('mods/captcha_photo_sets');
			$params	= array(
				'mode'			=> 'confirm',
			);
			$this->output_images($params);

			// Get the correct language name for the pictures to select
			$l_pics	= $user->lang['PVC_IMAGE_SETS']['PVC_' . strtoupper($this->pass_path)];

			$template->assign_vars(array(
				'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'			=> $user->lang('PHOTO_CODE_EXPLAIN', $l_pics, '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>'),
			));

			return 'mods/captcha_photo.html';
		}
	}

	function get_demo_template($id, $params = array())
	{
		if( !$this->is_installed() )
		{
			// Uninstalled demo
			global $config;
			if( empty($config['photo_captcha_img_path']) )
			{
				$config['photo_captcha_img_path'] = 'images/captcha/';
			}
			$this->select_image_sets();
			$params['demo_mode']	= 1;
			$params['pass']			= $this->pass_path;
			$params['fail']			= $this->fail_path;
		}
		elseif( empty($this->type) )
		{
			// Installed demo
			$this->init();
		}
		$params	= array_merge($params, array(
			'captcha_demo'		=>	'1',
			'mode'				=>	'visual',
			'select_captcha'	=>	$this->get_class_name(),
			'i'					=>	$id,
		));
		$this->output_images($params);
		return 'mods/captcha_photo_acp_demo.html';
	}

	function output_images($params = array())
	{
		global $template, $phpbb_root_path, $phpbb_admin_path, $phpEx;

		$base_link = ( defined('IN_ADMIN') ) ? "{$phpbb_admin_path}index.$phpEx": "{$phpbb_root_path}ucp.$phpEx";

		$params	= array_merge($params, array(
			'confirm_id'	=> $this->confirm_id,
			'type'			=> $this->type,
		));

		for ($column = 0; $column < $this->cols; $column++)
		{
			$template->assign_block_vars('photo_col', array());
			for ($row = 0; $row < $this->rows; $row++)
			{
				$idx			= $column * $this->rows + $row;
				$params['idx']	= $idx;
				$template->assign_block_vars('photo_col.photo_row', array(
					'IMG_URL'	=> append_sid($base_link, $params),
					'IDX'		=> $idx,
					'WIDTH'		=> $this->width,
					'HEIGHT'	=> $this->height,
				));
			}
		}
	}

	function reset()
	{
		global $db, $user;

		if( $this->demo_mode )
		{
			return;
		}

		$sql = 'DELETE FROM ' . CAPTCHA_PHOTO_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 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 ' . CAPTCHA_PHOTO_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 ' . CAPTCHA_PHOTO_TABLE . '
					WHERE ' . $db->sql_in_set('session_id', $sql_in);
				$db->sql_query($sql);
			}
		}
		$db->sql_freeresult($result);
	}

	function is_solved()
	{
		if ($this->request_code() && $this->solved === 0)
		{
			$this->validate();
		}
		return (bool) $this->solved;
	}

	function get_attempt_count()
	{
		return $this->attempts;
	}

	function has_config()
	{
		return true;
	}

	function get_name()
	{
		return 'PVC';
	}

	function get_class_name()
	{
		return 'phpbb_captcha_photo';
	}

	function uninstall()
	{
		// Don't remove any configuration stuff, as that would destroy
		// all future usage. If someone really wants to get rid of it,
		// they probably know enough to do it manually.
		$this->garbage_collect(0);
	}

	function install()
	{
		global $config, $phpbb_root_path, $phpEx, $db;

		$settings	= array(
			'2.0.0'	=> array(
				array('photo_captcha_img_path',			'images/captcha/'),
				array('photo_captcha_1_rows',			'4'),
				array('photo_captcha_1_number',			'5'),
				array('photo_captcha_1_width',			'100'),
				array('photo_captcha_1_height',			'100'),
				array('photo_captcha_2_rows',			'2'),
				array('photo_captcha_2_number',			'2'),
				array('photo_captcha_2_width',			'100'),
				array('photo_captcha_2_height',			'100'),
				array('photo_captcha_3_rows',			'2'),
				array('photo_captcha_3_number',			'3'),
				array('photo_captcha_3_width',			'100'),
				array('photo_captcha_3_height',			'100'),
				array('photo_captcha_rotate',			'3'),
				array('photo_captcha_mirror',			'3'),
				array('photo_captcha_filter_color',		'1'),
				array('photo_captcha_filter_blur',		'5'),
				array('photo_captcha_filter_mean',		'3'),
				array('photo_captcha_filter_smooth',	'4'),
				array('photo_captcha_filter_gblur',		'5'),
				array('photo_captcha_default_type',		'1'),
				array('photo_captcha_wave',				'4'),
				array('photo_captcha_noise',			'4'),
				array('photo_captcha_interlace',		'4'),
				array('photo_captcha_filter_gray',		'4'),
			)
		);

		foreach( $settings as $version => $version_settings )
		{
			if( version_compare($config['photo_captcha_version'], $version, '>=') )
			{
				break;
			}

			foreach( $version_settings as $details )
			{
				$name	= $details[0];
				if( !isset($config[$name]) )
				{
					$val	= $details[1];
					$is_dyn	= ( isset($details[2]) ) ? $details[2] : 0;
					set_config($name, $val, $is_dyn);
				}
			}
		}

		if (!class_exists('phpbb_db_tools'))
		{
			include("$phpbb_root_path/includes/db/db_tools.$phpEx");
		}
		$db_tool = new phpbb_db_tools($db);

		$tables = array(
			'2.0.0'	=> array(CAPTCHA_PHOTO_TABLE)
		);

		$schemas = array(
				CAPTCHA_PHOTO_TABLE		=> array (
					'COLUMNS' => array(
						'confirm_id'	=> array('CHAR:32', ''),
						'session_id'	=> array('CHAR:32', ''),
						'confirm_type'	=> array('TINT:3', 0),
						'code'			=> array('VCHAR:20', ''),
						'attempts'		=> array('UINT', 0),
						'pass_set'		=> array('VCHAR:255', ''),
						'fail_set'		=> array('VCHAR:255', ''),
					),
					'PRIMARY_KEY'		=> array('confirm_id', 'session_id'),
					'KEYS'				=> array(
						'confirm_type'	=> array('INDEX', 'confirm_type'),
					),
				),
		);

		foreach($tables as $version => $version_settings)
		{
			if( version_compare($config['photo_captcha_version'], $version, '>=') )
			{
				break;
			}

			foreach( $version_settings as $table )
			{
				if (!$db_tool->sql_table_exists($table))
				{
					$db_tool->sql_create_table($table, $schemas[$table]);
				}
			}
		}

		// Update version number to current version
		set_config('photo_captcha_version', $this->current_version);
	}

	function is_installed()
	{
		global $db, $phpbb_root_path, $phpEx;

		if (!class_exists('phpbb_db_tools'))
		{
			include("$phpbb_root_path/includes/db/db_tools.$phpEx");
		}
		$db_tool = new phpbb_db_tools($db);

		return $db_tool->sql_table_exists(CAPTCHA_PHOTO_TABLE);
	}

	function build_var_list()
	{
		global $user;

		// Define configuration vars
		// I'm using a little hack on photo_captcha_default_type for the template
		// building. I need to use a function in this class, but phpBB's captcha
		// plugin won't allow that if using the standard 'method' setting. So I'm
		// using 'function' as a work around.
		$this->vars	= array(
			'config'		=> array(
				'title'		=> '',
				'vars'		=> array(
					'legend1'						=> 'GENERAL_OPTIONS',
					'photo_captcha_default_type'	=> array('lang' => 'PVC_DEFAULT_TYPE',	'validate' => 'string',		'type' => 'select',			'function' => array($this, 'select_default_type'),	'explain' => true),
					'photo_captcha_img_path'		=> array('lang' => 'PVC_IMG_PATH',		'validate' => 'rpath',		'type' => 'text:20:255',	'explain' => true),
				)
			),
			'filters'	=> array(
				'title'		=> 'PVC_IMAGE_TRANFORMATIONS',
				'vars'		=> array(
					'legend1'						=> 'PVC_IMAGE_TRANFORMATIONS',
				)
			),
			'instances'	=> array(
				'title'		=> 'PVC_INSTANCE_SETTINGS',
				'explain'	=> $user->lang('PVC_INSTANCE_DESC', $this->max),
				'vars'		=> array()
			)
		);

		$captcha = new captcha();

		$notice	= array();
		if( !function_exists('imagefilter')  )
		{
			$this->disable_config($this->imgfilter_list, 'photo_captcha_filter_');
			$notice[] = $user->lang['PVC_NO_FILTER'];
			$this->filters = false;
		}

		for( $i=0, $num = count($captcha->order); $i< $num; $i++ )
		{
			if( !$captcha->order[$i][1] )
			{
				continue;
			}
			$name		= $captcha->order[$i][0];
			$cfg_key	= 'photo_captcha_';

			// Rotate isn't possible unless using PHP's bundled GD library
			if( $name == 'rotate' && !function_exists('imagerotate') )
			{
				$this->disable_config($name);
				$notice[] = $user->lang['PVC_NO_ROTATE'];
				continue;
			}

			if( in_array($name, $this->imgfilter_list) )
			{
				// imagefilter was added in PHP 5, but phpBB allows PHP 4
				if( !$this->filters )
				{
					continue;
				}
				$cfg_key	.= 'filter_';
			}

			$this->vars['filters']['vars'][$cfg_key . $name] = array(
				'lang'		=> 'PVC_' . strtoupper($name),
				'validate'	=> 'int:0:50',
				'type'		=> 'text:2:2',
				'explain'	=> true
			);
		}

		$this->modes = array(
			CONFIRM_REG		=> 'PVC_REG',
			CONFIRM_LOGIN	=> 'PVC_LOGIN',
			CONFIRM_POST	=> 'PVC_POST',
		);

		foreach($this->modes as $k=>$v)
		{
			$varname					= "photo_captcha_{$k}_";
			$this->vars['instances']['vars']	= array_merge($this->vars['instances']['vars'], array(
				'legend' . $k			=> $v,
				$varname . 'rows'		=> array('lang' => 'PVC_NUMBER',	'validate' => 'int:1:32',	'type' => 'text:3:2'),
				$varname . 'number'		=> array('lang' => 'PVC_ROWS',		'validate' => 'int:1:32',	'type' => 'text:3:2'),
				$varname . 'width'		=> array('lang' => 'PVC_WIDTH',		'validate' => 'int:20',		'type' => 'text:3:3'),
				$varname . 'height'		=> array('lang' => 'PVC_HEIGHT',	'validate' => 'int:20',		'type' => 'text:3:3'),
			));
		}
	}

	function acp_page($id, &$module)
	{
		global $user, $template, $config;

		$user->add_lang('acp/board');

		$this->install();

		$form_key = 'acp_photo_captcha';
		add_form_key($form_key);

		// We need to have an actual running instance of the captcha for the previews
		$preview_mode			= request_var('preview_mode', CONFIRM_REG);
		$this->init($preview_mode);

		$this->build_var_list();

		foreach($this->vars as $group=>$settings)
		{
			validate_config_vars($settings['vars'], $this->new_config, $this->error);
		}

		$module->tpl_name		= 'mods/captcha_photo_acp';
		$module->page_title		= 'PHOTO_CAPTCHA_SETTINGS';
		$submit					= ( isset($_REQUEST['submit'])  ) ? true: false;
		$preview				= ( isset($_REQUEST['preview']) ) ? true: false;
		$this->new_config		= (isset($_REQUEST['config'])) ? utf8_normalize_nfc(request_var('config', array('' => ''), true)) : $config;

		if ($submit && !check_form_key($form_key))
		{
			$this->error[] = $user->lang['FORM_INVALID'];
		}

		// Handle submitted options
		if ($submit && empty($this->error))
		{
			foreach ($this->vars as $group => $settings)
			{
				foreach($settings['vars'] as $config_name=>$v)
				{
					if (strpos($config_name, 'legend') !== false)
					{
						$total_pics		= 0;
						$current_legend	= $v;
						continue;
					}
					$name_parts = explode('_', $config_name);
					switch( array_pop($name_parts) )
					{
						case 'rows':
							$total_pics = $this->get_var($config_name);
						break;
						case 'number':
							$total_pics *= $this->get_var($config_name);
							if( $total_pics > $this->max )
							{
								$this->error[]	= sprintf($user->lang['PVC_MAX_ERROR'], $user->lang[$current_legend], $this->max);
							}
							$name_parts[] = 'rows';
							unset($config_update[implode('_', $name_parts)]);
						break;
					}
					$config_update[$config_name] = $this->new_config[$config_name];
				}
			}

			// Update config settings if there were no errors
			if( empty($this->error) )
			{
				foreach($config_update as $k=>$v)
				{
					set_config($k, $v);
				}
				add_log('admin', 'LOG_CONFIG_VISUAL');
				trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($module->u_action));
			}
		}

		$params = array();
		// Display current config settings
		foreach ($this->vars as $group => $settings)
		{
			if( !empty($settings['title']) )
			{
				$l_explain = '';
				if (!empty($settings['explain']))
				{
					$l_explain = $user->lang($settings['explain']);
				}
				else
				{
					$l_explain = $user->lang($settings['title'] . '_EXPLAIN');
				}

				$template->assign_block_vars('options', array(
					'S_TITLE'		=> true,
					'TITLE'			=> $user->lang($settings['title']),
					'TITLE_EXPLAIN'	=> $l_explain,
				));
			}

			foreach($settings['vars'] as $config_key => $var_details)
			{
				if (!is_array($var_details) && strpos($config_key, 'legend') === false)
				{
					continue;
				}

				if (strpos($config_key, 'legend') !== false)
				{
					$template->assign_block_vars('options', array(
						'S_LEGEND'		=> true,
						'LEGEND'		=> $user->lang($var_details)
					));

					continue;
				}

				$type = explode(':', $var_details['type']);

				$l_explain = '';
				if ( !empty($var_details['explain']) && isset($var_details['lang_explain']))
				{
					$l_explain = $user->lang($var_details['lang_explain']);
				}
				elseif (!empty($var_details['explain']))
				{
					$l_explain = $user->lang($var_details['lang'] . '_EXPLAIN');
				}

				$content = build_cfg_template($type, $config_key, $this->new_config, $config_key, $var_details);

				if (empty($content))
				{
					continue;
				}

				$template->assign_block_vars('options', array(
					'KEY'			=> $config_key,
					'TITLE'			=> $user->lang($var_details['lang']),
					'S_EXPLAIN'		=> ( isset($var_details['explain']) ) ? $var_details['explain'] : false,
					'TITLE_EXPLAIN'	=> $l_explain,
					'CONTENT'		=> $content,
					'PREVIEWS'		=> ( $group == 'filters' ) ? true : false,
					'PREVIEW_ON'	=> ( $group == 'filters' ) ? $this->preview_filter($id, $config_key, $var_details['lang'], true) : '',
					'PREVIEW_OFF'	=> ( $group == 'filters' ) ? $this->preview_filter($id, $config_key, $var_details['lang'], false) : '',
				));
				if( $group == 'filters' || $group == 'instances' )
				{
					$params[$config_key] = $this->new_config[$config_key];
				}

				unset($this->vars[$group]['vars'][$config_key]);
			}
		}

		$s_hidden_fields = build_hidden_fields(array(
			'select_captcha'	=>	$this->get_class_name(),
			'mode'				=>	'visual',
			'i'					=>	$id,
			'configure'			=>	'1',
		));

		$template->assign_vars(array(
			'S_HIDDEN_FIELDS'		=> $s_hidden_fields,
			'CAPTCHA_PREVIEW'		=> $this->get_demo_template($id, $params),
			'PREVIEW'				=> $preview,
			'PREVIEW_SELECT'		=> $this->select_default_type($preview_mode),
			'ERROR_MESSAGE'			=> implode('<br /><br />', $this->error),
			'NOTICE_MESSAGE'		=> implode('<br /><br />', $this->notice),
		));
	}

	function preview_filter($id, $filter, $filter_lang, $filter_state)
	{
		global $user, $phpbb_admin_path, $phpEx;

		$parts	= explode('_', $filter);
		$filter	= array_pop($parts);
		if( !$this->filters && in_array($filter, $this->imgfilter_list) )
		{
			// Can't preview
			return '';
		}

		$params	= array(
			'captcha_demo'		=>	'1',
			'mode'				=>	'visual',
			'select_captcha'	=>	$this->get_class_name(),
			'filter'			=>	$filter,
			'filter_on'			=>	$filter_state,
			'i'					=>	$id,
			'confirm_id'		=>	$this->confirm_id,
		);

		$l_alt	= ( $filter_state ) ? $user->lang['PVC_FILTER_ON'] : $user->lang['PVC_FILTER_OFF'];
		$l_alt	= sprintf($l_alt, $user->lang[$filter_lang]);

		$filter_link	= '<img src="' . append_sid("{$phpbb_admin_path}index.$phpEx", $params) . '" width="' . $this->width . '" height="' . $this->height . '" alt="' . $l_alt . '" title="' . $l_alt . '" />';

		return $filter_link;
	}

/*	get_var
		Fetches a var from either new_config or config, depending on what's available.
		Could do this inline, but it looks messy! new_config is pulled from _REQUEST
		on form submission.

	$var
		The var name.
	$default
		Default variable value if we go to request_var(). Depreciated.
*/
	function get_var($var, $default = 0)
	{
		global $config;
		return (isset($this->new_config[$var])) ? $this->new_config[$var] : $config[$var];
	}

/*	select_default_type
		Builds a selection list of CAPTCHA prompt types.

	$selected_value
		Value passed from a form or default settings.
	$key
		Unused; present for phpBB compatibility.
*/
	function select_default_type($selected_value, $key = '')
	{
		global $user;

		$options = '';
		foreach ($this->modes as $k=>$type)
		{
			$selected = ($selected_value == $k) ? ' selected="selected"' : '';
			$options .= '<option value="' . $k . '"' . $selected . '>' . $user->lang[$type] . '</option>';
		}

		return $options;
	}
}

?>
Return current item: phpBB Smith - Modifications/Hacks