Skip to content

Commit

Permalink
Merge pull request #528 from JustFanta01/feature/preserve-mod-date-main
Browse files Browse the repository at this point in the history
Preserve Last Modification Date
  • Loading branch information
SailReal authored Apr 22, 2024
2 parents 91478b8 + 4dbcdb8 commit 1ef6bfe
Show file tree
Hide file tree
Showing 19 changed files with 143 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.dropbox.core.v2.files.Metadata
internal object DropboxCloudNodeFactory {

fun from(parent: DropboxFolder, metadata: FileMetadata): DropboxFile {
return DropboxFile(parent, metadata.name, metadata.pathDisplay, metadata.size, metadata.serverModified)
return DropboxFile(parent, metadata.name, metadata.pathDisplay, metadata.size, metadata.clientModified)
}

@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import org.cryptomator.util.file.LruFileCacheUtil.Companion.retrieveFromLruCache
import java.io.File
import java.io.IOException
import java.io.OutputStream
import java.util.Date
import timber.log.Timber

internal class DropboxImpl(cloud: DropboxCloud, context: Context) {
Expand Down Expand Up @@ -167,6 +168,7 @@ internal class DropboxImpl(cloud: DropboxCloud, context: Context) {
client() //
.files() //
.uploadBuilder(file.path) //
.withClientModified(data.modifiedDate(context).orElse(Date())) //
.withMode(writeMode) //
.uploadAndFinish(it)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ internal object OnedriveCloudNodeFactory {
}

private fun lastModified(item: DriveItem): Date? {
return item.lastModifiedDateTime?.let {
return Date.from(it.toInstant())
}
return item.fileSystemInfo?.lastModifiedDateTime?.let { clientDate -> Date.from(clientDate.toInstant()) }
?: item.lastModifiedDateTime?.let { serverDate -> Date.from(serverDate.toInstant()) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.microsoft.graph.http.GraphServiceException
import com.microsoft.graph.models.DriveItem
import com.microsoft.graph.models.DriveItemCreateUploadSessionParameterSet
import com.microsoft.graph.models.DriveItemUploadableProperties
import com.microsoft.graph.models.FileSystemInfo
import com.microsoft.graph.models.Folder
import com.microsoft.graph.models.ItemReference
import com.microsoft.graph.options.Option
Expand Down Expand Up @@ -39,6 +40,8 @@ import org.cryptomator.util.file.LruFileCacheUtil.Companion.retrieveFromLruCache
import java.io.File
import java.io.IOException
import java.io.OutputStream
import java.time.OffsetDateTime
import java.time.ZoneId
import java.util.Date
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
Expand Down Expand Up @@ -202,14 +205,21 @@ internal class OnedriveImpl(private val cloud: OnedriveCloud, private val client
}
progressAware.onProgress(Progress.completed(UploadState.upload(file)))
return try {
OnedriveCloudNodeFactory.file(file.parent, result.get(), Date())
val driveItem: DriveItem = result.get()
val lastModifiedDate = getLastModifiedDateTime(driveItem) ?: Date()
OnedriveCloudNodeFactory.file(file.parent, driveItem, lastModifiedDate)
} catch (e: ExecutionException) {
throw FatalBackendException(e)
} catch (e: InterruptedException) {
throw FatalBackendException(e)
}
}

private fun getLastModifiedDateTime(driveItem: DriveItem): Date? {
return driveItem.fileSystemInfo?.lastModifiedDateTime?.let { clientDate -> Date.from(clientDate.toInstant()) }
?: driveItem.lastModifiedDateTime?.let { serverDate -> Date.from(serverDate.toInstant()) }
}

@Throws(NoSuchCloudFileException::class)
private fun uploadFile(file: OnedriveFile, data: DataSource, progressAware: ProgressAware<UploadState>, result: CompletableFuture<DriveItem>, conflictBehaviorOption: Option, size: Long) {
data.open(context)?.use { inputStream ->
Expand All @@ -228,12 +238,26 @@ internal class OnedriveImpl(private val cloud: OnedriveCloud, private val client
.putAsync(CopyStream.toByteArray(it)) //
.whenComplete { driveItem, error ->
run {
if (error == null) {
val modifiedDate = data.modifiedDate(context)
if (error != null) {
result.completeExceptionally(error)
return@whenComplete
}
if (modifiedDate.isPresent) {
patchAsyncLastModifiedDate(parentNodeInfo, driveItem, modifiedDate.get())
.whenComplete { driveItem, error ->
if (error == null) {
progressAware.onProgress(Progress.completed(UploadState.upload(file)))
result.complete(driveItem)
cacheNodeInfo(file, driveItem)
} else {
result.completeExceptionally(error)
}
}
} else { // current date is the default, no need to patch()
progressAware.onProgress(Progress.completed(UploadState.upload(file)))
result.complete(driveItem)
cacheNodeInfo(file, driveItem)
} else {
result.completeExceptionally(error)
}
}
}
Expand All @@ -244,13 +268,32 @@ internal class OnedriveImpl(private val cloud: OnedriveCloud, private val client
} ?: throw FatalBackendException("InputStream shouldn't bee null")
}

private fun patchAsyncLastModifiedDate(parentNodeInfo: OnedriveIdCache.NodeInfo, driveItem: DriveItem, modifiedDate: Date): CompletableFuture<DriveItem> {
val diffItem = DriveItem()
diffItem.fileSystemInfo = FileSystemInfo()
diffItem.fileSystemInfo!!.lastModifiedDateTime = OffsetDateTime.ofInstant(modifiedDate.toInstant(), ZoneId.systemDefault())
return drive(parentNodeInfo.driveId) //
.items(driveItem.id!!) //
.buildRequest() //
.patchAsync(diffItem) //
}

@Throws(IOException::class, NoSuchCloudFileException::class)
private fun chunkedUploadFile(file: OnedriveFile, data: DataSource, progressAware: ProgressAware<UploadState>, result: CompletableFuture<DriveItem>, conflictBehaviorOption: Option, size: Long) {
val parentNodeInfo = requireNodeInfo(file.parent)

val props = DriveItemUploadableProperties()
val modifiedDate = data.modifiedDate(context)

if (modifiedDate.isPresent) {
props.fileSystemInfo = FileSystemInfo()
props.fileSystemInfo!!.lastModifiedDateTime = OffsetDateTime.ofInstant(modifiedDate.get().toInstant(), ZoneId.systemDefault())
}

drive(parentNodeInfo.driveId) //
.items(parentNodeInfo.id) //
.itemWithPath(file.name) //
.createUploadSession(DriveItemCreateUploadSessionParameterSet.newBuilder().withItem(DriveItemUploadableProperties()).build()) //
.createUploadSession(DriveItemCreateUploadSessionParameterSet.newBuilder().withItem(props).build()) //
.buildRequest() //
.post()?.let { uploadSession ->
data.open(context)?.use { inputStream ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ internal class PCloudImpl(private val cloud: PCloud, private val client: ApiClie
}
return try {
client //
.createFile(file.parent.path, file.name, pCloudDataSource, Date(), listener, uploadOptions) //
.createFile(file.parent.path, file.name, pCloudDataSource, data.modifiedDate(context).orElse(Date()), listener, uploadOptions) //
.execute()
} catch (ex: ApiError) {
handleApiError(ex, file.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.cryptomator.data.cloud.googledrive
import android.content.Context
import com.google.api.client.googleapis.json.GoogleJsonResponseException
import com.google.api.client.http.HttpResponseException
import com.google.api.client.util.DateTime
import com.google.api.services.drive.Drive
import com.google.api.services.drive.model.File
import com.google.api.services.drive.model.Revision
Expand All @@ -25,6 +26,7 @@ import org.cryptomator.util.file.LruFileCacheUtil
import org.cryptomator.util.file.LruFileCacheUtil.Companion.retrieveFromLruCache
import java.io.IOException
import java.io.OutputStream
import java.util.Date
import timber.log.Timber

internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCloud, idCache: GoogleDriveIdCache) {
Expand Down Expand Up @@ -203,6 +205,7 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
val metadata = File()
metadata.name = file.name
progressAware.onProgress(Progress.started(UploadState.upload(file)))
metadata.setModifiedTime(DateTime(data.modifiedDate(context).orElse(Date())))
val uploadedFile = if (file.driveId != null && replace) {
updateFile(file, data, progressAware, size, metadata)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ abstract class CryptoImplDecorator(
progressAware.onProgress(Progress.progress(UploadState.encryption(cryptoFile)).between(0).and(ciphertextSize).withValue(encrypted))
}
encryptingWritableByteChannel.close()
data.modifiedDate(context).ifPresent { encryptedTmpFile.setLastModified(it.time) }
progressAware.onProgress(Progress.completed(UploadState.encryption(cryptoFile)))
return writeFromTmpFile(data, cryptoFile, encryptedTmpFile, progressAware, replace)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ open class CryptoImplVaultFormat7 : CryptoImplDecorator {
progressAware.onProgress(Progress.progress(UploadState.encryption(cloudFile)).between(0).and(ciphertextSize).withValue(encrypted))
}
encryptingWritableByteChannel.close()
data.modifiedDate(context).ifPresent { encryptedTmpFile.setLastModified(it.time) }
progressAware.onProgress(Progress.completed(UploadState.encryption(cloudFile)))
val targetFile = targetFile(cryptoFile, cloudFile, replace)
return file(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.cryptomator.domain.usecases.cloud.Progress
import org.cryptomator.domain.usecases.cloud.UploadState
import java.io.IOException
import java.io.OutputStream
import java.util.Date
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull

internal class WebDavImpl(private val cloud: WebDavCloud, private val connectionHandler: ConnectionHandlerHandlerImpl, private val context: Context) {
Expand Down Expand Up @@ -133,7 +134,7 @@ internal class WebDavImpl(private val cloud: WebDavCloud, private val connection
)
}
}.use {
connectionHandler.writeFile(absoluteUriFrom(uploadFile.path), it)
connectionHandler.writeFile(absoluteUriFrom(uploadFile.path), it, data.modifiedDate(context).orElse(Date()))
}
} ?: throw FatalBackendException("InputStream shouldn't bee null")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.cryptomator.data.cloud.webdav.WebDavNode
import org.cryptomator.domain.CloudFolder
import org.cryptomator.domain.exception.BackendException
import java.io.InputStream
import java.util.Date
import javax.inject.Inject

class ConnectionHandlerHandlerImpl @Inject internal constructor(httpClient: WebDavCompatibleHttpClient) {
Expand All @@ -27,8 +28,8 @@ class ConnectionHandlerHandlerImpl @Inject internal constructor(httpClient: WebD
}

@Throws(BackendException::class)
fun writeFile(url: String, inputStream: InputStream) {
webDavClient.writeFile(url, inputStream)
fun writeFile(url: String, inputStream: InputStream, modifiedDate: Date) {
webDavClient.writeFile(url, inputStream, modifiedDate)
}

@Throws(BackendException::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import java.io.IOException
import java.io.InputStream
import java.net.HttpURLConnection
import java.util.Collections
import java.util.Date
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
Expand Down Expand Up @@ -150,8 +151,9 @@ internal class WebDavClient(private val httpClient: WebDavCompatibleHttpClient)
}

@Throws(BackendException::class)
fun writeFile(url: String, inputStream: InputStream) {
fun writeFile(url: String, inputStream: InputStream, modifiedDate: Date) {
val builder = Request.Builder() //
.addHeader("X-OC-Mtime", modifiedDate.toInstant().toEpochMilli().div(1000).toString()) //
.put(InputStreamSourceBasedRequestBody.from(inputStream)) //
.url(url)
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.cryptomator.domain.usecases.cloud

import android.content.Context
import org.cryptomator.util.Optional
import java.io.ByteArrayInputStream
import java.io.IOException
import java.io.InputStream
import java.util.Date

class ByteArrayDataSource private constructor(private val bytes: ByteArray) : DataSource {

Expand All @@ -25,6 +27,10 @@ class ByteArrayDataSource private constructor(private val bytes: ByteArray) : Da
// do nothing because ByteArrayInputStream need no close
}

override fun modifiedDate(context: Context): Optional<Date> {
return Optional.empty()
}

companion object {

@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package org.cryptomator.domain.usecases.cloud

import android.content.Context
import org.cryptomator.domain.exception.CancellationException
import org.cryptomator.util.Optional
import java.io.IOException
import java.io.InputStream
import java.util.Date

class CancelAwareDataSource private constructor(private val delegate: DataSource, private val cancelled: Flag) : DataSource {

Expand Down Expand Up @@ -31,6 +33,13 @@ class CancelAwareDataSource private constructor(private val delegate: DataSource
delegate.close()
}

override fun modifiedDate(context: Context): Optional<Date> {
if (cancelled.get()) {
throw CancellationException()
}
return delegate.modifiedDate(context)
}

companion object {

@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.cryptomator.domain.usecases.cloud

import android.content.Context
import org.cryptomator.util.Optional
import java.io.Closeable
import java.io.IOException
import java.io.InputStream
import java.io.Serializable
import java.util.Date

interface DataSource : Serializable, Closeable {

Expand All @@ -15,4 +17,6 @@ interface DataSource : Serializable, Closeable {

fun decorate(delegate: DataSource): DataSource

fun modifiedDate(context: Context): Optional<Date>

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.cryptomator.domain.usecases.cloud

import android.content.Context
import org.cryptomator.util.Optional
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStream
import java.util.Date

class FileBasedDataSource private constructor(private val file: File) : DataSource {

Expand All @@ -26,6 +28,10 @@ class FileBasedDataSource private constructor(private val file: File) : DataSour
// Do nothing
}

override fun modifiedDate(context: Context): Optional<Date> {
return Optional.of(Date(file.lastModified()))
}

companion object {

@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ private File copyDataToFile(DataSource dataSource) {
InputStream in = CancelAwareDataSource.wrap(dataSource, cancelledFlag).open(context);
OutputStream out = new FileOutputStream(target);
copy(in, out);
dataSource.modifiedDate(context).ifPresent(value -> target.setLastModified(value.getTime()));
return target;
} catch (IOException e) {
throw new FatalBackendException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.cryptomator.domain.CloudNode
import org.cryptomator.domain.exception.BackendException
import org.cryptomator.domain.repository.CloudContentRepository
import org.cryptomator.domain.usecases.ProgressAware
import org.cryptomator.util.Optional
import org.hamcrest.CoreMatchers
import org.hamcrest.MatcherAssert
import org.junit.jupiter.params.ParameterizedTest
Expand All @@ -21,6 +22,7 @@ import java.io.ByteArrayInputStream
import java.io.IOException
import java.io.InputStream
import java.util.Arrays
import java.util.Date

class UploadFileTest {

Expand Down Expand Up @@ -105,6 +107,10 @@ class UploadFileTest {
return delegate
}

override fun modifiedDate(context: Context): Optional<Date> {
return Optional.of(Date())
}

@Throws(IOException::class)
override fun close() {
// do nothing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import android.content.Context
import android.net.Uri
import org.cryptomator.domain.usecases.cloud.DataSource
import org.cryptomator.presentation.util.ContentResolverUtil
import org.cryptomator.util.Optional
import java.io.IOException
import java.io.InputStream
import java.util.Date

class UriBasedDataSource private constructor(private val uri: Uri) : DataSource {

Expand All @@ -27,6 +29,10 @@ class UriBasedDataSource private constructor(private val uri: Uri) : DataSource
// do nothing
}

override fun modifiedDate(context: Context): Optional<Date> {
return Optional.ofNullable(ContentResolverUtil(context).fileModifiedDate(uri))
}

companion object {

@JvmStatic
Expand Down
Loading

0 comments on commit 1ef6bfe

Please sign in to comment.