#!/usr/bin/env php 2, 'c' => 'text/plain', 'cc' => 'text/plain', 'cpp' => 'text/plain', 'c++' => 'text/plain', 'dtd' => 'text/plain', 'h' => 'text/plain', 'log' => 'text/plain', 'rng' => 'text/plain', 'txt' => 'text/plain', 'xsd' => 'text/plain', 'php' => 1, 'inc' => 1, 'avi' => 'video/avi', 'bmp' => 'image/bmp', 'css' => 'text/css', 'gif' => 'image/gif', 'htm' => 'text/html', 'html' => 'text/html', 'htmls' => 'text/html', 'ico' => 'image/x-ico', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'js' => 'application/x-javascript', 'midi' => 'audio/midi', 'mid' => 'audio/midi', 'mod' => 'audio/mod', 'mov' => 'movie/quicktime', 'mp3' => 'audio/mp3', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'pdf' => 'application/pdf', 'png' => 'image/png', 'swf' => 'application/shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'wav' => 'audio/wav', 'xbm' => 'image/xbm', 'xml' => 'text/xml', ); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); $basename = basename(__FILE__); if (!strpos($_SERVER['REQUEST_URI'], $basename)) { chdir(Extract_Phar::$temp); include $web; return; } $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); if (!$pt || $pt == '/') { $pt = $web; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); exit; } $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { header('HTTP/1.0 404 Not Found'); echo "\n \n File Not Found<title>\n </head>\n <body>\n <h1>404 - File ", $pt, " Not Found</h1>\n </body>\n</html>"; exit; } $b = pathinfo($a); if (!isset($b['extension'])) { header('Content-Type: text/plain'); header('Content-Length: ' . filesize($a)); readfile($a); exit; } if (isset($mimes[$b['extension']])) { if ($mimes[$b['extension']] === 1) { include $a; exit; } if ($mimes[$b['extension']] === 2) { highlight_file($a); exit; } header('Content-Type: ' .$mimes[$b['extension']]); header('Content-Length: ' . filesize($a)); readfile($a); exit; } } class Extract_Phar { static $temp; static $origdir; const GZ = 0x1000; const BZ2 = 0x2000; const MASK = 0x3000; const START = 'index.php'; const LEN = 6685; static function go($return = false) { $fp = fopen(__FILE__, 'rb'); fseek($fp, self::LEN); $L = unpack('V', $a = (binary)fread($fp, 4)); $m = (binary)''; do { $read = 8192; if ($L[1] - strlen($m) < 8192) { $read = $L[1] - strlen($m); } $last = (binary)fread($fp, $read); $m .= $last; } while (strlen($last) && strlen($m) < $L[1]); if (strlen($m) < $L[1]) { die('ERROR: manifest length read was "' . strlen($m) .'" should be "' . $L[1] . '"'); } $info = self::_unpack($m); $f = $info['c']; if ($f & self::GZ) { if (!function_exists('gzinflate')) { die('Error: zlib extension is not enabled -' . ' gzinflate() function needed for zlib-compressed .phars'); } } if ($f & self::BZ2) { if (!function_exists('bzdecompress')) { die('Error: bzip2 extension is not enabled -' . ' bzdecompress() function needed for bz2-compressed .phars'); } } $temp = self::tmpdir(); if (!$temp || !is_writable($temp)) { $sessionpath = session_save_path(); if (strpos ($sessionpath, ";") !== false) $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); if (!file_exists($sessionpath) || !is_dir($sessionpath)) { die('Could not locate temporary directory to extract phar'); } $temp = $sessionpath; } $temp .= '/pharextract/'.basename(__FILE__, '.phar'); self::$temp = $temp; self::$origdir = getcwd(); @mkdir($temp, 0777, true); $temp = realpath($temp); if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { self::_removeTmpFiles($temp, getcwd()); @mkdir($temp, 0777, true); @file_put_contents($temp . '/' . md5_file(__FILE__), ''); foreach ($info['m'] as $path => $file) { $a = !file_exists(dirname($temp . '/' . $path)); @mkdir(dirname($temp . '/' . $path), 0777, true); clearstatcache(); if ($path[strlen($path) - 1] == '/') { @mkdir($temp . '/' . $path, 0777); } else { file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); @chmod($temp . '/' . $path, 0666); } } } chdir($temp); if (!$return) { include self::START; } } static function tmpdir() { if (strpos(PHP_OS, 'WIN') !== false) { if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { return $var; } if (is_dir('/temp') || mkdir('/temp')) { return realpath('/temp'); } return false; } if ($var = getenv('TMPDIR')) { return $var; } return realpath('/tmp'); } static function _unpack($m) { $info = unpack('V', substr($m, 0, 4)); $l = unpack('V', substr($m, 10, 4)); $m = substr($m, 14 + $l[1]); $s = unpack('V', substr($m, 0, 4)); $o = 0; $start = 4 + $s[1]; $ret['c'] = 0; for ($i = 0; $i < $info[1]; $i++) { $len = unpack('V', substr($m, $start, 4)); $start += 4; $savepath = substr($m, $start, $len[1]); $start += $len[1]; $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] & 0xffffffff); $ret['m'][$savepath][7] = $o; $o += $ret['m'][$savepath][2]; $start += 24 + $ret['m'][$savepath][5]; $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; } return $ret; } static function extractFile($path, $entry, $fp) { $data = ''; $c = $entry[2]; while ($c) { if ($c < 8192) { $data .= @fread($fp, $c); $c = 0; } else { $c -= 8192; $data .= @fread($fp, 8192); } } if ($entry[4] & self::GZ) { $data = gzinflate($data); } elseif ($entry[4] & self::BZ2) { $data = bzdecompress($data); } if (strlen($data) != $entry[0]) { die("Invalid internal .phar file (size error " . strlen($data) . " != " . $stat[7] . ")"); } if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) { die("Invalid internal .phar file (checksum error)"); } return $data; } static function _removeTmpFiles($temp, $origdir) { chdir($temp); foreach (glob('*') as $f) { if (file_exists($f)) { is_dir($f) ? @rmdir($f) : @unlink($f); if (file_exists($f) && is_dir($f)) { self::_removeTmpFiles($f, getcwd()); } } } @rmdir($temp); clearstatcache(); chdir($origdir); } } Extract_Phar::go(); __HALT_COMPILER(); ?> ���������� ���hart.phar���� ���index.phpS ��XS ��dUI"���������Hart.php =��X =�����������ConsoleColorText.php,��X,��H-:���������version���X���zf������ ���style.css���X���T:ն������<?php define('APPLICATION_VERSION', file_get_contents(__DIR__ . '/version')); define('APPLICATION_ID', 'HaRT (htaccess redirect test) version ' . APPLICATION_VERSION); date_default_timezone_set('Europe/Kiev'); if (!($zf2Path = getenv('ZF2_PATH'))) { if (!($zf2Path = get_cfg_var('zf2_path'))) { if (!($zf2Path = realpath('/usr/local/share/php-libs/latest/ZF2'))) { die('Zend Framework 2 path is undefined' . PHP_EOL); } } } include $zf2Path . '/library/Zend/Loader/AutoloaderFactory.php'; Zend\Loader\AutoloaderFactory::factory([ 'Zend\Loader\StandardAutoloader' => [ 'autoregister_zf' => true, ], ]); require_once __DIR__ . '/Hart.php'; // MAIN $opts = new Zend\Console\Getopt([ 'user|u-s' => 'Basic Auth user', 'line|l-s' => 'Line number or range, e.g: -l 10, -l 10:20, -l 10:', 'no-success' => 'Don\'t show success lines', 'password|p-s' => 'Basic Auth password', 'format|f-s' => 'format output (yt - YouTrack, html - HTML page, console - default)', 'version|v' => 'version', 'help|h' => 'help', ]); $opts->setOptionCallback('l', function ($line, Zend\Console\Getopt $opts) { $matches = []; if (preg_match('/^(\d+)\:?(\d+)?$/', $line, $matches)) { $start = $matches[1]; $end = isset($matches[2]) ? $matches[2] : null; if (null !== $end && $end <= $start) { return false; } } return true; }); $printHelp = function () use ($opts) { $message = $opts->getUsageMessage(); $message = str_replace('[ options ]', 'FILE [ options ]' . PHP_EOL, $message); echo APPLICATION_ID . PHP_EOL . PHP_EOL . $message . PHP_EOL . 'Example: hart.phar .htaccess -u tester -p test -f html > report.html' . PHP_EOL . PHP_EOL; }; try { $opts->parse(); } catch (Exception $e) { echo $e->getMessage(), PHP_EOL, PHP_EOL; $printHelp(); exit; } if ($opts->getOption('h')) { die($opts->getUsageMessage()); } if ($opts->getOption('v')) { die('Version: ' . APPLICATION_ID . PHP_EOL); } if (!($opts->getRemainingArgs())) { $printHelp(); exit; } $file = current($opts->getRemainingArgs()); $test = new Hart(); switch ($opts->getOption('f')) { case 'yt': $test->setFormat(Hart::FORMAT_YOUTRACK); break; case 'html': $test->setFormat(Hart::FORMAT_HTML); break; case '': case 'console': $test->setFormat(Hart::FORMAT_CONSOLE); break; default: die('Invalid format: ' . $opts->getOption('format')); } if ($opts->getOption('no-success')) { $test->setNoSuccess(true); } if (($line = $opts->getOption('l'))) { $test->setFilterLines($line); } if (($user = $opts->getOption('u')) && ($password = $opts->getOption('p'))) { $test->setCredentials($user, $password); } $test->run($file); echo PHP_EOL; <?php use Zend\Http\Client; use Zend\Http\Request; require_once(__DIR__ . '/ConsoleColorText.php'); class Hart { const FORMAT_CONSOLE = 'console'; const FORMAT_YOUTRACK = 'youTrack'; const FORMAT_HTML = 'html'; const TYPE_SRC_URL = 'SRC URL'; const TYPE_DST_URL = 'DST URL'; const TYPE_DST_URL_MATCH = 'DST Url Match'; const STATUS_SUCCESS = 'success'; const STATUS_FAILED = 'failed'; const STATUS_WARNING = 'warning'; const RULE_REGEX = '/^RewriteRule\s+\^([^\$]+)\$\s+([^\s]+)(?:\s+\[([^\s]+)\])/'; /** * @var Zend\Http\Client */ protected $client; protected $clientDefaultOptions = [ 'maxredirects' => 0, 'timeout' => 10, 'sslverifypeer' => false, ]; protected $validLocationUrlCodes = [301, 302, 200]; protected $warningLocationUrlCodes = [301]; protected $index; protected $line; protected $columns = [ 'line' => [ 'title' => 'Line', 'width' => 6, 'align' => 'center', ], 'testType' => [ 'title' => 'Test Type', 'width' => 15, ], 'url' => [ 'title' => 'URLs', 'width' => 122, ], 'message' => [ 'title' => 'Message', 'width' => 42, ], 'status' => [ 'title' => 'Status', 'width' => 9, ], ]; /** * @var ConsoleColorText */ protected $text; protected $_user; protected $_password; protected $format = self::FORMAT_CONSOLE; protected $noSuccess = false; protected $filterLines; function __construct($options = []) { $this->client = new Client(null, array_merge($this->clientDefaultOptions, $options)); $this->client->getRequest()->setMethod(Request::METHOD_HEAD); $this->text = new ConsoleColorText(); } public function getFormat() { return $this->format; } public function setFormat($format) { $this->format = $format; return $this; } /** * @return boolean */ public function isNoSuccess() { return $this->noSuccess; } /** * @param boolean $noSuccess * @return $this */ public function setNoSuccess($noSuccess) { $this->noSuccess = $noSuccess; return $this; } /** * @return mixed */ public function getFilterLines() { return $this->filterLines; } /** * @param mixed $filterLines * @return $this */ public function setFilterLines($filterLines) { $this->filterLines = $this->parseFilterLines($filterLines); return $this; } protected function parseFilterLines($filterLines) { $matches = []; if (!preg_match('/^(\d+)(\:)?(\d+)?$/', $filterLines, $matches)) { return null; } $start = $matches[1]; $end = isset($matches[3]) ? $matches[3] : null; return [$start, isset($matches[2]) ? $end : $start]; } public function head($url) { $this->_initCredentials(); return $this->client->setUri($url)->send(); } public function validateRedirect($sourceUrl, $destinationUrl, array $flags = []) { if (empty($flags['R'])) { $this->logError(self::TYPE_DST_URL, $destinationUrl, 'R flag is undefined'); return false; } $response = $this->head($sourceUrl); // Validate Source URL Redirect Code if ($flags['R'] != ($code = $response->getStatusCode())) { $this->logError( self::TYPE_SRC_URL, $this->_formatUrl($sourceUrl), 'HTTP ' . $code . ' (' . $flags['R'] . ' expected)' ); return false; } $this->logSuccess(self::TYPE_SRC_URL, $this->_formatUrl($sourceUrl), 'HTTP ' . $code); if ($this->areUrlEqual($sourceUrl, $destinationUrl)) { $this->logError(self::TYPE_DST_URL, $destinationUrl, 'infinite loop'); return false; } /** @var $location Zend\Http\Header\Location */ if (!($location = $response->getHeaders()->get('Location')) || !($locationUrl = $location->getFieldValue())) { $this->logError(self::TYPE_SRC_URL, $this->_formatUrl($sourceUrl), '"Location" header is undefined'); return false; } // Validate Location $locationResponse = $this->head($locationUrl); if (!($isValidLocationCode = in_array($locationResponse->getStatusCode(), $this->validLocationUrlCodes))) { $this->logError( self::TYPE_DST_URL, ' --> ' . $this->_formatUrl($locationUrl), 'unexpected HTTP code ' . $locationResponse->getStatusCode() ); } else { $message = 'HTTP ' . $locationResponse->getStatusCode(); if (in_array($locationResponse->getStatusCode(), $this->warningLocationUrlCodes)) { $this->logWarning(self::TYPE_DST_URL, ' --> ' . $this->_formatUrl($locationUrl), $message); } else { $this->logSuccess(self::TYPE_DST_URL, ' --> ' . $this->_formatUrl($locationUrl), $message); } } if (!$this->areUrlEqual($locationUrl, $destinationUrl)) { $this->logError(self::TYPE_DST_URL_MATCH, ' --> ' . $this->_formatUrl($destinationUrl)); } else { $this->logSuccess(self::TYPE_DST_URL_MATCH, ' --> ' . $this->_formatUrl($destinationUrl)); } return true; } protected function logWarning($testType, $url, $message = null) { return $this->log($testType, $url, $this->_formatWarning($message), $this->_formatWarning(self::STATUS_WARNING)); } protected function logError($testType, $url, $message = null) { return $this->log($testType, $url, $this->_formatError($message), $this->_formatError(self::STATUS_FAILED)); } protected function logSuccess($testType, $url, $message = null) { if ($this->isNoSuccess()) { return $this; } return $this->log($testType, $url, $this->_formatSuccess($message), $this->_formatSuccess(self::STATUS_SUCCESS)); } protected function _formatError($text) { switch ($this->getFormat()) { case self::FORMAT_YOUTRACK: return '{color:red}' . $text . '{color}'; case self::FORMAT_CONSOLE: return $this->text->paint($text, 'red'); case self::FORMAT_HTML: return '<span style="color:red">' . htmlspecialchars($text) . '</span>'; } return new Exception('Undefined Format: ' . $this->getFormat()); } protected function _formatWarning($text) { switch ($this->getFormat()) { case self::FORMAT_YOUTRACK: return '{color:orange}' . $text . '{color}'; case self::FORMAT_CONSOLE: return $this->text->paint($text, 'orange'); case self::FORMAT_HTML: return '<span style="color:orange">' . htmlspecialchars($text) . '</span>'; } return new Exception('Undefined Format: ' . $this->getFormat()); } protected function _formatSuccess($text) { switch ($this->getFormat()) { case self::FORMAT_YOUTRACK: return '{color:green}' . $text . '{color}'; case self::FORMAT_CONSOLE: return $this->text->paint($text, 'green'); case self::FORMAT_HTML: return '<span style="color:green">' . htmlspecialchars($text) . '</span>'; } return new Exception('Undefined Format: ' . $this->getFormat()); } protected function areUrlEqual($locationUrl, $destinationUrl) { if ('?' == substr($destinationUrl, -1)) { $destinationUrl = substr($destinationUrl, 0, -1); } return rawurldecode($locationUrl) == rawurldecode($destinationUrl); } public function log($testType, $url, $message, $status) { $line = $this->line + 1; $values = compact('line', 'testType', 'url', 'message', 'status'); $columns = []; foreach ($this->columns as $name => $column) { $align = array_key_exists('align', $column) ? $column['align'] : null; $value = $values[$name]; switch ($this->getFormat()) { case self::FORMAT_YOUTRACK: case self::FORMAT_CONSOLE: $columns[] = $this->sprint($value, $column['width'], $align); break; case self::FORMAT_HTML: $columns[] = $value; break; default: return new Exception('Undefined Format: ' . $this->getFormat()); } } switch ($this->getFormat()) { case self::FORMAT_YOUTRACK: echo implode('||', $columns) . PHP_EOL; break; case self::FORMAT_CONSOLE: echo implode('|', $columns) . PHP_EOL; break; case self::FORMAT_HTML: echo '<tr><td>' . implode('</td><td>', $columns) . '</td></tr>' . PHP_EOL; break; default: return new Exception('Undefined Format: ' . $this->getFormat()); } return $this; } protected function _formatUrl($url) { switch ($this->getFormat()) { case self::FORMAT_YOUTRACK: if (strlen($url) > 80) { $text = substr($url, 0, 10) . '...' . substr($url, -68); return '[' . $url . ' ' . $text . ']'; } return $url; case self::FORMAT_CONSOLE: return $url; case self::FORMAT_HTML: return '<a target="_blank" href="' . htmlspecialchars($url) . '">' . htmlspecialchars($url) . '</a>'; } return new Exception('Undefined Format: ' . $this->getFormat()); } protected function renderHeaders() { $columns = []; foreach ($this->columns as $column) { $align = array_key_exists('align', $column) ? $column['align'] : null; switch ($this->getFormat()) { case self::FORMAT_YOUTRACK: $columns[] = $this->sprint($column['title'], $column['width'], $align); break; case self::FORMAT_CONSOLE: $columns[] = $this->sprint($column['title'], $column['width'], $align); break; case self::FORMAT_HTML: $columns[] = htmlspecialchars($column['title']); break; default: return new Exception('Undefined Format: ' . $this->getFormat()); } } switch ($this->getFormat()) { case self::FORMAT_YOUTRACK: return implode('||', $columns) . PHP_EOL; break; case self::FORMAT_CONSOLE: return implode('|', $columns) . PHP_EOL; break; case self::FORMAT_HTML: return '<style>' . file_get_contents(__DIR__ . '/style.css') . '</style>' . '<table><tr><th>' . implode('</th><th>', $columns) . '</th></tr>' . PHP_EOL; break; default: return new Exception('Undefined Format: ' . $this->getFormat()); } } protected function sprint($text, $width, $align = null) { if (false !== (strpos($text, ConsoleColorText::START))) { $plainText = preg_replace("/\033\\[[\\d;]+m/", '', $text); $length = strlen($plainText); } else { $length = strlen($text); } switch ($align) { case 'center': $paddingLeft = floor(max($width - $length, 0) / 2); $paddingRight = ceil(max($width - $length, 0) / 2); break; case 'right': $paddingRight = ($width - $length > 1) ? 1 : 0; $paddingLeft = floor(max($width - $length - $paddingRight, 0)); break; case null: // break was intentionally omitted case 'left': // break was intentionally omitted default: $paddingLeft = ($width - $length > 1) ? 1 : 0; $paddingRight = max($width - $length - $paddingLeft, 0); } return str_repeat(' ', $paddingLeft) . $text . str_repeat(' ', $paddingRight); } public function run($file) { $srcUrls = []; $lines = file($file); if (($filterLines = $this->getFilterLines())) { list($start, $end) = $filterLines; if (null === $end) { $end = count($lines) - 1; } while (($start - 1) && array_key_exists($start - 1, $lines) && array_key_exists($start, $lines) && preg_match(self::RULE_REGEX, $lines[$start]) && 0 !== strpos($lines[$start - 1], '#') ) { $start--; } while (($start - 1) && array_key_exists($start - 1, $lines) && 0 === strpos($lines[$start - 1], '#')) { $start--; } while (array_key_exists($end, $lines) && trim($lines[$end])) { $end++; } if (array_key_exists($start, $lines)) { $lines = array_slice($lines, $start, $end - $start + 1, true); } else { $lines = []; } } $prevLine = null; echo $this->renderHeaders(); $this->index = 0; foreach ($lines as $this->line => $line) { $line = trim($line); if (empty($line)) { $srcUrls = []; continue; } if ($line[0] == '#' && ($url = trim(ltrim($line, '# '), "\n")) && 0 === strpos($url, 'http')) { $srcUrls[] = str_replace(' ', '%20', $url); } if (empty($srcUrls) || 0 !== strncmp($line, 'RewriteRule', 11)) { continue; } $matches = []; if (!preg_match(self::RULE_REGEX, $line, $matches)) { continue; } list(, , $substitution, $flags) = $matches; $this->index++; foreach ($srcUrls as $sourceUrl) { $this->validateRedirect($sourceUrl, $substitution, $this->_parseFlags($flags)); } $srcUrls = []; } if (self::FORMAT_HTML == $this->getFormat()) { echo '</table>'; } } protected function _parseFlags($value) { $fields = []; $parts = explode(',', $value); foreach ($parts as $part) { $list = explode('=', $part); $fields[$list[0]] = isset($list[1]) ? $list[1] : null; } return $fields; } protected function _initCredentials() { if (!empty($this->_user) && !empty($this->_password)) { $this->client->setAuth($this->_user, $this->_password); } return $this; } public function setCredentials($user, $password) { $this->_user = $user; $this->_password = $password; return $this; } } <?php class ConsoleColorText { const COLOR_BLACK = 'black'; const COLOR_GRAY = 'gray'; const COLOR_GREEN = 'green'; const COLOR_ORANGE = 'orange'; const COLOR_RED = 'red'; const START = "\033["; const END = "\033[0m"; private $colors = [ self::COLOR_BLACK => 0, self::COLOR_GREEN => 10, self::COLOR_ORANGE => 208, self::COLOR_RED => 196, ]; public function paint($string, $textColor = null, $backgroundColor = null, $attribute = 0) { if (false === ($color = $this->parseColor($textColor))) { trigger_error('Invalid Color "' . $textColor . '"', E_USER_WARNING); return $string; } $result = self::START . ((int) $attribute) . ';38;5;' . $color . 'm'; if (null !== $backgroundColor) { if (false === ($color = $this->parseColor($backgroundColor))) { trigger_error('Invalid Background Color "' . $backgroundColor . '"', E_USER_WARNING); return $result . $string . self::END; } else { $result .= self::START . '48;5;' . $color . 'm'; } } $result .= $string . self::END; return $result; } protected function parseColor($color) { if (null === $color) { return false; } if (is_numeric($color)) { return (int) $color; } if (array_key_exists($color, $this->colors)) { return $this->colors[$color]; } return false; } } 1.3.1table {border-collapse: collapse} table th {background-color: #DEDEDE} table td, table th {border: solid silver 1px; padding: 1px 5px} zREbp: lr���GBMB