From f780f997055a0cf8316b53dace3ea6c1a99acd97 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Mon, 3 Oct 2022 11:20:23 +0200 Subject: [PATCH 1/9] minor cleanup --- IOB-MAN/IOBManPanel.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/IOB-MAN/IOBManPanel.cs b/IOB-MAN/IOBManPanel.cs index a7c211e8..80737f82 100644 --- a/IOB-MAN/IOBManPanel.cs +++ b/IOB-MAN/IOBManPanel.cs @@ -1116,9 +1116,6 @@ namespace IOB_MAN { try { - //Task result = checkProcessStatusAsync(); - //result.Wait(); - Task result = Task.Run(() => checkProcessStatusAsync().ConfigureAwait(false)); result.Wait(); } From fa7eb5ad13d2832dc382531b3c24a59b4aab88e6 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Mon, 3 Oct 2022 19:23:50 +0200 Subject: [PATCH 2/9] bozza preliminare client FTP (da testare) --- EgwProxy.FTP.sln | 31 ++++ EgwProxy.Ftp.Test/App.config | 40 +++++ EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj | 61 ++++++++ EgwProxy.Ftp.Test/Program.cs | 146 +++++++++++++++++ EgwProxy.Ftp.Test/Properties/AssemblyInfo.cs | 36 +++++ EgwProxy.Ftp/Client.cs | 147 ++++++++++++++++++ EgwProxy.Ftp/EgwProxy.Ftp.csproj | 7 + .../EgwProxy.Icoel.Test.csproj | 10 -- 8 files changed, 468 insertions(+), 10 deletions(-) create mode 100644 EgwProxy.FTP.sln create mode 100644 EgwProxy.Ftp.Test/App.config create mode 100644 EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj create mode 100644 EgwProxy.Ftp.Test/Program.cs create mode 100644 EgwProxy.Ftp.Test/Properties/AssemblyInfo.cs create mode 100644 EgwProxy.Ftp/Client.cs create mode 100644 EgwProxy.Ftp/EgwProxy.Ftp.csproj diff --git a/EgwProxy.FTP.sln b/EgwProxy.FTP.sln new file mode 100644 index 00000000..c812fb12 --- /dev/null +++ b/EgwProxy.FTP.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32616.157 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EgwProxy.Ftp", "EgwProxy.Ftp\EgwProxy.Ftp.csproj", "{D03314DE-4EB2-4887-94B0-9CEB0C7090F9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EgwProxy.Ftp.Test", "EgwProxy.Ftp.Test\EgwProxy.Ftp.Test.csproj", "{A655A6AC-5997-46F9-9752-8C621B80516C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D03314DE-4EB2-4887-94B0-9CEB0C7090F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D03314DE-4EB2-4887-94B0-9CEB0C7090F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D03314DE-4EB2-4887-94B0-9CEB0C7090F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D03314DE-4EB2-4887-94B0-9CEB0C7090F9}.Release|Any CPU.Build.0 = Release|Any CPU + {A655A6AC-5997-46F9-9752-8C621B80516C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A655A6AC-5997-46F9-9752-8C621B80516C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A655A6AC-5997-46F9-9752-8C621B80516C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A655A6AC-5997-46F9-9752-8C621B80516C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E2472E21-58D3-4EAD-BC4A-BC3915B18BEF} + EndGlobalSection +EndGlobal diff --git a/EgwProxy.Ftp.Test/App.config b/EgwProxy.Ftp.Test/App.config new file mode 100644 index 00000000..2a567b47 --- /dev/null +++ b/EgwProxy.Ftp.Test/App.config @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj b/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj new file mode 100644 index 00000000..19c009dc --- /dev/null +++ b/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {A655A6AC-5997-46F9-9752-8C621B80516C} + Exe + EgwProxy.Icoel.Test + EgwProxy.Icoel.Test + v4.6.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + {d03314de-4eb2-4887-94b0-9ceb0c7090f9} + EgwProxy.Ftp + + + + + \ No newline at end of file diff --git a/EgwProxy.Ftp.Test/Program.cs b/EgwProxy.Ftp.Test/Program.cs new file mode 100644 index 00000000..e33f4d15 --- /dev/null +++ b/EgwProxy.Ftp.Test/Program.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Diagnostics; + +namespace EgwProxy.Icoel.Test +{ + internal class Program + { + #region Private Methods + + /// + /// legge conf in formato stringa + /// + /// + /// + protected static string ReadSetting(string key) + { + string answ = ""; + try + { + answ = $"{ConfigurationManager.AppSettings[key]}" ?? ""; + } + catch (Exception exc) + { + Console.Write("Eccezione in ReadSettings"); + Console.Write(exc.Message); + } + return answ; + } + + /// + /// Programma principale + /// + /// + private static void Main(string[] args) + { + // leggo conf da file ini (ip/port) + Console.WriteLine("Loading Files..."); + string userInput = ""; + + Console.WriteLine("------------ Connessione Server FTP ------------"); + + var ftpClient = new EgwProxy.Ftp.Client(ReadSetting("server"), ReadSetting("userName"), ReadSetting("passwd")); + Console.WriteLine(); + var testServer = ftpClient.serverOk(); + Console.WriteLine($"Test connessione: esito {testServer}"); + Console.WriteLine(); + Console.WriteLine("--- Upload ---"); + + Console.WriteLine("--- Download ---"); + + + + Console.WriteLine(); + Console.WriteLine("Premere un tasto x continuare..."); + Console.ReadKey(); + +#if false + // oggetto x connessione al sizer icoel + Connector IcoelSizer = new Connector(ReadSetting("IndirizzoIpSizer"), ReadSetting("SizerTcpPort")); + //Connector IcoelSizer = new Connector(setup.IndirizzoIpSizer, setup.SizerTcpPort); + + Console.WriteLine("------------ Parametri CurrBatch rilevati ------------"); + var cBatch = IcoelSizer.GetCurrBatchData(); + if (cBatch != null) + { + DisplayCurrBatch(cBatch); + } + + // ora effettua un pò di letture/scritture + try + { + Console.WriteLine("------------ TUTTE variety ------------"); + var varList = IcoelSizer.GetVarietyList(false); + var varietyData = IcoelSizer.GetLayoutForVarietyList(varList); + if (varietyData != null) + { + DisplayVarietyLayout(varietyData); + } + Console.WriteLine(); + Console.WriteLine("Premere un tasto x continuare..."); + userInput = Console.ReadLine(); + + Console.WriteLine("------------ Parametri velocità rilevati ------------"); + var perfMeter = IcoelSizer.GetPerfMeters(); + if (perfMeter != null) + { + DisplayPerfMeter(perfMeter); + } + Console.WriteLine(); + Console.WriteLine("Premere un tasto x continuare..."); + userInput = Console.ReadLine(); + + // solo attive + Console.WriteLine("------------ solo attive ------------"); + varList = IcoelSizer.GetVarietyList(); + varietyData = IcoelSizer.GetLayoutForVarietyList(varList); + if (varietyData != null) + { + DisplayVarietyLayout(varietyData); + } + Console.WriteLine(); + Console.WriteLine("Premere un tasto x continuare..."); + userInput = Console.ReadLine(); + + Console.WriteLine("------------ BATCH correnti ------------"); + var currBatch = IcoelSizer.GetCurrentBatch(); + foreach (var item in currBatch) + { + string lato = item.Key == 1 ? "SX" : "DX"; + Console.WriteLine($"[{item.Key}-{lato}] Grower code: {item.Value.GrowerCode} | Layout Name: {item.Value.LayoutName} | Totalling: [{item.Value.TotallingVarietyCode}] {item.Value.TotallingVariety} | Sizing: {item.Value.SizingProfileName} | Start {item.Value.StartTime} | End {item.Value.EndTime}"); + } + Console.WriteLine(); + Console.WriteLine("Premere un tasto x continuare..."); + userInput = Console.ReadLine(); + + Console.WriteLine("------------ Prova invio BATCH ------------"); + // recupero GUID x variety e layout + var varGuid = selVariety(IcoelSizer); + var layGuid = selLayout(IcoelSizer, varGuid); + + GrowerInfo GrowerData = new GrowerInfo(); + IcoelSizer.EnqueueBatch(GrowerData, varGuid, layGuid); + + currBatch = IcoelSizer.GetCurrentBatch(); + foreach (var item in currBatch) + { + string lato = item.Key == 1 ? "SX" : "DX"; + Console.WriteLine($"[{item.Key}-{lato}] Grower code: {item.Value.GrowerCode} | Layout Name: {item.Value.LayoutName} | Totalling: [{item.Value.TotallingVarietyCode}] {item.Value.TotallingVariety} | Sizing: {item.Value.SizingProfileName} | Start {item.Value.StartTime} | End {item.Value.EndTime}"); + } + Console.WriteLine("Test completato"); + Console.WriteLine("Premere un tasto x chiudere"); + Console.ReadKey(); + } + catch (Exception ex) + { + Console.WriteLine("ECCEZIONE" + ex.Message + ex.StackTrace); + Console.ReadKey(); + } +#endif + } + + #endregion Private Methods + } +} \ No newline at end of file diff --git a/EgwProxy.Ftp.Test/Properties/AssemblyInfo.cs b/EgwProxy.Ftp.Test/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..339a1f69 --- /dev/null +++ b/EgwProxy.Ftp.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("EgwProxy.Icoel.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("EgwProxy.Icoel.Test")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a655a6ac-5997-46f9-9752-8c621b80516c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/EgwProxy.Ftp/Client.cs b/EgwProxy.Ftp/Client.cs new file mode 100644 index 00000000..915b1401 --- /dev/null +++ b/EgwProxy.Ftp/Client.cs @@ -0,0 +1,147 @@ +using FluentFTP; + +namespace EgwProxy.Ftp +{ + /// + /// Client per operazioni FTP, basato su FluentFTP: https://github.com/robinrodricks/FluentFTP/wiki/Quick-Start-Example + /// + public class Client + { + #region Public Constructors + + /// + /// Inizializzazione di oggetto per comunicazione FTP + /// + /// + /// + /// + public Client(string server, string userName, string passwd) + { + _server = server; + _userName = userName; + _passwd = passwd; + if (!string.IsNullOrEmpty(server)) + { + // se ho user/pwd è autenticato... + if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(passwd)) + { + client = new FtpClient(server, userName, passwd); + } + //.. altrimenti anonimo... + else + { + client = new FtpClient(server); + } + } + } + + #endregion Public Constructors + + #region Public Methods + + /// + /// Verifica esistenza directory su server FTP remoto + /// + /// Percorso remoto da testare (ad es "/htdocs/extras/") + /// + public bool dirExists(string remotePath) + { + // connect to the server and automatically detect working FTP settings + if (!client.IsConnected) + { + client.AutoConnect(); + } + bool answ = client.DirectoryExists(remotePath); + // chiudo! + client.Disconnect(); + return answ; + } + + /// + /// Verifica esistenza file su server FTP remoto + /// + /// Percorso remoto da testare (ad es "/htdocs/big2.txt") + /// + public bool fileExists(string remotePath) + { + // connect to the server and automatically detect working FTP settings + if (!client.IsConnected) + { + client.AutoConnect(); + } + bool answ = client.FileExists(remotePath); + // chiudo! + client.Disconnect(); + return answ; + } + + /// + /// Caricamento singolo file + /// + /// Path locale del file da inviare (ad es: @"C:\MyVideo.mp4") + /// NOme remoto file per caricamento (ad es: "/htdocs/MyVideo.mp4") + public void sendFile(string fileName, string remoteName) + { + // connect to the server and automatically detect working FTP settings + if (!client.IsConnected) + { + client.AutoConnect(); + } + // effettuo caricamento puntuale + client.UploadFile(fileName, remoteName); + // chiudo! + client.Disconnect(); + } + + /// + /// Verifica connessione con server FTP remoto + /// + /// + public bool serverOk() + { + // connect to the server and automatically detect working FTP settings + if (!client.IsConnected) + { + client.AutoConnect(); + } + bool answ = client.IsConnected; + // chiudo! + client.Disconnect(); + return answ; + } + + /// + /// Caricamento singolo file + /// + /// Path directory da inviare (ad es:@"C:\website\videos\") + /// NOme remoto file per caricamento (ad es: @"/public_html/videos") + public void uploadDir(string dirPath, string remoteDir) + { + // connect to the server and automatically detect working FTP settings + if (!client.IsConnected) + { + client.AutoConnect(); + } + // upload della folder + files, cancellazione extra files = mirroring + client.UploadDirectory(dirPath, remoteDir, FtpFolderSyncMode.Mirror); + // chiudo! + client.Disconnect(); + } + + #endregion Public Methods + + #region Protected Properties + + protected string _passwd { get; set; } = ""; + protected string _server { get; set; } = ""; + protected string _userName { get; set; } = ""; + + #endregion Protected Properties + + #region Private Properties + + private FtpClient client { get; set; } + + #endregion Private Properties + } +} \ No newline at end of file diff --git a/EgwProxy.Ftp/EgwProxy.Ftp.csproj b/EgwProxy.Ftp/EgwProxy.Ftp.csproj new file mode 100644 index 00000000..9f5c4f4a --- /dev/null +++ b/EgwProxy.Ftp/EgwProxy.Ftp.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/EgwProxy.Icoel.Test/EgwProxy.Icoel.Test.csproj b/EgwProxy.Icoel.Test/EgwProxy.Icoel.Test.csproj index fcdfd69f..37efc51e 100644 --- a/EgwProxy.Icoel.Test/EgwProxy.Icoel.Test.csproj +++ b/EgwProxy.Icoel.Test/EgwProxy.Icoel.Test.csproj @@ -56,16 +56,6 @@ PreserveNewest - - - {E36544CB-D699-48D8-9F81-C2758E7C7D19} - EgwProxy.Icoel.DataLayer - - - {c45f5e6e-866b-4a34-a598-29aab2d178ad} - EgwProxy.Icoel - - From 5d8ac192ac268ff23eeb02f3a35f141389004f88 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Tue, 4 Oct 2022 11:05:34 +0200 Subject: [PATCH 3/9] OK primo test ftp - crea directory --- EgwProxy.Ftp.Test/App.config | 36 +++--- EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj | 12 +- EgwProxy.Ftp.Test/Program.cs | 37 ++++-- EgwProxy.Ftp.Test/Properties/AssemblyInfo.cs | 6 +- EgwProxy.Ftp.Test/packages.config | 5 + EgwProxy.Ftp/EgwProxy.Ftp.csproj | 6 +- EgwProxy.Ftp/{Client.cs => Manager.cs} | 117 ++++++++++++++----- 7 files changed, 160 insertions(+), 59 deletions(-) create mode 100644 EgwProxy.Ftp.Test/packages.config rename EgwProxy.Ftp/{Client.cs => Manager.cs} (58%) diff --git a/EgwProxy.Ftp.Test/App.config b/EgwProxy.Ftp.Test/App.config index 2a567b47..3d6ca2c2 100644 --- a/EgwProxy.Ftp.Test/App.config +++ b/EgwProxy.Ftp.Test/App.config @@ -1,18 +1,14 @@  - - - - - - - - - - - - - + + + + + + + + + @@ -37,4 +33,16 @@ - + + + + + + + + + + + + + \ No newline at end of file diff --git a/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj b/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj index 19c009dc..893c6780 100644 --- a/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj +++ b/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj @@ -6,8 +6,8 @@ AnyCPU {A655A6AC-5997-46F9-9752-8C621B80516C} Exe - EgwProxy.Icoel.Test - EgwProxy.Icoel.Test + EgwProxy.Ftp.Test + EgwProxy.Ftp.Test v4.6.2 512 true @@ -33,9 +33,16 @@ 4 + + ..\packages\FluentFTP.41.0.0\lib\net462\FluentFTP.dll + + + ..\packages\Microsoft.Extensions.Logging.Abstractions.2.1.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll + + @@ -49,6 +56,7 @@ + diff --git a/EgwProxy.Ftp.Test/Program.cs b/EgwProxy.Ftp.Test/Program.cs index e33f4d15..ed897449 100644 --- a/EgwProxy.Ftp.Test/Program.cs +++ b/EgwProxy.Ftp.Test/Program.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Configuration; using System.Diagnostics; -namespace EgwProxy.Icoel.Test +namespace EgwProxy.Ftp.Test { internal class Program { @@ -35,27 +35,42 @@ namespace EgwProxy.Icoel.Test /// private static void Main(string[] args) { - // leggo conf da file ini (ip/port) - Console.WriteLine("Loading Files..."); - string userInput = ""; - Console.WriteLine("------------ Connessione Server FTP ------------"); - var ftpClient = new EgwProxy.Ftp.Client(ReadSetting("server"), ReadSetting("userName"), ReadSetting("passwd")); + var ftpClient = new Manager(ReadSetting("server"), ReadSetting("userName"), ReadSetting("passwd")); Console.WriteLine(); var testServer = ftpClient.serverOk(); Console.WriteLine($"Test connessione: esito {testServer}"); + var srvType = ftpClient.serverType(); + Console.WriteLine($"Server: {srvType}"); Console.WriteLine(); - Console.WriteLine("--- Upload ---"); - - Console.WriteLine("--- Download ---"); - - + Console.WriteLine("--- Create folder ---"); + var testCreate = ftpClient.createDir("data/test_folder"); + Console.WriteLine(); + var folderContent = ftpClient.listDir("data/"); + foreach (var item in folderContent) + { + Console.Write(item); + } Console.WriteLine(); Console.WriteLine("Premere un tasto x continuare..."); Console.ReadKey(); + + //Console.WriteLine("--- Upload ---"); + + //Console.WriteLine(); + //Console.WriteLine("Premere un tasto x continuare..."); + //Console.ReadKey(); + + + //Console.WriteLine("--- Download ---"); + + //Console.WriteLine(); + //Console.WriteLine("Premere un tasto x continuare..."); + //Console.ReadKey(); + #if false // oggetto x connessione al sizer icoel Connector IcoelSizer = new Connector(ReadSetting("IndirizzoIpSizer"), ReadSetting("SizerTcpPort")); diff --git a/EgwProxy.Ftp.Test/Properties/AssemblyInfo.cs b/EgwProxy.Ftp.Test/Properties/AssemblyInfo.cs index 339a1f69..22301651 100644 --- a/EgwProxy.Ftp.Test/Properties/AssemblyInfo.cs +++ b/EgwProxy.Ftp.Test/Properties/AssemblyInfo.cs @@ -5,12 +5,12 @@ using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("EgwProxy.Icoel.Test")] +[assembly: AssemblyTitle("EgwProxy.Ftp.Test")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("EgwProxy.Icoel.Test")] -[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyProduct("EgwProxy.Ftp.Test")] +[assembly: AssemblyCopyright("Copyright © 2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/EgwProxy.Ftp.Test/packages.config b/EgwProxy.Ftp.Test/packages.config new file mode 100644 index 00000000..e11a86c4 --- /dev/null +++ b/EgwProxy.Ftp.Test/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/EgwProxy.Ftp/EgwProxy.Ftp.csproj b/EgwProxy.Ftp/EgwProxy.Ftp.csproj index 9f5c4f4a..83684d1a 100644 --- a/EgwProxy.Ftp/EgwProxy.Ftp.csproj +++ b/EgwProxy.Ftp/EgwProxy.Ftp.csproj @@ -1,7 +1,11 @@ - + netstandard2.0 + + + + diff --git a/EgwProxy.Ftp/Client.cs b/EgwProxy.Ftp/Manager.cs similarity index 58% rename from EgwProxy.Ftp/Client.cs rename to EgwProxy.Ftp/Manager.cs index 915b1401..b55ed9d6 100644 --- a/EgwProxy.Ftp/Client.cs +++ b/EgwProxy.Ftp/Manager.cs @@ -1,11 +1,13 @@ using FluentFTP; +using System.Collections.Generic; +using System.Linq; namespace EgwProxy.Ftp { /// /// Client per operazioni FTP, basato su FluentFTP: https://github.com/robinrodricks/FluentFTP/wiki/Quick-Start-Example /// - public class Client + public class Manager { #region Public Constructors @@ -15,7 +17,7 @@ namespace EgwProxy.Ftp /// /// /// - public Client(string server, string userName, string passwd) + public Manager(string server, string userName, string passwd) { _server = server; _userName = userName; @@ -39,6 +41,20 @@ namespace EgwProxy.Ftp #region Public Methods + /// + /// Creazione directory remota + /// + /// Nome directory remota da creare (ad es: @"/public_html/videos") + public bool createDir(string remoteDir) + { + tryConnect(); + // upload della folder + files, cancellazione extra files = mirroring + bool answ = client.CreateDirectory(remoteDir); + // chiudo! + client.Disconnect(); + return answ; + } + /// /// Verifica esistenza directory su server FTP remoto /// @@ -46,11 +62,7 @@ namespace EgwProxy.Ftp /// public bool dirExists(string remotePath) { - // connect to the server and automatically detect working FTP settings - if (!client.IsConnected) - { - client.AutoConnect(); - } + tryConnect(); bool answ = client.DirectoryExists(remotePath); // chiudo! client.Disconnect(); @@ -64,17 +76,28 @@ namespace EgwProxy.Ftp /// public bool fileExists(string remotePath) { - // connect to the server and automatically detect working FTP settings - if (!client.IsConnected) - { - client.AutoConnect(); - } + tryConnect(); bool answ = client.FileExists(remotePath); // chiudo! client.Disconnect(); return answ; } + /// + /// Mostra contenuto directory remota + /// + /// Nome directory remota da leggere (ad es: @"/public_html/videos") + public List listDir(string remoteDir) + { + tryConnect(); + // upload della folder + files, cancellazione extra files = mirroring + var dirContent = client.GetListing(remoteDir); + client.Disconnect(); + var answ = dirContent.Select(x => x.Name).ToList(); + // chiudo! + return answ; + } + /// /// Caricamento singolo file /// @@ -82,11 +105,7 @@ namespace EgwProxy.Ftp /// NOme remoto file per caricamento (ad es: "/htdocs/MyVideo.mp4") public void sendFile(string fileName, string remoteName) { - // connect to the server and automatically detect working FTP settings - if (!client.IsConnected) - { - client.AutoConnect(); - } + tryConnect(); // effettuo caricamento puntuale client.UploadFile(fileName, remoteName); // chiudo! @@ -99,29 +118,34 @@ namespace EgwProxy.Ftp /// public bool serverOk() { - // connect to the server and automatically detect working FTP settings - if (!client.IsConnected) - { - client.AutoConnect(); - } + tryConnect(); bool answ = client.IsConnected; // chiudo! client.Disconnect(); return answ; } + /// + /// Restituisce tipo server remoto + /// + /// + public string serverType() + { + tryConnect(); + FtpServer srvType = client.ServerType; + // chiudo! + client.Disconnect(); + return $"{srvType}"; + } + /// /// Caricamento singolo file /// /// Path directory da inviare (ad es:@"C:\website\videos\") - /// NOme remoto file per caricamento (ad es: @"/public_html/videos") + /// Nome remoto file per caricamento (ad es: @"/public_html/videos") public void uploadDir(string dirPath, string remoteDir) { - // connect to the server and automatically detect working FTP settings - if (!client.IsConnected) - { - client.AutoConnect(); - } + tryConnect(); // upload della folder + files, cancellazione extra files = mirroring client.UploadDirectory(dirPath, remoteDir, FtpFolderSyncMode.Mirror); // chiudo! @@ -133,7 +157,9 @@ namespace EgwProxy.Ftp #region Protected Properties protected string _passwd { get; set; } = ""; + protected string _server { get; set; } = ""; + protected string _userName { get; set; } = ""; #endregion Protected Properties @@ -143,5 +169,40 @@ namespace EgwProxy.Ftp private FtpClient client { get; set; } #endregion Private Properties + + #region Private Methods + + private void Client_ValidateCertificate(FluentFTP.Client.BaseClient.BaseFtpClient control, FtpSslValidationEventArgs e) + { + //if (e.PolicyErrors == SslPolicyErrors.None || e.Certificate.GetRawCertDataString() == TrustedRawCertData) + //{ + e.Accept = true; + //} + //else + //{ + // throw new Exception($"{e.PolicyErrors}{Environment.NewLine}{GetCertificateDetails(e.Certificate)}"); + //} + } + + private void tryConnect() + { + // connect to the server and automatically detect working FTP settings + if (!client.IsConnected) + { + var profiles = client.AutoDetect(); +#if false + // if any profiles are found, print the code to the console + if (profiles.Count > 0) + { + var code = profiles[0].ToCode(); + Console.WriteLine(code); + } +#endif + client.ValidateCertificate += Client_ValidateCertificate; + client.AutoConnect(); + } + } + + #endregion Private Methods } } \ No newline at end of file From 9c2fb93bad81150852d819d7cfd7abeec96f5515 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Tue, 4 Oct 2022 12:22:03 +0200 Subject: [PATCH 4/9] Ancor aupdate test FTP --- EgwProxy.Ftp.Test/App.config | 4 +- EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj | 6 +- EgwProxy.Ftp.Test/Program.cs | 59 ++++++++--- EgwProxy.Ftp.Test/test/FileProva.txt | 1 + EgwProxy.Ftp/Manager.cs | 109 +++++++++++++++------ 5 files changed, 134 insertions(+), 45 deletions(-) create mode 100644 EgwProxy.Ftp.Test/test/FileProva.txt diff --git a/EgwProxy.Ftp.Test/App.config b/EgwProxy.Ftp.Test/App.config index 3d6ca2c2..2125c56b 100644 --- a/EgwProxy.Ftp.Test/App.config +++ b/EgwProxy.Ftp.Test/App.config @@ -6,7 +6,9 @@ - + + + diff --git a/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj b/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj index 893c6780..7aa0bccc 100644 --- a/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj +++ b/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj @@ -64,6 +64,10 @@ EgwProxy.Ftp - + + + Always + + \ No newline at end of file diff --git a/EgwProxy.Ftp.Test/Program.cs b/EgwProxy.Ftp.Test/Program.cs index ed897449..d97e0c11 100644 --- a/EgwProxy.Ftp.Test/Program.cs +++ b/EgwProxy.Ftp.Test/Program.cs @@ -36,8 +36,10 @@ namespace EgwProxy.Ftp.Test private static void Main(string[] args) { Console.WriteLine("------------ Connessione Server FTP ------------"); - - var ftpClient = new Manager(ReadSetting("server"), ReadSetting("userName"), ReadSetting("passwd")); + string sSkipCert = ReadSetting("skipCert"); + bool skipCert = false; + bool.TryParse(sSkipCert, out skipCert); + var ftpClient = new Manager(ReadSetting("server"), ReadSetting("userName"), ReadSetting("passwd"), ReadSetting("rawCert"), skipCert); Console.WriteLine(); var testServer = ftpClient.serverOk(); Console.WriteLine($"Test connessione: esito {testServer}"); @@ -45,31 +47,58 @@ namespace EgwProxy.Ftp.Test Console.WriteLine($"Server: {srvType}"); Console.WriteLine(); - Console.WriteLine("--- Create folder ---"); - var testCreate = ftpClient.createDir("data/test_folder"); - Console.WriteLine(); - var folderContent = ftpClient.listDir("data/"); + + Console.WriteLine("--- Folder ---"); + var testExists = ftpClient.dirExists($"data/test_folder"); + if (testExists) + { + Console.WriteLine("Folder already exists!"); + } + else + { + Console.WriteLine("--- Create folder ---"); + var testCreate = ftpClient.createDir($"data/test_folder"); + Console.WriteLine(); + } + + Console.WriteLine("Contenuto folder remota: "); + var folderContent = ftpClient.listDir("data/", true); foreach (var item in folderContent) { - Console.Write(item); + Console.WriteLine(item); } Console.WriteLine(); Console.WriteLine("Premere un tasto x continuare..."); Console.ReadKey(); - //Console.WriteLine("--- Upload ---"); + Console.WriteLine("--- Upload file ---"); + ftpClient.sendFile("test/FileProva.txt", "data/test_folder/FileProva.txt"); + Console.WriteLine("Contenuto folder remota: "); + folderContent = ftpClient.listDir("data/", true); + foreach (var item in folderContent) + { + Console.WriteLine(item); + } - //Console.WriteLine(); - //Console.WriteLine("Premere un tasto x continuare..."); - //Console.ReadKey(); + Console.WriteLine(); + Console.WriteLine("Premere un tasto x continuare..."); + Console.ReadKey(); - //Console.WriteLine("--- Download ---"); + Console.WriteLine("--- Download File ---"); - //Console.WriteLine(); - //Console.WriteLine("Premere un tasto x continuare..."); - //Console.ReadKey(); + ftpClient.getFile("test/FileProva_02.txt", "data/test_folder/FileProva.txt"); + Console.WriteLine("Contenuto folder remota: "); + folderContent = ftpClient.listDir("data/", true); + foreach (var item in folderContent) + { + Console.WriteLine(item); + } + + Console.WriteLine(); + Console.WriteLine("Premere un tasto x continuare..."); + Console.ReadKey(); #if false // oggetto x connessione al sizer icoel diff --git a/EgwProxy.Ftp.Test/test/FileProva.txt b/EgwProxy.Ftp.Test/test/FileProva.txt new file mode 100644 index 00000000..94a4fa6c --- /dev/null +++ b/EgwProxy.Ftp.Test/test/FileProva.txt @@ -0,0 +1 @@ +File di prova \ No newline at end of file diff --git a/EgwProxy.Ftp/Manager.cs b/EgwProxy.Ftp/Manager.cs index b55ed9d6..b264d047 100644 --- a/EgwProxy.Ftp/Manager.cs +++ b/EgwProxy.Ftp/Manager.cs @@ -1,6 +1,8 @@ using FluentFTP; +using System; using System.Collections.Generic; using System.Linq; +using System.Net.Security; namespace EgwProxy.Ftp { @@ -17,11 +19,15 @@ namespace EgwProxy.Ftp /// /// /// - public Manager(string server, string userName, string passwd) + /// + /// + public Manager(string server, string userName, string passwd, string rawCert, bool skipCert) { _server = server; _userName = userName; _passwd = passwd; + _skipCert = skipCert; + _rawCert = rawCert; if (!string.IsNullOrEmpty(server)) { // se ho user/pwd è autenticato... @@ -84,20 +90,71 @@ namespace EgwProxy.Ftp } /// - /// Mostra contenuto directory remota + /// Scaricamento intera directory, modalità MIRROR /// - /// Nome directory remota da leggere (ad es: @"/public_html/videos") - public List listDir(string remoteDir) + /// Path directory da inviare (ad es:@"C:\website\videos\") + /// Nome remoto file per caricamento (ad es: @"/public_html/videos") + public void getDir(string dirPath, string remoteDir) { tryConnect(); // upload della folder + files, cancellazione extra files = mirroring - var dirContent = client.GetListing(remoteDir); + client.DownloadDirectory(dirPath, remoteDir, FtpFolderSyncMode.Mirror); + // chiudo! client.Disconnect(); - var answ = dirContent.Select(x => x.Name).ToList(); + } + + /// + /// Download singolo file + /// + /// Path locale del file da inviare (ad es: @"C:\MyVideo.mp4") + /// NOme remoto file per caricamento (ad es: "/htdocs/MyVideo.mp4") + public void getFile(string fileName, string remoteName) + { + tryConnect(); + // effettuo caricamento puntuale + client.DownloadFile(fileName, remoteName); + // chiudo! + client.Disconnect(); + } + + /// + /// Mostra contenuto directory remota + /// + /// Nome directory remota da leggere (ad es: @"/public_html/videos") + /// Indica se fare search ricorsivo + public List listDir(string remoteDir, bool recurse) + { + tryConnect(); + // upload della folder + files, cancellazione extra files = mirroring + FtpListItem[] dirContent; + if (recurse) + { + dirContent = client.GetListing(remoteDir, FtpListOption.Recursive); + } + else + { + dirContent = client.GetListing(remoteDir); + } + client.Disconnect(); + var answ = dirContent.Select(x => $"{x.Type} - {x.Name}").ToList(); // chiudo! return answ; } + /// + /// Caricamento intera directory, modalità MIRROR + /// + /// Path directory da inviare (ad es:@"C:\website\videos\") + /// Nome remoto file per caricamento (ad es: @"/public_html/videos") + public void sendDir(string dirPath, string remoteDir) + { + tryConnect(); + // upload della folder + files, cancellazione extra files = mirroring + client.UploadDirectory(dirPath, remoteDir, FtpFolderSyncMode.Mirror); + // chiudo! + client.Disconnect(); + } + /// /// Caricamento singolo file /// @@ -138,26 +195,19 @@ namespace EgwProxy.Ftp return $"{srvType}"; } - /// - /// Caricamento singolo file - /// - /// Path directory da inviare (ad es:@"C:\website\videos\") - /// Nome remoto file per caricamento (ad es: @"/public_html/videos") - public void uploadDir(string dirPath, string remoteDir) - { - tryConnect(); - // upload della folder + files, cancellazione extra files = mirroring - client.UploadDirectory(dirPath, remoteDir, FtpFolderSyncMode.Mirror); - // chiudo! - client.Disconnect(); - } - #endregion Public Methods + #region Protected Fields + + protected bool _skipCert = false; + + #endregion Protected Fields + #region Protected Properties protected string _passwd { get; set; } = ""; + protected string _rawCert { get; set; } = ""; protected string _server { get; set; } = ""; protected string _userName { get; set; } = ""; @@ -174,14 +224,17 @@ namespace EgwProxy.Ftp private void Client_ValidateCertificate(FluentFTP.Client.BaseClient.BaseFtpClient control, FtpSslValidationEventArgs e) { - //if (e.PolicyErrors == SslPolicyErrors.None || e.Certificate.GetRawCertDataString() == TrustedRawCertData) - //{ - e.Accept = true; - //} - //else - //{ - // throw new Exception($"{e.PolicyErrors}{Environment.NewLine}{GetCertificateDetails(e.Certificate)}"); - //} + if (e.PolicyErrors == SslPolicyErrors.None || _skipCert || e.Certificate.GetRawCertDataString() == _rawCert) + { + e.Accept = true; + } + else + { + Console.WriteLine($"{e.PolicyErrors}"); + Console.WriteLine($"Cert:{Environment.NewLine}{e.Certificate}"); + Console.WriteLine($"RawString:{Environment.NewLine}{e.Certificate.GetRawCertDataString()}"); + throw new Exception($"{e.PolicyErrors}{Environment.NewLine}{e.Certificate.GetRawCertDataString()}"); + } } private void tryConnect() From be6e838a3601f9efba23e2ba17e83a7b57d310f5 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Tue, 4 Oct 2022 16:49:42 +0200 Subject: [PATCH 5/9] Completata implementaizone test FTP - errore post upl,oad file - ok altri metodi --- EgwProxy.Ftp.Test/App.config | 95 ++-- EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj | 11 + EgwProxy.Ftp.Test/Program.cs | 476 ++++++++++++++------- EgwProxy.Ftp.Test/TestSetup.cs | 42 ++ EgwProxy.Ftp.Test/conf/.placeholder | 1 + EgwProxy.Ftp.Test/conf/testSetup.json | 76 ++++ EgwProxy.Ftp.Test/packages.config | 1 + EgwProxy.Ftp/Manager.cs | 77 +++- 8 files changed, 577 insertions(+), 202 deletions(-) create mode 100644 EgwProxy.Ftp.Test/TestSetup.cs create mode 100644 EgwProxy.Ftp.Test/conf/.placeholder create mode 100644 EgwProxy.Ftp.Test/conf/testSetup.json diff --git a/EgwProxy.Ftp.Test/App.config b/EgwProxy.Ftp.Test/App.config index 2125c56b..321eae2c 100644 --- a/EgwProxy.Ftp.Test/App.config +++ b/EgwProxy.Ftp.Test/App.config @@ -1,50 +1,51 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj b/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj index 7aa0bccc..aa7db66a 100644 --- a/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj +++ b/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj @@ -39,6 +39,9 @@ ..\packages\Microsoft.Extensions.Logging.Abstractions.2.1.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll + + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + @@ -53,9 +56,14 @@ + + + Always + + @@ -69,5 +77,8 @@ Always + + + \ No newline at end of file diff --git a/EgwProxy.Ftp.Test/Program.cs b/EgwProxy.Ftp.Test/Program.cs index d97e0c11..520810f2 100644 --- a/EgwProxy.Ftp.Test/Program.cs +++ b/EgwProxy.Ftp.Test/Program.cs @@ -1,13 +1,14 @@ -using System; -using System.Collections.Generic; +using Newtonsoft.Json; +using System; using System.Configuration; -using System.Diagnostics; +using System.IO; +using static System.Net.Mime.MediaTypeNames; namespace EgwProxy.Ftp.Test { internal class Program { - #region Private Methods + #region Protected Methods /// /// legge conf in formato stringa @@ -29,160 +30,343 @@ namespace EgwProxy.Ftp.Test return answ; } + #endregion Protected Methods + + #region Private Fields + + /// + /// Helper separatore dash + /// + private const string separator = "------------------------"; + + #endregion Private Fields + + #region Private Methods + /// /// Programma principale /// /// private static void Main(string[] args) { - Console.WriteLine("------------ Connessione Server FTP ------------"); - string sSkipCert = ReadSetting("skipCert"); - bool skipCert = false; - bool.TryParse(sSkipCert, out skipCert); - var ftpClient = new Manager(ReadSetting("server"), ReadSetting("userName"), ReadSetting("passwd"), ReadSetting("rawCert"), skipCert); + Console.WriteLine(separator); + Console.WriteLine("Test FTP Client"); + Console.WriteLine(separator); Console.WriteLine(); + string exePath = System.Reflection.Assembly.GetExecutingAssembly().Location; + string BaseDirectory = System.IO.Path.GetDirectoryName(exePath); + string testFile = Path.Combine(BaseDirectory, ReadSetting("testFile")); + Manager ftpClient = new Manager("", "", "", "", false); + if (!string.IsNullOrEmpty(testFile)) + { + Console.WriteLine(separator); + Console.WriteLine($"Mode json ({testFile})"); + Console.WriteLine(separator); + Console.WriteLine(); + if (File.Exists(testFile)) + { + var rawData = File.ReadAllText(testFile); + if (!string.IsNullOrEmpty(rawData)) + { + TestSetup testConf = new TestSetup(); + try + { + testConf = JsonConvert.DeserializeObject(rawData); + } + catch + { } + // setup server + ftpClient = new Manager(testConf.server, testConf.user, testConf.password, testConf.rawCert, testConf.skipCert); + serverTest(ftpClient); + + // eseguo per ogni step + foreach (var item in testConf.steps) + { + Console.WriteLine($"------ Step {item.id} | {item.description} ------"); + string esitoStep = ""; + switch (item.action) + { + case stepType.checkDir: + if (item.paramList != null && item.paramList.Count > 0) + { + string dir2check = item.paramList[0]; + var dirCheckExists = ftpClient.dirExists(dir2check); + esitoStep = dirCheckExists ? $"Directory {dir2check} found!" : $"Directory {dir2check} NOT found!"; + } + else + { + esitoStep = "Error: missing parameter!"; + } + break; + + case stepType.checkFile: + if (item.paramList != null && item.paramList.Count > 0) + { + string file2check = item.paramList[0]; + var dirCheckExists = ftpClient.fileExists(file2check); + esitoStep = dirCheckExists ? $"File {file2check} found!" : $"File {file2check} NOT found!"; + } + else + { + esitoStep = "Error: missing parameter!"; + } + break; + + case stepType.createDir: + if (item.paramList != null && item.paramList.Count > 0) + { + string dir2check = item.paramList[0]; + var preTest = ftpClient.dirExists(dir2check); + if (preTest) + { + esitoStep = "Error: Folder already exists!"; + } + else + { + var dirCreate = ftpClient.createDir(dir2check); + esitoStep = dirCreate ? $"Directory {dir2check} created!" : $"Error: {dir2check} NOT created!"; + } + + } + else + { + esitoStep = "Error: missing parameter!"; + } + break; + + case stepType.delDir: + if (item.paramList != null && item.paramList.Count > 0) + { + string dir2check = item.paramList[0]; + var preTest = ftpClient.dirExists(dir2check); + if (preTest) + { + var dirDelete = ftpClient.deleteDir(dir2check); + esitoStep = dirDelete ? $"Directory {dir2check} deleted!" : $"Error: {dir2check} NOT deleted!"; + } + else + { + esitoStep = "Error: Folder doesn't exists, delete not possible!"; + } + + } + else + { + esitoStep = "Error: missing parameter!"; + } + break; + + case stepType.delFile: + if (item.paramList != null && item.paramList.Count > 0) + { + string file2check = item.paramList[0]; + var preTest = ftpClient.fileExists(file2check); + if (preTest) + { + var dirDelete = ftpClient.deleteFile(file2check); + esitoStep = dirDelete ? $"File {file2check} deleted!" : $"Error: {file2check} NOT deleted!"; + } + else + { + esitoStep = "Error: File doesn't exists, delete not possible!"; + } + + } + else + { + esitoStep = "Error: missing parameter!"; + } + break; + + case stepType.downloadDir: + if (item.paramList != null && item.paramList.Count > 1) + { + string dir2check = item.paramList[0]; + string localDir = Path.Combine(BaseDirectory, item.paramList[1]); + var preTest = ftpClient.dirExists(dir2check); + if (preTest) + { + var dirDelete = ftpClient.getDir(localDir, dir2check); + esitoStep = dirDelete ? $"Directory {dir2check} downloaded!" : $"Error: {dir2check} NOT downloaded!"; + } + else + { + esitoStep = "Error: Folder doesn't exists, download not possible!"; + } + + } + else + { + esitoStep = "Error: missing parameters!"; + } + break; + + case stepType.downloadFile: + if (item.paramList != null && item.paramList.Count > 1) + { + string dir2check = item.paramList[0]; + string localDir = Path.Combine(BaseDirectory, item.paramList[1]); + var preTest = ftpClient.dirExists(dir2check); + if (preTest) + { + var dirDelete = ftpClient.getDir(localDir, dir2check); + esitoStep = dirDelete ? $"Directory {dir2check} downloaded!" : $"Error: {dir2check} NOT downloaded!"; + } + else + { + esitoStep = "Error: Folder doesn't exists, download not possible!"; + } + + } + else + { + esitoStep = "Error: missing parameters!"; + } + break; + + case stepType.uploadDir: + if (item.paramList != null && item.paramList.Count > 1) + { + string remoteDir = item.paramList[0]; + string localDir = Path.Combine(BaseDirectory, item.paramList[1]); + var dirUploaded = ftpClient.sendDir(localDir, remoteDir); + esitoStep = dirUploaded ? $"Directory {remoteDir} uploaded!" : $"Error: {remoteDir} NOT uploaded!"; + } + else + { + esitoStep = "Error: missing parameters!"; + } + break; + + case stepType.uploadFile: + if (item.paramList != null && item.paramList.Count > 1) + { + string remotePath = item.paramList[0]; + string localPath = Path.Combine(BaseDirectory, item.paramList[1]); + var fileUploaded = ftpClient.sendFile(localPath, remotePath); + esitoStep = fileUploaded ? $"File {remotePath} uploaded!" : $"Error: {remotePath} NOT uploaded!"; + } + else + { + esitoStep = "Error: missing parameters!"; + } + break; + + case stepType.listContent: + if (item.paramList != null && item.paramList.Count > 0) + { + string remoteDir = item.paramList[0]; + var preTest = ftpClient.dirExists(remoteDir); + if (preTest) + { + var listResult = ftpClient.listDir(remoteDir, false); + Console.WriteLine($"Content of dir {remoteDir}:"); + foreach (var itemList in listResult) + { + Console.WriteLine(itemList); + } + } + else + { + esitoStep = "Error: Folder doesn't exists, list not possible!"; + } + + } + else + { + esitoStep = "Error: missing parameter!"; + } + break; + + default: + break; + } + + + Console.WriteLine(esitoStep); + Console.WriteLine($"------ Done Step {item.id} ------"); + Console.WriteLine(); + Console.WriteLine("Press a key to continue..."); + Console.ReadKey(); + } + } + } + } + // test base su server steamware + else + { + Console.WriteLine(separator); + Console.WriteLine("Mode: base"); + Console.WriteLine(separator); + Console.WriteLine(); + + string sSkipCert = ReadSetting("skipCert"); + bool skipCert = false; + bool.TryParse(sSkipCert, out skipCert); + ftpClient = new Manager(ReadSetting("server"), ReadSetting("userName"), ReadSetting("passwd"), ReadSetting("rawCert"), skipCert); + serverTest(ftpClient); + + Console.WriteLine("--- Folder ---"); + var testExists = ftpClient.dirExists($"data/test_folder"); + if (testExists) + { + Console.WriteLine("Folder already exists!"); + } + else + { + Console.WriteLine("--- Create folder ---"); + var testCreate = ftpClient.createDir($"data/test_folder"); + Console.WriteLine(); + } + + Console.WriteLine("Contenuto folder remota: "); + var folderContent = ftpClient.listDir("data/", true); + foreach (var item in folderContent) + { + Console.WriteLine(item); + } + Console.WriteLine(); + Console.WriteLine("Premere un tasto x continuare..."); + Console.ReadKey(); + + Console.WriteLine("--- Upload file ---"); + ftpClient.sendFile("test/FileProva.txt", "data/test_folder/FileProva.txt"); + Console.WriteLine("Contenuto folder remota: "); + folderContent = ftpClient.listDir("data/", true); + foreach (var item in folderContent) + { + Console.WriteLine(item); + } + + Console.WriteLine(); + Console.WriteLine("Premere un tasto x continuare..."); + Console.ReadKey(); + + Console.WriteLine("--- Download File ---"); + + ftpClient.getFile("test/FileProva_02.txt", "data/test_folder/FileProva.txt"); + Console.WriteLine("Contenuto folder remota: "); + folderContent = ftpClient.listDir("data/", true); + foreach (var item in folderContent) + { + Console.WriteLine(item); + } + + Console.WriteLine(); + Console.WriteLine("Premere un tasto x continuare..."); + Console.ReadKey(); + } + } + + private static void serverTest(Manager ftpClient) + { + Console.WriteLine(separator); var testServer = ftpClient.serverOk(); Console.WriteLine($"Test connessione: esito {testServer}"); var srvType = ftpClient.serverType(); Console.WriteLine($"Server: {srvType}"); + Console.WriteLine(separator); Console.WriteLine(); - - - Console.WriteLine("--- Folder ---"); - var testExists = ftpClient.dirExists($"data/test_folder"); - if (testExists) - { - Console.WriteLine("Folder already exists!"); - } - else - { - Console.WriteLine("--- Create folder ---"); - var testCreate = ftpClient.createDir($"data/test_folder"); - Console.WriteLine(); - } - - Console.WriteLine("Contenuto folder remota: "); - var folderContent = ftpClient.listDir("data/", true); - foreach (var item in folderContent) - { - Console.WriteLine(item); - } - Console.WriteLine(); - Console.WriteLine("Premere un tasto x continuare..."); - Console.ReadKey(); - - - Console.WriteLine("--- Upload file ---"); - ftpClient.sendFile("test/FileProva.txt", "data/test_folder/FileProva.txt"); - Console.WriteLine("Contenuto folder remota: "); - folderContent = ftpClient.listDir("data/", true); - foreach (var item in folderContent) - { - Console.WriteLine(item); - } - - Console.WriteLine(); - Console.WriteLine("Premere un tasto x continuare..."); - Console.ReadKey(); - - - Console.WriteLine("--- Download File ---"); - - ftpClient.getFile("test/FileProva_02.txt", "data/test_folder/FileProva.txt"); - Console.WriteLine("Contenuto folder remota: "); - folderContent = ftpClient.listDir("data/", true); - foreach (var item in folderContent) - { - Console.WriteLine(item); - } - - Console.WriteLine(); - Console.WriteLine("Premere un tasto x continuare..."); - Console.ReadKey(); - -#if false - // oggetto x connessione al sizer icoel - Connector IcoelSizer = new Connector(ReadSetting("IndirizzoIpSizer"), ReadSetting("SizerTcpPort")); - //Connector IcoelSizer = new Connector(setup.IndirizzoIpSizer, setup.SizerTcpPort); - - Console.WriteLine("------------ Parametri CurrBatch rilevati ------------"); - var cBatch = IcoelSizer.GetCurrBatchData(); - if (cBatch != null) - { - DisplayCurrBatch(cBatch); - } - - // ora effettua un pò di letture/scritture - try - { - Console.WriteLine("------------ TUTTE variety ------------"); - var varList = IcoelSizer.GetVarietyList(false); - var varietyData = IcoelSizer.GetLayoutForVarietyList(varList); - if (varietyData != null) - { - DisplayVarietyLayout(varietyData); - } - Console.WriteLine(); - Console.WriteLine("Premere un tasto x continuare..."); - userInput = Console.ReadLine(); - - Console.WriteLine("------------ Parametri velocità rilevati ------------"); - var perfMeter = IcoelSizer.GetPerfMeters(); - if (perfMeter != null) - { - DisplayPerfMeter(perfMeter); - } - Console.WriteLine(); - Console.WriteLine("Premere un tasto x continuare..."); - userInput = Console.ReadLine(); - - // solo attive - Console.WriteLine("------------ solo attive ------------"); - varList = IcoelSizer.GetVarietyList(); - varietyData = IcoelSizer.GetLayoutForVarietyList(varList); - if (varietyData != null) - { - DisplayVarietyLayout(varietyData); - } - Console.WriteLine(); - Console.WriteLine("Premere un tasto x continuare..."); - userInput = Console.ReadLine(); - - Console.WriteLine("------------ BATCH correnti ------------"); - var currBatch = IcoelSizer.GetCurrentBatch(); - foreach (var item in currBatch) - { - string lato = item.Key == 1 ? "SX" : "DX"; - Console.WriteLine($"[{item.Key}-{lato}] Grower code: {item.Value.GrowerCode} | Layout Name: {item.Value.LayoutName} | Totalling: [{item.Value.TotallingVarietyCode}] {item.Value.TotallingVariety} | Sizing: {item.Value.SizingProfileName} | Start {item.Value.StartTime} | End {item.Value.EndTime}"); - } - Console.WriteLine(); - Console.WriteLine("Premere un tasto x continuare..."); - userInput = Console.ReadLine(); - - Console.WriteLine("------------ Prova invio BATCH ------------"); - // recupero GUID x variety e layout - var varGuid = selVariety(IcoelSizer); - var layGuid = selLayout(IcoelSizer, varGuid); - - GrowerInfo GrowerData = new GrowerInfo(); - IcoelSizer.EnqueueBatch(GrowerData, varGuid, layGuid); - - currBatch = IcoelSizer.GetCurrentBatch(); - foreach (var item in currBatch) - { - string lato = item.Key == 1 ? "SX" : "DX"; - Console.WriteLine($"[{item.Key}-{lato}] Grower code: {item.Value.GrowerCode} | Layout Name: {item.Value.LayoutName} | Totalling: [{item.Value.TotallingVarietyCode}] {item.Value.TotallingVariety} | Sizing: {item.Value.SizingProfileName} | Start {item.Value.StartTime} | End {item.Value.EndTime}"); - } - Console.WriteLine("Test completato"); - Console.WriteLine("Premere un tasto x chiudere"); - Console.ReadKey(); - } - catch (Exception ex) - { - Console.WriteLine("ECCEZIONE" + ex.Message + ex.StackTrace); - Console.ReadKey(); - } -#endif } #endregion Private Methods diff --git a/EgwProxy.Ftp.Test/TestSetup.cs b/EgwProxy.Ftp.Test/TestSetup.cs new file mode 100644 index 00000000..d00f3e10 --- /dev/null +++ b/EgwProxy.Ftp.Test/TestSetup.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EgwProxy.Ftp.Test +{ + public class TestSetup + { + public string server { get; set; } = ""; + public string user { get; set; } = ""; + public string password { get; set; } = ""; + public string rawCert { get; set; } = ""; + public bool skipCert { get; set; } = false; + public List steps { get; set; } + } + + public class singleStep + { + public string id { get; set; } = "00"; + public string description { get; set; } = "00"; + public stepType action { get; set; } = stepType.checkDir; + + public List paramList { get; set; } = new List(); + } + + public enum stepType + { + checkDir, + checkFile, + createDir, + delDir, + delFile, + downloadDir, + downloadFile, + uploadDir, + uploadFile, + listContent + } + +} diff --git a/EgwProxy.Ftp.Test/conf/.placeholder b/EgwProxy.Ftp.Test/conf/.placeholder new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/EgwProxy.Ftp.Test/conf/.placeholder @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/EgwProxy.Ftp.Test/conf/testSetup.json b/EgwProxy.Ftp.Test/conf/testSetup.json new file mode 100644 index 00000000..f50719b2 --- /dev/null +++ b/EgwProxy.Ftp.Test/conf/testSetup.json @@ -0,0 +1,76 @@ +{ + "server": "ftp.steamware.net", + "user": "testftpuser", + "password": "we4reFromB3rghem!", + "rawCert": "", + "skipCert": true, + "steps": [ + { + "id": "01", + "description": "Test base directory", + "action": "checkDir", + "paramList": [ + "data/" + ] + }, + { + "id": "02", + "description": "Create directory", + "action": "createDir", + "paramList": [ + "data/test_directory" + ] + }, + { + "id": "03", + "description": "Upload File", + "action": "uploadFile", + "paramList": [ + "data/test_directory/FileProva.txt", + "test/FileProva.txt" + ] + }, + { + "id": "04", + "description": "Browse Directory", + "action": "listContent", + "paramList": [ + "data/test_directory/" + ] + }, + { + "id": "05", + "description": "Upload File", + "action": "uploadFile", + "paramList": [ + "data/test_directory/FileProva_02.txt", + "test/FileProva.txt" + ] + }, + { + "id": "06", + "description": "Browse Directory", + "action": "listContent", + "paramList": [ + "data/test_directory/" + ] + }, + { + "id": "07", + "description": "Download Directory", + "action": "downloadDir", + "paramList": [ + "data/test_directory/", + "temp/mirror" + ] + }, + { + "id": "08", + "description": "Delete Remote Directory", + "action": "delDir", + "paramList": [ + "data/test_directory/" + ] + } + ] +} \ No newline at end of file diff --git a/EgwProxy.Ftp.Test/packages.config b/EgwProxy.Ftp.Test/packages.config index e11a86c4..657b8280 100644 --- a/EgwProxy.Ftp.Test/packages.config +++ b/EgwProxy.Ftp.Test/packages.config @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/EgwProxy.Ftp/Manager.cs b/EgwProxy.Ftp/Manager.cs index b264d047..a7de4f41 100644 --- a/EgwProxy.Ftp/Manager.cs +++ b/EgwProxy.Ftp/Manager.cs @@ -61,6 +61,48 @@ namespace EgwProxy.Ftp return answ; } + /// + /// Eliminazionedirectory remota + /// + /// Nome directory remota da eliminare + public bool deleteDir(string remoteDir) + { + tryConnect(); + bool answ = false; + try + { + // Elimina folder + client.DeleteDirectory(remoteDir); + answ = true; + } + catch + { } + // chiudo! + client.Disconnect(); + return answ; + } + + /// + /// Eliminazionedirectory remota + /// + /// Nome file remoto da eliminare + public bool deleteFile(string remoteFile) + { + tryConnect(); + bool answ = false; + try + { + // Elimina folder + client.DeleteFile(remoteFile); + answ = true; + } + catch + { } + // chiudo! + client.Disconnect(); + return answ; + } + /// /// Verifica esistenza directory su server FTP remoto /// @@ -94,13 +136,21 @@ namespace EgwProxy.Ftp /// /// Path directory da inviare (ad es:@"C:\website\videos\") /// Nome remoto file per caricamento (ad es: @"/public_html/videos") - public void getDir(string dirPath, string remoteDir) + public bool getDir(string dirPath, string remoteDir) { + bool answ = false; tryConnect(); - // upload della folder + files, cancellazione extra files = mirroring - client.DownloadDirectory(dirPath, remoteDir, FtpFolderSyncMode.Mirror); + try + { + // upload della folder + files, cancellazione extra files = mirroring + var result = client.DownloadDirectory(dirPath, remoteDir, FtpFolderSyncMode.Mirror); + answ = (result != null && result.Count > 0); + } + catch + { } // chiudo! client.Disconnect(); + return answ; } /// @@ -108,13 +158,16 @@ namespace EgwProxy.Ftp /// /// Path locale del file da inviare (ad es: @"C:\MyVideo.mp4") /// NOme remoto file per caricamento (ad es: "/htdocs/MyVideo.mp4") - public void getFile(string fileName, string remoteName) + public bool getFile(string fileName, string remoteName) { + bool answ = false; tryConnect(); // effettuo caricamento puntuale - client.DownloadFile(fileName, remoteName); + var result = client.DownloadFile(fileName, remoteName); + answ = result == FtpStatus.Success; // chiudo! client.Disconnect(); + return answ; } /// @@ -146,13 +199,16 @@ namespace EgwProxy.Ftp /// /// Path directory da inviare (ad es:@"C:\website\videos\") /// Nome remoto file per caricamento (ad es: @"/public_html/videos") - public void sendDir(string dirPath, string remoteDir) + public bool sendDir(string dirPath, string remoteDir) { + bool answ = false; tryConnect(); // upload della folder + files, cancellazione extra files = mirroring - client.UploadDirectory(dirPath, remoteDir, FtpFolderSyncMode.Mirror); + var result = client.UploadDirectory(dirPath, remoteDir, FtpFolderSyncMode.Mirror); + answ = (result != null && result.Count > 0); // chiudo! client.Disconnect(); + return answ; } /// @@ -160,13 +216,16 @@ namespace EgwProxy.Ftp /// /// Path locale del file da inviare (ad es: @"C:\MyVideo.mp4") /// NOme remoto file per caricamento (ad es: "/htdocs/MyVideo.mp4") - public void sendFile(string fileName, string remoteName) + public bool sendFile(string fileName, string remoteName) { + bool answ = false; tryConnect(); // effettuo caricamento puntuale - client.UploadFile(fileName, remoteName); + var result = client.UploadFile(fileName, remoteName); + answ = result == FtpStatus.Success; // chiudo! client.Disconnect(); + return answ; } /// From e9f5244b81e0a79251fc36e6fd1087f92d00b335 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Tue, 4 Oct 2022 16:52:44 +0200 Subject: [PATCH 6/9] Fix metodo controllo upload file --- EgwProxy.Ftp/Manager.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/EgwProxy.Ftp/Manager.cs b/EgwProxy.Ftp/Manager.cs index a7de4f41..389e0105 100644 --- a/EgwProxy.Ftp/Manager.cs +++ b/EgwProxy.Ftp/Manager.cs @@ -223,6 +223,11 @@ namespace EgwProxy.Ftp // effettuo caricamento puntuale var result = client.UploadFile(fileName, remoteName); answ = result == FtpStatus.Success; + // se insuccesso --> controllo se ci sia file... + if (!answ) + { + answ = fileExists(remoteName); + } // chiudo! client.Disconnect(); return answ; From be51449c624d4f0b4f7557522c1237e69bb10efc Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Tue, 4 Oct 2022 16:57:56 +0200 Subject: [PATCH 7/9] Update YAML x nuovo SDK Ftp --- .gitlab-ci.yml | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e4b8b668..bba601d3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -201,6 +201,23 @@ IOB-WIN-NEXT:build: script: - '& "$env:MSBUILD_PATH" "$env:APP_NAME\$env:APP_NAME.csproj" -target:Build /p:Configuration=Release /p:Platform="x86" /p:OutputPath=bin/ /nodeReuse:false /verbosity:minimal /m' +EgwProxy.Ftp:build: + stage: build + tags: + - win + variables: + APP_NAME: EgwProxy.Ftp + only: + refs: + - develop + - SDK/Ftp + before_script: + - *nuget-fix + - '& "$env:NUGET_PATH" restore "$env:APP_NAME.sln" -verbosity quiet' + - *version-fix + script: + - '& "$env:MSBUILD_PATH" "$env:APP_NAME\$env:APP_NAME.csproj" -target:Build /p:Configuration=Release /p:Platform="x86" /p:OutputPath=bin/ /nodeReuse:false /verbosity:minimal /m' + EgwProxy.Icoel:build: stage: build tags: @@ -252,9 +269,33 @@ EgwProxy.OsaiCncLib:build: script: - '& "$env:MSBUILD_PATH" "$env:APP_NAME\$env:APP_NAME.vbproj" -target:Build /p:Configuration=Release /p:Platform="x86" /p:OutputPath=bin/ /nodeReuse:false /verbosity:minimal /m' + # -------------------------------- # STAGING: (nuget beta) # -------------------------------- +EgwProxy.Ftp:build:staging: + stage: staging + needs: ["EgwProxy.Ftp:build"] + tags: + - win + variables: + CONFIG: Debug + APP_NAME: EgwProxy.Ftp + only: + refs: + - develop + before_script: + - *nuget-fix + - '& "$env:NUGET_PATH" restore "$env:APP_NAME.sln" -verbosity quiet' + - *version-fix + - *nuspec-fix + script: + - '& "$env:MSBUILD_PATH" "$env:APP_NAME\$env:APP_NAME.csproj" -target:Build /p:Configuration=$env:CONFIG /p:Platform="Any CPU" /p:OutputPath=bin/$env:CONFIG /verbosity:minimal /m' + - '& Remove-Item *.nupkg' + - '& $env:NUGET_PATH pack "$env:APP_NAME.Debug.nuspec"' + - '& "$env:NUGET_PATH" setapikey fe387daa-d07c-3207-877e-96c8be1be91b -source http://nexus.steamware.net/repository/nuget-hosted' + - '& "$env:NUGET_PATH" push *$env:NUM_DEB.nupkg -Source http://nexus.steamware.net/repository/nuget-hosted' + EgwProxy.Icoel:build:staging: stage: staging needs: ["EgwProxy.Icoel:build"] @@ -398,6 +439,30 @@ IOB-WIN-NEXT:release: - *hashBuild - *nexusUpload +EgwProxy.Ftp:build:release: + stage: release + needs: ["EgwProxy.Ftp:build"] + tags: + - win + variables: + CONFIG: Release + APP_NAME: EgwProxy.Ftp + only: + refs: + - SDK/Ftp + before_script: + - *nuget-fix + - '& "$env:NUGET_PATH" restore "$env:APP_NAME.sln" -verbosity quiet' + - *version-fix + - *nuspec-fix + script: + - '& "$env:MSBUILD_PATH" "$env:APP_NAME\$env:APP_NAME.csproj" -target:Build /p:Configuration=$env:CONFIG /p:Platform="Any CPU" /p:OutputPath=bin/$env:CONFIG /verbosity:minimal /m' + - '& Remove-Item *.nupkg' + - '& $env:NUGET_PATH pack "$env:APP_NAME.Release.nuspec"' +# - '& "$env:NUGET_PATH" pack "$env:APP_NAME\$env:APP_NAME.csproj" -properties Configuration=$env:CONFIG -Version $env:NUM_REL' + - '& "$env:NUGET_PATH" setapikey fe387daa-d07c-3207-877e-96c8be1be91b -source http://nexus.steamware.net/repository/nuget-hosted' + - '& "$env:NUGET_PATH" push *$env:NUM_REL.nupkg -Source http://nexus.steamware.net/repository/nuget-hosted' + EgwProxy.Icoel:build:release: stage: release needs: ["EgwProxy.Icoel:build"] @@ -472,6 +537,20 @@ EgwProxy.OsaiCncLib:build:release: # -------------------------------- # DocFx # -------------------------------- +EgwProxy.Ftp:docfx: + stage: docfx + needs: ["EgwProxy.Ftp:build"] + tags: + - win + variables: + APP_NAME: EgwProxy.Ftp + only: + - SDK/Ftp + script: + - docfx $env:APP_NAME/docfx.json + - mv $env:APP_NAME/_site "docfx" + - *DocReplica + EgwProxy.Icoel:docfx: stage: docfx needs: ["EgwProxy.Icoel:build"] From d233c5136450e654e075443af6585438b08ba9e8 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Tue, 4 Oct 2022 18:27:59 +0200 Subject: [PATCH 8/9] Update progetto netFramework 4.6.2 x FTP --- EgwProxy.FTP.sln | 16 +++--- EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj | 12 ++--- EgwProxy.Ftp/EgwProxy.Ftp.csproj | 60 +++++++++++++++++++--- EgwProxy.Ftp/Properties/AssemblyInfo.cs | 36 +++++++++++++ EgwProxy.Ftp/packages.config | 5 ++ 5 files changed, 108 insertions(+), 21 deletions(-) create mode 100644 EgwProxy.Ftp/Properties/AssemblyInfo.cs create mode 100644 EgwProxy.Ftp/packages.config diff --git a/EgwProxy.FTP.sln b/EgwProxy.FTP.sln index c812fb12..20d16d7b 100644 --- a/EgwProxy.FTP.sln +++ b/EgwProxy.FTP.sln @@ -1,26 +1,26 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32616.157 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.32901.82 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EgwProxy.Ftp", "EgwProxy.Ftp\EgwProxy.Ftp.csproj", "{D03314DE-4EB2-4887-94B0-9CEB0C7090F9}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EgwProxy.Ftp.Test", "EgwProxy.Ftp.Test\EgwProxy.Ftp.Test.csproj", "{A655A6AC-5997-46F9-9752-8C621B80516C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EgwProxy.Ftp", "EgwProxy.Ftp\EgwProxy.Ftp.csproj", "{35D95ED8-E48A-434D-A305-A83E48C8FC6F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D03314DE-4EB2-4887-94B0-9CEB0C7090F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D03314DE-4EB2-4887-94B0-9CEB0C7090F9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D03314DE-4EB2-4887-94B0-9CEB0C7090F9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D03314DE-4EB2-4887-94B0-9CEB0C7090F9}.Release|Any CPU.Build.0 = Release|Any CPU {A655A6AC-5997-46F9-9752-8C621B80516C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A655A6AC-5997-46F9-9752-8C621B80516C}.Debug|Any CPU.Build.0 = Debug|Any CPU {A655A6AC-5997-46F9-9752-8C621B80516C}.Release|Any CPU.ActiveCfg = Release|Any CPU {A655A6AC-5997-46F9-9752-8C621B80516C}.Release|Any CPU.Build.0 = Release|Any CPU + {35D95ED8-E48A-434D-A305-A83E48C8FC6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35D95ED8-E48A-434D-A305-A83E48C8FC6F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35D95ED8-E48A-434D-A305-A83E48C8FC6F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35D95ED8-E48A-434D-A305-A83E48C8FC6F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj b/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj index aa7db66a..8384cb13 100644 --- a/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj +++ b/EgwProxy.Ftp.Test/EgwProxy.Ftp.Test.csproj @@ -66,12 +66,6 @@ - - - {d03314de-4eb2-4887-94b0-9ceb0c7090f9} - EgwProxy.Ftp - - Always @@ -80,5 +74,11 @@ + + + {35d95ed8-e48a-434d-a305-a83e48c8fc6f} + EgwProxy.Ftp + + \ No newline at end of file diff --git a/EgwProxy.Ftp/EgwProxy.Ftp.csproj b/EgwProxy.Ftp/EgwProxy.Ftp.csproj index 83684d1a..760b0985 100644 --- a/EgwProxy.Ftp/EgwProxy.Ftp.csproj +++ b/EgwProxy.Ftp/EgwProxy.Ftp.csproj @@ -1,11 +1,57 @@ - - + + + - netstandard2.0 + Debug + AnyCPU + {35D95ED8-E48A-434D-A305-A83E48C8FC6F} + Library + Properties + EgwProxy.Ftp + EgwProxy.Ftp + v4.6.2 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 - - + + ..\packages\FluentFTP.41.0.0\lib\net462\FluentFTP.dll + + + ..\packages\Microsoft.Extensions.Logging.Abstractions.2.1.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll + + + + + + + + + - - + + + + + + + + + \ No newline at end of file diff --git a/EgwProxy.Ftp/Properties/AssemblyInfo.cs b/EgwProxy.Ftp/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..abba72de --- /dev/null +++ b/EgwProxy.Ftp/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("EgwProxy.Ftp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("EgwProxy.Ftp")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("35d95ed8-e48a-434d-a305-a83e48c8fc6f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/EgwProxy.Ftp/packages.config b/EgwProxy.Ftp/packages.config new file mode 100644 index 00000000..e11a86c4 --- /dev/null +++ b/EgwProxy.Ftp/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From e4db9763c473164db5e4da929c9d07b41c7e5477 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Tue, 4 Oct 2022 18:40:01 +0200 Subject: [PATCH 9/9] Update x FTP - conf x docFx - conf x nuget send --- EgwProxy.Ftp.Debug.nuspec | 20 ++++++++++ EgwProxy.Ftp.Release.nuspec | 19 ++++++++++ EgwProxy.Ftp.Test/docfx.json | 69 +++++++++++++++++++++++++++++++++++ EgwProxy.Ftp.Test/index.md | 17 +++++++++ EgwProxy.Ftp.Test/index.pdf | Bin 0 -> 28333 bytes EgwProxy.Ftp.Test/toc.yml | 6 +++ 6 files changed, 131 insertions(+) create mode 100644 EgwProxy.Ftp.Debug.nuspec create mode 100644 EgwProxy.Ftp.Release.nuspec create mode 100644 EgwProxy.Ftp.Test/docfx.json create mode 100644 EgwProxy.Ftp.Test/index.md create mode 100644 EgwProxy.Ftp.Test/index.pdf create mode 100644 EgwProxy.Ftp.Test/toc.yml diff --git a/EgwProxy.Ftp.Debug.nuspec b/EgwProxy.Ftp.Debug.nuspec new file mode 100644 index 00000000..18a977c3 --- /dev/null +++ b/EgwProxy.Ftp.Debug.nuspec @@ -0,0 +1,20 @@ + + + + EgwProxy.Ftp + #version# + EgwProxy.Ftp + Samuele E. Locatelli, EgalWare + false + MIT + Libreria per comunicazione con server Ftp (IOB-WIN-NEXT) - beta/unstable + #releaseNotes# + #copyright# + EgwProxy.Ftp EgwProxy Ftp + + + + + + + \ No newline at end of file diff --git a/EgwProxy.Ftp.Release.nuspec b/EgwProxy.Ftp.Release.nuspec new file mode 100644 index 00000000..428928da --- /dev/null +++ b/EgwProxy.Ftp.Release.nuspec @@ -0,0 +1,19 @@ + + + + EgwProxy.Ftp + #version# + EgwProxy.Ftp + Samuele E. Locatelli, EgalWare + false + MIT + Libreria per comunicazione con server Ftp (IOB-WIN-NEXT) + #releaseNotes# + #copyright# + EgwProxy.Ftp EgwProxy Ftp + + + + + + \ No newline at end of file diff --git a/EgwProxy.Ftp.Test/docfx.json b/EgwProxy.Ftp.Test/docfx.json new file mode 100644 index 00000000..435e3499 --- /dev/null +++ b/EgwProxy.Ftp.Test/docfx.json @@ -0,0 +1,69 @@ +{ + "metadata": [ + { + "src": [ + { + "files": [ + "*.csproj", + "*.vbproj" + ], + "cwd": ".", + "exclude": [ + "**/obj/**", + "**/bin/**", + "_site/**" + ] + } + ], + "dest": "obj/api" + } + ], + "build": { + "content": [ + { + "files": [ + "api/**.yml" + ], + "cwd": "obj" + }, + { + "files": [ + "api/*.md", + "articles/**.md", + "toc.yml", + "*.md" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "overwrite": [ + { + "files": [ + "apidoc/**.md" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "dest": "_site", + "template": [ + "default" + ] + } +} \ No newline at end of file diff --git a/EgwProxy.Ftp.Test/index.md b/EgwProxy.Ftp.Test/index.md new file mode 100644 index 00000000..2610c657 --- /dev/null +++ b/EgwProxy.Ftp.Test/index.md @@ -0,0 +1,17 @@ +# EgwProxy.Ftp Library + +Documentazione relativa alla libreria di interfaccia via FTP con server generici. + +Disponibile in forma di pacchetto nuget sul repo aziendale nexus.steamware.net: i pacchetti sono disponibili all'indirizzo + +https://nexus.steamware.net/#browse/browse:nuget-hosted + +Vedere la sezione Articles per maggiori informazioni sulle definizioni, l'impiego ed esempi. + +## Articles + +Per maggiori dettagli, definizioni e demo funzionamento si rimanda alla sezione Articles + +## Api + +Per ogni dettaglio e riferimento alla libreria si rimanda alla sezione Api Documentation \ No newline at end of file diff --git a/EgwProxy.Ftp.Test/index.pdf b/EgwProxy.Ftp.Test/index.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7d2af5fcb9d98cafe89a99fbb1851a1ce4a722b6 GIT binary patch literal 28333 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Ὸ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 b`J0rpchI+U_~Uticu}qdgMmRp;mv;mF&1d1 literal 0 HcmV?d00001 diff --git a/EgwProxy.Ftp.Test/toc.yml b/EgwProxy.Ftp.Test/toc.yml new file mode 100644 index 00000000..7eee9ffb --- /dev/null +++ b/EgwProxy.Ftp.Test/toc.yml @@ -0,0 +1,6 @@ + +- name: Articles + href: articles/ +- name: API Documentation + href: obj/api/ + homepage: api/index.md