wasDAVClient – Diff between revs 4 and 5
?pathlinks?
Rev 4 | Rev 5 | |||
---|---|---|---|---|
Line -... | Line 1... | |||
- | 1 | /////////////////////////////////////////////////////////////////////////// |
||
- | 2 | // Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 // |
||
- | 3 | // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
||
- | 4 | // rights of fair usage, the disclaimer and warranty conditions. // |
||
- | 5 | /////////////////////////////////////////////////////////////////////////// |
||
- | 6 | // Originally based on: WebDAV .NET client by Sergey Kazantsev |
||
- | 7 | |
||
1 | using System; |
8 | using System; |
|
2 | using System.Collections.Generic; |
9 | using System.Collections.Generic; |
|
3 | using System.IO; |
10 | using System.IO; |
|
4 | using System.Linq; |
11 | using System.Linq; |
|
5 | using System.Net; |
12 | using System.Net; |
|
6 | using System.Net.Http; |
13 | using System.Net.Http; |
|
Line 45... | Line 52... | |||
45 | private string _basePath = "/"; |
52 | private string _basePath = "/"; |
|
Line 46... | Line 53... | |||
46 | |
53 | |
|
47 | private string _encodedBasePath; |
54 | private string _encodedBasePath; |
|
Line 48... | Line -... | |||
48 | private string _server; |
- | ||
49 | |
55 | private string _server; |
|
50 | |
56 | |
|
51 | public Client(NetworkCredential credential = null) |
57 | public Client(ICredentials credential = null) |
|
Line 52... | Line 58... | |||
52 | { |
58 | { |
|
53 | var handler = new HttpClientHandler(); |
59 | var handler = new HttpClientHandler(); |
|
Line 62... | Line 68... | |||
62 | { |
68 | { |
|
63 | handler.Credentials = credential; |
69 | handler.Credentials = credential; |
|
64 | handler.PreAuthenticate = true; |
70 | handler.PreAuthenticate = true; |
|
65 | } |
71 | } |
|
Line 66... | Line -... | |||
66 | |
- | ||
67 | _client = new HttpClient(handler); |
72 | |
|
68 | _client.Timeout = TimeSpan.FromSeconds(Timeout); |
73 | _client = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(Timeout)}; |
|
Line 69... | Line -... | |||
69 | _client.DefaultRequestHeaders.ExpectContinue = false; |
- | ||
70 | |
74 | _client.DefaultRequestHeaders.ExpectContinue = false; |
|
71 | _uploadClient = new HttpClient(handler); |
75 | |
|
72 | _uploadClient.Timeout = TimeSpan.FromSeconds(Timeout); |
76 | _uploadClient = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(Timeout)}; |
|
Line 73... | Line 77... | |||
73 | _uploadClient.DefaultRequestHeaders.ExpectContinue = false; |
77 | _uploadClient.DefaultRequestHeaders.ExpectContinue = false; |
|
Line 79... | Line 83... | |||
79 | /// Specify the WebDAV hostname (required). |
83 | /// Specify the WebDAV hostname (required). |
|
80 | /// </summary> |
84 | /// </summary> |
|
81 | public string Server |
85 | public string Server |
|
82 | { |
86 | { |
|
83 | get { return _server; } |
87 | get { return _server; } |
|
84 | set |
- | ||
85 | { |
- | ||
86 | value = value.TrimEnd('/'); |
88 | set { _server = value.TrimEnd('/'); } |
|
87 | _server = value; |
- | ||
88 | } |
- | ||
89 | } |
89 | } |
|
Line 90... | Line 90... | |||
90 | |
90 | |
|
91 | /// <summary> |
91 | /// <summary> |
|
92 | /// Specify the path of a WebDAV directory to use as 'root' (default: /) |
92 | /// Specify the path of a WebDAV directory to use as 'root' (default: /) |
|
Line 169... | Line 169... | |||
169 | throw new wasDAVException("Failed deserializing data returned from server."); |
169 | throw new wasDAVException("Failed deserializing data returned from server."); |
|
170 | } |
170 | } |
|
Line 171... | Line 171... | |||
171 | |
171 | |
|
Line 172... | Line -... | |||
172 | var listUrl = listUri.ToString(); |
- | ||
173 | |
172 | var listUrl = listUri.ToString(); |
|
174 | var result = new List<Item>(items.Count()); |
173 | |
|
175 | foreach (var item in items) |
- | ||
176 | { |
174 | return items.AsParallel().Select(async item => |
|
177 | // If it's not a collection, add it to the result |
175 | { |
|
- | 176 | switch (!item.IsCollection) |
||
178 | if (!item.IsCollection) |
177 | { |
|
- | 178 | case true: |
||
- | 179 | return item; |
||
- | 180 | default: |
||
- | 181 | // If it's not the requested parent folder, add it to the result |
||
- | 182 | if (!string.Equals((await GetServerUrl(item.Href, true).ConfigureAwait(false)).ToString(), |
||
- | 183 | listUrl, StringComparison.CurrentCultureIgnoreCase)) |
||
- | 184 | { |
||
- | 185 | return item; |
||
179 | { |
186 | } |
|
- | 187 | break; |
||
180 | result.Add(item); |
188 | } |
|
181 | } |
- | ||
182 | else |
- | ||
183 | { |
- | ||
184 | // If it's not the requested parent folder, add it to the result |
- | ||
185 | var fullHref = await GetServerUrl(item.Href, true).ConfigureAwait(false); |
- | ||
186 | if (!string.Equals(fullHref.ToString(), listUrl, StringComparison.CurrentCultureIgnoreCase)) |
189 | |
|
187 | { |
- | ||
188 | result.Add(item); |
- | ||
189 | } |
- | ||
190 | } |
- | ||
191 | } |
190 | return null; |
|
192 | return result; |
191 | }).Select(o => o.Result).OfType<Item>(); |
|
193 | } |
192 | } |
|
194 | } |
193 | } |
|
195 | finally |
- | ||
196 | { |
194 | finally |
|
197 | if (response != null) |
195 | { |
|
198 | response.Dispose(); |
196 | response?.Dispose(); |
|
Line 199... | Line 197... | |||
199 | } |
197 | } |
|
200 | } |
198 | } |
|
201 | |
199 | |
|
202 | /// <summary> |
200 | /// <summary> |
|
203 | /// List all files present on the server. |
201 | /// List all files present on the server. |
|
204 | /// </summary> |
202 | /// </summary> |
|
205 | /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns> |
203 | /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns> |
|
206 | public async Task<Item> GetFolder(string path = "/") |
- | ||
207 | { |
204 | public async Task<Item> GetFolder(string path = "/") |
|
Line 208... | Line 205... | |||
208 | var listUri = await GetServerUrl(path, true).ConfigureAwait(false); |
205 | { |
|
209 | return await Get(listUri.Uri, path).ConfigureAwait(false); |
206 | return await Get((await GetServerUrl(path, true).ConfigureAwait(false)).Uri).ConfigureAwait(false); |
|
210 | } |
207 | } |
|
211 | |
208 | |
|
212 | /// <summary> |
209 | /// <summary> |
|
213 | /// List all files present on the server. |
210 | /// List all files present on the server. |
|
214 | /// </summary> |
211 | /// </summary> |
|
215 | /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns> |
- | ||
216 | public async Task<Item> GetFile(string path = "/") |
212 | /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns> |
|
Line 217... | Line 213... | |||
217 | { |
213 | public async Task<Item> GetFile(string path = "/") |
|
218 | var listUri = await GetServerUrl(path, false).ConfigureAwait(false); |
214 | { |
|
219 | return await Get(listUri.Uri, path).ConfigureAwait(false); |
215 | return await Get((await GetServerUrl(path, false).ConfigureAwait(false)).Uri).ConfigureAwait(false); |
|
220 | } |
216 | } |
|
221 | |
217 | |
|
222 | |
218 | |
|
223 | /// <summary> |
219 | /// <summary> |
|
224 | /// List all files present on the server. |
220 | /// List all files present on the server. |
|
225 | /// </summary> |
221 | /// </summary> |
|
Line 226... | Line -... | |||
226 | /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns> |
- | ||
227 | private async Task<Item> Get(Uri listUri, string path) |
222 | /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns> |
|
Line 228... | Line 223... | |||
228 | { |
223 | private async Task<Item> Get(Uri listUri) |
|
229 | // Depth header: http://webdav.org/specs/rfc4918.html#rfc.section.9.1.4 |
224 | { |
|
230 | IDictionary<string, string> headers = new Dictionary<string, string>(); |
225 | // Depth header: http://webdav.org/specs/rfc4918.html#rfc.section.9.1.4 |
|
Line 242... | Line 237... | |||
242 | |
237 | |
|
243 | if (response.StatusCode != HttpStatusCode.OK && |
238 | if (response.StatusCode != HttpStatusCode.OK && |
|
244 | (int) response.StatusCode != HttpStatusCode_MultiStatus) |
239 | (int) response.StatusCode != HttpStatusCode_MultiStatus) |
|
245 | { |
240 | { |
|
246 | throw new wasDAVException((int) response.StatusCode, |
241 | throw new wasDAVException((int) response.StatusCode, |
|
247 | string.Format("Failed retrieving item/folder (Status Code: {0})", response.StatusCode)); |
242 | $"Failed retrieving item/folder (Status Code: {response.StatusCode})"); |
|
Line 248... | Line 243... | |||
248 | } |
243 | } |
|
249 | |
244 | |
|
250 | using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) |
245 | using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) |
|
Line 259... | Line 254... | |||
259 | return result; |
254 | return result; |
|
260 | } |
255 | } |
|
261 | } |
256 | } |
|
262 | finally |
257 | finally |
|
263 | { |
258 | { |
|
264 | if (response != null) |
- | ||
265 | response.Dispose(); |
259 | response?.Dispose(); |
|
266 | } |
260 | } |
|
267 | } |
261 | } |
|
Line 268... | Line 262... | |||
268 | |
262 | |
|
269 | /// <summary> |
263 | /// <summary> |
|
Line 311... | Line 305... | |||
311 | |
305 | |
|
312 | return response.IsSuccessStatusCode; |
306 | return response.IsSuccessStatusCode; |
|
313 | } |
307 | } |
|
314 | finally |
308 | finally |
|
315 | { |
- | ||
316 | if (response != null) |
309 | { |
|
317 | response.Dispose(); |
310 | response?.Dispose(); |
|
318 | } |
311 | } |
|
Line 319... | Line 312... | |||
319 | } |
312 | } |
|
320 | |
313 | |
|
321 | |
314 | |
|
322 | /// <summary> |
315 | /// <summary> |
|
323 | /// Create a directory on the server |
316 | /// Create a directory on the server |
|
324 | /// </summary> |
317 | /// </summary> |
|
325 | /// <param name="remotePath">Destination path of the directory on the server</param> |
318 | /// <param name="remotePath">Destination path of the directory on the server.</param> |
|
326 | /// <param name="name"></param> |
319 | /// <param name="name">The name of the folder to create.</param> |
|
327 | public async Task<bool> CreateDir(string remotePath, string name) |
320 | public async Task<bool> CreateDir(string remotePath, string name) |
|
328 | { |
321 | { |
|
Line 348... | Line 341... | |||
348 | |
341 | |
|
349 | return response.IsSuccessStatusCode; |
342 | return response.IsSuccessStatusCode; |
|
350 | } |
343 | } |
|
351 | finally |
344 | finally |
|
352 | { |
- | ||
353 | if (response != null) |
345 | { |
|
354 | response.Dispose(); |
346 | response?.Dispose(); |
|
355 | } |
347 | } |
|
Line 356... | Line 348... | |||
356 | } |
348 | } |
|
357 | |
349 | |
|
358 | public async Task DeleteFolder(string href) |
350 | public async Task DeleteFolder(string href) |
|
359 | { |
- | ||
360 | var listUri = await GetServerUrl(href, true).ConfigureAwait(false); |
351 | { |
|
Line 361... | Line 352... | |||
361 | await Delete(listUri.Uri).ConfigureAwait(false); |
352 | await Delete((await GetServerUrl(href, true).ConfigureAwait(false)).Uri).ConfigureAwait(false); |
|
362 | } |
353 | } |
|
363 | |
354 | |
|
364 | public async Task DeleteFile(string href) |
- | ||
365 | { |
355 | public async Task DeleteFile(string href) |
|
Line 366... | Line 356... | |||
366 | var listUri = await GetServerUrl(href, false).ConfigureAwait(false); |
356 | { |
|
367 | await Delete(listUri.Uri).ConfigureAwait(false); |
357 | await Delete((await GetServerUrl(href, false).ConfigureAwait(false)).Uri).ConfigureAwait(false); |
|
Line 380... | Line 370... | |||
380 | } |
370 | } |
|
Line 381... | Line 371... | |||
381 | |
371 | |
|
382 | public async Task<bool> MoveFolder(string srcFolderPath, string dstFolderPath) |
372 | public async Task<bool> MoveFolder(string srcFolderPath, string dstFolderPath) |
|
383 | { |
373 | { |
|
- | 374 | // Should have a trailing slash. |
||
384 | // Should have a trailing slash. |
375 | return |
|
385 | var srcUri = await GetServerUrl(srcFolderPath, true).ConfigureAwait(false); |
376 | await |
|
386 | var dstUri = await GetServerUrl(dstFolderPath, true).ConfigureAwait(false); |
- | ||
387 | |
377 | Move((await GetServerUrl(srcFolderPath, true).ConfigureAwait(false)).Uri, |
|
388 | return await Move(srcUri.Uri, dstUri.Uri).ConfigureAwait(false); |
378 | (await GetServerUrl(dstFolderPath, true).ConfigureAwait(false)).Uri).ConfigureAwait(false); |
|
Line 389... | Line 379... | |||
389 | } |
379 | } |
|
390 | |
380 | |
|
391 | public async Task<bool> MoveFile(string srcFilePath, string dstFilePath) |
381 | public async Task<bool> MoveFile(string srcFilePath, string dstFilePath) |
|
- | 382 | { |
||
392 | { |
383 | // Should not have a trailing slash. |
|
393 | // Should not have a trailing slash. |
384 | return |
|
394 | var srcUri = await GetServerUrl(srcFilePath, false).ConfigureAwait(false); |
- | ||
395 | var dstUri = await GetServerUrl(dstFilePath, false).ConfigureAwait(false); |
385 | await |
|
396 | |
386 | Move((await GetServerUrl(srcFilePath, false).ConfigureAwait(false)).Uri, |
|
Line 397... | Line 387... | |||
397 | return await Move(srcUri.Uri, dstUri.Uri).ConfigureAwait(false); |
387 | (await GetServerUrl(dstFilePath, false).ConfigureAwait(false)).Uri).ConfigureAwait(false); |
|
398 | } |
388 | } |
|
399 | |
389 | |
|
Line 400... | Line 390... | |||
400 | |
390 | |
|
- | 391 | private async Task<bool> Move(Uri srcUri, Uri dstUri) |
||
401 | private async Task<bool> Move(Uri srcUri, Uri dstUri) |
392 | { |
|
- | 393 | const string requestContent = "MOVE"; |
||
Line 402... | Line 394... | |||
402 | { |
394 | |
|
403 | const string requestContent = "MOVE"; |
395 | IDictionary<string, string> headers = new Dictionary<string, string> |
|
404 | |
396 | { |
|
405 | IDictionary<string, string> headers = new Dictionary<string, string>(); |
397 | {"Destination", dstUri.ToString()} |
|
Line 434... | Line 426... | |||
434 | IDictionary<string, string> headers = null, byte[] content = null) |
426 | IDictionary<string, string> headers = null, byte[] content = null) |
|
435 | { |
427 | { |
|
436 | using (var request = new HttpRequestMessage(method, uri)) |
428 | using (var request = new HttpRequestMessage(method, uri)) |
|
437 | { |
429 | { |
|
438 | request.Headers.Connection.Add("Keep-Alive"); |
430 | request.Headers.Connection.Add("Keep-Alive"); |
|
439 | if (!string.IsNullOrWhiteSpace(UserAgent)) |
431 | request.Headers.UserAgent.Add(!string.IsNullOrWhiteSpace(UserAgent) |
|
440 | request.Headers.UserAgent.Add(new ProductInfoHeaderValue(UserAgent, UserAgentVersion)); |
432 | ? new ProductInfoHeaderValue(UserAgent, UserAgentVersion) |
|
441 | else |
- | ||
442 | request.Headers.UserAgent.Add(new ProductInfoHeaderValue("WebDAVClient", AssemblyVersion)); |
433 | : new ProductInfoHeaderValue("WebDAVClient", AssemblyVersion)); |
|
Line 443... | Line 434... | |||
443 | |
434 | |
|
444 | if (headers != null) |
435 | if (headers != null) |
|
445 | { |
436 | { |
|
446 | foreach (var key in headers.Keys) |
437 | foreach (var key in headers.Keys) |
|
Line 471... | Line 462... | |||
471 | IDictionary<string, string> headers = null) |
462 | IDictionary<string, string> headers = null) |
|
472 | { |
463 | { |
|
473 | using (var request = new HttpRequestMessage(method, uri)) |
464 | using (var request = new HttpRequestMessage(method, uri)) |
|
474 | { |
465 | { |
|
475 | request.Headers.Connection.Add("Keep-Alive"); |
466 | request.Headers.Connection.Add("Keep-Alive"); |
|
476 | if (!string.IsNullOrWhiteSpace(UserAgent)) |
467 | request.Headers.UserAgent.Add(!string.IsNullOrWhiteSpace(UserAgent) |
|
477 | request.Headers.UserAgent.Add(new ProductInfoHeaderValue(UserAgent, UserAgentVersion)); |
468 | ? new ProductInfoHeaderValue(UserAgent, UserAgentVersion) |
|
478 | else |
- | ||
479 | request.Headers.UserAgent.Add(new ProductInfoHeaderValue("WebDAVClient", AssemblyVersion)); |
469 | : new ProductInfoHeaderValue("WebDAVClient", AssemblyVersion)); |
|
Line 480... | Line 470... | |||
480 | |
470 | |
|
481 | if (headers != null) |
471 | if (headers != null) |
|
482 | { |
472 | { |
|
483 | foreach (var key in headers.Keys) |
473 | foreach (var key in headers.Keys) |
|
Line 490... | Line 480... | |||
490 | if (content != null) |
480 | if (content != null) |
|
491 | { |
481 | { |
|
492 | request.Content = new StreamContent(content); |
482 | request.Content = new StreamContent(content); |
|
493 | } |
483 | } |
|
Line 494... | Line -... | |||
494 | |
- | ||
495 | var client = _uploadClient ?? _client; |
484 | |
|
496 | return await client.SendAsync(request).ConfigureAwait(false); |
485 | return await (_uploadClient ?? _client).SendAsync(request).ConfigureAwait(false); |
|
497 | } |
486 | } |
|
Line 498... | Line 487... | |||
498 | } |
487 | } |
|
499 | |
488 | |
|
Line 516... | Line 505... | |||
516 | { |
505 | { |
|
517 | // Resolve the base path on the server |
506 | // Resolve the base path on the server |
|
518 | if (_encodedBasePath == null) |
507 | if (_encodedBasePath == null) |
|
519 | { |
508 | { |
|
520 | var baseUri = new UriBuilder(_server) {Path = _basePath}; |
509 | var baseUri = new UriBuilder(_server) {Path = _basePath}; |
|
521 | var root = await Get(baseUri.Uri, null).ConfigureAwait(false); |
510 | var root = await Get(baseUri.Uri).ConfigureAwait(false); |
|
Line 522... | Line 511... | |||
522 | |
511 | |
|
523 | _encodedBasePath = root.Href; |
512 | _encodedBasePath = root.Href; |
|
Line 580... | Line 569... | |||
580 | } |
569 | } |
|
581 | } |
570 | } |
|
Line 582... | Line 571... | |||
582 | |
571 | |
|
583 | public void Dispose() |
572 | public void Dispose() |
|
584 | { |
- | ||
585 | if(_client != null) |
573 | { |
|
586 | _client.Dispose(); |
- | ||
587 | if (_uploadClient != null) |
574 | _client?.Dispose(); |
|
588 | _uploadClient.Dispose(); |
575 | _uploadClient?.Dispose(); |
|
Line 589... | Line 576... | |||
589 | } |
576 | } |
|
590 | |
577 | |
|
591 | #endregion |
578 | #endregion |
|
592 | } |
579 | } |