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