private void restoreFileVersion(FileVersion fileVersion) {
    // Set labels/status
    String shortFileName = shortenFileName(fileVersion.getPath());
    String versionStr = Long.toString(fileVersion.getVersion());

    restoreButton.setEnabled(false);
    restoreStatusIconComposite.setVisible(true);
    restoreStatusTextLabel.setVisible(true);

    restoreStatusIconComposite.setAnimatedImage(
        IMAGE_LOADING_SPINNER_RESOURCE, IMAGE_LOADING_SPINNER_FRAME_RATE);
    restoreStatusTextLabel.setText(
        I18n.getText(
            "org.syncany.gui.history.DetailPanel.label.fileRestoreOngoing",
            shortFileName,
            versionStr));
    restoreStatusTextLabel.setCursor(new Cursor(Display.getDefault(), SWT.CURSOR_ARROW));
    restoreStatusTextLabel.setToolTipText("");

    restoredFile = null;

    layout();

    // Send restore request
    RestoreOperationOptions restoreOptions = new RestoreOperationOptions();
    restoreOptions.setFileHistoryId(fileVersion.getFileHistoryId());
    restoreOptions.setFileVersion(fileVersion.getVersion().intValue());

    pendingRestoreRequest = new RestoreFolderRequest();
    pendingRestoreRequest.setRoot(historyModel.getSelectedRoot());
    pendingRestoreRequest.setOptions(restoreOptions);

    eventBus.post(pendingRestoreRequest);
  }
示例#2
0
  private void handleGetFileRequest(GetFileRequest fileRequest) {
    try {
      FileHistoryId fileHistoryId = FileHistoryId.parseFileId(fileRequest.getFileHistoryId());
      long version = fileRequest.getVersion();

      FileVersion fileVersion = localDatabase.getFileVersion(fileHistoryId, version);
      FileContent fileContent = localDatabase.getFileContent(fileVersion.getChecksum(), true);
      Map<ChunkChecksum, MultiChunkId> multiChunks =
          localDatabase.getMultiChunkIdsByChecksums(fileContent.getChunks());

      TransferManager transferManager =
          config.getTransferPlugin().createTransferManager(config.getConnection());
      Downloader downloader = new Downloader(config, transferManager);
      Assembler assembler = new Assembler(config, localDatabase);

      downloader.downloadAndDecryptMultiChunks(new HashSet<MultiChunkId>(multiChunks.values()));

      File tempFile = assembler.assembleToCache(fileVersion);
      String tempFileToken = StringUtil.toHex(ObjectId.secureRandomBytes(40));

      GetFileResponse fileResponse =
          new GetFileResponse(fileRequest.getId(), fileRequest.getRoot(), tempFileToken);
      GetFileResponseInternal fileResponseInternal =
          new GetFileResponseInternal(fileResponse, tempFile);

      eventBus.post(fileResponseInternal);
    } catch (Exception e) {
      logger.log(Level.WARNING, "Cannot reassemble file.", e);
      eventBus.post(new BadRequestResponse(fileRequest.getId(), "Cannot reassemble file."));
    }
  }
示例#3
0
    private PartialFileHistory guessLastFileHistoryForFileWithMatchingChecksum(
        FileProperties fileProperties,
        Collection<PartialFileHistory> fileHistoriesWithSameChecksum) {
      PartialFileHistory lastFileHistory = null;

      // Check if they do not exist anymore --> assume it has moved!
      // We choose the best fileHistory to base on as follows:

      // 1. Ensure that it was modified at the same time and is the same size
      // 2. Check the fileHistory was deleted and the file does not actually exists
      // 3. Choose the one with the longest matching tail of the path to the new path

      for (PartialFileHistory fileHistoryWithSameChecksum : fileHistoriesWithSameChecksum) {
        FileVersion lastVersion = fileHistoryWithSameChecksum.getLastVersion();

        if (fileProperties.getLastModified() != lastVersion.getLastModified().getTime()
            || fileProperties.getSize() != lastVersion.getSize()) {
          continue;
        }

        File lastVersionOnLocalDisk =
            new File(config.getLocalDir() + File.separator + lastVersion.getPath());

        if (lastVersion.getStatus() != FileStatus.DELETED
            && !FileUtil.exists(lastVersionOnLocalDisk)) {
          if (lastFileHistory == null) {
            lastFileHistory = fileHistoryWithSameChecksum;
          } else {
            String filePath = fileProperties.getRelativePath();
            String currentPreviousPath = lastFileHistory.getLastVersion().getPath();
            String candidatePreviousPath = fileHistoryWithSameChecksum.getLastVersion().getPath();

            for (int i = 0; i < filePath.length(); i++) {
              if (!filePath.regionMatches(
                  filePath.length() - i,
                  candidatePreviousPath,
                  candidatePreviousPath.length() - i,
                  i)) {
                // The candidate no longer matches, take the current path.
                break;
              }
              if (!filePath.regionMatches(
                  filePath.length() - i,
                  currentPreviousPath,
                  currentPreviousPath.length() - i,
                  i)) {
                // The current previous path no longer matches, take the new candidate
                lastFileHistory = fileHistoryWithSameChecksum;
                break;
              }
            }
          }
        }
      }

      return lastFileHistory;
    }
示例#4
0
  @Test
  public void testFileHistoryClone() {
    FileVersion fileVersion1 = new FileVersion();
    fileVersion1.setVersion(1L);
    fileVersion1.setPath("/somepath");

    FileVersion fileVersion2 = new FileVersion();
    fileVersion2.setVersion(2L);
    fileVersion2.setPath("/somepath");

    PartialFileHistory fileHistory = new PartialFileHistory(FileHistoryId.parseFileId("1234"));
    fileHistory.addFileVersion(fileVersion1);
    fileHistory.addFileVersion(fileVersion2);

    PartialFileHistory fileHistoryClone = fileHistory.clone();

    assertEquals(fileHistory, fileHistoryClone);
    assertEquals(fileHistory.getFileVersions().size(), fileHistoryClone.getFileVersions().size());
    assertEquals(fileHistory.getFileVersions(), fileHistoryClone.getFileVersions());

    FileVersion fileVersion3 = new FileVersion();
    fileVersion3.setVersion(3L);
    fileVersion3.setPath("/somepath");

    fileHistoryClone.addFileVersion(fileVersion3);
    assertEquals(
        fileHistory.getFileVersions().size() + 1, fileHistoryClone.getFileVersions().size());
  }
示例#5
0
  @Test
  public void testContentChecksumCache() throws IOException {
    Database database = new Database();

    // Round 1: Add file history & version
    DatabaseVersion databaseVersion1 = TestDatabaseUtil.createDatabaseVersion();

    // - history 1, version 1
    FileVersion fileVersion1 = TestDatabaseUtil.createFileVersion("samechecksum1.jpg");
    fileVersion1.setChecksum(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0});

    PartialFileHistory fileHistory1 = new PartialFileHistory(11111111111111111L);

    databaseVersion1.addFileHistory(fileHistory1);
    databaseVersion1.addFileVersionToHistory(fileHistory1.getFileId(), fileVersion1);

    database.addDatabaseVersion(databaseVersion1);

    assertNotNull(database.getFileHistories(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}));
    assertEquals(1, database.getFileHistories(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}).size());

    // Round 2: Add two other versions with same checksum to new database version
    DatabaseVersion databaseVersion2 = TestDatabaseUtil.createDatabaseVersion(databaseVersion1);

    // - history 1, version 2
    FileVersion fileVersion11 =
        TestDatabaseUtil.createFileVersion("samechecksum2-renamed.jpg", fileVersion1);
    fileVersion11.setChecksum(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}); // same checksum!
    fileVersion11.setStatus(FileStatus.RENAMED);

    PartialFileHistory fileHistory11 =
        new PartialFileHistory(11111111111111111L); // same ID as above	

    databaseVersion2.addFileHistory(fileHistory11);
    databaseVersion2.addFileVersionToHistory(fileHistory11.getFileId(), fileVersion11);

    // - history 2, version 1
    FileVersion fileVersion2 = TestDatabaseUtil.createFileVersion("samechecksum2.jpg");
    fileVersion2.setChecksum(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}); // same checksum!

    PartialFileHistory fileHistory2 = new PartialFileHistory(22222222222222222L); // different ID	

    databaseVersion2.addFileHistory(fileHistory2);
    databaseVersion2.addFileVersionToHistory(fileHistory2.getFileId(), fileVersion2);

    // - history 3, version 1
    FileVersion fileVersion3 = TestDatabaseUtil.createFileVersion("samechecksum3.jpg");
    fileVersion3.setChecksum(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}); // same checksum!

    PartialFileHistory fileHistory3 = new PartialFileHistory(33333333333333333L); // different ID	

    databaseVersion2.addFileHistory(fileHistory3);
    databaseVersion2.addFileVersionToHistory(fileHistory3.getFileId(), fileVersion3);

    database.addDatabaseVersion(databaseVersion2);

    assertNotNull(database.getFileHistories(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}));
    assertEquals(3, database.getFileHistories(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}).size());
  }
示例#6
0
  private PartialFileHistory getFileHistoryByPathFromDatabaseVersion(
      DatabaseVersion databaseVersion, String path) {
    // TODO [medium] Extremely performance intensive, because this is called inside a loop above.
    // Implement better caching for database version!!!

    for (PartialFileHistory fileHistory : databaseVersion.getFileHistories()) {
      FileVersion lastVersion = fileHistory.getLastVersion();

      if (lastVersion.getStatus() != FileStatus.DELETED && lastVersion.getPath().equals(path)) {
        return fileHistory;
      }
    }

    return null;
  }
  /**
   * Finds the multichunks that need to be downloaded for the given file version -- using the local
   * database and given winners database. Returns a set of multichunk identifiers.
   */
  private Collection<MultiChunkId> determineMultiChunksToDownload(
      FileVersion fileVersion, MemoryDatabase winnersDatabase) {
    Set<MultiChunkId> multiChunksToDownload = new HashSet<MultiChunkId>();

    // First: Check if we know this file locally!
    List<MultiChunkId> multiChunkIds = localDatabase.getMultiChunkIds(fileVersion.getChecksum());

    if (multiChunkIds.size() > 0) {
      multiChunksToDownload.addAll(multiChunkIds);
    } else {
      // Second: We don't know it locally; must be from the winners database
      FileContent winningFileContent = winnersDatabase.getContent(fileVersion.getChecksum());
      boolean winningFileHasContent = winningFileContent != null;

      if (winningFileHasContent) { // File can be empty!
        List<ChunkChecksum> fileChunks = winningFileContent.getChunks();

        // TODO [medium] Instead of just looking for multichunks to download here, we should look
        // for chunks in local files as well
        // and return the chunk positions in the local files ChunkPosition (chunk123 at file12,
        // offset 200, size 250)

        Map<ChunkChecksum, MultiChunkId> checksumsWithMultiChunkIds =
            localDatabase.getMultiChunkIdsByChecksums(fileChunks);

        for (ChunkChecksum chunkChecksum : fileChunks) {
          MultiChunkId multiChunkIdForChunk = checksumsWithMultiChunkIds.get(chunkChecksum);
          if (multiChunkIdForChunk == null) {
            multiChunkIdForChunk = winnersDatabase.getMultiChunkIdForChunk(chunkChecksum);

            if (multiChunkIdForChunk == null) {
              throw new RuntimeException("Cannot find multichunk for chunk " + chunkChecksum);
            }
          }

          if (!multiChunksToDownload.contains(multiChunkIdForChunk)) {
            logger.log(
                Level.INFO,
                "  + Adding multichunk " + multiChunkIdForChunk + " to download list ...");
            multiChunksToDownload.add(multiChunkIdForChunk);
          }
        }
      }
    }

    return multiChunksToDownload;
  }
示例#8
0
  @Test
  public void testFilenameCache() throws IOException {
    Database database = new Database();

    // Round 1: Add file history & version
    DatabaseVersion databaseVersion1 = TestDatabaseUtil.createDatabaseVersion();

    FileVersion fileVersion1 = TestDatabaseUtil.createFileVersion("file1.jpg");
    PartialFileHistory fileHistory1 = new PartialFileHistory(11111111111111111L);

    databaseVersion1.addFileHistory(fileHistory1);
    databaseVersion1.addFileVersionToHistory(fileHistory1.getFileId(), fileVersion1);

    database.addDatabaseVersion(databaseVersion1);

    assertEquals(fileHistory1, database.getFileHistory("file1.jpg"));

    // Round 2: Add new version
    DatabaseVersion databaseVersion2 = TestDatabaseUtil.createDatabaseVersion(databaseVersion1);

    FileVersion fileVersion2 = TestDatabaseUtil.createFileVersion("file2.jpg", fileVersion1);
    PartialFileHistory fileHistory2 = new PartialFileHistory(11111111111111111L); // same ID	

    databaseVersion2.addFileHistory(fileHistory2);
    databaseVersion2.addFileVersionToHistory(fileHistory2.getFileId(), fileVersion2);

    database.addDatabaseVersion(databaseVersion2);

    assertNotNull(database.getFileHistory("file2.jpg"));
    assertEquals(2, database.getFileHistory("file2.jpg").getFileVersions().size());
    assertNull(database.getFileHistory("file1.jpg"));

    // Round 3: Add deleted version
    DatabaseVersion databaseVersion3 = TestDatabaseUtil.createDatabaseVersion(databaseVersion2);

    FileVersion fileVersion3 = TestDatabaseUtil.createFileVersion("file2.jpg", fileVersion2);
    fileVersion3.setStatus(FileStatus.DELETED);

    PartialFileHistory fileHistory3 = new PartialFileHistory(11111111111111111L); // same ID	

    databaseVersion3.addFileHistory(fileHistory3);
    databaseVersion3.addFileVersionToHistory(fileHistory3.getFileId(), fileVersion3);

    database.addDatabaseVersion(databaseVersion3);

    assertNull(database.getFileHistory("file2.jpg"));
  }
示例#9
0
  @Test
  public void testFilenameCacheDeleteAndNewOfSameFileInOneDatabaseVersion() throws IOException {
    Database database = new Database();

    // Round 1: Add file history & version
    DatabaseVersion databaseVersion1 = TestDatabaseUtil.createDatabaseVersion();

    FileVersion fileVersion1 = TestDatabaseUtil.createFileVersion("file1.jpg");
    PartialFileHistory fileHistory1 = new PartialFileHistory(11111111111111111L);

    databaseVersion1.addFileHistory(fileHistory1);
    databaseVersion1.addFileVersionToHistory(fileHistory1.getFileId(), fileVersion1);

    database.addDatabaseVersion(databaseVersion1);

    assertEquals(fileHistory1, database.getFileHistory("file1.jpg"));

    // Round 2: Add new version
    DatabaseVersion databaseVersion2 = TestDatabaseUtil.createDatabaseVersion(databaseVersion1);

    // - delete file1.jpg
    FileVersion fileVersion2 = TestDatabaseUtil.createFileVersion("file1.jpg", fileVersion1);
    fileVersion2.setStatus(FileStatus.DELETED);

    PartialFileHistory fileHistory2 = new PartialFileHistory(11111111111111111L); // same ID	

    databaseVersion2.addFileHistory(fileHistory2);
    databaseVersion2.addFileVersionToHistory(fileHistory2.getFileId(), fileVersion2);

    // - add file1.jpg (as FOLDER!)
    FileVersion fileVersion3 = TestDatabaseUtil.createFileVersion("file1.jpg"); // new file!
    fileVersion3.setType(FileType.FOLDER);

    PartialFileHistory fileHistory3 = new PartialFileHistory(222222222L); // new ID	!

    databaseVersion2.addFileHistory(fileHistory3);
    databaseVersion2.addFileVersionToHistory(fileHistory3.getFileId(), fileVersion3);

    // - add datbase version
    database.addDatabaseVersion(databaseVersion2);

    assertNotNull(database.getFileHistory("file1.jpg"));
    assertEquals(1, database.getFileHistory("file1.jpg").getFileVersions().size());
    assertEquals(fileHistory3, database.getFileHistory("file1.jpg"));
  }
  private void selectFileVersion(FileVersion fileVersion) {
    boolean isLastVersion = fileVersion.equals(selectedFileHistory.getLastVersion());
    boolean restoreInProgress = pendingRestoreRequest != null;
    boolean restoreButtonEnabled = !isLastVersion && !restoreInProgress;

    logger.log(
        Level.INFO,
        "Detail panel: Selecting file version"
            + fileVersion.getVersion()
            + "; Restore in progress = "
            + restoreInProgress
            + "; Last/current version = "
            + isLastVersion
            + " --> Button enabled = "
            + restoreButtonEnabled);

    restoreButton.setEnabled(restoreButtonEnabled);
  }
  private FileVersion createFileVersion(
      String path, FileType type, FileVersion basedOnFileVersion) {
    if (basedOnFileVersion == null) {
      FileVersion fileVersion = new FileVersion();
      fileVersion.setPath(path);
      fileVersion.setType(type);
      fileVersion.setVersion(1L);

      return fileVersion;
    } else {
      FileVersion fileVersion = basedOnFileVersion.clone();
      fileVersion.setPath(path);
      fileVersion.setType(type);
      fileVersion.setVersion(basedOnFileVersion.getVersion() + 1);

      return fileVersion;
    }
  }
示例#12
0
  @Test
  public void testFileHistoryGetVersionsNonEmpty() {
    FileVersion fileVersion = new FileVersion();
    fileVersion.setVersion(5L);
    fileVersion.setPath("/somepath");

    PartialFileHistory fileHistory = new PartialFileHistory(FileHistoryId.parseFileId("1234"));
    fileHistory.addFileVersion(fileVersion);

    assertNotNull(fileHistory.getLastVersion());
    assertNotNull(fileHistory.getFileVersions());
    assertEquals(1, fileHistory.getFileVersions().size());
    assertEquals(fileVersion, fileHistory.getLastVersion());
    assertNull(fileHistory.getFileVersions().get(1L));
    assertNull(fileHistory.getFileVersion(1L));
    assertEquals(fileVersion, fileHistory.getFileVersions().get(5L));
    assertEquals(fileVersion, fileHistory.getFileVersion(5L));
  }
示例#13
0
  public static FileVersion createFileVersion(String path) {
    FileVersion fileVersion = new FileVersion();

    fileVersion.setChecksum(new FileChecksum(TestFileUtil.createRandomArray(20)));
    fileVersion.setLastModified(new Date());
    fileVersion.setPath(path);
    fileVersion.setStatus(FileStatus.NEW);
    fileVersion.setType(FileType.FILE);
    fileVersion.setUpdated(new Date());
    fileVersion.setVersion(1L);

    return fileVersion;
  }
示例#14
0
    private PartialFileHistory guessLastFileHistoryForFolderOrSymlink(
        FileProperties fileProperties) {
      PartialFileHistory lastFileHistory = filePathCache.get(fileProperties.getRelativePath());

      if (lastFileHistory == null) {
        logger.log(
            Level.FINER,
            "   * No old file history found, starting new history (path: "
                + fileProperties.getRelativePath()
                + ", "
                + fileProperties.getType()
                + ")");
        return null;
      } else {
        FileVersion lastFileVersion = lastFileHistory.getLastVersion();

        if (lastFileVersion.getStatus() != FileStatus.DELETED
            && lastFileVersion.getType() == fileProperties.getType()) {
          logger.log(
              Level.FINER,
              "   * Found old file history "
                  + lastFileHistory.getFileHistoryId()
                  + " (by path: "
                  + fileProperties.getRelativePath()
                  + "), "
                  + fileProperties.getType()
                  + ", appending new version.");
          return lastFileHistory;
        } else {
          logger.log(
              Level.FINER,
              "   * No old file history found, starting new history (path: "
                  + fileProperties.getRelativePath()
                  + ", "
                  + fileProperties.getType()
                  + ")");
          return null;
        }
      }
    }
示例#15
0
  private Collection<MultiChunkEntry> determineMultiChunksToDownload(
      FileVersion fileVersion, Database localDatabase, Database winnersDatabase) {
    Set<MultiChunkEntry> multiChunksToDownload = new HashSet<MultiChunkEntry>();

    FileContent winningFileContent = localDatabase.getContent(fileVersion.getChecksum());

    if (winningFileContent == null) {
      winningFileContent = winnersDatabase.getContent(fileVersion.getChecksum());
    }

    boolean winningFileHasContent = winningFileContent != null;

    if (winningFileHasContent) { // File can be empty!
      Collection<ChunkEntryId> fileChunks =
          winningFileContent
              .getChunks(); // TODO [medium] Instead of just looking for multichunks to download
      // here, we should look for chunks in local files as well and return the
      // chunk positions in the local files ChunkPosition (chunk123 at file12,
      // offset 200, size 250)

      for (ChunkEntryId chunkChecksum : fileChunks) {
        MultiChunkEntry multiChunkForChunk = localDatabase.getMultiChunkForChunk(chunkChecksum);

        if (multiChunkForChunk == null) {
          multiChunkForChunk = winnersDatabase.getMultiChunkForChunk(chunkChecksum);
        }

        if (!multiChunksToDownload.contains(multiChunkForChunk)) {
          logger.log(
              Level.INFO,
              "  + Adding multichunk "
                  + StringUtil.toHex(multiChunkForChunk.getId())
                  + " to download list ...");
          multiChunksToDownload.add(multiChunkForChunk);
        }
      }
    }

    return multiChunksToDownload;
  }
示例#16
0
  public static FileVersion createFileVersion(String path, FileVersion basedOnFileVersion) {
    FileVersion fileVersion = basedOnFileVersion.clone();

    fileVersion.setPath(path);
    fileVersion.setStatus(FileStatus.CHANGED);
    fileVersion.setVersion(basedOnFileVersion.getVersion() + 1);

    return fileVersion;
  }
示例#17
0
  private void removeDeletedFiles(
      DatabaseVersion newDatabaseVersion, List<PartialFileHistory> fileHistoriesWithLastVersion) {
    logger.log(Level.FINER, "- Looking for deleted files ...");

    for (PartialFileHistory fileHistory : fileHistoriesWithLastVersion) {
      // Ignore this file history if it has been updated in this database version before (file
      // probably renamed!)
      if (newDatabaseVersion.getFileHistory(fileHistory.getFileHistoryId()) != null) {
        continue;
      }

      // Check if file exists, remove if it doesn't
      FileVersion lastLocalVersion = fileHistory.getLastVersion();
      File lastLocalVersionOnDisk =
          new File(config.getLocalDir() + File.separator + lastLocalVersion.getPath());

      // Ignore this file history if the last version is marked "DELETED"
      if (lastLocalVersion.getStatus() == FileStatus.DELETED) {
        continue;
      }

      // Add this file history if a new file with this name has been added (file type change)
      PartialFileHistory newFileWithSameName =
          getFileHistoryByPathFromDatabaseVersion(
              newDatabaseVersion, fileHistory.getLastVersion().getPath());

      // If file has VANISHED, mark as DELETED
      if (!FileUtil.exists(lastLocalVersionOnDisk) || newFileWithSameName != null) {
        PartialFileHistory deletedFileHistory =
            new PartialFileHistory(fileHistory.getFileHistoryId());
        FileVersion deletedVersion = lastLocalVersion.clone();

        deletedVersion.setStatus(FileStatus.DELETED);
        deletedVersion.setVersion(fileHistory.getLastVersion().getVersion() + 1);
        deletedVersion.setUpdated(new Date());

        logger.log(Level.FINER, "  + Deleted: Adding DELETED version: {0}", deletedVersion);
        logger.log(Level.FINER, "                           based on: {0}", lastLocalVersion);

        deletedFileHistory.addFileVersion(deletedVersion);
        newDatabaseVersion.addFileHistory(deletedFileHistory);
      }
    }
  }
示例#18
0
    private FileVersion createNewFileVersion(
        FileVersion lastFileVersion, FileProperties fileProperties) {
      FileVersion fileVersion = null;

      // Version
      if (lastFileVersion == null) {
        fileVersion = new FileVersion();
        fileVersion.setVersion(1L);
        fileVersion.setStatus(FileStatus.NEW);
      } else {
        fileVersion = lastFileVersion.clone();
        fileVersion.setVersion(lastFileVersion.getVersion() + 1);
      }

      // Simple attributes
      fileVersion.setPath(fileProperties.getRelativePath());
      fileVersion.setLinkTarget(fileProperties.getLinkTarget());
      fileVersion.setType(fileProperties.getType());
      fileVersion.setSize(fileProperties.getSize());
      fileVersion.setChecksum(fileProperties.getChecksum());
      fileVersion.setLastModified(new Date(fileProperties.getLastModified()));
      fileVersion.setUpdated(new Date());

      // Permissions
      if (EnvironmentUtil.isWindows()) {
        fileVersion.setDosAttributes(fileProperties.getDosAttributes());
        fileVersion.setPosixPermissions(DEFAULT_POSIX_PERMISSIONS);
      } else if (EnvironmentUtil.isUnixLikeOperatingSystem()) {
        fileVersion.setPosixPermissions(fileProperties.getPosixPermissions());
        fileVersion.setDosAttributes(DEFAULT_DOS_ATTRIBUTES);
      }

      // Status
      if (lastFileVersion != null) {
        if (fileVersion.getType() == FileType.FILE
            && FileChecksum.fileChecksumEquals(
                fileVersion.getChecksum(), lastFileVersion.getChecksum())) {
          fileVersion.setStatus(FileStatus.CHANGED);
        } else if (!fileVersion.getPath().equals(lastFileVersion.getPath())) {
          fileVersion.setStatus(FileStatus.RENAMED);
        } else {
          fileVersion.setStatus(FileStatus.CHANGED);
        }
      }

      return fileVersion;
    }
  private void updateTable(LsFolderRequest lsRequest, LsFolderResponse lsResponse) {
    logger.log(
        Level.INFO,
        "Detail panel: Updating table with "
            + lsResponse.getResult().getFileVersions().size()
            + " file versions ...");

    // Add new file version items
    List<PartialFileHistory> fileVersions =
        new ArrayList<>(lsResponse.getResult().getFileVersions().values());
    selectedFileHistory = fileVersions.get(0);

    for (FileVersion fileVersion : selectedFileHistory.getFileVersions().values()) {
      String checksumStr =
          (fileVersion.getChecksum() != null) ? fileVersion.getChecksum().toString() : "";

      TableItem tableItem = new TableItem(historyTable, SWT.NONE);

      tableItem.setData(fileVersion);
      tableItem.setImage(COLUMN_INDEX_STATUS, getStatusImage(fileVersion.getStatus()));
      tableItem.setText(COLUMN_INDEX_PATH, fileVersion.getPath());
      tableItem.setText(COLUMN_INDEX_VERSION, Long.toString(fileVersion.getVersion()));
      tableItem.setText(COLUMN_INDEX_TYPE, fileVersion.getType().toString());
      tableItem.setText(COLUMN_INDEX_SIZE, FileUtil.formatFileSize(fileVersion.getSize()));
      tableItem.setText(COLUMN_INDEX_POSIX_PERMS, fileVersion.getPosixPermissions());
      tableItem.setText(COLUMN_INDEX_DOS_ATTRS, fileVersion.getDosAttributes());
      tableItem.setText(COLUMN_INDEX_CHECKSUM, checksumStr);
      tableItem.setText(COLUMN_INDEX_LAST_MODIFIED, "" + fileVersion.getLastModified());
      tableItem.setText(COLUMN_INDEX_UPDATED, "" + fileVersion.getUpdated());
    }

    if (historyTable.getItemCount() > 0) {
      restoreButton.setEnabled(false);
      historyTable.select(historyTable.getItemCount() - 1);
    }

    resizeColumns();
  }
  @Test
  public void testChangedModifiedDate() throws Exception {
    // Setup
    Connection testConnection = TestConfigUtil.createTestLocalConnection();

    TestClient clientA = new TestClient("A", testConnection);
    TestClient clientB = new TestClient("B", testConnection);

    // Run

    // A, create two files with identical content and change mod. date of one of them
    clientA.createNewFile("A-file1.jpg", 50 * 1024);
    clientA.copyFile("A-file1.jpg", "A-file1-with-different-modified-date.jpg");
    clientA.getLocalFile("A-file1.jpg").setLastModified(0);
    clientA.up();

    // B, down, then move BOTH files
    clientB.down();
    assertFileListEquals(clientA.getLocalFiles(), clientB.getLocalFiles());
    assertDatabaseFileEquals(
        clientA.getLocalDatabaseFile(),
        clientB.getLocalDatabaseFile(),
        clientA.getConfig().getTransformer());

    clientB.moveFile("A-file1.jpg", "A-file1-moved.jpg");
    clientB.moveFile(
        "A-file1-with-different-modified-date.jpg",
        "A-file1-with-different-modified-date-moved.jpg");
    clientB.up();

    Database clientDatabaseB = clientB.loadLocalDatabase();

    PartialFileHistory file1Orig = clientDatabaseB.getFileHistory("A-file1-moved.jpg");
    PartialFileHistory file1WithDiffLastModDate =
        clientDatabaseB.getFileHistory("A-file1-with-different-modified-date-moved.jpg");

    assertNotNull(file1Orig);
    assertNotNull(file1WithDiffLastModDate);

    FileVersion fileVersion1OrigV1 = file1Orig.getFileVersion(1);
    FileVersion fileVersion1OrigV2 = file1Orig.getFileVersion(2);

    FileVersion fileVersion1WithDiffLastModDateV1 = file1WithDiffLastModDate.getFileVersion(1);
    FileVersion fileVersion1WithDiffLastModDateV2 = file1WithDiffLastModDate.getFileVersion(2);

    assertNotNull(fileVersion1OrigV1);
    assertNotNull(fileVersion1OrigV2);
    assertNotNull(fileVersion1WithDiffLastModDateV1);
    assertNotNull(fileVersion1WithDiffLastModDateV2);

    assertEquals("A-file1.jpg", fileVersion1OrigV1.getName());
    assertEquals("A-file1-moved.jpg", fileVersion1OrigV2.getName());
    assertEquals(
        "A-file1-with-different-modified-date.jpg", fileVersion1WithDiffLastModDateV1.getName());
    assertEquals(
        "A-file1-with-different-modified-date-moved.jpg",
        fileVersion1WithDiffLastModDateV2.getName());

    // Tear down
    clientA.cleanup();
    clientB.cleanup();
  }