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
}
}