From 982b4ec1988802a81d610d43ca18e716c67a5e82 Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Sun, 29 Oct 2023 17:04:24 +0300 Subject: [PATCH 01/15] Init commit --- C#/forSpbu/SimpleFtp/Program.cs | 3 +++ C#/forSpbu/SimpleFtp/SimpleFtp.cs | 6 ++++++ C#/forSpbu/SimpleFtp/SimpleFtp.csproj | 10 ++++++++++ C#/forSpbu/forSpbu.sln | 20 ++++++++++++++++---- 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 C#/forSpbu/SimpleFtp/Program.cs create mode 100644 C#/forSpbu/SimpleFtp/SimpleFtp.cs create mode 100644 C#/forSpbu/SimpleFtp/SimpleFtp.csproj diff --git a/C#/forSpbu/SimpleFtp/Program.cs b/C#/forSpbu/SimpleFtp/Program.cs new file mode 100644 index 0000000..e5dff12 --- /dev/null +++ b/C#/forSpbu/SimpleFtp/Program.cs @@ -0,0 +1,3 @@ +// See https://aka.ms/new-console-template for more information + +Console.WriteLine("Hello, World!"); \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp/SimpleFtp.cs b/C#/forSpbu/SimpleFtp/SimpleFtp.cs new file mode 100644 index 0000000..060980d --- /dev/null +++ b/C#/forSpbu/SimpleFtp/SimpleFtp.cs @@ -0,0 +1,6 @@ +namespace SimpleFtp; + +public class SimpleFtp +{ + +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp/SimpleFtp.csproj b/C#/forSpbu/SimpleFtp/SimpleFtp.csproj new file mode 100644 index 0000000..2b14c81 --- /dev/null +++ b/C#/forSpbu/SimpleFtp/SimpleFtp.csproj @@ -0,0 +1,10 @@ + + + + Exe + net7.0 + enable + enable + + + diff --git a/C#/forSpbu/forSpbu.sln b/C#/forSpbu/forSpbu.sln index 527f400..8cd78db 100644 --- a/C#/forSpbu/forSpbu.sln +++ b/C#/forSpbu/forSpbu.sln @@ -10,6 +10,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03.03", "03.03", "{882A9B9C EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "10.03", "10.03", "{EA6FC7D9-BDFB-49CD-AC00-FC5DDC5274B0}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023", "2023", "{6C82FFDE-03BA-44ED-B5E8-C90579DCF56D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "12.10", "12.10", "{7A21E6C1-9694-4896-AD34-C0BDB62BF665}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtp", "SimpleFtp\SimpleFtp.csproj", "{EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,12 +27,18 @@ Global {E007586F-9760-4744-BB25-EDEFD6BA860C}.Release|Any CPU.ActiveCfg = Release|Any CPU {E007586F-9760-4744-BB25-EDEFD6BA860C}.Release|Any CPU.Build.0 = Release|Any CPU {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Release|Any CPU.Build.0 = Release|Any CPU + {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E}.Release|Any CPU.Build.0 = Release|Any CPU + {EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution - {E007586F-9760-4744-BB25-EDEFD6BA860C} = {D3FCB669-E93F-4F0B-B9C5-6592CE93AC7F} + {E007586F-9760-4744-BB25-EDEFD6BA860C} = {D3FCB669-E93F-4F0B-B9C5-6592CE93AC7F} {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E} = {D3FCB669-E93F-4F0B-B9C5-6592CE93AC7F} + {7A21E6C1-9694-4896-AD34-C0BDB62BF665} = {6C82FFDE-03BA-44ED-B5E8-C90579DCF56D} + {EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5} = {7A21E6C1-9694-4896-AD34-C0BDB62BF665} EndGlobalSection EndGlobal From 4ec008bf5058022ab1e4a384de2d1cbb06b72e21 Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Mon, 30 Oct 2023 01:53:36 +0300 Subject: [PATCH 02/15] Checking request --- C#/forSpbu/SimpleFtp/Program.cs | 3 -- C#/forSpbu/SimpleFtp/SimpleFtp.cs | 6 --- C#/forSpbu/SimpleFtpClient/Program.cs | 8 ++++ .../SimpleFtpClient.csproj} | 0 C#/forSpbu/SimpleFtpServer/Program.cs | 6 +++ C#/forSpbu/SimpleFtpServer/Request.cs | 34 +++++++++++++++ C#/forSpbu/SimpleFtpServer/Response.cs | 11 +++++ C#/forSpbu/SimpleFtpServer/Server.cs | 41 +++++++++++++++++++ .../SimpleFtpServer/SimpleFtpServer.csproj | 11 +++++ C#/forSpbu/forSpbu.sln | 9 +++- 10 files changed, 119 insertions(+), 10 deletions(-) delete mode 100644 C#/forSpbu/SimpleFtp/Program.cs delete mode 100644 C#/forSpbu/SimpleFtp/SimpleFtp.cs create mode 100644 C#/forSpbu/SimpleFtpClient/Program.cs rename C#/forSpbu/{SimpleFtp/SimpleFtp.csproj => SimpleFtpClient/SimpleFtpClient.csproj} (100%) create mode 100644 C#/forSpbu/SimpleFtpServer/Program.cs create mode 100644 C#/forSpbu/SimpleFtpServer/Request.cs create mode 100644 C#/forSpbu/SimpleFtpServer/Response.cs create mode 100644 C#/forSpbu/SimpleFtpServer/Server.cs create mode 100644 C#/forSpbu/SimpleFtpServer/SimpleFtpServer.csproj diff --git a/C#/forSpbu/SimpleFtp/Program.cs b/C#/forSpbu/SimpleFtp/Program.cs deleted file mode 100644 index e5dff12..0000000 --- a/C#/forSpbu/SimpleFtp/Program.cs +++ /dev/null @@ -1,3 +0,0 @@ -// See https://aka.ms/new-console-template for more information - -Console.WriteLine("Hello, World!"); \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp/SimpleFtp.cs b/C#/forSpbu/SimpleFtp/SimpleFtp.cs deleted file mode 100644 index 060980d..0000000 --- a/C#/forSpbu/SimpleFtp/SimpleFtp.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace SimpleFtp; - -public class SimpleFtp -{ - -} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpClient/Program.cs b/C#/forSpbu/SimpleFtpClient/Program.cs new file mode 100644 index 0000000..f7bd332 --- /dev/null +++ b/C#/forSpbu/SimpleFtpClient/Program.cs @@ -0,0 +1,8 @@ +using System.Net; + +FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://127.0.0.1"); +request.Method = WebRequestMethods.Ftp.ListDirectory; +using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) +{ + response.GetResponseStream(); +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp/SimpleFtp.csproj b/C#/forSpbu/SimpleFtpClient/SimpleFtpClient.csproj similarity index 100% rename from C#/forSpbu/SimpleFtp/SimpleFtp.csproj rename to C#/forSpbu/SimpleFtpClient/SimpleFtpClient.csproj diff --git a/C#/forSpbu/SimpleFtpServer/Program.cs b/C#/forSpbu/SimpleFtpServer/Program.cs new file mode 100644 index 0000000..0b6422f --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Program.cs @@ -0,0 +1,6 @@ +// See https://aka.ms/new-console-template for more information + +using SimpleFtp; + +var server = new Request("2 ./Test/Files\n"); +Console.WriteLine("1"); diff --git a/C#/forSpbu/SimpleFtpServer/Request.cs b/C#/forSpbu/SimpleFtpServer/Request.cs new file mode 100644 index 0000000..7febae4 --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Request.cs @@ -0,0 +1,34 @@ +using System.Text.RegularExpressions; +namespace SimpleFtp; + +public abstract class Request +{ + public RequestType Type { get; private set; } + public string Path { get; private set; } + private readonly string _getPattern = $"({(int)RequestType.Get}) ([1-9a-zA-Z./]+)\n"; + private readonly string _listPattern = $"({(int)RequestType.List}) ([1-9a-zA-Z./]+)\n"; + + internal Request(string data) + { + if (Regex.IsMatch(data, _getPattern)) + { + var match = Regex.Match(data, _getPattern); + Type = RequestType.Get; + Path = match.Groups[2].Value; + } + if (Regex.IsMatch(data, _listPattern)) + { + var match = Regex.Match(data, _listPattern); + Type = RequestType.List; + Path = match.Groups[2].Value; + } + + throw new RequestParseException(); + } + + public enum RequestType + { + List = 1, + Get + } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Response.cs b/C#/forSpbu/SimpleFtpServer/Response.cs new file mode 100644 index 0000000..1698ccb --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Response.cs @@ -0,0 +1,11 @@ +namespace SimpleFtp; + +public class Response +{ + private Request.RequestType _type; + + public Response(Request request) + { + + } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Server.cs b/C#/forSpbu/SimpleFtpServer/Server.cs new file mode 100644 index 0000000..cd0befc --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Server.cs @@ -0,0 +1,41 @@ +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Text.RegularExpressions; + +namespace SimpleFtp; + +public class RequestParseException : ApplicationException +{ + public RequestParseException(string message) : base(message) + { + } + + public RequestParseException() + { + } +} +public class FtpServer +{ + private const int Port = 21; + private readonly TcpListener _listener = new (IPAddress.Any, Port); + + public async Task Listen() + { + _listener.Start(); + while (true) + { + var socket = await _listener.AcceptSocketAsync(); + Task.Run(async () => HandleClient(socket)); + } + } + + private void HandleClient(Socket socket) + { + var reader = new StreamReader(new NetworkStream(socket)); + var data = reader.ReadLineAsync().Result; + Console.WriteLine($"Received {data}"); + } + + +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/SimpleFtpServer.csproj b/C#/forSpbu/SimpleFtpServer/SimpleFtpServer.csproj new file mode 100644 index 0000000..8147ffd --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/SimpleFtpServer.csproj @@ -0,0 +1,11 @@ + + + + Exe + net7.0 + enable + enable + SimpleFtp + + + diff --git a/C#/forSpbu/forSpbu.sln b/C#/forSpbu/forSpbu.sln index 8cd78db..a8ae0dc 100644 --- a/C#/forSpbu/forSpbu.sln +++ b/C#/forSpbu/forSpbu.sln @@ -14,7 +14,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023", "2023", "{6C82FFDE-0 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "12.10", "12.10", "{7A21E6C1-9694-4896-AD34-C0BDB62BF665}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtp", "SimpleFtp\SimpleFtp.csproj", "{EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtpServer", "SimpleFtpServer\SimpleFtpServer.csproj", "{EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtpClient", "SimpleFtpClient\SimpleFtpClient.csproj", "{D5835D5B-B4F3-4AF6-8FDB-0133183D3304}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -34,11 +36,16 @@ Global {EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}.Debug|Any CPU.Build.0 = Debug|Any CPU {EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}.Release|Any CPU.ActiveCfg = Release|Any CPU {EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}.Release|Any CPU.Build.0 = Release|Any CPU + {D5835D5B-B4F3-4AF6-8FDB-0133183D3304}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5835D5B-B4F3-4AF6-8FDB-0133183D3304}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5835D5B-B4F3-4AF6-8FDB-0133183D3304}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5835D5B-B4F3-4AF6-8FDB-0133183D3304}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {E007586F-9760-4744-BB25-EDEFD6BA860C} = {D3FCB669-E93F-4F0B-B9C5-6592CE93AC7F} {A4F6ADD5-85FD-4F67-8B29-549DDDF6F82E} = {D3FCB669-E93F-4F0B-B9C5-6592CE93AC7F} {7A21E6C1-9694-4896-AD34-C0BDB62BF665} = {6C82FFDE-03BA-44ED-B5E8-C90579DCF56D} {EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5} = {7A21E6C1-9694-4896-AD34-C0BDB62BF665} + {D5835D5B-B4F3-4AF6-8FDB-0133183D3304} = {7A21E6C1-9694-4896-AD34-C0BDB62BF665} EndGlobalSection EndGlobal From e78d9580b78c43cf08c6881ced5be7ff76086e52 Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Mon, 30 Oct 2023 01:53:46 +0300 Subject: [PATCH 03/15] Checking request --- C#/forSpbu/SimpleFtpServer/Request.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/C#/forSpbu/SimpleFtpServer/Request.cs b/C#/forSpbu/SimpleFtpServer/Request.cs index 7febae4..358e488 100644 --- a/C#/forSpbu/SimpleFtpServer/Request.cs +++ b/C#/forSpbu/SimpleFtpServer/Request.cs @@ -1,7 +1,7 @@ using System.Text.RegularExpressions; namespace SimpleFtp; -public abstract class Request +public class Request { public RequestType Type { get; private set; } public string Path { get; private set; } From 1d5f2e1a76cb34c4783ed5b63874cabfd614a4cf Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Mon, 30 Oct 2023 02:18:54 +0300 Subject: [PATCH 04/15] Finished request --- C#/forSpbu/SimpleFtpServer/Request.cs | 34 ------------------- .../SimpleFtpServer/Request/GetRequest.cs | 12 +++++++ .../SimpleFtpServer/Request/IRequest.cs | 6 ++++ .../SimpleFtpServer/Request/ListRequest.cs | 12 +++++++ .../SimpleFtpServer/Request/RequestFactory.cs | 22 ++++++++++++ 5 files changed, 52 insertions(+), 34 deletions(-) delete mode 100644 C#/forSpbu/SimpleFtpServer/Request.cs create mode 100644 C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs create mode 100644 C#/forSpbu/SimpleFtpServer/Request/IRequest.cs create mode 100644 C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs create mode 100644 C#/forSpbu/SimpleFtpServer/Request/RequestFactory.cs diff --git a/C#/forSpbu/SimpleFtpServer/Request.cs b/C#/forSpbu/SimpleFtpServer/Request.cs deleted file mode 100644 index 358e488..0000000 --- a/C#/forSpbu/SimpleFtpServer/Request.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Text.RegularExpressions; -namespace SimpleFtp; - -public class Request -{ - public RequestType Type { get; private set; } - public string Path { get; private set; } - private readonly string _getPattern = $"({(int)RequestType.Get}) ([1-9a-zA-Z./]+)\n"; - private readonly string _listPattern = $"({(int)RequestType.List}) ([1-9a-zA-Z./]+)\n"; - - internal Request(string data) - { - if (Regex.IsMatch(data, _getPattern)) - { - var match = Regex.Match(data, _getPattern); - Type = RequestType.Get; - Path = match.Groups[2].Value; - } - if (Regex.IsMatch(data, _listPattern)) - { - var match = Regex.Match(data, _listPattern); - Type = RequestType.List; - Path = match.Groups[2].Value; - } - - throw new RequestParseException(); - } - - public enum RequestType - { - List = 1, - Get - } -} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs b/C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs new file mode 100644 index 0000000..229a649 --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs @@ -0,0 +1,12 @@ +namespace SimpleFtp; + +public class GetRequest : IRequest +{ + public static string Pattern => "2 (?[1-9a-zA-Z./]+)\n"; + private readonly string _path; + + public GetRequest(string path) + { + _path = path; + } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Request/IRequest.cs b/C#/forSpbu/SimpleFtpServer/Request/IRequest.cs new file mode 100644 index 0000000..713e3ad --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Request/IRequest.cs @@ -0,0 +1,6 @@ +namespace SimpleFtp; + +public interface IRequest +{ + public static abstract string Pattern { get; } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs b/C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs new file mode 100644 index 0000000..7427127 --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs @@ -0,0 +1,12 @@ +namespace SimpleFtp; + +public class ListRequest : IRequest +{ + public static string Pattern => "1 (?[1-9a-zA-Z./]+)\n"; + private readonly string _path; + + public ListRequest(string path) + { + _path = path; + } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Request/RequestFactory.cs b/C#/forSpbu/SimpleFtpServer/Request/RequestFactory.cs new file mode 100644 index 0000000..444b450 --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Request/RequestFactory.cs @@ -0,0 +1,22 @@ +using System.Text.RegularExpressions; + +namespace SimpleFtp; + +public static class RequestFactory +{ + public static IRequest Create(string request) + { + if (Regex.IsMatch(request, GetRequest.Pattern)) + { + var match = Regex.Match(request, GetRequest.Pattern); + return new GetRequest(match.Groups["path"].Value); + } + if (Regex.IsMatch(request, ListRequest.Pattern)) + { + var match = Regex.Match(request, ListRequest.Pattern); + return new ListRequest(match.Groups["path"].Value); + } + + throw new RequestParseException(); + } +} \ No newline at end of file From c43c48eb49a6e8767605cffbdd8f939ba5673d56 Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Mon, 30 Oct 2023 02:26:39 +0300 Subject: [PATCH 05/15] Added response --- .../Request/{RequestFactory.cs => Factory.cs} | 4 ++-- .../SimpleFtpServer/Request/GetRequest.cs | 2 +- .../SimpleFtpServer/Request/IRequest.cs | 2 +- .../SimpleFtpServer/Request/ListRequest.cs | 2 +- C#/forSpbu/SimpleFtpServer/Response.cs | 11 ---------- .../SimpleFtpServer/Response/Factory.cs | 22 +++++++++++++++++++ .../SimpleFtpServer/Response/GetResponse.cs | 11 ++++++++++ .../SimpleFtpServer/Response/IResponse.cs | 6 +++++ .../SimpleFtpServer/Response/ListResponse.cs | 11 ++++++++++ 9 files changed, 55 insertions(+), 16 deletions(-) rename C#/forSpbu/SimpleFtpServer/Request/{RequestFactory.cs => Factory.cs} (90%) delete mode 100644 C#/forSpbu/SimpleFtpServer/Response.cs create mode 100644 C#/forSpbu/SimpleFtpServer/Response/Factory.cs create mode 100644 C#/forSpbu/SimpleFtpServer/Response/GetResponse.cs create mode 100644 C#/forSpbu/SimpleFtpServer/Response/IResponse.cs create mode 100644 C#/forSpbu/SimpleFtpServer/Response/ListResponse.cs diff --git a/C#/forSpbu/SimpleFtpServer/Request/RequestFactory.cs b/C#/forSpbu/SimpleFtpServer/Request/Factory.cs similarity index 90% rename from C#/forSpbu/SimpleFtpServer/Request/RequestFactory.cs rename to C#/forSpbu/SimpleFtpServer/Request/Factory.cs index 444b450..308edd7 100644 --- a/C#/forSpbu/SimpleFtpServer/Request/RequestFactory.cs +++ b/C#/forSpbu/SimpleFtpServer/Request/Factory.cs @@ -1,8 +1,8 @@ using System.Text.RegularExpressions; -namespace SimpleFtp; +namespace SimpleFtp.Request; -public static class RequestFactory +public static class Factory { public static IRequest Create(string request) { diff --git a/C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs b/C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs index 229a649..1b9c034 100644 --- a/C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs +++ b/C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs @@ -1,4 +1,4 @@ -namespace SimpleFtp; +namespace SimpleFtp.Request; public class GetRequest : IRequest { diff --git a/C#/forSpbu/SimpleFtpServer/Request/IRequest.cs b/C#/forSpbu/SimpleFtpServer/Request/IRequest.cs index 713e3ad..c0bb5c1 100644 --- a/C#/forSpbu/SimpleFtpServer/Request/IRequest.cs +++ b/C#/forSpbu/SimpleFtpServer/Request/IRequest.cs @@ -1,4 +1,4 @@ -namespace SimpleFtp; +namespace SimpleFtp.Request; public interface IRequest { diff --git a/C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs b/C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs index 7427127..6f2343d 100644 --- a/C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs +++ b/C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs @@ -1,4 +1,4 @@ -namespace SimpleFtp; +namespace SimpleFtp.Request; public class ListRequest : IRequest { diff --git a/C#/forSpbu/SimpleFtpServer/Response.cs b/C#/forSpbu/SimpleFtpServer/Response.cs deleted file mode 100644 index 1698ccb..0000000 --- a/C#/forSpbu/SimpleFtpServer/Response.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace SimpleFtp; - -public class Response -{ - private Request.RequestType _type; - - public Response(Request request) - { - - } -} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Response/Factory.cs b/C#/forSpbu/SimpleFtpServer/Response/Factory.cs new file mode 100644 index 0000000..0f95adf --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Response/Factory.cs @@ -0,0 +1,22 @@ +using System.Text.RegularExpressions; + +namespace SimpleFtp.Response; + +public static class Factory +{ + public static IResponse Create(string request) + { + if (Regex.IsMatch(request, GetRequest.Pattern)) + { + var match = Regex.Match(request, GetRequest.Pattern); + return new GetRequest(match.Groups["path"].Value); + } + if (Regex.IsMatch(request, ListRequest.Pattern)) + { + var match = Regex.Match(request, ListRequest.Pattern); + return new ListRequest(match.Groups["path"].Value); + } + + throw new RequestParseException(); + } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Response/GetResponse.cs b/C#/forSpbu/SimpleFtpServer/Response/GetResponse.cs new file mode 100644 index 0000000..6f65ab5 --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Response/GetResponse.cs @@ -0,0 +1,11 @@ +namespace SimpleFtp.Response; + +public class GetResponse : IResponse +{ + private readonly byte[] _file; + + public GetResponse(byte[] fileBytes) + { + _file = fileBytes; + } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Response/IResponse.cs b/C#/forSpbu/SimpleFtpServer/Response/IResponse.cs new file mode 100644 index 0000000..607879f --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Response/IResponse.cs @@ -0,0 +1,6 @@ +namespace SimpleFtp.Response; + +public interface IResponse +{ + +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Response/ListResponse.cs b/C#/forSpbu/SimpleFtpServer/Response/ListResponse.cs new file mode 100644 index 0000000..bcaf2b2 --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Response/ListResponse.cs @@ -0,0 +1,11 @@ +namespace SimpleFtp.Response; + +public class ListResponse : IResponse +{ + private readonly (string name, bool isDir)[] _list; + + public ListResponse((string name, bool isDir)[] dirList) + { + _list = dirList; + } +} \ No newline at end of file From 5ccefddfbe0426afb593152493c1f4c590c3779f Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Mon, 30 Oct 2023 02:43:15 +0300 Subject: [PATCH 06/15] Saved facotry --- C#/forSpbu/SimpleFtpServer/Program.cs | 4 +-- .../Request/RequestParseException.cs | 5 ++++ .../SimpleFtpServer/Response/Factory.cs | 6 ++--- C#/forSpbu/SimpleFtpServer/Server.cs | 25 +++++-------------- 4 files changed, 16 insertions(+), 24 deletions(-) create mode 100644 C#/forSpbu/SimpleFtpServer/Request/RequestParseException.cs diff --git a/C#/forSpbu/SimpleFtpServer/Program.cs b/C#/forSpbu/SimpleFtpServer/Program.cs index 0b6422f..f05b608 100644 --- a/C#/forSpbu/SimpleFtpServer/Program.cs +++ b/C#/forSpbu/SimpleFtpServer/Program.cs @@ -2,5 +2,5 @@ using SimpleFtp; -var server = new Request("2 ./Test/Files\n"); -Console.WriteLine("1"); +var server = new FtpServer(); +await server.Listen(); diff --git a/C#/forSpbu/SimpleFtpServer/Request/RequestParseException.cs b/C#/forSpbu/SimpleFtpServer/Request/RequestParseException.cs new file mode 100644 index 0000000..ac4d26c --- /dev/null +++ b/C#/forSpbu/SimpleFtpServer/Request/RequestParseException.cs @@ -0,0 +1,5 @@ +namespace SimpleFtp.Request; + +public class RequestParseException : FormatException +{ +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Response/Factory.cs b/C#/forSpbu/SimpleFtpServer/Response/Factory.cs index 0f95adf..9955fe1 100644 --- a/C#/forSpbu/SimpleFtpServer/Response/Factory.cs +++ b/C#/forSpbu/SimpleFtpServer/Response/Factory.cs @@ -6,10 +6,10 @@ public static class Factory { public static IResponse Create(string request) { - if (Regex.IsMatch(request, GetRequest.Pattern)) + if (Regex.IsMatch(request, GetResponse.Pattern)) { - var match = Regex.Match(request, GetRequest.Pattern); - return new GetRequest(match.Groups["path"].Value); + var match = Regex.Match(request, GetResponse.Pattern); + return new GetResponse(match.Groups["path"].Value); } if (Regex.IsMatch(request, ListRequest.Pattern)) { diff --git a/C#/forSpbu/SimpleFtpServer/Server.cs b/C#/forSpbu/SimpleFtpServer/Server.cs index cd0befc..8164df9 100644 --- a/C#/forSpbu/SimpleFtpServer/Server.cs +++ b/C#/forSpbu/SimpleFtpServer/Server.cs @@ -4,38 +4,25 @@ using System.Text.RegularExpressions; namespace SimpleFtp; - -public class RequestParseException : ApplicationException -{ - public RequestParseException(string message) : base(message) - { - } - - public RequestParseException() - { - } -} public class FtpServer { private const int Port = 21; private readonly TcpListener _listener = new (IPAddress.Any, Port); - public async Task Listen() + public async Task Listen() { _listener.Start(); while (true) { - var socket = await _listener.AcceptSocketAsync(); - Task.Run(async () => HandleClient(socket)); + var client = await _listener.AcceptTcpClientAsync(); + Task.Run(() => HandleClient(client.GetStream())); } } - private void HandleClient(Socket socket) + private static void HandleClient(NetworkStream stream) { - var reader = new StreamReader(new NetworkStream(socket)); - var data = reader.ReadLineAsync().Result; + var reader = new StreamReader(stream); + var data = reader.ReadToEnd(); Console.WriteLine($"Received {data}"); } - - } \ No newline at end of file From f65c99b9628674c53f592dcba94bfda725b67420 Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Mon, 30 Oct 2023 03:21:09 +0300 Subject: [PATCH 07/15] Separated protocol --- .../SimpleFtp.Protocol/Request/GetRequest.cs | 11 +++++++ .../SimpleFtp.Protocol/Request/ListRequest.cs | 11 +++++++ .../SimpleFtp.Protocol/Request/Request.cs | 5 +++ .../Request/RequestFactory.cs | 31 +++++++++++++++++++ .../Request/RequestParseException.cs | 2 +- .../Response/GetResponse.cs | 11 +++++++ .../Response/ListResponse.cs | 11 +++++++ .../SimpleFtp.Protocol/Response/Response.cs | 5 +++ .../SimpleFtp.Protocol.csproj | 9 ++++++ .../Program.cs | 0 .../Server.cs | 19 ++++++++---- .../SimpleFtp.Server.csproj} | 4 +++ C#/forSpbu/SimpleFtpClient/Program.cs | 14 +++++---- C#/forSpbu/SimpleFtpServer/Request/Factory.cs | 22 ------------- .../SimpleFtpServer/Request/GetRequest.cs | 12 ------- .../SimpleFtpServer/Request/IRequest.cs | 6 ---- .../SimpleFtpServer/Request/ListRequest.cs | 12 ------- .../SimpleFtpServer/Response/Factory.cs | 22 ------------- .../SimpleFtpServer/Response/GetResponse.cs | 11 ------- .../SimpleFtpServer/Response/IResponse.cs | 6 ---- .../SimpleFtpServer/Response/ListResponse.cs | 11 ------- C#/forSpbu/forSpbu.sln | 9 +++++- 22 files changed, 128 insertions(+), 116 deletions(-) create mode 100644 C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs create mode 100644 C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs create mode 100644 C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs create mode 100644 C#/forSpbu/SimpleFtp.Protocol/Request/RequestFactory.cs rename C#/forSpbu/{SimpleFtpServer => SimpleFtp.Protocol}/Request/RequestParseException.cs (63%) create mode 100644 C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs create mode 100644 C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs create mode 100644 C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs create mode 100644 C#/forSpbu/SimpleFtp.Protocol/SimpleFtp.Protocol.csproj rename C#/forSpbu/{SimpleFtpServer => SimpleFtp.Server}/Program.cs (100%) rename C#/forSpbu/{SimpleFtpServer => SimpleFtp.Server}/Server.cs (53%) rename C#/forSpbu/{SimpleFtpServer/SimpleFtpServer.csproj => SimpleFtp.Server/SimpleFtp.Server.csproj} (72%) delete mode 100644 C#/forSpbu/SimpleFtpServer/Request/Factory.cs delete mode 100644 C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs delete mode 100644 C#/forSpbu/SimpleFtpServer/Request/IRequest.cs delete mode 100644 C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs delete mode 100644 C#/forSpbu/SimpleFtpServer/Response/Factory.cs delete mode 100644 C#/forSpbu/SimpleFtpServer/Response/GetResponse.cs delete mode 100644 C#/forSpbu/SimpleFtpServer/Response/IResponse.cs delete mode 100644 C#/forSpbu/SimpleFtpServer/Response/ListResponse.cs diff --git a/C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs new file mode 100644 index 0000000..168677a --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs @@ -0,0 +1,11 @@ +namespace SimpleFtp.Protocol; + +public class GetRequest : Request +{ + public string Path { get; private set; }; + + public GetRequest(string path) + { + Path = path; + } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs new file mode 100644 index 0000000..ff36385 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs @@ -0,0 +1,11 @@ +namespace SimpleFtp.Protocol; + +public class ListRequest : Request +{ + public string Path { get; private set; }; + + public ListRequest(string path) + { + Path = path; + } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs new file mode 100644 index 0000000..d26e6c7 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs @@ -0,0 +1,5 @@ +namespace SimpleFtp.Protocol; + +public abstract class Request +{ +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Request/RequestFactory.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/RequestFactory.cs new file mode 100644 index 0000000..cce5aa6 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/RequestFactory.cs @@ -0,0 +1,31 @@ +using System.Text.RegularExpressions; + +namespace SimpleFtp.Protocol; + +public static partial class RequestFactory +{ + private const string GetPattern = "2 (?[1-9a-zA-Z./]+)\n"; + private const string ListPattern = "1 (?[1-9a-zA-Z./]+)\n"; + + public static Request Create(string request) + { + if (GetRegex().IsMatch(request)) + { + var match = GetRegex().Match(request); + return new GetRequest(match.Groups["path"].Value); + } + if (ListRegex().IsMatch(request)) + { + var match = ListRegex().Match(request); + return new ListRequest(match.Groups["path"].Value); + } + + throw new RequestParseException(); + } + + [GeneratedRegex(GetPattern)] + private static partial Regex GetRegex(); + + [GeneratedRegex(ListPattern)] + private static partial Regex ListRegex(); +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Request/RequestParseException.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/RequestParseException.cs similarity index 63% rename from C#/forSpbu/SimpleFtpServer/Request/RequestParseException.cs rename to C#/forSpbu/SimpleFtp.Protocol/Request/RequestParseException.cs index ac4d26c..3d5753f 100644 --- a/C#/forSpbu/SimpleFtpServer/Request/RequestParseException.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/RequestParseException.cs @@ -1,4 +1,4 @@ -namespace SimpleFtp.Request; +namespace SimpleFtp.Protocol; public class RequestParseException : FormatException { diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs new file mode 100644 index 0000000..bdba684 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs @@ -0,0 +1,11 @@ +namespace SimpleFtp.Protocol; + +public class GetResponse : Response +{ + public byte[] File { get; private set; }; + + public GetResponse(byte[] fileBytes) + { + File = fileBytes; + } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs new file mode 100644 index 0000000..0b07829 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs @@ -0,0 +1,11 @@ +namespace SimpleFtp.Protocol; + +public class ListResponse : Response +{ + public (string name, bool isDir)[] List { get; private set; }; + + public ListResponse((string name, bool isDir)[] dirList) + { + List = dirList; + } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs new file mode 100644 index 0000000..d1d0081 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs @@ -0,0 +1,5 @@ +namespace SimpleFtp.Protocol; + +public abstract class Response +{ +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/SimpleFtp.Protocol.csproj b/C#/forSpbu/SimpleFtp.Protocol/SimpleFtp.Protocol.csproj new file mode 100644 index 0000000..6836c68 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Protocol/SimpleFtp.Protocol.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/C#/forSpbu/SimpleFtpServer/Program.cs b/C#/forSpbu/SimpleFtp.Server/Program.cs similarity index 100% rename from C#/forSpbu/SimpleFtpServer/Program.cs rename to C#/forSpbu/SimpleFtp.Server/Program.cs diff --git a/C#/forSpbu/SimpleFtpServer/Server.cs b/C#/forSpbu/SimpleFtp.Server/Server.cs similarity index 53% rename from C#/forSpbu/SimpleFtpServer/Server.cs rename to C#/forSpbu/SimpleFtp.Server/Server.cs index 8164df9..26b20b2 100644 --- a/C#/forSpbu/SimpleFtpServer/Server.cs +++ b/C#/forSpbu/SimpleFtp.Server/Server.cs @@ -1,7 +1,5 @@ -using System.Diagnostics; -using System.Net; +using System.Net; using System.Net.Sockets; -using System.Text.RegularExpressions; namespace SimpleFtp; public class FtpServer @@ -19,10 +17,19 @@ public async Task Listen() } } - private static void HandleClient(NetworkStream stream) + private static async void HandleClient(NetworkStream stream) { var reader = new StreamReader(stream); - var data = reader.ReadToEnd(); - Console.WriteLine($"Received {data}"); + var data = await reader.ReadToEndAsync(); + var request = Protocol.RequestFactory.Create(data); + var response = HandleRequest(request); + } + + private static Protocol.Response HandleRequest(Protocol.Request request) + { + if (request is Protocol.GetRequest) + { + request. + } } } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/SimpleFtpServer.csproj b/C#/forSpbu/SimpleFtp.Server/SimpleFtp.Server.csproj similarity index 72% rename from C#/forSpbu/SimpleFtpServer/SimpleFtpServer.csproj rename to C#/forSpbu/SimpleFtp.Server/SimpleFtp.Server.csproj index 8147ffd..aa1a880 100644 --- a/C#/forSpbu/SimpleFtpServer/SimpleFtpServer.csproj +++ b/C#/forSpbu/SimpleFtp.Server/SimpleFtp.Server.csproj @@ -8,4 +8,8 @@ SimpleFtp + + + + diff --git a/C#/forSpbu/SimpleFtpClient/Program.cs b/C#/forSpbu/SimpleFtpClient/Program.cs index f7bd332..ae6a4c0 100644 --- a/C#/forSpbu/SimpleFtpClient/Program.cs +++ b/C#/forSpbu/SimpleFtpClient/Program.cs @@ -1,8 +1,10 @@ using System.Net; +using System.Net.Sockets; -FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://127.0.0.1"); -request.Method = WebRequestMethods.Ftp.ListDirectory; -using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) -{ - response.GetResponseStream(); -} \ No newline at end of file +var client = new TcpClient(); +await client.ConnectAsync("localhost", 21); +using var stream = client.GetStream(); +using var reader = new StreamReader(stream); +using var writer = new StreamWriter(stream) { AutoFlush = true }; + +await writer.WriteLineAsync("1 .\n"); \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Request/Factory.cs b/C#/forSpbu/SimpleFtpServer/Request/Factory.cs deleted file mode 100644 index 308edd7..0000000 --- a/C#/forSpbu/SimpleFtpServer/Request/Factory.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.RegularExpressions; - -namespace SimpleFtp.Request; - -public static class Factory -{ - public static IRequest Create(string request) - { - if (Regex.IsMatch(request, GetRequest.Pattern)) - { - var match = Regex.Match(request, GetRequest.Pattern); - return new GetRequest(match.Groups["path"].Value); - } - if (Regex.IsMatch(request, ListRequest.Pattern)) - { - var match = Regex.Match(request, ListRequest.Pattern); - return new ListRequest(match.Groups["path"].Value); - } - - throw new RequestParseException(); - } -} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs b/C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs deleted file mode 100644 index 1b9c034..0000000 --- a/C#/forSpbu/SimpleFtpServer/Request/GetRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace SimpleFtp.Request; - -public class GetRequest : IRequest -{ - public static string Pattern => "2 (?[1-9a-zA-Z./]+)\n"; - private readonly string _path; - - public GetRequest(string path) - { - _path = path; - } -} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Request/IRequest.cs b/C#/forSpbu/SimpleFtpServer/Request/IRequest.cs deleted file mode 100644 index c0bb5c1..0000000 --- a/C#/forSpbu/SimpleFtpServer/Request/IRequest.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace SimpleFtp.Request; - -public interface IRequest -{ - public static abstract string Pattern { get; } -} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs b/C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs deleted file mode 100644 index 6f2343d..0000000 --- a/C#/forSpbu/SimpleFtpServer/Request/ListRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace SimpleFtp.Request; - -public class ListRequest : IRequest -{ - public static string Pattern => "1 (?[1-9a-zA-Z./]+)\n"; - private readonly string _path; - - public ListRequest(string path) - { - _path = path; - } -} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Response/Factory.cs b/C#/forSpbu/SimpleFtpServer/Response/Factory.cs deleted file mode 100644 index 9955fe1..0000000 --- a/C#/forSpbu/SimpleFtpServer/Response/Factory.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.RegularExpressions; - -namespace SimpleFtp.Response; - -public static class Factory -{ - public static IResponse Create(string request) - { - if (Regex.IsMatch(request, GetResponse.Pattern)) - { - var match = Regex.Match(request, GetResponse.Pattern); - return new GetResponse(match.Groups["path"].Value); - } - if (Regex.IsMatch(request, ListRequest.Pattern)) - { - var match = Regex.Match(request, ListRequest.Pattern); - return new ListRequest(match.Groups["path"].Value); - } - - throw new RequestParseException(); - } -} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Response/GetResponse.cs b/C#/forSpbu/SimpleFtpServer/Response/GetResponse.cs deleted file mode 100644 index 6f65ab5..0000000 --- a/C#/forSpbu/SimpleFtpServer/Response/GetResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace SimpleFtp.Response; - -public class GetResponse : IResponse -{ - private readonly byte[] _file; - - public GetResponse(byte[] fileBytes) - { - _file = fileBytes; - } -} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Response/IResponse.cs b/C#/forSpbu/SimpleFtpServer/Response/IResponse.cs deleted file mode 100644 index 607879f..0000000 --- a/C#/forSpbu/SimpleFtpServer/Response/IResponse.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace SimpleFtp.Response; - -public interface IResponse -{ - -} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpServer/Response/ListResponse.cs b/C#/forSpbu/SimpleFtpServer/Response/ListResponse.cs deleted file mode 100644 index bcaf2b2..0000000 --- a/C#/forSpbu/SimpleFtpServer/Response/ListResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace SimpleFtp.Response; - -public class ListResponse : IResponse -{ - private readonly (string name, bool isDir)[] _list; - - public ListResponse((string name, bool isDir)[] dirList) - { - _list = dirList; - } -} \ No newline at end of file diff --git a/C#/forSpbu/forSpbu.sln b/C#/forSpbu/forSpbu.sln index a8ae0dc..9d445e4 100644 --- a/C#/forSpbu/forSpbu.sln +++ b/C#/forSpbu/forSpbu.sln @@ -14,10 +14,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023", "2023", "{6C82FFDE-0 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "12.10", "12.10", "{7A21E6C1-9694-4896-AD34-C0BDB62BF665}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtpServer", "SimpleFtpServer\SimpleFtpServer.csproj", "{EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtp.Server", "SimpleFtp.Server\SimpleFtp.Server.csproj", "{EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtpClient", "SimpleFtpClient\SimpleFtpClient.csproj", "{D5835D5B-B4F3-4AF6-8FDB-0133183D3304}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtp.Protocol", "SimpleFtp.Protocol\SimpleFtp.Protocol.csproj", "{888C5E3A-3DE2-4405-B4E3-24ABB9A473C5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,6 +42,10 @@ Global {D5835D5B-B4F3-4AF6-8FDB-0133183D3304}.Debug|Any CPU.Build.0 = Debug|Any CPU {D5835D5B-B4F3-4AF6-8FDB-0133183D3304}.Release|Any CPU.ActiveCfg = Release|Any CPU {D5835D5B-B4F3-4AF6-8FDB-0133183D3304}.Release|Any CPU.Build.0 = Release|Any CPU + {888C5E3A-3DE2-4405-B4E3-24ABB9A473C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {888C5E3A-3DE2-4405-B4E3-24ABB9A473C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {888C5E3A-3DE2-4405-B4E3-24ABB9A473C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {888C5E3A-3DE2-4405-B4E3-24ABB9A473C5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {E007586F-9760-4744-BB25-EDEFD6BA860C} = {D3FCB669-E93F-4F0B-B9C5-6592CE93AC7F} @@ -47,5 +53,6 @@ Global {7A21E6C1-9694-4896-AD34-C0BDB62BF665} = {6C82FFDE-03BA-44ED-B5E8-C90579DCF56D} {EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5} = {7A21E6C1-9694-4896-AD34-C0BDB62BF665} {D5835D5B-B4F3-4AF6-8FDB-0133183D3304} = {7A21E6C1-9694-4896-AD34-C0BDB62BF665} + {888C5E3A-3DE2-4405-B4E3-24ABB9A473C5} = {7A21E6C1-9694-4896-AD34-C0BDB62BF665} EndGlobalSection EndGlobal From 35d127bd2ad2675979e73ae5e7323507e76dea69 Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Mon, 30 Oct 2023 13:38:51 +0300 Subject: [PATCH 08/15] Added handling --- .../SimpleFtp.Protocol/Request/GetRequest.cs | 2 +- .../SimpleFtp.Protocol/Request/ListRequest.cs | 2 +- .../Response/GetResponse.cs | 6 +- .../Response/ListResponse.cs | 12 +++- C#/forSpbu/SimpleFtp.Server/Server.cs | 57 +++++++++++++++---- 5 files changed, 63 insertions(+), 16 deletions(-) diff --git a/C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs index 168677a..cc3d6ab 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs @@ -2,7 +2,7 @@ public class GetRequest : Request { - public string Path { get; private set; }; + public string Path { get; private set; } public GetRequest(string path) { diff --git a/C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs index ff36385..5b1f6cd 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs @@ -2,7 +2,7 @@ public class ListRequest : Request { - public string Path { get; private set; }; + public string Path { get; private set; } public ListRequest(string path) { diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs index bdba684..12d5583 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs @@ -2,10 +2,14 @@ public class GetResponse : Response { - public byte[] File { get; private set; }; + public byte[] File { get; private set; } public GetResponse(byte[] fileBytes) { File = fileBytes; } + + public GetResponse() + { + } } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs index 0b07829..8971e80 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs @@ -2,10 +2,16 @@ public class ListResponse : Response { - public (string name, bool isDir)[] List { get; private set; }; - - public ListResponse((string name, bool isDir)[] dirList) + public IEnumerable<(string name, bool isDir)>? List { get; private set; } + + public int Size => List?.Count() ?? -1; + + public ListResponse(IEnumerable<(string name, bool isDir)> dirList) { List = dirList; } + + public ListResponse() + { + } } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Server/Server.cs b/C#/forSpbu/SimpleFtp.Server/Server.cs index 26b20b2..4d09998 100644 --- a/C#/forSpbu/SimpleFtp.Server/Server.cs +++ b/C#/forSpbu/SimpleFtp.Server/Server.cs @@ -1,5 +1,7 @@ -using System.Net; +using System.Diagnostics; +using System.Net; using System.Net.Sockets; +using SimpleFtp.Protocol; namespace SimpleFtp; public class FtpServer @@ -13,23 +15,58 @@ public async Task Listen() while (true) { var client = await _listener.AcceptTcpClientAsync(); - Task.Run(() => HandleClient(client.GetStream())); + HandleClient(client.GetStream()); } } - private static async void HandleClient(NetworkStream stream) + private static async Task HandleClient(NetworkStream stream) { - var reader = new StreamReader(stream); - var data = await reader.ReadToEndAsync(); - var request = Protocol.RequestFactory.Create(data); - var response = HandleRequest(request); + var data = await new StreamReader(stream).ReadToEndAsync(); + try + { + var response = HandleRequest(RequestFactory.Create(data)); + } + catch (RequestParseException) + { + } + } - private static Protocol.Response HandleRequest(Protocol.Request request) + private static Response HandleRequest(Request request) { - if (request is Protocol.GetRequest) + switch (request) { - request. + case ListRequest listRequest: + { + try + { + var files = Directory.GetFiles(listRequest.Path); + var directories = Directory.GetDirectories(listRequest.Path); + return new ListResponse(files.Select((string x) => (x, false)).Concat(directories.Select((string x) => (x, true)))); + } + catch (Exception e) when (e is IOException or UnauthorizedAccessException or ArgumentException) + { + } + + return new ListResponse(); + } + + case GetRequest getRequest: + { + try + { + return new GetResponse(File.ReadAllBytes(getRequest.Path)); + } + catch (Exception e) when (e is IOException or UnauthorizedAccessException or ArgumentException or NotSupportedException) + { + } + + return new GetResponse(); + } + default: + { + throw new UnreachableException(); + } } } } \ No newline at end of file From f38c42c78c26e8245ff3537c10879d33bfbdc6be Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Mon, 30 Oct 2023 14:27:34 +0300 Subject: [PATCH 09/15] Finished server --- .../SimpleFtp.Protocol/Response/GetResponse.cs | 7 +++++-- .../SimpleFtp.Protocol/Response/ListResponse.cs | 11 ++++++++--- .../SimpleFtp.Protocol/Response/Response.cs | 1 + C#/forSpbu/SimpleFtp.Server/Program.cs | 4 +--- C#/forSpbu/SimpleFtp.Server/Server.cs | 17 +++++++++-------- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs index 12d5583..ce6c1f4 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs @@ -2,14 +2,17 @@ public class GetResponse : Response { - public byte[] File { get; private set; } + private readonly byte[]? _file; + private int Size => _file?.Length ?? -1; public GetResponse(byte[] fileBytes) { - File = fileBytes; + _file = fileBytes; } public GetResponse() { } + + public override string ToString() => Size + " " + System.Text.Encoding.UTF8.GetString(_file ?? Array.Empty()); } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs index 8971e80..10e13a0 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs @@ -2,16 +2,21 @@ public class ListResponse : Response { - public IEnumerable<(string name, bool isDir)>? List { get; private set; } + private readonly IEnumerable<(string name, bool isDir)>? _list; - public int Size => List?.Count() ?? -1; + private int Size => _list?.Count() ?? -1; public ListResponse(IEnumerable<(string name, bool isDir)> dirList) { - List = dirList; + _list = dirList; } public ListResponse() { } + + public override string ToString() => + Size + " " + + string.Join(' ', (_list ?? Array.Empty<(string name, bool isDir)>()) + .Select<(string name, bool isDir), string>(x => x.name + " " + x.isDir)) + "\n"; } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs index d1d0081..f0ffaaa 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs @@ -2,4 +2,5 @@ public abstract class Response { + public abstract string ToString(); } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Server/Program.cs b/C#/forSpbu/SimpleFtp.Server/Program.cs index f05b608..759d293 100644 --- a/C#/forSpbu/SimpleFtp.Server/Program.cs +++ b/C#/forSpbu/SimpleFtp.Server/Program.cs @@ -1,6 +1,4 @@ -// See https://aka.ms/new-console-template for more information - -using SimpleFtp; +using SimpleFtp; var server = new FtpServer(); await server.Listen(); diff --git a/C#/forSpbu/SimpleFtp.Server/Server.cs b/C#/forSpbu/SimpleFtp.Server/Server.cs index 4d09998..9d69798 100644 --- a/C#/forSpbu/SimpleFtp.Server/Server.cs +++ b/C#/forSpbu/SimpleFtp.Server/Server.cs @@ -14,22 +14,24 @@ public async Task Listen() _listener.Start(); while (true) { - var client = await _listener.AcceptTcpClientAsync(); - HandleClient(client.GetStream()); + using var client = await _listener.AcceptTcpClientAsync(); + await HandleClient(client.GetStream()); } } - private static async Task HandleClient(NetworkStream stream) + private static async Task HandleClient(Stream stream) { - var data = await new StreamReader(stream).ReadToEndAsync(); + using var reader = new StreamReader(stream); + await using var writer = new StreamWriter(stream); + writer.AutoFlush = true; try { - var response = HandleRequest(RequestFactory.Create(data)); + var response = HandleRequest(RequestFactory.Create(await reader.ReadToEndAsync())); + await writer.WriteAsync(response.ToString()); } - catch (RequestParseException) + catch (Exception e) when (e is ArgumentOutOfRangeException or ObjectDisposedException or InvalidOperationException or RequestParseException) { } - } private static Response HandleRequest(Request request) @@ -50,7 +52,6 @@ private static Response HandleRequest(Request request) return new ListResponse(); } - case GetRequest getRequest: { try From 05ba1d887b0d14d512d9e01cd9ab838115756f1b Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Mon, 30 Oct 2023 14:40:41 +0300 Subject: [PATCH 10/15] Added client --- C#/forSpbu/SimpleFtp.Client/Client.cs | 33 +++++++++++++++++++ .../Program.cs | 0 .../SimpleFtp.Client.csproj} | 5 +++ C#/forSpbu/forSpbu.sln | 2 +- 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 C#/forSpbu/SimpleFtp.Client/Client.cs rename C#/forSpbu/{SimpleFtpClient => SimpleFtp.Client}/Program.cs (100%) rename C#/forSpbu/{SimpleFtpClient/SimpleFtpClient.csproj => SimpleFtp.Client/SimpleFtp.Client.csproj} (59%) diff --git a/C#/forSpbu/SimpleFtp.Client/Client.cs b/C#/forSpbu/SimpleFtp.Client/Client.cs new file mode 100644 index 0000000..0721399 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Client/Client.cs @@ -0,0 +1,33 @@ +using System.Net.Sockets; +using SimpleFtp.Protocol; + +namespace SimpleFtp.Client; + +public class FtpClient +{ + private readonly TcpClient _client = new TcpClient(); + public string Hostname { get; private set; } + public int Port { get; private set; } + + private FtpClient(string hostname, int port) + { + Hostname = hostname; + Port = port; + } + + public static async Task Connect(string hostname, int port) + { + var client = new FtpClient(hostname, port); + await client._client.ConnectAsync(hostname, port); + return client; + } + + public async Task SendRequest(Request request) + { + using var reader = new StreamReader(_client.GetStream()); + await using var writer = new StreamWriter(_client.GetStream()); + + await writer.WriteAsync(request.ToString()); + return ResponseFactory(reader.ReadToEndAsync()); + } +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtpClient/Program.cs b/C#/forSpbu/SimpleFtp.Client/Program.cs similarity index 100% rename from C#/forSpbu/SimpleFtpClient/Program.cs rename to C#/forSpbu/SimpleFtp.Client/Program.cs diff --git a/C#/forSpbu/SimpleFtpClient/SimpleFtpClient.csproj b/C#/forSpbu/SimpleFtp.Client/SimpleFtp.Client.csproj similarity index 59% rename from C#/forSpbu/SimpleFtpClient/SimpleFtpClient.csproj rename to C#/forSpbu/SimpleFtp.Client/SimpleFtp.Client.csproj index 2b14c81..6ce30c9 100644 --- a/C#/forSpbu/SimpleFtpClient/SimpleFtpClient.csproj +++ b/C#/forSpbu/SimpleFtp.Client/SimpleFtp.Client.csproj @@ -5,6 +5,11 @@ net7.0 enable enable + SimpleFtpClient + + + + diff --git a/C#/forSpbu/forSpbu.sln b/C#/forSpbu/forSpbu.sln index 9d445e4..59029a0 100644 --- a/C#/forSpbu/forSpbu.sln +++ b/C#/forSpbu/forSpbu.sln @@ -16,7 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "12.10", "12.10", "{7A21E6C1 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtp.Server", "SimpleFtp.Server\SimpleFtp.Server.csproj", "{EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtpClient", "SimpleFtpClient\SimpleFtpClient.csproj", "{D5835D5B-B4F3-4AF6-8FDB-0133183D3304}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtp.Client", "SimpleFtp.Client\SimpleFtp.Client.csproj", "{D5835D5B-B4F3-4AF6-8FDB-0133183D3304}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtp.Protocol", "SimpleFtp.Protocol\SimpleFtp.Protocol.csproj", "{888C5E3A-3DE2-4405-B4E3-24ABB9A473C5}" EndProject From 136b764cb2a1a7daaa529b7630ccffdd17cd7ca5 Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Tue, 31 Oct 2023 02:26:37 +0300 Subject: [PATCH 11/15] Finished client and server --- C#/forSpbu/SimpleFtp.Client/Client.cs | 18 +++++-- C#/forSpbu/SimpleFtp.Client/Program.cs | 18 +++---- .../SimpleFtp.Protocol/Request/GetRequest.cs | 5 ++ .../SimpleFtp.Protocol/Request/ListRequest.cs | 5 ++ .../SimpleFtp.Protocol/Request/Request.cs | 1 + .../Request/RequestFactory.cs | 4 +- .../Response/GetResponse.cs | 2 +- .../Response/ResponseFactory.cs | 53 +++++++++++++++++++ .../Response/ResponseParseException.cs | 5 ++ C#/forSpbu/SimpleFtp.Server/Program.cs | 1 + C#/forSpbu/SimpleFtp.Server/Server.cs | 42 +++++++++++---- 11 files changed, 127 insertions(+), 27 deletions(-) create mode 100644 C#/forSpbu/SimpleFtp.Protocol/Response/ResponseFactory.cs create mode 100644 C#/forSpbu/SimpleFtp.Protocol/Response/ResponseParseException.cs diff --git a/C#/forSpbu/SimpleFtp.Client/Client.cs b/C#/forSpbu/SimpleFtp.Client/Client.cs index 0721399..31502b8 100644 --- a/C#/forSpbu/SimpleFtp.Client/Client.cs +++ b/C#/forSpbu/SimpleFtp.Client/Client.cs @@ -6,6 +6,8 @@ namespace SimpleFtp.Client; public class FtpClient { private readonly TcpClient _client = new TcpClient(); + private StreamReader? _reader; + private StreamWriter? _writer; public string Hostname { get; private set; } public int Port { get; private set; } @@ -19,15 +21,21 @@ public static async Task Connect(string hostname, int port) { var client = new FtpClient(hostname, port); await client._client.ConnectAsync(hostname, port); + client._reader = new StreamReader(client._client.GetStream()); + client._writer = new StreamWriter(client._client.GetStream()); + client._writer.AutoFlush = true; return client; } - public async Task SendRequest(Request request) + public Response SendRequest(Request request) { - using var reader = new StreamReader(_client.GetStream()); - await using var writer = new StreamWriter(_client.GetStream()); + _writer?.Write(request.ToString()); + var data = _reader?.ReadLine() + "\n"; + return ResponseFactory.Create(data); + } - await writer.WriteAsync(request.ToString()); - return ResponseFactory(reader.ReadToEndAsync()); + public void Disconnect() + { + _client.Close(); } } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Client/Program.cs b/C#/forSpbu/SimpleFtp.Client/Program.cs index ae6a4c0..8b8e7ba 100644 --- a/C#/forSpbu/SimpleFtp.Client/Program.cs +++ b/C#/forSpbu/SimpleFtp.Client/Program.cs @@ -1,10 +1,10 @@ -using System.Net; -using System.Net.Sockets; +using SimpleFtp.Client; +using SimpleFtp.Protocol; -var client = new TcpClient(); -await client.ConnectAsync("localhost", 21); -using var stream = client.GetStream(); -using var reader = new StreamReader(stream); -using var writer = new StreamWriter(stream) { AutoFlush = true }; - -await writer.WriteLineAsync("1 .\n"); \ No newline at end of file +var client = await FtpClient.Connect("localhost", 21); +var request = RequestFactory.Create("1 .\n"); +var response = client.SendRequest(request); +Console.Write(response.ToString()); +response = client.SendRequest(request); +Console.Write(response.ToString()); +client.Disconnect(); \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs index cc3d6ab..bc00baa 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs @@ -8,4 +8,9 @@ public GetRequest(string path) { Path = path; } + + public override string ToString() + { + return "2 " + Path + "\n"; + } } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs index 5b1f6cd..3f8e5e9 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs @@ -8,4 +8,9 @@ public ListRequest(string path) { Path = path; } + + public override string ToString() + { + return "1 " + Path + "\n"; + } } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs index d26e6c7..fbeb539 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs @@ -2,4 +2,5 @@ public abstract class Request { + public abstract string ToString(); } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Request/RequestFactory.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/RequestFactory.cs index cce5aa6..5fb72cb 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Request/RequestFactory.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/RequestFactory.cs @@ -4,8 +4,8 @@ namespace SimpleFtp.Protocol; public static partial class RequestFactory { - private const string GetPattern = "2 (?[1-9a-zA-Z./]+)\n"; - private const string ListPattern = "1 (?[1-9a-zA-Z./]+)\n"; + private const string GetPattern = "2 (?[1-9a-zA-Z./\\\\]+)\n"; + private const string ListPattern = "1 (?[1-9a-zA-Z./\\\\]+)\n"; public static Request Create(string request) { diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs index ce6c1f4..11458e4 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs @@ -14,5 +14,5 @@ public GetResponse() { } - public override string ToString() => Size + " " + System.Text.Encoding.UTF8.GetString(_file ?? Array.Empty()); + public override string ToString() => Size + " " + System.Text.Encoding.UTF8.GetString(_file ?? Array.Empty()) + "\n"; } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/ResponseFactory.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/ResponseFactory.cs new file mode 100644 index 0000000..8280cca --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/ResponseFactory.cs @@ -0,0 +1,53 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SimpleFtp.Protocol; + +public static partial class ResponseFactory +{ + private const string ListPattern = "(?[0-9]+) ((?[1-9a-zA-Z./\\\\]+) (?False|True) )*((?[1-9a-zA-Z./\\\\]+) (?False|True))+\n"; + private const string GetPattern = "(?[0-9]+) (?.+)\n"; + + public static Response Create(string response) + { + if (ListRegex().IsMatch(response)) + { + var match = ListRegex().Match(response); + + var isDirs = match.Groups["isDirs"].Captures.Select(x => x.Value == "True").ToArray(); + var names = match.Groups["names"].Captures.Select(x => x.Value).ToArray(); + if (!int.TryParse(match.Groups["size"].Value, out var size) || names.Length != size) + { + throw new ResponseParseException(); + } + + var list = new (string, bool)[size]; + for (int i = 0; i < size; i++) + { + list[i] = (names[i], isDirs[i]); + } + return new ListResponse(list); + } + if (GetRegex().IsMatch(response)) + { + var match = GetRegex().Match(response); + + var content = match.Groups["content"].Value; + if (!int.TryParse(match.Groups["size"].Value, out _)) + { + throw new ResponseParseException(); + } + + return new GetResponse(Encoding.ASCII.GetBytes(content)); + } + + throw new ResponseParseException(); + } + + + [GeneratedRegex(ListPattern)] + private static partial Regex ListRegex(); + + [GeneratedRegex(GetPattern)] + private static partial Regex GetRegex(); +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/ResponseParseException.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/ResponseParseException.cs new file mode 100644 index 0000000..40fedc6 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/ResponseParseException.cs @@ -0,0 +1,5 @@ +namespace SimpleFtp.Protocol; + +public class ResponseParseException : FormatException +{ +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Server/Program.cs b/C#/forSpbu/SimpleFtp.Server/Program.cs index 759d293..bc1f4b2 100644 --- a/C#/forSpbu/SimpleFtp.Server/Program.cs +++ b/C#/forSpbu/SimpleFtp.Server/Program.cs @@ -1,4 +1,5 @@ using SimpleFtp; +using SimpleFtp.Protocol; var server = new FtpServer(); await server.Listen(); diff --git a/C#/forSpbu/SimpleFtp.Server/Server.cs b/C#/forSpbu/SimpleFtp.Server/Server.cs index 9d69798..48435c7 100644 --- a/C#/forSpbu/SimpleFtp.Server/Server.cs +++ b/C#/forSpbu/SimpleFtp.Server/Server.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Net; +using System.Net.NetworkInformation; using System.Net.Sockets; using SimpleFtp.Protocol; @@ -15,23 +16,44 @@ public async Task Listen() while (true) { using var client = await _listener.AcceptTcpClientAsync(); - await HandleClient(client.GetStream()); + await HandleClient(client); } } - private static async Task HandleClient(Stream stream) + private static async Task HandleClient(TcpClient client) { - using var reader = new StreamReader(stream); - await using var writer = new StreamWriter(stream); + Console.WriteLine("Connected"); + using var reader = new StreamReader(client.GetStream()); + await using var writer = new StreamWriter(client.GetStream()); writer.AutoFlush = true; - try - { - var response = HandleRequest(RequestFactory.Create(await reader.ReadToEndAsync())); - await writer.WriteAsync(response.ToString()); - } - catch (Exception e) when (e is ArgumentOutOfRangeException or ObjectDisposedException or InvalidOperationException or RequestParseException) + + + while (IsConnected(client)) { + try + { + var data = await reader.ReadLineAsync(); + if (string.IsNullOrEmpty(data)) + { + continue; + } + data += "\n"; + Console.Write($"Request: {data}"); + var response = HandleRequest(RequestFactory.Create(data)); + await writer.WriteAsync(response.ToString()); + Console.Write($"Response: {response.ToString()}"); + } + catch (Exception e) when (e is ArgumentOutOfRangeException or ObjectDisposedException or InvalidOperationException or RequestParseException) + { + } } + Console.WriteLine("Disconnected"); + } + + private static bool IsConnected(TcpClient client) + { + var tcpConnections = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections().Where(x => x.LocalEndPoint.Equals(client.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(client.Client.RemoteEndPoint)).ToArray(); + return tcpConnections.Length > 0 && tcpConnections.First().State == TcpState.Established; } private static Response HandleRequest(Request request) From 92c740928a2c8a7d903de31ca3c445934213112d Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Thu, 28 Dec 2023 02:23:35 +0300 Subject: [PATCH 12/15] Moved request --- C#/forSpbu/SimpleFtp.Server/Server.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/C#/forSpbu/SimpleFtp.Server/Server.cs b/C#/forSpbu/SimpleFtp.Server/Server.cs index 48435c7..7f6719e 100644 --- a/C#/forSpbu/SimpleFtp.Server/Server.cs +++ b/C#/forSpbu/SimpleFtp.Server/Server.cs @@ -38,9 +38,9 @@ private static async Task HandleClient(TcpClient client) continue; } data += "\n"; - Console.Write($"Request: {data}"); var response = HandleRequest(RequestFactory.Create(data)); await writer.WriteAsync(response.ToString()); + Console.Write($"Request: {data}"); Console.Write($"Response: {response.ToString()}"); } catch (Exception e) when (e is ArgumentOutOfRangeException or ObjectDisposedException or InvalidOperationException or RequestParseException) From 6fb65f25efe93887e79190c2d3557581aeaf97f0 Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Fri, 29 Dec 2023 19:37:11 +0300 Subject: [PATCH 13/15] Checkign --- C#/forSpbu/SimpleFtp.Client/Client.cs | 5 +++-- C#/forSpbu/SimpleFtp.Client/Program.cs | 2 -- C#/forSpbu/SimpleFtp.Server/Server.cs | 15 +++++++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/C#/forSpbu/SimpleFtp.Client/Client.cs b/C#/forSpbu/SimpleFtp.Client/Client.cs index 31502b8..cecee62 100644 --- a/C#/forSpbu/SimpleFtp.Client/Client.cs +++ b/C#/forSpbu/SimpleFtp.Client/Client.cs @@ -8,8 +8,6 @@ public class FtpClient private readonly TcpClient _client = new TcpClient(); private StreamReader? _reader; private StreamWriter? _writer; - public string Hostname { get; private set; } - public int Port { get; private set; } private FtpClient(string hostname, int port) { @@ -17,6 +15,9 @@ private FtpClient(string hostname, int port) Port = port; } + public string Hostname { get; private set; } + public int Port { get; private set; } + public static async Task Connect(string hostname, int port) { var client = new FtpClient(hostname, port); diff --git a/C#/forSpbu/SimpleFtp.Client/Program.cs b/C#/forSpbu/SimpleFtp.Client/Program.cs index 8b8e7ba..5d6c141 100644 --- a/C#/forSpbu/SimpleFtp.Client/Program.cs +++ b/C#/forSpbu/SimpleFtp.Client/Program.cs @@ -5,6 +5,4 @@ var request = RequestFactory.Create("1 .\n"); var response = client.SendRequest(request); Console.Write(response.ToString()); -response = client.SendRequest(request); -Console.Write(response.ToString()); client.Disconnect(); \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Server/Server.cs b/C#/forSpbu/SimpleFtp.Server/Server.cs index 7f6719e..55ff5e6 100644 --- a/C#/forSpbu/SimpleFtp.Server/Server.cs +++ b/C#/forSpbu/SimpleFtp.Server/Server.cs @@ -7,16 +7,17 @@ namespace SimpleFtp; public class FtpServer { - private const int Port = 21; - private readonly TcpListener _listener = new (IPAddress.Any, Port); + private const int _port = 32768; + private readonly TcpListener _listener = new (IPAddress.Any, _port); + private CancellationToken token = new (); public async Task Listen() { _listener.Start(); while (true) { - using var client = await _listener.AcceptTcpClientAsync(); - await HandleClient(client); + using var client = await _listener.AcceptTcpClientAsync(token); + Task.Run(() => HandleClient(client)); } } @@ -92,4 +93,10 @@ private static Response HandleRequest(Request request) } } } + + public bool Stop() + { + var tcpConnections = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections().Where(x => x.LocalEndPoint.Equals(client.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(client.Client.RemoteEndPoint)).ToArray(); + return tcpConnections.Length > 0 && tcpConnections.First().State == TcpState.Established; + } } \ No newline at end of file From e63cd3851f08be962cfdfc6a2c15c41dd8c1a1b8 Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Fri, 29 Dec 2023 20:49:55 +0300 Subject: [PATCH 14/15] Fixed --- C#/forSpbu/SimpleFtp.Client/Program.cs | 33 ++++++++++++++++--- .../SimpleFtp.Protocol/Request/Request.cs | 2 +- .../SimpleFtp.Protocol/Response/Response.cs | 2 +- C#/forSpbu/SimpleFtp.Server/Program.cs | 8 ++++- C#/forSpbu/SimpleFtp.Server/Server.cs | 27 ++++++++------- 5 files changed, 52 insertions(+), 20 deletions(-) diff --git a/C#/forSpbu/SimpleFtp.Client/Program.cs b/C#/forSpbu/SimpleFtp.Client/Program.cs index 5d6c141..2969b0a 100644 --- a/C#/forSpbu/SimpleFtp.Client/Program.cs +++ b/C#/forSpbu/SimpleFtp.Client/Program.cs @@ -1,8 +1,31 @@ using SimpleFtp.Client; using SimpleFtp.Protocol; -var client = await FtpClient.Connect("localhost", 21); -var request = RequestFactory.Create("1 .\n"); -var response = client.SendRequest(request); -Console.Write(response.ToString()); -client.Disconnect(); \ No newline at end of file +var client = await FtpClient.Connect("localhost", 32768); +Console.WriteLine("Connected"); +while (true) +{ + var command = Console.ReadLine(); + if (command == "exit") + { + client.Disconnect(); + break; + } + + if (command == null) + { + Console.WriteLine("Incorrect command"); + continue; + } + + try + { + var request = RequestFactory.Create(command + "\n"); + var response = client.SendRequest(request); + Console.Write(response.ToString()); + } + catch (RequestParseException) + { + Console.WriteLine("Incorrect command"); + } +} diff --git a/C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs b/C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs index fbeb539..997b096 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs @@ -2,5 +2,5 @@ public abstract class Request { - public abstract string ToString(); + public abstract override string ToString(); } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs b/C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs index f0ffaaa..9764824 100644 --- a/C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs +++ b/C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs @@ -2,5 +2,5 @@ public abstract class Response { - public abstract string ToString(); + public abstract override string ToString(); } \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Server/Program.cs b/C#/forSpbu/SimpleFtp.Server/Program.cs index bc1f4b2..ad03fb6 100644 --- a/C#/forSpbu/SimpleFtp.Server/Program.cs +++ b/C#/forSpbu/SimpleFtp.Server/Program.cs @@ -2,4 +2,10 @@ using SimpleFtp.Protocol; var server = new FtpServer(); -await server.Listen(); +var cancellation = new CancellationTokenSource(); +Task.Run(() => server.Listen(cancellation)); +var input = Console.ReadLine(); +if (input == "exit") +{ + cancellation.Cancel(); +} \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Server/Server.cs b/C#/forSpbu/SimpleFtp.Server/Server.cs index 55ff5e6..d78c21d 100644 --- a/C#/forSpbu/SimpleFtp.Server/Server.cs +++ b/C#/forSpbu/SimpleFtp.Server/Server.cs @@ -2,22 +2,25 @@ using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Reflection.Metadata; using SimpleFtp.Protocol; namespace SimpleFtp; public class FtpServer { - private const int _port = 32768; - private readonly TcpListener _listener = new (IPAddress.Any, _port); - private CancellationToken token = new (); + private const int Port = 32768; + private readonly TcpListener _listener = new (IPAddress.Any, Port); + private CancellationTokenSource? _cancellation; + private readonly List _clients = new(); - public async Task Listen() + public async Task Listen(CancellationTokenSource cancellation) { + _cancellation = cancellation; _listener.Start(); - while (true) + while (!_cancellation.IsCancellationRequested) { - using var client = await _listener.AcceptTcpClientAsync(token); - Task.Run(() => HandleClient(client)); + var client = await _listener.AcceptTcpClientAsync(_cancellation.Token); + _clients.Add(Task.Run(() => HandleClient(client))); } } @@ -41,8 +44,6 @@ private static async Task HandleClient(TcpClient client) data += "\n"; var response = HandleRequest(RequestFactory.Create(data)); await writer.WriteAsync(response.ToString()); - Console.Write($"Request: {data}"); - Console.Write($"Response: {response.ToString()}"); } catch (Exception e) when (e is ArgumentOutOfRangeException or ObjectDisposedException or InvalidOperationException or RequestParseException) { @@ -94,9 +95,11 @@ private static Response HandleRequest(Request request) } } - public bool Stop() + private void WaitForClients() { - var tcpConnections = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections().Where(x => x.LocalEndPoint.Equals(client.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(client.Client.RemoteEndPoint)).ToArray(); - return tcpConnections.Length > 0 && tcpConnections.First().State == TcpState.Established; + foreach (var task in _clients) + { + task.Wait(); + } } } \ No newline at end of file From a12505dc43ba4a6431c02a020b71b8cc72ca3f19 Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Fri, 29 Dec 2023 21:27:00 +0300 Subject: [PATCH 15/15] Added tests --- C#/forSpbu/SimpleFtp.Tests/GlobalUsings.cs | 1 + .../SimpleFtp.Tests/SimpleFtp.Tests.csproj | 26 +++++++ C#/forSpbu/SimpleFtp.Tests/Tests.cs | 67 +++++++++++++++++++ C#/forSpbu/forSpbu.sln | 7 ++ 4 files changed, 101 insertions(+) create mode 100644 C#/forSpbu/SimpleFtp.Tests/GlobalUsings.cs create mode 100644 C#/forSpbu/SimpleFtp.Tests/SimpleFtp.Tests.csproj create mode 100644 C#/forSpbu/SimpleFtp.Tests/Tests.cs diff --git a/C#/forSpbu/SimpleFtp.Tests/GlobalUsings.cs b/C#/forSpbu/SimpleFtp.Tests/GlobalUsings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/C#/forSpbu/SimpleFtp.Tests/SimpleFtp.Tests.csproj b/C#/forSpbu/SimpleFtp.Tests/SimpleFtp.Tests.csproj new file mode 100644 index 0000000..a7ac4a3 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Tests/SimpleFtp.Tests.csproj @@ -0,0 +1,26 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + diff --git a/C#/forSpbu/SimpleFtp.Tests/Tests.cs b/C#/forSpbu/SimpleFtp.Tests/Tests.cs new file mode 100644 index 0000000..9ce3696 --- /dev/null +++ b/C#/forSpbu/SimpleFtp.Tests/Tests.cs @@ -0,0 +1,67 @@ +using SimpleFtp.Client; +using SimpleFtp.Protocol; + +namespace SimpleFtp.Tests; + +public class Tests +{ + [Test] + public void TestListCommand() + { + var server = new FtpServer(); + var cancellation = new CancellationTokenSource(); + Task.Run(() => server.Listen(cancellation)); + + Directory.CreateDirectory("./tmp"); + File.WriteAllText("./tmp/a.txt", ""); + var request = RequestFactory.Create("1 ./tmp\n"); + var response = FtpClient.Connect("localhost", 32768).Result.SendRequest(request); + Assert.That(response.ToString(), Is.EqualTo("1 ./tmp\\a.txt False\n")); + } + + [Test] + public void TestGetCommand() + { + var server = new FtpServer(); + var cancellation = new CancellationTokenSource(); + Task.Run(() => server.Listen(cancellation)); + + var content = "asd@%!@# !@#%&*()"; + File.WriteAllText("./tmp.txt", content); + var request = RequestFactory.Create("2 ./tmp.txt\n"); + var response = FtpClient.Connect("localhost", 32768).Result.SendRequest(request); + Assert.That(response.ToString(), Is.EqualTo("17 " + content + "\n")); + } + + [Test] + public void TestConcurrentCommands() + { + var server = new FtpServer(); + var cancellation = new CancellationTokenSource(); + Task.Run(() => server.Listen(cancellation)); + + var content = "asd@%!@# !@#%&*()"; + File.WriteAllText("./tmp.txt", content); + var request = RequestFactory.Create("2 ./tmp.txt\n"); + var fstClient = FtpClient.Connect("localhost", 32768).Result; + var secClient = FtpClient.Connect("localhost", 32768).Result; + + var start = new ManualResetEvent(false); + + var fstThread = new Thread(() => + { + start.WaitOne(); + Assert.That(fstClient.SendRequest(request).ToString(), Is.EqualTo("17 " + content + "\n")); + }); + var secThread = new Thread(() => + { + start.WaitOne(); + Assert.That(secClient.SendRequest(request).ToString(), Is.EqualTo("17 " + content + "\n")); + }); + fstThread.Start(); + secThread.Start(); + start.Set(); + fstThread.Join(); + secThread.Join(); + } +} \ No newline at end of file diff --git a/C#/forSpbu/forSpbu.sln b/C#/forSpbu/forSpbu.sln index 59029a0..156d553 100644 --- a/C#/forSpbu/forSpbu.sln +++ b/C#/forSpbu/forSpbu.sln @@ -20,6 +20,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtp.Client", "SimpleF EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtp.Protocol", "SimpleFtp.Protocol\SimpleFtp.Protocol.csproj", "{888C5E3A-3DE2-4405-B4E3-24ABB9A473C5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFtp.Tests", "SimpleFtp.Tests\SimpleFtp.Tests.csproj", "{4B30B838-6F2B-48B2-BF87-16A9B003D12B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -46,6 +48,10 @@ Global {888C5E3A-3DE2-4405-B4E3-24ABB9A473C5}.Debug|Any CPU.Build.0 = Debug|Any CPU {888C5E3A-3DE2-4405-B4E3-24ABB9A473C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {888C5E3A-3DE2-4405-B4E3-24ABB9A473C5}.Release|Any CPU.Build.0 = Release|Any CPU + {4B30B838-6F2B-48B2-BF87-16A9B003D12B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B30B838-6F2B-48B2-BF87-16A9B003D12B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B30B838-6F2B-48B2-BF87-16A9B003D12B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B30B838-6F2B-48B2-BF87-16A9B003D12B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {E007586F-9760-4744-BB25-EDEFD6BA860C} = {D3FCB669-E93F-4F0B-B9C5-6592CE93AC7F} @@ -54,5 +60,6 @@ Global {EE4BF01B-FD41-47A7-B2BF-F9604CFE50B5} = {7A21E6C1-9694-4896-AD34-C0BDB62BF665} {D5835D5B-B4F3-4AF6-8FDB-0133183D3304} = {7A21E6C1-9694-4896-AD34-C0BDB62BF665} {888C5E3A-3DE2-4405-B4E3-24ABB9A473C5} = {7A21E6C1-9694-4896-AD34-C0BDB62BF665} + {4B30B838-6F2B-48B2-BF87-16A9B003D12B} = {7A21E6C1-9694-4896-AD34-C0BDB62BF665} EndGlobalSection EndGlobal