示例#1
0
        /// <summary>
        /// Creates <see cref="IHierarchyItemAsync"/> instance by path.
        /// </summary>
        /// <param name="path">Item relative path including query string.</param>
        /// <returns>Instance of corresponding <see cref="IHierarchyItemAsync"/> or null if item is not found.</returns>
        public override async Task <IHierarchyItemAsync> GetHierarchyItemAsync(string path)
        {
            path = path.Trim(new[] { ' ', '/' });

            //remove query string.
            int ind = path.IndexOf('?');

            if (ind > -1)
            {
                path = path.Remove(ind);
            }

            IHierarchyItemAsync item = null;

            item = await DavFolder.GetFolderAsync(this, path);

            if (item != null)
            {
                return(item);
            }

            item = await DavFile.GetFileAsync(this, path);

            if (item != null)
            {
                return(item);
            }

            Logger.LogDebug("Could not find item that corresponds to path: " + path);

            return(null); // no hierarchy item that corresponds to path parameter was found in the repository
        }
示例#2
0
        /// <summary>
        /// Handles GET and HEAD request.
        /// </summary>
        /// <param name="context">Instace of <see cref="DavContextBaseAsync"/>.</param>
        /// <param name="item">Instance of <see cref="IHierarchyItemAsync"/> which was returned by
        /// <see cref="DavContextBaseAsync.GetHierarchyItemAsync"/> for this request.</param>
        public async Task ProcessRequestAsync(DavContextBaseAsync context, IHierarchyItemAsync item)
        {
            string urlPath = context.Request.RawUrl.Substring(context.Request.ApplicationPath.TrimEnd('/').Length);

            if (item is IItemCollectionAsync)
            {
                // In case of GET requests to WebDAV folders we serve a web page to display
                // any information about this server and how to use it.

                // Remember to call EnsureBeforeResponseWasCalledAsync here if your context implementation
                // makes some useful things in BeforeResponseAsync.
                await context.EnsureBeforeResponseWasCalledAsync();

                string htmlName = "MyCustomHandlerPage.html";
                using (TextReader reader = File.OpenText(Path.Combine(htmlPath, htmlName)))
                {
                    string html = await reader.ReadToEndAsync();

                    html = html.Replace("_webDavServerRoot_", context.Request.ApplicationPath.TrimEnd('/'));
                    html = html.Replace("_webDavServerVersion_",
                                        typeof(DavEngineAsync).GetTypeInfo().Assembly.GetName().Version.ToString());

                    await WriteFileContentAsync(context, html, htmlName);
                }
            }
            else
            {
                await OriginalHandler.ProcessRequestAsync(context, item);
            }
        }
示例#3
0
        /// <summary>
        /// Called when this folder is being moved or renamed.
        /// </summary>
        /// <param name="destFolder">Destination folder.</param>
        /// <param name="destName">New name of this folder.</param>
        /// <param name="multistatus">Information about child items that failed to move.</param>
        public override async Task MoveToAsync(IItemCollectionAsync destFolder, string destName, MultistatusException multistatus)
        {
            // in this function we move item by item, because we want to check if each item is not locked.
            if (!(destFolder is DavFolder))
            {
                throw new DavException("Target folder doesn't exist", DavStatus.CONFLICT);
            }

            DavFolder targetFolder = (DavFolder)destFolder;

            if (IsRecursive(targetFolder))
            {
                throw new DavException("Cannot move folder to its subtree.", DavStatus.FORBIDDEN);
            }

            string newDirPath = System.IO.Path.Combine(targetFolder.FullPath, destName);
            string targetPath = targetFolder.Path + EncodeUtil.EncodeUrlPart(destName);

            try
            {
                // Remove item with the same name at destination if it exists.
                IHierarchyItemAsync item = await context.GetHierarchyItemAsync(targetPath);

                if (item != null)
                {
                    await item.DeleteAsync(multistatus);
                }

                await targetFolder.CreateFolderAsync(destName);
            }
            catch (DavException ex)
            {
                // Continue the operation but report error with destination path to client.
                multistatus.AddInnerException(targetPath, ex);
                return;
            }

            // Move child items.
            bool         movedSuccessfully = true;
            IFolderAsync createdFolder     = (IFolderAsync)await context.GetHierarchyItemAsync(targetPath);

            foreach (DavHierarchyItem item in (await GetChildrenAsync(new PropertyName[0], null, null, new List <OrderProperty>())).Page)
            {
                try
                {
                    await item.MoveToAsync(createdFolder, item.Name, multistatus);
                }
                catch (DavException ex)
                {
                    // Continue the operation but report error with child item to client.
                    multistatus.AddInnerException(item.Path, ex);
                    movedSuccessfully = false;
                }
            }

            if (movedSuccessfully)
            {
                await DeleteAsync(multistatus);
            }
        }
示例#4
0
        /// <summary>
        /// Performs file system operation with translating exceptions to those expected by WebDAV engine.
        /// </summary>
        /// <param name="item">Item on which operation is performed.</param>
        /// <param name="func">The action to be performed.</param>
        /// <param name="privilege">Privilege which is needed to perform the operation.
        /// If <see cref="UnauthorizedAccessException"/> is thrown  this method will convert it to
        /// <see cref="NeedPrivilegesException"/> exception and specify this privilege in it.</param>
        /// <typeparam name="T">Type of operation's result.</typeparam>
        /// <returns>Result returned by <paramref name="func"/>.</returns>
        public T FileOperation <T>(IHierarchyItemAsync item, Func <T> func, Privilege privilege)
        {
            try
            {
                using (impersonate())
                {
                    return(func());
                }
            }
            catch (UnauthorizedAccessException)
            {
                NeedPrivilegesException ex = new NeedPrivilegesException("Not enough privileges");
                ex.AddRequiredPrivilege(item.Path, privilege);
                throw ex;
            }
            catch (IOException ex)
            {
                int hr = Marshal.GetHRForException(ex);
                if (hr == ERROR_DISK_FULL)
                {
                    throw new InsufficientStorageException();
                }

                throw new DavException(ex.Message, DavStatus.CONFLICT);
            }
        }
示例#5
0
        /// <summary>
        /// Handles GET and HEAD request.
        /// </summary>
        /// <param name="context">Instace of <see cref="DavContextBaseAsync"/>.</param>
        /// <param name="item">Instance of <see cref="IHierarchyItemAsync"/> which was returned by
        /// <see cref="DavContextBaseAsync.GetHierarchyItemAsync"/> for this request.</param>
        public async Task ProcessRequestAsync(DavContextBaseAsync context, IHierarchyItemAsync item)
        {
            string urlPath = context.Request.RawUrl.Substring(context.Request.ApplicationPath.TrimEnd('/').Length);

            if (item is IItemCollectionAsync)
            {
                // In case of GET requests to WebDAV folders we serve a web page to display
                // any information about this server and how to use it.

                // Remember to call EnsureBeforeResponseWasCalledAsync here if your context implementation
                // makes some useful things in BeforeResponseAsync.
                await context.EnsureBeforeResponseWasCalledAsync();

                IHttpAsyncHandler page = (IHttpAsyncHandler)System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(
                    "~/MyCustomHandlerPage.aspx", typeof(MyCustomHandlerPage));

                if (Type.GetType("Mono.Runtime") != null)
                {
                    page.ProcessRequest(HttpContext.Current);
                }
                else
                {
                    // Here we call BeginProcessRequest instead of ProcessRequest to start an async page execution and be able to call RegisterAsyncTask if required.
                    // To call APM method (Begin/End) from TAP method (Task/async/await) the Task.FromAsync must be used.
                    await Task.Factory.FromAsync(page.BeginProcessRequest, page.EndProcessRequest, HttpContext.Current, null);
                }
            }
            else
            {
                await OriginalHandler.ProcessRequestAsync(context, item);
            }
        }
示例#6
0
        /// <summary>
        /// Called when children of this folder are being listed.
        /// </summary>
        /// <param name="propNames">List of properties to retrieve with the children. They will be queried by the engine later.</param>
        /// <returns>Children of this folder.</returns>
        public virtual async Task <IEnumerable <IHierarchyItemAsync> > GetChildrenAsync(IList <PropertyName> propNames)
        {
            // Enumerates all child files and folders.
            // You can filter children items in this implementation and
            // return only items that you want to be visible for this
            // particular user.

            IList <IHierarchyItemAsync> children = new List <IHierarchyItemAsync>();

            FileSystemInfo[] fileInfos = null;
            fileInfos = dirInfo.GetFileSystemInfos();

            foreach (FileSystemInfo fileInfo in fileInfos)
            {
                string childPath          = Path + EncodeUtil.EncodeUrlPart(fileInfo.Name);
                IHierarchyItemAsync child = await context.GetHierarchyItemAsync(childPath);

                if (child != null)
                {
                    children.Add(child);
                }
            }

            return(children);
        }
示例#7
0
        /// <summary>
        /// Copies this folder to another folder with option to rename it.
        /// </summary>
        /// <param name="destFolder">Folder to copy this folder to.</param>
        /// <param name="destName">New name of this folder.</param>
        /// <param name="deep">Whether children shall be copied.</param>
        /// <param name="multistatus">Container for errors. We put here errors which occur with
        /// individual items being copied.</param>
        public override async Task CopyToAsync(
            IItemCollectionAsync destFolder,
            string destName,
            bool deep,
            MultistatusException multistatus)
        {
            DavFolder destDavFolder = destFolder as DavFolder;

            if (destFolder == null)
            {
                throw new DavException("Destination folder doesn't exist", DavStatus.CONFLICT);
            }
            if (!await destDavFolder.ClientHasTokenAsync())
            {
                throw new LockedException("Doesn't have token for destination folder.");
            }

            if (isRecursive(destDavFolder))
            {
                throw new DavException("Cannot copy folder to its subtree", DavStatus.FORBIDDEN);
            }

            IHierarchyItemAsync destItem = await destDavFolder.FindChildAsync(destName);

            if (destItem != null)
            {
                try
                {
                    await destItem.DeleteAsync(multistatus);
                }
                catch (DavException ex)
                {
                    multistatus.AddInnerException(destItem.Path, ex);
                    return;
                }
            }

            DavFolder newDestFolder = await CopyThisItemAsync(destDavFolder, null, destName);

            // copy children
            if (deep)
            {
                foreach (IHierarchyItemAsync child in (await GetChildrenAsync(new PropertyName[0], null, null, null)).Page)
                {
                    var dbchild = child as DavHierarchyItem;
                    try
                    {
                        await dbchild.CopyToAsync(newDestFolder, child.Name, deep, multistatus);
                    }
                    catch (DavException ex)
                    {
                        multistatus.AddInnerException(dbchild.Path, ex);
                    }
                }
            }
            await Context.socketService.NotifyRefreshAsync(newDestFolder.Path);
        }
        /// <summary>
        /// Called when this file is being moved or renamed.
        /// </summary>
        /// <param name="destFolder">Destination folder.</param>
        /// <param name="destName">New name of this file.</param>
        /// <param name="multistatus">Information about items that failed to move.</param>
        public override async Task MoveToAsync(IItemCollectionAsync destFolder, string destName, MultistatusException multistatus)
        {
            DavFolder targetFolder = (DavFolder)destFolder;

            if (targetFolder == null || !Directory.Exists(targetFolder.FullPath))
            {
                throw new DavException("Target directory doesn't exist", DavStatus.CONFLICT);
            }

            string newDirPath = System.IO.Path.Combine(targetFolder.FullPath, destName);
            string targetPath = targetFolder.Path + EncodeUtil.EncodeUrlPart(destName);

            // If an item with the same name exists in target directory - remove it.
            try
            {
                IHierarchyItemAsync item = await context.GetHierarchyItemAsync(targetPath);

                if (item != null)
                {
                    await item.DeleteAsync(multistatus);
                }
            }
            catch (DavException ex)
            {
                // Report exception to client and continue with other items by returning from recursion.
                multistatus.AddInnerException(targetPath, ex);
                return;
            }

            // Move the file.
            try
            {
                File.Move(fileSystemInfo.FullName, newDirPath);

                var newFileInfo = new FileInfo(newDirPath);
                if (FileSystemInfoExtension.IsUsingFileSystemAttribute)
                {
                    await fileSystemInfo.MoveExtendedAttributes(newFileInfo);
                }

                // Locks should not be copied, delete them.
                if (await newFileInfo.HasExtendedAttributeAsync("Locks"))
                {
                    await newFileInfo.DeleteExtendedAttributeAsync("Locks");
                }
            }
            catch (UnauthorizedAccessException)
            {
                // Exception occurred with the item for which MoveTo was called - fail the operation.
                NeedPrivilegesException ex = new NeedPrivilegesException("Not enough privileges");
                ex.AddRequiredPrivilege(targetPath, Privilege.Bind);

                string parentPath = System.IO.Path.GetDirectoryName(Path);
                ex.AddRequiredPrivilege(parentPath, Privilege.Unbind);
                throw ex;
            }
        }
示例#9
0
        /// <summary>
        /// Called when this file is being copied.
        /// </summary>
        /// <param name="destFolder">Destination folder.</param>
        /// <param name="destName">New file name.</param>
        /// <param name="deep">Whether children items shall be copied. Ignored for files.</param>
        /// <param name="multistatus">Information about items that failed to copy.</param>
        public override async Task CopyToAsync(IItemCollectionAsync destFolder, string destName, bool deep, MultistatusException multistatus)
        {
            DavFolder targetFolder = (DavFolder)destFolder;

            if (targetFolder == null || !Directory.Exists(targetFolder.FullPath))
            {
                throw new DavException("Target directory doesn't exist", DavStatus.CONFLICT);
            }

            string newFilePath = System.IO.Path.Combine(targetFolder.FullPath, destName);
            string targetPath  = targetFolder.Path + EncodeUtil.EncodeUrlPart(destName);

            // If an item with the same name exists - remove it.
            try
            {
                IHierarchyItemAsync item = await context.GetHierarchyItemAsync(targetPath);

                if (item != null)
                {
                    await item.DeleteAsync(multistatus);
                }
            }
            catch (DavException ex)
            {
                // Report error with other item to client.
                multistatus.AddInnerException(targetPath, ex);
                return;
            }

            // Copy the file togather with all extended attributes (custom props and locks).
            try
            {
                File.Copy(fileSystemInfo.FullName, newFilePath);

                var newFileSystemInfo = new FileInfo(newFilePath);
                if (FileSystemInfoExtension.IsUsingFileSystemAttribute)
                {
                    await fileSystemInfo.CopyExtendedAttributes(newFileSystemInfo);
                }

                // Locks should not be copied, delete them.
                if (await fileSystemInfo.HasExtendedAttributeAsync("Locks"))
                {
                    await newFileSystemInfo.DeleteExtendedAttributeAsync("Locks");
                }
            }
            catch (UnauthorizedAccessException)
            {
                // Fail
                NeedPrivilegesException ex = new NeedPrivilegesException("Not enough privileges");
                string parentPath          = System.IO.Path.GetDirectoryName(Path);
                ex.AddRequiredPrivilege(parentPath, Privilege.Bind);
                throw ex;
            }
            await context.socketService.NotifyRefreshAsync(targetFolder.Path);
        }
示例#10
0
        /// <summary>
        /// Creates <see cref="IHierarchyItemAsync"/> instance by path.
        /// </summary>
        /// <param name="path">Item relative path including query string.</param>
        /// <returns>Instance of corresponding <see cref="IHierarchyItemAsync"/> or null if item is not found.</returns>
        public override async Task <IHierarchyItemAsync> GetHierarchyItemAsync(string path)
        {
            path = path.Trim(new[] { ' ', '/' });

            //remove query string.
            int ind = path.IndexOf('?');

            if (ind > -1)
            {
                path = path.Remove(ind);
            }

            IHierarchyItemAsync item = null;

            // Return items from [DAVLocation]/acl/ folder and subfolders.
            item = await AclFactory.GetAclItemAsync(this, path);

            if (item != null)
            {
                return(item);
            }

            // Return items from [DAVLocation]/calendars/ folder and subfolders.
            item = CalDavFactory.GetCalDavItem(this, path);
            if (item != null)
            {
                return(item);
            }

            // Return folder that corresponds to [DAVLocation] path. If no DavLocation is defined in config file this is a website root.
            item = DavLocationFolder.GetDavLocationFolder(this, path);
            if (item != null)
            {
                return(item);
            }

            item = await DavFolder.GetFolderAsync(this, path);

            if (item != null)
            {
                return(item);
            }

            item = await DavFile.GetFileAsync(this, path);

            if (item != null)
            {
                return(item);
            }

            Logger.LogDebug("Could not find item that corresponds to path: " + path);

            return(null); // no hierarchy item that corresponds to path parameter was found in the repository
        }
        /// <summary>
        /// Resolves path to instance of <see cref="IHierarchyItem"/>.
        /// This method is called by WebDAV engine to resolve paths it encounters
        /// in request.
        /// </summary>
        /// <param name="path">Relative path to the item including query string.</param>
        /// <returns><see cref="IHierarchyItem"/> instance if item is found, <c>null</c> otherwise.</returns>
        public override async Task <IHierarchyItemBaseAsync> GetHierarchyItemAsync(string path)
        {
            path = path.Trim(new[] { ' ', '/' });

            //remove query string.
            int ind = path.IndexOf('?');

            if (ind > -1)
            {
                path = path.Remove(ind);
            }

            IHierarchyItemAsync item = null;

            // Return items from [DAVLocation]/acl/ folder and subfolders.
            item = await AclFactory.GetAclItemAsync(this, path);

            if (item != null)
            {
                return(item);
            }

            // Return items from [DAVLocation]/addressbooks/ folder and subfolders.
            item = await CardDavFactory.GetCardDavItemAsync(this, path);

            if (item != null)
            {
                return(item);
            }

            // Return folder that corresponds to [DAVLocation] path. If no DavLocation section is defined in web.config/app.config [DAVLocation] is a website root.
            string davLocation = DavLocationFolder.DavLocationFolderPath.Trim('/');

            if (davLocation.Equals(path, StringComparison.InvariantCultureIgnoreCase))
            {
                return(new DavLocationFolder(this));
            }

            // Return any folders above [DAVLocation] path.
            // Root folder with ICalendarDiscovery/IAddressbookDiscovery implementation is required for calendars and address books discovery by CalDAV/CardDAV clients.
            // All other folders are returned just for folders structure browsing convenience using WebDAV clients during dev time, not required by CalDAV/CardDAV clients.
            if (davLocation.StartsWith(path, StringComparison.InvariantCultureIgnoreCase))
            {
                int    childFolderPathLength = (davLocation + "/").IndexOf('/', path.Length + 1);
                string childFolderPath       = davLocation.Substring(0, childFolderPathLength);
                return(new LogicalFolder(this, path, new [] { new LogicalFolder(this, childFolderPath) }));
            }

            Logger.LogDebug("Could not find item that corresponds to path: " + path);

            return(null); // no hierarchy item that corresponds to path parameter was found in the repository
        }
 /// <summary>Initializes a new instance of the <see cref="ItemMetadata"/> class.</summary>
 /// <param name="identifier">The identifier.</param>
 /// <param name="parentIdentifier">The parent identifier.</param>
 /// <param name="name">The name.</param>
 /// <param name="localItem">The local item.</param>
 /// <param name="serverItem">The server item.</param>
 protected ItemMetadata(
     string identifier,
     string parentIdentifier,
     string name,
     LocalItem localItem,
     IHierarchyItemAsync serverItem = null)
 {
     this.Identifier       = identifier;
     this.ParentIdentifier = parentIdentifier;
     this.Name             = name;
     this.LocalItem        = localItem;
     this.ServerItem       = serverItem;
 }
        /// <summary>
        /// Writes iOS / OS X CalDAV/CardDAV profile.
        /// </summary>
        /// <param name="context">Instace of <see cref="DavContextBaseAsync"/>.</param>
        /// <param name="item">ICalendarFolderAsync or IAddressbookFolderAsync item.</param>
        /// <returns></returns>
        private async Task WriteProfileAsync(DavContextBaseAsync context, IHierarchyItemAsync item, string htmlPath)
        {
            string mobileconfigFileName = null;
            string decription           = null;

            if (item is ICalendarFolderAsync)
            {
                mobileconfigFileName = "CalDAV.AppleProfileTemplete.mobileconfig";
                decription           = (item as ICalendarFolderAsync).CalendarDescription;
            }

            decription = !string.IsNullOrEmpty(decription) ? decription : item.Name;

            string templateContent = null;

            using (TextReader reader = new StreamReader(Path.Combine(htmlPath, mobileconfigFileName)))
            {
                templateContent = await reader.ReadToEndAsync();
            }

            Uri url = new Uri(context.Request.UrlPrefix);

            string payloadUUID = item.Path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Last(); // PayloadUUID

            string profile = string.Format(templateContent
                                           , url.Host                                     // host name
                                           , item.Path                                    // CalDAV / CardDAV Principal URL. Here we can return (await (item as ICurrentUserPrincipalAsync).GetCurrentUserPrincipalAsync()).Path if needed.
                                           , (context as DavContext).UserName             // user name
                                           , url.Port                                     // port
                                           , (url.Scheme == "https").ToString().ToLower() // SSL
                                           , decription                                   // CardDAV / CardDAV Account Description
                                           , Assembly.GetAssembly(this.GetType()).GetName().Version.ToString()
                                           , Assembly.GetAssembly(typeof(DavEngineAsync)).GetName().Version.ToString()
                                           , payloadUUID
                                           );

            byte[] profileBytes = SignProfile(context, profile);

            context.Response.ContentType = "application/x-apple-aspen-config";
            context.Response.AddHeader("Content-Disposition", "attachment; filename=profile.mobileconfig");
            context.Response.ContentLength = profileBytes.Length;
            using (BinaryWriter writer = new BinaryWriter(context.Response.OutputStream))
            {
                writer.Write(profileBytes);
            }
        }
示例#14
0
        /// <summary>
        /// Handles GET and HEAD request.
        /// </summary>
        /// <param name="context">Instace of <see cref="DavContextBaseAsync"/>.</param>
        /// <param name="item">Instance of <see cref="IHierarchyItemAsync"/> which was returned by
        /// <see cref="DavContextBaseAsync.GetHierarchyItemAsync"/> for this request.</param>
        public async Task ProcessRequestAsync(DavContextBaseAsync context, IHierarchyItemAsync item)
        {
            if (item is IItemCollectionAsync)
            {
                // In case of GET requests to WebDAV folders we serve a web page to display
                // any information about this server and how to use it.

                // Remember to call EnsureBeforeResponseWasCalledAsync here if your context implementation
                // makes some useful things in BeforeResponseAsync.
                await context.EnsureBeforeResponseWasCalledAsync();

                string htmlName = "MyCustomHandlerPage.html";
                string html     = await getFileContent(htmlName);

                html = html.Replace("_webDavServerRoot_", context.Request.ApplicationPath.TrimEnd('/'));
                html = html.Replace("_webDavServerVersion_",
                                    typeof(DavEngineAsync).GetTypeInfo().Assembly.GetName().Version.ToString());

                await WriteFileContentAsync(context, html, htmlName);
            }
            else if (context.Request.RawUrl.StartsWith("/wwwroot/"))
            {
                // The "/wwwroot/" is not a WebDAV folder. It can be used to store client script files,
                // images, static HTML files or any other files that does not require access via WebDAV.
                // Any request to the files in this folder will just serve them to client.

                await context.EnsureBeforeResponseWasCalledAsync();

                string relativeFilePath = context.Request.RawUrl.TrimStart('/').Replace('/', Path.DirectorySeparatorChar);

                // Remove query string.
                int queryIndex = relativeFilePath.LastIndexOf('?');
                if (queryIndex > -1)
                {
                    relativeFilePath = relativeFilePath.Remove(queryIndex);
                }
                await WriteFileContentAsync(context, await getFileContent(relativeFilePath), relativeFilePath);
            }
            else
            {
                await OriginalHandler.ProcessRequestAsync(context, item);
            }
        }
        protected static async Task FindLocksDownAsync(IHierarchyItemAsync root, bool skipShared)
        {
            IFolderAsync folder = root as IFolderAsync;

            if (folder != null)
            {
                foreach (IHierarchyItemAsync child in (await folder.GetChildrenAsync(new PropertyName[0], null, null, null)).Page)
                {
                    DavHierarchyItem dbchild = child as DavHierarchyItem;
                    if (await dbchild.ItemHasLockAsync(skipShared))
                    {
                        MultistatusException mex = new MultistatusException();
                        mex.AddInnerException(dbchild.Path, new LockedException());
                        throw mex;
                    }

                    await FindLocksDownAsync(child, skipShared);
                }
            }
        }
示例#16
0
        /// <summary>
        /// Called when children of this folder with paging information are being listed.
        /// </summary>
        /// <param name="propNames">List of properties to retrieve with the children. They will be queried by the engine later.</param>
        /// <param name="offset">The number of children to skip before returning the remaining items. Start listing from from next item.</param>
        /// <param name="nResults">The number of items to return.</param>
        /// <param name="orderProps">List of order properties requested by the client.</param>
        /// <returns>Items requested by the client and a total number of items in this folder.</returns>
        public virtual async Task <PageResults> GetChildrenAsync(IList <PropertyName> propNames, long?offset, long?nResults, IList <OrderProperty> orderProps)
        {
            // Enumerates all child files and folders.
            // You can filter children items in this implementation and
            // return only items that you want to be visible for this
            // particular user.

            IList <IHierarchyItemAsync> children = new List <IHierarchyItemAsync>();

            FileSystemInfo[] fileInfos  = null;
            long             totalItems = 0;

            fileInfos  = dirInfo.GetFileSystemInfos();
            totalItems = fileInfos.Length;

            // Apply sorting.
            fileInfos = SortChildren(fileInfos, orderProps);

            // Apply paging.
            if (offset.HasValue && nResults.HasValue)
            {
                fileInfos = fileInfos.Skip((int)offset.Value).Take((int)nResults.Value).ToArray();
            }

            foreach (FileSystemInfo fileInfo in fileInfos)
            {
                string childPath          = Path + EncodeUtil.EncodeUrlPart(fileInfo.Name);
                IHierarchyItemAsync child = await context.GetHierarchyItemAsync(childPath);

                if (child != null)
                {
                    children.Add(child);
                }
            }

            return(new PageResults(children, totalItems));
        }
示例#17
0
        /// <summary>
        /// Gets CalDAV items.
        /// </summary>
        /// <param name="path">Relative path requested.</param>
        /// <param name="context">Instance of <see cref="DavContext"/> class.</param>
        /// <returns>Object implementing various CalDAV items or null if no object corresponding to path is found.</returns>
        internal static IHierarchyItemAsync GetCalDavItem(DavContext context, string path)
        {
            IHierarchyItemAsync item = null;

            item = CalendarsRootFolder.GetCalendarsRootFolder(context, path);
            if (item != null)
            {
                return(item);
            }

            item = CalendarFolder.GetCalendarFolder(context, path);
            if (item != null)
            {
                return(item);
            }

            item = CalendarFile.GetCalendarFile(context, path);
            if (item != null)
            {
                return(item);
            }

            return(null);
        }
        /// <summary>
        /// Gets CardDAV items.
        /// </summary>
        /// <param name="path">Relative path requested.</param>
        /// <param name="context">Instance of <see cref="DavContext"/> class.</param>
        /// <returns>Object implementing various CardDAV items or null if no object corresponding to path is found.</returns>
        internal static IHierarchyItemAsync GetCardDavItem(DavContext context, string path)
        {
            IHierarchyItemAsync item = null;

            item = AddressbooksRootFolder.GetAddressbooksRootFolder(context, path);
            if (item != null)
            {
                return(item);
            }

            item = AddressbookFolder.GetAddressbookFolder(context, path);
            if (item != null)
            {
                return(item);
            }

            item = CardFile.GetCardFile(context, path);
            if (item != null)
            {
                return(item);
            }

            return(null);
        }
        /// <summary>
        /// Handles GET and HEAD request.
        /// </summary>
        /// <param name="context">Instace of <see cref="DavContextBaseAsync"/>.</param>
        /// <param name="item">Instance of <see cref="IHierarchyItemAsync"/> which was returned by
        /// <see cref="DavContextBaseAsync.GetHierarchyItemAsync"/> for this request.</param>
        public async Task ProcessRequestAsync(DavContextBaseAsync context, IHierarchyItemAsync item)
        {
            string urlPath = context.Request.RawUrl.Substring(context.Request.ApplicationPath.TrimEnd('/').Length);

            if (item is IItemCollectionAsync)
            {
                // In case of GET requests to WebDAV folders we serve a web page to display
                // any information about this server and how to use it.

                // Remember to call EnsureBeforeResponseWasCalledAsync here if your context implementation
                // makes some useful things in BeforeResponseAsync.
                await context.EnsureBeforeResponseWasCalledAsync();

                string htmlName = "MyCustomHandlerPage.html";
                using (TextReader reader = File.OpenText(Path.Combine(htmlPath, htmlName)))
                {
                    string html = await reader.ReadToEndAsync();

                    html = html.Replace("_webDavServerRoot_", context.Request.ApplicationPath.TrimEnd('/'));
                    html = html.Replace("_webDavServerVersion_",
                                        typeof(DavEngineAsync).GetTypeInfo().Assembly.GetName().Version.ToString());

                    await WriteFileContentAsync(context, html, htmlName);
                }
            }
            else if (urlPath.StartsWith("/AjaxFileBrowser/") || urlPath.StartsWith("/wwwroot/"))
            {
                // The "/AjaxFileBrowser/" are not a WebDAV folders. They can be used to store client script files,
                // images, static HTML files or any other files that does not require access via WebDAV.
                // Any request to the files in this folder will just serve them to the client.

                await context.EnsureBeforeResponseWasCalledAsync();

                string filePath = Path.Combine(htmlPath, urlPath.TrimStart('/').Replace('/', Path.DirectorySeparatorChar));

                // Remove query string.
                int queryIndex = filePath.LastIndexOf('?');
                if (queryIndex > -1)
                {
                    filePath = filePath.Remove(queryIndex);
                }

                if (!File.Exists(filePath))
                {
                    throw new DavException("File not found: " + filePath, DavStatus.NOT_FOUND);
                }

                Encoding encoding = context.Engine.ContentEncoding; // UTF-8 by default
                context.Response.ContentType = string.Format("{0}; charset={1}", MimeType.GetMimeType(Path.GetExtension(filePath)) ?? "application/octet-stream", encoding.WebName);

                // Return file content in case of GET request, in case of HEAD just return headers.
                if (context.Request.HttpMethod == "GET")
                {
                    using (FileStream fileStream = File.OpenRead(filePath))
                    {
                        context.Response.ContentLength = fileStream.Length;
                        await fileStream.CopyToAsync(context.Response.OutputStream);
                    }
                }
            }
            else
            {
                await OriginalHandler.ProcessRequestAsync(context, item);
            }
        }
示例#20
0
        /// <summary>
        /// Called when this folder is being moved or renamed.
        /// </summary>
        /// <param name="destFolder">Destination folder.</param>
        /// <param name="destName">New name of this folder.</param>
        /// <param name="multistatus">Information about child items that failed to move.</param>
        public override async Task MoveToAsync(IItemCollectionAsync destFolder, string destName, MultistatusException multistatus)
        {
            await RequireHasTokenAsync();

            DavFolder targetFolder = destFolder as DavFolder;

            if (targetFolder == null)
            {
                throw new DavException("Target folder doesn't exist", DavStatus.CONFLICT);
            }

            if (IsRecursive(targetFolder))
            {
                throw new DavException("Cannot move folder to its subtree.", DavStatus.FORBIDDEN);
            }

            string newDirPath = System.IO.Path.Combine(targetFolder.FullPath, destName);
            string targetPath = targetFolder.Path + EncodeUtil.EncodeUrlPart(destName);

            try
            {
                // Remove item with the same name at destination if it exists.
                IHierarchyItemAsync item = await context.GetHierarchyItemAsync(targetPath);

                if (item != null)
                {
                    await item.DeleteAsync(multistatus);
                }

                await targetFolder.CreateFolderAsync(destName);
            }
            catch (DavException ex)
            {
                // Continue the operation but report error with destination path to client.
                multistatus.AddInnerException(targetPath, ex);
                return;
            }

            // Move child items.
            bool         movedSuccessfully = true;
            IFolderAsync createdFolder     = (IFolderAsync)await context.GetHierarchyItemAsync(targetPath);

            foreach (DavHierarchyItem item in await GetChildrenAsync(new PropertyName[0]))
            {
                try
                {
                    await item.MoveToAsync(createdFolder, item.Name, multistatus);
                }
                catch (DavException ex)
                {
                    // Continue the operation but report error with child item to client.
                    multistatus.AddInnerException(item.Path, ex);
                    movedSuccessfully = false;
                }
            }

            if (movedSuccessfully)
            {
                await DeleteAsync(multistatus);
            }
            // Refresh client UI.
            await context.socketService.NotifyDeleteAsync(Path);

            await context.socketService.NotifyRefreshAsync(GetParentPath(targetPath));
        }
 /// <summary>Calculate item's name.</summary>
 /// <param name="itemIdentifier">The item identifier.</param>
 /// <param name="serverItem">The server item.</param>
 /// <returns>The <see cref="string"/>.</returns>
 private string GetItemDisplayName(string itemIdentifier, IHierarchyItemAsync serverItem)
 {
     return(serverItem?.DisplayName ?? this.LocationMapper.GetNameFromIdentifier(itemIdentifier));
 }
示例#22
0
 /// <summary>
 /// This handler shall only be invoked for <see cref="IFolderAsync"/> items or if original handler (which
 /// this handler substitutes) shall be called for the item.
 /// </summary>
 /// <param name="item">Instance of <see cref="IHierarchyItemAsync"/> which was returned by
 /// <see cref="DavContextBaseAsync.GetHierarchyItemAsync"/> for this request.</param>
 /// <returns>Returns <c>true</c> if this handler can handler this item.</returns>
 public bool AppliesTo(IHierarchyItemAsync item)
 {
     return(item is IFolderAsync || OriginalHandler.AppliesTo(item));
 }
示例#23
0
        /// <summary>
        /// Gets a user file system item info from the remote storage data.
        /// </summary>
        /// <param name="remoteStorageItem">Remote storage item info.</param>
        /// <returns>User file system item info.</returns>
        public static FileSystemItemMetadataExt GetUserFileSystemItemMetadata(IHierarchyItemAsync remoteStorageItem)
        {
            FileSystemItemMetadataExt userFileSystemItem;

            if (remoteStorageItem is IFileAsync)
            {
                userFileSystemItem = new FileMetadataExt();
            }
            else
            {
                userFileSystemItem = new FolderMetadataExt();
            }

            userFileSystemItem.Name           = remoteStorageItem.DisplayName;
            userFileSystemItem.Attributes     = FileAttributes.Normal;
            userFileSystemItem.CreationTime   = remoteStorageItem.CreationDate;
            userFileSystemItem.LastWriteTime  = remoteStorageItem.LastModified;
            userFileSystemItem.LastAccessTime = remoteStorageItem.LastModified;
            userFileSystemItem.ChangeTime     = remoteStorageItem.LastModified;

            // If the item is locked by another user, set the LockedByAnotherUser to true.
            // This will set read-only arrtibute on files.
            // Note that read-only attribute is a convenience feature, it does not protect files from modification.
            userFileSystemItem.LockedByAnotherUser = remoteStorageItem.ActiveLocks.Length > 0;

            if (remoteStorageItem is IFileAsync)
            {
                // We send the ETag to
                // the server inside If-Match header togeter with updated content from client.
                // This will make sure the changes on the server is not overwritten.
                ((FileMetadataExt)userFileSystemItem).ETag = ((IFileAsync)remoteStorageItem).Etag;

                ((FileMetadataExt)userFileSystemItem).Length = ((IFileAsync)remoteStorageItem).ContentLength;
            }
            ;

            // Set custom columns to be displayed in file manager.
            // We create property definitions when registering the sync root with corresponding IDs.
            List <FileSystemItemPropertyData> customProps = new List <FileSystemItemPropertyData>();
            LockInfo lockInfo = remoteStorageItem.ActiveLocks.FirstOrDefault();

            if (lockInfo != null)
            {
                customProps.AddRange(
                    new ServerLockInfo()
                {
                    LockToken             = lockInfo.LockToken.LockToken,
                    Owner                 = lockInfo.Owner,
                    Exclusive             = lockInfo.LockScope == LockScope.Exclusive,
                    LockExpirationDateUtc = DateTimeOffset.Now.Add(lockInfo.TimeOut)
                }.GetLockProperties(Path.Combine(Program.Settings.IconsFolderPath, "LockedByAnotherUser.ico"))
                    );
            }
            if (remoteStorageItem is IFileAsync)
            {
                customProps.Add(new FileSystemItemPropertyData((int)CustomColumnIds.ETag, ((IFileAsync)remoteStorageItem).Etag));
            }
            ;
            userFileSystemItem.CustomProperties = customProps;

            return(userFileSystemItem);
        }
示例#24
0
        /// <summary>
        /// Searches files and folders in current folder using search phrase and options.
        /// </summary>
        /// <param name="searchString">A phrase to search.</param>
        /// <param name="options">Search options.</param>
        /// <param name="propNames">
        /// List of properties to retrieve with each item returned by this method. They will be requested by the
        /// Engine in <see cref="IHierarchyItemAsync.GetPropertiesAsync(IList{PropertyName}, bool)"/> call.
        /// </param>
        /// <returns>List of <see cref="IHierarchyItemAsync"/> satisfying search request.</returns>
        public async Task <IEnumerable <IHierarchyItemAsync> > SearchAsync(string searchString, SearchOptions options, List <PropertyName> propNames)
        {
            bool includeSnippet = propNames.Any(s => s.Name == snippetProperty);

            // search both in file name and content
            string commandText =
                @"SELECT System.ItemPathDisplay" + (includeSnippet ? " ,System.Search.AutoSummary" : string.Empty) + " FROM SystemIndex " +
                @"WHERE scope ='file:@Path' AND (System.ItemNameDisplay LIKE '@Name' OR FREETEXT('""@Content""')) " +
                @"ORDER BY System.Search.Rank DESC";

            commandText = PrepareCommand(commandText,
                                         "@Path", this.dirInfo.FullName,
                                         "@Name", searchString,
                                         "@Content", searchString);

            Dictionary <string, string> foundItems = new Dictionary <string, string>();

            try
            {
                // Sending SQL request to Windows Search. To get search results file system indexing must be enabled.
                // To find how to enable indexing follow this link: http://windows.microsoft.com/en-us/windows/improve-windows-searches-using-index-faq
                using (OleDbConnection connection = new OleDbConnection(windowsSearchProvider))
                    using (OleDbCommand command = new OleDbCommand(commandText, connection))
                    {
                        connection.Open();
                        using (OleDbDataReader reader = command.ExecuteReader())
                        {
                            while (await reader.ReadAsync())
                            {
                                string snippet = string.Empty;
                                if (includeSnippet)
                                {
                                    snippet = reader.GetValue(1) != DBNull.Value ? reader.GetString(1) : null;
                                }
                                foundItems.Add(reader.GetString(0), snippet);
                            }
                        }
                    }
            }
            catch (OleDbException ex) // explaining OleDbException
            {
                context.Logger.LogError(ex.Message, ex);
                switch (ex.ErrorCode)
                {
                case -2147217900: throw new DavException("Illegal symbols in search phrase.", DavStatus.CONFLICT);

                default:          throw new DavException("Unknown error.", DavStatus.INTERNAL_ERROR);
                }
            }

            IList <IHierarchyItemAsync> subtreeItems = new List <IHierarchyItemAsync>();

            foreach (string path in foundItems.Keys)
            {
                IHierarchyItemAsync item = await context.GetHierarchyItemAsync(GetRelativePath(path));

                if (includeSnippet && item is DavFile)
                {
                    (item as DavFile).Snippet = HighlightKeywords(searchString.Trim('%'), foundItems[path]);
                }

                subtreeItems.Add(item);
            }
            return(subtreeItems);
        }
示例#25
0
        /// <summary>
        /// Searches files and folders in current folder using search phrase, offset, nResults and options.
        /// </summary>
        /// <param name="searchString">A phrase to search.</param>
        /// <param name="options">Search options.</param>
        /// <param name="propNames">
        /// List of properties to retrieve with each item returned by this method. They will be requested by the
        /// Engine in <see cref="IHierarchyItemAsync.GetPropertiesAsync(IList{PropertyName}, bool)"/> call.
        /// </param>
        /// <param name="offset">The number of children to skip before returning the remaining items. Start listing from from next item.</param>
        /// <param name="nResults">The number of items to return.</param>
        /// <returns>List of <see cref="IHierarchyItemAsync"/> satisfying search request.</returns>1
        /// <returns>Items satisfying search request and a total number.</returns>
        public async Task <PageResults> SearchAsync(string searchString, SearchOptions options, List <PropertyName> propNames, long?offset, long?nResults)
        {
            bool includeSnippet = propNames.Any(s => s.Name == snippetProperty);

            // search both in file name and content
            string commandText =
                @"SELECT System.ItemPathDisplay" + (includeSnippet ? " ,System.Search.AutoSummary" : string.Empty) + " FROM SystemIndex " +
                @"WHERE scope ='file:@Path' AND (System.ItemNameDisplay LIKE '@Name' OR FREETEXT('""@Content""')) " +
                @"ORDER BY System.Search.Rank DESC";

            commandText = PrepareCommand(commandText,
                                         "@Path", this.dirInfo.FullName,
                                         "@Name", searchString,
                                         "@Content", searchString);

            Dictionary <string, string> foundItems = new Dictionary <string, string>();

            try
            {
                // Sending SQL request to Windows Search. To get search results file system indexing must be enabled.
                // To find how to enable indexing follow this link: http://windows.microsoft.com/en-us/windows/improve-windows-searches-using-index-faq
                await using (OleDbConnection connection = new OleDbConnection(context.Config.WindowsSearchProvider))
                    await using (OleDbCommand command = new OleDbCommand(commandText, connection))
                    {
                        connection.Open();
                        await using (OleDbDataReader reader = command.ExecuteReader())
                        {
                            while (await reader.ReadAsync())
                            {
                                string snippet = string.Empty;
                                if (includeSnippet)
                                {
                                    snippet = reader.GetValue(1) != DBNull.Value ? reader.GetString(1) : null;
                                    // XML does not support control characters or permanently undefined Unicode characters. Removing them from snippet. https://www.w3.org/TR/xml/#charsets
                                    if (!string.IsNullOrEmpty(snippet) && invalidXmlCharsPattern.IsMatch(snippet))
                                    {
                                        snippet = invalidXmlCharsPattern.Replace(snippet, String.Empty);
                                    }
                                }
                                foundItems.Add(reader.GetString(0), snippet);
                            }
                        }
                    }
            }
            catch (OleDbException ex) // explaining OleDbException
            {
                context.Logger.LogError(ex.Message, ex);
                switch (ex.ErrorCode)
                {
                case -2147217900: throw new DavException("Illegal symbols in search phrase.", DavStatus.CONFLICT);

                default: throw new DavException("Unknown error.", DavStatus.INTERNAL_ERROR);
                }
            }

            IList <IHierarchyItemAsync> subtreeItems = new List <IHierarchyItemAsync>();

            foreach (string path in foundItems.Keys)
            {
                IHierarchyItemAsync item = await context.GetHierarchyItemAsync(GetRelativePath(path)) as IHierarchyItemAsync;

                if (item == null)
                {
                    continue;
                }

                if (includeSnippet && item is DavFile)
                {
                    (item as DavFile).Snippet = HighlightKeywords(searchString.Trim('%'), foundItems[path]);
                }

                subtreeItems.Add(item);
            }

            return(new PageResults(offset.HasValue && nResults.HasValue ? subtreeItems.Skip((int)offset.Value).Take((int)nResults.Value) : subtreeItems, subtreeItems.Count));
        }