diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/WebDavStorage.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/WebDavStorage.java index 37960f2d..fb8ec597 100644 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/WebDavStorage.java +++ b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/WebDavStorage.java @@ -1,6 +1,7 @@ package keepass2android.javafilestorage; import android.content.Context; +import java.math.BigInteger; import android.content.Intent; import android.net.Uri; @@ -27,6 +28,7 @@ import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -194,11 +196,73 @@ public class WebDavStorage extends JavaFileStorageBase { return client; } + public void renameOrMoveWebDavResource(String sourcePath, String destinationPath, boolean overwrite) throws Exception { + + ConnectionInfo sourceCi = splitStringToConnectionInfo(sourcePath); + ConnectionInfo destinationCi = splitStringToConnectionInfo(destinationPath); + + Request.Builder requestBuilder = new Request.Builder() + .url(new URL(sourceCi.URL)) + .method("MOVE", null) // "MOVE" is the HTTP method + .header("Destination", destinationCi.URL); // New URI for the resource + + // Add Overwrite header + if (overwrite) { + requestBuilder.header("Overwrite", "T"); // 'T' for true + } else { + requestBuilder.header("Overwrite", "F"); // 'F' for false + } + + Request request = requestBuilder.build(); + + Response response = getClient(sourceCi).newCall(request).execute(); + + // Check the status code + if (response.isSuccessful()) { + // WebDAV MOVE can return 201 (Created) if a new resource was created at dest, + // or 204 (No Content) if moved to a pre-existing destination (e.g., just renamed). + // A 200 OK might also be returned by some servers, though 201/204 are more common. + + } + else + { + throw new Exception("Rename/Move failed for " + sourceCi.URL + " to " + destinationCi.URL + ": " + response.code() + " " + response.message()); + } + } + + public static String generateRandomHexString(int length) { + SecureRandom secureRandom = new SecureRandom(); + // Generate enough bytes to ensure we can get the desired number of hex characters. + // Each byte converts to two hex characters. + // For 8 hex characters, we need 4 bytes. + int numBytes = (int) Math.ceil(length / 2.0); + byte[] randomBytes = new byte[numBytes]; + secureRandom.nextBytes(randomBytes); + + // Convert the byte array to a hexadecimal string + // BigInteger(1, randomBytes) treats the byte array as a positive number. + // toString(16) converts it to a hexadecimal string. + String hexString = new BigInteger(1, randomBytes).toString(16); + + // Pad with leading zeros if necessary (e.g., if the generated number is small) + // and then take the first 'length' characters. + // Using String.format to ensure leading zeros if the hexString is shorter. + return String.format("%0" + length + "d", new BigInteger(hexString, 16)).substring(0, length); + } @Override public void uploadFile(String path, byte[] data, boolean writeTransactional) throws Exception { + if (writeTransactional) + { + String randomSuffix = ".tmp." + generateRandomHexString(8); + uploadFile(path + randomSuffix, data, false); + renameOrMoveWebDavResource(path+randomSuffix, path, true); + return; + } + + try { ConnectionInfo ci = splitStringToConnectionInfo(path); @@ -245,8 +309,6 @@ public class WebDavStorage extends JavaFileStorageBase { .build(); - //TODO consider writeTransactional - //TODO check for error Response response = getClient(ci).newCall(request).execute(); checkStatus(response); diff --git a/src/java/JavaFileStorageTest-AS/app/src/main/java/com/crocoapps/javafilestoragetest2/MainActivity.java b/src/java/JavaFileStorageTest-AS/app/src/main/java/com/crocoapps/javafilestoragetest2/MainActivity.java index 1c81a604..5e8fcc35 100644 --- a/src/java/JavaFileStorageTest-AS/app/src/main/java/com/crocoapps/javafilestoragetest2/MainActivity.java +++ b/src/java/JavaFileStorageTest-AS/app/src/main/java/com/crocoapps/javafilestoragetest2/MainActivity.java @@ -548,7 +548,7 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag //storageToTest = new GoogleDriveAppDataFileStorage(); - /*storageToTest = new WebDavStorage(new ICertificateErrorHandler() { + storageToTest = new WebDavStorage(new ICertificateErrorHandler() { @Override public boolean onValidationError(String error) { return false; @@ -558,12 +558,12 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag public boolean alwaysFailOnValidationError() { return false; } - }); -*/ + }, 64*1024); + //storageToTest = new DropboxV2Storage(ctx,"4ybka4p4a1027n6", "1z5lv528un9nre8", !simulateRestart); //storageToTest = new DropboxFileStorage(ctx,"4ybka4p4a1027n6", "1z5lv528un9nre8", !simulateRestart); //storageToTest = new DropboxAppFolderFileStorage(ctx,"ax0268uydp1ya57", "3s86datjhkihwyc", true); - storageToTest = new GoogleDriveFullFileStorage(); + // storageToTest = new GoogleDriveFullFileStorage(); return storageToTest;