This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Thanks to: * Peppe Bergqvist for lots of testing, great feedback, ideas, code and icon themes * Mikael Magnusson for testing and whining ;) * Jonas Müller for being such a great friend :) */ error_reporting(E_ALL); set_time_limit(0); class Tumbo { var $filetype_information = array( 'archive' => array('tar', 'gz', 'bz2', 'deb', 'zip', 'rar', 'ace', 'rpm'), 'audio' => array('mp3', 'wav'), 'code' => array('c', 'cpp', 'h', 'asm', 'php', 'phps', 'pl', 'diff', 'patch'), // directory 'executable' => array('exe', 'dll', 'so'), 'html' => array('html'), 'image' => array('bmp', 'ico', 'xpm', 'gif', 'svg'), 'image-thumbnailable' => array('jpg', 'jpeg', 'png'), 'pdf' => array('pdf'), 'text' => array('txt', 'doc', 'rtf'), // unknown 'video' => array('mpg', 'mpeg', 'avi', 'mov') ); var $path; var $errors; // Stuff configured in config.php var $root; var $public_root; var $thumbnails; var $icon_theme; // Constructor function tumbo() { require('config.php'); $this->errors = array(); if (dir($root)) { $this->root = $root; } else { $this->error("The configured root directory ($root) doesn't exist."); } $this->public_root = $public_root; $thumbnails = $this->create_path(array($root, 'thumbnails')); if (is_writable($thumbnails) == false) { $this->error("The thumbnails directory ($thumbnails) is not writable."); } $this->set_icon_theme($icon_theme); $path = ''; if (isset($_GET['path']) && $_GET['path']) { $path = $this->fix_path(rawurldecode($_GET['path'])); } else { $path = '.'; } if (is_dir($path) == false) { $path = '.'; } $this->path = $path; } function set_icon_theme($icon_theme) { if (is_dir($this->create_path(array($this->root, 'icons', $icon_theme)))) { $this->icon_theme = $icon_theme; } else { $this->error("The configured icon theme ($icon_theme) doesn't exist."); } } function error($message) { array_push($this->errors, $message); } function fix_path($path) { $exploded_path = explode('/', $path); foreach ($exploded_path as $number => $part) { if ($part == '.') { unset($exploded_path[$number]); } elseif ($part == '..') { unset($exploded_path[$number], $exploded_path[$number - 1]); } } return implode($exploded_path, '/'); } function file_type($file) { $type = ''; if (is_dir($this->create_path(array($this->path, $file)))) { $type = 'directory'; } elseif (strstr($file, '.')) { $current_extension = $this->filename_extension($file); foreach ($this->filetype_information as $filetype => $extensions) { foreach ($extensions as $extension) { if ($current_extension == $extension) { $type = $filetype; break; } } if ($type) { break; } } } return $type ? $type : 'unknown'; } function generate_filelist() { $filelist = array(); $handle = opendir($this->path); while (($file = readdir($handle)) !== false) { // Should we ignore the presence of the file? if (substr($file, 0, 1) == '.' || ($this->path == '.' && $file == basename($_SERVER['PHP_SELF'])) // FIXME: Ignore query string || $file == 'Thumbs.db' || $file == 'favicon.ico') { continue; } $info = array('name' => $file, 'type' => $this->file_type($file)); if ($info['type'] == 'image-thumbnailable') { $info['checksum'] = $this->create_thumbnail($file); } array_push($filelist, $info); } closedir($handle); usort($filelist, array('Tumbo', 'sort_filelist_callback')); return $filelist; } // Extracts the extension of a filename function filename_extension($filename) { $tmp = explode('.', $filename); return strtolower($tmp[count($tmp) - 1]); } function has_errors() { return count($this->errors) > 0; } function display_errors() { echo "Tumbo experienced the following problems:"; echo ""; } function result() { echo '
' . "\n"; $hourglass = $this->create_path(array($this->public_root, 'icons', 'hourglass.png')); echo '
'; echo $this->image_tag($hourglass, "Loading..."); echo 'Working... Please wait.'; echo '
' . "\n"; flush(); $filelist = $this->generate_filelist(); echo '' . "\n"; if ($this->has_errors()) { $this->display_errors(); } else { $this->show_filelist($filelist); } echo "
\n"; } // Used with usort() in generate_filelist() function sort_filelist_callback($a, $b) { if ($a['type'] == 'directory' && $b['type'] != 'directory') { return -1; } elseif ($a['type'] != 'directory' && $b['type'] == 'directory') { return 1; } else { return strcasecmp($a['name'], $b['name']); } } // Makes sure a thumbnail is present, and returns the checksum of the image function create_thumbnail($filename) { // The size of image thumbnails :) $thumbnail_size = 48; $target_path = $this->create_path(array($this->path, $filename)); $checksum = md5_file($target_path); $_root = $this->root; $thumbnail_path = $this->create_path(array($this->root, 'thumbnails', "$checksum.jpg")); if (file_exists($thumbnail_path) == false) { $current_size = getimagesize($target_path); if ($current_size[0] > $thumbnail_size || $current_size[1] > $thumbnail_size) { if ($current_size[0] < $current_size[1]) { $height = $thumbnail_size; $width = (int)(($current_size[0] * $thumbnail_size) / $current_size[1]); } else { $height = (int)(($current_size[1] * $thumbnail_size) / $current_size[0]); $width = $thumbnail_size; } if ($this->filename_extension($filename) == 'jpg' || $this->filename_extension($filename) == 'jpeg') { $existing_image = imagecreatefromjpeg($target_path); } elseif ($this->filename_extension($filename) == 'png') { $existing_image = imagecreatefrompng($target_path); } if ($existing_image == false) { $this->error("Couldn't not create thumbnail of $filename. Is gd2 installed? Is the image corrupt?"); } $thumbnail = imagecreatetruecolor($width, $height); imagecopyresampled($thumbnail, $existing_image, 0, 0, 0, 0, imagesx($thumbnail), imagesy($thumbnail), imagesx($existing_image), imagesy($existing_image) ); imagejpeg($thumbnail, $thumbnail_path, 75); imagedestroy($thumbnail); imagedestroy($existing_image); } else { copy($target_path, $thumbnail_path); } } return $checksum; } function icon_path($name) { return $this->create_path(array($this->public_root, 'icons', $this->icon_theme, "$name.png")); } function image_tag($url, $alt) { return "\"$alt\""; } function icon_tag($icon, $alt) { $url = $this->icon_path($icon); return $this->image_tag($url, $alt); } function link_tag($url, $text) { return "$text"; } function create_path($array) { return implode('/', $array); } function show_filelist($filelist) { // Uh... FIXME FFS. if (strpos($_SERVER['REQUEST_URI'], '?')) { $base = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?')); } else { $base = $_SERVER['REQUEST_URI']; } $_path = $this->path; echo '
'; if ($_path == '.') { echo $this->icon_tag("up-disabled", "Up (disabled"); } else { echo $this->link_tag($base . '?path=' . $this->fix_path("$_path/.."), $this->icon_tag('up', 'Up')); } echo ''; echo $this->link_tag("http://termos.vemod.net/tumbo", "Tumbo"); echo ": " . ($_path != '.' ? $_path : 'Root'); echo ''; echo '
'; foreach ($filelist as $info) { $filename = $info['name']; $filetype = $info['type']; $checksum = isset($info['checksum']) ? $info['checksum'] : ''; $target_path = str_replace('%2F', '/', rawurlencode(($_path != '.' ? "$_path/" : '') . $filename)); if ($filetype == 'directory') { $target_path = $base . '?path=' . $target_path; } $icon = ''; if ($filetype == 'image-thumbnailable') { $icon = $this->create_path(array($this->public_root, 'thumbnails', "$checksum.jpg")); } else { $icon = $this->icon_path($filetype); } $wrapped_filename = $this->shorten_string($filename); echo ""; echo "
$wrapped_filename
"; echo "
\n"; } } function shorten_string($string) { if (strlen($string) > 12) { return substr($string, 0, 10) . '...'; } else { return $string; } } } ?>