// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net; using System.IO; using System.Data; namespace Spludlow.Net { public class Ftp { // active ports need to open on client // passive default all ports on server private int BufferSize = 1024 * 1024; private string Key; private NetworkCredential NetworkCredential; private bool UsePassive; private bool EnableSsl; private string Address; private string ConnectionGroupName; public Ftp(string key) { this.Key = key; NetworkCredential credential = Spludlow.Credentials.GetCredential(this.Key); string host = credential.Domain; this.UsePassive = true; this.EnableSsl = false; int trim = 0; if (host.Contains("*") == true) { this.UsePassive = false; ++trim; } if (host.Contains("$") == true) { this.EnableSsl = true; ++trim; } host = host.Substring(0, host.Length - trim); this.NetworkCredential = new NetworkCredential(credential.UserName, credential.Password); this.Address = "ftp://" + host; if (this.EnableSsl == true) Spludlow.WebServices.SetServerCertificateValidation(); } public Ftp(string host, string userName, string password, bool activeMode, bool secure) { this.NetworkCredential = new NetworkCredential(userName, password); this.UsePassive = !activeMode; this.EnableSsl = secure; this.Address = "ftp://" + host; if (this.EnableSsl == true) Spludlow.WebServices.SetServerCertificateValidation(); } private FtpWebRequest MakeRequest(string path, string method, bool useBinary) { if (path != null && path[0] != '/') throw new ApplicationException("FTP, Please supply an absolute FTP Path:\t" + path); if (this.ConnectionGroupName == null) this.ConnectionGroupName = this.Address + "/" + this.NetworkCredential.UserName; FtpWebRequest request = (FtpWebRequest)WebRequest.Create(this.Address + path); request.Method = method; request.Credentials = this.NetworkCredential; request.UsePassive = this.UsePassive; request.UseBinary = useBinary; request.KeepAlive = true; request.ConnectionGroupName = this.ConnectionGroupName; request.EnableSsl = this.EnableSsl; return request; } public string[] ListDirectory(string directory) { FtpWebRequest request = this.MakeRequest(directory, WebRequestMethods.Ftp.ListDirectory, false); List list = new List(); using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) { using (StreamReader reader = new StreamReader(response.GetResponseStream())) { string line = null; while ((line = reader.ReadLine()) != null) list.Add(line); } } list.Sort(); return list.ToArray(); } public string[] ListDirectoryDetails(string directory) { FtpWebRequest request = this.MakeRequest(directory, WebRequestMethods.Ftp.ListDirectoryDetails, false); List list = new List(); using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) { using (StreamReader reader = new StreamReader(response.GetResponseStream())) { string line = null; while ((line = reader.ReadLine()) != null) list.Add(line); } } return list.ToArray(); } public static DataTable ParseDirectoryDetailsUNIX(string[] directoryListings) { DateTime lookTime = DateTime.Now; DataTable table = Spludlow.Data.TextTable.ReadText(new string[] { "FullName LastWriteTime Length IsDirectory", "String DateTime Int64 Boolean", }); foreach (string line in directoryListings) { string[] words = Spludlow.Text.Split(line, ' ', true, false, 9); if (words.Length != 9) throw new ApplicationException("ParseDirectoryDetailsUnix; Bad word count in directory line:\t" + line); long fileLength = Int64.Parse(words[4]); DateTime lastWriteTime; if (words[6].Length == 1) words[6] = "0" + words[6]; if (words[7].Contains(":") == true) { // Sep 10 11:48 lastWriteTime = DateTime.ParseExact(words[5] + " " + words[6] + " " + lookTime.Year + " " + words[7], "MMM dd yyyy HH:mm", System.Globalization.CultureInfo.InvariantCulture); if (lastWriteTime > lookTime) lastWriteTime = lastWriteTime.AddYears(-1); } else { // Jun 25 2006 lastWriteTime = DateTime.ParseExact(words[5] + " " + words[6] + " " + words[7], "MMM dd yyyy", System.Globalization.CultureInfo.InvariantCulture); } bool isDirectory = false; if (words[0][0] == 'd') isDirectory = true; table.Rows.Add(new object[] { words[8], lastWriteTime, fileLength, isDirectory }); } return table; } public void DeleteFile(string filename) { FtpWebRequest request = this.MakeRequest(filename, WebRequestMethods.Ftp.DeleteFile, false); FtpWebResponse response = (FtpWebResponse)request.GetResponse(); response.Dispose(); } public void RemoveDirectory(string directory) { FtpWebRequest request = this.MakeRequest(directory, WebRequestMethods.Ftp.RemoveDirectory, false); FtpWebResponse response = (FtpWebResponse)request.GetResponse(); response.Dispose(); } public void MakeDirectory(string directory) { FtpWebRequest request = this.MakeRequest(directory, WebRequestMethods.Ftp.MakeDirectory, false); FtpWebResponse response = (FtpWebResponse)request.GetResponse(); response.Dispose(); } public long GetFileSize(string filename) { FtpWebRequest request = this.MakeRequest(filename, WebRequestMethods.Ftp.GetFileSize, false); try { using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) return response.ContentLength; } catch (WebException ee) { FtpWebResponse response = (FtpWebResponse)ee.Response; if (response.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable) return 0; throw ee; } } public bool FileExists(string filename) { FtpWebRequest request = this.MakeRequest(filename, WebRequestMethods.Ftp.GetFileSize, false); try { FtpWebResponse response = (FtpWebResponse)request.GetResponse(); response.Dispose(); return true; } catch (WebException ee) { FtpWebResponse response = (FtpWebResponse)ee.Response; if (response.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable) return false; throw ee; } } public void DownloadFile(string ftpFilename, string localFilename) { byte[] buffer = new byte[this.BufferSize]; FtpWebRequest request = this.MakeRequest(ftpFilename, WebRequestMethods.Ftp.DownloadFile, true); using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) { using (Stream responseStream = response.GetResponseStream()) { using (FileStream fileStream = new FileStream(localFilename, FileMode.Create)) { int count; while ((count = responseStream.Read(buffer, 0, buffer.Length)) > 0) fileStream.Write(buffer, 0, count); } } } } public void UploadText(string text, string ftpFilename) { byte[] buffer = Spludlow.Text.GetBytes(text); FtpWebRequest request = this.MakeRequest(ftpFilename, WebRequestMethods.Ftp.UploadFile, true); using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(buffer, 0, buffer.Length); } FtpWebResponse response = (FtpWebResponse)request.GetResponse(); response.Dispose(); } public void UploadFile(string localFilename, string ftpFilename) { byte[] buffer = new byte[this.BufferSize]; FtpWebRequest request = this.MakeRequest(ftpFilename, WebRequestMethods.Ftp.UploadFile, true); using (FileStream fileStream = new FileStream(localFilename, FileMode.Open)) { using (Stream requestStream = request.GetRequestStream()) { int count; while ((count = fileStream.Read(buffer, 0, buffer.Length)) > 0) requestStream.Write(buffer, 0, count); } } FtpWebResponse response = (FtpWebResponse)request.GetResponse(); response.Dispose(); } public long AppendFile(string localFilename, string ftpFilename) { long localSize = Spludlow.Io.Files.FileLength(localFilename); long ftpSize = this.GetFileSize(ftpFilename); long transfered = 0; if (ftpSize == localSize) return 0; if (ftpSize > localSize) throw new ApplicationException("FTP Append, FTP File size is greater than local file size:\t" + localFilename + ", " + ftpSize); byte[] buffer = new byte[this.BufferSize]; FtpWebRequest request = this.MakeRequest(ftpFilename, WebRequestMethods.Ftp.AppendFile, true); using (FileStream fileStream = new FileStream(localFilename, FileMode.Open)) { fileStream.Seek(ftpSize, SeekOrigin.Begin); using (Stream requestStream = request.GetRequestStream()) { int count; while ((count = fileStream.Read(buffer, 0, buffer.Length)) > 0) { requestStream.Write(buffer, 0, count); transfered += count; } } } FtpWebResponse response = (FtpWebResponse)request.GetResponse(); response.Dispose(); return transfered; } public int DownloadDirectory(string ftpDirectory, string localDirectory) { List ftpFilenames = new List(); foreach (string filename in this.ListDirectory(ftpDirectory)) ftpFilenames.Add(Path.GetFileName(filename)); List localFilenames = new List(); foreach (string filename in Directory.GetFiles(localDirectory)) localFilenames.Add(Path.GetFileName(filename)); List downloadFilenames = new List(); foreach (string ftpFilename in ftpFilenames) { if (localFilenames.Contains(ftpFilename) == false) downloadFilenames.Add(ftpFilename); } int goneCount = 0; int gotCount = 0; foreach (string localFilename in localFilenames) { if (ftpFilenames.Contains(localFilename) == false) ++goneCount; else ++gotCount; } foreach (string filename in downloadFilenames) { string ftpFilename = ftpDirectory + "/" + filename; string localFilename = localDirectory + @"\" + filename; this.DownloadFile(ftpFilename, localFilename); } Spludlow.Log.Info("FTP, Download Directory:" + this.Key + ", Ftp:" + ftpFilenames.Count + ", Local:" + localFilenames.Count + ", Download:" + downloadFilenames.Count + ", Gone:" + goneCount + ", Got:" + gotCount); return downloadFilenames.Count; } public int UploadDirectory(string localDirectory, string ftpDirectory) { List localFilenames = new List(); foreach (string filename in Directory.GetFiles(localDirectory)) localFilenames.Add(Path.GetFileName(filename)); List ftpFilenames = new List(); foreach (string filename in this.ListDirectory(ftpDirectory)) ftpFilenames.Add(Path.GetFileName(filename)); List uploadFilenames = new List(); foreach (string localFilename in localFilenames) { if (ftpFilenames.Contains(localFilename) == false) uploadFilenames.Add(localFilename); } foreach (string filename in uploadFilenames) { string localFilename = localDirectory + @"\" + filename; string ftpFilename = ftpDirectory + "/" + filename; this.UploadFile(localFilename, ftpFilename); } Spludlow.Log.Info("FTP, Upload Directory:" + this.Key + ", Ftp:" + ftpFilenames.Count + ", Local:" + localFilenames.Count + ", Upload:" + uploadFilenames.Count); return uploadFilenames.Count; } } }