Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / PHP

PHPFM: A Single File, Responsive File Manager

4.88/5 (9 votes)
5 Feb 2017CPOL1 min read 38.6K   917  
A very quick file manager for your PHP site

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

PHP
$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:

PHP
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(";// array("." => 2)
        $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:

PHP
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:

PHP
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:

PHP
$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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)