WebDAVExtensions.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. using System;
  2. using System.Linq;
  3. using System.Net;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. using System.Web;
  7. using WebDAVSharp.Server.Adapters;
  8. using WebDAVSharp.Server.Exceptions;
  9. using WebDAVSharp.Server.Stores;
  10. using static System.String;
  11. namespace WebDAVSharp.Server
  12. {
  13. /// <summary>
  14. /// This class holds extension methods for various types related to WebDAV#.
  15. /// </summary>
  16. internal static class WebDavExtensions
  17. {
  18. private static readonly Regex TokenRegex = new Regex(@"<urn:uuid:(?<Token>.*)>");
  19. /// <summary>
  20. /// Gets the Uri to the parent object.
  21. /// </summary>
  22. /// <param name="uri">The <see cref="Uri" /> of a resource, for which the parent Uri should be retrieved.</param>
  23. /// <returns>
  24. /// The parent <see cref="Uri" />.
  25. /// </returns>
  26. /// <exception cref="System.ArgumentNullException">uri</exception>
  27. /// <exception cref="System.InvalidOperationException">Cannot get parent of root</exception>
  28. /// <exception cref="ArgumentNullException"><paramref name="uri" /> is <c>null</c>.</exception>
  29. /// <exception cref="InvalidOperationException"><paramref name="uri" /> has no parent, it refers to a root resource.</exception>
  30. public static Uri GetParentUri(this Uri uri)
  31. {
  32. if (uri == null)
  33. throw new ArgumentNullException(nameof(uri));
  34. if (uri.Segments.Length == 1)
  35. throw new InvalidOperationException("Cannot get parent of root");
  36. string url = uri.ToString();
  37. int index = url.Length - 1;
  38. if (url[index] == '/')
  39. index--;
  40. while (url[index] != '/')
  41. index--;
  42. return new Uri(url.Substring(0, index + 1));
  43. }
  44. /// <summary>
  45. /// Sends a simple response with a specified HTTP status code but no content.
  46. /// </summary>
  47. /// <param name="context">The <see cref="IHttpListenerContext" /> to send the response through.</param>
  48. /// <param name="statusCode">The HTTP status code for the response.</param>
  49. /// <exception cref="System.ArgumentNullException">context</exception>
  50. /// <exception cref="ArgumentNullException"><paramref name="context" /> is <c>null</c>.</exception>
  51. public static void SendSimpleResponse(this IHttpListenerContext context, int statusCode = (int) HttpStatusCode.OK)
  52. {
  53. if (context == null)
  54. throw new ArgumentNullException(nameof(context));
  55. context.Response.StatusCode = statusCode;
  56. context.Response.StatusDescription = HttpWorkerRequest.GetStatusDescription(statusCode);
  57. context.Response.Close();
  58. }
  59. /// <summary>
  60. /// Gets the prefix <see cref="Uri" /> that matches the specified <see cref="Uri" />.
  61. /// </summary>
  62. /// <param name="uri">The <see cref="Uri" /> to find the most specific prefix <see cref="Uri" /> for.</param>
  63. /// <param name="server">
  64. /// The
  65. /// <see cref="WebDavServer" /> that hosts the WebDAV server and holds the collection
  66. /// of known prefixes.
  67. /// </param>
  68. /// <returns>
  69. /// The most specific <see cref="Uri" /> for the given <paramref name="uri" />.
  70. /// </returns>
  71. /// <exception cref="WebDAVSharp.Server.Exceptions.WebDavInternalServerException">Unable to find correct server root</exception>
  72. /// <exception cref="WebDavInternalServerException">
  73. /// <paramref name="uri" /> specifies a <see cref="Uri" /> that is not
  74. /// known to the <paramref name="server" />.
  75. /// </exception>
  76. public static Uri GetPrefixUri(this Uri uri, WebDavServer server)
  77. {
  78. string url = uri.ToString();
  79. string exactPrefix = server.Listener.Prefixes
  80. .FirstOrDefault(item => url.StartsWith(item, StringComparison.OrdinalIgnoreCase));
  81. if (!IsNullOrEmpty(exactPrefix))
  82. {
  83. return new Uri(exactPrefix);
  84. }
  85. string wildcardUrl = new UriBuilder(uri)
  86. {
  87. Host = "WebDAVSharpSpecialHostTag"
  88. }
  89. .ToString().Replace("WebDAVSharpSpecialHostTag", "*");
  90. string wildcardPrefix = server.Listener.Prefixes
  91. .FirstOrDefault(item => wildcardUrl.StartsWith(item, StringComparison.OrdinalIgnoreCase));
  92. if (!IsNullOrEmpty(wildcardPrefix))
  93. {
  94. return new Uri(wildcardPrefix.Replace("://*", $"://{uri.Host}"));
  95. }
  96. throw new WebDavInternalServerException("Unable to find correct server root");
  97. }
  98. /// <summary>
  99. /// Retrieves a store item through the specified
  100. /// <see cref="Uri" /> from the
  101. /// specified
  102. /// <see cref="WebDavServer" /> and
  103. /// <see cref="IWebDavStore" />.
  104. /// </summary>
  105. /// <param name="uri">The <see cref="Uri" /> to retrieve the store item for.</param>
  106. /// <param name="server">The <see cref="WebDavServer" /> that hosts the <paramref name="store" />.</param>
  107. /// <param name="store">The <see cref="IWebDavStore" /> from which to retrieve the store item.</param>
  108. /// <returns>
  109. /// The retrieved store item.
  110. /// </returns>
  111. /// <exception cref="System.ArgumentNullException">
  112. /// <para>
  113. /// <paramref name="uri" /> is <c>null</c>.
  114. /// </para>
  115. /// <para>
  116. /// <paramref name="server" /> is <c>null</c>.
  117. /// </para>
  118. /// <para>
  119. /// <paramref name="store" /> is <c>null</c>.
  120. /// </para>
  121. /// </exception>
  122. /// <exception cref="WebDAVSharp.Server.Exceptions.WebDavNotFoundException">If the item was not found.</exception>
  123. /// <exception cref="WebDavConflictException">
  124. /// <paramref name="uri" /> refers to a document in a collection, where the
  125. /// collection does not exist.
  126. /// </exception>
  127. /// <exception cref="WebDavNotFoundException"><paramref name="uri" /> refers to a document that does not exist.</exception>
  128. public static IWebDavStoreItem GetItem(this Uri uri, WebDavServer server, IWebDavStore store)
  129. {
  130. if (uri == null)
  131. throw new ArgumentNullException(nameof(uri));
  132. if (server == null)
  133. throw new ArgumentNullException(nameof(server));
  134. if (store == null)
  135. throw new ArgumentNullException(nameof(store));
  136. Uri prefixUri = uri.GetPrefixUri(server);
  137. IWebDavStoreCollection collection = store.Root;
  138. IWebDavStoreItem item = null;
  139. if (prefixUri.Segments.Length == uri.Segments.Length)
  140. return collection;
  141. for (int index = prefixUri.Segments.Length; index < uri.Segments.Length; index++)
  142. {
  143. string segmentName = Uri.UnescapeDataString(uri.Segments[index]);
  144. IWebDavStoreItem nextItem = collection.GetItemByName(segmentName.TrimEnd('/', '\\'));
  145. if (nextItem == null)
  146. throw new WebDavNotFoundException(); //throw new WebDavConflictException();
  147. if (index == uri.Segments.Length - 1)
  148. item = nextItem;
  149. else
  150. {
  151. collection = nextItem as IWebDavStoreCollection;
  152. if (collection == null)
  153. throw new WebDavNotFoundException();
  154. }
  155. }
  156. if (item == null)
  157. throw new WebDavNotFoundException();
  158. return item;
  159. }
  160. private static string Match(Regex regex, string html, int i = 1)
  161. {
  162. return regex.Match(html).Groups[i].Value.Trim();
  163. }
  164. public static Guid? GetLockTokenHeader(this IHttpListenerRequest request)
  165. {
  166. if (!request.Headers.AllKeys.Contains("Lock-Token"))
  167. return null;
  168. string r = Match(TokenRegex, request.Headers["Lock-Token"]);
  169. if (IsNullOrEmpty(r))
  170. return null;
  171. return new Guid(r);
  172. }
  173. public static Guid? GetLockTokenIfHeader(this IHttpListenerRequest request)
  174. {
  175. if (!request.Headers.AllKeys.Contains("If"))
  176. return null;
  177. string t = request.Headers["If"].Substring(2, request.Headers["If"].Length - 4);
  178. return new Guid(t);
  179. }
  180. public static string ToLockToken(this Guid? token)
  181. {
  182. if (token == null)
  183. throw new NullReferenceException("token");
  184. return "urn:uuid:" + token;
  185. }
  186. /// <summary>
  187. /// Gets the Timeout header : Second-number
  188. /// </summary>
  189. /// <param name="request">The request with the request included</param>
  190. /// <param name="store"></param>
  191. /// <returns>The value of the Timeout header as a string</returns>
  192. public static double? GetTimeoutHeader(this IHttpListenerRequest request, IWebDavStore store)
  193. {
  194. // get the value of the timeout header as a string
  195. string timeout = request.Headers["Timeout"];
  196. // check if the string is valid or not infinity
  197. // if so, try to parse it to an int
  198. if (!IsNullOrEmpty(timeout) && !timeout.Equals("infinity") && !timeout.Equals("Infinite, Second-4100000000"))
  199. {
  200. string num = timeout.Substring(timeout.IndexOf("Second-", StringComparison.Ordinal) + 7);
  201. double d;
  202. if (double.TryParse(num, out d))
  203. {
  204. return d;
  205. }
  206. }
  207. if (timeout.Equals("infinity") && timeout.Equals("Infinite, Second-4100000000") && store.LockSystem.AllowInfiniteCheckouts)
  208. return null;
  209. return 345600;
  210. }
  211. public static void SendException(this IHttpListenerContext context, WebDavException ex)
  212. {
  213. context.Response.StatusCode = ex.StatusCode;
  214. context.Response.StatusDescription = ex.StatusDescription;
  215. if (ex.Message == context.Response.StatusDescription)
  216. return;
  217. byte[] buffer = Encoding.UTF8.GetBytes(ex.Message);
  218. context.Response.ContentEncoding = Encoding.UTF8;
  219. context.Response.ContentLength64 = buffer.Length;
  220. context.Response.OutputStream.Write(buffer, 0, buffer.Length);
  221. context.Response.OutputStream.Flush();
  222. }
  223. }
  224. }