GitLab URL: https://gitlab.com/windowssnt/PHPFileManager. Feel free to contribute!
Introduction
I've tried a few file managers but all seem very complex. Why not making my own one?
- Single file. Just copy phpfm.php to your root directory and that's all.
- Bootstrap, jQuery, BootBox, Font Awesome, Chosen, Data Tables
- Login with HTTP digest if over HTTP and with form if over HTTPS.
- Multiple users supported with specific root folders and read/write access options per folder.
- Save users in self or in SQLite database.
- Create folder.
- Upload file.
- Upload multiple files in zip.
- Download file.
- Download multiple files in zip.
- Delete file/files.
Login
If you are in a HTTPS connection, the login is a simple bootstrap form:
For the first login, login as root without password.
The passwords are saved in sha-1
hash form and a logout option is provided (which terminates the session).
If you are in an HTTP connection, the passwords are saved in plain text but the authentication method is HTTP digest, in order to avoid transmitting the raw password.
$users Array
$users = array('root' => array('admin' => '1','password' => '',
'root' => '.','access' => array('.' => 2)))
The "access
" array is itself an array which contains pairs of directories and access levels (1 read, 2 write).
For security, PHPFM never allows access to any file that contains "..
" or starts with "/
".
If saving the users to self, a simple function is used to serialize the users
array:
function LoadUsers()
{
global $users;
if (count($users) == 1 && $users['root']['password'] == "")
return "\$users = array();\r\n";
$u = "\$users = array(\r\n";
$i = 0;
foreach($users as $username => $user)
{
if ($i > 0)
$u .= ",\r\n";
$acc = "array(";
$ii = 0;
foreach($user['access'] as $acxd => $acx)
{
if ($ii > 0)
$acc .= ",";
$acc .= sprintf("'%s' => %s",$acxd,$acx);
$ii++;
}
$acc .= ")";
$u .= sprintf("'%s' => array('admin' => '%s','password' =>
'%s','root' => '%s','access' => %s)",
$username,$user['admin'],$user['password'],$user['root'],$acc);
$i++;
}
$u .= "\r\n);";
return $u;
}
If saving to a database, SQLite is used. Once you have created a user (or changed the root password), you cannot anymore switch between the two methods.
Interface
File or Zip Upload
A simple PHP $_FILE
form is used:
if (array_key_exists("zip",$_POST) && $_POST['zip'] == 1)
{
$zip = new ZipArchive;
$zip->open($tempfile);
$nf = $zip->numFiles;
for($i = 0; $i < $nf ; $i++)
{
$fn2 = $zip->getNameIndex($i);
if (strstr($fn2,"..") !== false || strstr($fn2,"/") === $fn2)
{
unlink($tempfile);
$_SESSION['error'] = "File contains files with .. or /, not allowed.";
die;
}
}
$zip->extractTo($cr);
}
else
{
$dbdata = file_get_contents($tempfile);
$full = $_SESSION['current'].'/'.$fn;
file_put_contents($full,$dbdata);
}
unlink($tempfile);
File or Zip Download
Either a direct application/octet-stream
is used for a single file:
header('Content-Description: Download');
header('Content-Type: application/octet-stream');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header(sprintf("Content-disposition: attachment;filename=%s",basename($u)));
header('Content-Length: ' . filesize($u));
readfile($u);
or a ZIP download, checking also permissions:
$what = $_POST['massdownload'];
$fuf = "";
$dp = strrpos($_POST['massdownload'],"/");
if ($dp === false)
$fuf = $_SESSION['current'].'/';
else
$fuf = substr($_GET['down'],0,$dp);
if (AccessType($fuf) === 0)
{
$_SESSION['error'] = "Read access denied to folder <b>$root</b>.";
}
else
{
$arr = array();
$items = explode(',',$what);
foreach($items as $item)
{
if (strstr($item,"/") == $item)
die;
if (strstr($item,"..") !== false)
die;
$full = $_SESSION['current'].'/'.$item;
enumDir($full,$arr);
}
$tz = tempnam(".","zip");
if (file_exists($tz))
unlink($tz);
$tz .= ".zip";
if (file_exists($tz))
unlink($tz);
$zip = new ZipArchive;
$zipo = $zip->open($tz,ZipArchive::CREATE | ZipArchive::OVERWRITE);
foreach($arr as $a)
{
if (is_dir($a))
continue;
$rs = $zip->addFile($a,substr($a,2));
if (!$rs)
die;
}
$zip->close();
Down($tz,1);
unlink($tz);
die;
}
Have fun!
History
- 23rd January, 2017: First release