@@ -215,7 +215,19 @@ public void renameOrMoveWebDavResource(String sourcePath, String destinationPath
215215 .method ("MOVE" , null ) // "MOVE" is the HTTP method
216216 .header ("Destination" , destinationCi .URL ); // New URI for the resource
217217
218- // Add Overwrite header
218+ // Use delete-then-move strategy to avoid HTTP 409 conflicts
219+ if (overwrite ) {
220+ try {
221+ // Try to delete the destination file first if it exists
222+ deleteFileIfExists (destinationCi );
223+ } catch (Exception e ) {
224+ // Ignore deletion errors - the file might not exist or we might not have permission
225+ // The MOVE operation will fail if the destination can't be overwritten
226+ Log .d ("WebDavStorage" , "Failed to delete destination file before move (this may be normal): " + e .getMessage ());
227+ }
228+ }
229+
230+ // Add Overwrite header (but don't rely on it solely)
219231 if (overwrite ) {
220232 requestBuilder .header ("Overwrite" , "T" ); // 'T' for true
221233 } else {
@@ -235,7 +247,102 @@ public void renameOrMoveWebDavResource(String sourcePath, String destinationPath
235247 }
236248 else
237249 {
238- throw new Exception ("Rename/Move failed for " + sourceCi .URL + " to " + destinationCi .URL + ": " + response .code () + " " + response .message ());
250+ int statusCode = response .code ();
251+ String errorMessage = "Rename/Move failed for " + sourceCi .URL + " to " + destinationCi .URL + ": " + statusCode + " " + response .message ();
252+
253+ // If we get a 409 conflict and overwrite is true, try retry with enhanced cleanup
254+ if (overwrite && statusCode == 409 ) {
255+ try {
256+ response .close ();
257+ // Force delete destination and retry
258+ deleteFileIfExists (destinationCi );
259+ // Small delay to ensure server processes the deletion
260+ Thread .sleep (100 );
261+
262+ // Retry the MOVE operation
263+ Response retryResponse = getClient (sourceCi ).newCall (request ).execute ();
264+ if (retryResponse .isSuccessful ()) {
265+ retryResponse .close ();
266+ return ; // Success on retry
267+ } else {
268+ errorMessage = "Rename/Move failed even after retry for " + sourceCi .URL + " to " + destinationCi .URL + ": " + retryResponse .code () + " " + retryResponse .message ();
269+ retryResponse .close ();
270+ }
271+ } catch (Exception retryException ) {
272+ errorMessage = "Rename/Move failed and retry attempt also failed: " + errorMessage + " (Retry error: " + retryException .getMessage () + ")" ;
273+ }
274+ }
275+
276+ throw new Exception (errorMessage );
277+ }
278+ }
279+
280+ /**
281+ * Helper method to delete a file if it exists
282+ * Uses PROPFIND to check existence first to avoid errors on non-existent files
283+ */
284+ private void deleteFileIfExists (ConnectionInfo ci ) throws Exception {
285+ try {
286+ // First check if file exists using PROPFIND
287+ if (fileExists (ci )) {
288+ // File exists, proceed with deletion
289+ Request request = new Request .Builder ()
290+ .url (new URL (ci .URL ))
291+ .delete ()
292+ .build ();
293+ Response response = getClient (ci ).newCall (request ).execute ();
294+ try {
295+ // Accept 200 OK, 204 No Content, or 404 Not Found (already deleted)
296+ if (!response .isSuccessful () && response .code () != 404 ) {
297+ throw new Exception ("Delete failed with status: " + response .code () + " " + response .message ());
298+ }
299+ } finally {
300+ response .close ();
301+ }
302+ }
303+ } catch (FileNotFoundException e ) {
304+ // File doesn't exist, which is fine
305+ Log .d ("WebDavStorage" , "File does not exist, no deletion needed: " + ci .URL );
306+ }
307+ }
308+
309+ /**
310+ * Helper method to check if a file exists using PROPFIND
311+ */
312+ private boolean fileExists (ConnectionInfo ci ) throws Exception {
313+ try {
314+ Request request = new Request .Builder ()
315+ .url (new URL (ci .URL ))
316+ .method ("PROPFIND" , RequestBody .create (MediaType .parse ("application/xml" ),
317+ "<?xml version=\" 1.0\" encoding=\" utf-8\" ?>\n " +
318+ "<D:propfind xmlns:D=\" DAV:\" >\n " +
319+ " <D:prop>\n " +
320+ " <D:resourcetype/>\n " +
321+ " </D:prop>\n " +
322+ "</D:propfind>" ))
323+ .header ("Depth" , "0" )
324+ .header ("Content-Type" , "application/xml" )
325+ .build ();
326+
327+ Response response = getClient (ci ).newCall (request ).execute ();
328+ try {
329+ // 200 OK means file exists, 404 means it doesn't exist
330+ if (response .isSuccessful ()) {
331+ return true ;
332+ } else if (response .code () == 404 ) {
333+ return false ;
334+ } else {
335+ // For other status codes, assume file exists to be safe
336+ Log .w ("WebDavStorage" , "Unexpected status checking file existence: " + response .code () + " for " + ci .URL );
337+ return true ;
338+ }
339+ } finally {
340+ response .close ();
341+ }
342+ } catch (Exception e ) {
343+ // If PROPFIND fails, assume file exists to be safe
344+ Log .w ("WebDavStorage" , "Error checking file existence, assuming it exists: " + e .getMessage ());
345+ return true ;
239346 }
240347 }
241348
@@ -307,7 +414,7 @@ public long contentLength() {
307414 else
308415 {
309416 requestBody = RequestBody .create (data , MediaType .parse ("application/binary" ));
310- }
417+ }
311418
312419 Request request = new Request .Builder ()
313420 .url (new URL (ci .URL ))
@@ -645,4 +752,3 @@ public void prepareFileUsage(Context appContext, String path) {
645752 }
646753
647754}
648-
0 commit comments