From c7fe8f7eb8e6c6af2ad1398d334b634d6e422d3a Mon Sep 17 00:00:00 2001 From: David Kocher Date: Thu, 7 Aug 2025 12:32:06 +0200 Subject: [PATCH 01/20] Add tests. --- .../java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java | 1 + .../ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java | 3 +++ .../ch/cyberduck/core/onedrive/SharepointWriteFeatureTest.java | 3 +++ 3 files changed, 7 insertions(+) diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java index a5646f39d33..cf3d80cc541 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java @@ -110,6 +110,7 @@ public void testMove() throws BackgroundException { final PathAttributes renamedAttributes = attributesFinder.find(rename); assertNotNull(renamedAttributes); assertEquals(attributes, renamedAttributes); + assertEquals(attributes.getFileId(), renamedAttributes.getFileId()); assertNotEquals(attributes.getETag(), renamedAttributes.getETag()); assertEquals(target.attributes().getETag(), renamedAttributes.getETag()); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java index 61cc5f6c15d..ae8075aa770 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java @@ -90,6 +90,9 @@ public void testWrite() throws Exception { assertEquals(content.length, IOUtils.copyLarge(new ByteArrayInputStream(content), overwrite)); overwrite.close(); assertEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus()), new GraphAttributesFinderFeature(session, fileid).find(file)); + assertNotEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()), new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus())); + assertEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()).getFileId(), new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus()).getFileId()); + assertNotEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()).getETag(), new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus()).getETag()); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointWriteFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointWriteFeatureTest.java index 429f61a3f6c..6d3473866e4 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointWriteFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointWriteFeatureTest.java @@ -92,6 +92,9 @@ public void testWrite() throws Exception { assertEquals(content.length, IOUtils.copyLarge(new ByteArrayInputStream(content), overwrite)); overwrite.close(); assertEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus()), new GraphAttributesFinderFeature(session, fileid).find(file)); + assertNotEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()), new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus())); + assertEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()).getFileId(), new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus()).getFileId()); + assertNotEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()).getETag(), new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus()).getETag()); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } From dfef899fa5c5f2ed51ecc6fb1bce71b0c2a3decb Mon Sep 17 00:00:00 2001 From: David Kocher Date: Thu, 7 Aug 2025 12:58:21 +0200 Subject: [PATCH 02/20] Add UUID. --- core/src/main/java/ch/cyberduck/core/io/Checksum.java | 5 +++++ core/src/main/java/ch/cyberduck/core/io/HashAlgorithm.java | 3 ++- core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/ch/cyberduck/core/io/Checksum.java b/core/src/main/java/ch/cyberduck/core/io/Checksum.java index fb53d418242..34ad774f354 100644 --- a/core/src/main/java/ch/cyberduck/core/io/Checksum.java +++ b/core/src/main/java/ch/cyberduck/core/io/Checksum.java @@ -83,6 +83,11 @@ public static Checksum parse(final String hash) { return new Checksum(HashAlgorithm.md5, hash); } break; + case 36: + if(hash.matches("[a-fA-F0-9-]{36}")) { + return new Checksum(HashAlgorithm.uuid, hash); + } + break; case 40: if(hash.matches("[a-fA-F0-9]{40}")) { return new Checksum(HashAlgorithm.sha1, hash); diff --git a/core/src/main/java/ch/cyberduck/core/io/HashAlgorithm.java b/core/src/main/java/ch/cyberduck/core/io/HashAlgorithm.java index 074ff498fbc..6e07c413ed2 100644 --- a/core/src/main/java/ch/cyberduck/core/io/HashAlgorithm.java +++ b/core/src/main/java/ch/cyberduck/core/io/HashAlgorithm.java @@ -27,7 +27,8 @@ public enum HashAlgorithm { sha512, crc32, cdash64, - dropbox_content_hash; + dropbox_content_hash, + uuid; @Override public String toString() { diff --git a/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java b/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java index 05dc16f3943..14aa13b3515 100644 --- a/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java +++ b/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java @@ -40,5 +40,7 @@ public void testParse() { Checksum.parse("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")); assertEquals(new Checksum(HashAlgorithm.crc32, "d202ef8d"), Checksum.parse("d202ef8d")); + assertEquals(new Checksum(HashAlgorithm.uuid, "B9739CF2-E4DB-4439-BCD3-56B44A02516E"), + Checksum.parse("B9739CF2-E4DB-4439-BCD3-56B44A02516E")); } } From e2a8f378b14dcd5b39b22bb971d20998b7531188 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Thu, 7 Aug 2025 13:07:24 +0200 Subject: [PATCH 03/20] Set UUID as checksum to compare file changes. --- .../ch/cyberduck/core/onedrive/GraphProtocol.java | 11 ++++++++++- .../features/GraphAttributesFinderFeature.java | 7 +++++++ .../onedrive/GraphAttributesFinderFeatureTest.java | 5 +++++ .../cyberduck/core/onedrive/GraphMoveFeatureTest.java | 2 ++ .../core/onedrive/OneDriveWriteFeatureTest.java | 11 +++++++---- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphProtocol.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphProtocol.java index fe281f6fbd6..179389ea051 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphProtocol.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphProtocol.java @@ -18,8 +18,14 @@ import ch.cyberduck.core.AbstractProtocol; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Scheme; +import ch.cyberduck.core.synchronization.ChainedComparisonService; +import ch.cyberduck.core.synchronization.ChecksumComparisonService; +import ch.cyberduck.core.synchronization.Comparison; import ch.cyberduck.core.synchronization.ComparisonService; import ch.cyberduck.core.synchronization.DefaultComparisonService; +import ch.cyberduck.core.synchronization.TimestampComparisonService; + +import java.util.EnumSet; public abstract class GraphProtocol extends AbstractProtocol { @Override @@ -72,7 +78,10 @@ public VersioningMode getVersioningMode() { @SuppressWarnings("unchecked") public T getFeature(final Class type) { if(type == ComparisonService.class) { - return (T) new DefaultComparisonService(DefaultComparisonService.forFiles(this), ComparisonService.disabled); + return (T) new DefaultComparisonService(new ChainedComparisonService(EnumSet.of(Comparison.unknown, Comparison.notequal), + new ChecksumComparisonService(), + new TimestampComparisonService() + ), ComparisonService.disabled); } return super.getFeature(type); } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java index d3737e37896..3232019cd2d 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.AttributesAdapter; import ch.cyberduck.core.features.AttributesFinder; +import ch.cyberduck.core.io.Checksum; import ch.cyberduck.core.onedrive.GraphExceptionMappingService; import ch.cyberduck.core.onedrive.GraphSession; import ch.cyberduck.core.webloc.UrlFileWriterFactory; @@ -38,6 +39,8 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class GraphAttributesFinderFeature implements AttributesFinder, AttributesAdapter { @@ -89,6 +92,10 @@ private DriveItem.Metadata toMetadata(final Path file, final DriveItem item) thr public PathAttributes toAttributes(final DriveItem.Metadata metadata) { final PathAttributes attributes = new PathAttributes(); attributes.setETag(metadata.getETag()); + final Matcher matcher = Pattern.compile("\"\\{([0-9A-Z-]+)\\},\\d+\"").matcher(metadata.getETag()); + if(matcher.matches()) { + attributes.setChecksum(Checksum.parse(StringUtils.lowerCase(matcher.group(1)))); + } Optional webUrl = getWebUrl(metadata); if(metadata.isPackage()) { webUrl.ifPresent(url -> attributes.setSize(UrlFileWriterFactory.get().write(url).getBytes(Charset.defaultCharset()).length)); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java index bbbd4887f3d..afff836b25a 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Home; +import ch.cyberduck.core.io.HashAlgorithm; import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; @@ -63,6 +64,8 @@ public void testFindFile() throws Exception { assertNotEquals(-1L, attributes.getCreationDate()); assertNotEquals(-1L, attributes.getModificationDate()); assertNotNull(attributes.getETag()); + assertNotNull(attributes.getChecksum()); + assertEquals(HashAlgorithm.uuid, attributes.getChecksum().algorithm); assertNull(attributes.getVersionId()); assertNotNull(attributes.getLink()); assertNotNull(attributes.getFileId()); @@ -79,6 +82,8 @@ public void testFindDirectory() throws Exception { assertNotEquals(-1L, attributes.getCreationDate()); assertNotEquals(-1L, attributes.getModificationDate()); assertNotNull(attributes.getETag()); + assertNotNull(attributes.getChecksum()); + assertEquals(HashAlgorithm.uuid, attributes.getChecksum().algorithm); assertNull(attributes.getVersionId()); assertNotNull(attributes.getLink()); assertNotNull(attributes.getFileId()); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java index cf3d80cc541..015c1764202 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java @@ -84,6 +84,7 @@ public void testRename() throws BackgroundException { assertEquals(attributes, target.attributes()); assertEquals(attributes.getFileId(), target.attributes().getFileId()); assertNotEquals(attributes.getETag(), attributesFinder.find(rename).getETag()); + assertEquals(attributes.getChecksum(), attributesFinder.find(rename).getChecksum()); assertEquals(target.attributes().getETag(), attributesFinder.find(rename).getETag()); delete.delete(Collections.singletonList(rename), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -112,6 +113,7 @@ public void testMove() throws BackgroundException { assertEquals(attributes, renamedAttributes); assertEquals(attributes.getFileId(), renamedAttributes.getFileId()); assertNotEquals(attributes.getETag(), renamedAttributes.getETag()); + assertEquals(attributes.getChecksum(), attributesFinder.find(rename).getChecksum()); assertEquals(target.attributes().getETag(), renamedAttributes.getETag()); delete.delete(Collections.singletonList(targetDirectory), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java index ae8075aa770..17d730c0837 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java @@ -89,10 +89,13 @@ public void testWrite() throws Exception { assertNotNull(overwrite); assertEquals(content.length, IOUtils.copyLarge(new ByteArrayInputStream(content), overwrite)); overwrite.close(); - assertEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus()), new GraphAttributesFinderFeature(session, fileid).find(file)); - assertNotEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()), new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus())); - assertEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()).getFileId(), new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus()).getFileId()); - assertNotEquals(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()).getETag(), new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus()).getETag()); + final PathAttributes overwriteAttr = new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus()); + assertEquals(overwriteAttr, new GraphAttributesFinderFeature(session, fileid).find(file)); + final PathAttributes sourceAttr = new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()); + assertNotEquals(sourceAttr, overwriteAttr); + assertEquals(sourceAttr.getFileId(), overwriteAttr.getFileId()); + assertNotEquals(sourceAttr.getETag(), overwriteAttr.getETag()); + assertNotEquals(sourceAttr.getChecksum(), overwriteAttr.getChecksum()); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } From fc84dbc8920f236cfa8040d5461a4fcdd9fb621e Mon Sep 17 00:00:00 2001 From: David Kocher Date: Thu, 7 Aug 2025 13:15:51 +0200 Subject: [PATCH 04/20] Review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- core/src/main/java/ch/cyberduck/core/io/Checksum.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/ch/cyberduck/core/io/Checksum.java b/core/src/main/java/ch/cyberduck/core/io/Checksum.java index 34ad774f354..8d810a9bb01 100644 --- a/core/src/main/java/ch/cyberduck/core/io/Checksum.java +++ b/core/src/main/java/ch/cyberduck/core/io/Checksum.java @@ -84,7 +84,7 @@ public static Checksum parse(final String hash) { } break; case 36: - if(hash.matches("[a-fA-F0-9-]{36}")) { + if(hash.matches("^[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}$")) { return new Checksum(HashAlgorithm.uuid, hash); } break; From 2cce356985aadd0dd1dd7b92c95f431a2e9ff7b9 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Thu, 7 Aug 2025 13:15:03 +0200 Subject: [PATCH 05/20] Review. --- .../core/onedrive/features/GraphAttributesFinderFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java index 3232019cd2d..28bd4dbc3e0 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java @@ -94,7 +94,7 @@ public PathAttributes toAttributes(final DriveItem.Metadata metadata) { attributes.setETag(metadata.getETag()); final Matcher matcher = Pattern.compile("\"\\{([0-9A-Z-]+)\\},\\d+\"").matcher(metadata.getETag()); if(matcher.matches()) { - attributes.setChecksum(Checksum.parse(StringUtils.lowerCase(matcher.group(1)))); + attributes.setChecksum(Checksum.parse(matcher.group(1))); } Optional webUrl = getWebUrl(metadata); if(metadata.isPackage()) { From 284686bcc393bc74a6f5c21bea44640776818219 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Thu, 7 Aug 2025 13:15:22 +0200 Subject: [PATCH 06/20] Add null check. --- .../onedrive/features/GraphAttributesFinderFeature.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java index 28bd4dbc3e0..b09c6d3c929 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java @@ -92,9 +92,11 @@ private DriveItem.Metadata toMetadata(final Path file, final DriveItem item) thr public PathAttributes toAttributes(final DriveItem.Metadata metadata) { final PathAttributes attributes = new PathAttributes(); attributes.setETag(metadata.getETag()); - final Matcher matcher = Pattern.compile("\"\\{([0-9A-Z-]+)\\},\\d+\"").matcher(metadata.getETag()); - if(matcher.matches()) { - attributes.setChecksum(Checksum.parse(matcher.group(1))); + if(null != metadata.getETag()) { + final Matcher matcher = Pattern.compile("\"\\{([0-9A-Z-]+)\\},\\d+\"").matcher(metadata.getETag()); + if(matcher.matches()) { + attributes.setChecksum(Checksum.parse(matcher.group(1))); + } } Optional webUrl = getWebUrl(metadata); if(metadata.isPackage()) { From 1105ed533e4cd184a695dc89a54afce4f73ebcad Mon Sep 17 00:00:00 2001 From: David Kocher Date: Thu, 7 Aug 2025 13:17:21 +0200 Subject: [PATCH 07/20] Extract constant. --- .../core/onedrive/features/GraphAttributesFinderFeature.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java index b09c6d3c929..f004fc39d28 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java @@ -48,6 +48,8 @@ public class GraphAttributesFinderFeature implements AttributesFinder, Attribute private final GraphSession session; private final GraphFileIdProvider fileid; + private static final Pattern ETAG_PATTERN = Pattern.compile("\"\\{([0-9A-Z-]+)\\},\\d+\""); + public GraphAttributesFinderFeature(final GraphSession session, final GraphFileIdProvider fileid) { this.session = session; this.fileid = fileid; @@ -93,7 +95,7 @@ public PathAttributes toAttributes(final DriveItem.Metadata metadata) { final PathAttributes attributes = new PathAttributes(); attributes.setETag(metadata.getETag()); if(null != metadata.getETag()) { - final Matcher matcher = Pattern.compile("\"\\{([0-9A-Z-]+)\\},\\d+\"").matcher(metadata.getETag()); + final Matcher matcher = ETAG_PATTERN.matcher(metadata.getETag()); if(matcher.matches()) { attributes.setChecksum(Checksum.parse(matcher.group(1))); } From facb3cd6baf4c079bbf1a3ed845303e942227254 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Thu, 7 Aug 2025 14:08:51 +0200 Subject: [PATCH 08/20] Rename. --- .../ch/cyberduck/core/onedrive/GraphItemListService.java | 2 +- .../main/java/ch/cyberduck/core/onedrive/GraphSession.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphItemListService.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphItemListService.java index 469a2242668..998b8d3ace7 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphItemListService.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphItemListService.java @@ -44,6 +44,6 @@ protected Iterator getIterator(final Path directory) throws log.debug("Return files for folder {}", folder); // getQuery(null): return new ODataQuery with default set of parameters // require listing Publication/VersionId - return Files.getFiles(folder, session.getQuery(null).top(HostPreferencesFactory.get(session.getHost()).getInteger("onedrive.listing.chunksize"))); + return Files.getFiles(folder, session.select(null).top(HostPreferencesFactory.get(session.getHost()).getInteger("onedrive.listing.chunksize"))); } } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java index 88597b3435e..d884c5173e1 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java @@ -80,7 +80,7 @@ protected GraphSession(final Host host, final X509TrustManager trust, final X509 public abstract String getFileId(final DriveItem.Metadata metadata); - public ODataQuery getQuery(ODataQuery query) { + public ODataQuery select(ODataQuery query) { if(query == null) { query = new ODataQuery(); } @@ -110,8 +110,8 @@ public DriveItem getItem(final Path currentPath) throws BackgroundException { return this.getItem(currentPath, true); } - public DriveItem.Metadata getMetadata(final DriveItem item, ODataQuery query) throws IOException { - return item.getMetadata(getQuery(query)); + public DriveItem.Metadata getMetadata(final DriveItem item, final ODataQuery query) throws IOException { + return item.getMetadata(this.select(query)); } public abstract DriveItem getItem(final Path file, final boolean resolveLastItem) throws BackgroundException; From 4fced78703c5b15d5a2ab303c39a85fdd66fdc87 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Thu, 7 Aug 2025 14:10:57 +0200 Subject: [PATCH 09/20] Update dependency. --- onedrive/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onedrive/pom.xml b/onedrive/pom.xml index 4051c543c54..dd08b4d3f1c 100644 --- a/onedrive/pom.xml +++ b/onedrive/pom.xml @@ -23,7 +23,7 @@ onedrive - 3.2.3 + 3.2.4 From 193f493cc10ceb461b1e0ac4bce372b05a4c305d Mon Sep 17 00:00:00 2001 From: David Kocher Date: Sun, 10 Aug 2025 17:03:20 +0200 Subject: [PATCH 10/20] Revert "Add UUID." This reverts commit fea39f30 --- core/src/main/java/ch/cyberduck/core/io/Checksum.java | 5 ----- core/src/main/java/ch/cyberduck/core/io/HashAlgorithm.java | 3 +-- core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java | 2 -- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/core/src/main/java/ch/cyberduck/core/io/Checksum.java b/core/src/main/java/ch/cyberduck/core/io/Checksum.java index 8d810a9bb01..fb53d418242 100644 --- a/core/src/main/java/ch/cyberduck/core/io/Checksum.java +++ b/core/src/main/java/ch/cyberduck/core/io/Checksum.java @@ -83,11 +83,6 @@ public static Checksum parse(final String hash) { return new Checksum(HashAlgorithm.md5, hash); } break; - case 36: - if(hash.matches("^[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}$")) { - return new Checksum(HashAlgorithm.uuid, hash); - } - break; case 40: if(hash.matches("[a-fA-F0-9]{40}")) { return new Checksum(HashAlgorithm.sha1, hash); diff --git a/core/src/main/java/ch/cyberduck/core/io/HashAlgorithm.java b/core/src/main/java/ch/cyberduck/core/io/HashAlgorithm.java index 6e07c413ed2..074ff498fbc 100644 --- a/core/src/main/java/ch/cyberduck/core/io/HashAlgorithm.java +++ b/core/src/main/java/ch/cyberduck/core/io/HashAlgorithm.java @@ -27,8 +27,7 @@ public enum HashAlgorithm { sha512, crc32, cdash64, - dropbox_content_hash, - uuid; + dropbox_content_hash; @Override public String toString() { diff --git a/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java b/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java index 14aa13b3515..05dc16f3943 100644 --- a/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java +++ b/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java @@ -40,7 +40,5 @@ public void testParse() { Checksum.parse("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")); assertEquals(new Checksum(HashAlgorithm.crc32, "d202ef8d"), Checksum.parse("d202ef8d")); - assertEquals(new Checksum(HashAlgorithm.uuid, "B9739CF2-E4DB-4439-BCD3-56B44A02516E"), - Checksum.parse("B9739CF2-E4DB-4439-BCD3-56B44A02516E")); } } From b19a8bd6a9d6123e7f9a9a04a6e91fc841c38c4e Mon Sep 17 00:00:00 2001 From: David Kocher Date: Sun, 10 Aug 2025 17:30:35 +0200 Subject: [PATCH 11/20] Add support to parse Base64 encoded hex strings. --- .../java/ch/cyberduck/core/io/Checksum.java | 92 ++++++++++++------- 1 file changed, 59 insertions(+), 33 deletions(-) diff --git a/core/src/main/java/ch/cyberduck/core/io/Checksum.java b/core/src/main/java/ch/cyberduck/core/io/Checksum.java index fb53d418242..04360f42c17 100644 --- a/core/src/main/java/ch/cyberduck/core/io/Checksum.java +++ b/core/src/main/java/ch/cyberduck/core/io/Checksum.java @@ -18,6 +18,9 @@ * feedback@cyberduck.io */ +import ch.cyberduck.core.exception.UnsupportedException; + +import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.StringUtils; @@ -42,11 +45,16 @@ public Checksum(final HashAlgorithm algorithm, final byte[] digest) { this.base64 = Base64.encodeBase64String(digest); } - public Checksum(final HashAlgorithm algorithm, final String hexString) { + public Checksum(final HashAlgorithm algorithm, final String hexString) throws UnsupportedException { this.algorithm = algorithm; this.hash = hexString; this.hex = hexString; - this.base64 = null; + try { + this.base64 = Base64.encodeBase64String(Hex.decodeHex(hexString)); + } + catch(DecoderException e) { + throw new UnsupportedException(e); + } } public Checksum(final HashAlgorithm algorithm, final String hexString, final String base64String) { @@ -65,41 +73,59 @@ public Checksum(final Checksum other) { @Override public String toString() { - return hash; + return hex; } public static Checksum parse(final String hash) { if(StringUtils.isBlank(hash)) { return Checksum.NONE; } - switch(hash.length()) { - case 8: - if(hash.matches("[a-fA-F0-9]{8}")) { - return new Checksum(HashAlgorithm.crc32, hash); - } - break; - case 32: - if(hash.matches("[a-fA-F0-9]{32}")) { - return new Checksum(HashAlgorithm.md5, hash); - } - break; - case 40: - if(hash.matches("[a-fA-F0-9]{40}")) { - return new Checksum(HashAlgorithm.sha1, hash); - } - break; - case 64: - if(hash.matches("[A-Fa-f0-9]{64}")) { - return new Checksum(HashAlgorithm.sha256, hash); - } - break; - case 128: - if(hash.matches("[A-Fa-f0-9]{128}")) { - return new Checksum(HashAlgorithm.sha512, hash); - } - break; - default: - log.warn("Failure to detect algorithm for checksum {}", hash); + // Check for Base64 with propper padding + if(hash.matches("^[A-Za-z0-9+/]+=*$") && hash.length() % 4 == 0) { + final Checksum result = parseHex(Hex.encodeHexString(Base64.decodeBase64(hash))); + if(result != Checksum.NONE) { + return new Checksum(result.algorithm, result.hex, hash); + } + return Checksum.NONE; + } + // Parse as hex string + return parseHex(hash); + } + + private static Checksum parseHex(final String hexString) { + try { + switch(hexString.length()) { + case 8: + if(hexString.matches("[a-fA-F0-9]{8}")) { + return new Checksum(HashAlgorithm.crc32, hexString); + } + break; + case 32: + if(hexString.matches("[a-fA-F0-9]{32}")) { + return new Checksum(HashAlgorithm.md5, hexString); + } + break; + case 40: + if(hexString.matches("[a-fA-F0-9]{40}")) { + return new Checksum(HashAlgorithm.sha1, hexString); + } + break; + case 64: + if(hexString.matches("[A-Fa-f0-9]{64}")) { + return new Checksum(HashAlgorithm.sha256, hexString); + } + break; + case 128: + if(hexString.matches("[A-Fa-f0-9]{128}")) { + return new Checksum(HashAlgorithm.sha512, hexString); + } + break; + default: + log.warn("Failure to detect algorithm for checksum {}", hexString); + } + } + catch(UnsupportedException e) { + return Checksum.NONE; } return Checksum.NONE; } @@ -116,7 +142,7 @@ public boolean equals(final Object o) { if(algorithm != checksum.algorithm) { return false; } - if(!StringUtils.equalsIgnoreCase(hash, checksum.hash)) { + if(!StringUtils.equalsIgnoreCase(hex, checksum.hex)) { return false; } return true; @@ -125,7 +151,7 @@ public boolean equals(final Object o) { @Override public int hashCode() { int result = algorithm != null ? algorithm.hashCode() : 0; - result = 31 * result + (hash != null ? hash.hashCode() : 0); + result = 31 * result + (hex != null ? hex.hashCode() : 0); return result; } From 857620d92ef7f51174c77aa20bb2d707f416a967 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Sun, 10 Aug 2025 17:32:13 +0200 Subject: [PATCH 12/20] Adjust to failure thrown parsing hexadecimal string. --- .../core/box/BoxAttributesFinderFeature.java | 8 +++++++- .../cyberduck/core/io/CRC32ChecksumCompute.java | 8 +++++++- .../dropbox/DropboxAttributesFinderFeature.java | 8 +++++++- .../core/eue/ChunkListSHA256ChecksumCompute.java | 8 +++++++- .../importer/ThirdpartyBookmarkCollection.java | 16 ++++++++-------- .../NextcloudAttributesFinderFeature.java | 3 ++- 6 files changed, 38 insertions(+), 13 deletions(-) diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxAttributesFinderFeature.java b/box/src/main/java/ch/cyberduck/core/box/BoxAttributesFinderFeature.java index 0176931f922..a011911031f 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxAttributesFinderFeature.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxAttributesFinderFeature.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.box.io.swagger.client.model.File; import ch.cyberduck.core.box.io.swagger.client.model.Folder; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.AttributesAdapter; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.io.Checksum; @@ -75,7 +76,12 @@ public PathAttributes toAttributes(final File f) { attrs.setSize(f.getSize()); } attrs.setFileId(f.getId()); - attrs.setChecksum(new Checksum(HashAlgorithm.sha1, f.getSha1())); + try { + attrs.setChecksum(new Checksum(HashAlgorithm.sha1, f.getSha1())); + } + catch(UnsupportedException e) { + attrs.setChecksum(Checksum.NONE); + } attrs.setETag(f.getEtag()); return attrs; } diff --git a/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java b/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java index 3f68cab2d9b..cab0968cb35 100644 --- a/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java +++ b/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java @@ -17,6 +17,7 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ChecksumException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.transfer.TransferStatus; import org.apache.commons.io.IOUtils; @@ -44,6 +45,11 @@ public Checksum compute(final InputStream in, final TransferStatus status) throw finally { IOUtils.closeQuietly(normalized); } - return new Checksum(HashAlgorithm.crc32, String.format("%08x", crc32.getValue())); + try { + return new Checksum(HashAlgorithm.crc32, String.format("%08x", crc32.getValue())); + } + catch(UnsupportedException e) { + return Checksum.NONE; + } } } diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeature.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeature.java index 7eb465883a1..d0a0b375c64 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeature.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeature.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.NotfoundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.AttributesAdapter; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.io.Checksum; @@ -86,7 +87,12 @@ public PathAttributes toAttributes(final Metadata metadata) { if(file.getFileLockInfo() != null) { attributes.setLockId(String.valueOf(file.getFileLockInfo().getIsLockholder())); } - attributes.setChecksum(new Checksum(HashAlgorithm.dropbox_content_hash, file.getContentHash())); + try { + attributes.setChecksum(new Checksum(HashAlgorithm.dropbox_content_hash, file.getContentHash())); + } + catch(UnsupportedException e) { + attributes.setChecksum(Checksum.NONE); + } attributes.setVersionId(file.getRev()); } if(metadata instanceof FolderMetadata) { diff --git a/eue/src/main/java/ch/cyberduck/core/eue/ChunkListSHA256ChecksumCompute.java b/eue/src/main/java/ch/cyberduck/core/eue/ChunkListSHA256ChecksumCompute.java index 09b247538ec..37f845fe610 100644 --- a/eue/src/main/java/ch/cyberduck/core/eue/ChunkListSHA256ChecksumCompute.java +++ b/eue/src/main/java/ch/cyberduck/core/eue/ChunkListSHA256ChecksumCompute.java @@ -20,6 +20,7 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ChecksumException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.io.AbstractChecksumCompute; import ch.cyberduck.core.io.Checksum; import ch.cyberduck.core.io.HashAlgorithm; @@ -59,6 +60,11 @@ public Checksum compute(final Number length, final byte[] contentDigest) throws } digest.update(contentDigest); digest.update(intToBytes(length.intValue())); - return new Checksum(HashAlgorithm.cdash64, Base64.encodeBase64URLSafeString(digest.digest())); + try { + return new Checksum(HashAlgorithm.cdash64, Base64.encodeBase64URLSafeString(digest.digest())); + } + catch(UnsupportedException e) { + return Checksum.NONE; + } } } diff --git a/importer/src/main/java/ch/cyberduck/core/importer/ThirdpartyBookmarkCollection.java b/importer/src/main/java/ch/cyberduck/core/importer/ThirdpartyBookmarkCollection.java index dc11c453c6a..d8400e2be71 100644 --- a/importer/src/main/java/ch/cyberduck/core/importer/ThirdpartyBookmarkCollection.java +++ b/importer/src/main/java/ch/cyberduck/core/importer/ThirdpartyBookmarkCollection.java @@ -30,6 +30,7 @@ import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.LocalAccessDeniedException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.io.Checksum; import ch.cyberduck.core.io.ChecksumComputeFactory; import ch.cyberduck.core.io.HashAlgorithm; @@ -75,10 +76,10 @@ public void load() throws AccessDeniedException { } if(preferences.getBoolean(this.getConfiguration())) { // Previously imported - final Checksum previous = new Checksum(HashAlgorithm.md5, - preferences.getProperty(String.format("%s.checksum", this.getConfiguration()))); - log.debug("Saved previous checksum {} for bookmark {}", previous, file); - if(StringUtils.isNotBlank(previous.hash)) { + try { + final Checksum previous = new Checksum(HashAlgorithm.md5, + preferences.getProperty(String.format("%s.checksum", this.getConfiguration()))); + log.debug("Saved previous checksum {} for bookmark {}", previous, file); if(previous.equals(current)) { log.info("Skip importing bookmarks from {} with previously saved checksum {}", file, previous); } @@ -87,8 +88,7 @@ public void load() throws AccessDeniedException { // Should filter existing bookmarks. Skip import } } - else { - // Skip flagged + catch(UnsupportedException e) { log.debug("Skip importing bookmarks from {}", file); } } @@ -155,7 +155,7 @@ public boolean add(final Host bookmark) { if(credentials.isPublicKeyAuthentication()) { try { keychain.addPassword(bookmark.getHostname(), credentials.getIdentity().getAbbreviatedPath(), - credentials.getPassword()); + credentials.getPassword()); } catch(LocalAccessDeniedException e) { log.error("Failure {} saving credentials for {} in password store", e, bookmark); @@ -164,7 +164,7 @@ public boolean add(final Host bookmark) { else if(!credentials.isAnonymousLogin()) { try { keychain.addPassword(bookmark.getProtocol().getScheme(), bookmark.getPort(), - bookmark.getHostname(), credentials.getUsername(), credentials.getPassword()); + bookmark.getHostname(), credentials.getUsername(), credentials.getPassword()); } catch(LocalAccessDeniedException e) { log.error("Failure {} saving credentials for {} in password store", e, bookmark); diff --git a/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudAttributesFinderFeature.java b/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudAttributesFinderFeature.java index f5161d7602c..517392fc22f 100644 --- a/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudAttributesFinderFeature.java +++ b/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudAttributesFinderFeature.java @@ -23,6 +23,7 @@ import ch.cyberduck.core.dav.DAVSession; import ch.cyberduck.core.dav.DAVTimestampFeature; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.io.Checksum; import ch.cyberduck.core.io.HashAlgorithm; @@ -129,7 +130,7 @@ public PathAttributes toAttributes(final DavResource resource) { attributes.setChecksum(new Checksum(HashAlgorithm.valueOf(StringUtils.lowerCase(StringUtils.split(v, ":")[0])), StringUtils.lowerCase(StringUtils.split(v, ":")[1]))); } - catch(IllegalArgumentException e) { + catch(IllegalArgumentException | UnsupportedException e) { log.warn("Unsupported checksum {}", v); } } From 5fb166e47017c6ee40bf327db47af9620094287a Mon Sep 17 00:00:00 2001 From: David Kocher Date: Sun, 10 Aug 2025 17:32:33 +0200 Subject: [PATCH 13/20] Adjust tests. --- .../test/java/ch/cyberduck/core/PathAttributesTest.java | 2 +- core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java | 2 +- .../synchronization/ChecksumComparisonServiceTest.java | 6 +++--- .../synchronization/DefaultComparePathFilterTest.java | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/src/test/java/ch/cyberduck/core/PathAttributesTest.java b/core/src/test/java/ch/cyberduck/core/PathAttributesTest.java index d6c8215cada..d095aa004e7 100644 --- a/core/src/test/java/ch/cyberduck/core/PathAttributesTest.java +++ b/core/src/test/java/ch/cyberduck/core/PathAttributesTest.java @@ -77,7 +77,7 @@ public void testSerialize() { acl.addAll(new Acl.CanonicalUser("user1"), new Acl.Role(Acl.Role.READ), new Acl.Role(Acl.Role.WRITE)); acl.addAll(new Acl.CanonicalUser("user2"), new Acl.Role(Acl.Role.FULL)); attributes.setAcl(acl); - attributes.setChecksum(new Checksum(HashAlgorithm.crc32, "abcdefab")); + attributes.setChecksum(new Checksum(HashAlgorithm.crc32, "abcdefab", null)); attributes.setVersionId("v-1"); attributes.setFileId("f-1"); attributes.setDisplayname("d-1"); diff --git a/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java b/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java index 05dc16f3943..ff87f696303 100644 --- a/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java +++ b/core/src/test/java/ch/cyberduck/core/io/ChecksumTest.java @@ -25,7 +25,7 @@ public class ChecksumTest { @Test - public void testParse() { + public void testParse() throws Exception { assertEquals(new Checksum(HashAlgorithm.md5, "d41d8cd98f00b204e9800998ecf8427e"), Checksum.parse("d41d8cd98f00b204e9800998ecf8427e")); assertEquals(new Checksum(HashAlgorithm.sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709"), diff --git a/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java b/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java index b5b2749c6c1..9d447cb0132 100644 --- a/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java +++ b/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java @@ -12,12 +12,12 @@ public class ChecksumComparisonServiceTest { @Test - public void testCompare() { + public void testCompare() throws Exception { ComparisonService s = new ChecksumComparisonService(); assertEquals(Comparison.equal, s.compare(Path.Type.file, new PathAttributes() { @Override public Checksum getChecksum() { - return new Checksum(HashAlgorithm.md5, "a"); + return new Checksum(HashAlgorithm.md5, "a", null); } }, new PathAttributes().setChecksum(new Checksum(HashAlgorithm.md5, "a")) )); @@ -25,7 +25,7 @@ public Checksum getChecksum() { assertEquals(Comparison.notequal, s.compare(Path.Type.file, new PathAttributes() { @Override public Checksum getChecksum() { - return new Checksum(HashAlgorithm.md5, "b"); + return new Checksum(HashAlgorithm.md5, "b", null); } }, new PathAttributes().setChecksum(new Checksum(HashAlgorithm.md5, "a")))); } diff --git a/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparePathFilterTest.java b/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparePathFilterTest.java index 5edaaaedd56..5fa9a12e91c 100644 --- a/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparePathFilterTest.java +++ b/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparePathFilterTest.java @@ -39,7 +39,7 @@ public PathAttributes find(final Path file, final ListProgressListener listener) return new PathAttributes() { @Override public Checksum getChecksum() { - return new Checksum(HashAlgorithm.md5, "a"); + return new Checksum(HashAlgorithm.md5, "a", null); } }; } @@ -54,7 +54,7 @@ public boolean find(final Path file, final ListProgressListener listener) { ComparePathFilter s = new DefaultComparePathFilter(new NullSession(new Host(new TestProtocol())), find, attributes) { @Override protected Checksum checksum(final HashAlgorithm algorithm, final Local local) { - return new Checksum(HashAlgorithm.md5, "a"); + return new Checksum(HashAlgorithm.md5, "a", null); } }; final String path = new AlphanumericRandomStringService().random(); @@ -166,7 +166,7 @@ public PathAttributes find(final Path file, final ListProgressListener listener) return new PathAttributes() { @Override public Checksum getChecksum() { - return new Checksum(HashAlgorithm.md5, "b"); + return new Checksum(HashAlgorithm.md5, "b", null); } @Override @@ -189,7 +189,7 @@ public long getModificationDate() { ComparePathFilter s = new DefaultComparePathFilter(new NullSession(new Host(new TestProtocol())), find, attributes) { @Override protected Checksum checksum(final HashAlgorithm algorithm, final Local local) { - return new Checksum(HashAlgorithm.md5, "a"); + return new Checksum(HashAlgorithm.md5, "a", null); } }; assertEquals(Comparison.local, s.compare(new Path("t", EnumSet.of(Path.Type.file)), new NullLocal("t") { From 27e69bce4a61e09a7ebeb49eabb747b845f11731 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Sun, 10 Aug 2025 17:33:09 +0200 Subject: [PATCH 14/20] Parse QuickXorHash. --- .../features/GraphAttributesFinderFeature.java | 15 +++++++-------- .../GraphAttributesFinderFeatureTest.java | 7 +++---- .../core/onedrive/GraphMoveFeatureTest.java | 3 ++- .../core/onedrive/OneDriveWriteFeatureTest.java | 15 ++++++++------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java index f004fc39d28..856b3f949e8 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java @@ -33,14 +33,14 @@ import org.nuxeo.onedrive.client.OneDriveAPIException; import org.nuxeo.onedrive.client.types.DriveItem; import org.nuxeo.onedrive.client.types.DriveItemVersion; +import org.nuxeo.onedrive.client.types.File; import org.nuxeo.onedrive.client.types.FileSystemInfo; +import org.nuxeo.onedrive.client.types.Hashes; import org.nuxeo.onedrive.client.types.Publication; import java.io.IOException; import java.nio.charset.Charset; import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class GraphAttributesFinderFeature implements AttributesFinder, AttributesAdapter { @@ -48,8 +48,6 @@ public class GraphAttributesFinderFeature implements AttributesFinder, Attribute private final GraphSession session; private final GraphFileIdProvider fileid; - private static final Pattern ETAG_PATTERN = Pattern.compile("\"\\{([0-9A-Z-]+)\\},\\d+\""); - public GraphAttributesFinderFeature(final GraphSession session, final GraphFileIdProvider fileid) { this.session = session; this.fileid = fileid; @@ -94,10 +92,11 @@ private DriveItem.Metadata toMetadata(final Path file, final DriveItem item) thr public PathAttributes toAttributes(final DriveItem.Metadata metadata) { final PathAttributes attributes = new PathAttributes(); attributes.setETag(metadata.getETag()); - if(null != metadata.getETag()) { - final Matcher matcher = ETAG_PATTERN.matcher(metadata.getETag()); - if(matcher.matches()) { - attributes.setChecksum(Checksum.parse(matcher.group(1))); + final File file = metadata.getFile(); + if(file != null) { + final Hashes hashes = file.getHashes(); + if(hashes != null) { + attributes.setChecksum(Checksum.parse(hashes.getQuickXorHash())); } } Optional webUrl = getWebUrl(metadata); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java index afff836b25a..ae6ebd6f57b 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java @@ -22,7 +22,7 @@ import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Home; -import ch.cyberduck.core.io.HashAlgorithm; +import ch.cyberduck.core.io.Checksum; import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; @@ -64,8 +64,7 @@ public void testFindFile() throws Exception { assertNotEquals(-1L, attributes.getCreationDate()); assertNotEquals(-1L, attributes.getModificationDate()); assertNotNull(attributes.getETag()); - assertNotNull(attributes.getChecksum()); - assertEquals(HashAlgorithm.uuid, attributes.getChecksum().algorithm); + assertNotEquals(Checksum.NONE, attributes.getChecksum()); assertNull(attributes.getVersionId()); assertNotNull(attributes.getLink()); assertNotNull(attributes.getFileId()); @@ -83,7 +82,7 @@ public void testFindDirectory() throws Exception { assertNotEquals(-1L, attributes.getModificationDate()); assertNotNull(attributes.getETag()); assertNotNull(attributes.getChecksum()); - assertEquals(HashAlgorithm.uuid, attributes.getChecksum().algorithm); + assertEquals(Checksum.NONE, attributes.getChecksum().algorithm); assertNull(attributes.getVersionId()); assertNotNull(attributes.getLink()); assertNotNull(attributes.getFileId()); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java index 015c1764202..bcde51b38f0 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java @@ -104,7 +104,8 @@ public void testMove() throws BackgroundException { Path touchedFile = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); touch.touch(touchedFile, new TransferStatus().setMime("x-application/cyberduck")); final PathAttributes attributes = attributesFinder.find(touchedFile); - + assertEquals("AAAAAAAAAAAAAAAAAAAAAAAAAAA=", attributes.getChecksum().base64); + assertEquals("0000000000000000000000000000000000000000", attributes.getChecksum().hex); Path rename = new Path(targetDirectory, touchedFile.getName(), EnumSet.of(Path.Type.file)); assertTrue(move.isSupported(touchedFile, Optional.of(rename))); final Path target = move.move(touchedFile, rename, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java index 17d730c0837..576b2ace045 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java @@ -62,13 +62,13 @@ public void testWrite() throws Exception { final PathAttributes folderAttributes = new GraphAttributesFinderFeature(session, fileid).find(folder); final String folderEtag = folderAttributes.getETag(); final long folderTimestamp = folderAttributes.getModificationDate(); - final byte[] content = RandomUtils.nextBytes(5 * 1024 * 1024); + final byte[] sourceContent = RandomUtils.nextBytes(5 * 1024 * 1024); final TransferStatus status = new TransferStatus(); - status.setLength(content.length); + status.setLength(sourceContent.length); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final String id = new GraphTouchFeature(session, fileid).touch(file, new TransferStatus()).attributes().getFileId(); final StatusOutputStream out = feature.write(file, status, new DisabledConnectionCallback()); - new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out); + new StreamCopier(status, status).transfer(new ByteArrayInputStream(sourceContent), out); assertNotNull(out.getStatus()); assertTrue(status.isComplete()); assertNotSame(PathAttributes.EMPTY, status.getResponse()); @@ -76,18 +76,19 @@ public void testWrite() throws Exception { assertEquals(folderEtag, new GraphAttributesFinderFeature(session, fileid).find(folder).getETag()); assertEquals(folderTimestamp, new GraphAttributesFinderFeature(session, fileid).find(folder).getModificationDate()); assertTrue(new DefaultFindFeature(session).find(file)); - final byte[] compare = new byte[content.length]; - final InputStream stream = new GraphReadFeature(session, fileid).read(file, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); + final byte[] compare = new byte[sourceContent.length]; + final InputStream stream = new GraphReadFeature(session, fileid).read(file, new TransferStatus().setLength(sourceContent.length), new DisabledConnectionCallback()); IOUtils.readFully(stream, compare); stream.close(); - assertArrayEquals(content, compare); + assertArrayEquals(sourceContent, compare); final Path copy = new Path(file); copy.attributes().setCustom(Collections.emptyMap()); assertEquals(id, fileid.getFileId(copy)); // Overwrite final StatusOutputStream overwrite = feature.write(file, status.setExists(true), new DisabledConnectionCallback()); assertNotNull(overwrite); - assertEquals(content.length, IOUtils.copyLarge(new ByteArrayInputStream(content), overwrite)); + final byte[] overwriteContent = RandomUtils.nextBytes(5 * 1024 * 1024); + assertEquals(overwriteContent.length, IOUtils.copyLarge(new ByteArrayInputStream(overwriteContent), overwrite)); overwrite.close(); final PathAttributes overwriteAttr = new GraphAttributesFinderFeature(session, fileid).toAttributes(overwrite.getStatus()); assertEquals(overwriteAttr, new GraphAttributesFinderFeature(session, fileid).find(file)); From 9934617e220b7b67043655165845c1d45b5fa70a Mon Sep 17 00:00:00 2001 From: David Kocher Date: Sun, 10 Aug 2025 17:36:29 +0200 Subject: [PATCH 15/20] Fix test. --- .../core/onedrive/GraphAttributesFinderFeatureTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java index ae6ebd6f57b..8a7018a3699 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java @@ -82,7 +82,7 @@ public void testFindDirectory() throws Exception { assertNotEquals(-1L, attributes.getModificationDate()); assertNotNull(attributes.getETag()); assertNotNull(attributes.getChecksum()); - assertEquals(Checksum.NONE, attributes.getChecksum().algorithm); + assertEquals(Checksum.NONE, attributes.getChecksum()); assertNull(attributes.getVersionId()); assertNotNull(attributes.getLink()); assertNotNull(attributes.getFileId()); From 6c9d0006069f204603b76664f990deda1ec83335 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Sun, 10 Aug 2025 21:33:26 +0200 Subject: [PATCH 16/20] Fix test. --- .../core/synchronization/ChecksumComparisonServiceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java b/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java index 9d447cb0132..78ca2e14201 100644 --- a/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java +++ b/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java @@ -19,7 +19,7 @@ public void testCompare() throws Exception { public Checksum getChecksum() { return new Checksum(HashAlgorithm.md5, "a", null); } - }, new PathAttributes().setChecksum(new Checksum(HashAlgorithm.md5, "a")) + }, new PathAttributes().setChecksum(new Checksum(HashAlgorithm.md5, "a", null)) )); assertEquals(Comparison.notequal, s.compare(Path.Type.file, new PathAttributes() { @@ -27,7 +27,7 @@ public Checksum getChecksum() { public Checksum getChecksum() { return new Checksum(HashAlgorithm.md5, "b", null); } - }, new PathAttributes().setChecksum(new Checksum(HashAlgorithm.md5, "a")))); + }, new PathAttributes().setChecksum(new Checksum(HashAlgorithm.md5, "a", null)))); } @Test From 32f07b6e98c909849f65108f1031ebc5fd24b982 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Sun, 10 Aug 2025 21:36:39 +0200 Subject: [PATCH 17/20] Fallback to Hex parsing. --- core/src/main/java/ch/cyberduck/core/io/Checksum.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/ch/cyberduck/core/io/Checksum.java b/core/src/main/java/ch/cyberduck/core/io/Checksum.java index 04360f42c17..927f204cff5 100644 --- a/core/src/main/java/ch/cyberduck/core/io/Checksum.java +++ b/core/src/main/java/ch/cyberduck/core/io/Checksum.java @@ -86,7 +86,6 @@ public static Checksum parse(final String hash) { if(result != Checksum.NONE) { return new Checksum(result.algorithm, result.hex, hash); } - return Checksum.NONE; } // Parse as hex string return parseHex(hash); From a64c143a678c6f9484ea9fde16b16b9708ee63dc Mon Sep 17 00:00:00 2001 From: David Kocher Date: Mon, 11 Aug 2025 08:10:42 +0200 Subject: [PATCH 18/20] Check cancelation flag. --- .../java/ch/cyberduck/core/io/MD5FastChecksumCompute.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/ch/cyberduck/core/io/MD5FastChecksumCompute.java b/core/src/main/java/ch/cyberduck/core/io/MD5FastChecksumCompute.java index b6ad041a28f..d6026606a64 100644 --- a/core/src/main/java/ch/cyberduck/core/io/MD5FastChecksumCompute.java +++ b/core/src/main/java/ch/cyberduck/core/io/MD5FastChecksumCompute.java @@ -18,6 +18,7 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ChecksumCanceledException; import ch.cyberduck.core.exception.ChecksumException; +import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.transfer.TransferStatus; import org.apache.commons.io.IOUtils; @@ -41,12 +42,13 @@ public Checksum compute(final InputStream in, final TransferStatus status) throw this.normalize(in, status), status)); } - protected byte[] digest(final String algorithm, final InputStream in, final StreamCancelation cancelation) throws ChecksumException, ChecksumCanceledException { + protected byte[] digest(final String algorithm, final InputStream in, final StreamCancelation cancelation) throws ChecksumException, ConnectionCanceledException { final MD5 md = new MD5(); try { byte[] buffer = new byte[16384]; int bytesRead; while((bytesRead = in.read(buffer, 0, buffer.length)) != -1) { + cancelation.validate(); md.Update(buffer, 0, bytesRead); } } From 9c1423b300ec355e6f711d654ca3e3b802fec330 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Mon, 11 Aug 2025 08:10:49 +0200 Subject: [PATCH 19/20] Check cancelation flag. --- .../src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java b/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java index cab0968cb35..4b914378630 100644 --- a/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java +++ b/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java @@ -36,6 +36,7 @@ public Checksum compute(final InputStream in, final TransferStatus status) throw byte[] buffer = new byte[16384]; int bytesRead; while((bytesRead = normalized.read(buffer, 0, buffer.length)) != -1) { + status.validate(); crc32.update(buffer, 0, bytesRead); } } From 46bc3b137630dd3df3b21afeee560a3ac3118021 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Mon, 11 Aug 2025 09:35:23 +0200 Subject: [PATCH 20/20] Make stricter. --- core/src/main/java/ch/cyberduck/core/io/Checksum.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/ch/cyberduck/core/io/Checksum.java b/core/src/main/java/ch/cyberduck/core/io/Checksum.java index 927f204cff5..528630cef4f 100644 --- a/core/src/main/java/ch/cyberduck/core/io/Checksum.java +++ b/core/src/main/java/ch/cyberduck/core/io/Checksum.java @@ -80,8 +80,8 @@ public static Checksum parse(final String hash) { if(StringUtils.isBlank(hash)) { return Checksum.NONE; } - // Check for Base64 with propper padding - if(hash.matches("^[A-Za-z0-9+/]+=*$") && hash.length() % 4 == 0) { + // Check for Base64 with proper padding + if(hash.matches("^[A-Za-z0-9+/]+={0,2}$") && hash.length() % 4 == 0) { final Checksum result = parseHex(Hex.encodeHexString(Base64.decodeBase64(hash))); if(result != Checksum.NONE) { return new Checksum(result.algorithm, result.hex, hash);