Location: PHPKode > projects > Viscacha > classes/graphic/class.veriword.php
<?php
/*
	Viscacha - A bulletin board solution for easily managing your content
	Copyright (C) 2004-2009  The Viscacha Project

	Author: Matthias Mohr (et al.)
	Publisher: The Viscacha Project, http://www.viscacha.org
	Start Date: May 22, 2004

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

if (defined('VISCACHA_CORE') == false) { die('Error: Hacking Attempt'); }

class VeriWord {

	var $datasource = 'data/captcha.php';
	var $chars = "23456789ABCDEFGHJKLMNPRSTUVWXYZ";
	var $dir_fonts = './classes/fonts/';
	var $dir_noises = "./classes/graphic/noises/";
	var $dimensions;
	var $session;
	var $settings;

	function VeriWord() {
		global $config;
		$this->session = null;
		$this->dimensions = array(
			'w' => $config['botgfxtest_width'],
			'h' => $config['botgfxtest_height']
		);
		$this->settings = array();
	}

	function check() {
		global $gpc;
		$this->session = array(
			'word' => $gpc->get('response'),
			'id' => $gpc->get('challenge')
		);

		if (empty($this->session['id']) || empty($this->session['word'])) {
			return CAPTCHA_FAILURE;
		}

		$lines = file($this->datasource);
		$lines = array_map('trim', $lines);
		foreach ($lines as $row) {
			if (empty($row)) {
				continue;
			}
			$data = explode("\t", $row);
			if ($data[0] == $this->session['id'] && strcasecmp($data[2], $this->session['word']) == 0){
				return CAPTCHA_OK;
			}
		}
		return CAPTCHA_MISTAKE;
	}

	function generateCode($tabindex = 0) {
		global $tpl;
		$session = $this->_newSession();
		$data = array(
			'session' => $session,
			'tabindex' => $tabindex,
			'width' => $this->dimensions['w'],
			'height' => $this->dimensions['h']
		);
		$tpl->globalvars($data);
		return $tpl->parse('main/veriword');
	}

	function makeImage($session_expired_lang = 'Session Error<br>Refresh the Page') {
		global $config, $gpc;
		$challenge = $gpc->get('challenge');
		$this->settings['colortext'] = (bool) $config['botgfxtest_colortext'];
		$this->settings['filter'] = (bool) $config['botgfxtest_filter'];
		$jpeg_quality = (int) $config['botgfxtest_quality'];
		$format = ($config['botgfxtest_format'] == 'png') ? 'PNG' : 'JPEG';
		if ((ImageTypes() & constant("IMG_{$format}")) == false) {
			$format = ($format == 'PNG') ? 'JPEG' : 'PNG';
		}

		$lines = file($this->datasource);
		$lines = array_map('trim', $lines);
		foreach ($lines as $row) {
			if (empty($row)) {
				continue;
			}
			$data = explode("\t", $row);
			if ($data[0] == $challenge){
				$this->session = array(
					'word' => $data[2],
					'id' => $data[1]
				);
				break;
			}
		}
		if (empty($this->session['word'])) {
			$this->_errorImage($session_expired_lang);
		}

		// Generate the image
		$im = $this->_drawImage();

		send_nocache_header();
		switch($format){
			case 'PNG' :
				header("Content-type: image/png");
				imagepng($im);
			break;
			default:
				header("Content-type: image/jpeg");
				imagejpeg($im, '', $jpeg_quality);
		}
		imagedestroy($im);
	}

	function getError() {
		return null;
	}

	function _errorImage($text) {
		require('classes/graphic/class.text2image.php');
		$img = new text2image();
		$img->prepare($text, 0, 8);
		$img->build(4);
		$img->output();
	}

	function _newSession() {
		global $filesystem;

		$id = md5(microtime());
		$word = '';
		for ($i=1; $i <= rand(5,6); $i++) {
			$word .= substr($this->chars, mt_rand(0,strlen($this->chars)-1), 1);
		}
		$this->session = compact("word", "id");


		$time = time();
		$limit = $time-6*60*60; // 6h Zeit

		$lines = file($this->datasource);
		$lines = array_map('trim', $lines);

		$save = array("{$id}\t{$time}\t{$word}");
		foreach ($lines as $row) {
			if (empty($row)) {
				continue;
			}
			$data = explode("\t", $row);
			if (isset($data[1]) && $data[1] > $limit){
				$save[] = $row;
			}
		}

		$filesystem->file_put_contents($this->datasource, implode("\n", $save));

		return $this->session;
	}

	function _getFont() {
		$pre = 'captcha_';
		$ext = 'ttf';
		$fonts = array();
		$handle = opendir($this->dir_fonts);
		while ($file = readdir($handle)) {
			if ($file != "." && $file != ".." && !is_dir($this->dir_fonts.$file)) {
				$info = pathinfo($this->dir_fonts.$file);
				$prefix = substr($info['basename'], 0, strlen($pre));
				if (strtolower($info['extension']) == $ext && $prefix == $pre) {
					$fonts[] = $info['basename'];
				}
			}
		}
		if (count($fonts) == 0) {
			return $this->dir_fonts.'trebuchet.ttf';
		}
		else {
			$key = mt_rand(0, count($fonts)-1);
			return $this->dir_fonts.$fonts[$key];
		}
	}

	function _getNoise() {
		$ext = array('png', 'jpeg', 'jpg');
		$noises = array();
		$handle = opendir($this->dir_noises);
		while ($file = readdir($handle)) {
			if ($file != "." && $file != ".." && !is_dir($this->dir_noises.$file)) {
				$info = pathinfo($this->dir_noises.$file);
				if (in_array(strtolower($info['extension']), $ext)) {
					$noises[] = $info['basename'];
				}
			}
		}
		$key = mt_rand(0, count($noises)-1);
		return $this->dir_noises.$noises[$key];
	}

	function _imagecreate($w, $h) {
		$im = @imagecreatetruecolor($w, $h);
		if (!$im) {
			$im = imagecreate($w, $h);
		}
		return $im;
	}

	function _drawImage() {
		// Shorter use of dimensions
		extract($this->dimensions);

		/* get the noise image file*/
		$img_file = $this->_getNoise();
		if($img_file) {
			// Get noise file from stock
			$im_noise = @imagecreatefromjpeg($img_file);
		}
		else {
			// No noise file found, create a random noise
			$im_noise = $this->_imagecreate($w, $h);
			$bg_color = imagecolorallocate($im_noise, 255, 255, 255);
			imagefill($im_noise, 0, 0, $bg_color);

			for ($i = 0; $i < $h; $i++) {
				$line_color = imagecolorallocate($im_noise, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
				imagesetthickness($im_noise, mt_rand(1, 5));
				imageline(
					$im_noise,
					mt_rand(0, 30), // x1
					$i + mt_rand(-10, 10), // y1
					$w - mt_rand(0, 30), // x2
					$i + mt_rand(-10, 10), // y2
					$line_color
				);
			}
		}

		$im = $this->_imagecreate($w, $h);

		// Resize noise
		imagecopyresampled ($im, $im_noise, 0, 0, 0, 0, $w, $h, imagesx($im_noise), imagesy($im_noise));
		imagedestroy($im_noise);
		// Insert Text
		$im_text = $this->_draw_text();
		imagecopymerge ($im, $im_text, 0, 0, 0, 0, $w, $h, mt_rand(70,90));
		imagedestroy($im_text);

		return $im;
	}

	function _draw_text() {
		$text_font = $this->_getFont();
		$text_angle = mt_rand(-15,15);
		if ($text_angle > 8) {
			$spacing = mt_rand(3,8);
		}
		else {
			$spacing = mt_rand(0,5);
		}

		$text_size = $this->dimensions['h'];

		$box = $this->_imagettfbbox($text_size, $text_angle, $text_font, $this->session['word'], $spacing);

		$text_width = $this->_math_diff($box[2], $box[0]);
		$text_height = $this->_math_diff($box[5], $box[3]);
		$margin = ceil( ($text_width/strlen($this->session['word'])) * 0.5);

		$im_string = $this->_imagecreate( ceil($text_width + $margin*2), ceil($text_height + $margin*2) );

		$bg_color = imagecolorallocate ($im_string, 255, 255, 255);
		if ($this->settings['colortext']) {
			$color_array	= array();
			$color_array[]  = array(mt_rand(200,255), mt_rand(0,50), mt_rand(0,50)); // Rot
			$color_array[]  = array(mt_rand(0,50), mt_rand(200,255), mt_rand(0,50)); // Grün
			$color_array[]  = array(mt_rand(0,50), mt_rand(0,50), mt_rand(200,255)); // Blau
			$color_array[]  = array(mt_rand(200,255), mt_rand(0,50), mt_rand(200,255)); // Pink/Violett
			$color_array[]  = array(mt_rand(0,50), mt_rand(170,230), mt_rand(170,230)); // Türkis
			$color_array[]  = array(mt_rand(0,50), mt_rand(0,50), mt_rand(0,50)); // Grey
			$color_array[]  = array(mt_rand(150,200), mt_rand(40,150), mt_rand(0,40)); // Braun-ähnlich

			$text_color = array();
			$entries = strlen($this->session['word']);
			for($i = 0; $i < $entries; $i++) {
				$key = array_rand($color_array);
				$rgb = $color_array[$key];
				$text_color[] = imagecolorallocate ($im_string, $rgb[0], $rgb[1], $rgb[2]);
			}
		}
		else {
			$text_color = array();
			$entries = strlen($this->session['word']);
			for($i = 0; $i < $entries; $i++) {
				$rgb = mt_rand(0,50);
				$text_color[] = imagecolorallocate ($im_string, $rgb, $rgb, $rgb);
			}
		}

		imagefill($im_string, 0, 0, $bg_color);

		$this->_imagettftext($im_string, ceil($text_size*0.9), $text_angle, $margin, $margin + $text_height, $text_color, $text_font, $this->session['word'], $spacing);

		if ($this->settings['filter']) {
			$im_string = $this->_wave($im_string, mt_rand(2,8), true);
		}

		imagecolortransparent($im_string, $bg_color);

		$im_text = $this->_imagecreate($this->dimensions['w'], $this->dimensions['h']);
		imagecopyresampled ($im_text, $im_string, 0, 0, 0, 0, $this->dimensions['w'], $this->dimensions['h'], ceil($text_width+$margin*2), ceil($text_height+$margin*2));

		imagedestroy($im_string);

		return $im_text;
	}

	function _imagettftext($im, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0) {
		$numchar = strlen($text);
		$w = 0;
		for($i = 0; $i < $numchar; $i++) {
			   $char = substr($text, $i, 1);

			if (is_array($color)) {
				$c = array_pop($color);
			}
			else {
				$c = $color;
			}
			imagettftext($im, $size, $angle, ($x + $w + ($i * $spacing)), $y, $c, $font, $char);

			$width = $this->_imagettfbbox($size, $angle, $font, $char);
			$w = $w + $width[2];
		}
	}

	function _imagettfbbox($size, $angle, $font, $text, $spacing = 0) {
		// Get the boundingbox from imagettfbbox(), which is correct when angle is 0
		$bbox = imagettfbbox($size, 0, $font, $text);
		if ($angle == 0) {
			return $bbox;
		}

		// Rotate the boundingbox
		$angle = pi() * 2 - $angle * pi() * 2 / 360;
		for ($i=0; $i<4; $i++) {
			$x = $bbox[$i * 2];
			$y = $bbox[$i * 2 + 1];
			$bbox[$i * 2] = cos($angle) * $x - sin($angle) * $y;
			$bbox[$i * 2 + 1] = sin($angle) * $x + cos($angle) * $y;
		}

		$bbox[2] += (strlen($text)-1)*$spacing;
		$bbox[4] += (strlen($text)-1)*$spacing;

		return $bbox;
	}

	function _math_diff($x1, $x2) {
		$max = max($x1, $x2);
		$min = min($x1, $x2);
		$diff = $max-$min;
		if ($diff < 0) {
			$diff = $diff * (-1);
		}
		return $diff;
	}

	/**
	* Apply a wave filter to an image
	*
	* @param	image	 image			  Image  to convert
	* @param	int		   wave			   Amount of wave to apply
	* @param	bool	randirection	Randomize direction of wave
	*
	* @return	 image
	*/
	function _wave(&$image, $wave = 10, $randirection = true) {
		$image_width = imagesx($image);
		$image_height = imagesy($image);

		$temp = $this->_imagecreate($image_width, $image_height);

		if ($randirection) {
			$direction = (mt_rand(0, 1) == 1) ? true : false;
		}

		for ($x = 0; $x < $image_width; $x++) {
			for ($y = 0; $y < $image_height; $y++) {

				$xo = $wave * sin(2 * 3.1415 * $y / 128);
				$yo = $wave * cos(2 * 3.1415 * $x / 128);

				if ($direction) {
					$newx = $x - $xo;
					$newy = $y - $yo;
				}
				else {
					$newx = $x + $xo;
					$newy = $y + $yo;
				}

				if (($newx > 0 AND $newx < $image_width) AND ($newy > 0 AND $newy < $image_height)) {
					$index = imagecolorat($image, $newx, $newy);
					$colors = imagecolorsforindex($image, $index);
					$color = imagecolorresolve($temp, $colors['red'], $colors['green'], $colors['blue']);
				}
				else {
					$color = imagecolorresolve($temp, 255, 255, 255);
				}

				imagesetpixel($temp, $x, $y, $color);
			}
		}

		return $temp;
	}
}
?>
Return current item: Viscacha