1<?php 2 3################################################################# 4# Copyright notice 5# 6# (c) 2013 Jérôme Schneider <mail@jeromeschneider.fr> 7# All rights reserved 8# 9# http://flake.codr.fr 10# 11# This script is part of the Flake project. The Flake 12# project is free software; you can redistribute it 13# and/or modify it under the terms of the GNU General Public 14# License as published by the Free Software Foundation; either 15# version 2 of the License, or (at your option) any later version. 16# 17# The GNU General Public License can be found at 18# http://www.gnu.org/copyleft/gpl.html. 19# 20# This script is distributed in the hope that it will be useful, 21# but WITHOUT ANY WARRANTY; without even the implied warranty of 22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23# GNU General Public License for more details. 24# 25# This copyright notice MUST APPEAR in all copies of the script! 26################################################################# 27 28namespace Flake\Util; 29 30class Tools extends \Flake\Core\FLObject { 31 private function __construct() { # private constructor to force static class 32 } 33 34 static function getCurrentUrl() { 35 if (MONGOOSE_SERVER) { 36 $sUrl = $GLOBALS["_SERVER"]["REQUEST_URI"]; 37 if (array_key_exists("QUERY_STRING", $GLOBALS["_SERVER"]) && trim($GLOBALS["_SERVER"]["QUERY_STRING"]) !== "") { 38 $sUrl .= "?" . $GLOBALS["_SERVER"]["QUERY_STRING"]; 39 } 40 } else { 41 $sUrl = $GLOBALS["_SERVER"]["REQUEST_URI"]; # Would be REDIRECT_URL for ServerRewrite 42 } 43 44 return $sUrl; 45 } 46 47 static function getCurrentProtocol() { 48 if (isset($GLOBALS['_SERVER']['HTTP_X_FORWARDED_PROTO']) && !empty($GLOBALS['_SERVER']['HTTP_X_FORWARDED_PROTO'])) { 49 return $GLOBALS['_SERVER']['HTTP_X_FORWARDED_PROTO']; 50 } 51 52 if ((!empty($GLOBALS["_SERVER"]["HTTPS"]) && $GLOBALS["_SERVER"]['HTTPS'] !== 'off') || intval($_SERVER['SERVER_PORT']) === 443) { 53 return "https"; 54 } 55 56 return "http"; 57 } 58 59 static function deCamelCase($sString, $sGlue = " ") { 60 $sSep = md5(rand()); 61 $sRes = preg_replace('/(?!^)[[:upper:]][[:lower:]]/', '$0', preg_replace('/(?!^)[[:upper:]]+/', $sSep . '$0', $sString)); 62 if ($sGlue !== "" && preg_match('/^[[:upper:]].*/', $sRes)) { 63 $sRes = $sSep . $sRes; 64 } 65 66 return str_replace($sSep, $sGlue, $sRes); 67 } 68 69 static function serverToRelativeWebPath($sAbsPath) { 70 return "/" . str_replace(PROJECT_PATH_WWWROOT, "", $sAbsPath); 71 } 72 73 static function view_array($array_in) { 74 if (is_array($array_in)) { 75 $result = '<table border="1" cellpadding="1" cellspacing="0" bgcolor="white">'; 76 if (!count($array_in)) { 77 $result .= '<tr><td><font face="Verdana,Arial" size="1"><b>' . htmlspecialchars("EMPTY!") . '</b></font></td></tr>'; 78 } 79 foreach ($array_in as $key => $val) { 80 $result .= '<tr><td valign="top"><font face="Verdana,Arial" size="1">' . htmlspecialchars((string) $key) . '</font></td><td>'; 81 if (is_array($array_in[$key])) { 82 $result .= \Flake\Util\Tools::view_array($array_in[$key]); 83 } else { 84 if (is_object($val)) { 85 if (method_exists($val, "__toString")) { 86 $sWhat = nl2br(htmlspecialchars((string) $val)); 87 } else { 88 $sWhat = nl2br(htmlspecialchars(get_class($val))); 89 } 90 } elseif (is_bool($val)) { 91 $sWhat = ($val === true ? "boolean:TRUE" : "boolean:FALSE"); 92 } else { 93 $sWhat = nl2br(htmlspecialchars((string) $val)); 94 } 95 96 $result .= '<font face="Verdana,Arial" size="1" color="red">' . $sWhat . '<br /></font>'; 97 } 98 99 $result .= '</td></tr>'; 100 } 101 $result .= '</table>'; 102 } else { 103 $result = '<table border="1" cellpadding="1" cellspacing="0" bgcolor="white"> 104 <tr> 105 <td><font face="Verdana,Arial" size="1" color="red">' . nl2br(htmlspecialchars((string) $array_in)) . '<br /></font></td> 106 </tr> 107 </table>'; // Output it as a string. 108 } 109 110 return $result; 111 } 112 113 static function debug($var = "", $brOrHeader = 0) { 114 if ($brOrHeader === 0) { 115 try { 116 $trail = debug_backtrace(); 117 $trail = array_reverse($trail); 118 array_pop($trail); // la ligne d'appel à debug 119 array_pop($trail); // la ligne d'appel à debug 120 $aLastNode = array_pop($trail); // l'appel qui nous intéresse 121 122 if (array_key_exists("class", $aLastNode)) { 123 $sClass = @strval($aLastNode["class"]); 124 } else { 125 $sClass = ""; 126 } 127 128 if (array_key_exists("type", $aLastNode)) { 129 $sType = @strval($aLastNode["type"]); 130 } else { 131 $sType = ""; 132 } 133 134 $brOrHeader = $sClass . $sType . @strval($aLastNode['function']); 135 } catch (\Exception $e) { 136 $brOrHeader = "Undetermined context"; 137 } 138 } 139 140 if ($brOrHeader) { 141 echo '<table border="0" cellpadding="0" cellspacing="0" bgcolor="white" style="border:0px; margin-top:3px; margin-bottom:3px;"><tr><td style="background-color:#bbbbbb; font-family: verdana,arial; font-weight: bold; font-size: 10px;">' . htmlspecialchars((string) $brOrHeader) . '</td></tr><tr><td>'; 142 } 143 144 if (is_array($var)) { 145 echo \Flake\Util\Tools::view_array($var); 146 } elseif (is_object($var)) { 147 echo '<b>|Object:<pre>'; 148 print_r($var); 149 echo '</pre>|</b>'; 150 } elseif ((string) $var != '') { 151 echo '<b>|' . htmlspecialchars((string) $var) . '|</b>'; 152 } else { 153 echo '<b>| debug |</b>'; 154 } 155 156 if ($brOrHeader) { 157 echo '</td></tr></table>'; 158 } 159 } 160 161 static function debug_trail() { 162 $trail = debug_backtrace(); 163 $trail = array_reverse($trail); 164 array_pop($trail); 165 166 $path = []; 167 foreach ($trail as $dat) { 168 $path[] = $dat['class'] . $dat['type'] . $dat['function']; 169 } 170 171 return implode(' // ', $path); 172 } 173 174 static function POST($sVar = false) { 175 if ($sVar !== false) { 176 $aData = \Flake\Util\Tools::POST(); 177 if (array_key_exists($sVar, $aData)) { 178 return $aData[$sVar]; 179 } 180 181 return ""; 182 } 183 184 return is_array($GLOBALS["_POST"]) ? $GLOBALS["_POST"] : []; 185 } 186 187 static function GET($sVar = false) { 188 if ($sVar !== false) { 189 $aData = \Flake\Util\Tools::GET(); 190 if (array_key_exists($sVar, $aData)) { 191 return $aData[$sVar]; 192 } 193 194 return ""; 195 } 196 197 return is_array($GLOBALS["_GET"]) ? $GLOBALS["_GET"] : []; 198 } 199 200 static function GP($sVar = false) { 201 if ($sVar !== false) { 202 $aData = \Flake\Util\Tools::GP(); 203 if (array_key_exists($sVar, $aData)) { 204 return $aData[$sVar]; 205 } 206 207 return ""; 208 } 209 210 return array_merge( 211 \Flake\Util\Tools::GET(), 212 \Flake\Util\Tools::POST() 213 ); 214 } 215 216 static function safelock($sString) { 217 return substr(md5(PROJECT_SAFEHASH_SALT . ":" . $sString), 0, 5); 218 } 219 220 static function redirect($sUrl) { 221 header("Location: " . $sUrl); 222 exit(0); 223 } 224 225 static function redirectUsingMeta($sUrl) { 226 $sDoc = "<html><head><meta http-equiv='refresh' content='0; url=" . $sUrl . "'></meta></head><body></body></html>"; 227 echo $sDoc; 228 exit(0); 229 } 230 231 static function refreshPage() { 232 header("Location: " . \Flake\Util\Tools::getCurrentUrl()); 233 exit(0); 234 } 235 236 static function validEmail($sEmail) { 237 return (filter_var($sEmail, FILTER_VALIDATE_EMAIL) !== false); 238 } 239 240 static function filterFormInput($sInput) { 241 return strip_tags($sInput); 242 } 243 244 static function getHumanDate($iStamp) { 245 return ucwords(strftime("%A, %d %B %Y", $iStamp)); 246 } 247 248 static function getHumanTime($iStamp) { 249 return strftime("%Hh%M", $iStamp); 250 } 251 252 static function trimExplode($string, $delim = ",", $removeEmptyValues = false, $limit = 0) { 253 $explodedValues = explode($delim, $string); 254 255 $result = array_map('trim', $explodedValues); 256 257 if ($removeEmptyValues) { 258 $temp = []; 259 foreach ($result as $value) { 260 if ($value !== '') { 261 $temp[] = $value; 262 } 263 } 264 $result = $temp; 265 } 266 267 if ($limit != 0) { 268 if ($limit < 0) { 269 $result = array_slice($result, 0, $limit); 270 } elseif (count($result) > $limit) { 271 $lastElements = array_slice($result, $limit - 1); 272 $result = array_slice($result, 0, $limit - 1); 273 $result[] = implode($delim, $lastElements); 274 } 275 } 276 277 return $result; 278 } 279 280 /** 281 * Taken from TYPO3 282 * Returns true if the first part of $str matches the string $partStr. 283 * 284 * @param string Full string to check 285 * @param string Reference string which must be found as the "first part" of the full string 286 * 287 * @return bool True if $partStr was found to be equal to the first part of $str 288 */ 289 static function isFirstPartOfStr($str, $partStr) { 290 // Returns true, if the first part of a $str equals $partStr and $partStr is not '' 291 $psLen = strlen($partStr); 292 if ($psLen) { 293 return substr($str, 0, $psLen) == (string) $partStr; 294 } else { 295 return false; 296 } 297 } 298 299 /** 300 * Binary-reads a file. 301 * 302 * @param string $sPath: absolute server path to file 303 * 304 * @return string file contents 305 */ 306 static function file_readBin($sPath) { 307 $sData = ""; 308 $rFile = fopen($sPath, "rb"); 309 while (!feof($rFile)) { 310 $sData .= fread($rFile, 1024); 311 } 312 fclose($rFile); 313 314 return $sData; 315 } 316 317 /** 318 * Binary-writes a file. 319 * 320 * @param string $sPath: absolute server path to file 321 * @param string $sData: file contents 322 * @param bool $bUTF8: add UTF8-BOM or not ? 323 * 324 * @return void 325 */ 326 static function file_writeBin($sPath, $sData) { 327 $rFile = fopen($sPath, "wb"); 328 fputs($rFile, $sData); 329 fclose($rFile); 330 } 331 332 static function sendHtmlMail($sToAddress, $sSubject, $sBody, $sFromName, $sFromAddress, $sReplyToName, $sReplyToAddress) { 333 $sMessage = <<<TEST 334<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 335<html> 336 <head> 337 <title>Email</title> 338 </head> 339 <body> 340 {$sBody} 341 </body> 342</html> 343TEST; 344 345 $sHeaders = "From: " . $sFromName . "<" . $sFromAddress . ">" . "\r\n"; 346 $sHeaders .= "Reply-To: " . $sReplyToName . "<" . $sReplyToAddress . ">" . "\r\n"; 347 $sHeaders .= "Bcc: " . $sReplyToName . "<" . $sReplyToAddress . ">" . "\r\n"; 348 $sHeaders .= "Content-Type: text/html" . "\r\n"; 349 350 mail($sToAddress, $sSubject, $sMessage, $sHeaders); 351 } 352 353 static function shortMD5($sValue) { 354 return strtolower(substr(md5($sValue), 0, 5)); 355 } 356 357 static function overrideFirstWithSecond($sFirst, $sSecond) { 358 if (trim($sSecond) !== "") { 359 return $sSecond; 360 } 361 362 return "" . $sFirst; 363 } 364 365 static function parseTemplateCode($sCode, $aMarkers) { 366 $tplName = md5($sCode); 367 $loader = new \Twig\Loader\ArrayLoader([$tplName => $sCode]); 368 $env = new \Twig\Environment($loader); 369 $env->setCache(false); 370 371 return $env->render($tplName, $aMarkers); 372 } 373 374 static function is_a($object, $class) { 375 if (is_object($object)) { 376 return $object instanceof $class; 377 } 378 if (is_string($object)) { 379 if (is_object($class)) { 380 $class = get_class($class); 381 } 382 383 if (class_exists($class, true)) { # TRUE to autoload class 384 return @is_subclass_of($object, $class) || $object == $class; 385 } 386 387 if (interface_exists($class)) { 388 $reflect = new \ReflectionClass($object); 389 390 return $reflect->implementsInterface($class); 391 } 392 } 393 394 return false; 395 } 396 397 static function HTTPStatus($iCode, $sMessage) { 398 header("HTTP/1.1 404 Not Found"); 399 header("Status: 404 Not Found"); 400 exit("<h1>HTTP Status " . $iCode . " : " . $sMessage . "</h1>"); 401 } 402 403 static function number2Rank($a) { 404 $a = intval($a); 405 406 if ($a === 1) { 407 return "premier"; 408 } elseif ($a === 2) { 409 return "second"; 410 } 411 412 $sNumber = self::number2Human($a); 413 414 $sLastLetter = substr($sNumber, -1, 1); 415 if ($sLastLetter === "e") { 416 $sNumber = substr($sNumber, 0, -1); 417 } elseif ($sLastLetter === "q") { 418 $sNumber = $sNumber . "u"; 419 } elseif ($sLastLetter === "f") { 420 $sNumber = substr($sNumber, 0, -1) . "v"; 421 } 422 423 return $sNumber . "ième"; 424 } 425 426 static function number2Human($a) { 427 $temp = explode('.', $a); 428 if (isset($temp[1]) && $temp[1] != '') { 429 return self::number2Human($temp[0]) . ' virgule ' . self::number2Human($temp[1]); 430 } 431 432 if ($a < 0) { 433 return 'moins ' . self::number2Human(-$a); 434 } 435 436 if ($a < 17) { 437 switch ($a) { 438 case 0: return 'zero'; 439 case 1: return 'un'; 440 case 2: return 'deux'; 441 case 3: return 'trois'; 442 case 4: return 'quatre'; 443 case 5: return 'cinq'; 444 case 6: return 'six'; 445 case 7: return 'sept'; 446 case 8: return 'huit'; 447 case 9: return 'neuf'; 448 case 10: return 'dix'; 449 case 11: return 'onze'; 450 case 12: return 'douze'; 451 case 13: return 'treize'; 452 case 14: return 'quatorze'; 453 case 15: return 'quinze'; 454 case 16: return 'seize'; 455 } 456 } elseif ($a < 20) { 457 return 'dix-' . self::number2Human($a - 10); 458 } elseif ($a < 100) { 459 if ($a % 10 == 0) { 460 switch ($a) { 461 case 20: return 'vingt'; 462 case 30: return 'trente'; 463 case 40: return 'quarante'; 464 case 50: return 'cinquante'; 465 case 60: return 'soixante'; 466 case 70: return 'soixante-dix'; 467 case 80: return 'quatre-vingt'; 468 case 90: return 'quatre-vingt-dix'; 469 } 470 } elseif (substr($a, -1) == 1) { 471 if (((int) ($a / 10) * 10) < 70) { 472 return self::number2Human((int) ($a / 10) * 10) . '-et-un'; 473 } elseif ($a == 71) { 474 return 'soixante-et-onze'; 475 } elseif ($a == 81) { 476 return 'quatre-vingt-un'; 477 } elseif ($a == 91) { 478 return 'quatre-vingt-onze'; 479 } 480 } elseif ($a < 70) { 481 return self::number2Human($a - $a % 10) . '-' . self::number2Human($a % 10); 482 } elseif ($a < 80) { 483 return self::number2Human(60) . '-' . self::number2Human($a % 20); 484 } else { 485 return self::number2Human(80) . '-' . self::number2Human($a % 20); 486 } 487 } elseif ($a == 100) { 488 return 'cent'; 489 } elseif ($a < 200) { 490 return self::number2Human(100) . ' ' . self::number2Human($a % 100); 491 } elseif ($a < 1000) { 492 return self::number2Human((int) ($a / 100)) . ' ' . self::number2Human(100) . ' ' . self::number2Human($a % 100); 493 } elseif ($a == 1000) { 494 return 'mille'; 495 } elseif ($a < 2000) { 496 return self::number2Human(1000) . ' ' . self::number2Human($a % 1000) . ' '; 497 } elseif ($a < 1000000) { 498 return self::number2Human((int) ($a / 1000)) . ' ' . self::number2Human(1000) . ' ' . self::number2Human($a % 1000); 499 } 500 } 501 502 static function stringToUrlToken($sString) { 503 # Taken from TYPO3 extension realurl 504 505 $space = "-"; 506 $sString = strtr($sString, ' -+_\'', $space . $space . $space . $space . $space); // convert spaces 507 508 # De-activated; @see https://github.com/netgusto/Baikal/issues/244 509 #if(function_exists("iconv")) { 510 # $sString = iconv('UTF-8', 'ASCII//TRANSLIT', $sString); 511 #} 512 513 $sString = strtolower($sString); 514 515 $sString = preg_replace('/[^a-zA-Z0-9\\' . $space . ']/', '', $sString); 516 $sString = preg_replace('/\\' . $space . '{2,}/', $space, $sString); // Convert multiple 'spaces' to a single one 517 $sString = trim($sString, $space); 518 519 return $sString; 520 } 521 522 static function isCliPhp() { 523 return strtolower(php_sapi_name()) === "cli"; 524 } 525 526 static function getIP() { 527 $alt_ip = $_SERVER['REMOTE_ADDR']; 528 529 if (isset($_SERVER['HTTP_CLIENT_IP'])) { 530 $alt_ip = $_SERVER['HTTP_CLIENT_IP']; 531 } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) and preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) { 532 // make sure we dont pick up an internal IP defined by RFC1918 533 foreach ($matches[0] as $ip) { 534 if (!preg_match('#^(10|172\.16|192\.168)\.#', $ip)) { 535 $alt_ip = $ip; 536 break; 537 } 538 } 539 } elseif (isset($_SERVER['HTTP_FROM'])) { 540 $alt_ip = $_SERVER['HTTP_FROM']; 541 } 542 543 return $alt_ip; 544 } 545 546 static function getUserAgent() { 547 return $_SERVER['HTTP_USER_AGENT']; 548 } 549 550 ########### 551 static function appendSlash($sString) { 552 return self::appendString($sString, "/"); 553 } 554 555 static function prependSlash($sString) { 556 return self::prependString($sString, "/"); 557 } 558 559 static function stripBeginSlash($sString) { 560 return self::stripBeginString($sString, "/"); 561 } 562 563 static function stripEndSlash($sString) { 564 return self::stripEndString($sString, "/"); 565 } 566 567 static function trimSlashes($sString) { 568 return self::stripBeginSlash(self::stripEndSlash($sString)); 569 } 570 571 ########### 572 static function appendString($sString, $sAppend) { 573 if (substr($sString, -1 * strlen($sAppend)) !== $sAppend) { 574 $sString .= $sAppend; 575 } 576 577 return $sString; 578 } 579 580 static function prependString($sString, $sAppend) { 581 if (substr($sString, 0, 1 * strlen($sAppend)) !== $sAppend) { 582 $sString = $sAppend . $sString; 583 } 584 585 return $sString; 586 } 587 588 static function stripBeginString($sString, $sAppend) { 589 if (substr($sString, 0, 1 * strlen($sAppend)) === $sAppend) { 590 $sString = substr($sString, strlen($sAppend)); 591 } 592 593 return $sString; 594 } 595 596 static function stripEndString($sString, $sAppend) { 597 if (substr($sString, -1 * strlen($sAppend)) === $sAppend) { 598 $sString = substr($sString, 0, -1 * strlen($sAppend)); 599 } 600 601 return $sString; 602 } 603 604 static function trimStrings($sString, $sAppend) { 605 return self::stripBeginString(self::stripEndString($sString, $sAppend), $sAppend); 606 } 607 608 static function stringEndsWith($sHaystack, $sNeedle) { 609 return substr($sHaystack, strlen($sNeedle) * -1) === $sNeedle; 610 } 611 612 ########### 613 614 static function router() { 615 return "\Flake\Util\Router\QuestionMarkRewrite"; 616 } 617 618 static function arrayIsAssoc($aArray) { 619 if (!is_array($aArray)) { 620 throw new \Exception("\Flake\Util\Tools::arrayIsAssoc(): parameter has to be an array."); 621 } 622 623 # Taken from http://stackoverflow.com/questions/173400/php-arrays-a-good-way-to-check-if-an-array-is-associative-or-sequential#answer-4254008 624 # count() will return 0 if numeric, and > 0 if assoc, even partially 625 return (bool) count(array_filter(array_keys($aArray), 'is_string')); 626 } 627 628 static function arrayIsSeq($aArray) { 629 return !self::arrayIsAssoc($aArray); 630 } 631 632 static function echoAndCutClient($sMessage = '') { 633 ignore_user_abort(true); 634 # set_time_limit(0); 635 636 header("Connection: close"); 637 header("Content-Length: " . strlen($sMessage)); 638 echo $sMessage; 639 echo str_repeat("\r\n", 10); // just to be sure 640 flush(); 641 } 642 643 static function milliseconds() { 644 return intval((microtime(true) * 1000)); 645 } 646 647 static function stopWatch($sWhat) { 648 # return; 649 $iStop = \Flake\Util\Tools::milliseconds(); 650 651 $trail = debug_backtrace(); 652 $aLastNode = $trail[0]; // l'appel qui nous intéresse 653 $sFile = basename($aLastNode["file"]); 654 $iLine = intval($aLastNode["line"]); 655 656 if (!array_key_exists("FLAKE_STOPWATCHES", $GLOBALS)) { 657 $GLOBALS["FLAKE_STOPWATCHES"] = []; 658 } 659 660 if (!array_key_exists($sWhat, $GLOBALS["FLAKE_STOPWATCHES"])) { 661 $GLOBALS["FLAKE_STOPWATCHES"][$sWhat] = $iStop; 662 } else { 663 $iTime = $iStop - $GLOBALS["FLAKE_STOPWATCHES"][$sWhat]; 664 echo "<h3 style='color: silver'><span style='display: inline-block; width: 400px;'>@" . $sFile . "+" . $iLine . ":</span>" . $sWhat . ":" . $iTime . " ms</h1>"; 665 flush(); 666 } 667 } 668 669 # Taken from http://www.php.net/manual/en/function.gzdecode.php#82930 670 static function gzdecode($data, &$filename = '', &$error = '', $maxlength = null) { 671 $len = strlen($data); 672 if ($len < 18 || strcmp(substr($data, 0, 2), "\x1f\x8b")) { 673 $error = "Not in GZIP format."; 674 675 return null; // Not GZIP format (See RFC 1952) 676 } 677 $method = ord(substr($data, 2, 1)); // Compression method 678 $flags = ord(substr($data, 3, 1)); // Flags 679 if ($flags & 31 != $flags) { 680 $error = "Reserved bits not allowed."; 681 682 return null; 683 } 684 // NOTE: $mtime may be negative (PHP integer limitations) 685 $mtime = unpack("V", substr($data, 4, 4)); 686 $mtime = $mtime[1]; 687 $xfl = substr($data, 8, 1); 688 $os = substr($data, 8, 1); 689 $headerlen = 10; 690 $extralen = 0; 691 $extra = ""; 692 if ($flags & 4) { 693 // 2-byte length prefixed EXTRA data in header 694 if ($len - $headerlen - 2 < 8) { 695 return false; // invalid 696 } 697 $extralen = unpack("v", substr($data, 8, 2)); 698 $extralen = $extralen[1]; 699 if ($len - $headerlen - 2 - $extralen < 8) { 700 return false; // invalid 701 } 702 $extra = substr($data, 10, $extralen); 703 $headerlen += 2 + $extralen; 704 } 705 $filenamelen = 0; 706 $filename = ""; 707 if ($flags & 8) { 708 // C-style string 709 if ($len - $headerlen - 1 < 8) { 710 return false; // invalid 711 } 712 $filenamelen = strpos(substr($data, $headerlen), chr(0)); 713 if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) { 714 return false; // invalid 715 } 716 $filename = substr($data, $headerlen, $filenamelen); 717 $headerlen += $filenamelen + 1; 718 } 719 $commentlen = 0; 720 $comment = ""; 721 if ($flags & 16) { 722 // C-style string COMMENT data in header 723 if ($len - $headerlen - 1 < 8) { 724 return false; // invalid 725 } 726 $commentlen = strpos(substr($data, $headerlen), chr(0)); 727 if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) { 728 return false; // Invalid header format 729 } 730 $comment = substr($data, $headerlen, $commentlen); 731 $headerlen += $commentlen + 1; 732 } 733 $headercrc = ""; 734 if ($flags & 2) { 735 // 2-bytes (lowest order) of CRC32 on header present 736 if ($len - $headerlen - 2 < 8) { 737 return false; // invalid 738 } 739 $calccrc = crc32(substr($data, 0, $headerlen)) & 0xffff; 740 $headercrc = unpack("v", substr($data, $headerlen, 2)); 741 $headercrc = $headercrc[1]; 742 if ($headercrc != $calccrc) { 743 $error = "Header checksum failed."; 744 745 return false; // Bad header CRC 746 } 747 $headerlen += 2; 748 } 749 // GZIP FOOTER 750 $datacrc = unpack("V", substr($data, -8, 4)); 751 $datacrc = sprintf('%u', $datacrc[1] & 0xFFFFFFFF); 752 $isize = unpack("V", substr($data, -4)); 753 $isize = $isize[1]; 754 // decompression: 755 $bodylen = $len - $headerlen - 8; 756 if ($bodylen < 1) { 757 // IMPLEMENTATION BUG! 758 return null; 759 } 760 $body = substr($data, $headerlen, $bodylen); 761 $data = ""; 762 if ($bodylen > 0) { 763 switch ($method) { 764 case 8: 765 // Currently the only supported compression method: 766 $data = gzinflate($body, $maxlength); 767 break; 768 default: 769 $error = "Unknown compression method."; 770 771 return false; 772 } 773 } // zero-byte body content is allowed 774 // Verifiy CRC32 775 $crc = sprintf("%u", crc32($data)); 776 $crcOK = $crc == $datacrc; 777 $lenOK = $isize == strlen($data); 778 if (!$lenOK || !$crcOK) { 779 $error = ($lenOK ? '' : 'Length check FAILED. ') . ($crcOK ? '' : 'Checksum FAILED.'); 780 781 return false; 782 } 783 784 return $data; 785 } 786} 787