This commit is contained in:
杜恒 2021-04-19 19:21:21 +08:00
parent 5f2fc632bb
commit 34d9e81c56

View File

@ -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,7 +105,8 @@ class config {
static $local_config_file = '_filesconfig.php';
// get config
private function get_config($path) {
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) {
@ -113,7 +115,8 @@ class 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);
@ -150,7 +153,8 @@ 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);
@ -160,7 +164,8 @@ class config {
// construct
function __construct($is_doc = false) {
function __construct($is_doc = false)
{
// normalize OS paths
self::$__dir__ = real_path(__DIR__);
@ -247,10 +252,12 @@ class config {
};
// login page
function login_page($is_login_attempt, $sidx, $is_logout, $client_hash){
function login_page($is_login_attempt, $sidx, $is_logout, $client_hash)
{
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover, user-scalable=no, shrink-to-fit=no">
@ -259,7 +266,10 @@ function login_page($is_login_attempt, $sidx, $is_logout, $client_hash){
<link href="<?php echo config::$assets ?>css/files.css" rel="stylesheet">
<?php custom_script('css'); ?>
</head>
<body><div id="files-login-container"></div></body>
<body>
<div id="files-login-container"></div>
</body>
<script>
document.getElementById('files-login-container').innerHTML = '\
<h1 class="header mb-5">Login</h1>\
@ -282,15 +292,18 @@ function login_page($is_login_attempt, $sidx, $is_logout, $client_hash){
this.method = 'post';
}, false);
</script>
</html>
<?php exit; // end form and exit
}
// check login
function check_login($is_doc){
function check_login($is_doc)
{
if ($is_doc) foreach (['username', 'password'] as $val) if (empty(config::$config[$val])) error($val . ' cannot be empty.');
if (!session_start() && $is_doc) error('Failed to initiate PHP session_start();', 500);
function get_client_hash(){
function get_client_hash()
{
foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key) {
if (isset($_SERVER[$key]) && !empty($_SERVER[$key]) && filter_var($_SERVER[$key], FILTER_VALIDATE_IP)) return md5($_SERVER[$key] . $_SERVER['HTTP_USER_AGENT'] . __FILE__ . $_SERVER['HTTP_HOST']);
}
@ -317,7 +330,8 @@ function check_login($is_doc){
$is_login_attempt = isset($_POST['fusername']) && isset($_POST['fpassword']) && isset($_POST['client_hash']) && isset($_POST['sidx']);
// correct login set $_SESSION['login']
if($is_login_attempt &&
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 &&
@ -333,7 +347,6 @@ function check_login($is_doc){
// 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,49 +354,62 @@ function check_login($is_doc){
}
//
function mkdir_or_error($path){
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){
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){
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);
@ -396,7 +422,8 @@ function read_file($path, $mime = 'image/jpeg', $msg = false, $props = false, $c
}
// get mime
function get_mime($path){
function get_mime($path)
{
if (function_exists('mime_content_type')) {
return mime_content_type($path);
} else {
@ -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,12 +443,14 @@ 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;
@ -449,7 +479,8 @@ function is_exclude($path = false, $is_dir = true, $symlinked = false){
}
// valid root path
function valid_root_path($path, $is_dir = false){
function valid_root_path($path, $is_dir = false)
{
// invalid
if ($path === false) return;
@ -481,7 +512,8 @@ function valid_root_path($path, $is_dir = false){
}
// image create from
function image_create_from($path, $type){
function image_create_from($path, $type)
{
if (!$path || !$type) return;
if ($type === IMAGETYPE_JPEG) {
return imagecreatefromjpeg($path);
@ -497,7 +529,8 @@ function image_create_from($path, $type){
}
// 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);
@ -530,7 +563,8 @@ 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),
@ -543,7 +577,8 @@ function sharpen_image($image){
// exif orientation
// https://github.com/gumlet/php-image-resize/blob/master/lib/ImageResize.php
function exif_orientation($orientation, &$image){
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);
@ -551,7 +586,8 @@ function exif_orientation($orientation, &$image){
}
// resize image
function resize_image($path, $resize_dimensions){
function resize_image($path, $resize_dimensions)
{
// file size
$file_size = filesize($path);
@ -644,7 +680,8 @@ 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){
function get_url_path($dir)
{
if (!is_within_docroot($dir)) return false;
// if in __dir__ path, __dir__ relative
@ -655,7 +692,8 @@ function get_url_path($dir){
}
// 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;
@ -700,7 +738,8 @@ function get_dir($path, $files = false, $json_url = false){
}
// get menu sort
function get_menu_sort($dirs){
function get_menu_sort($dirs)
{
if (strpos(config::$config['menu_sort'], 'date') === 0) {
usort($dirs, function ($a, $b) {
return filemtime($a) - filemtime($b);
@ -715,7 +754,8 @@ 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']) {
@ -742,12 +782,14 @@ function get_dirs($path = false, &$arr = array(), $depth = 0) {
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){
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;
@ -770,7 +812,8 @@ function get_iptc($image_info){
}
// get exif
function get_exif($path){
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;
@ -799,7 +842,8 @@ function get_exif($path){
return $exif;
}
function get_image_location($exif){
function get_image_location($exif)
{
$arr = array('GPSLatitudeRef', 'GPSLatitude', 'GPSLongitudeRef', 'GPSLongitude');
foreach ($arr as $val) {
if (!isset($exif[$val])) return false;
@ -827,7 +871,8 @@ function get_image_location($exif){
return array($latitude, $longitude);
}
function gps2Num($coordPart){
function gps2Num($coordPart)
{
$parts = explode('/', $coordPart);
if (count($parts) <= 0)
return 0;
@ -837,7 +882,8 @@ function gps2Num($coordPart){
}
//
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);
@ -1003,7 +1049,8 @@ function get_files_data($dir, $url_path = false, &$dirsize = 0, &$files_count =
}
// get files
function get_files($dir){
function get_files($dir)
{
// invalid $dir
if (!$dir) json_error('Invalid directory');
@ -1018,28 +1065,34 @@ function get_files($dir){
}
/* start here */
function post($param){
function post($param)
{
return isset($_POST[$param]) && !empty($_POST[$param]) ? $_POST[$param] : false;
}
function get($param){
function get($param)
{
return isset($_GET[$param]) && !empty($_GET[$param]) ? $_GET[$param] : false;
}
function json_cache($arr = array(), $msg = false, $cache = true){
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'){
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);
header('content-type: text/html');
@ -1051,7 +1104,8 @@ function error($msg, $code = false){
}
// get valid menu cache
function get_valid_menu_cache($cache){
function get_valid_menu_cache($cache)
{
if (!$cache || !file_exists($cache)) return;
$json = @file_get_contents($cache);
if (empty($json)) return;
@ -1066,7 +1120,8 @@ function get_valid_menu_cache($cache){
}
// get root dirs
function 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) {
@ -1075,20 +1130,23 @@ function get_root_dirs(){
}
// 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']) {
$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 ||
if (
!$menu_cache_arr ||
count($menu_cache_arr) !== 3 ||
strlen($menu_cache_arr[0]) !== 6 ||
strlen($menu_cache_arr[1]) !== 6 ||
@ -1111,7 +1169,8 @@ function dirs(){
}
// 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;
$dir = config::$storage_path . '/' . $type;
@ -1125,9 +1184,11 @@ function custom_script($type){
if (post('action')) {
// basic post access security: XMLHttpRequest + post_hash created from server paths
if(!isset($_SERVER['HTTP_X_REQUESTED_WITH']) ||
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('post_hash') !== md5(__FILE__ . $_SERVER['HTTP_HOST'])
) json_error('Invalid request hash. Please Refresh browser.');
// post action
$action = post('action');
@ -1183,7 +1244,6 @@ if(post('action')){
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') {
header('Content-Type: application/json');
$version = post('version');
@ -1193,7 +1253,6 @@ if(post('action')){
$get = $writeable ? @file_get_contents($file) : false;
$put = $get && strpos($get, '<?php') === 0 && substr($get, -2) === '?>' && @file_put_contents(__FILE__, $get);
exit('{"success":' . ($put ? 'true' : 'false') . '}');
} else if ($action === 'license') {
header('Content-Type: application/json');
$key = isset($_POST['key']) ? trim($_POST['key']) : false;
@ -1294,7 +1353,8 @@ $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){
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);
@ -1354,6 +1414,7 @@ header('files-msg: [' . header_memory_time() . ']');
?>
<!doctype html>
<html<?php echo ' class="menu-' . ($menu_enabled ? 'enabled' : 'disabled sidebar-closed') . '"'; ?>>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
@ -1432,7 +1493,9 @@ var CodeMirror = {};
<script src="<?php echo config::$assets ?>js/files.js"></script>
</body>
</html>
<?php }}
<?php }
}
// htmlend
?>