using System; using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Web; using WebDAVSharp.Server.Adapters; using WebDAVSharp.Server.Exceptions; using WebDAVSharp.Server.Stores; using static System.String; namespace WebDAVSharp.Server { /// /// This class holds extension methods for various types related to WebDAV#. /// internal static class WebDavExtensions { private static readonly Regex TokenRegex = new Regex(@".*)>"); /// /// Gets the Uri to the parent object. /// /// The of a resource, for which the parent Uri should be retrieved. /// /// The parent . /// /// uri /// Cannot get parent of root /// is null. /// has no parent, it refers to a root resource. public static Uri GetParentUri(this Uri uri) { if (uri == null) throw new ArgumentNullException(nameof(uri)); if (uri.Segments.Length == 1) throw new InvalidOperationException("Cannot get parent of root"); string url = uri.ToString(); int index = url.Length - 1; if (url[index] == '/') index--; while (url[index] != '/') index--; return new Uri(url.Substring(0, index + 1)); } /// /// Sends a simple response with a specified HTTP status code but no content. /// /// The to send the response through. /// The HTTP status code for the response. /// context /// is null. public static void SendSimpleResponse(this IHttpListenerContext context, int statusCode = (int) HttpStatusCode.OK) { if (context == null) throw new ArgumentNullException(nameof(context)); context.Response.StatusCode = statusCode; context.Response.StatusDescription = HttpWorkerRequest.GetStatusDescription(statusCode); context.Response.Close(); } /// /// Gets the prefix that matches the specified . /// /// The to find the most specific prefix for. /// /// The /// that hosts the WebDAV server and holds the collection /// of known prefixes. /// /// /// The most specific for the given . /// /// Unable to find correct server root /// /// specifies a that is not /// known to the . /// public static Uri GetPrefixUri(this Uri uri, WebDavServer server) { string url = uri.ToString(); string exactPrefix = server.Listener.Prefixes .FirstOrDefault(item => url.StartsWith(item, StringComparison.OrdinalIgnoreCase)); if (!IsNullOrEmpty(exactPrefix)) { return new Uri(exactPrefix); } string wildcardUrl = new UriBuilder(uri) { Host = "WebDAVSharpSpecialHostTag" } .ToString().Replace("WebDAVSharpSpecialHostTag", "*"); string wildcardPrefix = server.Listener.Prefixes .FirstOrDefault(item => wildcardUrl.StartsWith(item, StringComparison.OrdinalIgnoreCase)); if (!IsNullOrEmpty(wildcardPrefix)) { return new Uri(wildcardPrefix.Replace("://*", $"://{uri.Host}")); } throw new WebDavInternalServerException("Unable to find correct server root"); } /// /// Retrieves a store item through the specified /// from the /// specified /// and /// . /// /// The to retrieve the store item for. /// The that hosts the . /// The from which to retrieve the store item. /// /// The retrieved store item. /// /// /// /// is null. /// /// /// is null. /// /// /// is null. /// /// /// If the item was not found. /// /// refers to a document in a collection, where the /// collection does not exist. /// /// refers to a document that does not exist. public static IWebDavStoreItem GetItem(this Uri uri, WebDavServer server, IWebDavStore store) { if (uri == null) throw new ArgumentNullException(nameof(uri)); if (server == null) throw new ArgumentNullException(nameof(server)); if (store == null) throw new ArgumentNullException(nameof(store)); Uri prefixUri = uri.GetPrefixUri(server); IWebDavStoreCollection collection = store.Root; IWebDavStoreItem item = null; if (prefixUri.Segments.Length == uri.Segments.Length) return collection; for (int index = prefixUri.Segments.Length; index < uri.Segments.Length; index++) { string segmentName = Uri.UnescapeDataString(uri.Segments[index]); IWebDavStoreItem nextItem = collection.GetItemByName(segmentName.TrimEnd('/', '\\')); if (nextItem == null) throw new WebDavNotFoundException(); //throw new WebDavConflictException(); if (index == uri.Segments.Length - 1) item = nextItem; else { collection = nextItem as IWebDavStoreCollection; if (collection == null) throw new WebDavNotFoundException(); } } if (item == null) throw new WebDavNotFoundException(); return item; } private static string Match(Regex regex, string html, int i = 1) { return regex.Match(html).Groups[i].Value.Trim(); } public static Guid? GetLockTokenHeader(this IHttpListenerRequest request) { if (!request.Headers.AllKeys.Contains("Lock-Token")) return null; string r = Match(TokenRegex, request.Headers["Lock-Token"]); if (IsNullOrEmpty(r)) return null; return new Guid(r); } public static Guid? GetLockTokenIfHeader(this IHttpListenerRequest request) { if (!request.Headers.AllKeys.Contains("If")) return null; string t = request.Headers["If"].Substring(2, request.Headers["If"].Length - 4); return new Guid(t); } public static string ToLockToken(this Guid? token) { if (token == null) throw new NullReferenceException("token"); return "urn:uuid:" + token; } /// /// Gets the Timeout header : Second-number /// /// The request with the request included /// /// The value of the Timeout header as a string public static double? GetTimeoutHeader(this IHttpListenerRequest request, IWebDavStore store) { // get the value of the timeout header as a string string timeout = request.Headers["Timeout"]; // check if the string is valid or not infinity // if so, try to parse it to an int if (!IsNullOrEmpty(timeout) && !timeout.Equals("infinity") && !timeout.Equals("Infinite, Second-4100000000")) { string num = timeout.Substring(timeout.IndexOf("Second-", StringComparison.Ordinal) + 7); double d; if (double.TryParse(num, out d)) { return d; } } if (timeout.Equals("infinity") && timeout.Equals("Infinite, Second-4100000000") && store.LockSystem.AllowInfiniteCheckouts) return null; return 345600; } public static void SendException(this IHttpListenerContext context, WebDavException ex) { context.Response.StatusCode = ex.StatusCode; context.Response.StatusDescription = ex.StatusDescription; if (ex.Message == context.Response.StatusDescription) return; byte[] buffer = Encoding.UTF8.GetBytes(ex.Message); context.Response.ContentEncoding = Encoding.UTF8; context.Response.ContentLength64 = buffer.Length; context.Response.OutputStream.Write(buffer, 0, buffer.Length); context.Response.OutputStream.Flush(); } } }