1<?php
2//////////////////////////////////////////////////////////////
3//   phpThumb() by James Heinrich <info@silisoftware.com>   //
4//        available at http://phpthumb.sourceforge.net      //
5//         and/or https://github.com/JamesHeinrich/phpThumb //
6//////////////////////////////////////////////////////////////
7///                                                         //
8// phpthumb.functions.php - general support functions       //
9//                                                         ///
10//////////////////////////////////////////////////////////////
11
12class phpthumb_functions {
13
14	static function user_function_exists($functionname) {
15		if (function_exists('get_defined_functions')) {
16			static $get_defined_functions = array();
17			if (empty($get_defined_functions)) {
18				$get_defined_functions = get_defined_functions();
19			}
20			return in_array(strtolower($functionname), $get_defined_functions['user']);
21		}
22		return function_exists($functionname);
23	}
24
25
26	static function builtin_function_exists($functionname) {
27		if (function_exists('get_defined_functions')) {
28			static $get_defined_functions = array();
29			if (empty($get_defined_functions)) {
30				$get_defined_functions = get_defined_functions();
31			}
32			return in_array(strtolower($functionname), $get_defined_functions['internal']);
33		}
34		return function_exists($functionname);
35	}
36
37
38	static function version_compare_replacement_sub($version1, $version2, $operator='') {
39		// If you specify the third optional operator argument, you can test for a particular relationship.
40		// The possible operators are: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne respectively.
41		// Using this argument, the function will return 1 if the relationship is the one specified by the operator, 0 otherwise.
42
43		// If a part contains special version strings these are handled in the following order:
44		// (any string not found in this list) < (dev) < (alpha = a) < (beta = b) < (RC = rc) < (#) < (pl = p)
45		static $versiontype_lookup = array();
46		if (empty($versiontype_lookup)) {
47			$versiontype_lookup['dev']   = 10001;
48			$versiontype_lookup['a']     = 10002;
49			$versiontype_lookup['alpha'] = 10002;
50			$versiontype_lookup['b']     = 10003;
51			$versiontype_lookup['beta']  = 10003;
52			$versiontype_lookup['RC']    = 10004;
53			$versiontype_lookup['rc']    = 10004;
54			$versiontype_lookup['#']     = 10005;
55			$versiontype_lookup['pl']    = 10006;
56			$versiontype_lookup['p']     = 10006;
57		}
58		$version1 = (isset($versiontype_lookup[$version1]) ? $versiontype_lookup[$version1] : $version1);
59		$version2 = (isset($versiontype_lookup[$version2]) ? $versiontype_lookup[$version2] : $version2);
60
61		switch ($operator) {
62			case '<':
63			case 'lt':
64				return intval($version1 < $version2);
65				break;
66			case '<=':
67			case 'le':
68				return intval($version1 <= $version2);
69				break;
70			case '>':
71			case 'gt':
72				return intval($version1 > $version2);
73				break;
74			case '>=':
75			case 'ge':
76				return intval($version1 >= $version2);
77				break;
78			case '==':
79			case '=':
80			case 'eq':
81				return intval($version1 == $version2);
82				break;
83			case '!=':
84			case '<>':
85			case 'ne':
86				return intval($version1 != $version2);
87				break;
88		}
89		if ($version1 == $version2) {
90			return 0;
91		} elseif ($version1 < $version2) {
92			return -1;
93		}
94		return 1;
95	}
96
97
98	static function version_compare_replacement($version1, $version2, $operator='') {
99		if (function_exists('version_compare')) {
100			// built into PHP v4.1.0+
101			return version_compare($version1, $version2, $operator);
102		}
103
104		// The function first replaces _, - and + with a dot . in the version strings
105		$version1 = strtr($version1, '_-+', '...');
106		$version2 = strtr($version2, '_-+', '...');
107
108		// and also inserts dots . before and after any non number so that for example '4.3.2RC1' becomes '4.3.2.RC.1'.
109		// Then it splits the results like if you were using explode('.',$ver). Then it compares the parts starting from left to right.
110		$version1 = preg_replace('#([0-9]+)([A-Z]+)([0-9]+)#i', "$1.$2.$3", $version1);
111		$version2 = preg_replace('#([0-9]+)([A-Z]+)([0-9]+)#i', "$1.$2.$3", $version2);
112
113		$parts1 = explode('.', $version1);
114		$parts2 = explode('.', $version1);
115		$parts_count = max(count($parts1), count($parts2));
116		for ($i = 0; $i < $parts_count; $i++) {
117			$comparison = phpthumb_functions::version_compare_replacement_sub($version1, $version2, $operator);
118			if ($comparison != 0) {
119				return $comparison;
120			}
121		}
122		return 0;
123	}
124
125	static function escapeshellarg_replacement($arg) {
126		if (function_exists('escapeshellarg') && !phpthumb_functions::FunctionIsDisabled('escapeshellarg')) {
127			return escapeshellarg($arg);
128		}
129		return '\''.str_replace('\'', '\\\'', $arg).'\'';
130	}
131
132	static function phpinfo_array() {
133		static $phpinfo_array = array();
134		if (empty($phpinfo_array)) {
135			ob_start();
136			phpinfo();
137			$phpinfo = ob_get_contents();
138			ob_end_clean();
139			$phpinfo_array = explode("\n", $phpinfo);
140		}
141		return $phpinfo_array;
142	}
143
144
145	static function exif_info() {
146		static $exif_info = array();
147		if (empty($exif_info)) {
148			// based on code by johnschaefer at gmx dot de
149			// from PHP help on gd_info()
150			$exif_info = array(
151				'EXIF Support'           => '',
152				'EXIF Version'           => '',
153				'Supported EXIF Version' => '',
154				'Supported filetypes'    => ''
155			);
156			$phpinfo_array = phpthumb_functions::phpinfo_array();
157			foreach ($phpinfo_array as $line) {
158				$line = trim(strip_tags($line));
159				foreach ($exif_info as $key => $value) {
160					if (strpos($line, $key) === 0) {
161						$newvalue = trim(str_replace($key, '', $line));
162						$exif_info[$key] = $newvalue;
163					}
164				}
165			}
166		}
167		return $exif_info;
168	}
169
170
171	static function ImageTypeToMIMEtype($imagetype) {
172		if (function_exists('image_type_to_mime_type') && ($imagetype >= 1) && ($imagetype <= 16)) {
173			// PHP v4.3.0+
174			return image_type_to_mime_type($imagetype);
175		}
176		static $image_type_to_mime_type = array(
177			1  => 'image/gif',                     // IMAGETYPE_GIF
178			2  => 'image/jpeg',                    // IMAGETYPE_JPEG
179			3  => 'image/png',                     // IMAGETYPE_PNG
180			4  => 'application/x-shockwave-flash', // IMAGETYPE_SWF
181			5  => 'image/psd',                     // IMAGETYPE_PSD
182			6  => 'image/bmp',                     // IMAGETYPE_BMP
183			7  => 'image/tiff',                    // IMAGETYPE_TIFF_II (intel byte order)
184			8  => 'image/tiff',                    // IMAGETYPE_TIFF_MM (motorola byte order)
185			9  => 'application/octet-stream',      // IMAGETYPE_JPC
186			10 => 'image/jp2',                     // IMAGETYPE_JP2
187			11 => 'application/octet-stream',      // IMAGETYPE_JPX
188			12 => 'application/octet-stream',      // IMAGETYPE_JB2
189			13 => 'application/x-shockwave-flash', // IMAGETYPE_SWC
190			14 => 'image/iff',                     // IMAGETYPE_IFF
191			15 => 'image/vnd.wap.wbmp',            // IMAGETYPE_WBMP
192			16 => 'image/xbm',                     // IMAGETYPE_XBM
193
194			'gif'  => 'image/gif',                 // IMAGETYPE_GIF
195			'jpg'  => 'image/jpeg',                // IMAGETYPE_JPEG
196			'jpeg' => 'image/jpeg',                // IMAGETYPE_JPEG
197			'png'  => 'image/png',                 // IMAGETYPE_PNG
198			'bmp'  => 'image/bmp',                 // IMAGETYPE_BMP
199			'ico'  => 'image/x-icon',
200		);
201
202		return (isset($image_type_to_mime_type[$imagetype]) ? $image_type_to_mime_type[$imagetype] : false);
203	}
204
205
206	static function TranslateWHbyAngle($width, $height, $angle) {
207		if (($angle % 180) == 0) {
208			return array($width, $height);
209		}
210		$newwidth  = (abs(sin(deg2rad($angle))) * $height) + (abs(cos(deg2rad($angle))) * $width);
211		$newheight = (abs(sin(deg2rad($angle))) * $width)  + (abs(cos(deg2rad($angle))) * $height);
212		return array($newwidth, $newheight);
213	}
214
215	static function HexCharDisplay($string) {
216		$len = strlen($string);
217		$output = '';
218		for ($i = 0; $i < $len; $i++) {
219			$output .= ' 0x'.str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
220		}
221		return $output;
222	}
223
224
225	static function IsHexColor($HexColorString) {
226		return preg_match('#^[0-9A-F]{6}$#i', $HexColorString);
227	}
228
229
230	static function ImageColorAllocateAlphaSafe(&$gdimg_hexcolorallocate, $R, $G, $B, $alpha=false) {
231		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.2', '>=') && ($alpha !== false)) {
232			return imagecolorallocatealpha($gdimg_hexcolorallocate, $R, $G, $B, intval($alpha));
233		} else {
234			return imagecolorallocate($gdimg_hexcolorallocate, $R, $G, $B);
235		}
236	}
237
238	static function ImageHexColorAllocate(&$gdimg_hexcolorallocate, $HexColorString, $dieOnInvalid=false, $alpha=false) {
239		if (!is_resource($gdimg_hexcolorallocate)) {
240			die('$gdimg_hexcolorallocate is not a GD resource in ImageHexColorAllocate()');
241		}
242		if (phpthumb_functions::IsHexColor($HexColorString)) {
243			$R = hexdec(substr($HexColorString, 0, 2));
244			$G = hexdec(substr($HexColorString, 2, 2));
245			$B = hexdec(substr($HexColorString, 4, 2));
246			return phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_hexcolorallocate, $R, $G, $B, $alpha);
247		}
248		if ($dieOnInvalid) {
249			die('Invalid hex color string: "'.$HexColorString.'"');
250		}
251		return imagecolorallocate($gdimg_hexcolorallocate, 0x00, 0x00, 0x00);
252	}
253
254
255	static function HexColorXOR($hexcolor) {
256		return strtoupper(str_pad(dechex(~hexdec($hexcolor) & 0xFFFFFF), 6, '0', STR_PAD_LEFT));
257	}
258
259
260	static function GetPixelColor(&$img, $x, $y) {
261		if (!is_resource($img)) {
262			return false;
263		}
264		return @imagecolorsforindex($img, @imagecolorat($img, $x, $y));
265	}
266
267
268	static function PixelColorDifferencePercent($currentPixel, $targetPixel) {
269		$diff = 0;
270		foreach ($targetPixel as $channel => $currentvalue) {
271			$diff = max($diff, (max($currentPixel[$channel], $targetPixel[$channel]) - min($currentPixel[$channel], $targetPixel[$channel])) / 255);
272		}
273		return $diff * 100;
274	}
275
276	static function GrayscaleValue($r, $g, $b) {
277		return round(($r * 0.30) + ($g * 0.59) + ($b * 0.11));
278	}
279
280
281	static function GrayscalePixel($OriginalPixel) {
282		$gray = phpthumb_functions::GrayscaleValue($OriginalPixel['red'], $OriginalPixel['green'], $OriginalPixel['blue']);
283		return array('red'=>$gray, 'green'=>$gray, 'blue'=>$gray);
284	}
285
286
287	static function GrayscalePixelRGB($rgb) {
288		$r = ($rgb >> 16) & 0xFF;
289		$g = ($rgb >>  8) & 0xFF;
290		$b =  $rgb        & 0xFF;
291		return ($r * 0.299) + ($g * 0.587) + ($b * 0.114);
292	}
293
294
295	static function ScaleToFitInBox($width, $height, $maxwidth=null, $maxheight=null, $allow_enlarge=true, $allow_reduce=true) {
296		$maxwidth  = (is_null($maxwidth)  ? $width  : $maxwidth);
297		$maxheight = (is_null($maxheight) ? $height : $maxheight);
298		$scale_x = 1;
299		$scale_y = 1;
300		if (($width > $maxwidth) || ($width < $maxwidth)) {
301			$scale_x = ($maxwidth / $width);
302		}
303		if (($height > $maxheight) || ($height < $maxheight)) {
304			$scale_y = ($maxheight / $height);
305		}
306		$scale = min($scale_x, $scale_y);
307		if (!$allow_enlarge) {
308			$scale = min($scale, 1);
309		}
310		if (!$allow_reduce) {
311			$scale = max($scale, 1);
312		}
313		return $scale;
314	}
315
316	static function ImageCopyResampleBicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) {
317		// ron at korving dot demon dot nl
318		// http://www.php.net/imagecopyresampled
319
320		$scaleX = ($src_w - 1) / $dst_w;
321		$scaleY = ($src_h - 1) / $dst_h;
322
323		$scaleX2 = $scaleX / 2.0;
324		$scaleY2 = $scaleY / 2.0;
325
326		$isTrueColor = imageistruecolor($src_img);
327
328		for ($y = $src_y; $y < $src_y + $dst_h; $y++) {
329			$sY   = $y * $scaleY;
330			$siY  = (int) $sY;
331			$siY2 = (int) $sY + $scaleY2;
332
333			for ($x = $src_x; $x < $src_x + $dst_w; $x++) {
334				$sX   = $x * $scaleX;
335				$siX  = (int) $sX;
336				$siX2 = (int) $sX + $scaleX2;
337
338				if ($isTrueColor) {
339
340					$c1 = imagecolorat($src_img, $siX, $siY2);
341					$c2 = imagecolorat($src_img, $siX, $siY);
342					$c3 = imagecolorat($src_img, $siX2, $siY2);
343					$c4 = imagecolorat($src_img, $siX2, $siY);
344
345					$r = (( $c1             +  $c2             +  $c3             +  $c4            ) >> 2) & 0xFF0000;
346					$g = ((($c1 & 0x00FF00) + ($c2 & 0x00FF00) + ($c3 & 0x00FF00) + ($c4 & 0x00FF00)) >> 2) & 0x00FF00;
347					$b = ((($c1 & 0x0000FF) + ($c2 & 0x0000FF) + ($c3 & 0x0000FF) + ($c4 & 0x0000FF)) >> 2);
348
349				} else {
350
351					$c1 = imagecolorsforindex($src_img, imagecolorat($src_img, $siX, $siY2));
352					$c2 = imagecolorsforindex($src_img, imagecolorat($src_img, $siX, $siY));
353					$c3 = imagecolorsforindex($src_img, imagecolorat($src_img, $siX2, $siY2));
354					$c4 = imagecolorsforindex($src_img, imagecolorat($src_img, $siX2, $siY));
355
356					$r = ($c1['red']   + $c2['red']   + $c3['red']   + $c4['red'] )  << 14;
357					$g = ($c1['green'] + $c2['green'] + $c3['green'] + $c4['green']) <<  6;
358					$b = ($c1['blue']  + $c2['blue']  + $c3['blue']  + $c4['blue'] ) >>  2;
359
360				}
361				imagesetpixel($dst_img, $dst_x + $x - $src_x, $dst_y + $y - $src_y, $r+$g+$b);
362			}
363		}
364		return true;
365	}
366
367
368	static function ImageCreateFunction($x_size, $y_size) {
369		$ImageCreateFunction = 'imagecreate';
370		if (phpthumb_functions::gd_version() >= 2.0) {
371			$ImageCreateFunction = 'imagecreatetruecolor';
372		}
373		if (!function_exists($ImageCreateFunction)) {
374			return phpthumb::ErrorImage($ImageCreateFunction.'() does not exist - no GD support?');
375		}
376		if (($x_size <= 0) || ($y_size <= 0)) {
377			return phpthumb::ErrorImage('Invalid image dimensions: '.$ImageCreateFunction.'('.$x_size.', '.$y_size.')');
378		}
379		return $ImageCreateFunction(round($x_size), round($y_size));
380	}
381
382
383	static function ImageCopyRespectAlpha(&$dst_im, &$src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $opacity_pct=100) {
384		$opacipct = $opacity_pct / 100;
385		for ($x = $src_x; $x < $src_w; $x++) {
386			for ($y = $src_y; $y < $src_h; $y++) {
387				$RealPixel    = phpthumb_functions::GetPixelColor($dst_im, $dst_x + $x, $dst_y + $y);
388				$OverlayPixel = phpthumb_functions::GetPixelColor($src_im, $x, $y);
389				$alphapct = $OverlayPixel['alpha'] / 127;
390				$overlaypct = (1 - $alphapct) * $opacipct;
391
392				$newcolor = phpthumb_functions::ImageColorAllocateAlphaSafe(
393					$dst_im,
394					round($RealPixel['red']   * (1 - $overlaypct)) + ($OverlayPixel['red']   * $overlaypct),
395					round($RealPixel['green'] * (1 - $overlaypct)) + ($OverlayPixel['green'] * $overlaypct),
396					round($RealPixel['blue']  * (1 - $overlaypct)) + ($OverlayPixel['blue']  * $overlaypct),
397					//$RealPixel['alpha']);
398					0);
399
400				imagesetpixel($dst_im, $dst_x + $x, $dst_y + $y, $newcolor);
401			}
402		}
403		return true;
404	}
405
406
407	static function ProportionalResize($old_width, $old_height, $new_width=false, $new_height=false) {
408		$old_aspect_ratio = $old_width / $old_height;
409		if (($new_width === false) && ($new_height === false)) {
410			return false;
411		} elseif ($new_width === false) {
412			$new_width = $new_height * $old_aspect_ratio;
413		} elseif ($new_height === false) {
414			$new_height = $new_width / $old_aspect_ratio;
415		}
416		$new_aspect_ratio = $new_width / $new_height;
417		if ($new_aspect_ratio == $old_aspect_ratio) {
418			// great, done
419		} elseif ($new_aspect_ratio < $old_aspect_ratio) {
420			// limited by width
421			$new_height = $new_width / $old_aspect_ratio;
422		} elseif ($new_aspect_ratio > $old_aspect_ratio) {
423			// limited by height
424			$new_width = $new_height * $old_aspect_ratio;
425		}
426		return array(intval(round($new_width)), intval(round($new_height)));
427	}
428
429
430	static function FunctionIsDisabled($function) {
431		static $DisabledFunctions = null;
432		if (is_null($DisabledFunctions)) {
433			$disable_functions_local  = explode(',',     strtolower(@ini_get('disable_functions')));
434			$disable_functions_global = explode(',', strtolower(@get_cfg_var('disable_functions')));
435			foreach ($disable_functions_local as $key => $value) {
436				$DisabledFunctions[trim($value)] = 'local';
437			}
438			foreach ($disable_functions_global as $key => $value) {
439				$DisabledFunctions[trim($value)] = 'global';
440			}
441			if (@ini_get('safe_mode')) {
442				$DisabledFunctions['shell_exec']     = 'local';
443				$DisabledFunctions['set_time_limit'] = 'local';
444			}
445		}
446		return isset($DisabledFunctions[strtolower($function)]);
447	}
448
449
450	static function SafeExec($command) {
451		static $AllowedExecFunctions = array();
452		if (empty($AllowedExecFunctions)) {
453			$AllowedExecFunctions = array('shell_exec'=>true, 'passthru'=>true, 'system'=>true, 'exec'=>true);
454			foreach ($AllowedExecFunctions as $key => $value) {
455				$AllowedExecFunctions[$key] = !phpthumb_functions::FunctionIsDisabled($key);
456			}
457		}
458		$command .= ' 2>&1'; // force redirect stderr to stdout
459		foreach ($AllowedExecFunctions as $execfunction => $is_allowed) {
460			if (!$is_allowed) {
461				continue;
462			}
463			$returnvalue = false;
464			switch ($execfunction) {
465				case 'passthru':
466				case 'system':
467					ob_start();
468					$execfunction($command);
469					$returnvalue = ob_get_contents();
470					ob_end_clean();
471					break;
472
473				case 'exec':
474					$output = array();
475					$lastline = $execfunction($command, $output);
476					$returnvalue = implode("\n", $output);
477					break;
478
479				case 'shell_exec':
480					ob_start();
481					$returnvalue = $execfunction($command);
482					ob_end_clean();
483					break;
484			}
485			return $returnvalue;
486		}
487		return false;
488	}
489
490
491	static function ApacheLookupURIarray($filename) {
492		// apache_lookup_uri() only works when PHP is installed as an Apache module.
493		if (php_sapi_name() == 'apache') {
494			//$property_exists_exists = function_exists('property_exists');
495			$keys = array('status', 'the_request', 'status_line', 'method', 'content_type', 'handler', 'uri', 'filename', 'path_info', 'args', 'boundary', 'no_cache', 'no_local_copy', 'allowed', 'send_bodyct', 'bytes_sent', 'byterange', 'clength', 'unparsed_uri', 'mtime', 'request_time');
496			if ($apacheLookupURIobject = @apache_lookup_uri($filename)) {
497				$apacheLookupURIarray = array();
498				foreach ($keys as $key) {
499					$apacheLookupURIarray[$key] = @$apacheLookupURIobject->$key;
500				}
501				return $apacheLookupURIarray;
502			}
503		}
504		return false;
505	}
506
507
508	static function gd_is_bundled() {
509		static $isbundled = null;
510		if (is_null($isbundled)) {
511			$gd_info = gd_info();
512			$isbundled = (strpos($gd_info['GD Version'], 'bundled') !== false);
513		}
514		return $isbundled;
515	}
516
517
518	static function gd_version($fullstring=false) {
519		static $cache_gd_version = array();
520		if (empty($cache_gd_version)) {
521			$gd_info = gd_info();
522			if (preg_match('#bundled \((.+)\)$#i', $gd_info['GD Version'], $matches)) {
523				$cache_gd_version[1] = $gd_info['GD Version'];  // e.g. "bundled (2.0.15 compatible)"
524				$cache_gd_version[0] = (float) $matches[1];     // e.g. "2.0" (not "bundled (2.0.15 compatible)")
525			} else {
526				$cache_gd_version[1] = $gd_info['GD Version'];                       // e.g. "1.6.2 or higher"
527				$cache_gd_version[0] = (float) substr($gd_info['GD Version'], 0, 3); // e.g. "1.6" (not "1.6.2 or higher")
528			}
529		}
530		return $cache_gd_version[intval($fullstring)];
531	}
532
533
534	static function filesize_remote($remotefile, $timeout=10) {
535		$size = false;
536		$url = phpthumb_functions::ParseURLbetter($remotefile);
537		if ($fp = @fsockopen($url['host'], ($url['port'] ? $url['port'] : 80), $errno, $errstr, $timeout)) {
538			fwrite($fp, 'HEAD '.@$url['path'].@$url['query'].' HTTP/1.0'."\r\n".'Host: '.@$url['host']."\r\n\r\n");
539			if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.0', '>=')) {
540				stream_set_timeout($fp, $timeout);
541			}
542			while (!feof($fp)) {
543				$headerline = fgets($fp, 4096);
544				if (preg_match('#^Content-Length: (.*)#i', $headerline, $matches)) {
545					$size = intval($matches[1]);
546					break;
547				}
548			}
549			fclose ($fp);
550		}
551		return $size;
552	}
553
554
555	static function filedate_remote($remotefile, $timeout=10) {
556		$date = false;
557		$url = phpthumb_functions::ParseURLbetter($remotefile);
558		if ($fp = @fsockopen($url['host'], ($url['port'] ? $url['port'] : 80), $errno, $errstr, $timeout)) {
559			fwrite($fp, 'HEAD '.@$url['path'].@$url['query'].' HTTP/1.0'."\r\n".'Host: '.@$url['host']."\r\n\r\n");
560			if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.0', '>=')) {
561				stream_set_timeout($fp, $timeout);
562			}
563			while (!feof($fp)) {
564				$headerline = fgets($fp, 4096);
565				if (preg_match('#^Last-Modified: (.*)#i', $headerline, $matches)) {
566					$date = strtotime($matches[1]) - date('Z');
567					break;
568				}
569			}
570			fclose ($fp);
571		}
572		return $date;
573	}
574
575
576	static function md5_file_safe($filename) {
577		// md5_file() doesn't exist in PHP < 4.2.0
578		if (function_exists('md5_file')) {
579			return md5_file($filename);
580		}
581		if ($fp = @fopen($filename, 'rb')) {
582			$rawData = '';
583			do {
584				$buffer = fread($fp, 8192);
585				$rawData .= $buffer;
586			} while (strlen($buffer) > 0);
587			fclose($fp);
588			return md5($rawData);
589		}
590		return false;
591	}
592
593
594	static function nonempty_min() {
595		$arg_list = func_get_args();
596		$acceptable = array();
597		foreach ($arg_list as $arg) {
598			if ($arg) {
599				$acceptable[] = $arg;
600			}
601		}
602		return min($acceptable);
603	}
604
605
606	static function LittleEndian2String($number, $minbytes=1) {
607		$intstring = '';
608		while ($number > 0) {
609			$intstring = $intstring.chr($number & 255);
610			$number >>= 8;
611		}
612		return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
613	}
614
615	static function OneOfThese() {
616		// return the first useful (non-empty/non-zero/non-false) value from those passed
617		$arg_list = func_get_args();
618		foreach ($arg_list as $key => $value) {
619			if ($value) {
620				return $value;
621			}
622		}
623		return false;
624	}
625
626	static function CaseInsensitiveInArray($needle, $haystack) {
627		$needle = strtolower($needle);
628		foreach ($haystack as $key => $value) {
629			if (is_array($value)) {
630				// skip?
631			} elseif ($needle == strtolower($value)) {
632				return true;
633			}
634		}
635		return false;
636	}
637
638	static function URLreadFsock($host, $file, &$errstr, $successonly=true, $port=80, $timeout=10) {
639		if (!function_exists('fsockopen') || phpthumb_functions::FunctionIsDisabled('fsockopen')) {
640			$errstr = 'fsockopen() unavailable';
641			return false;
642		}
643		//if ($fp = @fsockopen($host, $port, $errno, $errstr, $timeout)) {
644		if ($fp = @fsockopen((($port == 443) ? 'ssl://' : '').$host, $port, $errno, $errstr, $timeout)) { // https://github.com/JamesHeinrich/phpThumb/issues/39
645			$out  = 'GET '.$file.' HTTP/1.0'."\r\n";
646			$out .= 'Host: '.$host."\r\n";
647			$out .= 'Connection: Close'."\r\n\r\n";
648			fwrite($fp, $out);
649
650			$isHeader = true;
651			$Data_header = '';
652			$Data_body   = '';
653			$header_newlocation = '';
654			while (!feof($fp)) {
655				$line = fgets($fp, 1024);
656				if ($isHeader) {
657					$Data_header .= $line;
658				} else {
659					$Data_body .= $line;
660				}
661				if (preg_match('#^HTTP/[\\.0-9]+ ([0-9]+) (.+)$#i', rtrim($line), $matches)) {
662					list( , $errno, $errstr) = $matches;
663					$errno = intval($errno);
664				} elseif (preg_match('#^Location: (.*)$#i', rtrim($line), $matches)) {
665					$header_newlocation = $matches[1];
666				}
667				if ($isHeader && ($line == "\r\n")) {
668					$isHeader = false;
669					if ($successonly) {
670						switch ($errno) {
671							case 200:
672								// great, continue
673								break;
674
675							default:
676								$errstr = $errno.' '.$errstr.($header_newlocation ? '; Location: '.$header_newlocation : '');
677								fclose($fp);
678								return false;
679								break;
680						}
681					}
682				}
683			}
684			fclose($fp);
685			return $Data_body;
686		}
687		return null;
688	}
689
690	static function CleanUpURLencoding($url, $queryseperator='&') {
691		if (!preg_match('#^http#i', $url)) {
692			return $url;
693		}
694		$parse_url = phpthumb_functions::ParseURLbetter($url);
695		$pathelements = explode('/', $parse_url['path']);
696		$CleanPathElements = array();
697		$TranslationMatrix = array(' '=>'%20');
698		foreach ($pathelements as $key => $pathelement) {
699			$CleanPathElements[] = strtr($pathelement, $TranslationMatrix);
700		}
701		foreach ($CleanPathElements as $key => $value) {
702			if ($value === '') {
703				unset($CleanPathElements[$key]);
704			}
705		}
706
707		$queries = explode($queryseperator, (isset($parse_url['query']) ? $parse_url['query'] : ''));
708		$CleanQueries = array();
709		foreach ($queries as $key => $query) {
710			@list($param, $value) = explode('=', $query);
711			$CleanQueries[] = strtr($param, $TranslationMatrix).($value ? '='.strtr($value, $TranslationMatrix) : '');
712		}
713		foreach ($CleanQueries as $key => $value) {
714			if ($value === '') {
715				unset($CleanQueries[$key]);
716			}
717		}
718
719		$cleaned_url  = $parse_url['scheme'].'://';
720		$cleaned_url .= (@$parse_url['username'] ? $parse_url['host'].(@$parse_url['password'] ? ':'.$parse_url['password'] : '').'@' : '');
721		$cleaned_url .= $parse_url['host'];
722		$cleaned_url .= ((!empty($parse_url['port']) && ($parse_url['port'] != 80)) ? ':'.$parse_url['port'] : '');
723		$cleaned_url .= '/'.implode('/', $CleanPathElements);
724		$cleaned_url .= (@$CleanQueries ? '?'.implode($queryseperator, $CleanQueries) : '');
725		return $cleaned_url;
726	}
727
728	static function ParseURLbetter($url) {
729		$parsedURL = @parse_url($url);
730		if (!@$parsedURL['port']) {
731			switch (strtolower(@$parsedURL['scheme'])) {
732				case 'ftp':
733					$parsedURL['port'] = 21;
734					break;
735				case 'https':
736					$parsedURL['port'] = 443;
737					break;
738				case 'http':
739					$parsedURL['port'] = 80;
740					break;
741			}
742		}
743		return $parsedURL;
744	}
745
746	static function SafeURLread($url, &$error, $timeout=10, $followredirects=true) {
747		$error   = '';
748		$errstr  = '';
749		$rawData = '';
750
751		$parsed_url = phpthumb_functions::ParseURLbetter($url);
752		$alreadyLookedAtURLs[trim($url)] = true;
753
754		while (true) {
755			$tryagain = false;
756			$rawData = phpthumb_functions::URLreadFsock(@$parsed_url['host'], @$parsed_url['path'].'?'.@$parsed_url['query'], $errstr, true, (@$parsed_url['port'] ? @$parsed_url['port'] : 80), $timeout);
757			if ($followredirects && preg_match('#302 [a-z ]+; Location\\: (http.*)#i', $errstr, $matches)) {
758				$matches[1] = trim(@$matches[1]);
759				if (!@$alreadyLookedAtURLs[$matches[1]]) {
760					// loop through and examine new URL
761					$error .= 'URL "'.$url.'" redirected to "'.$matches[1].'"';
762
763					$tryagain = true;
764					$alreadyLookedAtURLs[$matches[1]] = true;
765					$parsed_url = phpthumb_functions::ParseURLbetter($matches[1]);
766				}
767			}
768			if (!$tryagain) {
769				break;
770			}
771		}
772
773		if ($rawData === false) {
774			$error .= 'Error opening "'.$url.'":'."\n\n".$errstr;
775			return false;
776		} elseif ($rawData === null) {
777			// fall through
778			$error .= 'Error opening "'.$url.'":'."\n\n".$errstr;
779		} else {
780			return $rawData;
781		}
782
783		if (function_exists('curl_version') && !phpthumb_functions::FunctionIsDisabled('curl_exec')) {
784			$ch = curl_init();
785			curl_setopt($ch, CURLOPT_URL, $url);
786			curl_setopt($ch, CURLOPT_HEADER, false);
787			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
788			curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
789			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
790			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
791			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, (bool) $followredirects);
792			curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
793			$rawData = curl_exec($ch);
794			curl_close($ch);
795			if (strlen($rawData) > 0) {
796				$error .= 'CURL succeeded ('.strlen($rawData).' bytes); ';
797				return $rawData;
798			}
799			$error .= 'CURL available but returned no data; ';
800		} else {
801			$error .= 'CURL unavailable; ';
802		}
803
804		$BrokenURLfopenPHPversions = array('4.4.2');
805		if (in_array(phpversion(), $BrokenURLfopenPHPversions)) {
806			$error .= 'fopen(URL) broken in PHP v'.phpversion().'; ';
807		} elseif (@ini_get('allow_url_fopen')) {
808			$rawData = '';
809			$error_fopen = '';
810			ob_start();
811			if ($fp = fopen($url, 'rb')) {
812				do {
813					$buffer = fread($fp, 8192);
814					$rawData .= $buffer;
815				} while (strlen($buffer) > 0);
816				fclose($fp);
817			} else {
818				$error_fopen .= trim(strip_tags(ob_get_contents()));
819			}
820			ob_end_clean();
821			$error .= $error_fopen;
822			if (!$error_fopen) {
823				$error .= '; "allow_url_fopen" succeeded ('.strlen($rawData).' bytes); ';
824				return $rawData;
825			}
826			$error .= '; "allow_url_fopen" enabled but returned no data ('.$error_fopen.'); ';
827		} else {
828			$error .= '"allow_url_fopen" disabled; ';
829		}
830
831		return false;
832	}
833
834	static function EnsureDirectoryExists($dirname, $mask = 0755) {
835		$directory_elements = explode(DIRECTORY_SEPARATOR, $dirname);
836		$startoffset = (!$directory_elements[0] ? 2 : 1);  // unix with leading "/" then start with 2nd element; Windows with leading "c:\" then start with 1st element
837		$open_basedirs = preg_split('#[;:]#', ini_get('open_basedir'));
838		foreach ($open_basedirs as $key => $open_basedir) {
839			if (preg_match('#^'.preg_quote($open_basedir).'#', $dirname) && (strlen($dirname) > strlen($open_basedir))) {
840				$startoffset = count(explode(DIRECTORY_SEPARATOR, $open_basedir));
841				break;
842			}
843		}
844		$i = $startoffset;
845		$endoffset = count($directory_elements);
846		for ($i = $startoffset; $i <= $endoffset; $i++) {
847			$test_directory = implode(DIRECTORY_SEPARATOR, array_slice($directory_elements, 0, $i));
848			if (!$test_directory) {
849				continue;
850			}
851			if (!@is_dir($test_directory)) {
852				if (@file_exists($test_directory)) {
853					// directory name already exists as a file
854					return false;
855				}
856				@mkdir($test_directory, $mask);
857				@chmod($test_directory, $mask);
858				if (!@is_dir($test_directory) || !@is_writeable($test_directory)) {
859					return false;
860				}
861			}
862		}
863		return true;
864	}
865
866
867	static function GetAllFilesInSubfolders($dirname) {
868		$AllFiles = array();
869		$dirname = rtrim(realpath($dirname), '/\\');
870		if ($dirhandle = @opendir($dirname)) {
871			while (($file = readdir($dirhandle)) !== false) {
872				$fullfilename = $dirname.DIRECTORY_SEPARATOR.$file;
873				if (is_file($fullfilename)) {
874					$AllFiles[] = $fullfilename;
875				} elseif (is_dir($fullfilename)) {
876					switch ($file) {
877						case '.':
878						case '..':
879							break;
880
881						default:
882							$AllFiles[] = $fullfilename;
883							$subfiles = phpthumb_functions::GetAllFilesInSubfolders($fullfilename);
884							foreach ($subfiles as $filename) {
885								$AllFiles[] = $filename;
886							}
887							break;
888					}
889				} else {
890					// ignore?
891				}
892			}
893			closedir($dirhandle);
894		}
895		sort($AllFiles);
896		return array_unique($AllFiles);
897	}
898
899
900	static function SanitizeFilename($filename) {
901		$filename = preg_replace('/[^'.preg_quote(' !#$%^()+,-.;<>=@[]_{}').'a-zA-Z0-9]/', '_', $filename);
902		if (phpthumb_functions::version_compare_replacement(phpversion(), '4.1.0', '>=')) {
903			$filename = trim($filename, '.');
904		}
905		return $filename;
906	}
907
908	static function PasswordStrength($password) {
909		$strength = 0;
910		$strength += strlen(preg_replace('#[^a-z]#',       '', $password)) * 0.5; // lowercase characters are weak
911		$strength += strlen(preg_replace('#[^A-Z]#',       '', $password)) * 0.8; // uppercase characters are somewhat better
912		$strength += strlen(preg_replace('#[^0-9]#',       '', $password)) * 1.0; // numbers are somewhat better
913		$strength += strlen(preg_replace('#[a-zA-Z0-9]#',  '', $password)) * 2.0; // other non-alphanumeric characters are best
914		return $strength;
915	}
916
917}
918
919
920////////////// END: class phpthumb_functions //////////////
921
922
923if (!function_exists('gd_info')) {
924	// built into PHP v4.3.0+ (with bundled GD2 library)
925	function gd_info() {
926		static $gd_info = array();
927		if (empty($gd_info)) {
928			// based on code by johnschaefer at gmx dot de
929			// from PHP help on gd_info()
930			$gd_info = array(
931				'GD Version'         => '',
932				'FreeType Support'   => false,
933				'FreeType Linkage'   => '',
934				'T1Lib Support'      => false,
935				'GIF Read Support'   => false,
936				'GIF Create Support' => false,
937				'JPG Support'        => false,
938				'PNG Support'        => false,
939				'WBMP Support'       => false,
940				'XBM Support'        => false
941			);
942			$phpinfo_array = phpthumb_functions::phpinfo_array();
943			foreach ($phpinfo_array as $line) {
944				$line = trim(strip_tags($line));
945				foreach ($gd_info as $key => $value) {
946					//if (strpos($line, $key) !== false) {
947					if (strpos($line, $key) === 0) {
948						$newvalue = trim(str_replace($key, '', $line));
949						$gd_info[$key] = $newvalue;
950					}
951				}
952			}
953			if (empty($gd_info['GD Version'])) {
954				// probable cause: "phpinfo() disabled for security reasons"
955				if (function_exists('imagetypes')) {
956					$imagetypes = imagetypes();
957					if ($imagetypes & IMG_PNG) {
958						$gd_info['PNG Support'] = true;
959					}
960					if ($imagetypes & IMG_GIF) {
961						$gd_info['GIF Create Support'] = true;
962					}
963					if ($imagetypes & IMG_JPG) {
964						$gd_info['JPG Support'] = true;
965					}
966					if ($imagetypes & IMG_WBMP) {
967						$gd_info['WBMP Support'] = true;
968					}
969				}
970				// to determine capability of GIF creation, try to use imagecreatefromgif on a 1px GIF
971				if (function_exists('imagecreatefromgif')) {
972					if ($tempfilename = phpthumb::phpThumb_tempnam()) {
973						if ($fp_tempfile = @fopen($tempfilename, 'wb')) {
974							fwrite($fp_tempfile, base64_decode('R0lGODlhAQABAIAAAH//AP///ywAAAAAAQABAAACAUQAOw==')); // very simple 1px GIF file base64-encoded as string
975							fclose($fp_tempfile);
976							@chmod($tempfilename, $this->getParameter('config_file_create_mask'));
977
978							// if we can convert the GIF file to a GD image then GIF create support must be enabled, otherwise it's not
979							$gd_info['GIF Read Support'] = (bool) @imagecreatefromgif($tempfilename);
980						}
981						unlink($tempfilename);
982					}
983				}
984				if (function_exists('imagecreatetruecolor') && @imagecreatetruecolor(1, 1)) {
985					$gd_info['GD Version'] = '2.0.1 or higher (assumed)';
986				} elseif (function_exists('imagecreate') && @imagecreate(1, 1)) {
987					$gd_info['GD Version'] = '1.6.0 or higher (assumed)';
988				}
989			}
990		}
991		return $gd_info;
992	}
993}
994
995
996if (!function_exists('is_executable')) {
997	// in PHP v3+, but v5.0+ for Windows
998	function is_executable($filename) {
999		// poor substitute, but better than nothing
1000		return file_exists($filename);
1001	}
1002}
1003
1004
1005if (!function_exists('preg_quote')) {
1006	// included in PHP v3.0.9+, but may be unavailable if not compiled in
1007	function preg_quote($string, $delimiter='\\') {
1008		static $preg_quote_array = array();
1009		if (empty($preg_quote_array)) {
1010			$escapeables = '.\\+*?[^]$(){}=!<>|:';
1011			for ($i = 0; $i < strlen($escapeables); $i++) {
1012				$strtr_preg_quote[$escapeables{$i}] = $delimiter.$escapeables{$i};
1013			}
1014		}
1015		return strtr($string, $strtr_preg_quote);
1016	}
1017}
1018
1019if (!function_exists('file_get_contents')) {
1020	// included in PHP v4.3.0+
1021	function file_get_contents($filename) {
1022		if (preg_match('#^(f|ht)tp\://#i', $filename)) {
1023			return SafeURLread($filename, $error);
1024		}
1025		if ($fp = @fopen($filename, 'rb')) {
1026			$rawData = '';
1027			do {
1028				$buffer = fread($fp, 8192);
1029				$rawData .= $buffer;
1030			} while (strlen($buffer) > 0);
1031			fclose($fp);
1032			return $rawData;
1033		}
1034		return false;
1035	}
1036}
1037
1038
1039if (!function_exists('file_put_contents')) {
1040	// included in PHP v5.0.0+
1041	function file_put_contents($filename, $filedata) {
1042		if ($fp = @fopen($filename, 'wb')) {
1043			fwrite($fp, $filedata);
1044			fclose($fp);
1045			return true;
1046		}
1047		return false;
1048	}
1049}
1050
1051if (!function_exists('imagealphablending')) {
1052	// built-in function requires PHP v4.0.6+ *and* GD v2.0.1+
1053	function imagealphablending(&$img, $blendmode=true) {
1054		// do nothing, this function is declared here just to
1055		// prevent runtime errors if GD2 is not available
1056		return true;
1057	}
1058}
1059
1060if (!function_exists('imagesavealpha')) {
1061	// built-in function requires PHP v4.3.2+ *and* GD v2.0.1+
1062	function imagesavealpha(&$img, $blendmode=true) {
1063		// do nothing, this function is declared here just to
1064		// prevent runtime errors if GD2 is not available
1065		return true;
1066	}
1067}
1068