441 lines
15 KiB
C#
441 lines
15 KiB
C#
|
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 {
|
||
|
/// <summary>
|
||
|
/// This class implements a disk-based <see cref="IWebDavStore" /> that maps to a folder on disk.
|
||
|
/// </summary>
|
||
|
[DebuggerDisplay("Directory ({Name})")]
|
||
|
public sealed class WebDavDiskStoreCollection : WebDavDiskStoreItem, IWebDavStoreCollection {
|
||
|
private readonly Dictionary<string, WeakReference> _items = new Dictionary<string, WeakReference>();
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initializes a new instance of the <see cref="WebDavDiskStoreCollection" /> class.
|
||
|
/// </summary>
|
||
|
/// <param name="parentCollection">The parent <see cref="WebDavDiskStoreCollection" /> that contains this <see cref="WebDavDiskStoreCollection" />.</param>
|
||
|
/// <param name="path">The path to the folder on this that this <see cref="WebDavDiskStoreCollection" /> maps to.</param>
|
||
|
public WebDavDiskStoreCollection(WebDavDiskStoreCollection parentCollection, string path)
|
||
|
: base(parentCollection, path) {
|
||
|
|
||
|
}
|
||
|
|
||
|
#region IWebDAVStoreCollection Members
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets a collection of all the items in this <see cref="IWebDavStoreCollection" />.
|
||
|
/// </summary>
|
||
|
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavUnauthorizedException">If the user is unauthorized or doesn't have access</exception>
|
||
|
public IEnumerable<IWebDavStoreItem> Items {
|
||
|
get {
|
||
|
HashSet<WeakReference> toDelete = new HashSet<WeakReference>(_items.Values);
|
||
|
List<IWebDavStoreItem> items = new List<IWebDavStoreItem>();
|
||
|
|
||
|
// TODO: Refactor to get rid of duplicate loop code
|
||
|
List<string> directories = new List<string>();
|
||
|
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<string> files = new List<string>();
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Checks if the user has access to the path
|
||
|
/// </summary>
|
||
|
/// <param name="path">The path.</param>
|
||
|
/// <returns>
|
||
|
/// True if access, false if not
|
||
|
/// </returns>
|
||
|
private bool CanRead(string path) {
|
||
|
if (File.Exists(path))
|
||
|
return CanReadFile(path);
|
||
|
return Directory.Exists(path) && CanReadDirectory(path);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Checks if access to the file is granted.
|
||
|
/// </summary>
|
||
|
/// <param name="path">The path to the file as a <see cref="string" /></param>
|
||
|
/// <returns>
|
||
|
/// The <see cref="bool" /> true if the user has access, else false
|
||
|
/// </returns>
|
||
|
/// <remarks>
|
||
|
/// Source: <see href="http://stackoverflow.com/questions/17318585/check-if-file-can-be-read" />
|
||
|
/// </remarks>
|
||
|
private static bool CanReadFile(string path) {
|
||
|
try {
|
||
|
File.Open(path, FileMode.Open, FileAccess.Read).Dispose();
|
||
|
return true;
|
||
|
}
|
||
|
catch (Exception) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Checks if access to the directory is granted.
|
||
|
/// </summary>
|
||
|
/// <param name="path">The path to the director as a <see cref="string" /></param>
|
||
|
/// <returns>
|
||
|
/// The <see cref="bool" /> true if the user has access, else false
|
||
|
/// </returns>
|
||
|
/// <remarks>
|
||
|
/// Source: <see href="http://stackoverflow.com/questions/11709862/check-if-directory-is-accessible-in-c" />
|
||
|
/// </remarks>
|
||
|
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<FileSystemAccessRule>().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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Retrieves a store item by its name.
|
||
|
/// </summary>
|
||
|
/// <param name="name">The name of the store item to retrieve.</param>
|
||
|
/// <returns>
|
||
|
/// The store item that has the specified
|
||
|
/// <paramref name="name" />;
|
||
|
/// or
|
||
|
/// <c>null</c> if there is no store item with that name.
|
||
|
/// </returns>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates a new collection with the specified name.
|
||
|
/// </summary>
|
||
|
/// <param name="name">The name of the new collection.</param>
|
||
|
/// <returns>
|
||
|
/// The created <see cref="IWebDavStoreCollection" /> instance.
|
||
|
/// </returns>
|
||
|
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavUnauthorizedException">When the user is unauthorized or has no access</exception>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Deletes a store item by its name.
|
||
|
/// </summary>
|
||
|
/// <param name="item">The name of the store item to delete.</param>
|
||
|
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavNotFoundException">If the item was not found.</exception>
|
||
|
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavUnauthorizedException">If the user is unauthorized or has no access.</exception>
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates a new document with the specified name.
|
||
|
/// </summary>
|
||
|
/// <param name="name">The name of the new document.</param>
|
||
|
/// <returns>
|
||
|
/// The created <see cref="IWebDavStoreDocument" /> instance.
|
||
|
/// </returns>
|
||
|
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavConflictException">If the item already exists</exception>
|
||
|
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavUnauthorizedException">If the user is unauthorized or has no access</exception>
|
||
|
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;
|
||
|
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Copies an existing store item into this collection, overwriting any existing items.
|
||
|
/// </summary>
|
||
|
/// <param name="source">The store item to copy from.</param>
|
||
|
/// <param name="destinationName">The name of the copy to create of <paramref name="source" />.</param>
|
||
|
/// <param name="includeContent">The boolean for copying the containing files/folders or not.</param>
|
||
|
/// <returns>
|
||
|
/// The created <see cref="IWebDavStoreItem" /> instance.
|
||
|
/// </returns>
|
||
|
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavUnauthorizedException">If the user is unauthorized or has no access</exception>
|
||
|
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;
|
||
|
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Directories the copy.
|
||
|
/// </summary>
|
||
|
/// <param name="sourceDirName">Name of the source dir.</param>
|
||
|
/// <param name="destDirName">Name of the dest dir.</param>
|
||
|
/// <param name="copySubDirs">if set to <c>true</c> [copy sub dirs].</param>
|
||
|
/// <exception cref="System.IO.DirectoryNotFoundException">Source directory does not exist or could not be found:
|
||
|
/// + sourceDirName</exception>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Moves an existing store item into this collection, overwriting any existing items.
|
||
|
/// </summary>
|
||
|
/// <param name="source">The store item to move.</param>
|
||
|
/// <param name="destinationName">The
|
||
|
/// <see cref="IWebDavStoreItem" /> that refers to the item that was moved,
|
||
|
/// in its new location.</param>
|
||
|
/// <returns>
|
||
|
/// The moved <see cref="IWebDavStoreItem" /> instance.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.Exception">Path to the source item not defined.</exception>
|
||
|
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavUnauthorizedException">If the user is unauthorized or has no access</exception>
|
||
|
/// <remarks>
|
||
|
/// Note that the method should fail without creating or overwriting content in the
|
||
|
/// target collection if the move cannot go through.
|
||
|
/// </remarks>
|
||
|
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
|
||
|
}
|
||
|
}
|