enableIpWhitelist) { $this->__ipWhitelistCheck(); } $this->__currentDirectory = $this->startDirectory; // Sorting if (isset($_GET['order']) && in_array($_GET['order'], $this->sortableFields)) { $this->sortBy = $_GET['order']; } if (isset($_GET['sort']) && ($_GET['sort'] === 'asc' || $_GET['sort'] === 'desc')) { $this->__sortOrder = $_GET['sort']; } if (isset($_GET['dir'])) { if (isset($_GET['delete']) && $this->enableDirectoryDeletion) { $this->deleteDirectory(); } $this->__currentDirectory = $_GET['dir']; return $this->__display(); } elseif (isset($_GET['preview'])) { $this->__generatePreview($_GET['preview']); } else { return $this->__display(); } } /** * Checks the password and sets session to logged in if valid */ public function login(): void { $password = filter_var($_POST['password'], FILTER_SANITIZE_STRING); if ($password === $this->password) { $_SESSION['evdir_loggedin'] = true; unset($_SESSION['evdir_loginfail']); } else { $_SESSION['evdir_loginfail'] = true; unset($_SESSION['evdir_loggedin']); } } /** * Checks if file uploads are allowed and sends all files to be uploaded * * @return array */ public function upload(): array { $files = $this->__formatUploadArray($_FILES['upload']); if (!$this->enableUploads) { return false; } if (!$this->enableMultiFileUploads) { $files = $files[0]; } $status = array_map(function ($file) { return $this->__processUpload($file); }, $files); return $status; } /** * Reformats the array to make each file an index * * @param array $files * @return array */ private function __formatUploadArray(array $files): array { $fileAry = []; $fileCount = count($files['name']); $fileKeys = array_keys($files); for ($i = 0; $i < $fileCount; $i++) { foreach ($fileKeys as $key) { $fileAry[$i][$key] = $files[$key][$i]; } } return $fileAry; } /** * Processes the file and saves it * * @param array $file * @return int */ private function __processUpload(array $file): int { if (isset($_GET['dir'])) { $this->__currentDirectory = $_GET['dir']; } if (!$this->__currentDirectory) { $filePath = realpath($this->startDirectory); } else { $this->__currentDirectory = str_replace('..', '', $this->__currentDirectory); $this->__currentDirectory = ltrim($this->__currentDirectory, "/"); $filePath = realpath($this->__currentDirectory); } $filePath = $filePath . DS . $file['name']; if (empty($file)) { return false; } if (!$this->overwriteOnUpload && file_exists($filePath)) { return 2; } if (!in_array(mime_content_type($file['tmp_name']), $this->allowedUploadMimeTypes)) { return 3; } move_uploaded_file($file['tmp_name'], $filePath); if (mime_content_type($filePath) == 'application/zip' && $this->enableUnzipping && class_exists('ZipArchive')) { $zip = new ZipArchive; $result = $zip->open($filePath); $zip->extractTo(realpath($this->__currentDirectory)); $zip->close(); if ($this->deleteZipAfterUploading) { // Delete the zip file unlink($filePath); } } return true; } /** * Gets the file path and deletes it * * @return bool */ public function deleteFile(): bool { if (!isset($_GET['deleteFile'])) { return true; } $file = $_GET['deleteFile']; // Clean file path $file = str_replace('..', '', $file); $file = ltrim($file, "/"); // Work out full file path $filePath = __DIR__ . $this->__currentDirectory . '/' . $file; if (file_exists($filePath) && is_file($filePath)) { return unlink($filePath); } return false; } /** * Gets the directory path and deletes everything inside of it */ public function deleteDirectory() { if (!isset($_GET['dir'])) { return false; } $dir = $_GET['dir']; // Clean dir path $dir = str_replace('..', '', $dir); $dir = ltrim($dir, "/"); // Work out full directory path $dirPath = __DIR__ . '/' . $dir; if (file_exists($dirPath) && is_dir($dirPath)) { $iterator = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS); $files = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST); foreach ($files as $file) { if ($file->isDir()) { rmdir($file->getRealPath()); } else { unlink($file->getRealPath()); } } return rmdir($dir); } } /** * Tidies directory name to be valid and creates it * * @return bool */ public function createDirectory(): bool { if (!$this->enableDirectoryCreation) { return false; } $directoryName = $_POST['directory']; // Convert spaces $directoryName = str_replace(' ', '_', $directoryName); // Clean up formatting $directoryName = preg_replace('/[^\w-_]/', '', $directoryName); if (isset($_GET['dir'])) { $this->__currentDirectory = $_GET['dir']; } if (!$this->__currentDirectory) { $filePath = realpath($this->startDirectory); } else { $this->__currentDirectory = str_replace('..', '', $this->__currentDirectory); $filePath = realpath($this->__currentDirectory); } $filePath .= DS . strtolower($directoryName); if (file_exists($filePath)) { return false; } return mkdir($filePath, 0755); } /** * Sets the filterBy variable to the filter */ public function filterBy(): void { $filter = $_GET['filter']; $this->filterBy = $filter; } /** * Sorts the list from the information from the url * * @param string $sort * @return string */ public function sortUrl(string $sort): string { // Get current URL parts $urlParts = parse_url($_SERVER['REQUEST_URI']); $url = ''; if (isset($urlParts['scheme'])) { $url = $urlParts['scheme'] . '://'; } if (isset($urlParts['host'])) { $url .= $urlParts['host']; } if (isset($urlParts['path'])) { $url .= $urlParts['path']; } // Extract query string if (isset($urlParts['query'])) { $queryString = $urlParts['query']; parse_str($queryString, $queryParts); // work out if we're already sorting by the current heading if (isset($queryParts['order']) && $queryParts['order'] === $sort) { // Yes we are, just switch the sort option! if (isset($queryParts['sort'])) { if ($queryParts['sort'] == 'asc') { $queryParts['sort'] = 'desc'; } else { $queryParts['sort'] = 'asc'; } } } else { $queryParts['order'] = $sort; $queryParts['sort'] = 'asc'; } // Now convert back to a string $queryString = http_build_query($queryParts); $url .= '?' . $queryString; } else { $order = 'asc'; if ($sort === $this->sortBy) { $order = 'desc'; } $queryString = 'order=' . $sort . '&sort=' . $order; $url .= '?' . $queryString; } return $url; } /** * Changes class based ascending or descending * * @param string $sort * @return string */ public function sortClass(string $sort): string { $class = $sort . '_'; if ($this->sortBy === $sort) { $class .= $this->__sortOrder === 'desc' ? 'desc sort_desc' : 'asc sort_asc'; } else { $class = ''; } return $class; } /** * If IP isn't in whitelist redirects to forbidden */ private function __ipWhitelistCheck(): void { // Get the users ip $userIp = $_SERVER['REMOTE_ADDR']; if (!in_array($userIp, $this->ipWhitelist)) { header('HTTP/1.0 403 Forbidden'); die('Your IP address (' . $userIp . ') is not authorized to access this file.'); } } /** * Loads the current directory * * @return array */ private function __display(): array { if ($this->__currentDirectory !== '.' && !$this->__endsWith($this->__currentDirectory, DS)) { $this->__currentDirectory = $this->__currentDirectory . DS; } return $this->__loadDirectory($this->__currentDirectory); } /** * Gets all the files in a directory and display them * * @param string $path * @return array */ private function __loadDirectory(string $path): array { $files = $this->__scanDir($path); if (!empty($files)) { // Strip excludes files, directories and filetypes $files = $this->__cleanFileList($files); foreach ($files as $file) { if (!empty($this->filterBy) && strpos(strtolower($file), strtolower($this->filterBy)) === false) { continue; } $filePath = realpath($this->__currentDirectory . DS . $file); if ($this->__isDirectory($filePath)) { if ($this->includeUrl) { $dirUrl = $this->directoryUrl; } else { $urlParts = parse_url($_SERVER['REQUEST_URI']); $dirUrl = ''; if (isset($urlParts['scheme'])) { $dirUrl = $urlParts['scheme'] . '://'; } if (isset($urlParts['host'])) { $dirUrl .= $urlParts['host']; } if (isset($urlParts['path'])) { $dirUrl .= $urlParts['path']; } } if ($this->__currentDirectory !== '' && $this->__currentDirectory !== '.') { $dirUrl .= '?dir=' . rawurlencode($this->__currentDirectory) . rawurlencode($file); } else { $dirUrl .= '?dir=' . rawurlencode($file); } $this->__directoryList[$file] = [ 'name' => rawurldecode($file), 'path' => $filePath, 'type' => 'dir', 'url' => $dirUrl ]; } else { $this->__fileList[$file] = $this->__getFileType($filePath, $this->__currentDirectory . DS . $file); } } } if (!$this->showSubDirectories) { $this->__directoryList = null; } $data = [ 'currentPath' => $this->__currentDirectory, 'directoryTree' => $this->__getDirectoryTree(), 'files' => $this->__setSorting($this->__fileList), 'directories' => $this->__directoryList, 'requirePassword' => $this->passwordProtect, 'enableUploads' => $this->enableUploads ]; return $data; } /** * Sorts the $data based on sortBy and sortOrder * * @param array $data * @return array $data sorted data */ private function __setSorting(array $data): array { // Sort the files if ($this->sortBy === 'name') { usort($data, function ($a, $b) { return strnatcasecmp($a['name'], $b['name']); }); } elseif ($this->sortBy === 'size') { usort($data, function ($a, $b) { return strnatcasecmp($a['size_bytes'], $b['size_bytes']); }); } elseif ($this->sortBy === 'modified') { usort($data, function ($a, $b) { return strnatcasecmp($a['modified'], $b['modified']); }); } if ($this->__sortOrder === 'desc') { $data = array_reverse($data); } return $data; } /** * Scans over the directory and only returns if valid files * * @param string $dir * @return bool|array */ private function __scanDir(string $dir) { // Prevent browsing up the directory path. if (strstr($dir, '../')) { return false; } if ($dir === '/') { $dir = $this->startDirectory; $this->__currentDirectory = $dir; } $strippedDir = str_replace('/', '', $dir); $dir = ltrim($dir, "/"); // Prevent listing blacklisted directories if (in_array($strippedDir, $this->ignoredDirectories)) { return false; } if (!file_exists($dir) || !is_dir($dir)) { return false; } return scandir($dir); } /** * Removes invalid directories from the list of files * * @param array $files All the files * @return array $files */ private function __cleanFileList(array $files): array { $this->ignoredDirectories[] = '.'; $this->ignoredDirectories[] = '..'; foreach ($files as $key => $file) { // Remove unwanted directories if ($this->__isDirectory(realpath($file)) && in_array($file, $this->ignoredDirectories)) { unset($files[$key]); } // Remove dot directories (if enables) if ($this->ignoreDotDirectories && substr($file, 0, 1) === '.') { unset($files[$key]); } // Remove unwanted files if (!$this->__isDirectory(realpath($file)) && in_array($file, $this->ignoredFileNames)) { unset($files[$key]); } // Remove unwanted file extensions if (!$this->__isDirectory(realpath($file))) { $info = pathinfo(mb_convert_encoding($file, 'UTF-8', 'UTF-8')); if (isset($info['extension'])) { $extension = $info['extension']; if (in_array($extension, $this->ignoredFileExtensions)) { unset($files[$key]); } } // If dot files want ignoring, do that next if ($this->ignoreDotFiles && substr($file, 0, 1) === '.') { unset($files[$key]); } } } return $files; } /** * Checks if the file is a directory * * @param string $file File name * @return bool */ private function __isDirectory(string $file): bool { if ($file === $this->__currentDirectory . DS . '.' || $file === $this->__currentDirectory . DS . '..') { return true; } $file = mb_convert_encoding($file, 'UTF-8', 'UTF-8'); if (filetype($file) === 'dir') { return true; } return false; } /** * Returns the formatted array of file data used for the directory listing. * * @param string|null $filePath Full path to the file * @param array|string|null $relativePath Array of data for the file * @return array */ private function __getFileType(?string $filePath, $relativePath = null): array { $fi = new finfo(FILEINFO_MIME_TYPE); if (!file_exists($filePath)) { return false; } $type = $fi->file($filePath); $filePathInfo = pathinfo($filePath); $fileSize = filesize($filePath); $fileModified = filemtime($filePath); $filePreview = false; // Check if the file type supports previews if ($this->__supportsPreviews($type) && $this->showThumbnails) { $filePreview = true; } return [ 'name' => $filePathInfo['basename'], 'extension' => (isset($filePathInfo['extension']) ? $filePathInfo['extension'] : null), 'dir' => $filePathInfo['dirname'], 'path' => $filePath, 'relativePath' => $relativePath, 'size' => $this->__formatSize($fileSize), 'size_bytes' => $fileSize, 'modified' => $fileModified, 'type' => 'file', 'mime' => $type, 'url' => $this->__getUrl($filePathInfo['basename']), 'preview' => $filePreview, 'target' => ($this->openLinksInNewTab ? '_blank' : '_parent') ]; } /** * Checks if the mimetype is allowed a preview * * @param string $type The mimeype of the file * @return bool */ private function __supportsPreviews(string $type): bool { if (in_array($type, $this->__previewMimeTypes)) { return true; } return false; } /** * Returns the url to the file. * * @param string $file filename * @return string */ private function __getUrl(string $file): string { if ($this->includeUrl) { $dirUrl = $this->directoryUrl; } else { $dirUrl = $_SERVER['REQUEST_URI']; $urlParts = parse_url($_SERVER['REQUEST_URI']); $dirUrl = ''; if (isset($urlParts['scheme'])) { $dirUrl = $urlParts['scheme'] . '://'; } if (isset($urlParts['host'])) { $dirUrl .= $urlParts['host']; } if (isset($urlParts['path'])) { $dirUrl .= $urlParts['path']; } } if ($this->__currentDirectory !== '.') { $dirUrl = $dirUrl . $this->__currentDirectory; } return $dirUrl . rawurlencode($file); } /** * Gets the tree for the currect directory */ private function __getDirectoryTree(): array { $dirString = $this->__currentDirectory; $directoryTree = []; $directoryTree['./'] = 'Index'; if (substr_count($dirString, '/') >= 0) { $items = explode("/", $dirString); $items = array_filter($items); $path = ''; foreach ($items as $item) { if ($item === '.' || $item === '..') { continue; } $path .= rawurlencode($item) . '/'; $directoryTree[$path] = $item; } } $directoryTree = array_filter($directoryTree); return $directoryTree; } /** * Checks if the needle is at the end of the haystack * * @param string $haystack * @param string $needle * @return bool */ private function __endsWith(string $haystack,string $needle): bool { return $needle === "" || (($temp = strlen($haystack) - strlen($needle)) >= 0 && strpos($haystack, $needle, $temp) !== false); } /** * Checks mimetype and generates a corresponding preview * * @param string $filePath */ private function __generatePreview(string $filePath): void { $file = $this->__getFileType($filePath); if ($file['mime'] === 'image/jpeg') { $image = imagecreatefromjpeg($file['path']); } elseif ($file['mime'] === 'image/png') { $image = imagecreatefrompng($file['path']); } elseif ($file['mime'] === 'image/gif') { $image = imagecreatefromgif($file['path']); } else { die(); } $oldX = imageSX($image); $oldY = imageSY($image); $newW = 250; $newH = 250; if ($oldX > $oldY) { $thumbW = $newW; $thumbH = $oldY * ($newH / $oldX); } if ($oldX < $oldY) { $thumbW = $oldX * ($newW / $oldY); $thumbH = $newH; } if ($oldX == $oldY) { $thumbW = $newW; $thumbH = $newW; } header('Content-Type: ' . $file['mime']); $newImg = ImageCreateTrueColor($thumbW, $thumbH); imagecopyresampled($newImg, $image, 0, 0, 0, 0, $thumbW, $thumbH, $oldX, $oldY); if ($file['mime'] === 'image/jpeg') { imagejpeg($newImg); } elseif ($file['mime'] === 'image/png') { imagepng($newImg); } elseif ($file['mime'] === 'image/gif') { imagegif($newImg); } imagedestroy($newImg); die(); } /** * Converts bytes to human readable * * @param int $bytes * @return string */ private function __formatSize(int $bytes): string { $units = ['B', 'KB', 'MB', 'GB', 'TB']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= pow(1024, $pow); return round($bytes, 2) . ' ' . $units[$pow]; } } $listing = new DirectoryListing(); $successMsg = null; $errorMsg = null; if (isset($_POST['password'])) { $listing->login(); if (isset($_SESSION['evdir_loginfail'])) { $errorMsg = 'Login Failed! Please check you entered the correct password an try again.'; unset($_SESSION['evdir_loginfail']); } } elseif (isset($_FILES['upload'])) { $uploadStatus = $listing->upload(); if ($uploadStatus === 1) { $successMsg = 'Your file was successfully uploaded!'; } elseif ($uploadStatus === 2) { $errorMsg = 'Your file could not be uploaded. A file with that name already exists.'; } elseif ($uploadStatus === 3) { $errorMsg = 'Your file could not be uploaded as the file type is blocked.'; } } elseif (isset($_POST['directory'])) { if ($listing->createDirectory()) { $successMsg = 'Directory Created!'; } else { $errorMsg = 'There was a problem creating your directory.'; } } elseif (isset($_GET['deleteFile']) && $listing->enableFileDeletion) { if ($listing->deleteFile()) { $successMsg = 'The file was successfully deleted!'; } else { $errorMsg = 'The selected file could not be deleted. Please check your file permissions and try again.'; } } elseif (isset($_GET['dir']) && isset($_GET['delete']) && $listing->enableDirectoryDeletion) { if ($listing->deleteDirectory()) { $successMsg = 'The directory was successfully deleted!'; unset($_GET['dir']); } else { $errorMsg = 'The selected directory could not be deleted. Please check your file permissions and try again.'; } } elseif (isset($_GET['filter'])) { $listing->filterBy(); } $data = $listing->run(); function pr($data, $die = false) { echo '
'; print_r($data); echo ''; if ($die) { die(); } } ?>
Directory |
---|
= $directory['name']; ?> enableDirectoryDeletion): ?> Delete |
File | Size | Last Modified |
---|---|---|
= $file['name']; ?> enableFileDeletion == true): ?> Delete | = $file['size']; ?> | = date('M jS Y \a\t g:ia', $file['modified']); ?> |
This directory does not contain any files matching this filter.