diff --git a/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj b/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj index 2fb2acd1..5c06df87 100644 --- a/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj +++ b/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj @@ -69,6 +69,7 @@ + @@ -97,6 +98,7 @@ True Resources.resx + diff --git a/src/NAppUpdate.Framework/Sources/FtpSource.cs b/src/NAppUpdate.Framework/Sources/FtpSource.cs new file mode 100644 index 00000000..0fead11f --- /dev/null +++ b/src/NAppUpdate.Framework/Sources/FtpSource.cs @@ -0,0 +1,136 @@ +using System; +using System.IO; +using System.Net; +using System.Text; +using System.Web.UI.WebControls; +using NAppUpdate.Framework.Common; + +namespace NAppUpdate.Framework.Sources +{ + public class FtpSource : IUpdateSource + { + public string HostUrl { get; set; } + + private string feedPath { get; } + + private string feedBasePath => Path.GetDirectoryName(feedPath); + + private int bufferSize = 2048; + + private NetworkCredential credentials { get; } + + /// Url of ftp server ("ftp://somesite.com/") + /// Local ftp path to feed ("/path/to/feed.xml") + /// Login of ftp user, if needed + /// Password of ftp user, if needed + public FtpSource(string hostUrl, string feedPath, string login = null, string password = null) + { + HostUrl = hostUrl; + this.feedPath = feedPath; + + if (login != null && password != null) + { + credentials = new NetworkCredential(login, password); + } + } + + private void TryConnectToHost() + { + var ftpConnRequest = (FtpWebRequest)FtpWebRequest.Create(HostUrl); + ftpConnRequest.Method = WebRequestMethods.Ftp.ListDirectory; + ftpConnRequest.UsePassive = true; + ftpConnRequest.KeepAlive = false; + ftpConnRequest.Credentials = credentials; + + try + { + ftpConnRequest.GetResponse(); + } + catch (WebException e) + { + throw new WebException($"Failed to connect to host: {HostUrl}. Error message: {e.Message}"); + } + } + + /// + /// Downloads remote file from ftp + /// + /// Path to file on server (example: "/path/to/file.txt") + /// Path on local machine, where file should be saved (if not provided, Temp folder will be used) + /// Path to saved on disk file (Temp folder) + private string DownloadRemoteFile(string path, string localPath = null) + { + try + { + string pathToSave = localPath ?? Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + // Create an FTP Request + var ftpRequest = + (FtpWebRequest)FtpWebRequest.Create( + $@"{HostUrl}{(HostUrl.EndsWith("/") ? string.Empty : "/")}{path}"); + ftpRequest.Credentials = credentials; + // Set options + ftpRequest.UseBinary = true; + ftpRequest.UsePassive = true; + ftpRequest.KeepAlive = true; + // Specify the Type of FTP Request + ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile; + + // Establish Return Communication with the FTP Server + using (var ftpResponse = (FtpWebResponse)ftpRequest.GetResponse()) + { + // Get the FTP Server's Response Stream + using (var ftpStream = ftpResponse.GetResponseStream()) + { + // Save data on disk + using (var localFileStream = new FileStream(pathToSave, FileMode.Create)) + { + byte[] byteBuffer = new byte[bufferSize]; + int bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize); + + while (bytesRead > 0) + { + localFileStream.Write(byteBuffer, 0, bytesRead); + bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize); + } + } + } + } + + return pathToSave; + } + catch (Exception ex) + { + throw new WebException( + $"An error occurred when trying to download file {Path.GetFileName(path)}: {ex.Message}"); + } + } + + #region IUpdateSource Members + + public String GetUpdatesFeed() + { + TryConnectToHost(); + + string feedFilePath = DownloadRemoteFile(feedPath); + string data = File.ReadAllText(feedFilePath); + + // Remove byteorder mark if necessary + int indexTagOpening = data.IndexOf('<'); + if (indexTagOpening > 0) + { + data = data.Substring(indexTagOpening); + } + + return data; + } + + public Boolean GetData(String filePath, String basePath, Action onProgress, + ref String tempLocation) + { + DownloadRemoteFile(Path.Combine(feedBasePath, filePath), tempLocation); + return true; + } + + #endregion + } +}