using PKISharp.WACS.Configuration; using PKISharp.WACS.Services; using System; using System.IO; using System.Net; namespace PKISharp.WACS.Clients { internal class FtpClient { private NetworkCredential? Credential { get; set; } private readonly ILogService _log; public FtpClient(NetworkCredentialOptions? options, ILogService log) { _log = log; if (options != null) { Credential = options.GetCredential(); } } private FtpWebRequest CreateRequest(string ftpPath) { var ftpUri = new Uri(ftpPath); var scheme = ftpUri.Scheme; if (ftpUri.Scheme == "ftps") { scheme = "ftp"; } var ftpConnection = scheme + "://" + ftpUri.Host + ":" + ftpUri.Port + ftpUri.AbsolutePath; var request = (FtpWebRequest)WebRequest.Create(ftpConnection); request.Credentials = Credential; if (ftpUri.Scheme == "ftps") { request.EnableSsl = true; request.UsePassive = true; } return request; } public void Upload(string ftpPath, string content) { EnsureDirectories(ftpPath); var stream = new MemoryStream(); using var writer = new StreamWriter(stream); writer.Write(content); writer.Flush(); stream.Position = 0; var request = CreateRequest(ftpPath); request.Method = WebRequestMethods.Ftp.UploadFile; using (var requestStream = request.GetRequestStream()) { stream.CopyTo(requestStream); } using var response = (FtpWebResponse)request.GetResponse(); _log.Verbose("Upload {ftpPath} status {StatusDescription}", ftpPath, response.StatusDescription?.Trim() ?? "(null)"); } private void EnsureDirectories(string ftpPath) { var ftpUri = new Uri(ftpPath); var directories = ftpUri.AbsolutePath.Split('/'); var path = ftpUri.Scheme + "://" + ftpUri.Host + ":" + (ftpUri.Port == -1 ? 21 : ftpUri.Port) + "/"; if (directories.Length > 1) { for (var i = 1; i < (directories.Length - 1); i++) { path = path + directories[i] + "/"; var request = CreateRequest(path); request.Method = WebRequestMethods.Ftp.MakeDirectory; try { using var response = (FtpWebResponse)request.GetResponse(); _log.Verbose("Create {path} status {StatusDescription}", path, response.StatusDescription?.Trim() ?? "(null)"); } catch (Exception ex) { _log.Verbose("Create {path} failed, may already exist ({Message})", path, ex.Message); } } } } public string GetFiles(string ftpPath) { var request = CreateRequest(ftpPath); request.Method = WebRequestMethods.Ftp.ListDirectory; string names; using (var response = (FtpWebResponse)request.GetResponse()) { var responseStream = response.GetResponseStream(); using var reader = new StreamReader(responseStream); names = reader.ReadToEnd(); } names = names.Trim(); _log.Verbose("Files in path {ftpPath}: {@names}", ftpPath, names); return names; } public void Delete(string ftpPath, FileType fileType) { var request = CreateRequest(ftpPath); if (fileType == FileType.File) { request.Method = WebRequestMethods.Ftp.DeleteFile; } else if (fileType == FileType.Directory) { request.Method = WebRequestMethods.Ftp.RemoveDirectory; } using var response = (FtpWebResponse)request.GetResponse(); _log.Verbose("Delete {ftpPath} status {StatusDescription}", ftpPath, response.StatusDescription?.Trim()); } public enum FileType { File, Directory } } }