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