<?php
/**
 * Zenphoto default captcha handler
 *
 * @author Stephen Billard (sbillard)
 * @package plugins
 * @subpackage spam
 */

// force UTF-8 Ø

$plugin_is_filter = 5|CLASS_PLUGIN;
$plugin_description = gettext("Zenphoto captcha handler.");
$plugin_author = "Stephen Billard (sbillard)";
$plugin_disable = ($_zp_captcha->name && $_zp_captcha->name != 'zpCaptcha')?sprintf(gettext('Only one Captcha handler plugin may be enabled. <a href="#%1$s"><code>%1$s</code></a> is already enabled.'),$_zp_captcha->name):'';

$option_interface = 'zpCaptcha';

class zpCaptcha extends _zp_captcha {

	var $name='zpCaptcha';

	/**
	 * Class instantiator
	 *
	 * @return captcha
	 */
	function __construct() {
		if (OFFSET_PATH == 2) {
			setOptionDefault('zenphoto_captcha_length', 5);
			setOptionDefault('zenphoto_captcha_font_size', 18);
			setOptionDefault('zenphoto_captcha_key', sha1($_SERVER['HTTP_HOST'].'a9606420399a77387af2a4b541414ee5'.getUserIP()));
			setOptionDefault('zenphoto_captcha_string', 'abcdefghijkmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWXYZ');
		}
	}

	/**
	 * Returns array of supported options for the admin-options handler
	 *
	 * @return unknown
	 */
	function getOptionsSupported() {
		$fontlist = zp_getFonts();
		$options = array(
								gettext('Hash key') => array('key' => 'zenphoto_captcha_key', 'type' => OPTION_TYPE_TEXTBOX,
												'order'=> 2,
												'desc' => gettext('The key used in hashing the CAPTCHA string. Note: this key will change with each successful CAPTCHA verification.')),
								gettext('Allowed characters') => array('key' => 'zenphoto_captcha_string', 'type' => OPTION_TYPE_TEXTBOX,
												'order'=> 1,
												'desc' => gettext('The characters which may appear in the CAPTCHA string.')),
								gettext('CAPTCHA length') => array('key' => 'zenphoto_captcha_length', 'type' => OPTION_TYPE_RADIO,
												'order'=> 0,
												'buttons' => array(gettext('3')=>3, gettext('4')=>4, gettext('5')=>5, gettext('6')=>6),
												'desc' => gettext('The number of characters in the CAPTCHA.')),
								gettext('CAPTCHA font') => array('key' => 'zenphoto_captcha_font', 'type' => OPTION_TYPE_SELECTOR,
												'order'=> 3,
												'selections' => array_merge(array('*'.gettext('random').'*'=>'*'),$fontlist),
												'desc' => gettext('The font to use for CAPTCHA characters.')),
								gettext('CAPTCHA font size') => array('key' => 'zenphoto_captcha_font_size', 'type' => OPTION_TYPE_CLEARTEXT,
												'order'=> 3.5,
												'desc' => gettext('The size to use if the font is scalable (<em>TTF</em> and <em>Imagick</em> fonts.)')),
				'' 			=> array('key' => 'zenphoto_captcha_image', 'type' => OPTION_TYPE_CUSTOM,
												'order' => 4,
												'desc' => gettext('Sample CAPTCHA image'))
								);
		return $options;
	}

	function handleOption($key, $cv) {
		$captcha = $this->getCaptcha(NULL);
		?>
		<span id="zenphoto_captcha_image_loc"><?php echo $captcha['html']; ?></span>
		<script type="text/javascript">
			// <!-- <![CDATA[
			$(document).ready(function() {
				$('#zenphoto_captcha_font').change(function(){
					base = $('#zenphoto_captcha_image_loc').html().replaceAll('">','');
					if ((i = base.indexOf('&'))>0) {
						base = base.substring(0,i);
					}
					$('#zenphoto_captcha_image_loc').html(base+'&amp;f='+$('#zenphoto_captcha_font').val()+'">');
				});
			});
			// ]]> -->
		</script>
		<?php
	}

	/**
	 * gets (or creates) the CAPTCHA encryption key
	 *
	 * @return string
	 */
	function getCaptchaKey() {
		global $_zp_authority;
		$key = getOption('zenphoto_captcha_key');
		if (empty($key)) {
			$admin = Zenphoto_Authority::getAnAdmin(array('`user`=' => $_zp_authority->master_user, '`valid`=' => 1));
			if (is_object($admin)) {
				$key = $admin->getPass();
			} else {
				$key = 'No admin set';
			}
			$key = sha1('zenphoto'.$key.'captcha key');
			setOption('zenphoto_captcha_key', $key);
		}
		return $key;
	}

	/**
	 * Checks if a CAPTCHA string matches the CAPTCHA attached to the comment post
	 * Returns true if there is a match.
	 *
	 * @param string $code
	 * @param string $code_ok
	 * @return bool
	 */
	function checkCaptcha($code, $code_ok) {
		$captcha_len = getOption('zenphoto_captcha_length');
		$key = $this->getCaptchaKey();
		$code_cypher = sha1(bin2hex(rc4($key, trim($code))));
		$code_ok = trim($code_ok);
		if ($code_cypher != $code_ok || strlen($code) != $captcha_len) {
			return false;
		}
		query('DELETE FROM '.prefix('captcha').' WHERE `ptime`<'.(time()-3600)); // expired tickets
		$result = query('DELETE FROM '.prefix('captcha').' WHERE `hash`="'.$code_cypher.'"');
		if ($result && db_affected_rows() == 1) {
			$len = rand(0, strlen($key)-1);
			$key = sha1(substr($key, 0, $len).$code.substr($key, $len));
			setOption('zenphoto_captcha_key', $key);
			return true;
		}
		return false;
	}

	/**
	 * generates a simple captcha
	 *
	 * @return array;
	 */
	function getCaptcha($prompt) {
		parent::getCaptcha($prompt);
		$captcha_len = getOption('zenphoto_captcha_length');
		$key = $this->getCaptchaKey();
		$lettre = getOption('zenphoto_captcha_string');
		$numlettre = strlen($lettre)-1;

		$string = '';
		for ($i=0; $i < $captcha_len; $i++) {
			$string .= $lettre[rand(0,$numlettre)];
		}
		$cypher = bin2hex(rc4($key, $string));
		$code=sha1($cypher);
		query('DELETE FROM '.prefix('captcha').' WHERE `ptime`<'.(time()-3600), false);  // expired tickets
		query("INSERT INTO " . prefix('captcha') . " (ptime, hash) VALUES (" . db_quote(time()) . "," . db_quote($code) . ")", false);
		$html = '<label for="code" class="captcha_label">'.$prompt.'</label><img id="captcha" src="'.WEBPATH .'/'.ZENFOLDER.'/'.PLUGIN_FOLDER.'/zpCaptcha/c.php?i='.$cypher.'" alt="Code" />';
		$input = '<input type="text" id="code" name="code" class="captchainputbox" />';
		$hidden = '<input type="hidden" name="code_h" value="'.$code.'" />';
		return array('input'=>$input, 'html'=>$html, 'hidden'=>$hidden);
	}
}

if ($plugin_disable) {
	setOption('zp_plugin_zpCaptcha', 0);
} else {
	$_zp_captcha = new zpCaptcha();
}

?>
