Current File : /home/users/barii/public_html/finansenl.com.pl/wodki/modules/captcha/classes/Captcha.php
<?php defined('SYSPATH') OR die('No direct access.');
/**
 * Captcha abstract class.
 *
 * @package		Captcha
 * @author		Michael Lavers
 * @author		Kohana Team
 * @copyright	(c) 2008-2010 Kohana Team
 * @license		http://kohanaphp.com/license.html
 */
abstract class Captcha
{
	/**
	 * @var object Captcha singleton
	 */
	public static $instance;

	/**
	 * @var string Style-dependent Captcha driver
	 */
	protected $driver;

	/**
	 * @var array Default config values
	 */
	public static $config = array
	(
		'style'      	=> 'basic',
		'width'      	=> 150,
		'height'     	=> 50,
		'complexity' 	=> 4,
		'background' 	=> '',
		'fontpath'   	=> '',
		'fonts'      	=> array(),
		'promote'    	=> FALSE,
	);

	/**
	 * @var string The correct Captcha challenge answer
	 */
	protected $response;

	/**
	 * @var string Image resource identifier
	 */
	protected $image;

	/**
	 * @var string Image type ("png", "gif" or "jpeg")
	 */
	protected $image_type = 'png';

	/**
	 * Singleton instance of Captcha.
	 *
	 * @param string $group Config group name
	 * @return object
	 */
	public static function instance($group = 'default')
	{
		if ( ! isset(Captcha::$instance))
		{
			// Load the configuration for this group
			$config = Kohana::$config->load('captcha')->get($group);

			// Set the captcha driver class name
			$class = 'Captcha_'.ucfirst($config['style']);

			// Create a new captcha instance
			Captcha::$instance = $captcha = new $class($group);

			// Save captcha response at shutdown
			//register_shutdown_function(array($captcha, 'update_response_session'));
		}

		return Captcha::$instance;
	}

	/**
	 * Constructs a new Captcha object.
	 *
	 * @throws Kohana_Exception
	 * @param string Config group name
	 * @return void
	 */
	public function __construct($group = NULL)
	{
		// Create a singleton instance once
		empty(Captcha::$instance) and Captcha::$instance = $this;

		// No config group name given
		if ( ! is_string($group))
		{
			$group = 'default';
		}

		// Load and validate config group
		if ( ! is_array($config = Kohana::$config->load('captcha')->get($group)))
			throw new Kohana_Exception('Captcha group not defined in :group configuration',
					array(':group' => $group));

		// All captcha config groups inherit default config group
		if ($group !== 'default')
		{
			// Load and validate default config group
			if ( ! is_array($default = Kohana::$config->load('captcha')->get('default')))
				throw new Kohana_Exception('Captcha group not defined in :group configuration',
					array(':group' => 'default'));

			// Merge config group with default config group
			$config += $default;
		}

		// Assign config values to the object
		foreach ($config as $key => $value)
		{
			if (array_key_exists($key, Captcha::$config))
			{
				Captcha::$config[$key] = $value;
			}
		}

		// Store the config group name as well, so the drivers can access it
		Captcha::$config['group'] = $group;

		// If using a background image, check if it exists
		if ( ! empty($config['background']))
		{
			Captcha::$config['background'] = str_replace('\\', '/', realpath($config['background']));

			if ( ! is_file(Captcha::$config['background']))
				throw new Kohana_Exception('The specified file, :file, was not found.',
					array(':file' => Captcha::$config['background']));
		}

		// If using any fonts, check if they exist
		if ( ! empty($config['fonts']))
		{
			Captcha::$config['fontpath'] = str_replace('\\', '/', realpath($config['fontpath'])).'/';

			foreach ($config['fonts'] as $font)
			{
				if ( ! is_file(Captcha::$config['fontpath'].$font))
					throw new Kohana_Exception('The specified file, :file, was not found.',
						array(':file' => Captcha::$config['fontpath'].$font));
			}
		}

		// Generate a new challenge
		$this->response = $this->generate_challenge();
	}

	/**
	 * Update captcha response session variable.
	 *
	 * @return void
	 */
	public function update_response_session()
	{
		// Store the correct Captcha response in a session
		Session::instance()->set('captcha_response', sha1(utf8::strtoupper($this->response)));
	}

	/**
	 * Validates user's Captcha response and updates response counter.
	 *
	 * @staticvar integer $counted Captcha attempts counter
	 * @param string $response User's captcha response
	 * @return boolean
	 */
	public static function valid($response)
	{
		// Maximum one count per page load
		static $counted;

		// User has been promoted, always TRUE and don't count anymore
		if (Captcha::instance()->promoted())
			return TRUE;

		// Challenge result
		$result = (bool) (sha1(utf8::strtoupper($response)) === Session::instance()->get('captcha_response'));

		// Increment response counter
		if ($counted !== TRUE)
		{
			$counted = TRUE;

			// Valid response
			if ($result === TRUE)
			{
				Captcha::instance()->valid_count(Session::instance()->get('captcha_valid_count') + 1);
			}
			// Invalid response
			else
			{
				Captcha::instance()->invalid_count(Session::instance()->get('captcha_invalid_count') + 1);
			}
		}

		return $result;
	}

	/**
	 * Gets or sets the number of valid Captcha responses for this session.
	 *
	 * @param integer $new_count New counter value
	 * @param boolean $invalid Trigger invalid counter (for internal use only)
	 * @return integer Counter value
	 */
	public function valid_count($new_count = NULL, $invalid = FALSE)
	{
		// Pick the right session to use
		$session = ($invalid === TRUE) ? 'captcha_invalid_count' : 'captcha_valid_count';

		// Update counter
		if ($new_count !== NULL)
		{
			$new_count = (int) $new_count;

			// Reset counter = delete session
			if ($new_count < 1)
			{
				Session::instance()->delete($session);
			}
			// Set counter to new value
			else
			{
				Session::instance()->set($session, (int) $new_count);
			}

			// Return new count
			return (int) $new_count;
		}

		// Return current count
		return (int) Session::instance()->get($session);
	}

	/**
	 * Gets or sets the number of invalid Captcha responses for this session.
	 *
	 * @param integer $new_count New counter value
	 * @return integer Counter value
	 */
	public function invalid_count($new_count = NULL)
	{
		return $this->valid_count($new_count, TRUE);
	}

	/**
	 * Resets the Captcha response counters and removes the count sessions.
	 *
	 * @return void
	 */
	public function reset_count()
	{
		$this->valid_count(0);
		$this->valid_count(0, TRUE);
	}

	/**
	 * Checks whether user has been promoted after having given enough valid responses.
	 *
	 * @param integer $threshold Valid response count threshold
	 * @return boolean
	 */
	public function promoted($threshold = NULL)
	{
		// Promotion has been disabled
		if (Captcha::$config['promote'] === FALSE)
			return FALSE;

		// Use the config threshold
		if ($threshold === NULL)
		{
			$threshold = Captcha::$config['promote'];
		}

		// Compare the valid response count to the threshold
		return ($this->valid_count() >= $threshold);
	}

	/**
	 * Magically outputs the Captcha challenge.
	 *
	 * @return mixed
	 */
	public function __toString()
	{
		return $this->render(TRUE);
	}

	/**
	 * Returns the image type.
	 *
	 * @param string $filename Filename
	 * @return string|boolean Image type ("png", "gif" or "jpeg")
	 */
	public function image_type($filename)
	{
		switch (strtolower(substr(strrchr($filename, '.'), 1)))
		{
			case 'png':
				return 'png';

			case 'gif':
				return 'gif';

			case 'jpg':
			case 'jpeg':
				// Return "jpeg" and not "jpg" because of the GD2 function names
				return 'jpeg';

			default:
				return FALSE;
		}
	}

	/**
	 * Creates an image resource with the dimensions specified in config.
	 * If a background image is supplied, the image dimensions are used.
	 *
	 * @throws Kohana_Exception If no GD2 support
	 * @param string $background Path to the background image file
	 * @return void
	 */
	public function image_create($background = NULL)
	{
		// Check for GD2 support
		if ( ! function_exists('imagegd2'))
			throw new Kohana_Exception('captcha.requires_GD2');

		// Create a new image (black)
		$this->image = imagecreatetruecolor(Captcha::$config['width'], Captcha::$config['height']);

		// Use a background image
		if ( ! empty($background))
		{
			// Create the image using the right function for the filetype
			$function = 'imagecreatefrom'.$this->image_type($background);
			$this->background_image = $function($background);

			// Resize the image if needed
			if (imagesx($this->background_image) !== Captcha::$config['width']
			    or imagesy($this->background_image) !== Captcha::$config['height'])
			{
				imagecopyresampled
				(
					$this->image, $this->background_image, 0, 0, 0, 0,
					Captcha::$config['width'], Captcha::$config['height'],
					imagesx($this->background_image), imagesy($this->background_image)
				);
			}

			// Free up resources
			imagedestroy($this->background_image);
		}
	}

	/**
	 * Fills the background with a gradient.
	 *
	 * @param resource $color1 GD image color identifier for start color
	 * @param resource $color2 GD image color identifier for end color
	 * @param string $direction Direction: 'horizontal' or 'vertical', 'random' by default
	 * @return void
	 */
	public function image_gradient($color1, $color2, $direction = NULL)
	{
		$directions = array('horizontal', 'vertical');

		// Pick a random direction if needed
		if ( ! in_array($direction, $directions))
		{
			$direction = $directions[array_rand($directions)];

			// Switch colors
			if (mt_rand(0, 1) === 1)
			{
				$temp = $color1;
				$color1 = $color2;
				$color2 = $temp;
			}
		}

		// Extract RGB values
		$color1 = imagecolorsforindex($this->image, $color1);
		$color2 = imagecolorsforindex($this->image, $color2);

		// Preparations for the gradient loop
		$steps = ($direction === 'horizontal') ? Captcha::$config['width'] : Captcha::$config['height'];

		$r1 = ($color1['red'] - $color2['red']) / $steps;
		$g1 = ($color1['green'] - $color2['green']) / $steps;
		$b1 = ($color1['blue'] - $color2['blue']) / $steps;

		if ($direction === 'horizontal')
		{
			$x1 =& $i;
			$y1 = 0;
			$x2 =& $i;
			$y2 = Captcha::$config['height'];
		}
		else
		{
			$x1 = 0;
			$y1 =& $i;
			$x2 = Captcha::$config['width'];
			$y2 =& $i;
		}

		// Execute the gradient loop
		for ($i = 0; $i <= $steps; $i++)
		{
			$r2 = $color1['red'] - floor($i * $r1);
			$g2 = $color1['green'] - floor($i * $g1);
			$b2 = $color1['blue'] - floor($i * $b1);
			$color = imagecolorallocate($this->image, $r2, $g2, $b2);

			imageline($this->image, $x1, $y1, $x2, $y2, $color);
		}
	}

	/**
	 * Returns the img html element or outputs the image to the browser.
	 *
	 * @param boolean $html Output as HTML
	 * @return mixed HTML, string or void
	 */
	public function image_render($html)
	{
		// Output html element
		if ($html === TRUE)
			return '<img src="'.url::site('captcha/'.Captcha::$config['group']).'" width="'.Captcha::$config['width'].'" height="'.Captcha::$config['height'].'" alt="Captcha" class="captcha" />';

		// Send the correct HTTP header
        Request::factory()->headers['Content-Type'] = 'image/'.$this->image_type;
        Request::factory()->headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0';
        Request::factory()->headers['Pragma'] = 'no-cache';
        Request::factory()->headers['Connection'] = 'close';

		// Pick the correct output function
		$function = 'image'.$this->image_type;
		$function($this->image);

		// Free up resources
		imagedestroy($this->image);
	}

	/* DRIVER METHODS */

	/**
	 * Generate a new Captcha challenge.
	 *
	 * @return string The challenge answer
	 */
	abstract public function generate_challenge();

	/**
	 * Output the Captcha challenge.
	 *
	 * @param boolean $html Render output as HTML
	 * @return mixed
	 */
	abstract public function render($html = TRUE);

} // End Captcha Class