wasDAVClient – Diff between revs 6 and 8

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 6 Rev 8
1 /////////////////////////////////////////////////////////////////////////// 1 ///////////////////////////////////////////////////////////////////////////
2 // Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 // 2 // Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 //
3 // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // 3 // Please see: http://www.gnu.org/licenses/gpl.html for legal details, //
4 // rights of fair usage, the disclaimer and warranty conditions. // 4 // rights of fair usage, the disclaimer and warranty conditions. //
5 /////////////////////////////////////////////////////////////////////////// 5 ///////////////////////////////////////////////////////////////////////////
6 // Originally based on: WebDAV .NET client by Sergey Kazantsev 6 // Originally based on: WebDAV .NET client by Sergey Kazantsev
7   7  
8 using System; 8 using System;
9 using System.Collections.Generic; 9 using System.Collections.Generic;
10 using System.IO; 10 using System.IO;
11 using System.Linq; 11 using System.Linq;
12 using System.Net; 12 using System.Net;
13 using System.Net.Http; 13 using System.Net.Http;
14 using System.Net.Http.Headers; 14 using System.Net.Http.Headers;
15 using System.Text; 15 using System.Text;
16 using System.Threading.Tasks; 16 using System.Threading.Tasks;
17 using wasDAVClient.Helpers; 17 using wasDAVClient.Helpers;
18 using wasDAVClient.Model; 18 using wasDAVClient.Model;
19   19  
20 namespace wasDAVClient 20 namespace wasDAVClient
21 { 21 {
22 public class Client : IClient, IDisposable 22 public class Client : IClient, IDisposable
23 { 23 {
24 private const int HttpStatusCode_MultiStatus = 207; 24 private const int HttpStatusCode_MultiStatus = 207;
25   25  
26 // http://webdav.org/specs/rfc4918.html#METHOD_PROPFIND 26 // http://webdav.org/specs/rfc4918.html#METHOD_PROPFIND
27 private const string PropFindRequestContent = 27 private const string PropFindRequestContent =
28 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + 28 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
29 "<propfind xmlns=\"DAV:\">" + 29 "<propfind xmlns=\"DAV:\">" +
30 "<allprop/>" + 30 "<allprop/>" +
31 //" <propname/>" + 31 //" <propname/>" +
32 //" <prop>" + 32 //" <prop>" +
33 //" <creationdate/>" + 33 //" <creationdate/>" +
34 //" <getlastmodified/>" + 34 //" <getlastmodified/>" +
35 //" <displayname/>" + 35 //" <displayname/>" +
36 //" <getcontentlength/>" + 36 //" <getcontentlength/>" +
37 //" <getcontenttype/>" + 37 //" <getcontenttype/>" +
38 //" <getetag/>" + 38 //" <getetag/>" +
39 //" <resourcetype/>" + 39 //" <resourcetype/>" +
40 //" </prop> " + 40 //" </prop> " +
41 "</propfind>"; 41 "</propfind>";
42   42  
43 private static readonly HttpMethod PropFind = new HttpMethod("PROPFIND"); 43 private static readonly HttpMethod PropFind = new HttpMethod("PROPFIND");
44 private static readonly HttpMethod MoveMethod = new HttpMethod("MOVE"); 44 private static readonly HttpMethod MoveMethod = new HttpMethod("MOVE");
45   45  
46 private static readonly HttpMethod MkCol = new HttpMethod(WebRequestMethods.Http.MkCol); 46 private static readonly HttpMethod MkCol = new HttpMethod(WebRequestMethods.Http.MkCol);
47   47  
48 private static readonly string AssemblyVersion = typeof(IClient).Assembly.GetName().Version.ToString(); 48 private static readonly string AssemblyVersion = typeof(IClient).Assembly.GetName().Version.ToString();
49   49  
50 private readonly HttpClient _client; 50 private readonly HttpClient _client;
51 private readonly HttpClient _uploadClient; 51 private readonly HttpClient _uploadClient;
52 private string _basePath = "/"; 52 private string _basePath = "/";
53   53  
54 private string _encodedBasePath; 54 private string _encodedBasePath;
55 private string _server; 55 private string _server;
56   56  
57 public Client(ICredentials credential = null) 57 public Client(ICredentials credential = null)
58 { 58 {
59 var handler = new HttpClientHandler(); 59 var handler = new HttpClientHandler();
60   60  
61 if (handler.SupportsProxy) 61 if (handler.SupportsProxy)
62 handler.Proxy = WebRequest.DefaultWebProxy; 62 handler.Proxy = WebRequest.DefaultWebProxy;
63   63  
64 if (handler.SupportsAutomaticDecompression) 64 if (handler.SupportsAutomaticDecompression)
65 handler.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; 65 handler.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
66   66  
67 if (credential != null) 67 if (credential != null)
68 { 68 {
69 handler.Credentials = credential; 69 handler.Credentials = credential;
70 handler.PreAuthenticate = true; 70 handler.PreAuthenticate = true;
71 } 71 }
72   72  
73 _client = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(Timeout)}; 73 _client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(Timeout) };
74 _client.DefaultRequestHeaders.ExpectContinue = false; 74 _client.DefaultRequestHeaders.ExpectContinue = false;
75   75  
76 _uploadClient = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(Timeout)}; 76 _uploadClient = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(Timeout) };
77 _uploadClient.DefaultRequestHeaders.ExpectContinue = false; 77 _uploadClient.DefaultRequestHeaders.ExpectContinue = false;
78 } 78 }
79   79  
80 #region WebDAV connection parameters 80 #region WebDAV connection parameters
81   81  
82 /// <summary> 82 /// <summary>
83 /// Specify the WebDAV hostname (required). 83 /// Specify the WebDAV hostname (required).
84 /// </summary> 84 /// </summary>
85 public string Server 85 public string Server
86 { 86 {
87 get { return _server; } 87 get { return _server; }
88 set { _server = value.TrimEnd('/'); } 88 set { _server = value.TrimEnd('/'); }
89 } 89 }
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: /)
93 /// </summary> 93 /// </summary>
94 public string BasePath 94 public string BasePath
95 { 95 {
96 get { return _basePath; } 96 get { return _basePath; }
97 set 97 set
98 { 98 {
99 value = value.Trim('/'); 99 value = value.Trim('/');
100 if (string.IsNullOrEmpty(value)) 100 if (string.IsNullOrEmpty(value))
101 _basePath = "/"; 101 _basePath = "/";
102 else 102 else
103 _basePath = "/" + value + "/"; 103 _basePath = "/" + value + "/";
104 } 104 }
105 } 105 }
106   106  
107 /// <summary> 107 /// <summary>
108 /// Specify an port (default: null = auto-detect) 108 /// Specify an port (default: null = auto-detect)
109 /// </summary> 109 /// </summary>
110 public int? Port { get; set; } 110 public int? Port { get; set; }
111   111  
112 /// <summary> 112 /// <summary>
113 /// Specify the UserAgent (and UserAgent version) string to use in requests 113 /// Specify the UserAgent (and UserAgent version) string to use in requests
114 /// </summary> 114 /// </summary>
115 public string UserAgent { get; set; } 115 public string UserAgent { get; set; }
116   116  
117 /// <summary> 117 /// <summary>
118 /// Specify the UserAgent (and UserAgent version) string to use in requests 118 /// Specify the UserAgent (and UserAgent version) string to use in requests
119 /// </summary> 119 /// </summary>
120 public string UserAgentVersion { get; set; } 120 public string UserAgentVersion { get; set; }
121   121  
122 /// <summary> 122 /// <summary>
123 /// The HTTP request timeout in seconds. 123 /// The HTTP request timeout in seconds.
124 /// </summary> 124 /// </summary>
125 public int Timeout { get; set; } = 60; 125 public int Timeout { get; set; } = 60;
126   126  
127 #endregion 127 #endregion WebDAV connection parameters
128   128  
129 #region WebDAV operations 129 #region WebDAV operations
130   130  
131 /// <summary> 131 /// <summary>
132 /// List all files present on the server. 132 /// List all files present on the server.
133 /// </summary> 133 /// </summary>
134 /// <param name="path">List only files in this path</param> 134 /// <param name="path">List only files in this path</param>
135 /// <param name="depth">Recursion depth</param> 135 /// <param name="depth">Recursion depth</param>
136 /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns> 136 /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns>
137 public async Task<IEnumerable<Item>> List(string path = "/", int? depth = 1) 137 public async Task<IEnumerable<Item>> List(string path = "/", string depth = Constants.DavDepth.MEMBERS)
138 { 138 {
139 var listUri = await GetServerUrl(path, true).ConfigureAwait(false); 139 var listUri = await GetServerUrl(path, true).ConfigureAwait(false);
140   140  
141 // Depth header: http://webdav.org/specs/rfc4918.html#rfc.section.9.1.4 141 // Depth header: http://webdav.org/specs/rfc4918.html#rfc.section.9.1.4
142 IDictionary<string, string> headers = new Dictionary<string, string>(); 142 IDictionary<string, string> headers = new Dictionary<string, string>();
143 if (depth != null) -  
144 { -  
145 headers.Add("Depth", depth.ToString()); 143 headers.Add("Depth", depth);
146 } -  
147   144  
148 HttpResponseMessage response = null; 145 HttpResponseMessage response = null;
149   146  
150 try 147 try
151 { 148 {
152 response = 149 response =
153 await 150 await
154 HttpRequest(listUri.Uri, PropFind, headers, Encoding.UTF8.GetBytes(PropFindRequestContent)) 151 HttpRequest(listUri.Uri, PropFind, headers, Encoding.UTF8.GetBytes(PropFindRequestContent))
155 .ConfigureAwait(false); 152 .ConfigureAwait(false);
156   153  
157 if (response.StatusCode != HttpStatusCode.OK && 154 if (response.StatusCode != HttpStatusCode.OK &&
158 (int) response.StatusCode != HttpStatusCode_MultiStatus) 155 (int)response.StatusCode != HttpStatusCode_MultiStatus)
159 { 156 {
160 throw new wasDAVException((int) response.StatusCode, "Failed retrieving items in folder."); 157 throw new wasDAVException((int)response.StatusCode, "Failed retrieving items in folder.");
161 } 158 }
162   159  
163 using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) 160 using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
164 { 161 {
165 var items = ResponseParser.ParseItems(stream); 162 var items = ResponseParser.ParseItems(stream);
166   163  
167 if (items == null) 164 if (items == null)
168 { 165 {
169 throw new wasDAVException("Failed deserializing data returned from server."); 166 throw new wasDAVException("Failed deserializing data returned from server.");
170 } 167 }
171   168  
172 var listUrl = listUri.ToString(); 169 var listUrl = listUri.ToString();
173   170  
174 return items.AsParallel().Select(async item => 171 return items.AsParallel().Select(async item =>
175 { 172 {
176 switch (!item.IsCollection) 173 switch (!item.IsCollection)
177 { 174 {
178 case true: 175 case true:
179 return item; 176 return item;
-   177  
180 default: 178 default:
181 // If it's not the requested parent folder, add it to the result 179 // 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(), 180 if (!string.Equals((await GetServerUrl(item.Href, true).ConfigureAwait(false)).ToString(),
183 listUrl, StringComparison.CurrentCultureIgnoreCase)) 181 listUrl, StringComparison.CurrentCultureIgnoreCase))
184 { 182 {
185 return item; 183 return item;
186 } 184 }
187 break; 185 break;
188 } 186 }
189   187  
190 return null; 188 return null;
191 }).Select(o => o.Result).OfType<Item>(); 189 }).Select(o => o.Result).OfType<Item>();
192 } 190 }
193 } 191 }
194 finally 192 finally
195 { 193 {
196 response?.Dispose(); 194 response?.Dispose();
197 } 195 }
198 } 196 }
199   197  
200 /// <summary> 198 /// <summary>
201 /// List all files present on the server. 199 /// List all files present on the server.
202 /// </summary> 200 /// </summary>
203 /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns> 201 /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns>
204 public async Task<Item> GetFolder(string path = "/") 202 public async Task<Item> GetFolder(string path = "/")
205 { 203 {
206 return await Get((await GetServerUrl(path, true).ConfigureAwait(false)).Uri).ConfigureAwait(false); 204 return await Get((await GetServerUrl(path, true).ConfigureAwait(false)).Uri).ConfigureAwait(false);
207 } 205 }
208   206  
209 /// <summary> 207 /// <summary>
210 /// List all files present on the server. 208 /// List all files present on the server.
211 /// </summary> 209 /// </summary>
212 /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns> 210 /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns>
213 public async Task<Item> GetFile(string path = "/") 211 public async Task<Item> GetFile(string path = "/")
214 { 212 {
215 return await Get((await GetServerUrl(path, false).ConfigureAwait(false)).Uri).ConfigureAwait(false); 213 return await Get((await GetServerUrl(path, false).ConfigureAwait(false)).Uri).ConfigureAwait(false);
216 } 214 }
217   -  
218   215  
219 /// <summary> 216 /// <summary>
220 /// List all files present on the server. 217 /// List all files present on the server.
221 /// </summary> 218 /// </summary>
222 /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns> 219 /// <returns>A list of files (entries without a trailing slash) and directories (entries with a trailing slash)</returns>
223 private async Task<Item> Get(Uri listUri) 220 private async Task<Item> Get(Uri listUri)
224 { 221 {
225 // Depth header: http://webdav.org/specs/rfc4918.html#rfc.section.9.1.4 222 // Depth header: http://webdav.org/specs/rfc4918.html#rfc.section.9.1.4
226 IDictionary<string, string> headers = new Dictionary<string, string>(); 223 IDictionary<string, string> headers = new Dictionary<string, string>();
227 headers.Add("Depth", "0"); 224 headers.Add("Depth", "0");
228   225  
229 HttpResponseMessage response = null; 226 HttpResponseMessage response = null;
230   227  
231 try 228 try
232 { 229 {
233 response = 230 response =
234 await 231 await
235 HttpRequest(listUri, PropFind, headers, Encoding.UTF8.GetBytes(PropFindRequestContent)) 232 HttpRequest(listUri, PropFind, headers, Encoding.UTF8.GetBytes(PropFindRequestContent))
236 .ConfigureAwait(false); 233 .ConfigureAwait(false);
237   234  
238 if (response.StatusCode != HttpStatusCode.OK && 235 if (response.StatusCode != HttpStatusCode.OK &&
239 (int) response.StatusCode != HttpStatusCode_MultiStatus) 236 (int)response.StatusCode != HttpStatusCode_MultiStatus)
240 { 237 {
241 throw new wasDAVException((int) response.StatusCode, 238 throw new wasDAVException((int)response.StatusCode,
242 $"Failed retrieving item/folder (Status Code: {response.StatusCode})"); 239 $"Failed retrieving item/folder (Status Code: {response.StatusCode})");
243 } 240 }
244   241  
245 using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) 242 using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
246 { 243 {
247 var result = ResponseParser.ParseItem(stream); 244 var result = ResponseParser.ParseItem(stream);
248   245  
249 if (result == null) 246 if (result == null)
250 { 247 {
251 throw new wasDAVException("Failed deserializing data returned from server."); 248 throw new wasDAVException("Failed deserializing data returned from server.");
252 } 249 }
253   250  
254 return result; 251 return result;
255 } 252 }
256 } 253 }
257 finally 254 finally
258 { 255 {
259 response?.Dispose(); 256 response?.Dispose();
260 } 257 }
261 } 258 }
262   259  
263 /// <summary> 260 /// <summary>
264 /// Download a file from the server 261 /// Download a file from the server
265 /// </summary> 262 /// </summary>
266 /// <param name="remoteFilePath">Source path and filename of the file on the server</param> 263 /// <param name="remoteFilePath">Source path and filename of the file on the server</param>
267 public async Task<Stream> Download(string remoteFilePath) 264 public async Task<Stream> Download(string remoteFilePath)
268 { 265 {
269 // Should not have a trailing slash. 266 // Should not have a trailing slash.
270 var downloadUri = await GetServerUrl(remoteFilePath, false).ConfigureAwait(false); 267 var downloadUri = await GetServerUrl(remoteFilePath, false).ConfigureAwait(false);
271   268  
272 var dictionary = new Dictionary<string, string> {{"translate", "f"}}; 269 var dictionary = new Dictionary<string, string> { { "translate", "f" } };
273 var response = await HttpRequest(downloadUri.Uri, HttpMethod.Get, dictionary).ConfigureAwait(false); 270 var response = await HttpRequest(downloadUri.Uri, HttpMethod.Get, dictionary).ConfigureAwait(false);
274 if (response.StatusCode != HttpStatusCode.OK) 271 if (response.StatusCode != HttpStatusCode.OK)
275 { 272 {
276 throw new wasDAVException((int) response.StatusCode, "Failed retrieving file."); 273 throw new wasDAVException((int)response.StatusCode, "Failed retrieving file.");
277 } 274 }
278 return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); 275 return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
279 } 276 }
280   277  
281 /// <summary> 278 /// <summary>
282 /// Download a file from the server 279 /// Download a file from the server
283 /// </summary> 280 /// </summary>
284 /// <param name="remoteFilePath">Source path and filename of the file on the server</param> 281 /// <param name="remoteFilePath">Source path and filename of the file on the server</param>
285 /// <param name="content"></param> 282 /// <param name="content"></param>
286 /// <param name="name"></param> 283 /// <param name="name"></param>
287 public async Task<bool> Upload(string remoteFilePath, Stream content, string name) 284 public async Task<bool> Upload(string remoteFilePath, Stream content, string name)
288 { 285 {
289 // Should not have a trailing slash. 286 // Should not have a trailing slash.
290 var uploadUri = 287 var uploadUri =
291 await GetServerUrl(remoteFilePath.TrimEnd('/') + "/" + name.TrimStart('/'), false).ConfigureAwait(false); 288 await GetServerUrl(remoteFilePath.TrimEnd('/') + "/" + name.TrimStart('/'), false).ConfigureAwait(false);
292   289  
293 HttpResponseMessage response = null; 290 HttpResponseMessage response = null;
294   291  
295 try 292 try
296 { 293 {
297 response = await HttpUploadRequest(uploadUri.Uri, HttpMethod.Put, content).ConfigureAwait(false); 294 response = await HttpUploadRequest(uploadUri.Uri, HttpMethod.Put, content).ConfigureAwait(false);
298   295  
299 if (response.StatusCode != HttpStatusCode.OK && 296 if (response.StatusCode != HttpStatusCode.OK &&
300 response.StatusCode != HttpStatusCode.NoContent && 297 response.StatusCode != HttpStatusCode.NoContent &&
301 response.StatusCode != HttpStatusCode.Created) 298 response.StatusCode != HttpStatusCode.Created)
302 { 299 {
303 throw new wasDAVException((int) response.StatusCode, "Failed uploading file."); 300 throw new wasDAVException((int)response.StatusCode, "Failed uploading file.");
304 } 301 }
305   302  
306 return response.IsSuccessStatusCode; 303 return response.IsSuccessStatusCode;
307 } 304 }
308 finally 305 finally
309 { 306 {
310 response?.Dispose(); 307 response?.Dispose();
311 } 308 }
312 } 309 }
313   -  
314   310  
315 /// <summary> 311 /// <summary>
316 /// Create a directory on the server 312 /// Create a directory on the server
317 /// </summary> 313 /// </summary>
318 /// <param name="remotePath">Destination path of the directory on the server.</param> 314 /// <param name="remotePath">Destination path of the directory on the server.</param>
319 /// <param name="name">The name of the folder to create.</param> 315 /// <param name="name">The name of the folder to create.</param>
320 public async Task<bool> CreateDir(string remotePath, string name) 316 public async Task<bool> CreateDir(string remotePath, string name)
321 { 317 {
322 // Should not have a trailing slash. 318 // Should not have a trailing slash.
323 var dirUri = 319 var dirUri =
324 await GetServerUrl(remotePath.TrimEnd('/') + "/" + name.TrimStart('/'), false).ConfigureAwait(false); 320 await GetServerUrl(remotePath.TrimEnd('/') + "/" + name.TrimStart('/'), false).ConfigureAwait(false);
325   321  
326 HttpResponseMessage response = null; 322 HttpResponseMessage response = null;
327   323  
328 try 324 try
329 { 325 {
330 response = await HttpRequest(dirUri.Uri, MkCol).ConfigureAwait(false); 326 response = await HttpRequest(dirUri.Uri, MkCol).ConfigureAwait(false);
331   327  
332 if (response.StatusCode == HttpStatusCode.Conflict) 328 if (response.StatusCode == HttpStatusCode.Conflict)
333 throw new wasDAVConflictException((int) response.StatusCode, "Failed creating folder."); 329 throw new wasDAVConflictException((int)response.StatusCode, "Failed creating folder.");
334   330  
335 if (response.StatusCode != HttpStatusCode.OK && 331 if (response.StatusCode != HttpStatusCode.OK &&
336 response.StatusCode != HttpStatusCode.NoContent && 332 response.StatusCode != HttpStatusCode.NoContent &&
337 response.StatusCode != HttpStatusCode.Created) 333 response.StatusCode != HttpStatusCode.Created)
338 { 334 {
339 throw new wasDAVException((int) response.StatusCode, "Failed creating folder."); 335 throw new wasDAVException((int)response.StatusCode, "Failed creating folder.");
340 } 336 }
341   337  
342 return response.IsSuccessStatusCode; 338 return response.IsSuccessStatusCode;
343 } 339 }
344 finally 340 finally
345 { 341 {
346 response?.Dispose(); 342 response?.Dispose();
347 } 343 }
348 } 344 }
349   345  
350 public async Task DeleteFolder(string href) 346 public async Task DeleteFolder(string href)
351 { 347 {
352 await Delete((await GetServerUrl(href, true).ConfigureAwait(false)).Uri).ConfigureAwait(false); 348 await Delete((await GetServerUrl(href, true).ConfigureAwait(false)).Uri).ConfigureAwait(false);
353 } 349 }
354   350  
355 public async Task DeleteFile(string href) 351 public async Task DeleteFile(string href)
356 { 352 {
357 await Delete((await GetServerUrl(href, false).ConfigureAwait(false)).Uri).ConfigureAwait(false); 353 await Delete((await GetServerUrl(href, false).ConfigureAwait(false)).Uri).ConfigureAwait(false);
358 } 354 }
359   -  
360   355  
361 private async Task Delete(Uri listUri) 356 private async Task Delete(Uri listUri)
362 { 357 {
363 var response = await HttpRequest(listUri, HttpMethod.Delete).ConfigureAwait(false); 358 var response = await HttpRequest(listUri, HttpMethod.Delete).ConfigureAwait(false);
364   359  
365 if (response.StatusCode != HttpStatusCode.OK && 360 if (response.StatusCode != HttpStatusCode.OK &&
366 response.StatusCode != HttpStatusCode.NoContent) 361 response.StatusCode != HttpStatusCode.NoContent)
367 { 362 {
368 throw new wasDAVException((int) response.StatusCode, "Failed deleting item."); 363 throw new wasDAVException((int)response.StatusCode, "Failed deleting item.");
369 } 364 }
370 } 365 }
371   366  
372 public async Task<bool> MoveFolder(string srcFolderPath, string dstFolderPath) 367 public async Task<bool> MoveFolder(string srcFolderPath, string dstFolderPath)
373 { 368 {
374 // Should have a trailing slash. 369 // Should have a trailing slash.
375 return 370 return
376 await 371 await
377 Move((await GetServerUrl(srcFolderPath, true).ConfigureAwait(false)).Uri, 372 Move((await GetServerUrl(srcFolderPath, true).ConfigureAwait(false)).Uri,
378 (await GetServerUrl(dstFolderPath, true).ConfigureAwait(false)).Uri).ConfigureAwait(false); 373 (await GetServerUrl(dstFolderPath, true).ConfigureAwait(false)).Uri).ConfigureAwait(false);
379 } 374 }
380   375  
381 public async Task<bool> MoveFile(string srcFilePath, string dstFilePath) 376 public async Task<bool> MoveFile(string srcFilePath, string dstFilePath)
382 { 377 {
383 // Should not have a trailing slash. 378 // Should not have a trailing slash.
384 return 379 return
385 await 380 await
386 Move((await GetServerUrl(srcFilePath, false).ConfigureAwait(false)).Uri, 381 Move((await GetServerUrl(srcFilePath, false).ConfigureAwait(false)).Uri,
387 (await GetServerUrl(dstFilePath, false).ConfigureAwait(false)).Uri).ConfigureAwait(false); 382 (await GetServerUrl(dstFilePath, false).ConfigureAwait(false)).Uri).ConfigureAwait(false);
388 } 383 }
389   -  
390   384  
391 private async Task<bool> Move(Uri srcUri, Uri dstUri) 385 private async Task<bool> Move(Uri srcUri, Uri dstUri)
392 { 386 {
393 const string requestContent = "MOVE"; 387 const string requestContent = "MOVE";
394   388  
395 IDictionary<string, string> headers = new Dictionary<string, string> 389 IDictionary<string, string> headers = new Dictionary<string, string>
396 { 390 {
397 {"Destination", dstUri.ToString()} 391 {"Destination", dstUri.ToString()}
398 }; 392 };
399   393  
400 var response = 394 var response =
401 await 395 await
402 HttpRequest(srcUri, MoveMethod, headers, Encoding.UTF8.GetBytes(requestContent)) 396 HttpRequest(srcUri, MoveMethod, headers, Encoding.UTF8.GetBytes(requestContent))
403 .ConfigureAwait(false); 397 .ConfigureAwait(false);
404   398  
405 if (response.StatusCode != HttpStatusCode.OK && 399 if (response.StatusCode != HttpStatusCode.OK &&
406 response.StatusCode != HttpStatusCode.Created) 400 response.StatusCode != HttpStatusCode.Created)
407 { 401 {
408 throw new wasDAVException((int) response.StatusCode, "Failed moving file."); 402 throw new wasDAVException((int)response.StatusCode, "Failed moving file.");
409 } 403 }
410   404  
411 return response.IsSuccessStatusCode; 405 return response.IsSuccessStatusCode;
412 } 406 }
413   407  
414 #endregion 408 #endregion WebDAV operations
415   409  
416 #region Server communication 410 #region Server communication
417   411  
418 /// <summary> 412 /// <summary>
419 /// Perform the WebDAV call and fire the callback when finished. 413 /// Perform the WebDAV call and fire the callback when finished.
420 /// </summary> 414 /// </summary>
421 /// <param name="uri"></param> 415 /// <param name="uri"></param>
422 /// <param name="method"></param> 416 /// <param name="method"></param>
423 /// <param name="headers"></param> 417 /// <param name="headers"></param>
424 /// <param name="content"></param> 418 /// <param name="content"></param>
425 private async Task<HttpResponseMessage> HttpRequest(Uri uri, HttpMethod method, 419 private async Task<HttpResponseMessage> HttpRequest(Uri uri, HttpMethod method,
426 IDictionary<string, string> headers = null, byte[] content = null) 420 IDictionary<string, string> headers = null, byte[] content = null)
427 { 421 {
428 using (var request = new HttpRequestMessage(method, uri)) 422 using (var request = new HttpRequestMessage(method, uri))
429 { 423 {
430 request.Headers.Connection.Add("Keep-Alive"); 424 request.Headers.Connection.Add("Keep-Alive");
431 request.Headers.UserAgent.Add(!string.IsNullOrWhiteSpace(UserAgent) 425 request.Headers.UserAgent.Add(!string.IsNullOrWhiteSpace(UserAgent)
432 ? new ProductInfoHeaderValue(UserAgent, UserAgentVersion) 426 ? new ProductInfoHeaderValue(UserAgent, UserAgentVersion)
433 : new ProductInfoHeaderValue("WebDAVClient", AssemblyVersion)); 427 : new ProductInfoHeaderValue("WebDAVClient", AssemblyVersion));
434 request.Headers.Add("Accept", @"*/*"); 428 request.Headers.Add("Accept", @"*/*");
435 request.Headers.Add("Accept-Encoding", "gzip,deflate"); 429 request.Headers.Add("Accept-Encoding", "gzip,deflate");
436   430  
437 if (headers != null) 431 if (headers != null)
438 { 432 {
439 foreach (var key in headers.Keys) 433 foreach (var key in headers.Keys)
440 { 434 {
441 request.Headers.Add(key, headers[key]); 435 request.Headers.Add(key, headers[key]);
442 } 436 }
443 } 437 }
444   438  
445 // Need to send along content? 439 // Need to send along content?
446 if (content != null) 440 if (content != null)
447 { 441 {
448 request.Content = new ByteArrayContent(content); 442 request.Content = new ByteArrayContent(content);
449 request.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml"); 443 request.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
450 } 444 }
451   445  
452 return await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); 446 return await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
453 } 447 }
454 } 448 }
455   449  
456 /// <summary> 450 /// <summary>
457 /// Perform the WebDAV call and fire the callback when finished. 451 /// Perform the WebDAV call and fire the callback when finished.
458 /// </summary> 452 /// </summary>
459 /// <param name="uri"></param> 453 /// <param name="uri"></param>
460 /// <param name="headers"></param> 454 /// <param name="headers"></param>
461 /// <param name="method"></param> 455 /// <param name="method"></param>
462 /// <param name="content"></param> 456 /// <param name="content"></param>
463 private async Task<HttpResponseMessage> HttpUploadRequest(Uri uri, HttpMethod method, Stream content, 457 private async Task<HttpResponseMessage> HttpUploadRequest(Uri uri, HttpMethod method, Stream content,
464 IDictionary<string, string> headers = null) 458 IDictionary<string, string> headers = null)
465 { 459 {
466 using (var request = new HttpRequestMessage(method, uri)) 460 using (var request = new HttpRequestMessage(method, uri))
467 { 461 {
468 request.Headers.Connection.Add("Keep-Alive"); 462 request.Headers.Connection.Add("Keep-Alive");
469 request.Headers.UserAgent.Add(!string.IsNullOrWhiteSpace(UserAgent) 463 request.Headers.UserAgent.Add(!string.IsNullOrWhiteSpace(UserAgent)
470 ? new ProductInfoHeaderValue(UserAgent, UserAgentVersion) 464 ? new ProductInfoHeaderValue(UserAgent, UserAgentVersion)
471 : new ProductInfoHeaderValue("WebDAVClient", AssemblyVersion)); 465 : new ProductInfoHeaderValue("WebDAVClient", AssemblyVersion));
472   466  
473 if (headers != null) 467 if (headers != null)
474 { 468 {
475 foreach (var key in headers.Keys) 469 foreach (var key in headers.Keys)
476 { 470 {
477 request.Headers.Add(key, headers[key]); 471 request.Headers.Add(key, headers[key]);
478 } 472 }
479 } 473 }
480   474  
481 // Need to send along content? 475 // Need to send along content?
482 if (content != null) 476 if (content != null)
483 { 477 {
484 request.Content = new StreamContent(content); 478 request.Content = new StreamContent(content);
485 } 479 }
486   480  
487 return await (_uploadClient ?? _client).SendAsync(request).ConfigureAwait(false); 481 return await (_uploadClient ?? _client).SendAsync(request).ConfigureAwait(false);
488 } 482 }
489 } 483 }
490   484  
491 /// <summary> 485 /// <summary>
492 /// Try to create an Uri with kind UriKind.Absolute 486 /// Try to create an Uri with kind UriKind.Absolute
493 /// This particular implementation also works on Mono/Linux 487 /// This particular implementation also works on Mono/Linux
494 /// It seems that on Mono it is expected behaviour that uris 488 /// It seems that on Mono it is expected behaviour that uris
495 /// of kind /a/b are indeed absolute uris since it referes to a file in /a/b. 489 /// of kind /a/b are indeed absolute uris since it referes to a file in /a/b.
496 /// https://bugzilla.xamarin.com/show_bug.cgi?id=30854 490 /// https://bugzilla.xamarin.com/show_bug.cgi?id=30854
497 /// </summary> 491 /// </summary>
498 /// <param name="uriString"></param> 492 /// <param name="uriString"></param>
499 /// <param name="uriResult"></param> 493 /// <param name="uriResult"></param>
500 /// <returns></returns> 494 /// <returns></returns>
501 private static bool TryCreateAbsolute(string uriString, out Uri uriResult) 495 private static bool TryCreateAbsolute(string uriString, out Uri uriResult)
502 { 496 {
503 return Uri.TryCreate(uriString, UriKind.Absolute, out uriResult) && uriResult.Scheme != Uri.UriSchemeFile; 497 return Uri.TryCreate(uriString, UriKind.Absolute, out uriResult) && uriResult.Scheme != Uri.UriSchemeFile;
504 } 498 }
505   499  
506 private async Task<UriBuilder> GetServerUrl(string path, bool appendTrailingSlash) 500 private async Task<UriBuilder> GetServerUrl(string path, bool appendTrailingSlash)
507 { 501 {
508 // Resolve the base path on the server 502 // Resolve the base path on the server
509 if (_encodedBasePath == null) 503 if (_encodedBasePath == null)
510 { 504 {
511 var baseUri = new UriBuilder(_server) {Path = _basePath}; 505 var baseUri = new UriBuilder(_server) { Path = _basePath };
512 var root = await Get(baseUri.Uri).ConfigureAwait(false); 506 var root = await Get(baseUri.Uri).ConfigureAwait(false);
513   507  
514 _encodedBasePath = root.Href; 508 _encodedBasePath = root.Href;
515 } 509 }
516   -  
517   510  
518 // If we've been asked for the "root" folder 511 // If we've been asked for the "root" folder
519 if (string.IsNullOrEmpty(path)) 512 if (string.IsNullOrEmpty(path))
520 { 513 {
521 // If the resolved base path is an absolute URI, use it 514 // If the resolved base path is an absolute URI, use it
522 Uri absoluteBaseUri; 515 Uri absoluteBaseUri;
523 if (TryCreateAbsolute(_encodedBasePath, out absoluteBaseUri)) 516 if (TryCreateAbsolute(_encodedBasePath, out absoluteBaseUri))
524 { 517 {
525 return new UriBuilder(absoluteBaseUri); 518 return new UriBuilder(absoluteBaseUri);
526 } 519 }
527   520  
528 // Otherwise, use the resolved base path relatively to the server 521 // Otherwise, use the resolved base path relatively to the server
529 var baseUri = new UriBuilder(_server) {Path = _encodedBasePath}; 522 var baseUri = new UriBuilder(_server) { Path = _encodedBasePath };
530 return baseUri; 523 return baseUri;
531 } 524 }
532   525  
533 // If the requested path is absolute, use it 526 // If the requested path is absolute, use it
534 Uri absoluteUri; 527 Uri absoluteUri;
535 if (TryCreateAbsolute(path, out absoluteUri)) 528 if (TryCreateAbsolute(path, out absoluteUri))
536 { 529 {
537 var baseUri = new UriBuilder(absoluteUri); 530 var baseUri = new UriBuilder(absoluteUri);
538 return baseUri; 531 return baseUri;
539 } 532 }
540 else 533 else
541 { 534 {
542 // Otherwise, create a URI relative to the server 535 // Otherwise, create a URI relative to the server
543 UriBuilder baseUri; 536 UriBuilder baseUri;
544 if (TryCreateAbsolute(_encodedBasePath, out absoluteUri)) 537 if (TryCreateAbsolute(_encodedBasePath, out absoluteUri))
545 { 538 {
546 baseUri = new UriBuilder(absoluteUri); 539 baseUri = new UriBuilder(absoluteUri);
547   540  
548 baseUri.Path = baseUri.Path.TrimEnd('/') + "/" + path.TrimStart('/'); 541 baseUri.Path = baseUri.Path.TrimEnd('/') + "/" + path.TrimStart('/');
549   542  
550 if (appendTrailingSlash && !baseUri.Path.EndsWith("/")) 543 if (appendTrailingSlash && !baseUri.Path.EndsWith("/"))
551 baseUri.Path += "/"; 544 baseUri.Path += "/";
552 } 545 }
553 else 546 else
554 { 547 {
555 baseUri = new UriBuilder(_server); 548 baseUri = new UriBuilder(_server);
556   549  
557 // Ensure we don't add the base path twice 550 // Ensure we don't add the base path twice
558 var finalPath = path; 551 var finalPath = path;
559 if (!finalPath.StartsWith(_encodedBasePath, StringComparison.InvariantCultureIgnoreCase)) 552 if (!finalPath.StartsWith(_encodedBasePath, StringComparison.InvariantCultureIgnoreCase))
560 { 553 {
561 finalPath = _encodedBasePath.TrimEnd('/') + "/" + path; 554 finalPath = _encodedBasePath.TrimEnd('/') + "/" + path;
562 } 555 }
563 if (appendTrailingSlash) 556 if (appendTrailingSlash)
564 finalPath = finalPath.TrimEnd('/') + "/"; 557 finalPath = finalPath.TrimEnd('/') + "/";
565   558  
566 baseUri.Path = finalPath; 559 baseUri.Path = finalPath;
567 } 560 }
568   -  
569   561  
570 return baseUri; 562 return baseUri;
571 } 563 }
572 } 564 }
573   565  
574 public void Dispose() 566 public void Dispose()
575 { 567 {
576 _client?.Dispose(); 568 _client?.Dispose();
577 _uploadClient?.Dispose(); 569 _uploadClient?.Dispose();
578 } 570 }
579   571  
580 #endregion 572 #endregion Server communication
581 } -  
582 } 573 }
-   574 }
583   575  
584
Generated by GNU Enscript 1.6.5.90.
-  
585   -  
586   -  
587   -