Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

Subs-Graphics.php ➔ imagecreatefrombmp()   D

Complexity

Conditions 28
Paths 73

Size

Total Lines 158
Code Lines 90

Duplication

Lines 40
Ratio 25.32 %

Importance

Changes 0
Metric Value
cc 28
eloc 90
nc 73
nop 1
dl 40
loc 158
rs 4.4803
c 0
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 http://www.simplemachines.org
15
 * @copyright 2017 Simple Machines and individual contributors
16
 * @license http://www.simplemachines.org/about/smf/license.php BSD
17
 *
18
 * @version 2.1 Beta 4
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 View Code Duplication
	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', empty($avatar_hash) ? $destName : $path . '/' . $attachID . '_' . $avatar_hash . '.dat'))
91
		{
92
			$destName = empty($avatar_hash) ? $destName : $path . '/' . $attachID . '_' . $avatar_hash . '.dat';
0 ignored issues
show
Bug introduced by
The variable $path does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
93
			list ($width, $height) = getimagesize($destName);
94
			$mime_type = 'image/' . $ext;
95
96
			// Write filesize in the database.
97
			$smcFunc['db_query']('', '
98
				UPDATE {db_prefix}attachments
99
				SET size = {int:filesize}, width = {int:width}, height = {int:height},
100
					mime_type = {string:mime_type}
101
				WHERE id_attach = {int:current_attachment}',
102
				array(
103
					'filesize' => filesize($destName),
104
					'width' => (int) $width,
105
					'height' => (int) $height,
106
					'current_attachment' => $attachID,
107
					'mime_type' => $mime_type,
108
				)
109
			);
110
			return true;
111
		}
112
		else
113
			return false;
114
	}
115
	else
116
	{
117
		$smcFunc['db_query']('', '
118
			DELETE FROM {db_prefix}attachments
119
			WHERE id_attach = {int:current_attachment}',
120
			array(
121
				'current_attachment' => $attachID,
122
			)
123
		);
124
125
		@unlink($destName . '.tmp');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
126
		return false;
127
	}
128
}
129
130
/**
131
 * Create a thumbnail of the given source.
132
 *
133
 * @uses resizeImageFile() function to achieve the resize.
134
 *
135
 * @param string $source The name of the source image
136
 * @param int $max_width The maximum allowed width
137
 * @param int $max_height The maximum allowed height
138
 * @return boolean Whether the thumbnail creation was successful.
139
 */
140
function createThumbnail($source, $max_width, $max_height)
141
{
142
	global $modSettings;
143
144
	$destName = $source . '_thumb.tmp';
145
146
	// Do the actual resize.
147 View Code Duplication
	if (!empty($modSettings['attachment_thumb_png']))
148
		$success = resizeImageFile($source, $destName, $max_width, $max_height, 3);
149
	else
150
		$success = resizeImageFile($source, $destName, $max_width, $max_height);
151
152
	// Okay, we're done with the temporary stuff.
153
	$destName = substr($destName, 0, -4);
154
155
	if ($success && @rename($destName . '.tmp', $destName))
156
		return true;
157
	else
158
	{
159
		@unlink($destName . '.tmp');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
160
		@touch($destName);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
161
		return false;
162
	}
163
}
164
165
/**
166
 * Used to re-econodes an image to a specified image format
167
 * - creates a copy of the file at the same location as fileName.
168
 * - the file would have the format preferred_format if possible, otherwise the default format is jpeg.
169
 * - the function makes sure that all non-essential image contents are disposed.
170
 *
171
 * @param string $fileName The path to the file
172
 * @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
173
 * @return boolean Whether the reencoding was successful
0 ignored issues
show
Documentation introduced by
Should the return type not be false|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
174
 */
175
function reencodeImage($fileName, $preferred_format = 0)
176
{
177
	if (!resizeImageFile($fileName, $fileName . '.tmp', null, null, $preferred_format))
178
	{
179
		if (file_exists($fileName . '.tmp'))
180
			unlink($fileName . '.tmp');
181
182
		return false;
183
	}
184
185
	if (!unlink($fileName))
186
		return false;
187
188
	if (!rename($fileName . '.tmp', $fileName))
189
		return false;
190
}
191
192
/**
193
 * Searches through the file to see if there's potentially harmful non-binary content.
194
 * - if extensiveCheck is true, searches for asp/php short tags as well.
195
 *
196
 * @param string $fileName The path to the file
197
 * @param bool $extensiveCheck Whether to perform extensive checks
198
 * @return bool Whether the image appears to be safe
199
 */
200
function checkImageContents($fileName, $extensiveCheck = false)
201
{
202
	$fp = fopen($fileName, 'rb');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fp. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
203
	if (!$fp)
204
		fatal_lang_error('attach_timeout');
205
206
	$prev_chunk = '';
207
	while (!feof($fp))
208
	{
209
		$cur_chunk = fread($fp, 8192);
210
211
		// Though not exhaustive lists, better safe than sorry.
212
		if (!empty($extensiveCheck))
213
		{
214
			// Paranoid check.  Use this if you have reason to distrust your host's security config.
215
			// Will result in MANY false positives, and is not suitable for photography sites.
216
			if (preg_match('~(iframe|\\<\\?|\\<%|html|eval|body|script\W|(?-i)[CFZ]WS[\x01-\x0E])~i', $prev_chunk . $cur_chunk) === 1)
217
			{
218
				fclose($fp);
219
				return false;
220
			}
221
		}
222
		else
223
		{
224
			// Check for potential infection - focus on clues for inline php & flash.
225
			// Will result in significantly fewer false positives than the paranoid check.
226
			if (preg_match('~(\\<\\?php\s|(?-i)[CFZ]WS[\x01-\x0E])~i', $prev_chunk . $cur_chunk) === 1)
227
			{
228
				fclose($fp);
229
				return false;
230
			}
231
		}
232
		$prev_chunk = $cur_chunk;
233
	}
234
	fclose($fp);
235
236
	return true;
237
}
238
239
/**
240
 * Sets a global $gd2 variable needed by some functions to determine
241
 * whether the GD2 library is present.
242
 *
243
 * @return bool Whether or not GD1 is available.
244
 */
245
function checkGD()
246
{
247
	global $gd2;
248
249
	// Check to see if GD is installed and what version.
250
	if (($extensionFunctions = get_extension_funcs('gd')) === false)
251
		return false;
252
253
	// Also determine if GD2 is installed and store it in a global.
254
	$gd2 = in_array('imagecreatetruecolor', $extensionFunctions) && function_exists('imagecreatetruecolor');
255
256
	return true;
257
}
258
259
/**
260
 * Checks whether the Imagick class is present.
261
 *
262
 * @return bool Whether or not the Imagick extension is available.
263
 */
264
function checkImagick()
265
{
266
	return class_exists('Imagick', false);
267
}
268
269
/**
270
 * Checks whether the MagickWand extension is present.
271
 *
272
 * @return bool Whether or not the MagickWand extension is available.
273
 */
274
 function checkMagickWand()
275
 {
276
 	return function_exists('newMagickWand');
277
 }
278
279
/**
280
 * See if we have enough memory to thumbnail an image
281
 *
282
 * @param array $sizes image size
283
 * @return bool Whether we do
284
 */
285
function imageMemoryCheck($sizes)
286
{
287
	global $modSettings;
288
289
	// doing the old 'set it and hope' way?
290
	if (empty($modSettings['attachment_thumb_memory']))
291
	{
292
		setMemoryLimit('128M');
293
		return true;
294
	}
295
296
	// 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
297
	// 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
298
	// simply a shortcut of 8bpp, 3 channels, 1.66 overhead
299
	$needed_memory = ($sizes[0] * $sizes[1] * 5);
300
301
	// if we need more, lets try to get it
302
	return setMemoryLimit($needed_memory, true);
303
}
304
305
/**
306
 * Resizes an image from a remote location or a local file.
307
 * Puts the resized image at the destination location.
308
 * The file would have the format preferred_format if possible,
309
 * otherwise the default format is jpeg.
310
 *
311
 * @param string $source The path to the source image
312
 * @param string $destination The path to the destination image
313
 * @param int $max_width The maximum allowed width
314
 * @param int $max_height The maximum allowed height
315
 * @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)
316
 * @return bool Whether it succeeded.
317
 */
318
function resizeImageFile($source, $destination, $max_width, $max_height, $preferred_format = 0)
319
{
320
	global $sourcedir;
321
322
	// Nothing to do without GD or IM/MW
323
	if (!checkGD() && !checkImagick() && !checkMagickWand())
324
		return false;
325
326
	static $default_formats = array(
327
		'1' => 'gif',
328
		'2' => 'jpeg',
329
		'3' => 'png',
330
		'6' => 'bmp',
331
		'15' => 'wbmp'
332
	);
333
334
	require_once($sourcedir . '/Subs-Package.php');
335
336
	// Get the image file, we have to work with something after all
337
	$fp_destination = fopen($destination, 'wb');
338
	if ($fp_destination && (substr($source, 0, 7) == 'http://' || substr($source, 0, 8) == 'https://'))
339
	{
340
		$fileContents = fetch_web_data($source);
341
342
		fwrite($fp_destination, $fileContents);
343
		fclose($fp_destination);
344
345
		$sizes = @getimagesize($destination);
346
	}
347
	elseif ($fp_destination)
348
	{
349
		$sizes = @getimagesize($source);
350
351
		$fp_source = fopen($source, 'rb');
352
		if ($fp_source !== false)
353
		{
354
			while (!feof($fp_source))
355
				fwrite($fp_destination, fread($fp_source, 8192));
356
			fclose($fp_source);
357
		}
358
		else
359
			$sizes = array(-1, -1, -1);
360
		fclose($fp_destination);
361
	}
362
	// We can't get to the file.
363
	else
364
		$sizes = array(-1, -1, -1);
365
366
	// See if we have -or- can get the needed memory for this operation
367
	// ImageMagick isn't subject to PHP's memory limits :)
368
	if (!(checkIMagick() || checkMagickWand()) && checkGD() && !imageMemoryCheck($sizes))
369
		return false;
370
371
	// A known and supported format?
372
	// @todo test PSD and gif.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
373
	if ((checkImagick() || checkMagickWand()) && isset($default_formats[$sizes[2]]))
374
	{
375
		return resizeImage(null, $destination, null, null, $max_width, $max_height, true, $preferred_format);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a resource.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
376
	}
377
	elseif (checkGD() && isset($default_formats[$sizes[2]]) && function_exists('imagecreatefrom' . $default_formats[$sizes[2]]))
378
	{
379
		$imagecreatefrom = 'imagecreatefrom' . $default_formats[$sizes[2]];
380
		if ($src_img = @$imagecreatefrom($destination))
381
		{
382
			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);
383
		}
384
	}
385
386
	return false;
387
}
388
389
/**
390
 * Resizes src_img proportionally to fit within max_width and max_height limits
391
 * if it is too large.
392
 * If GD2 is present, it'll use it to achieve better quality.
393
 * It saves the new image to destination_filename, as preferred_format
394
 * if possible, default is jpeg.
395
 * @uses Imagemagick (IMagick or MagickWand extension) or GD
396
 *
397
 * @param resource $src_img The source image
398
 * @param string $destName The path to the destination image
399
 * @param int $src_width The width of the source image
400
 * @param int $src_height The height of the source image
401
 * @param int $max_width The maximum allowed width
402
 * @param int $max_height The maximum allowed height
403
 * @param bool $force_resize = false Whether to forcibly resize it
404
 * @param int $preferred_format - 1 for gif, 2 for jpeg, 3 for png, 6 for bmp or 15 for wbmp
405
 * @return bool Whether the resize was successful
406
 */
407
function resizeImage($src_img, $destName, $src_width, $src_height, $max_width, $max_height, $force_resize = false, $preferred_format = 0)
408
{
409
	global $gd2, $modSettings;
410
411
	if (checkImagick() || checkMagickWand())
412
	{
413
		static $default_formats = array(
414
			'1' => 'gif',
415
			'2' => 'jpeg',
416
			'3' => 'png',
417
			'6' => 'bmp',
418
			'15' => 'wbmp'
419
		);
420
		$preferred_format = empty($preferred_format) || !isset($default_formats[$preferred_format]) ? 2 : $preferred_format;
421
422
		if (checkImagick())
423
		{
424
			$imagick = New Imagick($destName);
425
			$src_width = empty($src_width) ? $imagick->getImageWidth() : $src_width;
426
			$src_height = empty($src_height) ? $imagick->getImageHeight() : $src_height;
427
			$dest_width = empty($max_width) ? $src_width : $max_width;
428
			$dest_height = empty($max_height) ? $src_height : $max_height;
429
430
			if ($default_formats[$preferred_format] == 'jpeg')
431
				$imagick->setCompressionQuality(!empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
432
433
			$imagick->setImageFormat($default_formats[$preferred_format]);
434
			$imagick->resizeImage($dest_width, $dest_height, Imagick::FILTER_LANCZOS, 1, true);
435
			$success = $imagick->writeImage($destName);
436
		}
437
		else
438
		{
439
			$magick_wand = newMagickWand();
440
			MagickReadImage($magick_wand, $destName);
441
			$src_width = empty($src_width) ? MagickGetImageWidth($magick_wand) : $src_width;
442
			$src_height = empty($src_height) ? MagickGetImageSize($magick_wand) : $src_height;
443
			$dest_width = empty($max_width) ? $src_width : $max_width;
444
			$dest_height = empty($max_height) ? $src_height : $max_height;
445
446
			if ($default_formats[$preferred_format] == 'jpeg')
447
				MagickSetCompressionQuality($magick_wand, !empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
448
449
			MagickSetImageFormat($magick_wand, $default_formats[$preferred_format]);
450
			MagickResizeImage($magick_wand, $dest_width, $dest_height, MW_LanczosFilter, 1, true);
451
			$success = MagickWriteImage($magick_wand, $destName);
452
		}
453
454
		return !empty($success);
455
	}
456
	elseif (checkGD())
457
	{
458
		$success = false;
459
460
		// Determine whether to resize to max width or to max height (depending on the limits.)
461
		if (!empty($max_width) || !empty($max_height))
462
		{
463
			if (!empty($max_width) && (empty($max_height) || round($src_height * $max_width / $src_width) <= $max_height))
464
			{
465
				$dst_width = $max_width;
466
				$dst_height = round($src_height * $max_width / $src_width);
467
			}
468
			elseif (!empty($max_height))
469
			{
470
				$dst_width = round($src_width * $max_height / $src_height);
471
				$dst_height = $max_height;
472
			}
473
474
			// Don't bother resizing if it's already smaller...
475
			if (!empty($dst_width) && !empty($dst_height) && ($dst_width < $src_width || $dst_height < $src_height || $force_resize))
476
			{
477
				// (make a true color image, because it just looks better for resizing.)
478
				if ($gd2)
479
				{
480
					$dst_img = imagecreatetruecolor($dst_width, $dst_height);
481
482
					// Deal nicely with a PNG - because we can.
483
					if ((!empty($preferred_format)) && ($preferred_format == 3))
484
					{
485
						imagealphablending($dst_img, false);
486
						if (function_exists('imagesavealpha'))
487
							imagesavealpha($dst_img, true);
488
					}
489
				}
490
				else
491
					$dst_img = imagecreate($dst_width, $dst_height);
492
493
				// Resize it!
494
				if ($gd2)
495
					imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
496
				else
497
					imagecopyresamplebicubic($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
498
			}
499
			else
500
				$dst_img = $src_img;
501
		}
502
		else
503
			$dst_img = $src_img;
504
505
		// Save the image as ...
506
		if (!empty($preferred_format) && ($preferred_format == 3) && function_exists('imagepng'))
507
			$success = imagepng($dst_img, $destName);
508
		elseif (!empty($preferred_format) && ($preferred_format == 1) && function_exists('imagegif'))
509
			$success = imagegif($dst_img, $destName);
510
		elseif (function_exists('imagejpeg'))
511
			$success = imagejpeg($dst_img, $destName, !empty($modSettings['avatar_jpeg_quality']) ? $modSettings['avatar_jpeg_quality'] : 82);
512
513
		// Free the memory.
514
		imagedestroy($src_img);
515
		if ($dst_img != $src_img)
516
			imagedestroy($dst_img);
517
518
		return $success;
519
	}
520
	else
521
		// Without GD, no image resizing at all.
522
		return false;
523
}
524
525
/**
526
 * Copy image.
527
 * Used when imagecopyresample() is not available.
528
529
 * @param resource $dst_img The destination image - a GD image resource
530
 * @param resource $src_img The source image - a GD image resource
531
 * @param int $dst_x The "x" coordinate of the destination image
532
 * @param int $dst_y The "y" coordinate of the destination image
533
 * @param int $src_x The "x" coordinate of the source image
534
 * @param int $src_y The "y" coordinate of the source image
535
 * @param int $dst_w The width of the destination image
536
 * @param int $dst_h The height of the destination image
537
 * @param int $src_w The width of the destination image
538
 * @param int $src_h The height of the destination image
539
 */
540
function imagecopyresamplebicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)
541
{
542
	$palsize = imagecolorstotal($src_img);
543
	for ($i = 0; $i < $palsize; $i++)
544
	{
545
		$colors = imagecolorsforindex($src_img, $i);
546
		imagecolorallocate($dst_img, $colors['red'], $colors['green'], $colors['blue']);
547
	}
548
549
	$scaleX = ($src_w - 1) / $dst_w;
550
	$scaleY = ($src_h - 1) / $dst_h;
551
552
	$scaleX2 = (int) $scaleX / 2;
553
	$scaleY2 = (int) $scaleY / 2;
554
555
	for ($j = $src_y; $j < $dst_h; $j++)
556
	{
557
		$sY = (int) $j * $scaleY;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $sY. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
558
		$y13 = $sY + $scaleY2;
559
560
		for ($i = $src_x; $i < $dst_w; $i++)
561
		{
562
			$sX = (int) $i * $scaleX;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $sX. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
563
			$x34 = $sX + $scaleX2;
564
565
			$color1 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $y13));
566
			$color2 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $sY));
567
			$color3 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $y13));
568
			$color4 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $sY));
569
570
			$red = ($color1['red'] + $color2['red'] + $color3['red'] + $color4['red']) / 4;
571
			$green = ($color1['green'] + $color2['green'] + $color3['green'] + $color4['green']) / 4;
572
			$blue = ($color1['blue'] + $color2['blue'] + $color3['blue'] + $color4['blue']) / 4;
573
574
			$color = imagecolorresolve($dst_img, $red, $green, $blue);
575
			if ($color == -1)
576
			{
577
				if ($palsize++ < 256)
578
					imagecolorallocate($dst_img, $red, $green, $blue);
579
				$color = imagecolorclosest($dst_img, $red, $green, $blue);
580
			}
581
582
			imagesetpixel($dst_img, $i + $dst_x - $src_x, $j + $dst_y - $src_y, $color);
583
		}
584
	}
585
}
586
587
if (!function_exists('imagecreatefrombmp'))
588
{
589
	/**
590
	 * It is set only if it doesn't already exist (for forwards compatiblity.)
591
	 * It only supports uncompressed bitmaps.
592
	 *
593
	 * @param string $filename The name of the file
594
	 * @return resource An image identifier representing the bitmap image
0 ignored issues
show
Documentation introduced by
Should the return type not be false|resource?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
595
	 * obtained from the given filename.
596
	 */
597
	function imagecreatefrombmp($filename)
598
	{
599
		global $gd2;
600
601
		$fp = fopen($filename, 'rb');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fp. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
602
603
		$errors = error_reporting(0);
604
605
		$header = unpack('vtype/Vsize/Vreserved/Voffset', fread($fp, 14));
606
		$info = unpack('Vsize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vncolor/Vcolorimportant', fread($fp, 40));
607
608
		if ($header['type'] != 0x4D42)
609
			return false;
610
611
		if ($gd2)
612
			$dst_img = imagecreatetruecolor($info['width'], $info['height']);
613
		else
614
			$dst_img = imagecreate($info['width'], $info['height']);
615
616
		$palette_size = $header['offset'] - 54;
617
		$info['ncolor'] = $palette_size / 4;
618
619
		$palette = array();
620
621
		$palettedata = fread($fp, $palette_size);
622
		$n = 0;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $n. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
623
		for ($j = 0; $j < $palette_size; $j++)
624
		{
625
			$b = ord($palettedata{$j++});
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $b. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
626
			$g = ord($palettedata{$j++});
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $g. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
627
			$r = ord($palettedata{$j++});
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $r. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
628
629
			$palette[$n++] = imagecolorallocate($dst_img, $r, $g, $b);
630
		}
631
632
		$scan_line_size = ($info['bits'] * $info['width'] + 7) >> 3;
633
		$scan_line_align = $scan_line_size & 3 ? 4 - ($scan_line_size & 3) : 0;
634
635
		for ($y = 0, $l = $info['height'] - 1; $y < $info['height']; $y++, $l--)
636
		{
637
			fseek($fp, $header['offset'] + ($scan_line_size + $scan_line_align) * $l);
638
			$scan_line = fread($fp, $scan_line_size);
639
640
			if (strlen($scan_line) < $scan_line_size)
641
				continue;
642
643
			if ($info['bits'] == 32)
644
			{
645
				$x = 0;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $x. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
646 View Code Duplication
				for ($j = 0; $j < $scan_line_size; $x++)
647
				{
648
					$b = ord($scan_line{$j++});
649
					$g = ord($scan_line{$j++});
650
					$r = ord($scan_line{$j++});
651
					$j++;
652
653
					$color = imagecolorexact($dst_img, $r, $g, $b);
654
					if ($color == -1)
655
					{
656
						$color = imagecolorallocate($dst_img, $r, $g, $b);
657
658
						// Gah!  Out of colors?  Stupid GD 1... try anyhow.
659
						if ($color == -1)
660
							$color = imagecolorclosest($dst_img, $r, $g, $b);
661
					}
662
663
					imagesetpixel($dst_img, $x, $y, $color);
664
				}
665
			}
666
			elseif ($info['bits'] == 24)
667
			{
668
				$x = 0;
669 View Code Duplication
				for ($j = 0; $j < $scan_line_size; $x++)
670
				{
671
					$b = ord($scan_line{$j++});
672
					$g = ord($scan_line{$j++});
673
					$r = ord($scan_line{$j++});
674
675
					$color = imagecolorexact($dst_img, $r, $g, $b);
676
					if ($color == -1)
677
					{
678
						$color = imagecolorallocate($dst_img, $r, $g, $b);
679
680
						// Gah!  Out of colors?  Stupid GD 1... try anyhow.
681
						if ($color == -1)
682
							$color = imagecolorclosest($dst_img, $r, $g, $b);
683
					}
684
685
					imagesetpixel($dst_img, $x, $y, $color);
686
				}
687
			}
688
			elseif ($info['bits'] == 16)
689
			{
690
				$x = 0;
691
				for ($j = 0; $j < $scan_line_size; $x++)
692
				{
693
					$b1 = ord($scan_line{$j++});
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $b1. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
694
					$b2 = ord($scan_line{$j++});
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $b2. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
695
696
					$word = $b2 * 256 + $b1;
697
698
					$b = (($word & 31) * 255) / 31;
699
					$g = ((($word >> 5) & 31) * 255) / 31;
700
					$r = ((($word >> 10) & 31) * 255) / 31;
701
702
					// Scale the image colors up properly.
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'] == 8)
717
			{
718
				$x = 0;
719
				for ($j = 0; $j < $scan_line_size; $x++)
720
					imagesetpixel($dst_img, $x, $y, $palette[ord($scan_line{$j++})]);
721
			}
722
			elseif ($info['bits'] == 4)
723
			{
724
				$x = 0;
725
				for ($j = 0; $j < $scan_line_size; $x++)
726
				{
727
					$byte = ord($scan_line{$j++});
728
729
					imagesetpixel($dst_img, $x, $y, $palette[(int) ($byte / 16)]);
730 View Code Duplication
					if (++$x < $info['width'])
731
						imagesetpixel($dst_img, $x, $y, $palette[$byte & 15]);
732
				}
733
			}
734
			elseif ($info['bits'] == 1)
735
			{
736
				$x = 0;
737
				for ($j = 0; $j < $scan_line_size; $x++)
738
				{
739
					$byte = ord($scan_line{$j++});
740
741
					imagesetpixel($dst_img, $x, $y, $palette[(($byte) & 128) != 0]);
742
					for ($shift = 1; $shift < 8; $shift++) {
743 View Code Duplication
						if (++$x < $info['width']) imagesetpixel($dst_img, $x, $y, $palette[(($byte << $shift) & 128) != 0]);
744
					}
745
				}
746
			}
747
		}
748
749
		fclose($fp);
750
751
		error_reporting($errors);
752
753
		return $dst_img;
754
	}
755
}
756
757
/**
758
 * Writes a gif file to disk as a png file.
759
760
 * @param resource $gif A gif image resource
761
 * @param string $lpszFileName The name of the file
762
 * @param int $background_color The background color
763
 * @return boolean Whether the operation was successful
764
 */
765
function gif_outputAsPng($gif, $lpszFileName, $background_color = -1)
766
{
767
	if (!isset($gif) || @get_class($gif) != 'cgif' || !$gif->loaded || $lpszFileName == '')
768
		return false;
769
770
	$fd = $gif->get_png_data($background_color);
0 ignored issues
show
Bug introduced by
The method get_png_data cannot be called on $gif (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $fd. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
771
	if (strlen($fd) <= 0)
772
		return false;
773
774
	if (!($fh = @fopen($lpszFileName, 'wb')))
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fh. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
775
		return false;
776
777
	@fwrite($fh, $fd, strlen($fd));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
778
	@fflush($fh);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
779
	@fclose($fh);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

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

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
937
	$randomness_color = imagecolorallocate($code_image, $dotbgcolor[0], $dotbgcolor[1], $dotbgcolor[2]);
0 ignored issues
show
Bug introduced by
The variable $dotbgcolor does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
938
939
	// Some squares/rectanges for new extreme level
940
	if ($noiseType == 'extreme')
941
	{
942
		for ($i = 0; $i < mt_rand(1, 5); $i++)
943
		{
944
			$x1 = mt_rand(0, $total_width / 4);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $x1. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
945
			$x2 = $x1 + round(rand($total_width / 4, $total_width));
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $x2. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
946
			$y1 = mt_rand(0, $max_height);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $y1. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
947
			$y2 = $y1 + round(rand(0, $max_height / 3));
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $y2. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

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

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1013
				if (empty($fontcord))
1014
					$can_do_ttf = false;
1015
				elseif ($is_reverse)
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_reverse of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null 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...
1016
				{
1017
					imagefilledpolygon($code_image, $fontcord, 4, $fg_color);
1018
					// Put the character back!
1019
					imagettftext($code_image, $font_size, $angle, $font_x, $font_y, $randomness_color, $fontface, $character['id']);
1020
				}
1021
1022
				if ($can_do_ttf)
1023
					$cur_x = max($fontcord[2], $fontcord[4]) + ($angle == 0 ? 0 : 3);
1024
			}
1025
1026
			if (!$can_do_ttf)
1027
			{
1028
				// Rotating the characters a little...
1029
				if (function_exists('imagerotate'))
1030
				{
1031
					$char_image = $gd2 ? imagecreatetruecolor($character['width'], $character['height']) : imagecreate($character['width'], $character['height']);
1032
					$char_bgcolor = imagecolorallocate($char_image, $background_color[0], $background_color[1], $background_color[2]);
1033
					imagefilledrectangle($char_image, 0, 0, $character['width'] - 1, $character['height'] - 1, $char_bgcolor);
1034
					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]));
1035
					$rotated_char = imagerotate($char_image, mt_rand(-100, 100) / 10, $char_bgcolor);
1036
					imagecopy($code_image, $rotated_char, $cur_x, 0, 0, 0, $character['width'], $character['height']);
1037
					imagedestroy($rotated_char);
1038
					imagedestroy($char_image);
1039
				}
1040
1041
				// Sorry, no rotation available.
1042
				else
1043
					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]));
1044
				$cur_x += $character['width'] + $character_spacing;
1045
			}
1046
		}
1047
	}
1048
	// If disabled just show a cross.
1049
	else
1050
	{
1051
		imageline($code_image, 0, 0, $total_width, $max_height, $fg_color);
1052
		imageline($code_image, 0, $max_height, $total_width, 0, $fg_color);
1053
	}
1054
1055
	// Make the background color transparent on the hard image.
1056
	if (!$simpleBGColor)
1057
		imagecolortransparent($code_image, $bg_color);
1058
	if ($hasBorder)
1059
		imagerectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $fg_color);
1060
1061
	// Add some noise to the background?
1062
	if ($noiseType != 'none')
1063
	{
1064
		for ($i = mt_rand(0, 2); $i < $max_height; $i += mt_rand(1, 2))
1065
			for ($j = mt_rand(0, 10); $j < $total_width; $j += mt_rand(1, 10))
1066
				imagesetpixel($code_image, $j, $i, mt_rand(0, 1) ? $fg_color : $randomness_color);
1067
1068
		// Put in some lines too?
1069
		if ($noiseType != 'extreme')
1070
		{
1071
			$num_lines = $noiseType == 'high' ? mt_rand(3, 7) : mt_rand(2, 5);
1072
			for ($i = 0; $i < $num_lines; $i++)
1073
			{
1074
				if (mt_rand(0, 1))
1075
				{
1076
					$x1 = mt_rand(0, $total_width);
1077
					$x2 = mt_rand(0, $total_width);
1078
					$y1 = 0; $y2 = $max_height;
0 ignored issues
show
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
1079
				}
1080
				else
1081
				{
1082
					$y1 = mt_rand(0, $max_height);
1083
					$y2 = mt_rand(0, $max_height);
1084
					$x1 = 0; $x2 = $total_width;
0 ignored issues
show
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
1085
				}
1086
				imagesetthickness($code_image, mt_rand(1, 2));
1087
				imageline($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
1088
			}
1089
		}
1090
		else
1091
		{
1092
			// Put in some ellipse
1093
			$num_ellipse = $noiseType == 'extreme' ? mt_rand(6, 12) : mt_rand(2, 6);
1094
			for ($i = 0; $i < $num_ellipse; $i++)
1095
			{
1096
				$x1 = round(rand(($total_width / 4) * -1, $total_width + ($total_width / 4)));
1097
				$x2 = round(rand($total_width / 2, 2 * $total_width));
1098
				$y1 = round(rand(($max_height / 4) * -1, $max_height + ($max_height / 4)));
1099
				$y2 = round(rand($max_height / 2, 2 * $max_height));
1100
				imageellipse($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
1101
			}
1102
		}
1103
	}
1104
1105
	// Show the image.
1106
	if (function_exists('imagegif'))
1107
	{
1108
		header('Content-type: image/gif');
1109
		imagegif($code_image);
1110
	}
1111
	else
1112
	{
1113
		header('Content-type: image/png');
1114
		imagepng($code_image);
1115
	}
1116
1117
	// Bail out.
1118
	imagedestroy($code_image);
1119
	die();
1120
}
1121
1122
/**
1123
 * Show a letter for the visual verification code.
1124
 * Alternative function for showCodeImage() in case GD is missing.
1125
 * Includes an image from a random sub directory of default_theme_dir/fonts.
1126
 *
1127
 * @param string $letter A letter to show as an image
1128
 * @return void|false False if something went wrong
1129
 */
1130
function showLetterImage($letter)
1131
{
1132
	global $settings;
1133
1134
	if (!is_dir($settings['default_theme_dir'] . '/fonts'))
1135
		return false;
1136
1137
	// Get a list of the available font directories.
1138
	$font_dir = dir($settings['default_theme_dir'] . '/fonts');
1139
	$font_list = array();
1140
	while ($entry = $font_dir->read())
1141
		if ($entry[0] !== '.' && is_dir($settings['default_theme_dir'] . '/fonts/' . $entry) && file_exists($settings['default_theme_dir'] . '/fonts/' . $entry . '.gdf'))
1142
			$font_list[] = $entry;
1143
1144
	if (empty($font_list))
1145
		return false;
1146
1147
	// Pick a random font.
1148
	$random_font = $font_list[array_rand($font_list)];
1149
1150
	// Check if the given letter exists.
1151
	if (!file_exists($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.png'))
1152
		return false;
1153
1154
	// Include it!
1155
	header('Content-type: image/png');
1156
	include($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.png');
1157
1158
	// Nothing more to come.
1159
	die();
1160
}
1161
1162
?>