AUTH_SESSION_TTL) return false;
$_SESSION['nfm_last'] = time();
return true;
}
function do_login($u, $p){
if ($u === AUTH_USERNAME && make_pwd_hash($p) === AUTH_PASSWORD_MD5){
$_SESSION = array();
session_regenerate_id(true);
$_SESSION['nfm_user'] = $u;
$_SESSION['nfm_last'] = time();
return true;
}
return false;
}
function do_logout(){
$_SESSION = array();
if (ini_get('session.use_cookies')) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time()-42000, $params['path'], $params['domain'], !empty($params['secure']), !empty($params['httponly']));
}
session_destroy();
}
/* ===================== DEV HELPER: BUAT HASH PASSWORD (MD5) ===================== */
if (isset($_GET['a']) && $_GET['a']==='makehash') {
$err = '';
if ($_SERVER['REQUEST_METHOD']==='POST'){
$p = isset($_POST['p'])?$_POST['p']:'';
if ($p===''){ $err='Masukkan password'; }
else {
$h = make_pwd_hash($p);
header('Content-Type: text/plain; charset=utf-8');
echo "MD5 (tempel ke AUTH_PASSWORD_MD5):\n".$h."\n";
exit;
}
}
?>
Buat Hash Password (MD5)
Buat Hash Password (MD5)
'.$err.''; } ?>
Setelah dapat hash, tempel ke konstanta AUTH_PASSWORD_MD5 pada file ini.
Catatan: MD5 tidak direkomendasikan untuk sistem publik.
absolut berbasis $base (cwd) */
function resolvePath($candidate, $base){
$c = trim((string)$candidate);
if ($c === '') return $base;
if (isWindows()){
if (preg_match('#^[A-Za-z]:\\\\#',$c) || strpos($c,'\\\\')===0) return $c; // absolut
return rtrim($base, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$c;
} else {
if (strpos($c,'/') === 0) return $c; // absolut
return rtrim($base, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$c;
}
}
function isTextFile($path){
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
$textExt = array('txt','md','log','json','xml','yml','yaml','csv','ini','cfg','conf','php','js','ts','css','scss','less','html','htm','sh','bat','py','java','kt','c','cpp','cs','go','rb','lua','sql');
if (in_array($ext, $textExt, true)) return true;
if (is_file($path) && is_readable($path) && filesize($path) <= MAX_EDIT_BYTES){
$sample = @file_get_contents($path, false, null, 0, 1024);
if ($sample === false) return false;
return (strpos($sample, "\0") === false);
}
return false;
}
function isImageFile($path){
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
return in_array($ext, array('png','jpg','jpeg','gif','webp','bmp','svg'), true);
}
function bytesHuman($b){
$u = array('B','KB','MB','GB','TB'); $i=0;
while ($b>=1024 && $igetPathname(), strlen($src)+1);
$to = rtrim($dst, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$rel;
if ($f->isDir()){
if (!is_dir($to)) @mkdir($to, 0775, true);
} else {
@copy($f->getPathname(), $to);
}
}
return true;
}
function h($s){ return htmlspecialchars($s,ENT_QUOTES,'UTF-8'); }
function q($s){ return rawurlencode($s); }
function breadcrumbAbs($path){
if (isWindows()){
$p = rtrim($path, DIRECTORY_SEPARATOR);
if (preg_match('#^([A-Za-z]:)\\\\?#', $p, $m)) {
$drive = $m[1];
$rest = trim(substr($p, strlen($drive)), "\\/");
$parts = $rest !== '' ? explode('\\', $rest) : array();
$crumbs = array(''.h($drive.'\\').'');
$accum = $drive.'\\';
foreach($parts as $seg){
$accum = rtrim($accum, '\\').'\\'.$seg;
$crumbs[] = ''.h($seg).'';
}
return implode(' / ', $crumbs);
} else {
$rest = ltrim($p, "\\");
$parts = $rest!=='' ? explode('\\', $rest) : array();
$accum = '\\\\';
$crumbs = array(''.h('\\\\').'');
foreach($parts as $seg){
$accum = rtrim($accum, '\\').'\\'.$seg;
$crumbs[] = ''.h($seg).'';
}
return implode(' / ', $crumbs);
}
} else {
$p = rtrim($path, '/'); if ($p === '') $p = '/';
if ($p === '/') return '/';
$rel = ltrim($p, '/'); $parts = $rel !== '' ? explode('/', $rel) : array();
$crumbs = array('/'); $accum = '';
foreach($parts as $seg){ $accum .= '/'.$seg; $crumbs[] = ''.h($seg).''; }
return implode(' / ', $crumbs);
}
}
/* ===================== ROUTING LOGIN ===================== */
$action = isset($_GET['a'])?$_GET['a']:'';
if ($action === 'logout'){ do_logout(); header('Location: ?'); exit; }
if (AUTH_ENABLED && !is_logged_in()){
$err = '';
if ($action==='login' && $_SERVER['REQUEST_METHOD']==='POST'){
$u = isset($_POST['u'])?$_POST['u']:'';
$p = isset($_POST['p'])?$_POST['p']:'';
if (do_login($u,$p)){
$redir = isset($_GET['p']) ? '?p='.q($_GET['p']) : '?';
header('Location: '.$redir); exit;
} else {
$err = 'Username atau password salah.';
}
}
?>
– Login
'.h($err).''; } ?>
Buat hash MD5 di ?a=makehash, lalu tempel ke file.
'text/plain','log'=>'text/plain','md'=>'text/markdown','json'=>'application/json',
'xml'=>'application/xml','svg'=>'image/svg+xml','png'=>'image/png','jpg'=>'image/jpeg',
'jpeg'=>'image/jpeg','gif'=>'image/gif','webp'=>'image/webp','css'=>'text/css','js'=>'application/javascript','html'=>'text/html','htm'=>'text/html'
);
$ct = isset($map[$ext])?$map[$ext]:'application/octet-stream';
header('Content-Type: '.$ct.'; charset=utf-8');
header('Content-Length: '.filesize($path));
readfile($path); exit;
}
if ($action==='zip' && $_SERVER['REQUEST_METHOD']==='POST' && ALLOW_ZIP && is_dir($cwd) && is_writable($cwd)){
$items = isset($_POST['items'])?$_POST['items']:array();
$zipName = trim(isset($_POST['zipname'])?$_POST['zipname']:'archive_'.date('Ymd_His').'.zip');
if (!$items) exit('Tidak ada item dipilih.');
$zipPath = rtrim($cwd, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$zipName;
if (file_exists($zipPath)) exit('Nama arsip sudah ada.');
if (class_exists('ZipArchive')){
$zip = new ZipArchive();
if ($zip->open($zipPath, ZipArchive::CREATE)!==TRUE) exit('Gagal membuat ZIP.');
foreach($items as $rel){
$abs = rtrim($cwd, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$rel;
$abs = safePath($abs);
if (is_dir($abs) && is_readable($abs)){
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($abs, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
$base = basename($abs);
foreach($it as $f){
$local = $base.'/'.substr($f->getPathname(), strlen($abs)+1);
$local = str_replace(DIRECTORY_SEPARATOR,'/',$local);
if ($f->isDir()) $zip->addEmptyDir($local);
else $zip->addFile($f->getPathname(), $local);
}
} elseif (is_file($abs) && is_readable($abs)){
$zip->addFile($abs, basename($abs));
}
}
$zip->close();
header('Location: ?p='.rawurlencode($cwd).'&msg='.rawurlencode('ZIP dibuat: '.$zipName)); exit;
} else exit('ZipArchive tidak tersedia.');
}
if ($action==='unzip' && $_SERVER['REQUEST_METHOD']==='POST' && ALLOW_UNZIP){
$zipIn = isset($_POST['zipfile']) ? $_POST['zipfile'] : '';
$toIn = isset($_POST['todir']) ? $_POST['todir'] : '';
// perbaikan: relatif dari $cwd
$zipFile = safePath(resolvePath($zipIn, $cwd));
$toDir = safePath(resolvePath($toIn !== '' ? $toIn : $cwd, $cwd));
if (!is_readable($zipFile)) exit('File arsip tidak dapat dibaca.');
if (!is_dir($toDir)) @mkdir($toDir,0775,true);
if (!is_dir($toDir) || !is_writable($toDir)) exit('Tujuan tidak bisa dipakai.');
$done=false;
if (class_exists('ZipArchive') && strtolower(pathinfo($zipFile,PATHINFO_EXTENSION))==='zip'){
$zip=new ZipArchive();
if($zip->open($zipFile)===TRUE){ $zip->extractTo($toDir); $zip->close(); $done=true; }
}
if(!$done && class_exists('PharData')){
try{ $phar=new PharData($zipFile); $phar->extractTo($toDir,null,true); $done=true; }catch(Exception $e){}
}
if(!$done) exit('Gagal extract (ZipArchive/PharData tidak tersedia/format tidak didukung).');
header('Location: ?p='.rawurlencode($toDir).'&msg='.rawurlencode('Unzip berhasil')); exit;
}
if ($action==='upload' && $_SERVER['REQUEST_METHOD']==='POST'){
// SELALU ke folder aktif
$dest = is_dir($cwd) ? $cwd : START_DIR;
if (!is_writable($dest)) exit('Folder tujuan tidak writable.');
$notif=array();
if (!empty($_FILES['files'])){
$files = array();
if (is_array($_FILES['files']['name'])){
$count = count($_FILES['files']['name']);
for($i=0;$i<$count;$i++){
$files[] = array(
'name' => $_FILES['files']['name'][$i],
'tmp' => $_FILES['files']['tmp_name'][$i],
'err' => $_FILES['files']['error'][$i],
);
}
} else {
$files[] = array(
'name' => $_FILES['files']['name'],
'tmp' => $_FILES['files']['tmp_name'],
'err' => $_FILES['files']['error'],
);
}
foreach($files as $f){
$name=$f['name']; $tmp=$f['tmp']; $err=$f['err'];
if ($err!==UPLOAD_ERR_OK){ $notif[]="❌ {$name} (error {$err})"; continue; }
$target = uniqueName($dest, $name);
$in = @fopen($tmp, 'rb');
if (!$in){ $notif[]="❌ gagal buka tmp untuk {$name}"; continue; }
$out = @fopen($target, 'xb');
if (!$out){ fclose($in); $notif[]="❌ gagal tulis {$name}"; continue; }
$ok = stream_copy_to_stream($in, $out) !== false;
fclose($in); fclose($out);
if (!$ok){ @unlink($target); $notif[]="❌ gagal salin {$name}"; continue; }
@chmod($target, 0664);
$notif[] = "✅ ".basename($target);
}
}
header('Location: ?p='.rawurlencode($dest).'&msg='.rawurlencode(implode(' | ',$notif))); exit;
}
if ($action==='newfile' && $_SERVER['REQUEST_METHOD']==='POST'){
$dir = $cwd;
if (!is_dir($dir) || !is_writable($dir)) exit('Folder tujuan tidak writable.');
$name = trim(isset($_POST['name'])?$_POST['name']:'');
$content = isset($_POST['content'])?$_POST['content']:'';
if ($name==='') exit('Nama file kosong.');
$target=uniqueName($dir,$name);
if (@file_put_contents($target,$content)===false) exit('Gagal membuat file.');
header('Location: ?p='.rawurlencode($target).'&msg='.rawurlencode('File dibuat.')); exit;
}
if ($action==='newdir' && $_SERVER['REQUEST_METHOD']==='POST'){
$dir = $cwd;
if (!is_dir($dir) || !is_writable($dir)) exit('Folder tujuan tidak writable.');
$name=trim(isset($_POST['name'])?$_POST['name']:'');
if ($name==='') exit('Nama folder kosong.');
$target=rtrim($dir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$name;
if (file_exists($target)) exit('Folder sudah ada.');
if (!@mkdir($target,0775,true)) exit('Gagal membuat folder.');
header('Location: ?p='.rawurlencode($target).'&msg='.rawurlencode('Folder dibuat.')); exit;
}
if ($action==='rename' && $_SERVER['REQUEST_METHOD']==='POST'){
$old = safePath(isset($_POST['old'])?$_POST['old']:'');
$newBase = trim(isset($_POST['new'])?$_POST['new']:'');
// Sanitasi: hapus path separator agar tetap di folder yang sama
$newBase = preg_replace('#[\\\\/]+#','', $newBase);
if ($newBase==='') exit('Nama baru kosong / tidak valid.');
$dstDir = dirname($old);
if (!is_writable($old)) exit('Sumber tidak writable.');
if (!is_writable($dstDir)) exit('Folder tujuan tidak writable.');
$candidate = rtrim($dstDir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$newBase;
$new = file_exists($candidate) ? uniqueName($dstDir, $newBase) : $candidate;
if (!@rename($old,$new)) exit('Gagal rename.');
header('Location: ?p='.rawurlencode($new).'&msg='.rawurlencode('Rename berhasil.')); exit;
}
function rrmdir($dir){
if (!is_dir($dir)) return false;
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
foreach($it as $f){ if ($f->isDir()) @rmdir($f->getPathname()); else @unlink($f->getPathname()); }
return @rmdir($dir);
}
if ($action==='delete' && $_SERVER['REQUEST_METHOD']==='POST'){
$target=safePath(isset($_POST['target'])?$_POST['target']:''); $ok=false;
if (is_dir($target)) $ok=rrmdir($target);
elseif (is_file($target)) $ok=@unlink($target);
header('Location: ?p='.rawurlencode(dirname($target)).'&msg='.rawurlencode($ok?'Terhapus.':'Gagal hapus.')); exit;
}
if ($action==='save' && $_SERVER['REQUEST_METHOD']==='POST'){
$file=safePath(isset($_POST['file'])?$_POST['file']:''); $content=isset($_POST['content'])?$_POST['content']:'';
if (!is_file($file) || !is_writable($file)) exit('File tidak bisa ditulis.');
if (strlen($content) > MAX_EDIT_BYTES) exit('Konten terlalu besar.');
$ok = (@file_put_contents($file,$content)!==false);
header('Location: ?p='.rawurlencode($file).'&msg='.rawurlencode($ok?'Tersimpan.':'Gagal simpan.')); exit;
}
if ($action==='chmod' && $_SERVER['REQUEST_METHOD']==='POST' && ALLOW_CHMOD){
$target=safePath(isset($_POST['target'])?$_POST['target']:''); $mode=isset($_POST['mode'])?$_POST['mode']:'';
if (!preg_match('/^[0-7]{3,4}$/',$mode)) exit('Mode tidak valid.');
$ok=@chmod($target, octdec($mode));
header('Location: ?p='.rawurlencode($target).'&msg='.rawurlencode($ok?'CHMOD ok.':'CHMOD gagal.')); exit;
}
if ($action==='move' && $_SERVER['REQUEST_METHOD']==='POST'){
$src = safePath(isset($_POST['src'])?$_POST['src']:'');
$dstDir = safePath(isset($_POST['dst'])?$_POST['dst']:'');
if (!file_exists($src)) exit('Sumber tidak ditemukan.');
if (!is_dir($dstDir)) exit('Tujuan harus folder.');
if (!is_writable($dstDir)) exit('Tujuan tidak writable.');
if (is_dir($src) && isDescendantPath($src, $dstDir)) exit('Tidak boleh memindah folder ke dalam dirinya sendiri / turunannya.');
$target = uniqueName($dstDir, basename($src));
$ok = @rename($src, $target);
if (!$ok){ $ok = (is_dir($src)?(copyDir($src, $target) && rrmdir($src)):(@copy($src, $target) && @unlink($src))); }
header('Location: ?p='.rawurlencode($ok?$target:$src).'&msg='.rawurlencode($ok?'Dipindah.':'Gagal pindah.')); exit;
}
/* ===== Listing ===== */
$sortBy = isset($_GET['sort'])?$_GET['sort']:'name';
$sortDir = isset($_GET['dir'])?$_GET['dir']:'asc';
$keyword = ($action==='search' && ALLOW_SEARCH)?trim(isset($_GET['q'])?$_GET['q']:''):'';
$items = array();
if (is_dir($cwd) && is_readable($cwd)){
if ($action==='search' && $keyword!==''){
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($cwd, FilesystemIterator::SKIP_DOTS));
foreach($it as $fi){
$p = $fi->getPathname();
if (stripos($p,$keyword)!==false){
$items[] = array('name'=>$fi->getFilename(),'path'=>$p,'is_dir'=>$fi->isDir(),'size'=>$fi->isFile()?$fi->getSize():0,'mtime'=>$fi->getMTime());
}
}
} else {
$scan=@scandir($cwd);
if ($scan){
foreach($scan as $n){
if ($n==='.'||$n==='..') continue;
$p=rtrim($cwd, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$n;
$items[] = array('name'=>$n,'path'=>$p,'is_dir'=>@is_dir($p),'size'=>@is_file($p)?@filesize($p):0,'mtime'=>@filemtime($p)?@filemtime($p):0);
}
}
}
$items = sortItems($items,$sortBy,$sortDir);
}
$authUser = AUTH_ENABLED ? AUTH_USERNAME : 'guest';
?>
'.h((string)$_GET['msg']).'
'):''; ?>
Pratinjau & Edit: (read-only)'; ?>