using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Security.AccessControl; using System.Security.Principal; using WebDAVSharp.Server.Exceptions; namespace WebDAVSharp.Server.Stores.DiskStore { /// /// This class implements a disk-based that maps to a folder on disk. /// [DebuggerDisplay("Directory ({Name})")] public sealed class WebDavDiskStoreCollection : WebDavDiskStoreItem, IWebDavStoreCollection { private readonly Dictionary _items = new Dictionary(); /// /// Initializes a new instance of the class. /// /// The parent that contains this . /// The path to the folder on this that this maps to. public WebDavDiskStoreCollection(WebDavDiskStoreCollection parentCollection, string path) : base(parentCollection, path) { } #region IWebDAVStoreCollection Members /// /// Gets a collection of all the items in this . /// /// If the user is unauthorized or doesn't have access public IEnumerable Items { get { HashSet toDelete = new HashSet(_items.Values); List items = new List(); // TODO: Refactor to get rid of duplicate loop code List directories = new List(); try { // Impersonate the current user and get all the directories using (Identity.Impersonate()) { foreach (string dirName in Directory.GetDirectories(ItemPath)) { try { bool canread = CanReadDirectory(Path.Combine(ItemPath, dirName)); if (canread) { directories.Add(dirName); } } catch (Exception) { // do nothing } } } } catch { throw new WebDavUnauthorizedException(); } foreach (string subDirectoryPath in directories) { string name = Path.GetFileName(subDirectoryPath); WebDavDiskStoreCollection collection = null; WeakReference wr; if (_items.TryGetValue(name, out wr)) { collection = wr.Target as WebDavDiskStoreCollection; if (collection == null) toDelete.Remove(wr); } if (collection == null) { collection = new WebDavDiskStoreCollection(this, subDirectoryPath); _items[name] = new WeakReference(collection); } items.Add(collection); } List files = new List(); try { // Impersonate the current user and get all the files using (Identity.Impersonate()) { files.AddRange(Directory.GetFiles(ItemPath).Where(fileName => CanReadFile(Path.Combine(ItemPath, fileName)))); } } catch { throw new WebDavUnauthorizedException(); } foreach (string filePath in files) { string name = Path.GetFileName(filePath); WebDavDiskStoreDocument document = null; WeakReference wr; if (_items.TryGetValue(name, out wr)) { document = wr.Target as WebDavDiskStoreDocument; if (document == null) toDelete.Remove(wr); } if (document == null) { document = new WebDavDiskStoreDocument(this, filePath); _items[name] = new WeakReference(document); } items.Add(document); } return items.ToArray(); } } /// /// Checks if the user has access to the path /// /// The path. /// /// True if access, false if not /// private bool CanRead(string path) { if (File.Exists(path)) return CanReadFile(path); return Directory.Exists(path) && CanReadDirectory(path); } /// /// Checks if access to the file is granted. /// /// The path to the file as a /// /// The true if the user has access, else false /// /// /// Source: /// private static bool CanReadFile(string path) { try { File.Open(path, FileMode.Open, FileAccess.Read).Dispose(); return true; } catch (Exception) { return false; } } /// /// Checks if access to the directory is granted. /// /// The path to the director as a /// /// The true if the user has access, else false /// /// /// Source: /// private static bool CanReadDirectory(string path) { bool readAllow = false; bool readDeny = false; DirectorySecurity accessControlList = Directory.GetAccessControl(path); if (accessControlList == null) return false; AuthorizationRuleCollection accessRules = accessControlList.GetAccessRules(true, true, typeof(SecurityIdentifier)); foreach (FileSystemAccessRule rule in accessRules.Cast().Where(rule => (FileSystemRights.Read & rule.FileSystemRights) == FileSystemRights.Read)) { switch (rule.AccessControlType) { case AccessControlType.Allow: readAllow = true; break; case AccessControlType.Deny: readDeny = true; break; } } return readAllow && !readDeny; } /// /// Retrieves a store item by its name. /// /// The name of the store item to retrieve. /// /// The store item that has the specified /// ; /// or /// null if there is no store item with that name. /// public IWebDavStoreItem GetItemByName(string name) { string path = Path.Combine(ItemPath, name); using (Identity.Impersonate()) { if (File.Exists(path) && CanReadFile(path)) return new WebDavDiskStoreDocument(this, path); if (Directory.Exists(path) && CanReadDirectory(path)) return new WebDavDiskStoreCollection(this, path); } return null; } /// /// Creates a new collection with the specified name. /// /// The name of the new collection. /// /// The created instance. /// /// When the user is unauthorized or has no access public IWebDavStoreCollection CreateCollection(string name) { string path = Path.Combine(ItemPath, name); try { // Impersonate the current user and make file changes WindowsImpersonationContext wic = Identity.Impersonate(); Directory.CreateDirectory(path); wic.Undo(); } catch { throw new WebDavUnauthorizedException(); } return GetItemByName(name) as IWebDavStoreCollection; } /// /// Deletes a store item by its name. /// /// The name of the store item to delete. /// If the item was not found. /// If the user is unauthorized or has no access. public void Delete(IWebDavStoreItem item) { WebDavDiskStoreItem diskItem = (WebDavDiskStoreItem)item; string itemPath = diskItem.ItemPath; if (item is WebDavDiskStoreDocument) { if (!File.Exists(itemPath)) throw new WebDavNotFoundException(); try { // Impersonate the current user and delete the file WindowsImpersonationContext wic = Identity.Impersonate(); File.Delete(itemPath); wic.Undo(); } catch { throw new WebDavUnauthorizedException(); } } else { if (!Directory.Exists(itemPath)) throw new WebDavNotFoundException(); try { // Impersonate the current user and delete the directory WindowsImpersonationContext wic = Identity.Impersonate(); Directory.Delete(diskItem.ItemPath); wic.Undo(); } catch { throw new WebDavUnauthorizedException(); } } } /// /// Creates a new document with the specified name. /// /// The name of the new document. /// /// The created instance. /// /// If the item already exists /// If the user is unauthorized or has no access public IWebDavStoreDocument CreateDocument(string name) { string itemPath = Path.Combine(ItemPath, name); if (File.Exists(itemPath) || Directory.Exists(itemPath)) throw new WebDavConflictException(); try { // Impersonate the current user and delete the directory WindowsImpersonationContext wic = Identity.Impersonate(); File.Create(itemPath).Dispose(); wic.Undo(); } catch { throw new WebDavUnauthorizedException(); } WebDavDiskStoreDocument document = new WebDavDiskStoreDocument(this, itemPath); _items.Add(name, new WeakReference(document)); return document; } /// /// Copies an existing store item into this collection, overwriting any existing items. /// /// The store item to copy from. /// The name of the copy to create of . /// The boolean for copying the containing files/folders or not. /// /// The created instance. /// /// If the user is unauthorized or has no access public IWebDavStoreItem CopyItemHere(IWebDavStoreItem source, string destinationName, bool includeContent) { // We get the path of string destinationItemPath = Path.Combine(ItemPath, destinationName); // the moving of the item if (source.IsCollection) { try { // Impersonate the current user and move the directory WindowsImpersonationContext wic = Identity.Impersonate(); DirectoryCopy(source.ItemPath, destinationItemPath, true); wic.Undo(); } catch { throw new WebDavUnauthorizedException(); } // We return the moved file as a WebDAVDiskStoreDocument WebDavDiskStoreCollection collection = new WebDavDiskStoreCollection(this, destinationItemPath); _items.Add(destinationName, new WeakReference(collection)); return collection; } // We copy the file with an override. try { // Impersonate the current user and copy the file WindowsImpersonationContext wic = Identity.Impersonate(); File.Copy(source.ItemPath, destinationItemPath, true); wic.Undo(); } catch { throw new WebDavUnauthorizedException(); } // We return the moved file as a WebDAVDiskStoreDocument WebDavDiskStoreDocument document = new WebDavDiskStoreDocument(this, destinationItemPath); _items.Add(destinationName, new WeakReference(document)); return document; } /// /// Directories the copy. /// /// Name of the source dir. /// Name of the dest dir. /// if set to true [copy sub dirs]. /// Source directory does not exist or could not be found: /// + sourceDirName private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs) { // Get the subdirectories for the specified directory. DirectoryInfo dir = new DirectoryInfo(sourceDirName); DirectoryInfo[] dirs = dir.GetDirectories(); if (!dir.Exists) { throw new DirectoryNotFoundException( "Source directory does not exist or could not be found: " + sourceDirName); } // If the destination directory doesn't exist, create it. if (!Directory.Exists(destDirName)) { Directory.CreateDirectory(destDirName); } // If copying subdirectories, copy them and their contents to new location. if (!copySubDirs) return; // Get the files in the directory and copy them to the new location. FileInfo[] files = dir.GetFiles(); foreach (FileInfo file in files) file.CopyTo(Path.Combine(destDirName, file.Name), false); foreach (DirectoryInfo subdir in dirs) DirectoryCopy(subdir.FullName, Path.Combine(destDirName, subdir.Name), copySubDirs); } /// /// Moves an existing store item into this collection, overwriting any existing items. /// /// The store item to move. /// The /// that refers to the item that was moved, /// in its new location. /// /// The moved instance. /// /// Path to the source item not defined. /// If the user is unauthorized or has no access /// /// Note that the method should fail without creating or overwriting content in the /// target collection if the move cannot go through. /// public IWebDavStoreItem MoveItemHere(IWebDavStoreItem source, string destinationName) { // try to get the path of the source item string sourceItemPath = ""; WebDavDiskStoreItem sourceItem = (WebDavDiskStoreItem)source; sourceItemPath = sourceItem.ItemPath; if (sourceItemPath.Equals("")) { throw new Exception("Path to the source item not defined."); } // We get the path of string destinationItemPath = Path.Combine(ItemPath, destinationName); // the moving of the item if (source.IsCollection) { try { // Impersonate the current user and move the directory WindowsImpersonationContext wic = Identity.Impersonate(); Directory.Move(sourceItemPath, destinationItemPath); wic.Undo(); } catch { throw new WebDavUnauthorizedException(); } // We return the moved file as a WebDAVDiskStoreDocument var collection = new WebDavDiskStoreCollection(this, destinationItemPath); _items.Add(destinationName, new WeakReference(collection)); return collection; } try { // Impersonate the current user and move the file WindowsImpersonationContext wic = Identity.Impersonate(); File.Move(sourceItemPath, destinationItemPath); wic.Undo(); } catch { throw new WebDavUnauthorizedException(); } // We return the moved file as a WebDAVDiskStoreDocument WebDavDiskStoreDocument document = new WebDavDiskStoreDocument(this, destinationItemPath); _items.Add(destinationName, new WeakReference(document)); return document; } #endregion } }