GitLab URL: https://gitlab.com/windowssnt/PHPFileManager. Feel free to contribute!
Introduction
I 've tried a few file managers but all seems 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 provied (which terminates the session).
If you are in a 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 dircetories 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
23 - 01 - 2017 : First Release