PageRenderTime 59ms CodeModel.GetById 2ms app.highlight 47ms RepoModel.GetById 1ms app.codeStats 1ms

/php/Sources/Subs-Graphics.php

https://github.com/dekoza/openshift-smf-2.0.7
PHP | 1045 lines | 696 code | 152 blank | 197 comment | 183 complexity | 4ab72c96fcd60f808d8a91dc95c57ab5 MD5 | raw file
   1<?php
   2
   3/**
   4 * Simple Machines Forum (SMF)
   5 *
   6 * @package SMF
   7 * @author Simple Machines http://www.simplemachines.org
   8 * @copyright 2011 Simple Machines
   9 * @license http://www.simplemachines.org/about/smf/license.php BSD
  10 *
  11 * @version 2.0
  12 */
  13
  14// TrueType fonts supplied by www.LarabieFonts.com
  15
  16if (!defined('SMF'))
  17	die('Hacking attempt...');
  18
  19/*	This whole file deals almost exclusively with handling avatars,
  20	specifically uploaded ones.  It uses, for gifs at least, Gif Util... for
  21	more information on that, please see its website, shown above.  The other
  22	functions are as follows:
  23
  24	bool downloadAvatar(string url, int id_member, int max_width,
  25			int max_height)
  26		- downloads file from url and stores it locally for avatar use
  27		  by id_member.
  28		- supports GIF, JPG, PNG, BMP and WBMP formats.
  29		- detects if GD2 is available.
  30		- if GIF support isn't present in GD, handles GIFs with gif_loadFile()
  31		  and gif_outputAsPng().
  32		- uses resizeImageFile() to resize to max_width by max_height,
  33		  and saves the result to a file.
  34		- updates the database info for the member's avatar.
  35		- returns whether the download and resize was successful.
  36
  37	bool createThumbnail(string source, int max_width, int max_height)
  38		- create a thumbnail of the given source.
  39		- uses the resizeImageFile function to achieve the resize.
  40		- returns whether the thumbnail creation was successful.
  41
  42	bool reencodeImage(string fileName, int preferred_format = 0)
  43		- creates a copy of the file at the same location as fileName.
  44		- the file would have the format preferred_format if possible,
  45		  otherwise the default format is jpeg.
  46		- makes sure that all non-essential image contents are disposed.
  47		- returns true on success, false on failure.
  48
  49	bool checkImageContents(string fileName, bool extensiveCheck = false)
  50		- searches through the file to see if there's non-binary content.
  51		- if extensiveCheck is true, searches for asp/php short tags as well.
  52		- returns true on success, false on failure.
  53
  54	bool checkGD()
  55		- sets a global $gd2 variable needed by some functions to determine
  56		  whetehr the GD2 library is present.
  57		- returns whether or not GD1 is available.
  58
  59	void resizeImageFile(string source, string destination,
  60			int max_width, int max_height, int preferred_format = 0)
  61		- resizes an image from a remote location or a local file.
  62		- puts the resized image at the destination location.
  63		- the file would have the format preferred_format if possible,
  64		  otherwise the default format is jpeg.
  65		- returns whether it succeeded.
  66
  67	void resizeImage(resource src_img, string destination_filename,
  68			int src_width, int src_height, int max_width, int max_height,
  69			int preferred_format)
  70		- resizes src_img proportionally to fit within max_width and
  71		  max_height limits if it is too large.
  72		- if GD2 is present, it'll use it to achieve better quality.
  73		- saves the new image to destination_filename.
  74		- saves as preferred_format if possible, default is jpeg.
  75
  76	void imagecopyresamplebicubic(resource dest_img, resource src_img,
  77			int dest_x, int dest_y, int src_x, int src_y, int dest_w,
  78			int dest_h, int src_w, int src_h)
  79		- used when imagecopyresample() is not available.
  80
  81	resource gif_loadFile(string filename, int animation_index)
  82		- loads a gif file with the Yamasoft GIF utility class.
  83		- returns a new GD image.
  84
  85	bool gif_outputAsPng(resource gif, string destination_filename,
  86			int bgColor = -1)
  87		- writes a gif file to disk as a png file.
  88		- returns whether it was successful or not.
  89
  90	bool imagecreatefrombmp(string filename)
  91		- is set only if it doesn't already exist (for forwards compatiblity.)
  92		- only supports uncompressed bitmaps.
  93		- returns an image identifier representing the bitmap image obtained
  94		  from the given filename.
  95
  96	bool showCodeImage(string code)
  97		- show an image containing the visual verification code for registration.
  98		- requires the GD extension.
  99		- uses a random font for each letter from default_theme_dir/fonts.
 100		- outputs a gif or a png (depending on whether gif ix supported).
 101		- returns false if something goes wrong.
 102
 103	bool showLetterImage(string letter)
 104		- show a letter for the visual verification code.
 105		- alternative function for showCodeImage() in case GD is missing.
 106		- includes an image from a random sub directory of
 107		  default_theme_dir/fonts.
 108*/
 109
 110function downloadAvatar($url, $memID, $max_width, $max_height)
 111{
 112	global $modSettings, $sourcedir, $smcFunc;
 113
 114	$ext = !empty($modSettings['avatar_download_png']) ? 'png' : 'jpeg';
 115	$destName = 'avatar_' . $memID . '_' . time() . '.' . $ext;
 116
 117	// Just making sure there is a non-zero member.
 118	if (empty($memID))
 119		return false;
 120
 121	require_once($sourcedir . '/ManageAttachments.php');
 122	removeAttachments(array('id_member' => $memID));
 123
 124	$id_folder = !empty($modSettings['currentAttachmentUploadDir']) ? $modSettings['currentAttachmentUploadDir'] : 1;
 125	$avatar_hash = empty($modSettings['custom_avatar_enabled']) ? getAttachmentFilename($destName, false, null, true) : '';
 126	$smcFunc['db_insert']('',
 127		'{db_prefix}attachments',
 128		array(
 129			'id_member' => 'int', 'attachment_type' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-255', 'fileext' => 'string-8', 'size' => 'int',
 130			'id_folder' => 'int',
 131		),
 132		array(
 133			$memID, empty($modSettings['custom_avatar_enabled']) ? 0 : 1, $destName, $avatar_hash, $ext, 1,
 134			$id_folder,
 135		),
 136		array('id_attach')
 137	);
 138	$attachID = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach');
 139	// Retain this globally in case the script wants it.
 140	$modSettings['new_avatar_data'] = array(
 141		'id' => $attachID,
 142		'filename' => $destName,
 143		'type' => empty($modSettings['custom_avatar_enabled']) ? 0 : 1,
 144	);
 145
 146	$destName = (empty($modSettings['custom_avatar_enabled']) ? (is_array($modSettings['attachmentUploadDir']) ? $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']] : $modSettings['attachmentUploadDir']) : $modSettings['custom_avatar_dir']) . '/' . $destName . '.tmp';
 147
 148	// Resize it.
 149	if (!empty($modSettings['avatar_download_png']))
 150		$success = resizeImageFile($url, $destName, $max_width, $max_height, 3);
 151	else
 152		$success = resizeImageFile($url, $destName, $max_width, $max_height);
 153
 154	// Remove the .tmp extension.
 155	$destName = substr($destName, 0, -4);
 156
 157	if ($success)
 158	{
 159		// Walk the right path.
 160		if (!empty($modSettings['currentAttachmentUploadDir']))
 161		{
 162			if (!is_array($modSettings['attachmentUploadDir']))
 163				$modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
 164			$path = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
 165		}
 166		else
 167			$path = $modSettings['attachmentUploadDir'];
 168
 169		// Remove the .tmp extension from the attachment.
 170		if (rename($destName . '.tmp', empty($avatar_hash) ? $destName : $path . '/' . $attachID . '_' . $avatar_hash))
 171		{
 172			$destName = empty($avatar_hash) ? $destName : $path . '/' . $attachID . '_' . $avatar_hash;
 173			list ($width, $height) = getimagesize($destName);
 174			$mime_type = 'image/' . $ext;
 175
 176			// Write filesize in the database.
 177			$smcFunc['db_query']('', '
 178				UPDATE {db_prefix}attachments
 179				SET size = {int:filesize}, width = {int:width}, height = {int:height},
 180					mime_type = {string:mime_type}
 181				WHERE id_attach = {int:current_attachment}',
 182				array(
 183					'filesize' => filesize($destName),
 184					'width' => (int) $width,
 185					'height' => (int) $height,
 186					'current_attachment' => $attachID,
 187					'mime_type' => $mime_type,
 188				)
 189			);
 190			return true;
 191		}
 192		else
 193			return false;
 194	}
 195	else
 196	{
 197		$smcFunc['db_query']('', '
 198			DELETE FROM {db_prefix}attachments
 199			WHERE id_attach = {int:current_attachment}',
 200			array(
 201				'current_attachment' => $attachID,
 202			)
 203		);
 204
 205		@unlink($destName . '.tmp');
 206		return false;
 207	}
 208}
 209
 210function createThumbnail($source, $max_width, $max_height)
 211{
 212	global $modSettings;
 213
 214	$destName = $source . '_thumb.tmp';
 215
 216	// Do the actual resize.
 217	if (!empty($modSettings['attachment_thumb_png']))
 218		$success = resizeImageFile($source, $destName, $max_width, $max_height, 3);
 219	else
 220		$success = resizeImageFile($source, $destName, $max_width, $max_height);
 221
 222	// Okay, we're done with the temporary stuff.
 223	$destName = substr($destName, 0, -4);
 224
 225	if ($success && @rename($destName . '.tmp', $destName))
 226		return true;
 227	else
 228	{
 229		@unlink($destName . '.tmp');
 230		@touch($destName);
 231		return false;
 232	}
 233}
 234
 235function reencodeImage($fileName, $preferred_format = 0)
 236{
 237	// There is nothing we can do without GD, sorry!
 238	if (!checkGD())
 239		return false;
 240
 241	if (!resizeImageFile($fileName, $fileName . '.tmp', null, null, $preferred_format))
 242	{
 243		if (file_exists($fileName . '.tmp'))
 244			unlink($fileName . '.tmp');
 245
 246		return false;
 247	}
 248
 249	if (!unlink($fileName))
 250		return false;
 251
 252	if (!rename($fileName . '.tmp', $fileName))
 253		return false;
 254
 255	return true;
 256}
 257
 258function checkImageContents($fileName, $extensiveCheck = false)
 259{
 260	$fp = fopen($fileName, 'rb');
 261	if (!$fp)
 262		fatal_lang_error('attach_timeout');
 263
 264	$prev_chunk = '';
 265	while (!feof($fp))
 266	{
 267		$cur_chunk = fread($fp, 8192);
 268
 269		// Though not exhaustive lists, better safe than sorry.
 270		if (!empty($extensiveCheck))
 271		{
 272			// Paranoid check. Some like it that way.
 273			if (preg_match('~(iframe|\\<\\?|\\<%|html|eval|body|script\W|[CF]WS[\x01-\x0C])~i', $prev_chunk . $cur_chunk) === 1)
 274			{
 275				fclose($fp);
 276				return false;
 277			}
 278		}
 279		else
 280		{
 281			// Check for potential infection
 282			if (preg_match('~(iframe|html|eval|body|script\W|[CF]WS[\x01-\x0C])~i', $prev_chunk . $cur_chunk) === 1)
 283			{
 284				fclose($fp);
 285				return false;
 286			}
 287		}
 288		$prev_chunk = $cur_chunk;
 289	}
 290	fclose($fp);
 291
 292	return true;
 293}
 294
 295function checkGD()
 296{
 297	global $gd2;
 298
 299	// Check to see if GD is installed and what version.
 300	if (($extensionFunctions = get_extension_funcs('gd')) === false)
 301		return false;
 302
 303	// Also determine if GD2 is installed and store it in a global.
 304	$gd2 = in_array('imagecreatetruecolor', $extensionFunctions) && function_exists('imagecreatetruecolor');
 305
 306	return true;
 307}
 308
 309function resizeImageFile($source, $destination, $max_width, $max_height, $preferred_format = 0)
 310{
 311	global $sourcedir;
 312
 313	// Nothing to do without GD
 314	if (!checkGD())
 315		return false;
 316
 317	static $default_formats = array(
 318		'1' => 'gif',
 319		'2' => 'jpeg',
 320		'3' => 'png',
 321		'6' => 'bmp',
 322		'15' => 'wbmp'
 323	);
 324
 325	require_once($sourcedir . '/Subs-Package.php');
 326	@ini_set('memory_limit', '90M');
 327
 328	$success = false;
 329
 330	// Get the image file, we have to work with something after all
 331	$fp_destination = fopen($destination, 'wb');
 332	if ($fp_destination && substr($source, 0, 7) == 'http://')
 333	{
 334		$fileContents = fetch_web_data($source);
 335
 336		fwrite($fp_destination, $fileContents);
 337		fclose($fp_destination);
 338
 339		$sizes = @getimagesize($destination);
 340	}
 341	elseif ($fp_destination)
 342	{
 343		$sizes = @getimagesize($source);
 344
 345		$fp_source = fopen($source, 'rb');
 346		if ($fp_source !== false)
 347		{
 348			while (!feof($fp_source))
 349				fwrite($fp_destination, fread($fp_source, 8192));
 350			fclose($fp_source);
 351		}
 352		else
 353			$sizes = array(-1, -1, -1);
 354		fclose($fp_destination);
 355	}
 356	// We can't get to the file.
 357	else
 358		$sizes = array(-1, -1, -1);
 359
 360	// Gif? That might mean trouble if gif support is not available.
 361	if ($sizes[2] == 1 && !function_exists('imagecreatefromgif') && function_exists('imagecreatefrompng'))
 362	{
 363		// Download it to the temporary file... use the special gif library... and save as png.
 364		if ($img = @gif_loadFile($destination) && gif_outputAsPng($img, $destination))
 365			$sizes[2] = 3;
 366	}
 367
 368	// A known and supported format?
 369	if (isset($default_formats[$sizes[2]]) && function_exists('imagecreatefrom' . $default_formats[$sizes[2]]))
 370	{
 371		$imagecreatefrom = 'imagecreatefrom' . $default_formats[$sizes[2]];
 372		if ($src_img = @$imagecreatefrom($destination))
 373		{
 374			resizeImage($src_img, $destination, imagesx($src_img), imagesy($src_img), $max_width === null ? imagesx($src_img) : $max_width, $max_height === null ? imagesy($src_img) : $max_height, true, $preferred_format);
 375			$success = true;
 376		}
 377	}
 378
 379	return $success;
 380}
 381
 382function resizeImage($src_img, $destName, $src_width, $src_height, $max_width, $max_height, $force_resize = false, $preferred_format = 0)
 383{
 384	global $gd2, $modSettings;
 385
 386	// Without GD, no image resizing at all.
 387	if (!checkGD())
 388		return false;
 389
 390	$success = false;
 391
 392	// Determine whether to resize to max width or to max height (depending on the limits.)
 393	if (!empty($max_width) || !empty($max_height))
 394	{
 395		if (!empty($max_width) && (empty($max_height) || $src_height * $max_width / $src_width <= $max_height))
 396		{
 397			$dst_width = $max_width;
 398			$dst_height = floor($src_height * $max_width / $src_width);
 399		}
 400		elseif (!empty($max_height))
 401		{
 402			$dst_width = floor($src_width * $max_height / $src_height);
 403			$dst_height = $max_height;
 404		}
 405
 406		// Don't bother resizing if it's already smaller...
 407		if (!empty($dst_width) && !empty($dst_height) && ($dst_width < $src_width || $dst_height < $src_height || $force_resize))
 408		{
 409			// (make a true color image, because it just looks better for resizing.)
 410			if ($gd2)
 411			{
 412				$dst_img = imagecreatetruecolor($dst_width, $dst_height);
 413
 414				// Deal nicely with a PNG - because we can.
 415				if ((!empty($preferred_format)) && ($preferred_format == 3))
 416				{
 417					imagealphablending($dst_img, false);
 418					if (function_exists('imagesavealpha'))
 419						imagesavealpha($dst_img, true);
 420				}
 421			}
 422			else
 423				$dst_img = imagecreate($dst_width, $dst_height);
 424
 425			// Resize it!
 426			if ($gd2)
 427				imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
 428			else
 429				imagecopyresamplebicubic($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
 430		}
 431		else
 432			$dst_img = $src_img;
 433	}
 434	else
 435		$dst_img = $src_img;
 436
 437	// Save the image as ...
 438	if (!empty($preferred_format) && ($preferred_format == 3) && function_exists('imagepng'))
 439		$success = imagepng($dst_img, $destName);
 440	elseif (!empty($preferred_format) && ($preferred_format == 1) && function_exists('imagegif'))
 441		$success = imagegif($dst_img, $destName);
 442	elseif (function_exists('imagejpeg'))
 443		$success = imagejpeg($dst_img, $destName);
 444
 445	// Free the memory.
 446	imagedestroy($src_img);
 447	if ($dst_img != $src_img)
 448		imagedestroy($dst_img);
 449
 450	return $success;
 451}
 452
 453function imagecopyresamplebicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)
 454{
 455	$palsize = imagecolorstotal($src_img);
 456	for ($i = 0; $i < $palsize; $i++)
 457	{
 458		$colors = imagecolorsforindex($src_img, $i);
 459		imagecolorallocate($dst_img, $colors['red'], $colors['green'], $colors['blue']);
 460	}
 461
 462	$scaleX = ($src_w - 1) / $dst_w;
 463	$scaleY = ($src_h - 1) / $dst_h;
 464
 465	$scaleX2 = (int) $scaleX / 2;
 466	$scaleY2 = (int) $scaleY / 2;
 467
 468	for ($j = $src_y; $j < $dst_h; $j++)
 469	{
 470		$sY = (int) $j * $scaleY;
 471		$y13 = $sY + $scaleY2;
 472
 473		for ($i = $src_x; $i < $dst_w; $i++)
 474		{
 475			$sX = (int) $i * $scaleX;
 476			$x34 = $sX + $scaleX2;
 477
 478			$color1 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $y13));
 479			$color2 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $sY));
 480			$color3 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $y13));
 481			$color4 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $sY));
 482
 483			$red = ($color1['red'] + $color2['red'] + $color3['red'] + $color4['red']) / 4;
 484			$green = ($color1['green'] + $color2['green'] + $color3['green'] + $color4['green']) / 4;
 485			$blue = ($color1['blue'] + $color2['blue'] + $color3['blue'] + $color4['blue']) / 4;
 486
 487			$color = imagecolorresolve($dst_img, $red, $green, $blue);
 488			if ($color == -1)
 489			{
 490				if ($palsize++ < 256)
 491					imagecolorallocate($dst_img, $red, $green, $blue);
 492				$color = imagecolorclosest($dst_img, $red, $green, $blue);
 493			}
 494
 495			imagesetpixel($dst_img, $i + $dst_x - $src_x, $j + $dst_y - $src_y, $color);
 496		}
 497	}
 498}
 499
 500if (!function_exists('imagecreatefrombmp'))
 501{
 502	function imagecreatefrombmp($filename)
 503	{
 504		global $gd2;
 505
 506		$fp = fopen($filename, 'rb');
 507
 508		$errors = error_reporting(0);
 509
 510		$header = unpack('vtype/Vsize/Vreserved/Voffset', fread($fp, 14));
 511		$info = unpack('Vsize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vncolor/Vcolorimportant', fread($fp, 40));
 512
 513		if ($header['type'] != 0x4D42)
 514			false;
 515
 516		if ($gd2)
 517			$dst_img = imagecreatetruecolor($info['width'], $info['height']);
 518		else
 519			$dst_img = imagecreate($info['width'], $info['height']);
 520
 521		$palette_size = $header['offset'] - 54;
 522		$info['ncolor'] = $palette_size / 4;
 523
 524		$palette = array();
 525
 526		$palettedata = fread($fp, $palette_size);
 527		$n = 0;
 528		for ($j = 0; $j < $palette_size; $j++)
 529		{
 530			$b = ord($palettedata{$j++});
 531			$g = ord($palettedata{$j++});
 532			$r = ord($palettedata{$j++});
 533
 534			$palette[$n++] = imagecolorallocate($dst_img, $r, $g, $b);
 535		}
 536
 537		$scan_line_size = ($info['bits'] * $info['width'] + 7) >> 3;
 538		$scan_line_align = $scan_line_size & 3 ? 4 - ($scan_line_size & 3) : 0;
 539
 540		for ($y = 0, $l = $info['height'] - 1; $y < $info['height']; $y++, $l--)
 541		{
 542			fseek($fp, $header['offset'] + ($scan_line_size + $scan_line_align) * $l);
 543			$scan_line = fread($fp, $scan_line_size);
 544
 545			if (strlen($scan_line) < $scan_line_size)
 546				continue;
 547
 548			if ($info['bits'] == 32)
 549			{
 550				$x = 0;
 551				for ($j = 0; $j < $scan_line_size; $x++)
 552				{
 553					$b = ord($scan_line{$j++});
 554					$g = ord($scan_line{$j++});
 555					$r = ord($scan_line{$j++});
 556					$j++;
 557
 558					$color = imagecolorexact($dst_img, $r, $g, $b);
 559					if ($color == -1)
 560					{
 561						$color = imagecolorallocate($dst_img, $r, $g, $b);
 562
 563						// Gah!  Out of colors?  Stupid GD 1... try anyhow.
 564						if ($color == -1)
 565							$color = imagecolorclosest($dst_img, $r, $g, $b);
 566					}
 567
 568					imagesetpixel($dst_img, $x, $y, $color);
 569				}
 570			}
 571			elseif ($info['bits'] == 24)
 572			{
 573				$x = 0;
 574				for ($j = 0; $j < $scan_line_size; $x++)
 575				{
 576					$b = ord($scan_line{$j++});
 577					$g = ord($scan_line{$j++});
 578					$r = ord($scan_line{$j++});
 579
 580					$color = imagecolorexact($dst_img, $r, $g, $b);
 581					if ($color == -1)
 582					{
 583						$color = imagecolorallocate($dst_img, $r, $g, $b);
 584
 585						// Gah!  Out of colors?  Stupid GD 1... try anyhow.
 586						if ($color == -1)
 587							$color = imagecolorclosest($dst_img, $r, $g, $b);
 588					}
 589
 590					imagesetpixel($dst_img, $x, $y, $color);
 591				}
 592			}
 593			elseif ($info['bits'] == 16)
 594			{
 595				$x = 0;
 596				for ($j = 0; $j < $scan_line_size; $x++)
 597				{
 598					$b1 = ord($scan_line{$j++});
 599					$b2 = ord($scan_line{$j++});
 600
 601					$word = $b2 * 256 + $b1;
 602
 603					$b = (($word & 31) * 255) / 31;
 604					$g = ((($word >> 5) & 31) * 255) / 31;
 605					$r = ((($word >> 10) & 31) * 255) / 31;
 606
 607					// Scale the image colors up properly.
 608					$color = imagecolorexact($dst_img, $r, $g, $b);
 609					if ($color == -1)
 610					{
 611						$color = imagecolorallocate($dst_img, $r, $g, $b);
 612
 613						// Gah!  Out of colors?  Stupid GD 1... try anyhow.
 614						if ($color == -1)
 615							$color = imagecolorclosest($dst_img, $r, $g, $b);
 616					}
 617
 618					imagesetpixel($dst_img, $x, $y, $color);
 619				}
 620			}
 621			elseif ($info['bits'] == 8)
 622			{
 623				$x = 0;
 624				for ($j = 0; $j < $scan_line_size; $x++)
 625					imagesetpixel($dst_img, $x, $y, $palette[ord($scan_line{$j++})]);
 626			}
 627			elseif ($info['bits'] == 4)
 628			{
 629				$x = 0;
 630				for ($j = 0; $j < $scan_line_size; $x++)
 631				{
 632					$byte = ord($scan_line{$j++});
 633
 634					imagesetpixel($dst_img, $x, $y, $palette[(int) ($byte / 16)]);
 635					if (++$x < $info['width'])
 636						imagesetpixel($dst_img, $x, $y, $palette[$byte & 15]);
 637				}
 638			}
 639			else
 640			{
 641				// Sorry, I'm just not going to do monochrome :P.
 642			}
 643		}
 644
 645		fclose($fp);
 646
 647		error_reporting($errors);
 648
 649		return $dst_img;
 650	}
 651}
 652
 653function gif_loadFile($lpszFileName, $iIndex = 0)
 654{
 655	// The classes needed are in this file.
 656	loadClassFile('Class-Graphics.php');
 657	$gif = new gif_file();
 658
 659	if (!$gif->loadFile($lpszFileName, $iIndex))
 660		return false;
 661
 662	return $gif;
 663}
 664
 665function gif_outputAsPng($gif, $lpszFileName, $background_color = -1)
 666{
 667	if (!isset($gif) || @get_class($gif) != 'cgif' || !$gif->loaded || $lpszFileName == '')
 668		return false;
 669
 670	$fd = $gif->get_png_data($background_color);
 671	if (strlen($fd) <= 0)
 672		return false;
 673
 674	if (!($fh = @fopen($lpszFileName, 'wb')))
 675		return false;
 676
 677	@fwrite($fh, $fd, strlen($fd));
 678	@fflush($fh);
 679	@fclose($fh);
 680
 681	return true;
 682}
 683
 684// Create the image for the visual verification code.
 685function showCodeImage($code)
 686{
 687	global $settings, $user_info, $modSettings;
 688
 689	/*
 690		Note: The higher the value of visual_verification_type the harder the verification is - from 0 as disabled through to 4 as "Very hard".
 691	*/
 692
 693	// What type are we going to be doing?
 694	$imageType = $modSettings['visual_verification_type'];
 695	// Special case to allow the admin center to show samples.
 696	if ($user_info['is_admin'] && isset($_GET['type']))
 697		$imageType = (int) $_GET['type'];
 698
 699	// Some quick references for what we do.
 700	// Do we show no, low or high noise?
 701	$noiseType = $imageType == 3 ? 'low' : ($imageType == 4 ? 'high' : ($imageType == 5 ? 'extreme' : 'none'));
 702	// Can we have more than one font in use?
 703	$varyFonts = $imageType > 3 ? true : false;
 704	// Just a plain white background?
 705	$simpleBGColor = $imageType < 3 ? true : false;
 706	// Plain black foreground?
 707	$simpleFGColor = $imageType == 0 ? true : false;
 708	// High much to rotate each character.
 709	$rotationType = $imageType == 1 ? 'none' : ($imageType > 3 ? 'low' : 'high');
 710	// Do we show some characters inversed?
 711	$showReverseChars = $imageType > 3 ? true : false;
 712	// Special case for not showing any characters.
 713	$disableChars = $imageType == 0 ? true : false;
 714	// What do we do with the font colors. Are they one color, close to one color or random?
 715	$fontColorType = $imageType == 1 ? 'plain' : ($imageType > 3 ? 'random' : 'cyclic');
 716	// Are the fonts random sizes?
 717	$fontSizeRandom = $imageType > 3 ? true : false;
 718	// How much space between characters?
 719	$fontHorSpace = $imageType > 3 ? 'high' : ($imageType == 1 ? 'medium' : 'minus');
 720	// Where do characters sit on the image? (Fixed position or random/very random)
 721	$fontVerPos = $imageType == 1 ? 'fixed' : ($imageType > 3 ? 'vrandom' : 'random');
 722	// Make font semi-transparent?
 723	$fontTrans = $imageType == 2 || $imageType == 3 ? true : false;
 724	// Give the image a border?
 725	$hasBorder = $simpleBGColor;
 726
 727	// Is this GD2? Needed for pixel size.
 728	$testGD = get_extension_funcs('gd');
 729	$gd2 = in_array('imagecreatetruecolor', $testGD) && function_exists('imagecreatetruecolor');
 730	unset($testGD);
 731
 732	// The amount of pixels inbetween characters.
 733	$character_spacing = 1;
 734
 735	// What color is the background - generally white unless we're on "hard".
 736	if ($simpleBGColor)
 737		$background_color = array(255, 255, 255);
 738	else
 739		$background_color = isset($settings['verification_background']) ? $settings['verification_background'] : array(236, 237, 243);
 740
 741	// The color of the characters shown (red, green, blue).
 742	if ($simpleFGColor)
 743		$foreground_color = array(0, 0, 0);
 744	else
 745	{
 746		$foreground_color = array(64, 101, 136);
 747
 748		// Has the theme author requested a custom color?
 749		if (isset($settings['verification_foreground']))
 750			$foreground_color = $settings['verification_foreground'];
 751	}
 752
 753	if (!is_dir($settings['default_theme_dir'] . '/fonts'))
 754		return false;
 755
 756	// Get a list of the available fonts.
 757	$font_dir = dir($settings['default_theme_dir'] . '/fonts');
 758	$font_list = array();
 759	$ttfont_list = array();
 760	while ($entry = $font_dir->read())
 761	{
 762		if (preg_match('~^(.+)\.gdf$~', $entry, $matches) === 1)
 763			$font_list[] = $entry;
 764		elseif (preg_match('~^(.+)\.ttf$~', $entry, $matches) === 1)
 765			$ttfont_list[] = $entry;
 766	}
 767
 768	if (empty($font_list))
 769		return false;
 770
 771	// For non-hard things don't even change fonts.
 772	if (!$varyFonts)
 773	{
 774		$font_list = array($font_list[0]);
 775		// Try use Screenge if we can - it looks good!
 776		if (in_array('Screenge.ttf', $ttfont_list))
 777			$ttfont_list = array('Screenge.ttf');
 778		else
 779			$ttfont_list = empty($ttfont_list) ? array() : array($ttfont_list[0]);
 780
 781	}
 782
 783	// Create a list of characters to be shown.
 784	$characters = array();
 785	$loaded_fonts = array();
 786	for ($i = 0; $i < strlen($code); $i++)
 787	{
 788		$characters[$i] = array(
 789			'id' => $code{$i},
 790			'font' => array_rand($font_list),
 791		);
 792
 793		$loaded_fonts[$characters[$i]['font']] = null;
 794	}
 795
 796	// Load all fonts and determine the maximum font height.
 797	foreach ($loaded_fonts as $font_index => $dummy)
 798		$loaded_fonts[$font_index] = imageloadfont($settings['default_theme_dir'] . '/fonts/' . $font_list[$font_index]);
 799
 800	// Determine the dimensions of each character.
 801	$total_width = $character_spacing * strlen($code) + 20;
 802	$max_height = 0;
 803	foreach ($characters as $char_index => $character)
 804	{
 805		$characters[$char_index]['width'] = imagefontwidth($loaded_fonts[$character['font']]);
 806		$characters[$char_index]['height'] = imagefontheight($loaded_fonts[$character['font']]);
 807
 808		$max_height = max($characters[$char_index]['height'] + 5, $max_height);
 809		$total_width += $characters[$char_index]['width'];
 810	}
 811
 812	// Create an image.
 813	$code_image = $gd2 ? imagecreatetruecolor($total_width, $max_height) : imagecreate($total_width, $max_height);
 814
 815	// Draw the background.
 816	$bg_color = imagecolorallocate($code_image, $background_color[0], $background_color[1], $background_color[2]);
 817	imagefilledrectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $bg_color);
 818
 819	// Randomize the foreground color a little.
 820	for ($i = 0; $i < 3; $i++)
 821		$foreground_color[$i] = mt_rand(max($foreground_color[$i] - 3, 0), min($foreground_color[$i] + 3, 255));
 822	$fg_color = imagecolorallocate($code_image, $foreground_color[0], $foreground_color[1], $foreground_color[2]);
 823
 824	// Color for the dots.
 825	for ($i = 0; $i < 3; $i++)
 826		$dotbgcolor[$i] = $background_color[$i] < $foreground_color[$i] ? mt_rand(0, max($foreground_color[$i] - 20, 0)) : mt_rand(min($foreground_color[$i] + 20, 255), 255);
 827	$randomness_color = imagecolorallocate($code_image, $dotbgcolor[0], $dotbgcolor[1], $dotbgcolor[2]);
 828
 829	// Some squares/rectanges for new extreme level
 830	if ($noiseType == 'extreme')
 831	{
 832		for ($i = 0; $i < rand(1, 5); $i++)
 833		{
 834			$x1 = rand(0, $total_width / 4);
 835			$x2 = $x1 + round(rand($total_width / 4, $total_width));
 836			$y1 = rand(0, $max_height);
 837			$y2 = $y1 + round(rand(0, $max_height / 3));
 838			imagefilledrectangle($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
 839		}
 840	}
 841
 842	// Fill in the characters.
 843	if (!$disableChars)
 844	{
 845		$cur_x = 0;
 846		foreach ($characters as $char_index => $character)
 847		{
 848			// Can we use true type fonts?
 849			$can_do_ttf = function_exists('imagettftext');
 850
 851			// How much rotation will we give?
 852			if ($rotationType == 'none')
 853				$angle = 0;
 854			else
 855				$angle = mt_rand(-100, 100) / ($rotationType == 'high' ? 6 : 10);
 856
 857			// What color shall we do it?
 858			if ($fontColorType == 'cyclic')
 859			{
 860				// Here we'll pick from a set of acceptance types.
 861				$colors = array(
 862					array(10, 120, 95),
 863					array(46, 81, 29),
 864					array(4, 22, 154),
 865					array(131, 9, 130),
 866					array(0, 0, 0),
 867					array(143, 39, 31),
 868				);
 869				if (!isset($last_index))
 870					$last_index = -1;
 871				$new_index = $last_index;
 872				while ($last_index == $new_index)
 873					$new_index = mt_rand(0, count($colors) - 1);
 874				$char_fg_color = $colors[$new_index];
 875				$last_index = $new_index;
 876			}
 877			elseif ($fontColorType == 'random')
 878				$char_fg_color = array(mt_rand(max($foreground_color[0] - 2, 0), $foreground_color[0]), mt_rand(max($foreground_color[1] - 2, 0), $foreground_color[1]), mt_rand(max($foreground_color[2] - 2, 0), $foreground_color[2]));
 879			else
 880				$char_fg_color = array($foreground_color[0], $foreground_color[1], $foreground_color[2]);
 881
 882			if (!empty($can_do_ttf))
 883			{
 884				// GD2 handles font size differently.
 885				if ($fontSizeRandom)
 886					$font_size = $gd2 ? mt_rand(17, 19) : mt_rand(18, 25);
 887				else
 888					$font_size = $gd2 ? 18 : 24;
 889
 890				// Work out the sizes - also fix the character width cause TTF not quite so wide!
 891				$font_x = $fontHorSpace == 'minus' && $cur_x > 0 ? $cur_x - 3 : $cur_x + 5;
 892				$font_y = $max_height - ($fontVerPos == 'vrandom' ? mt_rand(2, 8) : ($fontVerPos == 'random' ? mt_rand(3, 5) : 5));
 893
 894				// What font face?
 895				if (!empty($ttfont_list))
 896					$fontface = $settings['default_theme_dir'] . '/fonts/' . $ttfont_list[mt_rand(0, count($ttfont_list) - 1)];
 897
 898				// What color are we to do it in?
 899				$is_reverse = $showReverseChars ? mt_rand(0, 1) : false;
 900				$char_color = function_exists('imagecolorallocatealpha') && $fontTrans ? imagecolorallocatealpha($code_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2], 50) : imagecolorallocate($code_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2]);
 901
 902				$fontcord = @imagettftext($code_image, $font_size, $angle, $font_x, $font_y, $char_color, $fontface, $character['id']);
 903				if (empty($fontcord))
 904					$can_do_ttf = false;
 905				elseif ($is_reverse)
 906				{
 907					imagefilledpolygon($code_image, $fontcord, 4, $fg_color);
 908					// Put the character back!
 909					imagettftext($code_image, $font_size, $angle, $font_x, $font_y, $randomness_color, $fontface, $character['id']);
 910				}
 911
 912				if ($can_do_ttf)
 913					$cur_x = max($fontcord[2], $fontcord[4]) + ($angle == 0 ? 0 : 3);
 914			}
 915
 916			if (!$can_do_ttf)
 917			{
 918				// Rotating the characters a little...
 919				if (function_exists('imagerotate'))
 920				{
 921					$char_image = $gd2 ? imagecreatetruecolor($character['width'], $character['height']) : imagecreate($character['width'], $character['height']);
 922					$char_bgcolor = imagecolorallocate($char_image, $background_color[0], $background_color[1], $background_color[2]);
 923					imagefilledrectangle($char_image, 0, 0, $character['width'] - 1, $character['height'] - 1, $char_bgcolor);
 924					imagechar($char_image, $loaded_fonts[$character['font']], 0, 0, $character['id'], imagecolorallocate($char_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2]));
 925					$rotated_char = imagerotate($char_image, mt_rand(-100, 100) / 10, $char_bgcolor);
 926					imagecopy($code_image, $rotated_char, $cur_x, 0, 0, 0, $character['width'], $character['height']);
 927					imagedestroy($rotated_char);
 928					imagedestroy($char_image);
 929				}
 930
 931				// Sorry, no rotation available.
 932				else
 933					imagechar($code_image, $loaded_fonts[$character['font']], $cur_x, floor(($max_height - $character['height']) / 2), $character['id'], imagecolorallocate($code_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2]));
 934				$cur_x += $character['width'] + $character_spacing;
 935			}
 936		}
 937	}
 938	// If disabled just show a cross.
 939	else
 940	{
 941		imageline($code_image, 0, 0, $total_width, $max_height, $fg_color);
 942		imageline($code_image, 0, $max_height, $total_width, 0, $fg_color);
 943	}
 944
 945	// Make the background color transparent on the hard image.
 946	if (!$simpleBGColor)
 947		imagecolortransparent($code_image, $bg_color);
 948	if ($hasBorder)
 949		imagerectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $fg_color);
 950
 951	// Add some noise to the background?
 952	if ($noiseType != 'none')
 953	{
 954		for ($i = mt_rand(0, 2); $i < $max_height; $i += mt_rand(1, 2))
 955			for ($j = mt_rand(0, 10); $j < $total_width; $j += mt_rand(1, 10))
 956				imagesetpixel($code_image, $j, $i, mt_rand(0, 1) ? $fg_color : $randomness_color);
 957
 958		// Put in some lines too?
 959		if ($noiseType != 'extreme')
 960		{
 961			$num_lines = $noiseType == 'high' ? mt_rand(3, 7) : mt_rand(2, 5);
 962			for ($i = 0; $i < $num_lines; $i++)
 963			{
 964				if (mt_rand(0, 1))
 965				{
 966					$x1 = mt_rand(0, $total_width);
 967					$x2 = mt_rand(0, $total_width);
 968					$y1 = 0; $y2 = $max_height;
 969				}
 970				else
 971				{
 972					$y1 = mt_rand(0, $max_height);
 973					$y2 = mt_rand(0, $max_height);
 974					$x1 = 0; $x2 = $total_width;
 975				}
 976				imagesetthickness($code_image, mt_rand(1, 2));
 977				imageline($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
 978			}
 979		}
 980		else
 981		{
 982			// Put in some ellipse
 983			$num_ellipse = $noiseType == 'extreme' ? mt_rand(6, 12) : mt_rand(2, 6);
 984			for ($i = 0; $i < $num_ellipse; $i++)
 985			{
 986				$x1 = round(rand(($total_width / 4) * -1, $total_width + ($total_width / 4)));
 987				$x2 = round(rand($total_width / 2, 2 * $total_width));
 988				$y1 = round(rand(($max_height / 4) * -1, $max_height + ($max_height / 4)));
 989				$y2 = round(rand($max_height / 2, 2 * $max_height));
 990				imageellipse($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
 991			}
 992		}
 993	}
 994
 995	// Show the image.
 996	if (function_exists('imagegif'))
 997	{
 998		header('Content-type: image/gif');
 999		imagegif($code_image);
1000	}
1001	else
1002	{
1003		header('Content-type: image/png');
1004		imagepng($code_image);
1005	}
1006
1007	// Bail out.
1008	imagedestroy($code_image);
1009	die();
1010}
1011
1012// Create a letter for the visual verification code.
1013function showLetterImage($letter)
1014{
1015	global $settings;
1016
1017	if (!is_dir($settings['default_theme_dir'] . '/fonts'))
1018		return false;
1019
1020	// Get a list of the available font directories.
1021	$font_dir = dir($settings['default_theme_dir'] . '/fonts');
1022	$font_list = array();
1023	while ($entry = $font_dir->read())
1024		if ($entry[0] !== '.' && is_dir($settings['default_theme_dir'] . '/fonts/' . $entry) && file_exists($settings['default_theme_dir'] . '/fonts/' . $entry . '.gdf'))
1025			$font_list[] = $entry;
1026
1027	if (empty($font_list))
1028		return false;
1029
1030	// Pick a random font.
1031	$random_font = $font_list[array_rand($font_list)];
1032
1033	// Check if the given letter exists.
1034	if (!file_exists($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.gif'))
1035		return false;
1036
1037	// Include it!
1038	header('Content-type: image/gif');
1039	include($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.gif');
1040
1041	// Nothing more to come.
1042	die();
1043}
1044
1045?>