|
|
@ -22,9 +22,12 @@ class WebDavClient { |
|
|
|
Future<HttpClientResponse> _send( |
|
|
|
Future<HttpClientResponse> _send( |
|
|
|
final String method, |
|
|
|
final String method, |
|
|
|
final String url, { |
|
|
|
final String url, { |
|
|
|
final Stream<Uint8List>? data, |
|
|
|
final Stream<Uint8List>? dataStream, |
|
|
|
|
|
|
|
final Uint8List? data, |
|
|
|
final Map<String, String>? headers, |
|
|
|
final Map<String, String>? headers, |
|
|
|
}) async { |
|
|
|
}) async { |
|
|
|
|
|
|
|
assert(dataStream == null || data == null, 'Only one of dataStream or data can be specified.'); |
|
|
|
|
|
|
|
|
|
|
|
final request = await HttpClient().openUrl( |
|
|
|
final request = await HttpClient().openUrl( |
|
|
|
method, |
|
|
|
method, |
|
|
|
Uri.parse(url), |
|
|
|
Uri.parse(url), |
|
|
@ -33,14 +36,17 @@ class WebDavClient { |
|
|
|
for (final header in { |
|
|
|
for (final header in { |
|
|
|
HttpHeaders.contentTypeHeader: 'application/xml', |
|
|
|
HttpHeaders.contentTypeHeader: 'application/xml', |
|
|
|
...?rootClient.baseHeaders, |
|
|
|
...?rootClient.baseHeaders, |
|
|
|
if (headers != null) ...headers, |
|
|
|
...?headers, |
|
|
|
if (rootClient.authentications.isNotEmpty) ...rootClient.authentications.first.headers, |
|
|
|
...?rootClient.authentications.firstOrNull?.headers, |
|
|
|
}.entries) { |
|
|
|
}.entries) { |
|
|
|
request.headers.add(header.key, header.value); |
|
|
|
request.headers.add(header.key, header.value); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (data != null) { |
|
|
|
if (data != null) { |
|
|
|
await request.addStream(data); |
|
|
|
request.add(data); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (dataStream != null) { |
|
|
|
|
|
|
|
await request.addStream(dataStream); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
final response = await request.close(); |
|
|
|
final response = await request.close(); |
|
|
@ -49,7 +55,7 @@ class WebDavClient { |
|
|
|
throw DynamiteApiException( |
|
|
|
throw DynamiteApiException( |
|
|
|
response.statusCode, |
|
|
|
response.statusCode, |
|
|
|
response.responseHeaders, |
|
|
|
response.responseHeaders, |
|
|
|
utf8.decode(await response.bodyBytes), |
|
|
|
await response.body, |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -78,24 +84,16 @@ class WebDavClient { |
|
|
|
Future<WebDavMultistatus> _parseResponse(final HttpClientResponse response) async => |
|
|
|
Future<WebDavMultistatus> _parseResponse(final HttpClientResponse response) async => |
|
|
|
WebDavMultistatus.fromXmlElement(xml.XmlDocument.parse(await response.body).rootElement); |
|
|
|
WebDavMultistatus.fromXmlElement(xml.XmlDocument.parse(await response.body).rootElement); |
|
|
|
|
|
|
|
|
|
|
|
Map<String, String>? _getUploadHeaders({ |
|
|
|
Map<String, String> _getUploadHeaders({ |
|
|
|
required final DateTime? lastModified, |
|
|
|
required final DateTime? lastModified, |
|
|
|
required final DateTime? created, |
|
|
|
required final DateTime? created, |
|
|
|
required final int? contentLength, |
|
|
|
required final int? contentLength, |
|
|
|
}) { |
|
|
|
}) => |
|
|
|
final headers = <String, String>{ |
|
|
|
{ |
|
|
|
if (lastModified != null) ...{ |
|
|
|
if (lastModified != null) 'X-OC-Mtime': (lastModified.millisecondsSinceEpoch ~/ 1000).toString(), |
|
|
|
'X-OC-Mtime': (lastModified.millisecondsSinceEpoch ~/ 1000).toString(), |
|
|
|
if (created != null) 'X-OC-CTime': (created.millisecondsSinceEpoch ~/ 1000).toString(), |
|
|
|
}, |
|
|
|
if (contentLength != null) HttpHeaders.contentLengthHeader: contentLength.toString(), |
|
|
|
if (created != null) ...{ |
|
|
|
|
|
|
|
'X-OC-CTime': (created.millisecondsSinceEpoch ~/ 1000).toString(), |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
if (contentLength != null) ...{ |
|
|
|
|
|
|
|
'Content-Length': contentLength.toString(), |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
return headers.isNotEmpty ? headers : null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Gets the WebDAV capabilities of the server. |
|
|
|
/// Gets the WebDAV capabilities of the server. |
|
|
|
Future<WebDavOptions> options() async { |
|
|
|
Future<WebDavOptions> options() async { |
|
|
@ -103,8 +101,8 @@ class WebDavClient { |
|
|
|
'OPTIONS', |
|
|
|
'OPTIONS', |
|
|
|
_constructPath(), |
|
|
|
_constructPath(), |
|
|
|
); |
|
|
|
); |
|
|
|
final davCapabilities = response.headers['dav']?.cast<String>().first ?? ''; |
|
|
|
final davCapabilities = response.headers['dav']?.first ?? ''; |
|
|
|
final davSearchCapabilities = response.headers['dasl']?.cast<String>().first ?? ''; |
|
|
|
final davSearchCapabilities = response.headers['dasl']?.first ?? ''; |
|
|
|
return WebDavOptions( |
|
|
|
return WebDavOptions( |
|
|
|
davCapabilities.split(',').map((final e) => e.trim()).where((final e) => e.isNotEmpty).toSet(), |
|
|
|
davCapabilities.split(',').map((final e) => e.trim()).where((final e) => e.isNotEmpty).toSet(), |
|
|
|
davSearchCapabilities.split(',').map((final e) => e.trim()).where((final e) => e.isNotEmpty).toSet(), |
|
|
|
davSearchCapabilities.split(',').map((final e) => e.trim()).where((final e) => e.isNotEmpty).toSet(), |
|
|
@ -138,12 +136,15 @@ class WebDavClient { |
|
|
|
final DateTime? lastModified, |
|
|
|
final DateTime? lastModified, |
|
|
|
final DateTime? created, |
|
|
|
final DateTime? created, |
|
|
|
}) => |
|
|
|
}) => |
|
|
|
putStream( |
|
|
|
_send( |
|
|
|
Stream.value(localData), |
|
|
|
'PUT', |
|
|
|
path, |
|
|
|
_constructPath(path), |
|
|
|
|
|
|
|
data: localData, |
|
|
|
|
|
|
|
headers: _getUploadHeaders( |
|
|
|
lastModified: lastModified, |
|
|
|
lastModified: lastModified, |
|
|
|
created: created, |
|
|
|
created: created, |
|
|
|
contentLength: localData.lengthInBytes, |
|
|
|
contentLength: null, |
|
|
|
|
|
|
|
), |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
/// Puts a new file at [path] with [localData] as content. |
|
|
|
/// Puts a new file at [path] with [localData] as content. |
|
|
@ -162,7 +163,7 @@ class WebDavClient { |
|
|
|
_send( |
|
|
|
_send( |
|
|
|
'PUT', |
|
|
|
'PUT', |
|
|
|
_constructPath(path), |
|
|
|
_constructPath(path), |
|
|
|
data: localData, |
|
|
|
dataStream: localData, |
|
|
|
headers: _getUploadHeaders( |
|
|
|
headers: _getUploadHeaders( |
|
|
|
lastModified: lastModified, |
|
|
|
lastModified: lastModified, |
|
|
|
created: created, |
|
|
|
created: created, |
|
|
@ -188,7 +189,7 @@ class WebDavClient { |
|
|
|
file.openRead().map((final chunk) { |
|
|
|
file.openRead().map((final chunk) { |
|
|
|
uploaded += chunk.length; |
|
|
|
uploaded += chunk.length; |
|
|
|
onProgress?.call(uploaded / fileStat.size); |
|
|
|
onProgress?.call(uploaded / fileStat.size); |
|
|
|
return Uint8List.fromList(chunk); |
|
|
|
return chunk as Uint8List; |
|
|
|
}), |
|
|
|
}), |
|
|
|
path, |
|
|
|
path, |
|
|
|
lastModified: lastModified, |
|
|
|
lastModified: lastModified, |
|
|
@ -246,20 +247,10 @@ class WebDavClient { |
|
|
|
await _send( |
|
|
|
await _send( |
|
|
|
'PROPFIND', |
|
|
|
'PROPFIND', |
|
|
|
_constructPath(path), |
|
|
|
_constructPath(path), |
|
|
|
data: Stream.value( |
|
|
|
data: utf8.encode( |
|
|
|
Uint8List.fromList( |
|
|
|
WebDavPropfind(prop: prop ?? WebDavPropWithoutValues()).toXmlElement(namespaces: namespaces).toXmlString(), |
|
|
|
utf8.encode( |
|
|
|
) as Uint8List, |
|
|
|
WebDavPropfind(prop: prop ?? WebDavPropWithoutValues()) |
|
|
|
headers: depth != null ? {'Depth': depth.value} : null, |
|
|
|
.toXmlElement(namespaces: namespaces) |
|
|
|
|
|
|
|
.toXmlString(), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
headers: { |
|
|
|
|
|
|
|
if (depth != null) ...{ |
|
|
|
|
|
|
|
'Depth': depth.value, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
@ -276,16 +267,12 @@ class WebDavClient { |
|
|
|
await _send( |
|
|
|
await _send( |
|
|
|
'REPORT', |
|
|
|
'REPORT', |
|
|
|
_constructPath(path), |
|
|
|
_constructPath(path), |
|
|
|
data: Stream.value( |
|
|
|
data: utf8.encode( |
|
|
|
Uint8List.fromList( |
|
|
|
|
|
|
|
utf8.encode( |
|
|
|
|
|
|
|
WebDavOcFilterFiles( |
|
|
|
WebDavOcFilterFiles( |
|
|
|
filterRules: filterRules, |
|
|
|
filterRules: filterRules, |
|
|
|
prop: prop ?? WebDavPropWithoutValues(), // coverage:ignore-line |
|
|
|
prop: prop ?? WebDavPropWithoutValues(), // coverage:ignore-line |
|
|
|
).toXmlElement(namespaces: namespaces).toXmlString(), |
|
|
|
).toXmlElement(namespaces: namespaces).toXmlString(), |
|
|
|
), |
|
|
|
) as Uint8List, |
|
|
|
), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
@ -303,16 +290,12 @@ class WebDavClient { |
|
|
|
final response = await _send( |
|
|
|
final response = await _send( |
|
|
|
'PROPPATCH', |
|
|
|
'PROPPATCH', |
|
|
|
_constructPath(path), |
|
|
|
_constructPath(path), |
|
|
|
data: Stream.value( |
|
|
|
data: utf8.encode( |
|
|
|
Uint8List.fromList( |
|
|
|
|
|
|
|
utf8.encode( |
|
|
|
|
|
|
|
WebDavPropertyupdate( |
|
|
|
WebDavPropertyupdate( |
|
|
|
set: set != null ? WebDavSet(prop: set) : null, |
|
|
|
set: set != null ? WebDavSet(prop: set) : null, |
|
|
|
remove: remove != null ? WebDavRemove(prop: remove) : null, |
|
|
|
remove: remove != null ? WebDavRemove(prop: remove) : null, |
|
|
|
).toXmlElement(namespaces: namespaces).toXmlString(), |
|
|
|
).toXmlElement(namespaces: namespaces).toXmlString(), |
|
|
|
), |
|
|
|
) as Uint8List, |
|
|
|
), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
); |
|
|
|
); |
|
|
|
final data = await _parseResponse(response); |
|
|
|
final data = await _parseResponse(response); |
|
|
|
for (final a in data.responses) { |
|
|
|
for (final a in data.responses) { |
|
|
|