From a4fc24b20d685320e34c71913a20bcfc056ce484 Mon Sep 17 00:00:00 2001
From: Kai Reinhard <K.Reinhard@micromata.de>
Date: Mon, 17 Dec 2018 22:59:31 +0000
Subject: [PATCH] Handling of recreating files and directories works now fine (opens a file dialog with the restored items).

---
 borgbutler-webapp/src/components/views/archives/FileListEntry.jsx                     |   11 ++-
 borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java       |   33 +++++++++++
 borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java               |   22 ++++++-
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java |   93 +++++++++++++++++++-----------
 4 files changed, 118 insertions(+), 41 deletions(-)

diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java
index c2bbbda..0c8bfbe 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java
@@ -9,6 +9,7 @@
 import de.micromata.borgbutler.json.JsonUtils;
 import de.micromata.borgbutler.json.borg.*;
 import de.micromata.borgbutler.utils.DateUtils;
+import de.micromata.borgbutler.utils.ReplaceUtils;
 import lombok.Setter;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.exec.CommandLine;
@@ -20,6 +21,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.naming.Context;
 import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -157,12 +159,24 @@
         return content;
     }
 
-    public static Path extractFiles(BorgRepoConfig repoConfig, String archive, String path) throws IOException {
-        Path tempDirWithPrefix = Files.createTempDirectory("borgbutler-extract-");
-        Context context = new Context().setWorkingDir(tempDirWithPrefix.toFile()).setRepoConfig(repoConfig)
+    /**
+     * Stores the file in a subdirectory named with the repos display name.
+     * @param restoreHomeDir
+     * @param repoConfig
+     * @param archive
+     * @param path
+     * @return Used sub directory with the restored content.
+     * @throws IOException
+     */
+    public static File extractFiles(File restoreHomeDir, BorgRepoConfig repoConfig, String archive, String path) throws IOException {
+        File restoreDir = new File(restoreHomeDir, ReplaceUtils.encodeFilename(repoConfig.getDisplayName(), true));
+        if (!restoreDir.exists()) {
+            restoreDir.mkdirs();
+        }
+        Context context = new Context().setWorkingDir(restoreDir).setRepoConfig(repoConfig)
                 .setCommand("extract").setArchive(archive).setArgs(path);
         execute(context);
-        return tempDirWithPrefix;
+        return restoreDir;
     }
 
     private static String execute(Context context) {
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java
index cc85168..8aca808 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java
@@ -1,13 +1,23 @@
 package de.micromata.borgbutler.config;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import de.micromata.borgbutler.BorgCommands;
 import lombok.Getter;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
 public class Configuration {
+    private Logger log = LoggerFactory.getLogger(Configuration.class);
+    /**
+     * Default dir name for restoring archives.
+     */
+    private static final String RESTORE_DIRNAME = "restore";
     @Getter
     private String borgCommand = "borg";
     /**
@@ -17,6 +27,15 @@
     @JsonProperty("cache_archive_content_max_disc_size_mb")
     private int cacheArchiveContentMaxDiscSizeMB = 100;
 
+    /**
+     * Default is restore inside BorgButler's home dir (~/.borgbutler/restore).
+     */
+    @Getter
+    @JsonProperty("restore_dir")
+    private String restoreDirPath;
+    @JsonIgnore
+    private File restoreHomeDir;
+
     @Getter
     @JsonProperty("repo-configs")
     private List<BorgRepoConfig> repoConfigs = new ArrayList<>();
@@ -36,4 +55,18 @@
         }
         return null;
     }
+
+    public File getRestoreHomeDir() {
+        if (restoreHomeDir == null) {
+            if (StringUtils.isNotBlank(restoreDirPath)) {
+                restoreHomeDir = new File(restoreDirPath);
+            } else {
+                restoreHomeDir = new File(ConfigurationHandler.getInstance().getWorkingDir(), RESTORE_DIRNAME);
+            }
+            if (!restoreHomeDir.exists()) {
+                log.info("Creating dir '" + restoreHomeDir.getAbsolutePath() + "' for restoring backup files and directories.");
+            }
+        }
+        return restoreHomeDir;
+    }
 }
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java
index eb9812d..f5e3a87 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java
@@ -26,6 +26,7 @@
 import java.awt.*;
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Files;
 import java.util.List;
 
 @Path("/archives")
@@ -79,14 +80,14 @@
     }
 
     @GET
-    @Path("/download")
+    @Path("/restore")
     @Produces(MediaType.APPLICATION_OCTET_STREAM)
     /**
      * @param archiveId
-     * @param fileNumber The fileNumber of the file in the archive served by BorgButler's
+     * @param fileNumber The fileNumber of the file or directory in the archive served by BorgButler's
      * {@link #getArchiveFileLIst(String, String, String, boolean, boolean)}
      */
-    public Response downloadFilebyPath(@QueryParam("archiveId") String archiveId, @QueryParam("fileNumber") int fileNumber) {
+    public Response restore(@QueryParam("archiveId") String archiveId, @QueryParam("fileNumber") int fileNumber) {
         log.info("Requesting file #" + fileNumber + " of archive '" + archiveId + "'.");
         FileSystemFilter filter = new FileSystemFilter().setFileNumber(fileNumber);
         List<BorgFilesystemItem> items = ButlerCache.getInstance().getArchiveContent(archiveId, false,
@@ -94,7 +95,7 @@
         if (CollectionUtils.isEmpty(items)) {
             log.error("Requested file #" + fileNumber + " not found in archive '" + archiveId
                     + ". (May-be the archive content isn't yet loaded to the cache.");
-            Response.ResponseBuilder builder = Response.status(404);
+            Response.ResponseBuilder builder = Response.status(Response.Status.NOT_FOUND);
             return builder.build();
         }
         if (items.size() != 1) {
@@ -103,18 +104,63 @@
             Response.ResponseBuilder builder = Response.status(404);
             return builder.build();
         }
-        BorgFilesystemItem item = items.get(0);
         Archive archive = ButlerCache.getInstance().getArchive(archiveId);
         if (archive == null) {
-            Response.ResponseBuilder builder = Response.status(404);
+            Response.ResponseBuilder builder = Response.status(Response.Status.NOT_FOUND);
             return builder.build();
         }
         BorgRepoConfig repoConfig = ConfigurationHandler.getConfiguration().getRepoConfig(archive.getRepoId());
-        java.nio.file.Path path = null;
-        java.nio.file.Path tempDir = null;
         try {
-            tempDir = BorgCommands.extractFiles(repoConfig, archive.getName(), item.getPath());
-            openFileBrowser(tempDir);
+            BorgFilesystemItem item = items.get(0);
+            File restoreHomeDir = ConfigurationHandler.getConfiguration().getRestoreHomeDir();
+            File restoreDir = BorgCommands.extractFiles(restoreHomeDir, repoConfig, archive.getName(), item.getPath());
+            List<java.nio.file.Path> files = DirUtils.listFiles(restoreDir.toPath());
+            if (CollectionUtils.isEmpty(files)) {
+                log.error("No files extracted.");
+                Response.ResponseBuilder builder = Response.status(Response.Status.NOT_FOUND);
+                return builder.build();
+            }
+            openFileBrowser(new File(restoreDir, item.getPath()));
+            Response.ResponseBuilder builder = Response.status(Response.Status.ACCEPTED);
+            return builder.build();
+        } catch (IOException ex) {
+            log.error("No file extracted: " + ex.getMessage(), ex);
+            Response.ResponseBuilder builder = Response.status(Response.Status.NOT_FOUND);
+            return builder.build();
+        }
+    }
+
+    public void openFileBrowser(File directory) {
+        if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE_FILE_DIR)) {
+            Desktop.getDesktop().browseFileDirectory(directory);
+        }
+    }
+
+    private Response handleRestoredFiles(BorgRepoConfig repoConfig, Archive archive) {
+        // Todo: Handle download from single files as well as download of zip archive (if BorgButler runs remote).
+        return null;
+       /* File file = path.toFile();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            FileUtils.copyFile(file, baos);
+        } catch (IOException ex) {
+            log.error(ex.getMessage(), ex);
+        }
+        BorgFilesystemItem item = items.get(0);
+        file = new File(item.getPath());
+        byte[] byteArray = baos.toByteArray();//result.getAsByteArrayOutputStream().toByteArray();
+        Response.ResponseBuilder builder = Response.ok(byteArray);
+        builder.header("Content-Disposition", "attachment; filename=" + file.getName());
+        // Needed to get the Content-Disposition by client:
+        builder.header("Access-Control-Expose-Headers", "Content-Disposition");
+        Response response = builder.build();
+        return response;
+
+        try {
+            //java.nio.file.Path tempDirWithPrefix = Files.createTempDirectory("borgbutler-extract-");
+            File restoreHomeDir = ConfigurationHandler.getConfiguration().getRestoreHomeDir();
+            File restoreDir = BorgCommands.extractFiles(restoreHomeDir, repoConfig, archive.getName(), item.getPath());
+            openFileBrowser(restoreDir);
             List<java.nio.file.Path> files = DirUtils.listFiles(tempDir);
             if (CollectionUtils.isEmpty(files)) {
                 log.error("No file extracted.");
@@ -122,39 +168,18 @@
                 return builder.build();
             }
             path = files.get(0);
-            File file = path.toFile();
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            try {
-                FileUtils.copyFile(file, baos);
-            } catch (IOException ex) {
-                log.error(ex.getMessage(), ex);
-            }
-            file = new File(item.getPath());
-            byte[] byteArray = baos.toByteArray();//result.getAsByteArrayOutputStream().toByteArray();
-            Response.ResponseBuilder builder = Response.ok(byteArray);
-            builder.header("Content-Disposition", "attachment; filename=" + file.getName());
-            // Needed to get the Content-Disposition by client:
-            builder.header("Access-Control-Expose-Headers", "Content-Disposition");
-            Response response = builder.build();
-            return response;
         } catch (IOException ex) {
             log.error("No file extracted: " + ex.getMessage(), ex);
             Response.ResponseBuilder builder = Response.status(404);
             return builder.build();
         } finally {
-/*            if (tempDir != null) {
+           if (tempDir != null) {
                 try {
                     FileUtils.deleteDirectory(tempDir.toFile());
                 } catch (IOException ex) {
                     log.error("Error while trying to delete temporary directory '" + tempDir.toString() + "': " + ex.getMessage(), ex);
                 }
-            }*/
-        }
-    }
-
-    public static void openFileBrowser(java.nio.file.Path path) {
-        if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE_FILE_DIR)) {
-            Desktop.getDesktop().browseFileDirectory(path.toFile());
-        }
+            }
+        }*/
     }
 }
diff --git a/borgbutler-webapp/src/components/views/archives/FileListEntry.jsx b/borgbutler-webapp/src/components/views/archives/FileListEntry.jsx
index ec8f51d..41a5511 100644
--- a/borgbutler-webapp/src/components/views/archives/FileListEntry.jsx
+++ b/borgbutler-webapp/src/components/views/archives/FileListEntry.jsx
@@ -7,20 +7,25 @@
 
 function download(archiveId, fileNumber) {
     let filename;
-    fetch(getRestServiceUrl('archives/download', {
+    fetch(getRestServiceUrl('archives/restore', {
         archiveId: archiveId,
         fileNumber: fileNumber
     }))
         .then(response => {
+            if (response.status === 202) { // ACCEPTED
+                // No download wanted (file or path was only restored on server).
+               return undefined;
+            }
             if (!response.ok) {
                 throw new Error(response.statusText);
             }
-
             filename = getResponseHeaderFilename(response.headers.get('Content-Disposition'));
             return response.blob();
         })
         .then(blob => {
-            fileDownload(blob, filename)
+            if (filename) {
+                fileDownload(blob, filename);
+            }
         })
         .catch(error => {
             console.log(error.toString());

--
Gitblit v1.10.0