resizeImage()   F
last analyzed

Complexity

Conditions 69
Paths 10658

Size

Total Lines 163
Code Lines 105

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 69
eloc 105
c 1
b 0
f 0
nc 10658
nop 8
dl 0
loc 163
rs 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * This file deals with low-level graphics operations performed on images,
5
 * specially as needed for avatars (uploaded avatars), attachments, or
6
 * visual verification images.
7
 * It uses, for gifs at least, Gif Util. For more information on that,
8
 * please see its website.
9
 * TrueType fonts supplied by www.LarabieFonts.com
10
 *
11
 * Simple Machines Forum (SMF)
12
 *
13
 * @package SMF
14
 * @author Simple Machines https://www.simplemachines.org
15
 * @copyright 2022 Simple Machines and individual contributors
16
 * @license https://www.simplemachines.org/about/smf/license.php BSD
17
 *
18
 * @version 2.1.2
19
 */
20
21
if (!defined('SMF'))
22
	die('No direct access...');
23
24
/**
25
 * downloads a file from a url and stores it locally for avatar use by id_member.
26
 * - supports GIF, JPG, PNG, BMP and WBMP formats.
27
 * - detects if GD2 is available.
28
 * - uses resizeImageFile() to resize to max_width by max_height, and saves the result to a file.
29
 * - updates the database info for the member's avatar.
30
 * - returns whether the download and resize was successful.
31
 *
32
 * @param string $url The full path to the temporary file
33
 * @param int $memID The member ID
34
 * @param int $max_width The maximum allowed width for the avatar
35
 * @param int $max_height The maximum allowed height for the avatar
36
 * @return boolean Whether the download and resize was successful.
37
 *
38
 */
39
function downloadAvatar($url, $memID, $max_width, $max_height)
40
{
41
	global $modSettings, $sourcedir, $smcFunc;
42
43
	$ext = !empty($modSettings['avatar_download_png']) ? 'png' : 'jpeg';
44
	$destName = 'avatar_' . $memID . '_' . time() . '.' . $ext;
45
46
	// Just making sure there is a non-zero member.
47
	if (empty($memID))
48
		return false;
49
50
	require_once($sourcedir . '/ManageAttachments.php');
51
	removeAttachments(array('id_member' => $memID));
52
53
	$id_folder = 1;
54
	$avatar_hash = '';
55
	$attachID = $smcFunc['db_insert']('',
56
		'{db_prefix}attachments',
57
		array(
58
			'id_member' => 'int', 'attachment_type' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-255', 'fileext' => 'string-8', 'size' => 'int',
59
			'id_folder' => 'int',
60
		),
61
		array(
62
			$memID, 1, $destName, $avatar_hash, $ext, 1,
63
			$id_folder,
64
		),
65
		array('id_attach'),
66
		1
67
	);
68
69
	// Retain this globally in case the script wants it.
70
	$modSettings['new_avatar_data'] = array(
71
		'id' => $attachID,
72
		'filename' => $destName,
73
		'type' => 1,
74
	);
75
76
	$destName = $modSettings['custom_avatar_dir'] . '/' . $destName . '.tmp';
77
78
	// Resize it.
79
	if (!empty($modSettings['avatar_download_png']))
80
		$success = resizeImageFile($url, $destName, $max_width, $max_height, 3);
81
	else
82
		$success = resizeImageFile($url, $destName, $max_width, $max_height);
83
84
	// Remove the .tmp extension.
85
	$destName = substr($destName, 0, -4);
86
87
	if ($success)
88
	{
89
		// Remove the .tmp extension from the attachment.
90
		if (rename($destName . '.tmp', $destName))
91
		{
92
			list ($width, $height) = getimagesize($destName);
93
			$mime_type = 'image/' . $ext;
94
95
			// Write filesize in the database.
96
			$smcFunc['db_query']('', '
97
				UPDATE {db_prefix}attachments
98
				SET size = {int:filesize}, width = {int:width}, height = {int:height},
99
					mime_type = {string:mime_type}
100
				WHERE id_attach = {int:current_attachment}',
101
				array(
102
					'filesize' => filesize($destName),
103
					'width' => (int) $width,
104
					'height' => (int) $height,
105
					'current_attachment' => $attachID,
106
					'mime_type' => $mime_type,
107
				)
108
			);
109
			return true;
110
		}
111
		else
112
			return false;
113
	}
114
	else
115
	{
116
		$smcFunc['db_query']('', '
117
			DELETE FROM {db_prefix}attachments
118
			WHERE id_attach = {int:current_attachment}',
119
			array(
120
				'current_attachment' => $attachID,
121
			)
122
		);
123
124
		@unlink($destName . '.tmp');
125
		return false;
126
	}
127
}
128
129
/**
130
 * Create a thumbnail of the given source.
131
 *
132
 * @uses resizeImageFile() function to achieve the resize.
133
 *
134
 * @param string $source The name of the source image
135
 * @param int $max_width The maximum allowed width
136
 * @param int $max_height The maximum allowed height
137
 * @return boolean Whether the thumbnail creation was successful.
138
 */
139
function createThumbnail($source, $max_width, $max_height)
140
{
141
	global $modSettings;
142
143
	$destName = $source . '_thumb.tmp';
144
145
	// Do the actual resize.
146
	if (!empty($modSettings['attachment_thumb_png']))
147
		$success = resizeImageFile($source, $destName, $max_width, $max_height, 3);
148
	else
149
		$success = resizeImageFile($source, $destName, $max_width, $max_height);
150
151
	// Okay, we're done with the temporary stuff.
152
	$destName = substr($destName, 0, -4);
153
154
	if ($success && @rename($destName . '.tmp', $destName))
155
		return true;
156
	else
157
	{
158
		@unlink($destName . '.tmp');
159
		@touch($destName);
160
		return false;
161
	}
162
}
163
164
/**
165
 * Used to re-econodes an image to a specified image format
166
 * - creates a copy of the file at the same location as fileName.
167
 * - the file would have the format preferred_format if possible, otherwise the default format is jpeg.
168
 * - the function makes sure that all non-essential image contents are disposed.
169
 *
170
 * @param string $fileName The path to the file
171
 * @param int $preferred_format The preferred format - 0 to automatically determine, 1 for gif, 2 for jpg, 3 for png, 6 for bmp and 15 for wbmp
172
 * @return boolean Whether the reencoding was successful
173
 */
174
function reencodeImage($fileName, $preferred_format = 0)
175
{
176
	if (!resizeImageFile($fileName, $fileName . '.tmp', null, null, $preferred_format))
177
	{
178
		if (file_exists($fileName . '.tmp'))
179
			unlink($fileName . '.tmp');
180
181
		return false;
182
	}
183
184
	if (!unlink($fileName))
185
		return false;
186
187
	if (!rename($fileName . '.tmp', $fileName))
188
		return false;
189
190
	return true;
191
}
192
193
/**
194
 * Searches through the file to see if there's potentially harmful non-binary content.
195
 * - if extensiveCheck is true, searches for asp/php short tags as well.
196
 *
197
 * @param string $fileName The path to the file
198
 * @param bool $extensiveCheck Whether to perform extensive checks
199
 * @return bool Whether the image appears to be safe
200
 */
201
function checkImageContents($fileName, $extensiveCheck = false)
202
{
203
	$fp = fopen($fileName, 'rb');
204
	if (!$fp)
0 ignored issues
show
introduced by
$fp is of type resource, thus it always evaluated to false.
Loading history...
205
		fatal_lang_error('attach_timeout');
206
207
	$prev_chunk = '';
208
	while (!feof($fp))
209
	{
210
		$cur_chunk = fread($fp, 8192);
211
212
		// Though not exhaustive lists, better safe than sorry.
213
		if (!empty($extensiveCheck))
214
		{
215
			// Paranoid check.  Use this if you have reason to distrust your host's security config.
216
			// Will result in MANY false positives, and is not suitable for photography sites.
217
			if (preg_match('~(iframe|\\<\\?|\\<%|html|eval|body|script\W|(?-i)[CFZ]WS[\x01-\x0E])~i', $prev_chunk . $cur_chunk) === 1)
218
			{
219
				fclose($fp);
220
				return false;
221
			}
222
		}
223
		else
224
		{
225
			// Check for potential infection - focus on clues for inline php & flash.
226
			// Will result in significantly fewer false positives than the paranoid check.
227
			if (preg_match('~(\\<\\?php\s|(?-i)[CFZ]WS[\x01-\x0E])~i', $prev_chunk . $cur_chunk) === 1)
228
			{
229
				fclose($fp);
230
				return false;
231
			}
232
		}
233
		$prev_chunk = $cur_chunk;
234
	}
235
	fclose($fp);
236
237
	return true;
238
}
239
240
/**
241
 * Sets a global $gd2 variable needed by some functions to determine
242
 * whether the GD2 library is present.
243
 *
244
 * @return bool Whether or not GD1 is available.
245
 */
246
function checkGD()
247
{
248
	global $gd2;
249
250
	// Check to see if GD is installed and what version.
251
	if (($extensionFunctions = get_extension_funcs('gd')) === false)
252
		return false;
253
254
	// Also determine if GD2 is installed and store it in a global.
255
	$gd2 = in_array('imagecreatetruecolor', $extensionFunctions) && function_exists('imagecreatetruecolor');
256
257
	return true;
258
}
259
260
/**
261
 * Checks whether the Imagick class is present.
262
 *
263
 * @return bool Whether or not the Imagick extension is available.
264
 */
265
function checkImagick()
266
{
267
	return class_exists('Imagick', false);
268
}
269
270
/**
271
 * Checks whether the MagickWand extension is present.
272
 *
273
 * @return bool Whether or not the MagickWand extension is available.
274
 */
275
function checkMagickWand()
276
{
277
	return function_exists('newMagickWand');
278
}
279
280
/**
281
 * See if we have enough memory to thumbnail an image
282
 *
283
 * @param array $sizes image size
284
 * @return bool Whether we do
285
 */
286
function imageMemoryCheck($sizes)
287
{
288
	global $modSettings;
289
290
	// doing the old 'set it and hope' way?
291
	if (empty($modSettings['attachment_thumb_memory']))
292
	{
293
		setMemoryLimit('128M');
294
		return true;
295
	}
296
297
	// Determine the memory requirements for this image, note: if you want to use an image formula W x H x bits/8 x channels x Overhead factor
298
	// you will need to account for single bit images as GD expands them to an 8 bit and will greatly overun the calculated value.  The 5 is
299
	// simply a shortcut of 8bpp, 3 channels, 1.66 overhead
300
	$needed_memory = ($sizes[0] * $sizes[1] * 5);
301
302
	// if we need more, lets try to get it
303
	return setMemoryLimit($needed_memory, true);
304
}
305
306
/**
307
 * Resizes an image from a remote location or a local file.
308
 * Puts the resized image at the destination location.
309
 * The file would have the format preferred_format if possible,
310
 * otherwise the default format is jpeg.
311
 *
312
 * @param string $source The path to the source image
313
 * @param string $destination The path to the destination image
314
 * @param int $max_width The maximum allowed width
315
 * @param int $max_height The maximum allowed height
316
 * @param int $preferred_format - The preferred format (0 to use jpeg, 1 for gif, 2 to force jpeg, 3 for png, 6 for bmp and 15 for wbmp)
317
 * @return bool Whether it succeeded.
318
 */
319
function resizeImageFile($source, $destination, $max_width, $max_height, $preferred_format = 0)
320
{
321
	global $sourcedir;
322
323
	// Nothing to do without GD or IM/MW
324
	if (!checkGD() && !checkImagick() && !checkMagickWand())
325
		return false;
326
327
	static $default_formats = array(
328
		'1' => 'gif',
329
		'2' => 'jpeg',
330
		'3' => 'png',
331
		'6' => 'bmp',
332
		'15' => 'wbmp'
333
	);
334
335
	// Get the image file, we have to work with something after all
336
	$fp_destination = fopen($destination, 'wb');
337
	if ($fp_destination && (substr($source, 0, 7) == 'http://' || substr($source, 0, 8) == 'https://'))
0 ignored issues
show
introduced by
$fp_destination is of type resource, thus it always evaluated to false.
Loading history...
338
	{
339
		$fileContents = fetch_web_data($source);
340
341
		$mime_valid = check_mime_type($fileContents, implode('|', array_map('image_type_to_mime_type', array_keys($default_formats))));
342
		if (empty($mime_valid))
343
			return false;
344
345
		fwrite($fp_destination, $fileContents);
346
		fclose($fp_destination);
347
348
		$sizes = @getimagesize($destination);
349
	}
350
	elseif ($fp_destination)
0 ignored issues
show
introduced by
$fp_destination is of type resource, thus it always evaluated to false.
Loading history...
351
	{
352
		$mime_valid = check_mime_type($source, implode('|', array_map('image_type_to_mime_type', array_keys($default_formats))), true);
353
		if (empty($mime_valid))
354
			return false;
355
356
		$sizes = @getimagesize($source);
357
358
		$fp_source = fopen($source, 'rb');
359
		if ($fp_source !== false)
360
		{
361
			while (!feof($fp_source))
362
				fwrite($fp_destination, fread($fp_source, 8192));
363
			fclose($fp_source);
364
		}
365
		else
366
			$sizes = array(-1, -1, -1);
367
		fclose($fp_destination);
368
	}
369
370
	// We can't get to the file. or a previous getimagesize failed.
371
	if (empty($sizes))
372
		$sizes = array(-1, -1, -1);
373
374
	// See if we have -or- can get the needed memory for this operation
375
	// ImageMagick isn't subject to PHP's memory limits :)
376
	if (!(checkIMagick() || checkMagickWand()) && checkGD() && !imageMemoryCheck($sizes))
377
		return false;
378
379
	// A known and supported format?
380
	// @todo test PSD and gif.
381
	if ((checkImagick() || checkMagickWand()) && isset($default_formats[$sizes[2]]))
382
	{
383
		return resizeImage(null, $destination, null, null, $max_width, $max_height, true, $preferred_format);
384
	}
385
	elseif (checkGD() && isset($default_formats[$sizes[2]]) && function_exists('imagecreatefrom' . $default_formats[$sizes[2]]))
386
	{
387
		$imagecreatefrom = 'imagecreatefrom' . $default_formats[$sizes[2]];
388
		if ($src_img = @$imagecreatefrom($destination))
389
		{
390
			return 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);
0 ignored issues
show
introduced by
The condition $max_width === null is always false.
Loading history...
introduced by
The condition $max_height === null is always false.
Loading history...
391
		}
392
	}
393
394
	return false;
395
}
396
397
/**
398
 * Resizes src_img proportionally to fit within max_width and max_height limits
399
 * if it is too large.
400
 * If GD2 is present, it'll use it to achieve better quality.
401
 * It saves the new image to destination_filename, as preferred_format
402
 * if possible, default is jpeg.
403
 *
404
 * Uses Imagemagick (IMagick or MagickWand extension) or GD
405
 *
406
 * @param resource $src_img The source image
407
 * @param string $destName The path to the destination image
408
 * @param int $src_width The width of the source image
409
 * @param int $src_height The height of the source image
410
 * @param int $max_width The maximum allowed width
411
 * @param int $max_height The maximum allowed height
412
 * @param bool $force_resize = false Whether to forcibly resize it
413
 * @param int $preferred_format - 1 for gif, 2 for jpeg, 3 for png, 6 for bmp or 15 for wbmp
414
 * @return bool Whether the resize was successful
415
 */
416
function resizeImage($src_img, $destName, $src_width, $src_height, $max_width, $max_height, $force_resize = false, $preferred_format = 0)
417
{
418
	global $gd2, $modSettings;
419
420
	$orientation = 0;
421
	if (function_exists('exif_read_data') && ($exif_data = @exif_read_data($destName)) !== false && !empty($exif_data['Orientation']))
422
		$orientation = $exif_data['Orientation'];
423
424
	if (checkImagick() || checkMagickWand())
425
	{
426
		static $default_formats = array(
427
			'1' => 'gif',
428
			'2' => 'jpeg',
429
			'3' => 'png',
430
			'6' => 'bmp',
431
			'15' => 'wbmp'
432
		);
433
		$preferred_format = empty($preferred_format) || !isset($default_formats[$preferred_format]) ? 2 : $preferred_format;
434
435
		if (checkImagick())
436
		{
437
			$imagick = New Imagick($destName);
438
			$src_width = empty($src_width) ? $imagick->getImageWidth() : $src_width;
439
			$src_height = empty($src_height) ? $imagick->getImageHeight() : $src_height;
440
			$dest_width = empty($max_width) ? $src_width : $max_width;
441
			$dest_height = empty($max_height) ? $src_height : $max_height;
442
443
			if ($default_formats[$preferred_format] == 'jpeg')
444
				$imagick->setCompressionQuality(!empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
445
446
			$imagick->setImageFormat($default_formats[$preferred_format]);
447
			$imagick->resizeImage($dest_width, $dest_height, Imagick::FILTER_LANCZOS, 1, true);
448
449
			if ($orientation > 1 && $preferred_format == 3)
450
			{
451
				if (in_array($orientation, [3, 4]))
452
					$imagick->rotateImage('#00000000', 180);
453
				elseif (in_array($orientation, [5, 6]))
454
					$imagick->rotateImage('#00000000', 90);
455
				elseif (in_array($orientation, [7, 8]))
456
					$imagick->rotateImage('#00000000', 270);
457
458
				if (in_array($orientation, [2, 4, 5, 7]))
459
					$imagick->flopImage();
460
			}
461
			$success = $imagick->writeImage($destName);
462
		}
463
		else
464
		{
465
			$magick_wand = newMagickWand();
0 ignored issues
show
Bug introduced by
The function newMagickWand was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

465
			$magick_wand = /** @scrutinizer ignore-call */ newMagickWand();
Loading history...
466
			MagickReadImage($magick_wand, $destName);
0 ignored issues
show
Bug introduced by
The function MagickReadImage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

466
			/** @scrutinizer ignore-call */ 
467
   MagickReadImage($magick_wand, $destName);
Loading history...
467
			$src_width = empty($src_width) ? MagickGetImageWidth($magick_wand) : $src_width;
0 ignored issues
show
Bug introduced by
The function MagickGetImageWidth was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

467
			$src_width = empty($src_width) ? /** @scrutinizer ignore-call */ MagickGetImageWidth($magick_wand) : $src_width;
Loading history...
468
			$src_height = empty($src_height) ? MagickGetImageSize($magick_wand) : $src_height;
0 ignored issues
show
Bug introduced by
The function MagickGetImageSize was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

468
			$src_height = empty($src_height) ? /** @scrutinizer ignore-call */ MagickGetImageSize($magick_wand) : $src_height;
Loading history...
469
			$dest_width = empty($max_width) ? $src_width : $max_width;
470
			$dest_height = empty($max_height) ? $src_height : $max_height;
471
472
			if ($default_formats[$preferred_format] == 'jpeg')
473
				MagickSetCompressionQuality($magick_wand, !empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
0 ignored issues
show
Bug introduced by
The function MagickSetCompressionQuality was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

473
				/** @scrutinizer ignore-call */ 
474
    MagickSetCompressionQuality($magick_wand, !empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
Loading history...
474
475
			MagickSetImageFormat($magick_wand, $default_formats[$preferred_format]);
0 ignored issues
show
Bug introduced by
The function MagickSetImageFormat was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

475
			/** @scrutinizer ignore-call */ 
476
   MagickSetImageFormat($magick_wand, $default_formats[$preferred_format]);
Loading history...
476
			MagickResizeImage($magick_wand, $dest_width, $dest_height, MW_LanczosFilter, 1, true);
0 ignored issues
show
Bug introduced by
The function MagickResizeImage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

476
			/** @scrutinizer ignore-call */ 
477
   MagickResizeImage($magick_wand, $dest_width, $dest_height, MW_LanczosFilter, 1, true);
Loading history...
Bug introduced by
The constant MW_LanczosFilter was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
477
478
			if ($orientation > 1)
479
			{
480
				if (in_array($orientation, [3, 4]))
481
					MagickResizeImage($magick_wand, NewPixelWand('white'), 180);
0 ignored issues
show
Bug introduced by
The function NewPixelWand was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

481
					MagickResizeImage($magick_wand, /** @scrutinizer ignore-call */ NewPixelWand('white'), 180);
Loading history...
482
				elseif (in_array($orientation, [5, 6]))
483
					MagickResizeImage($magick_wand, NewPixelWand('white'), 90);
484
				elseif (in_array($orientation, [7, 8]))
485
					MagickResizeImage($magick_wand, NewPixelWand('white'), 270);
486
487
				if (in_array($orientation, [2, 4, 5, 7]))
488
					MagickFlopImage($magick_wand);
0 ignored issues
show
Bug introduced by
The function MagickFlopImage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

488
					/** @scrutinizer ignore-call */ 
489
     MagickFlopImage($magick_wand);
Loading history...
489
			}
490
			$success = MagickWriteImage($magick_wand, $destName);
0 ignored issues
show
Bug introduced by
The function MagickWriteImage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

490
			$success = /** @scrutinizer ignore-call */ MagickWriteImage($magick_wand, $destName);
Loading history...
491
		}
492
493
		return !empty($success);
494
	}
495
	elseif (checkGD())
496
	{
497
		$success = false;
498
499
		// Determine whether to resize to max width or to max height (depending on the limits.)
500
		if (!empty($max_width) || !empty($max_height))
501
		{
502
			if (!empty($max_width) && (empty($max_height) || round($src_height * $max_width / $src_width) <= $max_height))
503
			{
504
				$dst_width = $max_width;
505
				$dst_height = round($src_height * $max_width / $src_width);
506
			}
507
			elseif (!empty($max_height))
508
			{
509
				$dst_width = round($src_width * $max_height / $src_height);
510
				$dst_height = $max_height;
511
			}
512
513
			// Don't bother resizing if it's already smaller...
514
			if (!empty($dst_width) && !empty($dst_height) && ($dst_width < $src_width || $dst_height < $src_height || $force_resize))
515
			{
516
				// (make a true color image, because it just looks better for resizing.)
517
				if ($gd2)
518
				{
519
					$dst_img = imagecreatetruecolor($dst_width, $dst_height);
520
521
					// Deal nicely with a PNG - because we can.
522
					if ((!empty($preferred_format)) && ($preferred_format == 3))
523
					{
524
						imagealphablending($dst_img, false);
525
						if (function_exists('imagesavealpha'))
526
							imagesavealpha($dst_img, true);
527
					}
528
				}
529
				else
530
					$dst_img = imagecreate($dst_width, $dst_height);
531
532
				// Resize it!
533
				if ($gd2)
534
					imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
535
				else
536
					imagecopyresamplebicubic($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
0 ignored issues
show
Bug introduced by
It seems like $dst_img can also be of type GdImage; however, parameter $dst_img of imagecopyresamplebicubic() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

536
					imagecopyresamplebicubic(/** @scrutinizer ignore-type */ $dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
Loading history...
537
			}
538
			else
539
				$dst_img = $src_img;
540
		}
541
		else
542
			$dst_img = $src_img;
543
544
		if ($orientation > 1)
545
		{
546
			if (in_array($orientation, [3, 4]))
547
				$dst_img = imagerotate($dst_img, 180, 0);
548
			elseif (in_array($orientation, [5, 6]))
549
				$dst_img = imagerotate($dst_img, 270, 0);
550
			elseif (in_array($orientation, [7, 8]))
551
				$dst_img = imagerotate($dst_img, 90, 0);
552
553
			if (in_array($orientation, [2, 4, 5, 7]))
554
				imageflip($dst_img, IMG_FLIP_HORIZONTAL);
555
		}
556
557
		// Save the image as ...
558
		if (!empty($preferred_format) && ($preferred_format == 3) && function_exists('imagepng'))
559
			$success = imagepng($dst_img, $destName);
560
		elseif (!empty($preferred_format) && ($preferred_format == 1) && function_exists('imagegif'))
561
			$success = imagegif($dst_img, $destName);
562
		elseif (!empty($preferred_format) && ($preferred_format == 6) && function_exists('imagebmp'))
563
			$success = imagebmp($dst_img, $destName);
564
		elseif (!empty($preferred_format) && ($preferred_format == 15) && function_exists('imagewbmp'))
565
			$success = imagewbmp($dst_img, $destName);
566
		elseif (function_exists('imagejpeg'))
567
			$success = imagejpeg($dst_img, $destName, !empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
568
569
		// Free the memory.
570
		imagedestroy($src_img);
571
		if ($dst_img != $src_img)
572
			imagedestroy($dst_img);
573
574
		return $success;
575
	}
576
	else
577
		// Without GD, no image resizing at all.
578
		return false;
579
}
580
581
/**
582
 * Copy image.
583
 * Used when imagecopyresample() is not available.
584
 *
585
 * @param resource $dst_img The destination image - a GD image resource
586
 * @param resource $src_img The source image - a GD image resource
587
 * @param int $dst_x The "x" coordinate of the destination image
588
 * @param int $dst_y The "y" coordinate of the destination image
589
 * @param int $src_x The "x" coordinate of the source image
590
 * @param int $src_y The "y" coordinate of the source image
591
 * @param int $dst_w The width of the destination image
592
 * @param int $dst_h The height of the destination image
593
 * @param int $src_w The width of the destination image
594
 * @param int $src_h The height of the destination image
595
 */
596
function imagecopyresamplebicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)
597
{
598
	$palsize = imagecolorstotal($src_img);
599
	for ($i = 0; $i < $palsize; $i++)
600
	{
601
		$colors = imagecolorsforindex($src_img, $i);
602
		imagecolorallocate($dst_img, $colors['red'], $colors['green'], $colors['blue']);
603
	}
604
605
	$scaleX = ($src_w - 1) / $dst_w;
606
	$scaleY = ($src_h - 1) / $dst_h;
607
608
	$scaleX2 = (int) $scaleX / 2;
609
	$scaleY2 = (int) $scaleY / 2;
610
611
	for ($j = $src_y; $j < $dst_h; $j++)
612
	{
613
		$sY = (int) $j * $scaleY;
614
		$y13 = $sY + $scaleY2;
615
616
		for ($i = $src_x; $i < $dst_w; $i++)
617
		{
618
			$sX = (int) $i * $scaleX;
619
			$x34 = $sX + $scaleX2;
620
621
			$color1 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $y13));
622
			$color2 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $sY));
623
			$color3 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $y13));
624
			$color4 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $sY));
625
626
			$red = ($color1['red'] + $color2['red'] + $color3['red'] + $color4['red']) / 4;
627
			$green = ($color1['green'] + $color2['green'] + $color3['green'] + $color4['green']) / 4;
628
			$blue = ($color1['blue'] + $color2['blue'] + $color3['blue'] + $color4['blue']) / 4;
629
630
			$color = imagecolorresolve($dst_img, $red, $green, $blue);
631
			if ($color == -1)
632
			{
633
				if ($palsize++ < 256)
634
					imagecolorallocate($dst_img, $red, $green, $blue);
635
				$color = imagecolorclosest($dst_img, $red, $green, $blue);
636
			}
637
638
			imagesetpixel($dst_img, $i + $dst_x - $src_x, $j + $dst_y - $src_y, $color);
639
		}
640
	}
641
}
642
643
if (!function_exists('imagecreatefrombmp'))
644
{
645
	/**
646
	 * It is set only if it doesn't already exist (for forwards compatiblity.)
647
	 * It only supports uncompressed bitmaps.
648
	 *
649
	 * @param string $filename The name of the file
650
	 * @return resource An image identifier representing the bitmap image
651
	 * obtained from the given filename.
652
	 */
653
	function imagecreatefrombmp($filename)
654
	{
655
		global $gd2;
656
657
		$fp = fopen($filename, 'rb');
658
659
		$errors = error_reporting(0);
660
661
		$header = unpack('vtype/Vsize/Vreserved/Voffset', fread($fp, 14));
662
		$info = unpack('Vsize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vncolor/Vcolorimportant', fread($fp, 40));
663
664
		if ($header['type'] != 0x4D42)
665
			return false;
666
667
		if ($gd2)
668
			$dst_img = imagecreatetruecolor($info['width'], $info['height']);
669
		else
670
			$dst_img = imagecreate($info['width'], $info['height']);
671
672
		$palette_size = $header['offset'] - 54;
673
		$info['ncolor'] = $palette_size / 4;
674
675
		$palette = array();
676
677
		$palettedata = fread($fp, $palette_size);
678
		$n = 0;
679
		for ($j = 0; $j < $palette_size; $j++)
680
		{
681
			$b = ord($palettedata[$j++]);
682
			$g = ord($palettedata[$j++]);
683
			$r = ord($palettedata[$j++]);
684
685
			$palette[$n++] = imagecolorallocate($dst_img, $r, $g, $b);
686
		}
687
688
		$scan_line_size = ($info['bits'] * $info['width'] + 7) >> 3;
689
		$scan_line_align = $scan_line_size & 3 ? 4 - ($scan_line_size & 3) : 0;
690
691
		for ($y = 0, $l = $info['height'] - 1; $y < $info['height']; $y++, $l--)
692
		{
693
			fseek($fp, $header['offset'] + ($scan_line_size + $scan_line_align) * $l);
694
			$scan_line = fread($fp, $scan_line_size);
695
696
			if (strlen($scan_line) < $scan_line_size)
697
				continue;
698
699
			if ($info['bits'] == 32)
700
			{
701
				$x = 0;
702
				for ($j = 0; $j < $scan_line_size; $x++)
703
				{
704
					$b = ord($scan_line[$j++]);
705
					$g = ord($scan_line[$j++]);
706
					$r = ord($scan_line[$j++]);
707
					$j++;
708
709
					$color = imagecolorexact($dst_img, $r, $g, $b);
710
					if ($color == -1)
711
					{
712
						$color = imagecolorallocate($dst_img, $r, $g, $b);
713
714
						// Gah!  Out of colors?  Stupid GD 1... try anyhow.
715
						if ($color == -1)
716
							$color = imagecolorclosest($dst_img, $r, $g, $b);
717
					}
718
719
					imagesetpixel($dst_img, $x, $y, $color);
720
				}
721
			}
722
			elseif ($info['bits'] == 24)
723
			{
724
				$x = 0;
725
				for ($j = 0; $j < $scan_line_size; $x++)
726
				{
727
					$b = ord($scan_line[$j++]);
728
					$g = ord($scan_line[$j++]);
729
					$r = ord($scan_line[$j++]);
730
731
					$color = imagecolorexact($dst_img, $r, $g, $b);
732
					if ($color == -1)
733
					{
734
						$color = imagecolorallocate($dst_img, $r, $g, $b);
735
736
						// Gah!  Out of colors?  Stupid GD 1... try anyhow.
737
						if ($color == -1)
738
							$color = imagecolorclosest($dst_img, $r, $g, $b);
739
					}
740
741
					imagesetpixel($dst_img, $x, $y, $color);
742
				}
743
			}
744
			elseif ($info['bits'] == 16)
745
			{
746
				$x = 0;
747
				for ($j = 0; $j < $scan_line_size; $x++)
748
				{
749
					$b1 = ord($scan_line[$j++]);
750
					$b2 = ord($scan_line[$j++]);
751
752
					$word = $b2 * 256 + $b1;
753
754
					$b = (($word & 31) * 255) / 31;
755
					$g = ((($word >> 5) & 31) * 255) / 31;
756
					$r = ((($word >> 10) & 31) * 255) / 31;
757
758
					// Scale the image colors up properly.
759
					$color = imagecolorexact($dst_img, $r, $g, $b);
760
					if ($color == -1)
761
					{
762
						$color = imagecolorallocate($dst_img, $r, $g, $b);
763
764
						// Gah!  Out of colors?  Stupid GD 1... try anyhow.
765
						if ($color == -1)
766
							$color = imagecolorclosest($dst_img, $r, $g, $b);
767
					}
768
769
					imagesetpixel($dst_img, $x, $y, $color);
770
				}
771
			}
772
			elseif ($info['bits'] == 8)
773
			{
774
				$x = 0;
775
				for ($j = 0; $j < $scan_line_size; $x++)
776
					imagesetpixel($dst_img, $x, $y, $palette[ord($scan_line[$j++])]);
777
			}
778
			elseif ($info['bits'] == 4)
779
			{
780
				$x = 0;
781
				for ($j = 0; $j < $scan_line_size; $x++)
782
				{
783
					$byte = ord($scan_line[$j++]);
784
785
					imagesetpixel($dst_img, $x, $y, $palette[(int) ($byte / 16)]);
786
787
					if (++$x < $info['width'])
788
						imagesetpixel($dst_img, $x, $y, $palette[$byte & 15]);
789
				}
790
			}
791
			elseif ($info['bits'] == 1)
792
			{
793
				$x = 0;
794
				for ($j = 0; $j < $scan_line_size; $x++)
795
				{
796
					$byte = ord($scan_line[$j++]);
797
798
					imagesetpixel($dst_img, $x, $y, $palette[(($byte) & 128) != 0]);
799
800
					for ($shift = 1; $shift < 8; $shift++)
801
					{
802
						if (++$x < $info['width'])
803
							imagesetpixel($dst_img, $x, $y, $palette[(($byte << $shift) & 128) != 0]);
804
					}
805
				}
806
			}
807
		}
808
809
		fclose($fp);
810
811
		error_reporting($errors);
812
813
		return $dst_img;
814
	}
815
}
816
817
/**
818
 * Writes a gif file to disk as a png file.
819
 *
820
 * @param gif_file $gif A gif image resource
821
 * @param string $lpszFileName The name of the file
822
 * @param int $background_color The background color
823
 * @return bool Whether the operation was successful
824
 */
825
function gif_outputAsPng($gif, $lpszFileName, $background_color = -1)
826
{
827
	if (!is_a($gif, 'gif_file') || $lpszFileName == '')
828
		return false;
829
830
	if (($fd = $gif->get_png_data($background_color)) === false)
831
		return false;
832
833
	if (($fh = @fopen($lpszFileName, 'wb')) === false)
834
		return false;
835
836
	@fwrite($fh, $fd, strlen($fd));
837
	@fflush($fh);
838
	@fclose($fh);
839
840
	return true;
841
}
842
843
/**
844
 * Show an image containing the visual verification code for registration.
845
 * Requires the GD extension.
846
 * Uses a random font for each letter from default_theme_dir/fonts.
847
 * Outputs a gif or a png (depending on whether gif ix supported).
848
 *
849
 * @param string $code The code to display
850
 * @return void|false False if something goes wrong.
851
 */
852
function showCodeImage($code)
853
{
854
	global $gd2, $settings, $user_info, $modSettings;
855
856
	// Note: The higher the value of visual_verification_type the harder the verification is - from 0 as disabled through to 4 as "Very hard".
857
858
	// What type are we going to be doing?
859
	$imageType = $modSettings['visual_verification_type'];
860
	// Special case to allow the admin center to show samples.
861
	if ($user_info['is_admin'] && isset($_GET['type']))
862
		$imageType = (int) $_GET['type'];
863
864
	// Some quick references for what we do.
865
	// Do we show no, low or high noise?
866
	$noiseType = $imageType == 3 ? 'low' : ($imageType == 4 ? 'high' : ($imageType == 5 ? 'extreme' : 'none'));
867
	// Can we have more than one font in use?
868
	$varyFonts = $imageType > 3 ? true : false;
869
	// Just a plain white background?
870
	$simpleBGColor = $imageType < 3 ? true : false;
871
	// Plain black foreground?
872
	$simpleFGColor = $imageType == 0 ? true : false;
873
	// High much to rotate each character.
874
	$rotationType = $imageType == 1 ? 'none' : ($imageType > 3 ? 'low' : 'high');
875
	// Do we show some characters inversed?
876
	$showReverseChars = $imageType > 3 ? true : false;
877
	// Special case for not showing any characters.
878
	$disableChars = $imageType == 0 ? true : false;
879
	// What do we do with the font colors. Are they one color, close to one color or random?
880
	$fontColorType = $imageType == 1 ? 'plain' : ($imageType > 3 ? 'random' : 'cyclic');
881
	// Are the fonts random sizes?
882
	$fontSizeRandom = $imageType > 3 ? true : false;
883
	// How much space between characters?
884
	$fontHorSpace = $imageType > 3 ? 'high' : ($imageType == 1 ? 'medium' : 'minus');
885
	// Where do characters sit on the image? (Fixed position or random/very random)
886
	$fontVerPos = $imageType == 1 ? 'fixed' : ($imageType > 3 ? 'vrandom' : 'random');
887
	// Make font semi-transparent?
888
	$fontTrans = $imageType == 2 || $imageType == 3 ? true : false;
889
	// Give the image a border?
890
	$hasBorder = $simpleBGColor;
891
892
	// The amount of pixels inbetween characters.
893
	$character_spacing = 1;
894
895
	// What color is the background - generally white unless we're on "hard".
896
	if ($simpleBGColor)
897
		$background_color = array(255, 255, 255);
898
	else
899
		$background_color = isset($settings['verification_background']) ? $settings['verification_background'] : array(236, 237, 243);
900
901
	// The color of the characters shown (red, green, blue).
902
	if ($simpleFGColor)
903
		$foreground_color = array(0, 0, 0);
904
	else
905
	{
906
		$foreground_color = array(64, 101, 136);
907
908
		// Has the theme author requested a custom color?
909
		if (isset($settings['verification_foreground']))
910
			$foreground_color = $settings['verification_foreground'];
911
	}
912
913
	if (!is_dir($settings['default_theme_dir'] . '/fonts'))
914
		return false;
915
916
	// Get a list of the available fonts.
917
	$font_dir = dir($settings['default_theme_dir'] . '/fonts');
918
	$font_list = array();
919
	$ttfont_list = array();
920
	$endian = unpack('v', pack('S', 0x00FF)) === 0x00FF;
921
	while ($entry = $font_dir->read())
922
	{
923
		if (preg_match('~^(.+)\.gdf$~', $entry, $matches) === 1)
924
		{
925
			if ($endian ^ (strpos($entry, '_end.gdf') === false))
926
				$font_list[] = $entry;
927
		}
928
		elseif (preg_match('~^(.+)\.ttf$~', $entry, $matches) === 1)
929
			$ttfont_list[] = $entry;
930
	}
931
932
	if (empty($font_list))
933
		return false;
934
935
	// For non-hard things don't even change fonts.
936
	if (!$varyFonts)
937
	{
938
		$font_list = array($font_list[0]);
939
		// Try use Screenge if we can - it looks good!
940
		if (in_array('AnonymousPro.ttf', $ttfont_list))
941
			$ttfont_list = array('AnonymousPro.ttf');
942
		else
943
			$ttfont_list = empty($ttfont_list) ? array() : array($ttfont_list[0]);
944
	}
945
946
	// Create a list of characters to be shown.
947
	$characters = array();
948
	$loaded_fonts = array();
949
	for ($i = 0; $i < strlen($code); $i++)
950
	{
951
		$characters[$i] = array(
952
			'id' => $code[$i],
953
			'font' => array_rand($font_list),
954
		);
955
956
		$loaded_fonts[$characters[$i]['font']] = null;
957
	}
958
959
	// Load all fonts and determine the maximum font height.
960
	foreach ($loaded_fonts as $font_index => $dummy)
961
		$loaded_fonts[$font_index] = imageloadfont($settings['default_theme_dir'] . '/fonts/' . $font_list[$font_index]);
962
963
	// Determine the dimensions of each character.
964
	if ($imageType == 4 || $imageType == 5)
965
		$extra = 80;
966
	else
967
		$extra = 45;
968
969
	$total_width = $character_spacing * strlen($code) + $extra;
970
	$max_height = 0;
971
	foreach ($characters as $char_index => $character)
972
	{
973
		$characters[$char_index]['width'] = imagefontwidth($loaded_fonts[$character['font']]);
974
		$characters[$char_index]['height'] = imagefontheight($loaded_fonts[$character['font']]);
975
976
		$max_height = max($characters[$char_index]['height'] + 5, $max_height);
977
		$total_width += $characters[$char_index]['width'];
978
	}
979
980
	// Create an image.
981
	$code_image = $gd2 ? imagecreatetruecolor($total_width, $max_height) : imagecreate($total_width, $max_height);
982
983
	// Draw the background.
984
	$bg_color = imagecolorallocate($code_image, $background_color[0], $background_color[1], $background_color[2]);
985
	imagefilledrectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $bg_color);
986
987
	// Randomize the foreground color a little.
988
	for ($i = 0; $i < 3; $i++)
989
		$foreground_color[$i] = mt_rand(max($foreground_color[$i] - 3, 0), min($foreground_color[$i] + 3, 255));
990
	$fg_color = imagecolorallocate($code_image, $foreground_color[0], $foreground_color[1], $foreground_color[2]);
991
992
	// Color for the dots.
993
	for ($i = 0; $i < 3; $i++)
994
		$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);
995
	$randomness_color = imagecolorallocate($code_image, $dotbgcolor[0], $dotbgcolor[1], $dotbgcolor[2]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dotbgcolor does not seem to be defined for all execution paths leading up to this point.
Loading history...
996
997
	// Some squares/rectanges for new extreme level
998
	if ($noiseType == 'extreme')
999
	{
1000
		for ($i = 0; $i < mt_rand(1, 5); $i++)
1001
		{
1002
			$x1 = mt_rand(0, $total_width / 4);
1003
			$x2 = $x1 + round(rand($total_width / 4, $total_width));
1004
			$y1 = mt_rand(0, $max_height);
1005
			$y2 = $y1 + round(rand(0, $max_height / 3));
1006
			imagefilledrectangle($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
0 ignored issues
show
Bug introduced by
$y2 of type double is incompatible with the type integer expected by parameter $y2 of imagefilledrectangle(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1006
			imagefilledrectangle($code_image, $x1, $y1, $x2, /** @scrutinizer ignore-type */ $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
Bug introduced by
$x2 of type double is incompatible with the type integer expected by parameter $x2 of imagefilledrectangle(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1006
			imagefilledrectangle($code_image, $x1, $y1, /** @scrutinizer ignore-type */ $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
1007
		}
1008
	}
1009
1010
	// Fill in the characters.
1011
	if (!$disableChars)
1012
	{
1013
		$cur_x = 0;
1014
		foreach ($characters as $char_index => $character)
1015
		{
1016
			// Can we use true type fonts?
1017
			$can_do_ttf = function_exists('imagettftext');
1018
1019
			// How much rotation will we give?
1020
			if ($rotationType == 'none')
1021
				$angle = 0;
1022
			else
1023
				$angle = mt_rand(-100, 100) / ($rotationType == 'high' ? 6 : 10);
1024
1025
			// What color shall we do it?
1026
			if ($fontColorType == 'cyclic')
1027
			{
1028
				// Here we'll pick from a set of acceptance types.
1029
				$colors = array(
1030
					array(10, 120, 95),
1031
					array(46, 81, 29),
1032
					array(4, 22, 154),
1033
					array(131, 9, 130),
1034
					array(0, 0, 0),
1035
					array(143, 39, 31),
1036
				);
1037
				if (!isset($last_index))
1038
					$last_index = -1;
1039
				$new_index = $last_index;
1040
				while ($last_index == $new_index)
1041
					$new_index = mt_rand(0, count($colors) - 1);
1042
				$char_fg_color = $colors[$new_index];
1043
				$last_index = $new_index;
1044
			}
1045
			elseif ($fontColorType == 'random')
1046
				$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]));
1047
			else
1048
				$char_fg_color = array($foreground_color[0], $foreground_color[1], $foreground_color[2]);
1049
1050
			if (!empty($can_do_ttf))
1051
			{
1052
				// GD2 handles font size differently.
1053
				if ($fontSizeRandom)
1054
					$font_size = $gd2 ? mt_rand(17, 19) : mt_rand(18, 25);
1055
				else
1056
					$font_size = $gd2 ? 18 : 24;
1057
1058
				// Work out the sizes - also fix the character width cause TTF not quite so wide!
1059
				$font_x = $fontHorSpace == 'minus' && $cur_x > 0 ? $cur_x - 3 : $cur_x + 5;
1060
				$font_y = $max_height - ($fontVerPos == 'vrandom' ? mt_rand(2, 8) : ($fontVerPos == 'random' ? mt_rand(3, 5) : 5));
1061
1062
				// What font face?
1063
				if (!empty($ttfont_list))
1064
					$fontface = $settings['default_theme_dir'] . '/fonts/' . $ttfont_list[mt_rand(0, count($ttfont_list) - 1)];
1065
1066
				// What color are we to do it in?
1067
				$is_reverse = $showReverseChars ? mt_rand(0, 1) : false;
1068
				$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]);
1069
1070
				$fontcord = @imagettftext($code_image, $font_size, $angle, $font_x, $font_y, $char_color, $fontface, $character['id']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $fontface does not seem to be defined for all execution paths leading up to this point.
Loading history...
1071
				if (empty($fontcord))
1072
					$can_do_ttf = false;
1073
				elseif ($is_reverse)
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_reverse of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1074
				{
1075
					imagefilledpolygon($code_image, $fontcord, 4, $fg_color);
1076
					// Put the character back!
1077
					imagettftext($code_image, $font_size, $angle, $font_x, $font_y, $randomness_color, $fontface, $character['id']);
1078
				}
1079
1080
				if ($can_do_ttf)
1081
					$cur_x = max($fontcord[2], $fontcord[4]) + ($angle == 0 ? 0 : 3);
1082
			}
1083
1084
			if (!$can_do_ttf)
1085
			{
1086
				// Rotating the characters a little...
1087
				if (function_exists('imagerotate'))
1088
				{
1089
					$char_image = $gd2 ? imagecreatetruecolor($character['width'], $character['height']) : imagecreate($character['width'], $character['height']);
1090
					$char_bgcolor = imagecolorallocate($char_image, $background_color[0], $background_color[1], $background_color[2]);
1091
					imagefilledrectangle($char_image, 0, 0, $character['width'] - 1, $character['height'] - 1, $char_bgcolor);
1092
					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]));
1093
					$rotated_char = imagerotate($char_image, mt_rand(-100, 100) / 10, $char_bgcolor);
1094
					imagecopy($code_image, $rotated_char, $cur_x, 0, 0, 0, $character['width'], $character['height']);
1095
					imagedestroy($rotated_char);
1096
					imagedestroy($char_image);
1097
				}
1098
1099
				// Sorry, no rotation available.
1100
				else
1101
					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]));
0 ignored issues
show
Bug introduced by
floor($max_height - $character['height'] / 2) of type double is incompatible with the type integer expected by parameter $y of imagechar(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1101
					imagechar($code_image, $loaded_fonts[$character['font']], $cur_x, /** @scrutinizer ignore-type */ floor(($max_height - $character['height']) / 2), $character['id'], imagecolorallocate($code_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2]));
Loading history...
1102
				$cur_x += $character['width'] + $character_spacing;
1103
			}
1104
		}
1105
	}
1106
	// If disabled just show a cross.
1107
	else
1108
	{
1109
		imageline($code_image, 0, 0, $total_width, $max_height, $fg_color);
1110
		imageline($code_image, 0, $max_height, $total_width, 0, $fg_color);
1111
	}
1112
1113
	// Make the background color transparent on the hard image.
1114
	if (!$simpleBGColor)
1115
		imagecolortransparent($code_image, $bg_color);
1116
	if ($hasBorder)
1117
		imagerectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $fg_color);
1118
1119
	// Add some noise to the background?
1120
	if ($noiseType != 'none')
1121
	{
1122
		for ($i = mt_rand(0, 2); $i < $max_height; $i += mt_rand(1, 2))
1123
			for ($j = mt_rand(0, 10); $j < $total_width; $j += mt_rand(1, 10))
1124
				imagesetpixel($code_image, $j, $i, mt_rand(0, 1) ? $fg_color : $randomness_color);
1125
1126
		// Put in some lines too?
1127
		if ($noiseType != 'extreme')
1128
		{
1129
			$num_lines = $noiseType == 'high' ? mt_rand(3, 7) : mt_rand(2, 5);
1130
			for ($i = 0; $i < $num_lines; $i++)
1131
			{
1132
				if (mt_rand(0, 1))
1133
				{
1134
					$x1 = mt_rand(0, $total_width);
1135
					$x2 = mt_rand(0, $total_width);
1136
					$y1 = 0;
1137
					$y2 = $max_height;
1138
				}
1139
				else
1140
				{
1141
					$y1 = mt_rand(0, $max_height);
1142
					$y2 = mt_rand(0, $max_height);
1143
					$x1 = 0;
1144
					$x2 = $total_width;
1145
				}
1146
				imagesetthickness($code_image, mt_rand(1, 2));
1147
				imageline($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
1148
			}
1149
		}
1150
		else
1151
		{
1152
			// Put in some ellipse
1153
			$num_ellipse = $noiseType == 'extreme' ? mt_rand(6, 12) : mt_rand(2, 6);
1154
			for ($i = 0; $i < $num_ellipse; $i++)
1155
			{
1156
				$x1 = round(rand(($total_width / 4) * -1, $total_width + ($total_width / 4)));
1157
				$x2 = round(rand($total_width / 2, 2 * $total_width));
1158
				$y1 = round(rand(($max_height / 4) * -1, $max_height + ($max_height / 4)));
1159
				$y2 = round(rand($max_height / 2, 2 * $max_height));
1160
				imageellipse($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
0 ignored issues
show
Bug introduced by
$x2 of type double is incompatible with the type integer expected by parameter $width of imageellipse(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1160
				imageellipse($code_image, $x1, $y1, /** @scrutinizer ignore-type */ $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
Bug introduced by
$y1 of type double is incompatible with the type integer expected by parameter $cy of imageellipse(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1160
				imageellipse($code_image, $x1, /** @scrutinizer ignore-type */ $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
Bug introduced by
$y2 of type double is incompatible with the type integer expected by parameter $height of imageellipse(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1160
				imageellipse($code_image, $x1, $y1, $x2, /** @scrutinizer ignore-type */ $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
Bug introduced by
$x1 of type double is incompatible with the type integer expected by parameter $cx of imageellipse(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1160
				imageellipse($code_image, /** @scrutinizer ignore-type */ $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
1161
			}
1162
		}
1163
	}
1164
1165
	// Show the image.
1166
	if (function_exists('imagegif'))
1167
	{
1168
		header('content-type: image/gif');
1169
		imagegif($code_image);
1170
	}
1171
	else
1172
	{
1173
		header('content-type: image/png');
1174
		imagepng($code_image);
1175
	}
1176
1177
	// Bail out.
1178
	imagedestroy($code_image);
1179
	die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1180
}
1181
1182
/**
1183
 * Show a letter for the visual verification code.
1184
 * Alternative function for showCodeImage() in case GD is missing.
1185
 * Includes an image from a random sub directory of default_theme_dir/fonts.
1186
 *
1187
 * @param string $letter A letter to show as an image
1188
 * @return void|false False if something went wrong
1189
 */
1190
function showLetterImage($letter)
1191
{
1192
	global $settings;
1193
1194
	if (!is_dir($settings['default_theme_dir'] . '/fonts'))
1195
		return false;
1196
1197
	// Get a list of the available font directories.
1198
	$font_dir = dir($settings['default_theme_dir'] . '/fonts');
1199
	$font_list = array();
1200
	while ($entry = $font_dir->read())
1201
		if ($entry[0] !== '.' && is_dir($settings['default_theme_dir'] . '/fonts/' . $entry) && file_exists($settings['default_theme_dir'] . '/fonts/' . $entry . '.gdf'))
1202
			$font_list[] = $entry;
1203
1204
	if (empty($font_list))
1205
		return false;
1206
1207
	// Pick a random font.
1208
	$random_font = $font_list[array_rand($font_list)];
1209
1210
	// Check if the given letter exists.
1211
	if (!file_exists($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . strtoupper($letter) . '.png'))
1212
		return false;
1213
1214
	// Include it!
1215
	header('content-type: image/png');
1216
	include($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . strtoupper($letter) . '.png');
1217
1218
	// Nothing more to come.
1219
	die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1220
}
1221
1222
?>