From 34d9e81c56e14d131cedeb64d36dd70438da0739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=9C=E6=81=92?= <2323333339@qq.com> Date: Mon, 19 Apr 2021 19:21:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/files.php | 1113 ++++++++++++++++++++++++--------------------- 1 file changed, 588 insertions(+), 525 deletions(-) diff --git a/library/files.php b/library/files.php index d6c1f26..33d8071 100644 --- a/library/files.php +++ b/library/files.php @@ -6,7 +6,8 @@ ini_set('display_startup_errors', 1); error_reporting(E_ALL); // config -class config { +class config +{ // DEFAULT CONFIG // Only edit directly if it is a temporary installation. Settings added here will be lost when updating! @@ -104,16 +105,18 @@ class config { static $local_config_file = '_filesconfig.php'; // get config - private function get_config($path) { - if(empty($path) || !file_exists($path)) return array(); + private function get_config($path) + { + if (empty($path) || !file_exists($path)) return array(); $config = include $path; - return empty($config) || !is_array($config) ? array() : array_map(function($v){ - return is_string($v) ? trim($v) : $v; + return empty($config) || !is_array($config) ? array() : array_map(function ($v) { + return is_string($v) ? trim($v) : $v; }, $config); } // dump config - private function dump_config($local_config, $storage_path, $storage_config, $user_config, $user_valid){ + private function dump_config($local_config, $storage_path, $storage_config, $user_config, $user_valid) + { // invalid and duplicate arrays $user_invalid = array_diff_key($user_config, self::$default); @@ -135,8 +138,8 @@ class config { $output = ' $props) { $is_empty = empty($props['arr']); - if(isset($props['hide']) && $props['hide']) continue; - foreach (['username', 'password', 'allow_tasks', '__dir__', '__file__'] as $prop) if(isset($props['arr'][$prop]) && !empty($props['arr'][$prop]) && is_string($props['arr'][$prop])) $props['arr'][$prop] = '***'; + if (isset($props['hide']) && $props['hide']) continue; + foreach (['username', 'password', 'allow_tasks', '__dir__', '__file__'] as $prop) if (isset($props['arr'][$prop]) && !empty($props['arr'][$prop]) && is_string($props['arr'][$prop])) $props['arr'][$prop] = '***'; $export = $is_empty ? 'array ()' : var_export($props['arr'], true); $comment = preg_replace('/\n/', " [" . count($props['arr']) . "]\n", $props['comment'], 1); $var = isset($props['var']) ? $props['var'] . ' = ' : 'return '; @@ -150,17 +153,19 @@ class config { //public static function helloWorld() { - public static function save_config($config = array()){ + public static function save_config($config = array()) + { $save_config = array_intersect_key(array_replace(self::$storage_config, $config), self::$default); $export = preg_replace("/ '/", " //'", var_export(array_replace(self::$default, $save_config), true)); - foreach ($save_config as $key => $value) if($value !== self::$default[$key]) $export = str_replace("//'" . $key, "'" . $key, $export); + foreach ($save_config as $key => $value) if ($value !== self::$default[$key]) $export = str_replace("//'" . $key, "'" . $key, $export); return @file_put_contents(config::$storage_config_realpath, 'storage_path must be a unique dir.'); + if ($is_doc && $storage_realpath === self::$__dir__) error('storage_path must be a unique dir.'); self::$storage_config_realpath = $storage_realpath ? $storage_realpath . '/config/config.php' : false; self::$storage_config = self::get_config(self::$storage_config_realpath); @@ -182,14 +187,14 @@ class config { self::$config = array_replace(self::$default, $user_valid); // dump config and exit; - if(isset($_GET['config'])) self::dump_config($local_config, $storage_path, self::$storage_config, $user_config, $user_valid); + if (isset($_GET['config'])) self::dump_config($local_config, $storage_path, self::$storage_config, $user_config, $user_valid); // CDN assets self::$assets = self::$prod ? 'https://cdn.jsdelivr.net/npm/files.photo.gallery@' . self::$version . '/' : ''; // root self::$root = real_path(self::$config['root']); - if($is_doc && !self::$root) error('root dir "' . self::$config['root'] . '" does not exist.'); + if ($is_doc && !self::$root) error('root dir "' . self::$config['root'] . '" does not exist.'); // doc root self::$doc_root = real_path($_SERVER['DOCUMENT_ROOT']); @@ -201,56 +206,58 @@ class config { $image_cache = self::$config['image_resize_enabled'] && self::$config['image_resize_cache'] && self::$config['load_images'] ? true : false; // cache enabled - if($image_cache || self::$config['cache']){ + if ($image_cache || self::$config['cache']) { // create storage_path - if(empty($storage_realpath)){ + if (empty($storage_realpath)) { $storage_path = is_string($storage_path) ? rtrim($storage_path, '\/') : false; - if(empty($storage_path)) error('Invalid storage_path parameter.'); + if (empty($storage_path)) error('Invalid storage_path parameter.'); mkdir_or_error($storage_path); $storage_realpath = real_path($storage_path); - if(empty($storage_realpath)) error("storage_path $storage_path does not exist and can't be created."); + if (empty($storage_realpath)) error("storage_path $storage_path does not exist and can't be created."); } self::$storage_path = $storage_realpath; // storage path is within doc root - if(is_within_docroot(self::$storage_path)) self::$storage_is_within_doc_root = true; + if (is_within_docroot(self::$storage_path)) self::$storage_is_within_doc_root = true; // cache_path real path self::$cache_path = self::$storage_path . '/cache'; // create storage dirs - if($is_doc){ + if ($is_doc) { $create_dirs = [$storage_realpath . '/config']; - if($image_cache) $create_dirs[] = self::$cache_path . '/images'; - if(self::$config['cache']) array_push($create_dirs, self::$cache_path . '/folders', self::$cache_path . '/menu'); - foreach($create_dirs as $create_dir) mkdir_or_error($create_dir); + if ($image_cache) $create_dirs[] = self::$cache_path . '/images'; + if (self::$config['cache']) array_push($create_dirs, self::$cache_path . '/folders', self::$cache_path . '/menu'); + foreach ($create_dirs as $create_dir) mkdir_or_error($create_dir); } // create/update config file, with default parameters commented out. - if($is_doc && self::$storage_config_realpath && (!file_exists(self::$storage_config_realpath) || filemtime(self::$storage_config_realpath) < filemtime(__FILE__))) self::save_config(); + if ($is_doc && self::$storage_config_realpath && (!file_exists(self::$storage_config_realpath) || filemtime(self::$storage_config_realpath) < filemtime(__FILE__))) self::save_config(); // image resize cache direct - if(self::$config['image_resize_cache_direct'] && !self::$has_login && self::$config['load_images'] && self::$config['image_resize_cache'] && self::$config['image_resize_enabled'] && self::$storage_is_within_doc_root) self::$image_resize_cache_direct = true; + if (self::$config['image_resize_cache_direct'] && !self::$has_login && self::$config['load_images'] && self::$config['image_resize_cache'] && self::$config['image_resize_enabled'] && self::$storage_is_within_doc_root) self::$image_resize_cache_direct = true; } // image_resize_dimensions_retina - if(self::$config['image_resize_dimensions_retina'] && self::$config['image_resize_dimensions_retina'] > self::$config['image_resize_dimensions']) self::$image_resize_dimensions_retina = self::$config['image_resize_dimensions_retina']; + if (self::$config['image_resize_dimensions_retina'] && self::$config['image_resize_dimensions_retina'] > self::$config['image_resize_dimensions']) self::$image_resize_dimensions_retina = self::$config['image_resize_dimensions_retina']; // dirs hash self::$dirs_hash = substr(md5(self::$doc_root . self::$__dir__ . self::$root . self::$version . self::$config['cache_key'] . self::$image_resize_cache_direct . self::$config['files_exclude'] . self::$config['dirs_exclude']), 0, 6); // login // $is_doc - if(self::$has_login) check_login($is_doc); + if (self::$has_login) check_login($is_doc); } }; // login page -function login_page($is_login_attempt, $sidx, $is_logout, $client_hash){ +function login_page($is_login_attempt, $sidx, $is_logout, $client_hash) +{ ?> - - + + + @@ -259,12 +266,15 @@ function login_page($is_login_attempt, $sidx, $is_logout, $client_hash){ -
+ + +
+ - - + = 5.5 && !password_needs_rehash(config::$config['password'], PASSWORD_DEFAULT) ? password_verify(trim($_POST['fpassword']), config::$config['password']) : (trim($_POST['fpassword']) == config::$config['password'])) && - $_POST['client_hash'] === $client_hash && + if ( + $is_login_attempt && + trim($_POST['fusername']) == config::$config['username'] && + (phpversion() >= 5.5 && !password_needs_rehash(config::$config['password'], PASSWORD_DEFAULT) ? password_verify(trim($_POST['fpassword']), config::$config['password']) : (trim($_POST['fpassword']) == config::$config['password'])) && + $_POST['client_hash'] === $client_hash && $_POST['sidx'] === $sidx - ){ + ) { $_SESSION['login'] = $login_hash; - // display login page and exit + // display login page and exit } else { login_page($is_login_attempt, $sidx, $is_logout, $client_hash); } - // not logged in (images or post API requests), don't show form. - } else if(post('action')){ + // not logged in (images or post API requests), don't show form. + } else if (post('action')) { json_error('login'); - } else { error('You are not logged in.', 401); } @@ -341,63 +354,77 @@ function check_login($is_doc){ } // -function mkdir_or_error($path){ - if(!file_exists($path) && !mkdir($path, 0777, true)) error('Failed to create ' . $path, 500); +function mkdir_or_error($path) +{ + if (!file_exists($path) && !mkdir($path, 0777, true)) error('Failed to create ' . $path, 500); } -function real_path($path){ +function real_path($path) +{ $real_path = realpath($path); return $real_path ? str_replace('\\', '/', $real_path) : false; } -function root_relative($dir){ +function root_relative($dir) +{ return ltrim(substr($dir, strlen(config::$root)), '\/'); } -function root_absolute($dir){ +function root_absolute($dir) +{ return config::$root . ($dir ? '/' . $dir : ''); } -function is_within_path($path, $root){ +function is_within_path($path, $root) +{ return strpos($path . '/', $root . '/') === 0; } -function is_within_root($path){ +function is_within_root($path) +{ return is_within_path($path, config::$root); } -function is_within_docroot($path){ +function is_within_docroot($path) +{ return is_within_path($path, config::$doc_root); } -function get_folders_cache_path($name){ +function get_folders_cache_path($name) +{ return config::$cache_path . '/folders/' . $name . '.json'; } -function get_json_cache_url($name){ +function get_json_cache_url($name) +{ $file = get_folders_cache_path($name); return file_exists($file) ? get_url_path($file) : false; } -function get_dir_cache_path($dir, $mtime = false){ - if(!config::$config['cache'] || !$dir) return; +function get_dir_cache_path($dir, $mtime = false) +{ + if (!config::$config['cache'] || !$dir) return; return get_folders_cache_path(get_dir_cache_hash($dir, $mtime)); } -function get_dir_cache_hash($dir, $mtime = false){ +function get_dir_cache_hash($dir, $mtime = false) +{ return config::$dirs_hash . '.' . substr(md5($dir), 0, 6) . '.' . ($mtime ?: filemtime($dir)); } -function header_memory_time(){ +function header_memory_time() +{ return (isset($_SERVER['REQUEST_TIME_FLOAT']) ? round(microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'], 3) . 's, ' : '') . round(memory_get_peak_usage() / 1048576, 1) . 'M'; } // read file // todo: add files-date header -function read_file($path, $mime = 'image/jpeg', $msg = false, $props = false, $cache_headers = false){ - if(!$path || !file_exists($path)) return false; - if($mime == 'image/svg') $mime .= '+xml'; +function read_file($path, $mime = 'image/jpeg', $msg = false, $props = false, $cache_headers = false) +{ + if (!$path || !file_exists($path)) return false; + if ($mime == 'image/svg') $mime .= '+xml'; header('content-type: ' . $mime); - header('content-length: ' . filesize($path)); + header('content-length: ' . filesize($path)); header('content-disposition: filename="' . basename($path) . '"'); - if($msg) header('files-msg: ' . $msg . ' [' . ($props ? $props . ', ' : '') . header_memory_time() . ']'); - if($cache_headers) set_cache_headers(); - if(!is_readable($path) || !readfile($path)) error('Failed to read file ' . $path . '.', 400); + if ($msg) header('files-msg: ' . $msg . ' [' . ($props ? $props . ', ' : '') . header_memory_time() . ']'); + if ($cache_headers) set_cache_headers(); + if (!is_readable($path) || !readfile($path)) error('Failed to read file ' . $path . '.', 400); exit; } // get mime -function get_mime($path){ - if(function_exists('mime_content_type')){ +function get_mime($path) +{ + if (function_exists('mime_content_type')) { return mime_content_type($path); } else { return function_exists('finfo_file') ? finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path) : false; @@ -405,7 +432,8 @@ function get_mime($path){ } // set cache headers -function set_cache_headers(){ +function set_cache_headers() +{ $seconds = 31536000; // 1 year; header('expires: ' . gmdate('D, d M Y H:i:s', time() + $seconds) . ' GMT'); header("cache-control: public, max-age=$seconds, s-maxage=$seconds, immutable"); @@ -415,114 +443,119 @@ function set_cache_headers(){ } // get image cache path -function get_image_cache_path($path, $image_resize_dimensions, $filesize, $filemtime){ +function get_image_cache_path($path, $image_resize_dimensions, $filesize, $filemtime) +{ return config::$cache_path . '/images/' . substr(md5($path), 0, 6) . '.' . $filesize . '.' . $filemtime . '.' . $image_resize_dimensions . '.jpg'; } // is excluded -function is_exclude($path = false, $is_dir = true, $symlinked = false){ +function is_exclude($path = false, $is_dir = true, $symlinked = false) +{ // early exit - if(!$path || $path === config::$root) return; + if (!$path || $path === config::$root) return; // exclude files PHP application - if($path === config::$__file__) return true; + if ($path === config::$__file__) return true; // symlinks not allowed - if($symlinked && !config::$config['allow_symlinks']) return true; + if ($symlinked && !config::$config['allow_symlinks']) return true; // exclude storage path - if(config::$storage_path && is_within_path($path, config::$storage_path)) return true; + if (config::$storage_path && is_within_path($path, config::$storage_path)) return true; // dirs_exclude: check root relative dir path - if(config::$config['dirs_exclude']) { + if (config::$config['dirs_exclude']) { $dirname = $is_dir ? $path : dirname($path); - if($dirname !== config::$root && preg_match(config::$config['dirs_exclude'], substr($dirname, strlen(config::$root)))) return true; + if ($dirname !== config::$root && preg_match(config::$config['dirs_exclude'], substr($dirname, strlen(config::$root)))) return true; } // files_exclude: check vs basename - if(!$is_dir){ + if (!$is_dir) { $basename = basename($path); - if($basename === config::$local_config_file) return true; - if(config::$config['files_exclude'] && preg_match(config::$config['files_exclude'], $basename)) return true; + if ($basename === config::$local_config_file) return true; + if (config::$config['files_exclude'] && preg_match(config::$config['files_exclude'], $basename)) return true; } } // valid root path -function valid_root_path($path, $is_dir = false){ +function valid_root_path($path, $is_dir = false) +{ // invalid - if($path === false) return; - if(!$is_dir && empty($path)) return; // path cannot be empty if file - if($path && substr($path, -1) == '/') return; // path should never be root absolute or end with / + if ($path === false) return; + if (!$is_dir && empty($path)) return; // path cannot be empty if file + if ($path && substr($path, -1) == '/') return; // path should never be root absolute or end with / // absolute path may differ if path contains symlink $root_absolute = root_absolute($path); $real_path = real_path($root_absolute); // file does not exist - if(!$real_path) return; + if (!$real_path) return; // security checks if path contains symlink - if($root_absolute !== $real_path) { - if(strpos(($is_dir ? $path : dirname($path)), ':') !== false) return; // dir may not contain ':' - if(strpos($path, '..') !== false) return; // path may not contain '..' - if(is_exclude($root_absolute, $is_dir, true)) return; + if ($root_absolute !== $real_path) { + if (strpos(($is_dir ? $path : dirname($path)), ':') !== false) return; // dir may not contain ':' + if (strpos($path, '..') !== false) return; // path may not contain '..' + if (is_exclude($root_absolute, $is_dir, true)) return; } // nope - if(!is_readable($real_path)) return; // not readable - if($is_dir && !is_dir($real_path)) return; // dir check - if(!$is_dir && !is_file($real_path)) return; // file check - if(is_exclude($real_path, $is_dir)) return; // exclude path + if (!is_readable($real_path)) return; // not readable + if ($is_dir && !is_dir($real_path)) return; // dir check + if (!$is_dir && !is_file($real_path)) return; // file check + if (is_exclude($real_path, $is_dir)) return; // exclude path // return root_absolute return $root_absolute; } // image create from -function image_create_from($path, $type){ - if(!$path || !$type) return; - if($type === IMAGETYPE_JPEG){ +function image_create_from($path, $type) +{ + if (!$path || !$type) return; + if ($type === IMAGETYPE_JPEG) { return imagecreatefromjpeg($path); } else if ($type === IMAGETYPE_PNG) { return imagecreatefrompng($path); } else if ($type === IMAGETYPE_GIF) { return imagecreatefromgif($path); } else if ($type === 18/*IMAGETYPE_WEBP*/) { - if(version_compare(PHP_VERSION, '5.4.0') >= 0) return imagecreatefromwebp($path); + if (version_compare(PHP_VERSION, '5.4.0') >= 0) return imagecreatefromwebp($path); } else if ($type === IMAGETYPE_BMP) { - if(version_compare(PHP_VERSION, '7.2.0') >= 0) return imagecreatefrombmp($path); + if (version_compare(PHP_VERSION, '7.2.0') >= 0) return imagecreatefrombmp($path); } } // get file (proxy or resize image) -function get_file($path, $resize = false){ +function get_file($path, $resize = false) +{ // validate - if(!$path) error('Invalid file request.', 404); + if (!$path) error('Invalid file request.', 404); $path = real_path($path); // in case of symlink path // mime $mime = get_mime($path); - if(!$mime) error('Empty mime type.', 415); + if (!$mime) error('Empty mime type.', 415); $mime_array = explode('/', $mime); // resize - if($resize){ - if($mime_array[0] !== 'image') error('' . basename($path) . ' (' . $mime . ') is not an image.', 415); - if(!config::$config['load_images']) error('Load images disabled.', 400); - if(!config::$config['image_resize_enabled']) error('Resize images disabled.', 400); + if ($resize) { + if ($mime_array[0] !== 'image') error('' . basename($path) . ' (' . $mime . ') is not an image.', 415); + if (!config::$config['load_images']) error('Load images disabled.', 400); + if (!config::$config['image_resize_enabled']) error('Resize images disabled.', 400); $resize_dimensions = intval($resize); - if(!$resize_dimensions) error("Invalid resize parameter $resize.", 400); - if(!in_array($resize_dimensions, [config::$config['image_resize_dimensions'], config::$image_resize_dimensions_retina])) error("Resize parameter $resize_dimensions is not allowed.", 400); + if (!$resize_dimensions) error("Invalid resize parameter $resize.", 400); + if (!in_array($resize_dimensions, [config::$config['image_resize_dimensions'], config::$image_resize_dimensions_retina])) error("Resize parameter $resize_dimensions is not allowed.", 400); resize_image($path, $resize_dimensions); - // proxy file + // proxy file } else { // disable if !proxy and path is within document root (file should never be proxied) - if(!config::$config['load_files_proxy_php'] && is_within_docroot($path)) error('File cannot be proxied.', 400); + if (!config::$config['load_files_proxy_php'] && is_within_docroot($path)) error('File cannot be proxied.', 400); // read file read_file($path, $mime, $msg = 'File ' . basename($path) . ' proxied.', false, true); @@ -530,28 +563,31 @@ function get_file($path, $resize = false){ } // sharpen resized image -function sharpen_image($image){ +function sharpen_image($image) +{ $matrix = array( array(-1, -1, -1), array(-1, 20, -1), array(-1, -1, -1), ); $divisor = array_sum(array_map('array_sum', $matrix)); - $offset = 0; + $offset = 0; imageconvolution($image, $matrix, $divisor, $offset); } // exif orientation // https://github.com/gumlet/php-image-resize/blob/master/lib/ImageResize.php -function exif_orientation($orientation, &$image){ - if(empty($orientation) || !is_numeric($orientation) || $orientation < 3 || $orientation > 8) return; +function exif_orientation($orientation, &$image) +{ + if (empty($orientation) || !is_numeric($orientation) || $orientation < 3 || $orientation > 8) return; $image = imagerotate($image, array(6 => 270, 5 => 270, 3 => 180, 4 => 180, 8 => 90, 7 => 90)[$orientation], null); - if(in_array($orientation, array(5, 4, 7)) && function_exists('imageflip')) imageflip($image, IMG_FLIP_HORIZONTAL); + if (in_array($orientation, array(5, 4, 7)) && function_exists('imageflip')) imageflip($image, IMG_FLIP_HORIZONTAL); return true; } - + // resize image -function resize_image($path, $resize_dimensions){ +function resize_image($path, $resize_dimensions) +{ // file size $file_size = filesize($path); @@ -561,21 +597,21 @@ function resize_image($path, $resize_dimensions){ // cache $cache = config::$config['image_resize_cache'] ? get_image_cache_path($path, $resize_dimensions, $file_size, filemtime($path)) : NULL; - if($cache) read_file($cache, null, 'Resized image served from cache', $header_props, true); + if ($cache) read_file($cache, null, 'Resized image served from cache', $header_props, true); // imagesize $info = getimagesize($path); - if(empty($info) || !is_array($info)) error('Invalid image / failed getimagesize().', 500); + if (empty($info) || !is_array($info)) error('Invalid image / failed getimagesize().', 500); $resize_ratio = max($info[0], $info[1]) / $resize_dimensions; // image_resize_max_pixels early exit - if(config::$config['image_resize_max_pixels'] && $info[0] * $info[1] > config::$config['image_resize_max_pixels']) error('Image resolution ' . $info[0] . ' x ' . $info[1] . ' (' . ($info[0] * $info[1]) . ' px) exceeds image_resize_max_pixels (' . config::$config['image_resize_max_pixels'] . ' px).', 400); + if (config::$config['image_resize_max_pixels'] && $info[0] * $info[1] > config::$config['image_resize_max_pixels']) error('Image resolution ' . $info[0] . ' x ' . $info[1] . ' (' . ($info[0] * $info[1]) . ' px) exceeds image_resize_max_pixels (' . config::$config['image_resize_max_pixels'] . ' px).', 400); // header props $header_props .= ', ' . $info['mime'] . ', ' . $info[0] . 'x' . $info[1] . ', ratio:' . round($resize_ratio, 2); // output original if resize ratio < image_resize_min_ratio - if($resize_ratio < max(config::$config['image_resize_min_ratio'], 1) && !read_file($path, $info['mime'], 'Original image served', $header_props, true)) error('File does not exist.', 404); + if ($resize_ratio < max(config::$config['image_resize_min_ratio'], 1) && !read_file($path, $info['mime'], 'Original image served', $header_props, true)) error('File does not exist.', 404); // Calculate new image dimensions. $resize_width = round($info[0] / $resize_ratio); @@ -583,12 +619,12 @@ function resize_image($path, $resize_dimensions){ // memory $memory_limit = config::$config['image_resize_memory_limit'] && function_exists('ini_get') ? (int) @ini_get('memory_limit') : false; - if($memory_limit && $memory_limit > -1){ + if ($memory_limit && $memory_limit > -1) { // $memory_required = ceil(($info[0] * $info[1] * 4 + $resize_width * $resize_height * 4) / 1048576); $memory_required = round(($info[0] * $info[1] * (isset($info['bits']) ? $info['bits'] / 8 : 1) * (isset($info['channels']) ? $info['channels'] : 3) * 1.33 + $resize_width * $resize_height * 4) / 1048576, 1); $new_memory_limit = function_exists('ini_set') ? max($memory_limit, config::$config['image_resize_memory_limit']) : $memory_limit; - if($memory_required > $new_memory_limit) error('Resizing this image requires at least ' . $memory_required . 'M. Your current PHP memory_limit is ' . $new_memory_limit .'M.', 400); - if($memory_limit < $new_memory_limit && @ini_set('memory_limit', $new_memory_limit . 'M')) $header_props .= ', ' . $memory_limit . 'M => ' . $new_memory_limit . 'M (min ' . $memory_required . 'M)'; + if ($memory_required > $new_memory_limit) error('Resizing this image requires at least ' . $memory_required . 'M. Your current PHP memory_limit is ' . $new_memory_limit . 'M.', 400); + if ($memory_limit < $new_memory_limit && @ini_set('memory_limit', $new_memory_limit . 'M')) $header_props .= ', ' . $memory_limit . 'M => ' . $new_memory_limit . 'M (min ' . $memory_required . 'M)'; } // new dimensions headers @@ -596,25 +632,25 @@ function resize_image($path, $resize_dimensions){ // create new $image $image = image_create_from($path, $info[2]); - if(!$image) error('Failed to create image resource.', 500); + if (!$image) error('Failed to create image resource.', 500); // Create final image with new dimensions. $new_image = imagecreatetruecolor($resize_width, $resize_height); - if(!call_user_func(config::$config['image_resize_function'], $new_image, $image, 0, 0, 0, 0, $resize_width, $resize_height, $info[0], $info[1])) error('Failed to resize image.', 500); + if (!call_user_func(config::$config['image_resize_function'], $new_image, $image, 0, 0, 0, 0, $resize_width, $resize_height, $info[0], $info[1])) error('Failed to resize image.', 500); // destroy original $image resource imagedestroy($image); // exif orientation $exif = function_exists('exif_read_data') ? @exif_read_data($path) : false; - if(!empty($exif) && is_array($exif) && isset($exif['Orientation']) && exif_orientation($exif['Orientation'], $new_image)) $header_props .= ', orientated from EXIF:' . $exif['Orientation']; + if (!empty($exif) && is_array($exif) && isset($exif['Orientation']) && exif_orientation($exif['Orientation'], $new_image)) $header_props .= ', orientated from EXIF:' . $exif['Orientation']; // sharpen resized image - if(config::$config['image_resize_sharpen']) sharpen_image($new_image); + if (config::$config['image_resize_sharpen']) sharpen_image($new_image); // save to cache - if($cache){ - if(!imagejpeg($new_image, $cache, config::$config['image_resize_quality'])) error('imagejpeg() failed to create and cache resized image.', 500); + if ($cache) { + if (!imagejpeg($new_image, $cache, config::$config['image_resize_quality'])) error('imagejpeg() failed to create and cache resized image.', 500); /* // store cache records in /images.json_decode(json)n $image_cache_path = config::$cache_path . '/images'; $image_cache_json = $image_cache_path . '/images.json'; @@ -622,19 +658,19 @@ function resize_image($path, $resize_dimensions){ $image_cache_arr[basename($cache)] = is_within_docroot($path) ? ltrim(substr($path, strlen(config::$doc_root)), '\/') : $path; file_put_contents($image_cache_json, json_encode($image_cache_arr, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES));*/ - // not cache / direct output + // not cache / direct output } else { set_cache_headers(); header('content-type: image/jpeg'); header('files-msg: Resized image served [' . $header_props . ', ' . header_memory_time() . ']'); - if(!imagejpeg($new_image, null, config::$config['image_resize_quality'])) error('imagejpeg() failed to create and output resized image.', 500); + if (!imagejpeg($new_image, null, config::$config['image_resize_quality'])) error('imagejpeg() failed to create and output resized image.', 500); } // destroy image imagedestroy($new_image); // cache readfile - if($cache && !read_file($cache, null, 'Resized image cached and served', $header_props, true)) error('Cache file does not exist.', 404); + if ($cache && !read_file($cache, null, 'Resized image cached and served', $header_props, true)) error('Cache file does not exist.', 404); // exit; @@ -644,27 +680,29 @@ function resize_image($path, $resize_dimensions){ // https://www.bitrepository.com/resize-an-image-keeping-its-aspect-ratio-using-php-and-gd.html } -function get_url_path($dir){ - if(!is_within_docroot($dir)) return false; +function get_url_path($dir) +{ + if (!is_within_docroot($dir)) return false; // if in __dir__ path, __dir__ relative - if(is_within_path($dir, config::$__dir__)) return $dir === config::$__dir__ ? '.' : substr($dir, strlen(config::$__dir__) + 1); + if (is_within_path($dir, config::$__dir__)) return $dir === config::$__dir__ ? '.' : substr($dir, strlen(config::$__dir__) + 1); // doc root, doc root relative return $dir === config::$doc_root ? '/' : substr($dir, strlen(config::$doc_root)); } // get dir -function get_dir($path, $files = false, $json_url = false){ +function get_dir($path, $files = false, $json_url = false) +{ // realpath $realpath = $path ? real_path($path) : false; - if(!$realpath) return; // no real path for any reason + if (!$realpath) return; // no real path for any reason $symlinked = $realpath !== $path; // path is symlinked at some point // exclude - if(is_exclude($path, true, $symlinked)) return; // exclude - if($symlinked && is_exclude($realpath, true, $symlinked)) return; // exclude check again symlink realpath + if (is_exclude($path, true, $symlinked)) return; // exclude + if ($symlinked && is_exclude($realpath, true, $symlinked)) return; // exclude check again symlink realpath // vars $filemtime = filemtime($realpath); @@ -684,26 +722,27 @@ function get_dir($path, $files = false, $json_url = false){ ); // url path - if($url_path) $arr['url_path'] = $url_path; + if ($url_path) $arr['url_path'] = $url_path; - // $files || config::menu_load_all - if($files) $arr['files'] = get_files_data($path, $url_path, $arr['dirsize'], $arr['files_count'], $arr['images_count']); + // $files || config::menu_load_all + if ($files) $arr['files'] = get_files_data($path, $url_path, $arr['dirsize'], $arr['files_count'], $arr['images_count']); - // json cache path - if($json_url && config::$storage_is_within_doc_root && !config::$has_login && config::$config['cache']){ + // json cache path + if ($json_url && config::$storage_is_within_doc_root && !config::$has_login && config::$config['cache']) { $json_cache = get_json_cache_url(get_dir_cache_hash($realpath, $filemtime)); - if($json_cache) $arr['json_cache'] = $json_cache; + if ($json_cache) $arr['json_cache'] = $json_cache; } // - return $arr; + return $arr; } // get menu sort -function get_menu_sort($dirs){ - if(strpos(config::$config['menu_sort'], 'date') === 0){ - usort($dirs, function($a, $b) { - return filemtime($a) - filemtime($b); +function get_menu_sort($dirs) +{ + if (strpos(config::$config['menu_sort'], 'date') === 0) { + usort($dirs, function ($a, $b) { + return filemtime($a) - filemtime($b); }); } else { /*usort($dirs, function($a, $b) { @@ -715,190 +754,197 @@ function get_menu_sort($dirs){ } // recursive directory scan -function get_dirs($path = false, &$arr = array(), $depth = 0) { +function get_dirs($path = false, &$arr = array(), $depth = 0) +{ // get this dir (ignore root, unless load all ... root already loaded into page) - if($depth || config::$config['menu_load_all']) { + if ($depth || config::$config['menu_load_all']) { $data = get_dir($path, config::$config['menu_load_all'], !config::$config['menu_load_all']); - if(!$data) return $arr; + if (!$data) return $arr; $arr[] = $data; // max depth - if(config::$config['menu_max_depth'] && $depth >= config::$config['menu_max_depth']) return $arr; + if (config::$config['menu_max_depth'] && $depth >= config::$config['menu_max_depth']) return $arr; // don't recursive if symlink - if($data['is_link'] && !config::$config['menu_recursive_symlinks']) return $arr; + if ($data['is_link'] && !config::$config['menu_recursive_symlinks']) return $arr; } // get dirs from files array if $data['files'] or glob subdirs - $subdirs = isset($data['files']) ? array_filter(array_map(function($file){ + $subdirs = isset($data['files']) ? array_filter(array_map(function ($file) { return $file['filetype'] === 'dir' ? root_absolute($file['path']) : false; - }, $data['files'])) : glob($path . '/*', GLOB_NOSORT|GLOB_ONLYDIR); + }, $data['files'])) : glob($path . '/*', GLOB_NOSORT | GLOB_ONLYDIR); // sort and loop subdirs - if(!empty($subdirs)) foreach(get_menu_sort($subdirs) as $subdir) get_dirs($subdir, $arr, $depth + 1); + if (!empty($subdirs)) foreach (get_menu_sort($subdirs) as $subdir) get_dirs($subdir, $arr, $depth + 1); // return return $arr; } -function safe_iptc_tag($val, $max_str = 1000){ +function safe_iptc_tag($val, $max_str = 1000) +{ $val = @substr($val, 0, $max_str); return @mb_detect_encoding($val, 'UTF-8', true) ? $val : @utf8_encode($val); } -function get_iptc($image_info){ - if(!$image_info || !isset($image_info['APP13']) || !function_exists('iptcparse')) return; - $app13 = @iptcparse($image_info['APP13']); - if(empty($app13)) return; - $iptc = array(); +function get_iptc($image_info) +{ + if (!$image_info || !isset($image_info['APP13']) || !function_exists('iptcparse')) return; + $app13 = @iptcparse($image_info['APP13']); + if (empty($app13)) return; + $iptc = array(); // title // ObjectName - if(isset($app13['2#005'][0])) $iptc['title'] = safe_iptc_tag($app13['2#005'][0]); + if (isset($app13['2#005'][0])) $iptc['title'] = safe_iptc_tag($app13['2#005'][0]); // description // Caption-Abstract - if(isset($app13['2#120'][0])) $iptc['description'] = safe_iptc_tag($app13['2#120'][0]); + if (isset($app13['2#120'][0])) $iptc['description'] = safe_iptc_tag($app13['2#120'][0]); // keywords array - if(isset($app13['2#025']) && !empty($app13['2#025']) && is_array($app13['2#025'])) { - $keywords = array_map(function($keyword){ + if (isset($app13['2#025']) && !empty($app13['2#025']) && is_array($app13['2#025'])) { + $keywords = array_map(function ($keyword) { return safe_iptc_tag($keyword, 100); }, $app13['2#025']); - if(!empty($keywords)) $iptc['keywords'] = $keywords; + if (!empty($keywords)) $iptc['keywords'] = $keywords; } - return $iptc; + return $iptc; } // get exif -function get_exif($path){ - if(!function_exists('exif_read_data')) return; - $exif_data = @exif_read_data($path, 'ANY_TAG', 0); // @exif_read_data($path); - if(empty($exif_data) || !is_array($exif_data)) return; - $exif = array(); - foreach (array('DateTime', 'DateTimeOriginal', 'ExposureTime', 'FNumber', 'FocalLength', 'Make', 'Model', 'Orientation', 'ISOSpeedRatings', 'Software') as $name) { - if(isset($exif_data[$name])) $exif[$name] = trim($exif_data[$name]); - } - if(isset($exif['DateTime'])) $exif['DateTime'] = @strtotime($exif['DateTime']); - if(isset($exif['DateTimeOriginal'])) $exif['DateTimeOriginal'] = @strtotime($exif['DateTimeOriginal']); +function get_exif($path) +{ + if (!function_exists('exif_read_data')) return; + $exif_data = @exif_read_data($path, 'ANY_TAG', 0); // @exif_read_data($path); + if (empty($exif_data) || !is_array($exif_data)) return; + $exif = array(); + foreach (array('DateTime', 'DateTimeOriginal', 'ExposureTime', 'FNumber', 'FocalLength', 'Make', 'Model', 'Orientation', 'ISOSpeedRatings', 'Software') as $name) { + if (isset($exif_data[$name])) $exif[$name] = trim($exif_data[$name]); + } + if (isset($exif['DateTime'])) $exif['DateTime'] = @strtotime($exif['DateTime']); + if (isset($exif['DateTimeOriginal'])) $exif['DateTimeOriginal'] = @strtotime($exif['DateTimeOriginal']); - /*LensInfo 24-70mm f/? + /*LensInfo 24-70mm f/? Lens EF24-70mm f/2.8L USM LensID 230*/ - // ApertureFNumber (f_stop) - if(isset($exif_data['COMPUTED']['ApertureFNumber'])) $exif['ApertureFNumber'] = $exif_data['COMPUTED']['ApertureFNumber']; + // ApertureFNumber (f_stop) + if (isset($exif_data['COMPUTED']['ApertureFNumber'])) $exif['ApertureFNumber'] = $exif_data['COMPUTED']['ApertureFNumber']; - // flash - if(isset($exif_data['Flash'])) $exif['Flash'] = ($exif_data['Flash'] & 1) != 0; + // flash + if (isset($exif_data['Flash'])) $exif['Flash'] = ($exif_data['Flash'] & 1) != 0; - // GPS - $gps = get_image_location($exif_data); - if(!empty($gps)) $exif['gps'] = $gps; + // GPS + $gps = get_image_location($exif_data); + if (!empty($gps)) $exif['gps'] = $gps; - // return - return $exif; + // return + return $exif; } -function get_image_location($exif){ - $arr = array('GPSLatitudeRef', 'GPSLatitude', 'GPSLongitudeRef', 'GPSLongitude'); - foreach ($arr as $val) { - if(!isset($exif[$val])) return false; - } +function get_image_location($exif) +{ + $arr = array('GPSLatitudeRef', 'GPSLatitude', 'GPSLongitudeRef', 'GPSLongitude'); + foreach ($arr as $val) { + if (!isset($exif[$val])) return false; + } $GPSLatitudeRef = $exif[$arr[0]]; $GPSLatitude = $exif[$arr[1]]; - $GPSLongitudeRef= $exif[$arr[2]]; + $GPSLongitudeRef = $exif[$arr[2]]; $GPSLongitude = $exif[$arr[3]]; - + $lat_degrees = count($GPSLatitude) > 0 ? gps2Num($GPSLatitude[0]) : 0; $lat_minutes = count($GPSLatitude) > 1 ? gps2Num($GPSLatitude[1]) : 0; $lat_seconds = count($GPSLatitude) > 2 ? gps2Num($GPSLatitude[2]) : 0; - + $lon_degrees = count($GPSLongitude) > 0 ? gps2Num($GPSLongitude[0]) : 0; $lon_minutes = count($GPSLongitude) > 1 ? gps2Num($GPSLongitude[1]) : 0; $lon_seconds = count($GPSLongitude) > 2 ? gps2Num($GPSLongitude[2]) : 0; - + $lat_direction = ($GPSLatitudeRef == 'W' or $GPSLatitudeRef == 'S') ? -1 : 1; $lon_direction = ($GPSLongitudeRef == 'W' or $GPSLongitudeRef == 'S') ? -1 : 1; - - $latitude = $lat_direction * ($lat_degrees + ($lat_minutes / 60) + ($lat_seconds / (60*60))); - $longitude = $lon_direction * ($lon_degrees + ($lon_minutes / 60) + ($lon_seconds / (60*60))); + + $latitude = $lat_direction * ($lat_degrees + ($lat_minutes / 60) + ($lat_seconds / (60 * 60))); + $longitude = $lon_direction * ($lon_degrees + ($lon_minutes / 60) + ($lon_seconds / (60 * 60))); return array($latitude, $longitude); } -function gps2Num($coordPart){ +function gps2Num($coordPart) +{ $parts = explode('/', $coordPart); - if(count($parts) <= 0) - return 0; - if(count($parts) == 1) - return $parts[0]; + if (count($parts) <= 0) + return 0; + if (count($parts) == 1) + return $parts[0]; return floatval($parts[0]) / floatval($parts[1]); } // -function get_files_data($dir, $url_path = false, &$dirsize = 0, &$files_count = 0, &$images_count = 0){ +function get_files_data($dir, $url_path = false, &$dirsize = 0, &$files_count = 0, &$images_count = 0) +{ // scandir $filenames = scandir($dir, SCANDIR_SORT_NONE); - if(empty($filenames)) return array(); + if (empty($filenames)) return array(); $items = array(); // loop filenames - foreach($filenames as $filename) { + foreach ($filenames as $filename) { // - if($filename === '.' || $filename === '..') continue; + if ($filename === '.' || $filename === '..') continue; $path = $dir . '/' . $filename; // paths $realpath = real_path($path); // differs from $path only if is symlinked - if(!$realpath) continue; // no real path for any reason, for example symlink dead + if (!$realpath) continue; // no real path for any reason, for example symlink dead $symlinked = $realpath !== $path; // path is symlinked at some point // filetype $filetype = filetype($realpath); $is_dir = $filetype === 'dir' ? true : false; - + // exclude - if(is_exclude($path, $is_dir, $symlinked)) continue; // exclude - if($symlinked && is_exclude($realpath, $is_dir, $symlinked)) continue; // exclude check again symlink realpath + if (is_exclude($path, $is_dir, $symlinked)) continue; // exclude + if ($symlinked && is_exclude($realpath, $is_dir, $symlinked)) continue; // exclude check again symlink realpath // vars - if(!$is_dir) $files_count ++; // files count + if (!$is_dir) $files_count++; // files count $is_link = $symlinked ? is_link($path) : false; // symlink $basename = $is_link ? (basename($realpath) ?: $filename) : $filename; $filemtime = filemtime($realpath); $is_readable = is_readable($realpath); $filesize = $is_dir ? false : filesize($realpath); - if($filesize) $dirsize += $filesize; + if ($filesize) $dirsize += $filesize; // url_path / symlink $item_url_path = $symlinked ? get_url_path($realpath) : false; // url_path from realpath if symlinked - if(!$item_url_path && $url_path) $item_url_path = $url_path . ($url_path === '/' ? '' : '/') . ($is_link ? basename($path) : $basename); + if (!$item_url_path && $url_path) $item_url_path = $url_path . ($url_path === '/' ? '' : '/') . ($is_link ? basename($path) : $basename); // root path // path relative to config::$root - if(!$symlinked || is_within_root($realpath)){ + if (!$symlinked || is_within_root($realpath)) { $root_path = root_relative($realpath); - // path is symlinked and !is_within_root(), get path-relative + // path is symlinked and !is_within_root(), get path-relative } else { // root path to symlink $root_path = root_relative($path); // check for symlink loop - if($is_link && $is_dir && $path && $root_path) { + if ($is_link && $is_dir && $path && $root_path) { $basename_path = basename($root_path); - if($basename_path && preg_match('/(\/|^)' . $basename_path. '\//', $root_path)){ + if ($basename_path && preg_match('/(\/|^)' . $basename_path . '\//', $root_path)) { $loop_path = ''; $segments = explode('/', $root_path); array_pop($segments); foreach ($segments as $segment) { $loop_path .= ($loop_path ? '/' : '') . $segment; - if($segment !== $basename_path) continue; + if ($segment !== $basename_path) continue; $loop_abs_path = root_absolute($loop_path); - if(!is_link($loop_abs_path) || $realpath !== real_path($loop_abs_path)) continue; + if (!is_link($loop_abs_path) || $realpath !== real_path($loop_abs_path)) continue; $root_path = $loop_path; $item_url_path = get_url_path($loop_abs_path) ?: $item_url_path; // new symlink is within doc_root break; @@ -922,72 +968,72 @@ function get_files_data($dir, $url_path = false, &$dirsize = 0, &$files_count = // optional props $ext = !$is_dir ? pathinfo($realpath, PATHINFO_EXTENSION) : false; - if($ext) { + if ($ext) { $ext = strtolower($ext); $item['ext'] = $ext; } $mime = $is_dir ? 'directory' : ($is_readable && (!$ext || config::$config['get_mime_type']) ? get_mime($realpath) : false); - if($mime) $item['mime'] = $mime; - if($item_url_path) $item['url_path'] = $item_url_path; + if ($mime) $item['mime'] = $mime; + if ($item_url_path) $item['url_path'] = $item_url_path; // image / check from mime, fallback to extension - $is_image = $is_dir ? false : ($mime ? (strtok($mime, '/') === 'image' && !strpos($mime, 'svg')) : in_array($ext, array('gif','jpg','jpeg','jpc','jp2','jpx','jb2','png','swf','psd','bmp','tiff','tif','wbmp','xbm','ico','webp'))); - if($is_image){ + $is_image = $is_dir ? false : ($mime ? (strtok($mime, '/') === 'image' && !strpos($mime, 'svg')) : in_array($ext, array('gif', 'jpg', 'jpeg', 'jpc', 'jp2', 'jpx', 'jb2', 'png', 'swf', 'psd', 'bmp', 'tiff', 'tif', 'wbmp', 'xbm', 'ico', 'webp'))); + if ($is_image) { // imagesize $imagesize = $is_readable ? @getimagesize($realpath, $info) : false; // image count and icon - $images_count ++; + $images_count++; $item['icon'] = 'image'; // is imagesize - if(!empty($imagesize) && is_array($imagesize)){ + if (!empty($imagesize) && is_array($imagesize)) { // start image array $image = array(); - foreach (array(0 => 'width', 1 => 'height', 2 => 'type', 'bits' => 'bits', 'channels' => 'channels', 'mime' => 'mime') as $key => $name) if(isset($imagesize[$key])) $image[$name] = $imagesize[$key]; + foreach (array(0 => 'width', 1 => 'height', 2 => 'type', 'bits' => 'bits', 'channels' => 'channels', 'mime' => 'mime') as $key => $name) if (isset($imagesize[$key])) $image[$name] = $imagesize[$key]; // mime from image - if(!$mime && isset($image['mime'])) $item['mime'] = $image['mime']; + if (!$mime && isset($image['mime'])) $item['mime'] = $image['mime']; // IPTC $iptc = $info ? get_iptc($info) : false; - if(!empty($iptc)) $image['iptc'] = $iptc; + if (!empty($iptc)) $image['iptc'] = $iptc; // EXIF $exif = get_exif($realpath); - if(!empty($exif)) { + if (!empty($exif)) { $image['exif'] = $exif; - if(isset($exif['DateTimeOriginal'])) $item['DateTimeOriginal'] = $exif['DateTimeOriginal']; + if (isset($exif['DateTimeOriginal'])) $item['DateTimeOriginal'] = $exif['DateTimeOriginal']; // invert width/height if exif orientation - if(isset($exif['Orientation']) && $exif['Orientation'] > 4 && $exif['Orientation'] < 9){ + if (isset($exif['Orientation']) && $exif['Orientation'] > 4 && $exif['Orientation'] < 9) { $image['width'] = $imagesize[1]; $image['height'] = $imagesize[0]; } } // image resize cache direct - if(config::$image_resize_cache_direct){ + if (config::$image_resize_cache_direct) { $resize1 = get_image_cache_path($realpath, config::$config['image_resize_dimensions'], $filesize, $filemtime); - if(file_exists($resize1)) $image['resize' . config::$config['image_resize_dimensions']] = get_url_path($resize1); + if (file_exists($resize1)) $image['resize' . config::$config['image_resize_dimensions']] = get_url_path($resize1); $retina = config::$image_resize_dimensions_retina; - if($retina){ + if ($retina) { $resize2 = get_image_cache_path($realpath, $retina, $filesize, $filemtime); - if(file_exists($resize2)) $image['resize' . $retina] = get_url_path($resize2); + if (file_exists($resize2)) $image['resize' . $retina] = get_url_path($resize2); } } // add image to item $item['image'] = $image; - // get real mime if getimagesize fails. Could be non-image disguised as image extension - } else if($is_readable && !$mime){ + // get real mime if getimagesize fails. Could be non-image disguised as image extension + } else if ($is_readable && !$mime) { $mime = get_mime($realpath); - if($mime) { + if ($mime) { $item['mime'] = $mime; - if(strtok($mime, '/') !== 'image'){ // unset images_count and icon because is not image after all - $images_count --; + if (strtok($mime, '/') !== 'image') { // unset images_count and icon because is not image after all + $images_count--; unset($item['icon']); } } @@ -996,102 +1042,114 @@ function get_files_data($dir, $url_path = false, &$dirsize = 0, &$files_count = // add to items with basename as key $items[$basename] = $item; - } + } - // - return $items; + // + return $items; } // get files -function get_files($dir){ +function get_files($dir) +{ // invalid $dir - if(!$dir) json_error('Invalid directory'); + if (!$dir) json_error('Invalid directory'); // cache $cache = get_dir_cache_path(real_path($dir)); // read cache or get dir and cache - if(!read_file($cache, 'application/json', 'files json served from cache')) { + if (!read_file($cache, 'application/json', 'files json served from cache')) { json_cache(get_dir($dir, true), 'files json created' . ($cache ? ' and cached' : ''), $cache); } } /* start here */ -function post($param){ - return isset($_POST[$param]) && !empty($_POST[$param]) ? $_POST[$param] : false; +function post($param) +{ + return isset($_POST[$param]) && !empty($_POST[$param]) ? $_POST[$param] : false; } -function get($param){ - return isset($_GET[$param]) && !empty($_GET[$param]) ? $_GET[$param] : false; +function get($param) +{ + return isset($_GET[$param]) && !empty($_GET[$param]) ? $_GET[$param] : false; } -function json_cache($arr = array(), $msg = false, $cache = true){ - header('content-type: application/json'); - $json = empty($arr) ? '{}' : json_encode($arr, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); - if($cache) @file_put_contents($cache, $json); - if($msg) header('files-msg: ' . $msg . ' [' . header_memory_time() . ']'); - echo $json; +function json_cache($arr = array(), $msg = false, $cache = true) +{ + header('content-type: application/json'); + $json = empty($arr) ? '{}' : json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + if ($cache) @file_put_contents($cache, $json); + if ($msg) header('files-msg: ' . $msg . ' [' . header_memory_time() . ']'); + echo $json; } -function json_error($error = 'Error'){ - header('Content-Type: application/json'); - exit('{"error":"' . $error . '"}'); +function json_error($error = 'Error') +{ + header('Content-Type: application/json'); + exit('{"error":"' . $error . '"}'); } -function json_success($success){ +function json_success($success) +{ header('Content-Type: application/json'); exit('{"success":"' . $success . '"}'); } -function error($msg, $code = false){ +function error($msg, $code = false) +{ // 400 Bad Request, 403 Forbidden, 401 Unauthorized, 404 Not Found, 500 Internal Server Error - if($code) http_response_code($code); + if ($code) http_response_code($code); header('content-type: text/html'); header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0'); header('Cache-Control: post-check=0, pre-check=0', false); header('Pragma: no-cache'); - exit('

Error

' . $msg); + exit('

Error

' . $msg); } // get valid menu cache -function get_valid_menu_cache($cache){ - if(!$cache || !file_exists($cache)) return; +function get_valid_menu_cache($cache) +{ + if (!$cache || !file_exists($cache)) return; $json = @file_get_contents($cache); - if(empty($json)) return; - if(!config::$config['menu_cache_validate']) return $json; + if (empty($json)) return; + if (!config::$config['menu_cache_validate']) return $json; $arr = @json_decode($json, true); - if(empty($arr)) return; + if (empty($arr)) return; foreach ($arr as $key => $val) { $path = $val['path']; - if(strpos($path, '/') !== false && $val['mtime'] !== @filemtime(root_absolute($path))) return; // skip shallow 1st level dirs, and compare filemtime + if (strpos($path, '/') !== false && $val['mtime'] !== @filemtime(root_absolute($path))) return; // skip shallow 1st level dirs, and compare filemtime } return $json; } // get root dirs -function get_root_dirs(){ - $root_dirs = glob(config::$root . '/*', GLOB_ONLYDIR|GLOB_NOSORT); - if(empty($root_dirs)) return array(); - return array_filter($root_dirs, function($dir){ +function get_root_dirs() +{ + $root_dirs = glob(config::$root . '/*', GLOB_ONLYDIR | GLOB_NOSORT); + if (empty($root_dirs)) return array(); + return array_filter($root_dirs, function ($dir) { return !is_exclude($dir, true, is_link($dir)); }); } // get menu cache hash -function get_menu_cache_hash($root_dirs){ +function get_menu_cache_hash($root_dirs) +{ $mtime_count = filemtime(config::$root); foreach ($root_dirs as $root_dir) $mtime_count += filemtime($root_dir); return substr(md5(config::$doc_root . config::$__dir__ . config::$root), 0, 6) . '.' . substr(md5(config::$version . config::$config['cache_key'] . config::$config['menu_max_depth'] . config::$config['menu_load_all'] . (config::$config['menu_load_all'] ? config::$config['files_exclude'] . config::$image_resize_cache_direct : '') . config::$has_login . config::$config['dirs_exclude'] . config::$config['menu_sort']), 0, 6) . '.' . $mtime_count; } // get dirs -function dirs(){ +function dirs() +{ // get menu_cache_hash - if(config::$config['cache']){ + if (config::$config['cache']) { $menu_cache_hash = post('menu_cache_hash'); // get menu cache hash $menu_cache_arr = $menu_cache_hash ? explode('.', $menu_cache_hash) : false; - if(!$menu_cache_arr || - count($menu_cache_arr) !== 3 || - strlen($menu_cache_arr[0]) !== 6 || - strlen($menu_cache_arr[1]) !== 6 || + if ( + !$menu_cache_arr || + count($menu_cache_arr) !== 3 || + strlen($menu_cache_arr[0]) !== 6 || + strlen($menu_cache_arr[1]) !== 6 || !is_numeric($menu_cache_arr[2]) ) json_error('Invalid menu cache hash'); // early exit } @@ -1099,92 +1157,94 @@ function dirs(){ $json = $cache ? get_valid_menu_cache($cache) : false; // get valid json menu cache // $json is valid from menu cache file - if($json){ + if ($json) { header('content-type: application/json'); header('files-msg: valid menu cache hash [' . $menu_cache_hash . ']' . (!config::$config['menu_cache_validate'] ? '[deep validation disabled]' : '') . '[' . header_memory_time() . ']'); echo (post('localstorage') ? '{"localstorage":"1"}' : $json); - - // reload dirs + + // reload dirs } else { json_cache(get_dirs(config::$root), 'dirs reloaded' . ($cache ? ' and cached.' : ' [cache disabled]'), $cache); } } // custom_script (css + js in storage) -function custom_script($type){ +function custom_script($type) +{ // todo maybe just use one file custom.css/js for easy edit? - if(!config::$storage_path || !config::$storage_is_within_doc_root) return; + if (!config::$storage_path || !config::$storage_is_within_doc_root) return; $dir = config::$storage_path . '/' . $type; $files = file_exists($dir) ? glob($dir . '/*.' . $type) : false; - if(empty($files)) return; + if (empty($files)) return; $template = $type === 'css' ? '' : ''; - foreach($files as $file) echo str_replace('%url%', get_url_path($file) . '?' . filemtime($file), $template) . PHP_EOL; + foreach ($files as $file) echo str_replace('%url%', get_url_path($file) . '?' . filemtime($file), $template) . PHP_EOL; } // POST -if(post('action')){ +if (post('action')) { // basic post access security: XMLHttpRequest + post_hash created from server paths - if(!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || - $_SERVER['HTTP_X_REQUESTED_WITH'] !== 'XMLHttpRequest' || - post('post_hash') !== md5(__FILE__ . $_SERVER['HTTP_HOST'])) json_error('Invalid request hash. Please Refresh browser.'); + if ( + !isset($_SERVER['HTTP_X_REQUESTED_WITH']) || + $_SERVER['HTTP_X_REQUESTED_WITH'] !== 'XMLHttpRequest' || + post('post_hash') !== md5(__FILE__ . $_SERVER['HTTP_HOST']) + ) json_error('Invalid request hash. Please Refresh browser.'); - // post action - $action = post('action'); + // post action + $action = post('action'); // new config(); - // dirs - if($action === 'dirs'){ + // dirs + if ($action === 'dirs') { dirs(post('localstorage')); - // files - } else if($action === 'files'){ - if(!isset($_POST['dir'])) json_error('Missing dir parameter'); + // files + } else if ($action === 'files') { + if (!isset($_POST['dir'])) json_error('Missing dir parameter'); get_files(valid_root_path($_POST['dir'], true)); - // file - } else if($action === 'file'){ + // file + } else if ($action === 'file') { // valid path $file = valid_root_path(post('file')); - if(!$file) json_error('Invalid file path'); + if (!$file) json_error('Invalid file path'); $file = real_path($file); // in case of symlink path - // file write - if(post('write')) { - if(!config::$config['code_allow_edit']) json_error('Code editing has been disabled.'); - if(!is_writeable($file) || !is_file($file)) json_error('File is not writeable.'); + // file write + if (post('write')) { + if (!config::$config['code_allow_edit']) json_error('Code editing has been disabled.'); + if (!is_writeable($file) || !is_file($file)) json_error('File is not writeable.'); $write_success = @file_put_contents($file, post('write')); $cache_file = $write_success ? get_dir_cache_path(dirname($file)) : false; - if($cache_file && file_exists($cache_file)) @unlink($cache_file); + if ($cache_file && file_exists($cache_file)) @unlink($cache_file); json_success($write_success); - // get - } else { + // get + } else { header('content-type: text/plain; charset=utf-8'); readfile($file); - } + } - // check login - } else if($action === 'check_login'){ + // check login + } else if ($action === 'check_login') { header('content-type: application/json'); echo '{"success":true}'; - // check updates - } else if($action === 'check_updates'){ + // check updates + } else if ($action === 'check_updates') { header('Content-Type: application/json'); $data = @file_get_contents('https://data.jsdelivr.com/v1/package/npm/files.photo.gallery'); $json = $data ? @json_decode($data, true) : false; $latest = !empty($json) && isset($json['versions']) ? $json['versions'][0] : false; - if($latest) { + if ($latest) { $is_new = version_compare($latest, config::$version) > 0; - exit('{"success":' . ($is_new ? '"'.$latest.'"' : 'false') . ($is_new ? ',"writeable":' . (is_writable(__DIR__) && is_writable(__FILE__) ? 'true' : 'false') : '') . '}'); + exit('{"success":' . ($is_new ? '"' . $latest . '"' : 'false') . ($is_new ? ',"writeable":' . (is_writable(__DIR__) && is_writable(__FILE__) ? 'true' : 'false') : '') . '}'); } exit('{"error": true }'); - - } else if($action === 'do_update'){ + } else if ($action === 'do_update') { header('Content-Type: application/json'); $version = post('version'); $file = 'https://cdn.jsdelivr.net/npm/files.photo.gallery' . ($version ? '@' . $version : '') . '/index.php'; @@ -1193,246 +1253,249 @@ if(post('action')){ $get = $writeable ? @file_get_contents($file) : false; $put = $get && strpos($get, '' && @file_put_contents(__FILE__, $get); exit('{"success":' . ($put ? 'true' : 'false') . '}'); - - } else if($action === 'license'){ + } else if ($action === 'license') { header('Content-Type: application/json'); $key = isset($_POST['key']) ? trim($_POST['key']) : false; $success = config::$storage_config_realpath && $key && config::save_config(array('license_key' => $key)); exit('{"success":' . ($success ? 'true, "md5" : "' . md5($key) . '"' : 'false') . '}'); - // invalid action - } else { + // invalid action + } else { json_error('invalid action: ' . $action); } -// GET -} else /*if($_SERVER['REQUEST_METHOD'] === 'GET')*/{ + // GET +} else /*if($_SERVER['REQUEST_METHOD'] === 'GET')*/ { - // file/image - if(isset($_GET['file'])){ + // file/image + if (isset($_GET['file'])) { new config(); get_file(valid_root_path(get('file')), get('resize')); - // download - } else if(isset($_GET['download'])){ + // download + } else if (isset($_GET['download'])) { new config(); - // valid download + // valid download $download = valid_root_path(get('download')); - if(!$download) error('Invalid download path ' . get('download') . '', 404); + if (!$download) error('Invalid download path ' . get('download') . '', 404); $download = real_path($download); // in case of symlink path - // required for some browsers - if(@ini_get('zlib.output_compression')) @ini_set('zlib.output_compression', 'Off'); + // required for some browsers + if (@ini_get('zlib.output_compression')) @ini_set('zlib.output_compression', 'Off'); - // headers - header('Content-Description: File Transfer'); - header('Content-Type: application/octet-stream'); - header('Content-Disposition: attachment; filename="' . basename($download) . '"'); - header('Content-Transfer-Encoding: binary'); - header('Expires: 0'); - header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); - header('Pragma: public'); - header('Content-Length: ' . filesize($download)); - ob_clean(); - flush(); - readfile($download); + // headers + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename="' . basename($download) . '"'); + header('Content-Transfer-Encoding: binary'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: ' . filesize($download)); + ob_clean(); + flush(); + readfile($download); - // tasks plugin - } else if(get('task')){ + // tasks plugin + } else if (get('task')) { // new config with tests new config(true); // get plugin $tasks_path = config::$storage_path . '/plugins/tasks.php'; - if(!file_exists($tasks_path)) error("Tasks plugin does not exist at $tasks_path", 404); + if (!file_exists($tasks_path)) error("Tasks plugin does not exist at $tasks_path", 404); include $tasks_path; exit; -// main document - } else { + // main document + } else { -// new config, with tests -new config(true); + // new config, with tests + new config(true); -// validate exclude regex -if(config::$config['files_exclude'] && @preg_match(config::$config['files_exclude'], '') === false) error('Invalid files_exclude regex ' . config::$config['files_exclude'] . ''); -if(config::$config['dirs_exclude'] && @preg_match(config::$config['dirs_exclude'], '') === false) error('Invalid dirs_exclude regex ' . config::$config['dirs_exclude'] . ''); + // validate exclude regex + if (config::$config['files_exclude'] && @preg_match(config::$config['files_exclude'], '') === false) error('Invalid files_exclude regex ' . config::$config['files_exclude'] . ''); + if (config::$config['dirs_exclude'] && @preg_match(config::$config['dirs_exclude'], '') === false) error('Invalid dirs_exclude regex ' . config::$config['dirs_exclude'] . ''); -// start path -$start_path = config::$config['start_path']; -if($start_path){ - $real_start_path = real_path($start_path); - if(!$real_start_path) error('start_path ' . $start_path . ' does not exist.'); - if(!is_within_root($real_start_path)) error('start_path ' . $start_path . ' is not within root dir ' . config::$config['root']); - $start_path = root_relative($real_start_path); -} + // start path + $start_path = config::$config['start_path']; + if ($start_path) { + $real_start_path = real_path($start_path); + if (!$real_start_path) error('start_path ' . $start_path . ' does not exist.'); + if (!is_within_root($real_start_path)) error('start_path ' . $start_path . ' is not within root dir ' . config::$config['root']); + $start_path = root_relative($real_start_path); + } -// root dirs (if menu) -$root_dirs = config::$config['menu_enabled'] || config::$config['breadcrumbs'] ? get_root_dirs() : false; -$menu_enabled = config::$config['menu_enabled'] && !empty($root_dirs) ? true : false; -$breadcrumbs = config::$config['breadcrumbs'] && !empty($root_dirs) ? true : false; + // root dirs (if menu) + $root_dirs = config::$config['menu_enabled'] || config::$config['breadcrumbs'] ? get_root_dirs() : false; + $menu_enabled = config::$config['menu_enabled'] && !empty($root_dirs) ? true : false; + $breadcrumbs = config::$config['breadcrumbs'] && !empty($root_dirs) ? true : false; -// get menu cache hash -$menu_cache_hash = false; -$menu_cache_file = false; -if($menu_enabled){ - $menu_cache_hash = get_menu_cache_hash($root_dirs); - // menu cache file (if cache, !menu_cache_validate, exists and is within doc root) - if(config::$storage_is_within_doc_root && config::$config['cache'] && !config::$config['menu_cache_validate']) { - $menu_cache_path = config::$cache_path . '/menu/' . $menu_cache_hash . '.json'; - $menu_cache_file = file_exists($menu_cache_path) ? get_url_path($menu_cache_path) : false; - if($menu_cache_file) $menu_cache_file .= '?' . filemtime($menu_cache_path); - } -} + // get menu cache hash + $menu_cache_hash = false; + $menu_cache_file = false; + if ($menu_enabled) { + $menu_cache_hash = get_menu_cache_hash($root_dirs); + // menu cache file (if cache, !menu_cache_validate, exists and is within doc root) + if (config::$storage_is_within_doc_root && config::$config['cache'] && !config::$config['menu_cache_validate']) { + $menu_cache_path = config::$cache_path . '/menu/' . $menu_cache_hash . '.json'; + $menu_cache_file = file_exists($menu_cache_path) ? get_url_path($menu_cache_path) : false; + if ($menu_cache_file) $menu_cache_file .= '?' . filemtime($menu_cache_path); + } + } -// init path -$query = config::$config['history'] && $_SERVER['QUERY_STRING'] ? explode('&', $_SERVER['QUERY_STRING']) : false; -$query_path = $query && strpos($query[0], '=') === false && $query[0] != 'debug' ? rtrim(rawurldecode($query[0]), '/') : false; -$query_path_valid = $query_path ? valid_root_path($query_path, true) : false; -$init_path = $query_path ?: $start_path ?: ''; + // init path + $query = config::$config['history'] && $_SERVER['QUERY_STRING'] ? explode('&', $_SERVER['QUERY_STRING']) : false; + $query_path = $query && strpos($query[0], '=') === false && $query[0] != 'debug' ? rtrim(rawurldecode($query[0]), '/') : false; + $query_path_valid = $query_path ? valid_root_path($query_path, true) : false; + $init_path = $query_path ?: $start_path ?: ''; -// init dirs, with files if cache -function get_dir_init($dir){ - $cache = get_dir_cache_path($dir); - if(file_exists($cache)) return json_decode(file_get_contents($cache), true); - return get_dir($dir); -} + // init dirs, with files if cache + function get_dir_init($dir) + { + $cache = get_dir_cache_path($dir); + if (file_exists($cache)) return json_decode(file_get_contents($cache), true); + return get_dir($dir); + } -// get dirs for root and start path -$dirs = array('' => get_dir_init(config::$root)); -if($query_path){ - if($query_path_valid) $dirs[$query_path] = get_dir_init($query_path_valid); -} else if($start_path){ - $dirs[$start_path] = get_dir_init($real_start_path); -} + // get dirs for root and start path + $dirs = array('' => get_dir_init(config::$root)); + if ($query_path) { + if ($query_path_valid) $dirs[$query_path] = get_dir_init($query_path_valid); + } else if ($start_path) { + $dirs[$start_path] = get_dir_init($real_start_path); + } -// resize image types -$resize_image_types = array('jpeg', 'jpg', 'png', 'gif'); -if(version_compare(PHP_VERSION, '5.4.0') >= 0) { - $resize_image_types[] = 'webp'; - if(version_compare(PHP_VERSION, '7.2.0') >= 0) $resize_image_types[] = 'bmp'; -} + // resize image types + $resize_image_types = array('jpeg', 'jpg', 'png', 'gif'); + if (version_compare(PHP_VERSION, '5.4.0') >= 0) { + $resize_image_types[] = 'webp'; + if (version_compare(PHP_VERSION, '7.2.0') >= 0) $resize_image_types[] = 'bmp'; + } -// image resize memory limit -$image_resize_memory_limit = config::$config['image_resize_enabled'] && config::$config['image_resize_memory_limit'] && function_exists('ini_get') ? (int) @ini_get('memory_limit') : 0; -if($image_resize_memory_limit && function_exists('ini_set')) $image_resize_memory_limit = max($image_resize_memory_limit, config::$config['image_resize_memory_limit']); + // image resize memory limit + $image_resize_memory_limit = config::$config['image_resize_enabled'] && config::$config['image_resize_memory_limit'] && function_exists('ini_get') ? (int) @ini_get('memory_limit') : 0; + if ($image_resize_memory_limit && function_exists('ini_set')) $image_resize_memory_limit = max($image_resize_memory_limit, config::$config['image_resize_memory_limit']); -$wtc = config::$config[base64_decode('bGljZW5zZV9rZXk')]; + $wtc = config::$config[base64_decode('bGljZW5zZV9rZXk')]; -// exclude some user settings from frontend -$exclude = array_diff_key(config::$config, array_flip(array('root', 'start_path', 'image_resize_cache', 'image_resize_quality', 'image_resize_function', 'image_resize_cache_direct', 'menu_sort', 'menu_load_all', 'cache_key', 'storage_path', 'files_exclude', 'dirs_exclude', 'username', 'password', 'breadcrumbs', 'allow_tasks', 'allow_symlinks', 'menu_recursive_symlinks', 'image_resize_sharpen', 'get_mime_type', 'license_key'))); -$json_config = array_replace($exclude, array( - 'breadcrumbs' => $breadcrumbs, - 'script' => basename(__FILE__), - 'menu_enabled' => $menu_enabled, - 'menu_cache_hash' => $menu_cache_hash, - 'menu_cache_file' => $menu_cache_file, - 'query_path' => $query_path, - 'query_path_valid' => $query_path_valid ? true : false, - 'init_path' => $init_path, - 'dirs' => $dirs, - 'dirs_hash' => config::$dirs_hash, - 'resize_image_types' => $resize_image_types, - 'post_hash' => md5(__FILE__ . $_SERVER['HTTP_HOST']), - 'image_cache_hash' => config::$config['load_images'] ? substr(md5(config::$doc_root . config::$root . config::$config['image_resize_function'] . config::$config['image_resize_quality']), 0, 6) : false, - 'image_resize_dimensions_retina' => config::$image_resize_dimensions_retina, - 'location_hash' => md5(config::$root), - 'has_login' => config::$has_login, - 'version' => config::$version, - 'index_html' => intval(get('index_html')), - 'server_exif' => function_exists('exif_read_data'), - 'image_resize_memory_limit' => $image_resize_memory_limit, - 'qrx' => $wtc && is_string($wtc) ? substr(md5($wtc), 0, strlen($wtc)) : false -)); + // exclude some user settings from frontend + $exclude = array_diff_key(config::$config, array_flip(array('root', 'start_path', 'image_resize_cache', 'image_resize_quality', 'image_resize_function', 'image_resize_cache_direct', 'menu_sort', 'menu_load_all', 'cache_key', 'storage_path', 'files_exclude', 'dirs_exclude', 'username', 'password', 'breadcrumbs', 'allow_tasks', 'allow_symlinks', 'menu_recursive_symlinks', 'image_resize_sharpen', 'get_mime_type', 'license_key'))); + $json_config = array_replace($exclude, array( + 'breadcrumbs' => $breadcrumbs, + 'script' => basename(__FILE__), + 'menu_enabled' => $menu_enabled, + 'menu_cache_hash' => $menu_cache_hash, + 'menu_cache_file' => $menu_cache_file, + 'query_path' => $query_path, + 'query_path_valid' => $query_path_valid ? true : false, + 'init_path' => $init_path, + 'dirs' => $dirs, + 'dirs_hash' => config::$dirs_hash, + 'resize_image_types' => $resize_image_types, + 'post_hash' => md5(__FILE__ . $_SERVER['HTTP_HOST']), + 'image_cache_hash' => config::$config['load_images'] ? substr(md5(config::$doc_root . config::$root . config::$config['image_resize_function'] . config::$config['image_resize_quality']), 0, 6) : false, + 'image_resize_dimensions_retina' => config::$image_resize_dimensions_retina, + 'location_hash' => md5(config::$root), + 'has_login' => config::$has_login, + 'version' => config::$version, + 'index_html' => intval(get('index_html')), + 'server_exif' => function_exists('exif_read_data'), + 'image_resize_memory_limit' => $image_resize_memory_limit, + 'qrx' => $wtc && is_string($wtc) ? substr(md5($wtc), 0, strlen($wtc)) : false + )); -// memory and time -header('files-msg: [' . header_memory_time() . ']'); + // memory and time + header('files-msg: [' . header_memory_time() . ']'); -// htmlstart -?> - -> - - - - - <?php echo $init_path ? basename($init_path) : '/'; ?> - - - - + // htmlstart + ?> + + > - -
- - -
- -
-
- - - - - - - + + + - - + + - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - + + + \ No newline at end of file + ?> \ No newline at end of file