/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var path = command.Argument; var currentPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchFileAsync(currentPath, path, cancellationToken); IUnixFileSystemEntry foundEntry = fileInfo?.Entry; if (foundEntry == null) { var parts = path.Split(new[] { ' ' }, 2); if (parts.Length != 2) return new FtpResponse(550, "File not found."); DateTimeOffset modificationTime; if (!parts[0].TryParseTimestamp("UTC", out modificationTime)) return new FtpResponse(550, "File not found."); path = parts[1]; currentPath = Data.Path.Clone(); fileInfo = await Data.FileSystem.SearchFileAsync(currentPath, path, cancellationToken); if (fileInfo?.Entry == null) return new FtpResponse(550, "File not found."); foundEntry = await Data.FileSystem.SetMacTimeAsync(fileInfo.Entry, modificationTime, null, null, cancellationToken); } return new FtpResponse(213, $"{foundEntry.LastWriteTime?.ToUniversalTime():yyyyMMddHHmmss.fff}"); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var directoryName = command.Argument; var currentPath = Data.Path.Clone(); var dirInfo = await Data.FileSystem.SearchDirectoryAsync(currentPath, directoryName, cancellationToken); if (dirInfo == null) return new FtpResponse(550, "Not a valid directory."); if (dirInfo.FileName == null) return new FtpResponse(550, "ROOT folder not allowed."); if (dirInfo.Entry != null) { await Connection.WriteAsync($"521-\"{currentPath.GetFullPath(dirInfo.FileName)}\" directory already exists", cancellationToken); return new FtpResponse(521, "Taking no action."); } try { var targetDirectory = currentPath.Count == 0 ? Data.FileSystem.Root : currentPath.Peek(); var newDirectory = await Data.FileSystem.CreateDirectoryAsync(targetDirectory, dirInfo.FileName, cancellationToken); return new FtpResponse(257, $"\"{currentPath.GetFullPath(newDirectory.Name)}\" created."); } catch (IOException) { return new FtpResponse(550, "Bad pathname syntax."); } }
private async Task<FtpResponse> ProcessMlstAsync(FtpCommand command, CancellationToken cancellationToken) { var argument = command.Argument; var path = Data.Path.Clone(); IUnixFileSystemEntry targetEntry; if (string.IsNullOrEmpty(argument)) { targetEntry = path.Count == 0 ? Data.FileSystem.Root : path.Peek(); } else { var foundEntry = await Data.FileSystem.SearchEntryAsync(path, argument, cancellationToken); if (foundEntry?.Entry == null) return new FtpResponse(550, "File system entry not found."); targetEntry = foundEntry.Entry; } await Connection.WriteAsync($"250- {targetEntry.Name}", cancellationToken); var entries = new List<IUnixFileSystemEntry>() { targetEntry, }; var enumerator = new DirectoryListingEnumerator(entries, Data.FileSystem, path, false); var formatter = new FactsListFormatter(Data.User, enumerator, Data.ActiveMlstFacts, true); while (enumerator.MoveNext()) { var name = enumerator.Name; var entry = enumerator.Entry; var line = formatter.Format(entry, name); await Connection.WriteAsync($" {line}", cancellationToken); } return new FtpResponse(250, "End"); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var restartPosition = Data.RestartPosition; Data.RestartPosition = null; if (!Data.TransferMode.IsBinary && Data.TransferMode.FileType != FtpFileType.Ascii) throw new NotSupportedException(); var fileName = command.Argument; var currentPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchFileAsync(currentPath, fileName, cancellationToken); if (fileInfo?.Entry == null) return new FtpResponse(550, "File doesn't exist."); using (var input = await Data.FileSystem.OpenReadAsync(fileInfo.Entry, restartPosition ?? 0, cancellationToken)) { await Connection.WriteAsync(new FtpResponse(150, "Opening connection for data transfer."), cancellationToken); using (var responseSocket = await Connection.CreateResponseSocket()) { responseSocket.WriteStream.WriteTimeout = 10000; using (var stream = await Connection.CreateEncryptedStream(responseSocket.WriteStream)) { var buffer = new byte[BufferSize]; int receivedBytes; while ((receivedBytes = await input.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) != 0) { await stream.WriteAsync(buffer, 0, receivedBytes, cancellationToken); } } } } return new FtpResponse(226, "File download succeeded."); }
/// <inheritdoc/> public override Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var transferMode = FtpTransferMode.Parse(command.Argument); FtpResponse response; if (transferMode.FileType == FtpFileType.Ascii) { response = new FtpResponse(200, "ASCII transfer mode active."); } else if (transferMode.IsBinary) { response = new FtpResponse(200, "Binary transfer mode active."); } else { response = new FtpResponse(504, $"Mode {command.Argument} not supported."); } if (response.Code == 200) { Connection.Data.TransferMode = transferMode; } return Task.FromResult(response); }
/// <inheritdoc/> public override Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (Data.CurrentDirectory.IsRoot) return Task.FromResult(new FtpResponse(550, "Not a valid directory.")); Data.Path.Pop(); return Task.FromResult(new FtpResponse(200, "Command okay.")); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (Data.RenameFrom == null) return new FtpResponse(503, "RNTO must be preceded by a RNFR."); if (Data.RenameFrom.Entry == null) return new FtpResponse(550, "Item specified for RNFR doesn't exist."); var fileName = command.Argument; var tempPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchEntryAsync(tempPath, fileName, cancellationToken); if (fileInfo == null) return new FtpResponse(550, "Directory doesn't exist."); if (fileInfo.Entry != null) { var fullName = tempPath.GetFullPath(fileInfo.FileName); return new FtpResponse(553, $"Target name already exists ({fullName})."); } var targetDir = fileInfo.Directory; await Data.FileSystem.MoveAsync(Data.RenameFrom.Directory, Data.RenameFrom.Entry, targetDir, fileInfo.FileName, cancellationToken); Data.RenameFrom = null; return new FtpResponse(250, "Renamed file successfully."); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(command.Argument)) return new FtpResponse(501, "No file name."); var parts = new List<string>(); string part; var remaining = command.Argument.ChompFromEnd(out part); if (IsSupportedTimeZome(part)) { // 5 part format // SITE <sp> UTIME <sp> filename <sp> datetime1 <sp> datetime2 <sp> datetime3 <sp> UTC parts.Add(part); for (int i = 0; i != 3; ++i) { remaining = remaining.ChompFromEnd(out part); parts.Add(remaining); } parts.Add(remaining); parts.Reverse(); return await SetTimestamp5(parts, cancellationToken); } parts.AddRange(command.Argument.Split(new[] { ' ' }, 2)); while (parts.Count != 2) parts.Add(string.Empty); return await SetTimestamp2(parts, cancellationToken); }
/// <inheritdoc/> public override Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var path = Connection.Data.Path.GetFullPath(); if (path.EndsWith("/") && path.Length > 1) path = path.Substring(0, path.Length - 1); return Task.FromResult(new FtpResponse(257, $"\"{path}\"")); }
/// <inheritdoc/> public override Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var listDir = string.Equals(command.Name, "MLSD", StringComparison.OrdinalIgnoreCase); if (listDir) return ProcessMlsdAsync(command, cancellationToken); return ProcessMlstAsync(command, cancellationToken); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (Data.PassiveSocketClient != null) { await Data.PassiveSocketClient.DisconnectAsync(); Data.PassiveSocketClient.Dispose(); Data.PassiveSocketClient = null; } if (Data.TransferTypeCommandUsed != null && !string.Equals(command.Name, Data.TransferTypeCommandUsed, StringComparison.OrdinalIgnoreCase)) return new FtpResponse(500, $"Cannot use {command.Name} when {Data.TransferTypeCommandUsed} was used before."); int port; var isEpsv = string.Equals(command.Name, "EPSV", StringComparison.OrdinalIgnoreCase); if (isEpsv) { if (string.IsNullOrEmpty(command.Argument) || string.Equals(command.Argument, "ALL", StringComparison.OrdinalIgnoreCase)) { port = 0; } else { port = Convert.ToInt32(command.Argument, 10); } } else { port = 0; } Data.TransferTypeCommandUsed = command.Name; var sem = new SemaphoreSlim(0, 1); var listener = new TcpSocketListener(); listener.ConnectionReceived += (sender, args) => { Data.PassiveSocketClient = args.SocketClient; sem.Release(); }; await listener.StartListeningAsync(port); var localPort = listener.LocalPort; if (isEpsv || Server.ServerAddress.Contains(":")) { var listenerAddress = new Address(localPort); await Connection.WriteAsync(new FtpResponse(229, $"Entering Extended Passive Mode ({listenerAddress})."), cancellationToken); } else { var listenerAddress = new Address(Server.ServerAddress, localPort); await Connection.WriteAsync(new FtpResponse(227, $"Entering Passive Mode ({listenerAddress})."), cancellationToken); } await sem.WaitAsync(TimeSpan.FromSeconds(5), cancellationToken); await listener.StopListeningAsync(); listener.Dispose(); return null; }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var parts = command.Argument.Split(new[] { ' ' }, 2); if (parts.Length != 2) return new FtpResponse(551, "Facts or file name missing."); var factInfos = new Dictionary<string, string>(); foreach (var factEntry in parts[0].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { var keyValue = factEntry.Split(new[] { '=' }, 2); if (keyValue.Length != 2) return new FtpResponse(551, $"Invalid format for fact {factEntry}."); factInfos.Add(keyValue[0], keyValue[1]); } var path = parts[1]; var currentPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchFileAsync(currentPath, path, cancellationToken); if (fileInfo?.Entry == null) return new FtpResponse(550, "File not found."); var facts = new List<IFact>(); foreach (var factInfo in factInfos) { CreateFactDelegate createFact; if (!_knownFacts.TryGetValue(factInfo.Key, out createFact)) return new FtpResponse(551, $"Unsupported fact {factInfo.Key}."); var fact = createFact(factInfo.Value); facts.Add(fact); } DateTimeOffset? modificationTime = null, creationTime = null; foreach (var fact in facts) { switch (fact.Name.ToLowerInvariant()) { case "modify": modificationTime = ((ModifyFact)fact).Timestamp; break; case "create": creationTime = ((CreateFact)fact).Timestamp; break; default: return new FtpResponse(551, $"Unsupported fact {fact.Name}."); } } if (creationTime != null || modificationTime != null) await Data.FileSystem.SetMacTimeAsync(fileInfo.Entry, modificationTime, null, creationTime, cancellationToken); var fullName = currentPath.GetFullPath() + fileInfo.FileName; var responseText = new StringBuilder(); foreach (var fact in facts) responseText.AppendFormat("{0}={1};", fact.Name, fact.Value); responseText.AppendFormat(" {0}", fullName); return new FtpResponse(213, responseText.ToString()); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var argument = FtpCommand.Parse(command.Argument); FtpCommandHandlerExtension extension; if (!Extensions.TryGetValue(argument.Name, out extension)) return new FtpResponse(500, "Syntax error, command unrecognized."); return await extension.Process(argument, cancellationToken); }
/// <inheritdoc/> public override Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (Data.BackgroundCommandHandler.Cancel()) { return Task.FromResult(new FtpResponse(226, "File transfer aborting.")); } return Task.FromResult(new FtpResponse(226, "Cannot abort - no active transfer.")); }
/// <inheritdoc/> public override Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(command.Argument)) return Task.FromResult(new FtpResponse(501, "Protection buffer size not specified.")); var bufferSize = Convert.ToInt32(command.Argument, 10); if (bufferSize != 0) return Task.FromResult(new FtpResponse(501, "A protection buffer size other than 0 is not supported.")); return Task.FromResult(new FtpResponse(200, $"Protection buffer size set to {bufferSize}.")); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var fileName = command.Argument; var tempPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchFileAsync(tempPath, fileName, cancellationToken); if (fileInfo?.Entry == null) return new FtpResponse(550, $"File not found ({fileName})."); return new FtpResponse(213, $"{fileInfo.Entry.Size}"); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var path = command.Argument; var currentPath = Data.Path.Clone(); var subDir = await Data.FileSystem.GetDirectoryAsync(currentPath, path, cancellationToken); if (subDir == null) return new FtpResponse(550, "Not a valid directory."); Data.Path = currentPath; return new FtpResponse(200, $"Directory changed to {currentPath.GetFullPath()}"); }
/// <inheritdoc/> public override Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { return Task.FromResult(new FtpResponse(221, "Service closing control connection.") { AfterWriteAction = () => { Connection.SocketStream.Flush(); Connection.Close(); } }); }
/// <inheritdoc/> public override Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(command.Argument)) return Task.FromResult(new FtpResponse(501, "Syntax error in parameters or arguments.")); var argument = FtpCommand.Parse(command.Argument); FtpCommandHandlerExtension extension; if (!Extensions.TryGetValue(argument.Name, out extension)) return Task.FromResult(new FtpResponse(500, "Syntax error, command unrecognized.")); return extension.Process(argument, cancellationToken); }
/// <inheritdoc/> public override Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var helpArg = command.Argument; if (string.IsNullOrEmpty(helpArg)) helpArg = "SITE"; switch (helpArg) { case "SITE": return ShowHelpSite(cancellationToken); default: return Task.FromResult(new FtpResponse(501, "Syntax error in parameters or arguments.")); } }
/// <inheritdoc/> public override Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var arg = command.Argument; if (string.IsNullOrEmpty(arg)) arg = "TLS"; switch (arg.ToUpperInvariant()) { case "TLS": return ElevateToTls(cancellationToken); default: return Task.FromResult(new FtpResponse(504, $"Authentication mode {arg} not supported.")); } }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var fileName = command.Argument; var tempPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchEntryAsync(tempPath, fileName, cancellationToken); if (fileInfo == null) return new FtpResponse(550, "Directory doesn't exist."); if (fileInfo.Entry == null) return new FtpResponse(550, "Source entry doesn't exist."); Data.RenameFrom = fileInfo; var fullName = tempPath.GetFullPath(fileInfo.FileName); return new FtpResponse(350, $"Rename started ({fullName})."); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { string mode = (string.IsNullOrEmpty(command.Argument) ? "data" : command.Argument).ToLowerInvariant(); switch (mode) { case "data": return await SendBlstWithDataConnection(cancellationToken); case "control": case "direct": return await SendBlstDirectly(cancellationToken); } return new FtpResponse(501, $"Mode {mode} not supported."); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var restartPosition = Data.RestartPosition; Data.RestartPosition = null; if (!Data.TransferMode.IsBinary && Data.TransferMode.FileType != FtpFileType.Ascii) throw new NotSupportedException(); var fileName = command.Argument; if (string.IsNullOrEmpty(fileName)) return new FtpResponse(501, "No file name specified"); var currentPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchFileAsync(currentPath, fileName, cancellationToken); if (fileInfo == null) return new FtpResponse(550, "Not a valid directory."); if (fileInfo.FileName == null) return new FtpResponse(553, "File name not allowed."); var doReplace = restartPosition.GetValueOrDefault() == 0 && fileInfo.Entry != null; await Connection.WriteAsync(new FtpResponse(150, "Opening connection for data transfer."), cancellationToken); using (var responseSocket = await Connection.CreateResponseSocket()) { responseSocket.ReadStream.ReadTimeout = 10000; using (var stream = await Connection.CreateEncryptedStream(responseSocket.ReadStream)) { IBackgroundTransfer backgroundTransfer; if (doReplace) { backgroundTransfer = await Data.FileSystem.ReplaceAsync(fileInfo.Entry, stream, cancellationToken); } else if (restartPosition.GetValueOrDefault() == 0 || fileInfo.Entry == null) { backgroundTransfer = await Data.FileSystem.CreateAsync(fileInfo.Directory, fileInfo.FileName, stream, cancellationToken); } else { backgroundTransfer = await Data.FileSystem.AppendAsync(fileInfo.Entry, restartPosition ?? 0, stream, cancellationToken); } if (backgroundTransfer != null) Server.EnqueueBackgroundTransfer(backgroundTransfer, Connection); } } return new FtpResponse(226, "Uploaded file successfully."); }
/// <inheritdoc /> public override Task <IFtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var facts = command.Argument.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); var factsFeature = Connection.Features.Get <IMlstFactsFeature>(); factsFeature.ActiveMlstFacts.Clear(); foreach (var fact in facts) { if (!MlstCommandHandler.KnownFacts.Contains(fact)) { return(Task.FromResult <IFtpResponse>(new FtpResponse(501, T("Syntax error in parameters or arguments.")))); } factsFeature.ActiveMlstFacts.Add(fact); } return(Task.FromResult <IFtpResponse>(new FtpResponse(200, T("Command okay.")))); }
/// <inheritdoc/> public override async Task <IFtpResponse?> Process(FtpCommand command, CancellationToken cancellationToken) { var mode = (string.IsNullOrEmpty(command.Argument) ? "data" : command.Argument).ToLowerInvariant(); switch (mode) { case "data": return(await SendBlstWithDataConnection(cancellationToken).ConfigureAwait(false)); case "control": case "direct": return(await SendBlstDirectly().ConfigureAwait(false)); default: return(new FtpResponse(501, T("Mode {0} not supported.", mode))); } }
/// <inheritdoc/> public override Task <IFtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(command.Argument)) { return(Task.FromResult <IFtpResponse>(new FtpResponse(501, T("Data channel protection level not specified.")))); } var loginStateMachine = Connection.ConnectionServices.GetRequiredService <IFtpLoginStateMachine>(); var authMechanism = loginStateMachine.SelectedAuthenticationMechanism; if (authMechanism == null) { return(Task.FromResult <IFtpResponse>(new FtpResponse(503, T("No authentication mechanism selected.")))); } return(authMechanism.HandleProtAsync(command.Argument.Trim(), cancellationToken)); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var password = command.Argument; var validationResult = Server.MembershipProvider.ValidateUser(Connection.Data.User.Name, password); if (validationResult.IsSuccess) { var isAnonymous = validationResult.Status == MemberValidationStatus.Anonymous; var userId = isAnonymous ? password : Connection.Data.User.Name; Connection.Data.User = validationResult.User; Connection.Data.IsLoggedIn = true; Connection.Data.IsAnonymous = isAnonymous; Connection.Data.FileSystem = await Server.FileSystemClassFactory.Create(userId, isAnonymous); Connection.Data.Path = new Stack<IUnixDirectoryEntry>(); return new FtpResponse(230, "Password ok, FTP server ready"); } return new FtpResponse(530, "Username or password incorrect"); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var path = command.Argument; var currentPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchFileAsync(currentPath, path, cancellationToken); if (fileInfo?.Entry == null) return new FtpResponse(550, "File does not exist."); try { await Data.FileSystem.UnlinkAsync(fileInfo.Entry, cancellationToken); return new FtpResponse(250, "File deleted successfully."); } catch (Exception) { return new FtpResponse(550, "Couldn't delete file."); } }
private Task<FtpResponse> ProcessOptionUtf8(FtpCommand command, CancellationToken cancellationToken) { switch (command.Argument.ToUpperInvariant()) { case "ON": Connection.Encoding = Encoding.UTF8; return Task.FromResult(new FtpResponse(200, "Command okay.")); case "": Connection.Data.NlstEncoding = null; return Task.FromResult(new FtpResponse(200, "Command okay.")); case "NLST": Connection.Data.NlstEncoding = Encoding.UTF8; return Task.FromResult(new FtpResponse(200, "Command okay.")); default: return Task.FromResult(new FtpResponse(501, "Syntax error in parameters or arguments.")); } }
/// <inheritdoc/> public override Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(command.Argument)) return Task.FromResult(new FtpResponse(501, "Data channel protection level not specified.")); switch (command.Argument.ToUpperInvariant()) { case "C": Data.CreateEncryptedStream = null; break; case "P": Data.CreateEncryptedStream = CreateSslStream; break; default: return Task.FromResult(new FtpResponse(501, "A data channel protection level other than C, or P is not supported.")); } return Task.FromResult(new FtpResponse(200, $"Data channel protection level set to {command.Argument}.")); }
/// <inheritdoc/> public override async Task <IFtpResponse?> Process(FtpCommand command, CancellationToken cancellationToken) { await FtpContext.ServerCommandWriter .WriteAsync( new SendResponseServerCommand(new FtpResponse(150, T("Opening data connection."))), cancellationToken) .ConfigureAwait(false); await FtpContext.ServerCommandWriter .WriteAsync( new DataConnectionServerCommand( (dataConnection, ct) => ExecuteSend(dataConnection, command, ct), command), cancellationToken) .ConfigureAwait(false); return(null); }
/// <inheritdoc/> public override Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var helpArg = command.Argument; if (string.IsNullOrEmpty(helpArg)) { helpArg = "SITE"; } switch (helpArg) { case "SITE": return(ShowHelpSite(cancellationToken)); default: return(Task.FromResult(new FtpResponse(501, "Syntax error in parameters or arguments."))); } }
/// <inheritdoc/> public override Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var arg = command.Argument; if (string.IsNullOrEmpty(arg)) { arg = "TLS"; } switch (arg.ToUpperInvariant()) { case "TLS": return(ElevateToTls(cancellationToken)); default: return(Task.FromResult(new FtpResponse(504, $"Authentication mode {arg} not supported."))); } }
/// <inheritdoc/> public override async Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(command.Argument)) { var taskStates = _backgroundTransferWorker.GetStates(); var statusMessage = new StringBuilder(); statusMessage.AppendFormat("Server functional, {0} open connections", _server.Statistics.ActiveConnections); if (taskStates.Count != 0) { statusMessage.AppendFormat(", {0} active background transfers", taskStates.Count); } return(new FtpResponse(211, statusMessage.ToString())); } var mask = command.Argument; if (!mask.EndsWith("*")) { mask += "*"; } var globOptions = new GlobOptions(); globOptions.Evaluation.CaseInsensitive = Data.FileSystem.FileSystemEntryComparer.Equals("a", "A"); var glob = Glob.Parse(mask, globOptions); var formatter = new LongListFormatter(); await Connection.WriteAsync($"211-STAT {command.Argument}", cancellationToken).ConfigureAwait(false); var entries = await Data.FileSystem.GetEntriesAsync(Data.CurrentDirectory, cancellationToken) .ConfigureAwait(false); foreach (var entry in entries.Where(x => glob.IsMatch(x.Name))) { var line = formatter.Format(entry, entry.Name); Connection.Log?.LogDebug(line); await Connection.WriteAsync($" {line}", cancellationToken).ConfigureAwait(false); } return(new FtpResponse(211, "STAT")); }
/// <inheritdoc/> public override async Task <IFtpResponse?> Process(FtpCommand command, CancellationToken cancellationToken) { var renameFrom = Connection.Features.Get <IRenameCommandFeature?>()?.RenameFrom; if (renameFrom == null) { return(new FtpResponse(503, T("RNTO must be preceded by a RNFR."))); } if (renameFrom.Entry == null) { return(new FtpResponse(550, T("Item specified for RNFR doesn't exist."))); } var fsFeature = Connection.Features.Get <IFileSystemFeature>(); var fileName = command.Argument; var tempPath = fsFeature.Path.Clone(); var fileInfo = await fsFeature.FileSystem.SearchEntryAsync(tempPath, fileName, cancellationToken).ConfigureAwait(false); if (fileInfo == null) { return(new FtpResponse(550, T("Directory doesn't exist."))); } if (fileInfo.FileName == null) { return(new FtpResponse(550, T("ROOT folder not allowed."))); } if (fileInfo.Entry != null) { var fullName = tempPath.GetFullPath(fileInfo.FileName); return(new FtpResponse(553, T("Target name already exists ({0}).", fullName))); } var targetDir = fileInfo.Directory; await fsFeature.FileSystem.MoveAsync(renameFrom.Directory, renameFrom.Entry, targetDir, fileInfo.FileName, cancellationToken).ConfigureAwait(false); Connection.Features.Set <IRenameCommandFeature?>(null); return(new FtpResponse(250, T("Renamed file successfully."))); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var path = command.Argument; var currentPath = Data.Path.Clone(); var subDir = await Data.FileSystem.GetDirectoryAsync(currentPath, path, cancellationToken); if (subDir == null) return new FtpResponse(550, "Not a valid directory."); try { if (Data.Path.IsChildOfOrSameAs(currentPath, Data.FileSystem)) return new FtpResponse(550, "Not a valid directory (is same or parent of current directory)."); await Data.FileSystem.UnlinkAsync(subDir, cancellationToken); return new FtpResponse(250, "Directory removed."); } catch (Exception) { return new FtpResponse(550, "Couldn't remove directory (locked?)."); } }
private async Task <IFtpResponse?> SendBlstWithDataConnection( FtpCommand command, CancellationToken cancellationToken) { await FtpContext.ServerCommandWriter.WriteAsync( new SendResponseServerCommand(new FtpResponse(150, T("Opening data connection."))), cancellationToken) .ConfigureAwait(false); await FtpContext.ServerCommandWriter .WriteAsync( new DataConnectionServerCommand( ExecuteSend, command), cancellationToken) .ConfigureAwait(false); return(null); }
/// <inheritdoc/> public override Task <IFtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(command.Argument)) { return(Task.FromResult <IFtpResponse>(new FtpResponse(501, T("Protection buffer size not specified.")))); } var loginStateMachine = Connection.ConnectionServices.GetRequiredService <IFtpLoginStateMachine>(); var authMechanism = loginStateMachine.SelectedAuthenticationMechanism; if (authMechanism == null) { return(Task.FromResult <IFtpResponse>(new FtpResponse(503, T("No authentication mechanism selected.")))); } var size = Convert.ToInt32(command.Argument, 10); return(authMechanism.HandlePbszAsync(size, cancellationToken)); }
private async Task <FtpResponse> ProcessMlsdAsync(FtpCommand command, CancellationToken cancellationToken) { var argument = command.Argument; var path = Data.Path.Clone(); IUnixDirectoryEntry dirEntry; if (string.IsNullOrEmpty(argument)) { dirEntry = path.Count == 0 ? Data.FileSystem.Root : path.Peek(); } else { var foundEntry = await Data.FileSystem.SearchEntryAsync(path, argument, cancellationToken).ConfigureAwait(false); if (foundEntry?.Entry == null) { return(new FtpResponse(550, "File system entry not found.")); } dirEntry = foundEntry.Entry as IUnixDirectoryEntry; if (dirEntry == null) { return(new FtpResponse(501, "Not a directory.")); } if (!dirEntry.IsRoot) { path.Push(dirEntry); } } await Connection.WriteAsync(new FtpResponse(150, "Opening data connection."), cancellationToken).ConfigureAwait(false); return(await Connection.SendResponseAsync( client => ExecuteSendAsync(client, path, dirEntry, cancellationToken), ex => { _logger?.LogError(ex, ex.Message); return new FtpResponse(425, "Can't open data connection."); }) .ConfigureAwait(false)); }
/// <summary> /// Creates a new <see cref="IFtpDataConnectionFeature"/> instance. /// </summary> /// <param name="ftpCommand">The FTP command that initiated the creation of the feature.</param> /// <param name="addressFamily">The address family for the address to be selected.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task returning the <see cref="IFtpDataConnectionFeature"/> instance.</returns> public async Task <IFtpDataConnectionFeature> CreateFeatureAsync( FtpCommand ftpCommand, AddressFamily?addressFamily, CancellationToken cancellationToken) { var connection = _connectionAccessor.FtpConnection; var listener = await _pasvListenerFactory.CreateTcpListenerAsync( connection, addressFamily, 0, cancellationToken).ConfigureAwait(false); return(new PassiveDataConnectionFeature( listener, _validators, ftpCommand, connection, listener.PasvEndPoint, _logger)); }
private Task <FtpResponse> ProcessOptionUtf8(FtpCommand command, CancellationToken cancellationToken) { switch (command.Argument.ToUpperInvariant()) { case "ON": Connection.Encoding = Encoding.UTF8; return(Task.FromResult(new FtpResponse(200, "Command okay."))); case "": Connection.Data.NlstEncoding = null; return(Task.FromResult(new FtpResponse(200, "Command okay."))); case "NLST": Connection.Data.NlstEncoding = Encoding.UTF8; return(Task.FromResult(new FtpResponse(200, "Command okay."))); default: return(Task.FromResult(new FtpResponse(501, "Syntax error in parameters or arguments."))); } }
/// <inheritdoc /> public override async Task <IFtpResponse?> Process(FtpCommand command, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(command.Argument)) { return(new FtpResponse(501, T("Syntax error in parameters or arguments."))); } var loginStateMachine = Connection.ConnectionServices.GetRequiredService <IFtpLoginStateMachine>(); if (loginStateMachine.Status != SecurityStatus.Unauthenticated && loginStateMachine.Status != SecurityStatus.Authenticated) { return(new FtpResponse(503, T("Bad sequence of commands"))); } var hostInfo = ParseHost(command.Argument); var hostSelector = Connection.ConnectionServices.GetRequiredService <IFtpHostSelector>(); return(await hostSelector.SelectHostAsync(hostInfo, cancellationToken).ConfigureAwait(false)); }
/// <inheritdoc/> public override async Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var restartPosition = Data.RestartPosition; Data.RestartPosition = null; if (!Data.TransferMode.IsBinary && Data.TransferMode.FileType != FtpFileType.Ascii) { throw new NotSupportedException(); } var fileName = command.Argument; var currentPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchFileAsync(currentPath, fileName, cancellationToken); if (fileInfo?.Entry == null) { return(new FtpResponse(550, "File doesn't exist.")); } using (var input = await Data.FileSystem.OpenReadAsync(fileInfo.Entry, restartPosition ?? 0, cancellationToken)) { await Connection.WriteAsync(new FtpResponse(150, "Opening connection for data transfer."), cancellationToken); using (var responseSocket = await Connection.CreateResponseSocket()) { responseSocket.WriteStream.WriteTimeout = 10000; using (var stream = await Connection.CreateEncryptedStream(responseSocket.WriteStream)) { var buffer = new byte[BufferSize]; int receivedBytes; while ((receivedBytes = await input.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) != 0) { await stream.WriteAsync(buffer, 0, receivedBytes, cancellationToken); } } } } return(new FtpResponse(226, "File download succeeded.")); }
private async Task <FtpResponse> ProcessMlstAsync(FtpCommand command, CancellationToken cancellationToken) { var argument = command.Argument; var path = Data.Path.Clone(); IUnixFileSystemEntry targetEntry; if (string.IsNullOrEmpty(argument)) { targetEntry = path.Count == 0 ? Data.FileSystem.Root : path.Peek(); } else { var foundEntry = await Data.FileSystem.SearchEntryAsync(path, argument, cancellationToken).ConfigureAwait(false); if (foundEntry?.Entry == null) { return(new FtpResponse(550, "File system entry not found.")); } targetEntry = foundEntry.Entry; } await Connection.WriteAsync($"250- {targetEntry.Name}", cancellationToken).ConfigureAwait(false); var entries = new List <IUnixFileSystemEntry>() { targetEntry, }; var enumerator = new DirectoryListingEnumerator(entries, Data.FileSystem, path, false); var formatter = new FactsListFormatter(Data.User, enumerator, Data.ActiveMlstFacts, true); while (enumerator.MoveNext()) { var name = enumerator.Name; var entry = enumerator.Entry; var line = formatter.Format(entry, name); await Connection.WriteAsync($" {line}", cancellationToken).ConfigureAwait(false); } return(new FtpResponse(250, "End")); }
/// <inheritdoc/> public override async Task <IFtpResponse?> Process(FtpCommand command, CancellationToken cancellationToken) { if (Connection.Features.Get <IFtpDataConnectionConfigurationFeature>()?.LimitToEpsv ?? false) { // EPSV ALL was sent from the client return(new FtpResponse( 500, $"Cannot use {command.Name} when EPSV ALL was used before.")); } try { var connectionFeature = Connection.Features.Get <IConnectionFeature>(); var address = Parse(command.Argument, connectionFeature.RemoteEndPoint); if (address == null) { return(new FtpResponse(501, T("Syntax error in parameters or arguments."))); } var feature = await _dataConnectionFeatureFactory.CreateFeatureAsync(command, address, _options.DataPort) .ConfigureAwait(false); var oldFeature = Connection.Features.Get <IFtpDataConnectionFeature>(); try { oldFeature.Dispose(); } catch { // Ignore dispose errors! } Connection.Features.Set(feature); } catch (NotSupportedException ex) { return(new FtpResponse(522, T("Extended port failure - {0}.", ex.Message))); } return(new FtpResponse(200, T("Command okay."))); }
/// <inheritdoc/> public override Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(command.Argument)) { return(Task.FromResult(new FtpResponse(501, "Data channel protection level not specified."))); } switch (command.Argument.ToUpperInvariant()) { case "C": Data.CreateEncryptedStream = null; break; case "P": Data.CreateEncryptedStream = CreateSslStream; break; default: return(Task.FromResult(new FtpResponse(501, "A data channel protection level other than C, or P is not supported."))); } return(Task.FromResult(new FtpResponse(200, $"Data channel protection level set to {command.Argument}."))); }
bool ReadCommand() { if (!ReceiveBuffer.Contains(new byte[] { 13, 10 })) { return(false); } string line = reader.ReadLine(); FtpCommand command = line.BeforeFirst(' ').ToUpperInvariant().Parse((FtpCommand)0); Trace.TraceInformation("{0}-> {1}", RemoteEndPoint, line); if (!commands.TryGetValue(command, out Action <string> action)) { SendAnswer("502 Command not implemented."); } else { action(line.AfterFirst(' ')); } return(true); }
/// <summary> /// Opens a filestream to the given filename /// </summary> /// <param name="fileName"></param> /// <param name="command"></param> /// <returns></returns> private async Task <Stream> OpenFileStreamAsync(string fileName, FtpCommand command) { EnsureLoggedIn(); Logger?.LogDebug($"[FtpClient] Opening filestream for {fileName}, {command}"); dataStream = await ConnectDataStreamAsync(); var retrResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope { FtpCommand = command, Data = fileName }); if ((retrResponse.FtpStatusCode != FtpStatusCode.DataAlreadyOpen) && (retrResponse.FtpStatusCode != FtpStatusCode.OpeningData) && (retrResponse.FtpStatusCode != FtpStatusCode.ClosingData)) { throw new FtpException(retrResponse.ResponseMessage); } return(dataStream); }
/// <inheritdoc/> public override async Task<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (_supportedFeatures == null) { var features = new List<string>(); features.AddRange(Connection.CommandHandlers.Values.SelectMany(x => x.GetSupportedFeatures()).Select(x => x.BuildInfo(Connection)).Distinct()); _supportedFeatures = features; } if (_supportedFeatures.Count == 0) { return new FtpResponse(211, "No extensions supported"); } await Connection.WriteAsync("211-Extensions supported:", cancellationToken); foreach (var supportedFeature in _supportedFeatures) { await Connection.WriteAsync($" {supportedFeature}", cancellationToken); } return new FtpResponse(211, "END"); }
/// <inheritdoc/> public override async Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var fileName = command.Argument; var tempPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchEntryAsync(tempPath, fileName, cancellationToken); if (fileInfo == null) { return(new FtpResponse(550, "Directory doesn't exist.")); } if (fileInfo.Entry == null) { return(new FtpResponse(550, "Source entry doesn't exist.")); } Data.RenameFrom = fileInfo; var fullName = tempPath.GetFullPath(fileInfo.FileName); return(new FtpResponse(350, $"Rename started ({fullName}).")); }
/// <inheritdoc/> public override async Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var path = command.Argument; var currentPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchFileAsync(currentPath, path, cancellationToken); if (fileInfo?.Entry == null) { return(new FtpResponse(550, "File does not exist.")); } try { await Data.FileSystem.UnlinkAsync(fileInfo.Entry, cancellationToken); return(new FtpResponse(250, "File deleted successfully.")); } catch (Exception) { return(new FtpResponse(550, "Couldn't delete file.")); } }
/// <inheritdoc/> public override async Task <IFtpResponse?> Process(FtpCommand command, CancellationToken cancellationToken) { var restartPosition = Connection.Features.Get <IRestCommandFeature?>()?.RestartPosition; Connection.Features.Set <IRestCommandFeature?>(null); var transferMode = Connection.Features.Get <ITransferConfigurationFeature>().TransferMode; if (!transferMode.IsBinary && transferMode.FileType != FtpFileType.Ascii) { throw new NotSupportedException(); } var fsFeature = Connection.Features.Get <IFileSystemFeature>(); var fileName = command.Argument; var currentPath = fsFeature.Path.Clone(); var fileInfo = await fsFeature.FileSystem.SearchFileAsync(currentPath, fileName, cancellationToken).ConfigureAwait(false); if (fileInfo?.Entry == null) { return(new FtpResponse(550, T("File doesn't exist."))); } using (var input = await fsFeature.FileSystem.OpenReadAsync(fileInfo.Entry, restartPosition ?? 0, cancellationToken).ConfigureAwait(false)) { await FtpContext.ServerCommandWriter .WriteAsync( new SendResponseServerCommand(new FtpResponse(150, T("Opening connection for data transfer."))), cancellationToken) .ConfigureAwait(false); // ReSharper disable once AccessToDisposedClosure return(await Connection.SendDataAsync( (dataConnection, ct) => ExecuteSendAsync(dataConnection, input, ct), _logger, cancellationToken) .ConfigureAwait(false)); } }
/// <inheritdoc /> public override async Task <IFtpResponse?> Process(FtpCommand command, CancellationToken cancellationToken) { var catalogLoader = Connection.ConnectionServices.GetRequiredService <IFtpCatalogLoader>(); var localizationFeature = Connection.Features.Get <ILocalizationFeature>(); if (string.IsNullOrWhiteSpace(command.Argument)) { localizationFeature.Language = catalogLoader.DefaultLanguage; localizationFeature.Catalog = catalogLoader.DefaultCatalog; } else { var match = _languagePattern.Match(command.Argument.Trim()); if (!match.Success) { return(new FtpResponse(501, T("Bad argument"))); } try { var language = new CultureInfo(match.Value); var catalog = await catalogLoader.LoadAsync(language, cancellationToken) .ConfigureAwait(false); if (catalog is null) { return(new FtpResponse(504, T("Unsupported parameter"))); } localizationFeature.Language = language; localizationFeature.Catalog = catalog; } catch (CultureNotFoundException) { return(new FtpResponse(504, T("Unsupported parameter"))); } } return(new FtpResponse(200, T("Command okay"))); }
/// <inheritdoc/> public override async Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var restartPosition = Data.RestartPosition; Data.RestartPosition = null; if (!Data.TransferMode.IsBinary && Data.TransferMode.FileType != FtpFileType.Ascii) { throw new NotSupportedException(); } var fileName = command.Argument; if (string.IsNullOrEmpty(fileName)) { return(new FtpResponse(501, "No file name specified")); } var currentPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchFileAsync(currentPath, fileName, cancellationToken).ConfigureAwait(false); if (fileInfo == null) { return(new FtpResponse(550, "Not a valid directory.")); } if (fileInfo.FileName == null) { return(new FtpResponse(553, "File name not allowed.")); } var doReplace = restartPosition.GetValueOrDefault() == 0 && fileInfo.Entry != null; await Connection.WriteAsync(new FtpResponse(150, "Opening connection for data transfer."), cancellationToken).ConfigureAwait(false); return(await Connection .SendResponseAsync( client => ExecuteSendAsync(client, doReplace, fileInfo, restartPosition, cancellationToken)) .ConfigureAwait(false)); }
/// <inheritdoc/> public override async Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (Connection.Data.User == null) { return(new FtpResponse(530, "No user name given")); } var password = command.Argument; foreach (var membershipProvider in _membershipProviders) { var validationResult = await membershipProvider.ValidateUserAsync(Connection.Data.User.Name, password) .ConfigureAwait(false); if (validationResult.IsSuccess) { var accountInformation = new DefaultAccountInformation( validationResult.User ?? throw new InvalidOperationException("The user property must be set if validation succeeded."), membershipProvider); Connection.Data.User = validationResult.User; #pragma warning disable 618 Connection.Data.IsAnonymous = validationResult.User is IAnonymousFtpUser; #pragma warning restore 618 Connection.Data.IsLoggedIn = true; Connection.Data.AuthenticatedBy = membershipProvider; Connection.Data.FileSystem = await _fileSystemClassFactory .Create(accountInformation) .ConfigureAwait(false); Connection.Data.Path = new Stack <IUnixDirectoryEntry>(); return(new FtpResponse(230, "Password ok, FTP server ready")); } } return(new FtpResponse(530, "Username or password incorrect")); }
/// <inheritdoc/> public override async Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (_supportedFeatures == null) { var features = new List <string>(); features.AddRange(Connection.CommandHandlers.Values.SelectMany(x => x.GetSupportedFeatures()).Select(x => x.BuildInfo(Connection)).Distinct()); _supportedFeatures = features; } if (_supportedFeatures.Count == 0) { return(new FtpResponse(211, "No extensions supported")); } await Connection.WriteAsync("211-Extensions supported:", cancellationToken); foreach (var supportedFeature in _supportedFeatures) { await Connection.WriteAsync($" {supportedFeature}", cancellationToken); } return(new FtpResponse(211, "END")); }
/// <inheritdoc/> public override async Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (Data.RenameFrom == null) { return(new FtpResponse(503, "RNTO must be preceded by a RNFR.")); } if (Data.RenameFrom.Entry == null) { return(new FtpResponse(550, "Item specified for RNFR doesn't exist.")); } var fileName = command.Argument; var tempPath = Data.Path.Clone(); var fileInfo = await Data.FileSystem.SearchEntryAsync(tempPath, fileName, cancellationToken).ConfigureAwait(false); if (fileInfo == null) { return(new FtpResponse(550, "Directory doesn't exist.")); } if (fileInfo.FileName == null) { return(new FtpResponse(550, "ROOT folder not allowed.")); } if (fileInfo.Entry != null) { var fullName = tempPath.GetFullPath(fileInfo.FileName); return(new FtpResponse(553, $"Target name already exists ({fullName}).")); } var targetDir = fileInfo.Directory; await Data.FileSystem.MoveAsync(Data.RenameFrom.Directory, Data.RenameFrom.Entry, targetDir, fileInfo.FileName, cancellationToken).ConfigureAwait(false); Data.RenameFrom = null; return(new FtpResponse(250, "Renamed file successfully.")); }
/// <inheritdoc/> public override async Task <IFtpResponse?> Process(FtpCommand command, CancellationToken cancellationToken) { var fsFeature = Connection.Features.Get <IFileSystemFeature>(); var directoryName = command.Argument; var currentPath = fsFeature.Path.Clone(); var dirInfo = await fsFeature.FileSystem.SearchDirectoryAsync(currentPath, directoryName, cancellationToken).ConfigureAwait(false); if (dirInfo == null) { return(new FtpResponse(550, T("Not a valid directory."))); } if (dirInfo.FileName == null) { return(new FtpResponse(550, T("ROOT folder not allowed."))); } if (dirInfo.Entry != null) { var message = T("\"{0}\" directory already exists", currentPath.GetFullPath(dirInfo.FileName)); return(new FtpResponseList( 521, message, T("Taking no action."), Enumerable.Empty <string>())); } try { var targetDirectory = currentPath.Count == 0 ? fsFeature.FileSystem.Root : currentPath.Peek(); var newDirectory = await fsFeature.FileSystem.CreateDirectoryAsync(targetDirectory, dirInfo.FileName, cancellationToken).ConfigureAwait(false); return(new FtpResponse(257, T("\"{0}\" created.", currentPath.GetFullPath(newDirectory.Name)))); } catch (IOException) { return(new FtpResponse(550, T("Bad pathname syntax."))); } }
/// <inheritdoc /> public override Task <IFtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { var loginStateMachine = Connection.ConnectionServices.GetRequiredService <IFtpLoginStateMachine>(); var authenticationMechanism = loginStateMachine.SelectedAuthenticationMechanism; if (authenticationMechanism == null) { return(Task.FromResult <IFtpResponse>(new FtpResponse(503, T("Bad sequence of commands")))); } byte[] data; try { data = Convert.FromBase64String(command.Argument); } catch (FormatException) { return(Task.FromResult <IFtpResponse>(new FtpResponse(501, T("Syntax error in parameters or arguments.")))); } return(authenticationMechanism.HandleAdatAsync(data, cancellationToken)); }