Passed
Push — release-2.1 ( 0e5bfc...908430 )
by Mathias
07:53 queued 12s
created

resizeImageFile()   D

Complexity

Conditions 26
Paths 43

Size

Total Lines 76
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 26
eloc 42
nc 43
nop 5
dl 0
loc 76
rs 4.1666
c 2
b 0
f 0

How to fix   Long Method    Complexity   

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:

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 2021 Simple Machines and individual contributors
16
 * @license https://www.simplemachines.org/about/smf/license.php BSD
17
 *
18
 * @version 2.1 RC4
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 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 resource, thus it always evaluated to false.
Loading history...
336
	{
337
		$fileContents = fetch_web_data($source);
338
339
		$mime_valid = check_mime_type($fileContents, implode('|', array_map('image_type_to_mime_type', array_keys($default_formats))));
340
		if (empty($mime_valid))
341
			return false;
342
343
		fwrite($fp_destination, $fileContents);
344
		fclose($fp_destination);
345
346
		$sizes = @getimagesize($destination);
347
	}
348
	elseif ($fp_destination)
0 ignored issues
show
introduced by
$fp_destination is of type resource, thus it always evaluated to false.
Loading history...
349
	{
350
		$mime_valid = check_mime_type($source, implode('|', array_map('image_type_to_mime_type', array_keys($default_formats))), true);
351
		if (empty($mime_valid))
352
			return false;
353
354
		$sizes = @getimagesize($source);
355
356
		$fp_source = fopen($source, 'rb');
357
		if ($fp_source !== false)
358
		{
359
			while (!feof($fp_source))
360
				fwrite($fp_destination, fread($fp_source, 8192));
361
			fclose($fp_source);
362
		}
363
		else
364
			$sizes = array(-1, -1, -1);
365
		fclose($fp_destination);
366
	}
367
368
	// We can't get to the file. or a previous getimagesize failed.
369
	if (empty($sizes))
370
		$sizes = array(-1, -1, -1);
371
372
	// See if we have -or- can get the needed memory for this operation
373
	// ImageMagick isn't subject to PHP's memory limits :)
374
	if (!(checkIMagick() || checkMagickWand()) && checkGD() && !imageMemoryCheck($sizes))
375
		return false;
376
377
	// A known and supported format?
378
	// @todo test PSD and gif.
379
	if ((checkImagick() || checkMagickWand()) && isset($default_formats[$sizes[2]]))
380
	{
381
		return resizeImage(null, $destination, null, null, $max_width, $max_height, true, $preferred_format);
382
	}
383
	elseif (checkGD() && isset($default_formats[$sizes[2]]) && function_exists('imagecreatefrom' . $default_formats[$sizes[2]]))
384
	{
385
		$imagecreatefrom = 'imagecreatefrom' . $default_formats[$sizes[2]];
386
		if ($src_img = @$imagecreatefrom($destination))
387
		{
388
			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...
389
		}
390
	}
391
392
	return false;
393
}
394
395
/**
396
 * Resizes src_img proportionally to fit within max_width and max_height limits
397
 * if it is too large.
398
 * If GD2 is present, it'll use it to achieve better quality.
399
 * It saves the new image to destination_filename, as preferred_format
400
 * if possible, default is jpeg.
401
 *
402
 * Uses Imagemagick (IMagick or MagickWand extension) or GD
403
 *
404
 * @param resource $src_img The source image
405
 * @param string $destName The path to the destination image
406
 * @param int $src_width The width of the source image
407
 * @param int $src_height The height of the source image
408
 * @param int $max_width The maximum allowed width
409
 * @param int $max_height The maximum allowed height
410
 * @param bool $force_resize = false Whether to forcibly resize it
411
 * @param int $preferred_format - 1 for gif, 2 for jpeg, 3 for png, 6 for bmp or 15 for wbmp
412
 * @return bool Whether the resize was successful
413
 */
414
function resizeImage($src_img, $destName, $src_width, $src_height, $max_width, $max_height, $force_resize = false, $preferred_format = 0)
415
{
416
	global $gd2, $modSettings;
417
418
	$orientation = 0;
419
	if (function_exists('exif_read_data') && ($exif_data = @exif_read_data($destName)) !== false && !empty($exif_data['Orientation']))
420
		$orientation = $exif_data['Orientation'];
421
422
	if (checkImagick() || checkMagickWand())
423
	{
424
		static $default_formats = array(
425
			'1' => 'gif',
426
			'2' => 'jpeg',
427
			'3' => 'png',
428
			'6' => 'bmp',
429
			'15' => 'wbmp'
430
		);
431
		$preferred_format = empty($preferred_format) || !isset($default_formats[$preferred_format]) ? 2 : $preferred_format;
432
433
		if (checkImagick())
434
		{
435
			$imagick = New Imagick($destName);
436
			$src_width = empty($src_width) ? $imagick->getImageWidth() : $src_width;
437
			$src_height = empty($src_height) ? $imagick->getImageHeight() : $src_height;
438
			$dest_width = empty($max_width) ? $src_width : $max_width;
439
			$dest_height = empty($max_height) ? $src_height : $max_height;
440
441
			if ($default_formats[$preferred_format] == 'jpeg')
442
				$imagick->setCompressionQuality(!empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
443
444
			$imagick->setImageFormat($default_formats[$preferred_format]);
445
			$imagick->resizeImage($dest_width, $dest_height, Imagick::FILTER_LANCZOS, 1, true);
446
447
			if ($orientation > 1 && $preferred_format == 3)
448
			{
449
				if (in_array($orientation, [3, 4]))
450
					$imagick->rotateImage('#00000000', 180);
451
				elseif (in_array($orientation, [5, 6]))
452
					$imagick->rotateImage('#00000000', 90);
453
				elseif (in_array($orientation, [7, 8]))
454
					$imagick->rotateImage('#00000000', 270);
455
456
				if (in_array($orientation, [2, 4, 5, 7]))
457
					$imagick->flopImage();
458
			}
459
			$success = $imagick->writeImage($destName);
460
		}
461
		else
462
		{
463
			$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

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

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

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

466
			$src_height = empty($src_height) ? /** @scrutinizer ignore-call */ MagickGetImageSize($magick_wand) : $src_height;
Loading history...
467
			$dest_width = empty($max_width) ? $src_width : $max_width;
468
			$dest_height = empty($max_height) ? $src_height : $max_height;
469
470
			if ($default_formats[$preferred_format] == 'jpeg')
471
				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

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

473
			/** @scrutinizer ignore-call */ 
474
   MagickSetImageFormat($magick_wand, $default_formats[$preferred_format]);
Loading history...
474
			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

474
			/** @scrutinizer ignore-call */ 
475
   MagickResizeImage($magick_wand, $dest_width, $dest_height, MW_LanczosFilter, 1, true);
Loading history...
475
476
			if ($orientation > 1)
477
			{
478
				if (in_array($orientation, [3, 4]))
479
					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

479
					MagickResizeImage($magick_wand, /** @scrutinizer ignore-call */ NewPixelWand('white'), 180);
Loading history...
480
				elseif (in_array($orientation, [5, 6]))
481
					MagickResizeImage($magick_wand, NewPixelWand('white'), 90);
482
				elseif (in_array($orientation, [7, 8]))
483
					MagickResizeImage($magick_wand, NewPixelWand('white'), 270);
484
485
				if (in_array($orientation, [2, 4, 5, 7]))
486
					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

486
					/** @scrutinizer ignore-call */ 
487
     MagickFlopImage($magick_wand);
Loading history...
487
			}
488
			$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

488
			$success = /** @scrutinizer ignore-call */ MagickWriteImage($magick_wand, $destName);
Loading history...
489
		}
490
491
		return !empty($success);
492
	}
493
	elseif (checkGD())
494
	{
495
		$success = false;
496
497
		// Determine whether to resize to max width or to max height (depending on the limits.)
498
		if (!empty($max_width) || !empty($max_height))
499
		{
500
			if (!empty($max_width) && (empty($max_height) || round($src_height * $max_width / $src_width) <= $max_height))
501
			{
502
				$dst_width = $max_width;
503
				$dst_height = round($src_height * $max_width / $src_width);
504
			}
505
			elseif (!empty($max_height))
506
			{
507
				$dst_width = round($src_width * $max_height / $src_height);
508
				$dst_height = $max_height;
509
			}
510
511
			// Don't bother resizing if it's already smaller...
512
			if (!empty($dst_width) && !empty($dst_height) && ($dst_width < $src_width || $dst_height < $src_height || $force_resize))
513
			{
514
				// (make a true color image, because it just looks better for resizing.)
515
				if ($gd2)
516
				{
517
					$dst_img = imagecreatetruecolor($dst_width, $dst_height);
518
519
					// Deal nicely with a PNG - because we can.
520
					if ((!empty($preferred_format)) && ($preferred_format == 3))
521
					{
522
						imagealphablending($dst_img, false);
523
						if (function_exists('imagesavealpha'))
524
							imagesavealpha($dst_img, true);
525
					}
526
				}
527
				else
528
					$dst_img = imagecreate($dst_width, $dst_height);
529
530
				// Resize it!
531
				if ($gd2)
532
					imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
533
				else
534
					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

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

1000
			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

1000
			imagefilledrectangle($code_image, $x1, $y1, $x2, /** @scrutinizer ignore-type */ $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
1001
		}
1002
	}
1003
1004
	// Fill in the characters.
1005
	if (!$disableChars)
1006
	{
1007
		$cur_x = 0;
1008
		foreach ($characters as $char_index => $character)
1009
		{
1010
			// Can we use true type fonts?
1011
			$can_do_ttf = function_exists('imagettftext');
1012
1013
			// How much rotation will we give?
1014
			if ($rotationType == 'none')
1015
				$angle = 0;
1016
			else
1017
				$angle = mt_rand(-100, 100) / ($rotationType == 'high' ? 6 : 10);
1018
1019
			// What color shall we do it?
1020
			if ($fontColorType == 'cyclic')
1021
			{
1022
				// Here we'll pick from a set of acceptance types.
1023
				$colors = array(
1024
					array(10, 120, 95),
1025
					array(46, 81, 29),
1026
					array(4, 22, 154),
1027
					array(131, 9, 130),
1028
					array(0, 0, 0),
1029
					array(143, 39, 31),
1030
				);
1031
				if (!isset($last_index))
1032
					$last_index = -1;
1033
				$new_index = $last_index;
1034
				while ($last_index == $new_index)
1035
					$new_index = mt_rand(0, count($colors) - 1);
1036
				$char_fg_color = $colors[$new_index];
1037
				$last_index = $new_index;
1038
			}
1039
			elseif ($fontColorType == 'random')
1040
				$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]));
1041
			else
1042
				$char_fg_color = array($foreground_color[0], $foreground_color[1], $foreground_color[2]);
1043
1044
			if (!empty($can_do_ttf))
1045
			{
1046
				// GD2 handles font size differently.
1047
				if ($fontSizeRandom)
1048
					$font_size = $gd2 ? mt_rand(17, 19) : mt_rand(18, 25);
1049
				else
1050
					$font_size = $gd2 ? 18 : 24;
1051
1052
				// Work out the sizes - also fix the character width cause TTF not quite so wide!
1053
				$font_x = $fontHorSpace == 'minus' && $cur_x > 0 ? $cur_x - 3 : $cur_x + 5;
1054
				$font_y = $max_height - ($fontVerPos == 'vrandom' ? mt_rand(2, 8) : ($fontVerPos == 'random' ? mt_rand(3, 5) : 5));
1055
1056
				// What font face?
1057
				if (!empty($ttfont_list))
1058
					$fontface = $settings['default_theme_dir'] . '/fonts/' . $ttfont_list[mt_rand(0, count($ttfont_list) - 1)];
1059
1060
				// What color are we to do it in?
1061
				$is_reverse = $showReverseChars ? mt_rand(0, 1) : false;
1062
				$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]);
1063
1064
				$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...
1065
				if (empty($fontcord))
1066
					$can_do_ttf = false;
1067
				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...
1068
				{
1069
					imagefilledpolygon($code_image, $fontcord, 4, $fg_color);
1070
					// Put the character back!
1071
					imagettftext($code_image, $font_size, $angle, $font_x, $font_y, $randomness_color, $fontface, $character['id']);
1072
				}
1073
1074
				if ($can_do_ttf)
1075
					$cur_x = max($fontcord[2], $fontcord[4]) + ($angle == 0 ? 0 : 3);
1076
			}
1077
1078
			if (!$can_do_ttf)
1079
			{
1080
				// Rotating the characters a little...
1081
				if (function_exists('imagerotate'))
1082
				{
1083
					$char_image = $gd2 ? imagecreatetruecolor($character['width'], $character['height']) : imagecreate($character['width'], $character['height']);
1084
					$char_bgcolor = imagecolorallocate($char_image, $background_color[0], $background_color[1], $background_color[2]);
1085
					imagefilledrectangle($char_image, 0, 0, $character['width'] - 1, $character['height'] - 1, $char_bgcolor);
1086
					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]));
1087
					$rotated_char = imagerotate($char_image, mt_rand(-100, 100) / 10, $char_bgcolor);
1088
					imagecopy($code_image, $rotated_char, $cur_x, 0, 0, 0, $character['width'], $character['height']);
1089
					imagedestroy($rotated_char);
1090
					imagedestroy($char_image);
1091
				}
1092
1093
				// Sorry, no rotation available.
1094
				else
1095
					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

1095
					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...
1096
				$cur_x += $character['width'] + $character_spacing;
1097
			}
1098
		}
1099
	}
1100
	// If disabled just show a cross.
1101
	else
1102
	{
1103
		imageline($code_image, 0, 0, $total_width, $max_height, $fg_color);
1104
		imageline($code_image, 0, $max_height, $total_width, 0, $fg_color);
1105
	}
1106
1107
	// Make the background color transparent on the hard image.
1108
	if (!$simpleBGColor)
1109
		imagecolortransparent($code_image, $bg_color);
1110
	if ($hasBorder)
1111
		imagerectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $fg_color);
1112
1113
	// Add some noise to the background?
1114
	if ($noiseType != 'none')
1115
	{
1116
		for ($i = mt_rand(0, 2); $i < $max_height; $i += mt_rand(1, 2))
1117
			for ($j = mt_rand(0, 10); $j < $total_width; $j += mt_rand(1, 10))
1118
				imagesetpixel($code_image, $j, $i, mt_rand(0, 1) ? $fg_color : $randomness_color);
1119
1120
		// Put in some lines too?
1121
		if ($noiseType != 'extreme')
1122
		{
1123
			$num_lines = $noiseType == 'high' ? mt_rand(3, 7) : mt_rand(2, 5);
1124
			for ($i = 0; $i < $num_lines; $i++)
1125
			{
1126
				if (mt_rand(0, 1))
1127
				{
1128
					$x1 = mt_rand(0, $total_width);
1129
					$x2 = mt_rand(0, $total_width);
1130
					$y1 = 0;
1131
					$y2 = $max_height;
1132
				}
1133
				else
1134
				{
1135
					$y1 = mt_rand(0, $max_height);
1136
					$y2 = mt_rand(0, $max_height);
1137
					$x1 = 0;
1138
					$x2 = $total_width;
1139
				}
1140
				imagesetthickness($code_image, mt_rand(1, 2));
1141
				imageline($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
1142
			}
1143
		}
1144
		else
1145
		{
1146
			// Put in some ellipse
1147
			$num_ellipse = $noiseType == 'extreme' ? mt_rand(6, 12) : mt_rand(2, 6);
1148
			for ($i = 0; $i < $num_ellipse; $i++)
1149
			{
1150
				$x1 = round(rand(($total_width / 4) * -1, $total_width + ($total_width / 4)));
1151
				$x2 = round(rand($total_width / 2, 2 * $total_width));
1152
				$y1 = round(rand(($max_height / 4) * -1, $max_height + ($max_height / 4)));
1153
				$y2 = round(rand($max_height / 2, 2 * $max_height));
1154
				imageellipse($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
0 ignored issues
show
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

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

1154
				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

1154
				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

1154
				imageellipse($code_image, $x1, $y1, $x2, /** @scrutinizer ignore-type */ $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
Loading history...
1155
			}
1156
		}
1157
	}
1158
1159
	// Show the image.
1160
	if (function_exists('imagegif'))
1161
	{
1162
		header('content-type: image/gif');
1163
		imagegif($code_image);
1164
	}
1165
	else
1166
	{
1167
		header('content-type: image/png');
1168
		imagepng($code_image);
1169
	}
1170
1171
	// Bail out.
1172
	imagedestroy($code_image);
1173
	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...
1174
}
1175
1176
/**
1177
 * Show a letter for the visual verification code.
1178
 * Alternative function for showCodeImage() in case GD is missing.
1179
 * Includes an image from a random sub directory of default_theme_dir/fonts.
1180
 *
1181
 * @param string $letter A letter to show as an image
1182
 * @return void|false False if something went wrong
1183
 */
1184
function showLetterImage($letter)
1185
{
1186
	global $settings;
1187
1188
	if (!is_dir($settings['default_theme_dir'] . '/fonts'))
1189
		return false;
1190
1191
	// Get a list of the available font directories.
1192
	$font_dir = dir($settings['default_theme_dir'] . '/fonts');
1193
	$font_list = array();
1194
	while ($entry = $font_dir->read())
1195
		if ($entry[0] !== '.' && is_dir($settings['default_theme_dir'] . '/fonts/' . $entry) && file_exists($settings['default_theme_dir'] . '/fonts/' . $entry . '.gdf'))
1196
			$font_list[] = $entry;
1197
1198
	if (empty($font_list))
1199
		return false;
1200
1201
	// Pick a random font.
1202
	$random_font = $font_list[array_rand($font_list)];
1203
1204
	// Check if the given letter exists.
1205
	if (!file_exists($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . strtoupper($letter) . '.png'))
1206
		return false;
1207
1208
	// Include it!
1209
	header('content-type: image/png');
1210
	include($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . strtoupper($letter) . '.png');
1211
1212
	// Nothing more to come.
1213
	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...
1214
}
1215
1216
?>