add support for transactional upload

This commit is contained in:
Philipp Crocoll
2025-07-15 12:18:45 +02:00
parent 913222d7cb
commit cfb5098b38
2 changed files with 68 additions and 6 deletions

View File

@@ -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);

View File

@@ -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;