$dir, 'created' => date('Y-m-d H:i', filectime($wsPath)), 'modified' => date('Y-m-d H:i', filemtime($wsPath)), 'size' => getDirectorySize($wsPath) ]; } } } echo json_encode(['workspaces' => $workspaces]); exit; } if ($action === 'view') { header('Content-Type: application/json'); $name = preg_replace('/[^a-zA-Z0-9\-\_\.]/', '', $_GET['name'] ?? ''); if (empty($name)) { echo json_encode(['error' => 'Invalid workspace name']); exit; } $wsPath = WORKSPACE_DIR . '/' . $name; if (!is_dir($wsPath)) { echo json_encode(['error' => 'Workspace not found']); exit; } // Check for sniper-report.html first $reportPath = $wsPath . '/sniper-report.html'; if (file_exists($reportPath)) { echo json_encode(['reportUrl' => '/loot/workspace/' . urlencode($name) . '/sniper-report.html']); } else { // Use our custom report viewer echo json_encode(['reportUrl' => 'report.php?name=' . urlencode($name)]); } exit; } if ($action === 'download') { $name = preg_replace('/[^a-zA-Z0-9\-\_\.]/', '', $_GET['name'] ?? ''); if (empty($name)) { die('Invalid workspace name'); } $exportFile = EXPORT_DIR . '/' . $name . '.tar.gz'; if (file_exists($exportFile)) { header('Content-Type: application/gzip'); header('Content-Disposition: attachment; filename="' . $name . '.tar.gz"'); header('Content-Length: ' . filesize($exportFile)); readfile($exportFile); exit; } else { die('Export file not found. Please export the workspace first.'); } } } // Handle POST requests (delete, export) if ($_SERVER['REQUEST_METHOD'] === 'POST') { header('Content-Type: application/json'); $data = json_decode(file_get_contents('php://input'), true); $action = $data['action'] ?? ''; $name = preg_replace('/[^a-zA-Z0-9\-\_\.]/', '', $data['name'] ?? ''); if (empty($name)) { echo json_encode(['success' => false, 'error' => 'Invalid workspace name']); exit; } $wsPath = WORKSPACE_DIR . '/' . $name; if ($action === 'delete') { if (is_dir($wsPath)) { // Delete directory recursively deleteDirectory($wsPath); echo json_encode(['success' => true, 'message' => 'Workspace deleted']); } else { echo json_encode(['success' => false, 'error' => 'Workspace not found']); } exit; } if ($action === 'export') { if (!is_dir($wsPath)) { echo json_encode(['success' => false, 'error' => 'Workspace not found']); exit; } $exportFile = EXPORT_DIR . '/' . $name . '.tar.gz'; // Create tar.gz archive $cmd = "cd " . escapeshellarg(WORKSPACE_DIR) . " && tar -czf " . escapeshellarg($exportFile) . " " . escapeshellarg($name) . " 2>&1"; $output = shell_exec($cmd); if (file_exists($exportFile)) { $size = filesize($exportFile); echo json_encode([ 'success' => true, 'downloadUrl' => 'workspaces.php?action=download&name=' . urlencode($name), 'filename' => $name . '.tar.gz', 'size' => formatBytes($size), 'message' => 'Export created successfully' ]); } else { echo json_encode([ 'success' => false, 'error' => 'Failed to create export: ' . $output ]); } exit; } } echo json_encode(['error' => 'Invalid request']); // Helper functions function getDirectorySize($dir) { $size = 0; foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)) as $file) { $size += $file->getSize(); } return formatBytes($size); } function formatBytes($bytes) { if ($bytes >= 1073741824) { return number_format($bytes / 1073741824, 2) . ' GB'; } elseif ($bytes >= 1048576) { return number_format($bytes / 1048576, 2) . ' MB'; } elseif ($bytes >= 1024) { return number_format($bytes / 1024, 2) . ' KB'; } else { return $bytes . ' B'; } } function deleteDirectory($dir) { if (!is_dir($dir)) return false; $files = array_diff(scandir($dir), ['.', '..']); foreach ($files as $file) { $path = $dir . '/' . $file; is_dir($path) ? deleteDirectory($path) : unlink($path); } return rmdir($dir); }