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 eef865a77..0a8c1687b 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); } + internal 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 92b8ffe18..ca60e3ffb 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs @@ -149,32 +149,12 @@ public void AppendHeader( string name, string value) { if(string.Compare(name, "Content-Disposition", true) == 0) { - value = GetEncodedContentDisposition(value); + value = GXUtil.EncodeContentDispositionHeader(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/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs b/dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs new file mode 100644 index 000000000..b0b135861 --- /dev/null +++ b/dotnet/test/DotNetCoreUnitTest/HttpUtils/HttpUtils.cs @@ -0,0 +1,66 @@ +using System; +using GeneXus.Application; +using GeneXus.Utils; +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 = GXUtil.EncodeContentDispositionHeader(contentDisposition, browserType); + + Assert.Equal(expectedContentDisposition, encodedValue); + } + } +}