PHP Malware Analysis

manager3.php

md5: f521e6db720668d110bf95f1bc6776ca

Jump to:

Screenshot


Attributes

Emails

Encoding

Environment

Execution

Files

Input

Title

URLs
  • http://ffmpeg.org/ (Deobfuscated, Original)
  • https://cdn.jsdelivr.net/npm/@exeba/list.js@2.3.1/dist/list.min.js (Deobfuscated, HTML, Original)
  • https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js (Deobfuscated, HTML, Original)
  • https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js (Deobfuscated, HTML, Original)
  • https://cdn.jsdelivr.net/npm/codemirror@5.63.3/mode/meta.js (Deobfuscated, HTML, Original)
  • https://cdn.jsdelivr.net/npm/dayjs@1.10.7/dayjs.min.js (Deobfuscated, HTML, Original)
  • https://cdn.jsdelivr.net/npm/dayjs@1.10.7/plugin/localizedFormat.js (Deobfuscated, HTML, Original)
  • https://cdn.jsdelivr.net/npm/dayjs@1.10.7/plugin/relativeTime.js (Deobfuscated, HTML, Original)
  • https://cdn.jsdelivr.net/npm/files.photo.gallery@ (Deobfuscated, Original)
  • https://cdn.jsdelivr.net/npm/files.photo.gallery@0.3.1/ (Traces)
  • https://cdn.jsdelivr.net/npm/files.photo.gallery@0.3.1/css/files.css (HTML)
  • https://cdn.jsdelivr.net/npm/files.photo.gallery@0.3.1/js/files.js (HTML)
  • https://cdn.jsdelivr.net/npm/filesize@8.0.6/lib/filesize.min.js (Deobfuscated, HTML, Original)
  • https://cdn.jsdelivr.net/npm/headroom.js@0.12.0/dist/headroom.min.js (HTML)
  • https://cdn.jsdelivr.net/npm/js-file-downloader@1.1.22/dist/js-file-downloader.min.js (Deobfuscated, Original)
  • https://cdn.jsdelivr.net/npm/list.js@2.3.1/dist/list.min.js (Deobfuscated, HTML, Original)
  • https://cdn.jsdelivr.net/npm/mousetrap@1.6.5/mousetrap.min.js (HTML)
  • https://cdn.jsdelivr.net/npm/screenfull@5.1.0/dist/screenfull.min.js (Deobfuscated, HTML, Original)
  • https://cdn.jsdelivr.net/npm/sweetalert2@11.1.9/dist/sweetalert2.min.js (Deobfuscated, HTML, Original)
  • https://cdn.jsdelivr.net/npm/yall-js@3.2.0/dist/yall.min.js (Deobfuscated, HTML, Original)
  • https://data.jsdelivr.com/v1/package/npm/files.photo.gallery (Deobfuscated, Original)
  • https://files.photo.gallery/tools/hash/ (Deobfuscated, Original)
  • https://forum.photo.gallery/viewtopic.php?f=66&t=9964 (Deobfuscated, Original, Traces)
  • https://github.com/gavmck/resize/blob/master/php/lib/resize-class.php (Original)
  • https://github.com/gumlet/php-image-resize/blob/master/lib/ImageResize.php (Deobfuscated, Original)
  • https://github.com/maxim/smart_resize_image/blob/master/smart_resize_image.function.php (Original)
  • https://www.bitrepository.com/resize-an-image-keeping-its-aspect-ratio-using-php-and-gd.html (Original)
  • https://www.paypal.com/sdk/js?client-id=ARE4H7QcoXmKKWTfKk-pXAnsIfW1Zox3buwKma_y-mn4RjQZjS8Ghp1-JUxcevnkl5KI0vL-UIJ-uEfU (HTML)
  • https://www.paypal.com/tagmanager/pptm.js?id=localhost& (HTML)
  • https://www.php.net/manual/en/features.file-upload.errors.php (Deobfuscated, Original)
  • https://www.php.net/manual/en/function.exec.php (Deobfuscated, Original)


Deobfuscated PHP code

<?php

// errors
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// config
class config
{
    // DEFAULT CONFIG
    // Only edit directly if it is a temporary installation. Settings added here will be lost when updating!
    // Instead, add options from external config file in your storage_path [_files/config/config.php]
    // READ MORE: https://forum.photo.gallery/viewtopic.php?f=66&t=9964
    public static $default = array(
        // paths
        'root' => '',
        // root path relative to script / empty is same as files app location
        'start_path' => false,
        // start path relative to script. If empty, root is start path
        // login
        'username' => '',
        'password' => '',
        // Add password directly or use https://files.photo.gallery/tools/hash/ to encrypt the password (encrypted password is more secure, as it prevents your password from being exposed directly in a file).
        // images
        'load_images' => true,
        'load_files_proxy_php' => false,
        'load_images_max_filesize' => 1000000,
        // maximum file size (bytes) for un-resized images loaded into list
        'load_svg_max_filesize' => 100000,
        // 100k
        'image_resize_enabled' => true,
        // resize images for the interface
        'image_resize_cache' => true,
        // todo: remove this option and just use 'cache?
        'image_resize_dimensions' => 320,
        'image_resize_dimensions_retina' => 480,
        'image_resize_dimensions_allowed' => '',
        // comma-separated list of allowed resize dimensions
        'image_resize_types' => 'jpeg, png, gif, webp, bmp',
        // image types to resize / jpeg, png, gif, webp, bmp
        'image_resize_quality' => 85,
        'image_resize_function' => 'imagecopyresampled',
        // imagecopyresampled / imagecopyresized
        'image_resize_sharpen' => true,
        'image_resize_memory_limit' => 128,
        // 128 MB is suffient to resize images around 6000 px / 0 = ignore memory
        'image_resize_max_pixels' => 30000000,
        // 30 MP equivalent to an image 6000 x 5000 / 0 = no limit
        'image_resize_min_ratio' => 1.5,
        // min size diff original vs resize. Only resizes if ratio > min ratio
        'image_resize_cache_direct' => false,
        // if enabled and delete cache, must increase cache_key
        'folder_preview_image' => true,
        // enable folder preview images / might be slow as it requires searching each dir for images
        'folder_preview_default' => '_filespreview.jpg',
        // use this image as folder preview if exists in dir
        // menu
        'menu_enabled' => true,
        'menu_show' => true,
        'menu_max_depth' => 5,
        'menu_sort' => 'name_asc',
        // name_asc, name_desc, date_asc, date_desc
        'menu_cache_validate' => true,
        'menu_load_all' => false,
        'menu_recursive_symlinks' => true,
        // List sub-directories of symlinks in the main menu. May cause loops and duplicates
        // files layout
        'layout' => 'rows',
        // list, imagelist, blocks, grid, rows, columns
        'sort' => 'name_asc',
        // name, date, filesize, kind / asc, desc
        'sort_dirs_first' => true,
        // sort dirs on top
        // cache
        'cache' => true,
        'cache_key' => 0,
        'storage_path' => '_files',
        // exclude files directories regex
        'files_exclude' => '',
        // '/\.(pdf|jpe?g)$/i'
        'dirs_exclude' => '',
        //'/\/Convert|\/football|\/node_modules(\/|$)/i',
        'allow_symlinks' => true,
        // allow symlinks
        // various
        'history' => true,
        'breadcrumbs' => true,
        'transitions' => true,
        'click' => 'popup',
        // popup, modal, download, window, menu / default item click
        'click_window' => '',
        // 'pdf, html, php, zip' / list of file extensions to open directly on click
        'click_window_popup' => true,
        // Popup instead of new tab. Useful for viewing PDF, HTML and text type documents / desktop only
        'code_max_load' => 100000,
        // max filesize of text files to load and preview
        'topbar_sticky' => 'scroll',
        // true, false, 'scroll'
        'check_updates' => false,
        // show notification in topbar with option to update when new version is available
        'allow_tasks' => false,
        'get_mime_type' => false,
        // get file mime type from server (slow) instead of from extension (fast)
        'context_menu' => true,
        // disable context-menu button and right-click menu
        'prevent_right_click' => false,
        // blocks browser right-click menu on sensitive items (images, list items, menu)
        'license_key' => '',
        'filter_live' => true,
        // live search filtering on keyboard input / does not apply for mobile devices
        'filter_props' => 'name, filetype, mime, features, title',
        // file properties to filter / name, filetype, mime, features, title, headline, description, creator, credit, copyright, keywords, city, sub-location, province-state'
        'download_dir' => 'zip',
        // download all files in folder / 'zip' / 'files' / '' false (disabled)
        'download_dir_cache' => 'dir',
        // enable caching of created zip dirs / 'dir' / 'storage' / '' false (disabled)
        // filemanager options
        'allow_upload' => false,
        // allow uploader
        'allow_delete' => false,
        // allow deleting files and folders
        'allow_rename' => false,
        // allow renaming files and folders
        'allow_new_folder' => false,
        // allow make new directory
        'allow_new_file' => false,
        // allow make new empty file
        'allow_duplicate' => false,
        // allow duplicate files
        'allow_text_edit' => false,
        // allow editing text-based files in modal
        'demo_mode' => false,
        // block all filemanager operations but allow them to show in interface / used in Files app demo
        // uploader options
        'upload_allowed_file_types' => '',
        // comma-separated list of allowed upload file types / empty = allow any / 'jpeg, jpg, image/*'
        'upload_max_filesize' => 0,
        // [bytes] / 0 = unlimited (but limited by server PHP upload_max_filesize)
        'upload_note' => '',
        // include a small text note at bottom of uploader / 'Max file size %upload_max_filesize%'
        'upload_exists' => 'increment',
        // 'increment' / 'overwrite' / 'fail'
        // popup options
        'popup_video' => true,
        // opens videos in the popup (instead of modal)
        'popup_transition' => 'glide',
        // none, slide, glide, fade, zoom, pop, elastic
        'popup_transition_play' => 'inherit',
        // transition in play mode
        'popup_interval' => 5000,
        // interval ms between slides in play mode
        'popup_caption' => true,
        // enable popup caption
        'popup_caption_hide' => true,
        // autohide popup caption after a few seconds without user input
        'popup_caption_style' => 'block',
        // block, box, gradient, topbar, none
        'popup_caption_align' => 'center-left',
        // left, center-left, center, right
        // video
        'video_thumbs' => true,
        // allow video thumbnails / requires FFmpeg and PHP exec() function enabled.
        'video_ffmpeg_path' => 'ffmpeg',
        // path to ffmpeg command, normally 'ffmpeg' http://ffmpeg.org/
        'video_autoplay' => true,
        // video autoplay on click
        // language
        'lang_default' => 'en',
        // default language if browser lang is not supported/detected or lang_auto is disabled
        'lang_auto' => true,
        // automatically load language based on detected browser language
        'lang_menu' => false,
    );
    // config (will popuplate)
    public static $config = array();
    // app vars
    static $__dir__ = "/var/www/html";
    static $__file__ = "/var/www/html/manager3.php.cf6eb2c9bf1fb4ebc58b7883af08523b.bin";
    static $assets;
    static $prod = true;
    static $version = '0.3.1';
    static $root;
    static $doc_root;
    static $has_login = false;
    static $storage_path;
    static $storage_is_within_doc_root = false;
    static $storage_config_realpath;
    static $storage_config;
    static $cache_path;
    static $image_resize_cache_direct;
    static $image_resize_dimensions_retina = false;
    static $dirs_hash = false;
    static $local_config_file = '_filesconfig.php';
    static $username = false;
    static $password = false;
    static $x3_path = false;
    // get config
    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;
        }, $config);
    }
    // files check system and config [diagnostics]
    private function files_check($local_config, $storage_path, $storage_config, $user_config, $user_valid)
    {
        echo '<!doctype html><html><head><title>Files App check system and config.</title><meta name="robots" content="noindex,nofollow"><style>body{font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; color: #444;line-height:1.6;margin:0 auto;max-width:700px}.container{background-color:#F3F3F3;padding:.5vw 2vw 2vw;border-radius:3px;margin:1vw;overflow:scroll}.test:before{display:inline-block;width:18px;text-align:center;margin-right:5px}.neutral:before{color:#BBB}.success:before{color:#78a642}.success:before,.neutral:before{content:"\\2713"}.fail:before{content:"\\2716";color:firebrick}</style></head><body><div class="container"><h2>Files App ' . config::$version . '</h2><div style="margin:-1rem 0 .5rem">' . (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] . '<br>' : '') . 'PHP ' . phpversion() . '<br>' . (isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '') . '<p><i>* The following tests are only to help diagnose feature-specific issues.</i></p></div>';
        // output helper
        function prop($name, $success = 'neutral', $val = false)
        {
            return '<div class="test ' . (is_bool($success) ? $success ? 'success' : 'fail' : $success) . '">' . $name . ($val ? ': <b>' . $val . '</b>' : '') . '</div>';
        }
        echo prop('storage_path exists', file_exists(config::$config['storage_path']));
        if (file_exists(config::$config['storage_path'])) {
            echo prop('storage_path is_writeable', is_writable(config::$config['storage_path']));
        }
        echo prop('root is_writeable', is_writable(config::$config['root']));
        // extension_loaded
        if (function_exists('extension_loaded')) {
            foreach (['gd', 'exif'] as $name) {
                echo prop($name, extension_loaded($name));
            }
        }
        echo prop('ZipArchive', class_exists('ZipArchive'));
        // function_exsists
        foreach (['mime_content_type', 'finfo_file', 'iptcparse', 'exif_imagetype', 'session_start', 'ini_get', 'exec'] as $name) {
            echo prop($name . '()', function_exists($name));
        }
        // ffmpeg
        if (function_exists('exec')) {
            echo prop('ffmpeg', !!exec('type -P ' . config::$config['video_ffmpeg_path']));
        }
        // ini_get
        if (function_exists('ini_get')) {
            foreach (['memory_limit', 'file_uploads', 'upload_max_filesize', 'post_max_size', 'max_file_uploads'] as $name) {
                echo prop($name, 'neutral', @ini_get($name));
            }
        }
        echo "</div><div class=\"container\"><h3>Config</h3>";
        // invalid and duplicate arrays
        $user_invalid = array_diff_key($user_config, self::$default);
        $user_duplicate = array_intersect_assoc($user_valid, self::$default);
        // items
        $items = array(['arr' => $local_config, 'comment' => "// LOCAL CONFIG\n// " . self::$local_config_file], ['arr' => $storage_config, 'comment' => "// STORAGE CONFIG\n// " . rtrim($storage_path ?: '', '\\/') . '/config/config.php'], ['arr' => $user_invalid, 'comment' => "// INVALID PARAMS\n// The following custom parameters will be ignored as they are not valid:", 'var' => '$invalid', 'hide' => empty($user_invalid)], ['arr' => $user_duplicate, 'comment' => "// DUPLICATE DEFAULT PARAMS\n// The following custom parameters will have no effect as they are identical to defaults:", 'var' => '$duplicate', 'hide' => empty($user_duplicate)], ['arr' => $user_valid, 'comment' => "// USER CONFIG\n// User config parameters.", 'var' => '$user', 'hide' => (empty($local_config) || empty($storage_config)) && empty($user_invalid)], ['arr' => self::$config, 'comment' => "// CONFIG\n// User parameters merged with default parameters.", 'var' => '$config'], ['arr' => self::$default, 'comment' => "// DEFAULT CONFIG\n// Default config parameters.", 'var' => '$default']);
        // loop
        $output = "<?php\n";
        foreach ($items as $arr => $props) {
            $is_empty = empty($props['arr']);
            if (isset($props['hide']) && $props['hide']) {
                continue;
            }
            foreach (['username', 'password', 'license_key', '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 ';
            $output .= PHP_EOL . $comment . PHP_EOL . $var . $export . ';' . PHP_EOL;
        }
        highlight_string($output . PHP_EOL . ';?>');
        echo "</div></body></html>";
        exit;
    }
    // save config
    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);
            }
        }
        return @file_put_contents(config::$storage_config_realpath, "<?php \n\n// CONFIG / https://forum.photo.gallery/viewtopic.php?f=66&t=9964\n// Uncomment the parameters you want to edit.\nreturn " . $export . ';');
    }
    // construct
    function __construct($is_doc = false)
    {
        // normalize OS paths
        self::$__dir__ = real_path("/var/www/html");
        self::$__file__ = real_path("/var/www/html/manager3.php.cf6eb2c9bf1fb4ebc58b7883af08523b.bin");
        // local config
        $local_config = self::get_config(self::$local_config_file);
        // storage config
        $storage_path = isset($local_config['storage_path']) ? $local_config['storage_path'] : self::$default['storage_path'];
        $storage_realpath = !empty($storage_path) ? real_path($storage_path) : false;
        if ($is_doc && $storage_realpath === self::$__dir__) {
            error('<strong>storage_path must be a unique dir.</strong>');
        }
        self::$storage_config_realpath = $storage_realpath ? $storage_realpath . '/config/config.php' : false;
        self::$storage_config = self::get_config(self::$storage_config_realpath);
        // config
        $user_config = array_replace(self::$storage_config, $local_config);
        $user_valid = array_intersect_key($user_config, self::$default);
        self::$config = array_replace(self::$default, $user_valid);
        // files check with ?check=true
        if (get('check')) {
            self::files_check($local_config, $storage_path, self::$storage_config, $user_config, $user_valid);
        }
        // if(get('phpinfo')) { phpinfo(); exit; } // check system phpinfo with ?phpinfo=true / disabled for security / un-comment if you want to use
        // 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.');
        }
        // doc root
        self::$doc_root = real_path($_SERVER['DOCUMENT_ROOT']);
        // login credentials
        self::$username = self::$config['username'];
        self::$password = self::$config['password'];
        // X3 compatibility / x3 login / images from X3 resize cache / invalidate X3 cache on filemanager op / X3 license
        $x3_path = dirname(self::$root);
        self::$x3_path = file_exists($x3_path . '/app/x3.inc.php') ? $x3_path : false;
        if (self::$x3_path && self::$username === 'x3') {
            $x3_config = file_exists($x3_path . '/config/config.user.json') ? json_decode(file_get_contents($x3_path . '/config/config.user.json'), true) : false;
            self::$username = isset($x3_config['back']['panel']['username']) ? $x3_config['back']['panel']['username'] : 'admin';
            self::$password = isset($x3_config['back']['panel']['password']) ? $x3_config['back']['panel']['password'] : 'admin';
            if (self::$username === 'admin' && self::$password === 'admin' && isset($x3_config['back']['panel']['use_db'])) {
                error('Sorry, Files app is not compatible with X3 panel database login. Assign "username" and "password" separately in _files/config/config.php.', 403);
            }
        }
        // has_login
        self::$has_login = self::$username || self::$password ? true : false;
        // $image_cache
        $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']) {
            // create storage_path
            if (empty($storage_realpath)) {
                $storage_path = is_string($storage_path) ? rtrim($storage_path, '\\/') : false;
                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 <strong>{$storage_path}</strong> does not exist and can't be created.");
                }
                self::$storage_config_realpath = $storage_realpath . '/config/config.php';
                // update since it wasn't assigned
            }
            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;
            }
            // cache_path real path
            self::$cache_path = self::$storage_path . '/cache';
            // create storage dirs
            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);
                }
            }
            // 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("/var/www/html/manager3.php.cf6eb2c9bf1fb4ebc58b7883af08523b.bin"))) {
                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;
            }
        }
        // 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
        if (self::$has_login) {
            check_login($is_doc);
        }
    }
}
// login page
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">
    <meta name="robots" content="noindex,nofollow">
    <title>Login</title>
    <link href="<?php 
    echo config::$assets;
    ?>css/files.css" rel="stylesheet">
    <?php 
    get_include('css/custom.css');
    ?>
  </head>
  <body><div id="files-login-container"></div></body>
  <script>
    document.getElementById('files-login-container').innerHTML = '\
    <h1 class="header mb-5">Login</h1>\
    <?php 
    if ($is_login_attempt && $_POST['sidx'] !== $sidx) {
        ?><div class="alert alert-danger" role="alert"><strong>PHP session ID mismatch</strong><br>If the error persists, your PHP is incorrectly creating new session ID for each request.</div><?php 
    } else {
        if ($is_login_attempt) {
            ?>\
    <div class="alert alert-danger" role="alert">Incorrect login!</div><?php 
        } else {
            if ($is_logout) {
                ?>\
    <div class="alert alert-warning" role="alert">You are now logged out.</div><?php 
            }
        }
    }
    ?>\
    <form>\
      <div class="mylogin">\
        <input type="text" name="username" placeholder="Username">\
        <input type="password" name="password" placeholder="Password">\
      </div>\
        <input type="text" name="fusername" class="form-control form-control-lg mb-3" placeholder="Username" required autofocus spellcheck="false" autocorrect="off" autocapitalize="off">\
        <input type="password" name="fpassword" class="form-control form-control-lg mb-3" placeholder="Password" required spellcheck="false">\
      <input type="hidden" name="client_hash" value="<?php 
    echo $client_hash;
    ?>">\
      <input type="hidden" name="sidx" value="<?php 
    echo $sidx;
    ?>">\
      <input type="submit" value="Login" class="btn btn-lg btn-files-light btn-login">\
    </form>';
    document.getElementsByTagName('form')[0].addEventListener('submit', function(){
      this.action = '<?php 
    echo isset($_GET['logout']) ? strtok($_SERVER['REQUEST_URI'], '?') : $_SERVER['REQUEST_URI'];
    ?>';
      this.method = 'post';
    }, false);
  </script>
</html>
<?php 
    exit;
}
// check login
function check_login($is_doc)
{
    if ($is_doc && empty(config::$username)) {
        error('Username cannot be empty.');
    }
    if ($is_doc && empty(config::$password)) {
        error('Password cannot be empty.');
    }
    if (!session_start() && !$is_dor) {
        error('Failed to initiate PHP session_start();', 500);
    }
    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'] . "/var/www/html/manager3.php.cf6eb2c9bf1fb4ebc58b7883af08523b.bin" . $_SERVER['HTTP_HOST']);
            }
        }
        error('Invalid IP', 401);
    }
    // hash
    $client_hash = get_client_hash();
    $login_hash = md5(config::$username . config::$password . $client_hash);
    // login status
    $is_logout = isset($_GET['logout']) && isset($_SESSION['login']);
    if ($is_logout) {
        unset($_SESSION['login']);
    }
    $is_logged_in = !$is_logout && isset($_SESSION['login']) && $_SESSION['login'] === $login_hash;
    // not logged in
    if (!$is_logged_in) {
        // login only on html pages
        if ($is_doc) {
            // vars
            $sidx = md5(session_id());
            $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 && trim($_POST['fusername']) == config::$username && (phpversion() >= 5.5 && !password_needs_rehash(config::$password, PASSWORD_DEFAULT) ? password_verify(trim($_POST['fpassword']), config::$password) : trim($_POST['fpassword']) == config::$password) && $_POST['client_hash'] === $client_hash && $_POST['sidx'] === $sidx) {
                $_SESSION['login'] = $login_hash;
                // 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')) {
                json_error('login');
            } else {
                error('You are not logged in.', 401);
            }
        }
    }
}
//
function mkdir_or_error($path)
{
    if (!file_exists($path) && !mkdir($path, 0777, true)) {
        error('Failed to create ' . $path, 500);
    }
}
function real_path($path)
{
    $real_path = realpath($path);
    return $real_path ? str_replace('\\', '/', $real_path) : false;
}
function root_relative($dir)
{
    return ltrim(substr($dir, strlen(config::$root)), '\\/');
}
function root_absolute($dir)
{
    return config::$root . ($dir ? '/' . $dir : '');
}
function is_within_path($path, $root)
{
    return strpos($path . '/', $root . '/') === 0;
}
function is_within_root($path)
{
    return is_within_path($path, config::$root);
}
function is_within_docroot($path)
{
    return is_within_path($path, config::$doc_root);
}
function get_folders_cache_path($name)
{
    return config::$cache_path . '/folders/' . $name . '.json';
}
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;
    }
    return get_folders_cache_path(get_dir_cache_hash($dir, $mtime));
}
function get_dir_cache_hash($dir, $mtime = false)
{
    return config::$dirs_hash . '.' . substr(md5($dir), 0, 6) . '.' . ($mtime ?: filemtime($dir));
}
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 = false, $msg = false, $props = false, $cache_headers = false, $clone = false)
{
    if (!$path || !file_exists($path)) {
        return false;
    }
    $cloned = $clone && @copy($path, $clone) ? true : false;
    //if($mime == 'image/svg') $mime .= '+xml';
    header('content-type: ' . ($mime ?: 'image/jpeg'));
    header('content-length: ' . filesize($path));
    header('content-disposition: filename="' . basename($path) . '"');
    if ($msg) {
        header('files-msg: ' . $msg . ($cloned ? ' [cloned to ' . basename($clone) . ']' : '') . ' [' . ($props ? $props . ', ' : '') . header_memory_time() . ']');
    }
    if ($cache_headers) {
        set_cache_headers();
    }
    if (!is_readable($path) || readfile($path) === false) {
        error('Failed to read file ' . $path . '.', 400);
    }
    exit;
}
// get mime
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;
    }
}
// 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=31536000, s-maxage=31536000, immutable");
    header('pragma: cache');
    // header("Last-Modified:" . gmdate('D, d M Y H:i:s', time() - $seconds) . ' GMT');
    // etag?
}
// get image cache path
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 exclude
function is_exclude($path = false, $is_dir = true, $symlinked = false)
{
    // early exit
    if (!$path || $path === config::$root) {
        return;
    }
    // exclude all paths that start with /_files* (reserved for any files and folders to be ignored and hidden from Files app)
    if (strpos($path, '/_files') !== false) {
        return true;
    }
    // exclude files PHP application
    if ($path === config::$__file__) {
        return true;
    }
    // symlinks not allowed
    if ($symlinked && !config::$config['allow_symlinks']) {
        return true;
    }
    // exclude storage path
    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']) {
        $dirname = $is_dir ? $path : dirname($path);
        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) {
        $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;
        }
    }
}
// valid root path
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 /
    // 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;
    }
    // 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;
        }
    }
    // 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
    // return root_absolute
    return $root_absolute;
}
// image create from
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) {
                    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);
                        }
                    }
                }
            }
        }
    }
}
// get file (proxy or resize image)
function get_file($path, $resize = false)
{
    // validate
    if (!$path) {
        error('Invalid file request.', 404);
    }
    $path = real_path($path);
    // in case of symlink path
    $mime = get_mime($path);
    // may return false if server does not support mime_content_type() or finfo_file()
    // video thumbnail (FFmpeg)
    if ($resize == 'video') {
        // requirements with diagnostics / only check $mime if $mime detected
        if ($mime && strtok($mime, '/') !== 'video') {
            error('<strong>' . basename($path) . '</strong> (' . $mime . ') is not a video.', 415);
        }
        foreach (array('video_thumbs', 'load_images', 'image_resize_cache', 'video_ffmpeg_path') as $key) {
            if (empty(config::$config[$key])) {
                error($key . ' option disabled.', 400);
            }
        }
        if (!function_exists('exec')) {
            error('PHP <a href="https://www.php.net/manual/en/function.exec.php" target="_blank">exec()</a> function is disabled on this server.', 400);
        }
        if (empty(exec('type -P ' . config::$config['video_ffmpeg_path']))) {
            error('Can\'t find <a href="http://ffmpeg.org/" target="_blank">FFmpeg</a> in location "' . config::$config['video_ffmpeg_path'] . '".', 400);
        }
        // get cache path
        $cache = get_image_cache_path($path, 480, filesize($path), filemtime($path));
        // check for cached video thumbnail / $path, $mime, $msg, $props, $cache_headers
        if ($cache) {
            read_file($cache, null, 'Video thumb served from cache', null, true);
        }
        // ffmpeg command
        $cmd = escapeshellarg(config::$config['video_ffmpeg_path']) . ' -i ' . escapeshellarg($path) . ' -deinterlace -an -ss 1 -t 1 -vf "thumbnail,scale=480:320:force_original_aspect_ratio=increase,crop=480:320" -r 1 -y -f mjpeg ' . $cache . ' 2>&1';
        // try to execute command
        exec($cmd, $output, $result_code);
        // fail if result_code is anything else than 0
        if ($result_code) {
            error("Error generating thumbnail for video (\$result_code {$result_code})", 400);
        }
        // output created video thumbnail
        read_file($cache, null, 'Video thumb created', null, true);
        // resize image
    } else {
        if ($resize) {
            if ($mime && strtok($mime, '/') !== 'image') {
                error('<strong>' . basename($path) . '</strong> (' . $mime . ') is not an image.', 415);
            }
            foreach (['load_images', 'image_resize_enabled'] as $key) {
                if (!config::$config[$key]) {
                    error('[' . $key . '] disabled.', 400);
                }
            }
            $resize_dimensions = intval($resize);
            if (!$resize_dimensions) {
                error("Invalid resize parameter <strong>{$resize}</strong>.", 400);
            }
            $allowed = config::$config['image_resize_dimensions_allowed'] ?: [];
            if (!in_array($resize_dimensions, array_merge([config::$config['image_resize_dimensions'], config::$config['image_resize_dimensions_retina']], array_map('intval', is_array($allowed) ? $allowed : explode(',', $allowed))))) {
                error("Resize parameter <strong>{$resize_dimensions}</strong> is not allowed.", 400);
            }
            resize_image($path, $resize_dimensions);
            // 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);
            }
            // read file / $mime or 'application/octet-stream'
            read_file($path, $mime ?: 'application/octet-stream', $msg = 'File ' . basename($path) . ' proxied.', false, true);
        }
    }
}
// sharpen resized 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;
    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;
    }
    $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);
    }
    return true;
}
// resize image
function resize_image($path, $resize_dimensions, $clone = false)
{
    // file size
    $file_size = filesize($path);
    // header props
    $header_props = 'w:' . $resize_dimensions . ', q:' . config::$config['image_resize_quality'] . ', ' . config::$config['image_resize_function'] . ', cache:' . (config::$config['image_resize_cache'] ? '1' : '0');
    // 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, $clone);
    }
    // imagesize
    $info = getimagesize($path);
    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 <strong>' . $info[0] . ' x ' . $info[1] . '</strong> (' . $info[0] * $info[1] . ' px) exceeds <strong>image_resize_max_pixels</strong> (' . config::$config['image_resize_max_pixels'] . ' px).', 400);
    }
    // header props
    $header_props .= ', ' . $info['mime'] . ', ' . $info[0] . 'x' . $info[1] . ', ratio:' . round($resize_ratio, 2);
    // check if image type is in image_resize_types / jpeg, png, gif, webp, bmp
    $is_resize_type = in_array(image_type_to_extension($info[2], false), array_map(function ($key) {
        $type = trim(strtolower($key));
        return $type === 'jpg' ? 'jpeg' : $type;
    }, explode(',', config::$config['image_resize_types'])));
    // serve original if !$is_resize_type || resize ratio < image_resize_min_ratio
    if ((!$is_resize_type || $resize_ratio < max(config::$config['image_resize_min_ratio'], 1)) && !read_file($path, $info['mime'], 'Original image served', $header_props, true, $clone)) {
        error('File does not exist.', 404);
    }
    // Calculate new image dimensions.
    $resize_width = round($info[0] / $resize_ratio);
    $resize_height = round($info[1] / $resize_ratio);
    // 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) {
        // $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 <strong>' . $memory_required . 'M</strong>. Your current PHP memory_limit is <strong>' . $new_memory_limit . 'M</strong>.', 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
    $header_props .= ', ' . $resize_width . 'x' . $resize_height;
    // create new $image
    $image = image_create_from($path, $info[2]);
    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);
    }
    // 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'];
    }
    // sharpen resized 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('<strong>imagejpeg()</strong> failed to create and cache resized image.', 500);
        }
        // clone cache (used for folder previews)
        if ($clone) {
            @copy($cache, $clone);
        }
        // cache disabled / 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('<strong>imagejpeg()</strong> 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, $clone)) {
        error('Cache file does not exist.', 404);
    }
    //
    exit;
}
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);
    }
    // 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)
{
    // realpath
    $realpath = $path ? real_path($path) : false;
    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
    // vars
    $filemtime = filemtime($realpath);
    $url_path = get_url_path($realpath) ?: ($symlinked ? get_url_path($path) : false);
    $is_readable = is_readable($realpath);
    // array
    $arr = array('basename' => (basename($realpath) ?: basename($path)) ?: '', 'fileperms' => substr(sprintf('%o', fileperms($realpath)), -4), 'filetype' => 'dir', 'is_readable' => $is_readable, 'is_writeable' => is_writeable($realpath), 'is_link' => $symlinked ? is_link($path) : false, 'is_dir' => true, 'mime' => 'directory', 'mtime' => $filemtime, 'path' => root_relative($path));
    // url path
    if ($url_path) {
        $arr['url_path'] = $url_path;
    }
    // get_files() || config::menu_load_all
    if ($files && $is_readable) {
        // files array
        $arr['files'] = get_files_data($path, $url_path, $arr['dirsize'], $arr['files_count'], $arr['images_count'], $arr['preview']);
        // download_dir cache direct access to zip / better caching and no need to access PHP / only works when download_dir_cache === 'dir'
        /*if($url_path && config::$config['download_dir'] === 'zip' && config::$config['download_dir_cache'] === 'dir') {
            $zip = $realpath . '/_files.zip';
            if(file_exists($zip) && filemtime($zip) >= $filemtime) $arr['zip'] = get_url_path($zip);
          }*/
    }
    // 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;
        }
    }
    //
    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);
        });
    } else {
        natcasesort($dirs);
    }
    return substr(config::$config['menu_sort'], -4) === 'desc' ? array_reverse($dirs) : $dirs;
}
// recursive directory scan
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']) {
        $data = get_dir($path, config::$config['menu_load_all'], !config::$config['menu_load_all']);
        if (!$data) {
            return $arr;
        }
        //
        $arr[] = $data;
        // max depth
        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;
        }
    }
    // get dirs from files array if $data['files'] or glob subdirs
    $subdirs = isset($data['files']) ? array_filter(array_map(function ($file) {
        return $file['filetype'] === 'dir' ? root_absolute($file['path']) : false;
    }, $data['files'])) : glob($path . '/*', "GLOB_OO__V]R");
    // sort and loop subdirs
    if (!empty($subdirs)) {
        foreach (get_menu_sort($subdirs) as $subdir) {
            get_dirs($subdir, $arr, $depth + 1);
        }
    }
    // return
    return $arr;
}
// encode to UTF-8 when required
function safe_iptc_tag($val)
{
    $val = @substr($val, 0, 1000);
    return @mb_detect_encoding($val, 'UTF-8', true) ? $val : @utf8_encode($val);
}
// get IPTC
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();
    // loop title, headline, description, creator, credit, copyright, keywords, city, sub-location and province-state
    foreach (['title' => '005', 'headline' => '105', 'description' => '120', 'creator' => '080', 'credit' => '110', 'copyright' => '116', 'keywords' => '025', 'city' => '090', 'sub-location' => '092', 'province-state' => '095'] as $name => $code) {
        if (isset($app13['2#' . $code][0]) && !empty($app13['2#' . $code][0])) {
            $iptc[$name] = $name === 'keywords' ? $app13['2#' . $code] : safe_iptc_tag($app13['2#' . $code][0]);
        }
    }
    // 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']);
    }
    /*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'];
    }
    // 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;
    }
    // return
    return $exif;
}
// exif GPS / get_image_location
function get_image_location($exif)
{
    $arr = array();
    foreach (array('GPSLatitude', 'GPSLongitude') as $key) {
        if (!isset($exif[$key]) || !isset($exif[$key . 'Ref'])) {
            return false;
        }
        $coordinate = $exif[$key];
        if (is_string($coordinate)) {
            $coordinate = array_map('trim', explode(',', $coordinate));
        }
        for ($i = 0; $i < 3; $i++) {
            $part = explode('/', $coordinate[$i]);
            if (count($part) == 1) {
                $coordinate[$i] = $part[0];
            } else {
                if (count($part) == 2) {
                    if ($part[1] == 0) {
                        return false;
                    }
                    // can't be 0 / invalid GPS
                    $coordinate[$i] = floatval($part[0]) / floatval($part[1]);
                } else {
                    $coordinate[$i] = 0;
                }
            }
        }
        list($degrees, $minutes, $seconds) = $coordinate;
        $sign = $exif[$key . 'Ref'] == 'W' || $exif[$key . 'Ref'] == 'S' ? -1 : 1;
        $arr[] = $sign * ($degrees + $minutes / 60 + $seconds / 3600);
    }
    return $arr;
}
/*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]];
  $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)));

  return array($latitude, $longitude);
}

function gps2Num($coordPart){
  $parts = explode('/', $coordPart);
  if(count($parts) <= 0) return 0;
  if(count($parts) == 1) return $parts[0];
  if($parts[1] == 0) return 0;
  return floatval($parts[0]) / floatval($parts[1]);
}*/
//
function get_files_data($dir, $url_path = false, &$dirsize = 0, &$files_count = 0, &$images_count = 0, &$preview = false)
{
    // scandir
    $filenames = scandir($dir, SCANDIR_SORT_NONE);
    if (empty($filenames)) {
        return array();
    }
    $items = array();
    // look for folder_preview_default (might be excluded in loop)
    if (config::$config['folder_preview_default'] && in_array(config::$config['folder_preview_default'], $filenames)) {
        $preview = config::$config['folder_preview_default'];
    }
    // loop filenames
    foreach ($filenames as $filename) {
        //
        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
        $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
        // vars
        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;
        }
        // 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);
        }
        // root path // path relative to config::$root
        if (!$symlinked || is_within_root($realpath)) {
            $root_path = root_relative($realpath);
            // 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) {
                $basename_path = basename($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;
                        }
                        $loop_abs_path = root_absolute($loop_path);
                        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;
                    }
                }
            }
        }
        // add properties
        $item = array('basename' => $basename, 'fileperms' => substr(sprintf('%o', fileperms($realpath)), -4), 'filetype' => $filetype, 'filesize' => $filesize, 'is_readable' => $is_readable, 'is_writeable' => is_writeable($realpath), 'is_link' => $is_link, 'is_dir' => $is_dir, 'mtime' => $filemtime, 'path' => $root_path);
        // optional props
        //$ext = !$is_dir ? pathinfo($realpath, PATHINFO_EXTENSION) : false;
        $ext = !$is_dir ? substr(strrchr($realpath, '.'), 1) : false;
        if ($ext) {
            $ext = strtolower($ext);
            $item['ext'] = $ext;
        }
        $mime = $is_dir ? 'directory' : ($is_readable && (!$ext || $ext === 'ts' || config::$config['get_mime_type']) ? get_mime($realpath) : false);
        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) {
            // imagesize
            $imagesize = $is_readable ? @getimagesize($realpath, $info) : false;
            // image count and icon
            $images_count++;
            $item['icon'] = 'image';
            // is imagesize
            if (!empty($imagesize) && is_array($imagesize)) {
                // set folder_preview
                if (!$preview && in_array($ext, array('gif', 'jpg', 'jpeg', 'png'))) {
                    $preview = $basename;
                }
                // 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];
                    }
                }
                // mime from image
                if (!$mime && isset($image['mime'])) {
                    $item['mime'] = $image['mime'];
                }
                // IPTC
                $iptc = $info ? get_iptc($info) : false;
                if (!empty($iptc)) {
                    $image['iptc'] = $iptc;
                }
                // EXIF
                $exif = get_exif($realpath);
                if (!empty($exif)) {
                    $image['exif'] = $exif;
                    if (isset($exif['DateTimeOriginal'])) {
                        $item['DateTimeOriginal'] = $exif['DateTimeOriginal'];
                    }
                    // invert width/height if exif orientation
                    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) {
                    $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);
                    }
                    $retina = config::$image_resize_dimensions_retina;
                    if ($retina) {
                        $resize2 = get_image_cache_path($realpath, $retina, $filesize, $filemtime);
                        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) {
                    $mime = get_mime($realpath);
                    if ($mime) {
                        $item['mime'] = $mime;
                        if (strtok($mime, '/') !== 'image') {
                            // unset images_count and icon because is not image after all
                            $images_count--;
                            unset($item['icon']);
                        }
                    }
                }
            }
        }
        // add to items with basename as key
        $items[$basename] = $item;
    }
    // Sort dirs on top and natural case sort / need to do in JS anyway
    uasort($items, function ($a, $b) {
        if (!config::$config['sort_dirs_first'] || $a['is_dir'] === $b['is_dir']) {
            return strnatcasecmp($a['basename'], $b['basename']);
        }
        return $b['is_dir'] ? 1 : -1;
    });
    //
    return $items;
}
// get files
function get_files($dir)
{
    // invalid $dir
    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')) {
        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 get($param)
{
    return isset($_GET[$param]) && !empty($_GET[$param]) ? $_GET[$param] : false;
}
function json_cache($arr = array(), $msg = false, $cache = true)
{
    $json = empty($arr) ? '{}' : json_encode($arr, "JSON_UOWWKA\\_O_W^]W_O__ERROR");
    if (empty($json)) {
        json_error(json_last_error() ? json_last_error_msg() : 'json_encode() error');
    }
    if ($cache) {
        @file_put_contents($cache, $json);
    }
    if ($msg) {
        header('files-msg: ' . $msg . ' [' . header_memory_time() . ']');
    }
    header('content-type: application/json');
    echo $json;
}
function json_error($error = 'Error')
{
    json_exit(array('error' => $error));
}
function json_success($success = 'Success')
{
    json_exit(array('success' => $success));
}
function json_toggle($success, $error)
{
    json_exit(array_filter(array('success' => $success, 'error' => empty($success) ? $error : 0)));
}
function json_exit($arr = array())
{
    header('content-type: application/json');
    exit(json_encode($arr));
}
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');
    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('<h2>Error</h2>' . $msg);
}
// get valid menu cache
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;
    }
    $arr = @json_decode($json, true);
    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
    }
    return $json;
}
// get root dirs
function get_root_dirs()
{
    $root_dirs = glob(config::$root . '/*', "GLOB_OO__V]R");
    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)
{
    $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()
{
    // 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 || 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
    }
    $cache = config::$config['cache'] ? config::$cache_path . '/menu/' . $menu_cache_hash . '.json' : false;
    // get cache path
    $json = $cache ? get_valid_menu_cache($cache) : false;
    // get valid json menu cache
    // $json is valid from menu cache file
    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
    } else {
        json_cache(get_dirs(config::$root), 'dirs reloaded' . ($cache ? ' and cached.' : ' [cache disabled]'), $cache);
    }
}
// include file html, php, css, js
function get_include($file)
{
    if (!config::$storage_path) {
        return;
    }
    $path = config::$storage_path . '/' . $file;
    if (!file_exists($path)) {
        return;
    }
    $ext = pathinfo($path, PATHINFO_EXTENSION);
    if (in_array($ext, ['html', 'php'])) {
        return include $path;
    }
    if (!config::$storage_is_within_doc_root) {
        return;
    }
    $src = get_url_path($path) . '?' . filemtime($path);
    if ($ext === 'js') {
        echo '<script src="' . $src . '"></script>';
    }
    if ($ext === 'css') {
        echo '<link href="' . $src . '" rel="stylesheet">';
    }
}
// POST
if (post('action')) {
    // post action
    $action = post('action');
    //
    new config();
    // filemanager actions [beta]
    if ($action === 'fm') {
        // validate task
        $task = post('task');
        if (empty($task) || !isset(config::$config['allow_' . $task]) || !config::$config['allow_' . $task]) {
            json_error('invalid task');
        }
        // demo_mode
        if (config::$config['demo_mode']) {
            json_error('Action not allowed in demo mode');
        }
        // license required for file manager action
        if (!config::$config['license_key']) {
            json_error('License required!');
        }
        // valid path / path must be inside assigned root
        $is_dir = post('is_dir');
        $path = valid_root_path(post('path'), $is_dir);
        if (empty($path)) {
            json_error('invalid path ' . post('path'));
        }
        $path = real_path($path);
        // in case of symlink path
        // name_is_allowed / trim name, fail if empty or dodgy characters, mkfile, mkdir, rename, duplicate
        function name_is_allowed($name)
        {
            $name = $name ? trim($name) : false;
            // trim
            // block empty / <>:"'/\|?*# chars / .. / endswith .
            if (empty($name) || preg_match('/[<>:"\'\\/\\\\|?*#]|\\.\\.|\\.$/', $name)) {
                json_error('invalid name ' . $name);
            }
            return $name;
        }
        // filemanager json_toggle
        function fm_json_toggle($success, $error)
        {
            fm_json_exit($success, array_filter(array('success' => $success, 'error' => empty($success) ? $error : 0)));
        }
        // filemanager json_exit / includes feature to invalidate X3 cache if $x3_path
        function fm_json_exit($success, $arr)
        {
            if ($success && config::$x3_path) {
                touch(config::$x3_path . '/app/x3.inc.php');
            }
            json_exit($arr);
        }
        // UPLOAD
        if ($task === 'upload') {
            // upload path must be dir
            if (!$is_dir) {
                json_error('invalid dir ' . post('path'));
            }
            // upload path must be writeable
            if (!is_writable($path)) {
                json_error('upload dir ' . post('path') . ' is not writeable');
            }
            // get $_FILES['file']
            $file = isset($_FILES) && isset($_FILES['file']) && is_array($_FILES['file']) ? $_FILES['file'] : false;
            // invalid $_FILES['file']
            if (empty($file) || !isset($file['error']) || is_array($file['error'])) {
                json_error('invalid $_FILES[]');
            }
            // PHP meaningful file upload errors / https://www.php.net/manual/en/features.file-upload.errors.php
            if ($file['error'] !== 0) {
                $upload_errors = array(1 => 'Uploaded file exceeds upload_max_filesize directive in php.ini', 2 => 'Uploaded file exceeds MAX_FILE_SIZE directive specified in the HTML form', 3 => 'The uploaded file was only partially uploaded', 4 => 'No file was uploaded', 6 => 'Missing a temporary folder', 7 => 'Failed to write file to disk.', 8 => 'A PHP extension stopped the file upload.');
                json_error(isset($upload_errors[$file['error']]) ? $upload_errors[$file['error']] : 'unknown error');
            }
            // invalid $file['size']
            if (!isset($file['size']) || empty($file['size'])) {
                json_error('invalid file size');
            }
            // $file['size'] must not exceed $config['upload_max_filesize']
            if (config::$config['upload_max_filesize'] && $file['size'] > config::$config['upload_max_filesize']) {
                json_error('File size [' . $file['size'] . '] exceeds upload_max_filesize option [' . config::$config['upload_max_filesize'] . ']');
            }
            // filename
            $filename = $file['name'];
            // security: slashes are never ever allowed in filenames / always basenamed() but just in case
            if (strpos($filename, '/') !== false || strpos($filename, '\\') !== false) {
                json_error('Illegal \\slash/ in filename ' . $filename);
            }
            // allow only valid file types from config::$config['upload_allowed_file_types'] / 'image/*, .pdf, .mp4'
            $allowed_file_types = !empty(config::$config['upload_allowed_file_types']) ? array_filter(array_map('trim', explode(',', config::$config['upload_allowed_file_types']))) : false;
            if (!empty($allowed_file_types)) {
                $mime = get_mime($file['tmp_name']) ?: $file['type'];
                // mime from PHP or upload[type]
                $ext = strrchr(strtolower($filename), '.');
                $is_valid = false;
                // check if extension match || wildcard match mime type image/*
                foreach ($allowed_file_types as $allowed_file_type) {
                    if ($ext === '.' . ltrim($allowed_file_type, '.') || fnmatch($allowed_file_type, $mime)) {
                        $is_valid = true;
                        break;
                    }
                }
                if (!$is_valid) {
                    json_error('invalid file type ' . $filename);
                }
                // extra security: check if image is image
                if (function_exists('exif_imagetype') && in_array($ext, ['.gif', '.jpeg', '.jpg', '.png', '.swf', '.psd', '.bmp', '.tif', '.tiff', 'webp']) && !@exif_imagetype($file['tmp_name'])) {
                    json_error('invalid image type ' . $filename);
                }
            }
            // file naming if !overwrite and file exists
            if (config::$config['upload_exists'] !== 'overwrite' && file_exists("{$path}/{$filename}")) {
                // fail if !increment / 'upload_exists' => 'fail' || false || '' empty
                if (config::$config['upload_exists'] !== 'increment') {
                    json_error("{$filename} already exists");
                }
                // increment filename / 'upload_exists' => 'increment'
                $name = pathinfo($filename, PATHINFO_FILENAME);
                $ext = pathinfo($filename, PATHINFO_EXTENSION);
                $inc = 1;
                while (file_exists($path . '/' . $name . '-' . $inc . '.' . $ext)) {
                    $inc++;
                }
                $filename = $name . '-' . $inc . '.' . $ext;
            }
            // all is well! attempt to move_uploaded_file()
            if (@move_uploaded_file($file['tmp_name'], "{$path}/{$filename}")) {
                fm_json_exit(true, array(
                    'success' => true,
                    'filename' => $filename,
                    // return filename in case it was incremented or renamed
                    'url' => get_url_path("{$path}/{$filename}"),
                ));
            }
            // error if failed to move uploaded file
            json_error('failed to move_uploaded_file()');
            // DELETE
        } else {
            if ($task === 'delete') {
                // dir recursive
                if ($is_dir) {
                    // success/fail count
                    $success = 0;
                    $fail = 0;
                    // recursive rmdir
                    function rrmdir($dir, &$success, &$fail)
                    {
                        //global $success, $fail;
                        if (!is_readable($dir)) {
                            return $fail++;
                        }
                        $files = array_diff(scandir($dir), array('.', '..'));
                        if (!empty($files)) {
                            foreach ($files as $file) {
                                is_dir("{$dir}/{$file}") ? rrmdir("{$dir}/{$file}", $success, $fail) : (@unlink("{$dir}/{$file}") ? $success++ : $fail++);
                            }
                        }
                        @rmdir($dir) ? $success++ : $fail++;
                    }
                    // recursive rmdir start
                    rrmdir($path, $success, $fail);
                    // response with partial success/fail count or error if there is !$success
                    fm_json_exit($success, array_filter(array('success' => $success, 'fail' => $fail, 'error' => empty($success) ? 'Failed to delete dir' : 0)));
                    // single file
                } else {
                    fm_json_toggle(@unlink($path), 'PHP unlink() failed');
                }
                // new_folder || new_file
            } else {
                if ($task === 'new_folder' || $task === 'new_file') {
                    if (!$is_dir) {
                        json_error('invalid dir ' . post('path'));
                    }
                    // parent path must be dir
                    if (!is_writable($path)) {
                        json_error(post('path') . ' is not writeable.');
                    }
                    // dir must be writeable
                    $name = name_is_allowed(post('name'));
                    // trim and check valid
                    $file_path = $path . '/' . $name;
                    if (file_exists($file_path)) {
                        json_error($name . ' already exists');
                    }
                    fm_json_toggle($task === 'new_folder' ? @mkdir($file_path) : @touch($file_path), $task . ' failed');
                    // rename $path (file or dir)
                } else {
                    if ($task === 'rename') {
                        if (!is_writable($path)) {
                            json_error(post('path') . ' is not writeable.');
                        }
                        // path must be writeable
                        $name = name_is_allowed(post('name'));
                        // trim and check valid
                        $new_path = dirname($path) . '/' . $name;
                        if (file_exists($new_path)) {
                            json_error("{$name} already exists.");
                        }
                        // new name exists
                        // security: prevent renaming 'file.html' to 'file.php' / file must already be *.php when renaming
                        if (!$is_dir && stripos($path, '.php') === false && stripos($name, '.php') !== false) {
                            json_error('cannot rename files to .php');
                        }
                        fm_json_toggle(@rename($path, $new_path), 'PHP rename() failed');
                        // duplicate file
                    } else {
                        if ($task === 'duplicate') {
                            if ($is_dir) {
                                json_error('Can\'t duplicate dir');
                            }
                            $parent_dir = dirname($path);
                            if (!is_writable($parent_dir)) {
                                json_error(basename($parent_dir) . ' is not writeable.');
                            }
                            // dir must be writeable
                            $name = name_is_allowed(post('name'));
                            // trim and check valid
                            $copy_path = $parent_dir . '/' . $name;
                            if (file_exists($copy_path)) {
                                json_error($name . ' already exists.');
                            }
                            fm_json_toggle(@copy($path, $copy_path), 'PHP copy() failed');
                            // text / code edit
                        } else {
                            if ($task === 'text_edit') {
                                if ($is_dir) {
                                    json_error('Can\'t write text to directory');
                                }
                                if (!is_writeable($path) || !is_file($path)) {
                                    json_error('File is not writeable');
                                }
                                $success = isset($_POST['text']) && @file_put_contents($path, $_POST['text']) !== false ? 1 : 0;
                                // text could be '' (empty)
                                if ($success) {
                                    @touch(dirname($path));
                                }
                                // invalidate any cache by updating parent dir mtime
                                fm_json_toggle($success, 'PHP file_put_contents() failed');
                            }
                        }
                    }
                }
            }
        }
        // dirs
    } else {
        if ($action === 'dirs') {
            dirs(post('localstorage'));
            // files
        } else {
            if ($action === 'files') {
                if (!isset($_POST['dir'])) {
                    json_error('Missing dir parameter');
                }
                get_files(valid_root_path($_POST['dir'], true));
                // file read
            } else {
                if ($action === 'file') {
                    // valid path
                    $file = valid_root_path(post('file'));
                    if (!$file) {
                        error('Invalid file path');
                    }
                    // read text file
                    header('content-type:text/plain;charset=utf-8');
                    if (@readfile(real_path($file)) === false) {
                        error('failed to read file ' . post('file'), 500);
                    }
                    // check login
                } else {
                    if ($action === 'check_login') {
                        json_success(true);
                        // check updates
                    } else {
                        if ($action === 'check_updates') {
                            $json = @json_decode(@file_get_contents('https://data.jsdelivr.com/v1/package/npm/files.photo.gallery'), true);
                            $latest = !empty($json) && isset($json['versions'][0]) && version_compare($json['versions'][0], config::$version) > 0 ? $json['versions'][0] : false;
                            json_exit(array('success' => $latest, 'writeable' => $latest && is_writable("/var/www/html/manager3.php.cf6eb2c9bf1fb4ebc58b7883af08523b.bin")));
                            // do update
                        } else {
                            if ($action === 'do_update') {
                                $version = post('version');
                                if (!$version || version_compare($version, config::$version) <= 0 || !is_writable("/var/www/html/manager3.php.cf6eb2c9bf1fb4ebc58b7883af08523b.bin")) {
                                    json_error();
                                }
                                // requirements
                                $get = @file_get_contents('https://cdn.jsdelivr.net/npm/files.photo.gallery@' . $version . '/index.php');
                                if (empty($get) || strpos($get, '<?php') !== 0) {
                                    json_error();
                                }
                                // basic validation
                                json_success(array('success' => @file_put_contents("/var/www/html/manager3.php.cf6eb2c9bf1fb4ebc58b7883af08523b.bin", $get)));
                                // store license
                            } else {
                                if ($action === 'license') {
                                    $key = post('key') ? trim(post('key')) : false;
                                    json_exit(array('success' => $key && config::$storage_config_realpath && config::save_config(array('license_key' => $key)), 'md5' => $key ? md5($key) : false));
                                    // invalid action
                                } else {
                                    json_error('invalid action: ' . $action);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    // GET
} else {
    /*if($_SERVER['REQUEST_METHOD'] === 'GET')*/
    // download_dir_zip / download files in directory as zip file
    if (get('download_dir_zip')) {
        new config();
        // check download_dir enabled
        if (config::$config['download_dir'] !== 'zip') {
            error('<strong>download_dir</strong> Zip disabled.', 403);
        }
        // valid dir
        $dir = valid_root_path(get('download_dir_zip'), true);
        if (!$dir) {
            error('Invalid download path <strong>' . get('download_dir_zip') . '</strong>', 404);
        }
        $dir = real_path($dir);
        // in case of symlink path
        // create zip cache directly in dir (recommended, so that dir can be renamed while zip cache remains)
        if (!config::$storage_path || config::$config['download_dir_cache'] === 'dir') {
            if (!is_writable($dir)) {
                error('Dir ' . basename($dir) . ' is not writeable.', 500);
            }
            $zip_file_name = '_files.zip';
            $zip_file = $dir . '/' . $zip_file_name;
            // create zip file in storage _files/zip/$dirname.$md5.zip /
        } else {
            mkdir_or_error(config::$storage_path . '/zip');
            $zip_file_name = basename($dir) . '.' . substr(md5($dir), 0, 6) . '.zip';
            $zip_file = config::$storage_path . '/zip/' . $zip_file_name;
        }
        // cached / download_dir_cache && file_exists() && zip is not older than dir time
        $cached = !empty(config::$config['download_dir_cache']) && file_exists($zip_file) && filemtime($zip_file) >= filemtime($dir);
        // create zip if !cached
        if (!$cached) {
            // use shell zip command instead / probably faster and more robust than PHP / if use, comment out PHP ZipArchive method starting below
            // exec('zip ' . $zip_file . ' ' . $dir . '/*.* -j -x _files*', $out, $res);
            // check that ZipArchive class exists
            if (!class_exists('ZipArchive')) {
                error('Missing PHP ZipArchive class.', 500);
            }
            // glob files / must be readable / is_file / !symlink / !is_exclude
            $files = array_filter(glob($dir . '/*', GLOB_NOSORT), function ($file) {
                return is_readable($file) && is_file($file) && !is_link($file) && !is_exclude($file, false);
            });
            // !no files available to zip
            if (empty($files)) {
                error('No files to zip!', 400);
            }
            // new ZipArchive
            $zip = new ZipArchive();
            // create new $zip_file
            if ($zip->open($zip_file, ZipArchive::CREATE | ZIPARCHIVE::OVERWRITE) !== true) {
                error('Failed to create ZIP file ' . $zip_file_name . '.', 500);
            }
            // add files to zip / flatten with basename()
            foreach ($files as $file) {
                $zip->addFile($file, basename($file));
            }
            // no files added (for some reason)
            if (!$zip->numFiles) {
                error('Could not add any files to ' . $zip_file_name . '.', 500);
            }
            // close zip
            $zip->close();
            // make sure created zip file exists / just in case
            if (!file_exists($zip_file)) {
                error('Zip file ' . $zip_file_name . ' does not exist.', 500);
            }
        }
        // redirect instead of readfile() / might be useful if readfile() fails and/or for caching and performance
        /*$zip_url = get_url_path($zip_file);
          if($zip_url){
            header('Location:' . $zip_url . '?' . filemtime($dir), true, 302);
            exit;
          }*/
        // output headers
        if (config::$has_login) {
            header('cache-control: must-revalidate, post-check=0, pre-check=0');
            header('cache-control: public');
            header('expires: 0');
            header('pragma: public');
        } else {
            set_cache_headers();
        }
        header('content-description: File Transfer');
        header('content-disposition: attachment; filename="' . addslashes(basename($dir)) . '.zip"');
        $content_length = filesize($zip_file);
        header('content-length: ' . $content_length);
        header('content-transfer-encoding: binary');
        header('content-type: application/zip');
        header('files-msg: [' . $zip_file_name . '][' . ($cached ? 'cached' : 'created') . ']');
        // ignore user abort so we can delete file also on download cancel
        if (empty(config::$config['download_dir_cache'])) {
            @ignore_user_abort(true);
        }
        // clear output buffer for large files
        while (ob_get_level()) {
            ob_end_clean();
        }
        // output zip readfile()
        if (!readfile($zip_file)) {
            error('Failed to readfile(' . $zip_file_name . ').', 500);
        }
        // delete temp zip file if cache disable
        if (empty(config::$config['download_dir_cache'])) {
            @unlink($zip_file);
        }
        // folder preview image
    } else {
        if (get('preview')) {
            new config();
            // allow only if only if folder_preview_image + load_images + image_resize_enabled
            foreach (['folder_preview_image', 'load_images', 'image_resize_enabled'] as $key) {
                if (!config::$config[$key]) {
                    error('[' . $key . '] disabled.', 400);
                }
            }
            // get real path and validate
            $path = valid_root_path(get('preview'), true);
            // make sure is valid dir
            if (!$path) {
                error('Invalid directory.', 404);
            }
            // 1. first check for default '_filespreview.jpg' inside dir
            $default = config::$config['folder_preview_default'] ? $path . '/' . config::$config['folder_preview_default'] : false;
            if ($default && file_exists($default)) {
                header('files-preview: folder_preview_default found [' . config::$config['folder_preview_default'] . ']');
                resize_image($default, config::$config['image_resize_dimensions']);
            }
            // 2. check preview cache
            $cache = config::$cache_path . '/images/preview.' . substr(md5($path), 0, 6) . '.jpg';
            // cache file exists
            if (file_exists($cache)) {
                // make sure cache file is valid (must be newer than dir updated time)
                if (filemtime($cache) >= filemtime($path)) {
                    read_file($cache, null, 'preview image served from cache', null, true);
                }
                // delete expired cache file if is older than dir updated time [silent]
                @unlink($cache);
            }
            // 3. glob images / GLOB_BRACE may fail on some non GNU systems, like Solaris.
            $images = @glob($path . '/*.{jpg,JPG,jpeg,JPEG,png,PNG,gif,GIF}', "GLOB_N_SOWT");
            // loop images to locate first match that is not excluded
            if (!empty($images)) {
                foreach ($images as $image) {
                    if (!is_exclude($image, false)) {
                        header('files-preview: glob() found [' . basename($image) . ']');
                        resize_image($image, config::$config['image_resize_dimensions'], $cache);
                        // + clone into $cache
                        break;
                    }
                }
            }
            // 4. nothing found (no images in dir)
            // create empty 1px in $cache, and output (so next check knows dir is empty or has no images, unless updated)
            if (imagejpeg(imagecreate(1, 1), $cache)) {
                read_file($cache, 'image/jpeg', '1px placeholder image created and cached', null, true);
            }
            // file/image
        } else {
            if (isset($_GET['file'])) {
                new config();
                get_file(valid_root_path(get('file')), get('resize'));
                // download
            } else {
                if (isset($_GET['download'])) {
                    new config();
                    // valid download
                    $download = valid_root_path(get('download'));
                    if (!$download) {
                        error('Invalid download path <strong>' . get('download') . '</strong>', 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');
                    }
                    // 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));
                    while (ob_get_level()) {
                        ob_end_clean();
                    }
                    readfile($download);
                    // 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 <strong>{$tasks_path}</strong>", 404);
                        }
                        include $tasks_path;
                        exit;
                    } else {
                        // 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 <strong>' . config::$config['files_exclude'] . '</strong>');
                        }
                        if (config::$config['dirs_exclude'] && @preg_match(config::$config['dirs_exclude'], '') === false) {
                            error('Invalid dirs_exclude regex <strong>' . config::$config['dirs_exclude'] . '</strong>');
                        }
                        // 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;
                        // 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'] && isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING']) ? explode('&', $_SERVER['QUERY_STRING']) : false;
                        $query_path = $query && strpos($query[0], '=') === false ? 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(real_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);
                            }
                        }
                        // 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']);
                        }
                        // wtc
                        $wtc = config::$config["license_key"];
                        // look for custom language files _files/lang/*.json
                        function lang_custom()
                        {
                            $dir = config::$storage_path ? config::$storage_path . '/lang' : false;
                            $files = $dir && file_exists($dir) ? glob($dir . '/*.json') : false;
                            if (empty($files)) {
                                return false;
                            }
                            $langs = array();
                            foreach ($files as $path) {
                                $json = @file_get_contents($path);
                                $data = !empty($json) ? @json_decode($json, true) : false;
                                if (!empty($data)) {
                                    $langs[strtok(basename($path), '.')] = $data;
                                }
                            }
                            return !empty($langs) ? $langs : 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', 'video_thumbs', 'video_ffmpeg_path', 'folder_preview_default', 'image_resize_dimensions_allowed', 'download_dir_cache')));
                        // json config
                        $json_config = array_replace($exclude, array('breadcrumbs' => $breadcrumbs, 'script' => basename("/var/www/html/manager3.php.cf6eb2c9bf1fb4ebc58b7883af08523b.bin"), '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, '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, 'video_thumbs_enabled' => config::$config['video_thumbs'] && config::$config['video_ffmpeg_path'] && config::$config['load_images'] && config::$config['image_resize_cache'] && @function_exists('exec') && @exec('type -P ' . config::$config['video_ffmpeg_path']), 'lang_custom' => lang_custom(), 'x3_path' => config::$x3_path ? get_url_path(config::$x3_path) : false));
                        function php_directive_value_to_bytes($directive)
                        {
                            $val = function_exists('ini_get') ? @ini_get($directive) : false;
                            if (empty($val) || !is_string($val)) {
                                return 0;
                            }
                            preg_match('/^(?<value>\\d+)(?<option>[K|M|G]*)$/i', $val, $matches);
                            $value = (int) $matches['value'];
                            $option = strtoupper($matches['option']);
                            if ($option === 'K') {
                                $value *= 1024;
                            } elseif ($option === 'M') {
                                $value *= 1048576;
                            } elseif ($option === 'G') {
                                $value *= 1073741824;
                            }
                            return $value;
                        }
                        // upload options
                        if (config::$config['allow_upload']) {
                            if (function_exists('ini_get') && !@ini_get('file_uploads')) {
                                error('PHP file_uploads disabled on this server.', 500);
                            }
                            // get max_filesize from all potential limitations (must be > 0)
                            $max_sizes = array_filter(array(php_directive_value_to_bytes('upload_max_filesize'), php_directive_value_to_bytes('post_max_size'), config::$config['upload_max_filesize']));
                            // get min val from max_sizes[] || no limit
                            $json_config['upload_max_filesize'] = !empty($max_sizes) ? min($max_sizes) : 0;
                        }
                        // memory and time
                        header('files-msg: [' . header_memory_time() . ']');
                        // htmlstart
                        ?>
<!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">
    <meta name="robots" content="noindex,nofollow">
    <title><?php 
                        echo $init_path ? basename($init_path) : '/';
                        ?></title>
    <?php 
                        get_include('include/head.html');
                        ?>
    <link href="<?php 
                        echo config::$assets;
                        ?>css/files.css" rel="stylesheet">
    <?php 
                        get_include('css/custom.css');
                        ?>
  </head>

  <body class="body-loading"><svg viewBox="0 0 18 18" class="svg-preloader svg-preloader-active preloader-body"><circle cx="9" cy="9" r="8" pathLength="100" class="svg-preloader-circle"></svg>
    <main id="main">
      <?php 
                        $topbar_classes = array();
                        if (config::$config['topbar_sticky']) {
                            array_push($topbar_classes, 'topbar-sticky');
                        }
                        if ($breadcrumbs) {
                            array_push($topbar_classes, 'has-breadcrumbs');
                        }
                        ?>
      <nav id="topbar"<?php 
                        if (!empty($topbar_classes)) {
                            echo ' class="' . join(' ', $topbar_classes) . '"';
                        }
                        ?>>
        <div id="topbar-top">
          <div id="search-container"><input id="search" type="search" placeholder="search" size="1" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" disabled></div>
          <div id="change-layout" class="dropdown"></div>
          <div id="change-sort" class="dropdown"></div>
        </div>
        <?php 
                        if ($breadcrumbs) {
                            ?>
        <div id="topbar-breadcrumbs">
          <div class="breadcrumbs-info"></div>
          <div id="breadcrumbs"></div>
        </div>
        <?php 
                        }
                        ?>
        <div id="topbar-info" class="info-hidden"></div>
        <div id="files-sortbar"></div>
      </nav>
      <!-- files list container -->
      <div><div id="files" class="list files-<?php 
                        echo config::$config['layout'];
                        ?>"></div></div>
    </main>
<?php 
                        if ($menu_enabled) {
                            ?>
    <aside id="sidebar">
      <button id="sidebar-toggle" type="button" class="btn-icon"></button>
      <div id="sidebar-inner">
        <div id="sidebar-topbar"></div>
        <div id="sidebar-menu"></div>
      </div>
    </aside>
    <div id="sidebar-bg"></div> 
<?php 
                        }
                        ?>

    <!-- modal -->
    <div id="modal-bg"></div>
    <div class="modal" id="files_modal" tabindex="-1" role="dialog" data-action="close"></div>

    <!-- context menu -->
    <div id="contextmenu" class="dropdown-menu"></div>

    <!-- custom footer html -->
    <?php 
                        get_include('include/footer.html');
                        ?>

    <!-- Javascript -->
    <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.1.9/dist/sweetalert2.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
    <!--<script src="https://cdn.jsdelivr.net/npm/list.js@2.3.1/dist/list.min.js"></script>-->
    <script src="https://cdn.jsdelivr.net/npm/@exeba/list.js@2.3.1/dist/list.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/yall-js@3.2.0/dist/yall.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/filesize@8.0.6/lib/filesize.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/screenfull@5.1.0/dist/screenfull.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dayjs@1.10.7/dayjs.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dayjs@1.10.7/plugin/localizedFormat.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dayjs@1.10.7/plugin/relativeTime.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>
<?php 
                        if (config::$config['download_dir'] === 'files') {
                            ?>
    <script src="https://cdn.jsdelivr.net/npm/js-file-downloader@1.1.22/dist/js-file-downloader.min.js"></script>
<?php 
                        }
                        ?>
    <script>
var _c = <?php 
                        echo json_encode($json_config, "JSON_U_WW_Y_____^]W_OO_ERROR");
                        ?>;
var CodeMirror = {};
    </script>
    <script src="https://cdn.jsdelivr.net/npm/codemirror@5.63.3/mode/meta.js"></script>
    <!-- custom -->
    <?php 
                        get_include('js/custom.js');
                        ?>
    <!-- files -->
    <script src="<?php 
                        echo config::$assets;
                        ?>js/files.js"></script>

  </body>
</html>
<?php 
                    }
                }
            }
        }
    }
}
// htmlend

Execution traces

data/traces/f521e6db720668d110bf95f1bc6776ca_trace-1676251970.6703.xt
Version: 3.1.0beta2
File format: 4
TRACE START [2023-02-12 23:33:16.568101]
1	0	1	0.000235	393528
1	3	0	0.002813	935768	{main}	1		/var/www/html/uploads/manager3.php	0	0
2	4	0	0.002838	935768	ini_set	0		/var/www/html/uploads/manager3.php	4	2	'display_errors'	1
2	4	1	0.002857	935840
2	4	R			''
2	5	0	0.002872	935768	ini_set	0		/var/www/html/uploads/manager3.php	5	2	'display_startup_errors'	1
2	5	1	0.002886	935840
2	5	R			''
2	6	0	0.002899	935768	error_reporting	0		/var/www/html/uploads/manager3.php	6	1	32767
2	6	1	0.002913	935840
2	6	R			22527
2	7	0	0.002929	935800	post	1		/var/www/html/uploads/manager3.php	1289	1	'action'
2	7	1	0.002945	935800
2	7	R			FALSE
2	8	0	0.002959	935800	get	1		/var/www/html/uploads/manager3.php	1531	1	'download_dir_zip'
2	8	1	0.002973	935800
2	8	R			FALSE
2	9	0	0.002986	935800	get	1		/var/www/html/uploads/manager3.php	1632	1	'preview'
2	9	1	0.002999	935800
2	9	R			FALSE
2	10	0	0.003012	935800	get	1		/var/www/html/uploads/manager3.php	1713	1	'task'
2	10	1	0.003025	935800
2	10	R			FALSE
2	11	0	0.003039	935840	config->__construct	1		/var/www/html/uploads/manager3.php	1728	1	TRUE
3	12	0	0.003053	935840	real_path	1		/var/www/html/uploads/manager3.php	231	1	'/var/www/html/uploads'
4	13	0	0.003067	935840	realpath	0		/var/www/html/uploads/manager3.php	429	1	'/var/www/html/uploads'
4	13	1	0.003081	935920
4	13	R			'/var/www/html/uploads'
3		A						/var/www/html/uploads/manager3.php	429	$real_path = '/var/www/html/uploads'
4	14	0	0.003108	935888	str_replace	0		/var/www/html/uploads/manager3.php	430	3	'\\'	'/'	'/var/www/html/uploads'
4	14	1	0.003123	935984
4	14	R			'/var/www/html/uploads'
3	12	1	0.003139	935888
3	12	R			'/var/www/html/uploads'
2		A						/var/www/html/uploads/manager3.php	231	self::__dir__ = '/var/www/html/uploads'
3	15	0	0.003165	935824	real_path	1		/var/www/html/uploads/manager3.php	232	1	'/var/www/html/uploads/manager3.php'
4	16	0	0.003178	935824	realpath	0		/var/www/html/uploads/manager3.php	429	1	'/var/www/html/uploads/manager3.php'
4	16	1	0.003191	935920
4	16	R			'/var/www/html/uploads/manager3.php'
3		A						/var/www/html/uploads/manager3.php	429	$real_path = '/var/www/html/uploads/manager3.php'
4	17	0	0.003218	935888	str_replace	0		/var/www/html/uploads/manager3.php	430	3	'\\'	'/'	'/var/www/html/uploads/manager3.php'
4	17	1	0.003232	935984
4	17	R			'/var/www/html/uploads/manager3.php'
3	15	1	0.003248	935888
3	15	R			'/var/www/html/uploads/manager3.php'
2		A						/var/www/html/uploads/manager3.php	232	self::__file__ = '/var/www/html/uploads/manager3.php'
3	18	0	0.003276	935888	config->get_config	1		/var/www/html/uploads/manager3.php	235	1	'_filesconfig.php'
4	19	0	0.003291	935888	file_exists	0		/var/www/html/uploads/manager3.php	154	1	'_filesconfig.php'
4	19	1	0.003315	935928
4	19	R			FALSE
3	18	1	0.003330	935888
3	18	R			[]
2		A						/var/www/html/uploads/manager3.php	235	$local_config = []
2		A						/var/www/html/uploads/manager3.php	238	$storage_path = '_files'
3	20	0	0.003366	935888	real_path	1		/var/www/html/uploads/manager3.php	239	1	'_files'
4	21	0	0.003378	935888	realpath	0		/var/www/html/uploads/manager3.php	429	1	'_files'
4	21	1	0.003396	935920
4	21	R			FALSE
3		A						/var/www/html/uploads/manager3.php	429	$real_path = FALSE
3	20	1	0.003421	935888
3	20	R			FALSE
2		A						/var/www/html/uploads/manager3.php	239	$storage_realpath = FALSE
2		A						/var/www/html/uploads/manager3.php	241	self::storage_config_realpath = FALSE
3	22	0	0.003458	935888	config->get_config	1		/var/www/html/uploads/manager3.php	242	1	FALSE
3	22	1	0.003471	935888
3	22	R			[]
2		A						/var/www/html/uploads/manager3.php	242	self::storage_config = []
3	23	0	0.003495	935888	array_replace	0		/var/www/html/uploads/manager3.php	245	2	[]	[]
3	23	1	0.003510	936008
3	23	R			[]
2		A						/var/www/html/uploads/manager3.php	245	$user_config = []
3	24	0	0.003534	935944	array_intersect_key	0		/var/www/html/uploads/manager3.php	246	2	[]	['root' => '', 'start_path' => FALSE, 'username' => '', 'password' => '', 'load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_cache' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_dimensions_allowed' => '', 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_quality' => 85, 'image_resize_function' => 'imagecopyresampled', 'image_resize_sharpen' => TRUE, 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'image_resize_cache_direct' => FALSE, 'folder_preview_image' => TRUE, 'folder_preview_default' => '_filespreview.jpg', 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_sort' => 'name_asc', 'menu_cache_validate' => TRUE, 'menu_load_all' => FALSE, 'menu_recursive_symlinks' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'cache_key' => 0, 'storage_path' => '_files', 'files_exclude' => '', 'dirs_exclude' => '', 'allow_symlinks' => TRUE, 'history' => TRUE, 'breadcrumbs' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'allow_tasks' => FALSE, 'get_mime_type' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'license_key' => '', 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'download_dir_cache' => 'dir', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_thumbs' => TRUE, 'video_ffmpeg_path' => 'ffmpeg', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]
3	24	1	0.003636	936064
3	24	R			[]
2		A						/var/www/html/uploads/manager3.php	246	$user_valid = []
3	25	0	0.003660	936000	array_replace	0		/var/www/html/uploads/manager3.php	247	2	['root' => '', 'start_path' => FALSE, 'username' => '', 'password' => '', 'load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_cache' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_dimensions_allowed' => '', 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_quality' => 85, 'image_resize_function' => 'imagecopyresampled', 'image_resize_sharpen' => TRUE, 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'image_resize_cache_direct' => FALSE, 'folder_preview_image' => TRUE, 'folder_preview_default' => '_filespreview.jpg', 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_sort' => 'name_asc', 'menu_cache_validate' => TRUE, 'menu_load_all' => FALSE, 'menu_recursive_symlinks' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'cache_key' => 0, 'storage_path' => '_files', 'files_exclude' => '', 'dirs_exclude' => '', 'allow_symlinks' => TRUE, 'history' => TRUE, 'breadcrumbs' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'allow_tasks' => FALSE, 'get_mime_type' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'license_key' => '', 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'download_dir_cache' => 'dir', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_thumbs' => TRUE, 'video_ffmpeg_path' => 'ffmpeg', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]	[]
3	25	1	0.003747	944312
3	25	R			['root' => '', 'start_path' => FALSE, 'username' => '', 'password' => '', 'load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_cache' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_dimensions_allowed' => '', 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_quality' => 85, 'image_resize_function' => 'imagecopyresampled', 'image_resize_sharpen' => TRUE, 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'image_resize_cache_direct' => FALSE, 'folder_preview_image' => TRUE, 'folder_preview_default' => '_filespreview.jpg', 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_sort' => 'name_asc', 'menu_cache_validate' => TRUE, 'menu_load_all' => FALSE, 'menu_recursive_symlinks' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'cache_key' => 0, 'storage_path' => '_files', 'files_exclude' => '', 'dirs_exclude' => '', 'allow_symlinks' => TRUE, 'history' => TRUE, 'breadcrumbs' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'allow_tasks' => FALSE, 'get_mime_type' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'license_key' => '', 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'download_dir_cache' => 'dir', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_thumbs' => TRUE, 'video_ffmpeg_path' => 'ffmpeg', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]
2		A						/var/www/html/uploads/manager3.php	247	self::config = ['root' => '', 'start_path' => FALSE, 'username' => '', 'password' => '', 'load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_cache' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_dimensions_allowed' => '', 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_quality' => 85, 'image_resize_function' => 'imagecopyresampled', 'image_resize_sharpen' => TRUE, 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'image_resize_cache_direct' => FALSE, 'folder_preview_image' => TRUE, 'folder_preview_default' => '_filespreview.jpg', 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_sort' => 'name_asc', 'menu_cache_validate' => TRUE, 'menu_load_all' => FALSE, 'menu_recursive_symlinks' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'cache_key' => 0, 'storage_path' => '_files', 'files_exclude' => '', 'dirs_exclude' => '', 'allow_symlinks' => TRUE, 'history' => TRUE, 'breadcrumbs' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'allow_tasks' => FALSE, 'get_mime_type' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'license_key' => '', 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'download_dir_cache' => 'dir', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_thumbs' => TRUE, 'video_ffmpeg_path' => 'ffmpeg', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]
3	26	0	0.003939	944192	get	1		/var/www/html/uploads/manager3.php	250	1	'check'
3	26	1	0.003953	944192
3	26	R			FALSE
2		A						/var/www/html/uploads/manager3.php	254	self::assets = 'https://cdn.jsdelivr.net/npm/files.photo.gallery@0.3.1/'
3	27	0	0.003983	944272	real_path	1		/var/www/html/uploads/manager3.php	257	1	''
4	28	0	0.003996	944272	realpath	0		/var/www/html/uploads/manager3.php	429	1	''
4	28	1	0.004010	944352
4	28	R			'/var/www/html/uploads'
3		A						/var/www/html/uploads/manager3.php	429	$real_path = '/var/www/html/uploads'
4	29	0	0.004037	944320	str_replace	0		/var/www/html/uploads/manager3.php	430	3	'\\'	'/'	'/var/www/html/uploads'
4	29	1	0.004051	944416
4	29	R			'/var/www/html/uploads'
3	27	1	0.004067	944320
3	27	R			'/var/www/html/uploads'
2		A						/var/www/html/uploads/manager3.php	257	self::root = '/var/www/html/uploads'
3	30	0	0.004098	944320	real_path	1		/var/www/html/uploads/manager3.php	261	1	'/var/www/html'
4	31	0	0.004111	944320	realpath	0		/var/www/html/uploads/manager3.php	429	1	'/var/www/html'
4	31	1	0.004124	944392
4	31	R			'/var/www/html'
3		A						/var/www/html/uploads/manager3.php	429	$real_path = '/var/www/html'
4	32	0	0.004148	944360	str_replace	0		/var/www/html/uploads/manager3.php	430	3	'\\'	'/'	'/var/www/html'
4	32	1	0.004163	944456
4	32	R			'/var/www/html'
3	30	1	0.004177	944360
3	30	R			'/var/www/html'
2		A						/var/www/html/uploads/manager3.php	261	self::doc_root = '/var/www/html'
2		A						/var/www/html/uploads/manager3.php	264	self::username = ''
2		A						/var/www/html/uploads/manager3.php	265	self::password = ''
3	33	0	0.004223	944360	dirname	0		/var/www/html/uploads/manager3.php	268	1	'/var/www/html/uploads'
3	33	1	0.004236	944440
3	33	R			'/var/www/html'
2		A						/var/www/html/uploads/manager3.php	268	$x3_path = '/var/www/html'
3	34	0	0.004261	944464	file_exists	0		/var/www/html/uploads/manager3.php	269	1	'/var/www/html/app/x3.inc.php'
3	34	1	0.004281	944504
3	34	R			FALSE
2		A						/var/www/html/uploads/manager3.php	269	self::x3_path = FALSE
2		A						/var/www/html/uploads/manager3.php	278	self::has_login = FALSE
2		A						/var/www/html/uploads/manager3.php	281	$image_cache = TRUE
3	35	0	0.004331	944408	rtrim	0		/var/www/html/uploads/manager3.php	288	2	'_files'	'\\/'
3	35	1	0.004345	944480
3	35	R			'_files'
2		A						/var/www/html/uploads/manager3.php	288	$storage_path = '_files'
3	36	0	0.004370	944408	mkdir_or_error	1		/var/www/html/uploads/manager3.php	290	1	'_files'
4	37	0	0.004383	944408	file_exists	0		/var/www/html/uploads/manager3.php	426	1	'_files'
4	37	1	0.004398	944448
4	37	R			FALSE
4	38	0	0.004412	944408	mkdir	0		/var/www/html/uploads/manager3.php	426	3	'_files'	511	TRUE
4	38	1	0.004464	944632
4	38	R			TRUE
3	36	1	0.004480	944520
3	39	0	0.004487	944520	real_path	1		/var/www/html/uploads/manager3.php	291	1	'_files'
4	40	0	0.004500	944520	realpath	0		/var/www/html/uploads/manager3.php	429	1	'_files'
4	40	1	0.004516	944608
4	40	R			'/var/www/html/uploads/_files'
3		A						/var/www/html/uploads/manager3.php	429	$real_path = '/var/www/html/uploads/_files'
4	41	0	0.004542	944576	str_replace	0		/var/www/html/uploads/manager3.php	430	3	'\\'	'/'	'/var/www/html/uploads/_files'
4	41	1	0.004557	944672
4	41	R			'/var/www/html/uploads/_files'
3	39	1	0.004572	944576
3	39	R			'/var/www/html/uploads/_files'
2		A						/var/www/html/uploads/manager3.php	291	$storage_realpath = '/var/www/html/uploads/_files'
2		A						/var/www/html/uploads/manager3.php	293	self::storage_config_realpath = '/var/www/html/uploads/_files/config/config.php'
2		A						/var/www/html/uploads/manager3.php	295	self::storage_path = '/var/www/html/uploads/_files'
3	42	0	0.004625	944656	is_within_docroot	1		/var/www/html/uploads/manager3.php	298	1	'/var/www/html/uploads/_files'
4	43	0	0.004639	944656	is_within_path	1		/var/www/html/uploads/manager3.php	445	2	'/var/www/html/uploads/_files'	'/var/www/html'
5	44	0	0.004653	944752	strpos	0		/var/www/html/uploads/manager3.php	439	2	'/var/www/html/uploads/_files/'	'/var/www/html/'
5	44	1	0.004668	944824
5	44	R			0
4	43	1	0.004681	944656
4	43	R			TRUE
3	42	1	0.004694	944656
3	42	R			TRUE
2		A						/var/www/html/uploads/manager3.php	298	self::storage_is_within_doc_root = TRUE
2		A						/var/www/html/uploads/manager3.php	301	self::cache_path = '/var/www/html/uploads/_files/cache'
2		A						/var/www/html/uploads/manager3.php	305	$create_dirs = [0 => '/var/www/html/uploads/_files/config']
2		A						/var/www/html/uploads/manager3.php	306	$create_dirs[] = '/var/www/html/uploads/_files/cache/images'
3	45	0	0.004757	945408	array_push	0		/var/www/html/uploads/manager3.php	307	3	[0 => '/var/www/html/uploads/_files/config', 1 => '/var/www/html/uploads/_files/cache/images']	'/var/www/html/uploads/_files/cache/folders'	'/var/www/html/uploads/_files/cache/menu'
3	45	1	0.004774	945472
3	45	R			4
3	46	0	0.004787	945408	mkdir_or_error	1		/var/www/html/uploads/manager3.php	308	1	'/var/www/html/uploads/_files/config'
4	47	0	0.004801	945408	file_exists	0		/var/www/html/uploads/manager3.php	426	1	'/var/www/html/uploads/_files/config'
4	47	1	0.004820	945448
4	47	R			FALSE
4	48	0	0.004833	945408	mkdir	0		/var/www/html/uploads/manager3.php	426	3	'/var/www/html/uploads/_files/config'	511	TRUE
4	48	1	0.004872	945520
4	48	R			TRUE
3	46	1	0.004887	945408
3	49	0	0.004894	945408	mkdir_or_error	1		/var/www/html/uploads/manager3.php	308	1	'/var/www/html/uploads/_files/cache/images'
4	50	0	0.004908	945408	file_exists	0		/var/www/html/uploads/manager3.php	426	1	'/var/www/html/uploads/_files/cache/images'
4	50	1	0.004926	945448
4	50	R			FALSE
4	51	0	0.004940	945408	mkdir	0		/var/www/html/uploads/manager3.php	426	3	'/var/www/html/uploads/_files/cache/images'	511	TRUE
4	51	1	0.004991	945520
4	51	R			TRUE
3	49	1	0.005006	945408
3	52	0	0.005013	945408	mkdir_or_error	1		/var/www/html/uploads/manager3.php	308	1	'/var/www/html/uploads/_files/cache/folders'
4	53	0	0.005027	945408	file_exists	0		/var/www/html/uploads/manager3.php	426	1	'/var/www/html/uploads/_files/cache/folders'
4	53	1	0.005044	945448
4	53	R			FALSE
4	54	0	0.005058	945408	mkdir	0		/var/www/html/uploads/manager3.php	426	3	'/var/www/html/uploads/_files/cache/folders'	511	TRUE
4	54	1	0.005091	945520
4	54	R			TRUE
3	52	1	0.005105	945408
3	55	0	0.005112	945408	mkdir_or_error	1		/var/www/html/uploads/manager3.php	308	1	'/var/www/html/uploads/_files/cache/menu'
4	56	0	0.005125	945408	file_exists	0		/var/www/html/uploads/manager3.php	426	1	'/var/www/html/uploads/_files/cache/menu'
4	56	1	0.005143	945448
4	56	R			FALSE
4	57	0	0.005156	945408	mkdir	0		/var/www/html/uploads/manager3.php	426	3	'/var/www/html/uploads/_files/cache/menu'	511	TRUE
4	57	1	0.005188	945520
4	57	R			TRUE
3	55	1	0.005202	945408
3	58	0	0.005209	945408	file_exists	0		/var/www/html/uploads/manager3.php	312	1	'/var/www/html/uploads/_files/config/config.php'
3	58	1	0.005228	945448
3	58	R			FALSE
3	59	0	0.005242	945408	config::save_config	1		/var/www/html/uploads/manager3.php	312	1	???
4	60	0	0.005256	945408	array_replace	0		/var/www/html/uploads/manager3.php	221	2	[]	[]
4	60	1	0.005269	945528
4	60	R			[]
4	61	0	0.005282	945464	array_intersect_key	0		/var/www/html/uploads/manager3.php	221	2	[]	['root' => '', 'start_path' => FALSE, 'username' => '', 'password' => '', 'load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_cache' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_dimensions_allowed' => '', 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_quality' => 85, 'image_resize_function' => 'imagecopyresampled', 'image_resize_sharpen' => TRUE, 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'image_resize_cache_direct' => FALSE, 'folder_preview_image' => TRUE, 'folder_preview_default' => '_filespreview.jpg', 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_sort' => 'name_asc', 'menu_cache_validate' => TRUE, 'menu_load_all' => FALSE, 'menu_recursive_symlinks' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'cache_key' => 0, 'storage_path' => '_files', 'files_exclude' => '', 'dirs_exclude' => '', 'allow_symlinks' => TRUE, 'history' => TRUE, 'breadcrumbs' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'allow_tasks' => FALSE, 'get_mime_type' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'license_key' => '', 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'download_dir_cache' => 'dir', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_thumbs' => TRUE, 'video_ffmpeg_path' => 'ffmpeg', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]
4	61	1	0.005372	945584
4	61	R			[]
3		A						/var/www/html/uploads/manager3.php	221	$save_config = []
4	62	0	0.005398	945464	array_replace	0		/var/www/html/uploads/manager3.php	222	2	['root' => '', 'start_path' => FALSE, 'username' => '', 'password' => '', 'load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_cache' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_dimensions_allowed' => '', 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_quality' => 85, 'image_resize_function' => 'imagecopyresampled', 'image_resize_sharpen' => TRUE, 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'image_resize_cache_direct' => FALSE, 'folder_preview_image' => TRUE, 'folder_preview_default' => '_filespreview.jpg', 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_sort' => 'name_asc', 'menu_cache_validate' => TRUE, 'menu_load_all' => FALSE, 'menu_recursive_symlinks' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'cache_key' => 0, 'storage_path' => '_files', 'files_exclude' => '', 'dirs_exclude' => '', 'allow_symlinks' => TRUE, 'history' => TRUE, 'breadcrumbs' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'allow_tasks' => FALSE, 'get_mime_type' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'license_key' => '', 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'download_dir_cache' => 'dir', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_thumbs' => TRUE, 'video_ffmpeg_path' => 'ffmpeg', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]	[]
4	62	1	0.005485	953776
4	62	R			['root' => '', 'start_path' => FALSE, 'username' => '', 'password' => '', 'load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_cache' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_dimensions_allowed' => '', 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_quality' => 85, 'image_resize_function' => 'imagecopyresampled', 'image_resize_sharpen' => TRUE, 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'image_resize_cache_direct' => FALSE, 'folder_preview_image' => TRUE, 'folder_preview_default' => '_filespreview.jpg', 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_sort' => 'name_asc', 'menu_cache_validate' => TRUE, 'menu_load_all' => FALSE, 'menu_recursive_symlinks' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'cache_key' => 0, 'storage_path' => '_files', 'files_exclude' => '', 'dirs_exclude' => '', 'allow_symlinks' => TRUE, 'history' => TRUE, 'breadcrumbs' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'allow_tasks' => FALSE, 'get_mime_type' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'license_key' => '', 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'download_dir_cache' => 'dir', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_thumbs' => TRUE, 'video_ffmpeg_path' => 'ffmpeg', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]
4	63	0	0.005593	953712	var_export	0		/var/www/html/uploads/manager3.php	222	2	['root' => '', 'start_path' => FALSE, 'username' => '', 'password' => '', 'load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_cache' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_dimensions_allowed' => '', 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_quality' => 85, 'image_resize_function' => 'imagecopyresampled', 'image_resize_sharpen' => TRUE, 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'image_resize_cache_direct' => FALSE, 'folder_preview_image' => TRUE, 'folder_preview_default' => '_filespreview.jpg', 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_sort' => 'name_asc', 'menu_cache_validate' => TRUE, 'menu_load_all' => FALSE, 'menu_recursive_symlinks' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'cache_key' => 0, 'storage_path' => '_files', 'files_exclude' => '', 'dirs_exclude' => '', 'allow_symlinks' => TRUE, 'history' => TRUE, 'breadcrumbs' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'allow_tasks' => FALSE, 'get_mime_type' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'license_key' => '', 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'download_dir_cache' => 'dir', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_thumbs' => TRUE, 'video_ffmpeg_path' => 'ffmpeg', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]	TRUE
4	63	1	0.005739	957872
4	63	R			'array (\n  \'root\' => \'\',\n  \'start_path\' => false,\n  \'username\' => \'\',\n  \'password\' => \'\',\n  \'load_images\' => true,\n  \'load_files_proxy_php\' => false,\n  \'load_images_max_filesize\' => 1000000,\n  \'load_svg_max_filesize\' => 100000,\n  \'image_resize_enabled\' => true,\n  \'image_resize_cache\' => true,\n  \'image_resize_dimensions\' => 320,\n  \'image_resize_dimensions_retina\' => 480,\n  \'image_resize_dimensions_allowed\' => \'\',\n  \'image_resize_types\' => \'jpeg, png, gif, web'
4	64	0	0.005789	949560	preg_replace	0		/var/www/html/uploads/manager3.php	222	3	'/  \'/'	'  //\''	'array (\n  \'root\' => \'\',\n  \'start_path\' => false,\n  \'username\' => \'\',\n  \'password\' => \'\',\n  \'load_images\' => true,\n  \'load_files_proxy_php\' => false,\n  \'load_images_max_filesize\' => 1000000,\n  \'load_svg_max_filesize\' => 100000,\n  \'image_resize_enabled\' => true,\n  \'image_resize_cache\' => true,\n  \'image_resize_dimensions\' => 320,\n  \'image_resize_dimensions_retina\' => 480,\n  \'image_resize_dimensions_allowed\' => \'\',\n  \'image_resize_types\' => \'jpeg, png, gif, web'
4	64	1	0.005873	952728
4	64	R			'array (\n  //\'root\' => \'\',\n  //\'start_path\' => false,\n  //\'username\' => \'\',\n  //\'password\' => \'\',\n  //\'load_images\' => true,\n  //\'load_files_proxy_php\' => false,\n  //\'load_images_max_filesize\' => 1000000,\n  //\'load_svg_max_filesize\' => 100000,\n  //\'image_resize_enabled\' => true,\n  //\'image_resize_cache\' => true,\n  //\'image_resize_dimensions\' => 320,\n  //\'image_resize_dimensions_retina\' => 480,\n  //\'image_resize_dimensions_allowed\' => \'\',\n  //\'image_resize_type'
3		A						/var/www/html/uploads/manager3.php	222	$export = 'array (\n  //\'root\' => \'\',\n  //\'start_path\' => false,\n  //\'username\' => \'\',\n  //\'password\' => \'\',\n  //\'load_images\' => true,\n  //\'load_files_proxy_php\' => false,\n  //\'load_images_max_filesize\' => 1000000,\n  //\'load_svg_max_filesize\' => 100000,\n  //\'image_resize_enabled\' => true,\n  //\'image_resize_cache\' => true,\n  //\'image_resize_dimensions\' => 320,\n  //\'image_resize_dimensions_retina\' => 480,\n  //\'image_resize_dimensions_allowed\' => \'\',\n  //\'image_resize_type'
4	65	0	0.005962	951608	file_put_contents	0		/var/www/html/uploads/manager3.php	224	2	'/var/www/html/uploads/_files/config/config.php'	'<?php \n\n// CONFIG / https://forum.photo.gallery/viewtopic.php?f=66&t=9964\n// Uncomment the parameters you want to edit.\nreturn array (\n  //\'root\' => \'\',\n  //\'start_path\' => false,\n  //\'username\' => \'\',\n  //\'password\' => \'\',\n  //\'load_images\' => true,\n  //\'load_files_proxy_php\' => false,\n  //\'load_images_max_filesize\' => 1000000,\n  //\'load_svg_max_filesize\' => 100000,\n  //\'image_resize_enabled\' => true,\n  //\'image_resize_cache\' => true,\n  //\'image_resize_dimensions\''
4	65	1	0.006056	951680
4	65	R			2837
3	59	1	0.006081	945408
2		A						/var/www/html/uploads/manager3.php	319	self::image_resize_dimensions_retina = 480
3	66	0	0.006121	945504	md5	0		/var/www/html/uploads/manager3.php	322	1	'/var/www/html/var/www/html/uploads/var/www/html/uploads0.3.10'
3	66	1	0.006144	945600
3	66	R			'17042622a2c53ec164000929593e308e'
3	67	0	0.006167	945472	substr	0		/var/www/html/uploads/manager3.php	322	3	'17042622a2c53ec164000929593e308e'	0	6
3	67	1	0.006190	945600
3	67	R			'170426'
2		A						/var/www/html/uploads/manager3.php	322	self::dirs_hash = '170426'
2	11	1	0.006229	944592
1		A						/var/www/html/uploads/manager3.php	1735	$start_path = FALSE
2	68	0	0.006260	944552	get_root_dirs	1		/var/www/html/uploads/manager3.php	1744	0
3	69	0	0.006279	944600	glob	0		/var/www/html/uploads/manager3.php	1232	2	'/var/www/html/uploads/*'	8196
3	69	1	0.006329	945152
3	69	R			[0 => '/var/www/html/uploads/_files', 1 => '/var/www/html/uploads/data']
2		A						/var/www/html/uploads/manager3.php	1232	$root_dirs = [0 => '/var/www/html/uploads/_files', 1 => '/var/www/html/uploads/data']
3	70	0	0.006379	945360	array_filter	0		/var/www/html/uploads/manager3.php	1236	2	[0 => '/var/www/html/uploads/_files', 1 => '/var/www/html/uploads/data']	class Closure { virtual $closure = "{closure}", public $parameter = ['$dir' => '<required>'] }
4	71	0	0.006435	945488	{closure:/var/www/html/uploads/manager3.php:1234-1236}	1		/var/www/html/uploads/manager3.php	1236	1	'/var/www/html/uploads/_files'
5	72	0	0.006459	945488	is_link	0		/var/www/html/uploads/manager3.php	1235	1	'/var/www/html/uploads/_files'
5	72	1	0.006489	945560
5	72	R			FALSE
5	73	0	0.006511	945520	is_exclude	1		/var/www/html/uploads/manager3.php	1235	3	'/var/www/html/uploads/_files'	TRUE	FALSE
6	74	0	0.006533	945520	strpos	0		/var/www/html/uploads/manager3.php	512	2	'/var/www/html/uploads/_files'	'/_files'
6	74	1	0.006567	945592
6	74	R			21
5	73	1	0.006588	945520
5	73	R			TRUE
4	71	1	0.006609	945520
4	71	R			FALSE
4	75	0	0.006630	945520	{closure:/var/www/html/uploads/manager3.php:1234-1236}	1		/var/www/html/uploads/manager3.php	1236	1	'/var/www/html/uploads/data'
5	76	0	0.006654	945520	is_link	0		/var/www/html/uploads/manager3.php	1235	1	'/var/www/html/uploads/data'
5	76	1	0.006677	945560
5	76	R			FALSE
5	77	0	0.006697	945520	is_exclude	1		/var/www/html/uploads/manager3.php	1235	3	'/var/www/html/uploads/data'	TRUE	FALSE
6	78	0	0.006719	945520	strpos	0		/var/www/html/uploads/manager3.php	512	2	'/var/www/html/uploads/data'	'/_files'
6	78	1	0.006740	945592
6	78	R			FALSE
6	79	0	0.006761	945520	is_within_path	1		/var/www/html/uploads/manager3.php	521	2	'/var/www/html/uploads/data'	'/var/www/html/uploads/_files'
7	80	0	0.006782	945632	strpos	0		/var/www/html/uploads/manager3.php	439	2	'/var/www/html/uploads/data/'	'/var/www/html/uploads/_files/'
7	80	1	0.006803	945704
7	80	R			FALSE
6	79	1	0.006823	945520
6	79	R			FALSE
5	77	1	0.006845	945520
5	77	R			NULL
4	75	1	0.006865	945520
4	75	R			TRUE
3	70	1	0.006885	945840
3	70	R			[1 => '/var/www/html/uploads/data']
2	68	1	0.006910	945016
2	68	R			[1 => '/var/www/html/uploads/data']
1		A						/var/www/html/uploads/manager3.php	1744	$root_dirs = [1 => '/var/www/html/uploads/data']
1		A						/var/www/html/uploads/manager3.php	1745	$menu_enabled = TRUE
1		A						/var/www/html/uploads/manager3.php	1746	$breadcrumbs = TRUE
1		A						/var/www/html/uploads/manager3.php	1749	$menu_cache_hash = FALSE
1		A						/var/www/html/uploads/manager3.php	1750	$menu_cache_file = FALSE
2	81	0	0.007002	945016	get_menu_cache_hash	1		/var/www/html/uploads/manager3.php	1752	1	[1 => '/var/www/html/uploads/data']
3	82	0	0.007018	945016	filemtime	0		/var/www/html/uploads/manager3.php	1241	1	'/var/www/html/uploads'
3	82	1	0.007035	945080
3	82	R			1676251970
2		A						/var/www/html/uploads/manager3.php	1241	$mtime_count = 1676251970
3	83	0	0.007060	945040	filemtime	0		/var/www/html/uploads/manager3.php	1242	1	'/var/www/html/uploads/data'
3	83	1	0.007075	945088
3	83	R			1676251970
2		A						/var/www/html/uploads/manager3.php	1242	$mtime_count += 1676251970
3	84	0	0.007101	945128	md5	0		/var/www/html/uploads/manager3.php	1243	1	'/var/www/html/var/www/html/uploads/var/www/html/uploads'
3	84	1	0.007115	945224
3	84	R			'356888cdb6aec723f199c5dc79a286d0'
3	85	0	0.007130	945112	substr	0		/var/www/html/uploads/manager3.php	1243	3	'356888cdb6aec723f199c5dc79a286d0'	0	6
3	85	1	0.007144	945240
3	85	R			'356888'
3	86	0	0.007160	945120	md5	0		/var/www/html/uploads/manager3.php	1243	1	'0.3.105name_asc'
3	86	1	0.007173	945216
3	86	R			'295aa76f3a02cf22574cd0ae0fe9420c'
3	87	0	0.007187	945144	substr	0		/var/www/html/uploads/manager3.php	1243	3	'295aa76f3a02cf22574cd0ae0fe9420c'	0	6
3	87	1	0.007201	945272
3	87	R			'295aa7'
2	81	1	0.007216	945104
2	81	R			'356888.295aa7.3352503940'
1		A						/var/www/html/uploads/manager3.php	1752	$menu_cache_hash = '356888.295aa7.3352503940'
1		A						/var/www/html/uploads/manager3.php	1762	$query = FALSE
1		A						/var/www/html/uploads/manager3.php	1763	$query_path = FALSE
1		A						/var/www/html/uploads/manager3.php	1764	$query_path_valid = FALSE
1		A						/var/www/html/uploads/manager3.php	1765	$init_path = ''
2	88	0	0.007291	945104	get_dir_init	1		/var/www/html/uploads/manager3.php	1775	1	'/var/www/html/uploads'
3	89	0	0.007305	945104	real_path	1		/var/www/html/uploads/manager3.php	1769	1	'/var/www/html/uploads'
4	90	0	0.007319	945104	realpath	0		/var/www/html/uploads/manager3.php	429	1	'/var/www/html/uploads'
4	90	1	0.007334	945184
4	90	R			'/var/www/html/uploads'
3		A						/var/www/html/uploads/manager3.php	429	$real_path = '/var/www/html/uploads'
4	91	0	0.007360	945152	str_replace	0		/var/www/html/uploads/manager3.php	430	3	'\\'	'/'	'/var/www/html/uploads'
4	91	1	0.007376	945248
4	91	R			'/var/www/html/uploads'
3	89	1	0.007392	945152
3	89	R			'/var/www/html/uploads'
3	92	0	0.007412	945152	get_dir_cache_path	1		/var/www/html/uploads/manager3.php	1769	2	'/var/www/html/uploads'	???
4	93	0	0.007427	945152	get_dir_cache_hash	1		/var/www/html/uploads/manager3.php	456	2	'/var/www/html/uploads'	FALSE
5	94	0	0.007442	945184	md5	0		/var/www/html/uploads/manager3.php	459	1	'/var/www/html/uploads'
5	94	1	0.007455	945280
5	94	R			'efc1f9341a6f3e19acc38a2871eef237'
5	95	0	0.007470	945248	substr	0		/var/www/html/uploads/manager3.php	459	3	'efc1f9341a6f3e19acc38a2871eef237'	0	6
5	95	1	0.007484	945376
5	95	R			'efc1f9'
5	96	0	0.007498	945192	filemtime	0		/var/www/html/uploads/manager3.php	459	1	'/var/www/html/uploads'
5	96	1	0.007513	945224
5	96	R			1676251970
4	93	1	0.007528	945200
4	93	R			'170426.efc1f9.1676251970'
4	97	0	0.007542	945200	get_folders_cache_path	1		/var/www/html/uploads/manager3.php	456	1	'170426.efc1f9.1676251970'
4	97	1	0.007557	945312
4	97	R			'/var/www/html/uploads/_files/cache/folders/170426.efc1f9.1676251970.json'
3	92	1	0.007575	945256
3	92	R			'/var/www/html/uploads/_files/cache/folders/170426.efc1f9.1676251970.json'
2		A						/var/www/html/uploads/manager3.php	1769	$cache = '/var/www/html/uploads/_files/cache/folders/170426.efc1f9.1676251970.json'
3	98	0	0.007605	945208	file_exists	0		/var/www/html/uploads/manager3.php	1770	1	'/var/www/html/uploads/_files/cache/folders/170426.efc1f9.1676251970.json'
3	98	1	0.007635	945248
3	98	R			FALSE
3	99	0	0.007650	945208	get_dir	1		/var/www/html/uploads/manager3.php	1771	3	'/var/www/html/uploads'	???	???
4	100	0	0.007664	945208	real_path	1		/var/www/html/uploads/manager3.php	773	1	'/var/www/html/uploads'
5	101	0	0.007691	945208	realpath	0		/var/www/html/uploads/manager3.php	429	1	'/var/www/html/uploads'
5	101	1	0.007706	945288
5	101	R			'/var/www/html/uploads'
4		A						/var/www/html/uploads/manager3.php	429	$real_path = '/var/www/html/uploads'
5	102	0	0.007732	945256	str_replace	0		/var/www/html/uploads/manager3.php	430	3	'\\'	'/'	'/var/www/html/uploads'
5	102	1	0.007765	945352
5	102	R			'/var/www/html/uploads'
4	100	1	0.007780	945256
4	100	R			'/var/www/html/uploads'
3		A						/var/www/html/uploads/manager3.php	773	$realpath = '/var/www/html/uploads'
3		A						/var/www/html/uploads/manager3.php	775	$symlinked = FALSE
4	103	0	0.007818	945256	is_exclude	1		/var/www/html/uploads/manager3.php	778	3	'/var/www/html/uploads'	TRUE	FALSE
4	103	1	0.007832	945256
4	103	R			NULL
4	104	0	0.007846	945256	filemtime	0		/var/www/html/uploads/manager3.php	782	1	'/var/www/html/uploads'
4	104	1	0.007859	945296
4	104	R			1676251970
3		A						/var/www/html/uploads/manager3.php	782	$filemtime = 1676251970
4	105	0	0.007883	945256	get_url_path	1		/var/www/html/uploads/manager3.php	783	1	'/var/www/html/uploads'
5	106	0	0.007897	945256	is_within_docroot	1		/var/www/html/uploads/manager3.php	760	1	'/var/www/html/uploads'
6	107	0	0.007910	945256	is_within_path	1		/var/www/html/uploads/manager3.php	445	2	'/var/www/html/uploads'	'/var/www/html'
7	108	0	0.007924	945344	strpos	0		/var/www/html/uploads/manager3.php	439	2	'/var/www/html/uploads/'	'/var/www/html/'
7	108	1	0.007938	945416
7	108	R			0
6	107	1	0.007952	945256
6	107	R			TRUE
5	106	1	0.007965	945256
5	106	R			TRUE
5	109	0	0.007977	945256	is_within_path	1		/var/www/html/uploads/manager3.php	763	2	'/var/www/html/uploads'	'/var/www/html/uploads'
6	110	0	0.007991	945352	strpos	0		/var/www/html/uploads/manager3.php	439	2	'/var/www/html/uploads/'	'/var/www/html/uploads/'
6	110	1	0.008004	945424
6	110	R			0
5	109	1	0.008017	945256
5	109	R			TRUE
4	105	1	0.008031	945256
4	105	R			'.'
3		A						/var/www/html/uploads/manager3.php	783	$url_path = '.'
4	111	0	0.008055	945256	is_readable	0		/var/www/html/uploads/manager3.php	784	1	'/var/www/html/uploads'
4	111	1	0.008071	945296
4	111	R			TRUE
3		A						/var/www/html/uploads/manager3.php	784	$is_readable = TRUE
4	112	0	0.008095	945256	basename	0		/var/www/html/uploads/manager3.php	788	1	'/var/www/html/uploads'
4	112	1	0.008111	945320
4	112	R			'uploads'
4	113	0	0.008126	945984	fileperms	0		/var/www/html/uploads/manager3.php	789	1	'/var/www/html/uploads'
4	113	1	0.008139	946024
4	113	R			16895
4	114	0	0.008152	945984	sprintf	0		/var/www/html/uploads/manager3.php	789	2	'%o'	16895
4	114	1	0.008167	946368
4	114	R			'40777'
4	115	0	0.008179	946304	substr	0		/var/www/html/uploads/manager3.php	789	2	'40777'	-4
4	115	1	0.008197	946400
4	115	R			'0777'
4	116	0	0.008211	946016	is_writeable	0		/var/www/html/uploads/manager3.php	792	1	'/var/www/html/uploads'
4	116	1	0.008227	946056
4	116	R			TRUE
4	117	0	0.008241	946016	root_relative	1		/var/www/html/uploads/manager3.php	797	1	'/var/www/html/uploads'
5	118	0	0.008256	946016	substr	0		/var/www/html/uploads/manager3.php	433	2	'/var/www/html/uploads'	21
5	118	1	0.008269	946080
5	118	R			''
5	119	0	0.008281	946016	ltrim	0		/var/www/html/uploads/manager3.php	433	2	''	'\\/'
5	119	1	0.008295	946088
5	119	R			''
4	117	1	0.008307	946016
4	117	R			''
3		A						/var/www/html/uploads/manager3.php	797	$arr = ['basename' => 'uploads', 'fileperms' => '0777', 'filetype' => 'dir', 'is_readable' => TRUE, 'is_writeable' => TRUE, 'is_link' => FALSE, 'is_dir' => TRUE, 'mime' => 'directory', 'mtime' => 1676251970, 'path' => '']
3		A						/var/www/html/uploads/manager3.php	801	$arr['url_path'] = '.'
3	99	1	0.008357	945968
3	99	R			['basename' => 'uploads', 'fileperms' => '0777', 'filetype' => 'dir', 'is_readable' => TRUE, 'is_writeable' => TRUE, 'is_link' => FALSE, 'is_dir' => TRUE, 'mime' => 'directory', 'mtime' => 1676251970, 'path' => '', 'url_path' => '.']
2	88	1	0.008385	945856
2	88	R			['basename' => 'uploads', 'fileperms' => '0777', 'filetype' => 'dir', 'is_readable' => TRUE, 'is_writeable' => TRUE, 'is_link' => FALSE, 'is_dir' => TRUE, 'mime' => 'directory', 'mtime' => 1676251970, 'path' => '', 'url_path' => '.']
1		A						/var/www/html/uploads/manager3.php	1775	$dirs = ['' => ['basename' => 'uploads', 'fileperms' => '0777', 'filetype' => 'dir', 'is_readable' => TRUE, 'is_writeable' => TRUE, 'is_link' => FALSE, 'is_dir' => TRUE, 'mime' => 'directory', 'mtime' => 1676251970, 'path' => '', 'url_path' => '.']]
1		A						/var/www/html/uploads/manager3.php	1783	$resize_image_types = [0 => 'jpeg', 1 => 'jpg', 2 => 'png', 3 => 'gif']
2	120	0	0.008452	946232	version_compare	0		/var/www/html/uploads/manager3.php	1784	2	'7.2.34-37+ubuntu22.04.1+deb.sury.org+1'	'5.4.0'
2	120	1	0.008469	946296
2	120	R			1
1		A						/var/www/html/uploads/manager3.php	1785	$resize_image_types[] = 'webp'
2	121	0	0.008494	946608	version_compare	0		/var/www/html/uploads/manager3.php	1786	2	'7.2.34-37+ubuntu22.04.1+deb.sury.org+1'	'7.2.0'
2	121	1	0.008510	946672
2	121	R			1
1		A						/var/www/html/uploads/manager3.php	1786	$resize_image_types[] = 'bmp'
2	122	0	0.008535	946608	function_exists	0		/var/www/html/uploads/manager3.php	1790	1	'ini_get'
2	122	1	0.008550	946648
2	122	R			TRUE
2	123	0	0.008563	946608	ini_get	0		/var/www/html/uploads/manager3.php	1790	1	'memory_limit'
2	123	1	0.008578	946672
2	123	R			'128M'
1		A						/var/www/html/uploads/manager3.php	1790	$image_resize_memory_limit = 128
2	124	0	0.008604	946608	function_exists	0		/var/www/html/uploads/manager3.php	1791	1	'ini_set'
2	124	1	0.008618	946648
2	124	R			TRUE
2	125	0	0.008632	946608	max	0		/var/www/html/uploads/manager3.php	1791	2	128	128
2	125	1	0.008646	946640
2	125	R			128
1		A						/var/www/html/uploads/manager3.php	1791	$image_resize_memory_limit = 128
2	126	0	0.008670	946608	base64_decode	0		/var/www/html/uploads/manager3.php	1794	1	'bGljZW5zZV9rZXk'
2	126	1	0.008685	946680
2	126	R			'license_key'
1		A						/var/www/html/uploads/manager3.php	1794	$wtc = ''
2	127	0	0.008711	946608	array_flip	0		/var/www/html/uploads/manager3.php	1811	1	[0 => 'root', 1 => 'start_path', 2 => 'image_resize_cache', 3 => 'image_resize_quality', 4 => 'image_resize_function', 5 => 'image_resize_cache_direct', 6 => 'menu_sort', 7 => 'menu_load_all', 8 => 'cache_key', 9 => 'storage_path', 10 => 'files_exclude', 11 => 'dirs_exclude', 12 => 'username', 13 => 'password', 14 => 'breadcrumbs', 15 => 'allow_tasks', 16 => 'allow_symlinks', 17 => 'menu_recursive_symlinks', 18 => 'image_resize_sharpen', 19 => 'get_mime_type', 20 => 'license_key', 21 => 'video_thumbs', 22 => 'video_ffmpeg_path', 23 => 'folder_preview_default', 24 => 'image_resize_dimensions_allowed', 25 => 'download_dir_cache']
2	127	1	0.008748	947976
2	127	R			['root' => 0, 'start_path' => 1, 'image_resize_cache' => 2, 'image_resize_quality' => 3, 'image_resize_function' => 4, 'image_resize_cache_direct' => 5, 'menu_sort' => 6, 'menu_load_all' => 7, 'cache_key' => 8, 'storage_path' => 9, 'files_exclude' => 10, 'dirs_exclude' => 11, 'username' => 12, 'password' => 13, 'breadcrumbs' => 14, 'allow_tasks' => 15, 'allow_symlinks' => 16, 'menu_recursive_symlinks' => 17, 'image_resize_sharpen' => 18, 'get_mime_type' => 19, 'license_key' => 20, 'video_thumbs' => 21, 'video_ffmpeg_path' => 22, 'folder_preview_default' => 23, 'image_resize_dimensions_allowed' => 24, 'download_dir_cache' => 25]
2	128	0	0.008788	947944	array_diff_key	0		/var/www/html/uploads/manager3.php	1811	2	['root' => '', 'start_path' => FALSE, 'username' => '', 'password' => '', 'load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_cache' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_dimensions_allowed' => '', 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_quality' => 85, 'image_resize_function' => 'imagecopyresampled', 'image_resize_sharpen' => TRUE, 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'image_resize_cache_direct' => FALSE, 'folder_preview_image' => TRUE, 'folder_preview_default' => '_filespreview.jpg', 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_sort' => 'name_asc', 'menu_cache_validate' => TRUE, 'menu_load_all' => FALSE, 'menu_recursive_symlinks' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'cache_key' => 0, 'storage_path' => '_files', 'files_exclude' => '', 'dirs_exclude' => '', 'allow_symlinks' => TRUE, 'history' => TRUE, 'breadcrumbs' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'allow_tasks' => FALSE, 'get_mime_type' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'license_key' => '', 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'download_dir_cache' => 'dir', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_thumbs' => TRUE, 'video_ffmpeg_path' => 'ffmpeg', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]	['root' => 0, 'start_path' => 1, 'image_resize_cache' => 2, 'image_resize_quality' => 3, 'image_resize_function' => 4, 'image_resize_cache_direct' => 5, 'menu_sort' => 6, 'menu_load_all' => 7, 'cache_key' => 8, 'storage_path' => 9, 'files_exclude' => 10, 'dirs_exclude' => 11, 'username' => 12, 'password' => 13, 'breadcrumbs' => 14, 'allow_tasks' => 15, 'allow_symlinks' => 16, 'menu_recursive_symlinks' => 17, 'image_resize_sharpen' => 18, 'get_mime_type' => 19, 'license_key' => 20, 'video_thumbs' => 21, 'video_ffmpeg_path' => 22, 'folder_preview_default' => 23, 'image_resize_dimensions_allowed' => 24, 'download_dir_cache' => 25]
2	128	1	0.008908	950624
2	128	R			['load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'folder_preview_image' => TRUE, 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_cache_validate' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'history' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]
1		A						/var/www/html/uploads/manager3.php	1811	$exclude = ['load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'folder_preview_image' => TRUE, 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_cache_validate' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'history' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]
2	129	0	0.009042	950560	basename	0		/var/www/html/uploads/manager3.php	1816	1	'/var/www/html/uploads/manager3.php'
2	129	1	0.009058	950632
2	129	R			'manager3.php'
2	130	0	0.009075	950680	md5	0		/var/www/html/uploads/manager3.php	1826	1	'/var/www/html/var/www/html/uploadsimagecopyresampled85'
2	130	1	0.009090	950776
2	130	R			'f53b3cea7b4c2970384c976d83e7dbd0'
2	131	0	0.009105	950664	substr	0		/var/www/html/uploads/manager3.php	1826	3	'f53b3cea7b4c2970384c976d83e7dbd0'	0	6
2	131	1	0.009120	950792
2	131	R			'f53b3c'
2	132	0	0.009134	950632	md5	0		/var/www/html/uploads/manager3.php	1828	1	'/var/www/html/uploads'
2	132	1	0.009147	950728
2	132	R			'efc1f9341a6f3e19acc38a2871eef237'
2	133	0	0.009163	950696	get	1		/var/www/html/uploads/manager3.php	1831	1	'index_html'
2	133	1	0.009177	950696
2	133	R			FALSE
2	134	0	0.009190	950696	function_exists	0		/var/www/html/uploads/manager3.php	1832	1	'exif_read_data'
2	134	1	0.009204	950736
2	134	R			TRUE
2	135	0	0.009219	950696	function_exists	0		/var/www/html/uploads/manager3.php	1835	1	'exec'
2	135	1	0.009233	950736
2	135	R			TRUE
2	136	0	0.009246	950736	exec	0		/var/www/html/uploads/manager3.php	1835	1	'type -P ffmpeg'
2	136	1	0.009969	950816
2	136	R			'ffmpeg: not found'
2	137	0	0.010000	950696	lang_custom	1		/var/www/html/uploads/manager3.php	1836	0
2		A						/var/www/html/uploads/manager3.php	1798	$dir = '/var/www/html/uploads/_files/lang'
3	138	0	0.010030	950760	file_exists	0		/var/www/html/uploads/manager3.php	1799	1	'/var/www/html/uploads/_files/lang'
3	138	1	0.010055	950800
3	138	R			FALSE
2		A						/var/www/html/uploads/manager3.php	1799	$files = FALSE
2	137	1	0.010082	950696
2	137	R			FALSE
2	139	0	0.010096	950696	array_replace	0		/var/www/html/uploads/manager3.php	1815	2	['load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'folder_preview_image' => TRUE, 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_cache_validate' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'history' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE]	['breadcrumbs' => TRUE, 'script' => 'manager3.php', 'menu_enabled' => TRUE, 'menu_cache_hash' => '356888.295aa7.3352503940', 'menu_cache_file' => FALSE, 'query_path' => FALSE, 'query_path_valid' => FALSE, 'init_path' => '', 'dirs' => ['' => [...]], 'dirs_hash' => '170426', 'resize_image_types' => [0 => 'jpeg', 1 => 'jpg', 2 => 'png', 3 => 'gif', 4 => 'webp', 5 => 'bmp'], 'image_cache_hash' => 'f53b3c', 'image_resize_dimensions_retina' => 480, 'location_hash' => 'efc1f9341a6f3e19acc38a2871eef237', 'has_login' => FALSE, 'version' => '0.3.1', 'index_html' => 0, 'server_exif' => TRUE, 'image_resize_memory_limit' => 128, 'qrx' => FALSE, 'video_thumbs_enabled' => TRUE, 'lang_custom' => FALSE, 'x3_path' => FALSE]
2	139	1	0.010199	959008
2	139	R			['load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'folder_preview_image' => TRUE, 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_cache_validate' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'history' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE, 'breadcrumbs' => TRUE, 'script' => 'manager3.php', 'menu_cache_hash' => '356888.295aa7.3352503940', 'menu_cache_file' => FALSE, 'query_path' => FALSE, 'query_path_valid' => FALSE, 'init_path' => '', 'dirs' => ['' => [...]], 'dirs_hash' => '170426', 'resize_image_types' => [0 => 'jpeg', 1 => 'jpg', 2 => 'png', 3 => 'gif', 4 => 'webp', 5 => 'bmp'], 'image_cache_hash' => 'f53b3c', 'location_hash' => 'efc1f9341a6f3e19acc38a2871eef237', 'has_login' => FALSE, 'version' => '0.3.1', 'index_html' => 0, 'server_exif' => TRUE, 'qrx' => FALSE, 'video_thumbs_enabled' => TRUE, 'lang_custom' => FALSE, 'x3_path' => FALSE]
1		A						/var/www/html/uploads/manager3.php	1815	$json_config = ['load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'folder_preview_image' => TRUE, 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_cache_validate' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'history' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE, 'breadcrumbs' => TRUE, 'script' => 'manager3.php', 'menu_cache_hash' => '356888.295aa7.3352503940', 'menu_cache_file' => FALSE, 'query_path' => FALSE, 'query_path_valid' => FALSE, 'init_path' => '', 'dirs' => ['' => [...]], 'dirs_hash' => '170426', 'resize_image_types' => [0 => 'jpeg', 1 => 'jpg', 2 => 'png', 3 => 'gif', 4 => 'webp', 5 => 'bmp'], 'image_cache_hash' => 'f53b3c', 'location_hash' => 'efc1f9341a6f3e19acc38a2871eef237', 'has_login' => FALSE, 'version' => '0.3.1', 'index_html' => 0, 'server_exif' => TRUE, 'qrx' => FALSE, 'video_thumbs_enabled' => TRUE, 'lang_custom' => FALSE, 'x3_path' => FALSE]
2	140	0	0.010378	957608	header_memory_time	1		/var/www/html/uploads/manager3.php	1866	0
3	141	0	0.010392	957608	microtime	0		/var/www/html/uploads/manager3.php	463	1	TRUE
3	141	1	0.010406	957648
3	141	R			1676251970.6806
3	142	0	0.010422	957608	round	0		/var/www/html/uploads/manager3.php	463	2	0.010602951049805	3
3	142	1	0.010436	957680
3	142	R			0.011
3	143	0	0.010451	957648	memory_get_peak_usage	0		/var/www/html/uploads/manager3.php	463	0
3	143	1	0.010464	957648
3	143	R			1511312
3	144	0	0.010478	957648	round	0		/var/www/html/uploads/manager3.php	463	2	1.4412994384766	1
3	144	1	0.010491	957720
3	144	R			1.4
2	140	1	0.010504	957648
2	140	R			'0.011s, 1.4M'
2	145	0	0.010519	957664	header	0		/var/www/html/uploads/manager3.php	1866	1	'files-msg: [0.011s, 1.4M]'
2	145	1	0.010537	957768
2	145	R			NULL
2	146	0	0.010565	957680	get_include	1		/var/www/html/uploads/manager3.php	1877	1	'include/head.html'
2		A						/var/www/html/uploads/manager3.php	1278	$path = '/var/www/html/uploads/_files/include/head.html'
3	147	0	0.010593	957760	file_exists	0		/var/www/html/uploads/manager3.php	1279	1	'/var/www/html/uploads/_files/include/head.html'
3	147	1	0.010612	957800
3	147	R			FALSE
2	146	1	0.010626	957680
2	148	0	0.010634	957680	get_include	1		/var/www/html/uploads/manager3.php	1879	1	'css/custom.css'
2		A						/var/www/html/uploads/manager3.php	1278	$path = '/var/www/html/uploads/_files/css/custom.css'
3	149	0	0.010660	957760	file_exists	0		/var/www/html/uploads/manager3.php	1279	1	'/var/www/html/uploads/_files/css/custom.css'
3	149	1	0.010678	957800
3	149	R			FALSE
2	148	1	0.010692	957680
1		A						/var/www/html/uploads/manager3.php	1885	$topbar_classes = []
2	150	0	0.010711	957704	array_push	0		/var/www/html/uploads/manager3.php	1886	2	[]	'topbar-sticky'
2	150	1	0.010726	958144
2	150	R			1
2	151	0	0.010739	958080	array_push	0		/var/www/html/uploads/manager3.php	1887	2	[0 => 'topbar-sticky']	'has-breadcrumbs'
2	151	1	0.010759	958144
2	151	R			2
2	152	0	0.010773	958080	join	0		/var/www/html/uploads/manager3.php	1889	2	' '	[0 => 'topbar-sticky', 1 => 'has-breadcrumbs']
2	152	1	0.010790	958208
2	152	R			'topbar-sticky has-breadcrumbs'
2	153	0	0.010807	958080	get_include	1		/var/www/html/uploads/manager3.php	1926	1	'include/footer.html'
2		A						/var/www/html/uploads/manager3.php	1278	$path = '/var/www/html/uploads/_files/include/footer.html'
3	154	0	0.010834	958160	file_exists	0		/var/www/html/uploads/manager3.php	1279	1	'/var/www/html/uploads/_files/include/footer.html'
3	154	1	0.010850	958200
3	154	R			FALSE
2	153	1	0.010864	958080
2	155	0	0.010872	958080	json_encode	0		/var/www/html/uploads/manager3.php	1944	2	['load_images' => TRUE, 'load_files_proxy_php' => FALSE, 'load_images_max_filesize' => 1000000, 'load_svg_max_filesize' => 100000, 'image_resize_enabled' => TRUE, 'image_resize_dimensions' => 320, 'image_resize_dimensions_retina' => 480, 'image_resize_types' => 'jpeg, png, gif, webp, bmp', 'image_resize_memory_limit' => 128, 'image_resize_max_pixels' => 30000000, 'image_resize_min_ratio' => 1.5, 'folder_preview_image' => TRUE, 'menu_enabled' => TRUE, 'menu_show' => TRUE, 'menu_max_depth' => 5, 'menu_cache_validate' => TRUE, 'layout' => 'rows', 'sort' => 'name_asc', 'sort_dirs_first' => TRUE, 'cache' => TRUE, 'history' => TRUE, 'transitions' => TRUE, 'click' => 'popup', 'click_window' => '', 'click_window_popup' => TRUE, 'code_max_load' => 100000, 'topbar_sticky' => 'scroll', 'check_updates' => FALSE, 'context_menu' => TRUE, 'prevent_right_click' => FALSE, 'filter_live' => TRUE, 'filter_props' => 'name, filetype, mime, features, title', 'download_dir' => 'zip', 'allow_upload' => FALSE, 'allow_delete' => FALSE, 'allow_rename' => FALSE, 'allow_new_folder' => FALSE, 'allow_new_file' => FALSE, 'allow_duplicate' => FALSE, 'allow_text_edit' => FALSE, 'demo_mode' => FALSE, 'upload_allowed_file_types' => '', 'upload_max_filesize' => 0, 'upload_note' => '', 'upload_exists' => 'increment', 'popup_video' => TRUE, 'popup_transition' => 'glide', 'popup_transition_play' => 'inherit', 'popup_interval' => 5000, 'popup_caption' => TRUE, 'popup_caption_hide' => TRUE, 'popup_caption_style' => 'block', 'popup_caption_align' => 'center-left', 'video_autoplay' => TRUE, 'lang_default' => 'en', 'lang_auto' => TRUE, 'lang_menu' => FALSE, 'breadcrumbs' => TRUE, 'script' => 'manager3.php', 'menu_cache_hash' => '356888.295aa7.3352503940', 'menu_cache_file' => FALSE, 'query_path' => FALSE, 'query_path_valid' => FALSE, 'init_path' => '', 'dirs' => ['' => [...]], 'dirs_hash' => '170426', 'resize_image_types' => [0 => 'jpeg', 1 => 'jpg', 2 => 'png', 3 => 'gif', 4 => 'webp', 5 => 'bmp'], 'image_cache_hash' => 'f53b3c', 'location_hash' => 'efc1f9341a6f3e19acc38a2871eef237', 'has_login' => FALSE, 'version' => '0.3.1', 'index_html' => 0, 'server_exif' => TRUE, 'qrx' => FALSE, 'video_thumbs_enabled' => TRUE, 'lang_custom' => FALSE, 'x3_path' => FALSE]	896
2	155	1	0.010971	962240
2	155	R			'{\n    "load_images": true,\n    "load_files_proxy_php": false,\n    "load_images_max_filesize": 1000000,\n    "load_svg_max_filesize": 100000,\n    "image_resize_enabled": true,\n    "image_resize_dimensions": 320,\n    "image_resize_dimensions_retina": 480,\n    "image_resize_types": "jpeg, png, gif, webp, bmp",\n    "image_resize_memory_limit": 128,\n    "image_resize_max_pixels": 30000000,\n    "image_resize_min_ratio": 1.5,\n    "folder_preview_image": true,\n    "menu_enabled": true,\n    "menu_show":'
2	156	0	0.011011	958192	get_include	1		/var/www/html/uploads/manager3.php	1949	1	'js/custom.js'
2		A						/var/www/html/uploads/manager3.php	1278	$path = '/var/www/html/uploads/_files/js/custom.js'
3	157	0	0.011037	958272	file_exists	0		/var/www/html/uploads/manager3.php	1279	1	'/var/www/html/uploads/_files/js/custom.js'
3	157	1	0.011057	958312
3	157	R			FALSE
2	156	1	0.011072	958192
1	3	1	0.011083	958192
			0.011137	708192
TRACE END   [2023-02-12 23:33:16.579116]


Generated HTML code

<html class="menu-disabled sidebar-closed has-scrollbars"><head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="robots" content="noindex,nofollow">
    <title>html</title>
        <link href="https://cdn.jsdelivr.net/npm/files.photo.gallery@0.3.1/css/files.css" rel="stylesheet">
      <script src="https://www.paypal.com/tagmanager/pptm.js?id=localhost&amp;t=xo&amp;v=5.0.352&amp;source=payments_sdk&amp;client_id=ARE4H7QcoXmKKWTfKk-pXAnsIfW1Zox3buwKma_y-mn4RjQZjS8Ghp1-JUxcevnkl5KI0vL-UIJ-uEfU&amp;vault=false" id="xo-pptm" async=""></script></head>

  <body class="modal-open" data-current-path="/" style="opacity: 1; overflow: hidden; padding-right: 0px;">
    <main id="main">
            <nav id="topbar" class="topbar-sticky headroom headroom--top headroom--bottom">
        <div id="topbar-top">
          <div id="search-container"><input id="search" type="search" placeholder="filter" size="1" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" title="ctrl-F"><svg viewBox="0 0 24 24" class="svg-icon svg-search"><path class="svg-path-search" d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z"></path></svg></div>
          <div id="change-layout" class="dropdown mouse-hover"><button type="button" class="btn-icon btn-topbar"><svg viewBox="0 0 24 24" class="svg-icon svg-layout_rows"><path class="svg-path-layout_rows" d="M3,19H9V12H3V19M10,19H22V12H10V19M3,5V11H22V5H3Z"></path></svg></button><div class="dropdown-menu dropdown-menu-topbar dropdown-menu-center"><h6 class="dropdown-header" data-lang="layout">layout</h6><div><button class="dropdown-item" data-action="list"><svg viewBox="0 0 24 24" class="svg-icon svg-layout_list"><path class="svg-path-layout_list" d="M7,5H21V7H7V5M7,13V11H21V13H7M4,4.5A1.5,1.5 0 0,1 5.5,6A1.5,1.5 0 0,1 4,7.5A1.5,1.5 0 0,1 2.5,6A1.5,1.5 0 0,1 4,4.5M4,10.5A1.5,1.5 0 0,1 5.5,12A1.5,1.5 0 0,1 4,13.5A1.5,1.5 0 0,1 2.5,12A1.5,1.5 0 0,1 4,10.5M7,19V17H21V19H7M4,16.5A1.5,1.5 0 0,1 5.5,18A1.5,1.5 0 0,1 4,19.5A1.5,1.5 0 0,1 2.5,18A1.5,1.5 0 0,1 4,16.5Z"></path></svg><span class="dropdown-text" data-lang="list">list<span></span></span></button><button class="dropdown-item" data-action="imagelist"><svg viewBox="0 0 24 24" class="svg-icon svg-layout_imagelist"><path class="svg-path-layout_imagelist" d="M3,4H7V8H3V4M9,5V7H21V5H9M3,10H7V14H3V10M9,11V13H21V11H9M3,16H7V20H3V16M9,17V19H21V17H9"></path></svg><span class="dropdown-text" data-lang="imagelist">imagelist<span></span></span></button><button class="dropdown-item" data-action="blocks"><svg viewBox="0 0 24 24" class="svg-icon svg-layout_blocks"><path class="svg-path-layout_blocks" d="M2 14H8V20H2M16 8H10V10H16M2 10H8V4H2M10 4V6H22V4M10 20H16V18H10M10 16H22V14H10"></path></svg><span class="dropdown-text" data-lang="blocks">blocks<span></span></span></button><button class="dropdown-item" data-action="grid"><svg viewBox="0 0 24 24" class="svg-icon svg-layout_grid"><path class="svg-path-layout_grid" d="M3,9H7V5H3V9M3,14H7V10H3V14M8,14H12V10H8V14M13,14H17V10H13V14M8,9H12V5H8V9M13,5V9H17V5H13M18,14H22V10H18V14M3,19H7V15H3V19M8,19H12V15H8V19M13,19H17V15H13V19M18,19H22V15H18V19M18,5V9H22V5H18Z"></path></svg><span class="dropdown-text" data-lang="grid">grid<span></span></span></button><button class="dropdown-item active" data-action="rows"><svg viewBox="0 0 24 24" class="svg-icon svg-layout_rows"><path class="svg-path-layout_rows" d="M3,19H9V12H3V19M10,19H22V12H10V19M3,5V11H22V5H3Z"></path></svg><span class="dropdown-text" data-lang="rows">rows<span></span></span></button><button class="dropdown-item" data-action="columns"><svg viewBox="0 0 24 24" class="svg-icon svg-layout_columns"><path class="svg-path-layout_columns" d="M2,5V19H8V5H2M9,5V10H15V5H9M16,5V14H22V5H16M9,11V19H15V11H9M16,15V19H22V15H16Z"></path></svg><span class="dropdown-text" data-lang="columns">columns<span></span></span></button></div><div id="layout-options"><div id="layout-sizer"><label for="layout-sizer-range" class="form-label mb-0"><span data-lang="size">size</span><span data-lang="rows" class="layout-label-type">rows</span></label><input type="range" class="form-range" id="layout-sizer-range" value="200" min="80" max="220" list="layout-size-default" style="--range-default-pos:0.5;"><datalist id="layout-size-default"><option value="150"></option></datalist></div><div id="layout-spacer"><label for="layout-spacer-range" class="form-label mb-0"><span data-lang="space">space</span><span data-lang="rows" class="layout-label-type">rows</span></label><input type="range" class="form-range" id="layout-spacer-range" value="50" min="0" max="100" list="layout-space-default"><datalist id="layout-space-default"><option value="50"></option></datalist></div><div id="cover-toggle" style="display: none;"><div class="form-check"><input class="form-check-input" type="checkbox" id="covertoggle" checked=""><label class="form-check-label" for="covertoggle" data-lang="uniform">uniform</label></div></div><div id="imagelist-square" style="display: none;"><div class="form-check"><input class="form-check-input" type="checkbox" id="imagelistsquare" checked=""><label class="form-check-label" for="imagelistsquare" data-lang="uniform">uniform</label></div></div></div></div></div>
          <div id="change-sort" class="dropdown mouse-hover"><button type="button" class="btn-icon btn-topbar"><svg viewBox="0 0 24 24" class="svg-icon svg-sort_name_asc"><path class="svg-path-sort_name_asc" d="M9.25 5L12.5 1.75L15.75 5H9.25M8.89 14.3H6L5.28 17H2.91L6 7H9L12.13 17H9.67L8.89 14.3M6.33 12.68H8.56L7.93 10.56L7.67 9.59L7.42 8.63H7.39L7.17 9.6L6.93 10.58L6.33 12.68M13.05 17V15.74L17.8 8.97V8.91H13.5V7H20.73V8.34L16.09 15V15.08H20.8V17H13.05Z"></path></svg></button><div class="dropdown-menu dropdown-menu-topbar dropdown-menu-center"><h6 class="dropdown-header" data-lang="sort">sort</h6><button class="dropdown-item active sort-asc" data-action="name"><svg viewBox="0 0 24 24" class="svg-icon svg-menu_down"><path class="svg-path-menu_down" d="M7,13L12,18L17,13H7Z"></path><path class="svg-path-menu_up" d="M7,12L12,7L17,12H7Z"></path></svg><svg viewBox="0 0 24 24" class="svg-icon svg-sort_name_asc"><path class="svg-path-sort_name_asc" d="M9.25 5L12.5 1.75L15.75 5H9.25M8.89 14.3H6L5.28 17H2.91L6 7H9L12.13 17H9.67L8.89 14.3M6.33 12.68H8.56L7.93 10.56L7.67 9.59L7.42 8.63H7.39L7.17 9.6L6.93 10.58L6.33 12.68M13.05 17V15.74L17.8 8.97V8.91H13.5V7H20.73V8.34L16.09 15V15.08H20.8V17H13.05Z"></path><path class="svg-path-sort_name_desc" d="M15.75 19L12.5 22.25L9.25 19H15.75M8.89 14.3H6L5.28 17H2.91L6 7H9L12.13 17H9.67L8.89 14.3M6.33 12.68H8.56L7.93 10.56L7.67 9.59L7.42 8.63H7.39L7.17 9.6L6.93 10.58L6.33 12.68M13.05 17V15.74L17.8 8.97V8.91H13.5V7H20.73V8.34L16.09 15V15.08H20.8V17H13.05Z"></path></svg><span class="dropdown-text" data-lang="name">name</span></button><button class="dropdown-item" data-action="kind"><svg viewBox="0 0 24 24" class="svg-icon svg-menu_down"><path class="svg-path-menu_down" d="M7,13L12,18L17,13H7Z"></path><path class="svg-path-menu_up" d="M7,12L12,7L17,12H7Z"></path></svg><svg viewBox="0 0 24 24" class="svg-icon svg-sort_kind_asc"><path class="svg-path-sort_kind_asc" d="M3 11H15V13H3M3 18V16H21V18M3 6H9V8H3Z"></path><path class="svg-path-sort_kind_desc" d="M3,13H15V11H3M3,6V8H21V6M3,18H9V16H3V18Z"></path></svg><span class="dropdown-text" data-lang="kind">kind</span></button><button class="dropdown-item" data-action="size"><svg viewBox="0 0 24 24" class="svg-icon svg-menu_down"><path class="svg-path-menu_down" d="M7,13L12,18L17,13H7Z"></path><path class="svg-path-menu_up" d="M7,12L12,7L17,12H7Z"></path></svg><svg viewBox="0 0 24 24" class="svg-icon svg-sort_size_asc"><path class="svg-path-sort_size_asc" d="M10,13V11H18V13H10M10,19V17H14V19H10M10,7V5H22V7H10M6,17H8.5L5,20.5L1.5,17H4V7H1.5L5,3.5L8.5,7H6V17Z"></path><path class="svg-path-sort_size_desc" d="M10,13V11H18V13H10M10,19V17H14V19H10M10,7V5H22V7H10M6,17H8.5L5,20.5L1.5,17H4V7H1.5L5,3.5L8.5,7H6V17Z"></path></svg><span class="dropdown-text" data-lang="size">size</span></button><button class="dropdown-item" data-action="date"><svg viewBox="0 0 24 24" class="svg-icon svg-menu_down"><path class="svg-path-menu_down" d="M7,13L12,18L17,13H7Z"></path><path class="svg-path-menu_up" d="M7,12L12,7L17,12H7Z"></path></svg><svg viewBox="0 0 24 24" class="svg-icon svg-sort_date_asc"><path class="svg-path-sort_date_asc" d="M7.78 7C9.08 7.04 10 7.53 10.57 8.46C11.13 9.4 11.41 10.56 11.39 11.95C11.4 13.5 11.09 14.73 10.5 15.62C9.88 16.5 8.95 16.97 7.71 17C6.45 16.96 5.54 16.5 4.96 15.56C4.38 14.63 4.09 13.45 4.09 12S4.39 9.36 5 8.44C5.59 7.5 6.5 7.04 7.78 7M7.75 8.63C7.31 8.63 6.96 8.9 6.7 9.46C6.44 10 6.32 10.87 6.32 12C6.31 13.15 6.44 14 6.69 14.54C6.95 15.1 7.31 15.37 7.77 15.37C8.69 15.37 9.16 14.24 9.17 12C9.17 9.77 8.7 8.65 7.75 8.63M13.33 17V15.22L13.76 15.24L14.3 15.22L15.34 15.03C15.68 14.92 16 14.78 16.26 14.58C16.59 14.35 16.86 14.08 17.07 13.76C17.29 13.45 17.44 13.12 17.53 12.78L17.5 12.77C17.05 13.19 16.38 13.4 15.47 13.41C14.62 13.4 13.91 13.15 13.34 12.65S12.5 11.43 12.46 10.5C12.47 9.5 12.81 8.69 13.47 8.03C14.14 7.37 15 7.03 16.12 7C17.37 7.04 18.29 7.45 18.88 8.24C19.47 9 19.76 10 19.76 11.19C19.75 12.15 19.61 13 19.32 13.76C19.03 14.5 18.64 15.13 18.12 15.64C17.66 16.06 17.11 16.38 16.47 16.61C15.83 16.83 15.12 16.96 14.34 17H13.33M16.06 8.63C15.65 8.64 15.32 8.8 15.06 9.11C14.81 9.42 14.68 9.84 14.68 10.36C14.68 10.8 14.8 11.16 15.03 11.46C15.27 11.77 15.63 11.92 16.11 11.93C16.43 11.93 16.7 11.86 16.92 11.74C17.14 11.61 17.3 11.46 17.41 11.28C17.5 11.17 17.53 10.97 17.53 10.71C17.54 10.16 17.43 9.69 17.2 9.28C16.97 8.87 16.59 8.65 16.06 8.63M9.25 5L12.5 1.75L15.75 5H9.25"></path><path class="svg-path-sort_date_desc" d="M7.78 7C9.08 7.04 10 7.53 10.57 8.46C11.13 9.4 11.41 10.56 11.39 11.95C11.4 13.5 11.09 14.73 10.5 15.62C9.88 16.5 8.95 16.97 7.71 17C6.45 16.96 5.54 16.5 4.96 15.56C4.38 14.63 4.09 13.45 4.09 12S4.39 9.36 5 8.44C5.59 7.5 6.5 7.04 7.78 7M7.75 8.63C7.31 8.63 6.96 8.9 6.7 9.46C6.44 10 6.32 10.87 6.32 12C6.31 13.15 6.44 14 6.69 14.54C6.95 15.1 7.31 15.37 7.77 15.37C8.69 15.37 9.16 14.24 9.17 12C9.17 9.77 8.7 8.65 7.75 8.63M13.33 17V15.22L13.76 15.24L14.3 15.22L15.34 15.03C15.68 14.92 16 14.78 16.26 14.58C16.59 14.35 16.86 14.08 17.07 13.76C17.29 13.45 17.44 13.12 17.53 12.78L17.5 12.77C17.05 13.19 16.38 13.4 15.47 13.41C14.62 13.4 13.91 13.15 13.34 12.65S12.5 11.43 12.46 10.5C12.47 9.5 12.81 8.69 13.47 8.03C14.14 7.37 15 7.03 16.12 7C17.37 7.04 18.29 7.45 18.88 8.24C19.47 9 19.76 10 19.76 11.19C19.75 12.15 19.61 13 19.32 13.76C19.03 14.5 18.64 15.13 18.12 15.64C17.66 16.06 17.11 16.38 16.47 16.61C15.83 16.83 15.12 16.96 14.34 17H13.33M16.06 8.63C15.65 8.64 15.32 8.8 15.06 9.11C14.81 9.42 14.68 9.84 14.68 10.36C14.68 10.8 14.8 11.16 15.03 11.46C15.27 11.77 15.63 11.92 16.11 11.93C16.43 11.93 16.7 11.86 16.92 11.74C17.14 11.61 17.3 11.46 17.41 11.28C17.5 11.17 17.53 10.97 17.53 10.71C17.54 10.16 17.43 9.69 17.2 9.28C16.97 8.87 16.59 8.65 16.06 8.63M15.75 19L12.5 22.25L9.25 19H15.75Z"></path></svg><span class="dropdown-text" data-lang="date">date</span></button></div></div>
        <button class="btn-icon btn-topbar" id="topbar-fullscreen"><svg viewBox="0 0 24 24" class="svg-icon svg-expand"><path class="svg-path-expand" d="M10,21V19H6.41L10.91,14.5L9.5,13.09L5,17.59V14H3V21H10M14.5,10.91L19,6.41V10H21V3H14V5H17.59L13.09,9.5L14.5,10.91Z"></path><path class="svg-path-collapse" d="M19.5,3.09L15,7.59V4H13V11H20V9H16.41L20.91,4.5L19.5,3.09M4,13V15H7.59L3.09,19.5L4.5,20.91L9,16.41V20H11V13H4Z"></path></svg></button></div>
                <div id="topbar-info" class="info-hidden"></div>
        <div id="files-sortbar" class="sortbar-rows"><div class="sortbar-inner"><div class="sortbar-item sortbar-name sortbar-active sort-asc" data-action="name"><span data-lang="name" class="sortbar-item-text">name</span><svg viewBox="0 0 24 24" class="svg-icon svg-menu_down"><path class="svg-path-menu_down" d="M7,13L12,18L17,13H7Z"></path><path class="svg-path-menu_up" d="M7,12L12,7L17,12H7Z"></path></svg></div><div class="sortbar-item sortbar-kind" data-action="kind"><span data-lang="kind" class="sortbar-item-text">kind</span><svg viewBox="0 0 24 24" class="svg-icon svg-menu_down"><path class="svg-path-menu_down" d="M7,13L12,18L17,13H7Z"></path><path class="svg-path-menu_up" d="M7,12L12,7L17,12H7Z"></path></svg></div><div class="sortbar-item sortbar-size" data-action="size"><span data-lang="size" class="sortbar-item-text">size</span><svg viewBox="0 0 24 24" class="svg-icon svg-menu_down"><path class="svg-path-menu_down" d="M7,13L12,18L17,13H7Z"></path><path class="svg-path-menu_up" d="M7,12L12,7L17,12H7Z"></path></svg></div><div class="sortbar-item sortbar-date" data-action="date"><span data-lang="date" class="sortbar-item-text">date</span><svg viewBox="0 0 24 24" class="svg-icon svg-menu_down"><path class="svg-path-menu_down" d="M7,13L12,18L17,13H7Z"></path><path class="svg-path-menu_up" d="M7,12L12,7L17,12H7Z"></path></svg></div></div></div>
      </nav>
      <!-- files list container -->
      <div><div id="files" class="list files-rows" style="--imagelist-height:100px; --imagelist-min-height:auto;"><a href="./beneri.se_malware_analysis" target="_blank" class="files-a files-a-svg" data-name="beneri.se_malware_analysis" style="opacity: 1;"><svg viewBox="0 0 56 56" class="svg-file svg-none files-svg"><path class="svg-file-bg" d="M36.985,0H7.963C7.155,0,6.5,0.655,6.5,1.926V55c0,0.345,0.655,1,1.463,1h40.074 c0.808,0,1.463-0.655,1.463-1V12.978c0-0.696-0.093-0.92-0.257-1.085L37.607,0.257C37.442,0.093,37.218,0,36.985,0z"></path><polygon class="svg-file-flip" points="37.5,0.151 37.5,12 49.349,12"></polygon></svg><div class="files-data"><span class="name">beneri.se_malware_analysis</span><span class="icon"><svg viewBox="0 0 24 24" class="svg-icon svg-file_default"><path class="svg-path-file_default" d="M14,10H19.5L14,4.5V10M5,3H15L21,9V19A2,2 0 0,1 19,21H5C3.89,21 3,20.1 3,19V5C3,3.89 3.89,3 5,3M5,5V19H19V12H12V5H5Z"></path></svg></span><span class="size">0 B</span><span class="ext"><span class="ext-inner">se_malware_analysis</span></span><span class="date"><time datetime="2023-02-12T20:32:43-05:00" data-time="1676251963" data-format="ll" title="Sunday, February 12, 2023 8:32 PM ~ a few seconds ago" data-title-format="LLLL">Feb 12, 2023</time></span><span class="flex"></span></div><span class="context-button files-context" data-action="context"><svg viewBox="0 0 24 24" class="svg-icon svg-dots"><path class="svg-path-dots" d="M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"></path><path class="svg-path-minus" d="M19,13H5V11H19V13Z"></path></svg></span></a></div></div>
    </main>

    <!-- modal -->
    <div id="modal-bg"></div>
    <div class="modal" id="files_modal" tabindex="-1" role="dialog" data-action="close"><div class="modal-dialog" role="document">	  <div class="modal-content">	    <div class="modal-header">	      <h5 class="modal-title"></h5>	      <div class="modal-buttons">	      	<div class="modal-code-buttons" style="display: none"><button type="button" class="btn btn-1 is-icon" data-action="copy" data-tooltip="copy text" data-lang="copy text"><svg viewBox="0 0 24 24" class="svg-icon svg-clipboard"><path class="svg-path-clipboard" d="M19,3H14.82C14.4,1.84 13.3,1 12,1C10.7,1 9.6,1.84 9.18,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M12,3A1,1 0 0,1 13,4A1,1 0 0,1 12,5A1,1 0 0,1 11,4A1,1 0 0,1 12,3M7,7H17V5H19V19H5V5H7V7M7.5,13.5L9,12L11,14L15.5,9.5L17,11L11,17L7.5,13.5Z"></path></svg></button><button type="button" class="btn btn-1 is-icon" data-action="fullscreen"><svg viewBox="0 0 24 24" class="svg-icon svg-expand"><path class="svg-path-expand" d="M10,21V19H6.41L10.91,14.5L9.5,13.09L5,17.59V14H3V21H10M14.5,10.91L19,6.41V10H21V3H14V5H17.59L13.09,9.5L14.5,10.91Z"></path><path class="svg-path-collapse" d="M19.5,3.09L15,7.59V4H13V11H20V9H16.41L20.91,4.5L19.5,3.09M4,13V15H7.59L3.09,19.5L4.5,20.91L9,16.41V20H11V13H4Z"></path></svg></button></div><button class="btn btn-1 is-icon" data-action="close" data-lang="close" title="Close"><svg viewBox="0 0 24 24" class="svg-icon svg-close"><path class="svg-path-close" d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"></path></svg></button>	      </div>	    </div>	    <div class="modal-body"></div>	  </div>	</div></div>

    <!-- context menu -->
    <div id="contextmenu" class="dropdown-menu"></div>

    <!-- custom footer html -->
    
    <!-- Javascript -->
    <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.1.9/dist/sweetalert2.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
    <!--<script src="https://cdn.jsdelivr.net/npm/list.js@2.3.1/dist/list.min.js"></script>-->
    <script src="https://cdn.jsdelivr.net/npm/@exeba/list.js@2.3.1/dist/list.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/yall-js@3.2.0/dist/yall.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/filesize@8.0.6/lib/filesize.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/screenfull@5.1.0/dist/screenfull.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dayjs@1.10.7/dayjs.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dayjs@1.10.7/plugin/localizedFormat.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dayjs@1.10.7/plugin/relativeTime.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>
    <script>
var _c = {
    "load_images": true,
    "load_files_proxy_php": false,
    "load_images_max_filesize": 1000000,
    "load_svg_max_filesize": 100000,
    "image_resize_enabled": true,
    "image_resize_dimensions": 320,
    "image_resize_dimensions_retina": 480,
    "image_resize_types": "jpeg, png, gif, webp, bmp",
    "image_resize_memory_limit": 128,
    "image_resize_max_pixels": 30000000,
    "image_resize_min_ratio": 1.5,
    "folder_preview_image": true,
    "menu_enabled": false,
    "menu_show": true,
    "menu_max_depth": 5,
    "menu_cache_validate": true,
    "layout": "rows",
    "sort": "name_asc",
    "sort_dirs_first": true,
    "cache": true,
    "history": true,
    "transitions": true,
    "click": "popup",
    "click_window": "",
    "click_window_popup": true,
    "code_max_load": 100000,
    "topbar_sticky": "scroll",
    "check_updates": false,
    "context_menu": true,
    "prevent_right_click": false,
    "filter_live": true,
    "filter_props": "name, filetype, mime, features, title",
    "download_dir": "zip",
    "allow_upload": false,
    "allow_delete": false,
    "allow_rename": false,
    "allow_new_folder": false,
    "allow_new_file": false,
    "allow_duplicate": false,
    "allow_text_edit": false,
    "demo_mode": false,
    "upload_allowed_file_types": "",
    "upload_max_filesize": 0,
    "upload_note": "",
    "upload_exists": "increment",
    "popup_video": true,
    "popup_transition": "glide",
    "popup_transition_play": "inherit",
    "popup_interval": 5000,
    "popup_caption": true,
    "popup_caption_hide": true,
    "popup_caption_style": "block",
    "popup_caption_align": "center-left",
    "video_autoplay": true,
    "lang_default": "en",
    "lang_auto": true,
    "lang_menu": false,
    "breadcrumbs": false,
    "script": "manager3.php",
    "menu_cache_hash": false,
    "menu_cache_file": false,
    "query_path": false,
    "query_path_valid": false,
    "init_path": "",
    "dirs": {
        "": {
            "basename": "html",
            "fileperms": "0777",
            "filetype": "dir",
            "is_readable": true,
            "is_writeable": true,
            "is_link": false,
            "is_dir": true,
            "mime": "directory",
            "mtime": 1676251964,
            "path": "",
            "url_path": "."
        }
    },
    "dirs_hash": "0025e3",
    "resize_image_types": [
        "jpeg",
        "jpg",
        "png",
        "gif",
        "webp",
        "bmp"
    ],
    "image_cache_hash": "483187",
    "location_hash": "40d1b2d83998fabacb726e5bc3d22129",
    "has_login": false,
    "version": "0.3.1",
    "index_html": 0,
    "server_exif": true,
    "qrx": false,
    "video_thumbs_enabled": true,
    "lang_custom": false,
    "x3_path": false
};
var CodeMirror = {};
    </script>
    <script src="https://cdn.jsdelivr.net/npm/codemirror@5.63.3/mode/meta.js"></script>
    <!-- custom -->
        <!-- files -->
    <script src="https://cdn.jsdelivr.net/npm/files.photo.gallery@0.3.1/js/files.js"></script><script src="https://cdn.jsdelivr.net/npm/mousetrap@1.6.5/mousetrap.min.js"></script><script src="https://cdn.jsdelivr.net/npm/headroom.js@0.12.0/dist/headroom.min.js"></script>		<div class="pswp pswp--has_mouse" tabindex="-1" role="dialog" aria-hidden="true">	    	<div class="pswp__bg"></div>	    	<div class="pswp__scroll-wrap">	    		<div class="pswp__container server-exif">		        <div class="pswp__item"></div>		        <div class="pswp__item"></div>		        <div class="pswp__item"></div>	        </div>	        <div class="pswp__ui pswp__ui--hidden pswp__caption-align-center-left">	          <div class="pswp__top-bar">	            <div class="pswp__counter"></div>	            <div class="pswp__search"></div>	            <div class="pswp__topbar-spacer"></div>	            <svg viewBox="0 0 18 18" class="pswp__preloader svg-preloader"><circle cx="9" cy="9" r="8" pathLength="100" class="svg-preloader-circle"></circle></svg><span class="context-button pswp__button pswp__button--contextmenu" data-action="context"><svg viewBox="0 0 24 24" class="svg-icon svg-dots"><path class="svg-path-dots" d="M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"></path><path class="svg-path-minus" d="M19,13H5V11H19V13Z"></path></svg></span><button class="pswp__button pswp__button--zoom"><svg viewBox="0 0 24 24" class="svg-icon svg-zoom_in"><path class="svg-path-zoom_in" d="M15.5,14L20.5,19L19,20.5L14,15.5V14.71L13.73,14.43C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.43,13.73L14.71,14H15.5M9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14M12,10H10V12H9V10H7V9H9V7H10V9H12V10Z"></path><path class="svg-path-zoom_out" d="M15.5,14H14.71L14.43,13.73C15.41,12.59 16,11.11 16,9.5A6.5,6.5 0 0,0 9.5,3A6.5,6.5 0 0,0 3,9.5A6.5,6.5 0 0,0 9.5,16C11.11,16 12.59,15.41 13.73,14.43L14,14.71V15.5L19,20.5L20.5,19L15.5,14M9.5,14C7,14 5,12 5,9.5C5,7 7,5 9.5,5C12,5 14,7 14,9.5C14,12 12,14 9.5,14M7,9H12V10H7V9Z"></path></svg></button>	            <button class="pswp__button pswp__button--play"><svg viewBox="0 0 24 24" class="svg-icon svg-play"><path class="svg-path-play" d="M8,5.14V19.14L19,12.14L8,5.14Z"></path><path class="svg-path-pause" d="M14,19H18V5H14M6,19H10V5H6V19Z"></path></svg></button>	            <button class="pswp__button pswp__button--fs"><svg viewBox="0 0 24 24" class="svg-icon svg-expand"><path class="svg-path-expand" d="M10,21V19H6.41L10.91,14.5L9.5,13.09L5,17.59V14H3V21H10M14.5,10.91L19,6.41V10H21V3H14V5H17.59L13.09,9.5L14.5,10.91Z"></path><path class="svg-path-collapse" d="M19.5,3.09L15,7.59V4H13V11H20V9H16.41L20.91,4.5L19.5,3.09M4,13V15H7.59L3.09,19.5L4.5,20.91L9,16.41V20H11V13H4Z"></path></svg></button>	            <button class="pswp__button pswp__button--close"><svg viewBox="0 0 24 24" class="svg-icon svg-close"><path class="svg-path-close" d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"></path></svg></button>	          </div>	          <button class="pswp__button pswp__button--arrow--left"><svg viewBox="0 0 24 24" class="svg-icon svg-chevron_left"><path class="svg-path-chevron_left" d="M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z"></path></svg></button><button class="pswp__button pswp__button--arrow--right"><svg viewBox="0 0 24 24" class="svg-icon svg-chevron_right"><path class="svg-path-chevron_right" d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"></path></svg></button>	          <div class="pswp__timer"></div>	          <div class="pswp__caption pswp__caption-style-block pswp__caption-hide">	          	<button class="pswp__button pswp__button--lock-caption"><svg viewBox="0 0 24 24" class="svg-icon svg-lock_outline"><path class="svg-path-lock_outline" d="M12,17C10.89,17 10,16.1 10,15C10,13.89 10.89,13 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10C4,8.89 4.89,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z"></path><path class="svg-path-lock_open_outline" d="M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10A2,2 0 0,1 6,8H15V6A3,3 0 0,0 12,3A3,3 0 0,0 9,6H7A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,17A2,2 0 0,1 10,15A2,2 0 0,1 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17Z"></path></svg></button>	          	<div class="pswp__caption__center"></div>	          </div>	        </div>	    	</div>			</div>

  

<script src="https://www.paypal.com/sdk/js?client-id=ARE4H7QcoXmKKWTfKk-pXAnsIfW1Zox3buwKma_y-mn4RjQZjS8Ghp1-JUxcevnkl5KI0vL-UIJ-uEfU" data-uid-auto="uid_ldjccltlhrxlfzzisewywfrqccypzs"></script><div class="modal fade show" id="license_modal" tabindex="-1" aria-labelledby="license_modal-label" aria-modal="true" role="dialog" style="display: block;">		  <div class="modal-dialog">		    <div class="modal-content">		      <div class="modal-body"><div id="pay" class="mb-2"><div id="pay_info" class="mb-3 mt-1"><strong>Purchase a license [$39]</strong> to unlock features and support dev!		<div><div class="license-features"><div class="license-feature"><svg viewBox="0 0 24 24" class="svg-icon svg-check"><path class="svg-path-check" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg>Remove this popup</div><div class="license-feature"><svg viewBox="0 0 24 24" class="svg-icon svg-check"><path class="svg-path-check" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg>Upload</div><div class="license-feature"><svg viewBox="0 0 24 24" class="svg-icon svg-check"><path class="svg-path-check" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg>Download folder</div><div class="license-feature"><svg viewBox="0 0 24 24" class="svg-icon svg-check"><path class="svg-path-check" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg>Code and text editor</div><div class="license-feature"><svg viewBox="0 0 24 24" class="svg-icon svg-check"><path class="svg-path-check" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg>Create new file</div><div class="license-feature"><svg viewBox="0 0 24 24" class="svg-icon svg-check"><path class="svg-path-check" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg>Create new folder</div><div class="license-feature"><svg viewBox="0 0 24 24" class="svg-icon svg-check"><path class="svg-path-check" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg>Rename</div><div class="license-feature"><svg viewBox="0 0 24 24" class="svg-icon svg-check"><path class="svg-path-check" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg>Delete</div><div class="license-feature"><svg viewBox="0 0 24 24" class="svg-icon svg-check"><path class="svg-path-check" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg>Duplicate file</div><div class="license-feature"><svg viewBox="0 0 24 24" class="svg-icon svg-check"><path class="svg-path-check" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg>Dedicated support</div><div class="license-feature"><svg viewBox="0 0 24 24" class="svg-icon svg-check"><path class="svg-path-check" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg>Multi-user, panorama and much more coming soon!</div></div></div><small>* After purchase, you will receive <strong>license key</strong> by email.</small>		</div><div id="pay_pp"><div id="zoid-paypal-buttons-uid_6707980b5f_mde6mzi6ndc" class="paypal-buttons paypal-buttons-context-iframe paypal-buttons-label-unknown paypal-buttons-layout-vertical" data-paypal-smart-button-version="5.0.352" style="height: 142px; transition: all 0.2s ease-in-out 0s;"><style nonce="">
                    #zoid-paypal-buttons-uid_6707980b5f_mde6mzi6ndc {
                        position: relative;
                        display: inline-block;
                        width: 100%;
                        min-height: 35px;
                        min-width: 200px;
                        max-width: 750px;
                        font-size: 0;
                    }

                    #zoid-paypal-buttons-uid_6707980b5f_mde6mzi6ndc > iframe {
                        position: absolute;
                        top: 0;
                        left: 0;
                        width: 100%;
                        height: 100%;
                    }

                    #zoid-paypal-buttons-uid_6707980b5f_mde6mzi6ndc > iframe.component-frame {
                        z-index: 100;
                    }

                    #zoid-paypal-buttons-uid_6707980b5f_mde6mzi6ndc > iframe.prerender-frame {
                        transition: opacity .2s linear;
                        z-index: 200;
                    }

                    #zoid-paypal-buttons-uid_6707980b5f_mde6mzi6ndc > iframe.visible {
                        opacity: 1;
                    }

                    #zoid-paypal-buttons-uid_6707980b5f_mde6mzi6ndc > iframe.invisible {
                        opacity: 0;
                        pointer-events: none;
                    }

                    #zoid-paypal-buttons-uid_6707980b5f_mde6mzi6ndc > .smart-menu {
                        position: absolute;
                        z-index: 300;
                        top: 0;
                        left: 0;
                        width: 100%;
                    }
                </style><iframe allowtransparency="true" name="__zoid__paypal_buttons__eyJzZW5kZXIiOnsiZG9tYWluIjoiaHR0cDovL2xvY2FsaG9zdCJ9LCJtZXRhRGF0YSI6eyJ3aW5kb3dSZWYiOnsidHlwZSI6InBhcmVudCIsImRpc3RhbmNlIjowfX0sInJlZmVyZW5jZSI6eyJ0eXBlIjoicmF3IiwidmFsIjoie1widWlkXCI6XCJ6b2lkLXBheXBhbC1idXR0b25zLXVpZF82NzA3OTgwYjVmX21kZTZtemk2bmRjXCIsXCJjb250ZXh0XCI6XCJpZnJhbWVcIixcInRhZ1wiOlwicGF5cGFsLWJ1dHRvbnNcIixcImNoaWxkRG9tYWluTWF0Y2hcIjp7XCJfX3R5cGVfX1wiOlwicmVnZXhcIixcIl9fdmFsX19cIjpcIlxcXFwucGF5cGFsXFxcXC4oY29tfGNuKSg6XFxcXGQrKT8kXCJ9LFwidmVyc2lvblwiOlwiMTBfMV8wXCIsXCJwcm9wc1wiOntcImNyZWF0ZU9yZGVyXCI6e1wiX190eXBlX19cIjpcImNyb3NzX2RvbWFpbl9mdW5jdGlvblwiLFwiX192YWxfX1wiOntcImlkXCI6XCJ1aWRfZDZkZTY5MDM0Yl9tZGU2bXppNm5kY1wiLFwibmFtZVwiOlwiY3JlYXRlT3JkZXJcIn19LFwib25BcHByb3ZlXCI6e1wiX190eXBlX19cIjpcImNyb3NzX2RvbWFpbl9mdW5jdGlvblwiLFwiX192YWxfX1wiOntcImlkXCI6XCJ1aWRfZDE1MGQzMmYxZl9tZGU2bXppNm5kY1wiLFwibmFtZVwiOlwib25BcHByb3ZlXCJ9fSxcImNzcE5vbmNlXCI6e1wiX190eXBlX19cIjpcInVuZGVmaW5lZFwifSxcImZ1bmRpbmdTb3VyY2VcIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwic3R5bGVcIjp7XCJjdXN0b21cIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwibGFiZWxcIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwibGF5b3V0XCI6XCJ2ZXJ0aWNhbFwiLFwiY29sb3JcIjpcImdvbGRcIixcInNoYXBlXCI6XCJyZWN0XCIsXCJ0YWdsaW5lXCI6ZmFsc2UsXCJoZWlnaHRcIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwicGVyaW9kXCI6e1wiX190eXBlX19cIjpcInVuZGVmaW5lZFwifSxcIm1lbnVQbGFjZW1lbnRcIjpcImJlbG93XCJ9LFwic3RvcmFnZVN0YXRlXCI6e1wiZ2V0XCI6e1wiX190eXBlX19cIjpcImNyb3NzX2RvbWFpbl9mdW5jdGlvblwiLFwiX192YWxfX1wiOntcImlkXCI6XCJ1aWRfNGFhMjdhNzQ3OV9tZGU2bXppNm5kY1wiLFwibmFtZVwiOlwiZ2V0XCJ9fSxcInNldFwiOntcIl9fdHlwZV9fXCI6XCJjcm9zc19kb21haW5fZnVuY3Rpb25cIixcIl9fdmFsX19cIjp7XCJpZFwiOlwidWlkX2MxZDJjOWJlN2ZfbWRlNm16aTZuZGNcIixcIm5hbWVcIjpcInNldFwifX19LFwic2Vzc2lvblN0YXRlXCI6e1wiZ2V0XCI6e1wiX190eXBlX19cIjpcImNyb3NzX2RvbWFpbl9mdW5jdGlvblwiLFwiX192YWxfX1wiOntcImlkXCI6XCJ1aWRfODI5NzU3ZGI0OF9tZGU2bXppNm5kY1wiLFwibmFtZVwiOlwiZ2V0XCJ9fSxcInNldFwiOntcIl9fdHlwZV9fXCI6XCJjcm9zc19kb21haW5fZnVuY3Rpb25cIixcIl9fdmFsX19cIjp7XCJpZFwiOlwidWlkXzJhYTllZDBjYmJfbWRlNm16aTZuZGNcIixcIm5hbWVcIjpcInNldFwifX19LFwiY29tcG9uZW50c1wiOltcImJ1dHRvbnNcIl0sXCJsb2NhbGVcIjp7XCJjb3VudHJ5XCI6XCJVU1wiLFwibGFuZ1wiOlwiZW5cIn0sXCJjcmVhdGVCaWxsaW5nQWdyZWVtZW50XCI6e1wiX190eXBlX19cIjpcInVuZGVmaW5lZFwifSxcImNyZWF0ZVN1YnNjcmlwdGlvblwiOntcIl9fdHlwZV9fXCI6XCJ1bmRlZmluZWRcIn0sXCJvbkNvbXBsZXRlXCI6e1wiX190eXBlX19cIjpcInVuZGVmaW5lZFwifSxcIm9uU2hpcHBpbmdDaGFuZ2VcIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwib25TaGlwcGluZ0FkZHJlc3NDaGFuZ2VcIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwib25TaGlwcGluZ09wdGlvbnNDaGFuZ2VcIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwib25DYW5jZWxcIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwib25DbGlja1wiOntcIl9fdHlwZV9fXCI6XCJ1bmRlZmluZWRcIn0sXCJnZXRQcmVyZW5kZXJEZXRhaWxzXCI6e1wiX190eXBlX19cIjpcImNyb3NzX2RvbWFpbl9mdW5jdGlvblwiLFwiX192YWxfX1wiOntcImlkXCI6XCJ1aWRfY2E4YmFiNGY5Nl9tZGU2bXppNm5kY1wiLFwibmFtZVwiOlwiZ2V0UHJlcmVuZGVyRGV0YWlsc1wifX0sXCJnZXRQb3B1cEJyaWRnZVwiOntcIl9fdHlwZV9fXCI6XCJjcm9zc19kb21haW5fZnVuY3Rpb25cIixcIl9fdmFsX19cIjp7XCJpZFwiOlwidWlkXzY2ZmFjMDRkYTZfbWRlNm16aTZuZGNcIixcIm5hbWVcIjpcImdldFBvcHVwQnJpZGdlXCJ9fSxcIm9uSW5pdFwiOntcIl9fdHlwZV9fXCI6XCJjcm9zc19kb21haW5fZnVuY3Rpb25cIixcIl9fdmFsX19cIjp7XCJpZFwiOlwidWlkX2FlODdhZWFjMmFfbWRlNm16aTZuZGNcIixcIm5hbWVcIjpcIm9uSW5pdFwifX0sXCJnZXRRdWVyaWVkRWxpZ2libGVGdW5kaW5nXCI6e1wiX190eXBlX19cIjpcImNyb3NzX2RvbWFpbl9mdW5jdGlvblwiLFwiX192YWxfX1wiOntcImlkXCI6XCJ1aWRfMjYwZTgzOWFjOF9tZGU2bXppNm5kY1wiLFwibmFtZVwiOlwiZ2V0UXVlcmllZEVsaWdpYmxlRnVuZGluZ1wifX0sXCJjbGllbnRJRFwiOlwiQVJFNEg3UWNvWG1LS1dUZktrLXBYQW5zSWZXMVpveDNidXdLbWFfeS1tbjRSalFaalM4R2hwMS1KVXhjZXZua2w1S0kwdkwtVUlKLXVFZlVcIixcImNsaWVudEFjY2Vzc1Rva2VuXCI6e1wiX190eXBlX19cIjpcInVuZGVmaW5lZFwifSxcInBhcnRuZXJBdHRyaWJ1dGlvbklEXCI6e1wiX190eXBlX19cIjpcInVuZGVmaW5lZFwifSxcIm1lcmNoYW50UmVxdWVzdGVkUG9wdXBzRGlzYWJsZWRcIjpmYWxzZSxcImVuYWJsZVRocmVlRG9tYWluU2VjdXJlXCI6ZmFsc2UsXCJzZGtDb3JyZWxhdGlvbklEXCI6XCJmMTAwNDY0ZmZlNDM5XCIsXCJzdG9yYWdlSURcIjpcInVpZF82ODBkOTg5MWJkX21kZTZtemk2bmRjXCIsXCJzZXNzaW9uSURcIjpcInVpZF9mNTVlMzliYzc3X21kZTZtemk2bmRjXCIsXCJidXR0b25Mb2NhdGlvblwiOlwibG9jYWxob3N0XCIsXCJidXR0b25TZXNzaW9uSURcIjpcInVpZF8xNjBhOWRhNTE2X21kZTZtemk2bmRjXCIsXCJlbmFibGVWYXVsdFwiOntcIl9fdHlwZV9fXCI6XCJ1bmRlZmluZWRcIn0sXCJlbnZcIjpcInByb2R1Y3Rpb25cIixcImFtb3VudFwiOntcIl9fdHlwZV9fXCI6XCJ1bmRlZmluZWRcIn0sXCJzdGFnZUhvc3RcIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwiYnV0dG9uU2l6ZVwiOntcIl9fdHlwZV9fXCI6XCJ1bmRlZmluZWRcIn0sXCJhcGlTdGFnZUhvc3RcIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwiZnVuZGluZ0VsaWdpYmlsaXR5XCI6e1wicGF5cGFsXCI6e1wiZWxpZ2libGVcIjp0cnVlLFwidmF1bHRhYmxlXCI6dHJ1ZX0sXCJwYXlsYXRlclwiOntcImVsaWdpYmxlXCI6ZmFsc2UsXCJwcm9kdWN0c1wiOntcInBheUluM1wiOntcImVsaWdpYmxlXCI6ZmFsc2UsXCJ2YXJpYW50XCI6bnVsbH0sXCJwYXlJbjRcIjp7XCJlbGlnaWJsZVwiOmZhbHNlLFwidmFyaWFudFwiOm51bGx9LFwicGF5bGF0ZXJcIjp7XCJlbGlnaWJsZVwiOmZhbHNlLFwidmFyaWFudFwiOm51bGx9fX0sXCJjYXJkXCI6e1wiZWxpZ2libGVcIjp0cnVlLFwiYnJhbmRlZFwiOnRydWUsXCJpbnN0YWxsbWVudHNcIjpmYWxzZSxcInZlbmRvcnNcIjp7XCJ2aXNhXCI6e1wiZWxpZ2libGVcIjp0cnVlLFwidmF1bHRhYmxlXCI6dHJ1ZX0sXCJtYXN0ZXJjYXJkXCI6e1wiZWxpZ2libGVcIjp0cnVlLFwidmF1bHRhYmxlXCI6dHJ1ZX0sXCJhbWV4XCI6e1wiZWxpZ2libGVcIjp0cnVlLFwidmF1bHRhYmxlXCI6dHJ1ZX0sXCJkaXNjb3ZlclwiOntcImVsaWdpYmxlXCI6ZmFsc2UsXCJ2YXVsdGFibGVcIjp0cnVlfSxcImhpcGVyXCI6e1wiZWxpZ2libGVcIjpmYWxzZSxcInZhdWx0YWJsZVwiOmZhbHNlfSxcImVsb1wiOntcImVsaWdpYmxlXCI6ZmFsc2UsXCJ2YXVsdGFibGVcIjp0cnVlfSxcImpjYlwiOntcImVsaWdpYmxlXCI6ZmFsc2UsXCJ2YXVsdGFibGVcIjp0cnVlfX0sXCJndWVzdEVuYWJsZWRcIjpmYWxzZX0sXCJ2ZW5tb1wiOntcImVsaWdpYmxlXCI6ZmFsc2V9LFwiaXRhdVwiOntcImVsaWdpYmxlXCI6ZmFsc2V9LFwiY3JlZGl0XCI6e1wiZWxpZ2libGVcIjpmYWxzZX0sXCJhcHBsZXBheVwiOntcImVsaWdpYmxlXCI6ZmFsc2V9LFwic2VwYVwiOntcImVsaWdpYmxlXCI6ZmFsc2V9LFwiaWRlYWxcIjp7XCJlbGlnaWJsZVwiOmZhbHNlfSxcImJhbmNvbnRhY3RcIjp7XCJlbGlnaWJsZVwiOmZhbHNlfSxcImdpcm9wYXlcIjp7XCJlbGlnaWJsZVwiOmZhbHNlfSxcImVwc1wiOntcImVsaWdpYmxlXCI6ZmFsc2V9LFwic29mb3J0XCI6e1wiZWxpZ2libGVcIjpmYWxzZX0sXCJteWJhbmtcIjp7XCJlbGlnaWJsZVwiOmZhbHNlfSxcInAyNFwiOntcImVsaWdpYmxlXCI6ZmFsc2V9LFwiemltcGxlclwiOntcImVsaWdpYmxlXCI6ZmFsc2V9LFwid2VjaGF0cGF5XCI6e1wiZWxpZ2libGVcIjpmYWxzZX0sXCJwYXl1XCI6e1wiZWxpZ2libGVcIjpmYWxzZX0sXCJibGlrXCI6e1wiZWxpZ2libGVcIjpmYWxzZX0sXCJ0cnVzdGx5XCI6e1wiZWxpZ2libGVcIjpmYWxzZX0sXCJveHhvXCI6e1wiZWxpZ2libGVcIjpmYWxzZX0sXCJtYXhpbWFcIjp7XCJlbGlnaWJsZVwiOmZhbHNlfSxcImJvbGV0b1wiOntcImVsaWdpYmxlXCI6ZmFsc2V9LFwiYm9sZXRvYmFuY2FyaW9cIjp7XCJlbGlnaWJsZVwiOmZhbHNlfSxcIm1lcmNhZG9wYWdvXCI6e1wiZWxpZ2libGVcIjpmYWxzZX0sXCJtdWx0aWJhbmNvXCI6e1wiZWxpZ2libGVcIjpmYWxzZX0sXCJzYXRpc3BheVwiOntcImVsaWdpYmxlXCI6ZmFsc2V9fSxcInBsYXRmb3JtXCI6XCJkZXNrdG9wXCIsXCJyZW1lbWJlcmVkXCI6W10sXCJleHBlcmltZW50XCI6e1wiZW5hYmxlVmVubW9cIjpmYWxzZSxcImVuYWJsZVZlbm1vQXBwTGFiZWxcIjpmYWxzZX0sXCJwYXltZW50UmVxdWVzdFwiOntcIl9fdHlwZV9fXCI6XCJ1bmRlZmluZWRcIn0sXCJmbG93XCI6XCJwdXJjaGFzZVwiLFwicmVtZW1iZXJcIjp7XCJfX3R5cGVfX1wiOlwiY3Jvc3NfZG9tYWluX2Z1bmN0aW9uXCIsXCJfX3ZhbF9fXCI6e1wiaWRcIjpcInVpZF82ZjNmOGRkYTc3X21kZTZtemk2bmRjXCIsXCJuYW1lXCI6XCJyZW1lbWJlclwifX0sXCJjdXJyZW5jeVwiOlwiVVNEXCIsXCJpbnRlbnRcIjpcImNhcHR1cmVcIixcImJ1eWVyQ291bnRyeVwiOntcIl9fdHlwZV9fXCI6XCJ1bmRlZmluZWRcIn0sXCJjb21taXRcIjp0cnVlLFwidmF1bHRcIjpmYWxzZSxcImVuYWJsZUZ1bmRpbmdcIjpbXSxcImRpc2FibGVGdW5kaW5nXCI6W10sXCJkaXNhYmxlQ2FyZFwiOltdLFwibWVyY2hhbnRJRFwiOltdLFwicmVuZGVyZWRCdXR0b25zXCI6W1wicGF5cGFsXCIsXCJjYXJkXCJdLFwiY3NwXCI6e1wibm9uY2VcIjpcIlwifSxcIm5vbmNlXCI6XCJcIixcImdldFBhZ2VVcmxcIjp7XCJfX3R5cGVfX1wiOlwiY3Jvc3NfZG9tYWluX2Z1bmN0aW9uXCIsXCJfX3ZhbF9fXCI6e1wiaWRcIjpcInVpZF8zMjZlMjg0YzEzX21kZTZtemk2bmRjXCIsXCJuYW1lXCI6XCJnZXRQYWdlVXJsXCJ9fSxcInVzZXJJRFRva2VuXCI6e1wiX190eXBlX19cIjpcInVuZGVmaW5lZFwifSxcImNsaWVudE1ldGFkYXRhSURcIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwiZGVidWdcIjpmYWxzZSxcInRlc3RcIjp7XCJhY3Rpb25cIjpcImNoZWNrb3V0XCJ9LFwid2FsbGV0XCI6e1wiX190eXBlX19cIjpcInVuZGVmaW5lZFwifSxcInBheW1lbnRNZXRob2ROb25jZVwiOntcIl9fdHlwZV9fXCI6XCJ1bmRlZmluZWRcIn0sXCJwYXltZW50TWV0aG9kVG9rZW5cIjp7XCJfX3R5cGVfX1wiOlwidW5kZWZpbmVkXCJ9LFwiYnJhbmRlZFwiOntcIl9fdHlwZV9fXCI6XCJ1bmRlZmluZWRcIn0sXCJhcHBsZVBheVN1cHBvcnRcIjpmYWxzZSxcInN1cHBvcnRzUG9wdXBzXCI6dHJ1ZSxcInN1cHBvcnRlZE5hdGl2ZUJyb3dzZXJcIjpmYWxzZSxcInVzZXJFeHBlcmllbmNlRmxvd1wiOntcIl9fdHlwZV9fXCI6XCJ1bmRlZmluZWRcIn0sXCJhcHBsZVBheVwiOntcIl9fdHlwZV9fXCI6XCJ1bmRlZmluZWRcIn0sXCJleHBlcmllbmNlXCI6XCJcIixcImFsbG93QmlsbGluZ1BheW1lbnRzXCI6dHJ1ZX0sXCJleHBvcnRzXCI6e1wiaW5pdFwiOntcIl9fdHlwZV9fXCI6XCJjcm9zc19kb21haW5fZnVuY3Rpb25cIixcIl9fdmFsX19cIjp7XCJpZFwiOlwidWlkXzUxODUxNDFjMTRfbWRlNm16aTZuZGNcIixcIm5hbWVcIjpcImluaXRcIn19LFwiY2xvc2VcIjp7XCJfX3R5cGVfX1wiOlwiY3Jvc3NfZG9tYWluX2Z1bmN0aW9uXCIsXCJfX3ZhbF9fXCI6e1wiaWRcIjpcInVpZF8xZGJhZWYzYTg2X21kZTZtemk2bmRjXCIsXCJuYW1lXCI6XCJjbG9zZTo6bWVtb2l6ZWRcIn19LFwiY2hlY2tDbG9zZVwiOntcIl9fdHlwZV9fXCI6XCJjcm9zc19kb21haW5fZnVuY3Rpb25cIixcIl9fdmFsX19cIjp7XCJpZFwiOlwidWlkXzU3MTdhNWY5OThfbWRlNm16aTZuZGNcIixcIm5hbWVcIjpcImNoZWNrQ2xvc2VcIn19LFwicmVzaXplXCI6e1wiX190eXBlX19cIjpcImNyb3NzX2RvbWFpbl9mdW5jdGlvblwiLFwiX192YWxfX1wiOntcImlkXCI6XCJ1aWRfYzBhNWIxNjY0N19tZGU2bXppNm5kY1wiLFwibmFtZVwiOlwiTmVcIn19LFwib25FcnJvclwiOntcIl9fdHlwZV9fXCI6XCJjcm9zc19kb21haW5fZnVuY3Rpb25cIixcIl9fdmFsX19cIjp7XCJpZFwiOlwidWlkXzVjOWVhOGI5MTFfbWRlNm16aTZuZGNcIixcIm5hbWVcIjpcIkJlXCJ9fSxcInNob3dcIjp7XCJfX3R5cGVfX1wiOlwiY3Jvc3NfZG9tYWluX2Z1bmN0aW9uXCIsXCJfX3ZhbF9fXCI6e1wiaWRcIjpcInVpZF81ZGMxOWYyZmRhX21kZTZtemk2bmRjXCIsXCJuYW1lXCI6XCJoZVwifX0sXCJoaWRlXCI6e1wiX190eXBlX19cIjpcImNyb3NzX2RvbWFpbl9mdW5jdGlvblwiLFwiX192YWxfX1wiOntcImlkXCI6XCJ1aWRfMzliODI5YWZhYV9tZGU2bXppNm5kY1wiLFwibmFtZVwiOlwiZ2VcIn19LFwiZXhwb3J0XCI6e1wiX190eXBlX19cIjpcImNyb3NzX2RvbWFpbl9mdW5jdGlvblwiLFwiX192YWxfX1wiOntcImlkXCI6XCJ1aWRfY2FjOTc0ZWRhNF9tZGU2bXppNm5kY1wiLFwibmFtZVwiOlwiVWVcIn19fX0ifX0__" title="PayPal" allowpaymentrequest="allowpaymentrequest" scrolling="no" id="jsx-iframe-293b5f0222" class="component-frame visible" style="background-color: transparent; border: none;"></iframe><div id="smart-menu" class="smart-menu"></div><div id="installments-modal" class="installments-modal"></div><iframe name="__detect_close_uid_f29fa2f92b_mde6mzi6ndc__" style="display: none;"></iframe></div></div></div>		<form id="license_form" novalidate="">			<input id="license_key" class="form-control" type="text" placeholder="LICENSE-KEY" required="">			<div class="float-right mt-3">				<button id="continue_free" type="button" class="btn btn-light" data-bs-dismiss="modal">No thanks!</button>				<button id="license_save_button" type="submit" class="btn btn-success text-light" style="display:none;">Save</button>			</div>		</form></div>		      		    </div>		  </div>		</div><div class="modal-backdrop fade show"></div></body></html>

Original PHP code

<?php

// errors
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// config
class config {

  // DEFAULT CONFIG
  // Only edit directly if it is a temporary installation. Settings added here will be lost when updating!
  // Instead, add options from external config file in your storage_path [_files/config/config.php]
  // READ MORE: https://forum.photo.gallery/viewtopic.php?f=66&t=9964
  public static $default = array(

    // paths
    'root' => '', // root path relative to script / empty is same as files app location
    'start_path' => false, // start path relative to script. If empty, root is start path

    // login
    'username' => '',
    'password' => '', // Add password directly or use https://files.photo.gallery/tools/hash/ to encrypt the password (encrypted password is more secure, as it prevents your password from being exposed directly in a file).

    // images
    'load_images' => true,
    'load_files_proxy_php' => false,
    'load_images_max_filesize' => 1000000, // maximum file size (bytes) for un-resized images loaded into list
    'load_svg_max_filesize' => 100000, // 100k
    'image_resize_enabled' => true, // resize images for the interface
    'image_resize_cache' => true, // todo: remove this option and just use 'cache?
    'image_resize_dimensions' => 320,
    'image_resize_dimensions_retina' => 480,
    'image_resize_dimensions_allowed' => '', // comma-separated list of allowed resize dimensions
    'image_resize_types' => 'jpeg, png, gif, webp, bmp', // image types to resize / jpeg, png, gif, webp, bmp
    'image_resize_quality' => 85,
    'image_resize_function' => 'imagecopyresampled', // imagecopyresampled / imagecopyresized
    'image_resize_sharpen' => true,
    'image_resize_memory_limit' => 128, // 128 MB is suffient to resize images around 6000 px / 0 = ignore memory
    'image_resize_max_pixels' => 30000000, // 30 MP equivalent to an image 6000 x 5000 / 0 = no limit
    'image_resize_min_ratio' => 1.5, // min size diff original vs resize. Only resizes if ratio > min ratio
    'image_resize_cache_direct' => false, // if enabled and delete cache, must increase cache_key
    'folder_preview_image' => true, // enable folder preview images / might be slow as it requires searching each dir for images
    'folder_preview_default' => '_filespreview.jpg', // use this image as folder preview if exists in dir

    // menu
    'menu_enabled' => true,
    'menu_show' => true,
    'menu_max_depth' => 5,
    'menu_sort' => 'name_asc', // name_asc, name_desc, date_asc, date_desc
    'menu_cache_validate' => true,
    'menu_load_all' => false,
    'menu_recursive_symlinks' => true, // List sub-directories of symlinks in the main menu. May cause loops and duplicates

    // files layout
    'layout' => 'rows', // list, imagelist, blocks, grid, rows, columns
    'sort' => 'name_asc', // name, date, filesize, kind / asc, desc
    'sort_dirs_first' => true, // sort dirs on top

    // cache
    'cache' => true,
    'cache_key' => 0,
    'storage_path' => '_files',

    // exclude files directories regex
    'files_exclude' => '', // '/\.(pdf|jpe?g)$/i'
    'dirs_exclude' => '', //'/\/Convert|\/football|\/node_modules(\/|$)/i',
    'allow_symlinks' => true, // allow symlinks

    // various
    'history' => true,
    'breadcrumbs' => true,
    'transitions' => true,
    'click' => 'popup', // popup, modal, download, window, menu / default item click 
    'click_window' => '', // 'pdf, html, php, zip' / list of file extensions to open directly on click
    'click_window_popup' => true, // Popup instead of new tab. Useful for viewing PDF, HTML and text type documents / desktop only
    'code_max_load' => 100000, // max filesize of text files to load and preview
    'topbar_sticky' => 'scroll', // true, false, 'scroll'
    'check_updates' => false, // show notification in topbar with option to update when new version is available
    'allow_tasks' => false,
    'get_mime_type' => false, // get file mime type from server (slow) instead of from extension (fast)
    'context_menu' => true, // disable context-menu button and right-click menu
    'prevent_right_click' => false, // blocks browser right-click menu on sensitive items (images, list items, menu)
    'license_key' => '',
    'filter_live' => true, // live search filtering on keyboard input / does not apply for mobile devices
    'filter_props' => 'name, filetype, mime, features, title', // file properties to filter / name, filetype, mime, features, title, headline, description, creator, credit, copyright, keywords, city, sub-location, province-state'
    'download_dir' => 'zip', // download all files in folder / 'zip' / 'files' / '' false (disabled)
    'download_dir_cache' => 'dir', // enable caching of created zip dirs / 'dir' / 'storage' / '' false (disabled)
    
    // filemanager options
    'allow_upload' => false, // allow uploader
    'allow_delete' => false, // allow deleting files and folders
    'allow_rename' => false, // allow renaming files and folders
    'allow_new_folder' => false, // allow make new directory
    'allow_new_file' => false, // allow make new empty file
    'allow_duplicate' => false, // allow duplicate files
    'allow_text_edit' => false, // allow editing text-based files in modal
    'demo_mode' => false, // block all filemanager operations but allow them to show in interface / used in Files app demo

    // uploader options
    'upload_allowed_file_types' => '', // comma-separated list of allowed upload file types / empty = allow any / 'jpeg, jpg, image/*'
    'upload_max_filesize' => 0, // [bytes] / 0 = unlimited (but limited by server PHP upload_max_filesize)
    'upload_note' => '', // include a small text note at bottom of uploader / 'Max file size %upload_max_filesize%'
    'upload_exists' => 'increment', // 'increment' / 'overwrite' / 'fail'

    // popup options
    'popup_video' => true, // opens videos in the popup (instead of modal)
    'popup_transition' => 'glide', // none, slide, glide, fade, zoom, pop, elastic
    'popup_transition_play' => 'inherit', // transition in play mode
    'popup_interval' => 5000, // interval ms between slides in play mode
    'popup_caption' => true, // enable popup caption
    'popup_caption_hide' => true, // autohide popup caption after a few seconds without user input
    'popup_caption_style' => 'block', // block, box, gradient, topbar, none
    'popup_caption_align' => 'center-left', // left, center-left, center, right

    // video
    'video_thumbs' => true, // allow video thumbnails / requires FFmpeg and PHP exec() function enabled.
    'video_ffmpeg_path' => 'ffmpeg', // path to ffmpeg command, normally 'ffmpeg' http://ffmpeg.org/
    'video_autoplay' => true, // video autoplay on click

    // language
    'lang_default' => 'en', // default language if browser lang is not supported/detected or lang_auto is disabled
    'lang_auto' => true, // automatically load language based on detected browser language
    'lang_menu' => false, // display dropdown menu to select language
  );

  // config (will popuplate)
  public static $config = array();

  // app vars
  static $__dir__ = __DIR__;
  static $__file__ = __FILE__;
  static $assets;
  static $prod = true;
  static $version = '0.3.1';
  static $root;
  static $doc_root;
  static $has_login = false;
  static $storage_path;
  static $storage_is_within_doc_root = false;
  static $storage_config_realpath;
  static $storage_config;
  static $cache_path;
  static $image_resize_cache_direct;
  static $image_resize_dimensions_retina = false;
  static $dirs_hash = false;
  static $local_config_file = '_filesconfig.php';
  static $username = false;
  static $password = false;
  static $x3_path = false;

  // get config
  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; 
    }, $config);
  }

  // files check system and config [diagnostics]
  private function files_check($local_config, $storage_path, $storage_config, $user_config, $user_valid){

    // BASIC DIAGNOSTICS
    echo '<!doctype html><html><head><title>Files App check system and config.</title><meta name="robots" content="noindex,nofollow"><style>body{font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; color: #444;line-height:1.6;margin:0 auto;max-width:700px}.container{background-color:#F3F3F3;padding:.5vw 2vw 2vw;border-radius:3px;margin:1vw;overflow:scroll}.test:before{display:inline-block;width:18px;text-align:center;margin-right:5px}.neutral:before{color:#BBB}.success:before{color:#78a642}.success:before,.neutral:before{content:"\2713"}.fail:before{content:"\2716";color:firebrick}</style></head><body><div class="container"><h2>Files App ' . config::$version . '</h2><div style="margin:-1rem 0 .5rem">' . (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] . '<br>' : '') . 'PHP ' . phpversion() . '<br>' . (isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '') . '<p><i>* The following tests are only to help diagnose feature-specific issues.</i></p></div>';
    // output helper
    function prop($name, $success = 'neutral', $val = false){
      return '<div class="test ' . (is_bool($success) ? ($success ? 'success' : 'fail') : $success) . '">'. $name . ($val ? ': <b>' . $val . '</b>' : '') . '</div>';
    }
    // filesystem
    echo prop('storage_path exists', file_exists(config::$config['storage_path']));
    if(file_exists(config::$config['storage_path'])) echo prop('storage_path is_writeable', is_writable(config::$config['storage_path']));
    echo prop('root is_writeable', is_writable(config::$config['root']));
    // extension_loaded
    if(function_exists('extension_loaded')) foreach (['gd', 'exif'] as $name) echo prop($name, extension_loaded($name));
    // zip
    echo prop('ZipArchive', class_exists('ZipArchive'));
    // function_exsists
    foreach (['mime_content_type', 'finfo_file', 'iptcparse', 'exif_imagetype', 'session_start', 'ini_get', 'exec'] as $name) echo prop($name . '()', function_exists($name));
    // ffmpeg
    if(function_exists('exec')) echo prop('ffmpeg', !!exec('type -P ' . config::$config['video_ffmpeg_path']));
    // ini_get
    if(function_exists('ini_get')) foreach (['memory_limit', 'file_uploads', 'upload_max_filesize', 'post_max_size', 'max_file_uploads'] as $name) echo prop($name, 'neutral', @ini_get($name));

    // CONFIG OUTPUT
    echo '</div><div class="container"><h3>Config</h3>';
    // invalid and duplicate arrays
    $user_invalid = array_diff_key($user_config, self::$default);
    $user_duplicate = array_intersect_assoc($user_valid, self::$default);

    // items
    $items = array(
      ['arr' => $local_config, 'comment' => "// LOCAL CONFIG\n// " . self::$local_config_file],
      ['arr' => $storage_config, 'comment' => "// STORAGE CONFIG\n// " . rtrim($storage_path ?: '', '\/') . '/config/config.php'],
      ['arr' => $user_invalid, 'comment' => "// INVALID PARAMS\n// The following custom parameters will be ignored as they are not valid:", 'var' => '$invalid', 'hide' => empty($user_invalid)],
      ['arr' => $user_duplicate, 'comment' => "// DUPLICATE DEFAULT PARAMS\n// The following custom parameters will have no effect as they are identical to defaults:", 'var' => '$duplicate', 'hide' => empty($user_duplicate)],
      ['arr' => $user_valid, 'comment' => "// USER CONFIG\n// User config parameters.", 'var' => '$user', 'hide' => (empty($local_config) || empty($storage_config)) && empty($user_invalid)],
      ['arr' => self::$config, 'comment' => "// CONFIG\n// User parameters merged with default parameters.", 'var' => '$config'],
      ['arr' => self::$default, 'comment' => "// DEFAULT CONFIG\n// Default config parameters.", 'var' => '$default'],
      //['arr' => array_diff_key(get_class_vars('config'), array_flip(['default', 'config'])), 'comment' => "// STATIC VARS\n// Static app vars.", 'var' => '$static']
    );

    // loop
    $output = '<?php' . PHP_EOL;
    foreach ($items as $arr => $props) {
      $is_empty = empty($props['arr']);
      if(isset($props['hide']) && $props['hide']) continue;
      foreach (['username', 'password', 'license_key', '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 ';
      $output .= PHP_EOL . $comment . PHP_EOL . $var . $export . ';' . PHP_EOL;
    }
    highlight_string($output . PHP_EOL . ';?>');
    echo '</div></body></html>';
    exit;
  }

  // save config
  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);
    return @file_put_contents(config::$storage_config_realpath, '<?php ' . PHP_EOL . PHP_EOL . '// CONFIG / https://forum.photo.gallery/viewtopic.php?f=66&t=9964' . PHP_EOL . '// Uncomment the parameters you want to edit.' . PHP_EOL . 'return ' . $export . ';');
  }

  // construct
  function __construct($is_doc = false) {

    // normalize OS paths
    self::$__dir__ = real_path(__DIR__);
    self::$__file__ = real_path(__FILE__);

    // local config
    $local_config = self::get_config(self::$local_config_file);

    // storage config
    $storage_path = isset($local_config['storage_path']) ? $local_config['storage_path'] : self::$default['storage_path'];
    $storage_realpath = !empty($storage_path) ? real_path($storage_path) : false;
    if($is_doc && $storage_realpath === self::$__dir__) error('<strong>storage_path must be a unique dir.</strong>');
    self::$storage_config_realpath = $storage_realpath ? $storage_realpath . '/config/config.php' : false;
    self::$storage_config = self::get_config(self::$storage_config_realpath);

    // config
    $user_config = array_replace(self::$storage_config, $local_config);
    $user_valid = array_intersect_key($user_config, self::$default);
    self::$config = array_replace(self::$default, $user_valid);

    // files check with ?check=true
    if(get('check')) self::files_check($local_config, $storage_path, self::$storage_config, $user_config, $user_valid);
    // if(get('phpinfo')) { phpinfo(); exit; } // check system phpinfo with ?phpinfo=true / disabled for security / un-comment if you want to use

    // 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.');

    // doc root
    self::$doc_root = real_path($_SERVER['DOCUMENT_ROOT']);

    // login credentials
    self::$username = self::$config['username'];
    self::$password = self::$config['password'];

    // X3 compatibility / x3 login / images from X3 resize cache / invalidate X3 cache on filemanager op / X3 license
    $x3_path = dirname(self::$root);
    self::$x3_path = file_exists($x3_path . '/app/x3.inc.php') ? $x3_path : false;
    if(self::$x3_path && self::$username === 'x3'){
      $x3_config = file_exists($x3_path . '/config/config.user.json') ? json_decode(file_get_contents($x3_path . '/config/config.user.json'), true) : false;
      self::$username = isset($x3_config['back']['panel']['username']) ? $x3_config['back']['panel']['username'] : 'admin';
      self::$password = isset($x3_config['back']['panel']['password']) ? $x3_config['back']['panel']['password'] : 'admin';
      if(self::$username === 'admin' && self::$password === 'admin' && isset($x3_config['back']['panel']['use_db'])) error('Sorry, Files app is not compatible with X3 panel database login. Assign "username" and "password" separately in _files/config/config.php.', 403);
    }

    // has_login
    self::$has_login = self::$username || self::$password ? true : false;

    // $image_cache
    $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']){

      // create storage_path
      if(empty($storage_realpath)){
        $storage_path = is_string($storage_path) ? rtrim($storage_path, '\/') : false;
        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 <strong>$storage_path</strong> does not exist and can't be created.");
        self::$storage_config_realpath = $storage_realpath . '/config/config.php'; // update since it wasn't assigned
      }
      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;

      // cache_path real path
      self::$cache_path = self::$storage_path . '/cache';

      // create storage dirs
      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);
      }

      // 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();  

      // 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;
    }

    // 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
    if(self::$has_login) check_login($is_doc);
  }
};

// login page
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">
    <meta name="robots" content="noindex,nofollow">
    <title>Login</title>
    <link href="<?php echo config::$assets ?>css/files.css" rel="stylesheet">
    <?php get_include('css/custom.css'); ?>
  </head>
  <body><div id="files-login-container"></div></body>
  <script>
    document.getElementById('files-login-container').innerHTML = '\
    <h1 class="header mb-5">Login</h1>\
    <?php if($is_login_attempt && $_POST['sidx'] !== $sidx) { ?><div class="alert alert-danger" role="alert"><strong>PHP session ID mismatch</strong><br>If the error persists, your PHP is incorrectly creating new session ID for each request.</div><?php } else if($is_login_attempt) { ?>\
    <div class="alert alert-danger" role="alert">Incorrect login!</div><?php } else if($is_logout) { ?>\
    <div class="alert alert-warning" role="alert">You are now logged out.</div><?php } ?>\
    <form>\
      <div class="mylogin">\
        <input type="text" name="username" placeholder="Username">\
        <input type="password" name="password" placeholder="Password">\
      </div>\
        <input type="text" name="fusername" class="form-control form-control-lg mb-3" placeholder="Username" required autofocus spellcheck="false" autocorrect="off" autocapitalize="off">\
        <input type="password" name="fpassword" class="form-control form-control-lg mb-3" placeholder="Password" required spellcheck="false">\
      <input type="hidden" name="client_hash" value="<?php echo $client_hash; ?>">\
      <input type="hidden" name="sidx" value="<?php echo $sidx; ?>">\
      <input type="submit" value="Login" class="btn btn-lg btn-files-light btn-login">\
    </form>';
    document.getElementsByTagName('form')[0].addEventListener('submit', function(){
      this.action = '<?php echo isset($_GET['logout']) ? strtok($_SERVER['REQUEST_URI'], '?') : $_SERVER['REQUEST_URI']; ?>';
      this.method = 'post';
    }, false);
  </script>
</html>
<?php exit; // end form and exit
}

// check login
function check_login($is_doc){
  if($is_doc && empty(config::$username)) error('Username cannot be empty.');
  if($is_doc && empty(config::$password)) error('Password cannot be empty.');
  if(!session_start() && !$is_dor) error('Failed to initiate PHP session_start();', 500);
  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']);
    }
    error('Invalid IP', 401);
  }

  // hash
  $client_hash = get_client_hash();
  $login_hash = md5(config::$username . config::$password . $client_hash);

  // login status
  $is_logout = isset($_GET['logout']) && isset($_SESSION['login']);
  if($is_logout) unset($_SESSION['login']);
  $is_logged_in = !$is_logout && isset($_SESSION['login']) && $_SESSION['login'] === $login_hash;

  // not logged in
  if(!$is_logged_in){

    // login only on html pages
    if($is_doc){

      // vars
      $sidx = md5(session_id());
      $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 &&
        trim($_POST['fusername']) == config::$username && 
        (phpversion() >= 5.5 && !password_needs_rehash(config::$password, PASSWORD_DEFAULT) ? password_verify(trim($_POST['fpassword']), config::$password) : (trim($_POST['fpassword']) == config::$password)) && 
        $_POST['client_hash'] === $client_hash && 
        $_POST['sidx'] === $sidx
      ){
        $_SESSION['login'] = $login_hash;

      // 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')){
      json_error('login');

    } else {
      error('You are not logged in.', 401);
    }
  }
}

//
function mkdir_or_error($path){
  if(!file_exists($path) && !mkdir($path, 0777, true)) error('Failed to create ' . $path, 500);
}
function real_path($path){
  $real_path = realpath($path);
  return $real_path ? str_replace('\\', '/', $real_path) : false;
}
function root_relative($dir){
  return ltrim(substr($dir, strlen(config::$root)), '\/');
}
function root_absolute($dir){
  return config::$root . ($dir ? '/' . $dir : '');
}
function is_within_path($path, $root){
  return strpos($path . '/', $root . '/') === 0;
}
function is_within_root($path){
  return is_within_path($path, config::$root);
}
function is_within_docroot($path){
  return is_within_path($path, config::$doc_root);
}
function get_folders_cache_path($name){
  return config::$cache_path . '/folders/' . $name . '.json';
}
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;
  return get_folders_cache_path(get_dir_cache_hash($dir, $mtime));
}
function get_dir_cache_hash($dir, $mtime = false){
  return config::$dirs_hash . '.' . substr(md5($dir), 0, 6) . '.' . ($mtime ?: filemtime($dir));
  //
}
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 = false, $msg = false, $props = false, $cache_headers = false, $clone = false){
  if(!$path || !file_exists($path)) return false;
  $cloned = $clone && @copy($path, $clone) ? true : false;
  //if($mime == 'image/svg') $mime .= '+xml';
  header('content-type: ' . ($mime ?: 'image/jpeg'));
	header('content-length: ' . filesize($path));
  header('content-disposition: filename="' . basename($path) . '"');
  if($msg) header('files-msg: ' . $msg . ($cloned ? ' [cloned to ' . basename($clone) . ']' : '') . ' [' . ($props ? $props . ', ' : '') . header_memory_time() . ']');
  if($cache_headers) set_cache_headers();
  if(!is_readable($path) || readfile($path) === false) error('Failed to read file ' . $path . '.', 400);
  exit;
}

// get mime
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;
  }
}

// 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");
  header('pragma: cache');
  // header("Last-Modified:" . gmdate('D, d M Y H:i:s', time() - $seconds) . ' GMT');
  // etag?
}

// get image cache path
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 exclude
function is_exclude($path = false, $is_dir = true, $symlinked = false){

  // early exit
  if(!$path || $path === config::$root) return;

  // exclude all paths that start with /_files* (reserved for any files and folders to be ignored and hidden from Files app)
  if(strpos($path, '/_files') !== false) return true;

  // exclude files PHP application
  if($path === config::$__file__) return true;

  // symlinks not allowed
  if($symlinked && !config::$config['allow_symlinks']) return true; 

  // exclude storage path
  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']) {
    $dirname = $is_dir ? $path : dirname($path);
    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){
    $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;
  }
}

// valid root path
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 /

  // 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;

  // 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;
  }

  // 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

  // return root_absolute
  return $root_absolute;
}

// image create from
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);
  } else if ($type === IMAGETYPE_BMP) {
    if(version_compare(PHP_VERSION, '7.2.0') >= 0) return imagecreatefrombmp($path);
  }
}

// get file (proxy or resize image)
function get_file($path, $resize = false){

  // validate
  if(!$path) error('Invalid file request.', 404);
  $path = real_path($path); // in case of symlink path
  $mime = get_mime($path); // may return false if server does not support mime_content_type() or finfo_file()

  // video thumbnail (FFmpeg)
  if($resize == 'video') {

    // requirements with diagnostics / only check $mime if $mime detected
    if($mime && strtok($mime, '/') !== 'video') error('<strong>' . basename($path) . '</strong> (' . $mime . ') is not a video.', 415);
    foreach (array('video_thumbs', 'load_images', 'image_resize_cache', 'video_ffmpeg_path') as $key) if(empty(config::$config[$key])) error($key . ' option disabled.', 400);
    if(!function_exists('exec')) error('PHP <a href="https://www.php.net/manual/en/function.exec.php" target="_blank">exec()</a> function is disabled on this server.', 400);
    if(empty(exec('type -P ' . config::$config['video_ffmpeg_path']))) error('Can\'t find <a href="http://ffmpeg.org/" target="_blank">FFmpeg</a> in location "' . config::$config['video_ffmpeg_path'] . '".', 400);

    // get cache path
    $cache = get_image_cache_path($path, 480, filesize($path), filemtime($path));

    // check for cached video thumbnail / $path, $mime, $msg, $props, $cache_headers
    if($cache) read_file($cache, null, 'Video thumb served from cache', null, true);

    // ffmpeg command
    $cmd = escapeshellarg(config::$config['video_ffmpeg_path']) . ' -i ' . escapeshellarg($path) . ' -deinterlace -an -ss 1 -t 1 -vf "thumbnail,scale=480:320:force_original_aspect_ratio=increase,crop=480:320" -r 1 -y -f mjpeg ' . $cache . ' 2>&1';

    // try to execute command
    exec($cmd, $output, $result_code);

    // fail if result_code is anything else than 0
    if($result_code) error("Error generating thumbnail for video (\$result_code $result_code)", 400);

    // output created video thumbnail
    read_file($cache, null, 'Video thumb created', null, true);

  // resize image
  } else if($resize){
    if($mime && strtok($mime, '/') !== 'image') error('<strong>' . basename($path) . '</strong> (' . $mime . ') is not an image.', 415);
    foreach (['load_images', 'image_resize_enabled'] as $key) if(!config::$config[$key]) error('[' .$key . '] disabled.', 400);
    $resize_dimensions = intval($resize);
    if(!$resize_dimensions) error("Invalid resize parameter <strong>$resize</strong>.", 400);
    $allowed = config::$config['image_resize_dimensions_allowed'] ?: [];
    if(!in_array($resize_dimensions, array_merge([config::$config['image_resize_dimensions'], config::$config['image_resize_dimensions_retina']], array_map('intval', is_array($allowed) ? $allowed : explode(',', $allowed))))) error("Resize parameter <strong>$resize_dimensions</strong> is not allowed.", 400);
    resize_image($path, $resize_dimensions);

  // 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);

    // read file / $mime or 'application/octet-stream'
    read_file($path, ($mime ?: 'application/octet-stream'), $msg = 'File ' . basename($path) . ' proxied.', false, true);
  }
}

// sharpen resized 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; 
  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;
  $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);
  return true;
}
  
// resize image
function resize_image($path, $resize_dimensions, $clone = false){

  // file size
  $file_size = filesize($path);

  // header props
  $header_props = 'w:' . $resize_dimensions . ', q:' . config::$config['image_resize_quality'] . ', ' . config::$config['image_resize_function'] . ', cache:' . (config::$config['image_resize_cache'] ? '1' : '0');

  // 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, $clone);

  // imagesize
  $info = getimagesize($path);
  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 <strong>' . $info[0] . ' x ' . $info[1] . '</strong> (' . ($info[0] * $info[1]) . ' px) exceeds <strong>image_resize_max_pixels</strong> (' . config::$config['image_resize_max_pixels'] . ' px).', 400);

  // header props
  $header_props .= ', ' . $info['mime'] . ', ' . $info[0] . 'x' . $info[1] . ', ratio:' . round($resize_ratio, 2);

  // check if image type is in image_resize_types / jpeg, png, gif, webp, bmp
  $is_resize_type = in_array(image_type_to_extension($info[2], false), array_map(function($key){
    $type = trim(strtolower($key));
    return $type === 'jpg' ? 'jpeg' : $type;
  }, explode(',', config::$config['image_resize_types'])));

  // serve original if !$is_resize_type || resize ratio < image_resize_min_ratio
  if((!$is_resize_type || $resize_ratio < max(config::$config['image_resize_min_ratio'], 1)) && !read_file($path, $info['mime'], 'Original image served', $header_props, true, $clone)) error('File does not exist.', 404);

  // Calculate new image dimensions.
  $resize_width  = round($info[0] / $resize_ratio);
  $resize_height = round($info[1] / $resize_ratio);

  // 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){
    // $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 <strong>' . $memory_required . 'M</strong>. Your current PHP memory_limit is <strong>' . $new_memory_limit .'M</strong>.', 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
  $header_props .= ', ' . $resize_width . 'x' . $resize_height;

  // create new $image
  $image = image_create_from($path, $info[2]);
  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);

  // 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'];

  // sharpen resized 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('<strong>imagejpeg()</strong> failed to create and cache resized image.', 500);

    // clone cache (used for folder previews)
    if($clone) @copy($cache, $clone);

  // cache disabled / 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('<strong>imagejpeg()</strong> 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, $clone)) error('Cache file does not exist.', 404);

  //
  exit;
  // https://github.com/maxim/smart_resize_image/blob/master/smart_resize_image.function.php
  // https://github.com/gavmck/resize/blob/master/php/lib/resize-class.php
  // https://github.com/gumlet/php-image-resize/blob/master/lib/ImageResize.php
  // 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;

  // if in __dir__ path, __dir__ relative
  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){

  // realpath
  $realpath = $path ? real_path($path) : false;
  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

  // vars
  $filemtime = filemtime($realpath);
  $url_path = get_url_path($realpath) ?: ($symlinked ? get_url_path($path) : false);
  $is_readable = is_readable($realpath);

  // array
  $arr = array(
    'basename' => basename($realpath) ?: basename($path) ?: '',
    'fileperms' => substr(sprintf('%o', fileperms($realpath)), -4),
    'filetype' => 'dir',
    'is_readable' => $is_readable,
    'is_writeable' => is_writeable($realpath),
    'is_link' => $symlinked ? is_link($path) : false,
    'is_dir' => true,
    'mime' => 'directory',
    'mtime' => $filemtime,
    'path' => root_relative($path)
  );

  // url path
  if($url_path) $arr['url_path'] = $url_path;

  // get_files() || config::menu_load_all
  if($files && $is_readable) {

    // files array
    $arr['files'] = get_files_data($path, $url_path, $arr['dirsize'], $arr['files_count'], $arr['images_count'], $arr['preview']);

    // download_dir cache direct access to zip / better caching and no need to access PHP / only works when download_dir_cache === 'dir'
    /*if($url_path && config::$config['download_dir'] === 'zip' && config::$config['download_dir_cache'] === 'dir') {
      $zip = $realpath . '/_files.zip';
      if(file_exists($zip) && filemtime($zip) >= $filemtime) $arr['zip'] = get_url_path($zip);
    }*/
  }

	// 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;
  }

  //
	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); 
    });
  } else {
    natcasesort($dirs);
  }
  return substr(config::$config['menu_sort'], -4) === 'desc' ? array_reverse($dirs) : $dirs;
}

// recursive directory scan
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']) {
    $data = get_dir($path, config::$config['menu_load_all'], !config::$config['menu_load_all']);
    if(!$data) return $arr;

    //
    $arr[] = $data;

    // max depth
    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;
  }

  // get dirs from files array if $data['files'] or glob subdirs
  $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);

  // sort and loop subdirs
  if(!empty($subdirs)) foreach(get_menu_sort($subdirs) as $subdir) get_dirs($subdir, $arr, $depth + 1);

  // return
  return $arr;
}

// encode to UTF-8 when required
function safe_iptc_tag($val){
  $val = @substr($val, 0, 1000);
  return @mb_detect_encoding($val, 'UTF-8', true) ? $val : @utf8_encode($val);
}

// get IPTC
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();

  // loop title, headline, description, creator, credit, copyright, keywords, city, sub-location and province-state
  foreach (['title'=>'005', 'headline'=>'105', 'description'=>'120', 'creator'=>'080', 'credit'=>'110', 'copyright'=>'116', 'keywords'=>'025', 'city'=>'090', 'sub-location'=>'092', 'province-state'=>'095'] as $name => $code) {
    if(isset($app13['2#' . $code][0]) && !empty($app13['2#' . $code][0])) $iptc[$name] = $name === 'keywords' ? $app13['2#' . $code] : safe_iptc_tag($app13['2#' . $code][0]);
  }

  // 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']);

	/*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'];

	// 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;

	// return
	return $exif;
}

// exif GPS / get_image_location
function get_image_location($exif) {
  $arr = array();
  foreach (array('GPSLatitude', 'GPSLongitude') as $key) {
    if(!isset($exif[$key]) || !isset($exif[$key.'Ref'])) return false;
    $coordinate = $exif[$key];
    if(is_string($coordinate)) $coordinate = array_map('trim', explode(',', $coordinate));
    for ($i = 0; $i < 3; $i++) {
      $part = explode('/', $coordinate[$i]);
      if (count($part) == 1) {
        $coordinate[$i] = $part[0];
      } else if (count($part) == 2) {
        if($part[1] == 0) return false; // can't be 0 / invalid GPS
        $coordinate[$i] = floatval($part[0])/floatval($part[1]);
      } else {
        $coordinate[$i] = 0;
      }
    }
    list($degrees, $minutes, $seconds) = $coordinate;
    $sign = ($exif[$key.'Ref'] == 'W' || $exif[$key.'Ref'] == 'S') ? -1 : 1;
    $arr[] = $sign * ($degrees + $minutes/60 + $seconds/3600);
  }
  return $arr;
}

/*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]];
  $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)));

  return array($latitude, $longitude);
}

function gps2Num($coordPart){
  $parts = explode('/', $coordPart);
  if(count($parts) <= 0) return 0;
  if(count($parts) == 1) return $parts[0];
  if($parts[1] == 0) return 0;
  return floatval($parts[0]) / floatval($parts[1]);
}*/

// 
function get_files_data($dir, $url_path = false, &$dirsize = 0, &$files_count = 0, &$images_count = 0, &$preview = false){

  // scandir
  $filenames = scandir($dir, SCANDIR_SORT_NONE);
  if(empty($filenames)) return array();
  $items = array();

  // look for folder_preview_default (might be excluded in loop)
  if(config::$config['folder_preview_default'] && in_array(config::$config['folder_preview_default'], $filenames)) $preview = config::$config['folder_preview_default'];

  // loop filenames
  foreach($filenames as $filename) {

    //
    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
    $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

    // vars
    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;

    // 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);

    // root path // path relative to config::$root
    if(!$symlinked || is_within_root($realpath)){
      $root_path = root_relative($realpath);

    // 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) {
        $basename_path = basename($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;
            $loop_abs_path = root_absolute($loop_path);
            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;
          }
        }
      }
    }

    // add properties
    $item = array(
      'basename' => $basename,
      'fileperms' => substr(sprintf('%o', fileperms($realpath)), -4),
      'filetype' => $filetype,
      'filesize' => $filesize,
      'is_readable' => $is_readable,
      'is_writeable' => is_writeable($realpath),
      'is_link' => $is_link,
      'is_dir' => $is_dir,
      'mtime' => $filemtime,
      'path' => $root_path
    );

    // optional props
    //$ext = !$is_dir ? pathinfo($realpath, PATHINFO_EXTENSION) : false;
    $ext = !$is_dir ? substr(strrchr($realpath, '.'), 1) : false;
    if($ext) {
      $ext = strtolower($ext);
      $item['ext'] = $ext;
    }
    $mime = $is_dir ? 'directory' : ($is_readable && (!$ext || $ext === 'ts' || config::$config['get_mime_type']) ? get_mime($realpath) : false);
    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){

      // imagesize
      $imagesize = $is_readable ? @getimagesize($realpath, $info) : false;

      // image count and icon
      $images_count ++;
      $item['icon'] = 'image';

      // is imagesize
      if(!empty($imagesize) && is_array($imagesize)){

        // set folder_preview
        if(!$preview && in_array($ext, array('gif','jpg','jpeg','png'))) $preview = $basename;

        // 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];

        // mime from image
        if(!$mime && isset($image['mime'])) $item['mime'] = $image['mime'];

        // IPTC
        $iptc = $info ? get_iptc($info) : false;
        if(!empty($iptc)) $image['iptc'] = $iptc;

        // EXIF
        $exif = get_exif($realpath);
        if(!empty($exif)) {
          $image['exif'] = $exif;
          if(isset($exif['DateTimeOriginal'])) $item['DateTimeOriginal'] = $exif['DateTimeOriginal'];
          // invert width/height if exif orientation
          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){
          $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);
          $retina = config::$image_resize_dimensions_retina;
          if($retina){
            $resize2 = get_image_cache_path($realpath, $retina, $filesize, $filemtime);
            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){
        $mime = get_mime($realpath);
        if($mime) {
          $item['mime'] = $mime;
          if(strtok($mime, '/') !== 'image'){ // unset images_count and icon because is not image after all
            $images_count --;
            unset($item['icon']);
          }
        }
      }
    }

    // add to items with basename as key
    $items[$basename] = $item;
	}

  // Sort dirs on top and natural case sort / need to do in JS anyway
  uasort($items, function($a, $b){
    if(!config::$config['sort_dirs_first'] || $a['is_dir'] === $b['is_dir']) return strnatcasecmp($a['basename'], $b['basename']);
    return $b['is_dir'] ? 1 : -1;
  });

	//
	return $items;
}

// get files
function get_files($dir){

  // invalid $dir
  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')) {
    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 get($param){
	return isset($_GET[$param]) && !empty($_GET[$param]) ? $_GET[$param] : false;
}
function json_cache($arr = array(), $msg = false, $cache = true){
  $json = empty($arr) ? '{}' : json_encode($arr, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_PARTIAL_OUTPUT_ON_ERROR);
  if(empty($json)) json_error(json_last_error() ? json_last_error_msg() : 'json_encode() error');
	if($cache) @file_put_contents($cache, $json);
	if($msg) header('files-msg: ' . $msg . ' [' . header_memory_time() . ']');
  header('content-type: application/json');
	echo $json;
}
function json_error($error = 'Error'){
  json_exit(array('error' => $error));
}
function json_success($success = 'Success'){
  json_exit(array('success' => $success));
}
function json_toggle($success, $error){
  json_exit(array_filter(array('success' => $success, 'error' => empty($success) ? $error : 0)));
}
function json_exit($arr = array()){
  header('content-type: application/json');
  exit(json_encode($arr));
}
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');
  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('<h2>Error</h2>' . $msg);
}

// get valid menu cache
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;
  $arr = @json_decode($json, true);
  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
  }
  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){
    return !is_exclude($dir, true, is_link($dir));
  });
}

// get menu cache hash
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(){

  // 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 || 
      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
  }
  $cache = config::$config['cache'] ? config::$cache_path . '/menu/' . $menu_cache_hash . '.json' : false; // get cache path
  $json = $cache ? get_valid_menu_cache($cache) : false; // get valid json menu cache

  // $json is valid from menu cache file
  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
  } else {
    json_cache(get_dirs(config::$root), 'dirs reloaded' . ($cache ? ' and cached.' : ' [cache disabled]'), $cache);
  }
}

// include file html, php, css, js
function get_include($file){
  if(!config::$storage_path) return;
  $path = config::$storage_path . '/' . $file;
  if(!file_exists($path)) return;
  $ext = pathinfo($path, PATHINFO_EXTENSION);
  if(in_array($ext, ['html', 'php'])) return include $path;
  if(!config::$storage_is_within_doc_root) return;
  $src = get_url_path($path) . '?' . filemtime($path);
  if($ext === 'js') echo '<script src="' . $src . '"></script>';
  if($ext === 'css') echo '<link href="' . $src . '" rel="stylesheet">';
}

// POST
if(post('action')){

	// post action
	$action = post('action');

  //
  new config();

  // filemanager actions [beta]
  if($action === 'fm') {

    // validate task
    $task = post('task');
    if(empty($task) || !isset(config::$config['allow_' . $task]) || !config::$config['allow_' . $task]) json_error('invalid task');
    // demo_mode
    if(config::$config['demo_mode']) json_error('Action not allowed in demo mode');
    // license required for file manager action
    if(!config::$config['license_key']) json_error('License required!');

    // valid path / path must be inside assigned root
    $is_dir = post('is_dir');
    $path = valid_root_path(post('path'), $is_dir);
    if(empty($path)) json_error('invalid path ' . post('path'));
    $path = real_path($path); // in case of symlink path

    // name_is_allowed / trim name, fail if empty or dodgy characters, mkfile, mkdir, rename, duplicate
    function name_is_allowed($name){
      $name = $name ? trim($name) : false; // trim
      // block empty / <>:"'/\|?*# chars / .. / endswith .
      if(empty($name) || preg_match('/[<>:"\'\/\\\|?*#]|\.\.|\.$/', $name)) json_error('invalid name ' . $name);
      return $name; // return valid trimmed name
    }

    // filemanager json_toggle
    function fm_json_toggle($success, $error){
      fm_json_exit($success, array_filter(array('success' => $success, 'error' => empty($success) ? $error : 0)));
    }
    // filemanager json_exit / includes feature to invalidate X3 cache if $x3_path
    function fm_json_exit($success, $arr){
      if($success && config::$x3_path) touch(config::$x3_path . '/app/x3.inc.php');
      json_exit($arr);
    } 

    // UPLOAD
    if($task === 'upload'){
      // upload path must be dir
      if(!$is_dir) json_error('invalid dir ' . post('path'));
      // upload path must be writeable
      if(!is_writable($path)) json_error('upload dir ' . post('path') . ' is not writeable');
      // get $_FILES['file']
      $file = isset($_FILES) && isset($_FILES['file']) && is_array($_FILES['file']) ? $_FILES['file'] : false;
      // invalid $_FILES['file']
      if(empty($file) || !isset($file['error']) || is_array($file['error'])) json_error('invalid $_FILES[]');
      // PHP meaningful file upload errors / https://www.php.net/manual/en/features.file-upload.errors.php
      if($file['error'] !== 0) {
        $upload_errors = array(
          1 => 'Uploaded file exceeds upload_max_filesize directive in php.ini',
          2 => 'Uploaded file exceeds MAX_FILE_SIZE directive specified in the HTML form',
          3 => 'The uploaded file was only partially uploaded',
          4 => 'No file was uploaded',
          6 => 'Missing a temporary folder',
          7 => 'Failed to write file to disk.',
          8 => 'A PHP extension stopped the file upload.'
        );
        json_error(isset($upload_errors[$file['error']]) ? $upload_errors[$file['error']] : 'unknown error');
      }
      // invalid $file['size']
      if(!isset($file['size']) || empty($file['size'])) json_error('invalid file size');
      // $file['size'] must not exceed $config['upload_max_filesize']
      if(config::$config['upload_max_filesize'] && $file['size'] > config::$config['upload_max_filesize']) json_error('File size [' . $file['size'] . '] exceeds upload_max_filesize option [' . config::$config['upload_max_filesize'] . ']');
      // filename
      $filename = $file['name'];
      // security: slashes are never ever allowed in filenames / always basenamed() but just in case
      if(strpos($filename, '/') !== false || strpos($filename, '\\') !== false) json_error('Illegal \slash/ in filename ' . $filename);
      // allow only valid file types from config::$config['upload_allowed_file_types'] / 'image/*, .pdf, .mp4'
      $allowed_file_types = !empty(config::$config['upload_allowed_file_types']) ? array_filter(array_map('trim', explode(',', config::$config['upload_allowed_file_types']))) : false;
      if(!empty($allowed_file_types)){
        $mime = get_mime($file['tmp_name']) ?: $file['type']; // mime from PHP or upload[type]
        $ext = strrchr(strtolower($filename), '.');
        $is_valid = false;
        // check if extension match || wildcard match mime type image/*
        foreach ($allowed_file_types as $allowed_file_type) if($ext === ('.'.ltrim($allowed_file_type, '.')) || fnmatch($allowed_file_type, $mime)) {
          $is_valid = true;
          break;
        }
        if(!$is_valid) json_error('invalid file type ' . $filename);
        // extra security: check if image is image
        if(function_exists('exif_imagetype') && in_array($ext, ['.gif', '.jpeg', '.jpg', '.png', '.swf', '.psd', '.bmp', '.tif', '.tiff', 'webp']) && !@exif_imagetype($file['tmp_name'])) json_error('invalid image type ' . $filename);
      }

      // file naming if !overwrite and file exists
      if(config::$config['upload_exists'] !== 'overwrite' && file_exists("$path/$filename")){

        // fail if !increment / 'upload_exists' => 'fail' || false || '' empty
        if(config::$config['upload_exists'] !== 'increment') json_error("$filename already exists");

        // increment filename / 'upload_exists' => 'increment'
        $name = pathinfo($filename, PATHINFO_FILENAME);
        $ext = pathinfo($filename, PATHINFO_EXTENSION);
        $inc = 1;
        while(file_exists($path . '/' . $name . '-' . $inc . '.' . $ext)) $inc ++;
        $filename = $name . '-' . $inc . '.' . $ext;
      }

      // all is well! attempt to move_uploaded_file()
      if(@move_uploaded_file($file['tmp_name'], "$path/$filename")) fm_json_exit(true, array(
        'success' => true,
        'filename' => $filename, // return filename in case it was incremented or renamed
        'url' => get_url_path("$path/$filename") // for usage with showLinkToFileUploadResult
      ));

      // error if failed to move uploaded file
      json_error('failed to move_uploaded_file()');

    // DELETE
    } else if($task === 'delete'){

      // dir recursive
      if($is_dir){

        // success/fail count
        $success = 0;
        $fail = 0;

        // recursive rmdir
        function rrmdir($dir, &$success, &$fail) {
          //global $success, $fail;
          if(!is_readable($dir)) return $fail ++;
          $files = array_diff(scandir($dir), array('.','..'));
          if(!empty($files)) foreach ($files as $file) {
            is_dir("$dir/$file") ? rrmdir("$dir/$file", $success, $fail) : (@unlink("$dir/$file") ? $success++ : $fail++);
          }
          @rmdir($dir) ? $success ++ : $fail ++;
        }

        // recursive rmdir start
        rrmdir($path, $success, $fail);

        // response with partial success/fail count or error if there is !$success
        fm_json_exit($success, array_filter(array('success' => $success, 'fail' => $fail, 'error' => (empty($success) ? 'Failed to delete dir' : 0))));

      // single file
      } else {
        fm_json_toggle(@unlink($path), 'PHP unlink() failed');
      }

    // new_folder || new_file
    } else if($task === 'new_folder' || $task === 'new_file'){
      if(!$is_dir) json_error('invalid dir ' . post('path')); // parent path must be dir
      if(!is_writable($path)) json_error(post('path') . ' is not writeable.'); // dir must be writeable
      $name = name_is_allowed(post('name')); // trim and check valid
      $file_path = $path . '/' . $name;
      if(file_exists($file_path)) json_error($name . ' already exists');
      fm_json_toggle($task === 'new_folder' ? @mkdir($file_path) : @touch($file_path), $task . ' failed');

    // rename $path (file or dir)
    } else if($task === 'rename'){
      if(!is_writable($path)) json_error(post('path') . ' is not writeable.'); // path must be writeable
      $name = name_is_allowed(post('name')); // trim and check valid
      $new_path = dirname($path) . '/' . $name;
      if(file_exists($new_path)) json_error("$name already exists."); // new name exists
      // security: prevent renaming 'file.html' to 'file.php' / file must already be *.php when renaming
      if(!$is_dir && stripos($path, '.php') === false && stripos($name, '.php') !== false) json_error('cannot rename files to .php');
      fm_json_toggle(@rename($path, $new_path), 'PHP rename() failed');

    // duplicate file
    } else if($task === 'duplicate'){
      if($is_dir) json_error('Can\'t duplicate dir');
      $parent_dir = dirname($path);
      if(!is_writable($parent_dir)) json_error(basename($parent_dir) . ' is not writeable.'); // dir must be writeable
      $name = name_is_allowed(post('name')); // trim and check valid
      $copy_path = $parent_dir . '/' . $name;
      if(file_exists($copy_path)) json_error($name . ' already exists.');
      fm_json_toggle(@copy($path, $copy_path), 'PHP copy() failed');
    
    // text / code edit
    } else if($task === 'text_edit'){
      if($is_dir) json_error('Can\'t write text to directory');
      if(!is_writeable($path) || !is_file($path)) json_error('File is not writeable');
      $success = isset($_POST['text']) && @file_put_contents($path, $_POST['text']) !== false ? 1 : 0; // text could be '' (empty)
      if($success) @touch(dirname($path)); // invalidate any cache by updating parent dir mtime
      fm_json_toggle($success, 'PHP file_put_contents() failed');
    }

	// dirs
	} else if($action === 'dirs'){
    dirs(post('localstorage'));

	// files
	} else if($action === 'files'){
    if(!isset($_POST['dir'])) json_error('Missing dir parameter');
    get_files(valid_root_path($_POST['dir'], true));

	// file read
	} else if($action === 'file'){

    // valid path
    $file = valid_root_path(post('file'));
    if(!$file) error('Invalid file path');

    // read text file
    header('content-type:text/plain;charset=utf-8');
    if(@readfile(real_path($file)) === false) error('failed to read file ' . post('file'), 500);

  // check login
  } else if($action === 'check_login'){
    json_success(true);

  // check updates
  } else if($action === 'check_updates'){
    $json = @json_decode(@file_get_contents('https://data.jsdelivr.com/v1/package/npm/files.photo.gallery'), true);
    $latest = !empty($json) && isset($json['versions'][0]) && version_compare($json['versions'][0], config::$version) > 0 ? $json['versions'][0] : false;
    json_exit(array(
      'success' => $latest,
      'writeable' => $latest && is_writable(__FILE__) // only check writeable if $latest
    ));

  // do update
  } else if($action === 'do_update'){
    $version = post('version');
    if(!$version || version_compare($version, config::$version) <= 0 || !is_writable(__FILE__)) json_error(); // requirements
    $get = @file_get_contents('https://cdn.jsdelivr.net/npm/files.photo.gallery@' . $version . '/index.php');
    if(empty($get) || strpos($get, '<?php') !== 0) json_error(); // basic validation
    json_success(array('success' => @file_put_contents(__FILE__, $get)));

  // store license
  } else if($action === 'license'){
    $key = post('key') ? trim(post('key')) : false;
    json_exit(array(
      'success' => $key && config::$storage_config_realpath && config::save_config(array('license_key' => $key)),
      'md5' => $key ? md5($key) : false
    ));

  // invalid action
	} else {
    json_error('invalid action: ' . $action);
  }

// GET
} else /*if($_SERVER['REQUEST_METHOD'] === 'GET')*/{

  // download_dir_zip / download files in directory as zip file
  if(get('download_dir_zip')) {
    new config();

    // check download_dir enabled
    if(config::$config['download_dir'] !== 'zip') error('<strong>download_dir</strong> Zip disabled.', 403);

    // valid dir
    $dir = valid_root_path(get('download_dir_zip'), true);
    if(!$dir) error('Invalid download path <strong>' . get('download_dir_zip') . '</strong>', 404);
    $dir = real_path($dir); // in case of symlink path

    // create zip cache directly in dir (recommended, so that dir can be renamed while zip cache remains)
    if(!config::$storage_path || config::$config['download_dir_cache'] === 'dir') {
      if(!is_writable($dir)) error('Dir ' . basename($dir) . ' is not writeable.', 500); 
      $zip_file_name = '_files.zip';
      $zip_file = $dir . '/' . $zip_file_name;

    // create zip file in storage _files/zip/$dirname.$md5.zip / 
    } else {
      mkdir_or_error(config::$storage_path . '/zip');
      $zip_file_name = basename($dir) . '.' . substr(md5($dir), 0, 6) . '.zip';
      $zip_file = config::$storage_path . '/zip/' . $zip_file_name;
    }

    // cached / download_dir_cache && file_exists() && zip is not older than dir time
    $cached = !empty(config::$config['download_dir_cache']) && file_exists($zip_file) && filemtime($zip_file) >= filemtime($dir);

    // create zip if !cached
    if(!$cached){

      // use shell zip command instead / probably faster and more robust than PHP / if use, comment out PHP ZipArchive method starting below
      // exec('zip ' . $zip_file . ' ' . $dir . '/*.* -j -x _files*', $out, $res);

      // check that ZipArchive class exists
      if(!class_exists('ZipArchive')) error('Missing PHP ZipArchive class.', 500); 

      // glob files / must be readable / is_file / !symlink / !is_exclude
      $files = array_filter(glob($dir. '/*', GLOB_NOSORT), function($file){
        return is_readable($file) && is_file($file) && !is_link($file) && !is_exclude($file, false);
      });

      // !no files available to zip
      if(empty($files)) error('No files to zip!', 400);
      
      // new ZipArchive
      $zip = new ZipArchive();

      // create new $zip_file
      if($zip->open($zip_file, ZipArchive::CREATE | ZIPARCHIVE::OVERWRITE) !== true) error('Failed to create ZIP file ' . $zip_file_name . '.', 500);

      // add files to zip / flatten with basename()
      foreach($files as $file) $zip->addFile($file, basename($file));

      // no files added (for some reason)
      if(!$zip->numFiles) error('Could not add any files to ' . $zip_file_name . '.', 500);

      // close zip
      $zip->close();

      // make sure created zip file exists / just in case
      if(!file_exists($zip_file)) error('Zip file ' . $zip_file_name . ' does not exist.', 500);
    }

    // redirect instead of readfile() / might be useful if readfile() fails and/or for caching and performance
    /*$zip_url = get_url_path($zip_file);
    if($zip_url){
      header('Location:' . $zip_url . '?' . filemtime($dir), true, 302);
      exit;
    }*/

    // output headers
    if(config::$has_login) {
      header('cache-control: must-revalidate, post-check=0, pre-check=0');
      header('cache-control: public');
      header('expires: 0');
      header('pragma: public');
    } else {
      set_cache_headers();
    }
    header('content-description: File Transfer');
    header('content-disposition: attachment; filename="' . addslashes(basename($dir)) . '.zip"');
    $content_length = filesize($zip_file);
    header('content-length: ' . $content_length);
    header('content-transfer-encoding: binary');
    header('content-type: application/zip');
    header('files-msg: [' . $zip_file_name . '][' . ($cached ? 'cached' : 'created') . ']');

    // ignore user abort so we can delete file also on download cancel
    if(empty(config::$config['download_dir_cache'])) @ignore_user_abort(true);

    // clear output buffer for large files
    while (ob_get_level()) ob_end_clean();

    // output zip readfile()
    if(!readfile($zip_file)) error('Failed to readfile(' . $zip_file_name . ').', 500);

    // delete temp zip file if cache disable
    if(empty(config::$config['download_dir_cache'])) @unlink($zip_file);


  // folder preview image
  } else if(get('preview')){
    new config();

    // allow only if only if folder_preview_image + load_images + image_resize_enabled
    foreach (['folder_preview_image', 'load_images', 'image_resize_enabled'] as $key) if(!config::$config[$key]) error('[' .$key . '] disabled.', 400);

    // get real path and validate
    $path = valid_root_path(get('preview'), true); // make sure is valid dir
    if(!$path) error('Invalid directory.', 404);


    // 1. first check for default '_filespreview.jpg' inside dir
    $default = config::$config['folder_preview_default'] ? $path . '/' . config::$config['folder_preview_default'] : false;
    if($default && file_exists($default)) {
      header('files-preview: folder_preview_default found [' . config::$config['folder_preview_default'] . ']');
      resize_image($default, config::$config['image_resize_dimensions']);
    }


    // 2. check preview cache
    $cache = config::$cache_path . '/images/preview.' . substr(md5($path), 0, 6) . '.jpg';

    // cache file exists
    if(file_exists($cache)) {

      // make sure cache file is valid (must be newer than dir updated time)
      if(filemtime($cache) >= filemtime($path)) read_file($cache, null, 'preview image served from cache', null, true);

      // delete expired cache file if is older than dir updated time [silent]
      @unlink($cache);
    }


    // 3. glob images / GLOB_BRACE may fail on some non GNU systems, like Solaris.
    $images = @glob($path . '/*.{jpg,JPG,jpeg,JPEG,png,PNG,gif,GIF}', GLOB_NOSORT|GLOB_BRACE);

    // loop images to locate first match that is not excluded
    if(!empty($images)) foreach ($images as $image) {
      if(!is_exclude($image, false)) {
        header('files-preview: glob() found [' . basename($image) . ']');
        resize_image($image, config::$config['image_resize_dimensions'], $cache); // + clone into $cache
        break; exit; // just in case
      }
    }


    // 4. nothing found (no images in dir)
    // create empty 1px in $cache, and output (so next check knows dir is empty or has no images, unless updated)
    if(imagejpeg(imagecreate(1, 1), $cache)) read_file($cache, 'image/jpeg', '1px placeholder image created and cached', null, true);


	// file/image
	} else if(isset($_GET['file'])){
    new config();
    get_file(valid_root_path(get('file')), get('resize'));

	// download
	} else if(isset($_GET['download'])){
    new config();

		// valid download
    $download = valid_root_path(get('download'));
    if(!$download) error('Invalid download path <strong>' . get('download') . '</strong>', 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');

	  // 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));
	  while (ob_get_level()) ob_end_clean();
	  readfile($download);

  // 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 <strong>$tasks_path</strong>", 404);
    include $tasks_path;
    exit;

// main document
	} else {

// 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 <strong>' . config::$config['files_exclude'] . '</strong>');
if(config::$config['dirs_exclude'] && @preg_match(config::$config['dirs_exclude'], '') === false) error('Invalid dirs_exclude regex <strong>' . config::$config['dirs_exclude'] . '</strong>');

// 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;

// 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'] && isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING']) ? explode('&', $_SERVER['QUERY_STRING']) : false;
$query_path = $query && strpos($query[0], '=') === false ? 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(real_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);
}

// 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']);

// wtc
$wtc = config::$config[base64_decode('bGljZW5zZV9rZXk')];

// look for custom language files _files/lang/*.json
function lang_custom() {
  $dir = config::$storage_path ? config::$storage_path . '/lang' : false;
  $files = $dir && file_exists($dir) ? glob($dir . '/*.json') : false;
  if(empty($files)) return false;
  $langs = array();
  foreach ($files as $path) {
    $json = @file_get_contents($path);
    $data = !empty($json) ? @json_decode($json, true) : false;
    if(!empty($data)) $langs[strtok(basename($path), '.')] = $data;
  }
  return !empty($langs) ? $langs : 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', 'video_thumbs', 'video_ffmpeg_path', 'folder_preview_default', 'image_resize_dimensions_allowed', 'download_dir_cache')));

// json config
$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,
  '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,
  'video_thumbs_enabled' => config::$config['video_thumbs'] && config::$config['video_ffmpeg_path'] && config::$config['load_images'] && config::$config['image_resize_cache'] && @function_exists('exec') && @exec('type -P ' . config::$config['video_ffmpeg_path']),
  'lang_custom' => lang_custom(),
  'x3_path' => config::$x3_path ? get_url_path(config::$x3_path) : false
));

function php_directive_value_to_bytes($directive) {
  $val = function_exists('ini_get') ? @ini_get($directive) : false;
  if (empty($val) || !is_string($val)) return 0;
  preg_match('/^(?<value>\d+)(?<option>[K|M|G]*)$/i', $val, $matches);
  $value = (int) $matches['value'];
  $option = strtoupper($matches['option']);
  if ($option === 'K') {
    $value *= 1024;
  } elseif ($option === 'M') {
    $value *= 1024 * 1024;
  } elseif ($option === 'G') {
    $value *= 1024 * 1024 * 1024;
  }
  return $value;
}

// upload options
if(config::$config['allow_upload']) {
  if(function_exists('ini_get') && !@ini_get('file_uploads')) error('PHP file_uploads disabled on this server.', 500);
  // get max_filesize from all potential limitations (must be > 0)
  $max_sizes = array_filter(array(php_directive_value_to_bytes('upload_max_filesize'), php_directive_value_to_bytes('post_max_size'), config::$config['upload_max_filesize']));
  // get min val from max_sizes[] || no limit
  $json_config['upload_max_filesize'] = !empty($max_sizes) ? min($max_sizes) : 0;
}

// memory and time
header('files-msg: [' . header_memory_time() . ']');

// htmlstart
?>
<!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">
    <meta name="robots" content="noindex,nofollow">
    <title><?php echo $init_path ? basename($init_path) : '/'; ?></title>
    <?php get_include('include/head.html'); ?>
    <link href="<?php echo config::$assets ?>css/files.css" rel="stylesheet">
    <?php get_include('css/custom.css'); ?>
  </head>

  <body class="body-loading"><svg viewBox="0 0 18 18" class="svg-preloader svg-preloader-active preloader-body"><circle cx="9" cy="9" r="8" pathLength="100" class="svg-preloader-circle"></svg>
    <main id="main">
      <?php
      $topbar_classes = array();
      if(config::$config['topbar_sticky']) array_push($topbar_classes, 'topbar-sticky');
      if($breadcrumbs) array_push($topbar_classes, 'has-breadcrumbs');
      ?>
      <nav id="topbar"<?php if(!empty($topbar_classes)) echo ' class="' . join(' ', $topbar_classes) . '"'; ?>>
        <div id="topbar-top">
          <div id="search-container"><input id="search" type="search" placeholder="search" size="1" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" disabled></div>
          <div id="change-layout" class="dropdown"></div>
          <div id="change-sort" class="dropdown"></div>
        </div>
        <?php if($breadcrumbs) { ?>
        <div id="topbar-breadcrumbs">
          <div class="breadcrumbs-info"></div>
          <div id="breadcrumbs"></div>
        </div>
        <?php } ?>
        <div id="topbar-info" class="info-hidden"></div>
        <div id="files-sortbar"></div>
      </nav>
      <!-- files list container -->
      <div><div id="files" class="list files-<?php echo config::$config['layout']; ?>"></div></div>
    </main>
<?php if($menu_enabled) { ?>
    <aside id="sidebar">
      <button id="sidebar-toggle" type="button" class="btn-icon"></button>
      <div id="sidebar-inner">
        <div id="sidebar-topbar"></div>
        <div id="sidebar-menu"></div>
      </div>
    </aside>
    <div id="sidebar-bg"></div> 
<?php } ?>

    <!-- modal -->
    <div id="modal-bg"></div>
    <div class="modal" id="files_modal" tabindex="-1" role="dialog" data-action="close"></div>

    <!-- context menu -->
    <div id="contextmenu" class="dropdown-menu"></div>

    <!-- custom footer html -->
    <?php get_include('include/footer.html'); ?>

    <!-- Javascript -->
    <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.1.9/dist/sweetalert2.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
    <!--<script src="https://cdn.jsdelivr.net/npm/list.js@2.3.1/dist/list.min.js"></script>-->
    <script src="https://cdn.jsdelivr.net/npm/@exeba/list.js@2.3.1/dist/list.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/yall-js@3.2.0/dist/yall.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/filesize@8.0.6/lib/filesize.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/screenfull@5.1.0/dist/screenfull.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dayjs@1.10.7/dayjs.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dayjs@1.10.7/plugin/localizedFormat.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dayjs@1.10.7/plugin/relativeTime.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>
<?php if(config::$config['download_dir'] === 'files') { ?>
    <script src="https://cdn.jsdelivr.net/npm/js-file-downloader@1.1.22/dist/js-file-downloader.min.js"></script>
<?php } ?>
    <script>
var _c = <?php echo json_encode($json_config, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_PARTIAL_OUTPUT_ON_ERROR); ?>;
var CodeMirror = {};
    </script>
    <script src="https://cdn.jsdelivr.net/npm/codemirror@5.63.3/mode/meta.js"></script>
    <!-- custom -->
    <?php get_include('js/custom.js'); ?>
    <!-- files -->
    <script src="<?php echo config::$assets ?>js/files.js"></script>

  </body>
</html>
<?php }}
// htmlend
?>