001/* 002 * Syncany, www.syncany.org 003 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com> 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package org.syncany.operations.restore; 019 020import java.io.IOException; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Set; 024import java.util.logging.Level; 025import java.util.logging.Logger; 026 027import org.syncany.config.Config; 028import org.syncany.database.FileContent.FileChecksum; 029import org.syncany.database.FileVersion; 030import org.syncany.database.FileVersion.FileType; 031import org.syncany.database.MultiChunkEntry.MultiChunkId; 032import org.syncany.database.PartialFileHistory.FileHistoryId; 033import org.syncany.database.SqlDatabase; 034import org.syncany.operations.AbstractTransferOperation; 035import org.syncany.operations.Assembler; 036import org.syncany.operations.Downloader; 037import org.syncany.operations.restore.RestoreOperationResult.RestoreResultCode; 038import org.syncany.plugins.transfer.StorageException; 039 040public class RestoreOperation extends AbstractTransferOperation { 041 private static final Logger logger = Logger.getLogger(RestoreOperation.class.getSimpleName()); 042 public static final String ACTION_ID = "restore"; 043 044 private RestoreOperationOptions options; 045 046 private SqlDatabase localDatabase; 047 private Downloader downloader; 048 049 private Assembler assembler; 050 051 public RestoreOperation(Config config) { 052 this(config, new RestoreOperationOptions()); 053 } 054 055 public RestoreOperation(Config config, RestoreOperationOptions options) { 056 super(config, ACTION_ID); 057 058 this.options = options; 059 this.localDatabase = new SqlDatabase(config); 060 this.downloader = new Downloader(config, transferManager); 061 this.assembler = new Assembler(config, localDatabase, null); 062 } 063 064 @Override 065 public RestoreOperationResult execute() throws Exception { 066 logger.log(Level.INFO, ""); 067 logger.log(Level.INFO, "Running 'Restore' at client " + config.getMachineName() + " ..."); 068 logger.log(Level.INFO, "--------------------------------------------"); 069 070 // Find file history 071 FileHistoryId restoreFileHistoryId = findFileHistoryId(); 072 073 if (restoreFileHistoryId == null) { 074 return new RestoreOperationResult(RestoreResultCode.NACK_NO_FILE); 075 } 076 077 // Find file version 078 FileVersion restoreFileVersion = findRestoreFileVersion(restoreFileHistoryId); 079 080 if (restoreFileVersion == null) { 081 return new RestoreOperationResult(RestoreResultCode.NACK_NO_FILE); 082 } 083 else if (restoreFileVersion.getType() == FileType.FOLDER) { 084 return new RestoreOperationResult(RestoreResultCode.NACK_INVALID_FILE); 085 } 086 087 logger.log(Level.INFO, "Restore file identified: " + restoreFileVersion); 088 089 // Download multichunks 090 downloadMultiChunks(restoreFileVersion); 091 092 // Restore file 093 logger.log(Level.INFO, "- Restoring: " + restoreFileVersion); 094 095 RestoreFileSystemAction restoreAction = new RestoreFileSystemAction(config, assembler, restoreFileVersion, options.getRelativeTargetPath()); 096 RestoreFileSystemActionResult restoreResult = restoreAction.execute(); 097 098 return new RestoreOperationResult(RestoreResultCode.ACK, restoreResult.getTargetFile()); 099 } 100 101 private FileHistoryId findFileHistoryId() { 102 return localDatabase.expandFileHistoryId(options.getFileHistoryId()); 103 } 104 105 private FileVersion findRestoreFileVersion(FileHistoryId restoreFileHistoryId) { 106 if (options.getFileVersion() != null) { 107 return localDatabase.getFileVersion(restoreFileHistoryId, options.getFileVersion()); 108 } 109 else { 110 List<FileVersion> fileHistory = localDatabase.getFileHistory(restoreFileHistoryId); 111 112 if (fileHistory.size() >= 2) { 113 // In this case, we automatically restore the "previous" version 114 return fileHistory.get(fileHistory.size()-2); 115 } 116 else if (fileHistory.size() == 1){ 117 // In this case, we restore the last version. This is likely a deleted version. 118 return fileHistory.get(0); 119 } 120 else { 121 return null; 122 } 123 } 124 } 125 126 private void downloadMultiChunks(FileVersion restoreFileVersion) throws StorageException, IOException { 127 Set<MultiChunkId> multiChunksToDownload = new HashSet<MultiChunkId>(); 128 FileChecksum restoreFileChecksum = restoreFileVersion.getChecksum(); 129 130 if (restoreFileChecksum != null) { 131 multiChunksToDownload.addAll(localDatabase.getMultiChunkIds(restoreFileChecksum)); 132 133 logger.log(Level.INFO, "Downloading " + multiChunksToDownload.size() + " multichunk(s) to restore file ..."); 134 downloader.downloadAndDecryptMultiChunks(multiChunksToDownload); 135 } 136 } 137}