* @version 1.1.1 (August 10, 2005) * @package AutoIndex */ class Admin { /** * @var int The level of the logged in user */ private $level; /** * @var string The name of the logged in user */ private $username; /** * @param string $path The path of the directory to create * @return bool True on success, false on failure */ public static function mkdir_recursive($path) { $path = Item::make_sure_slash($path); if (@is_dir($path)) { return true; } if (!self::mkdir_recursive(dirname($path))) { return false; } return @mkdir($path, 0755); } /** * Deletes a directory and all its contents. * * @param string $path The path of the directory to delete * @return bool True on success, false on failure */ private static function rmdir_recursive($path) { $path = Item::make_sure_slash($path); $list = @scandir($path); if ($list === false) { return false; } foreach ($list as $file) { if ($file == '' || $file == '.' || $file == '..') { continue; } $dir = "$path$file/"; @is_dir($dir) ? self::rmdir_recursive($dir) : @unlink($dir); } return @rmdir($path); } /** * Copies a remote file to the local server. * * @param string $protocol Either ftp:// or http:// * @param string $url The rest of the URL after the protocol */ private static function copy_remote_file($protocol, $url) { if ($protocol == '' || $url == '') { throw new ExceptionDisplay('Please go back and enter a file to copy.'); } global $dir; $local_file = $dir . Item::get_basename($url); if (@file_exists($local_file)) { throw new ExceptionDisplay('The file already exists in this directory.'); } $remote = $protocol . $url; $r = @fopen($remote, 'rb'); if ($r === false) { throw new ExceptionDisplay('Cannot open remote file for reading: ' . Url::html_output($remote) . ''); } $l = @fopen($local_file, 'wb'); if ($l === false) { throw new ExceptionDisplay('Cannot open local file for writing.'); } while (true) { $temp = fread($r, 8192); if ($temp === '') { break; } fwrite($l, $temp); } fclose($l); fclose($r); } /** * @param string $filename The path to the file that stores the info * @param string $old_name The old name of the file or folder to update inside of $filename * @param string $new_name The new name of the file or folder */ private static function update_file_info($filename, $old_name, $new_name) { if (!@is_file($filename)) { throw new ExceptionDisplay('The file ' . Url::html_output($filename) . ' does not exist.'); } $text = @file_get_contents($filename); if ($text === false) { throw new ExceptionDisplay('Cannot open file ' . Url::html_output($filename) . ' for reading.'); } $h = @fopen($filename, 'wb'); if ($h === false) { throw new ExceptionDisplay('Cannot open file ' . Url::html_output($filename) . ' for writing.'); } fwrite($h, preg_replace('/^' . preg_quote($old_name, '/') . '/m', $new_name, $text)); fclose($h); } /** * Validates a potential new password. * * @param string $pass1 The new password * @param string $pass2 The new password typed again */ private static function validate_new_password($pass1, $pass2) { if ($pass1 != $pass2) { throw new ExceptionDisplay('Passwords do not match.'); } if (strlen($pass1) < 6) { throw new ExceptionDisplay('Password must be at least 6 characters long.'); } } /** * Changes a user's password. * * @param string $username The username * @param string $old_pass The user's old password * @param string $new_pass1 The new password * @param string $new_pass2 The new password typed again */ private static function change_password($username, $old_pass, $new_pass1, $new_pass2) { self::validate_new_password($new_pass1, $new_pass2); $accounts = new Accounts(); if (!$accounts -> user_exists($username)) { throw new ExceptionDisplay('Cannot change password: username does not exist.'); } if (!$accounts -> is_valid_user(new User($username, sha1($old_pass)))) { throw new ExceptionDisplay('Incorrect old password.'); } global $config; $h = @fopen($config -> __get('user_list'), 'wb'); if ($h === false) { throw new ExceptionDisplay("Could not open file $user_list for writing." . ' Make sure PHP has write permission to this file.'); } foreach ($accounts as $this_user) { if (strcasecmp($this_user -> username, $username) === 0) { $this_user = new User($username, sha1($new_pass1), $this_user -> level, $this_user -> home_dir); } fwrite($h, $this_user -> __toString()); } fclose($h); $_SESSION['password'] = sha1($new_pass1); throw new ExceptionDisplay('Password successfully changed.'); } /** * Changes a user's level. * * @param string $username The username * @param int $new_level The user's new level */ private static function change_user_level($username, $new_level) { if ($new_level < BANNED || $new_level > ADMIN) { throw new ExceptionDisplay('Invalid user level.'); } $accounts = new Accounts(); if (!$accounts -> user_exists($username)) { throw new ExceptionDisplay('Cannot change level: username does not exist.'); } global $config; $h = @fopen($config -> __get('user_list'), 'wb'); if ($h === false) { throw new ExceptionDisplay("Could not open file $user_list for writing." . ' Make sure PHP has write permission to this file.'); } foreach ($accounts as $this_user) { if (strcasecmp($this_user -> username, $username) === 0) { $this_user = new User($username, $this_user -> sha1_pass, $new_level, $this_user -> home_dir); } fwrite($h, $this_user -> __toString()); } fclose($h); throw new ExceptionDisplay('User level successfully changed.'); } /** * @param string $username The name of the new user to create * @param string $pass1 The raw password * @param string $pass2 The raw password repeated again for verification * @param int $level The level of the user (use GUEST USER ADMIN constants) * @param string $home_dir The home directory of the user, or blank for the default */ private static function add_user($username, $pass1, $pass2, $level, $home_dir = '') { self::validate_new_password($pass1, $pass2); $username_reg_exp = '/^[A-Za-z0-9_-]+$/'; if (!preg_match($username_reg_exp, $username)) { throw new ExceptionDisplay('The username must only contain alpha-numeric characters, underscores, or dashes.' . '
It must match the regular expression: ' . Url::html_output($username_reg_exp) . ''); } if ($home_dir != '') { $home_dir = Item::make_sure_slash($home_dir); if (!@is_dir($home_dir)) { throw new ExceptionDisplay('The user\'s home directory is not valid directory.'); } } $list = new Accounts(); if ($list -> user_exists($username)) { throw new ExceptionDisplay('This username already exists.'); } global $config; $h = @fopen($config -> __get('user_list'), 'ab'); if ($h === false) { throw new ExceptionDisplay('User list file could not be opened for writing.'); } $new_user = new User($username, sha1($pass1), $level, $home_dir); fwrite($h, $new_user -> __toString()); fclose($h); throw new ExceptionDisplay('User successfully added.'); } /** * @param string $username Deletes user with the name $username */ private static function del_user($username) { $accounts = new Accounts(); if (!$accounts -> user_exists($username)) { throw new ExceptionDisplay('Cannot delete user: username does not exist.'); } global $config; $h = @fopen($config -> __get('user_list'), 'wb'); if ($h === false) { throw new ExceptionDisplay("Could not open file $user_list for writing." . ' Make sure PHP has write permission to this file.'); } foreach ($accounts as $this_user) { if (strcasecmp($this_user -> username, $username) !== 0) { fwrite($h, $this_user -> __toString()); } } fclose($h); throw new ExceptionDisplay('User successfully removed.'); } /** * @param User $current_user This user is checked to make sure it really is an admin */ public function __construct(User $current_user) { if (!($current_user instanceof UserLoggedIn)) { throw new ExceptionDisplay('You must be logged in to access this section.'); } $this -> level = $current_user -> level; $this -> username = $current_user -> username; } /** * @param string $action */ public function action($action) { //This is a list of the actions moderators can do (otherwise, the user must be an admin) $mod_actions = array('edit_description', 'change_password', 'ftp'); if (in_array(strtolower($action), $mod_actions)) { if ($this -> level < MODERATOR) { throw new ExceptionDisplay('You must be a moderator to access this section.'); } } else if ($this -> level < ADMIN) { throw new ExceptionDisplay('You must be an administrator to access this section.'); } switch (strtolower($action)) { case 'config': { /** Include the config generator file. */ if (!@include_once(CONFIG_GENERATOR)) { throw new ExceptionDisplay('Error including file ' . CONFIG_GENERATOR . ''); } die(); } case 'rename': { if (!isset($_GET['filename'])) { throw new ExceptionDisplay('No filenames specified.'); } global $dir; $old = $dir . Url::clean_input($_GET['filename']); if (!@file_exists($old)) { header('HTTP/1.0 404 Not Found'); throw new ExceptionDisplay('Specified file could not be found.'); } if (isset($_GET['new_name'])) { $new = $dir . Url::clean_input($_GET['new_name']); if ($old == $new) { throw new ExceptionDisplay('Filename unchanged.'); } if (@file_exists($new)) { throw new ExceptionDisplay('Cannot overwrite existing file.'); } if (@rename($old, $new)) { global $config; if (DOWNLOAD_COUNT) { self::update_file_info($config -> __get('download_count'), $old, $new); } if (DESCRIPTION_FILE) { self::update_file_info($config -> __get('description_file'), $old, $new); } throw new ExceptionDisplay('File renamed successfully.'); } throw new ExceptionDisplay('Error renaming file.'); } global $words, $subdir; throw new ExceptionDisplay('

' . $words -> __get('renaming') . ' ' . Url::html_output($_GET['filename']) . '

' . $words -> __get('new filename') . ':
(' . $words -> __get('you can also move the file by specifying a path') . ')

' . '' . '' . '

'); } case 'delete': { if (!isset($_GET['filename'])) { throw new ExceptionDisplay('No filename specified.'); } if (isset($_GET['sure'])) { global $dir; $to_delete = $dir . Url::clean_input($_GET['filename']); if (!@file_exists($to_delete)) { header('HTTP/1.0 404 Not Found'); throw new ExceptionDisplay('Specified file could not be found.'); } if (@is_dir($to_delete)) { if (self::rmdir_recursive($to_delete)) { throw new ExceptionDisplay('Folder successfully deleted.'); } throw new ExceptionDisplay('Error deleting folder.'); } if (@unlink($to_delete)) { throw new ExceptionDisplay('File successfully deleted.'); } throw new ExceptionDisplay('Error deleting file.'); } global $words, $subdir; throw new ExceptionDisplay('

' . $words -> __get('are you sure you want to delete the file') . ' ' . Url::html_output($_GET['filename']) . '?

' . '

' . '' . '

'); } case 'add_user': { if (isset($_POST['username'], $_POST['pass1'], $_POST['pass2'], $_POST['level'], $_POST['home_dir'])) { self::add_user($_POST['username'], $_POST['pass1'], $_POST['pass2'], (int)$_POST['level'], $_POST['home_dir']); } global $words; throw new ExceptionDisplay($words -> __get('add user') . ':

' . $words -> __get('username') . ':
' . $words -> __get('password') . ':
' . $words -> __get('password') . ':
' . $words -> __get('level') . ':

Home Directory: ' . '
(leave blank to use the default base directory)

'); } case 'change_password': { if (isset($_POST['pass1'], $_POST['pass2'], $_POST['old_pass'])) { self::change_password($this -> username, $_POST['old_pass'], $_POST['pass1'], $_POST['pass2']); } throw new ExceptionDisplay('

Old password:
New password:
New password:

'); } case 'change_user_level': { if (isset($_POST['username'], $_POST['level'])) { self::change_user_level($_POST['username'], (int)$_POST['level']); } $accounts = new Accounts(); $out = '

Select user:

Select new level:

'); } case 'del_user': { if (isset($_POST['username'])) { if (isset($_POST['sure'])) { self::del_user($_POST['username']); } global $words; throw new ExceptionDisplay('

' . $words -> __get('are you sure you want to remove the user') . ' '.$_POST['username'] . '?

' . '
' . '

'); } global $words; $accounts = new Accounts(); $out = '

' . $words -> __get('select user to remove') . ':

'); } case 'edit_description': { if (isset($_GET['filename'])) { global $dir; $filename = $dir . $_GET['filename']; if (isset($_GET['description'])) { global $descriptions, $config; if (DESCRIPTION_FILE && $descriptions -> is_set($filename)) //if it's already set, update the old description { //update the new description on disk $h = @fopen($config -> __get('description_file'), 'wb'); if ($h === false) { throw new ExceptionDisplay('Could not open description file for writing.' . ' Make sure PHP has write permission to this file.'); } foreach ($descriptions as $file => $info) { fwrite($h, "$file\t" . (($file == $filename) ? $_GET['description'] : $info) . "\n"); } fclose($h); //update the new description in memory $descriptions -> set($filename, $_GET['description']); } else if ($_GET['description'] != '') //if it's not set, add it to the end { $h = @fopen($config -> __get('description_file'), 'ab'); if ($h === false) { throw new ExceptionDisplay('Could not open description file for writing.' . ' Make sure PHP has write permission to this file.'); } fwrite($h, "$filename\t" . $_GET['description'] . "\n"); fclose($h); //read the description file with the updated data $descriptions = new ConfigData($config -> __get('description_file')); } } else { global $words, $subdir, $descriptions; $current_desc = (DESCRIPTION_FILE && $descriptions -> is_set($filename) ? $descriptions -> __get($filename) : ''); throw new ExceptionDisplay('

' . $words -> __get('enter the new description for the file') . ' ' . Url::html_output($_GET['filename']) . ':

' . '

'); } } else { throw new ExceptionDisplay('No filename specified.'); } break; } case 'edit_hidden': { if (!HIDDEN_FILES) { throw new ExceptionDisplay('The file hiding system is not in use. To enable it, reconfigure the script.'); } global $hidden_list; if (isset($_GET['add']) && $_GET['add'] != '') { global $config; $h = @fopen($config -> __get('hidden_files'), 'ab'); if ($h === false) { throw new ExceptionDisplay('Unable to open hidden files list for writing.'); } fwrite($h, $_GET['add'] . "\n"); fclose($h); throw new ExceptionDisplay('Hidden file added.'); } if (isset($_GET['remove'])) { global $config; $h = @fopen($config -> __get('hidden_files'), 'wb'); if ($h === false) { throw new ExceptionDisplay('Unable to open hidden files list for writing.'); } foreach ($hidden_list as $hid) { if ($hid != $_GET['remove']) { fwrite($h, $hid . "\n"); } } fclose($h); throw new ExceptionDisplay('Hidden file removed.'); } global $words; $str = '

' . $words -> __get('add a new hidden file') . ':

' . '

You can also use wildcards (?, *, +) for each entry.
' . 'If you want to do the opposite of "hidden files" - show only certain files - ' . 'put a colon in front of those entries.

' . '

'; $str .= '

' . $words -> __get('remove a hidden file') . ':

'; throw new ExceptionDisplay($str); } case 'edit_banned': { if (!BANNED_LIST) { throw new ExceptionDisplay('The banning system is not in use. To enable it, reconfigure the script.'); } if (isset($_GET['add']) && $_GET['add'] != '') { global $config; $h = @fopen($config -> __get('banned_list'), 'ab'); if ($h === false) { throw new ExceptionDisplay('Unable to open banned_list for writing.'); } fwrite($h, $_GET['add'] . "\n"); fclose($h); throw new ExceptionDisplay('Ban added.'); } if (isset($_GET['remove'])) { global $b_list, $config; $h = @fopen($config -> __get('banned_list'), 'wb'); if ($h === false) { throw new ExceptionDisplay('Unable to open banned_list for writing.'); } foreach ($b_list as $ban) { if ($ban != $_GET['remove']) { fwrite($h, $ban . "\n"); } } fclose($h); throw new ExceptionDisplay('Ban removed.'); } global $b_list, $words; $str = '

' . $words -> __get('add a new ban') . ':

' . '

'; $str .= '

' . $words -> __get('remove a ban') . ':

'; throw new ExceptionDisplay($str); } case 'stats': { if (!LOG_FILE) { throw new ExceptionDisplay('The logging system has not been enabled.'); } $stats = new Stats(); $stats -> display(); break; } case 'view_log': { if (!LOG_FILE) { throw new ExceptionDisplay('The logging system has not been enabled.'); } global $log; if (isset($_GET['num'])) { $log -> display((int)$_GET['num']); } global $words; throw new ExceptionDisplay($words -> __get('how many entries would you like to view') . '?
' . '
'); } case 'create_dir': { if (isset($_GET['name'])) { global $dir; if (!self::mkdir_recursive($dir . $_GET['name'])) { throw new ExceptionDisplay('Error creating new folder.'); } } else { global $words, $subdir; throw new ExceptionDisplay('

' . $words -> __get('enter the new name') . ':

' . '

'); } break; } case 'copy_url': { if (isset($_GET['protocol'], $_GET['copy_file'])) { self::copy_remote_file(rawurldecode($_GET['protocol']), rawurldecode($_GET['copy_file'])); throw new ExceptionDisplay('Copy was successful.'); } global $dir; $text = '

Enter the name of the remote file you would like to copy:

http://
ftp://

'; echo new Display($text); die(); } case 'ftp': { if (isset($_POST['host'], $_POST['port'], $_POST['directory'], $_POST['ftp_username'], $_POST['ftp_password'])) { if ($_POST['host'] == '') { throw new ExceptionDisplay('Please go back and enter a hostname.'); } if ($_POST['ftp_username'] == '' && $_POST['ftp_password'] == '') //anonymous login { $_POST['ftp_username'] = 'anonymous'; $_POST['ftp_password'] = 'autoindex@sourceforge.net'; } if ($_POST['directory'] == '') { $_POST['directory'] = './'; } if ($_POST['port'] == '') { $_POST['port'] = 21; } $_SESSION['ftp'] = array( 'host' => $_POST['host'], 'port' => (int)$_POST['port'], 'directory' => Item::make_sure_slash($_POST['directory']), 'username' => $_POST['ftp_username'], 'password' => $_POST['ftp_password'], 'passive' => isset($_POST['passive']) ); } if (isset($_GET['set_dir'])) { $_SESSION['ftp']['directory'] = $_GET['set_dir']; } global $subdir; if (isset($_GET['ftp_logout'])) { unset($_SESSION['ftp']); $text = '

Logout successful. Go back.

'; } else if (isset($_SESSION['ftp'])) { try { $ftp = new Ftp($_SESSION['ftp']['host'], $_SESSION['ftp']['port'], $_SESSION['ftp']['passive'], $_SESSION['ftp']['directory'], $_SESSION['ftp']['username'], $_SESSION['ftp']['password']); } catch (ExceptionFatal $e) { unset($_SESSION['ftp']); throw $e; } if (isset($_GET['filename']) && $_GET['filename'] != '') //transfer local to FTP { global $dir; $name = rawurldecode($_GET['filename']); $ftp -> put_file($dir . $name, Item::get_basename($name)); throw new ExceptionDisplay('File successfully transferred to FTP server.'); } if (isset($_GET['transfer']) && $_GET['transfer'] != '') //transfer FTP to local { global $dir; $name = rawurldecode($_GET['transfer']); $ftp -> get_file($dir . Item::get_basename($name), $name); throw new ExceptionDisplay('File successfully transferred from FTP server.'); } global $words; $text = '

Logout of FTP server
Back to index.

'; } else { $text = '

FTP server: port
Passive Mode

Username:
Password: (Leave these blank to login anonymously)

Directory:

Back to index.

'; } echo new Display($text); die(); } default: { throw new ExceptionDisplay('Invalid admin action.'); } } } /** * @return string The HTML text that makes up the admin panel */ public function __toString() { global $words, $subdir; $str = ''; //only ADMIN accounts if ($this -> level >= ADMIN) $str = '

' . $words -> __get('reconfigure script') . '

' . $words -> __get('edit list of hidden files') . '
' . $words -> __get('edit ban list') . '

' . $words -> __get('create new directory in this folder') . '
' . $words -> __get('copy url') . '

' . $words -> __get('view entries from log file') . '
' . $words -> __get('view statistics from log file') . '

' . $words -> __get('add new user') . '
' . $words -> __get('delete user') . '
Change a user\'s level

'; //MODERATOR and ADMIN accounts if ($this -> level >= MODERATOR) $str .= '

Change your password

FTP browser

'; return $str; } } ?>