using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Web; using System.Xml; using Common.Logging; using WebDAVSharp.Server.Adapters; using WebDAVSharp.Server.Exceptions; using WebDAVSharp.Server.Stores; using WebDAVSharp.Server.Utilities; namespace WebDAVSharp.Server.MethodHandlers { /// /// This class implements the PROPFIND HTTP method for WebDAV#. /// internal class WebDavPropfindMethodHandler : WebDavMethodHandlerBase, IWebDavMethodHandler { private ILog _log; private String _requestedFilePath; private List _requestedProperties; private List _webDavStoreItems; /// /// Gets the collection of the names of the HTTP methods handled by this instance. /// /// /// The names. /// public IEnumerable Names { get { return new[] { "PROPFIND" }; } } /// /// Processes the request. /// /// The through which the request came in from the client. /// The /// object containing both the request and response /// objects to use. /// The that the is hosting. /// public void ProcessRequest(WebDavServer server, HttpContext context, IWebDavStore store, String fileName) { _log = LogManager.GetCurrentClassLogger(); /*************************************************************************************************** * Retreive all the information from the request ***************************************************************************************************/ // Read the headers, ... bool isPropname = false; int depth = GetDepthHeader(context.Request); _requestedFilePath = server.ServiceUrl + fileName;//GetRequestUri(context.Request.Url2.ToString()); // make sure _requestedFilePath ends with / _requestedFilePath = _requestedFilePath.TrimEnd('/') + '/'; try { _webDavStoreItems = GetWebDavStoreItems(GetItem(server, store, fileName), depth); } catch (UnauthorizedAccessException) { throw new WebDavUnauthorizedException(); } // Get the XmlDocument from the request XmlDocument requestDoc = GetXmlDocument(context.Request); // See what is requested _requestedProperties = new List(); if (requestDoc.DocumentElement != null) { if (requestDoc.DocumentElement.LocalName != "propfind") _log.Debug("PROPFIND method without propfind in xml document"); else { XmlNode n = requestDoc.DocumentElement.FirstChild; if (n == null) _log.Debug("propfind element without children"); else { switch (n.LocalName) { case "allprop": _requestedProperties = GetAllProperties(); break; case "propname": isPropname = true; _requestedProperties = GetAllProperties(); break; case "prop": foreach (XmlNode child in n.ChildNodes) _requestedProperties.Add(new WebDavProperty(child.LocalName, "", child.NamespaceURI)); break; default: _requestedProperties.Add(new WebDavProperty(n.LocalName, "", n.NamespaceURI)); break; } } } } else _requestedProperties = GetAllProperties(); /*************************************************************************************************** * Create the body for the response ***************************************************************************************************/ XmlDocument responseDoc = ResponseDocument(context, isPropname); /*************************************************************************************************** * Send the response ***************************************************************************************************/ SendResponse(context, responseDoc); } #region RetrieveInformation /// /// Get the URI to the location /// If no slash at the end of the URI, this method adds one /// /// The that contains the URI /// /// The that contains the given uri /// private static Uri GetRequestUri(string uri) { return new Uri(uri.EndsWith("/") ? uri : uri + "/"); } /// /// Convert the given /// to a /// of /// /// This list depends on the "Depth" header /// /// The that needs to be converted /// The "Depth" header /// /// A of /// /// private static List GetWebDavStoreItems(IWebDavStoreItem iWebDavStoreItem, int depth) { ILog _log = LogManager.GetCurrentClassLogger(); List list = new List(); //IWebDavStoreCollection // if the item is a collection IWebDavStoreCollection collection = iWebDavStoreItem as IWebDavStoreCollection; if (collection != null) { list.Add(collection); if (depth == 0) return list; foreach (IWebDavStoreItem item in collection.Items) { try { list.Add(item); } catch (Exception ex) { _log.Debug(ex.Message + "\r\n" + ex.StackTrace); } } return list; } // if the item is not a document, throw conflict exception if (!(iWebDavStoreItem is IWebDavStoreDocument)) throw new WebDavConflictException(); // add the item to the list list.Add(iWebDavStoreItem); return list; } /// /// Reads the XML body of the /// /// and converts it to an /// /// /// The /// /// The that contains the request body /// private XmlDocument GetXmlDocument(HttpRequest request) { try { StreamReader reader = new StreamReader(request.InputStream, Encoding.UTF8); string requestBody = reader.ReadToEnd(); reader.Close(); if (!String.IsNullOrEmpty(requestBody)) { var xmlDocument = new XmlDocument(); xmlDocument.LoadXml(requestBody); return xmlDocument; } } catch (Exception) { _log.Warn("XmlDocument has not been read correctly"); } return new XmlDocument(); } /// /// Adds the standard properties for an Propfind allprop request to a of /// /// /// The list with all the /// private List GetAllProperties() { List list = new List { new WebDavProperty("creationdate"), new WebDavProperty("displayname"), new WebDavProperty("getcontentlength"), new WebDavProperty("getcontenttype"), new WebDavProperty("getetag"), new WebDavProperty("getlastmodified"), new WebDavProperty("resourcetype"), new WebDavProperty("supportedlock"), new WebDavProperty("ishidden") }; //list.Add(new WebDAVProperty("getcontentlanguage")); //list.Add(new WebDAVProperty("lockdiscovery")); return list; } #endregion #region BuildResponseBody /// /// Builds the containing the response body /// /// The /// The boolean defining the Propfind propname request /// /// The containing the response body /// private XmlDocument ResponseDocument(HttpContext context, bool propname) { // Create the basic response XmlDocument XmlDocument responseDoc = new XmlDocument(); const string responseXml = ""; responseDoc.LoadXml(responseXml); // Generate the manager XmlNamespaceManager manager = new XmlNamespaceManager(responseDoc.NameTable); manager.AddNamespace("D", "DAV:"); manager.AddNamespace("Office", "schemas-microsoft-com:office:office"); manager.AddNamespace("Repl", "http://schemas.microsoft.com/repl/"); manager.AddNamespace("Z", "urn:schemas-microsoft-com:"); int count = 0; foreach (IWebDavStoreItem webDavStoreItem in _webDavStoreItems) { // Create the response element WebDavProperty responseProperty = new WebDavProperty("response", ""); XmlElement responseElement = responseProperty.ToXmlElement(responseDoc); // The href element Uri result; //String result; if (count == 0) { Uri.TryCreate(new Uri(_requestedFilePath), "", out result); //result = _requestedFilePath; } else { Uri.TryCreate(new Uri(_requestedFilePath), webDavStoreItem.Name, out result); //result = _requestedFilePath + webDavStoreItem.Name; } WebDavProperty hrefProperty = new WebDavProperty("href", result.AbsoluteUri); responseElement.AppendChild(hrefProperty.ToXmlElement(responseDoc)); count++; // The propstat element WebDavProperty propstatProperty = new WebDavProperty("propstat", ""); XmlElement propstatElement = propstatProperty.ToXmlElement(responseDoc); // The prop element WebDavProperty propProperty = new WebDavProperty("prop", ""); XmlElement propElement = propProperty.ToXmlElement(responseDoc); foreach (WebDavProperty davProperty in _requestedProperties) { propElement.AppendChild(PropChildElement(davProperty, responseDoc, webDavStoreItem, propname)); } // Add the prop element to the propstat element propstatElement.AppendChild(propElement); // The status element WebDavProperty statusProperty = new WebDavProperty("status", "HTTP/1.1 " + context.Response.StatusCode + " " + HttpWorkerRequest.GetStatusDescription(context.Response.StatusCode)); propstatElement.AppendChild(statusProperty.ToXmlElement(responseDoc)); // Add the propstat element to the response element responseElement.AppendChild(propstatElement); // Add the response element to the multistatus element responseDoc.DocumentElement.AppendChild(responseElement); } return responseDoc; } /// /// Gives the /// of a /// /// with or without values /// or with or without child elements /// /// The /// The containing the response body /// The /// The boolean defining the Propfind propname request /// /// The of the containing a value or child elements /// private XmlElement PropChildElement(WebDavProperty webDavProperty, XmlDocument xmlDocument, IWebDavStoreItem iWebDavStoreItem, bool isPropname) { // If Propfind request contains a propname element if (isPropname) { webDavProperty.Value = String.Empty; return webDavProperty.ToXmlElement(xmlDocument); } // If not, add the values to webDavProperty webDavProperty.Value = GetWebDavPropertyValue(iWebDavStoreItem, webDavProperty); XmlElement xmlElement = webDavProperty.ToXmlElement(xmlDocument); // If the webDavProperty is the resourcetype property // and the webDavStoreItem is a collection // add the collection XmlElement as a child to the xmlElement if (webDavProperty.Name != "resourcetype" || !iWebDavStoreItem.IsCollection) return xmlElement; WebDavProperty collectionProperty = new WebDavProperty("collection", ""); xmlElement.AppendChild(collectionProperty.ToXmlElement(xmlDocument)); return xmlElement; } /// /// Gets the correct value for a /// /// The defines the values /// The that needs a value /// /// A containing the value /// private string GetWebDavPropertyValue(IWebDavStoreItem webDavStoreItem, WebDavProperty davProperty) { switch (davProperty.Name) { case "creationdate": return webDavStoreItem.CreationDate.ToUniversalTime().ToString("s") + "Z"; case "displayname": return webDavStoreItem.Name; case "getcontentlanguage": // still to implement !!! return String.Empty; case "getcontentlength": return (!webDavStoreItem.IsCollection ? "" + ((IWebDavStoreDocument)webDavStoreItem).Size : ""); case "getcontenttype": return (!webDavStoreItem.IsCollection ? "" + ((IWebDavStoreDocument)webDavStoreItem).MimeType : ""); case "getetag": return (!webDavStoreItem.IsCollection ? "" + ((IWebDavStoreDocument)webDavStoreItem).Etag : ""); case "getlastmodified": return webDavStoreItem.ModificationDate.ToUniversalTime().ToString("R"); case "lockdiscovery": // still to implement !!! return String.Empty; case "resourcetype": return ""; case "supportedlock": // still to implement !!! return ""; //webDavProperty.Value = ""; case "ishidden": return "" + webDavStoreItem.Hidden; default: return String.Empty; } } #endregion #region SendResponse /// /// Sends the response /// /// The containing the response /// The containing the response body private static void SendResponse(HttpContext context, XmlDocument responseDocument) { // convert the XmlDocument byte[] responseBytes = Encoding.UTF8.GetBytes(responseDocument.InnerXml); // HttpStatusCode doesn't contain WebDav status codes, but HttpWorkerRequest can handle these WebDav status codes context.Response.StatusCode = (int)WebDavStatusCode.MultiStatus; context.Response.StatusDescription = HttpWorkerRequest.GetStatusDescription((int)WebDavStatusCode.MultiStatus); //context.Response.ContentLength64 = responseBytes.Length; //context.Response.AdaptedInstance.ContentType = "text/xml"; context.Response.ContentType = "text/xml"; context.Response.OutputStream.Write(responseBytes, 0, responseBytes.Length); //context.Response.Close(); } #endregion } }