示例#1
0
        /// <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");
        }
示例#4
0
        /// <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.");
        }
示例#5
0
        /// <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);
        }
示例#6
0
 /// <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."));
 }
示例#7
0
        /// <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);
        }
示例#9
0
 /// <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;
        }
示例#12
0
        /// <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());
        }
示例#13
0
        /// <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);
        }
示例#14
0
        /// <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."));
        }
示例#15
0
 /// <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}."));
 }
示例#16
0
        /// <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}");
        }
示例#17
0
 /// <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()}");
 }
示例#18
0
 /// <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();
         }
     });
 }
示例#19
0
        /// <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);
        }
示例#20
0
        /// <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."));
            }
        }
示例#21
0
        /// <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."));
            }
        }
示例#22
0
        /// <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}).");
        }
示例#23
0
        /// <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."))));
        }
示例#26
0
        /// <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)));
            }
        }
示例#27
0
        /// <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));
        }
示例#28
0
 /// <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");
 }
示例#29
0
 /// <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.");
     }
 }
示例#30
0
 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."));
     }
 }
示例#31
0
 /// <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.")));
            }
        }
示例#34
0
        /// <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.")));
            }
        }
示例#35
0
        /// <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.")));
        }
示例#37
0
 /// <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);
        }
示例#39
0
        /// <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));
        }
示例#40
0
        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));
        }
示例#41
0
        /// <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));
        }
示例#42
0
        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.")));
            }
        }
示例#43
0
        /// <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."));
        }
示例#45
0
        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);
        }
示例#49
0
        /// <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);
        }
示例#50
0
        /// <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");
        }
示例#51
0
        /// <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."));
            }
        }
示例#53
0
        /// <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")));
        }
示例#55
0
        /// <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));
        }
示例#56
0
        /// <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"));
        }
示例#58
0
        /// <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.")));
            }
        }
示例#60
0
        /// <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));
        }