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 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 public 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 public 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 (int) ($version1 < $version2); 65 break; 66 case '<=': 67 case 'le': 68 return (int) ($version1 <= $version2); 69 break; 70 case '>': 71 case 'gt': 72 return (int) ($version1 > $version2); 73 break; 74 case '>=': 75 case 'ge': 76 return (int) ($version1 >= $version2); 77 break; 78 case '==': 79 case '=': 80 case 'eq': 81 return (int) ($version1 == $version2); 82 break; 83 case '!=': 84 case '<>': 85 case 'ne': 86 return (int) ($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 public 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('#([\d]+)([A-Z]+)([\d]+)#i', '$1.$2.$3', $version1); 111 $version2 = preg_replace('#([\d]+)([A-Z]+)([\d]+)#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 = self::version_compare_replacement_sub($version1, $version2, $operator); 118 if ($comparison != 0) { 119 return $comparison; 120 } 121 } 122 return 0; 123 } 124 125 public static function escapeshellarg_replacement($arg) { 126 if (function_exists('escapeshellarg') && !self::FunctionIsDisabled('escapeshellarg')) { 127 return escapeshellarg($arg); 128 } 129 return '\''.str_replace('\'', '\\\'', $arg).'\''; 130 } 131 132 public 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 public 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 = self::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 public 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 public 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 public 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 public static function IsHexColor($HexColorString) { 226 return preg_match('#^[0-9A-F]{6}$#i', $HexColorString); 227 } 228 229 230 public static function ImageColorAllocateAlphaSafe(&$gdimg_hexcolorallocate, $R, $G, $B, $alpha=false) { 231 if (self::version_compare_replacement(PHP_VERSION, '4.3.2', '>=') && ($alpha !== false)) { 232 return imagecolorallocatealpha($gdimg_hexcolorallocate, $R, $G, $B, (int) $alpha); 233 } else { 234 return imagecolorallocate($gdimg_hexcolorallocate, $R, $G, $B); 235 } 236 } 237 238 public 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 (self::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 self::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 public static function HexColorXOR($hexcolor) { 256 return strtoupper(str_pad(dechex(~hexdec($hexcolor) & 0xFFFFFF), 6, '0', STR_PAD_LEFT)); 257 } 258 259 260 public 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 public 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 public static function GrayscaleValue($r, $g, $b) { 277 return round(($r * 0.30) + ($g * 0.59) + ($b * 0.11)); 278 } 279 280 281 public static function GrayscalePixel($OriginalPixel) { 282 $gray = self::GrayscaleValue($OriginalPixel[ 'red'], $OriginalPixel[ 'green'], $OriginalPixel[ 'blue']); 283 return array('red'=>$gray, 'green'=>$gray, 'blue'=>$gray); 284 } 285 286 287 public 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 public static function ScaleToFitInBox($width, $height, $maxwidth=null, $maxheight=null, $allow_enlarge=true, $allow_reduce=true) { 296 $maxwidth = (null === $maxwidth ? $width : $maxwidth); 297 $maxheight = (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 public 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 public static function ImageCreateFunction($x_size, $y_size) { 369 $ImageCreateFunction = 'imagecreate'; 370 if (self::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 public 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 = self::GetPixelColor($dst_im, $dst_x + $x, $dst_y + $y); 388 $OverlayPixel = self::GetPixelColor($src_im, $x, $y); 389 $alphapct = $OverlayPixel['alpha'] / 127; 390 $overlaypct = (1 - $alphapct) * $opacipct; 391 392 $newcolor = self::ImageColorAllocateAlphaSafe( 393 $dst_im, 394 $RealPixel['alpha'] == 127 ? $OverlayPixel['red'] : ($OverlayPixel['alpha'] == 127 ? $RealPixel['red'] : (round($RealPixel['red'] * (1 - $overlaypct)) + ($OverlayPixel['red'] * $overlaypct))), 395 $RealPixel['alpha'] == 127 ? $OverlayPixel['green'] : ($OverlayPixel['alpha'] == 127 ? $RealPixel['green'] : (round($RealPixel['green'] * (1 - $overlaypct)) + ($OverlayPixel['green'] * $overlaypct))), 396 $RealPixel['alpha'] == 127 ? $OverlayPixel['blue'] : ($OverlayPixel['alpha'] == 127 ? $RealPixel['blue'] : (round($RealPixel['blue'] * (1 - $overlaypct)) + ($OverlayPixel['blue'] * $overlaypct))), 397// 0); 398 min([$RealPixel['alpha'], floor($OverlayPixel['alpha'] * $opacipct)]) 399 ); 400 401 imagesetpixel($dst_im, $dst_x + $x, $dst_y + $y, $newcolor); 402 } 403 } 404 return true; 405 } 406 407 408 public static function ProportionalResize($old_width, $old_height, $new_width=false, $new_height=false) { 409 $old_aspect_ratio = $old_width / $old_height; 410 if (($new_width === false) && ($new_height === false)) { 411 return false; 412 } elseif ($new_width === false) { 413 $new_width = $new_height * $old_aspect_ratio; 414 } elseif ($new_height === false) { 415 $new_height = $new_width / $old_aspect_ratio; 416 } 417 $new_aspect_ratio = $new_width / $new_height; 418 if ($new_aspect_ratio == $old_aspect_ratio) { 419 // great, done 420 } elseif ($new_aspect_ratio < $old_aspect_ratio) { 421 // limited by width 422 $new_height = $new_width / $old_aspect_ratio; 423 } elseif ($new_aspect_ratio > $old_aspect_ratio) { 424 // limited by height 425 $new_width = $new_height * $old_aspect_ratio; 426 } 427 return array( 428 (int) round($new_width), 429 (int) round($new_height) 430 ); 431 } 432 433 434 public static function FunctionIsDisabled($function) { 435 static $DisabledFunctions = null; 436 if (null === $DisabledFunctions) { 437 $disable_functions_local = explode(',', strtolower(@ini_get('disable_functions'))); 438 $disable_functions_global = explode(',', strtolower(@get_cfg_var('disable_functions'))); 439 foreach ($disable_functions_local as $key => $value) { 440 $DisabledFunctions[trim($value)] = 'local'; 441 } 442 foreach ($disable_functions_global as $key => $value) { 443 $DisabledFunctions[trim($value)] = 'global'; 444 } 445 if (@ini_get('safe_mode')) { 446 $DisabledFunctions['shell_exec'] = 'local'; 447 $DisabledFunctions['set_time_limit'] = 'local'; 448 } 449 } 450 return isset($DisabledFunctions[strtolower($function)]); 451 } 452 453 454 public static function SafeExec($command) { 455 static $AllowedExecFunctions = array(); 456 if (empty($AllowedExecFunctions)) { 457 $AllowedExecFunctions = array('shell_exec'=>true, 'passthru'=>true, 'system'=>true, 'exec'=>true); 458 foreach ($AllowedExecFunctions as $key => $value) { 459 $AllowedExecFunctions[$key] = !self::FunctionIsDisabled($key); 460 } 461 } 462 $command .= ' 2>&1'; // force redirect stderr to stdout 463 foreach ($AllowedExecFunctions as $execfunction => $is_allowed) { 464 if (!$is_allowed) { 465 continue; 466 } 467 $returnvalue = false; 468 switch ($execfunction) { 469 case 'passthru': 470 case 'system': 471 ob_start(); 472 $execfunction($command); 473 $returnvalue = ob_get_contents(); 474 ob_end_clean(); 475 break; 476 477 case 'exec': 478 $output = array(); 479 $lastline = $execfunction($command, $output); 480 $returnvalue = implode("\n", $output); 481 break; 482 483 case 'shell_exec': 484 ob_start(); 485 $returnvalue = $execfunction($command); 486 ob_end_clean(); 487 break; 488 } 489 return $returnvalue; 490 } 491 return false; 492 } 493 494 495 public static function ApacheLookupURIarray($filename) { 496 // apache_lookup_uri() only works when PHP is installed as an Apache module. 497 if (PHP_SAPI == 'apache') { 498 //$property_exists_exists = function_exists('property_exists'); 499 $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'); 500 if ($apacheLookupURIobject = @apache_lookup_uri($filename)) { 501 $apacheLookupURIarray = array(); 502 foreach ($keys as $key) { 503 $apacheLookupURIarray[$key] = @$apacheLookupURIobject->$key; 504 } 505 return $apacheLookupURIarray; 506 } 507 } 508 return false; 509 } 510 511 512 public static function gd_is_bundled() { 513 static $isbundled = null; 514 if (null === $isbundled) { 515 $gd_info = gd_info(); 516 $isbundled = (strpos($gd_info['GD Version'], 'bundled') !== false); 517 } 518 return $isbundled; 519 } 520 521 522 public static function gd_version($fullstring=false) { 523 static $cache_gd_version = array(); 524 if (empty($cache_gd_version)) { 525 $gd_info = gd_info(); 526 if (preg_match('#bundled \((.+)\)$#i', $gd_info['GD Version'], $matches)) { 527 $cache_gd_version[1] = $gd_info['GD Version']; // e.g. "bundled (2.0.15 compatible)" 528 $cache_gd_version[0] = (float) $matches[1]; // e.g. "2.0" (not "bundled (2.0.15 compatible)") 529 } else { 530 $cache_gd_version[1] = $gd_info['GD Version']; // e.g. "1.6.2 or higher" 531 $cache_gd_version[0] = (float) substr($gd_info['GD Version'], 0, 3); // e.g. "1.6" (not "1.6.2 or higher") 532 } 533 } 534 return $cache_gd_version[ (int) $fullstring ]; 535 } 536 537 538 public static function filesize_remote($remotefile, $timeout=10) { 539 $size = false; 540 $parsed_url = self::ParseURLbetter($remotefile); 541 if ($fp = @fsockopen($parsed_url['host'], $parsed_url['port'], $errno, $errstr, $timeout)) { 542 fwrite($fp, 'HEAD '.$parsed_url['path'].$parsed_url['query'].' HTTP/1.0'."\r\n".'Host: '.$parsed_url['host']."\r\n\r\n"); 543 if (self::version_compare_replacement(PHP_VERSION, '4.3.0', '>=')) { 544 stream_set_timeout($fp, $timeout); 545 } 546 while (!feof($fp)) { 547 $headerline = fgets($fp, 4096); 548 if (preg_match('#^Content-Length: (.*)#i', $headerline, $matches)) { 549 $size = (int) $matches[ 1]; 550 break; 551 } 552 } 553 fclose ($fp); 554 } 555 return $size; 556 } 557 558 559 public static function filedate_remote($remotefile, $timeout=10) { 560 $date = false; 561 $parsed_url = self::ParseURLbetter($remotefile); 562 if ($fp = @fsockopen($parsed_url['host'], $parsed_url['port'], $errno, $errstr, $timeout)) { 563 fwrite($fp, 'HEAD '.$parsed_url['path'].$parsed_url['query'].' HTTP/1.0'."\r\n".'Host: '.$parsed_url['host']."\r\n\r\n"); 564 if (self::version_compare_replacement(PHP_VERSION, '4.3.0', '>=')) { 565 stream_set_timeout($fp, $timeout); 566 } 567 while (!feof($fp)) { 568 $headerline = fgets($fp, 4096); 569 if (preg_match('#^Last-Modified: (.*)#i', $headerline, $matches)) { 570 $date = strtotime($matches[1]) - date('Z'); 571 break; 572 } 573 } 574 fclose ($fp); 575 } 576 return $date; 577 } 578 579 580 public static function md5_file_safe($filename) { 581 // md5_file() doesn't exist in PHP < 4.2.0 582 if (function_exists('md5_file')) { 583 return md5_file($filename); 584 } 585 if ($fp = @fopen($filename, 'rb')) { 586 $rawData = ''; 587 do { 588 $buffer = fread($fp, 8192); 589 $rawData .= $buffer; 590 } while (strlen($buffer) > 0); 591 fclose($fp); 592 return md5($rawData); 593 } 594 return false; 595 } 596 597 598 public static function nonempty_min() { 599 $arg_list = func_get_args(); 600 $acceptable = array(); 601 foreach ($arg_list as $arg) { 602 if ($arg) { 603 $acceptable[] = $arg; 604 } 605 } 606 return min($acceptable); 607 } 608 609 610 public static function LittleEndian2String($number, $minbytes=1) { 611 $intstring = ''; 612 while ($number > 0) { 613 $intstring .= chr($number & 255); 614 $number >>= 8; 615 } 616 return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT); 617 } 618 619 public static function OneOfThese() { 620 // return the first useful (non-empty/non-zero/non-false) value from those passed 621 $arg_list = func_get_args(); 622 foreach ($arg_list as $key => $value) { 623 if ($value) { 624 return $value; 625 } 626 } 627 return false; 628 } 629 630 public static function CaseInsensitiveInArray($needle, $haystack) { 631 $needle = strtolower($needle); 632 foreach ($haystack as $key => $value) { 633 if (is_array($value)) { 634 // skip? 635 } elseif ($needle == strtolower($value)) { 636 return true; 637 } 638 } 639 return false; 640 } 641 642 public static function URLreadFsock($host, $file, &$errstr, $successonly=true, $port=-1, $timeout=10) { 643 if (!function_exists('fsockopen') || self::FunctionIsDisabled('fsockopen')) { 644 $errstr = 'URLreadFsock says: function fsockopen() unavailable'; 645 return false; 646 } 647 $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. 648 //if ($fp = @fsockopen($host, $port, $errno, $errstr, $timeout)) { 649 if ($fp = @fsockopen((($port == 443) ? 'ssl://' : '').$host, $port, $errno, $errstr, $timeout)) { // https://github.com/JamesHeinrich/phpThumb/issues/39 650 $out = 'GET '.$file.' HTTP/1.0'."\r\n"; 651 $out .= 'Host: '.$host."\r\n"; 652 $out .= 'Connection: Close'."\r\n\r\n"; 653 fwrite($fp, $out); 654 655 $isHeader = true; 656 $data_header = ''; 657 $data_body = ''; 658 $header_newlocation = ''; 659 while (!feof($fp)) { 660 $line = fgets($fp, 1024); 661 if ($isHeader) { 662 $data_header .= $line; 663 } else { 664 $data_body .= $line; 665 } 666 if (preg_match('#^HTTP/[\\.\d]+ ([\d]+) (.+)$#i', rtrim($line), $matches)) { 667 list( , $errno, $errstr) = $matches; 668 $errno = (int) $errno; 669 } elseif (preg_match('#^Location: (.*)$#i', rtrim($line), $matches)) { 670 $header_newlocation = $matches[1]; 671 } 672 if ($isHeader && ($line == "\r\n")) { 673 $isHeader = false; 674 if ($successonly) { 675 switch ($errno) { 676 case 200: 677 // great, continue 678 break; 679 680 default: 681 $errstr = $errno.' '.$errstr.($header_newlocation ? '; Location: '.$header_newlocation : ''); 682 fclose($fp); 683 return false; 684 break; 685 } 686 } 687 } 688 } 689 fclose($fp); 690 return $data_body; 691 } 692 return null; 693 } 694 695 public static function CleanUpURLencoding($url, $queryseperator='&') { 696 if (!0 === stripos($url, "http") ) { 697 return $url; 698 } 699 $parsed_url = self::ParseURLbetter($url); 700 $pathelements = explode('/', $parsed_url['path']); 701 $CleanPathElements = array(); 702 $TranslationMatrix = array(' '=>'%20'); 703 foreach ($pathelements as $key => $pathelement) { 704 $CleanPathElements[] = strtr($pathelement, $TranslationMatrix); 705 } 706 foreach ($CleanPathElements as $key => $value) { 707 if ($value === '') { 708 unset($CleanPathElements[$key]); 709 } 710 } 711 712 $queries = explode($queryseperator, $parsed_url['query']); 713 $CleanQueries = array(); 714 foreach ($queries as $key => $query) { 715 @list($param, $value) = explode('=', $query); 716 $CleanQueries[] = strtr($param, $TranslationMatrix).($value ? '='.strtr($value, $TranslationMatrix) : ''); 717 } 718 foreach ($CleanQueries as $key => $value) { 719 if ($value === '') { 720 unset($CleanQueries[$key]); 721 } 722 } 723 724 $cleaned_url = $parsed_url['scheme'].'://'; 725 $cleaned_url .= ($parsed_url['username'] ? $parsed_url['username'].($parsed_url['password'] ? ':'.$parsed_url['password'] : '').'@' : ''); 726 $cleaned_url .= $parsed_url['host']; 727 $cleaned_url .= (($parsed_url['port'] && ($parsed_url['port'] != self::URLschemeDefaultPort($parsed_url['scheme']))) ? ':'.$parsed_url['port'] : ''); 728 $cleaned_url .= '/'.implode('/', $CleanPathElements); 729 $cleaned_url .= (!empty($CleanQueries) ? '?'.implode($queryseperator, $CleanQueries) : ''); 730 return $cleaned_url; 731 } 732 733 public static function URLschemeDefaultPort($scheme) { 734 static $schemePort = array( 735 'ftp' => 21, 736 'http' => 80, 737 'https' => 443, 738 ); 739 return (isset($schemePort[strtolower($scheme)]) ? $schemePort[strtolower($scheme)] : null); 740 } 741 742 public static function ParseURLbetter($url) { 743 $parsedURL = @parse_url($url); 744 foreach (array('scheme', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment') as $key) { // ensure all possible array keys are always returned 745 if (!array_key_exists($key, $parsedURL)) { 746 $parsedURL[$key] = null; 747 } 748 } 749 $parsedURL['port'] = ($parsedURL['port'] ? $parsedURL['port'] : self::URLschemeDefaultPort($parsedURL['scheme'])); 750 return $parsedURL; 751 } 752 753 public static function SafeURLread($url, &$error, $timeout=10, $followredirects=true) { 754 $error = ''; 755 $errstr = ''; 756 $rawData = ''; 757 758 $parsed_url = self::ParseURLbetter($url); 759 $alreadyLookedAtURLs[trim($url)] = true; 760 761 while (true) { 762 $tryagain = false; 763 $rawData = self::URLreadFsock($parsed_url['host'], $parsed_url['path'].'?'.$parsed_url['query'], $errstr, true, $parsed_url['port'], $timeout); 764 if ($followredirects && preg_match('#302 [a-z ]+; Location\\: (http.*)#i', $errstr, $matches)) { 765 $matches[1] = trim(@$matches[1]); 766 if (!@$alreadyLookedAtURLs[$matches[1]]) { 767 // loop through and examine new URL 768 $error .= 'URL "'.$url.'" redirected to "'.$matches[1].'"'; 769 770 $tryagain = true; 771 $alreadyLookedAtURLs[$matches[1]] = true; 772 $parsed_url = self::ParseURLbetter($matches[ 1]); 773 } 774 } 775 if (!$tryagain) { 776 break; 777 } 778 } 779 780 if ($rawData === false) { 781 $error .= 'Error opening "'.$url.'":'."\n\n".$errstr; 782 return false; 783 } elseif ($rawData === null) { 784 // fall through 785 $error .= 'Error opening "'.$url.'":'."\n\n".$errstr; 786 } else { 787 return $rawData; 788 } 789 790 if (function_exists('curl_version') && !self::FunctionIsDisabled('curl_exec')) { 791 $ch = curl_init(); 792 curl_setopt($ch, CURLOPT_URL, $url); 793 curl_setopt($ch, CURLOPT_HEADER, false); 794 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 795 curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); 796 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); 797 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); 798 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, (bool) $followredirects); 799 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); 800 $rawData = curl_exec($ch); 801 curl_close($ch); 802 if (strlen($rawData) > 0) { 803 $error .= 'CURL succeeded ('.strlen($rawData).' bytes); '; 804 return $rawData; 805 } 806 $error .= 'CURL available but returned no data; '; 807 } else { 808 $error .= 'CURL unavailable; '; 809 } 810 811 $BrokenURLfopenPHPversions = array('4.4.2'); 812 if (in_array(PHP_VERSION, $BrokenURLfopenPHPversions)) { 813 $error .= 'fopen(URL) broken in PHP v'. PHP_VERSION .'; '; 814 } elseif (@ini_get('allow_url_fopen')) { 815 $rawData = ''; 816 $error_fopen = ''; 817 ob_start(); 818 if ($fp = fopen($url, 'rb')) { 819 do { 820 $buffer = fread($fp, 8192); 821 $rawData .= $buffer; 822 } while (strlen($buffer) > 0); 823 fclose($fp); 824 } else { 825 $error_fopen .= trim(strip_tags(ob_get_contents())); 826 } 827 ob_end_clean(); 828 $error .= $error_fopen; 829 if (!$error_fopen) { 830 $error .= '; "allow_url_fopen" succeeded ('.strlen($rawData).' bytes); '; 831 return $rawData; 832 } 833 $error .= '; "allow_url_fopen" enabled but returned no data ('.$error_fopen.'); '; 834 } else { 835 $error .= '"allow_url_fopen" disabled; '; 836 } 837 838 return false; 839 } 840 841 public static function EnsureDirectoryExists($dirname, $mask = 0755) { 842 $directory_elements = explode(DIRECTORY_SEPARATOR, $dirname); 843 $startoffset = (!$directory_elements[0] ? 2 : 1); // unix with leading "/" then start with 2nd element; Windows with leading "c:\" then start with 1st element 844 $open_basedirs = preg_split('#[;:]#', ini_get('open_basedir')); 845 foreach ($open_basedirs as $key => $open_basedir) { 846 if (preg_match('#^'.preg_quote($open_basedir).'#', $dirname) && (strlen($dirname) > strlen($open_basedir))) { 847 $startoffset = substr_count($open_basedir, DIRECTORY_SEPARATOR) + 1; 848 break; 849 } 850 } 851 $i = $startoffset; 852 $endoffset = count($directory_elements); 853 for ($i = $startoffset; $i <= $endoffset; $i++) { 854 $test_directory = implode(DIRECTORY_SEPARATOR, array_slice($directory_elements, 0, $i)); 855 if (!$test_directory) { 856 continue; 857 } 858 if (!@is_dir($test_directory)) { 859 if (@file_exists($test_directory)) { 860 // directory name already exists as a file 861 return false; 862 } 863 @mkdir($test_directory, $mask); 864 @chmod($test_directory, $mask); 865 if (!@is_dir($test_directory) || !@is_writable($test_directory)) { 866 return false; 867 } 868 } 869 } 870 return true; 871 } 872 873 874 public static function GetAllFilesInSubfolders($dirname) { 875 $AllFiles = array(); 876 $dirname = rtrim(realpath($dirname), '/\\'); 877 if ($dirhandle = @opendir($dirname)) { 878 while (($file = readdir($dirhandle)) !== false) { 879 $fullfilename = $dirname.DIRECTORY_SEPARATOR.$file; 880 if (is_file($fullfilename)) { 881 $AllFiles[] = $fullfilename; 882 } elseif (is_dir($fullfilename)) { 883 switch ($file) { 884 case '.': 885 case '..': 886 break; 887 888 default: 889 $AllFiles[] = $fullfilename; 890 $subfiles = self::GetAllFilesInSubfolders($fullfilename); 891 foreach ($subfiles as $filename) { 892 $AllFiles[] = $filename; 893 } 894 break; 895 } 896 } else { 897 // ignore? 898 } 899 } 900 closedir($dirhandle); 901 } 902 sort($AllFiles); 903 return array_unique($AllFiles); 904 } 905 906 907 public static function SanitizeFilename($filename) { 908 $filename = preg_replace('/[^'.preg_quote(' !#$%^()+,-.;<>=@[]_{}').'a-zA-Z0-9]/', '_', $filename); 909 if (self::version_compare_replacement(PHP_VERSION, '4.1.0', '>=')) { 910 $filename = trim($filename, '.'); 911 } 912 return $filename; 913 } 914 915 public static function PasswordStrength($password) { 916 $strength = 0; 917 $strength += strlen(preg_replace('#[^a-z]#', '', $password)) * 0.5; // lowercase characters are weak 918 $strength += strlen(preg_replace('#[^A-Z]#', '', $password)) * 0.8; // uppercase characters are somewhat better 919 $strength += strlen(preg_replace('#[^0-9]#', '', $password)) * 1.0; // numbers are somewhat better 920 $strength += strlen(preg_replace('#[a-zA-Z0-9]#', '', $password)) * 2.0; // other non-alphanumeric characters are best 921 return $strength; 922 } 923 924} 925 926 927////////////// END: class phpthumb_functions ////////////// 928 929 930if (!function_exists('gd_info')) { 931 // built into PHP v4.3.0+ (with bundled GD2 library) 932 function gd_info() { 933 static $gd_info = array(); 934 if (empty($gd_info)) { 935 // based on code by johnschaefer at gmx dot de 936 // from PHP help on gd_info() 937 $gd_info = array( 938 'GD Version' => '', 939 'FreeType Support' => false, 940 'FreeType Linkage' => '', 941 'T1Lib Support' => false, 942 'GIF Read Support' => false, 943 'GIF Create Support' => false, 944 'JPG Support' => false, 945 'PNG Support' => false, 946 'WBMP Support' => false, 947 'XBM Support' => false 948 ); 949 $phpinfo_array = phpthumb_functions::phpinfo_array(); 950 foreach ($phpinfo_array as $line) { 951 $line = trim(strip_tags($line)); 952 foreach ($gd_info as $key => $value) { 953 //if (strpos($line, $key) !== false) { 954 if (strpos($line, $key) === 0) { 955 $newvalue = trim(str_replace($key, '', $line)); 956 $gd_info[$key] = $newvalue; 957 } 958 } 959 } 960 if (empty($gd_info['GD Version'])) { 961 // probable cause: "phpinfo() disabled for security reasons" 962 if (function_exists('imagetypes')) { 963 $imagetypes = imagetypes(); 964 if ($imagetypes & IMG_PNG) { 965 $gd_info['PNG Support'] = true; 966 } 967 if ($imagetypes & IMG_GIF) { 968 $gd_info['GIF Create Support'] = true; 969 } 970 if ($imagetypes & IMG_JPG) { 971 $gd_info['JPG Support'] = true; 972 } 973 if ($imagetypes & IMG_WBMP) { 974 $gd_info['WBMP Support'] = true; 975 } 976 } 977 // to determine capability of GIF creation, try to use imagecreatefromgif on a 1px GIF 978 if (function_exists('imagecreatefromgif')) { 979 if ($tempfilename = phpthumb::phpThumb_tempnam()) { 980 if ($fp_tempfile = @fopen($tempfilename, 'wb')) { 981 fwrite($fp_tempfile, base64_decode('R0lGODlhAQABAIAAAH//AP///ywAAAAAAQABAAACAUQAOw==')); // very simple 1px GIF file base64-encoded as string 982 fclose($fp_tempfile); 983 @chmod($tempfilename, $this->getParameter('config_file_create_mask')); 984 985 // if we can convert the GIF file to a GD image then GIF create support must be enabled, otherwise it's not 986 $gd_info['GIF Read Support'] = (bool) @imagecreatefromgif($tempfilename); 987 } 988 unlink($tempfilename); 989 } 990 } 991 if (function_exists('imagecreatetruecolor') && @imagecreatetruecolor(1, 1)) { 992 $gd_info['GD Version'] = '2.0.1 or higher (assumed)'; 993 } elseif (function_exists('imagecreate') && @imagecreate(1, 1)) { 994 $gd_info['GD Version'] = '1.6.0 or higher (assumed)'; 995 } 996 } 997 } 998 return $gd_info; 999 } 1000} 1001 1002 1003if (!function_exists('is_executable')) { 1004 // in PHP v3+, but v5.0+ for Windows 1005 function is_executable($filename) { 1006 // poor substitute, but better than nothing 1007 return file_exists($filename); 1008 } 1009} 1010 1011 1012if (!function_exists('preg_quote')) { 1013 // included in PHP v3.0.9+, but may be unavailable if not compiled in 1014 function preg_quote($string, $delimiter='\\') { 1015 static $preg_quote_array = array(); 1016 if (empty($preg_quote_array)) { 1017 $escapeables = '.\\+*?[^]$(){}=!<>|:'; 1018 for ($i = 0, $iMax = strlen($escapeables); $i < $iMax; $i++) { 1019 $strtr_preg_quote[$escapeables{$i}] = $delimiter.$escapeables{$i}; 1020 } 1021 } 1022 return strtr($string, $strtr_preg_quote); 1023 } 1024} 1025 1026if (!function_exists('file_get_contents')) { 1027 // included in PHP v4.3.0+ 1028 function file_get_contents($filename) { 1029 if (preg_match('#^(f|ht)tp\://#i', $filename)) { 1030 return SafeURLread($filename, $error); 1031 } 1032 if ($fp = @fopen($filename, 'rb')) { 1033 $rawData = ''; 1034 do { 1035 $buffer = fread($fp, 8192); 1036 $rawData .= $buffer; 1037 } while (strlen($buffer) > 0); 1038 fclose($fp); 1039 return $rawData; 1040 } 1041 return false; 1042 } 1043} 1044 1045 1046if (!function_exists('file_put_contents')) { 1047 // included in PHP v5.0.0+ 1048 function file_put_contents($filename, $filedata) { 1049 if ($fp = @fopen($filename, 'wb')) { 1050 fwrite($fp, $filedata); 1051 fclose($fp); 1052 return true; 1053 } 1054 return false; 1055 } 1056} 1057 1058if (!function_exists('imagealphablending')) { 1059 // built-in function requires PHP v4.0.6+ *and* GD v2.0.1+ 1060 function imagealphablending(&$img, $blendmode=true) { 1061 // do nothing, this function is declared here just to 1062 // prevent runtime errors if GD2 is not available 1063 return true; 1064 } 1065} 1066 1067if (!function_exists('imagesavealpha')) { 1068 // built-in function requires PHP v4.3.2+ *and* GD v2.0.1+ 1069 function imagesavealpha(&$img, $blendmode=true) { 1070 // do nothing, this function is declared here just to 1071 // prevent runtime errors if GD2 is not available 1072 return true; 1073 } 1074} 1075