From fd71286bbdbbb69022dee73ba911650ff3832fbd Mon Sep 17 00:00:00 2001 From: Gonzalo Gallotti Date: Wed, 3 Aug 2022 15:52:46 -0300 Subject: [PATCH 1/4] Content Disposition use Standard method to encode. Skip if Safari. --- .../GxClasses/Core/Web/GxHttpServer.cs | 24 +------ .../GxClasses/Helpers/HttpHelper.cs | 34 ++++++++++ .../DotNetCoreUnitTest/HttpUtils/HttpUtils.cs | 66 +++++++++++++++++++ 3 files changed, 102 insertions(+), 22 deletions(-) create mode 100644 dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs diff --git a/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs b/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs index 14d4b2e10..bdf15c331 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs @@ -143,32 +143,12 @@ public void AppendHeader( string name, string value) { if(string.Compare(name, "Content-Disposition", true) == 0) { - value = GetEncodedContentDisposition(value); + value = HttpHelper.GetEncodedContentDisposition(value, _context.GetBrowserType()); } if (_context!=null) _context.SetHeader(name, value); } - private string GetEncodedContentDisposition(string value) - { - int filenameIdx = value.ToLower().IndexOf("filename"); - if(filenameIdx != -1) - { - int eqIdx = value.ToLower().IndexOf("=", filenameIdx); - if (eqIdx != -1) - { - string filename = value.Substring(eqIdx + 1).Trim(); - try - { - value = value.Substring(0, eqIdx + 1) + Uri.EscapeDataString(filename); - } - catch(UriFormatException) //Contains High Surrogate Chars - { - value = value.Substring(0, eqIdx + 1) + GXUtil.UrlEncode(filename); - } - } - } - return value; - } + } public class GxSoapRequest : GxHttpRequest diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs index 8fb9bf054..79c55dd05 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs @@ -28,6 +28,7 @@ using System.Runtime.Serialization; using GeneXus.Mime; using System.Text.RegularExpressions; +using System.Net.Http.Headers; namespace GeneXus.Http { @@ -353,6 +354,39 @@ public static string[] GetParameterValues(string query) } } + public static string GetEncodedContentDisposition(string value, int browserType) + { + int filenameIdx = value.IndexOf("filename", StringComparison.OrdinalIgnoreCase); + int eqIdx = value.IndexOf("=", filenameIdx); + if (filenameIdx == -1 || eqIdx == -1 || browserType == GxContext.BROWSER_SAFARI) //Safari does not supports yet ContentDispositon file name encoding value. + { + return value; + } + + string rawFilename = value.Substring(eqIdx + 1).Trim(); + try + { + string dispositionType = value.Substring(0, value.IndexOf(";")).Trim(); + value = new ContentDispositionHeaderValue(dispositionType) { FileName = rawFilename }.ToString(); + } + catch (Exception) + { + value = value.Substring(0, eqIdx + 1) + EncodeContentDispositionFileName(rawFilename); + } + return value; + } + + private static string EncodeContentDispositionFileName(string filename) + { + try + { + return Uri.EscapeDataString(filename); + } + catch (UriFormatException) //Contains High Surrogate Chars + { + return GXUtil.UrlEncode(filename); + } + } } #if NETCORE public class HttpCookieCollection : Dictionary diff --git a/dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs b/dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs new file mode 100644 index 000000000..79941071e --- /dev/null +++ b/dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs @@ -0,0 +1,66 @@ +using System; +using GeneXus.Application; +using GeneXus.Http; +using Xunit; + +namespace DotNetCoreUnitTest.HttpUtils +{ + public class TestHttpUtils + { + [Fact] + public void TestContentDispositionHeaderEncoding1() + { + String contentDisposition = "attachment; filename=file.pdf"; + String expectedContentDisposition = contentDisposition; + DoTest(contentDisposition, expectedContentDisposition); + } + + [Fact] + public void TestContentDispositionHeaderEncoding2() + { + String contentDisposition = "attachment; filename=file.pdf"; + String expectedContentDisposition = contentDisposition; + DoTest(contentDisposition, expectedContentDisposition, GxContext.BROWSER_SAFARI); + } + + [Fact] + public void TestContentDispositionHeaderEncoding3() + { + String contentDisposition = "attachment; filename=注文詳細.xlsx"; + String expectedContentDisposition = "attachment; filename=\"=?utf-8?B?5rOo5paH6Kmz57SwLnhsc3g=?=\""; + DoTest(contentDisposition, expectedContentDisposition); + } + + [Fact] + public void TestContentDispositionHeaderEncoding4() + { + String contentDisposition = "attachment; filename=注文詳細.xlsx"; + String expectedContentDisposition = contentDisposition; + //Safari does not support rfc5987 + DoTest(contentDisposition, expectedContentDisposition, GxContext.BROWSER_SAFARI); + } + + [Fact] + public void TestContentDispositionHeaderEncoding5() + { + String contentDisposition = "form-data; filename=file.pdf"; + String expectedContentDisposition = contentDisposition; + DoTest(contentDisposition, expectedContentDisposition); + } + + [Fact] + public void TestContentDispositionHeaderEncoding6() + { + String contentDisposition = "ATTACHMENT; FILEname=注文詳細.xlsx"; + String expectedContentDisposition = "ATTACHMENT; filename=\"=?utf-8?B?5rOo5paH6Kmz57SwLnhsc3g=?=\""; + DoTest(contentDisposition, expectedContentDisposition); + } + + private static void DoTest(string contentDisposition, string expectedContentDisposition, int browserType = GxContext.BROWSER_CHROME) + { + string encodedValue = HttpHelper.GetEncodedContentDisposition(contentDisposition, browserType); + + Assert.Equal(expectedContentDisposition, encodedValue); + } + } +} From 6c09f4c5892a86992b7abee46ac8da13fed40e99 Mon Sep 17 00:00:00 2001 From: Gonzalo Gallotti Date: Fri, 5 Aug 2022 11:34:03 -0300 Subject: [PATCH 2/4] Add .NET Framework Tests --- .../DotNetUnitTest/HttpUtils/HttpUtils.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs diff --git a/dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs b/dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs new file mode 100644 index 000000000..b8ab228e6 --- /dev/null +++ b/dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs @@ -0,0 +1,66 @@ +using System; +using GeneXus.Application; +using GeneXus.Http; +using Xunit; + +namespace DotNetUnitTest.HttpUtils +{ + public class TestHttpUtils + { + [Fact] + public void TestContentDispositionHeaderEncoding1() + { + String contentDisposition = "attachment; filename=file.pdf"; + String expectedContentDisposition = contentDisposition; + DoTest(contentDisposition, expectedContentDisposition); + } + + [Fact] + public void TestContentDispositionHeaderEncoding2() + { + String contentDisposition = "attachment; filename=file.pdf"; + String expectedContentDisposition = contentDisposition; + DoTest(contentDisposition, expectedContentDisposition, GxContext.BROWSER_SAFARI); + } + + [Fact] + public void TestContentDispositionHeaderEncoding3() + { + String contentDisposition = "attachment; filename=注文詳細.xlsx"; + String expectedContentDisposition = "attachment; filename=\"=?utf-8?B?5rOo5paH6Kmz57SwLnhsc3g=?=\""; + DoTest(contentDisposition, expectedContentDisposition); + } + + [Fact] + public void TestContentDispositionHeaderEncoding4() + { + String contentDisposition = "attachment; filename=注文詳細.xlsx"; + String expectedContentDisposition = contentDisposition; + //Safari does not support rfc5987 + DoTest(contentDisposition, expectedContentDisposition, GxContext.BROWSER_SAFARI); + } + + [Fact] + public void TestContentDispositionHeaderEncoding5() + { + String contentDisposition = "form-data; filename=file.pdf"; + String expectedContentDisposition = contentDisposition; + DoTest(contentDisposition, expectedContentDisposition); + } + + [Fact] + public void TestContentDispositionHeaderEncoding6() + { + String contentDisposition = "ATTACHMENT; FILEname=注文詳細.xlsx"; + String expectedContentDisposition = "ATTACHMENT; filename=\"=?utf-8?B?5rOo5paH6Kmz57SwLnhsc3g=?=\""; + DoTest(contentDisposition, expectedContentDisposition); + } + + private static void DoTest(string contentDisposition, string expectedContentDisposition, int browserType = GxContext.BROWSER_CHROME) + { + string encodedValue = HttpHelper.GetEncodedContentDisposition(contentDisposition, browserType); + + Assert.Equal(expectedContentDisposition, encodedValue); + } + } +} From 12c707392caddba326e0df307bfda72961ae532f Mon Sep 17 00:00:00 2001 From: Gonzalo Gallotti Date: Fri, 5 Aug 2022 12:30:22 -0300 Subject: [PATCH 3/4] Refactoring. --- .../GxClasses/Core/GXUtilsCommon.cs | 35 +++++++++++++++++++ .../GxClasses/Core/Web/GxHttpServer.cs | 2 +- .../GxClasses/Helpers/HttpHelper.cs | 34 ------------------ .../DotNetCoreUnitTest/HttpUtils/HttpUtils.cs | 4 +-- .../DotNetUnitTest/HttpUtils/HttpUtils.cs | 4 +-- 5 files changed, 40 insertions(+), 39 deletions(-) diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs index eef865a77..52cd9447f 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs @@ -45,6 +45,7 @@ using System.Security; using System.Threading.Tasks; using System.Drawing.Imaging; +using System.Net.Http.Headers; namespace GeneXus.Utils { @@ -4196,6 +4197,40 @@ internal static string AttributeEncode(string sText) return HttpUtility.HtmlAttributeEncode(sText); } + public static string EncodeContentDispositionHeader(string value, int browserType) + { + int filenameIdx = value.IndexOf("filename", StringComparison.OrdinalIgnoreCase); + int eqIdx = value.IndexOf("=", filenameIdx); + if (filenameIdx == -1 || eqIdx == -1 || browserType == GxContext.BROWSER_SAFARI) //Safari does not supports yet ContentDispositon file name encoding value. + { + return value; + } + + string rawFilename = value.Substring(eqIdx + 1).Trim(); + try + { + string dispositionType = value.Substring(0, value.IndexOf(";")).Trim(); + value = new ContentDispositionHeaderValue(dispositionType) { FileName = rawFilename }.ToString(); + } + catch (Exception) + { + value = value.Substring(0, eqIdx + 1) + EncodeContentDispositionFileName(rawFilename); + } + return value; + } + + private static string EncodeContentDispositionFileName(string filename) + { + try + { + return Uri.EscapeDataString(filename); + } + catch (UriFormatException) //Contains High Surrogate Chars + { + return GXUtil.UrlEncode(filename); + } + } + public static string HtmlEndTag(HTMLElement element) { if ((Preferences.DocType == HTMLDocType.HTML4 || Preferences.DocType == HTMLDocType.NONE || Preferences.DocType == HTMLDocType.HTML4S) && diff --git a/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs b/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs index ee0050172..ca60e3ffb 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs @@ -149,7 +149,7 @@ public void AppendHeader( string name, string value) { if(string.Compare(name, "Content-Disposition", true) == 0) { - value = HttpHelper.GetEncodedContentDisposition(value, _context.GetBrowserType()); + value = GXUtil.EncodeContentDispositionHeader(value, _context.GetBrowserType()); } if (_context!=null) _context.SetHeader(name, value); diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs index 18a96cb04..109c871e6 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs @@ -25,7 +25,6 @@ using System.Runtime.Serialization; using GeneXus.Mime; using System.Text.RegularExpressions; -using System.Net.Http.Headers; namespace GeneXus.Http { @@ -351,39 +350,6 @@ public static string[] GetParameterValues(string query) } } - public static string GetEncodedContentDisposition(string value, int browserType) - { - int filenameIdx = value.IndexOf("filename", StringComparison.OrdinalIgnoreCase); - int eqIdx = value.IndexOf("=", filenameIdx); - if (filenameIdx == -1 || eqIdx == -1 || browserType == GxContext.BROWSER_SAFARI) //Safari does not supports yet ContentDispositon file name encoding value. - { - return value; - } - - string rawFilename = value.Substring(eqIdx + 1).Trim(); - try - { - string dispositionType = value.Substring(0, value.IndexOf(";")).Trim(); - value = new ContentDispositionHeaderValue(dispositionType) { FileName = rawFilename }.ToString(); - } - catch (Exception) - { - value = value.Substring(0, eqIdx + 1) + EncodeContentDispositionFileName(rawFilename); - } - return value; - } - - private static string EncodeContentDispositionFileName(string filename) - { - try - { - return Uri.EscapeDataString(filename); - } - catch (UriFormatException) //Contains High Surrogate Chars - { - return GXUtil.UrlEncode(filename); - } - } } #if NETCORE public class HttpCookieCollection : Dictionary diff --git a/dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs b/dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs index 79941071e..b0b135861 100644 --- a/dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs +++ b/dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs @@ -1,6 +1,6 @@ using System; using GeneXus.Application; -using GeneXus.Http; +using GeneXus.Utils; using Xunit; namespace DotNetCoreUnitTest.HttpUtils @@ -58,7 +58,7 @@ public void TestContentDispositionHeaderEncoding6() private static void DoTest(string contentDisposition, string expectedContentDisposition, int browserType = GxContext.BROWSER_CHROME) { - string encodedValue = HttpHelper.GetEncodedContentDisposition(contentDisposition, browserType); + string encodedValue = GXUtil.EncodeContentDispositionHeader(contentDisposition, browserType); Assert.Equal(expectedContentDisposition, encodedValue); } diff --git a/dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs b/dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs index b8ab228e6..fd829fb22 100644 --- a/dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs +++ b/dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs @@ -1,6 +1,6 @@ using System; using GeneXus.Application; -using GeneXus.Http; +using GeneXus.Utils; using Xunit; namespace DotNetUnitTest.HttpUtils @@ -58,7 +58,7 @@ public void TestContentDispositionHeaderEncoding6() private static void DoTest(string contentDisposition, string expectedContentDisposition, int browserType = GxContext.BROWSER_CHROME) { - string encodedValue = HttpHelper.GetEncodedContentDisposition(contentDisposition, browserType); + string encodedValue = GXUtil.EncodeContentDispositionHeader(contentDisposition, browserType); Assert.Equal(expectedContentDisposition, encodedValue); } From 761726c74d89474eed51edc0886979b77dc84d60 Mon Sep 17 00:00:00 2001 From: Gonzalo Gallotti Date: Fri, 5 Aug 2022 15:56:44 -0300 Subject: [PATCH 4/4] Change method visibility to to internal --- .../GxClasses/Properties/AssemblyInfo.cs | 1 + .../GxClasses/Core/GXUtilsCommon.cs | 2 +- .../DotNetUnitTest/HttpUtils/HttpUtils.cs | 66 ------------------- 3 files changed, 2 insertions(+), 67 deletions(-) delete mode 100644 dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs diff --git a/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs b/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs index e1acfec27..d35486342 100644 --- a/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs +++ b/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs @@ -7,3 +7,4 @@ [assembly: InternalsVisibleTo("GeneXus.Deploy.AzureFunctions.Handlers")] [assembly: InternalsVisibleTo("AzureFunctionsTest")] [assembly: InternalsVisibleTo("GXQueue")] +[assembly: InternalsVisibleTo("DotNetCoreUnitTest")] diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs index 52cd9447f..0a8c1687b 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs @@ -4197,7 +4197,7 @@ internal static string AttributeEncode(string sText) return HttpUtility.HtmlAttributeEncode(sText); } - public static string EncodeContentDispositionHeader(string value, int browserType) + internal static string EncodeContentDispositionHeader(string value, int browserType) { int filenameIdx = value.IndexOf("filename", StringComparison.OrdinalIgnoreCase); int eqIdx = value.IndexOf("=", filenameIdx); diff --git a/dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs b/dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs deleted file mode 100644 index fd829fb22..000000000 --- a/dotnet/test/DotNetUnitTest/HttpUtils/HttpUtils.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using GeneXus.Application; -using GeneXus.Utils; -using Xunit; - -namespace DotNetUnitTest.HttpUtils -{ - public class TestHttpUtils - { - [Fact] - public void TestContentDispositionHeaderEncoding1() - { - String contentDisposition = "attachment; filename=file.pdf"; - String expectedContentDisposition = contentDisposition; - DoTest(contentDisposition, expectedContentDisposition); - } - - [Fact] - public void TestContentDispositionHeaderEncoding2() - { - String contentDisposition = "attachment; filename=file.pdf"; - String expectedContentDisposition = contentDisposition; - DoTest(contentDisposition, expectedContentDisposition, GxContext.BROWSER_SAFARI); - } - - [Fact] - public void TestContentDispositionHeaderEncoding3() - { - String contentDisposition = "attachment; filename=注文詳細.xlsx"; - String expectedContentDisposition = "attachment; filename=\"=?utf-8?B?5rOo5paH6Kmz57SwLnhsc3g=?=\""; - DoTest(contentDisposition, expectedContentDisposition); - } - - [Fact] - public void TestContentDispositionHeaderEncoding4() - { - String contentDisposition = "attachment; filename=注文詳細.xlsx"; - String expectedContentDisposition = contentDisposition; - //Safari does not support rfc5987 - DoTest(contentDisposition, expectedContentDisposition, GxContext.BROWSER_SAFARI); - } - - [Fact] - public void TestContentDispositionHeaderEncoding5() - { - String contentDisposition = "form-data; filename=file.pdf"; - String expectedContentDisposition = contentDisposition; - DoTest(contentDisposition, expectedContentDisposition); - } - - [Fact] - public void TestContentDispositionHeaderEncoding6() - { - String contentDisposition = "ATTACHMENT; FILEname=注文詳細.xlsx"; - String expectedContentDisposition = "ATTACHMENT; filename=\"=?utf-8?B?5rOo5paH6Kmz57SwLnhsc3g=?=\""; - DoTest(contentDisposition, expectedContentDisposition); - } - - private static void DoTest(string contentDisposition, string expectedContentDisposition, int browserType = GxContext.BROWSER_CHROME) - { - string encodedValue = GXUtil.EncodeContentDispositionHeader(contentDisposition, browserType); - - Assert.Equal(expectedContentDisposition, encodedValue); - } - } -}