8 * 1024 * 1024) { echo "Arquivo grande demais para ver. Faça o download."; exit; } readfile($fp); exit; } $errors[] = 'Arquivo não encontrado ou não é texto.'; } // ---------- AÇÕES (POST) ---------- if ($is_write) { $action = $_POST['action']; if ($action === 'mkdir') { $name = basename($_POST['folder_name'] ?? ''); if ($name === '' || $name === '.' || $name === '..') $errors[] = 'Nome de pasta inválido.'; else { $novo = $current_dir . $name; if (file_exists($novo)) $errors[] = 'Essa pasta já existe.'; elseif (@mkdir($novo, 0755)) $success = 'Pasta criada: ' . h($name); else $errors[] = 'Erro ao criar a pasta (permissão?).'; } } elseif ($action === 'delete') { $name = basename($_POST['item'] ?? ''); $ip = secure_path(($rel_dir ? $rel_dir . '/' : '') . $name, $base_real); if ($ip !== false && rtrim($ip, '/') . '/' !== $base_real && strpos(str_replace('\\','/',$ip), $base_real) === 0) { if (is_dir($ip)) { if (rdelete($ip)) $success = 'Pasta removida: ' . h($name); else $errors[] = 'Erro ao remover a pasta.'; } elseif (is_file($ip)) { if (@unlink($ip)) $success = 'Arquivo removido: ' . h($name); else $errors[] = 'Erro ao remover o arquivo.'; } else $errors[] = 'Item não encontrado.'; } else $errors[] = 'Operação não permitida.'; } elseif ($action === 'rename') { $de = basename($_POST['from'] ?? ''); $para = basename($_POST['to'] ?? ''); $src = secure_path(($rel_dir ? $rel_dir . '/' : '') . $de, $base_real); if ($para === '' || strpbrk($para, '/\\?%*:|"<>') !== false) $errors[] = 'Novo nome inválido.'; elseif ($src !== false && file_exists($src)) { $dst = $current_dir . $para; if (file_exists($dst)) $errors[] = 'Já existe um item com esse nome.'; elseif (@rename($src, $dst)) $success = 'Renomeado para ' . h($para); else $errors[] = 'Erro ao renomear.'; } else $errors[] = 'Item de origem não encontrado.'; } elseif ($action === 'copy') { $de = basename($_POST['source'] ?? ''); $para = basename($_POST['target'] ?? ''); $src = secure_path(($rel_dir ? $rel_dir . '/' : '') . $de, $base_real); if ($para === '') $errors[] = 'Nome de destino inválido.'; elseif ($src !== false && is_file($src)) { $dst = $current_dir . $para; if (file_exists($dst)) $errors[] = 'Destino já existe: ' . h($para); elseif (@copy($src, $dst)) $success = 'Copiado para ' . h($para); else $errors[] = 'Erro ao copiar.'; } else $errors[] = 'Origem inválida.'; } elseif ($action === 'upload_url') { $url = trim($_POST['url'] ?? ''); if ($url === '' || !filter_var($url, FILTER_VALIDATE_URL)) $errors[] = 'URL inválida.'; else { $host = parse_url($url, PHP_URL_HOST); if (preg_match('/^(localhost|127\.|0\.|::1|10\.|192\.168\.|169\.254\.)/i', (string)$host)) { $errors[] = 'URL não permitida.'; } else { $name = basename(parse_url($url, PHP_URL_PATH)) ?: ('download_' . time()); $dst = $current_dir . $name; $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION)); if ($allowed_types && $ext && !in_array($ext, $allowed_types, true)) $errors[] = 'Tipo não permitido: ' . h($ext); elseif (file_exists($dst)) $errors[] = 'Já existe: ' . h($name); else { $ok = baixar_url($url, $dst, $max_size, $msg); if ($ok) $success = 'Baixado: ' . h($name); else $errors[] = 'Falha no download: ' . h($msg); } } } } elseif ($action === 'upload_local' && isset($_FILES['fileToUpload'])) { $f = $_FILES['fileToUpload']; $name = basename($f['name']); $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION)); $dst = $current_dir . $name; if (!is_uploaded_file($f['tmp_name'])) $errors[] = 'Upload inválido.'; elseif ($f['size'] > $max_size) $errors[] = 'Arquivo grande demais.'; elseif ($allowed_types && !in_array($ext, $allowed_types, true)) $errors[] = 'Tipo não permitido.'; elseif (file_exists($dst)) $errors[] = 'Já existe um arquivo com esse nome.'; elseif (move_uploaded_file($f['tmp_name'], $dst)) $success = 'Enviado: ' . h($name) . ' (' . fmt_size($f['size']) . ')'; else $errors[] = 'Erro ao salvar o upload.'; } elseif ($action === 'bulk_delete') { $sel = (array) ($_POST['file'] ?? []); $ok = 0; $fail = 0; foreach ($sel as $name) { $name = basename($name); $ip = secure_path(($rel_dir ? $rel_dir . '/' : '') . $name, $base_real); if ($ip !== false && rtrim($ip, '/') . '/' !== $base_real && strpos(str_replace('\\','/',$ip), $base_real) === 0) { if (rdelete($ip)) $ok++; else $fail++; } else $fail++; } if ($ok) $success = $ok . ' item(ns) excluído(s).' . ($fail ? ' ' . $fail . ' falharam.' : ''); elseif ($fail) $errors[] = 'Nada excluído.'; else $errors[] = 'Nada selecionado.'; } elseif ($action === 'bulk_zip') { $sel = array_values(array_filter(array_map('basename', (array) ($_POST['file'] ?? [])))); if (!$sel) $errors[] = 'Nada selecionado.'; elseif (!class_exists('ZipArchive')) $errors[] = 'Zip não disponível no servidor.'; else { $zipname = 'arquivos_' . date('ymd_His') . '.zip'; $zippath = $current_dir . $zipname; $zip = new ZipArchive(); if ($zip->open($zippath, ZipArchive::CREATE) !== true) $errors[] = 'Não consegui criar o zip (permissão?).'; else { foreach ($sel as $name) { $src = secure_path(($rel_dir ? $rel_dir . '/' : '') . $name, $base_real); if ($src === false) continue; if (is_file($src)) zip_store($zip, $src, $name); elseif (is_dir($src)) { $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($src, FilesystemIterator::SKIP_DOTS)); foreach ($it as $file) { if ($file->isFile()) { $local = $name . '/' . ltrim(str_replace($src, '', str_replace('\\','/',$file->getPathname())), '/'); zip_store($zip, $file->getPathname(), $local); } } } } $zip->close(); $success = 'Zip criado: ' . h($zipname); } } } } // Adiciona 1 arquivo ao zip; vídeo/imagem/compactado entram sem recomprimir (rápido). function zip_store($zip, $path, $local) { if (!$zip->addFile($path, $local)) return; static $stored = ['mp4','mkv','avi','mov','webm','ts','m4v','mp3','aac','ogg','flac','jpg','jpeg','png','gif','webp','zip','rar','7z','gz','apk','pdf']; $e = strtolower(pathinfo($path, PATHINFO_EXTENSION)); if (in_array($e, $stored, true) && method_exists($zip, 'setCompressionName')) { @$zip->setCompressionName($local, ZipArchive::CM_STORE); } } // ---------- FUNÇÕES AUXILIARES ---------- function h($s) { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); } function fmt_size($b) { $b = (float)$b; if ($b <= 0) return '0 B'; $u = ['B','KB','MB','GB','TB','PB']; $i = min(floor(log($b, 1024)), count($u) - 1); return round($b / pow(1024, $i), 2) . ' ' . $u[$i]; } function rdelete($path) { if (is_link($path) || is_file($path)) return @unlink($path); if (is_dir($path)) { foreach (array_diff(scandir($path), ['.','..']) as $f) { if (!rdelete($path . '/' . $f)) return false; } return @rmdir($path); } return false; } function baixar_url($url, $dst, $max, &$msg) { $msg = ''; $out = @fopen($dst, 'wb'); if (!$out) { $msg = 'sem permissão de escrita'; return false; } $ch = curl_init($url); $total = 0; $aborted = false; curl_setopt_array($ch, [ CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 5, CURLOPT_TIMEOUT => 0, CURLOPT_CONNECTTIMEOUT => 20, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_USERAGENT => 'Mozilla/5.0', CURLOPT_WRITEFUNCTION => function ($ch, $data) use ($out, &$total, $max, &$aborted) { $total += strlen($data); if ($total > $max) { $aborted = true; return 0; } return fwrite($out, $data); }, ]); $ok = curl_exec($ch); $code = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE); $err = curl_error($ch); curl_close($ch); fclose($out); if ($aborted) { @unlink($dst); $msg = 'arquivo maior que o limite'; return false; } if ($ok === false || $code >= 400) { @unlink($dst); $msg = $err !== '' ? $err : ('HTTP ' . $code); return false; } return true; } // Download por partes, com retomada (Range) — não trava arquivo grande. function download_file($file, $name) { $size = filesize($file); $fp = fopen($file, 'rb'); if ($fp === false || $size === false) { http_response_code(404); exit; } @ini_set('zlib.output_compression', 'Off'); while (ob_get_level()) ob_end_clean(); ignore_user_abort(true); $start = 0; $end = $size - 1; $partial = false; if (isset($_SERVER['HTTP_RANGE']) && preg_match('/bytes=\s*(\d*)\s*-\s*(\d*)/i', $_SERVER['HTTP_RANGE'], $m)) { if ($m[1] === '' && $m[2] !== '') { $start = max(0, $size - (int)$m[2]); } else { if ($m[1] !== '') $start = (int)$m[1]; if ($m[2] !== '') $end = (int)$m[2]; } if ($start > $end || $start >= $size) { header('HTTP/1.1 416 Requested Range Not Satisfiable'); header("Content-Range: bytes */$size"); fclose($fp); exit; } $partial = true; } $length = $end - $start + 1; $ascii = preg_replace('/[^\x20-\x7E]/', '_', function_exists('iconv') ? (@iconv('UTF-8','ASCII//TRANSLIT//IGNORE',$name) ?: $name) : $name); $ascii = str_replace(['"','\\','~','^','`',"'"], '', $ascii); header('Content-Type: application/octet-stream'); header("Content-Disposition: attachment; filename=\"$ascii\"; filename*=UTF-8''" . rawurlencode($name)); header('Accept-Ranges: bytes'); header('X-Accel-Buffering: no'); if ($partial) { header('HTTP/1.1 206 Partial Content'); header("Content-Range: bytes $start-$end/$size"); } header('Content-Length: ' . $length); if ($start > 0) fseek($fp, $start); $left = $length; $chunk = 1024 * 1024; while ($left > 0 && !feof($fp) && connection_status() === CONNECTION_NORMAL) { $read = $left > $chunk ? $chunk : $left; $buf = fread($fp, $read); if ($buf === false) break; echo $buf; flush(); $left -= strlen($buf); } fclose($fp); } // ============================================================================ // PÁGINA PRINCIPAL (visual Bootstrap, estilo Tiny File Manager) // ============================================================================ $BOOT = 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css'; $FA = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css'; $BOOTJS = 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js'; $dirs = []; $files = []; $items = @scandir($current_dir); if ($items !== false) { foreach ($items as $it) { if ($it === '.' || $it === '..') continue; $full = $current_dir . $it; if (is_dir($full)) $dirs[] = $it; else $files[] = $it; } natcasesort($dirs); natcasesort($files); } $parent_rel = $display_rel !== '' ? trim(implode('/', array_slice(explode('/', $display_rel), 0, -1)), '/') : null; ?>
Painel de controle