Skip to content

Commit

Permalink
feat(nextcloud): Add support for checksums for uploading WebDAV files
Browse files Browse the repository at this point in the history
Signed-off-by: provokateurin <[email protected]>
  • Loading branch information
provokateurin committed Aug 29, 2024
1 parent 63330c5 commit f0ab700
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 0 deletions.
23 changes: 23 additions & 0 deletions packages/nextcloud/lib/src/api/webdav/webdav_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import 'package:nextcloud/src/api/webdav/webdav.dart';
import 'package:nextcloud/utils.dart';
import 'package:universal_io/io.dart' show File, FileStat;

/// The algorithms supported for the oc:checksum prop and OC-Checksum header.
const supportedChecksumAlgorithms = ['md5', 'sha1', 'sha256', 'sha3-256', 'adler32'];

/// WebDavClient class
class WebDavClient {
// ignore: public_member_api_docs
Expand Down Expand Up @@ -99,6 +102,7 @@ class WebDavClient {
PathUri path, {
DateTime? lastModified,
DateTime? created,
String? checksum,
}) {
final request = http.Request('PUT', _constructUri(path))..bodyBytes = localData;

Expand All @@ -107,6 +111,7 @@ class WebDavClient {
lastModified: lastModified,
created: created,
contentLength: localData.length,
checksum: checksum,
);
_addBaseHeaders(request);
return request;
Expand All @@ -124,12 +129,14 @@ class WebDavClient {
PathUri path, {
DateTime? lastModified,
DateTime? created,
String? checksum,
}) {
final request = put_Request(
localData,
path,
lastModified: lastModified,
created: created,
checksum: checksum,
);

return csrfClient.send(request);
Expand All @@ -145,6 +152,7 @@ class WebDavClient {
required int contentLength,
DateTime? lastModified,
DateTime? created,
String? checksum,
void Function(double progress)? onProgress,
}) {
final request = http.StreamedRequest('PUT', _constructUri(path));
Expand All @@ -155,6 +163,7 @@ class WebDavClient {
lastModified: lastModified,
created: created,
contentLength: contentLength,
checksum: checksum,
);

if (onProgress != null) {
Expand Down Expand Up @@ -192,13 +201,15 @@ class WebDavClient {
required int contentLength,
DateTime? lastModified,
DateTime? created,
String? checksum,
void Function(double progress)? onProgress,
}) {
final request = putStream_Request(
localData,
path,
lastModified: lastModified,
created: created,
checksum: checksum,
contentLength: contentLength,
onProgress: onProgress,
);
Expand All @@ -216,6 +227,7 @@ class WebDavClient {
PathUri path, {
DateTime? lastModified,
DateTime? created,
String? checksum,
void Function(double progress)? onProgress,
}) {
// Authentication and content-type headers are already set by the putStream_Request.
Expand All @@ -225,6 +237,7 @@ class WebDavClient {
path,
lastModified: lastModified,
created: created,
checksum: checksum,
contentLength: fileStat.size,
onProgress: onProgress,
);
Expand All @@ -244,6 +257,7 @@ class WebDavClient {
PathUri path, {
DateTime? lastModified,
DateTime? created,
String? checksum,
void Function(double progress)? onProgress,
}) {
final request = putFile_Request(
Expand All @@ -252,6 +266,7 @@ class WebDavClient {
path,
lastModified: lastModified,
created: created,
checksum: checksum,
onProgress: onProgress,
);

Expand Down Expand Up @@ -571,13 +586,21 @@ class WebDavClient {
required int contentLength,
DateTime? lastModified,
DateTime? created,
String? checksum,
}) {
if (lastModified != null) {
request.headers['X-OC-Mtime'] = lastModified.secondsSinceEpoch.toString();
}
if (created != null) {
request.headers['X-OC-CTime'] = created.secondsSinceEpoch.toString();
}
if (checksum != null) {
final parts = checksum.split(':');
if (parts.length != 2 || !supportedChecksumAlgorithms.contains(parts[0].toLowerCase()) || parts[1].isEmpty) {
throw ArgumentError.value(checksum, 'checksum', 'Invalid checksum');
}
request.headers['OC-Checksum'] = checksum;
}
request.headers['content-length'] = contentLength.toString();
}

Expand Down
19 changes: 19 additions & 0 deletions packages/nextcloud/test/api/webdav/webdav_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,25 @@ void main() {
expect(props.ocTags!.tags!.single, 'example');
});

test('Upload file with checksum', () async {
final file = File('test/files/test.png');
await tester.client.webdav.putFile(
file,
file.statSync(),
PathUri.parse('test/checksum.png'),
checksum: 'md5:abc',
);

final response = await tester.client.webdav.propfind(
PathUri.parse('test/checksum.png'),
prop: const WebDavPropWithoutValues.fromBools(
ocChecksums: true,
),
);
final props = response.responses.single.propstats.first.prop;
expect(props.ocChecksums!.checksums!.single, 'md5:abc');
});

test('Upload and download file', () async {
final destinationDir = Directory.systemTemp.createTempSync();
final destination = File('${destinationDir.path}/test.png');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
PUT http://localhost/remote\.php/webdav/test/checksum\.png
authorization: Bearer mock
content-length: 8650
content-type: application/xml
oc-checksum: md5:abc
ocs-apirequest: true
requesttoken: token
.+
PROPFIND http://localhost/remote\.php/webdav/test/checksum\.png
authorization: Bearer mock
content-type: application/xml
ocs-apirequest: true
requesttoken: token
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud\.org/ns" xmlns:nc="http://nextcloud\.org/ns" xmlns:ocs="http://open-collaboration-services\.org/ns" xmlns:ocm="http://open-cloud-mesh\.org/ns"><d:prop><oc:checksums/></d:prop></d:propfind>

0 comments on commit f0ab700

Please sign in to comment.