Completed
Push — master ( fc47d5...df58bd )
by Richard
13s queued 10s
created

phpthumb::ImageResizeFunction()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 12
rs 9.6111
c 0
b 0
f 0
cc 5
nc 4
nop 10

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
//////////////////////////////////////////////////////////////
3
//   phpThumb() by James Heinrich <[email protected]>   //
4
//        available at http://phpthumb.sourceforge.net      //
5
//         and/or https://github.com/JamesHeinrich/phpThumb //
6
//////////////////////////////////////////////////////////////
7
///                                                         //
8
// See: phpthumb.readme.txt for usage instructions          //
9
//                                                         ///
10
//////////////////////////////////////////////////////////////
11
12
if (!class_exists('phpthumb_functions'))
13
{
14
ob_start();
15
	if(!include_once __DIR__ . '/phpthumb.functions.php')
16
	{
17
	ob_end_flush();
18
	die('failed to include_once("'. __DIR__ .'/phpthumb.functions.php")');
19
}
20
ob_end_clean();
21
}
22
23
class phpthumb {
24
25
	// public:
26
	// START PARAMETERS (for object mode and phpThumb.php)
27
	// See phpthumb.readme.txt for descriptions of what each of these values are
28
	public $src  = null;     // SouRCe filename
29
	public $new  = null;     // NEW image (phpThumb.php only)
30
	public $w    = null;     // Width
31
	public $h    = null;     // Height
32
	public $wp   = null;     // Width  (Portrait Images Only)
33
	public $hp   = null;     // Height (Portrait Images Only)
34
	public $wl   = null;     // Width  (Landscape Images Only)
35
	public $hl   = null;     // Height (Landscape Images Only)
36
	public $ws   = null;     // Width  (Square Images Only)
37
	public $hs   = null;     // Height (Square Images Only)
38
	public $f    = null;     // output image Format
39
	public $q    = 75;       // jpeg output Quality
40
	public $sx   = null;     // Source crop top-left X position
41
	public $sy   = null;     // Source crop top-left Y position
42
	public $sw   = null;     // Source crop Width
43
	public $sh   = null;     // Source crop Height
44
	public $zc   = null;     // Zoom Crop
45
	public $bc   = null;     // Border Color
46
	public $bg   = null;     // BackGround color
47
	public $fltr = array();  // FiLTeRs
48
	public $goto = null;     // GO TO url after processing
49
	public $err  = null;     // default ERRor image filename
50
	public $xto  = null;     // extract eXif Thumbnail Only
51
	public $ra   = null;     // Rotate by Angle
52
	public $ar   = null;     // Auto Rotate
53
	public $aoe  = null;     // Allow Output Enlargement
54
	public $far  = null;     // Fixed Aspect Ratio
55
	public $iar  = null;     // Ignore Aspect Ratio
56
	public $maxb = null;     // MAXimum Bytes
57
	public $down = null;     // DOWNload thumbnail filename
58
	public $md5s = null;     // MD5 hash of Source image
59
	public $sfn  = 0;        // Source Frame Number
60
	public $dpi  = 150;      // Dots Per Inch for vector source formats
61
	public $sia  = null;     // Save Image As filename
62
63
	public $file = null;     // >>>deprecated, DO NOT USE, will be removed in future versions<<<
64
65
	public $phpThumbDebug = null;
66
	// END PARAMETERS
67
68
69
	// public:
70
	// START CONFIGURATION OPTIONS (for object mode only)
71
	// See phpThumb.config.php for descriptions of what each of these settings do
72
73
	// * Directory Configuration
74
	public $config_cache_directory                      = null;
75
	public $config_cache_directory_depth                = 0;
76
	public $config_cache_disable_warning                = true;
77
	public $config_cache_source_enabled                 = false;
78
	public $config_cache_source_directory               = null;
79
	public $config_temp_directory                       = null;
80
	public $config_document_root                        = null;
81
82
	// * Default output configuration:
83
	public $config_output_format                        = 'jpeg';
84
	public $config_output_maxwidth                      = 0;
85
	public $config_output_maxheight                     = 0;
86
	public $config_output_interlace                     = true;
87
88
	// * Error message configuration
89
	public $config_error_image_width                    = 400;
90
	public $config_error_image_height                   = 100;
91
	public $config_error_message_image_default          = '';
92
	public $config_error_bgcolor                        = 'CCCCFF';
93
	public $config_error_textcolor                      = 'FF0000';
94
	public $config_error_fontsize                       = 1;
95
	public $config_error_die_on_error                   = false;
96
	public $config_error_silent_die_on_error            = false;
97
	public $config_error_die_on_source_failure          = true;
98
99
	// * Anti-Hotlink Configuration:
100
	public $config_nohotlink_enabled                    = true;
101
	public $config_nohotlink_valid_domains              = array();
102
	public $config_nohotlink_erase_image                = true;
103
	public $config_nohotlink_text_message               = 'Off-server thumbnailing is not allowed';
104
	// * Off-server Linking Configuration:
105
	public $config_nooffsitelink_enabled                = false;
106
	public $config_nooffsitelink_valid_domains          = array();
107
	public $config_nooffsitelink_require_refer          = false;
108
	public $config_nooffsitelink_erase_image            = true;
109
	public $config_nooffsitelink_watermark_src          = '';
110
	public $config_nooffsitelink_text_message           = 'Off-server linking is not allowed';
111
112
	// * Border & Background default colors
113
	public $config_border_hexcolor                      = '000000';
114
	public $config_background_hexcolor                  = 'FFFFFF';
115
116
	// * TrueType Fonts
117
	public $config_ttf_directory                        = './fonts';
118
119
	public $config_max_source_pixels                    = null;
120
	public $config_use_exif_thumbnail_for_speed         = false;
121
	public $config_allow_local_http_src                 = false;
122
123
	public $config_imagemagick_path                     = null;
124
	public $config_prefer_imagemagick                   = true;
125
	public $config_imagemagick_use_thumbnail            = true;
126
127
	public $config_cache_maxage                         = null;
128
	public $config_cache_maxsize                        = null;
129
	public $config_cache_maxfiles                       = null;
130
	public $config_cache_source_filemtime_ignore_local  = false;
131
	public $config_cache_source_filemtime_ignore_remote = true;
132
	public $config_cache_default_only_suffix            = false;
133
	public $config_cache_force_passthru                 = true;
134
	public $config_cache_prefix                         = '';    // default value set in the constructor below
135
136
	// * MySQL
137
	public $config_mysql_extension                      = null;
138
	public $config_mysql_query                          = null;
139
	public $config_mysql_hostname                       = null;
140
	public $config_mysql_username                       = null;
141
	public $config_mysql_password                       = null;
142
	public $config_mysql_database                       = null;
143
144
	// * Security
145
	public $config_high_security_enabled                = true;
146
	public $config_high_security_password               = null;
147
	public $config_high_security_url_separator          = '&';
148
	public $config_disable_debug                        = true;
149
	public $config_allow_src_above_docroot              = false;
150
	public $config_allow_src_above_phpthumb             = true;
151
	public $config_auto_allow_symlinks                  = true;    // allow symlink target directories without explicitly whitelisting them
152
	public $config_additional_allowed_dirs              = array(); // additional directories to allow source images to be read from
153
	public $config_file_create_mask                     = 0755;
154
	public $config_dir_create_mask                      = 0755;
155
156
	// * HTTP fopen
157
	public $config_http_fopen_timeout                   = 10;
158
	public $config_http_follow_redirect                 = true;
159
160
	// * Compatability
161
	public $config_disable_pathinfo_parsing             = false;
162
	public $config_disable_imagecopyresampled           = false;
163
	public $config_disable_onlycreateable_passthru      = false;
164
	public $config_disable_realpath                     = false;
165
166
	public $config_http_user_agent                      = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7';
167
168
	// END CONFIGURATION OPTIONS
169
170
171
	// public: error messages (read-only; persistant)
172
	public $debugmessages = array();
173
	public $debugtiming   = array();
174
	public $fatalerror    = null;
175
176
177
	// private: (should not be modified directly)
178
	public $thumbnailQuality = 75;
179
	public $thumbnailFormat  = null;
180
181
	public $sourceFilename   = null;
182
	public $rawImageData     = null;
183
	public $IMresizedData    = null;
184
	public $outputImageData  = null;
185
186
	public $useRawIMoutput   = false;
187
188
	public $gdimg_output     = null;
189
	public $gdimg_source     = null;
190
191
	public $getimagesizeinfo = null;
192
193
	public $source_width  = null;
194
	public $source_height = null;
195
196
	public $thumbnailCropX = null;
197
	public $thumbnailCropY = null;
198
	public $thumbnailCropW = null;
199
	public $thumbnailCropH = null;
200
201
	public $exif_thumbnail_width  = null;
202
	public $exif_thumbnail_height = null;
203
	public $exif_thumbnail_type   = null;
204
	public $exif_thumbnail_data   = null;
205
	public $exif_raw_data         = null;
206
207
	public $thumbnail_width        = null;
208
	public $thumbnail_height       = null;
209
	public $thumbnail_image_width  = null;
210
	public $thumbnail_image_height = null;
211
212
	public $tempFilesToDelete = array();
213
	public $cache_filename    = null;
214
215
	public $AlphaCapableFormats = array( 'png', 'ico', 'gif', 'webp');
216
	public $is_alpha = false;
217
218
	public $iswindows        = null;
219
	public $issafemode       = null;
220
	public $php_memory_limit = null;
221
222
	public $phpthumb_version = '1.7.15-202002130926';
223
224
	//////////////////////////////////////////////////////////////////////
225
226
	// public: constructor
227
	public function __construct() {
228
		$this->phpThumb();
229
	}
230
231
	public function phpThumb() {
232
		$this->DebugTimingMessage('phpThumb() constructor', __FILE__, __LINE__);
233
		$this->DebugMessage('phpThumb() v'.$this->phpthumb_version, __FILE__, __LINE__);
234
235
		foreach (array(ini_get('memory_limit'), get_cfg_var('memory_limit')) as $php_config_memory_limit) {
236
			if ('' !== $php_config_memory_limit) {
237
				if (strtoupper($php_config_memory_limit[ strlen($php_config_memory_limit) - 1 ]) == 'G') { // PHP memory limit expressed in Gigabytes
238
					$php_config_memory_limit = (int) substr($php_config_memory_limit, 0, -1) * 1073741824;
239
				} elseif (strtoupper($php_config_memory_limit[ strlen($php_config_memory_limit) - 1 ]) == 'M') { // PHP memory limit expressed in Megabytes
240
					$php_config_memory_limit = (int) substr($php_config_memory_limit, 0, -1) * 1048576;
241
				}
242
				$this->php_memory_limit = max($this->php_memory_limit, $php_config_memory_limit);
243
			}
244
		}
245
		if ($this->php_memory_limit > 0) { // could be "-1" for "no limit"
246
			$this->config_max_source_pixels = round($this->php_memory_limit * 0.20); // 20% of memory_limit
247
		}
248
249
		$this->iswindows  = (bool) (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN');
250
		$this->issafemode = (bool) preg_match('#(1|ON)#i', ini_get('safe_mode'));
251
		$this->config_document_root = (!empty($_SERVER['DOCUMENT_ROOT']) ? $_SERVER['DOCUMENT_ROOT']   : $this->config_document_root);
252
		$this->config_cache_prefix  = ( isset($_SERVER['SERVER_NAME'])   ? $_SERVER['SERVER_NAME'].'_' : '');
253
254
		$this->purgeTempFiles(); // purge existing temp files if re-initializing object
255
256
		$php_sapi_name = strtolower(function_exists('php_sapi_name') ? PHP_SAPI : '');
257
		if ($php_sapi_name == 'cli') {
258
			$this->config_allow_src_above_docroot = true;
259
		}
260
261
		if (!$this->config_disable_debug) {
262
			// if debug mode is enabled, force phpThumbDebug output, do not allow normal thumbnails to be generated
263
			$this->phpThumbDebug = (null === $this->phpThumbDebug ? 9 : max(1, (int) $this->phpThumbDebug));
264
		}
265
	}
266
267
	public function __destruct() {
268
		$this->purgeTempFiles();
269
	}
270
271
	// public:
272
	public function purgeTempFiles() {
273
		foreach ($this->tempFilesToDelete as $tempFileToDelete) {
274
			if (file_exists($tempFileToDelete)) {
275
				$this->DebugMessage('Deleting temp file "'.$tempFileToDelete.'"', __FILE__, __LINE__);
276
				@unlink($tempFileToDelete);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

276
				/** @scrutinizer ignore-unhandled */ @unlink($tempFileToDelete);

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...
277
			}
278
		}
279
		$this->tempFilesToDelete = array();
280
		return true;
281
	}
282
283
	// public:
284
	public function setSourceFilename($sourceFilename) {
285
		//$this->resetObject();
286
		//$this->rawImageData   = null;
287
		$this->sourceFilename = $sourceFilename;
288
		$this->src            = $sourceFilename;
289
		if (null === $this->config_output_format) {
290
			$sourceFileExtension = strtolower(substr(strrchr($sourceFilename, '.'), 1));
291
			if (preg_match('#^[a-z]{3,4}$#', $sourceFileExtension)) {
292
				$this->config_output_format = $sourceFileExtension;
293
				$this->DebugMessage('setSourceFilename('.$sourceFilename.') set $this->config_output_format to "'.$sourceFileExtension.'"', __FILE__, __LINE__);
294
			} else {
295
				$this->DebugMessage('setSourceFilename('.$sourceFilename.') did NOT set $this->config_output_format to "'.$sourceFileExtension.'" because it did not seem like an appropriate image format', __FILE__, __LINE__);
296
			}
297
		}
298
		$this->DebugMessage('setSourceFilename('.$sourceFilename.') set $this->sourceFilename to "'.$this->sourceFilename.'"', __FILE__, __LINE__);
299
		return true;
300
	}
301
302
	// public:
303
	public function setSourceData($rawImageData, $sourceFilename='') {
304
		//$this->resetObject();
305
		//$this->sourceFilename = null;
306
		$this->rawImageData   = $rawImageData;
307
		$this->DebugMessage('setSourceData() setting $this->rawImageData ('.strlen($this->rawImageData).' bytes; magic="'.substr($this->rawImageData, 0, 4).'" ('.phpthumb_functions::HexCharDisplay(substr($this->rawImageData, 0, 4)).'))', __FILE__, __LINE__);
308
		if ($this->config_cache_source_enabled) {
309
			$sourceFilename = ($sourceFilename ? $sourceFilename : md5($rawImageData));
310
			if (!is_dir($this->config_cache_source_directory)) {
311
				$this->ErrorImage('$this->config_cache_source_directory ('.$this->config_cache_source_directory.') is not a directory');
312
			} elseif (!@is_writable($this->config_cache_source_directory)) {
313
				$this->ErrorImage('$this->config_cache_source_directory ('.$this->config_cache_source_directory.') is not writable');
314
			}
315
			$this->DebugMessage('setSourceData() attempting to save source image to "'.$this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename).'"', __FILE__, __LINE__);
316
			if ($fp = @fopen($this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename), 'wb')) {
317
				fwrite($fp, $rawImageData);
318
				fclose($fp);
319
			} elseif (!$this->phpThumbDebug) {
320
				$this->ErrorImage('setSourceData() failed to write to source cache ('.$this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename).')');
321
			}
322
		}
323
		return true;
324
	}
325
326
	// public:
327
	public function setSourceImageResource($gdimg) {
328
		//$this->resetObject();
329
		$this->gdimg_source = $gdimg;
330
		return true;
331
	}
332
333
	// public:
334
	public function setParameter($param, $value) {
335
		if ($param == 'src') {
336
			$this->setSourceFilename($this->ResolveFilenameToAbsolute($value));
337
		} elseif (@is_array($this->$param)) {
338
			if (is_array($value)) {
339
				foreach ($value as $arraykey => $arrayvalue) {
340
					array_push($this->$param, $arrayvalue);
341
				}
342
			} else {
343
				array_push($this->$param, $value);
344
			}
345
		} else {
346
			$this->$param = $value;
347
		}
348
		return true;
349
	}
350
351
	// public:
352
	public function getParameter($param) {
353
		//if (property_exists('phpThumb', $param)) {
354
			return $this->$param;
355
		//}
356
		//$this->DebugMessage('setParameter() attempting to get non-existant parameter "'.$param.'"', __FILE__, __LINE__);
357
		//return false;
358
	}
359
360
361
	// public:
362
	public function GenerateThumbnail() {
363
364
		$this->setOutputFormat();
365
			$this->phpThumbDebug('8a');
366
		$this->ResolveSource();
367
			$this->phpThumbDebug('8b');
368
		$this->SetCacheFilename();
369
			$this->phpThumbDebug('8c');
370
		$this->ExtractEXIFgetImageSize();
371
			$this->phpThumbDebug('8d');
372
		if ($this->useRawIMoutput) {
373
			$this->DebugMessage('Skipping rest of GenerateThumbnail() because ($this->useRawIMoutput == true)', __FILE__, __LINE__);
374
			return true;
375
		}
376
			$this->phpThumbDebug('8e');
377
		if (!$this->SourceImageToGD()) {
378
			$this->DebugMessage('SourceImageToGD() failed', __FILE__, __LINE__);
379
			return false;
380
		}
381
			$this->phpThumbDebug('8f');
382
		$this->Rotate();
383
			$this->phpThumbDebug('8g');
384
		$this->CreateGDoutput();
385
			$this->phpThumbDebug('8h');
386
387
		// default values, also applicable for far="C"
388
		$destination_offset_x = round(($this->thumbnail_width  - $this->thumbnail_image_width)  / 2);
389
		$destination_offset_y = round(($this->thumbnail_height - $this->thumbnail_image_height) / 2);
390
		if (($this->far == 'L') || ($this->far == 'TL') || ($this->far == 'BL')) {
391
			$destination_offset_x = 0;
392
		}
393
		if (($this->far == 'R') || ($this->far == 'TR') || ($this->far == 'BR')) {
394
			$destination_offset_x =  round($this->thumbnail_width  - $this->thumbnail_image_width);
395
		}
396
		if (($this->far == 'T') || ($this->far == 'TL') || ($this->far == 'TR')) {
397
			$destination_offset_y = 0;
398
		}
399
		if (($this->far == 'B') || ($this->far == 'BL') || ($this->far == 'BR')) {
400
			$destination_offset_y =  round($this->thumbnail_height - $this->thumbnail_image_height);
401
		}
402
403
//		// copy/resize image to appropriate dimensions
404
//		$borderThickness = 0;
405
//		if (!empty($this->fltr)) {
406
//			foreach ($this->fltr as $key => $value) {
407
//				if (preg_match('#^bord\|([0-9]+)#', $value, $matches)) {
408
//					$borderThickness = $matches[1];
409
//					break;
410
//				}
411
//			}
412
//		}
413
//		if ($borderThickness > 0) {
414
//			//$this->DebugMessage('Skipping ImageResizeFunction() because BorderThickness="'.$borderThickness.'"', __FILE__, __LINE__);
415
//			$this->thumbnail_image_height /= 2;
416
//		}
417
		$this->ImageResizeFunction(
418
			$this->gdimg_output,
419
			$this->gdimg_source,
420
			$destination_offset_x,
421
			$destination_offset_y,
422
			$this->thumbnailCropX,
423
			$this->thumbnailCropY,
424
			$this->thumbnail_image_width,
425
			$this->thumbnail_image_height,
426
			$this->thumbnailCropW,
427
			$this->thumbnailCropH
428
		);
429
430
		$this->DebugMessage('memory_get_usage() after copy-resize = '.(function_exists('memory_get_usage') ? @memory_get_usage() : 'n/a'), __FILE__, __LINE__);
431
		imagedestroy($this->gdimg_source);
432
		$this->DebugMessage('memory_get_usage() after imagedestroy = '.(function_exists('memory_get_usage') ? @memory_get_usage() : 'n/a'), __FILE__, __LINE__);
433
434
			$this->phpThumbDebug('8i');
435
		$this->AntiOffsiteLinking();
436
			$this->phpThumbDebug('8j');
437
		$this->ApplyFilters();
438
			$this->phpThumbDebug('8k');
439
		$this->AlphaChannelFlatten();
440
			$this->phpThumbDebug('8l');
441
		$this->MaxFileSize();
442
			$this->phpThumbDebug('8m');
443
444
		$this->DebugMessage('GenerateThumbnail() completed successfully', __FILE__, __LINE__);
445
		return true;
446
	}
447
448
449
	// public:
450
	public function RenderOutput() {
451
		if (!$this->useRawIMoutput && !is_resource($this->gdimg_output)) {
452
			$this->DebugMessage('RenderOutput() failed because !is_resource($this->gdimg_output)', __FILE__, __LINE__);
453
			return false;
454
		}
455
		if (!$this->thumbnailFormat) {
456
			$this->DebugMessage('RenderOutput() failed because $this->thumbnailFormat is empty', __FILE__, __LINE__);
457
			return false;
458
		}
459
		if ($this->useRawIMoutput) {
460
			$this->DebugMessage('RenderOutput copying $this->IMresizedData ('.strlen($this->IMresizedData).' bytes) to $this->outputImage', __FILE__, __LINE__);
461
			$this->outputImageData = $this->IMresizedData;
462
			return true;
463
		}
464
465
		$builtin_formats = array();
466
		if (function_exists('imagetypes')) {
467
			$imagetypes = imagetypes();
468
			$builtin_formats['wbmp'] = (bool) ($imagetypes & IMG_WBMP);
469
			$builtin_formats['jpg']  = (bool) ($imagetypes & IMG_JPG);
470
			$builtin_formats['gif']  = (bool) ($imagetypes & IMG_GIF);
471
			$builtin_formats['png']  = (bool) ($imagetypes & IMG_PNG);
472
			if (defined('IMG_WEBP')) {
473
				$builtin_formats['webp'] = (bool) ($imagetypes & IMG_WEBP); // PHP 5.6.25, 7.0.10
474
			}
475
			if (defined('IMG_BMP')) {
476
				$builtin_formats['bmp']  = (bool) ($imagetypes & IMG_BMP);  // PHP 7.2.0
477
			}
478
		}
479
480
		$this->DebugMessage('imageinterlace($this->gdimg_output, '. (int) $this->config_output_interlace .')', __FILE__, __LINE__);
481
		imageinterlace($this->gdimg_output, (int) $this->config_output_interlace);
482
483
		$this->DebugMessage('RenderOutput() attempting image'.strtolower(@$this->thumbnailFormat).'($this->gdimg_output)', __FILE__, __LINE__);
484
		ob_start();
485
		switch ($this->thumbnailFormat) {
486
			case 'wbmp':
487
				if (empty($builtin_formats['wbmp'])) {
488
					$this->DebugMessage('GD does not have required built-in support for WBMP output', __FILE__, __LINE__);
489
					ob_end_clean();
490
					return false;
491
				}
492
				imagewbmp($this->gdimg_output, null, $this->thumbnailQuality);
493
				$this->outputImageData = ob_get_contents();
494
				break;
495
496
			case 'jpeg':
497
			case 'jpg':  // should be "jpeg" not "jpg" but just in case...
498
				if (empty($builtin_formats['jpg'])) {
499
					$this->DebugMessage('GD does not have required built-in support for JPEG output', __FILE__, __LINE__);
500
					ob_end_clean();
501
					return false;
502
				}
503
				imagejpeg($this->gdimg_output, null, $this->thumbnailQuality);
504
				$this->outputImageData = ob_get_contents();
505
				break;
506
507
			case 'png':
508
				if (empty($builtin_formats['png'])) {
509
					$this->DebugMessage('GD does not have required built-in support for PNG output', __FILE__, __LINE__);
510
					ob_end_clean();
511
					return false;
512
				}
513
				if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.1.2', '>=')) {
514
					// https://github.com/JamesHeinrich/phpThumb/issues/24
515
516
					/* http://php.net/manual/en/function.imagepng.php:
517
					from php source (gd.h):
518
					2.0.12: Compression level: 0-9 or -1, where 0 is NO COMPRESSION at all,
519
					:: 1 is FASTEST but produces larger files, 9 provides the best
520
					:: compression (smallest files) but takes a long time to compress, and
521
					:: -1 selects the default compiled into the zlib library.
522
					Conclusion: Based on the Zlib manual (http://www.zlib.net/manual.html) the default compression level is set to 6.
523
					*/
524
					if (($this->thumbnailQuality >= -1) && ($this->thumbnailQuality <= 9)) {
525
						$PNGquality = $this->thumbnailQuality;
526
					} else {
527
						$this->DebugMessage('Specified thumbnailQuality "'.$this->thumbnailQuality.'" is outside the accepted range (0-9, or -1). Using 6 as default value.', __FILE__, __LINE__);
528
						$PNGquality = 6;
529
					}
530
					imagepng($this->gdimg_output, null, $PNGquality);
531
				} else {
532
					imagepng($this->gdimg_output);
533
				}
534
				$this->outputImageData = ob_get_contents();
535
				break;
536
537
			case 'gif':
538
				if (empty($builtin_formats['gif'])) {
539
					$this->DebugMessage('GD does not have required built-in support for GIF output', __FILE__, __LINE__);
540
					ob_end_clean();
541
					return false;
542
				}
543
				imagegif($this->gdimg_output);
544
				$this->outputImageData = ob_get_contents();
545
				break;
546
547
			case 'webp':
548
				if (empty($builtin_formats['webp'])) {
549
					$this->DebugMessage('GD does not have required built-in support for WebP output', __FILE__, __LINE__);
550
					ob_end_clean();
551
					return false;
552
				}
553
				imagewebp($this->gdimg_output);
554
				$this->outputImageData = ob_get_contents();
555
				break;
556
557
			case 'bmp':
558
				if (!empty($builtin_formats['bmp'])) {
559
					imagebmp($this->gdimg_output);
560
					$this->outputImageData = ob_get_contents();
561
					break;
562
				}
563
				$this->DebugMessage('GD does not have required built-in support for BMP output', __FILE__, __LINE__);
564
				if (!@include_once __DIR__ .'/phpthumb.bmp.php' ) {
565
					$this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.bmp.php" which is required for BMP format output', __FILE__, __LINE__);
566
					ob_end_clean();
567
					return false;
568
				}
569
				$phpthumb_bmp = new phpthumb_bmp();
570
				$this->outputImageData = $phpthumb_bmp->GD2BMPstring($this->gdimg_output);
571
				unset($phpthumb_bmp);
572
				break;
573
574
			case 'ico':
575
				if (!@include_once __DIR__ .'/phpthumb.ico.php' ) {
576
					$this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.ico.php" which is required for ICO format output', __FILE__, __LINE__);
577
					ob_end_clean();
578
					return false;
579
				}
580
				$phpthumb_ico = new phpthumb_ico();
581
				$arrayOfOutputImages = array($this->gdimg_output);
582
				$this->outputImageData = $phpthumb_ico->GD2ICOstring($arrayOfOutputImages);
583
				unset($phpthumb_ico);
584
				break;
585
586
			default:
587
				$this->DebugMessage('RenderOutput failed because $this->thumbnailFormat "'.$this->thumbnailFormat.'" is not valid', __FILE__, __LINE__);
588
				ob_end_clean();
589
				return false;
590
		}
591
		ob_end_clean();
592
		if (!$this->outputImageData) {
593
			$this->DebugMessage('RenderOutput() for "'.$this->thumbnailFormat.'" failed', __FILE__, __LINE__);
594
			ob_end_clean();
595
			return false;
596
		}
597
		$this->DebugMessage('RenderOutput() completing with $this->outputImageData = '.strlen($this->outputImageData).' bytes', __FILE__, __LINE__);
598
		return true;
599
	}
600
601
602
	// public:
603
	public function RenderToFile($filename) {
604
		if (preg_match('#^[a-z0-9]+://#i', $filename)) {
605
			$this->DebugMessage('RenderToFile() failed because $filename ('.$filename.') is a URL', __FILE__, __LINE__);
606
			return false;
607
		}
608
		// render thumbnail to this file only, do not cache, do not output to browser
609
		//$renderfilename = $this->ResolveFilenameToAbsolute(dirname($filename)).DIRECTORY_SEPARATOR.basename($filename);
610
		$renderfilename = $filename;
611
		if (($filename[0] != '/') && ($filename[0] != '\\') && ($filename[1] != ':')) {
612
			$renderfilename = $this->ResolveFilenameToAbsolute($renderfilename);
613
		}
614
		if (!@is_writable(dirname($renderfilename))) {
615
			$this->DebugMessage('RenderToFile() failed because "'.dirname($renderfilename).'/" is not writable', __FILE__, __LINE__);
616
			return false;
617
		}
618
		if (@is_file($renderfilename) && !@is_writable($renderfilename)) {
619
			$this->DebugMessage('RenderToFile() failed because "'.$renderfilename.'" is not writable', __FILE__, __LINE__);
620
			return false;
621
		}
622
623
		if ($this->RenderOutput()) {
624
			if (file_put_contents($renderfilename, $this->outputImageData)) {
625
				@chmod($renderfilename, $this->getParameter('config_file_create_mask'));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

625
				/** @scrutinizer ignore-unhandled */ @chmod($renderfilename, $this->getParameter('config_file_create_mask'));

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...
626
				$this->DebugMessage('RenderToFile('.$renderfilename.') succeeded', __FILE__, __LINE__);
627
				return true;
628
			}
629
			if (!@file_exists($renderfilename)) {
630
				$this->DebugMessage('RenderOutput ['.$this->thumbnailFormat.'('.$renderfilename.')] did not appear to fail, but the output image does not exist either...', __FILE__, __LINE__);
631
			}
632
		} else {
633
			$this->DebugMessage('RenderOutput ['.$this->thumbnailFormat.'('.$renderfilename.')] failed', __FILE__, __LINE__);
634
		}
635
		return false;
636
	}
637
638
639
	// public:
640
	public function OutputThumbnail() {
641
		$this->purgeTempFiles();
642
643
		if (!$this->useRawIMoutput && !is_resource($this->gdimg_output)) {
644
			$this->DebugMessage('OutputThumbnail() failed because !is_resource($this->gdimg_output)', __FILE__, __LINE__);
645
			return false;
646
		}
647
		if (headers_sent()) {
648
			return $this->ErrorImage('OutputThumbnail() failed - headers already sent');
649
		}
650
651
		$downloadfilename = phpthumb_functions::SanitizeFilename(is_string($this->sia) ? $this->sia : ($this->down ? $this->down : 'phpThumb_generated_thumbnail'.'.'.$this->thumbnailFormat));
652
		$this->DebugMessage('Content-Disposition header filename set to "'.$downloadfilename.'"', __FILE__, __LINE__);
653
		if ($downloadfilename) {
654
			header('Content-Disposition: '.($this->down ? 'attachment' : 'inline').'; filename="'.$downloadfilename.'"');
655
		} else {
656
			$this->DebugMessage('failed to send Content-Disposition header because $downloadfilename is empty', __FILE__, __LINE__);
657
		}
658
659
		if ($this->useRawIMoutput) {
660
661
			header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
0 ignored issues
show
Bug introduced by
Are you sure phpthumb_functions::Imag...$this->thumbnailFormat) of type false|mixed|string can be used in concatenation? ( Ignorable by Annotation )

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

661
			header('Content-Type: './** @scrutinizer ignore-type */ phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
Loading history...
662
			echo $this->IMresizedData;
663
664
		} else {
665
666
			$this->DebugMessage('imageinterlace($this->gdimg_output, '. (int) $this->config_output_interlace .')', __FILE__, __LINE__);
667
			imageinterlace($this->gdimg_output, (int) $this->config_output_interlace);
668
			switch ($this->thumbnailFormat) {
669
				case 'jpeg':
670
					header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
671
					$ImageOutFunction = 'image'.$this->thumbnailFormat;
672
					@$ImageOutFunction($this->gdimg_output, null, $this->thumbnailQuality);
673
					break;
674
675
				case 'png':
676
				case 'gif':
677
				case 'webp':
678
					$ImageOutFunction = 'image'.$this->thumbnailFormat;
679
					if (!function_exists($ImageOutFunction)) {
680
						$this->DebugMessage($ImageOutFunction.' is not available', __FILE__, __LINE__);
681
						return false;
682
					}
683
					header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
684
					@$ImageOutFunction($this->gdimg_output);
685
					break;
686
687
				case 'bmp':
688
					if (function_exists('imagebmp')) {
689
						header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
690
						imagebmp($this->gdimg_output);
691
						break;
692
					}
693
					if (!@include_once __DIR__ .'/phpthumb.bmp.php' ) {
694
						$this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.bmp.php" which is required for BMP format output', __FILE__, __LINE__);
695
						return false;
696
					}
697
					$phpthumb_bmp = new phpthumb_bmp();
698
					if (is_object($phpthumb_bmp)) {
699
						$bmp_data = $phpthumb_bmp->GD2BMPstring($this->gdimg_output);
700
						unset($phpthumb_bmp);
701
						if (!$bmp_data) {
702
							$this->DebugMessage('$phpthumb_bmp->GD2BMPstring() failed', __FILE__, __LINE__);
703
							return false;
704
						}
705
						header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
706
						echo $bmp_data;
707
					} else {
708
						$this->DebugMessage('new phpthumb_bmp() failed', __FILE__, __LINE__);
709
						return false;
710
					}
711
					break;
712
713
				case 'ico':
714
					if (!@include_once __DIR__ .'/phpthumb.ico.php' ) {
715
						$this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.ico.php" which is required for ICO format output', __FILE__, __LINE__);
716
						return false;
717
					}
718
					$phpthumb_ico = new phpthumb_ico();
719
					if (is_object($phpthumb_ico)) {
720
						$arrayOfOutputImages = array($this->gdimg_output);
721
						$ico_data = $phpthumb_ico->GD2ICOstring($arrayOfOutputImages);
722
						unset($phpthumb_ico);
723
						if (!$ico_data) {
724
							$this->DebugMessage('$phpthumb_ico->GD2ICOstring() failed', __FILE__, __LINE__);
725
							return false;
726
						}
727
						header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
728
						echo $ico_data;
729
					} else {
730
						$this->DebugMessage('new phpthumb_ico() failed', __FILE__, __LINE__);
731
						return false;
732
					}
733
					break;
734
735
				default:
736
					$this->DebugMessage('OutputThumbnail failed because $this->thumbnailFormat "'.$this->thumbnailFormat.'" is not valid', __FILE__, __LINE__);
737
					return false;
738
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
739
			}
740
741
		}
742
		return true;
743
	}
744
745
746
	// public:
747
	public function CleanUpCacheDirectory() {
748
		$this->DebugMessage('CleanUpCacheDirectory() set to purge ('.(null === $this->config_cache_maxage ? 'NULL' : number_format($this->config_cache_maxage / 86400, 1)).' days; '.(null === $this->config_cache_maxsize ? 'NULL' : number_format($this->config_cache_maxsize / 1048576, 2)).' MB; '.(null === $this->config_cache_maxfiles ? 'NULL' : number_format($this->config_cache_maxfiles)).' files)', __FILE__, __LINE__);
749
750
		if (!is_writable($this->config_cache_directory)) {
751
			$this->DebugMessage('CleanUpCacheDirectory() skipped because "'.$this->config_cache_directory.'" is not writable', __FILE__, __LINE__);
752
			return true;
753
		}
754
755
		// cache status of cache directory for 1 hour to avoid hammering the filesystem functions
756
		$phpThumbCacheStats_filename = $this->config_cache_directory.DIRECTORY_SEPARATOR.'phpThumbCacheStats.txt';
757
		if (file_exists($phpThumbCacheStats_filename) && is_readable($phpThumbCacheStats_filename) && (filemtime($phpThumbCacheStats_filename) >= (time() - 3600))) {
758
			$this->DebugMessage('CleanUpCacheDirectory() skipped because "'.$phpThumbCacheStats_filename.'" is recently modified', __FILE__, __LINE__);
759
			return true;
760
		}
761
		if (!@touch($phpThumbCacheStats_filename)) {
762
			$this->DebugMessage('touch('.$phpThumbCacheStats_filename.') failed', __FILE__, __LINE__);
763
		}
764
765
		$DeletedKeys = array();
766
		$AllFilesInCacheDirectory = array();
767
		if (($this->config_cache_maxage > 0) || ($this->config_cache_maxsize > 0) || ($this->config_cache_maxfiles > 0)) {
768
			$CacheDirOldFilesAge  = array();
769
			$CacheDirOldFilesSize = array();
770
			$AllFilesInCacheDirectory = phpthumb_functions::GetAllFilesInSubfolders($this->config_cache_directory);
771
			foreach ($AllFilesInCacheDirectory as $fullfilename) {
772
				if (preg_match('#'.preg_quote($this->config_cache_prefix).'#i', $fullfilename) && file_exists($fullfilename)) {
773
					$CacheDirOldFilesAge[$fullfilename] = @fileatime($fullfilename);
774
					if ($CacheDirOldFilesAge[$fullfilename] == 0) {
775
						$CacheDirOldFilesAge[$fullfilename] = @filemtime($fullfilename);
776
					}
777
					$CacheDirOldFilesSize[$fullfilename] = @filesize($fullfilename);
778
				}
779
			}
780
			if (empty($CacheDirOldFilesSize)) {
781
				$this->DebugMessage('CleanUpCacheDirectory() skipped because $CacheDirOldFilesSize is empty (phpthumb_functions::GetAllFilesInSubfolders('.$this->config_cache_directory.') found no files)', __FILE__, __LINE__);
782
				return true;
783
			}
784
			$DeletedKeys['zerobyte'] = array();
785
			foreach ($CacheDirOldFilesSize as $fullfilename => $filesize) {
786
				// purge all zero-size files more than an hour old (to prevent trying to delete just-created and/or in-use files)
787
				$cutofftime = time() - 3600;
788
				if (($filesize == 0) && ($CacheDirOldFilesAge[$fullfilename] < $cutofftime)) {
789
					$this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__);
790
					if (@unlink($fullfilename)) {
791
						$DeletedKeys['zerobyte'][] = $fullfilename;
792
						unset($CacheDirOldFilesSize[$fullfilename]);
793
						unset($CacheDirOldFilesAge[$fullfilename]);
794
					}
795
				}
796
			}
797
			$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['zerobyte']).' zero-byte files', __FILE__, __LINE__);
798
			asort($CacheDirOldFilesAge);
799
800
			if ($this->config_cache_maxfiles > 0) {
801
				$TotalCachedFiles = count($CacheDirOldFilesAge);
802
				$DeletedKeys['maxfiles'] = array();
803
				foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
804
					if ($TotalCachedFiles > $this->config_cache_maxfiles) {
805
						$this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__);
806
						if (@unlink($fullfilename)) {
807
							$TotalCachedFiles--;
808
							$DeletedKeys['maxfiles'][] = $fullfilename;
809
						}
810
					} else {
811
						// there are few enough files to keep the rest
812
						break;
813
					}
814
				}
815
				$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['maxfiles']).' files based on (config_cache_maxfiles='.$this->config_cache_maxfiles.')', __FILE__, __LINE__);
816
				foreach ($DeletedKeys['maxfiles'] as $fullfilename) {
817
					unset($CacheDirOldFilesAge[$fullfilename]);
818
					unset($CacheDirOldFilesSize[$fullfilename]);
819
				}
820
			}
821
822
			if ($this->config_cache_maxage > 0) {
823
				$mindate = time() - $this->config_cache_maxage;
824
				$DeletedKeys['maxage'] = array();
825
				foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
826
					if ($filedate > 0) {
827
						if ($filedate < $mindate) {
828
							$this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__);
829
							if (@unlink($fullfilename)) {
830
								$DeletedKeys['maxage'][] = $fullfilename;
831
							}
832
						} else {
833
							// the rest of the files are new enough to keep
834
							break;
835
						}
836
					}
837
				}
838
				$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['maxage']).' files based on (config_cache_maxage='.$this->config_cache_maxage.')', __FILE__, __LINE__);
839
				foreach ($DeletedKeys['maxage'] as $fullfilename) {
840
					unset($CacheDirOldFilesAge[$fullfilename]);
841
					unset($CacheDirOldFilesSize[$fullfilename]);
842
				}
843
			}
844
845
			if ($this->config_cache_maxsize > 0) {
846
				$TotalCachedFileSize = array_sum($CacheDirOldFilesSize);
847
				$DeletedKeys['maxsize'] = array();
848
				foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
849
					if ($TotalCachedFileSize > $this->config_cache_maxsize) {
850
						$this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__);
851
						if (@unlink($fullfilename)) {
852
							$TotalCachedFileSize -= $CacheDirOldFilesSize[$fullfilename];
853
							$DeletedKeys['maxsize'][] = $fullfilename;
854
						}
855
					} else {
856
						// the total filesizes are small enough to keep the rest of the files
857
						break;
858
					}
859
				}
860
				$this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['maxsize']).' files based on (config_cache_maxsize='.$this->config_cache_maxsize.')', __FILE__, __LINE__);
861
				foreach ($DeletedKeys['maxsize'] as $fullfilename) {
862
					unset($CacheDirOldFilesAge[$fullfilename]);
863
					unset($CacheDirOldFilesSize[$fullfilename]);
864
				}
865
			}
866
867
		} else {
868
			$this->DebugMessage('skipping CleanUpCacheDirectory() because config set to not use it', __FILE__, __LINE__);
869
		}
870
		$totalpurged = 0;
871
		foreach ($DeletedKeys as $key => $value) {
872
			$totalpurged += count($value);
873
		}
874
		$this->DebugMessage('CleanUpCacheDirectory() purged '.$totalpurged.' files (from '.count($AllFilesInCacheDirectory).') based on config settings', __FILE__, __LINE__);
875
		if ($totalpurged > 0) {
876
			$empty_dirs = array();
877
			foreach ($AllFilesInCacheDirectory as $fullfilename) {
878
				if (is_dir($fullfilename)) {
879
					$empty_dirs[$this->realPathSafe($fullfilename)] = 1;
880
				} else {
881
					unset($empty_dirs[$this->realPathSafe(dirname($fullfilename))]);
882
				}
883
			}
884
			krsort($empty_dirs);
885
			$totalpurgeddirs = 0;
886
			foreach ($empty_dirs as $empty_dir => $dummy) {
887
				if ($empty_dir == $this->config_cache_directory) {
888
					// shouldn't happen, but just in case, don't let it delete actual cache directory
889
					continue;
890
				} elseif (@rmdir($empty_dir)) {
891
					$totalpurgeddirs++;
892
				} else {
893
					$this->DebugMessage('failed to rmdir('.$empty_dir.')', __FILE__, __LINE__);
894
				}
895
			}
896
			$this->DebugMessage('purged '.$totalpurgeddirs.' empty directories', __FILE__, __LINE__);
897
		}
898
		return true;
899
	}
900
901
	//////////////////////////////////////////////////////////////////////
902
903
	// private: re-initializator (call between rendering multiple images with one object)
904
	public function resetObject() {
905
		$class_vars = get_class_vars(get_class($this));
906
		foreach ($class_vars as $key => $value) {
907
			// do not clobber debug or config info
908
			if (!preg_match('#^(config_|debug|fatalerror)#i', $key)) {
909
				$this->$key = $value;
910
			}
911
		}
912
		$this->phpThumb(); // re-initialize some class variables
913
		return true;
914
	}
915
916
	//////////////////////////////////////////////////////////////////////
917
918
	public function ResolveSource() {
919
		if (is_resource($this->gdimg_source)) {
920
			$this->DebugMessage('ResolveSource() exiting because is_resource($this->gdimg_source)', __FILE__, __LINE__);
921
			return true;
922
		}
923
		if ($this->rawImageData) {
924
			$this->sourceFilename = null;
925
			$this->DebugMessage('ResolveSource() exiting because $this->rawImageData is set ('.number_format(strlen($this->rawImageData)).' bytes)', __FILE__, __LINE__);
926
			return true;
927
		}
928
		if ($this->sourceFilename) {
929
			$this->sourceFilename = $this->ResolveFilenameToAbsolute($this->sourceFilename);
930
			$this->DebugMessage('$this->sourceFilename set to "'.$this->sourceFilename.'"', __FILE__, __LINE__);
931
		} elseif ($this->src) {
932
			$this->sourceFilename = $this->ResolveFilenameToAbsolute($this->src);
933
			$this->DebugMessage('$this->sourceFilename set to "'.$this->sourceFilename.'" from $this->src ('.$this->src.')', __FILE__, __LINE__);
934
		} else {
935
			return $this->ErrorImage('$this->sourceFilename and $this->src are both empty');
936
		}
937
		if ($this->iswindows && ((substr($this->sourceFilename, 0, 2) == '//') || (substr($this->sourceFilename, 0, 2) == '\\\\'))) {
0 ignored issues
show
Bug introduced by
It seems like $this->sourceFilename can also be of type false; however, parameter $string of substr() does only seem to accept string, 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

937
		if ($this->iswindows && ((substr(/** @scrutinizer ignore-type */ $this->sourceFilename, 0, 2) == '//') || (substr($this->sourceFilename, 0, 2) == '\\\\'))) {
Loading history...
938
			// Windows \\share\filename.ext
939
		} elseif (preg_match('#^[a-z0-9]+://#i', $this->sourceFilename, $protocol_matches)) {
0 ignored issues
show
Bug introduced by
It seems like $this->sourceFilename can also be of type false; however, parameter $subject of preg_match() does only seem to accept string, 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

939
		} elseif (preg_match('#^[a-z0-9]+://#i', /** @scrutinizer ignore-type */ $this->sourceFilename, $protocol_matches)) {
Loading history...
940
			if (preg_match('#^(f|ht)tps?\://#i', $this->sourceFilename)) {
941
				// URL
942
				if ($this->config_http_user_agent) {
943
					ini_set('user_agent', $this->config_http_user_agent);
944
				}
945
			} else {
946
				return $this->ErrorImage('only FTP and HTTP/HTTPS protocols are allowed, "'.$protocol_matches[1].'" is not');
947
		}
948
		} elseif (!@file_exists($this->sourceFilename)) {
0 ignored issues
show
Bug introduced by
It seems like $this->sourceFilename can also be of type false; however, parameter $filename of file_exists() does only seem to accept string, 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

948
		} elseif (!@file_exists(/** @scrutinizer ignore-type */ $this->sourceFilename)) {
Loading history...
949
			return $this->ErrorImage('"'.$this->sourceFilename.'" does not exist');
950
		} elseif (!@is_file($this->sourceFilename)) {
0 ignored issues
show
Bug introduced by
It seems like $this->sourceFilename can also be of type false; however, parameter $filename of is_file() does only seem to accept string, 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

950
		} elseif (!@is_file(/** @scrutinizer ignore-type */ $this->sourceFilename)) {
Loading history...
951
			return $this->ErrorImage('"'.$this->sourceFilename.'" is not a file');
952
		}
953
		return true;
954
	}
955
956
957
	public function setOutputFormat() {
958
		static $alreadyCalled = false;
959
		if ($this->thumbnailFormat && $alreadyCalled) {
960
			return true;
961
		}
962
		$alreadyCalled = true;
963
964
		$AvailableImageOutputFormats = array();
965
		$AvailableImageOutputFormats[] = 'text';
966
		if (@is_readable( __DIR__ .'/phpthumb.ico.php')) {
967
			$AvailableImageOutputFormats[] = 'ico';
968
		}
969
		if (@is_readable( __DIR__ .'/phpthumb.bmp.php')) {
970
			$AvailableImageOutputFormats[] = 'bmp';
971
		}
972
973
		$this->thumbnailFormat = 'ico';
974
975
		// Set default output format based on what image types are available
976
		if (function_exists('imagetypes')) {
977
			$imagetypes = imagetypes();
978
			if ($imagetypes & IMG_WBMP) {
979
				$this->thumbnailFormat         = 'wbmp';
980
				$AvailableImageOutputFormats[] = 'wbmp';
981
			}
982
			if ($imagetypes & IMG_GIF) {
983
				$this->thumbnailFormat         = 'gif';
984
				$AvailableImageOutputFormats[] = 'gif';
985
			}
986
			if ($imagetypes & IMG_WEBP) {
987
				$this->thumbnailFormat         = 'webp';
988
				$AvailableImageOutputFormats[] = 'webp';
989
			}
990
			if ($imagetypes & IMG_PNG) {
991
				$this->thumbnailFormat         = 'png';
992
				$AvailableImageOutputFormats[] = 'png';
993
			}
994
			if ($imagetypes & IMG_JPG) {
995
				$this->thumbnailFormat         = 'jpeg';
996
				$AvailableImageOutputFormats[] = 'jpeg';
997
			}
998
		} else {
999
			$this->DebugMessage('imagetypes() does not exist - GD support might not be enabled?',  __FILE__, __LINE__);
1000
		}
1001
		if ($this->ImageMagickVersion()) {
1002
			$IMformats = array('jpeg', 'png', 'gif', 'bmp', 'ico', 'wbmp', 'webp');
1003
			$this->DebugMessage('Addding ImageMagick formats to $AvailableImageOutputFormats ('.implode(';', $AvailableImageOutputFormats).')', __FILE__, __LINE__);
1004
			foreach ($IMformats as $key => $format) {
1005
				$AvailableImageOutputFormats[] = $format;
1006
			}
1007
		}
1008
		$AvailableImageOutputFormats = array_unique($AvailableImageOutputFormats);
1009
		$this->DebugMessage('$AvailableImageOutputFormats = array('.implode(';', $AvailableImageOutputFormats).')', __FILE__, __LINE__);
1010
1011
		$this->f = preg_replace('#[^a-z]#', '', strtolower($this->f));
1012
		if (strtolower($this->config_output_format) == 'jpg') {
1013
			$this->config_output_format = 'jpeg';
1014
		}
1015
		if (strtolower($this->f) == 'jpg') {
1016
			$this->f = 'jpeg';
1017
		}
1018
		if (phpthumb_functions::CaseInsensitiveInArray($this->config_output_format, $AvailableImageOutputFormats)) {
1019
			// set output format to config default if that format is available
1020
			$this->DebugMessage('$this->thumbnailFormat set to $this->config_output_format "'.strtolower($this->config_output_format).'"', __FILE__, __LINE__);
1021
			$this->thumbnailFormat = strtolower($this->config_output_format);
1022
		} elseif ($this->config_output_format) {
1023
			$this->DebugMessage('$this->thumbnailFormat staying as "'.$this->thumbnailFormat.'" because $this->config_output_format ('.strtolower($this->config_output_format).') is not in $AvailableImageOutputFormats', __FILE__, __LINE__);
1024
		}
1025
		if ($this->f && phpthumb_functions::CaseInsensitiveInArray($this->f, $AvailableImageOutputFormats) ) {
1026
			// override output format if $this->f is set and that format is available
1027
			$this->DebugMessage('$this->thumbnailFormat set to $this->f "'.strtolower($this->f).'"', __FILE__, __LINE__);
1028
			$this->thumbnailFormat = strtolower($this->f);
1029
		} elseif ($this->f) {
1030
			$this->DebugMessage('$this->thumbnailFormat staying as "'.$this->thumbnailFormat.'" because $this->f ('.strtolower($this->f).') is not in $AvailableImageOutputFormats', __FILE__, __LINE__);
1031
		}
1032
1033
		// for JPEG images, quality 1 (worst) to 99 (best)
1034
		// quality < 25 is nasty, with not much size savings - not recommended
1035
		// problems with 100 - invalid JPEG?
1036
		$this->thumbnailQuality = max(1, min(99, ($this->q ? (int) $this->q : 75)));
1037
		$this->DebugMessage('$this->thumbnailQuality set to "'.$this->thumbnailQuality.'"', __FILE__, __LINE__);
1038
1039
		return true;
1040
	}
1041
1042
1043
	public function setCacheDirectory() {
1044
		// resolve cache directory to absolute pathname
1045
		$this->DebugMessage('setCacheDirectory() starting with config_cache_directory = "'.$this->config_cache_directory.'"', __FILE__, __LINE__);
1046
		if ($this->config_cache_directory[ 0 ] == '.') {
1047
			if (preg_match('#^(f|ht)tps?\://#i', $this->src)) {
1048
				if (!$this->config_cache_disable_warning) {
1049
					$this->ErrorImage('$this->config_cache_directory ('.$this->config_cache_directory.') cannot be used for remote images. Adjust "cache_directory" or "cache_disable_warning" in phpThumb.config.php');
1050
				}
1051
			} elseif ($this->src) {
1052
				// resolve relative cache directory to source image
1053
				$this->config_cache_directory = dirname($this->ResolveFilenameToAbsolute($this->src)).DIRECTORY_SEPARATOR.$this->config_cache_directory;
0 ignored issues
show
Bug introduced by
It seems like $this->ResolveFilenameToAbsolute($this->src) can also be of type false; however, parameter $path of dirname() does only seem to accept string, 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

1053
				$this->config_cache_directory = dirname(/** @scrutinizer ignore-type */ $this->ResolveFilenameToAbsolute($this->src)).DIRECTORY_SEPARATOR.$this->config_cache_directory;
Loading history...
1054
			} else {
1055
				// $this->new is probably set
1056
			}
1057
		}
1058
		if (substr($this->config_cache_directory, -1) == '/') {
1059
			$this->config_cache_directory = substr($this->config_cache_directory, 0, -1);
1060
		}
1061
		if ($this->iswindows) {
1062
			$this->config_cache_directory = str_replace('/', DIRECTORY_SEPARATOR, $this->config_cache_directory);
1063
		}
1064
		if ($this->config_cache_directory) {
1065
			$real_cache_path = $this->realPathSafe($this->config_cache_directory);
1066
			if (!$real_cache_path) {
1067
				$this->DebugMessage('$this->realPathSafe($this->config_cache_directory) failed for "'.$this->config_cache_directory.'"', __FILE__, __LINE__);
1068
				if (!is_dir($this->config_cache_directory)) {
1069
					$this->DebugMessage('!is_dir('.$this->config_cache_directory.')', __FILE__, __LINE__);
1070
				}
1071
			}
1072
			if ($real_cache_path) {
1073
				$this->DebugMessage('setting config_cache_directory to $this->realPathSafe('.$this->config_cache_directory.') = "'.$real_cache_path.'"', __FILE__, __LINE__);
1074
				$this->config_cache_directory = $real_cache_path;
1075
			}
1076
		}
1077
		if (!is_dir($this->config_cache_directory)) {
1078
			if (!$this->config_cache_disable_warning) {
1079
				$this->ErrorImage('$this->config_cache_directory ('.$this->config_cache_directory.') does not exist. Adjust "cache_directory" or "cache_disable_warning" in phpThumb.config.php');
1080
			}
1081
			$this->DebugMessage('$this->config_cache_directory ('.$this->config_cache_directory.') is not a directory', __FILE__, __LINE__);
1082
			$this->config_cache_directory = null;
1083
		} elseif (!@is_writable($this->config_cache_directory)) {
1084
			$this->DebugMessage('$this->config_cache_directory is not writable ('.$this->config_cache_directory.')', __FILE__, __LINE__);
1085
		}
1086
1087
		$this->InitializeTempDirSetting();
1088
		if (!@is_dir($this->config_temp_directory) && !@is_writable($this->config_temp_directory) && @is_dir($this->config_cache_directory) && @is_writable($this->config_cache_directory)) {
1089
			$this->DebugMessage('setting $this->config_temp_directory = $this->config_cache_directory ('.$this->config_cache_directory.')', __FILE__, __LINE__);
1090
			$this->config_temp_directory = $this->config_cache_directory;
1091
		}
1092
		return true;
1093
	}
1094
1095
	/* Takes the array of path segments up to now, and the next segment (maybe a modifier: empty, . or ..)
1096
	   Applies it, adding or removing from $segments as a result. Returns nothing. */
1097
	// http://support.silisoftware.com/phpBB3/viewtopic.php?t=961
1098
	public function applyPathSegment(&$segments, $segment) {
1099
		if ($segment == '.') {
1100
			return; // always remove
1101
		}
1102
		if ($segment == '') {
1103
			$test = array_pop($segments);
1104
			if (null === $test) {
1105
				$segments[] = $segment; // keep the first empty block
1106
			} elseif ($test == '') {
1107
				$test = array_pop($segments);
1108
				if (null === $test) {
1109
					$segments[] = $test;
1110
					$segments[] = $segment; // keep the second one too
1111
				} else { // put both back and ignore segment
1112
					$segments[] = $test;
1113
					$segments[] = $test;
1114
				}
1115
			} else {
1116
				$segments[] = $test; // ignore empty blocks
1117
			}
1118
		} else {
1119
			if ($segment == '..') {
1120
				$test = array_pop($segments);
1121
				if (null === $test) {
1122
					$segments[] = $segment;
1123
				} elseif ($test == '..') {
1124
					$segments[] = $test;
1125
					$segments[] = $segment;
1126
				} else {
1127
					if ($test == '') {
1128
						$segments[] = $test;
1129
					} // else nothing, remove both
1130
				}
1131
			} else {
1132
				$segments[] = $segment;
1133
			}
1134
		}
1135
	}
1136
1137
	/* Takes array of path components, normalizes it: removes empty slots and '.', collapses '..' and folder names.  Returns array. */
1138
	// http://support.silisoftware.com/phpBB3/viewtopic.php?t=961
1139
	public function normalizePath($segments) {
1140
		$parts = array();
1141
		foreach ($segments as $segment) {
1142
			$this->applyPathSegment($parts, $segment);
1143
		}
1144
		return $parts;
1145
	}
1146
1147
	/* True if the provided path points (without resolving symbolic links) into one of the allowed directories. */
1148
	// http://support.silisoftware.com/phpBB3/viewtopic.php?t=961
1149
	public function matchPath($path, $allowed_dirs) {
1150
		if (!empty($allowed_dirs)) {
1151
			foreach ($allowed_dirs as $one_dir) {
1152
				if (preg_match('#^'.preg_quote(str_replace(DIRECTORY_SEPARATOR, '/', $this->realPathSafe($one_dir))).'#', $path)) {
1153
					return true;
1154
				}
1155
			}
1156
		}
1157
		return false;
1158
	}
1159
1160
	/* True if the provided path points inside one of open_basedirs (or if open_basedirs are disabled) */
1161
	// http://support.silisoftware.com/phpBB3/viewtopic.php?t=961
1162
	public function isInOpenBasedir($path) {
1163
		static $open_basedirs = null;
1164
		if (null === $open_basedirs) {
1165
			$ini_text = ini_get('open_basedir');
1166
			$this->DebugMessage('open_basedir: "'.$ini_text.'"', __FILE__, __LINE__);
1167
			$open_basedirs = array();
1168
			if (strlen($ini_text) > 0) {
1169
				foreach (preg_split('#[;:]#', $ini_text) as $key => $value) {
1170
					$open_basedirs[$key] = $this->realPathSafe($value);
1171
				}
1172
			}
1173
		}
1174
		return (empty($open_basedirs) || $this->matchPath($path, $open_basedirs));
1175
	}
1176
1177
	/* Resolves all symlinks in $path, checking that each continuous part ends in an allowed zone. Returns null, if any component leads outside of allowed zone. */
1178
	// http://support.silisoftware.com/phpBB3/viewtopic.php?t=961
1179
	public function resolvePath($path, $allowed_dirs) {
1180
		$this->DebugMessage('resolvePath: '.$path.' (allowed_dirs: '.print_r($allowed_dirs, true).')', __FILE__, __LINE__);
1181
1182
		// add base path to the top of the list
1183
		if (!$this->config_allow_src_above_docroot) {
1184
			array_unshift($allowed_dirs, $this->realPathSafe($this->config_document_root));
1185
		} else {
1186
			if (!$this->config_allow_src_above_phpthumb) {
1187
				array_unshift($allowed_dirs, $this->realPathSafe( __DIR__ ));
1188
			} else {
1189
				// no checks are needed, offload the work to realpath and forget about it
1190
				$this->DebugMessage('resolvePath: checks disabled, returning '.$this->realPathSafe($path), __FILE__, __LINE__);
1191
				return $this->realPathSafe($path);
1192
			}
1193
		}
1194
		if ($path == '') {
1195
			return null; // save us trouble
1196
		}
1197
1198
		do {
1199
			$this->DebugMessage('resolvePath: iteration, path='.$path.', base path = '.$allowed_dirs[0], __FILE__, __LINE__);
1200
1201
			$parts = array();
1202
			// do not use "cleaner" foreach version of this loop as later code relies on both $segments and $i
1203
			// http://support.silisoftware.com/phpBB3/viewtopic.php?t=964
1204
			$segments = explode(DIRECTORY_SEPARATOR, $path);
1205
			for ($i = 0, $iMax = count($segments); $i < $iMax; $i++) {
1206
				$this->applyPathSegment($parts, $segments[$i]);
1207
				$thispart = implode(DIRECTORY_SEPARATOR, $parts);
1208
				if ($this->isInOpenBasedir($thispart)) {
1209
					if (is_link($thispart)) {
1210
						break;
1211
					}
1212
				}
1213
			}
1214
1215
			$this->DebugMessage('resolvePath: stop at component '.$i, __FILE__, __LINE__);
1216
			// test the part up to here
1217
			$path = implode(DIRECTORY_SEPARATOR, $parts);
1218
			$this->DebugMessage('resolvePath: stop at path='.$path, __FILE__, __LINE__);
1219
			if (!$this->matchPath($path, $allowed_dirs)) {
1220
				$this->DebugMessage('resolvePath: no match, returning null', __FILE__, __LINE__);
1221
				return null;
1222
			}
1223
			if ($i >= count($segments)) { // reached end
1224
				$this->DebugMessage('resolvePath: path parsed, over', __FILE__, __LINE__);
1225
				break;
1226
			}
1227
			// else it's symlink, rewrite path
1228
			$path = readlink($path);
1229
			$this->DebugMessage('resolvePath: symlink matched, target='.$path, __FILE__, __LINE__);
1230
1231
			/*
1232
			Replace base path with symlink target.
1233
			Assuming:
1234
			  /www/img/external -> /external
1235
			This is allowed:
1236
			  GET /www/img/external/../external/test/pic.jpg
1237
			This isn't:
1238
			  GET /www/img/external/../www/img/pic.jpg
1239
			So there's only one base path which is the last symlink target, but any number of stable whitelisted paths.
1240
			*/
1241
			if ($this->config_auto_allow_symlinks) {
1242
				$allowed_dirs[0] = $path;
1243
			}
1244
			$path = $path.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, array_slice($segments,$i + 1));
1245
		} while (true);
1246
		return $path;
1247
	}
1248
1249
1250
	public function realPathSafe($filename) {
1251
		// http://php.net/manual/en/function.realpath.php -- "Note: The running script must have executable permissions on all directories in the hierarchy, otherwise realpath() will return FALSE"
1252
		// realPathSafe() provides a reasonable facsimile of realpath() but does not resolve symbolic links, nor does it check that the file/path actually exists
1253
		if (!$this->config_disable_realpath) {
1254
			return realpath($filename);
1255
		}
1256
1257
		// http://stackoverflow.com/questions/21421569
1258
		$newfilename = preg_replace('#[\\/]+#', DIRECTORY_SEPARATOR, $filename);
1259
		if (!preg_match('#^'.DIRECTORY_SEPARATOR.'#', $newfilename)) {
1260
			$newfilename =  __DIR__ .DIRECTORY_SEPARATOR.$newfilename;
1261
		}
1262
		do {
1263
			$beforeloop = $newfilename;
1264
1265
			// Replace all sequences of more than one / with a single one [[ If you're working on a system that treats // at the start of a path as special, make sure you replace multiple / characters at the start with two of them. This is the only place where POSIX allows (but does not mandate) special handling for multiples, in all other cases, multiple / characters are equivalent to a single one.]]
1266
			$newfilename = preg_replace('#'.DIRECTORY_SEPARATOR.'+#', DIRECTORY_SEPARATOR, $newfilename);
1267
1268
			// Replace all occurrences of /./ with /
1269
			$newfilename = preg_replace('#'.DIRECTORY_SEPARATOR.'\\.'.DIRECTORY_SEPARATOR.'#', DIRECTORY_SEPARATOR, $newfilename);
1270
1271
			// Remove ./ if at the start
1272
			$newfilename = preg_replace('#^\\.'.DIRECTORY_SEPARATOR.'#', '', $newfilename);
1273
1274
			// Remove /. if at the end
1275
			$newfilename = preg_replace('#'.DIRECTORY_SEPARATOR.'\\.$#', '', $newfilename);
1276
1277
			// Replace /anything/../ with /
1278
			$newfilename = preg_replace('#'.DIRECTORY_SEPARATOR.'[^'.DIRECTORY_SEPARATOR.']+'.DIRECTORY_SEPARATOR.'\\.\\.'.DIRECTORY_SEPARATOR.'#', DIRECTORY_SEPARATOR, $newfilename);
1279
1280
			// Remove /anything/.. if at the end
1281
			$newfilename = preg_replace('#'.DIRECTORY_SEPARATOR.'[^'.DIRECTORY_SEPARATOR.']+'.DIRECTORY_SEPARATOR.'\\.\\.$#', '', $newfilename);
1282
1283
		} while ($newfilename != $beforeloop);
1284
		return $newfilename;
1285
	}
1286
1287
1288
	public function ResolveFilenameToAbsolute($filename) {
1289
		if (empty($filename)) {
1290
			return false;
1291
		}
1292
1293
		if (preg_match('#^[a-z0-9]+\:/{1,2}#i', $filename)) {
1294
			// eg: http://host/path/file.jpg (HTTP URL)
1295
			// eg: ftp://host/path/file.jpg  (FTP URL)
1296
			// eg: data1:/path/file.jpg      (Netware path)
1297
1298
			//$AbsoluteFilename = $filename;
1299
			return $filename;
1300
1301
		} elseif ($this->iswindows && isset($filename[1]) && ($filename[1] == ':')) {
1302
1303
			// absolute pathname (Windows)
1304
			$AbsoluteFilename = $filename;
1305
1306
		} elseif ($this->iswindows && ((substr($filename, 0, 2) == '//') || (substr($filename, 0, 2) == '\\\\'))) {
1307
1308
			// absolute pathname (Windows)
1309
			$AbsoluteFilename = $filename;
1310
1311
		} elseif ($filename[0] == '/') {
1312
1313
			if (@is_readable($filename) && !@is_readable($this->config_document_root.$filename)) {
0 ignored issues
show
Bug introduced by
Are you sure $this->config_document_root of type mixed can be used in concatenation? ( Ignorable by Annotation )

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

1313
			if (@is_readable($filename) && !@is_readable(/** @scrutinizer ignore-type */ $this->config_document_root.$filename)) {
Loading history...
1314
1315
				// absolute filename (*nix)
1316
				$AbsoluteFilename = $filename;
1317
1318
			} elseif (isset($filename[1]) && ($filename[1] == '~')) {
1319
1320
				// /~user/path
1321
				if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray($filename)) {
1322
					$AbsoluteFilename = $ApacheLookupURIarray['filename'];
1323
				} else {
1324
					$AbsoluteFilename = $this->realPathSafe($filename);
1325
					if (@is_readable($AbsoluteFilename)) {
1326
						$this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.$filename.'", but the correct filename ('.$AbsoluteFilename.') seems to have been resolved with $this->realPathSafe($filename)', __FILE__, __LINE__);
1327
					} elseif (is_dir(dirname($AbsoluteFilename))) {
1328
						$this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname($filename).'", but the correct directory ('.dirname($AbsoluteFilename).') seems to have been resolved with $this->realPathSafe(.)', __FILE__, __LINE__);
1329
					} else {
1330
						return $this->ErrorImage('phpthumb_functions::ApacheLookupURIarray() failed for "'.$filename.'". This has been known to fail on Apache2 - try using the absolute filename for the source image (ex: "/home/user/httpdocs/image.jpg" instead of "/~user/image.jpg")');
1331
					}
1332
				}
1333
1334
			} else {
1335
1336
				// relative filename (any OS)
1337
				if (preg_match('#^'.preg_quote($this->config_document_root).'#', $filename)) {
0 ignored issues
show
Bug introduced by
It seems like $this->config_document_root can also be of type mixed; however, parameter $str of preg_quote() does only seem to accept string, 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

1337
				if (preg_match('#^'.preg_quote(/** @scrutinizer ignore-type */ $this->config_document_root).'#', $filename)) {
Loading history...
1338
					$AbsoluteFilename = $filename;
1339
					$this->DebugMessage('ResolveFilenameToAbsolute() NOT prepending $this->config_document_root ('.$this->config_document_root.') to $filename ('.$filename.') resulting in ($AbsoluteFilename = "'.$AbsoluteFilename.'")', __FILE__, __LINE__);
1340
				} else {
1341
					$AbsoluteFilename = $this->config_document_root.$filename;
1342
					$this->DebugMessage('ResolveFilenameToAbsolute() prepending $this->config_document_root ('.$this->config_document_root.') to $filename ('.$filename.') resulting in ($AbsoluteFilename = "'.$AbsoluteFilename.'")', __FILE__, __LINE__);
1343
				}
1344
1345
			}
1346
1347
		} else {
1348
1349
			// relative to current directory (any OS)
1350
			$AbsoluteFilename =  __DIR__ .DIRECTORY_SEPARATOR.preg_replace('#[/\\\\]#', DIRECTORY_SEPARATOR, $filename);
1351
1352
			if (substr(dirname(@$_SERVER['PHP_SELF']), 0, 2) == '/~') {
1353
				if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray(dirname(@$_SERVER['PHP_SELF']))) {
1354
					$AbsoluteFilename = $ApacheLookupURIarray['filename'].DIRECTORY_SEPARATOR.$filename;
1355
				} else {
1356
					$AbsoluteFilename = $this->realPathSafe('.').DIRECTORY_SEPARATOR.$filename;
1357
					if (@is_readable($AbsoluteFilename)) {
1358
						$this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname(@$_SERVER['PHP_SELF']).'", but the correct filename ('.$AbsoluteFilename.') seems to have been resolved with $this->realPathSafe(.)/$filename', __FILE__, __LINE__);
1359
					} elseif (is_dir(dirname($AbsoluteFilename))) {
1360
						$this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname(@$_SERVER['PHP_SELF']).'", but the correct directory ('.dirname($AbsoluteFilename).') seems to have been resolved with $this->realPathSafe(.)', __FILE__, __LINE__);
1361
					} else {
1362
						return $this->ErrorImage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname(@$_SERVER['PHP_SELF']).'". This has been known to fail on Apache2 - try using the absolute filename for the source image');
1363
					}
1364
				}
1365
			}
1366
1367
		}
1368
		/*
1369
		// removed 2014-May-30: http://support.silisoftware.com/phpBB3/viewtopic.php?t=961
1370
		if (is_link($AbsoluteFilename)) {
1371
			$this->DebugMessage('is_link()==true, changing "'.$AbsoluteFilename.'" to "'.readlink($AbsoluteFilename).'"', __FILE__, __LINE__);
1372
			$AbsoluteFilename = readlink($AbsoluteFilename);
1373
		}
1374
		if ($this->realPathSafe($AbsoluteFilename)) {
1375
			$AbsoluteFilename = $this->realPathSafe($AbsoluteFilename);
1376
		}
1377
		*/
1378
		if ($this->iswindows) {
1379
			$AbsoluteFilename = preg_replace('#^'.preg_quote($this->realPathSafe($this->config_document_root)).'#i', str_replace('\\', '\\\\', $this->realPathSafe($this->config_document_root)), $AbsoluteFilename);
1380
			$AbsoluteFilename = str_replace(DIRECTORY_SEPARATOR, '/', $AbsoluteFilename);
1381
		}
1382
		$resolvedAbsoluteFilename = $this->resolvePath($AbsoluteFilename, $this->config_additional_allowed_dirs);
1383
		if (!$this->config_allow_src_above_docroot && !preg_match('#^'.preg_quote(str_replace(DIRECTORY_SEPARATOR, '/', $this->realPathSafe($this->config_document_root))).'#', $resolvedAbsoluteFilename)) {
1384
			$this->DebugMessage('!$this->config_allow_src_above_docroot therefore setting "'.$AbsoluteFilename.'" (outside "'.$this->realPathSafe($this->config_document_root).'") to null', __FILE__, __LINE__);
1385
			return false;
1386
		}
1387
		if (!$this->config_allow_src_above_phpthumb && !preg_match('#^'.preg_quote(str_replace(DIRECTORY_SEPARATOR, '/',  __DIR__ )).'#', $resolvedAbsoluteFilename)) {
1388
			$this->DebugMessage('!$this->config_allow_src_above_phpthumb therefore setting "'.$AbsoluteFilename.'" (outside "'. __DIR__ .'") to null', __FILE__, __LINE__);
1389
			return false;
1390
		}
1391
		return $resolvedAbsoluteFilename;
1392
	}
1393
1394
1395
	public function file_exists_ignoreopenbasedir($filename, $cached=true) {
1396
		static $open_basedirs = null;
1397
		static $file_exists_cache = array();
1398
		if (!$cached || !isset($file_exists_cache[$filename])) {
1399
			if (null === $open_basedirs) {
1400
				$open_basedirs = preg_split('#[;:]#', ini_get('open_basedir'));
1401
			}
1402
			if (empty($open_basedirs) || in_array(dirname($filename), $open_basedirs)) {
1403
				$file_exists_cache[$filename] = file_exists($filename);
1404
			} elseif ($this->iswindows) {
1405
				$ls_filename = trim(phpthumb_functions::SafeExec('dir /b '.phpthumb_functions::escapeshellarg_replacement($filename)));
0 ignored issues
show
Bug introduced by
It seems like phpthumb_functions::Safe...replacement($filename)) can also be of type false; however, parameter $str of trim() does only seem to accept string, 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

1405
				$ls_filename = trim(/** @scrutinizer ignore-type */ phpthumb_functions::SafeExec('dir /b '.phpthumb_functions::escapeshellarg_replacement($filename)));
Loading history...
1406
				$file_exists_cache[$filename] = ($ls_filename == basename($filename));  // command dir /b return only filename without path
1407
			} else {
1408
				$ls_filename = trim(phpthumb_functions::SafeExec('ls '.phpthumb_functions::escapeshellarg_replacement($filename)));
1409
				$file_exists_cache[$filename] = ($ls_filename == $filename);
1410
			}
1411
		}
1412
		return $file_exists_cache[$filename];
1413
	}
1414
1415
1416
	public function ImageMagickWhichConvert() {
1417
		static $WhichConvert = null;
1418
		if (null === $WhichConvert) {
1419
			if ($this->iswindows) {
1420
				$WhichConvert = false;
1421
			} else {
1422
				$IMwhichConvertCacheFilename = $this->config_cache_directory.DIRECTORY_SEPARATOR.'phpThumbCacheIMwhichConvert.txt';
1423
				if (($cachedwhichconvertstring = @file_get_contents($IMwhichConvertCacheFilename)) !== false) {
1424
					$WhichConvert = $cachedwhichconvertstring;
1425
				} else {
1426
					$WhichConvert = trim(phpthumb_functions::SafeExec('which convert'));
0 ignored issues
show
Bug introduced by
It seems like phpthumb_functions::SafeExec('which convert') can also be of type false; however, parameter $str of trim() does only seem to accept string, 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

1426
					$WhichConvert = trim(/** @scrutinizer ignore-type */ phpthumb_functions::SafeExec('which convert'));
Loading history...
1427
					@file_put_contents($IMwhichConvertCacheFilename, $WhichConvert);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for file_put_contents(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1427
					/** @scrutinizer ignore-unhandled */ @file_put_contents($IMwhichConvertCacheFilename, $WhichConvert);

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...
1428
					@chmod($IMwhichConvertCacheFilename, $this->getParameter('config_file_create_mask'));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1428
					/** @scrutinizer ignore-unhandled */ @chmod($IMwhichConvertCacheFilename, $this->getParameter('config_file_create_mask'));

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...
1429
				}
1430
			}
1431
		}
1432
		return $WhichConvert;
1433
	}
1434
1435
1436
	public function ImageMagickCommandlineBase() {
1437
		static $commandline = null;
1438
		if (null === $commandline) {
1439
			if ($this->issafemode) {
1440
				$commandline = '';
1441
				return $commandline;
1442
			}
1443
1444
			$IMcommandlineBaseCacheFilename = $this->config_cache_directory.DIRECTORY_SEPARATOR.'phpThumbCacheIMcommandlineBase.txt';
1445
			if (($commandline = @file_get_contents($IMcommandlineBaseCacheFilename)) !== false) {
1446
				return $commandline;
1447
			}
1448
1449
			$commandline = (null !== $this->config_imagemagick_path ? $this->config_imagemagick_path : '');
0 ignored issues
show
Unused Code introduced by
The assignment to $commandline is dead and can be removed.
Loading history...
1450
1451
			if ($this->config_imagemagick_path && ($this->config_imagemagick_path != $this->realPathSafe($this->config_imagemagick_path))) {
1452
				if (@is_executable($this->realPathSafe($this->config_imagemagick_path))) {
1453
					$this->DebugMessage('Changing $this->config_imagemagick_path ('.$this->config_imagemagick_path.') to $this->realPathSafe($this->config_imagemagick_path) ('.$this->realPathSafe($this->config_imagemagick_path).')', __FILE__, __LINE__);
1454
					$this->config_imagemagick_path = $this->realPathSafe($this->config_imagemagick_path);
1455
				} else {
1456
					$this->DebugMessage('Leaving $this->config_imagemagick_path as ('.$this->config_imagemagick_path.') because !is_execuatable($this->realPathSafe($this->config_imagemagick_path)) ('.$this->realPathSafe($this->config_imagemagick_path).')', __FILE__, __LINE__);
1457
				}
1458
			}
1459
			$this->DebugMessage('                  file_exists('.$this->config_imagemagick_path.') = '. (int) (@file_exists($this->config_imagemagick_path)), __FILE__, __LINE__);
1460
			$this->DebugMessage('file_exists_ignoreopenbasedir('.$this->config_imagemagick_path.') = '. (int) $this->file_exists_ignoreopenbasedir($this->config_imagemagick_path), __FILE__, __LINE__);
1461
			$this->DebugMessage('                      is_file('.$this->config_imagemagick_path.') = '. (int) (@is_file($this->config_imagemagick_path)), __FILE__, __LINE__);
1462
			$this->DebugMessage('                is_executable('.$this->config_imagemagick_path.') = '. (int) (@is_executable($this->config_imagemagick_path)), __FILE__, __LINE__);
1463
1464
			if ($this->file_exists_ignoreopenbasedir($this->config_imagemagick_path)) {
1465
1466
				$this->DebugMessage('using ImageMagick path from $this->config_imagemagick_path ('.$this->config_imagemagick_path.')', __FILE__, __LINE__);
1467
				if ($this->iswindows) {
1468
					$commandline = substr($this->config_imagemagick_path, 0, 2).' && cd '.phpthumb_functions::escapeshellarg_replacement(str_replace('/', DIRECTORY_SEPARATOR, substr(dirname($this->config_imagemagick_path), 2))).' && '.phpthumb_functions::escapeshellarg_replacement(basename($this->config_imagemagick_path));
1469
				} else {
1470
					$commandline = phpthumb_functions::escapeshellarg_replacement($this->config_imagemagick_path);
1471
				}
1472
1473
			} else {
1474
1475
				$which_convert = $this->ImageMagickWhichConvert();
1476
				$IMversion     = $this->ImageMagickVersion();
1477
1478
				if ($which_convert && ($which_convert[0] == '/') && $this->file_exists_ignoreopenbasedir($which_convert)) {
1479
1480
					// `which convert` *should* return the path if "convert" exist, or nothing if it doesn't
1481
					// other things *may* get returned, like "sh: convert: not found" or "no convert in /usr/local/bin /usr/sbin /usr/bin /usr/ccs/bin"
1482
					// so only do this if the value returned exists as a file
1483
					$this->DebugMessage('using ImageMagick path from `which convert` ('.$which_convert.')', __FILE__, __LINE__);
1484
					$commandline = 'convert';
1485
1486
				} elseif ($IMversion) {
1487
1488
					$this->DebugMessage('setting ImageMagick path to $this->config_imagemagick_path ('.$this->config_imagemagick_path.') ['.$IMversion.']', __FILE__, __LINE__);
1489
					$commandline = $this->config_imagemagick_path;
1490
1491
				} else {
1492
1493
					$this->DebugMessage('ImageMagickThumbnailToGD() aborting because cannot find convert in $this->config_imagemagick_path ('.$this->config_imagemagick_path.'), and `which convert` returned ('.$which_convert.')', __FILE__, __LINE__);
1494
					$commandline = '';
1495
1496
				}
1497
1498
			}
1499
1500
			@file_put_contents($IMcommandlineBaseCacheFilename, $commandline);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for file_put_contents(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1500
			/** @scrutinizer ignore-unhandled */ @file_put_contents($IMcommandlineBaseCacheFilename, $commandline);

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...
1501
			@chmod($IMcommandlineBaseCacheFilename, $this->getParameter('config_file_create_mask'));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1501
			/** @scrutinizer ignore-unhandled */ @chmod($IMcommandlineBaseCacheFilename, $this->getParameter('config_file_create_mask'));

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...
1502
		}
1503
		return $commandline;
1504
	}
1505
1506
1507
	public function ImageMagickVersion($returnRAW=false) {
1508
		static $versionstring = null;
1509
		if (null === $versionstring) {
1510
			$versionstring = array(0=>false, 1=>false);
1511
1512
			$IMversionCacheFilename = $this->config_cache_directory.DIRECTORY_SEPARATOR.'phpThumbCacheIMversion.txt';
1513
			if ($cachedversionstring = @file_get_contents($IMversionCacheFilename)) {
1514
1515
				$versionstring = explode("\n", $cachedversionstring, 2);
1516
				$versionstring[0] = ($versionstring[0] ? $versionstring[0] : false); // "false" is stored as an empty string in the cache file
1517
				$versionstring[1] = ($versionstring[1] ? $versionstring[1] : false); // "false" is stored as an empty string in the cache file
1518
1519
			} else {
1520
1521
				$commandline = $this->ImageMagickCommandlineBase();
1522
				$commandline = (null !== $commandline ? $commandline : '');
1523
				if ($commandline) {
1524
					$commandline .= ' --version';
1525
					$this->DebugMessage('ImageMagick version checked with "'.$commandline.'"', __FILE__, __LINE__);
1526
					$versionstring[1] = trim(phpthumb_functions::SafeExec($commandline));
0 ignored issues
show
Bug introduced by
It seems like phpthumb_functions::SafeExec($commandline) can also be of type false; however, parameter $str of trim() does only seem to accept string, 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

1526
					$versionstring[1] = trim(/** @scrutinizer ignore-type */ phpthumb_functions::SafeExec($commandline));
Loading history...
1527
					if (preg_match('#^Version: [^\d]*([ 0-9\\.\\:Q/\\-]+)#i', $versionstring[1], $matches)) {
1528
						$versionstring[0] = trim($matches[1]);
1529
					} else {
1530
						$versionstring[0] = false;
1531
						$this->DebugMessage('ImageMagick did not return recognized version string ('.$versionstring[1].')', __FILE__, __LINE__);
1532
					}
1533
					$this->DebugMessage('ImageMagick convert --version says "'.@$matches[0].'"', __FILE__, __LINE__);
1534
				}
1535
1536
				@file_put_contents($IMversionCacheFilename, $versionstring[0]."\n".$versionstring[1]);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for file_put_contents(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1536
				/** @scrutinizer ignore-unhandled */ @file_put_contents($IMversionCacheFilename, $versionstring[0]."\n".$versionstring[1]);

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...
1537
				@chmod($IMversionCacheFilename, $this->getParameter('config_file_create_mask'));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1537
				/** @scrutinizer ignore-unhandled */ @chmod($IMversionCacheFilename, $this->getParameter('config_file_create_mask'));

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...
1538
1539
			}
1540
		}
1541
		return $versionstring[ (int) $returnRAW ];
1542
	}
1543
1544
1545
	public function ImageMagickSwitchAvailable($switchname) {
1546
		static $IMoptions = null;
1547
		if (null === $IMoptions) {
1548
			$IMoptions = array();
1549
			$commandline = $this->ImageMagickCommandlineBase();
1550
			if (null !== $commandline) {
1551
				$commandline .= ' -help';
1552
				$IMhelp_lines = explode("\n", phpthumb_functions::SafeExec($commandline));
0 ignored issues
show
Bug introduced by
It seems like phpthumb_functions::SafeExec($commandline) can also be of type false; however, parameter $string of explode() does only seem to accept string, 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

1552
				$IMhelp_lines = explode("\n", /** @scrutinizer ignore-type */ phpthumb_functions::SafeExec($commandline));
Loading history...
1553
				foreach ($IMhelp_lines as $line) {
1554
					if (preg_match('#^[\\+\\-]([a-z\\-]+) #', trim($line), $matches)) {
1555
						$IMoptions[$matches[1]] = true;
1556
					}
1557
				}
1558
			}
1559
		}
1560
		if (is_array($switchname)) {
1561
			$allOK = true;
1562
			foreach ($switchname as $key => $value) {
1563
				if (!isset($IMoptions[$value])) {
1564
					$allOK = false;
1565
					break;
1566
				}
1567
			}
1568
			$this->DebugMessage('ImageMagickSwitchAvailable('.implode(';', $switchname).') = '. (int) $allOK .'', __FILE__, __LINE__);
1569
		} else {
1570
			$allOK = isset($IMoptions[$switchname]);
1571
			$this->DebugMessage('ImageMagickSwitchAvailable('.$switchname.') = '. (int) $allOK .'', __FILE__, __LINE__);
1572
		}
1573
		return $allOK;
1574
	}
1575
1576
1577
	public function ImageMagickFormatsList() {
1578
		static $IMformatsList = null;
1579
		if (null === $IMformatsList) {
1580
			$IMformatsList = '';
1581
			$commandline = $this->ImageMagickCommandlineBase();
1582
			if (null !== $commandline) {
1583
				$commandline = dirname($commandline).DIRECTORY_SEPARATOR.str_replace('convert', 'identify', basename($commandline));
1584
				$commandline .= ' -list format';
1585
				$IMformatsList = phpthumb_functions::SafeExec($commandline);
1586
			}
1587
		}
1588
		return $IMformatsList;
1589
	}
1590
1591
1592
	public function SourceDataToTempFile() {
1593
		if ($IMtempSourceFilename = $this->phpThumb_tempnam()) {
1594
			$IMtempSourceFilename = $this->realPathSafe($IMtempSourceFilename);
1595
			ob_start();
1596
			$fp_tempfile = fopen($IMtempSourceFilename, 'wb');
1597
			$tempfile_open_error  = ob_get_contents();
1598
			ob_end_clean();
1599
			if ($fp_tempfile) {
0 ignored issues
show
introduced by
$fp_tempfile is of type false|resource, thus it always evaluated to false.
Loading history...
1600
				fwrite($fp_tempfile, $this->rawImageData);
1601
				fclose($fp_tempfile);
1602
				@chmod($IMtempSourceFilename, $this->getParameter('config_file_create_mask'));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1602
				/** @scrutinizer ignore-unhandled */ @chmod($IMtempSourceFilename, $this->getParameter('config_file_create_mask'));

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...
1603
				$this->sourceFilename = $IMtempSourceFilename;
1604
				$this->DebugMessage('ImageMagickThumbnailToGD() setting $this->sourceFilename to "'.$IMtempSourceFilename.'" from $this->rawImageData ('.strlen($this->rawImageData).' bytes)', __FILE__, __LINE__);
1605
			} else {
1606
				$this->DebugMessage('ImageMagickThumbnailToGD() FAILED setting $this->sourceFilename to "'.$IMtempSourceFilename.'" (failed to open for writing: "'.$tempfile_open_error.'")', __FILE__, __LINE__);
1607
			}
1608
			unset($tempfile_open_error, $IMtempSourceFilename);
1609
			return true;
1610
		}
1611
		$this->DebugMessage('SourceDataToTempFile() FAILED because $this->phpThumb_tempnam() failed', __FILE__, __LINE__);
1612
		return false;
1613
	}
1614
1615
1616
	public function ImageMagickThumbnailToGD() {
1617
		// http://www.imagemagick.org/script/command-line-options.php
1618
1619
		$this->useRawIMoutput = true;
1620
		if (phpthumb_functions::gd_version()) {
1621
			// if GD is not available, must use whatever ImageMagick can output
1622
1623
			// $UnAllowedParameters contains options that can only be processed in GD, not ImageMagick
1624
			// note: 'fltr' *may* need to be processed by GD, but we'll check that in more detail below
1625
			$UnAllowedParameters = array('xto', 'ar', 'bg', 'bc');
1626
			// 'ra' may be part of this list, if not a multiple of 90 degrees
1627
			foreach ($UnAllowedParameters as $parameter) {
1628
				if (isset($this->$parameter)) {
1629
					$this->DebugMessage('$this->useRawIMoutput=false because "'.$parameter.'" is set', __FILE__, __LINE__);
1630
					$this->useRawIMoutput = false;
1631
					break;
1632
				}
1633
			}
1634
		}
1635
		$this->DebugMessage('$this->useRawIMoutput='.($this->useRawIMoutput ? 'true' : 'false').' after checking $UnAllowedParameters', __FILE__, __LINE__);
1636
		$ImageCreateFunction = '';
1637
		$outputFormat = $this->thumbnailFormat;
1638
		if (phpthumb_functions::gd_version()) {
1639
			if ($this->useRawIMoutput) {
1640
				switch ($this->thumbnailFormat) {
1641
					case 'gif':
1642
						$ImageCreateFunction = 'imagecreatefromgif';
1643
						$this->is_alpha = true;
1644
						break;
1645
					case 'png':
1646
						$ImageCreateFunction = 'imagecreatefrompng';
1647
						$this->is_alpha = true;
1648
						break;
1649
					case 'jpg':
1650
					case 'jpeg':
1651
						$ImageCreateFunction = 'imagecreatefromjpeg';
1652
						break;
1653
					case 'webp':
1654
						$ImageCreateFunction = 'imagecreatefromwebp';
1655
						$this->is_alpha = true;
1656
						break;
1657
					default:
1658
						$this->DebugMessage('Forcing output to PNG because $this->thumbnailFormat ('.$this->thumbnailFormat.' is not a GD-supported format)', __FILE__, __LINE__);
1659
						$outputFormat = 'png';
1660
						$ImageCreateFunction = 'imagecreatefrompng';
1661
						$this->is_alpha = true;
1662
						$this->useRawIMoutput = false;
1663
						break;
1664
				}
1665
				if (!function_exists($ImageCreateFunction)) {
1666
					// ImageMagickThumbnailToGD() depends on imagecreatefrompng/imagecreatefromgif
1667
					//$this->DebugMessage('ImageMagickThumbnailToGD() aborting because '.@$ImageCreateFunction.'() is not available', __FILE__, __LINE__);
1668
					$this->useRawIMoutput = true;
1669
					//return false;
1670
				}
1671
			} else {
1672
				$outputFormat = 'png';
1673
				$ImageCreateFunction = 'imagecreatefrompng';
1674
				$this->is_alpha = true;
1675
				$this->useRawIMoutput = false;
1676
			}
1677
		}
1678
1679
		// http://freealter.org/doc_distrib/ImageMagick-5.1.1/www/convert.html
1680
		if (!$this->sourceFilename && $this->rawImageData) {
1681
			$this->SourceDataToTempFile();
1682
		}
1683
		if (!$this->sourceFilename) {
1684
			$this->DebugMessage('ImageMagickThumbnailToGD() aborting because $this->sourceFilename is empty', __FILE__, __LINE__);
1685
			$this->useRawIMoutput = false;
1686
			return false;
1687
		}
1688
		if ($this->issafemode) {
1689
			$this->DebugMessage('ImageMagickThumbnailToGD() aborting because safe_mode is enabled', __FILE__, __LINE__);
1690
			$this->useRawIMoutput = false;
1691
			return false;
1692
		}
1693
// TO BE FIXED
1694
//if (true) {
1695
//	$this->DebugMessage('ImageMagickThumbnailToGD() aborting it is broken right now', __FILE__, __LINE__);
1696
//	$this->useRawIMoutput = false;
1697
//	return false;
1698
//}
1699
1700
		$commandline = $this->ImageMagickCommandlineBase();
1701
		if ($commandline) {
1702
			$commandline .= ' '.phpthumb_functions::escapeshellarg_replacement(preg_replace('#[/\\\\]#', DIRECTORY_SEPARATOR, $this->sourceFilename).(($outputFormat == 'gif') ? '' : '['. (int) $this->sfn .']')); // [0] means first frame of (GIF) animation, can be ignored
1703
			if ($IMtempfilename = $this->phpThumb_tempnam()) {
1704
				$IMtempfilename = $this->realPathSafe($IMtempfilename);
1705
1706
				$IMuseExplicitImageOutputDimensions = false;
1707
				if ($this->ImageMagickSwitchAvailable('thumbnail') && $this->config_imagemagick_use_thumbnail) {
1708
					$IMresizeParameter = 'thumbnail';
1709
				} else {
1710
					$IMresizeParameter = 'resize';
1711
1712
					// some (older? around 2002) versions of IM won't accept "-resize 100x" but require "-resize 100x100"
1713
					$commandline_test = $this->ImageMagickCommandlineBase().' logo: -resize 1x '.phpthumb_functions::escapeshellarg_replacement($IMtempfilename).' 2>&1';
1714
					$IMresult_test = phpthumb_functions::SafeExec($commandline_test);
1715
					$IMuseExplicitImageOutputDimensions = preg_match('#image dimensions are zero#i', $IMresult_test);
0 ignored issues
show
Bug introduced by
It seems like $IMresult_test can also be of type false; however, parameter $subject of preg_match() does only seem to accept string, 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

1715
					$IMuseExplicitImageOutputDimensions = preg_match('#image dimensions are zero#i', /** @scrutinizer ignore-type */ $IMresult_test);
Loading history...
1716
					$this->DebugMessage('IMuseExplicitImageOutputDimensions = '. (int) $IMuseExplicitImageOutputDimensions, __FILE__, __LINE__);
1717
					if ($fp_im_temp = @fopen($IMtempfilename, 'wb')) {
1718
						// erase temp image so ImageMagick logo doesn't get output if other processing fails
1719
						fclose($fp_im_temp);
1720
						@chmod($IMtempfilename, $this->getParameter('config_file_create_mask'));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1720
						/** @scrutinizer ignore-unhandled */ @chmod($IMtempfilename, $this->getParameter('config_file_create_mask'));

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...
1721
					}
1722
				}
1723
1724
1725
				ob_start();
1726
				$getimagesize = getimagesize($this->sourceFilename);
1727
				$GetImageSizeError = ob_get_contents();
1728
				ob_end_clean();
1729
				if (is_array($getimagesize)) {
1730
					$this->DebugMessage('getimagesize('.$this->sourceFilename.') SUCCEEDED: '.print_r($getimagesize, true), __FILE__, __LINE__);
1731
				} else {
1732
					$this->DebugMessage('getimagesize('.$this->sourceFilename.') FAILED with error "'.$GetImageSizeError.'"', __FILE__, __LINE__);
1733
				}
1734
				if (null !== $this->dpi && $this->ImageMagickSwitchAvailable('density')) {
1735
					// for vector source formats only (WMF, PDF, etc)
1736
					if (is_array($getimagesize) && isset($getimagesize[2]) && ($getimagesize[2] == IMAGETYPE_PNG)) {
1737
						// explicitly exclude PNG from "-flatten" to make sure transparency is preserved
1738
						// https://github.com/JamesHeinrich/phpThumb/issues/65
1739
					} else {
1740
						$commandline .= ' -flatten';
1741
						$commandline .= ' -density '.phpthumb_functions::escapeshellarg_replacement($this->dpi);
1742
					}
1743
				}
1744
				if (is_array($getimagesize)) {
1745
					$this->DebugMessage('getimagesize('.$this->sourceFilename.') returned [w='.$getimagesize[0].';h='.$getimagesize[1].';f='.$getimagesize[2].']', __FILE__, __LINE__);
1746
					$this->source_width  = $getimagesize[0];
1747
					$this->source_height = $getimagesize[1];
1748
					$this->DebugMessage('source dimensions set to '.$this->source_width.'x'.$this->source_height, __FILE__, __LINE__);
1749
					$this->SetOrientationDependantWidthHeight();
1750
1751
					if (!preg_match('#('.implode('|', $this->AlphaCapableFormats).')#i', $outputFormat)) {
1752
						// not a transparency-capable format
1753
						$commandline .= ' -background '.phpthumb_functions::escapeshellarg_replacement('#'.($this->bg ? $this->bg : 'FFFFFF'));
1754
						if (!stristr($commandline, ' -flatten')) {
1755
							$commandline .= ' -flatten';
1756
						}
1757
					} else {
1758
						if ($getimagesize[2] == IMAGETYPE_PNG && !$this->bg) {
1759
							$commandline .= ' -background none';
1760
						}
1761
					}
1762
					if ($getimagesize[2] == IMAGETYPE_GIF) {
1763
						$commandline .= ' -coalesce'; // may be needed for animated GIFs
1764
					}
1765
					if ($this->source_width || $this->source_height) {
1766
						if ($this->zc) {
1767
1768
							$borderThickness = 0;
1769
							if (!empty($this->fltr)) {
1770
								foreach ($this->fltr as $key => $value) {
1771
									if (preg_match('#^bord\|([\d]+)#', $value, $matches)) {
1772
										$borderThickness = $matches[1];
1773
										break;
1774
									}
1775
								}
1776
							}
1777
							$wAll = (int) max($this->w, $this->wp, $this->wl, $this->ws) - (2 * $borderThickness);
1778
							$hAll = (int) max($this->h, $this->hp, $this->hl, $this->hs) - (2 * $borderThickness);
1779
							$imAR = $this->source_width / $this->source_height;
1780
							$zcAR = (($wAll && $hAll) ? $wAll / $hAll : 1);
1781
							$side  = phpthumb_functions::nonempty_min($this->source_width, $this->source_height, max($wAll, $hAll));
1782
							$sideX = phpthumb_functions::nonempty_min($this->source_width,                       $wAll, round($hAll * $zcAR));
0 ignored issues
show
Unused Code introduced by
The assignment to $sideX is dead and can be removed.
Loading history...
1783
							$sideY = phpthumb_functions::nonempty_min(                     $this->source_height, $hAll, round($wAll / $zcAR));
1784
1785
							$thumbnailH = round(max($sideY, ($sideY * $zcAR) / $imAR));
1786
							if ($this->aoe == 1) {
1787
								$commandline .= ' -'.$IMresizeParameter.' "'.$wAll.'x'.$hAll.'^"';
1788
							} else {
1789
								$commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement(($IMuseExplicitImageOutputDimensions ? $thumbnailH : '').'x'.$thumbnailH);
1790
							}
1791
1792
							switch (strtoupper($this->zc)) {
1793
								case 'T':
1794
									$commandline .= ' -gravity north';
1795
									break;
1796
								case 'B':
1797
									$commandline .= ' -gravity south';
1798
									break;
1799
								case 'L':
1800
									$commandline .= ' -gravity west';
1801
									break;
1802
								case 'R':
1803
									$commandline .= ' -gravity east';
1804
									break;
1805
								case 'TL':
1806
									$commandline .= ' -gravity northwest';
1807
									break;
1808
								case 'TR':
1809
									$commandline .= ' -gravity northeast';
1810
									break;
1811
								case 'BL':
1812
									$commandline .= ' -gravity southwest';
1813
									break;
1814
								case 'BR':
1815
									$commandline .= ' -gravity southeast';
1816
									break;
1817
								case '1':
1818
								case 'C':
1819
								default:
1820
									$commandline .= ' -gravity center';
1821
									break;
1822
							}
1823
1824
							if (($wAll > 0) && ($hAll > 0)) {
1825
								$commandline .= ' -crop '.phpthumb_functions::escapeshellarg_replacement($wAll.'x'.$hAll.'+0+0');
1826
							} else {
1827
								$commandline .= ' -crop '.phpthumb_functions::escapeshellarg_replacement($side.'x'.$side.'+0+0');
1828
							}
1829
							if ($this->ImageMagickSwitchAvailable('repage')) {
1830
								$commandline .= ' +repage';
1831
							} else {
1832
								$this->DebugMessage('Skipping "+repage" because ImageMagick (v'.$this->ImageMagickVersion().') does not support it', __FILE__, __LINE__);
1833
							}
1834
1835
						} elseif ($this->sw || $this->sh || $this->sx || $this->sy) {
1836
1837
							$crop_param   = '';
1838
							$crop_param  .=     ($this->sw ? (($this->sw < 2) ? round($this->sw * $this->source_width)  : $this->sw) : $this->source_width);
1839
							$crop_param  .= 'x'.($this->sh ? (($this->sh < 2) ? round($this->sh * $this->source_height) : $this->sh) : $this->source_height);
1840
							$crop_param  .= '+'.(($this->sx < 2) ? round($this->sx * $this->source_width)  : $this->sx);
1841
							$crop_param  .= '+'.(($this->sy < 2) ? round($this->sy * $this->source_height) : $this->sy);
1842
// TO BE FIXED
1843
// makes 1x1 output
1844
// http://trainspotted.com/phpThumb/phpThumb.php?src=/content/CNR/47/CNR-4728-LD-L-20110723-898.jpg&w=100&h=100&far=1&f=png&fltr[]=lvl&sx=0.05&sy=0.25&sw=0.92&sh=0.42
1845
// '/usr/bin/convert' -density 150 -thumbnail 100x100 -contrast-stretch '0.1%' '/var/www/vhosts/trainspotted.com/httpdocs/content/CNR/47/CNR-4728-LD-L-20110723-898.jpg[0]' png:'/var/www/vhosts/trainspotted.com/httpdocs/phpThumb/_cache/pThumbIIUlvj'
1846
							$commandline .= ' -crop '.phpthumb_functions::escapeshellarg_replacement($crop_param);
1847
1848
							// this is broken for aoe=1, but unsure how to fix. Send advice to [email protected]
1849
							if ($this->w || $this->h) {
1850
								//if ($this->ImageMagickSwitchAvailable('repage')) {
1851
if (false) {
1852
// TO BE FIXED
1853
// newer versions of ImageMagick require -repage <geometry>
1854
									$commandline .= ' -repage';
1855
								} else {
1856
									$this->DebugMessage('Skipping "-repage" because ImageMagick (v'.$this->ImageMagickVersion().') does not support it', __FILE__, __LINE__);
1857
								}
1858
								if ($IMuseExplicitImageOutputDimensions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $IMuseExplicitImageOutputDimensions 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...
1859
									if ($this->w && !$this->h) {
1860
										$this->h = ceil($this->w / ($this->source_width / $this->source_height));
1861
									} elseif ($this->h && !$this->w) {
1862
										$this->w = ceil($this->h * ($this->source_width / $this->source_height));
1863
									}
1864
								}
1865
								$commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement($this->w.'x'.$this->h);
1866
							}
1867
1868
						} else {
1869
1870
							if ($this->iar && ((int) $this->w > 0) && ((int) $this->h > 0)) {
1871
1872
								list($nw, $nh) = phpthumb_functions::TranslateWHbyAngle($this->w, $this->h, $this->ra);
1873
								$nw = ((round($nw) != 0) ? round($nw) : '');
1874
								$nh = ((round($nh) != 0) ? round($nh) : '');
1875
								$commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement($nw.'x'.$nh.'!');
1876
1877
							} elseif ($this->far && ((int) $this->w > 0) && ((int) $this->h > 0)) {
1878
1879
								$commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement(phpthumb_functions::nonempty_min($this->w, $getimagesize[0]).'x'.phpthumb_functions::nonempty_min($this->h, $getimagesize[1]));
1880
								$commandline .= ' -gravity center';
1881
								if ($this->bg) {
1882
								$commandline .= ' -background '.phpthumb_functions::escapeshellarg_replacement('#'.$this->bg);
1883
								} else {
1884
									$commandline .= ' -background none';
1885
								}
1886
								$commandline .= ' -extent '.phpthumb_functions::escapeshellarg_replacement($this->w.'x'.$this->h);
0 ignored issues
show
Bug introduced by
Are you sure $this->w of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1886
								$commandline .= ' -extent '.phpthumb_functions::escapeshellarg_replacement(/** @scrutinizer ignore-type */ $this->w.'x'.$this->h);
Loading history...
Bug introduced by
Are you sure $this->h of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1886
								$commandline .= ' -extent '.phpthumb_functions::escapeshellarg_replacement($this->w.'x'./** @scrutinizer ignore-type */ $this->h);
Loading history...
1887
1888
							} else {
1889
1890
								$this->w = (($this->aoe && $this->w) ? $this->w : ($this->w ? phpthumb_functions::nonempty_min($this->w, $getimagesize[0]) : ''));
1891
								$this->h = (($this->aoe && $this->h) ? $this->h : ($this->h ? phpthumb_functions::nonempty_min($this->h, $getimagesize[1]) : ''));
1892
								if ($this->w || $this->h) {
1893
									if ($IMuseExplicitImageOutputDimensions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $IMuseExplicitImageOutputDimensions 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...
1894
										if ($this->w && !$this->h) {
1895
											$this->h = ceil($this->w / ($this->source_width / $this->source_height));
1896
										} elseif ($this->h && !$this->w) {
1897
											$this->w = ceil($this->h * ($this->source_width / $this->source_height));
1898
										}
1899
									}
1900
									list($nw, $nh) = phpthumb_functions::TranslateWHbyAngle($this->w, $this->h, $this->ra);
1901
									$nw = ((round($nw) != 0) ? round($nw) : '');
1902
									$nh = ((round($nh) != 0) ? round($nh) : '');
1903
									$commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement($nw.'x'.$nh);
1904
								}
1905
1906
							}
1907
						}
1908
					}
1909
1910
				} else {
1911
1912
					$this->DebugMessage('getimagesize('.$this->sourceFilename.') failed', __FILE__, __LINE__);
1913
					if ($this->w || $this->h) {
1914
						$exactDimensionsBang = (($this->iar && ((int) $this->w > 0) && ((int) $this->h > 0)) ? '!' : '');
1915
						if ($IMuseExplicitImageOutputDimensions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $IMuseExplicitImageOutputDimensions 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...
1916
							// unknown source aspect ratio, just put large number and hope IM figures it out
1917
							$commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement(($this->w ? $this->w : '9999').'x'.($this->h ? $this->h : '9999').$exactDimensionsBang);
1918
						} else {
1919
							$commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement($this->w.'x'.$this->h.$exactDimensionsBang);
1920
						}
1921
					}
1922
1923
				}
1924
1925
				if ($this->ra) {
1926
					$this->ra = (int) $this->ra;
1927
					if ($this->ImageMagickSwitchAvailable('rotate')) {
1928
						if (!preg_match('#('.implode('|', $this->AlphaCapableFormats).')#i', $outputFormat) || phpthumb_functions::version_compare_replacement($this->ImageMagickVersion(), '6.3.7', '>=')) {
1929
							$this->DebugMessage('Using ImageMagick rotate', __FILE__, __LINE__);
1930
							$commandline .= ' -rotate '.phpthumb_functions::escapeshellarg_replacement($this->ra);
1931
							if (($this->ra % 90) != 0) {
1932
								if (preg_match('#('.implode('|', $this->AlphaCapableFormats).')#i', $outputFormat)) {
1933
									// alpha-capable format
1934
									$commandline .= ' -background rgba(255,255,255,0)';
1935
								} else {
1936
									$commandline .= ' -background '.phpthumb_functions::escapeshellarg_replacement('#'.($this->bg ? $this->bg : 'FFFFFF'));
1937
								}
1938
							}
1939
							$this->ra = 0;
1940
						} else {
1941
							$this->DebugMessage('Not using ImageMagick rotate because alpha background buggy before v6.3.7', __FILE__, __LINE__);
1942
						}
1943
					} else {
1944
						$this->DebugMessage('Not using ImageMagick rotate because not supported', __FILE__, __LINE__);
1945
					}
1946
				}
1947
1948
				$successfullyProcessedFilters = array();
1949
				foreach ($this->fltr as $filterkey => $filtercommand) {
1950
					@list($command, $parameter) = explode('|', $filtercommand, 2);
1951
					switch ($command) {
1952
						case 'brit':
1953
							if ($this->ImageMagickSwitchAvailable('modulate')) {
1954
								$commandline .= ' -modulate '.phpthumb_functions::escapeshellarg_replacement((100 + (int) $parameter).',100,100');
1955
								$successfullyProcessedFilters[] = $filterkey;
1956
							}
1957
							break;
1958
1959
						case 'cont':
1960
							if ($this->ImageMagickSwitchAvailable('contrast')) {
1961
								$contDiv10 = round((int) $parameter / 10);
1962
								if ($contDiv10 > 0) {
1963
									$contDiv10 = min($contDiv10, 100);
1964
									for ($i = 0; $i < $contDiv10; $i++) {
1965
										$commandline .= ' -contrast'; // increase contrast by 10%
1966
									}
1967
								} elseif ($contDiv10 < 0) {
1968
									$contDiv10 = max($contDiv10, -100);
1969
									for ($i = $contDiv10; $i < 0; $i++) {
1970
										$commandline .= ' +contrast'; // decrease contrast by 10%
1971
									}
1972
								} else {
1973
									// do nothing
1974
								}
1975
								$successfullyProcessedFilters[] = $filterkey;
1976
							}
1977
							break;
1978
1979
						case 'ds':
1980
							if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) {
1981
								if ($parameter == 100) {
1982
									$commandline .= ' -colorspace GRAY';
1983
									$commandline .= ' -modulate 100,0,100';
1984
								} else {
1985
									$commandline .= ' -modulate '.phpthumb_functions::escapeshellarg_replacement('100,'.(100 - (int) $parameter).',100');
1986
								}
1987
								$successfullyProcessedFilters[] = $filterkey;
1988
							}
1989
							break;
1990
1991
						case 'sat':
1992
							if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) {
1993
								if ($parameter == -100) {
1994
									$commandline .= ' -colorspace GRAY';
1995
									$commandline .= ' -modulate 100,0,100';
1996
								} else {
1997
									$commandline .= ' -modulate '.phpthumb_functions::escapeshellarg_replacement('100,'.(100 + (int) $parameter).',100');
1998
								}
1999
								$successfullyProcessedFilters[] = $filterkey;
2000
							}
2001
							break;
2002
2003
						case 'gray':
2004
							if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) {
2005
								$commandline .= ' -colorspace GRAY';
2006
								$commandline .= ' -modulate 100,0,100';
2007
								$successfullyProcessedFilters[] = $filterkey;
2008
							}
2009
							break;
2010
2011
						case 'clr':
2012
							if ($this->ImageMagickSwitchAvailable(array('fill', 'colorize'))) {
2013
								@list($amount, $color) = explode('|', $parameter);
2014
								$commandline .= ' -fill '.phpthumb_functions::escapeshellarg_replacement('#'.preg_replace('#[^0-9A-F]#i', '', $color));
2015
								$commandline .= ' -colorize '.phpthumb_functions::escapeshellarg_replacement(min(max((int) $amount, 0), 100));
2016
                                $successfullyProcessedFilters[] = $filterkey;
2017
							}
2018
							break;
2019
2020
						case 'sep':
2021
							if ($this->ImageMagickSwitchAvailable('sepia-tone')) {
2022
								@list($amount, $color) = explode('|', $parameter);
2023
								$amount = ($amount ? $amount : 80);
2024
								if (!$color) {
2025
									$commandline .= ' -sepia-tone '.phpthumb_functions::escapeshellarg_replacement(min(max((int) $amount, 0), 100).'%');
2026
									$successfullyProcessedFilters[] = $filterkey;
2027
								}
2028
							}
2029
							break;
2030
2031
						case 'gam':
2032
							@list($amount) = explode('|', $parameter);
2033
							$amount = min(max((float) $amount, 0.001), 10);
2034
							if (number_format($amount, 3) != '1.000') {
2035
								if ($this->ImageMagickSwitchAvailable('gamma')) {
2036
									$commandline .= ' -gamma '.phpthumb_functions::escapeshellarg_replacement($amount);
2037
									$successfullyProcessedFilters[] = $filterkey;
2038
								}
2039
							}
2040
							break;
2041
2042
						case 'neg':
2043
							if ($this->ImageMagickSwitchAvailable('negate')) {
2044
								$commandline .= ' -negate';
2045
								$successfullyProcessedFilters[] = $filterkey;
2046
							}
2047
							break;
2048
2049
						case 'th':
2050
							@list($amount) = explode('|', $parameter);
2051
							if ($this->ImageMagickSwitchAvailable(array('threshold', 'dither', 'monochrome'))) {
2052
								$commandline .= ' -threshold '.phpthumb_functions::escapeshellarg_replacement(round(min(max((int) $amount, 0), 255) / 2.55).'%');
2053
								$commandline .= ' -dither';
2054
								$commandline .= ' -monochrome';
2055
								$successfullyProcessedFilters[] = $filterkey;
2056
							}
2057
							break;
2058
2059
						case 'rcd':
2060
							if ($this->ImageMagickSwitchAvailable(array('colors', 'dither'))) {
2061
								@list($colors, $dither) = explode('|', $parameter);
2062
								$colors = ($colors                ?  (int) $colors : 256);
2063
								$dither  = ((strlen($dither) > 0) ? (bool) $dither : true);
2064
								$commandline .= ' -colors '.phpthumb_functions::escapeshellarg_replacement(max($colors, 8)); // ImageMagick will otherwise fail with "cannot quantize to fewer than 8 colors"
2065
								$commandline .= ($dither ? ' -dither' : ' +dither');
2066
								$successfullyProcessedFilters[] = $filterkey;
2067
							}
2068
							break;
2069
2070
						case 'flip':
2071
							if ($this->ImageMagickSwitchAvailable(array('flip', 'flop'))) {
2072
								if (strpos(strtolower($parameter), 'x') !== false) {
2073
									$commandline .= ' -flop';
2074
								}
2075
								if (strpos(strtolower($parameter), 'y') !== false) {
2076
									$commandline .= ' -flip';
2077
								}
2078
								$successfullyProcessedFilters[] = $filterkey;
2079
							}
2080
							break;
2081
2082
						case 'edge':
2083
							if ($this->ImageMagickSwitchAvailable('edge')) {
2084
								$parameter = (!empty($parameter) ? $parameter : 2);
2085
								$commandline .= ' -edge '.phpthumb_functions::escapeshellarg_replacement(!empty($parameter) ? (int) $parameter : 1);
2086
								$successfullyProcessedFilters[] = $filterkey;
2087
							}
2088
							break;
2089
2090
						case 'emb':
2091
							if ($this->ImageMagickSwitchAvailable(array('emboss', 'negate'))) {
2092
								$parameter = (!empty($parameter) ? $parameter : 2);
2093
								$commandline .= ' -emboss '.phpthumb_functions::escapeshellarg_replacement((int) $parameter);
2094
								if ($parameter < 2) {
2095
									$commandline .= ' -negate'; // ImageMagick negates the image for some reason with '-emboss 1';
2096
								}
2097
								$successfullyProcessedFilters[] = $filterkey;
2098
							}
2099
							break;
2100
2101
						case 'lvl':
2102
							@list($band, $method, $threshold) = explode('|', $parameter);
2103
							$band      = ($band ? preg_replace('#[^RGBA\\*]#', '', strtoupper($band))       : '*');
2104
							$method    = ((strlen($method) > 0)    ? (int) $method :   2);
2105
							$threshold = ((strlen($threshold) > 0) ? min(max((float) $threshold, 0), 100) : 0.1);
2106
2107
							$band = preg_replace('#[^RGBA\\*]#', '', strtoupper($band));
2108
2109
							if (($method > 1) && !$this->ImageMagickSwitchAvailable(array('channel', 'contrast-stretch'))) {
2110
								// Because ImageMagick processing happens before PHP-GD filters, and because some
2111
								// clipping is involved in the "lvl" filter, if "lvl" happens before "wb" then the
2112
								// "wb" filter will have (almost) no effect. Therefore, if "wb" is enabled then
2113
								// force the "lvl" filter to be processed by GD, not ImageMagick.
2114
								foreach ($this->fltr as $fltr_key => $fltr_value) {
2115
									list($fltr_cmd) = explode('|', $fltr_value);
2116
									if ($fltr_cmd == 'wb') {
2117
										$this->DebugMessage('Setting "lvl" filter method to "0" (from "'.$method.'") because white-balance filter also enabled', __FILE__, __LINE__);
2118
										$method = 0;
2119
									}
2120
								}
2121
							}
2122
2123
							switch ($method) {
2124
								case 0: // internal RGB
2125
								case 1: // internal grayscale
2126
									break;
2127
								case 2: // ImageMagick "contrast-stretch"
2128
									if ($this->ImageMagickSwitchAvailable('contrast-stretch')) {
2129
										if ($band != '*') {
2130
											$commandline .= ' -channel '.phpthumb_functions::escapeshellarg_replacement(strtoupper($band));
2131
										}
2132
										$threshold = preg_replace('#[^0-9\\.]#', '', $threshold); // should be unneccesary, but just to be double-sure
2133
										//$commandline .= ' -contrast-stretch '.phpthumb_functions::escapeshellarg_replacement($threshold.'%');
2134
										$commandline .= ' -contrast-stretch \''.$threshold.'%\'';
2135
										if ($band != '*') {
2136
											$commandline .= ' +channel';
2137
										}
2138
										$successfullyProcessedFilters[] = $filterkey;
2139
									}
2140
									break;
2141
								case 3: // ImageMagick "normalize"
2142
									if ($this->ImageMagickSwitchAvailable('normalize')) {
2143
										if ($band != '*') {
2144
											$commandline .= ' -channel '.phpthumb_functions::escapeshellarg_replacement(strtoupper($band));
2145
										}
2146
										$commandline .= ' -normalize';
2147
										if ($band != '*') {
2148
											$commandline .= ' +channel';
2149
										}
2150
										$successfullyProcessedFilters[] = $filterkey;
2151
									}
2152
									break;
2153
								default:
2154
									$this->DebugMessage('unsupported method ('.$method.') for "lvl" filter', __FILE__, __LINE__);
2155
									break;
2156
							}
2157
							if (isset($this->fltr[$filterkey]) && ($method > 1)) {
2158
								$this->fltr[$filterkey] = $command.'|'.$band.'|0|'.$threshold;
2159
								$this->DebugMessage('filter "lvl" remapped from method "'.$method.'" to method "0" because ImageMagick support is missing', __FILE__, __LINE__);
2160
							}
2161
							break;
2162
2163
						case 'wb':
2164
							if ($this->ImageMagickSwitchAvailable(array('channel', 'contrast-stretch'))) {
2165
								@list($threshold) = explode('|', $parameter);
2166
								$threshold = (!empty($threshold) ? min(max((float) $threshold, 0), 100) : 0.1);
2167
								$threshold = preg_replace('#[^0-9\\.]#', '', $threshold); // should be unneccesary, but just to be double-sure
2168
								//$commandline .= ' -channel R -contrast-stretch '.phpthumb_functions::escapeshellarg_replacement($threshold.'%'); // doesn't work on Windows because most versions of PHP do not properly
2169
								//$commandline .= ' -channel G -contrast-stretch '.phpthumb_functions::escapeshellarg_replacement($threshold.'%'); // escape special characters (such as %) and just replace them with spaces
2170
								//$commandline .= ' -channel B -contrast-stretch '.phpthumb_functions::escapeshellarg_replacement($threshold.'%'); // https://bugs.php.net/bug.php?id=43261
2171
								$commandline .= ' -channel R -contrast-stretch \''.$threshold.'%\'';
2172
								$commandline .= ' -channel G -contrast-stretch \''.$threshold.'%\'';
2173
								$commandline .= ' -channel B -contrast-stretch \''.$threshold.'%\'';
2174
								$commandline .= ' +channel';
2175
								$successfullyProcessedFilters[] = $filterkey;
2176
							}
2177
							break;
2178
2179
						case 'blur':
2180
							if ($this->ImageMagickSwitchAvailable('blur')) {
2181
								@list($radius) = explode('|', $parameter);
2182
								$radius = (!empty($radius) ? min(max((int) $radius, 0), 25) : 1);
2183
								$commandline .= ' -blur '.phpthumb_functions::escapeshellarg_replacement($radius);
2184
								$successfullyProcessedFilters[] = $filterkey;
2185
							}
2186
							break;
2187
2188
						case 'gblr':
2189
							@list($radius) = explode('|', $parameter);
2190
							$radius = (!empty($radius) ? min(max((int) $radius, 0), 25) : 1);
2191
							// "-gaussian" changed to "-gaussian-blur" sometime around 2009
2192
							if ($this->ImageMagickSwitchAvailable('gaussian-blur')) {
2193
								$commandline .= ' -gaussian-blur '.phpthumb_functions::escapeshellarg_replacement($radius);
2194
								$successfullyProcessedFilters[] = $filterkey;
2195
							} elseif ($this->ImageMagickSwitchAvailable('gaussian')) {
2196
								$commandline .= ' -gaussian '.phpthumb_functions::escapeshellarg_replacement($radius);
2197
								$successfullyProcessedFilters[] = $filterkey;
2198
							}
2199
							break;
2200
2201
						case 'usm':
2202
							if ($this->ImageMagickSwitchAvailable('unsharp')) {
2203
								@list($amount, $radius, $threshold) = explode('|', $parameter);
2204
								$amount    = ($amount            ? min(max((int) $amount,    0), 255) : 80);
2205
								$radius    = ($radius            ? min(max((int) $radius,    0),  10) : 0.5);
2206
								$threshold = ('' !== $threshold ? min(max((int) $threshold, 0),  50) : 3);
2207
								$commandline .= ' -unsharp '.phpthumb_functions::escapeshellarg_replacement(number_format(($radius * 2) - 1, 2, '.', '').'x1+'.number_format($amount / 100, 2, '.', '').'+'.number_format($threshold / 100, 2, '.', ''));
2208
								$successfullyProcessedFilters[] = $filterkey;
2209
							}
2210
							break;
2211
2212
						case 'bord':
2213
							if ($this->ImageMagickSwitchAvailable(array('border', 'bordercolor', 'thumbnail', 'crop'))) {
2214
								if (!$this->zc) {
2215
									@list($width, $rX, $rY, $color) = explode('|', $parameter);
2216
									$width = (int) $width;
2217
									$rX    = (int) $rX;
2218
									$rY    = (int) $rY;
2219
									if ($width && !$rX && !$rY) {
2220
										if (!phpthumb_functions::IsHexColor($color)) {
2221
											$color = ((!empty($this->bc) && phpthumb_functions::IsHexColor($this->bc)) ? $this->bc : '000000');
2222
										}
2223
										$commandline .= ' -border '.phpthumb_functions::escapeshellarg_replacement((int) $width);
2224
										$commandline .= ' -bordercolor '.phpthumb_functions::escapeshellarg_replacement('#'.$color);
2225
2226
										if (preg_match('# \\-crop "([\d]+)x([\d]+)\\+0\\+0" #', $commandline, $matches)) {
2227
											$commandline = str_replace(' -crop "'.$matches[1].'x'.$matches[2].'+0+0" ', ' -crop '.phpthumb_functions::escapeshellarg_replacement(($matches[1] - (2 * $width)).'x'.($matches[2] - (2 * $width)).'+0+0').' ', $commandline);
2228
										} elseif (preg_match('# \\-'.$IMresizeParameter.' "([0-9]+)x([0-9]+)" #', $commandline, $matches)) {
2229
											$commandline = str_replace(' -'.$IMresizeParameter.' "'.$matches[1].'x'.$matches[2].'" ', ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement(($matches[1] - (2 * $width)).'x'.($matches[2] - (2 * $width))).' ', $commandline);
2230
										}
2231
										$successfullyProcessedFilters[] = $filterkey;
2232
									}
2233
								}
2234
							}
2235
							break;
2236
2237
						case 'crop':
2238
							break;
2239
2240
						case 'sblr':
2241
							break;
2242
2243
						case 'mean':
2244
							break;
2245
2246
						case 'smth':
2247
							break;
2248
2249
						case 'bvl':
2250
							break;
2251
2252
						case 'wmi':
2253
							break;
2254
2255
						case 'wmt':
2256
							break;
2257
2258
						case 'over':
2259
							break;
2260
2261
						case 'hist':
2262
							break;
2263
2264
						case 'fram':
2265
							break;
2266
2267
						case 'drop':
2268
							break;
2269
2270
						case 'mask':
2271
							break;
2272
2273
						case 'elip':
2274
							break;
2275
2276
						case 'ric':
2277
							break;
2278
2279
						case 'stc':
2280
							break;
2281
2282
						case 'size':
2283
							break;
2284
2285
						default:
2286
							$this->DebugMessage('Unknown $this->fltr['.$filterkey.'] ('.$filtercommand.') -- deleting filter command', __FILE__, __LINE__);
2287
							$successfullyProcessedFilters[] = $filterkey;
2288
							break;
2289
					}
2290
					if (!isset($this->fltr[$filterkey])) {
2291
						$this->DebugMessage('Processed $this->fltr['.$filterkey.'] ('.$filtercommand.') with ImageMagick', __FILE__, __LINE__);
2292
					} else {
2293
						$this->DebugMessage('Skipping $this->fltr['.$filterkey.'] ('.$filtercommand.') with ImageMagick', __FILE__, __LINE__);
2294
					}
2295
				}
2296
				$this->DebugMessage('Remaining $this->fltr after ImageMagick: ('.$this->phpThumbDebugVarDump($this->fltr).')', __FILE__, __LINE__);
2297
				if (count($this->fltr) > 0) {
2298
					$this->useRawIMoutput = false;
2299
				}
2300
2301
				if (preg_match('#jpe?g#i', $outputFormat) && $this->q) {
2302
					if ($this->ImageMagickSwitchAvailable(array('quality', 'interlace'))) {
2303
						$commandline .= ' -quality '.phpthumb_functions::escapeshellarg_replacement($this->thumbnailQuality);
2304
						if ($this->config_output_interlace) {
2305
							// causes weird things with animated GIF... leave for JPEG only
2306
							$commandline .= ' -interlace line '; // Use Line or Plane to create an interlaced PNG or GIF or progressive JPEG image
2307
						}
2308
					}
2309
				}
2310
				$commandline .= ' '.$outputFormat.':'.phpthumb_functions::escapeshellarg_replacement($IMtempfilename);
2311
				if (!$this->iswindows) {
2312
					$commandline .= ' 2>&1';
2313
				}
2314
				$this->DebugMessage('ImageMagick called as ('.$commandline.')', __FILE__, __LINE__);
2315
				$IMresult = phpthumb_functions::SafeExec($commandline);
2316
				clearstatcache();
2317
				if (!@file_exists($IMtempfilename) || !@filesize($IMtempfilename)) {
2318
					$this->FatalError('ImageMagick failed with message ('.trim($IMresult).')');
0 ignored issues
show
Bug introduced by
It seems like $IMresult can also be of type false; however, parameter $str of trim() does only seem to accept string, 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

2318
					$this->FatalError('ImageMagick failed with message ('.trim(/** @scrutinizer ignore-type */ $IMresult).')');
Loading history...
2319
					$this->DebugMessage('ImageMagick failed with message ('.trim($IMresult).')', __FILE__, __LINE__);
2320
					if ($this->iswindows && !$IMresult) {
2321
						$this->DebugMessage('Check to make sure that PHP has read+write permissions to "'.dirname($IMtempfilename).'"', __FILE__, __LINE__);
2322
					}
2323
2324
				} else {
2325
2326
					foreach ($successfullyProcessedFilters as $dummy => $filterkey) {
2327
						unset($this->fltr[$filterkey]);
2328
					}
2329
					$this->IMresizedData = file_get_contents($IMtempfilename);
2330
					$getimagesize_imresized = @getimagesize($IMtempfilename);
2331
					$this->DebugMessage('getimagesize('.$IMtempfilename.') returned [w='.$getimagesize_imresized[0].';h='.$getimagesize_imresized[1].';f='.$getimagesize_imresized[2].']', __FILE__, __LINE__);
2332
					if (($this->config_max_source_pixels > 0) && (($getimagesize_imresized[0] * $getimagesize_imresized[1]) > $this->config_max_source_pixels)) {
2333
						$this->DebugMessage('skipping ImageMagickThumbnailToGD::'.$ImageCreateFunction.'() because IM output is too large ('.$getimagesize_imresized[0].'x'.$getimagesize_imresized[0].' = '.($getimagesize_imresized[0] * $getimagesize_imresized[1]).' > '.$this->config_max_source_pixels.')', __FILE__, __LINE__);
2334
					} elseif (function_exists(@$ImageCreateFunction) && ($this->gdimg_source = @$ImageCreateFunction($IMtempfilename))) {
2335
						$this->source_width  = imagesx($this->gdimg_source);
2336
						$this->source_height = imagesy($this->gdimg_source);
2337
						$this->DebugMessage('ImageMagickThumbnailToGD::'.$ImageCreateFunction.'() succeeded, $this->gdimg_source is now ('.$this->source_width.'x'.$this->source_height.')', __FILE__, __LINE__);
2338
						$this->DebugMessage('ImageMagickThumbnailToGD() returning $this->IMresizedData ('.strlen($this->IMresizedData).' bytes)', __FILE__, __LINE__);
2339
					} else {
2340
						$this->useRawIMoutput = true;
2341
						$this->DebugMessage('$this->useRawIMoutput set to TRUE because '.@$ImageCreateFunction.'('.$IMtempfilename.') failed', __FILE__, __LINE__);
2342
					}
2343
					if (file_exists($IMtempfilename)) {
2344
						$this->DebugMessage('deleting "'.$IMtempfilename.'"', __FILE__, __LINE__);
2345
						@unlink($IMtempfilename);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

2345
						/** @scrutinizer ignore-unhandled */ @unlink($IMtempfilename);

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...
2346
					}
2347
					return true;
2348
2349
				}
2350
				if (file_exists($IMtempfilename)) {
2351
					$this->DebugMessage('deleting "'.$IMtempfilename.'"', __FILE__, __LINE__);
2352
					@unlink($IMtempfilename);
2353
				}
2354
2355
			} elseif ($this->issafemode) {
2356
				$this->DebugMessage('ImageMagickThumbnailToGD() aborting because PHP safe_mode is enabled and phpThumb_tempnam() failed', __FILE__, __LINE__);
2357
				$this->useRawIMoutput = false;
2358
			} else {
2359
				if (file_exists($IMtempfilename)) {
2360
					$this->DebugMessage('deleting "'.$IMtempfilename.'"', __FILE__, __LINE__);
2361
					@unlink($IMtempfilename);
2362
				}
2363
				$this->DebugMessage('ImageMagickThumbnailToGD() aborting, phpThumb_tempnam() failed', __FILE__, __LINE__);
2364
			}
2365
		} else {
2366
			$this->DebugMessage('ImageMagickThumbnailToGD() aborting because ImageMagickCommandlineBase() failed', __FILE__, __LINE__);
2367
		}
2368
		$this->useRawIMoutput = false;
2369
		return false;
2370
	}
2371
2372
2373
	public function Rotate() {
2374
		if ($this->ra || $this->ar) {
2375
			if (!function_exists('imagerotate')) {
2376
				$this->DebugMessage('!function_exists(imagerotate)', __FILE__, __LINE__);
2377
				return false;
2378
			}
2379
			if (!include_once __DIR__ .'/phpthumb.filters.php' ) {
2380
				$this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.filters.php" which is required for applying filters ('.implode(';', $this->fltr).')', __FILE__, __LINE__);
2381
				return false;
2382
			}
2383
2384
			$this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor);
2385
			if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) {
2386
				return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"');
2387
			}
2388
2389
			$rotate_angle = 0;
2390
			if ($this->ra) {
2391
2392
				$rotate_angle = (float) $this->ra;
2393
2394
			} else {
2395
2396
				if ($this->ar == 'x') {
2397
					if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '4.2.0', '>=')) {
2398
						if ($this->sourceFilename) {
2399
							if (function_exists('exif_read_data')) {
2400
								if ($exif_data = @exif_read_data($this->sourceFilename, 'IFD0')) {
2401
									// http://sylvana.net/jpegcrop/exif_orientation.html
2402
									switch (@$exif_data['Orientation']) {
2403
										case 1:
2404
											$rotate_angle = 0;
2405
											break;
2406
										case 3:
2407
											$rotate_angle = 180;
2408
											break;
2409
										case 6:
2410
											$rotate_angle = 270;
2411
											break;
2412
										case 8:
2413
											$rotate_angle = 90;
2414
											break;
2415
2416
										default:
2417
											$this->DebugMessage('EXIF auto-rotate failed because unknown $exif_data[Orientation] "'.@$exif_data['Orientation'].'"', __FILE__, __LINE__);
2418
											return false;
2419
											break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2420
									}
2421
									$this->DebugMessage('EXIF auto-rotate set to '.$rotate_angle.' degrees ($exif_data[Orientation] = "'.@$exif_data['Orientation'].'")', __FILE__, __LINE__);
2422
								} else {
2423
									$this->DebugMessage('failed: exif_read_data('.$this->sourceFilename.')', __FILE__, __LINE__);
2424
									return false;
2425
								}
2426
							} else {
2427
								$this->DebugMessage('!function_exists(exif_read_data)', __FILE__, __LINE__);
2428
								return false;
2429
							}
2430
						} else {
2431
							$this->DebugMessage('Cannot auto-rotate from EXIF data because $this->sourceFilename is empty', __FILE__, __LINE__);
2432
							return false;
2433
						}
2434
					} else {
2435
						$this->DebugMessage('Cannot auto-rotate from EXIF data because PHP is less than v4.2.0 ('. PHP_VERSION .')', __FILE__, __LINE__);
2436
						return false;
2437
					}
2438
				} elseif (($this->ar == 'l') && ($this->source_height > $this->source_width)) {
2439
					$rotate_angle = 270;
2440
				} elseif (($this->ar == 'L') && ($this->source_height > $this->source_width)) {
2441
					$rotate_angle = 90;
2442
				} elseif (($this->ar == 'p') && ($this->source_width > $this->source_height)) {
2443
					$rotate_angle = 90;
2444
				} elseif (($this->ar == 'P') && ($this->source_width > $this->source_height)) {
2445
					$rotate_angle = 270;
2446
				}
2447
2448
			}
2449
			if ($rotate_angle % 90) {
2450
				$this->is_alpha = true;
2451
			}
2452
			phpthumb_filters::ImprovedImageRotate($this->gdimg_source, $rotate_angle, $this->config_background_hexcolor, $this->bg, $this);
2453
			$this->source_width  = imagesx($this->gdimg_source);
2454
			$this->source_height = imagesy($this->gdimg_source);
2455
		}
2456
		return true;
2457
	}
2458
2459
2460
	public function FixedAspectRatio() {
2461
		// optional fixed-dimension images (regardless of aspect ratio)
2462
2463
		if (!$this->far) {
2464
			// do nothing
2465
			return true;
2466
		}
2467
2468
		if (!$this->w || !$this->h) {
2469
			return false;
2470
		}
2471
		$this->thumbnail_width  = $this->w;
2472
		$this->thumbnail_height = $this->h;
2473
		$this->is_alpha = true;
2474
		if ($this->thumbnail_image_width >= $this->thumbnail_width) {
2475
2476
			$aspectratio = $this->thumbnail_image_height / $this->thumbnail_image_width;
2477
			if ($this->w) {
2478
				$this->thumbnail_image_height = round($this->thumbnail_image_width * $aspectratio);
2479
				$this->thumbnail_height = ($this->h ? $this->h : $this->thumbnail_image_height);
2480
			} elseif ($this->thumbnail_image_height < $this->thumbnail_height) {
2481
				$this->thumbnail_image_height = $this->thumbnail_height;
2482
				$this->thumbnail_image_width  = round($this->thumbnail_image_height / $aspectratio);
2483
			}
2484
2485
		} else {
2486
2487
			$aspectratio = $this->thumbnail_image_width / $this->thumbnail_image_height;
2488
			if ($this->h) {
2489
				$this->thumbnail_image_width = round($this->thumbnail_image_height * $aspectratio);
2490
			} elseif ($this->thumbnail_image_width < $this->thumbnail_width) {
2491
				$this->thumbnail_image_width = $this->thumbnail_width;
2492
				$this->thumbnail_image_height  = round($this->thumbnail_image_width / $aspectratio);
2493
			}
2494
2495
		}
2496
		return true;
2497
	}
2498
2499
2500
	public function OffsiteDomainIsAllowed($hostname, $allowed_domains) {
2501
		static $domain_is_allowed = array();
2502
		$hostname = strtolower($hostname);
2503
		if (!isset($domain_is_allowed[$hostname])) {
2504
			$domain_is_allowed[$hostname] = false;
2505
			foreach ($allowed_domains as $valid_domain) {
2506
				$starpos = strpos($valid_domain, '*');
2507
				if ($starpos !== false) {
2508
					$valid_domain = substr($valid_domain, $starpos + 1);
2509
					if (preg_match('#'.preg_quote($valid_domain).'$#', $hostname)) {
2510
						$domain_is_allowed[$hostname] = true;
2511
						break;
2512
					}
2513
				} else {
2514
					if (strtolower($valid_domain) === $hostname) {
2515
						$domain_is_allowed[$hostname] = true;
2516
						break;
2517
					}
2518
				}
2519
			}
2520
		}
2521
		return $domain_is_allowed[$hostname];
2522
	}
2523
2524
2525
	public function AntiOffsiteLinking() {
2526
		// Optional anti-offsite hijacking of the thumbnail script
2527
		$allow   = true;
2528
		if ($allow && $this->config_nooffsitelink_enabled && (@$_SERVER['HTTP_REFERER'] || $this->config_nooffsitelink_require_refer)) {
2529
			$this->DebugMessage('AntiOffsiteLinking() checking $_SERVER[HTTP_REFERER] "'.@$_SERVER['HTTP_REFERER'].'"', __FILE__, __LINE__);
2530
			foreach ($this->config_nooffsitelink_valid_domains as $key => $valid_domain) {
2531
				// $_SERVER['HTTP_HOST'] contains the port number, so strip it out here to make default configuration work
2532
				list($clean_domain) = explode(':', $valid_domain);
2533
				$this->config_nooffsitelink_valid_domains[$key] = $clean_domain;
2534
			}
2535
			$parsed_url = phpthumb_functions::ParseURLbetter(@$_SERVER['HTTP_REFERER']);
2536
			if (!$this->OffsiteDomainIsAllowed(@$parsed_url['host'], $this->config_nooffsitelink_valid_domains)) {
2537
				$allow   = false;
2538
				//$this->DebugMessage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is NOT in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')', __FILE__, __LINE__);
2539
				$this->ErrorImage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is NOT in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')');
2540
			} else {
2541
				$this->DebugMessage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')', __FILE__, __LINE__);
2542
			}
2543
		}
2544
2545
		if ($allow && $this->config_nohotlink_enabled && preg_match('#^(f|ht)tps?\://#i', $this->src)) {
2546
			$parsed_url = phpthumb_functions::ParseURLbetter($this->src);
2547
			//if (!phpthumb_functions::CaseInsensitiveInArray(@$parsed_url['host'], $this->config_nohotlink_valid_domains)) {
2548
			if (!$this->OffsiteDomainIsAllowed(@$parsed_url['host'], $this->config_nohotlink_valid_domains)) {
2549
				// This domain is not allowed
2550
				$allow = false;
2551
				$this->DebugMessage('AntiOffsiteLinking() - "'.$parsed_url['host'].'" is NOT in $this->config_nohotlink_valid_domains ('.implode(';', $this->config_nohotlink_valid_domains).')', __FILE__, __LINE__);
2552
			} else {
2553
				$this->DebugMessage('AntiOffsiteLinking() - "'.$parsed_url['host'].'" is in $this->config_nohotlink_valid_domains ('.implode(';', $this->config_nohotlink_valid_domains).')', __FILE__, __LINE__);
2554
			}
2555
		}
2556
2557
		if ($allow) {
2558
			$this->DebugMessage('AntiOffsiteLinking() says this is allowed', __FILE__, __LINE__);
2559
			return true;
2560
		}
2561
2562
		if (!phpthumb_functions::IsHexColor($this->config_error_bgcolor)) {
2563
			return $this->ErrorImage('Invalid hex color string "'.$this->config_error_bgcolor.'" for $this->config_error_bgcolor');
2564
		}
2565
		if (!phpthumb_functions::IsHexColor($this->config_error_textcolor)) {
2566
			return $this->ErrorImage('Invalid hex color string "'.$this->config_error_textcolor.'" for $this->config_error_textcolor');
2567
		}
2568
		if ($this->config_nooffsitelink_erase_image) {
2569
2570
			return $this->ErrorImage($this->config_nooffsitelink_text_message, $this->thumbnail_width, $this->thumbnail_height);
2571
2572
		} else {
2573
2574
			$this->config_nooffsitelink_watermark_src = $this->ResolveFilenameToAbsolute($this->config_nooffsitelink_watermark_src);
2575
			if (is_file($this->config_nooffsitelink_watermark_src)) {
0 ignored issues
show
Bug introduced by
It seems like $this->config_nooffsitelink_watermark_src can also be of type false; however, parameter $filename of is_file() does only seem to accept string, 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

2575
			if (is_file(/** @scrutinizer ignore-type */ $this->config_nooffsitelink_watermark_src)) {
Loading history...
2576
2577
				if (!include_once __DIR__ .'/phpthumb.filters.php' ) {
2578
					$this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.filters.php" which is required for applying watermark', __FILE__, __LINE__);
2579
					return false;
2580
				}
2581
				$watermark_img = $this->ImageCreateFromStringReplacement(file_get_contents($this->config_nooffsitelink_watermark_src));
0 ignored issues
show
Bug introduced by
It seems like $this->config_nooffsitelink_watermark_src can also be of type false; however, parameter $filename of file_get_contents() does only seem to accept string, 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

2581
				$watermark_img = $this->ImageCreateFromStringReplacement(file_get_contents(/** @scrutinizer ignore-type */ $this->config_nooffsitelink_watermark_src));
Loading history...
Bug introduced by
file_get_contents($this-...sitelink_watermark_src) cannot be passed to phpthumb::ImageCreateFromStringReplacement() as the parameter $RawImageData expects a reference. ( Ignorable by Annotation )

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

2581
				$watermark_img = $this->ImageCreateFromStringReplacement(/** @scrutinizer ignore-type */ file_get_contents($this->config_nooffsitelink_watermark_src));
Loading history...
2582
				$phpthumbFilters = new phpthumb_filters();
2583
				$phpthumbFilters->phpThumbObject = &$this;
2584
				$opacity = 50;
2585
				$margin  = 5;
2586
				$phpthumbFilters->WatermarkOverlay($this->gdimg_output, $watermark_img, '*', $opacity, $margin);
2587
				imagedestroy($watermark_img);
2588
				unset($phpthumbFilters);
2589
2590
			} else {
2591
2592
				$nohotlink_text_array = explode("\n", wordwrap($this->config_nooffsitelink_text_message, floor($this->thumbnail_width / imagefontwidth($this->config_error_fontsize)), "\n"));
0 ignored issues
show
Bug introduced by
floor($this->thumbnail_w...config_error_fontsize)) of type double is incompatible with the type integer expected by parameter $width of wordwrap(). ( Ignorable by Annotation )

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

2592
				$nohotlink_text_array = explode("\n", wordwrap($this->config_nooffsitelink_text_message, /** @scrutinizer ignore-type */ floor($this->thumbnail_width / imagefontwidth($this->config_error_fontsize)), "\n"));
Loading history...
2593
				$nohotlink_text_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_error_textcolor);
2594
2595
				$topoffset = round(($this->thumbnail_height - (count($nohotlink_text_array) * imagefontheight($this->config_error_fontsize))) / 2);
2596
2597
				$rowcounter = 0;
2598
				$this->DebugMessage('AntiOffsiteLinking() writing '.count($nohotlink_text_array).' lines of text "'.$this->config_nooffsitelink_text_message.'" (in #'.$this->config_error_textcolor.') on top of image', __FILE__, __LINE__);
2599
				foreach ($nohotlink_text_array as $textline) {
2600
					$leftoffset = max(0, round(($this->thumbnail_width - (strlen($textline) * imagefontwidth($this->config_error_fontsize))) / 2));
2601
					imagestring($this->gdimg_output, $this->config_error_fontsize, $leftoffset, $topoffset + ($rowcounter++ * imagefontheight($this->config_error_fontsize)), $textline, $nohotlink_text_color);
2602
				}
2603
2604
			}
2605
2606
		}
2607
		return true;
2608
	}
2609
2610
2611
	public function AlphaChannelFlatten() {
2612
		if (!$this->is_alpha) {
2613
			// image doesn't have alpha transparency, no need to flatten
2614
			$this->DebugMessage('skipping AlphaChannelFlatten() because !$this->is_alpha', __FILE__, __LINE__);
2615
			return false;
2616
		}
2617
		switch ($this->thumbnailFormat) {
2618
			case 'png':
2619
			case 'webp':
2620
			case 'ico':
2621
				// image has alpha transparency, but output as PNG, WEBP or ICO which can handle it
2622
				$this->DebugMessage('skipping AlphaChannelFlatten() because ($this->thumbnailFormat == "'.$this->thumbnailFormat.'")', __FILE__, __LINE__);
2623
				return false;
2624
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2625
2626
			case 'gif':
2627
				// image has alpha transparency, but output as GIF which can handle only single-color transparency
2628
				$CurrentImageColorTransparent = imagecolortransparent($this->gdimg_output);
2629
				if ($CurrentImageColorTransparent == -1) {
2630
					// no transparent color defined
2631
2632
					if (phpthumb_functions::gd_version() < 2.0) {
2633
						$this->DebugMessage('AlphaChannelFlatten() failed because GD version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
2634
						return false;
2635
					}
2636
2637
					if ($img_alpha_mixdown_dither = @imagecreatetruecolor(imagesx($this->gdimg_output), imagesy($this->gdimg_output))) {
2638
2639
						$dither_color = array();
2640
						for ($i = 0; $i <= 255; $i++) {
2641
							$dither_color[$i] = imagecolorallocate($img_alpha_mixdown_dither, $i, $i, $i);
2642
						}
2643
2644
						// scan through current truecolor image copy alpha channel to temp image as grayscale
2645
						for ($x = 0; $x < $this->thumbnail_width; $x++) {
2646
							for ($y = 0; $y < $this->thumbnail_height; $y++) {
2647
								$PixelColor = phpthumb_functions::GetPixelColor($this->gdimg_output, $x, $y);
2648
								imagesetpixel($img_alpha_mixdown_dither, $x, $y, $dither_color[ $PixelColor[ 'alpha'] * 2 ]);
2649
							}
2650
						}
2651
2652
						// dither alpha channel grayscale version down to 2 colors
2653
						imagetruecolortopalette($img_alpha_mixdown_dither, true, 2);
2654
2655
						// reduce color palette to 256-1 colors (leave one palette position for transparent color)
2656
						imagetruecolortopalette($this->gdimg_output, true, 255);
2657
2658
						// allocate a new color for transparent color index
2659
						$TransparentColor = imagecolorallocate($this->gdimg_output, 1, 254, 253);
2660
						imagecolortransparent($this->gdimg_output, $TransparentColor);
2661
2662
						// scan through alpha channel image and note pixels with >50% transparency
2663
						for ($x = 0; $x < $this->thumbnail_width; $x++) {
2664
							for ($y = 0; $y < $this->thumbnail_height; $y++) {
2665
								$AlphaChannelPixel = phpthumb_functions::GetPixelColor($img_alpha_mixdown_dither, $x, $y);
2666
								if ($AlphaChannelPixel['red'] > 127) {
2667
									imagesetpixel($this->gdimg_output, $x, $y, $TransparentColor);
2668
								}
2669
							}
2670
						}
2671
						imagedestroy($img_alpha_mixdown_dither);
2672
2673
						$this->DebugMessage('AlphaChannelFlatten() set image to 255+1 colors with transparency for GIF output', __FILE__, __LINE__);
2674
						return true;
2675
2676
					} else {
2677
						$this->DebugMessage('AlphaChannelFlatten() failed imagecreate('.imagesx($this->gdimg_output).', '.imagesy($this->gdimg_output).')', __FILE__, __LINE__);
2678
						return false;
2679
					}
2680
2681
				} else {
2682
					// a single transparent color already defined, leave as-is
2683
					$this->DebugMessage('skipping AlphaChannelFlatten() because ($this->thumbnailFormat == "'.$this->thumbnailFormat.'") and imagecolortransparent() returned "'.$CurrentImageColorTransparent.'"', __FILE__, __LINE__);
2684
					return true;
2685
				}
2686
				break;
2687
		}
2688
		$this->DebugMessage('continuing AlphaChannelFlatten() for output format "'.$this->thumbnailFormat.'"', __FILE__, __LINE__);
2689
		// image has alpha transparency, and is being output in a format that doesn't support it -- flatten
2690
		if ($gdimg_flatten_temp = phpthumb_functions::ImageCreateFunction($this->thumbnail_width, $this->thumbnail_height)) {
2691
2692
			$this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor);
2693
			if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) {
2694
				return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"');
2695
			}
2696
			$background_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_background_hexcolor);
2697
			imagefilledrectangle($gdimg_flatten_temp, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $background_color);
2698
			imagecopy($gdimg_flatten_temp, $this->gdimg_output, 0, 0, 0, 0, $this->thumbnail_width, $this->thumbnail_height);
2699
2700
			imagealphablending($this->gdimg_output, true);
2701
			imagesavealpha($this->gdimg_output, false);
2702
			imagecolortransparent($this->gdimg_output, -1);
2703
			imagecopy($this->gdimg_output, $gdimg_flatten_temp, 0, 0, 0, 0, $this->thumbnail_width, $this->thumbnail_height);
2704
2705
			imagedestroy($gdimg_flatten_temp);
2706
			return true;
2707
2708
		} else {
2709
			$this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__);
2710
		}
2711
		return false;
2712
	}
2713
2714
2715
	public function ApplyFilters() {
2716
		if ($this->fltr && is_array($this->fltr)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->fltr of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2717
			if (!include_once __DIR__ .'/phpthumb.filters.php' ) {
2718
				$this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.filters.php" which is required for applying filters ('.implode(';', $this->fltr).')', __FILE__, __LINE__);
2719
				return false;
2720
			}
2721
			$phpthumbFilters = new phpthumb_filters();
2722
			$phpthumbFilters->phpThumbObject = &$this;
2723
			foreach ($this->fltr as $filtercommand) {
2724
				@list($command, $parameter) = explode('|', $filtercommand, 2);
2725
				$this->DebugMessage('Attempting to process filter command "'.$command.'('.$parameter.')"', __FILE__, __LINE__);
2726
				switch ($command) {
2727
					case 'brit': // Brightness
2728
						$phpthumbFilters->Brightness($this->gdimg_output, $parameter);
2729
						break;
2730
2731
					case 'cont': // Contrast
2732
						$phpthumbFilters->Contrast($this->gdimg_output, $parameter);
2733
						break;
2734
2735
					case 'ds': // Desaturation
2736
						$phpthumbFilters->Desaturate($this->gdimg_output, $parameter, '');
2737
						break;
2738
2739
					case 'sat': // Saturation
2740
						$phpthumbFilters->Saturation($this->gdimg_output, $parameter, '');
2741
						break;
2742
2743
					case 'gray': // Grayscale
2744
						$phpthumbFilters->Grayscale($this->gdimg_output);
2745
						break;
2746
2747
					case 'clr': // Colorize
2748
						if (phpthumb_functions::gd_version() < 2) {
2749
							$this->DebugMessage('Skipping Colorize() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
2750
							break;
2751
						}
2752
						@list($amount, $color) = explode('|', $parameter, 2);
2753
						$phpthumbFilters->Colorize($this->gdimg_output, $amount, $color);
2754
						break;
2755
2756
					case 'sep': // Sepia
2757
						if (phpthumb_functions::gd_version() < 2) {
2758
							$this->DebugMessage('Skipping Sepia() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
2759
							break;
2760
						}
2761
						@list($amount, $color) = explode('|', $parameter, 2);
2762
						$phpthumbFilters->Sepia($this->gdimg_output, $amount, $color);
2763
						break;
2764
2765
					case 'gam': // Gamma correction
2766
						$phpthumbFilters->Gamma($this->gdimg_output, $parameter);
2767
						break;
2768
2769
					case 'neg': // Negative colors
2770
						$phpthumbFilters->Negative($this->gdimg_output);
2771
						break;
2772
2773
					case 'th': // Threshold
2774
						$phpthumbFilters->Threshold($this->gdimg_output, $parameter);
2775
						break;
2776
2777
					case 'rcd': // ReduceColorDepth
2778
						if (phpthumb_functions::gd_version() < 2) {
2779
							$this->DebugMessage('Skipping ReduceColorDepth() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
2780
							break;
2781
						}
2782
						@list($colors, $dither) = explode('|', $parameter, 2);
2783
						$colors = ($colors                ?  (int) $colors : 256);
2784
						$dither  = ((strlen($dither) > 0) ? (bool) $dither : true);
2785
						$phpthumbFilters->ReduceColorDepth($this->gdimg_output, $colors, $dither);
2786
						break;
2787
2788
					case 'flip': // Flip
2789
						$phpthumbFilters->Flip($this->gdimg_output, strpos(strtolower($parameter), 'x') !== false, strpos(strtolower($parameter), 'y') !== false);
2790
						break;
2791
2792
					case 'edge': // EdgeDetect
2793
						$phpthumbFilters->EdgeDetect($this->gdimg_output);
2794
						break;
2795
2796
					case 'emb': // Emboss
2797
						$phpthumbFilters->Emboss($this->gdimg_output);
2798
						break;
2799
2800
					case 'bvl': // Bevel
2801
						@list($width, $color1, $color2) = explode('|', $parameter, 3);
2802
						$phpthumbFilters->Bevel($this->gdimg_output, $width, $color1, $color2);
2803
						break;
2804
2805
					case 'lvl': // autoLevels
2806
						@list($band, $method, $threshold) = explode('|', $parameter, 3);
2807
						$band      = ($band ? preg_replace('#[^RGBA\\*]#', '', strtoupper($band)) : '*');
2808
						$method    = ((strlen($method) > 0)    ? (int) $method :   2);
2809
						$threshold = ((strlen($threshold) > 0) ? (float) $threshold : 0.1);
2810
2811
						$phpthumbFilters->HistogramStretch($this->gdimg_output, $band, $method, $threshold);
2812
						break;
2813
2814
					case 'wb': // WhiteBalance
2815
						$phpthumbFilters->WhiteBalance($this->gdimg_output, $parameter);
2816
						break;
2817
2818
					case 'hist': // Histogram overlay
2819
						if (phpthumb_functions::gd_version() < 2) {
2820
							$this->DebugMessage('Skipping HistogramOverlay() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
2821
							break;
2822
						}
2823
						@list($bands, $colors, $width, $height, $alignment, $opacity, $margin_x, $margin_y) = explode('|', $parameter, 8);
2824
						$bands     = ($bands     ? $bands     :  '*');
2825
						$colors    = ($colors    ? $colors    :   '');
2826
						$width     = ($width     ? $width     : 0.25);
2827
						$height    = ($height    ? $height    : 0.25);
2828
						$alignment = ($alignment ? $alignment : 'BR');
2829
						$opacity   = ($opacity   ? $opacity   :   50);
2830
						$margin_x  = ($margin_x  ? $margin_x  :    5);
2831
						// $margin_y -- it wasn't forgotten, let the value always pass unchanged
2832
						$phpthumbFilters->HistogramOverlay($this->gdimg_output, $bands, $colors, $width, $height, $alignment, $opacity, $margin_x, $margin_y);
2833
						break;
2834
2835
					case 'fram': // Frame
2836
						@list($frame_width, $edge_width, $color_frame, $color1, $color2) = explode('|', $parameter, 5);
2837
						$phpthumbFilters->Frame($this->gdimg_output, $frame_width, $edge_width, $color_frame, $color1, $color2);
2838
						break;
2839
2840
					case 'drop': // DropShadow
2841
						if (phpthumb_functions::gd_version() < 2) {
2842
							$this->DebugMessage('Skipping DropShadow() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
2843
							return false;
2844
						}
2845
						$this->is_alpha = true;
2846
						@list($distance, $width, $color, $angle, $fade) = explode('|', $parameter, 5);
2847
						$phpthumbFilters->DropShadow($this->gdimg_output, $distance, $width, $color, $angle, $fade);
2848
						break;
2849
2850
					case 'mask': // Mask cropping
2851
						if (phpthumb_functions::gd_version() < 2) {
2852
							$this->DebugMessage('Skipping Mask() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
2853
							return false;
2854
						}
2855
						@list($mask_filename, $invert) = explode('|', $parameter, 2);
2856
						$mask_filename = $this->ResolveFilenameToAbsolute($mask_filename);
2857
						if (@is_readable($mask_filename) && ($fp_mask = @fopen($mask_filename, 'rb'))) {
0 ignored issues
show
Bug introduced by
It seems like $mask_filename can also be of type false; however, parameter $filename of is_readable() does only seem to accept string, 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

2857
						if (@is_readable(/** @scrutinizer ignore-type */ $mask_filename) && ($fp_mask = @fopen($mask_filename, 'rb'))) {
Loading history...
Bug introduced by
It seems like $mask_filename can also be of type false; however, parameter $filename of fopen() does only seem to accept string, 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

2857
						if (@is_readable($mask_filename) && ($fp_mask = @fopen(/** @scrutinizer ignore-type */ $mask_filename, 'rb'))) {
Loading history...
2858
							$MaskImageData = '';
2859
							do {
2860
								$buffer = fread($fp_mask, 8192);
2861
								$MaskImageData .= $buffer;
2862
							} while (strlen($buffer) > 0);
2863
							fclose($fp_mask);
2864
							if ($gdimg_mask = $this->ImageCreateFromStringReplacement($MaskImageData)) {
2865
								if ($invert && phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) {
2866
									imagefilter($gdimg_mask, IMG_FILTER_NEGATE);
2867
								}
2868
								$this->is_alpha = true;
2869
								$phpthumbFilters->ApplyMask($gdimg_mask, $this->gdimg_output);
2870
								imagedestroy($gdimg_mask);
2871
							} else {
2872
								$this->DebugMessage('ImageCreateFromStringReplacement() failed for "'.$mask_filename.'"', __FILE__, __LINE__);
2873
							}
2874
						} else {
2875
							$this->DebugMessage('Cannot open mask file "'.$mask_filename.'"', __FILE__, __LINE__);
0 ignored issues
show
Bug introduced by
Are you sure $mask_filename of type false|null|string can be used in concatenation? ( Ignorable by Annotation )

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

2875
							$this->DebugMessage('Cannot open mask file "'./** @scrutinizer ignore-type */ $mask_filename.'"', __FILE__, __LINE__);
Loading history...
2876
						}
2877
						break;
2878
2879
					case 'elip': // Ellipse cropping
2880
						if (phpthumb_functions::gd_version() < 2) {
2881
							$this->DebugMessage('Skipping Ellipse() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
2882
							return false;
2883
						}
2884
						$this->is_alpha = true;
2885
						$phpthumbFilters->Ellipse($this->gdimg_output);
2886
						break;
2887
2888
					case 'ric': // RoundedImageCorners
2889
						if (phpthumb_functions::gd_version() < 2) {
2890
							$this->DebugMessage('Skipping RoundedImageCorners() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
2891
							return false;
2892
						}
2893
						@list($radius_x, $radius_y) = explode('|', $parameter, 2);
2894
						if (($radius_x < 1) || ($radius_y < 1)) {
2895
							$this->DebugMessage('Skipping RoundedImageCorners('.$radius_x.', '.$radius_y.') because x/y radius is less than 1', __FILE__, __LINE__);
2896
							break;
2897
						}
2898
						$this->is_alpha = true;
2899
						$phpthumbFilters->RoundedImageCorners($this->gdimg_output, $radius_x, $radius_y);
2900
						break;
2901
2902
					case 'crop': // Crop
2903
						@list($left, $right, $top, $bottom) = explode('|', $parameter, 4);
2904
						$phpthumbFilters->Crop($this->gdimg_output, $left, $right, $top, $bottom);
2905
						break;
2906
2907
					case 'bord': // Border
2908
						@list($border_width, $radius_x, $radius_y, $hexcolor_border) = explode('|', $parameter, 4);
2909
						$this->is_alpha = true;
2910
						$phpthumbFilters->ImageBorder($this->gdimg_output, $border_width, $radius_x, $radius_y, $hexcolor_border);
2911
						break;
2912
2913
					case 'over': // Overlay
2914
						@list($filename, $underlay, $margin, $opacity) = explode('|', $parameter, 4);
2915
						$underlay = (bool) ($underlay              ? $underlay : false);
2916
						$margin   =        ((strlen($margin)  > 0) ? $margin   : ($underlay ? 0.1 : 0.0));
2917
						$opacity  =        ((strlen($opacity) > 0) ? $opacity  : 100);
2918
						if (($margin > 0) && ($margin < 1)) {
2919
							$margin = min(0.499, $margin);
2920
						} elseif (($margin > -1) && ($margin < 0)) {
2921
							$margin = max(-0.499, $margin);
2922
						}
2923
2924
						$filename = $this->ResolveFilenameToAbsolute($filename);
2925
						if (@is_readable($filename) && ($fp_watermark = @fopen($filename, 'rb'))) {
2926
							$WatermarkImageData = '';
2927
							do {
2928
								$buffer = fread($fp_watermark, 8192);
2929
								$WatermarkImageData .= $buffer;
2930
							} while (strlen($buffer) > 0);
2931
							fclose($fp_watermark);
2932
							if ($img_watermark = $this->ImageCreateFromStringReplacement($WatermarkImageData)) {
2933
								if (($margin > 0) && ($margin < 1)) {
2934
									$resized_x = max(1, imagesx($this->gdimg_output) - round(2 * (imagesx($this->gdimg_output) * $margin)));
2935
									$resized_y = max(1, imagesy($this->gdimg_output) - round(2 * (imagesy($this->gdimg_output) * $margin)));
2936
								} else {
2937
									$resized_x = max(1, imagesx($this->gdimg_output) - round(2 * $margin));
2938
									$resized_y = max(1, imagesy($this->gdimg_output) - round(2 * $margin));
2939
								}
2940
2941
								if ($underlay) {
2942
2943
									if ($img_watermark_resized = phpthumb_functions::ImageCreateFunction(imagesx($this->gdimg_output), imagesy($this->gdimg_output))) {
2944
										imagealphablending($img_watermark_resized, false);
2945
										imagesavealpha($img_watermark_resized, true);
2946
										$this->ImageResizeFunction($img_watermark_resized, $img_watermark, 0, 0, 0, 0, imagesx($img_watermark_resized), imagesy($img_watermark_resized), imagesx($img_watermark), imagesy($img_watermark));
2947
										if ($img_source_resized = phpthumb_functions::ImageCreateFunction($resized_x, $resized_y)) {
2948
											imagealphablending($img_source_resized, false);
2949
											imagesavealpha($img_source_resized, true);
2950
											$this->ImageResizeFunction($img_source_resized, $this->gdimg_output, 0, 0, 0, 0, imagesx($img_source_resized), imagesy($img_source_resized), imagesx($this->gdimg_output), imagesy($this->gdimg_output));
2951
											$phpthumbFilters->WatermarkOverlay($img_watermark_resized, $img_source_resized, 'C', $opacity, $margin);
2952
											imagecopy($this->gdimg_output, $img_watermark_resized, 0, 0, 0, 0, imagesx($this->gdimg_output), imagesy($this->gdimg_output));
2953
										} else {
2954
											$this->DebugMessage('phpthumb_functions::ImageCreateFunction('.$resized_x.', '.$resized_y.')', __FILE__, __LINE__);
2955
										}
2956
										imagedestroy($img_watermark_resized);
2957
									} else {
2958
										$this->DebugMessage('phpthumb_functions::ImageCreateFunction('.imagesx($this->gdimg_output).', '.imagesy($this->gdimg_output).')', __FILE__, __LINE__);
2959
									}
2960
2961
								} else { // overlay
2962
2963
									if ($img_watermark_resized = phpthumb_functions::ImageCreateFunction($resized_x, $resized_y)) {
2964
										imagealphablending($img_watermark_resized, false);
2965
										imagesavealpha($img_watermark_resized, true);
2966
										$this->ImageResizeFunction($img_watermark_resized, $img_watermark, 0, 0, 0, 0, imagesx($img_watermark_resized), imagesy($img_watermark_resized), imagesx($img_watermark), imagesy($img_watermark));
2967
										$phpthumbFilters->WatermarkOverlay($this->gdimg_output, $img_watermark_resized, 'C', $opacity, $margin);
2968
										imagedestroy($img_watermark_resized);
2969
									} else {
2970
										$this->DebugMessage('phpthumb_functions::ImageCreateFunction('.$resized_x.', '.$resized_y.')', __FILE__, __LINE__);
2971
									}
2972
2973
								}
2974
								imagedestroy($img_watermark);
2975
2976
							} else {
2977
								$this->DebugMessage('ImageCreateFromStringReplacement() failed for "'.$filename.'"', __FILE__, __LINE__);
2978
							}
2979
						} else {
2980
							$this->DebugMessage('Cannot open overlay file "'.$filename.'"', __FILE__, __LINE__);
0 ignored issues
show
Bug introduced by
Are you sure $filename of type false|null|string can be used in concatenation? ( Ignorable by Annotation )

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

2980
							$this->DebugMessage('Cannot open overlay file "'./** @scrutinizer ignore-type */ $filename.'"', __FILE__, __LINE__);
Loading history...
2981
						}
2982
						break;
2983
2984
					case 'wmi': // WaterMarkImage
2985
						@list($filename, $alignment, $opacity, $margin['x'], $margin['y'], $rotate_angle) = explode('|', $parameter, 6);
2986
						// $margin can be pixel margin or percent margin if $alignment is text, or max width/height if $alignment is position like "50x75"
2987
						$alignment    = ($alignment            ? $alignment            : 'BR');
2988
						$opacity      = ('' != $opacity ? (int) $opacity : 50);
2989
						$rotate_angle = ('' != $rotate_angle ? (int) $rotate_angle : 0);
2990
						if (!preg_match('#^([0-9\\.\\-]*)x([0-9\\.\\-]*)$#i', $alignment, $matches)) {
2991
							$margins = array('x', 'y');
2992
							foreach ($margins as $xy) {
2993
								$margin[$xy] = ('' !== $margin[ $xy ] ? $margin[ $xy] : 5);
2994
								if (($margin[$xy] > 0) && ($margin[$xy] < 1)) {
2995
									$margin[$xy] = min(0.499, $margin[$xy]);
2996
								} elseif (($margin[$xy] > -1) && ($margin[$xy] < 0)) {
2997
									$margin[$xy] = max(-0.499, $margin[$xy]);
2998
								}
2999
							}
3000
						}
3001
3002
						$filename = $this->ResolveFilenameToAbsolute($filename);
3003
						if (@is_readable($filename)) {
3004
							if ($img_watermark = $this->ImageCreateFromFilename($filename)) {
3005
								if ($rotate_angle !== 0) {
3006
									$phpthumbFilters->ImprovedImageRotate($img_watermark, $rotate_angle, 'FFFFFF', null, $this);
3007
								}
3008
								if (preg_match('#^([0-9\\.\\-]*)x([0-9\\.\\-]*)$#i', $alignment, $matches)) {
3009
									$watermark_max_width  = (int) ($margin[ 'x'] ? $margin[ 'x'] : imagesx($img_watermark));
3010
									$watermark_max_height = (int) ($margin[ 'y'] ? $margin[ 'y'] : imagesy($img_watermark));
3011
									$scale = phpthumb_functions::ScaleToFitInBox(imagesx($img_watermark), imagesy($img_watermark), $watermark_max_width, $watermark_max_height, true, true);
3012
									$this->DebugMessage('Scaling watermark by a factor of '.number_format($scale, 4), __FILE__, __LINE__);
3013
									if (($scale > 1) || ($scale < 1)) {
3014
										if ($img_watermark2 = phpthumb_functions::ImageCreateFunction($scale * imagesx($img_watermark), $scale * imagesy($img_watermark))) {
3015
											imagealphablending($img_watermark2, false);
3016
											imagesavealpha($img_watermark2, true);
3017
											$this->ImageResizeFunction($img_watermark2, $img_watermark, 0, 0, 0, 0, imagesx($img_watermark2), imagesy($img_watermark2), imagesx($img_watermark), imagesy($img_watermark));
3018
											$img_watermark = $img_watermark2;
3019
										} else {
3020
											$this->DebugMessage('ImageCreateFunction('.($scale * imagesx($img_watermark)).', '.($scale * imagesx($img_watermark)).') failed', __FILE__, __LINE__);
3021
										}
3022
									}
3023
									$watermark_dest_x = round($matches[1] - (imagesx($img_watermark) / 2));
3024
									$watermark_dest_y = round($matches[2] - (imagesy($img_watermark) / 2));
3025
									$alignment = $watermark_dest_x.'x'.$watermark_dest_y;
3026
								}
3027
								$phpthumbFilters->WatermarkOverlay($this->gdimg_output, $img_watermark, $alignment, $opacity, $margin['x'], $margin['y']);
3028
								imagedestroy($img_watermark);
3029
								if (isset($img_watermark2) && is_resource($img_watermark2)) {
3030
									imagedestroy($img_watermark2);
3031
								}
3032
							} else {
3033
								$this->DebugMessage('ImageCreateFromFilename() failed for "'.$filename.'"', __FILE__, __LINE__);
3034
							}
3035
						} else {
3036
							$this->DebugMessage('!is_readable('.$filename.')', __FILE__, __LINE__);
3037
						}
3038
						break;
3039
3040
					case 'wmt': // WaterMarkText
3041
						@list($text, $size, $alignment, $hex_color, $ttffont, $opacity, $margin, $angle, $bg_color, $bg_opacity, $fillextend, $lineheight) = explode('|', $parameter, 12);
3042
						$text       = ($text            ? $text       : '');
3043
						$size       = ($size            ? $size       : 3);
3044
						$alignment  = ($alignment       ? $alignment  : 'BR');
3045
						$hex_color  = ($hex_color       ? $hex_color  : '000000');
3046
						$ttffont    = ($ttffont         ? $ttffont    : '');
3047
						$opacity    = ('' != $opacity ? $opacity    : 50);
3048
						$margin     = ('' != $margin ? $margin     : 5);
3049
						$angle      = ('' != $angle ? $angle      : 0);
3050
						$bg_color   = ($bg_color        ? $bg_color   : false);
3051
						$bg_opacity = ($bg_opacity      ? $bg_opacity : 0);
3052
						$fillextend = ($fillextend      ? $fillextend : '');
3053
						$lineheight = ($lineheight      ? $lineheight : 1.0);
3054
3055
						if (basename($ttffont) == $ttffont) {
3056
							$ttffont = $this->realPathSafe($this->config_ttf_directory.DIRECTORY_SEPARATOR.$ttffont);
3057
						} else {
3058
							$ttffont = $this->ResolveFilenameToAbsolute($ttffont);
3059
						}
3060
						$phpthumbFilters->WatermarkText($this->gdimg_output, $text, $size, $alignment, $hex_color, $ttffont, $opacity, $margin, $angle, $bg_color, $bg_opacity, $fillextend, $lineheight);
3061
						break;
3062
3063
					case 'blur': // Blur
3064
						@list($radius) = explode('|', $parameter, 1);
3065
						$radius = ($radius ? $radius : 1);
3066
						if (phpthumb_functions::gd_version() >= 2) {
3067
							$phpthumbFilters->Blur($this->gdimg_output, $radius);
3068
						} else {
3069
							$this->DebugMessage('Skipping Blur() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
3070
						}
3071
						break;
3072
3073
					case 'gblr': // Gaussian Blur
3074
						$phpthumbFilters->BlurGaussian($this->gdimg_output);
3075
						break;
3076
3077
					case 'sblr': // Selective Blur
3078
						$phpthumbFilters->BlurSelective($this->gdimg_output);
3079
						break;
3080
3081
					case 'mean': // MeanRemoval blur
3082
						$phpthumbFilters->MeanRemoval($this->gdimg_output);
3083
						break;
3084
3085
					case 'smth': // Smooth blur
3086
						$phpthumbFilters->Smooth($this->gdimg_output, $parameter);
3087
						break;
3088
3089
					case 'usm': // UnSharpMask sharpening
3090
						@list($amount, $radius, $threshold) = explode('|', $parameter, 3);
3091
						$amount    = ($amount            ? $amount    : 80);
3092
						$radius    = ($radius            ? $radius    : 0.5);
3093
						$threshold = ('' !== $threshold ? $threshold : 3);
3094
						if (phpthumb_functions::gd_version() >= 2.0) {
3095
							ob_start();
3096
							if (!@include_once __DIR__ .'/phpthumb.unsharp.php' ) {
3097
								$include_error = ob_get_contents();
3098
								if ($include_error) {
3099
									$this->DebugMessage('include_once("'. __DIR__ .'/phpthumb.unsharp.php") generated message: "'.$include_error.'"', __FILE__, __LINE__);
3100
								}
3101
								$this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.unsharp.php" which is required for unsharp masking', __FILE__, __LINE__);
3102
								ob_end_clean();
3103
								return false;
3104
							}
3105
							ob_end_clean();
3106
							phpUnsharpMask::applyUnsharpMask($this->gdimg_output, $amount, $radius, $threshold);
3107
						} else {
3108
							$this->DebugMessage('Skipping unsharp mask because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
3109
							return false;
3110
						}
3111
						break;
3112
3113
					case 'size': // Resize
3114
						@list($newwidth, $newheight, $stretch) = explode('|', $parameter);
3115
						$newwidth  = (!$newwidth  ? imagesx($this->gdimg_output) : ((($newwidth  > 0) && ($newwidth  < 1)) ? round($newwidth  * imagesx($this->gdimg_output)) : round($newwidth)));
0 ignored issues
show
Bug introduced by
$newwidth of type string is incompatible with the type double expected by parameter $val of round(). ( Ignorable by Annotation )

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

3115
						$newwidth  = (!$newwidth  ? imagesx($this->gdimg_output) : ((($newwidth  > 0) && ($newwidth  < 1)) ? round($newwidth  * imagesx($this->gdimg_output)) : round(/** @scrutinizer ignore-type */ $newwidth)));
Loading history...
3116
						$newheight = (!$newheight ? imagesy($this->gdimg_output) : ((($newheight > 0) && ($newheight < 1)) ? round($newheight * imagesy($this->gdimg_output)) : round($newheight)));
3117
						$stretch   = ($stretch ? true : false);
3118
						if ($stretch) {
3119
							$scale_x = phpthumb_functions::ScaleToFitInBox(imagesx($this->gdimg_output), imagesx($this->gdimg_output), $newwidth,  $newwidth,  true, true);
3120
							$scale_y = phpthumb_functions::ScaleToFitInBox(imagesy($this->gdimg_output), imagesy($this->gdimg_output), $newheight, $newheight, true, true);
3121
						} else {
3122
							$scale_x = phpthumb_functions::ScaleToFitInBox(imagesx($this->gdimg_output), imagesy($this->gdimg_output), $newwidth, $newheight, true, true);
3123
							$scale_y = $scale_x;
3124
						}
3125
						$this->DebugMessage('Scaling watermark ('.($stretch ? 'with' : 'without').' stretch) by a factor of "'.number_format($scale_x, 4).' x '.number_format($scale_y, 4).'"', __FILE__, __LINE__);
3126
						if (($scale_x > 1) || ($scale_x < 1) || ($scale_y > 1) || ($scale_y < 1)) {
3127
							if ($img_temp = phpthumb_functions::ImageCreateFunction(imagesx($this->gdimg_output), imagesy($this->gdimg_output))) {
3128
								imagecopy($img_temp, $this->gdimg_output, 0, 0, 0, 0, imagesx($this->gdimg_output), imagesy($this->gdimg_output));
3129
								if ($this->gdimg_output = phpthumb_functions::ImageCreateFunction($scale_x * imagesx($img_temp), $scale_y * imagesy($img_temp))) {
3130
									imagealphablending($this->gdimg_output, false);
3131
									imagesavealpha($this->gdimg_output, true);
3132
									$this->ImageResizeFunction($this->gdimg_output, $img_temp, 0, 0, 0, 0, imagesx($this->gdimg_output), imagesy($this->gdimg_output), imagesx($img_temp), imagesy($img_temp));
3133
								} else {
3134
									$this->DebugMessage('ImageCreateFunction('.($scale_x * imagesx($img_temp)).', '.($scale_y * imagesy($img_temp)).') failed', __FILE__, __LINE__);
3135
								}
3136
								imagedestroy($img_temp);
3137
							} else {
3138
								$this->DebugMessage('ImageCreateFunction('.imagesx($this->gdimg_output).', '.imagesy($this->gdimg_output).') failed', __FILE__, __LINE__);
3139
							}
3140
						}
3141
						break;
3142
3143
					case 'rot': // ROTate
3144
						@list($angle, $bgcolor) = explode('|', $parameter, 2);
3145
						$phpthumbFilters->ImprovedImageRotate($this->gdimg_output, $angle, $bgcolor, null, $this);
3146
						break;
3147
3148
					case 'stc': // Source Transparent Color
3149
						@list($hexcolor, $min_limit, $max_limit) = explode('|', $parameter, 3);
3150
						if (!phpthumb_functions::IsHexColor($hexcolor)) {
3151
							$this->DebugMessage('Skipping SourceTransparentColor hex color is invalid ('.$hexcolor.')', __FILE__, __LINE__);
3152
							return false;
3153
						}
3154
						$min_limit = ('' !== $min_limit ? $min_limit :  5);
3155
						$max_limit = ('' !== $max_limit ? $max_limit : 10);
3156
						if ($gdimg_mask = $phpthumbFilters->SourceTransparentColorMask($this->gdimg_output, $hexcolor, $min_limit, $max_limit)) {
3157
							$this->is_alpha = true;
3158
							$phpthumbFilters->ApplyMask($gdimg_mask, $this->gdimg_output);
3159
							imagedestroy($gdimg_mask);
3160
						} else {
3161
							$this->DebugMessage('SourceTransparentColorMask() failed for "'.$hexcolor.','.$min_limit.','.$max_limit.'"', __FILE__, __LINE__);
3162
						}
3163
						break;
3164
				}
3165
				$this->DebugMessage('Finished processing filter command "'.$command.'('.$parameter.')"', __FILE__, __LINE__);
3166
			}
3167
		}
3168
		return true;
3169
	}
3170
3171
3172
	public function MaxFileSize() {
3173
		if (phpthumb_functions::gd_version() < 2) {
3174
			$this->DebugMessage('Skipping MaxFileSize() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
3175
			return false;
3176
		}
3177
		if ($this->maxb > 0) {
3178
			switch ($this->thumbnailFormat) {
3179
				case 'png':
3180
				case 'gif':
3181
					$imgRenderFunction = 'image'.$this->thumbnailFormat;
3182
3183
					ob_start();
3184
					$imgRenderFunction($this->gdimg_output);
3185
					$imgdata = ob_get_contents();
3186
					ob_end_clean();
3187
3188
					if (strlen($imgdata) > $this->maxb) {
3189
						for ($i = 8; $i >= 1; $i--) {
3190
							$tempIMG = imagecreatetruecolor(imagesx($this->gdimg_output), imagesy($this->gdimg_output));
3191
							imagecopy($tempIMG, $this->gdimg_output, 0, 0, 0, 0, imagesx($this->gdimg_output), imagesy($this->gdimg_output));
0 ignored issues
show
Bug introduced by
It seems like $tempIMG can also be of type false; however, parameter $dst_im of imagecopy() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

3191
							imagecopy(/** @scrutinizer ignore-type */ $tempIMG, $this->gdimg_output, 0, 0, 0, 0, imagesx($this->gdimg_output), imagesy($this->gdimg_output));
Loading history...
3192
							imagetruecolortopalette($tempIMG, true, pow(2, $i));
0 ignored issues
show
Bug introduced by
It seems like $tempIMG can also be of type false; however, parameter $image of imagetruecolortopalette() 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

3192
							imagetruecolortopalette(/** @scrutinizer ignore-type */ $tempIMG, true, pow(2, $i));
Loading history...
3193
							ob_start();
3194
							$imgRenderFunction($tempIMG);
3195
							$imgdata = ob_get_contents();
3196
							ob_end_clean();
3197
3198
							if (strlen($imgdata) <= $this->maxb) {
3199
								imagetruecolortopalette($this->gdimg_output, true, pow(2, $i));
3200
								break;
3201
							}
3202
						}
3203
					}
3204
					break;
3205
3206
				case 'jpeg':
3207
					ob_start();
3208
					imagejpeg($this->gdimg_output);
3209
					$imgdata = ob_get_contents();
3210
					ob_end_clean();
3211
3212
					if (strlen($imgdata) > $this->maxb) {
3213
						for ($i = 3; $i < 20; $i++) {
3214
							$q = round(100 * (1 - log10($i / 2)));
3215
							ob_start();
3216
							imagejpeg($this->gdimg_output, null, $q);
0 ignored issues
show
Bug introduced by
$q of type double is incompatible with the type integer expected by parameter $quality of imagejpeg(). ( Ignorable by Annotation )

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

3216
							imagejpeg($this->gdimg_output, null, /** @scrutinizer ignore-type */ $q);
Loading history...
3217
							$imgdata = ob_get_contents();
3218
							ob_end_clean();
3219
3220
							$this->thumbnailQuality = $q;
3221
							if (strlen($imgdata) <= $this->maxb) {
3222
								break;
3223
							}
3224
						}
3225
					}
3226
					if (strlen($imgdata) > $this->maxb) {
3227
						return false;
3228
					}
3229
					break;
3230
3231
				default:
3232
					return false;
3233
			}
3234
		}
3235
		return true;
3236
	}
3237
3238
3239
	public function CalculateThumbnailDimensions() {
3240
		$this->DebugMessage('CalculateThumbnailDimensions() starting with [W,H,sx,sy,sw,sh] initially set to ['.$this->source_width.','.$this->source_height.','.$this->sx.','.$this->sy.','.$this->sw.','.$this->sh.']', __FILE__, __LINE__);
3241
//echo $this->source_width.'x'.$this->source_height.'<hr>';
3242
		$this->thumbnailCropX = ($this->sx ? (($this->sx >= 2) ? $this->sx : round($this->sx * $this->source_width))  : 0);
3243
//echo $this->thumbnailCropX.'<br>';
3244
		$this->thumbnailCropY = ($this->sy ? (($this->sy >= 2) ? $this->sy : round($this->sy * $this->source_height)) : 0);
3245
//echo $this->thumbnailCropY.'<br>';
3246
		$this->thumbnailCropW = ($this->sw ? (($this->sw >= 2) ? $this->sw : round($this->sw * $this->source_width))  : $this->source_width);
3247
//echo $this->thumbnailCropW.'<br>';
3248
		$this->thumbnailCropH = ($this->sh ? (($this->sh >= 2) ? $this->sh : round($this->sh * $this->source_height)) : $this->source_height);
3249
//echo $this->thumbnailCropH.'<hr>';
3250
3251
		// limit source area to original image area
3252
		$this->thumbnailCropW = max(1, min($this->thumbnailCropW, $this->source_width  - $this->thumbnailCropX));
3253
		$this->thumbnailCropH = max(1, min($this->thumbnailCropH, $this->source_height - $this->thumbnailCropY));
3254
3255
		$this->DebugMessage('CalculateThumbnailDimensions() starting with [x,y,w,h] initially set to ['.$this->thumbnailCropX.','.$this->thumbnailCropY.','.$this->thumbnailCropW.','.$this->thumbnailCropH.']', __FILE__, __LINE__);
3256
3257
3258
		if ($this->zc && $this->w && $this->h) {
3259
			// Zoom Crop
3260
			// retain proportional resizing we did above, but crop off larger dimension so smaller
3261
			// dimension fully fits available space
3262
3263
			$scaling_X = $this->source_width  / $this->w;
3264
			$scaling_Y = $this->source_height / $this->h;
3265
			if ($scaling_X > $scaling_Y) {
3266
				// some of the width will need to be cropped
3267
				$allowable_width = $this->source_width / $scaling_X * $scaling_Y;
3268
				$this->thumbnailCropW = round($allowable_width);
3269
				$this->thumbnailCropX = round(($this->source_width - $allowable_width) / 2);
3270
3271
			} elseif ($scaling_Y > $scaling_X) {
3272
				// some of the height will need to be cropped
3273
				$allowable_height = $this->source_height / $scaling_Y * $scaling_X;
3274
				$this->thumbnailCropH = round($allowable_height);
3275
				$this->thumbnailCropY = round(($this->source_height - $allowable_height) / 2);
3276
3277
			} else {
3278
				// image fits perfectly, no cropping needed
3279
			}
3280
			$this->thumbnail_width  = $this->w;
3281
			$this->thumbnail_height = $this->h;
3282
			$this->thumbnail_image_width  = $this->thumbnail_width;
3283
			$this->thumbnail_image_height = $this->thumbnail_height;
3284
3285
		} elseif ($this->iar && $this->w && $this->h) {
3286
3287
			// Ignore Aspect Ratio
3288
			// stretch image to fit exactly 'w' x 'h'
3289
			$this->thumbnail_width  = $this->w;
3290
			$this->thumbnail_height = $this->h;
3291
			$this->thumbnail_image_width  = $this->thumbnail_width;
3292
			$this->thumbnail_image_height = $this->thumbnail_height;
3293
3294
		} else {
3295
3296
			$original_aspect_ratio = $this->thumbnailCropW / $this->thumbnailCropH;
3297
			if ($this->aoe) {
3298
				if ($this->w && $this->h) {
3299
					$maxwidth  = min($this->w, $this->h * $original_aspect_ratio);
3300
					$maxheight = min($this->h, $this->w / $original_aspect_ratio);
3301
				} elseif ($this->w) {
3302
					$maxwidth  = $this->w;
3303
					$maxheight = $this->w / $original_aspect_ratio;
3304
				} elseif ($this->h) {
3305
					$maxwidth  = $this->h * $original_aspect_ratio;
3306
					$maxheight = $this->h;
3307
				} else {
3308
					$maxwidth  = $this->thumbnailCropW;
3309
					$maxheight = $this->thumbnailCropH;
3310
				}
3311
			} else {
3312
				$maxwidth  = phpthumb_functions::nonempty_min($this->w, $this->thumbnailCropW, $this->config_output_maxwidth);
3313
				$maxheight = phpthumb_functions::nonempty_min($this->h, $this->thumbnailCropH, $this->config_output_maxheight);
3314
//echo $maxwidth.'x'.$maxheight.'<br>';
3315
				$maxwidth  = min($maxwidth, $maxheight * $original_aspect_ratio);
3316
				$maxheight = min($maxheight, $maxwidth / $original_aspect_ratio);
3317
//echo $maxwidth.'x'.$maxheight.'<hr>';
3318
			}
3319
3320
			$this->thumbnail_image_width  = $maxwidth;
3321
			$this->thumbnail_image_height = $maxheight;
3322
			$this->thumbnail_width  = $maxwidth;
3323
			$this->thumbnail_height = $maxheight;
3324
3325
			$this->FixedAspectRatio();
3326
		}
3327
3328
		$this->thumbnail_width  = max(1, floor($this->thumbnail_width));
3329
		$this->thumbnail_height = max(1, floor($this->thumbnail_height));
3330
		return true;
3331
	}
3332
3333
3334
	public function CreateGDoutput() {
3335
		$this->CalculateThumbnailDimensions();
3336
3337
		// create the GD image (either true-color or 256-color, depending on GD version)
3338
		$this->gdimg_output = phpthumb_functions::ImageCreateFunction($this->thumbnail_width, $this->thumbnail_height);
3339
3340
		// images that have transparency must have the background filled with the configured 'bg' color otherwise the transparent color will appear as black
3341
		imagesavealpha($this->gdimg_output, true);
3342
		if ($this->is_alpha && phpthumb_functions::gd_version() >= 2) {
3343
3344
			imagealphablending($this->gdimg_output, false);
3345
			$output_full_alpha = phpthumb_functions::ImageColorAllocateAlphaSafe($this->gdimg_output, 255, 255, 255, 127);
3346
			imagefilledrectangle($this->gdimg_output, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $output_full_alpha);
3347
3348
		} else {
3349
3350
			$current_transparent_color = imagecolortransparent($this->gdimg_source);
3351
			if ($this->bg || (@$current_transparent_color >= 0)) {
3352
3353
				$this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor);
3354
				if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) {
3355
					return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"');
3356
				}
3357
				$background_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_background_hexcolor);
3358
				imagefilledrectangle($this->gdimg_output, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $background_color);
3359
3360
			}
3361
3362
		}
3363
		$this->DebugMessage('CreateGDoutput() returning canvas "'.$this->thumbnail_width.'x'.$this->thumbnail_height.'"', __FILE__, __LINE__);
3364
		return true;
3365
	}
3366
3367
	public function SetOrientationDependantWidthHeight() {
3368
		$this->DebugMessage('SetOrientationDependantWidthHeight() starting with "'.$this->source_width.'"x"'.$this->source_height.'"', __FILE__, __LINE__);
3369
		if ($this->source_height > $this->source_width) {
3370
			// portrait
3371
			$this->w = phpthumb_functions::OneOfThese($this->wp, $this->w, $this->ws, $this->wl);
3372
			$this->h = phpthumb_functions::OneOfThese($this->hp, $this->h, $this->hs, $this->hl);
3373
		} elseif ($this->source_height < $this->source_width) {
3374
			// landscape
3375
			$this->w = phpthumb_functions::OneOfThese($this->wl, $this->w, $this->ws, $this->wp);
3376
			$this->h = phpthumb_functions::OneOfThese($this->hl, $this->h, $this->hs, $this->hp);
3377
		} else {
3378
			// square
3379
			$this->w = phpthumb_functions::OneOfThese($this->ws, $this->w, $this->wl, $this->wp);
3380
			$this->h = phpthumb_functions::OneOfThese($this->hs, $this->h, $this->hl, $this->hp);
3381
		}
3382
		//$this->w = round($this->w ? $this->w : (($this->h && $this->source_height) ? $this->h * $this->source_width  / $this->source_height : $this->w));
3383
		//$this->h = round($this->h ? $this->h : (($this->w && $this->source_width)  ? $this->w * $this->source_height / $this->source_width  : $this->h));
3384
		$this->DebugMessage('SetOrientationDependantWidthHeight() setting w="'. (int) $this->w .'", h="'. (int) $this->h .'"', __FILE__, __LINE__);
3385
		return true;
3386
	}
3387
3388
	public function ExtractEXIFgetImageSize() {
3389
		$this->DebugMessage('starting ExtractEXIFgetImageSize()', __FILE__, __LINE__);
3390
3391
		if (preg_match('#^http:#i', $this->src) && !$this->sourceFilename && $this->rawImageData) {
3392
			$this->SourceDataToTempFile();
3393
		}
3394
		if (null === $this->getimagesizeinfo) {
3395
			if ($this->sourceFilename) {
3396
				$this->getimagesizeinfo = @getimagesize($this->sourceFilename);
3397
				$this->source_width  = $this->getimagesizeinfo[0];
3398
				$this->source_height = $this->getimagesizeinfo[1];
3399
				$this->DebugMessage('getimagesize('.$this->sourceFilename.') says image is '.$this->source_width.'x'.$this->source_height, __FILE__, __LINE__);
3400
			} else {
3401
				$this->DebugMessage('skipping getimagesize() because $this->sourceFilename is empty', __FILE__, __LINE__);
3402
			}
3403
		} else {
3404
			$this->DebugMessage('skipping getimagesize() because !is_null($this->getimagesizeinfo)', __FILE__, __LINE__);
3405
		}
3406
3407
		if (is_resource($this->gdimg_source)) {
3408
3409
			$this->source_width  = imagesx($this->gdimg_source);
3410
			$this->source_height = imagesy($this->gdimg_source);
3411
3412
			$this->SetOrientationDependantWidthHeight();
3413
3414
		} elseif ($this->rawImageData && !$this->sourceFilename) {
3415
3416
			if ($this->SourceImageIsTooLarge($this->source_width, $this->source_height)) {
3417
				$this->DebugMessage('NOT bypassing EXIF and getimagesize sections because source image is too large for GD ('.$this->source_width.'x'.$this->source_width.'='.($this->source_width * $this->source_height * 5).'MB)', __FILE__, __LINE__);
3418
			} else {
3419
				$this->DebugMessage('bypassing EXIF and getimagesize sections because $this->rawImageData is set, and $this->sourceFilename is not set, and source image is not too large for GD ('.$this->source_width.'x'.$this->source_width.'='.($this->source_width * $this->source_height * 5).'MB)', __FILE__, __LINE__);
3420
			}
3421
3422
		}
3423
3424
		if (!empty($this->getimagesizeinfo)) {
3425
			// great
3426
			$this->getimagesizeinfo['filesize'] = @filesize($this->sourceFilename);
3427
		} elseif (!$this->rawImageData) {
3428
			$this->DebugMessage('getimagesize("'.$this->sourceFilename.'") failed', __FILE__, __LINE__);
3429
		}
3430
3431
		if ($this->config_prefer_imagemagick) {
3432
			if ($this->ImageMagickThumbnailToGD()) {
3433
				return true;
3434
			}
3435
			$this->DebugMessage('ImageMagickThumbnailToGD() failed', __FILE__, __LINE__);
3436
		}
3437
3438
		$this->source_width  = $this->getimagesizeinfo[0];
3439
		$this->source_height = $this->getimagesizeinfo[1];
3440
3441
		$this->SetOrientationDependantWidthHeight();
3442
3443
		if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '4.2.0', '>=') && function_exists('exif_read_data')) {
3444
			switch ($this->getimagesizeinfo[2]) {
3445
				case IMAGETYPE_JPEG:
3446
				case IMAGETYPE_TIFF_II:
3447
				case IMAGETYPE_TIFF_MM:
3448
					$this->exif_raw_data = @exif_read_data($this->sourceFilename, 0, true);
3449
					break;
3450
			}
3451
		}
3452
		if (function_exists('exif_thumbnail') && ($this->getimagesizeinfo[2] == IMAGETYPE_JPEG)) {
3453
			// Extract EXIF info from JPEGs
3454
3455
			$this->exif_thumbnail_width  = '';
3456
			$this->exif_thumbnail_height = '';
3457
			$this->exif_thumbnail_type   = '';
3458
3459
			// The parameters width, height and imagetype are available since PHP v4.3.0
3460
			if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '4.3.0', '>=')) {
3461
3462
				$this->exif_thumbnail_data = @exif_thumbnail($this->sourceFilename, $this->exif_thumbnail_width, $this->exif_thumbnail_height, $this->exif_thumbnail_type);
0 ignored issues
show
Bug introduced by
$this->exif_thumbnail_width of type string is incompatible with the type integer expected by parameter $width of exif_thumbnail(). ( Ignorable by Annotation )

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

3462
				$this->exif_thumbnail_data = @exif_thumbnail($this->sourceFilename, /** @scrutinizer ignore-type */ $this->exif_thumbnail_width, $this->exif_thumbnail_height, $this->exif_thumbnail_type);
Loading history...
Bug introduced by
$this->exif_thumbnail_type of type string is incompatible with the type integer expected by parameter $imagetype of exif_thumbnail(). ( Ignorable by Annotation )

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

3462
				$this->exif_thumbnail_data = @exif_thumbnail($this->sourceFilename, $this->exif_thumbnail_width, $this->exif_thumbnail_height, /** @scrutinizer ignore-type */ $this->exif_thumbnail_type);
Loading history...
Bug introduced by
$this->exif_thumbnail_height of type string is incompatible with the type integer expected by parameter $height of exif_thumbnail(). ( Ignorable by Annotation )

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

3462
				$this->exif_thumbnail_data = @exif_thumbnail($this->sourceFilename, $this->exif_thumbnail_width, /** @scrutinizer ignore-type */ $this->exif_thumbnail_height, $this->exif_thumbnail_type);
Loading history...
3463
3464
			} else {
3465
3466
				// older versions of exif_thumbnail output an error message but NOT return false on failure
3467
				ob_start();
3468
				$this->exif_thumbnail_data = exif_thumbnail($this->sourceFilename);
3469
				$exit_thumbnail_error = ob_get_contents();
3470
				ob_end_clean();
3471
				if (!$exit_thumbnail_error && $this->exif_thumbnail_data) {
3472
3473
					if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) {
3474
						$this->exif_thumbnail_width  = imagesx($gdimg_exif_temp);
3475
						$this->exif_thumbnail_height = imagesy($gdimg_exif_temp);
3476
						$this->exif_thumbnail_type   = 2; // (2 == JPEG) before PHP v4.3.0 only JPEG format EXIF thumbnails are returned
3477
						unset($gdimg_exif_temp);
3478
					} else {
3479
						return $this->ErrorImage('Failed - $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data) in '.__FILE__.' on line '.__LINE__);
3480
					}
3481
3482
				}
3483
3484
			}
3485
3486
		} elseif (!function_exists('exif_thumbnail')) {
3487
3488
			$this->DebugMessage('exif_thumbnail() does not exist, cannot extract EXIF thumbnail', __FILE__, __LINE__);
3489
3490
		}
3491
3492
		$this->DebugMessage('EXIF thumbnail extraction: (size='.strlen($this->exif_thumbnail_data).'; type="'.$this->exif_thumbnail_type.'"; '. (int) $this->exif_thumbnail_width .'x'. (int) $this->exif_thumbnail_height .')', __FILE__, __LINE__);
3493
3494
		// see if EXIF thumbnail can be used directly with no processing
3495
		if ($this->config_use_exif_thumbnail_for_speed && $this->exif_thumbnail_data) {
3496
			while (true) {
3497
				if (!$this->xto) {
3498
					$source_ar = $this->source_width / $this->source_height;
3499
					$exif_ar = $this->exif_thumbnail_width / $this->exif_thumbnail_height;
3500
					if (number_format($source_ar, 2) != number_format($exif_ar, 2)) {
3501
						$this->DebugMessage('not using EXIF thumbnail because $source_ar != $exif_ar ('.$source_ar.' != '.$exif_ar.')', __FILE__, __LINE__);
3502
						break;
3503
					}
3504
					if ($this->w && ($this->w != $this->exif_thumbnail_width)) {
3505
						$this->DebugMessage('not using EXIF thumbnail because $this->w != $this->exif_thumbnail_width ('.$this->w.' != '.$this->exif_thumbnail_width.')', __FILE__, __LINE__);
3506
						break;
3507
					}
3508
					if ($this->h && ($this->h != $this->exif_thumbnail_height)) {
3509
						$this->DebugMessage('not using EXIF thumbnail because $this->h != $this->exif_thumbnail_height ('.$this->h.' != '.$this->exif_thumbnail_height.')', __FILE__, __LINE__);
3510
						break;
3511
					}
3512
					$CannotBeSetParameters = array('sx', 'sy', 'sh', 'sw', 'far', 'bg', 'bc', 'fltr', 'phpThumbDebug');
3513
					foreach ($CannotBeSetParameters as $parameter) {
3514
						if ($this->$parameter) {
3515
							break 2;
3516
						}
3517
					}
3518
				}
3519
3520
				$this->DebugMessage('setting $this->gdimg_source = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data)', __FILE__, __LINE__);
3521
				$this->gdimg_source = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data);
3522
				$this->source_width  = imagesx($this->gdimg_source);
3523
				$this->source_height = imagesy($this->gdimg_source);
3524
				return true;
3525
			}
3526
		}
3527
3528
		if (($this->config_max_source_pixels > 0) && (($this->source_width * $this->source_height) > $this->config_max_source_pixels)) {
3529
3530
			// Source image is larger than would fit in available PHP memory.
3531
			// If ImageMagick is installed, use it to generate the thumbnail.
3532
			// Else, if an EXIF thumbnail is available, use that as the source image.
3533
			// Otherwise, no choice but to fail with an error message
3534
			$this->DebugMessage('image is '.$this->source_width.'x'.$this->source_height.' and therefore contains more pixels ('.($this->source_width * $this->source_height).') than $this->config_max_source_pixels setting ('.$this->config_max_source_pixels.')', __FILE__, __LINE__);
3535
			if (!$this->config_prefer_imagemagick && $this->ImageMagickThumbnailToGD()) {
3536
				// excellent, we have a thumbnailed source image
3537
				return true;
3538
			}
3539
3540
		}
3541
		return true;
3542
	}
3543
3544
3545
	public function SetCacheFilename() {
3546
		if (null !== $this->cache_filename) {
3547
			$this->DebugMessage('$this->cache_filename already set, skipping SetCacheFilename()', __FILE__, __LINE__);
3548
			return true;
3549
		}
3550
		if (null === $this->config_cache_directory) {
3551
			$this->setCacheDirectory();
3552
			if (!$this->config_cache_directory) {
3553
				$this->DebugMessage('SetCacheFilename() failed because $this->config_cache_directory is empty', __FILE__, __LINE__);
3554
				return false;
3555
			}
3556
		}
3557
		$this->setOutputFormat();
3558
3559
		if (!$this->sourceFilename && !$this->rawImageData && $this->src) {
3560
			$this->sourceFilename = $this->ResolveFilenameToAbsolute($this->src);
3561
		}
3562
3563
		if ($this->config_cache_default_only_suffix && $this->sourceFilename) {
3564
			// simplified cache filenames:
3565
			// only use default parameters in phpThumb.config.php
3566
			// substitute source filename into * in $this->config_cache_default_only_suffix
3567
			// (eg: '*_thumb' becomes 'picture_thumb.jpg')
3568
			if (strpos($this->config_cache_default_only_suffix, '*') === false) {
0 ignored issues
show
Bug introduced by
$this->config_cache_default_only_suffix of type true is incompatible with the type string expected by parameter $haystack of strpos(). ( Ignorable by Annotation )

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

3568
			if (strpos(/** @scrutinizer ignore-type */ $this->config_cache_default_only_suffix, '*') === false) {
Loading history...
3569
				$this->DebugMessage('aborting simplified caching filename because no * in "'.$this->config_cache_default_only_suffix.'"', __FILE__, __LINE__);
0 ignored issues
show
Bug introduced by
Are you sure $this->config_cache_default_only_suffix of type true can be used in concatenation? ( Ignorable by Annotation )

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

3569
				$this->DebugMessage('aborting simplified caching filename because no * in "'./** @scrutinizer ignore-type */ $this->config_cache_default_only_suffix.'"', __FILE__, __LINE__);
Loading history...
3570
			} else {
3571
				preg_match('#(.+)(\\.[a-z0-9]+)?$#i', basename($this->sourceFilename), $matches);
3572
				$this->cache_filename = $this->config_cache_directory.DIRECTORY_SEPARATOR.rawurlencode(str_replace('*', @$matches[1], $this->config_cache_default_only_suffix)).'.'.strtolower($this->thumbnailFormat);
3573
				return true;
3574
			}
3575
		}
3576
3577
		$this->cache_filename = '';
3578
		if ($this->new) {
3579
			$broad_directory_name = strtolower(md5($this->new));
3580
			$this->cache_filename .= '_new'.$broad_directory_name;
3581
		} elseif ($this->md5s) {
3582
			// source image MD5 hash provided
3583
			$this->DebugMessage('SetCacheFilename() _raw set from $this->md5s = "'.$this->md5s.'"', __FILE__, __LINE__);
3584
			$broad_directory_name = $this->md5s;
3585
			$this->cache_filename .= '_raw'.$this->md5s;
3586
		} elseif (!$this->src && $this->rawImageData) {
3587
			$this->DebugMessage('SetCacheFilename() _raw set from md5($this->rawImageData) = "'.md5($this->rawImageData).'"', __FILE__, __LINE__);
3588
			$broad_directory_name = strtolower(md5($this->rawImageData));
3589
			$this->cache_filename .= '_raw'.$broad_directory_name;
3590
		} else {
3591
			$this->DebugMessage('SetCacheFilename() _src set from md5($this->sourceFilename) "'.$this->sourceFilename.'" = "'.md5($this->sourceFilename).'"', __FILE__, __LINE__);
3592
			$broad_directory_name = strtolower(md5($this->sourceFilename));
3593
			$this->cache_filename .= '_src'.$broad_directory_name;
3594
		}
3595
		if (!empty($_SERVER['HTTP_REFERER']) && $this->config_nooffsitelink_enabled) {
3596
			$parsed_url1 = @phpthumb_functions::ParseURLbetter(@$_SERVER['HTTP_REFERER']);
3597
			$parsed_url2 = @phpthumb_functions::ParseURLbetter('http://'.@$_SERVER['HTTP_HOST']);
3598
			if (@$parsed_url1['host'] && @$parsed_url2['host'] && ($parsed_url1['host'] != $parsed_url2['host'])) {
3599
				// include "_offsite" only if nooffsitelink_enabled and if referrer doesn't match the domain of the current server
3600
				$this->cache_filename .= '_offsite';
3601
			}
3602
		}
3603
3604
		$ParametersString = '';
3605
		if ($this->fltr && is_array($this->fltr)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->fltr of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
3606
			$ParametersString .= '_fltr'.implode('_fltr', $this->fltr);
3607
		}
3608
		$FilenameParameters1 = array('ar', 'bg', 'bc', 'far', 'sx', 'sy', 'sw', 'sh', 'zc');
3609
		foreach ($FilenameParameters1 as $key) {
3610
			if ($this->$key) {
3611
				$ParametersString .= '_'.$key.$this->$key;
3612
			}
3613
		}
3614
		$FilenameParameters2 = array('h', 'w', 'wl', 'wp', 'ws', 'hp', 'hs', 'xto', 'ra', 'iar', 'aoe', 'maxb', 'sfn', 'dpi');
3615
		foreach ($FilenameParameters2 as $key) {
3616
			if ($this->$key) {
3617
				$ParametersString .= '_'.$key. (int) $this->$key;
3618
			}
3619
		}
3620
		if ($this->thumbnailFormat == 'jpeg') {
3621
			// only JPEG output has variable quality option
3622
			$ParametersString .= '_q'. (int) $this->thumbnailQuality;
3623
		}
3624
		$this->DebugMessage('SetCacheFilename() _par set from md5('.$ParametersString.')', __FILE__, __LINE__);
3625
		$this->cache_filename .= '_par'.strtolower(md5($ParametersString));
3626
3627
		if ($this->md5s) {
3628
			// source image MD5 hash provided
3629
			// do not source image modification date --
3630
			// cached image will be used even if file was modified or removed
3631
		} elseif (!$this->config_cache_source_filemtime_ignore_remote && preg_match('#^(f|ht)tps?\://#i', $this->src)) {
3632
			$this->cache_filename .= '_dat'. (int) phpthumb_functions::filedate_remote($this->src);
3633
		} elseif (!$this->config_cache_source_filemtime_ignore_local && $this->src && !$this->rawImageData) {
3634
			$this->cache_filename .= '_dat'. (int) (@filemtime($this->sourceFilename));
3635
		}
3636
3637
		$this->cache_filename .= '.'.strtolower($this->thumbnailFormat);
3638
		$broad_directories = '';
3639
		for ($i = 0; $i < $this->config_cache_directory_depth; $i++) {
3640
			$broad_directories .= DIRECTORY_SEPARATOR.substr($broad_directory_name, 0, $i + 1);
3641
		}
3642
3643
		$this->cache_filename = $this->config_cache_directory.$broad_directories.DIRECTORY_SEPARATOR.$this->config_cache_prefix.rawurlencode($this->cache_filename);
3644
		return true;
3645
	}
3646
3647
3648
	public function SourceImageIsTooLarge($width, $height) {
3649
		if (!$this->config_max_source_pixels) {
3650
			return false;
3651
		}
3652
		if ($this->php_memory_limit && function_exists('memory_get_usage')) {
3653
			$available_memory = $this->php_memory_limit - memory_get_usage();
3654
			return (bool) (($width * $height * 5) > $available_memory);
3655
		}
3656
		return (bool) (($width * $height) > $this->config_max_source_pixels);
3657
	}
3658
3659
	public function ImageCreateFromFilename($filename) {
3660
		// try to create GD image source directly via GD, if possible,
3661
		// rather than buffering to memory and creating with imagecreatefromstring
3662
		$ImageCreateWasAttempted = false;
3663
		$gd_image = false;
3664
3665
		$this->DebugMessage('starting ImageCreateFromFilename('.$filename.')', __FILE__, __LINE__);
3666
		if ($filename && ($getimagesizeinfo = @getimagesize($filename))) {
3667
			if (!$this->SourceImageIsTooLarge($getimagesizeinfo[0], $getimagesizeinfo[1])) {
3668
				$ImageCreateFromFunction = array(
3669
					1  => 'imagecreatefromgif',
3670
					2  => 'imagecreatefromjpeg',
3671
					3  => 'imagecreatefrompng',
3672
					15 => 'imagecreatefromwbmp',
3673
				);
3674
				$this->DebugMessage('ImageCreateFromFilename found ($getimagesizeinfo[2]=='.@$getimagesizeinfo[2].')', __FILE__, __LINE__);
3675
				switch (@$getimagesizeinfo[2]) {
3676
					case 1:  // GIF
3677
					case 2:  // JPEG
3678
					case 3:  // PNG
3679
					case 15: // WBMP
3680
						$ImageCreateFromFunctionName = $ImageCreateFromFunction[$getimagesizeinfo[2]];
3681
						if (function_exists($ImageCreateFromFunctionName)) {
3682
							$this->DebugMessage('Calling '.$ImageCreateFromFunctionName.'('.$filename.')', __FILE__, __LINE__);
3683
							$ImageCreateWasAttempted = true;
3684
							$gd_image = $ImageCreateFromFunctionName($filename);
3685
						} else {
3686
							$this->DebugMessage('NOT calling '.$ImageCreateFromFunctionName.'('.$filename.') because !function_exists('.$ImageCreateFromFunctionName.')', __FILE__, __LINE__);
3687
						}
3688
						break;
3689
3690
					case 4:  // SWF
3691
					case 5:  // PSD
3692
					case 6:  // BMP
3693
					case 7:  // TIFF (LE)
3694
					case 8:  // TIFF (BE)
3695
					case 9:  // JPC
3696
					case 10: // JP2
3697
					case 11: // JPX
3698
					case 12: // JB2
3699
					case 13: // SWC
3700
					case 14: // IFF
3701
					case 16: // XBM
3702
						$this->DebugMessage('No built-in image creation function for image type "'.@$getimagesizeinfo[2].'" ($getimagesizeinfo[2])', __FILE__, __LINE__);
3703
						break;
3704
3705
					default:
3706
						$this->DebugMessage('Unknown value for $getimagesizeinfo[2]: "'.@$getimagesizeinfo[2].'"', __FILE__, __LINE__);
3707
						break;
3708
				}
3709
			} else {
3710
				$this->DebugMessage('image is '.$getimagesizeinfo[0].'x'.$getimagesizeinfo[1].' and therefore contains more pixels ('.($getimagesizeinfo[0] * $getimagesizeinfo[1]).') than $this->config_max_source_pixels setting ('.$this->config_max_source_pixels.')', __FILE__, __LINE__);
3711
				return false;
3712
			}
3713
		} else {
3714
			$this->DebugMessage('empty $filename or getimagesize('.$filename.') failed', __FILE__, __LINE__);
3715
		}
3716
3717
		if (!$gd_image) {
3718
			// cannot create from filename, attempt to create source image with imagecreatefromstring, if possible
3719
			if ($ImageCreateWasAttempted) {
3720
				$this->DebugMessage($ImageCreateFromFunctionName.'() was attempted but FAILED', __FILE__, __LINE__);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ImageCreateFromFunctionName does not seem to be defined for all execution paths leading up to this point.
Loading history...
3721
			}
3722
			$this->DebugMessage('Populating $rawimagedata', __FILE__, __LINE__);
3723
			$rawimagedata = '';
3724
			if ($fp = @fopen($filename, 'rb')) {
3725
				$filesize = filesize($filename);
3726
				$blocksize = 8192;
3727
				$blockreads = ceil($filesize / $blocksize);
3728
				for ($i = 0; $i < $blockreads; $i++) {
3729
					$rawimagedata .= fread($fp, $blocksize);
3730
				}
3731
				fclose($fp);
3732
			} else {
3733
				$this->DebugMessage('cannot fopen('.$filename.')', __FILE__, __LINE__);
3734
			}
3735
			if ($rawimagedata) {
3736
				$this->DebugMessage('attempting ImageCreateFromStringReplacement($rawimagedata ('.strlen($rawimagedata).' bytes), true)', __FILE__, __LINE__);
3737
				$gd_image = $this->ImageCreateFromStringReplacement($rawimagedata, true);
3738
			}
3739
		}
3740
		return $gd_image;
3741
	}
3742
3743
	public function SourceImageToGD() {
3744
		if (is_resource($this->gdimg_source)) {
3745
			$this->source_width  = imagesx($this->gdimg_source);
3746
			$this->source_height = imagesy($this->gdimg_source);
3747
			$this->DebugMessage('skipping SourceImageToGD() because $this->gdimg_source is already a resource ('.$this->source_width.'x'.$this->source_height.')', __FILE__, __LINE__);
3748
			return true;
3749
		}
3750
		$this->DebugMessage('starting SourceImageToGD()', __FILE__, __LINE__);
3751
3752
		if ($this->config_prefer_imagemagick) {
3753
			if (empty($this->sourceFilename) && !empty($this->rawImageData)) {
3754
				$this->DebugMessage('Copying raw image data to temp file and trying again with ImageMagick', __FILE__, __LINE__);
3755
				if ($tempnam = $this->phpThumb_tempnam()) {
3756
					if (file_put_contents($tempnam, $this->rawImageData)) {
3757
						$this->sourceFilename = $tempnam;
3758
						if ($this->ImageMagickThumbnailToGD()) {
3759
							// excellent, we have a thumbnailed source image
3760
							$this->DebugMessage('ImageMagickThumbnailToGD() succeeded', __FILE__, __LINE__);
3761
						} else {
3762
							$this->DebugMessage('ImageMagickThumbnailToGD() failed', __FILE__, __LINE__);
3763
						}
3764
						@chmod($tempnam, $this->getParameter('config_file_create_mask'));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

3764
						/** @scrutinizer ignore-unhandled */ @chmod($tempnam, $this->getParameter('config_file_create_mask'));

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...
3765
					} else {
3766
						$this->DebugMessage('failed to put $this->rawImageData into temp file "'.$tempnam.'"', __FILE__, __LINE__);
3767
					}
3768
				} else {
3769
					$this->DebugMessage('failed to generate temp file name', __FILE__, __LINE__);
3770
				}
3771
			}
3772
		}
3773
		if (!$this->gdimg_source && $this->rawImageData) {
3774
3775
			if ($this->SourceImageIsTooLarge($this->source_width, $this->source_height)) {
3776
				$memory_get_usage = (function_exists('memory_get_usage') ? memory_get_usage() : 0);
3777
				return $this->ErrorImage('Source image is too large ('.$this->source_width.'x'.$this->source_height.' = '.number_format($this->source_width * $this->source_height / 1000000, 1).'Mpx, max='.number_format($this->config_max_source_pixels / 1000000, 1).'Mpx) for GD creation (either install ImageMagick or increase PHP memory_limit to at least '.ceil(($memory_get_usage + (5 * $this->source_width * $this->source_height)) / 1048576).'M).');
3778
			}
3779
			if ($this->md5s && ($this->md5s != md5($this->rawImageData))) {
3780
				return $this->ErrorImage('$this->md5s != md5($this->rawImageData)'."\n".'"'.$this->md5s.'" != '."\n".'"'.md5($this->rawImageData).'"');
3781
			}
3782
			//if ($this->issafemode) {
3783
			//	return $this->ErrorImage('Cannot generate thumbnails from raw image data when PHP SAFE_MODE enabled');
3784
			//}
3785
			$this->gdimg_source = $this->ImageCreateFromStringReplacement($this->rawImageData);
3786
			if (!$this->gdimg_source) {
3787
				if (substr($this->rawImageData, 0, 2) === 'BM') {
3788
					$this->getimagesizeinfo[2] = 6; // BMP
3789
				} elseif (substr($this->rawImageData, 0, 4) === 'II'."\x2A\x00") {
3790
					$this->getimagesizeinfo[2] = 7; // TIFF (littlendian)
3791
				} elseif (substr($this->rawImageData, 0, 4) === 'MM'."\x00\x2A") {
3792
					$this->getimagesizeinfo[2] = 8; // TIFF (bigendian)
3793
				}
3794
				$this->DebugMessage('SourceImageToGD.ImageCreateFromStringReplacement() failed with unknown image type "'.substr($this->rawImageData, 0, 4).'" ('.phpthumb_functions::HexCharDisplay(substr($this->rawImageData, 0, 4)).')', __FILE__, __LINE__);
3795
//				return $this->ErrorImage('Unknown image type identified by "'.substr($this->rawImageData, 0, 4).'" ('.phpthumb_functions::HexCharDisplay(substr($this->rawImageData, 0, 4)).') in SourceImageToGD()['.__LINE__.']');
3796
			}
3797
3798
		} elseif (!$this->gdimg_source && $this->sourceFilename) {
3799
3800
			if ($this->md5s && ($this->md5s != phpthumb_functions::md5_file_safe($this->sourceFilename))) {
3801
				return $this->ErrorImage('$this->md5s != md5(sourceFilename)'."\n".'"'.$this->md5s.'" != '."\n".'"'.phpthumb_functions::md5_file_safe($this->sourceFilename).'"');
0 ignored issues
show
Bug introduced by
Are you sure phpthumb_functions::md5_...($this->sourceFilename) of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

3801
				return $this->ErrorImage('$this->md5s != md5(sourceFilename)'."\n".'"'.$this->md5s.'" != '."\n".'"'./** @scrutinizer ignore-type */ phpthumb_functions::md5_file_safe($this->sourceFilename).'"');
Loading history...
3802
			}
3803
			switch (@$this->getimagesizeinfo[2]) {
3804
				case 1:
3805
				case 3:
3806
					// GIF or PNG input file may have transparency
3807
					$this->is_alpha = true;
3808
					break;
3809
			}
3810
			if (!$this->SourceImageIsTooLarge($this->source_width, $this->source_height)) {
3811
				$this->gdimg_source = $this->ImageCreateFromFilename($this->sourceFilename);
3812
			}
3813
3814
		}
3815
3816
		while (true) {
3817
			if ($this->gdimg_source) {
3818
				$this->DebugMessage('Not using EXIF thumbnail data because $this->gdimg_source is already set', __FILE__, __LINE__);
3819
				break;
3820
			}
3821
			if (!$this->exif_thumbnail_data) {
3822
				$this->DebugMessage('Not using EXIF thumbnail data because $this->exif_thumbnail_data is empty', __FILE__, __LINE__);
3823
				break;
3824
			}
3825
			if (ini_get('safe_mode')) {
3826
				if (!$this->SourceImageIsTooLarge($this->source_width, $this->source_height)) {
3827
					$this->DebugMessage('Using EXIF thumbnail data because source image too large and safe_mode enabled', __FILE__, __LINE__);
3828
					$this->aoe = true;
3829
				} else {
3830
					break;
3831
				}
3832
			} else {
3833
				if (!$this->config_use_exif_thumbnail_for_speed) {
3834
					$this->DebugMessage('Not using EXIF thumbnail data because $this->config_use_exif_thumbnail_for_speed is FALSE', __FILE__, __LINE__);
3835
					break;
3836
				}
3837
				if (($this->thumbnailCropX != 0) || ($this->thumbnailCropY != 0)) {
3838
					$this->DebugMessage('Not using EXIF thumbnail data because source cropping is enabled ('.$this->thumbnailCropX.','.$this->thumbnailCropY.')', __FILE__, __LINE__);
3839
					break;
3840
				}
3841
				if (($this->w > $this->exif_thumbnail_width) || ($this->h > $this->exif_thumbnail_height)) {
3842
					$this->DebugMessage('Not using EXIF thumbnail data because EXIF thumbnail is too small ('.$this->exif_thumbnail_width.'x'.$this->exif_thumbnail_height.' vs '.$this->w.'x'.$this->h.')', __FILE__, __LINE__);
3843
					break;
3844
				}
3845
				$source_ar = $this->source_width / $this->source_height;
3846
				$exif_ar   = $this->exif_thumbnail_width / $this->exif_thumbnail_height;
3847
				if (number_format($source_ar, 2) != number_format($exif_ar, 2)) {
3848
					$this->DebugMessage('not using EXIF thumbnail because $source_ar != $exif_ar ('.$source_ar.' != '.$exif_ar.')', __FILE__, __LINE__);
3849
					break;
3850
				}
3851
			}
3852
3853
			// EXIF thumbnail exists, and is equal to or larger than destination thumbnail, and will be use as source image
3854
			$this->DebugMessage('Trying to use EXIF thumbnail as source image', __FILE__, __LINE__);
3855
3856
			if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) {
3857
3858
				$this->DebugMessage('Successfully using EXIF thumbnail as source image', __FILE__, __LINE__);
3859
				$this->gdimg_source   = $gdimg_exif_temp;
3860
				$this->source_width   = $this->exif_thumbnail_width;
3861
				$this->source_height  = $this->exif_thumbnail_height;
3862
				$this->thumbnailCropW = $this->source_width;
3863
				$this->thumbnailCropH = $this->source_height;
3864
				return true;
3865
3866
			} else {
3867
				$this->DebugMessage('$this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false) failed', __FILE__, __LINE__);
3868
			}
3869
3870
			break;
3871
		}
3872
3873
		if (!$this->gdimg_source) {
3874
			$this->DebugMessage('$this->gdimg_source is still empty', __FILE__, __LINE__);
3875
3876
			$this->DebugMessage('ImageMagickThumbnailToGD() failed', __FILE__, __LINE__);
3877
3878
			$imageHeader = '';
3879
			$gd_info = gd_info();
3880
			$GDreadSupport = false;
3881
			switch (@$this->getimagesizeinfo[2]) {
3882
				case 1:
3883
					$imageHeader = 'Content-Type: image/gif';
3884
					$GDreadSupport = (bool) @$gd_info['GIF Read Support'];
3885
					break;
3886
				case 2:
3887
					$imageHeader = 'Content-Type: image/jpeg';
3888
					$GDreadSupport = (bool) @$gd_info['JPG Support'];
3889
					break;
3890
				case 3:
3891
					$imageHeader = 'Content-Type: image/png';
3892
					$GDreadSupport = (bool) @$gd_info['PNG Support'];
3893
					break;
3894
			}
3895
			if ($imageHeader) {
3896
				// cannot create image for whatever reason (maybe imagecreatefromjpeg et al are not available?)
3897
				// and ImageMagick is not available either, no choice but to output original (not resized/modified) data and exit
3898
				if ($this->config_error_die_on_source_failure) {
3899
					$errormessages = array();
3900
					$errormessages[] = 'All attempts to create GD image source failed.';
3901
					if ($this->fatalerror) {
3902
						$errormessages[] = $this->fatalerror;
3903
					}
3904
					if ($this->issafemode) {
3905
						$errormessages[] = 'Safe Mode enabled, therefore ImageMagick is unavailable. (disable Safe Mode if possible)';
3906
					} elseif (!$this->ImageMagickVersion()) {
3907
						$errormessages[] = 'ImageMagick is not installed (it is highly recommended that you install it).';
3908
					}
3909
					if ($this->SourceImageIsTooLarge($this->getimagesizeinfo[0], $this->getimagesizeinfo[1])) {
3910
						$memory_get_usage = (function_exists('memory_get_usage') ? memory_get_usage() : 0);
3911
						$errormessages[] = 'Source image is too large ('.$this->getimagesizeinfo[0].'x'.$this->getimagesizeinfo[1].' = '.number_format($this->getimagesizeinfo[0] * $this->getimagesizeinfo[1] / 1000000, 1).'Mpx, max='.number_format($this->config_max_source_pixels / 1000000, 1).'Mpx) for GD creation (either install ImageMagick or increase PHP memory_limit to at least '.ceil(($memory_get_usage + (5 * $this->getimagesizeinfo[0] * $this->getimagesizeinfo[1])) / 1048576).'M).';
3912
					} elseif (!$GDreadSupport) {
3913
						$errormessages[] = 'GD does not have read support for "'.$imageHeader.'".';
3914
					} else {
3915
						$errormessages[] = 'Source image probably corrupt.';
3916
					}
3917
					$this->ErrorImage(implode("\n", $errormessages));
3918
3919
				} else {
3920
					$this->DebugMessage('All attempts to create GD image source failed ('.(ini_get('safe_mode') ? 'Safe Mode enabled, ImageMagick unavailable and source image probably too large for GD': ($GDreadSupport ? 'source image probably corrupt' : 'GD does not have read support for "'.$imageHeader.'"')).'), cannot generate thumbnail');
3921
					//$this->DebugMessage('All attempts to create GD image source failed ('.($GDreadSupport ? 'source image probably corrupt' : 'GD does not have read support for "'.$imageHeader.'"').'), outputing raw image', __FILE__, __LINE__);
3922
					//if (!$this->phpThumbDebug) {
3923
					//	header($imageHeader);
3924
					//	echo $this->rawImageData;
3925
					//	exit;
3926
					//}
3927
					return false;
3928
				}
3929
			}
3930
3931
			//switch (substr($this->rawImageData, 0, 2)) {
3932
			//	case 'BM':
3933
			switch (@$this->getimagesizeinfo[2]) {
3934
				case 6:
3935
					ob_start();
3936
					if (!@include_once __DIR__ .'/phpthumb.bmp.php' ) {
3937
						ob_end_clean();
3938
						return $this->ErrorImage('include_once('. __DIR__ .'/phpthumb.bmp.php) failed');
3939
					}
3940
					ob_end_clean();
3941
					if ($fp = @fopen($this->sourceFilename, 'rb')) {
3942
						$this->rawImageData = '';
3943
						while (!feof($fp)) {
3944
							$this->rawImageData .= fread($fp, 32768);
3945
						}
3946
						fclose($fp);
3947
					}
3948
					$phpthumb_bmp = new phpthumb_bmp();
3949
					$this->gdimg_source = $phpthumb_bmp->phpthumb_bmp2gd($this->rawImageData, phpthumb_functions::gd_version() >= 2.0);
3950
					unset($phpthumb_bmp);
3951
					if ($this->gdimg_source) {
3952
						$this->DebugMessage('$phpthumb_bmp->phpthumb_bmp2gd() succeeded', __FILE__, __LINE__);
3953
					} else {
3954
						return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on BMP source conversion' : 'phpthumb_bmp2gd() failed');
3955
					}
3956
					break;
3957
			//}
3958
			//switch (substr($this->rawImageData, 0, 4)) {
3959
			//	case 'II'."\x2A\x00":
3960
			//	case 'MM'."\x00\x2A":
3961
				case 7:
3962
				case 8:
3963
					return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on TIFF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support TIFF source images without it');
3964
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
3965
3966
				//case "\xD7\xCD\xC6\x9A":
3967
				//	return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on WMF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support WMF source images without it');
3968
				//	break;
3969
			}
3970
3971
			if (!$this->gdimg_source) {
3972
				if ($this->rawImageData) {
3973
					$HeaderFourBytes = substr($this->rawImageData, 0, 4);
3974
				} elseif ($this->sourceFilename) {
3975
					if ($fp = @fopen($this->sourceFilename, 'rb')) {
3976
						$HeaderFourBytes = fread($fp, 4);
3977
						fclose($fp);
3978
					} else {
3979
						return $this->ErrorImage('failed to open "'.$this->sourceFilename.'" SourceImageToGD() ['.__LINE__.']');
3980
					}
3981
				} else {
3982
					return $this->ErrorImage('Unable to create image, neither filename nor image data suppplied in SourceImageToGD() ['.__LINE__.']');
3983
				}
3984
				if (!$this->ImageMagickVersion() && !phpthumb_functions::gd_version()) {
3985
					return $this->ErrorImage('Neither GD nor ImageMagick seem to be installed on this server. At least one (preferably GD), or better both, MUST be installed for phpThumb to work.');
3986
				} elseif ($HeaderFourBytes == "\xD7\xCD\xC6\x9A") { // WMF
3987
					return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on WMF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support WMF source images without it');
3988
				} elseif ($HeaderFourBytes == '%PDF') { // "%PDF"
3989
					return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick and GhostScript are both required for PDF source images; GhostScript may not be properly configured' : 'ImageMagick and/or GhostScript are unavailable and phpThumb() does not support PDF source images without them');
3990
				} elseif (substr($HeaderFourBytes, 0, 3) == "\xFF\xD8\xFF") { // JPEG
3991
					return $this->ErrorImage('Image (JPEG) is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting');
3992
				} elseif ($HeaderFourBytes == '%PNG') { // "%PNG"
3993
					return $this->ErrorImage('Image (PNG) is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting');
3994
				} elseif (substr($HeaderFourBytes, 0, 3) == 'GIF') { // GIF
3995
					return $this->ErrorImage('Image (GIF) is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting');
3996
				}
3997
				return $this->ErrorImage('Unknown image type identified by "'.$HeaderFourBytes.'" ('.phpthumb_functions::HexCharDisplay($HeaderFourBytes).') in SourceImageToGD() ['.__LINE__.']');
3998
			}
3999
		}
4000
4001
		if (!$this->gdimg_source) {
4002
			if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) {
4003
				$this->DebugMessage('All other attempts failed, but successfully using EXIF thumbnail as source image', __FILE__, __LINE__);
4004
				$this->gdimg_source = $gdimg_exif_temp;
4005
				// override allow-enlarging setting if EXIF thumbnail is the only source available
4006
				// otherwise thumbnails larger than the EXIF thumbnail will be created at EXIF size
4007
				$this->aoe = true;
4008
				return true;
4009
			}
4010
			return false;
4011
		}
4012
4013
		$this->source_width  = imagesx($this->gdimg_source);
4014
		$this->source_height = imagesy($this->gdimg_source);
4015
		return true;
4016
	}
4017
4018
4019
	public function phpThumbDebugVarDump($var) {
4020
		if (null === $var) {
4021
			return 'NULL';
4022
		} elseif (is_bool($var)) {
4023
			return ($var ? 'TRUE' : 'FALSE');
4024
		} elseif (is_string($var)) {
4025
			return 'string('.strlen($var).')'.str_repeat(' ', max(0, 3 - strlen(strlen($var)))).' "'.$var.'"';
4026
		} elseif (is_int($var)) {
4027
			return 'integer     '.$var;
4028
		} elseif (is_float($var)) {
4029
			return 'float       '.$var;
4030
		} elseif (is_array($var)) {
4031
			ob_start();
4032
			var_dump($var);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($var) looks like debug code. Are you sure you do not want to remove it?
Loading history...
4033
			$vardumpoutput = ob_get_contents();
4034
			ob_end_clean();
4035
			return strtr($vardumpoutput, "\n\r\t", '   ');
4036
		}
4037
		return gettype($var);
4038
	}
4039
4040
	public function phpThumbDebug($level='') {
4041
		if ($level && ($this->phpThumbDebug !== $level)) {
4042
			return true;
4043
		}
4044
		if ($this->config_disable_debug) {
4045
			return $this->ErrorImage('phpThumbDebug disabled');
4046
		}
4047
4048
		$FunctionsExistance  = array('exif_thumbnail', 'gd_info', 'image_type_to_mime_type', 'getimagesize', 'imagecopyresampled', 'imagecopyresized', 'imagecreate', 'imagecreatefromstring', 'imagecreatetruecolor', 'imageistruecolor', 'imagerotate', 'imagetypes', 'version_compare', 'imagecreatefromgif', 'imagecreatefromjpeg', 'imagecreatefrompng', 'imagecreatefromwbmp', 'imagecreatefromxbm', 'imagecreatefromxpm', 'imagecreatefromstring', 'imagecreatefromgd', 'imagecreatefromgd2', 'imagecreatefromgd2part', 'imagejpeg', 'imagegif', 'imagepng', 'imagewbmp');
4049
		$ParameterNames      = array('src', 'new', 'w', 'h', 'f', 'q', 'sx', 'sy', 'sw', 'sh', 'far', 'bg', 'bc', 'file', 'goto', 'err', 'xto', 'ra', 'ar', 'aoe', 'iar', 'maxb');
4050
		$ConfigVariableNames = array('document_root', 'temp_directory', 'output_format', 'output_maxwidth', 'output_maxheight', 'error_message_image_default', 'error_bgcolor', 'error_textcolor', 'error_fontsize', 'error_die_on_error', 'error_silent_die_on_error', 'error_die_on_source_failure', 'nohotlink_enabled', 'nohotlink_valid_domains', 'nohotlink_erase_image', 'nohotlink_text_message', 'nooffsitelink_enabled', 'nooffsitelink_valid_domains', 'nooffsitelink_require_refer', 'nooffsitelink_erase_image', 'nooffsitelink_text_message', 'high_security_enabled', 'allow_src_above_docroot', 'allow_src_above_phpthumb', 'max_source_pixels', 'use_exif_thumbnail_for_speed', 'border_hexcolor', 'background_hexcolor', 'ttf_directory', 'disable_pathinfo_parsing', 'disable_imagecopyresampled');
4051
		$OtherVariableNames  = array('phpThumbDebug', 'thumbnailQuality', 'thumbnailFormat', 'gdimg_output', 'gdimg_source', 'sourceFilename', 'source_width', 'source_height', 'thumbnailCropX', 'thumbnailCropY', 'thumbnailCropW', 'thumbnailCropH', 'exif_thumbnail_width', 'exif_thumbnail_height', 'exif_thumbnail_type', 'thumbnail_width', 'thumbnail_height', 'thumbnail_image_width', 'thumbnail_image_height');
4052
4053
		$DebugOutput = array();
4054
		$DebugOutput[] = 'phpThumb() version          = '.$this->phpthumb_version;
4055
		$DebugOutput[] = 'phpversion()                = '.@PHP_VERSION;
4056
		$DebugOutput[] = 'PHP_OS                      = '.PHP_OS;
4057
		$DebugOutput[] = '$_SERVER[SERVER_SOFTWARE]   = '.@$_SERVER['SERVER_SOFTWARE'];
4058
		$DebugOutput[] = '__FILE__                    = '.__FILE__;
4059
		$DebugOutput[] = 'realpath(.)                 = '.@realpath('.');
4060
		$DebugOutput[] = '$_SERVER[PHP_SELF]          = '.@$_SERVER['PHP_SELF'];
4061
		$DebugOutput[] = '$_SERVER[HOST_NAME]         = '.@$_SERVER['HOST_NAME'];
4062
		$DebugOutput[] = '$_SERVER[HTTP_REFERER]      = '.@$_SERVER['HTTP_REFERER'];
4063
		$DebugOutput[] = '$_SERVER[QUERY_STRING]      = '.@$_SERVER['QUERY_STRING'];
4064
		$DebugOutput[] = '$_SERVER[PATH_INFO]         = '.@$_SERVER['PATH_INFO'];
4065
		$DebugOutput[] = '$_SERVER[DOCUMENT_ROOT]     = '.@$_SERVER['DOCUMENT_ROOT'];
4066
		$DebugOutput[] = 'getenv(DOCUMENT_ROOT)       = '.@getenv('DOCUMENT_ROOT');
4067
		$DebugOutput[] = '';
4068
4069
		$DebugOutput[] = 'get_magic_quotes_gpc()         = '.$this->phpThumbDebugVarDump(@get_magic_quotes_gpc());
4070
		$DebugOutput[] = 'get_magic_quotes_runtime()     = '.$this->phpThumbDebugVarDump(@get_magic_quotes_runtime());
4071
		$DebugOutput[] = 'error_reporting()              = '.$this->phpThumbDebugVarDump(error_reporting());
4072
		$DebugOutput[] = 'ini_get(error_reporting)       = '.$this->phpThumbDebugVarDump(@ini_get('error_reporting'));
4073
		$DebugOutput[] = 'ini_get(display_errors)        = '.$this->phpThumbDebugVarDump(@ini_get('display_errors'));
4074
		$DebugOutput[] = 'ini_get(allow_url_fopen)       = '.$this->phpThumbDebugVarDump(@ini_get('allow_url_fopen'));
4075
		$DebugOutput[] = 'ini_get(disable_functions)     = '.$this->phpThumbDebugVarDump(@ini_get('disable_functions'));
4076
		$DebugOutput[] = 'get_cfg_var(disable_functions) = '.$this->phpThumbDebugVarDump(@get_cfg_var('disable_functions'));
4077
		$DebugOutput[] = 'ini_get(safe_mode)             = '.$this->phpThumbDebugVarDump(@ini_get('safe_mode'));
4078
		$DebugOutput[] = 'ini_get(open_basedir)          = '.$this->phpThumbDebugVarDump(@ini_get('open_basedir'));
4079
		$DebugOutput[] = 'ini_get(max_execution_time)    = '.$this->phpThumbDebugVarDump(@ini_get('max_execution_time'));
4080
		$DebugOutput[] = 'ini_get(memory_limit)          = '.$this->phpThumbDebugVarDump(@ini_get('memory_limit'));
4081
		$DebugOutput[] = 'get_cfg_var(memory_limit)      = '.$this->phpThumbDebugVarDump(@get_cfg_var('memory_limit'));
4082
		$DebugOutput[] = 'memory_get_usage()             = '.(function_exists('memory_get_usage') ? $this->phpThumbDebugVarDump(@memory_get_usage()) : 'n/a');
4083
		$DebugOutput[] = '';
4084
4085
		$DebugOutput[] = '$this->config_prefer_imagemagick            = '.$this->phpThumbDebugVarDump($this->config_prefer_imagemagick);
4086
		$DebugOutput[] = '$this->config_imagemagick_path              = '.$this->phpThumbDebugVarDump($this->config_imagemagick_path);
4087
		$DebugOutput[] = '$this->ImageMagickWhichConvert()            = '.$this->ImageMagickWhichConvert();
4088
		$IMpathUsed = ($this->config_imagemagick_path ? $this->config_imagemagick_path : $this->ImageMagickWhichConvert());
4089
		$DebugOutput[] = '[actual ImageMagick path used]              = '.$this->phpThumbDebugVarDump($IMpathUsed);
4090
		$DebugOutput[] = 'file_exists([actual ImageMagick path used]) = '.$this->phpThumbDebugVarDump(@file_exists($IMpathUsed));
4091
		$DebugOutput[] = 'ImageMagickVersion(false)                   = '.$this->ImageMagickVersion(false);
4092
		$DebugOutput[] = 'ImageMagickVersion(true)                    = '.$this->ImageMagickVersion(true);
4093
		$DebugOutput[] = '';
4094
4095
		$DebugOutput[] = '$this->config_cache_directory               = '.$this->phpThumbDebugVarDump($this->config_cache_directory);
4096
		$DebugOutput[] = '$this->config_cache_directory_depth         = '.$this->phpThumbDebugVarDump($this->config_cache_directory_depth);
4097
		$DebugOutput[] = '$this->config_cache_disable_warning         = '.$this->phpThumbDebugVarDump($this->config_cache_disable_warning);
4098
		$DebugOutput[] = '$this->config_cache_maxage                  = '.$this->phpThumbDebugVarDump($this->config_cache_maxage);
4099
		$DebugOutput[] = '$this->config_cache_maxsize                 = '.$this->phpThumbDebugVarDump($this->config_cache_maxsize);
4100
		$DebugOutput[] = '$this->config_cache_maxfiles                = '.$this->phpThumbDebugVarDump($this->config_cache_maxfiles);
4101
		$DebugOutput[] = '$this->config_cache_force_passthru          = '.$this->phpThumbDebugVarDump($this->config_cache_force_passthru);
4102
		$DebugOutput[] = '$this->cache_filename                       = '.$this->phpThumbDebugVarDump($this->cache_filename);
4103
		$DebugOutput[] = 'is_readable($this->config_cache_directory)  = '.$this->phpThumbDebugVarDump(@is_readable($this->config_cache_directory));
4104
		$DebugOutput[] = 'is_writable($this->config_cache_directory)  = '.$this->phpThumbDebugVarDump(@is_writable($this->config_cache_directory));
4105
		$DebugOutput[] = 'is_readable($this->cache_filename)          = '.$this->phpThumbDebugVarDump(@is_readable($this->cache_filename));
4106
		$DebugOutput[] = 'is_writable($this->cache_filename)          = '.(@file_exists($this->cache_filename) ? $this->phpThumbDebugVarDump(@is_writable($this->cache_filename)) : 'n/a');
4107
		$DebugOutput[] = '';
4108
4109
		foreach ($ConfigVariableNames as $varname) {
4110
			$varname = 'config_'.$varname;
4111
			$value = $this->$varname;
4112
			$DebugOutput[] = '$this->'.str_pad($varname, 37, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
4113
		}
4114
		$DebugOutput[] = '';
4115
		foreach ($OtherVariableNames as $varname) {
4116
			$value = $this->$varname;
4117
			$DebugOutput[] = '$this->'.str_pad($varname, 27, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
4118
		}
4119
		$DebugOutput[] = 'strlen($this->rawImageData)        = '.strlen(@$this->rawImageData);
4120
		$DebugOutput[] = 'strlen($this->exif_thumbnail_data) = '.strlen(@$this->exif_thumbnail_data);
4121
		$DebugOutput[] = '';
4122
4123
		foreach ($ParameterNames as $varname) {
4124
			$value = $this->$varname;
4125
			$DebugOutput[] = '$this->'.str_pad($varname, 4, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
4126
		}
4127
		$DebugOutput[] = '';
4128
4129
		foreach ($FunctionsExistance as $functionname) {
4130
			$DebugOutput[] = 'builtin_function_exists('.$functionname.')'.str_repeat(' ', 23 - strlen($functionname)).' = '.$this->phpThumbDebugVarDump(phpthumb_functions::builtin_function_exists($functionname));
4131
		}
4132
		$DebugOutput[] = '';
4133
4134
		$gd_info = gd_info();
4135
		foreach ($gd_info as $key => $value) {
4136
			$DebugOutput[] = 'gd_info.'.str_pad($key, 34, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
4137
		}
4138
		$DebugOutput[] = '';
4139
4140
		$exif_info = phpthumb_functions::exif_info();
4141
		foreach ($exif_info as $key => $value) {
4142
			$DebugOutput[] = 'exif_info.'.str_pad($key, 26, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
4143
		}
4144
		$DebugOutput[] = '';
4145
4146
		if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray(dirname(@$_SERVER['PHP_SELF']))) {
4147
			foreach ($ApacheLookupURIarray as $key => $value) {
4148
				$DebugOutput[] = 'ApacheLookupURIarray.'.str_pad($key, 15, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value);
4149
			}
4150
		} else {
4151
				$DebugOutput[] = 'ApacheLookupURIarray() -- FAILED';
4152
		}
4153
		$DebugOutput[] = '';
4154
4155
		if (isset($_GET) && is_array($_GET)) {
4156
			foreach ($_GET as $key => $value) {
4157
				$DebugOutput[] = '$_GET['.$key.']'.str_repeat(' ', 30 - strlen($key)).'= '.$this->phpThumbDebugVarDump($value);
4158
			}
4159
		}
4160
		if (isset($_POST) && is_array($_POST)) {
4161
			foreach ($_POST as $key => $value) {
4162
				$DebugOutput[] = '$_POST['.$key.']'.str_repeat(' ', 29 - strlen($key)).'= '.$this->phpThumbDebugVarDump($value);
4163
			}
4164
		}
4165
		$DebugOutput[] = '';
4166
4167
		$DebugOutput[] = '$this->debugmessages:';
4168
		foreach ($this->debugmessages as $errorstring) {
4169
			$DebugOutput[] = '  * '.$errorstring;
4170
		}
4171
		$DebugOutput[] = '';
4172
4173
		$DebugOutput[] = '$this->debugtiming:';
4174
		foreach ($this->debugtiming as $timestamp => $timingstring) {
4175
			$DebugOutput[] = '  * '.$timestamp.' '.$timingstring;
4176
		}
4177
		$DebugOutput[] = '  * Total processing time: '.number_format(max(array_keys($this->debugtiming)) - min(array_keys($this->debugtiming)), 6);
4178
4179
		$this->f = (isset($_GET['f']) ? $_GET['f'] : $this->f); // debug modes 0-2 don't recognize text mode otherwise
4180
		return $this->ErrorImage(implode("\n", $DebugOutput), 700, 500, true);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->ErrorImage(implod...utput), 700, 500, true) targeting phpthumb::ErrorImage() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
4181
	}
4182
4183
	public function FatalError($text) {
4184
		if (null === $this->fatalerror) {
4185
			$this->fatalerror = $text;
4186
		}
4187
		return true;
4188
	}
4189
4190
	public function ErrorImage($text, $width=0, $height=0, $forcedisplay=false) {
4191
		$width  = ($width  ? $width  : $this->config_error_image_width);
4192
		$height = ($height ? $height : $this->config_error_image_height);
4193
4194
		$text = 'phpThumb() v'.$this->phpthumb_version."\n".'http://phpthumb.sourceforge.net'."\n\n".($this->config_disable_debug ? 'Error messages disabled.'."\n\n".'edit phpThumb.config.php and (temporarily) set'."\n".'$PHPTHUMB_CONFIG[\'disable_debug\'] = false;'."\n".'to view the details of this error' : $text);
4195
4196
		$this->FatalError($text);
4197
		$this->DebugMessage($text, __FILE__, __LINE__);
4198
		$this->purgeTempFiles();
4199
		if ($this->config_error_silent_die_on_error) {
4200
			exit;
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...
4201
		}
4202
		if ($this->phpThumbDebug && !$forcedisplay) {
4203
			return false;
4204
		}
4205
		if (!$this->config_error_die_on_error && !$forcedisplay) {
4206
			return false;
4207
		}
4208
		if ($this->err || $this->config_error_message_image_default) {
4209
			// Show generic custom error image instead of error message
4210
			// for use on production sites where you don't want debug messages
4211
			if (($this->err == 'showerror') || $this->phpThumbDebug) {
4212
				// fall through and actually show error message even if default error image is set
4213
			} else {
4214
				header('Location: '.($this->err ? $this->err : $this->config_error_message_image_default));
4215
				exit;
4216
			}
4217
		}
4218
		$this->setOutputFormat();
4219
		if (!$this->thumbnailFormat || !$this->config_disable_debug || (phpthumb_functions::gd_version() < 1)) {
4220
			$this->thumbnailFormat = 'text';
4221
		}
4222
		if (@$this->thumbnailFormat == 'text') {
4223
			// bypass all GD functions and output text error message
4224
			if (!headers_sent()) {
4225
				header('Content-type: text/plain');
4226
				echo $text;
4227
			} else {
4228
				echo '<pre>'.htmlspecialchars($text).'</pre>';
4229
			}
4230
			exit;
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...
4231
		}
4232
4233
		$FontWidth  = imagefontwidth($this->config_error_fontsize);
4234
		$FontHeight = imagefontheight($this->config_error_fontsize);
4235
4236
		$LinesOfText = explode("\n", @wordwrap($text, floor($width / $FontWidth), "\n", true));
0 ignored issues
show
Bug introduced by
floor($width / $FontWidth) of type double is incompatible with the type integer expected by parameter $width of wordwrap(). ( Ignorable by Annotation )

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

4236
		$LinesOfText = explode("\n", @wordwrap($text, /** @scrutinizer ignore-type */ floor($width / $FontWidth), "\n", true));
Loading history...
4237
		$height = max($height, count($LinesOfText) * $FontHeight);
4238
4239
		$headers_file = '';
4240
		$headers_line = '';
4241
		if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '4.3.0', '>=') && headers_sent($headers_file, $headers_line)) {
0 ignored issues
show
Bug introduced by
$headers_line of type string is incompatible with the type integer expected by parameter $line of headers_sent(). ( Ignorable by Annotation )

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

4241
		if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '4.3.0', '>=') && headers_sent($headers_file, /** @scrutinizer ignore-type */ $headers_line)) {
Loading history...
4242
4243
			echo "\n".'**Headers already sent in file "'.$headers_file.'" on line "'.$headers_line.'", dumping error message as text:**<br><pre>'."\n\n".$text."\n".'</pre>';
4244
4245
		} elseif (headers_sent()) {
4246
4247
			echo "\n".'**Headers already sent, dumping error message as text:**<br><pre>'."\n\n".$text."\n".'</pre>';
4248
4249
		} elseif ($gdimg_error = imagecreate($width, $height)) {
4250
4251
			$background_color = phpthumb_functions::ImageHexColorAllocate($gdimg_error, $this->config_error_bgcolor,   true);
4252
			$text_color       = phpthumb_functions::ImageHexColorAllocate($gdimg_error, $this->config_error_textcolor, true);
4253
			imagefilledrectangle($gdimg_error, 0, 0, $width, $height, $background_color);
4254
			$lineYoffset = 0;
4255
			foreach ($LinesOfText as $line) {
4256
				imagestring($gdimg_error, $this->config_error_fontsize, 2, $lineYoffset, $line, $text_color);
4257
				$lineYoffset += $FontHeight;
4258
			}
4259
			if (function_exists('imagetypes')) {
4260
				$imagetypes = imagetypes();
4261
				if ($imagetypes & IMG_PNG) {
4262
					header('Content-Type: image/png');
4263
					imagepng($gdimg_error);
4264
				} elseif ($imagetypes & IMG_GIF) {
4265
					header('Content-Type: image/gif');
4266
					imagegif($gdimg_error);
4267
				} elseif ($imagetypes & IMG_JPG) {
4268
					header('Content-Type: image/jpeg');
4269
					imagejpeg($gdimg_error);
4270
				} elseif ($imagetypes & IMG_WBMP) {
4271
					header('Content-Type: image/vnd.wap.wbmp');
4272
					imagewbmp($gdimg_error);
4273
				}
4274
			}
4275
			imagedestroy($gdimg_error);
4276
4277
		}
4278
		if (!headers_sent()) {
4279
			echo "\n".'**Failed to send graphical error image, dumping error message as text:**<br>'."\n\n".$text;
4280
		}
4281
		exit;
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...
4282
	}
4283
4284
	public function ImageCreateFromStringReplacement(&$RawImageData, $DieOnErrors=false) {
4285
		// there are serious bugs in the non-bundled versions of GD which may cause
4286
		// PHP to segfault when calling imagecreatefromstring() - avoid if at all possible
4287
		// when not using a bundled version of GD2
4288
		if (!phpthumb_functions::gd_version()) {
4289
			if ($DieOnErrors) {
4290
				if (!headers_sent()) {
4291
					// base64-encoded error image in GIF format
4292
					$ERROR_NOGD = 'R0lGODlhIAAgALMAAAAAABQUFCQkJDY2NkZGRldXV2ZmZnJycoaGhpSUlKWlpbe3t8XFxdXV1eTk5P7+/iwAAAAAIAAgAAAE/vDJSau9WILtTAACUinDNijZtAHfCojS4W5H+qxD8xibIDE9h0OwWaRWDIljJSkUJYsN4bihMB8th3IToAKs1VtYM75cyV8sZ8vygtOE5yMKmGbO4jRdICQCjHdlZzwzNW4qZSQmKDaNjhUMBX4BBAlmMywFSRWEmAI6b5gAlhNxokGhooAIK5o/pi9vEw4Lfj4OLTAUpj6IabMtCwlSFw0DCKBoFqwAB04AjI54PyZ+yY3TD0ss2YcVmN/gvpcu4TOyFivWqYJlbAHPpOntvxNAACcmGHjZzAZqzSzcq5fNjxFmAFw9iFRunD1epU6tsIPmFCAJnWYE0FURk7wJDA0MTKpEzoWAAskiAAA7';
4293
					header('Content-Type: image/gif');
4294
					echo base64_decode($ERROR_NOGD);
4295
				} else {
4296
					echo '*** ERROR: No PHP-GD support available ***';
4297
				}
4298
				exit;
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...
4299
			} else {
4300
				$this->DebugMessage('ImageCreateFromStringReplacement() failed: gd_version says "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__);
4301
				return false;
4302
			}
4303
		}
4304
		if (phpthumb_functions::gd_is_bundled()) {
4305
			$this->DebugMessage('ImageCreateFromStringReplacement() calling built-in imagecreatefromstring()', __FILE__, __LINE__);
4306
			return @imagecreatefromstring($RawImageData);
4307
		}
4308
		if ($this->issafemode) {
4309
			$this->DebugMessage('ImageCreateFromStringReplacement() failed: cannot create temp file in SAFE_MODE', __FILE__, __LINE__);
4310
			return false;
4311
		}
4312
4313
		switch (substr($RawImageData, 0, 3)) {
4314
			case 'GIF':
4315
				$ICFSreplacementFunctionName = 'imagecreatefromgif';
4316
				break;
4317
			case "\xFF\xD8\xFF":
4318
				$ICFSreplacementFunctionName = 'imagecreatefromjpeg';
4319
				break;
4320
			case "\x89".'PN':
4321
				$ICFSreplacementFunctionName = 'imagecreatefrompng';
4322
				break;
4323
			default:
4324
				$this->DebugMessage('ImageCreateFromStringReplacement() failed: unknown fileformat signature "'.phpthumb_functions::HexCharDisplay(substr($RawImageData, 0, 3)).'"', __FILE__, __LINE__);
4325
				return false;
4326
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
4327
		}
4328
		$ErrorMessage = '';
4329
		if ($tempnam = $this->phpThumb_tempnam()) {
4330
			if ($fp_tempnam = @fopen($tempnam, 'wb')) {
4331
				fwrite($fp_tempnam, $RawImageData);
4332
				fclose($fp_tempnam);
4333
				@chmod($tempnam, $this->getParameter('config_file_create_mask'));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

4333
				/** @scrutinizer ignore-unhandled */ @chmod($tempnam, $this->getParameter('config_file_create_mask'));

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...
4334
				if (($ICFSreplacementFunctionName == 'imagecreatefromgif') && !function_exists($ICFSreplacementFunctionName)) {
4335
4336
					// Need to create from GIF file, but imagecreatefromgif does not exist
4337
					ob_start();
4338
					if (!@include_once __DIR__ .'/phpthumb.gif.php' ) {
4339
						$ErrorMessage = 'Failed to include required file "'. __DIR__ .'/phpthumb.gif.php" in '.__FILE__.' on line '.__LINE__;
4340
						$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
4341
					}
4342
					ob_end_clean();
4343
					// gif_loadFileToGDimageResource() cannot read from raw data, write to file first
4344
					if ($tempfilename = $this->phpThumb_tempnam()) {
4345
						if ($fp_tempfile = @fopen($tempfilename, 'wb')) {
4346
							fwrite($fp_tempfile, $RawImageData);
4347
							fclose($fp_tempfile);
4348
							$gdimg_source = gif_loadFileToGDimageResource($tempfilename);
4349
							$this->DebugMessage('gif_loadFileToGDimageResource('.$tempfilename.') completed', __FILE__, __LINE__);
4350
							$this->DebugMessage('deleting "'.$tempfilename.'"', __FILE__, __LINE__);
4351
							unlink($tempfilename);
4352
							return $gdimg_source;
4353
						} else {
4354
							$ErrorMessage = 'Failed to open tempfile in '.__FILE__.' on line '.__LINE__;
4355
							$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
4356
						}
4357
					} else {
4358
						$ErrorMessage = 'Failed to open generate tempfile name in '.__FILE__.' on line '.__LINE__;
4359
						$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
4360
					}
4361
4362
				} elseif (function_exists($ICFSreplacementFunctionName) && ($gdimg_source = @$ICFSreplacementFunctionName($tempnam))) {
4363
4364
					// great
4365
					$this->DebugMessage($ICFSreplacementFunctionName.'('.$tempnam.') succeeded', __FILE__, __LINE__);
4366
					$this->DebugMessage('deleting "'.$tempnam.'"', __FILE__, __LINE__);
4367
					unlink($tempnam);
4368
					return $gdimg_source;
4369
4370
				} else {
4371
4372
					// GD functions not available, or failed to create image
4373
					$this->DebugMessage($ICFSreplacementFunctionName.'('.$tempnam.') '.(function_exists($ICFSreplacementFunctionName) ? 'failed' : 'does not exist'), __FILE__, __LINE__);
4374
					if (isset($_GET['phpThumbDebug'])) {
4375
						$this->phpThumbDebug();
4376
					}
4377
4378
				}
4379
			} else {
4380
				$ErrorMessage = 'Failed to fopen('.$tempnam.', "wb") in '.__FILE__.' on line '.__LINE__."\n".'You may need to set $PHPTHUMB_CONFIG[temp_directory] in phpThumb.config.php';
4381
				if ($this->issafemode) {
4382
					$ErrorMessage = 'ImageCreateFromStringReplacement() failed in '.__FILE__.' on line '.__LINE__.': cannot create temp file in SAFE_MODE';
4383
				}
4384
				$this->DebugMessage($ErrorMessage, __FILE__, __LINE__);
4385
			}
4386
			$this->DebugMessage('deleting "'.$tempnam.'"', __FILE__, __LINE__);
4387
			@unlink($tempnam);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

4387
			/** @scrutinizer ignore-unhandled */ @unlink($tempnam);

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...
4388
		} else {
4389
			$ErrorMessage = 'Failed to generate phpThumb_tempnam() in '.__FILE__.' on line '.__LINE__."\n".'You may need to set $PHPTHUMB_CONFIG[temp_directory] in phpThumb.config.php';
4390
			if ($this->issafemode) {
4391
				$ErrorMessage = 'ImageCreateFromStringReplacement() failed in '.__FILE__.' on line '.__LINE__.': cannot create temp file in SAFE_MODE';
4392
			}
4393
		}
4394
		if ($DieOnErrors && $ErrorMessage) {
4395
			return $this->ErrorImage($ErrorMessage);
4396
		}
4397
		return false;
4398
	}
4399
4400
	public function ImageResizeFunction(&$dst_im, &$src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH) {
4401
		$this->DebugMessage('ImageResizeFunction($o, $s, '.$dstX.', '.$dstY.', '.$srcX.', '.$srcY.', '.$dstW.', '.$dstH.', '.$srcW.', '.$srcH.')', __FILE__, __LINE__);
4402
		if (($dstW == $srcW) && ($dstH == $srcH)) {
4403
			return imagecopy($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH);
4404
		}
4405
		if (phpthumb_functions::gd_version() >= 2.0) {
4406
			if ($this->config_disable_imagecopyresampled) {
4407
				return phpthumb_functions::ImageCopyResampleBicubic($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
4408
			}
4409
			return imagecopyresampled($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
4410
		}
4411
		return imagecopyresized($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
4412
	}
4413
4414
	public function InitializeTempDirSetting() {
4415
		$this->config_temp_directory = ($this->config_temp_directory ? $this->config_temp_directory : $this->realPathSafe((function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : ''))); // sys_get_temp_dir added in PHP v5.2.1
4416
		$this->config_temp_directory = ($this->config_temp_directory ? $this->config_temp_directory : $this->realPathSafe(ini_get('upload_tmp_dir')));
4417
		$this->config_temp_directory = ($this->config_temp_directory ? $this->config_temp_directory : $this->realPathSafe(getenv('TMPDIR')));
4418
		$this->config_temp_directory = ($this->config_temp_directory ? $this->config_temp_directory : $this->realPathSafe(getenv('TMP')));
4419
		return true;
4420
	}
4421
4422
	public function phpThumb_tempnam() {
4423
		$this->InitializeTempDirSetting();
4424
		$tempnam = $this->realPathSafe(tempnam($this->config_temp_directory, 'pThumb'));
4425
		$this->tempFilesToDelete[$tempnam] = $tempnam;
4426
		$this->DebugMessage('phpThumb_tempnam() returning "'.$tempnam.'"', __FILE__, __LINE__);
4427
		return $tempnam;
4428
	}
4429
4430
	public function DebugMessage($message, $file='', $line='') {
4431
		$this->debugmessages[] = $message.($file ? ' in file "'.(basename($file) ? basename($file) : $file).'"' : '').($line ? ' on line '.$line : '');
4432
		return true;
4433
	}
4434
4435
	public function DebugTimingMessage($message, $file='', $line='', $timestamp=0) {
4436
		if (!$timestamp) {
4437
			$timestamp = array_sum(explode(' ', microtime()));
4438
		}
4439
		$this->debugtiming[number_format($timestamp, 6, '.', '')] = ': '.$message.($file ? ' in file "'.(basename($file) ? basename($file) : $file).'"' : '').($line ? ' on line '.$line : '');
4440
		return true;
4441
	}
4442
4443
}
4444