private async Task <FtpResponse> ExecuteSend(TcpClient responseSocket, FtpCommand command, CancellationToken cancellationToken) { // Parse arguments in a way that's compatible with broken FTP clients var argument = new ListArguments(command.Argument); var showHidden = argument.All; // Instantiate the formatter IListFormatter formatter; if (string.Equals(command.Name, "NLST", StringComparison.OrdinalIgnoreCase)) { formatter = new ShortListFormatter(); } else if (string.Equals(command.Name, "LS", StringComparison.OrdinalIgnoreCase)) { if (argument.PreferLong) { formatter = new LongListFormatter(); } else { formatter = new ShortListFormatter(); } } else { formatter = new LongListFormatter(); } // Parse the given path to determine the mask (e.g. when information about a file was requested) var directoriesToProcess = new Queue <DirectoryQueueItem>(); // Use braces to avoid the definition of mask and path in the following parts // of this function. { var mask = "*"; var path = Data.Path.Clone(); foreach (var searchPath in argument.Paths) { var foundEntry = await Data.FileSystem.SearchEntryAsync(path, searchPath, cancellationToken).ConfigureAwait(false); if (foundEntry?.Directory == null) { return(new FtpResponse(550, "File system entry not found.")); } if (!(foundEntry.Entry is IUnixDirectoryEntry dirEntry)) { mask = foundEntry.FileName; }
/// <inheritdoc/> public override async Task <IFtpResponse> 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 fsFeature = Connection.Features.Get <IFileSystemFeature>(); var globOptions = new GlobOptions(); globOptions.Evaluation.CaseInsensitive = fsFeature.FileSystem.FileSystemEntryComparer.Equals("a", "A"); var glob = Glob.Parse(mask, globOptions); var formatter = new LongListFormatter(); var entries = await fsFeature.FileSystem.GetEntriesAsync(fsFeature.CurrentDirectory, cancellationToken) .ConfigureAwait(false); var lines = entries.Where(x => glob.IsMatch(x.Name)) .Select(x => formatter.Format(x, x.Name)) .ToList(); return(new FtpResponseList( 211, $"STAT {command.Argument}", "STAT", lines)); }
/// <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<FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(command.Argument)) { var taskStates = Server.GetBackgroundTaskStates(); 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 mmOptions = new Options() { IgnoreCase = Data.FileSystem.FileSystemEntryComparer.Equals("a", "A"), NoGlobStar = true, Dot = true, }; var mm = new Minimatcher(mask, mmOptions); var formatter = new LongListFormatter(); await Connection.WriteAsync($"211-STAT {command.Argument}", cancellationToken); foreach (var entry in (await Data.FileSystem.GetEntriesAsync(Data.CurrentDirectory, cancellationToken)).Where(x => mm.IsMatch(x.Name))) { var line = formatter.Format(entry, entry.Name); Connection.Log?.Debug(line); await Connection.WriteAsync($" {line}", cancellationToken); } return new FtpResponse(211, "STAT"); }
private async Task <IFtpResponse?> ExecuteSend(IFtpDataConnection dataConnection, FtpCommand command, CancellationToken cancellationToken) { var encodingFeature = Connection.Features.Get <IEncodingFeature>(); // Parse arguments in a way that's compatible with broken FTP clients var argument = new ListArguments(command.Argument); var showHidden = argument.All; // Instantiate the formatter Encoding encoding; IListFormatter formatter; if (string.Equals(command.Name, "NLST", StringComparison.OrdinalIgnoreCase)) { encoding = encodingFeature.NlstEncoding; formatter = new ShortListFormatter(); } else if (string.Equals(command.Name, "LS", StringComparison.OrdinalIgnoreCase)) { encoding = encodingFeature.Encoding; if (argument.PreferLong) { formatter = new LongListFormatter(); } else { formatter = new ShortListFormatter(); } } else { encoding = encodingFeature.Encoding; formatter = new LongListFormatter(); } // Parse the given path to determine the mask (e.g. when information about a file was requested) var directoriesToProcess = new Queue <DirectoryQueueItem>(); var fsFeature = Connection.Features.Get <IFileSystemFeature>(); // Use braces to avoid the definition of mask and path in the following parts // of this function. { if (argument.Paths.Count != 0) { foreach (var searchPath in argument.Paths) { var mask = "*"; var path = fsFeature.Path.Clone(); var foundEntry = await fsFeature.FileSystem.SearchEntryAsync(path, searchPath, cancellationToken).ConfigureAwait(false); if (foundEntry?.Directory == null) { return(new FtpResponse(550, T("File system entry not found."))); } if (!(foundEntry.Entry is IUnixDirectoryEntry dirEntry)) { if (!string.IsNullOrEmpty(foundEntry.FileName)) { mask = foundEntry.FileName !; } } else if (!dirEntry.IsRoot) { path.Push(dirEntry); } directoriesToProcess.Enqueue(new DirectoryQueueItem(path, mask)); } }
/// <inheritdoc/> public override async Task <FtpResponse> Process(FtpCommand command, CancellationToken cancellationToken) { await Connection.WriteAsync(new FtpResponse(150, "Opening data connection."), cancellationToken); ITcpSocketClient responseSocket; try { responseSocket = await Connection.CreateResponseSocket(); } catch (Exception) { return(new FtpResponse(425, "Can't open data connection.")); } try { // Parse arguments in a way that's compatible with broken FTP clients var argument = new ListArguments(command.Argument); var showHidden = argument.All; // Instantiate the formatter IListFormatter formatter; if (string.Equals(command.Name, "NLST", StringComparison.OrdinalIgnoreCase)) { formatter = new ShortListFormatter(); } else if (string.Equals(command.Name, "LS", StringComparison.OrdinalIgnoreCase)) { formatter = new LongListFormatter(); } else { formatter = new LongListFormatter(); } // Parse the given path to determine the mask (e.g. when information about a file was requested) var directoriesToProcess = new Queue <DirectoryQueueItem>(); // Use braces to avoid the definition of mask and path in the following parts // of this function. { var mask = "*"; var path = Data.Path.Clone(); if (!string.IsNullOrEmpty(argument.Path)) { var foundEntry = await Data.FileSystem.SearchEntryAsync(path, argument.Path, cancellationToken); if (foundEntry?.Directory == null) { return(new FtpResponse(550, "File system entry not found.")); } var dirEntry = foundEntry.Entry as IUnixDirectoryEntry; if (dirEntry == null) { mask = foundEntry.FileName; } else if (!dirEntry.IsRoot) { path.Push(dirEntry); } } directoriesToProcess.Enqueue(new DirectoryQueueItem(path, mask)); } var encoding = Data.NlstEncoding ?? Connection.Encoding; using (var stream = await Connection.CreateEncryptedStream(responseSocket.WriteStream)) { using (var writer = new StreamWriter(stream, encoding, 4096, true) { NewLine = "\r\n", }) { while (directoriesToProcess.Count != 0) { var queueItem = directoriesToProcess.Dequeue(); var currentPath = queueItem.Path; var mask = queueItem.Mask; var currentDirEntry = currentPath.Count != 0 ? currentPath.Peek() : Data.FileSystem.Root; if (argument.Recursive) { var line = currentPath.ToDisplayString() + ":"; Connection.Log?.Debug(line); await writer.WriteLineAsync(line); } var mmOptions = new Options() { IgnoreCase = Data.FileSystem.FileSystemEntryComparer.Equals("a", "A"), NoGlobStar = true, Dot = true, }; var mm = new Minimatcher(mask, mmOptions); var entries = await Data.FileSystem.GetEntriesAsync(currentDirEntry, cancellationToken); var enumerator = new DirectoryListingEnumerator(entries, Data.FileSystem, currentPath, true); while (enumerator.MoveNext()) { var name = enumerator.Name; if (!enumerator.IsDotEntry) { if (!mm.IsMatch(name)) { continue; } if (name.StartsWith(".") && !showHidden) { continue; } } var entry = enumerator.Entry; if (argument.Recursive && !enumerator.IsDotEntry) { var dirEntry = entry as IUnixDirectoryEntry; if (dirEntry != null) { var subDirPath = currentPath.Clone(); subDirPath.Push(dirEntry); directoriesToProcess.Enqueue(new DirectoryQueueItem(subDirPath, "*")); } } var line = formatter.Format(entry, name); Connection.Log?.Debug(line); await writer.WriteLineAsync(line); } } } } } finally { responseSocket.Dispose(); } // Use 250 when the connection stays open. return(new FtpResponse(250, "Closing data connection.")); }