checkGD()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nop 0
dl 0
loc 12
rs 10
c 0
b 0
f 0
nc 3
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 http://www.simplemachines.org
15
 * @copyright 2019 Simple Machines and individual contributors
16
 * @license http://www.simplemachines.org/about/smf/license.php BSD
17
 *
18
 * @version 2.1 RC2
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
191
/**
192
 * Searches through the file to see if there's potentially harmful non-binary content.
193
 * - if extensiveCheck is true, searches for asp/php short tags as well.
194
 *
195
 * @param string $fileName The path to the file
196
 * @param bool $extensiveCheck Whether to perform extensive checks
197
 * @return bool Whether the image appears to be safe
198
 */
199
function checkImageContents($fileName, $extensiveCheck = false)
200
{
201
	$fp = fopen($fileName, 'rb');
202
	if (!$fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
203
		fatal_lang_error('attach_timeout');
204
205
	$prev_chunk = '';
206
	while (!feof($fp))
207
	{
208
		$cur_chunk = fread($fp, 8192);
209
210
		// Though not exhaustive lists, better safe than sorry.
211
		if (!empty($extensiveCheck))
212
		{
213
			// Paranoid check.  Use this if you have reason to distrust your host's security config.
214
			// Will result in MANY false positives, and is not suitable for photography sites.
215
			if (preg_match('~(iframe|\\<\\?|\\<%|html|eval|body|script\W|(?-i)[CFZ]WS[\x01-\x0E])~i', $prev_chunk . $cur_chunk) === 1)
216
			{
217
				fclose($fp);
218
				return false;
219
			}
220
		}
221
		else
222
		{
223
			// Check for potential infection - focus on clues for inline php & flash.
224
			// Will result in significantly fewer false positives than the paranoid check.
225
			if (preg_match('~(\\<\\?php\s|(?-i)[CFZ]WS[\x01-\x0E])~i', $prev_chunk . $cur_chunk) === 1)
226
			{
227
				fclose($fp);
228
				return false;
229
			}
230
		}
231
		$prev_chunk = $cur_chunk;
232
	}
233
	fclose($fp);
234
235
	return true;
236
}
237
238
/**
239
 * Sets a global $gd2 variable needed by some functions to determine
240
 * whether the GD2 library is present.
241
 *
242
 * @return bool Whether or not GD1 is available.
243
 */
244
function checkGD()
245
{
246
	global $gd2;
247
248
	// Check to see if GD is installed and what version.
249
	if (($extensionFunctions = get_extension_funcs('gd')) === false)
250
		return false;
251
252
	// Also determine if GD2 is installed and store it in a global.
253
	$gd2 = in_array('imagecreatetruecolor', $extensionFunctions) && function_exists('imagecreatetruecolor');
254
255
	return true;
256
}
257
258
/**
259
 * Checks whether the Imagick class is present.
260
 *
261
 * @return bool Whether or not the Imagick extension is available.
262
 */
263
function checkImagick()
264
{
265
	return class_exists('Imagick', false);
266
}
267
268
/**
269
 * Checks whether the MagickWand extension is present.
270
 *
271
 * @return bool Whether or not the MagickWand extension is available.
272
 */
273
function checkMagickWand()
274
{
275
	return function_exists('newMagickWand');
276
}
277
278
/**
279
 * See if we have enough memory to thumbnail an image
280
 *
281
 * @param array $sizes image size
282
 * @return bool Whether we do
283
 */
284
function imageMemoryCheck($sizes)
285
{
286
	global $modSettings;
287
288
	// doing the old 'set it and hope' way?
289
	if (empty($modSettings['attachment_thumb_memory']))
290
	{
291
		setMemoryLimit('128M');
292
		return true;
293
	}
294
295
	// 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
296
	// 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
297
	// simply a shortcut of 8bpp, 3 channels, 1.66 overhead
298
	$needed_memory = ($sizes[0] * $sizes[1] * 5);
299
300
	// if we need more, lets try to get it
301
	return setMemoryLimit($needed_memory, true);
302
}
303
304
/**
305
 * Resizes an image from a remote location or a local file.
306
 * Puts the resized image at the destination location.
307
 * The file would have the format preferred_format if possible,
308
 * otherwise the default format is jpeg.
309
 *
310
 * @param string $source The path to the source image
311
 * @param string $destination The path to the destination image
312
 * @param int $max_width The maximum allowed width
313
 * @param int $max_height The maximum allowed height
314
 * @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)
315
 * @return bool Whether it succeeded.
316
 */
317
function resizeImageFile($source, $destination, $max_width, $max_height, $preferred_format = 0)
318
{
319
	global $sourcedir;
320
321
	// Nothing to do without GD or IM/MW
322
	if (!checkGD() && !checkImagick() && !checkMagickWand())
323
		return false;
324
325
	static $default_formats = array(
326
		'1' => 'gif',
327
		'2' => 'jpeg',
328
		'3' => 'png',
329
		'6' => 'bmp',
330
		'15' => 'wbmp'
331
	);
332
333
	// Get the image file, we have to work with something after all
334
	$fp_destination = fopen($destination, 'wb');
335
	if ($fp_destination && (substr($source, 0, 7) == 'http://' || substr($source, 0, 8) == 'https://'))
0 ignored issues
show
introduced by
$fp_destination is of type false|resource, thus it always evaluated to false.
Loading history...
336
	{
337
		$fileContents = fetch_web_data($source);
338
339
		fwrite($fp_destination, $fileContents);
340
		fclose($fp_destination);
341
342
		$sizes = @getimagesize($destination);
343
	}
344
	elseif ($fp_destination)
0 ignored issues
show
introduced by
$fp_destination is of type false|resource, thus it always evaluated to false.
Loading history...
345
	{
346
		$sizes = @getimagesize($source);
347
348
		$fp_source = fopen($source, 'rb');
349
		if ($fp_source !== false)
350
		{
351
			while (!feof($fp_source))
352
				fwrite($fp_destination, fread($fp_source, 8192));
353
			fclose($fp_source);
354
		}
355
		else
356
			$sizes = array(-1, -1, -1);
357
		fclose($fp_destination);
358
	}
359
	// We can't get to the file.
360
	else
361
		$sizes = array(-1, -1, -1);
362
363
	// See if we have -or- can get the needed memory for this operation
364
	// ImageMagick isn't subject to PHP's memory limits :)
365
	if (!(checkIMagick() || checkMagickWand()) && checkGD() && !imageMemoryCheck($sizes))
366
		return false;
367
368
	// A known and supported format?
369
	// @todo test PSD and gif.
370
	if ((checkImagick() || checkMagickWand()) && isset($default_formats[$sizes[2]]))
371
	{
372
		return resizeImage(null, $destination, null, null, $max_width, $max_height, true, $preferred_format);
373
	}
374
	elseif (checkGD() && isset($default_formats[$sizes[2]]) && function_exists('imagecreatefrom' . $default_formats[$sizes[2]]))
375
	{
376
		$imagecreatefrom = 'imagecreatefrom' . $default_formats[$sizes[2]];
377
		if ($src_img = @$imagecreatefrom($destination))
378
		{
379
			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...
380
		}
381
	}
382
383
	return false;
384
}
385
386
/**
387
 * Resizes src_img proportionally to fit within max_width and max_height limits
388
 * if it is too large.
389
 * If GD2 is present, it'll use it to achieve better quality.
390
 * It saves the new image to destination_filename, as preferred_format
391
 * if possible, default is jpeg.
392
 *
393
 * @uses Imagemagick (IMagick or MagickWand extension) or GD
394
 *
395
 * @param resource $src_img The source image
396
 * @param string $destName The path to the destination image
397
 * @param int $src_width The width of the source image
398
 * @param int $src_height The height of the source image
399
 * @param int $max_width The maximum allowed width
400
 * @param int $max_height The maximum allowed height
401
 * @param bool $force_resize = false Whether to forcibly resize it
402
 * @param int $preferred_format - 1 for gif, 2 for jpeg, 3 for png, 6 for bmp or 15 for wbmp
403
 * @return bool Whether the resize was successful
404
 */
405
function resizeImage($src_img, $destName, $src_width, $src_height, $max_width, $max_height, $force_resize = false, $preferred_format = 0)
406
{
407
	global $gd2, $modSettings;
408
409
	if (checkImagick() || checkMagickWand())
410
	{
411
		static $default_formats = array(
412
			'1' => 'gif',
413
			'2' => 'jpeg',
414
			'3' => 'png',
415
			'6' => 'bmp',
416
			'15' => 'wbmp'
417
		);
418
		$preferred_format = empty($preferred_format) || !isset($default_formats[$preferred_format]) ? 2 : $preferred_format;
419
420
		if (checkImagick())
421
		{
422
			$imagick = New Imagick($destName);
423
			$src_width = empty($src_width) ? $imagick->getImageWidth() : $src_width;
424
			$src_height = empty($src_height) ? $imagick->getImageHeight() : $src_height;
425
			$dest_width = empty($max_width) ? $src_width : $max_width;
426
			$dest_height = empty($max_height) ? $src_height : $max_height;
427
428
			if ($default_formats[$preferred_format] == 'jpeg')
429
				$imagick->setCompressionQuality(!empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
430
431
			$imagick->setImageFormat($default_formats[$preferred_format]);
432
			$imagick->resizeImage($dest_width, $dest_height, Imagick::FILTER_LANCZOS, 1, true);
433
			$success = $imagick->writeImage($destName);
434
		}
435
		else
436
		{
437
			$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

437
			$magick_wand = /** @scrutinizer ignore-call */ newMagickWand();
Loading history...
438
			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

438
			/** @scrutinizer ignore-call */ 
439
   MagickReadImage($magick_wand, $destName);
Loading history...
439
			$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

439
			$src_width = empty($src_width) ? /** @scrutinizer ignore-call */ MagickGetImageWidth($magick_wand) : $src_width;
Loading history...
440
			$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

440
			$src_height = empty($src_height) ? /** @scrutinizer ignore-call */ MagickGetImageSize($magick_wand) : $src_height;
Loading history...
441
			$dest_width = empty($max_width) ? $src_width : $max_width;
442
			$dest_height = empty($max_height) ? $src_height : $max_height;
443
444
			if ($default_formats[$preferred_format] == 'jpeg')
445
				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

445
				/** @scrutinizer ignore-call */ 
446
    MagickSetCompressionQuality($magick_wand, !empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
Loading history...
446
447
			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

447
			/** @scrutinizer ignore-call */ 
448
   MagickSetImageFormat($magick_wand, $default_formats[$preferred_format]);
Loading history...
448
			MagickResizeImage($magick_wand, $dest_width, $dest_height, MW_LanczosFilter, 1, true);
0 ignored issues
show
Bug introduced by
The constant MW_LanczosFilter was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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

448
			/** @scrutinizer ignore-call */ 
449
   MagickResizeImage($magick_wand, $dest_width, $dest_height, MW_LanczosFilter, 1, true);
Loading history...
449
			$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

449
			$success = /** @scrutinizer ignore-call */ MagickWriteImage($magick_wand, $destName);
Loading history...
450
		}
451
452
		return !empty($success);
453
	}
454
	elseif (checkGD())
455
	{
456
		$success = false;
457
458
		// Determine whether to resize to max width or to max height (depending on the limits.)
459
		if (!empty($max_width) || !empty($max_height))
460
		{
461
			if (!empty($max_width) && (empty($max_height) || round($src_height * $max_width / $src_width) <= $max_height))
462
			{
463
				$dst_width = $max_width;
464
				$dst_height = round($src_height * $max_width / $src_width);
465
			}
466
			elseif (!empty($max_height))
467
			{
468
				$dst_width = round($src_width * $max_height / $src_height);
469
				$dst_height = $max_height;
470
			}
471
472
			// Don't bother resizing if it's already smaller...
473
			if (!empty($dst_width) && !empty($dst_height) && ($dst_width < $src_width || $dst_height < $src_height || $force_resize))
474
			{
475
				// (make a true color image, because it just looks better for resizing.)
476
				if ($gd2)
477
				{
478
					$dst_img = imagecreatetruecolor($dst_width, $dst_height);
479
480
					// Deal nicely with a PNG - because we can.
481
					if ((!empty($preferred_format)) && ($preferred_format == 3))
482
					{
483
						imagealphablending($dst_img, false);
0 ignored issues
show
Bug introduced by
It seems like $dst_img can also be of type false; however, parameter $image of imagealphablending() 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

483
						imagealphablending(/** @scrutinizer ignore-type */ $dst_img, false);
Loading history...
484
						if (function_exists('imagesavealpha'))
485
							imagesavealpha($dst_img, true);
0 ignored issues
show
Bug introduced by
It seems like $dst_img can also be of type false; however, parameter $image of imagesavealpha() 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

485
							imagesavealpha(/** @scrutinizer ignore-type */ $dst_img, true);
Loading history...
486
					}
487
				}
488
				else
489
					$dst_img = imagecreate($dst_width, $dst_height);
490
491
				// Resize it!
492
				if ($gd2)
493
					imagecopyresampled($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 false; however, parameter $dst_image of imagecopyresampled() 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

493
					imagecopyresampled(/** @scrutinizer ignore-type */ $dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
Loading history...
494
				else
495
					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 false; 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

495
					imagecopyresamplebicubic(/** @scrutinizer ignore-type */ $dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
Loading history...
496
			}
497
			else
498
				$dst_img = $src_img;
499
		}
500
		else
501
			$dst_img = $src_img;
502
503
		// Save the image as ...
504
		if (!empty($preferred_format) && ($preferred_format == 3) && function_exists('imagepng'))
505
			$success = imagepng($dst_img, $destName);
0 ignored issues
show
Bug introduced by
It seems like $dst_img can also be of type false; however, parameter $image of imagepng() 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

505
			$success = imagepng(/** @scrutinizer ignore-type */ $dst_img, $destName);
Loading history...
506
		elseif (!empty($preferred_format) && ($preferred_format == 1) && function_exists('imagegif'))
507
			$success = imagegif($dst_img, $destName);
0 ignored issues
show
Bug introduced by
It seems like $dst_img can also be of type false; however, parameter $image of imagegif() 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

507
			$success = imagegif(/** @scrutinizer ignore-type */ $dst_img, $destName);
Loading history...
508
		elseif (function_exists('imagejpeg'))
509
			$success = imagejpeg($dst_img, $destName, !empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
0 ignored issues
show
Bug introduced by
It seems like $dst_img can also be of type false; however, parameter $image of imagejpeg() 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

509
			$success = imagejpeg(/** @scrutinizer ignore-type */ $dst_img, $destName, !empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
Loading history...
510
511
		// Free the memory.
512
		imagedestroy($src_img);
513
		if ($dst_img != $src_img)
514
			imagedestroy($dst_img);
0 ignored issues
show
Bug introduced by
It seems like $dst_img can also be of type false; however, parameter $image of imagedestroy() 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

514
			imagedestroy(/** @scrutinizer ignore-type */ $dst_img);
Loading history...
515
516
		return $success;
517
	}
518
	else
519
		// Without GD, no image resizing at all.
520
		return false;
521
}
522
523
/**
524
 * Copy image.
525
 * Used when imagecopyresample() is not available.
526
 *
527
 * @param resource $dst_img The destination image - a GD image resource
528
 * @param resource $src_img The source image - a GD image resource
529
 * @param int $dst_x The "x" coordinate of the destination image
530
 * @param int $dst_y The "y" coordinate of the destination image
531
 * @param int $src_x The "x" coordinate of the source image
532
 * @param int $src_y The "y" coordinate of the source image
533
 * @param int $dst_w The width of the destination image
534
 * @param int $dst_h The height of the destination image
535
 * @param int $src_w The width of the destination image
536
 * @param int $src_h The height of the destination image
537
 */
538
function imagecopyresamplebicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)
539
{
540
	$palsize = imagecolorstotal($src_img);
541
	for ($i = 0; $i < $palsize; $i++)
542
	{
543
		$colors = imagecolorsforindex($src_img, $i);
544
		imagecolorallocate($dst_img, $colors['red'], $colors['green'], $colors['blue']);
545
	}
546
547
	$scaleX = ($src_w - 1) / $dst_w;
548
	$scaleY = ($src_h - 1) / $dst_h;
549
550
	$scaleX2 = (int) $scaleX / 2;
551
	$scaleY2 = (int) $scaleY / 2;
552
553
	for ($j = $src_y; $j < $dst_h; $j++)
554
	{
555
		$sY = (int) $j * $scaleY;
556
		$y13 = $sY + $scaleY2;
557
558
		for ($i = $src_x; $i < $dst_w; $i++)
559
		{
560
			$sX = (int) $i * $scaleX;
561
			$x34 = $sX + $scaleX2;
562
563
			$color1 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $y13));
564
			$color2 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $sY));
565
			$color3 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $y13));
566
			$color4 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $sY));
567
568
			$red = ($color1['red'] + $color2['red'] + $color3['red'] + $color4['red']) / 4;
569
			$green = ($color1['green'] + $color2['green'] + $color3['green'] + $color4['green']) / 4;
570
			$blue = ($color1['blue'] + $color2['blue'] + $color3['blue'] + $color4['blue']) / 4;
571
572
			$color = imagecolorresolve($dst_img, $red, $green, $blue);
573
			if ($color == -1)
574
			{
575
				if ($palsize++ < 256)
576
					imagecolorallocate($dst_img, $red, $green, $blue);
577
				$color = imagecolorclosest($dst_img, $red, $green, $blue);
578
			}
579
580
			imagesetpixel($dst_img, $i + $dst_x - $src_x, $j + $dst_y - $src_y, $color);
581
		}
582
	}
583
}
584
585
if (!function_exists('imagecreatefrombmp'))
586
{
587
	/**
588
	 * It is set only if it doesn't already exist (for forwards compatiblity.)
589
	 * It only supports uncompressed bitmaps.
590
	 *
591
	 * @param string $filename The name of the file
592
	 * @return resource An image identifier representing the bitmap image
593
	 * obtained from the given filename.
594
	 */
595
	function imagecreatefrombmp($filename)
596
	{
597
		global $gd2;
598
599
		$fp = fopen($filename, 'rb');
600
601
		$errors = error_reporting(0);
602
603
		$header = unpack('vtype/Vsize/Vreserved/Voffset', fread($fp, 14));
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fread() 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

603
		$header = unpack('vtype/Vsize/Vreserved/Voffset', fread(/** @scrutinizer ignore-type */ $fp, 14));
Loading history...
604
		$info = unpack('Vsize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vncolor/Vcolorimportant', fread($fp, 40));
605
606
		if ($header['type'] != 0x4D42)
607
			return false;
608
609
		if ($gd2)
610
			$dst_img = imagecreatetruecolor($info['width'], $info['height']);
611
		else
612
			$dst_img = imagecreate($info['width'], $info['height']);
613
614
		$palette_size = $header['offset'] - 54;
615
		$info['ncolor'] = $palette_size / 4;
616
617
		$palette = array();
618
619
		$palettedata = fread($fp, $palette_size);
620
		$n = 0;
621
		for ($j = 0; $j < $palette_size; $j++)
622
		{
623
			$b = ord($palettedata{$j++});
624
			$g = ord($palettedata{$j++});
625
			$r = ord($palettedata{$j++});
626
627
			$palette[$n++] = imagecolorallocate($dst_img, $r, $g, $b);
0 ignored issues
show
Bug introduced by
It seems like $dst_img can also be of type false; however, parameter $image of imagecolorallocate() 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

627
			$palette[$n++] = imagecolorallocate(/** @scrutinizer ignore-type */ $dst_img, $r, $g, $b);
Loading history...
628
		}
629
630
		$scan_line_size = ($info['bits'] * $info['width'] + 7) >> 3;
631
		$scan_line_align = $scan_line_size & 3 ? 4 - ($scan_line_size & 3) : 0;
632
633
		for ($y = 0, $l = $info['height'] - 1; $y < $info['height']; $y++, $l--)
634
		{
635
			fseek($fp, $header['offset'] + ($scan_line_size + $scan_line_align) * $l);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fseek() 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

635
			fseek(/** @scrutinizer ignore-type */ $fp, $header['offset'] + ($scan_line_size + $scan_line_align) * $l);
Loading history...
636
			$scan_line = fread($fp, $scan_line_size);
637
638
			if (strlen($scan_line) < $scan_line_size)
639
				continue;
640
641
			if ($info['bits'] == 32)
642
			{
643
				$x = 0;
644
				for ($j = 0; $j < $scan_line_size; $x++)
645
				{
646
					$b = ord($scan_line{$j++});
647
					$g = ord($scan_line{$j++});
648
					$r = ord($scan_line{$j++});
649
					$j++;
650
651
					$color = imagecolorexact($dst_img, $r, $g, $b);
0 ignored issues
show
Bug introduced by
It seems like $dst_img can also be of type false; however, parameter $image of imagecolorexact() 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

651
					$color = imagecolorexact(/** @scrutinizer ignore-type */ $dst_img, $r, $g, $b);
Loading history...
652
					if ($color == -1)
653
					{
654
						$color = imagecolorallocate($dst_img, $r, $g, $b);
655
656
						// Gah!  Out of colors?  Stupid GD 1... try anyhow.
657
						if ($color == -1)
658
							$color = imagecolorclosest($dst_img, $r, $g, $b);
0 ignored issues
show
Bug introduced by
It seems like $dst_img can also be of type false; however, parameter $image of imagecolorclosest() 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

658
							$color = imagecolorclosest(/** @scrutinizer ignore-type */ $dst_img, $r, $g, $b);
Loading history...
659
					}
660
661
					imagesetpixel($dst_img, $x, $y, $color);
0 ignored issues
show
Bug introduced by
It seems like $dst_img can also be of type false; however, parameter $image of imagesetpixel() 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

661
					imagesetpixel(/** @scrutinizer ignore-type */ $dst_img, $x, $y, $color);
Loading history...
662
				}
663
			}
664
			elseif ($info['bits'] == 24)
665
			{
666
				$x = 0;
667
				for ($j = 0; $j < $scan_line_size; $x++)
668
				{
669
					$b = ord($scan_line{$j++});
670
					$g = ord($scan_line{$j++});
671
					$r = ord($scan_line{$j++});
672
673
					$color = imagecolorexact($dst_img, $r, $g, $b);
674
					if ($color == -1)
675
					{
676
						$color = imagecolorallocate($dst_img, $r, $g, $b);
677
678
						// Gah!  Out of colors?  Stupid GD 1... try anyhow.
679
						if ($color == -1)
680
							$color = imagecolorclosest($dst_img, $r, $g, $b);
681
					}
682
683
					imagesetpixel($dst_img, $x, $y, $color);
684
				}
685
			}
686
			elseif ($info['bits'] == 16)
687
			{
688
				$x = 0;
689
				for ($j = 0; $j < $scan_line_size; $x++)
690
				{
691
					$b1 = ord($scan_line{$j++});
692
					$b2 = ord($scan_line{$j++});
693
694
					$word = $b2 * 256 + $b1;
695
696
					$b = (($word & 31) * 255) / 31;
697
					$g = ((($word >> 5) & 31) * 255) / 31;
698
					$r = ((($word >> 10) & 31) * 255) / 31;
699
700
					// Scale the image colors up properly.
701
					$color = imagecolorexact($dst_img, $r, $g, $b);
702
					if ($color == -1)
703
					{
704
						$color = imagecolorallocate($dst_img, $r, $g, $b);
705
706
						// Gah!  Out of colors?  Stupid GD 1... try anyhow.
707
						if ($color == -1)
708
							$color = imagecolorclosest($dst_img, $r, $g, $b);
709
					}
710
711
					imagesetpixel($dst_img, $x, $y, $color);
712
				}
713
			}
714
			elseif ($info['bits'] == 8)
715
			{
716
				$x = 0;
717
				for ($j = 0; $j < $scan_line_size; $x++)
718
					imagesetpixel($dst_img, $x, $y, $palette[ord($scan_line{$j++})]);
719
			}
720
			elseif ($info['bits'] == 4)
721
			{
722
				$x = 0;
723
				for ($j = 0; $j < $scan_line_size; $x++)
724
				{
725
					$byte = ord($scan_line{$j++});
726
727
					imagesetpixel($dst_img, $x, $y, $palette[(int) ($byte / 16)]);
728
729
					if (++$x < $info['width'])
730
						imagesetpixel($dst_img, $x, $y, $palette[$byte & 15]);
731
				}
732
			}
733
			elseif ($info['bits'] == 1)
734
			{
735
				$x = 0;
736
				for ($j = 0; $j < $scan_line_size; $x++)
737
				{
738
					$byte = ord($scan_line{$j++});
739
740
					imagesetpixel($dst_img, $x, $y, $palette[(($byte) & 128) != 0]);
741
742
					for ($shift = 1; $shift < 8; $shift++)
743
					{
744
						if (++$x < $info['width'])
745
							imagesetpixel($dst_img, $x, $y, $palette[(($byte << $shift) & 128) != 0]);
746
					}
747
				}
748
			}
749
		}
750
751
		fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fclose() 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

751
		fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
752
753
		error_reporting($errors);
754
755
		return $dst_img;
756
	}
757
}
758
759
/**
760
 * Writes a gif file to disk as a png file.
761
 *
762
 * @param resource $gif A gif image resource
763
 * @param string $lpszFileName The name of the file
764
 * @param int $background_color The background color
765
 * @return boolean Whether the operation was successful
766
 */
767
function gif_outputAsPng($gif, $lpszFileName, $background_color = -1)
768
{
769
	if (!isset($gif) || @get_class($gif) != 'cgif' || !$gif->loaded || $lpszFileName == '')
0 ignored issues
show
Bug introduced by
$gif of type resource is incompatible with the type object expected by parameter $object of get_class(). ( Ignorable by Annotation )

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

769
	if (!isset($gif) || @get_class(/** @scrutinizer ignore-type */ $gif) != 'cgif' || !$gif->loaded || $lpszFileName == '')
Loading history...
770
		return false;
771
772
	$fd = $gif->get_png_data($background_color);
773
	if (strlen($fd) <= 0)
774
		return false;
775
776
	if (!($fh = @fopen($lpszFileName, 'wb')))
777
		return false;
778
779
	@fwrite($fh, $fd, strlen($fd));
780
	@fflush($fh);
781
	@fclose($fh);
782
783
	return true;
784
}
785
786
/**
787
 * Show an image containing the visual verification code for registration.
788
 * Requires the GD extension.
789
 * Uses a random font for each letter from default_theme_dir/fonts.
790
 * Outputs a gif or a png (depending on whether gif ix supported).
791
 *
792
 * @param string $code The code to display
793
 * @return void|false False if something goes wrong.
794
 */
795
function showCodeImage($code)
796
{
797
	global $gd2, $settings, $user_info, $modSettings;
798
799
	// Note: The higher the value of visual_verification_type the harder the verification is - from 0 as disabled through to 4 as "Very hard".
800
801
	// What type are we going to be doing?
802
	$imageType = $modSettings['visual_verification_type'];
803
	// Special case to allow the admin center to show samples.
804
	if ($user_info['is_admin'] && isset($_GET['type']))
805
		$imageType = (int) $_GET['type'];
806
807
	// Some quick references for what we do.
808
	// Do we show no, low or high noise?
809
	$noiseType = $imageType == 3 ? 'low' : ($imageType == 4 ? 'high' : ($imageType == 5 ? 'extreme' : 'none'));
810
	// Can we have more than one font in use?
811
	$varyFonts = $imageType > 3 ? true : false;
812
	// Just a plain white background?
813
	$simpleBGColor = $imageType < 3 ? true : false;
814
	// Plain black foreground?
815
	$simpleFGColor = $imageType == 0 ? true : false;
816
	// High much to rotate each character.
817
	$rotationType = $imageType == 1 ? 'none' : ($imageType > 3 ? 'low' : 'high');
818
	// Do we show some characters inversed?
819
	$showReverseChars = $imageType > 3 ? true : false;
820
	// Special case for not showing any characters.
821
	$disableChars = $imageType == 0 ? true : false;
822
	// What do we do with the font colors. Are they one color, close to one color or random?
823
	$fontColorType = $imageType == 1 ? 'plain' : ($imageType > 3 ? 'random' : 'cyclic');
824
	// Are the fonts random sizes?
825
	$fontSizeRandom = $imageType > 3 ? true : false;
826
	// How much space between characters?
827
	$fontHorSpace = $imageType > 3 ? 'high' : ($imageType == 1 ? 'medium' : 'minus');
828
	// Where do characters sit on the image? (Fixed position or random/very random)
829
	$fontVerPos = $imageType == 1 ? 'fixed' : ($imageType > 3 ? 'vrandom' : 'random');
830
	// Make font semi-transparent?
831
	$fontTrans = $imageType == 2 || $imageType == 3 ? true : false;
832
	// Give the image a border?
833
	$hasBorder = $simpleBGColor;
834
835
	// The amount of pixels inbetween characters.
836
	$character_spacing = 1;
837
838
	// What color is the background - generally white unless we're on "hard".
839
	if ($simpleBGColor)
840
		$background_color = array(255, 255, 255);
841
	else
842
		$background_color = isset($settings['verification_background']) ? $settings['verification_background'] : array(236, 237, 243);
843
844
	// The color of the characters shown (red, green, blue).
845
	if ($simpleFGColor)
846
		$foreground_color = array(0, 0, 0);
847
	else
848
	{
849
		$foreground_color = array(64, 101, 136);
850
851
		// Has the theme author requested a custom color?
852
		if (isset($settings['verification_foreground']))
853
			$foreground_color = $settings['verification_foreground'];
854
	}
855
856
	if (!is_dir($settings['default_theme_dir'] . '/fonts'))
857
		return false;
858
859
	// Get a list of the available fonts.
860
	$font_dir = dir($settings['default_theme_dir'] . '/fonts');
861
	$font_list = array();
862
	$ttfont_list = array();
863
	$endian = unpack('v', pack('S', 0x00FF)) === 0x00FF;
864
	while ($entry = $font_dir->read())
865
	{
866
		if (preg_match('~^(.+)\.gdf$~', $entry, $matches) === 1)
867
		{
868
			if ($endian ^ (strpos($entry, '_end.gdf') === false))
869
				$font_list[] = $entry;
870
		}
871
		elseif (preg_match('~^(.+)\.ttf$~', $entry, $matches) === 1)
872
			$ttfont_list[] = $entry;
873
	}
874
875
	if (empty($font_list))
876
		return false;
877
878
	// For non-hard things don't even change fonts.
879
	if (!$varyFonts)
880
	{
881
		$font_list = array($font_list[0]);
882
		// Try use Screenge if we can - it looks good!
883
		if (in_array('AnonymousPro.ttf', $ttfont_list))
884
			$ttfont_list = array('AnonymousPro.ttf');
885
		else
886
			$ttfont_list = empty($ttfont_list) ? array() : array($ttfont_list[0]);
887
	}
888
889
	// Create a list of characters to be shown.
890
	$characters = array();
891
	$loaded_fonts = array();
892
	for ($i = 0; $i < strlen($code); $i++)
893
	{
894
		$characters[$i] = array(
895
			'id' => $code{$i},
896
			'font' => array_rand($font_list),
897
		);
898
899
		$loaded_fonts[$characters[$i]['font']] = null;
900
	}
901
902
	// Load all fonts and determine the maximum font height.
903
	foreach ($loaded_fonts as $font_index => $dummy)
904
		$loaded_fonts[$font_index] = imageloadfont($settings['default_theme_dir'] . '/fonts/' . $font_list[$font_index]);
905
906
	// Determine the dimensions of each character.
907
	if ($imageType == 4 || $imageType == 5)
908
		$extra = 80;
909
	else
910
		$extra = 45;
911
912
	$total_width = $character_spacing * strlen($code) + $extra;
913
	$max_height = 0;
914
	foreach ($characters as $char_index => $character)
915
	{
916
		$characters[$char_index]['width'] = imagefontwidth($loaded_fonts[$character['font']]);
917
		$characters[$char_index]['height'] = imagefontheight($loaded_fonts[$character['font']]);
918
919
		$max_height = max($characters[$char_index]['height'] + 5, $max_height);
920
		$total_width += $characters[$char_index]['width'];
921
	}
922
923
	// Create an image.
924
	$code_image = $gd2 ? imagecreatetruecolor($total_width, $max_height) : imagecreate($total_width, $max_height);
925
926
	// Draw the background.
927
	$bg_color = imagecolorallocate($code_image, $background_color[0], $background_color[1], $background_color[2]);
0 ignored issues
show
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imagecolorallocate() 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

927
	$bg_color = imagecolorallocate(/** @scrutinizer ignore-type */ $code_image, $background_color[0], $background_color[1], $background_color[2]);
Loading history...
928
	imagefilledrectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $bg_color);
0 ignored issues
show
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imagefilledrectangle() 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

928
	imagefilledrectangle(/** @scrutinizer ignore-type */ $code_image, 0, 0, $total_width - 1, $max_height - 1, $bg_color);
Loading history...
929
930
	// Randomize the foreground color a little.
931
	for ($i = 0; $i < 3; $i++)
932
		$foreground_color[$i] = mt_rand(max($foreground_color[$i] - 3, 0), min($foreground_color[$i] + 3, 255));
933
	$fg_color = imagecolorallocate($code_image, $foreground_color[0], $foreground_color[1], $foreground_color[2]);
934
935
	// Color for the dots.
936
	for ($i = 0; $i < 3; $i++)
937
		$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);
938
	$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...
939
940
	// Some squares/rectanges for new extreme level
941
	if ($noiseType == 'extreme')
942
	{
943
		for ($i = 0; $i < mt_rand(1, 5); $i++)
944
		{
945
			$x1 = mt_rand(0, $total_width / 4);
946
			$x2 = $x1 + round(rand($total_width / 4, $total_width));
947
			$y1 = mt_rand(0, $max_height);
948
			$y2 = $y1 + round(rand(0, $max_height / 3));
949
			imagefilledrectangle($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 $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

949
			imagefilledrectangle($code_image, $x1, $y1, /** @scrutinizer ignore-type */ $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 $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

949
			imagefilledrectangle($code_image, $x1, $y1, $x2, /** @scrutinizer ignore-type */ $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
950
		}
951
	}
952
953
	// Fill in the characters.
954
	if (!$disableChars)
955
	{
956
		$cur_x = 0;
957
		foreach ($characters as $char_index => $character)
958
		{
959
			// Can we use true type fonts?
960
			$can_do_ttf = function_exists('imagettftext');
961
962
			// How much rotation will we give?
963
			if ($rotationType == 'none')
964
				$angle = 0;
965
			else
966
				$angle = mt_rand(-100, 100) / ($rotationType == 'high' ? 6 : 10);
967
968
			// What color shall we do it?
969
			if ($fontColorType == 'cyclic')
970
			{
971
				// Here we'll pick from a set of acceptance types.
972
				$colors = array(
973
					array(10, 120, 95),
974
					array(46, 81, 29),
975
					array(4, 22, 154),
976
					array(131, 9, 130),
977
					array(0, 0, 0),
978
					array(143, 39, 31),
979
				);
980
				if (!isset($last_index))
981
					$last_index = -1;
982
				$new_index = $last_index;
983
				while ($last_index == $new_index)
984
					$new_index = mt_rand(0, count($colors) - 1);
985
				$char_fg_color = $colors[$new_index];
986
				$last_index = $new_index;
987
			}
988
			elseif ($fontColorType == 'random')
989
				$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]));
990
			else
991
				$char_fg_color = array($foreground_color[0], $foreground_color[1], $foreground_color[2]);
992
993
			if (!empty($can_do_ttf))
994
			{
995
				// GD2 handles font size differently.
996
				if ($fontSizeRandom)
997
					$font_size = $gd2 ? mt_rand(17, 19) : mt_rand(18, 25);
998
				else
999
					$font_size = $gd2 ? 18 : 24;
1000
1001
				// Work out the sizes - also fix the character width cause TTF not quite so wide!
1002
				$font_x = $fontHorSpace == 'minus' && $cur_x > 0 ? $cur_x - 3 : $cur_x + 5;
1003
				$font_y = $max_height - ($fontVerPos == 'vrandom' ? mt_rand(2, 8) : ($fontVerPos == 'random' ? mt_rand(3, 5) : 5));
1004
1005
				// What font face?
1006
				if (!empty($ttfont_list))
1007
					$fontface = $settings['default_theme_dir'] . '/fonts/' . $ttfont_list[mt_rand(0, count($ttfont_list) - 1)];
1008
1009
				// What color are we to do it in?
1010
				$is_reverse = $showReverseChars ? mt_rand(0, 1) : false;
1011
				$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]);
0 ignored issues
show
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imagecolorallocatealpha() 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

1011
				$char_color = function_exists('imagecolorallocatealpha') && $fontTrans ? imagecolorallocatealpha(/** @scrutinizer ignore-type */ $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]);
Loading history...
1012
1013
				$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...
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imagettftext() 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

1013
				$fontcord = @imagettftext(/** @scrutinizer ignore-type */ $code_image, $font_size, $angle, $font_x, $font_y, $char_color, $fontface, $character['id']);
Loading history...
1014
				if (empty($fontcord))
1015
					$can_do_ttf = false;
1016
				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...
1017
				{
1018
					imagefilledpolygon($code_image, $fontcord, 4, $fg_color);
0 ignored issues
show
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imagefilledpolygon() 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

1018
					imagefilledpolygon(/** @scrutinizer ignore-type */ $code_image, $fontcord, 4, $fg_color);
Loading history...
1019
					// Put the character back!
1020
					imagettftext($code_image, $font_size, $angle, $font_x, $font_y, $randomness_color, $fontface, $character['id']);
1021
				}
1022
1023
				if ($can_do_ttf)
1024
					$cur_x = max($fontcord[2], $fontcord[4]) + ($angle == 0 ? 0 : 3);
1025
			}
1026
1027
			if (!$can_do_ttf)
1028
			{
1029
				// Rotating the characters a little...
1030
				if (function_exists('imagerotate'))
1031
				{
1032
					$char_image = $gd2 ? imagecreatetruecolor($character['width'], $character['height']) : imagecreate($character['width'], $character['height']);
1033
					$char_bgcolor = imagecolorallocate($char_image, $background_color[0], $background_color[1], $background_color[2]);
1034
					imagefilledrectangle($char_image, 0, 0, $character['width'] - 1, $character['height'] - 1, $char_bgcolor);
1035
					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]));
0 ignored issues
show
Bug introduced by
It seems like $char_image can also be of type false; however, parameter $image of imagechar() 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

1035
					imagechar(/** @scrutinizer ignore-type */ $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]));
Loading history...
1036
					$rotated_char = imagerotate($char_image, mt_rand(-100, 100) / 10, $char_bgcolor);
0 ignored issues
show
Bug introduced by
It seems like $char_image can also be of type false; however, parameter $image of imagerotate() 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

1036
					$rotated_char = imagerotate(/** @scrutinizer ignore-type */ $char_image, mt_rand(-100, 100) / 10, $char_bgcolor);
Loading history...
1037
					imagecopy($code_image, $rotated_char, $cur_x, 0, 0, 0, $character['width'], $character['height']);
0 ignored issues
show
Bug introduced by
It seems like $rotated_char can also be of type false; however, parameter $src_im of imagecopy() 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

1037
					imagecopy($code_image, /** @scrutinizer ignore-type */ $rotated_char, $cur_x, 0, 0, 0, $character['width'], $character['height']);
Loading history...
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $dst_im of imagecopy() 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

1037
					imagecopy(/** @scrutinizer ignore-type */ $code_image, $rotated_char, $cur_x, 0, 0, 0, $character['width'], $character['height']);
Loading history...
1038
					imagedestroy($rotated_char);
0 ignored issues
show
Bug introduced by
It seems like $rotated_char can also be of type false; however, parameter $image of imagedestroy() 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

1038
					imagedestroy(/** @scrutinizer ignore-type */ $rotated_char);
Loading history...
1039
					imagedestroy($char_image);
1040
				}
1041
1042
				// Sorry, no rotation available.
1043
				else
1044
					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

1044
					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...
1045
				$cur_x += $character['width'] + $character_spacing;
1046
			}
1047
		}
1048
	}
1049
	// If disabled just show a cross.
1050
	else
1051
	{
1052
		imageline($code_image, 0, 0, $total_width, $max_height, $fg_color);
0 ignored issues
show
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imageline() 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

1052
		imageline(/** @scrutinizer ignore-type */ $code_image, 0, 0, $total_width, $max_height, $fg_color);
Loading history...
1053
		imageline($code_image, 0, $max_height, $total_width, 0, $fg_color);
1054
	}
1055
1056
	// Make the background color transparent on the hard image.
1057
	if (!$simpleBGColor)
1058
		imagecolortransparent($code_image, $bg_color);
0 ignored issues
show
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imagecolortransparent() 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

1058
		imagecolortransparent(/** @scrutinizer ignore-type */ $code_image, $bg_color);
Loading history...
1059
	if ($hasBorder)
1060
		imagerectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $fg_color);
0 ignored issues
show
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imagerectangle() 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

1060
		imagerectangle(/** @scrutinizer ignore-type */ $code_image, 0, 0, $total_width - 1, $max_height - 1, $fg_color);
Loading history...
1061
1062
	// Add some noise to the background?
1063
	if ($noiseType != 'none')
1064
	{
1065
		for ($i = mt_rand(0, 2); $i < $max_height; $i += mt_rand(1, 2))
1066
			for ($j = mt_rand(0, 10); $j < $total_width; $j += mt_rand(1, 10))
1067
				imagesetpixel($code_image, $j, $i, mt_rand(0, 1) ? $fg_color : $randomness_color);
0 ignored issues
show
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imagesetpixel() 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

1067
				imagesetpixel(/** @scrutinizer ignore-type */ $code_image, $j, $i, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
1068
1069
		// Put in some lines too?
1070
		if ($noiseType != 'extreme')
1071
		{
1072
			$num_lines = $noiseType == 'high' ? mt_rand(3, 7) : mt_rand(2, 5);
1073
			for ($i = 0; $i < $num_lines; $i++)
1074
			{
1075
				if (mt_rand(0, 1))
1076
				{
1077
					$x1 = mt_rand(0, $total_width);
1078
					$x2 = mt_rand(0, $total_width);
1079
					$y1 = 0;
1080
					$y2 = $max_height;
1081
				}
1082
				else
1083
				{
1084
					$y1 = mt_rand(0, $max_height);
1085
					$y2 = mt_rand(0, $max_height);
1086
					$x1 = 0;
1087
					$x2 = $total_width;
1088
				}
1089
				imagesetthickness($code_image, mt_rand(1, 2));
0 ignored issues
show
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imagesetthickness() 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

1089
				imagesetthickness(/** @scrutinizer ignore-type */ $code_image, mt_rand(1, 2));
Loading history...
1090
				imageline($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
1091
			}
1092
		}
1093
		else
1094
		{
1095
			// Put in some ellipse
1096
			$num_ellipse = $noiseType == 'extreme' ? mt_rand(6, 12) : mt_rand(2, 6);
1097
			for ($i = 0; $i < $num_ellipse; $i++)
1098
			{
1099
				$x1 = round(rand(($total_width / 4) * -1, $total_width + ($total_width / 4)));
1100
				$x2 = round(rand($total_width / 2, 2 * $total_width));
1101
				$y1 = round(rand(($max_height / 4) * -1, $max_height + ($max_height / 4)));
1102
				$y2 = round(rand($max_height / 2, 2 * $max_height));
1103
				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

1103
				imageellipse($code_image, $x1, $y1, /** @scrutinizer ignore-type */ $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

1103
				imageellipse($code_image, $x1, $y1, $x2, /** @scrutinizer ignore-type */ $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

1103
				imageellipse($code_image, $x1, /** @scrutinizer ignore-type */ $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imageellipse() 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

1103
				imageellipse(/** @scrutinizer ignore-type */ $code_image, $x1, $y1, $x2, $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

1103
				imageellipse($code_image, /** @scrutinizer ignore-type */ $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
1104
			}
1105
		}
1106
	}
1107
1108
	// Show the image.
1109
	if (function_exists('imagegif'))
1110
	{
1111
		header('content-type: image/gif');
1112
		imagegif($code_image);
0 ignored issues
show
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imagegif() 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

1112
		imagegif(/** @scrutinizer ignore-type */ $code_image);
Loading history...
1113
	}
1114
	else
1115
	{
1116
		header('content-type: image/png');
1117
		imagepng($code_image);
0 ignored issues
show
Bug introduced by
It seems like $code_image can also be of type false; however, parameter $image of imagepng() 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

1117
		imagepng(/** @scrutinizer ignore-type */ $code_image);
Loading history...
1118
	}
1119
1120
	// Bail out.
1121
	imagedestroy($code_image);
1122
	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...
1123
}
1124
1125
/**
1126
 * Show a letter for the visual verification code.
1127
 * Alternative function for showCodeImage() in case GD is missing.
1128
 * Includes an image from a random sub directory of default_theme_dir/fonts.
1129
 *
1130
 * @param string $letter A letter to show as an image
1131
 * @return void|false False if something went wrong
1132
 */
1133
function showLetterImage($letter)
1134
{
1135
	global $settings;
1136
1137
	if (!is_dir($settings['default_theme_dir'] . '/fonts'))
1138
		return false;
1139
1140
	// Get a list of the available font directories.
1141
	$font_dir = dir($settings['default_theme_dir'] . '/fonts');
1142
	$font_list = array();
1143
	while ($entry = $font_dir->read())
1144
		if ($entry[0] !== '.' && is_dir($settings['default_theme_dir'] . '/fonts/' . $entry) && file_exists($settings['default_theme_dir'] . '/fonts/' . $entry . '.gdf'))
1145
			$font_list[] = $entry;
1146
1147
	if (empty($font_list))
1148
		return false;
1149
1150
	// Pick a random font.
1151
	$random_font = $font_list[array_rand($font_list)];
1152
1153
	// Check if the given letter exists.
1154
	if (!file_exists($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.png'))
1155
		return false;
1156
1157
	// Include it!
1158
	header('content-type: image/png');
1159
	include($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.png');
1160
1161
	// Nothing more to come.
1162
	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...
1163
}
1164
1165
?>