Beispiel #1
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.");
        }