A formatter for the MLST command
Inheritance: IListFormatter
        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");
        }
Example #2
0
        /// <inheritdoc/>
        public override async Task<FtpResponse> Process(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;
            }

            var dirEntry = targetEntry as IUnixDirectoryEntry;
            var isDirEntry = dirEntry != null;

            var listDir = string.Equals(command.Name, "MLSD", StringComparison.OrdinalIgnoreCase);
            if (listDir && !isDirEntry)
                return new FtpResponse(501, "Not a directory.");

            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
            {
                var formatter = new FactsListFormatter(Data.User, Data.FileSystem, path, Data.ActiveMlstFacts);

                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",
                    })
                    {
                        if (listDir)
                        {
                            foreach (var line in formatter.GetPrefix(dirEntry))
                            {
                                Connection.Log?.Debug(line);
                                await writer.WriteLineAsync(line);
                            }

                            foreach (var entry in await Data.FileSystem.GetEntriesAsync(dirEntry, cancellationToken))
                            {
                                var line = formatter.Format(entry);
                                Connection.Log?.Debug(line);
                                await writer.WriteLineAsync(line);
                            }

                            foreach (var line in formatter.GetSuffix(dirEntry))
                            {
                                Connection.Log?.Debug(line);
                                await writer.WriteLineAsync(line);
                            }
                        }
                        else
                        {
                            var line = formatter.Format(targetEntry);
                            Connection.Log?.Debug(line);
                            await writer.WriteLineAsync(line);
                        }
                        await writer.FlushAsync();
                    }
                    await stream.FlushAsync(cancellationToken);
                }
            }
            finally
            {
                responseSocket.Dispose();
            }

            // Use 250 when the connection stays open.
            return new FtpResponse(226, "Closing data connection.");
        }
        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);
                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);
            ITcpSocketClient responseSocket;
            try
            {
                responseSocket = await Connection.CreateResponseSocket();
            }
            catch (Exception)
            {
                return new FtpResponse(425, "Can't open data connection.");
            }

            try
            {
                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",
                    })
                    {
                        var entries = await Data.FileSystem.GetEntriesAsync(dirEntry, cancellationToken);
                        var enumerator = new DirectoryListingEnumerator(entries, Data.FileSystem, path, true);
                        var formatter = new FactsListFormatter(Data.User, enumerator, Data.ActiveMlstFacts, false);
                        while (enumerator.MoveNext())
                        {
                            var name = enumerator.Name;
                            var entry = enumerator.Entry;
                            var line = formatter.Format(entry, name);
                            Connection.Log?.Debug(line);
                            await writer.WriteLineAsync(line);
                        }
                        await writer.FlushAsync();
                    }
                    await stream.FlushAsync(cancellationToken);
                }
            }
            finally
            {
                responseSocket.Dispose();
            }

            // Use 250 when the connection stays open.
            return new FtpResponse(226, "Closing data connection.");
        }