WebDavPropfindMethodHandler.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Text;
  6. using System.Web;
  7. using System.Xml;
  8. using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.Adapters;
  9. using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.Exceptions;
  10. using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.Stores;
  11. using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.Utilities;
  12. namespace Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.MethodHandlers
  13. {
  14. /// <summary>
  15. /// This class implements the <c>PROPFIND</c> HTTP method for WebDAV#.
  16. /// </summary>
  17. internal class WebDavPropfindMethodHandler : WebDavMethodHandlerBase, IWebDavMethodHandler
  18. {
  19. private List<WebDavProperty> _requestedProperties;
  20. private Uri _requestUri;
  21. private List<IWebDavStoreItem> _webDavStoreItems;
  22. /// <summary>
  23. /// Gets the collection of the names of the HTTP methods handled by this instance.
  24. /// </summary>
  25. /// <value>
  26. /// The names.
  27. /// </value>
  28. public IEnumerable<string> Names => new[]
  29. {
  30. "PROPFIND"
  31. };
  32. /// <summary>
  33. /// Processes the request.
  34. /// </summary>
  35. /// <param name="server">The <see cref="WebDavServer" /> through which the request came in from the client.</param>
  36. /// <param name="context">
  37. /// The
  38. /// <see cref="IHttpListenerContext" /> object containing both the request and response
  39. /// objects to use.
  40. /// </param>
  41. /// <param name="store">The <see cref="IWebDavStore" /> that the <see cref="WebDavServer" /> is hosting.</param>
  42. /// <exception cref="WebDavUnauthorizedException"></exception>
  43. public void ProcessRequest(WebDavServer server, IHttpListenerContext context, IWebDavStore store)
  44. {
  45. /***************************************************************************************************
  46. * Retreive all the information from the request
  47. ***************************************************************************************************/
  48. // Read the headers, ...
  49. var isPropname = false;
  50. var depth = GetDepthHeader(context.Request);
  51. _requestUri = GetRequestUri(context.Request.Url.ToString());
  52. try
  53. {
  54. _webDavStoreItems = GetWebDavStoreItems(context.Request.Url.GetItem(server, store), depth);
  55. }
  56. catch (UnauthorizedAccessException)
  57. {
  58. throw new WebDavUnauthorizedException();
  59. }
  60. // Get the XmlDocument from the request
  61. var requestDoc = GetXmlDocument(context.Request);
  62. // See what is requested
  63. _requestedProperties = new List<WebDavProperty>();
  64. if (requestDoc.DocumentElement != null)
  65. {
  66. if (requestDoc.DocumentElement.LocalName != "propfind")
  67. {
  68. }
  69. else
  70. {
  71. var n = requestDoc.DocumentElement.FirstChild;
  72. if (n == null)
  73. {
  74. }
  75. else
  76. {
  77. switch (n.LocalName)
  78. {
  79. case "allprop":
  80. _requestedProperties = GetAllProperties();
  81. break;
  82. case "propname":
  83. isPropname = true;
  84. _requestedProperties = GetAllProperties();
  85. break;
  86. case "prop":
  87. foreach (XmlNode child in n.ChildNodes)
  88. _requestedProperties.Add(new WebDavProperty(child.LocalName, "", child.NamespaceURI));
  89. break;
  90. default:
  91. _requestedProperties.Add(new WebDavProperty(n.LocalName, "", n.NamespaceURI));
  92. break;
  93. }
  94. }
  95. }
  96. }
  97. else
  98. _requestedProperties = GetAllProperties();
  99. /***************************************************************************************************
  100. * Create the body for the response
  101. ***************************************************************************************************/
  102. var responseDoc = ResponseDocument(context, isPropname);
  103. /***************************************************************************************************
  104. * Send the response
  105. ***************************************************************************************************/
  106. SendResponse(context, responseDoc);
  107. }
  108. #region SendResponse
  109. /// <summary>
  110. /// Sends the response
  111. /// </summary>
  112. /// <param name="context">The <see cref="IHttpListenerContext" /> containing the response</param>
  113. /// <param name="responseDocument">The <see cref="XmlDocument" /> containing the response body</param>
  114. private static void SendResponse(IHttpListenerContext context, XmlDocument responseDocument)
  115. {
  116. // convert the XmlDocument
  117. var responseBytes = Encoding.UTF8.GetBytes(responseDocument.InnerXml);
  118. // HttpStatusCode doesn't contain WebDav status codes, but HttpWorkerRequest can handle these WebDav status codes
  119. context.Response.StatusCode = (int) WebDavStatusCode.MultiStatus;
  120. context.Response.StatusDescription =
  121. HttpWorkerRequest.GetStatusDescription((int) WebDavStatusCode.MultiStatus);
  122. context.Response.ContentLength64 = responseBytes.Length;
  123. context.Response.AdaptedInstance.ContentType = "text/xml";
  124. context.Response.OutputStream.Write(responseBytes, 0, responseBytes.Length);
  125. context.Response.Close();
  126. }
  127. #endregion
  128. #region RetrieveInformation
  129. /// <summary>
  130. /// Get the URI to the location
  131. /// If no slash at the end of the URI, this method adds one
  132. /// </summary>
  133. /// <param name="uri">The <see cref="string" /> that contains the URI</param>
  134. /// <returns>
  135. /// The <see cref="Uri" /> that contains the given uri
  136. /// </returns>
  137. private static Uri GetRequestUri(string uri)
  138. {
  139. return new Uri(uri.EndsWith("/") ? uri : uri + "/");
  140. }
  141. /// <summary>
  142. /// Convert the given
  143. /// <see cref="IWebDavStoreItem" /> to a
  144. /// <see cref="List{T}" /> of
  145. /// <see cref="IWebDavStoreItem" />
  146. /// This list depends on the "Depth" header
  147. /// </summary>
  148. /// <param name="iWebDavStoreItem">The <see cref="IWebDavStoreItem" /> that needs to be converted</param>
  149. /// <param name="depth">The "Depth" header</param>
  150. /// <returns>
  151. /// A <see cref="List{T}" /> of <see cref="IWebDavStoreItem" />
  152. /// </returns>
  153. /// <exception cref="WebDavConflictException"></exception>
  154. private static List<IWebDavStoreItem> GetWebDavStoreItems(IWebDavStoreItem iWebDavStoreItem, int depth)
  155. {
  156. var list = new List<IWebDavStoreItem>();
  157. //IWebDavStoreCollection
  158. // if the item is a collection
  159. var collection = iWebDavStoreItem as IWebDavStoreCollection;
  160. if (collection != null)
  161. {
  162. list.Add(collection);
  163. if (depth == 0)
  164. return list;
  165. foreach (var item in collection.Items)
  166. {
  167. try
  168. {
  169. list.Add(item);
  170. }
  171. catch (Exception ex)
  172. {
  173. Debug.Print(ex.ToString());
  174. }
  175. }
  176. return list;
  177. }
  178. // if the item is not a document, throw conflict exception
  179. if (!(iWebDavStoreItem is IWebDavStoreDocument))
  180. throw new WebDavConflictException();
  181. // add the item to the list
  182. list.Add(iWebDavStoreItem);
  183. return list;
  184. }
  185. /// <summary>
  186. /// Reads the XML body of the
  187. /// <see cref="IHttpListenerRequest" />
  188. /// and converts it to an
  189. /// <see cref="XmlDocument" />
  190. /// </summary>
  191. /// <param name="request">The <see cref="IHttpListenerRequest" /></param>
  192. /// <returns>
  193. /// The <see cref="XmlDocument" /> that contains the request body
  194. /// </returns>
  195. private XmlDocument GetXmlDocument(IHttpListenerRequest request)
  196. {
  197. try
  198. {
  199. var reader = new StreamReader(request.InputStream, Encoding.UTF8);
  200. var requestBody = reader.ReadToEnd();
  201. reader.Close();
  202. if (!string.IsNullOrEmpty(requestBody))
  203. {
  204. var xmlDocument = new XmlDocument();
  205. xmlDocument.LoadXml(requestBody);
  206. return xmlDocument;
  207. }
  208. }
  209. catch (Exception)
  210. {
  211. }
  212. return new XmlDocument();
  213. }
  214. /// <summary>
  215. /// Adds the standard properties for an Propfind allprop request to a <see cref="List{T}" /> of
  216. /// <see cref="WebDavProperty" />
  217. /// </summary>
  218. /// <returns>
  219. /// The list with all the <see cref="WebDavProperty" />
  220. /// </returns>
  221. private List<WebDavProperty> GetAllProperties()
  222. {
  223. var list = new List<WebDavProperty>
  224. {
  225. new WebDavProperty("creationdate"),
  226. new WebDavProperty("displayname"),
  227. new WebDavProperty("getcontentlength"),
  228. new WebDavProperty("getcontenttype"),
  229. new WebDavProperty("getetag"),
  230. new WebDavProperty("getlastmodified"),
  231. new WebDavProperty("resourcetype"),
  232. new WebDavProperty("supportedlock"),
  233. new WebDavProperty("ishidden")
  234. };
  235. //list.Add(new WebDAVProperty("getcontentlanguage"));
  236. //list.Add(new WebDAVProperty("lockdiscovery"));
  237. return list;
  238. }
  239. #endregion
  240. #region BuildResponseBody
  241. /// <summary>
  242. /// Builds the <see cref="XmlDocument" /> containing the response body
  243. /// </summary>
  244. /// <param name="context">The <see cref="IHttpListenerContext" /></param>
  245. /// <param name="propname">The boolean defining the Propfind propname request</param>
  246. /// <returns>
  247. /// The <see cref="XmlDocument" /> containing the response body
  248. /// </returns>
  249. private XmlDocument ResponseDocument(IHttpListenerContext context, bool propname)
  250. {
  251. // Create the basic response XmlDocument
  252. var responseDoc = new XmlDocument();
  253. const string responseXml = "<?xml version=\"1.0\"?><D:multistatus xmlns:D=\"DAV:\"></D:multistatus>";
  254. responseDoc.LoadXml(responseXml);
  255. // Generate the manager
  256. var manager = new XmlNamespaceManager(responseDoc.NameTable);
  257. manager.AddNamespace("D", "DAV:");
  258. manager.AddNamespace("Office", "schemas-microsoft-com:office:office");
  259. manager.AddNamespace("Repl", "http://schemas.microsoft.com/repl/");
  260. manager.AddNamespace("Z", "urn:schemas-microsoft-com:");
  261. var count = 0;
  262. foreach (var webDavStoreItem in _webDavStoreItems)
  263. {
  264. // Create the response element
  265. var responseProperty = new WebDavProperty("response", "");
  266. var responseElement = responseProperty.ToXmlElement(responseDoc);
  267. // The href element
  268. Uri result;
  269. if (count == 0)
  270. {
  271. Uri.TryCreate(_requestUri, "", out result);
  272. }
  273. else
  274. {
  275. Uri.TryCreate(_requestUri, webDavStoreItem.Name, out result);
  276. }
  277. var hrefProperty = new WebDavProperty("href", result.AbsoluteUri);
  278. responseElement.AppendChild(hrefProperty.ToXmlElement(responseDoc));
  279. count++;
  280. // The propstat element
  281. var propstatProperty = new WebDavProperty("propstat", "");
  282. var propstatElement = propstatProperty.ToXmlElement(responseDoc);
  283. // The prop element
  284. var propProperty = new WebDavProperty("prop", "");
  285. var propElement = propProperty.ToXmlElement(responseDoc);
  286. foreach (var davProperty in _requestedProperties)
  287. {
  288. propElement.AppendChild(PropChildElement(davProperty, responseDoc, webDavStoreItem, propname));
  289. }
  290. // Add the prop element to the propstat element
  291. propstatElement.AppendChild(propElement);
  292. // The status element
  293. var statusProperty = new WebDavProperty("status",
  294. "HTTP/1.1 " + context.Response.StatusCode + " " +
  295. HttpWorkerRequest.GetStatusDescription(context.Response.StatusCode));
  296. propstatElement.AppendChild(statusProperty.ToXmlElement(responseDoc));
  297. // Add the propstat element to the response element
  298. responseElement.AppendChild(propstatElement);
  299. // Add the response element to the multistatus element
  300. responseDoc.DocumentElement.AppendChild(responseElement);
  301. }
  302. return responseDoc;
  303. }
  304. /// <summary>
  305. /// Gives the
  306. /// <see cref="XmlElement" /> of a
  307. /// <see cref="WebDavProperty" />
  308. /// with or without values
  309. /// or with or without child elements
  310. /// </summary>
  311. /// <param name="webDavProperty">The <see cref="WebDavProperty" /></param>
  312. /// <param name="xmlDocument">The <see cref="XmlDocument" /> containing the response body</param>
  313. /// <param name="iWebDavStoreItem">The <see cref="IWebDavStoreItem" /></param>
  314. /// <param name="isPropname">The boolean defining the Propfind propname request</param>
  315. /// <returns>
  316. /// The <see cref="XmlElement" /> of the <see cref="WebDavProperty" /> containing a value or child elements
  317. /// </returns>
  318. private XmlElement PropChildElement(WebDavProperty webDavProperty, XmlDocument xmlDocument,
  319. IWebDavStoreItem iWebDavStoreItem, bool isPropname)
  320. {
  321. // If Propfind request contains a propname element
  322. if (isPropname)
  323. {
  324. webDavProperty.Value = string.Empty;
  325. return webDavProperty.ToXmlElement(xmlDocument);
  326. }
  327. // If not, add the values to webDavProperty
  328. webDavProperty.Value = GetWebDavPropertyValue(iWebDavStoreItem, webDavProperty);
  329. var xmlElement = webDavProperty.ToXmlElement(xmlDocument);
  330. // If the webDavProperty is the resourcetype property
  331. // and the webDavStoreItem is a collection
  332. // add the collection XmlElement as a child to the xmlElement
  333. if (webDavProperty.Name != "resourcetype" || !iWebDavStoreItem.IsCollection)
  334. return xmlElement;
  335. var collectionProperty = new WebDavProperty("collection", "");
  336. xmlElement.AppendChild(collectionProperty.ToXmlElement(xmlDocument));
  337. return xmlElement;
  338. }
  339. /// <summary>
  340. /// Gets the correct value for a <see cref="WebDavProperty" />
  341. /// </summary>
  342. /// <param name="webDavStoreItem">The <see cref="IWebDavStoreItem" /> defines the values</param>
  343. /// <param name="davProperty">The <see cref="WebDavProperty" /> that needs a value</param>
  344. /// <returns>
  345. /// A <see cref="string" /> containing the value
  346. /// </returns>
  347. private string GetWebDavPropertyValue(IWebDavStoreItem webDavStoreItem, WebDavProperty davProperty)
  348. {
  349. switch (davProperty.Name)
  350. {
  351. case "creationdate":
  352. return webDavStoreItem.CreationDate.ToUniversalTime().ToString("s") + "Z";
  353. case "displayname":
  354. return webDavStoreItem.Name;
  355. case "getcontentlanguage":
  356. // still to implement !!!
  357. return string.Empty;
  358. case "getcontentlength":
  359. return !webDavStoreItem.IsCollection ? "" + ((IWebDavStoreDocument) webDavStoreItem).Size : "";
  360. case "getcontenttype":
  361. return !webDavStoreItem.IsCollection ? "" + ((IWebDavStoreDocument) webDavStoreItem).MimeType : "";
  362. case "getetag":
  363. return !webDavStoreItem.IsCollection ? "" + ((IWebDavStoreDocument) webDavStoreItem).Etag : "";
  364. case "getlastmodified":
  365. return webDavStoreItem.ModificationDate.ToUniversalTime().ToString("R");
  366. case "lockdiscovery":
  367. // still to implement !!!
  368. return string.Empty;
  369. case "resourcetype":
  370. return "";
  371. case "supportedlock":
  372. // still to implement !!!
  373. return "";
  374. //webDavProperty.Value = "<D:lockentry><D:lockscope><D:shared/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry>";
  375. case "ishidden":
  376. return "" + webDavStoreItem.Hidden;
  377. default:
  378. return string.Empty;
  379. }
  380. }
  381. #endregion
  382. }
  383. }