private async Task <Engines.CollectionActionResult> LocalExecuteAsync( ITargetActions <CollectionTarget, DocumentTarget, MissingTarget> handler, Uri sourceUrl, SelectionResult sourceSelectionResult, FileSystemTarget targetInfo, DepthHeader depth, bool overwrite, CancellationToken cancellationToken) { Debug.Assert(sourceSelectionResult.Collection != null, "sourceSelectionResult.Collection != null"); var engine = new RecursiveExecutionEngine <CollectionTarget, DocumentTarget, MissingTarget>( handler, overwrite, Logger); CollectionTarget parentCollection; ITarget targetItem; if (targetInfo.Collection != null) { var collTarget = targetInfo.NewCollectionTarget(); parentCollection = collTarget.Parent; targetItem = collTarget; } else if (targetInfo.Document != null) { var docTarget = targetInfo.NewDocumentTarget(); parentCollection = docTarget.Parent; targetItem = docTarget; } else { var missingTarget = targetInfo.NewMissingTarget(); parentCollection = missingTarget.Parent; targetItem = missingTarget; } Debug.Assert(parentCollection != null, "Cannt copy or move the root collection."); if (parentCollection == null) { throw new InvalidOperationException("Cannt copy or move the root collection."); } return(await ExecuteAsync( engine, sourceUrl, sourceSelectionResult, parentCollection, targetItem, depth, cancellationToken) ); }
/// <summary> /// Executes the COPY or MOVE recursively /// </summary> /// <param name="sourcePath">The source path</param> /// <param name="destination">The destination URI</param> /// <param name="depth">The depth</param> /// <param name="overwrite">Can the destination be overwritten?</param> /// <param name="mode">The COPY mode to use</param> /// <param name="isMove">Is this a move operation?</param> /// <param name="cancellationToken">The cancellcation token</param> /// <returns>The result of the operation</returns> protected async Task <IWebDavResult> ExecuteAsync( string sourcePath, Uri destination, DepthHeader depth, bool overwrite, RecursiveProcessingMode mode, bool isMove, CancellationToken cancellationToken) { var sourceSelectionResult = await _rootFileSystem.SelectAsync(sourcePath, cancellationToken); if (sourceSelectionResult.IsMissing) { if (WebDavContext.RequestHeaders.IfNoneMatch != null) { throw new WebDavException(WebDavStatusCode.PreconditionFailed); } throw new WebDavException(WebDavStatusCode.NotFound); } await WebDavContext.RequestHeaders .ValidateAsync(sourceSelectionResult.TargetEntry, cancellationToken); IWebDavResult result; IImplicitLock sourceTempLock; var lockManager = _rootFileSystem.LockManager; if (isMove) { var sourceLockRequirements = new Lock( sourceSelectionResult.TargetEntry.Path, WebDavContext.PublicRelativeRequestUrl, depth != DepthHeader.Zero, new XElement(WebDavXml.Dav + "owner", WebDavContext.User.Identity.Name), LockAccessType.Write, LockShareMode.Shared, TimeoutHeader.Infinite); sourceTempLock = lockManager == null ? new ImplicitLock(true) : await lockManager.LockImplicitAsync( _rootFileSystem, WebDavContext.RequestHeaders.If?.Lists, sourceLockRequirements, cancellationToken) ; if (!sourceTempLock.IsSuccessful) { return(sourceTempLock.CreateErrorResponse()); } } else { sourceTempLock = new ImplicitLock(true); } try { var sourceUrl = WebDavContext.PublicAbsoluteRequestUrl; var destinationUrl = new Uri(sourceUrl, destination); // Ignore different schemes if (!WebDavContext.PublicControllerUrl.IsBaseOf(destinationUrl) || mode == RecursiveProcessingMode.PreferCrossServer) { if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace("Using cross-server mode"); } if (Logger.IsEnabled(LogLevel.Debug)) { Logger.LogDebug($"{WebDavContext.PublicControllerUrl} is not a base of {destinationUrl}"); } using (var remoteHandler = await CreateRemoteTargetActionsAsync( destinationUrl, cancellationToken) ) { if (remoteHandler == null) { throw new WebDavException( WebDavStatusCode.BadGateway, "No remote handler for given client"); } // For error reporting sourceUrl = WebDavContext.PublicRootUrl.MakeRelativeUri(sourceUrl); var remoteTargetResult = await RemoteExecuteAsync( remoteHandler, sourceUrl, sourceSelectionResult, destinationUrl, depth, overwrite, cancellationToken); result = remoteTargetResult.Evaluate(WebDavContext); } } else { // Copy or move from one known file system to another var destinationPath = WebDavContext.PublicControllerUrl.MakeRelativeUri(destinationUrl).ToString(); // For error reporting sourceUrl = WebDavContext.PublicRootUrl.MakeRelativeUri(sourceUrl); destinationUrl = WebDavContext.PublicRootUrl.MakeRelativeUri(destinationUrl); var destinationSelectionResult = await _rootFileSystem.SelectAsync(destinationPath, cancellationToken); if (destinationSelectionResult.IsMissing && destinationSelectionResult.MissingNames.Count != 1) { Logger.LogDebug( $"{destinationUrl}: The target is missing with the following path parts: {string.Join(", ", destinationSelectionResult.MissingNames)}"); throw new WebDavException(WebDavStatusCode.Conflict); } var destLockRequirements = new Lock( new Uri(destinationPath, UriKind.Relative), destinationUrl, isMove || depth != DepthHeader.Zero, new XElement(WebDavXml.Dav + "owner", WebDavContext.User.Identity.Name), LockAccessType.Write, LockShareMode.Shared, TimeoutHeader.Infinite); var destTempLock = lockManager == null ? new ImplicitLock(true) : await lockManager.LockImplicitAsync( _rootFileSystem, WebDavContext.RequestHeaders.If?.Lists, destLockRequirements, cancellationToken) ; if (!destTempLock.IsSuccessful) { return(destTempLock.CreateErrorResponse()); } try { var isSameFileSystem = ReferenceEquals( sourceSelectionResult.TargetFileSystem, destinationSelectionResult.TargetFileSystem); var localMode = isSameFileSystem && mode == RecursiveProcessingMode.PreferFastest ? RecursiveProcessingMode.PreferFastest : RecursiveProcessingMode.PreferCrossFileSystem; var handler = CreateLocalTargetActions(localMode); var targetInfo = FileSystemTarget.FromSelectionResult( destinationSelectionResult, destinationUrl, handler); var targetResult = await LocalExecuteAsync( handler, sourceUrl, sourceSelectionResult, targetInfo, depth, overwrite, cancellationToken); result = targetResult.Evaluate(WebDavContext); } finally { await destTempLock.DisposeAsync(cancellationToken); } } } finally { await sourceTempLock.DisposeAsync(cancellationToken); } if (isMove && lockManager != null) { var locksToRemove = await lockManager .GetAffectedLocksAsync(sourcePath, true, false, cancellationToken) ; foreach (var activeLock in locksToRemove) { await lockManager.ReleaseAsync( activeLock.Path, new Uri(activeLock.StateToken), cancellationToken) ; } } return(result); }