/// <summary> /// Renames a file on the FTP server /// </summary> /// <param name="from"></param> /// <param name="to"></param> /// <returns></returns> public async Task RenameAsync(string from, string to) { EnsureLoggedIn(); LoggerHelper.Debug($"[FtpClient] Renaming from {from}, to {to}"); var renameFromResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope { FtpCommand = FtpCommand.RNFR, Data = from }); if (renameFromResponse.FtpStatusCode != FtpStatusCode.FileCommandPending) { throw new FtpException(renameFromResponse.ResponseMessage); } var renameToResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope { FtpCommand = FtpCommand.RNTO, Data = to }); if (renameToResponse.FtpStatusCode != FtpStatusCode.FileActionOK && renameToResponse.FtpStatusCode != FtpStatusCode.ClosingData) { throw new FtpException(renameFromResponse.ResponseMessage); } }
public void Dispose() { LoggerHelper.Debug("Disposing of FtpClient"); Task.WaitAny(LogOutAsync()); ControlStream?.Dispose(); dataSocketSemaphore?.Dispose(); }
/// <summary> /// Informs the FTP server of the client being used /// </summary> /// <param name="clientName"></param> /// <returns></returns> public async Task <FtpResponse> SetClientName(string clientName) { EnsureLoggedIn(); LoggerHelper.Debug($"[FtpClient] Setting client name to {clientName}"); return(await ControlStream.SendCommandAsync(new FtpCommandEnvelope { FtpCommand = FtpCommand.CLNT, Data = clientName })); }
/// <summary> /// Throws an exception if the server response is not one of the given acceptable codes /// </summary> /// <param name="response"></param> /// <param name="codes"></param> /// <returns></returns> private async Task BailIfResponseNotAsync(FtpResponse response, params FtpStatusCode[] codes) { if (codes.Any(x => x == response.FtpStatusCode)) { return; } LoggerHelper.Debug($"Bailing due to response codes being {response.FtpStatusCode}, which is not one of: [{string.Join(",", codes)}]"); await LogOutAsync(); throw new FtpException(response.ResponseMessage); }
/// <summary> /// Lists all directories in the current working directory /// </summary> /// <returns></returns> public async Task <IEnumerable <FtpNodeInformation> > ListDirectoriesAsync() { try { EnsureLoggedIn(); LoggerHelper.Debug($"[FtpClient] Listing directories in {WorkingDirectory}"); return(await directoryProvider.ListDirectoriesAsync()); } finally { await ControlStream.GetResponseAsync(); } }
/// <summary> /// Provides a stream which can be written to /// </summary> /// <param name="fileName"></param> /// <returns></returns> public async Task <Stream> OpenFileWriteStreamAsync(string fileName) { string filePath = WorkingDirectory.CombineAsUriWith(fileName); LoggerHelper.Debug($"[FtpClient] Opening file read stream for {filePath}"); var segments = filePath.Split('/') .Where(x => !x.IsNullOrWhiteSpace()) .ToList(); await CreateDirectoryStructureRecursively(segments.Take(segments.Count - 1).ToArray(), filePath.StartsWith("/")); return(new FtpDataStream(await OpenFileStreamAsync(filePath, FtpCommand.STOR), this)); }
/// <summary> /// Creates a directory on the FTP Server /// </summary> /// <param name="directory"></param> /// <returns></returns> public async Task CreateDirectoryAsync(string directory) { if (directory.IsNullOrWhiteSpace() || directory.Equals(".")) { throw new ArgumentOutOfRangeException(nameof(directory), "Directory supplied was not valid"); } LoggerHelper.Debug($"[FtpClient] Creating directory {directory}"); EnsureLoggedIn(); await CreateDirectoryStructureRecursively(directory.Split('/'), directory.StartsWith("/")); }
/// <summary> /// Lists all directories in the current working directory /// </summary> /// <param name="fileName"></param> /// <returns></returns> public async Task DeleteFileAsync(string fileName) { EnsureLoggedIn(); LoggerHelper.Debug($"[FtpClient] Deleting file {fileName}"); var response = await ControlStream.SendCommandAsync(new FtpCommandEnvelope { FtpCommand = FtpCommand.DELE, Data = fileName }); if (!response.IsSuccess) { throw new FtpException(response.ResponseMessage); } }
/// <summary> /// Determines the file size of the given file /// </summary> /// <param name="fileName"></param> /// <returns></returns> public async Task <long> GetFileSizeAsync(string fileName) { EnsureLoggedIn(); LoggerHelper.Debug($"[FtpClient] Getting file size for {fileName}"); var sizeResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope { FtpCommand = FtpCommand.SIZE, Data = fileName }); if (sizeResponse.FtpStatusCode != FtpStatusCode.FileStatus) { throw new FtpException(sizeResponse.ResponseMessage); } long fileSize = long.Parse(sizeResponse.ResponseMessage); return(fileSize); }
/// <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(); LoggerHelper.Debug($"[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); }
/// <summary> /// Deletes the given directory from the FTP server /// </summary> /// <param name="directory"></param> /// <returns></returns> public async Task DeleteDirectoryAsync(string directory) { if (directory.IsNullOrWhiteSpace() || directory.Equals(".")) { throw new ArgumentOutOfRangeException(nameof(directory), "Directory supplied was not valid"); } if (directory == "/") { return; } LoggerHelper.Debug($"[FtpClient] Deleting directory {directory}"); EnsureLoggedIn(); var rmdResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope { FtpCommand = FtpCommand.RMD, Data = directory }); switch (rmdResponse.FtpStatusCode) { case FtpStatusCode.CommandOK: case FtpStatusCode.FileActionOK: return; case FtpStatusCode.ActionNotTakenFileUnavailable: await DeleteNonEmptyDirectory(directory); return; default: throw new FtpException(rmdResponse.ResponseMessage); } }
/// <summary> /// Creates a directory structure recursively given a path /// </summary> /// <param name="directories"></param> /// <param name="isRootedPath"></param> /// <returns></returns> private async Task CreateDirectoryStructureRecursively(IEnumerable <string> directories, bool isRootedPath) { LoggerHelper.Debug($"[FtpClient] Creating directory structure recursively {string.Join("/", directories)}"); string originalPath = WorkingDirectory; if (isRootedPath && directories.Any()) { await ChangeWorkingDirectoryAsync("/"); } if (!directories.Any()) { return; } if (directories.Count() == 1) { await ControlStream.SendCommandAsync(new FtpCommandEnvelope { FtpCommand = FtpCommand.MKD, Data = directories.First() }); await ChangeWorkingDirectoryAsync(originalPath); return; } foreach (string directory in directories) { if (directory.IsNullOrWhiteSpace()) { continue; } var response = await ControlStream.SendCommandAsync(new FtpCommandEnvelope { FtpCommand = FtpCommand.CWD, Data = directory }); if (response.FtpStatusCode != FtpStatusCode.ActionNotTakenFileUnavailable) { continue; } await ControlStream.SendCommandAsync(new FtpCommandEnvelope { FtpCommand = FtpCommand.MKD, Data = directory }); await ControlStream.SendCommandAsync(new FtpCommandEnvelope { FtpCommand = FtpCommand.CWD, Data = directory }); } await ChangeWorkingDirectoryAsync(originalPath); }
/// <summary> /// Provides a stream which contains the data of the given filename on the FTP server /// </summary> /// <param name="fileName"></param> /// <returns></returns> public async Task <Stream> OpenFileReadStreamAsync(string fileName) { LoggerHelper.Debug($"[FtpClient] Opening file read stream for {fileName}"); return(new FtpDataStream(await OpenFileStreamAsync(fileName, FtpCommand.RETR), this)); }