diff --git a/src/main/java/cz/kamma/kfmanager/MainApp.java b/src/main/java/cz/kamma/kfmanager/MainApp.java index f5f728c..a4dbf29 100644 --- a/src/main/java/cz/kamma/kfmanager/MainApp.java +++ b/src/main/java/cz/kamma/kfmanager/MainApp.java @@ -15,7 +15,7 @@ import java.io.InputStreamReader; */ public class MainApp { - public static final String APP_VERSION = "1.0.0"; + public static final String APP_VERSION = "1.0.1"; public enum OS { WINDOWS, LINUX, MACOS, UNKNOWN diff --git a/src/main/java/cz/kamma/kfmanager/service/FileOperations.java b/src/main/java/cz/kamma/kfmanager/service/FileOperations.java index b96dbdc..cdc04ea 100644 --- a/src/main/java/cz/kamma/kfmanager/service/FileOperations.java +++ b/src/main/java/cz/kamma/kfmanager/service/FileOperations.java @@ -790,9 +790,14 @@ public class FileOperations { throw new IOException("Entry not found in archive: " + entryPath); } - private static boolean isArchiveFile(File f) { + public static boolean isArchiveFile(File f) { if (f == null) return false; - String n = f.getName().toLowerCase(); + return isArchiveFile(f.getName()); + } + + public static boolean isArchiveFile(String filename) { + if (filename == null) return false; + String n = filename.toLowerCase(); return n.endsWith(".war") || n.endsWith(".zip") || n.endsWith(".jar") || n.endsWith(".tar") || n.endsWith(".tar.gz") || n.endsWith(".tgz") || n.endsWith(".7z") || n.endsWith(".rar"); } @@ -989,6 +994,110 @@ public class FileOperations { } } + /** + * Extract an archive into a target directory + */ + public static void extractArchive(File archiveFile, File targetDirectory, ProgressCallback callback) throws Exception { + if (!targetDirectory.exists()) { + Files.createDirectories(targetDirectory.toPath()); + } + + String name = archiveFile.getName().toLowerCase(); + if (name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".war")) { + unzip(archiveFile, targetDirectory, callback); + } else if (name.endsWith(".tar.gz") || name.endsWith(".tgz")) { + extractTarGz(archiveFile, targetDirectory, callback); + } else if (name.endsWith(".tar")) { + extractTar(archiveFile, targetDirectory, callback); + } else if (name.endsWith(".7z")) { + extractSevenZ(archiveFile, targetDirectory, callback); + } else if (name.endsWith(".rar")) { + extractRar(archiveFile, targetDirectory, callback); + } else { + throw new IOException("Unsupported archive format: " + archiveFile.getName()); + } + } + + private static void extractTarGz(File archive, File targetDir, ProgressCallback callback) throws IOException { + try (InputStream fis = Files.newInputStream(archive.toPath()); + InputStream gzis = new org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream(fis); + org.apache.commons.compress.archivers.tar.TarArchiveInputStream tais = new org.apache.commons.compress.archivers.tar.TarArchiveInputStream(gzis)) { + extractTarInternal(tais, targetDir, callback); + } + } + + private static void extractTar(File archive, File targetDir, ProgressCallback callback) throws IOException { + try (InputStream fis = Files.newInputStream(archive.toPath()); + org.apache.commons.compress.archivers.tar.TarArchiveInputStream tais = new org.apache.commons.compress.archivers.tar.TarArchiveInputStream(fis)) { + extractTarInternal(tais, targetDir, callback); + } + } + + private static void extractTarInternal(org.apache.commons.compress.archivers.tar.TarArchiveInputStream tais, File targetDir, ProgressCallback callback) throws IOException { + org.apache.commons.compress.archivers.tar.TarArchiveEntry entry; + long current = 0; + while ((entry = tais.getNextTarEntry()) != null) { + current++; + if (callback != null) callback.onProgress(current, -1, entry.getName()); + + File newFile = new File(targetDir, entry.getName()); + if (entry.isDirectory()) { + newFile.mkdirs(); + } else { + newFile.getParentFile().mkdirs(); + Files.copy(tais, newFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + } + } + + private static void extractSevenZ(File archive, File targetDir, ProgressCallback callback) throws IOException { + try (org.apache.commons.compress.archivers.sevenz.SevenZFile sevenZFile = new org.apache.commons.compress.archivers.sevenz.SevenZFile(archive)) { + org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry entry; + long current = 0; + while ((entry = sevenZFile.getNextEntry()) != null) { + current++; + if (callback != null) callback.onProgress(current, -1, entry.getName()); + + File newFile = new File(targetDir, entry.getName()); + if (entry.isDirectory()) { + newFile.mkdirs(); + } else { + newFile.getParentFile().mkdirs(); + try (OutputStream os = Files.newOutputStream(newFile.toPath())) { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = sevenZFile.read(buffer)) != -1) { + os.write(buffer, 0, bytesRead); + } + } + } + } + } + } + + private static void extractRar(File archiveFile, File targetDir, ProgressCallback callback) throws Exception { + try (com.github.junrar.Archive archive = new com.github.junrar.Archive(archiveFile)) { + com.github.junrar.rarfile.FileHeader fh = archive.nextFileHeader(); + long current = 0; + while (fh != null) { + current++; + String entryName = fh.getFileName().replace('\\', '/'); + if (callback != null) callback.onProgress(current, -1, entryName); + + File newFile = new File(targetDir, entryName); + if (fh.isDirectory()) { + newFile.mkdirs(); + } else { + newFile.getParentFile().mkdirs(); + try (OutputStream os = Files.newOutputStream(newFile.toPath())) { + archive.extractFile(fh, os); + } + } + fh = archive.nextFileHeader(); + } + } + } + /** * Unzip a zip file into a target directory */ diff --git a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java index e735031..e58fb40 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java @@ -26,20 +26,8 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import java.io.InputStream; -import java.io.OutputStream; -import org.apache.commons.compress.archivers.tar.TarArchiveEntry; -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; -import org.apache.commons.compress.archivers.sevenz.SevenZFile; -import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry; -import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; -import com.github.junrar.Archive; -import com.github.junrar.rarfile.FileHeader; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermissions; @@ -755,7 +743,7 @@ public class FilePanelTab extends JPanel { int row = fileTable.getSelectedRow(); if (row >= 0) { FileItem item = (viewMode == ViewMode.BRIEF) ? tableModel.getItemFromBriefLayout(row, briefCurrentColumn) : tableModel.getItem(row); - if (item != null && (item.isDirectory() || isArchiveFile(item.getFile()))) { + if (item != null && (item.isDirectory() || FileOperations.isArchiveFile(item.getFile()))) { openSelectedItem(); } } @@ -1241,30 +1229,11 @@ public class FilePanelTab extends JPanel { } } - private boolean isArchiveFile(File f) { - if (f == null) return false; - String n = f.getName().toLowerCase(); - return n.endsWith(".war") || n.endsWith(".zip") || n.endsWith(".jar") || n.endsWith(".tar") || n.endsWith(".tar.gz") || n.endsWith(".tgz") || n.endsWith(".7z") || n.endsWith(".rar"); - } - private Path extractArchiveToTemp(File archive) { if (archive == null || !archive.isFile()) return null; - String name = archive.getName().toLowerCase(); try { Path tempDir = Files.createTempDirectory("kfmanager-archive-"); - - if (name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".war")) { - extractZip(archive, tempDir); - } else if (name.endsWith(".tar.gz") || name.endsWith(".tgz")) { - extractTarGz(archive, tempDir); - } else if (name.endsWith(".tar")) { - extractTar(archive, tempDir); - } else if (name.endsWith(".7z")) { - extractSevenZ(archive, tempDir); - } else if (name.endsWith(".rar")) { - extractRar(archive, tempDir); - } - + FileOperations.extractArchive(archive, tempDir.toFile(), null); return tempDir; } catch (Exception ex) { // extraction failed; attempt best-effort cleanup @@ -1275,116 +1244,6 @@ public class FilePanelTab extends JPanel { } } - private void extractZip(File archive, Path tempDir) throws IOException { - try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(archive.toPath()))) { - ZipEntry entry; - while ((entry = zis.getNextEntry()) != null) { - String entryName = entry.getName(); - Path resolved = tempDir.resolve(entryName).normalize(); - if (!resolved.startsWith(tempDir)) { - zis.closeEntry(); - continue; - } - - if (entry.isDirectory() || entryName.endsWith("/")) { - Files.createDirectories(resolved); - } else { - Path parent = resolved.getParent(); - if (parent != null) Files.createDirectories(parent); - Files.copy(zis, resolved, StandardCopyOption.REPLACE_EXISTING); - } - zis.closeEntry(); - } - } - } - - private void extractSevenZ(File archive, Path tempDir) throws IOException { - try (SevenZFile sevenZFile = new SevenZFile(archive)) { - SevenZArchiveEntry entry; - while ((entry = sevenZFile.getNextEntry()) != null) { - String entryName = entry.getName(); - Path resolved = tempDir.resolve(entryName).normalize(); - if (!resolved.startsWith(tempDir)) { - continue; - } - - if (entry.isDirectory()) { - Files.createDirectories(resolved); - } else { - Path parent = resolved.getParent(); - if (parent != null) Files.createDirectories(parent); - - try (OutputStream os = Files.newOutputStream(resolved)) { - byte[] buffer = new byte[8192]; - int bytesRead; - while ((bytesRead = sevenZFile.read(buffer)) != -1) { - os.write(buffer, 0, bytesRead); - } - } - } - } - } - } - - private void extractRar(File archiveFile, Path tempDir) throws Exception { - try (Archive archive = new Archive(archiveFile)) { - FileHeader fh = archive.nextFileHeader(); - while (fh != null) { - String entryName = fh.getFileName().replace('\\', '/'); - Path resolved = tempDir.resolve(entryName).normalize(); - if (!resolved.startsWith(tempDir)) { - fh = archive.nextFileHeader(); - continue; - } - - if (fh.isDirectory()) { - Files.createDirectories(resolved); - } else { - Path parent = resolved.getParent(); - if (parent != null) Files.createDirectories(parent); - try (OutputStream os = Files.newOutputStream(resolved)) { - archive.extractFile(fh, os); - } - } - fh = archive.nextFileHeader(); - } - } - } - - private void extractTarGz(File archive, Path tempDir) throws IOException { - try (InputStream fis = Files.newInputStream(archive.toPath()); - InputStream gzis = new GzipCompressorInputStream(fis); - TarArchiveInputStream tais = new TarArchiveInputStream(gzis)) { - extractTarInternal(tais, tempDir); - } - } - - private void extractTar(File archive, Path tempDir) throws IOException { - try (InputStream fis = Files.newInputStream(archive.toPath()); - TarArchiveInputStream tais = new TarArchiveInputStream(fis)) { - extractTarInternal(tais, tempDir); - } - } - - private void extractTarInternal(TarArchiveInputStream tais, Path tempDir) throws IOException { - TarArchiveEntry entry; - while ((entry = tais.getNextTarEntry()) != null) { - String entryName = entry.getName(); - Path resolved = tempDir.resolve(entryName).normalize(); - if (!resolved.startsWith(tempDir)) { - continue; - } - - if (entry.isDirectory()) { - Files.createDirectories(resolved); - } else { - Path parent = resolved.getParent(); - if (parent != null) Files.createDirectories(parent); - Files.copy(tais, resolved, StandardCopyOption.REPLACE_EXISTING); - } - } - } - private void deleteTempDirRecursively(Path dir) { if (dir == null) return; try { @@ -1412,7 +1271,7 @@ public class FilePanelTab extends JPanel { if (item.getName().equals("..")) { navigateUp(); - } else if (isArchiveFile(item.getFile())) { + } else if (FileOperations.isArchiveFile(item.getFile())) { Path temp = extractArchiveToTemp(item.getFile()); if (temp != null) { // Delete any previous temp dir if different @@ -1551,7 +1410,7 @@ public class FilePanelTab extends JPanel { if (item.getName().equals("..")) { navigateUp(); - } else if (isArchiveFile(item.getFile())) { + } else if (FileOperations.isArchiveFile(item.getFile())) { Path temp = extractArchiveToTemp(item.getFile()); if (temp != null) { try { diff --git a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java index 93e62dc..592c39b 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java +++ b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java @@ -1837,24 +1837,24 @@ public class MainWindow extends JFrame { } /** - * Unzip selected zip file + * Extract selected archive */ private void unzipFiles() { List selectedItems = activePanel.getSelectedItems(); if (selectedItems.isEmpty()) { JOptionPane.showMessageDialog(this, "No files selected", - "Unzip", + "Extract", JOptionPane.INFORMATION_MESSAGE); requestFocusInActivePanel(); return; } - File zipFile = selectedItems.get(0).getFile(); - if (!zipFile.getName().toLowerCase().endsWith(".zip")) { + File archiveFile = selectedItems.get(0).getFile(); + if (!FileOperations.isArchiveFile(archiveFile)) { JOptionPane.showMessageDialog(this, - "Selected file is not a ZIP archive", - "Unzip", + "Selected file is not a supported archive", + "Extract", JOptionPane.ERROR_MESSAGE); requestFocusInActivePanel(); return; @@ -1865,18 +1865,18 @@ public class MainWindow extends JFrame { final FilePanel sourcePanel = activePanel; int result = showConfirmWithBackground( - String.format("Unzip %s to:\n%s", zipFile.getName(), targetDir.getAbsolutePath()), - "Unzip"); + String.format("Extract %s to:\n%s", archiveFile.getName(), targetDir.getAbsolutePath()), + "Extract archive"); if (result == 0 || result == 1) { boolean background = (result == 1); if (background) { - addOperationToQueue("Unzip", String.format("Unzip %s to %s", zipFile.getName(), targetDir.getName()), - (cb) -> FileOperations.unzip(zipFile, targetDir, cb), () -> sourcePanel.unselectAll(), targetPanel); + addOperationToQueue("Extract", String.format("Extract %s to %s", archiveFile.getName(), targetDir.getName()), + (cb) -> FileOperations.extractArchive(archiveFile, targetDir, cb), () -> sourcePanel.unselectAll(), targetPanel); } else { performFileOperation((callback) -> { - FileOperations.unzip(zipFile, targetDir, callback); - }, "Unzipped into " + targetDir.getName(), false, true, () -> sourcePanel.unselectAll(), targetPanel); + FileOperations.extractArchive(archiveFile, targetDir, callback); + }, "Extracted into " + targetDir.getName(), false, true, () -> sourcePanel.unselectAll(), targetPanel); } } else { if (activePanel != null && activePanel.getFileTable() != null) {