diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index 04663dd9c..f822ba417 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -45,6 +45,11 @@ jobs: dotnet-version: '6.0.x' include-prerelease: false + - name: Install .NET 8 + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.0.100' + - name: Calculate environment variables id: buildVariables run: | @@ -185,14 +190,14 @@ jobs: $PushToAzureArgs += "--skip-duplicate" } - dotnet $PushToAzureArgs + dotnet $PushToAzureArgs || exit 1 $pushedToAzure += 1 if (!$IsPrerelease) { - dotnet $PushToGitHubArgs + dotnet $PushToGitHubArgs || exit 1 $pushedToGitHub += 1 - dotnet $PushToNugetArgs + dotnet $PushToNugetArgs || exit 1 $pushedToNuget += 1 } diff --git a/.github/workflows/External-Storage-Tests.yml b/.github/workflows/External-Storage-Tests.yml index 5fae5df91..fbdafdbba 100644 --- a/.github/workflows/External-Storage-Tests.yml +++ b/.github/workflows/External-Storage-Tests.yml @@ -32,6 +32,12 @@ jobs: uses: actions/setup-dotnet@v2 with: dotnet-version: '6.0.x' + + - name: Install .NET 8 + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.x' + include-prerelease: true - name: Restore packages run: dotnet restore $Env:SolutionFile diff --git a/.github/workflows/call-beta-bot.yml b/.github/workflows/call-beta-bot.yml new file mode 100644 index 000000000..d7cb4406e --- /dev/null +++ b/.github/workflows/call-beta-bot.yml @@ -0,0 +1,18 @@ +name: Call Beta Bot + +on: + # Triggers the workflow on push to beta branch or changes in a pull request to main branch + push: + branches: [ "beta" ] + pull_request: + types: [ opened, synchronize, reopened, ready_for_review, closed, labeled, unlabeled ] + branches: [ "master" ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + call-workflow: + if: github.repository_owner == 'genexuslabs' + uses: genexuslabs/build-genexus-reusable-workflow/.github/workflows/run-beta-bot.yml@main + secrets: inherit diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..70b0c49ec --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,91 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# ******** NOTE ******** + +name: "CodeQL" + +on: + push: + branches: [ master, beta*, release-* ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '28 9 * * 6' + +jobs: + analyze: + name: Analyze + runs-on: windows-latest + + env: + Configuration: Release + SolutionFile: dotnet\CodeqlSolution.sln + + strategy: + fail-fast: false + matrix: + language: [ 'csharp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + debug: true + languages: ${{ matrix.language }} + + - name: Install .NET 6 + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '6.0.x' + + - name: Install .NET 8 + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.0.100' + + - name: Create temporary solution + run: | + $TempSolution = "CodeqlSolution" + dotnet new sln --name $TempSolution --output dotnet --force + dotnet msbuild dotnet\DotNetStandardClasses.sln /t:DumpProjects -p:DumpSolutionName=$TempSolution /m:1 -p:DumpSolutionTargetFrameworkDefault=net6 + + - name: Build temporary solution + run: dotnet build dotnet\CodeqlSolution.sln --framework net6.0 + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + #- name: Autobuild + # uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" \ No newline at end of file diff --git a/.github/workflows/fortify-net-framework.yml b/.github/workflows/fortify-net-framework.yml index 0c37aa982..8d4a1ce26 100644 --- a/.github/workflows/fortify-net-framework.yml +++ b/.github/workflows/fortify-net-framework.yml @@ -27,17 +27,22 @@ jobs: with: repository: '' + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1.1 + with: + vs-prerelease: true + # Java 8 required by ScanCentral Client and FoD Uploader(Univeral CI Tool) - name: Setup Java uses: actions/setup-java@v1 with: java-version: 1.8 - - - name: Setup MSBuild - uses: microsoft/setup-msbuild@v1 + + - name: Install .NET 8 + uses: actions/setup-dotnet@v3 with: - vs-version: 10.0 - + dotnet-version: '8.0.100' + - name: Create temporal solution run: | $fortifysolution = "FortifySolution" diff --git a/.github/workflows/fortify.yml b/.github/workflows/fortify.yml index 4da890cd9..b2f08d3b8 100644 --- a/.github/workflows/fortify.yml +++ b/.github/workflows/fortify.yml @@ -27,17 +27,22 @@ jobs: with: repository: '' + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1.1 + with: + vs-prerelease: true + # Java 8 required by ScanCentral Client and FoD Uploader(Univeral CI Tool) - name: Setup Java uses: actions/setup-java@v1 with: java-version: 1.8 - - - name: Setup MSBuild - uses: microsoft/setup-msbuild@v1 + + - name: Install .NET 8 + uses: actions/setup-dotnet@v3 with: - vs-version: 10.0 - + dotnet-version: '8.0.100' + - name: Create temporal solution run: | $fortifysolution = "FortifySolution" diff --git a/.github/workflows/veracode.yml b/.github/workflows/veracode.yml index 0f41536ad..b635e46b3 100644 --- a/.github/workflows/veracode.yml +++ b/.github/workflows/veracode.yml @@ -50,6 +50,11 @@ jobs: dotnet-version: '6.0.x' include-prerelease: false + - name: Install .NET 8 + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.x' + include-prerelease: true - name: Build run: | @@ -90,7 +95,7 @@ jobs: pipeline-results-json: results.json source-base-path-1: "^.*/${{ github.event.repository.name }}/dotnet/:dotnet/" - - uses: github/codeql-action/upload-sarif@v1 + - uses: github/codeql-action/upload-sarif@v2 with: # Path to SARIF file relative to the root of the repository sarif_file: veracode-results.sarif diff --git a/README.md b/README.md index 2b52c8372..6f4534743 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # GeneXus Standard Classes for .NET and .NET Framework GeneXus Standard Classes for .NET and .NET Framework generators. -## Build status -| Branch | Status -|---|--- -|master|[![](https://github.com/genexuslabs/dotnetclasses/workflows/Build/badge.svg?branch=master)](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Amaster) -|beta|[![](https://github.com/genexuslabs/dotnetclasses/workflows/Build/badge.svg?branch=beta)](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Abeta) +## Repo status +| Branch | Build | Security +|---|---|--- +|master|[![](https://github.com/genexuslabs/dotnetclasses/workflows/Build/badge.svg?branch=master)](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Amaster)|[![CodeQL](https://github.com/genexuslabs/DotNetClasses/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/genexuslabs/DotNetClasses/actions/workflows/codeql-analysis.yml) +|beta|[![](https://github.com/genexuslabs/dotnetclasses/workflows/Build/badge.svg?branch=beta)](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Abeta)|[![CodeQL](https://github.com/genexuslabs/DotNetClasses/actions/workflows/codeql-analysis.yml/badge.svg?branch=beta)](https://github.com/genexuslabs/DotNetClasses/actions/workflows/codeql-analysis.yml) ## Modules @@ -66,8 +66,8 @@ This repository contains projects for .NET and .NET Framework. It is organized a # How to build ## Requirements -- Visual Studio 2022 -- .NET 6 +- Visual Studio 2022 (17.8 or higher). +- .NET 6 & .NET 8 - .NET Framework 4.7 DevPack # Instructions diff --git a/dotnet/Directory.Build.props b/dotnet/Directory.Build.props index 760d6f7c4..73083e09b 100644 --- a/dotnet/Directory.Build.props +++ b/dotnet/Directory.Build.props @@ -3,7 +3,7 @@ 11.0.0.0 1 $([MSBuild]::Add($(MajorFileVersion), 100)) - 25 + 31 $(COMMIT_NUMBER) 0 $(MajorFileVersion).$(MinorFileVersion).$(PatchFileVersion) @@ -29,6 +29,8 @@ NU5105;CS0618;CS8032;CS0618;SYSLIB0021;SYSLIB0023 true True + + NU1900;NU1901;NU1902;NU1903;NU1904 diff --git a/dotnet/DotNetStandardClasses.sln b/dotnet/DotNetStandardClasses.sln index 8650b334f..01e391f0b 100644 --- a/dotnet/DotNetStandardClasses.sln +++ b/dotnet/DotNetStandardClasses.sln @@ -243,6 +243,26 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GxOffice", "src\dotnetcore\ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "excel", "excel", "{B521FF6D-E081-4DE6-AC00-A9BE3B6439D1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXEventRouter", "src\dotnetcore\Providers\Messaging\GXEventRouter\GXEventRouter.csproj", "{5BBC75F0-E51A-4EBD-A628-92498D319B1D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXAzureEventGrid", "src\dotnetcore\Providers\Messaging\GXAzureEventGrid\GXAzureEventGrid.csproj", "{7250CDB1-95C4-4822-B01B-3CBD73324CC9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreAttackMitigationTest", "test\DotNetCoreAttackMitigationTest\DotNetCoreAttackMitigationTest.csproj", "{2D615969-53E2-4B77-9A9A-75C33865CF76}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreChunkedTest", "test\DotNetCoreChunkedTest\DotNetCoreChunkedTest.csproj", "{5D2B1299-479F-430A-8D72-34D44FB299FD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetPDFUnitTest", "test\DotNetPdfTest\DotNetPDFUnitTest.csproj", "{0FCFB078-5584-469F-92CC-61B0A6216D0D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXOtel.Diagnostics", "src\dotnetcore\Providers\OpenTelemetry\Diagnostics\GXOtel.Diagnostics\GXOtel.Diagnostics.csproj", "{A42066E8-DDB9-4767-AEFA-E6D13EFF051A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreCmdTest", "test\DotNetCoreCmdTest\DotNetCoreCmdTest.csproj", "{956402BD-AC8C-426E-961B-B77B3F3EDAEB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{46DAAFD1-FAF5-4904-8EC5-406BE04E5538}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogTest", "test\benchmarks\LogTest\LogTest.csproj", "{A1DBDCE0-4F09-445F-A202-9B260CDD46CF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccessTokenController_Test", "test\NativeAccessControllerTest\AccessTokenController_Test.csproj", "{A5589382-DB6F-4450-AE2B-6C6AA1643EF1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -585,6 +605,42 @@ Global {E1CD114A-F8A5-4246-B5E3-FD27EDEC7C82}.Debug|Any CPU.Build.0 = Debug|Any CPU {E1CD114A-F8A5-4246-B5E3-FD27EDEC7C82}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1CD114A-F8A5-4246-B5E3-FD27EDEC7C82}.Release|Any CPU.Build.0 = Release|Any CPU + {5BBC75F0-E51A-4EBD-A628-92498D319B1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5BBC75F0-E51A-4EBD-A628-92498D319B1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5BBC75F0-E51A-4EBD-A628-92498D319B1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5BBC75F0-E51A-4EBD-A628-92498D319B1D}.Release|Any CPU.Build.0 = Release|Any CPU + {7250CDB1-95C4-4822-B01B-3CBD73324CC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7250CDB1-95C4-4822-B01B-3CBD73324CC9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7250CDB1-95C4-4822-B01B-3CBD73324CC9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7250CDB1-95C4-4822-B01B-3CBD73324CC9}.Release|Any CPU.Build.0 = Release|Any CPU + {2D615969-53E2-4B77-9A9A-75C33865CF76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D615969-53E2-4B77-9A9A-75C33865CF76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D615969-53E2-4B77-9A9A-75C33865CF76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D615969-53E2-4B77-9A9A-75C33865CF76}.Release|Any CPU.Build.0 = Release|Any CPU + {5D2B1299-479F-430A-8D72-34D44FB299FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D2B1299-479F-430A-8D72-34D44FB299FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D2B1299-479F-430A-8D72-34D44FB299FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D2B1299-479F-430A-8D72-34D44FB299FD}.Release|Any CPU.Build.0 = Release|Any CPU + {0FCFB078-5584-469F-92CC-61B0A6216D0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0FCFB078-5584-469F-92CC-61B0A6216D0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0FCFB078-5584-469F-92CC-61B0A6216D0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0FCFB078-5584-469F-92CC-61B0A6216D0D}.Release|Any CPU.Build.0 = Release|Any CPU + {A42066E8-DDB9-4767-AEFA-E6D13EFF051A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A42066E8-DDB9-4767-AEFA-E6D13EFF051A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A42066E8-DDB9-4767-AEFA-E6D13EFF051A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A42066E8-DDB9-4767-AEFA-E6D13EFF051A}.Release|Any CPU.Build.0 = Release|Any CPU + {956402BD-AC8C-426E-961B-B77B3F3EDAEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {956402BD-AC8C-426E-961B-B77B3F3EDAEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {956402BD-AC8C-426E-961B-B77B3F3EDAEB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {956402BD-AC8C-426E-961B-B77B3F3EDAEB}.Release|Any CPU.Build.0 = Release|Any CPU + {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Release|Any CPU.Build.0 = Release|Any CPU + {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -701,6 +757,16 @@ Global {BE108BE8-805D-4FCF-86BA-B2EAF581FFB2} = {F900A4AD-7249-41B4-B918-CB9E8C73747C} {E1CD114A-F8A5-4246-B5E3-FD27EDEC7C82} = {B521FF6D-E081-4DE6-AC00-A9BE3B6439D1} {B521FF6D-E081-4DE6-AC00-A9BE3B6439D1} = {2261B65E-3757-4E5B-9DCD-EAE8D1E236A3} + {5BBC75F0-E51A-4EBD-A628-92498D319B1D} = {4C43F2DA-59E5-46F5-B691-195449498555} + {7250CDB1-95C4-4822-B01B-3CBD73324CC9} = {30159B0F-BE61-4DB7-AC02-02851426BE4B} + {2D615969-53E2-4B77-9A9A-75C33865CF76} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} + {5D2B1299-479F-430A-8D72-34D44FB299FD} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} + {0FCFB078-5584-469F-92CC-61B0A6216D0D} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} + {A42066E8-DDB9-4767-AEFA-E6D13EFF051A} = {BBE020D4-C0FF-41A9-9EB1-D1EE12CC4BB8} + {956402BD-AC8C-426E-961B-B77B3F3EDAEB} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} + {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} + {A1DBDCE0-4F09-445F-A202-9B260CDD46CF} = {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} + {A5589382-DB6F-4450-AE2B-6C6AA1643EF1} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C} diff --git a/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj b/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj index 6d8118577..45d27f4a7 100644 --- a/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj +++ b/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj @@ -1,6 +1,6 @@ - net462;net6.0 + net462;net6.0;net8.0 GeneXus.Data.NTier GeneXus.Data.DynService.Core CA1812 @@ -18,4 +18,8 @@ + + + + \ No newline at end of file diff --git a/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj b/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj index 0cbb8aec8..16e8434b3 100644 --- a/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj +++ b/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj @@ -1,6 +1,6 @@ - net462;net6.0 + net462;net6.0;net8.0 GeneXus.Data.NTier GeneXus.Data.DynService.DynamoDB false @@ -13,6 +13,7 @@ + diff --git a/dotnet/src/dotnetcommon/GxCryptography/EncryptionProvider.cs b/dotnet/src/dotnetcommon/GxCryptography/EncryptionProvider.cs index c17a4064e..549cd8bcd 100644 --- a/dotnet/src/dotnetcommon/GxCryptography/EncryptionProvider.cs +++ b/dotnet/src/dotnetcommon/GxCryptography/EncryptionProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography; using System.IO; @@ -58,7 +58,9 @@ public NativeSymmetricEncryption(string algorithm, int keySize) _symAlg = Aes.Create(); break; default: +#pragma warning disable SYSLIB0045 // Type or member is obsolete _symAlg = SymmetricAlgorithm.Create(algorithm); +#pragma warning restore SYSLIB0045 // Type or member is obsolete break; } if (_symAlg != null) diff --git a/dotnet/src/dotnetcommon/GxCryptography/GxCryptography.csproj b/dotnet/src/dotnetcommon/GxCryptography/GxCryptography.csproj index b5ffc6cc1..fef7ad094 100644 --- a/dotnet/src/dotnetcommon/GxCryptography/GxCryptography.csproj +++ b/dotnet/src/dotnetcommon/GxCryptography/GxCryptography.csproj @@ -1,8 +1,8 @@ - net462;net6.0 + net462;net6.0;net8.0 GxCryptography - 618;1607;1698;SYSLIB0021;SYSLIB0027;SYSLIB0028;SYSLIB0023 + 618;1607;1698;SYSLIB0021;SYSLIB0027;SYSLIB0028;SYSLIB0023;SYSLIB0022 GxCryptography Data Access GeneXus.Cryptography diff --git a/dotnet/src/dotnetcommon/GxCryptographyCommon/GxCryptographyCommon.csproj b/dotnet/src/dotnetcommon/GxCryptographyCommon/GxCryptographyCommon.csproj index 4b040e8f1..d6db9c1ef 100644 --- a/dotnet/src/dotnetcommon/GxCryptographyCommon/GxCryptographyCommon.csproj +++ b/dotnet/src/dotnetcommon/GxCryptographyCommon/GxCryptographyCommon.csproj @@ -1,6 +1,6 @@ - net462;net6.0 + net462;net6.0;net8.0 GxCryptographyCommon 618;1607;1698 GxCryptographyCommon diff --git a/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj b/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj index a46827308..080986242 100644 --- a/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj +++ b/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj @@ -1,12 +1,16 @@ - net462;net6.0 + net462;net6.0;net8.0 GeneXus.Encryption GxEncrypt Encrypt64 Decrypt64 GeneXus.Encrypt - - + + + + + + \ No newline at end of file diff --git a/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBConnection.cs b/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBConnection.cs index 34bf4cea4..2d64302a9 100644 --- a/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBConnection.cs +++ b/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBConnection.cs @@ -12,7 +12,6 @@ using GeneXus.Cache; using GeneXus.Data.Cosmos; using GeneXus.Data.NTier.CosmosDB; -using log4net; using Microsoft.Azure.Cosmos; namespace GeneXus.Data.NTier @@ -25,6 +24,24 @@ public override IDataReader GetCacheDataReader(CacheItem item, bool computeSize, { return new GxCosmosDBCacheDataReader(item, computeSize, keyCache); } + protected override string BuildConnectionStringForLog(string datasourceName, string userId, + string userPassword, string databaseName, string port, string schema, string extra) + { + StringBuilder connectionString = new StringBuilder(); + if (!string.IsNullOrEmpty(datasourceName)) + { + connectionString.AppendFormat("Data Source={0};", NaV); + } + if (userId != null) + { + connectionString.AppendFormat(";User ID={0};Password={1}", userId, NaV); + } + if (!string.IsNullOrEmpty(extra)) + { + connectionString.AppendFormat(";{0}", extra); + } + return connectionString.ToString(); + } } public class CosmosDBConnection : ServiceConnection @@ -47,7 +64,7 @@ public class CosmosDBConnection : ServiceConnection //Options not supported by the spec yet //private const string DISTINCT = "DISTINCT"; - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(CosmosDBConnection)); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); public override string ConnectionString { get diff --git a/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBDataReader.cs b/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBDataReader.cs index f74528139..45ee1b70a 100644 --- a/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBDataReader.cs +++ b/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBDataReader.cs @@ -9,7 +9,6 @@ using GeneXus.Cache; using GeneXus.Data.NTier; using GeneXus.Data.NTier.CosmosDB; -using log4net; using Microsoft.Azure.Cosmos; using Newtonsoft.Json; @@ -27,7 +26,7 @@ public class CosmosDBDataReader : IDataReader private int ItemCount; private List> Items = null; - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(CosmosDBDataReader)); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); private void CheckCurrentPosition() { if (m_currentEntry == null) diff --git a/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBRequestWrapper.cs b/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBRequestWrapper.cs index aaa3a62c2..faf6b9f97 100644 --- a/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBRequestWrapper.cs +++ b/dotnet/src/dotnetcore/DynService/Cosmos/CosmosDBRequestWrapper.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading.Tasks; using GeneXus.Data.Cosmos; -using log4net; using Microsoft.Azure.Cosmos; using Newtonsoft.Json; @@ -14,7 +13,7 @@ public class RequestWrapper private readonly Container m_container; private readonly CosmosClient m_cosmosClient; private readonly QueryDefinition m_queryDefinition; - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(RequestWrapper)); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); public string idValue { get; set; } public object partitionKeyValue { get; set; } public bool queryByPK { get; set; } diff --git a/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj b/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj index 120fad8f9..0c61c7593 100644 --- a/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj +++ b/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 GeneXus.Data.NTier GeneXus.Data.DynService.CosmosDB false @@ -13,7 +13,9 @@ - + + + diff --git a/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj b/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj index 6626d65c7..d57e3d3ee 100644 --- a/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj +++ b/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 NETCORE DynService OData GeneXus.DynService.OData.Core @@ -12,8 +12,8 @@ + - diff --git a/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj b/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj index 5d7ca697c..e95fc82c8 100644 --- a/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj +++ b/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 NETCORE; GXGeolocation GXHttpServices WebSocket GeneXus.Classes.Web.Core @@ -29,7 +29,9 @@ - + + + @@ -43,11 +45,5 @@ - - - False - ..\libs\Jayrock.dll - - \ No newline at end of file diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXBCRestService.cs b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXBCRestService.cs index 4b603f65b..e93c59ac1 100644 --- a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXBCRestService.cs +++ b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXBCRestService.cs @@ -43,10 +43,10 @@ public override Task Post() bool gxinsertorupdate = IsRestParameter(INSERT_OR_UPDATE_PARAMETER); GxSilentTrnSdt entity = (GxSilentTrnSdt)Activator.CreateInstance(_worker.GetType(), new Object[] { _gxContext }); - var entity_interface = MakeRestType(entity, false); + object entity_interface = MakeRestType(entity, false); entity_interface = ReadRequestBodySDTObj(entity_interface.GetType()); - var worker_interface = MakeRestType(_worker, false); + object worker_interface = MakeRestType(_worker, false); worker_interface.GetType().GetMethod("CopyFrom").Invoke(worker_interface, new object[] { entity_interface }); if (gxcheck) @@ -191,12 +191,12 @@ public override Task Put(object parameters) { bool gxcheck = IsRestParameter(CHECK_PARAMETER); GxSilentTrnSdt entity = (GxSilentTrnSdt)Activator.CreateInstance(_worker.GetType(), new Object[] { _gxContext }); - var entity_interface = MakeRestType(entity, false); + object entity_interface = MakeRestType(entity, false); entity_interface = ReadRequestBodySDTObj(entity_interface.GetType()); string entityHash = entity_interface.GetType().GetProperty("Hash").GetValue(entity_interface) as string; ReflectionHelper.CallBCMethod(_worker, LOAD_METHOD, key); - var worker_interface = MakeRestType(_worker, false); + object worker_interface = MakeRestType(_worker, false); string currentHash = worker_interface.GetType().GetProperty("Hash").GetValue(worker_interface) as string; if (entityHash == currentHash) { @@ -249,7 +249,7 @@ private object ReadRequestBodySDTObj(Type type) { using (var reader = new StreamReader(_httpContext.Request.Body)) { - var sdtData = reader.ReadToEnd(); + string sdtData = reader.ReadToEnd(); using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(sdtData))) { DataContractJsonSerializer serializer = new DataContractJsonSerializer(type); diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs index 25573f464..5851c9081 100644 --- a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs +++ b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs @@ -15,20 +15,17 @@ using GeneXus.Http; using GeneXus.HttpHandlerFactory; using GeneXus.Metadata; -using GeneXus.Procedure; using GeneXus.Utils; -using log4net; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Net.Http.Headers; namespace GxClasses.Web.Middleware { internal class GXRouting : IGXRouting { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(IGXRouting)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public static string VirtualPath = string.Empty; public static string LocalPath = Directory.GetCurrentDirectory(); @@ -47,7 +44,7 @@ internal class GXRouting : IGXRouting static Regex SDSVC_PATTERN = new Regex("([^/]+/)*(sdsvc_[^/]+/[^/]+)(\\?.*)*"); - const string PRIVATE_DIR = "private"; + internal const string PRIVATE_DIR = "private"; public Dictionary servicesPathUrl = new Dictionary(); public Dictionary> servicesMap = new Dictionary>(); public Dictionary, String>> servicesMapData = new Dictionary, string>>(); @@ -61,6 +58,7 @@ public GXRouting(string baseURL) ServicesGroupSetting(); ServicesFunctionsMetadata(); GetAzureDeploy(); + SvcFiles(); } static public List GetRouteController(Dictionary apiPaths, @@ -353,19 +351,53 @@ internal string GetGxRouteValue(string path) } public bool ServiceInPath(String path, out String actualPath) { - actualPath = string.Empty; - foreach (String subPath in servicesPathUrl.Keys) + actualPath = null; + string tmppath = path.Substring(0, 1).Equals("/") ? path.Substring(1) : path; + int pos = tmppath.IndexOf("/"); + if (pos > 0) { - if (path.ToLower().Contains($"/{subPath.ToLower()}")) + string innerPath = tmppath.Substring(pos, tmppath.Length - pos); + actualPath = FindPath(innerPath, servicesPathUrl, true); + } + if (String.IsNullOrEmpty(actualPath)) + { + // fallback + actualPath = FindPath(path, servicesPathUrl, false); + if (String.IsNullOrEmpty(actualPath)) + { + actualPath = String.Empty; + GXLogging.Debug(log, $"ServiceInPath path:{path} not found"); + return false; + } + else + { + return true; + } + } + else { + return true; + } + } + + private String FindPath(string innerPath, Dictionary servicesPathUrl, bool startTxt) + { + string actualPath = String.Empty; + foreach (var subPath in from String subPath in servicesPathUrl.Keys + select subPath) + { + bool match = false; + innerPath = innerPath.ToLower(); + match = (startTxt)? innerPath.StartsWith($"/{subPath.ToLower()}"): innerPath.Contains($"/{subPath.ToLower()}"); + if (match) { actualPath = subPath.ToLower(); GXLogging.Debug(log, $"ServiceInPath actualPath:{actualPath}"); - return true; + return subPath; } } - GXLogging.Debug(log, $"ServiceInPath path:{path} not found"); - return false; + return null; } + public GxRestWrapper GetController(HttpContext context, string controller, string methodName, Dictionary variableAlias) { return GetController(context, new ControllerInfo() { Name = controller, MethodName = methodName, VariableAlias = variableAlias }); @@ -380,8 +412,7 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle GxContext gxContext = GxContext.CreateDefaultInstance(); gxContext.HttpContext = context; context.NewSessionCheck(); - string nspace; - Config.GetValueOf("AppMainNamespace", out nspace); + string nspace = Preferences.AppMainNamespace; String tmpController = controller; String addNspace = string.Empty; @@ -400,7 +431,7 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle bool privateDirExists = Directory.Exists(privateDir); GXLogging.Debug(log, $"PrivateDir:{privateDir} asssemblycontroller:{asssemblycontroller}"); - + string svcFile=null; if (privateDirExists && File.Exists(Path.Combine(privateDir, $"{asssemblycontroller.ToLower()}.grp.json"))) { controller = tmpController; @@ -415,9 +446,7 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle else { string controllerLower = controller.ToLower(); - string svcFile = SvcFile($"{controller}{SvcExtension}"); - if (svcFile==null) - svcFile = SvcFile($"{controllerLower}{SvcExtension}"); + svcFile = SvcFile($"{controller}{SvcExtension}"); if (File.Exists(svcFile)) { string[] controllerAssemblyQualifiedName = new string(File.ReadLines(svcFile).First().SkipWhile(c => c != '"') @@ -449,22 +478,26 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle GXLogging.Warn(log, $"Controller was not found"); return null; } - string SvcFile(string controller) + + private void SvcFiles() { - if (svcFiles == null) + svcFiles = new HashSet(new CaseInsensitiveStringEqualityComparer()); + foreach (string file in Directory.GetFiles(ContentRootPath, SvcExtensionPattern, SearchOption.AllDirectories)) { - svcFiles = new HashSet(); - foreach (string file in Directory.GetFiles(ContentRootPath, SvcExtensionPattern, SearchOption.AllDirectories)) - { - svcFiles.Add(file); - } + svcFiles.Add(file); } + } + string SvcFile(string controller) + { string fileName; - string controllerFullName = Path.Combine(ContentRootPath, controller); - if (svcFiles.TryGetValue(new FileInfo(controllerFullName).FullName, out fileName)) + string controllerFullName = new FileInfo(Path.Combine(ContentRootPath, controller)).FullName; + if (svcFiles.TryGetValue(controllerFullName, out fileName)) return fileName; else + { + GXLogging.Warn(log, "Service file not found:" + controllerFullName); return null; + } } public void ServicesGroupSetting() @@ -604,7 +637,18 @@ public string GAM } + internal class CaseInsensitiveStringEqualityComparer : IEqualityComparer + { + public bool Equals(string x, string y) + { + return string.Equals(x, y, StringComparison.OrdinalIgnoreCase); + } + public int GetHashCode(string obj) + { + return obj.ToLower().GetHashCode(); + } + } public static class AzureFeature { public const string AzureServerless = "AzureServerless"; diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs index 549b6ba9e..d072c5d27 100644 --- a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs +++ b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs @@ -1,17 +1,16 @@ using System; +using System.Collections.Generic; +using System.IO; +using System.Net; using System.Reflection; +using System.Threading.Tasks; +using GeneXus.Application; using GeneXus.Configuration; -using log4net; +using GeneXus.Http; +using GeneXus.Mime; +using GeneXus.Procedure; using GeneXus.Utils; using Microsoft.AspNetCore.Http; -using System.Threading.Tasks; -using Microsoft.Extensions.Options; -using System.Net; -using GeneXus.Mime; -using GeneXus.Http; -using System.Collections.Generic; -using GeneXus.Application; -using System.IO; namespace GeneXus.HttpHandlerFactory { @@ -32,32 +31,13 @@ public class BaseUrls public class HandlerFactory { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.HttpHandlerFactory.HandlerFactory)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private string _basePath; static Dictionary _aspxObjects = new Dictionary(){ {"gxoauthlogout",typeof(GXOAuthLogout)}, {"gxoauthuserinfo",typeof(GXOAuthUserInfo)}, {"gxoauthaccesstoken",typeof(GXOAuthAccessToken)}, {"gxmulticall",typeof(GXMultiCall)}}; - static Dictionary _aspxRewrite = new Dictionary(){ - {"oauth/access_token","gxoauthaccesstoken.aspx"}, - {"oauth/logout","gxoauthlogout.aspx"}, - {"oauth/userinfo","gxoauthuserinfo.aspx"}, - {"oauth/gam/signin","agamextauthinput.aspx"}, - {"oauth/gam/callback","agamextauthinput.aspx"}, - {"oauth/gam/access_token","agamoauth20getaccesstoken.aspx"}, - {"oauth/gam/userinfo","agamoauth20getuserinfo.aspx"}, - {"oauth/gam/signout","agamextauthinput.aspx"}, - {"saml/gam/signin","Saml2/SignIn"}, - {"saml/gam/callback","gamexternalauthenticationinputsaml20_ws.aspx"}, - {"saml/gam/signout","Saml2/Logout"}, - {"oauth/requesttokenservice","agamstsauthappgetaccesstoken.aspx"}, - {"oauth/queryaccesstoken","agamstsauthappvalidaccesstoken.aspx"}, - {"oauth/gam/v2.0/access_token","agamoauth20getaccesstoken_v20.aspx"}, - {"oauth/gam/v2.0/userinfo","agamoauth20getuserinfo_v20.aspx"}, - {"oauth/gam/v2.0/requesttokenanduserinfo","aGAMSSORestRequestTokenAndUserInfo_v20.aspx"}}; - private const string QUERYVIEWER_NAMESPACE = "QueryViewer.Services"; - private const string GXFLOW_NSPACE = "GXflow.Programs"; private static List GxNamespaces; public HandlerFactory() @@ -95,8 +75,16 @@ public async Task Invoke(HttpContext context) handler.sendAdditionalHeaders(); return Task.CompletedTask; }); - handler.ProcessRequest(context); - await Task.CompletedTask; + GXWebProcedure gxWebProc = handler as GXWebProcedure; + if (gxWebProc != null && gxWebProc.GetAsyncEnabledInternal()) + { + await gxWebProc.ProcessRequestAsync(context); + } + else + { + handler.ProcessRequest(context); + await Task.CompletedTask; + } handler.ControlOutputWriter?.Flush(); } else @@ -124,9 +112,9 @@ private static string ObjectUrl(string requestPath, string basePath) } lastSegment = CleanUploadUrlSuffix(lastSegment.TrimStart('/').ToLower()); GXLogging.Debug(log, "ObjectUrl:", lastSegment); - if (_aspxRewrite.ContainsKey(lastSegment)) + if (HttpHelper.GAMServices.ContainsKey(lastSegment)) { - return _aspxRewrite[lastSegment]; + return HttpHelper.GAMServices[lastSegment]; } return lastSegment; } @@ -169,11 +157,15 @@ public IHttpHandler GetHandler(HttpContext context, string requestType, string u string className; if (cname.StartsWith("agxpl_", StringComparison.OrdinalIgnoreCase) || cname.Equals("gxqueryviewerforsd", StringComparison.OrdinalIgnoreCase)) { - className = $"{QUERYVIEWER_NAMESPACE}.{cname}"; + className = $"{HttpHelper.QUERYVIEWER_NAMESPACE}.{cname}"; } else if (Preferences.GxpmEnabled && (cname.StartsWith("awf", StringComparison.OrdinalIgnoreCase) || cname.StartsWith("wf", StringComparison.OrdinalIgnoreCase) || cname.StartsWith("apwf", StringComparison.OrdinalIgnoreCase))) { - className = $"{GXFLOW_NSPACE}.{cname}"; + className = $"{HttpHelper.GXFLOW_NSPACE}.{cname}"; + } + else if (HttpHelper.GamServicesInternalName.Contains(cname)) + { + className = $"{HttpHelper.GAM_NSPACE}.{cname}"; } else { diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs b/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs index ae0051f63..84430c30a 100644 --- a/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs +++ b/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs @@ -1,11 +1,8 @@ -using GeneXus.Configuration; -using GeneXus.Metadata; -using GeneXus.Notifications.WebSocket; -using GeneXus.Procedure; -using GeneXus.Services; -using GeneXus.Utils; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; -using log4net; +#endif using System; using System.Collections.Generic; using System.Linq; @@ -13,12 +10,18 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using GeneXus.Configuration; +using GeneXus.Metadata; +using GeneXus.Notifications.WebSocket; +using GeneXus.Procedure; +using GeneXus.Services; +using GeneXus.Utils; namespace GeneXus.Http.WebSocket { public class WSHandler: WebSocketHandler, IGXWebSocketAsync { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(WSHandler)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private const string GX_NOTIFICATIONINFO_NAME = "GeneXus.Core.genexus.server.SdtNotificationInfo"; protected static WebSocketConnectionManager WebSocketConnectionManager = new WebSocketConnectionManager(); @@ -120,8 +123,7 @@ private bool ExecuteHandler(HandlerType type, Object[] parameters) { try { - string nSpace = string.Empty; - Config.GetValueOf("AppMainNamespace", out nSpace); + string nSpace = Preferences.AppMainNamespace; GXProcedure obj = null; try { diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Properties/AssemblyInfo.cs b/dotnet/src/dotnetcore/GxClasses.Web/Properties/AssemblyInfo.cs index 7138fabec..c6a284870 100644 --- a/dotnet/src/dotnetcore/GxClasses.Web/Properties/AssemblyInfo.cs +++ b/dotnet/src/dotnetcore/GxClasses.Web/Properties/AssemblyInfo.cs @@ -5,4 +5,6 @@ [assembly: InternalsVisibleTo("DotNetCoreWebUnitTest")] [assembly: InternalsVisibleTo("GxNetCoreStartup")] [assembly: InternalsVisibleTo("GeneXus.Deploy.AzureFunctions.Handlers")] -[assembly: InternalsVisibleTo("AzureFunctionsTest")] \ No newline at end of file +[assembly: InternalsVisibleTo("AzureFunctionsTest")] +[assembly: InternalsVisibleTo("DotNetCoreAttackMitigationTest")] +[assembly: InternalsVisibleTo("DotNetCoreChunkedTest")] diff --git a/dotnet/src/dotnetcore/GxClasses/Domain/GXXmlReadWrite.cs b/dotnet/src/dotnetcore/GxClasses/Domain/GXXmlReadWrite.cs index 369c75818..2c5fe2630 100644 --- a/dotnet/src/dotnetcore/GxClasses/Domain/GXXmlReadWrite.cs +++ b/dotnet/src/dotnetcore/GxClasses/Domain/GXXmlReadWrite.cs @@ -1,19 +1,17 @@ using System; -using System.Xml; using System.Collections; using System.Collections.Specialized; -using System.Text; -using System.Net; using System.IO; +using System.Net; +using System.Text; +using System.Xml; using System.Xml.Schema; +using System.Xml.XPath; +using System.Xml.Xsl; using GeneXus.Application; using GeneXus.Http.Client; using GeneXus.Http.Server; using GeneXus.Utils; -using log4net; - -using System.Xml.Xsl; -using System.Xml.XPath; namespace GeneXus.XML { @@ -32,7 +30,6 @@ public interface IGxXmlFunctions } public class GXXMLReader : IDisposable { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Configuration.Config)); public const short ElementType = 1; public const short EndTagType = 2; public const short TextType = 4; @@ -275,14 +272,6 @@ public void OpenFromString(string s) Uri baseUri = new Uri( sBaseDirectory ); Resolver.Myself = baseUri; CreateXMLSettings(); - try - { - if (File.Exists(s)) - XMLInput = new FileStream(s, FileMode.Open, FileAccess.Read); - } - catch(Exception ex) { - GXLogging.Warn(log, "OpenFromString file failed", ex); - } if (XMLInput ==null) XMLInput = new StringReader(s); } @@ -1143,7 +1132,7 @@ internal void AppendNode(GXSOAPContext ctx, Node node, int extent) char[] trimChars = { '\t', ' ' }; - if (node.NodeType != ElementType) return; + if (node==null || node.NodeType != ElementType) return; switch (node.NodeType) { case ElementType: @@ -1896,7 +1885,7 @@ public void Dispose() public class GXXMLWriter: IDisposable { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXXMLWriter)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private XmlTextWriter writer; private short errorCode; @@ -2142,6 +2131,10 @@ string removeUnallowedChars( string s) public short WriteElement (string Name, string Value) { WriteStartElement(Name); + if (Value==null) + { + Value = string.Empty; + } valueBuffer = Value; return 0; } diff --git a/dotnet/src/dotnetcore/GxClasses/Domain/JArray.cs b/dotnet/src/dotnetcore/GxClasses/Domain/JArray.cs new file mode 100644 index 000000000..8d53a77e4 --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Domain/JArray.cs @@ -0,0 +1,360 @@ +#region License, Terms and Conditions +// +// Jayrock - A JSON-RPC implementation for the Microsoft .NET Framework +// Written by Atif Aziz (atif.aziz@skybow.com) +// Copyright (c) Atif Aziz. All rights reserved. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 2.1 of the License, or (at your option) +// any later version. +// +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +#endregion + +namespace GeneXus.Application +{ + #region Imports + + using System; + using System.Collections; + using System.Globalization; + using System.Text; + using GeneXus.Utils; + + #endregion + + /// + /// An ordered sequence of values. This class also provides a number of + /// methods that can be found on a JavaScript Array for sake of parity. + /// + /// + /// + /// Public Domain 2002 JSON.org, ported to C# by Are Bjolseth (teleplan.no) + /// and re-adapted by Atif Aziz (www.raboof.com) + /// + + [ Serializable ] + internal class JArray : CollectionBase, IJayrockCompatible + { + public JArray() {} + + public JArray(IEnumerable collection) + { + foreach (object item in collection) + List.Add(item); + } + + public virtual object this[int index] + { + get { return InnerList[index]; } + set { List[index] = value; } + } + + public int Length + { + get { return Count; } + } + + public JArray Put(object value) + { + Add(value); + return this; + } + + public virtual void Add(object value) + { + List.Add(value); + } + public virtual void Add(int index, object value) + { + InnerList.Insert(index, value); + } + + public virtual void Remove(object value) + { + List.Remove(value); + } + + public virtual bool Contains(object value) + { + return List.Contains(value); + } + + public virtual int IndexOf(object value) + { + return List.IndexOf(value); + } + + public virtual bool HasValueAt(int index) + { + return this[index] != null; + } + + public virtual object GetValue(int index) + { + return GetValue(index, null); + } + + public virtual object GetValue(int index, object defaultValue) + { + object value = this[index]; + return value != null ? value : defaultValue; + } + + public virtual bool GetBoolean(int index) + { + return GetBoolean(index, false); + } + + public virtual bool GetBoolean(int index, bool defaultValue) + { + object value = GetValue(index); + if (value == null) return defaultValue; + return Convert.ToBoolean(value, CultureInfo.InvariantCulture); + } + + public virtual double GetDouble(int index) + { + return GetDouble(index, float.NaN); + } + + public virtual double GetDouble(int index, float defaultValue) + { + object value = GetValue(index); + if (value == null) return defaultValue; + return Convert.ToDouble(value, CultureInfo.InvariantCulture); + } + + public virtual int GetInt32(int index) + { + return GetInt32(index, 0); + } + + public virtual int GetInt32(int index, int defaultValue) + { + object value = GetValue(index); + if (value == null) return defaultValue; + return Convert.ToInt32(value, CultureInfo.InvariantCulture); + } + + public virtual string GetString(int index) + { + return GetString(index, string.Empty); + } + + public virtual string GetString(int index, string defaultValue) + { + object value = GetValue(index); + if (value == null) return defaultValue; + return value.ToString(); + } + + public virtual JArray GetArray(int index) + { + return (JArray) GetValue(index); + } + + public virtual JObject GetObject(int index) + { + return (JObject) GetValue(index); + } + + protected override void OnValidate(object value) + { + // + // Null values are allowed in a JSON array so don't delegate + // to the base class (CollectionBase) implementation since that + // disallows null entries by default. + // + } + + /// + /// Make an JSON external form string of this JsonArray. For + /// compactness, no unnecessary whitespace is added. + /// + /// + /// This method assumes that the data structure is acyclical. + /// + + public override string ToString() + { + return TextJsonSerializer.SerializeToJayrockCompatibleJson(this); + } + + + /// + /// Copies the elements to a new object array. + /// + + public virtual object[] ToArray() + { + return (object[]) ToArray(typeof(object)); + } + + /// + /// Copies the elements to a new array of the specified type. + /// + + public virtual Array ToArray(Type elementType) + { + return InnerList.ToArray(elementType); + } + + public virtual void Reverse() + { + InnerList.Reverse(); + } + + // + // Methods that imitate the JavaScript array methods. + // + + /// + /// Appends new elements to an array. + /// + /// + /// The new length of the array. + /// + /// + /// This method appends elements in the order in which they appear. If + /// one of the arguments is an array, it is added as a single element. + /// Use the method to join the elements from two or + /// more arrays. + /// + + public int Push(object value) + { + Add(value); + return Count; + } + + /// + /// Appends new elements to an array. + /// + /// + /// The new length of the array. + /// + /// + /// This method appends elements in the order in which they appear. If + /// one of the arguments is an array, it is added as a single element. + /// Use the method to join the elements from two or + /// more arrays. + /// + + public int Push(params object[] values) + { + if (values != null) + { + foreach (object value in values) + Push(value); + } + + return Count; + } + + /// + /// Removes the last element from an array and returns it. + /// + /// + /// If the array is empty, null is returned. + /// + + public object Pop() + { + if (Count == 0) + return null; + + object lastValue = InnerList[Count - 1]; + RemoveAt(Count - 1); + return lastValue; + } + + /// + /// Returns a new array consisting of a combination of two or more + /// arrays. + /// + + public JArray Concat(params object[] values) + { + JArray newArray = new JArray(this); + + if (values != null) + { + foreach (object value in values) + { + JArray arrayValue = value as JArray; + + if (arrayValue != null) + { + foreach (object arrayValueValue in arrayValue) + newArray.Push(arrayValueValue); + } + else + { + newArray.Push(value); + } + } + } + + return newArray; + } + + /// + /// Removes the first element from an array and returns it. + /// + + public object Shift() + { + if (Count == 0) + return null; + + object firstValue = InnerList[0]; + RemoveAt(0); + return firstValue; + } + + /// + /// Returns an array with specified elements inserted at the beginning. + /// + /// + /// The unshift method inserts elements into the start of an array, so + /// they appear in the same order in which they appear in the argument + /// list. + /// + + public JArray Unshift(object value) + { + List.Insert(0, value); + return this; + } + + /// + /// Returns an array with specified elements inserted at the beginning. + /// + /// + /// The unshift method inserts elements into the start of an array, so + /// they appear in the same order in which they appear in the argument + /// list. + /// + + public JArray Unshift(params object[] values) + { + if (values != null) + { + foreach (object value in values) + Unshift(value); + } + + return this; + } + } +} diff --git a/dotnet/src/dotnetcore/GxClasses/Domain/JNull.cs b/dotnet/src/dotnetcore/GxClasses/Domain/JNull.cs new file mode 100644 index 000000000..434ad643d --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Domain/JNull.cs @@ -0,0 +1,87 @@ +#region License, Terms and Conditions +// +// Jayrock - A JSON-RPC implementation for the Microsoft .NET Framework +// Written by Atif Aziz (atif.aziz@skybow.com) +// Copyright (c) Atif Aziz. All rights reserved. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 2.1 of the License, or (at your option) +// any later version. +// +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +#endregion + +namespace GeneXus.Application +{ + #region Imports + + using System; + //using System.Data.SqlTypes; + #endregion + + /// + /// Represent the one and only representation of the "null" value in JSON. + /// + + internal interface IJayrockCompatible + { + + } + [ Serializable ] + internal sealed class JNull : IJayrockCompatible + { + public static readonly JNull Value = new JNull(); + + public override string ToString() + { + return "null"; + } + + public static bool LogicallyEquals(object o) + { + // + // Equals a null reference. + // + + if (o == null) + return true; + + // + // Equals self, of course. + // + + if (o.Equals(JNull.Value)) + return true; + + // + // Equals the logical null value used in database applications. + // + + //if (Convert.IsDBNull(o)) + // return true; + + // + // Equals any type that supports logical nullability and whose + // current state is logically null. + // + + //INullable nullable = o as INullable; + + // if (nullable == null) + return false; + + //return nullable.IsNull; + } + + private JNull() {} + } +} diff --git a/dotnet/src/dotnetcore/GxClasses/Domain/JObject.cs b/dotnet/src/dotnetcore/GxClasses/Domain/JObject.cs new file mode 100644 index 000000000..e1aadfc30 --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Domain/JObject.cs @@ -0,0 +1,123 @@ +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 2.1 of the License, or (at your option) +// any later version. +// +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +using System; +using System.Collections; +using System.Collections.Specialized; +using GeneXus.Utils; +namespace GeneXus.Application +{ + /// the implementation does internally try to remember the order in which + /// the keys were added in order facilitate human-readability as in when + /// an instance is rendered as text. + + [ Serializable ] + internal class JObject : OrderedDictionary, IJayrockCompatible + { + public JObject() {} + + /// + /// Construct a JObject from a IDictionary + /// + + public JObject(IDictionary members) + { + foreach (DictionaryEntry entry in members) + { + if (entry.Key == null) + throw new Exception("InvalidMemberException"); + + base.Add(entry.Key.ToString(), entry.Value); + } + + } + + public virtual bool HasMembers + { + get { return Count > 0; } + } + + + /// + /// Accumulate values under a key. It is similar to the Put method except + /// that if there is already an object stored under the key then a + /// JArray is stored under the key to hold all of the accumulated values. + /// If there is already a JArray, then the new value is appended to it. + /// In contrast, the Put method replaces the previous value. + /// + + public virtual JObject Accumulate(string name, object value) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + + object current = base[name]; + + if (current == null) + { + Put(name, value); + } + else + { + IList values = current as IList; + + if (values != null) + { + values.Add(value); + } + else + { + values = new JArray + { + current, + value + }; + Put(name, values); + } + } + + return this; + } + + /// + /// Put a key/value pair in the JObject. If the value is null, + /// then the key will be removed from the JObject if it is present. + /// + + public virtual JObject Put(string name, object value) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + + if (value != null) + this[name] = value; + else + Remove(name); + + return this; + } + + public virtual ICollection Names + { + get + { + return base.Keys; + } + } + + /// + /// Overridden to return a JSON formatted object as a string. + /// + + public override string ToString() + { + return TextJsonSerializer.SerializeToJayrockCompatibleJson(this); + } + + + } +} diff --git a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj index a153ec7c7..b2165e979 100644 --- a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj +++ b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj @@ -1,13 +1,14 @@ - net6.0 + net6.0;net8.0 Library NETCORE;NODATIME Data Access GeneXus.Classes.Core true 618;1607;1698;SYSLIB0021;SYSLIB0027;SYSLIB0028;SYSLIB0023 + $(TargetsForTfmSpecificContentInPackage);CustomContentTarget @@ -119,7 +120,6 @@ - @@ -151,25 +151,31 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + - - + - + - + @@ -182,11 +188,25 @@ - - - - False - ..\libs\Jayrock.dll - - + + + + False + ..\libs\net8.0\Jayrock.dll + + + False + ..\libs\Jayrock.dll + True + + + + + + lib/$(TargetFramework) + true + + + + \ No newline at end of file diff --git a/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs b/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs index f6d093183..d7cdc1ca2 100644 --- a/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs +++ b/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs @@ -1,11 +1,11 @@ -using System; -using System.Collections.Generic; +using System; using System.Text; namespace GxClasses.Helpers { public class BasicAuthenticationHeaderValue { + const char UserNamePasswordSeparator= ':'; public BasicAuthenticationHeaderValue(string authenticationHeaderValue) { if (!string.IsNullOrWhiteSpace(authenticationHeaderValue)) @@ -19,7 +19,7 @@ public BasicAuthenticationHeaderValue(string authenticationHeaderValue) } private readonly string _authenticationHeaderValue; - private string[] _splitDecodedCredentials; + private string _usernamePassword; public bool IsValidBasicAuthenticationHeaderValue { get; private set; } public string UserIdentifier { get; private set; } @@ -32,11 +32,11 @@ private bool TryDecodeHeaderValue() { return false; } - var encodedCredentials = _authenticationHeaderValue.Substring(headerSchemeLength); + string encodedCredentials = _authenticationHeaderValue.Substring(headerSchemeLength); try { - var decodedCredentials = Convert.FromBase64String(encodedCredentials); - _splitDecodedCredentials = System.Text.Encoding.ASCII.GetString(decodedCredentials).Split(':'); + byte[] decodedCredentials = Convert.FromBase64String(encodedCredentials); + _usernamePassword = Encoding.ASCII.GetString(decodedCredentials); return true; } catch (FormatException) @@ -47,13 +47,19 @@ private bool TryDecodeHeaderValue() private void ReadAuthenticationHeaderValue() { - IsValidBasicAuthenticationHeaderValue = _splitDecodedCredentials!= null && _splitDecodedCredentials.Length == 2 - && !string.IsNullOrWhiteSpace(_splitDecodedCredentials[0]) - && !string.IsNullOrWhiteSpace(_splitDecodedCredentials[1]); + IsValidBasicAuthenticationHeaderValue = !string.IsNullOrEmpty(_usernamePassword) && _usernamePassword.Contains(UserNamePasswordSeparator); if (IsValidBasicAuthenticationHeaderValue) { - UserIdentifier = _splitDecodedCredentials[0]; - UserPassword = _splitDecodedCredentials[1]; + int separatorIndex = _usernamePassword.IndexOf(UserNamePasswordSeparator); + UserIdentifier = _usernamePassword.Substring(0, separatorIndex); + if (separatorIndex + 1 < _usernamePassword.Length) + { + UserPassword = _usernamePassword.Substring(separatorIndex + 1); + } + else + { + UserPassword = string.Empty; + } } } } diff --git a/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs b/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs index f4b03d19a..af51f7be6 100644 --- a/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs +++ b/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs @@ -2,9 +2,13 @@ using System.Collections; using System.Globalization; using System.Runtime.Serialization; +using System.Text.Json.Serialization; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; +#endif using GeographicLib; -using log4net; using NetTopologySuite.Geometries; using NetTopologySuite.IO; @@ -202,9 +206,10 @@ internal static object STRingN(object instance, int i) [KnownType(typeof(System.Double[]))] [KnownType(typeof(System.Collections.ArrayList))] [DataContract] + [JsonConverter(typeof(CustomGeospatialConverter))] public class Geospatial : IGeographicNative { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.Geospatial)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); internal const string EMPTY_GEOMETRY = "GEOMETRYCOLLECTION EMPTY"; const string EMPTY_GEOGRAPHY = "GEOGRAPHY EMPTY"; @@ -406,7 +411,7 @@ public double Latitude } } - public String JSONPointToWKT(JArray coords) + internal String JSONPointToWKT(JArray coords) { String[] jbuffer = new String[] { "", "" }; jbuffer[0] = ""; diff --git a/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs b/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs index 2109ab09c..1c8dea312 100644 --- a/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs +++ b/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs @@ -7,8 +7,14 @@ [assembly: InternalsVisibleTo("AzureFunctionsTest")] [assembly: InternalsVisibleTo("GXQueue")] [assembly: InternalsVisibleTo("GXMessageBroker")] +[assembly: InternalsVisibleTo("GXEventRouter")] [assembly: InternalsVisibleTo("DotNetCoreUnitTest")] [assembly: InternalsVisibleTo("DotNetCoreWebUnitTest")] +[assembly: InternalsVisibleTo("DotNetCoreAttackMitigationTest")] [assembly: InternalsVisibleTo("GeneXus.Deploy.AzureFunctions.Handlers")] [assembly: InternalsVisibleTo("AzureFunctionsTest")] [assembly: InternalsVisibleTo("GXMessageBroker")] +[assembly: InternalsVisibleTo("GeneXus.SD.Store.StoreManager")] +[assembly: InternalsVisibleTo("DotNetCoreChunkedTest")] +[assembly: InternalsVisibleTo("DotNetCoreChunkedTest")] +[assembly: InternalsVisibleTo("GeneXus.OpenTelemetry.Diagnostics")] diff --git a/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs b/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs new file mode 100644 index 000000000..a015eca6d --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs @@ -0,0 +1,34 @@ +using System; +using GeneXus.Configuration; +using Microsoft.Extensions.Logging; + +namespace GeneXus.Services.Log +{ + public static class GXLogService + { + private static string AZURE_APPLICATION_INSIGHTS_LOG = "AZUREAPPLICATIONINSIGHTS"; + const string OTEL_AZUREMONITOR_EXPORTER = "OTEL_AZUREMONITOR_EXPORTER"; + const string OPENTELEMETRY = "OPENTELEMETRY"; + internal static string OTEL_LOGS_EXPORTER = "OTEL_LOGS_EXPORTER"; + const string LOG_OUTPUT = "LOG_OUTPUT"; + + public static ILoggerFactory GetLogFactory() + { + string otelLogEnvVar = Environment.GetEnvironmentVariable(OTEL_LOGS_EXPORTER); + if (string.IsNullOrEmpty(otelLogEnvVar) || !otelLogEnvVar.ToLower().Equals("none")) + { + if (Config.GetValueOf(LOG_OUTPUT, out string logProvider)) + { + if (logProvider == OTEL_AZUREMONITOR_EXPORTER) + return OpentelemetryLogProvider.GetAzureMonitorLoggerFactory(); + else if (logProvider == AZURE_APPLICATION_INSIGHTS_LOG) + return OpentelemetryLogProvider.GetAzureAppInsightsLoggerFactory(); + else + if (logProvider == OPENTELEMETRY) + return OpentelemetryLogProvider.GetOpentelemetryLoggerFactory(); + } + } + return null; + } + } +} diff --git a/dotnet/src/dotnetcore/GxClasses/Services/LogService/OpenTelemetry/OtelLogProvider.cs b/dotnet/src/dotnetcore/GxClasses/Services/LogService/OpenTelemetry/OtelLogProvider.cs new file mode 100644 index 000000000..20fef179b --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Services/LogService/OpenTelemetry/OtelLogProvider.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Azure.Monitor.OpenTelemetry.Exporter; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Logs; +using OpenTelemetry.Resources; +using OpenTelemetry.Exporter; + +namespace GeneXus.Services.Log +{ + public class OpentelemetryLogProvider : ILoggerFactory + { + private static string APPLICATIONINSIGHTS_CONNECTION_STRING = "APPLICATIONINSIGHTS_CONNECTION_STRING"; + private const string LOG_LEVEL_ENVVAR = "GX_LOG_LEVEL"; + public static ILoggerFactory loggerFactory; + + public static ILoggerFactory GetAzureMonitorLoggerFactory() + { + string appInsightsConnection = Environment.GetEnvironmentVariable(APPLICATIONINSIGHTS_CONNECTION_STRING); + try + { + + if (appInsightsConnection != null) + { + LogLevel loglevel = GetLogLevel(); + loggerFactory = LoggerFactory.Create(builder => + { + builder.AddOpenTelemetry(options => + { + options.AddAzureMonitorLogExporter(o => o.ConnectionString = appInsightsConnection); + if (GenerateOtelLogsToConsole()) + options.AddConsoleExporter(); + }).SetMinimumLevel(loglevel); + }); + } + else + { + throw new ArgumentNullException(APPLICATIONINSIGHTS_CONNECTION_STRING, "Opentelemetry Provider is Azure Monitor. Application Insight Log could not be initialized due to missing APPLICATIONINSIGHTS_CONNECTION_STRING environment variable."); + } + } + catch (Exception ex) + { + throw ex; + } + + return loggerFactory; + } + + public static ILoggerFactory GetAzureAppInsightsLoggerFactory() + { + string appInsightsConnection = Environment.GetEnvironmentVariable(APPLICATIONINSIGHTS_CONNECTION_STRING); + try + { + + if (appInsightsConnection != null) + { + LogLevel loglevel = GetLogLevel(); + loggerFactory = LoggerFactory.Create(builder => builder.AddApplicationInsights( + + configureTelemetryConfiguration: (config) => + config.ConnectionString = appInsightsConnection, + configureApplicationInsightsLoggerOptions: (options) => { } + ).SetMinimumLevel(loglevel) + ); + } + else + { + throw new ArgumentNullException(APPLICATIONINSIGHTS_CONNECTION_STRING, "LogOutput is Application Insights. Application Insight Log could not be initialized due to missing APPLICATIONINSIGHTS_CONNECTION_STRING environment variable."); + } + } + catch (Exception ex) + { + throw ex; + } + + return loggerFactory; + } + + public static ILoggerFactory GetOpentelemetryLoggerFactory() + { + LogLevel loglevel = GetLogLevel(); + loggerFactory = LoggerFactory.Create(builder => builder.AddOpenTelemetry(logging => + { + var resourceBuilder = ResourceBuilder.CreateDefault() + .AddTelemetrySdk(); + + logging.SetResourceBuilder(resourceBuilder) + .AddOtlpExporter(); + + if (GenerateOtelLogsToConsole()) + logging.AddConsoleExporter(); + + }) + .SetMinimumLevel(loglevel)); + return loggerFactory; + } + private static LogLevel GetLogLevel() + { + string loglevelvalue = Environment.GetEnvironmentVariable(LOG_LEVEL_ENVVAR); + LogLevel loglevel = LogLevel.Information; + if (!string.IsNullOrEmpty(loglevelvalue)) + { + if (!Enum.TryParse(loglevelvalue, out loglevel)) + { + CustomLogLevel customLogLevel = CustomLogLevel.info; + if (Enum.TryParse(loglevelvalue, out customLogLevel)) + { + loglevel = toLogLevel(customLogLevel); + } + else + loglevel = LogLevel.Information; + } + } + return loglevel; + } + + private static bool GenerateOtelLogsToConsole() + { + string otelLogsEnvVar = Environment.GetEnvironmentVariable(GXLogService.OTEL_LOGS_EXPORTER); + if (string.IsNullOrEmpty(otelLogsEnvVar)) { return false; } + return otelLogsEnvVar.ToLower().Contains("console") || otelLogsEnvVar.ToLower().Contains("logging"); + } + + public void AddProvider(ILoggerProvider provider) + { + loggerFactory.AddProvider(provider); + } + public void Dispose() + { + loggerFactory.Dispose(); + } + public ILogger CreateLogger(string name) + { + return loggerFactory.CreateLogger(name); + } + private enum CustomLogLevel + { + none, + all, + debug, + info, + warn, + error, + fatal + } + private static LogLevel toLogLevel(CustomLogLevel customLogLevel) + { + switch (customLogLevel) + { + case CustomLogLevel.none: return LogLevel.None; + case CustomLogLevel.all: return LogLevel.Trace; + case CustomLogLevel.debug: return LogLevel.Debug; + case CustomLogLevel.info: return LogLevel.Information; + case CustomLogLevel.warn: return LogLevel.Warning; + case CustomLogLevel.error: return LogLevel.Error; + case CustomLogLevel.fatal: return LogLevel.Critical; + default: return LogLevel.Information; + } + } + } +} \ No newline at end of file diff --git a/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs b/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs index 125f05ff5..3dfdf2b00 100644 --- a/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs +++ b/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs @@ -1,6 +1,6 @@ using System; +using System.Text.RegularExpressions; using GxClasses.Helpers; -using log4net; namespace GeneXus.Services.OpenTelemetry { @@ -11,10 +11,56 @@ public interface IOpenTelemetryProvider public static class OpenTelemetryService { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(OpenTelemetryService)); - private static string OPENTELEMETRY_SERVICE = "Observability"; - public static string GX_ACTIVITY_SOURCE_NAME = "GeneXus.Tracing"; - + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(OpenTelemetryService).FullName); + + const string OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES"; + const string OTEL_SERVICE_NAME = "OTEL_SERVICE_NAME"; + const string OTEL_SERVICE_VERSION = "OTEL_SERVICE_VERSION"; + const string OPENTELEMETRY_SERVICE = "Observability"; + + public static string GX_ACTIVITY_SOURCE_NAME= GetServiceNameValue(OTEL_RESOURCE_ATTRIBUTES); + public static string GX_ACTIVITY_SOURCE_VERSION= GetServiceVersionValue(OTEL_RESOURCE_ATTRIBUTES); + + private static string GetServiceNameValue(string input) + { + string otelServiceNameEnvVar = Environment.GetEnvironmentVariable(OTEL_SERVICE_NAME); + if (!string.IsNullOrEmpty(otelServiceNameEnvVar)) + return otelServiceNameEnvVar; + + string pattern = @"(?:\b\w+\b=\w+)(?:,(?:\b\w+\b=\w+))*"; + MatchCollection matches = Regex.Matches(input, pattern); + + foreach (Match match in matches) + { + string[] keyValue = match.Value.Split('='); + + if (keyValue[0] == "service.name") + { + return keyValue[1]; + } + } + return "GeneXus.Tracing"; + } + private static string GetServiceVersionValue(string input) + { + string otelServiceNameEnvVar = Environment.GetEnvironmentVariable(OTEL_SERVICE_VERSION); + if (!string.IsNullOrEmpty(otelServiceNameEnvVar)) + return otelServiceNameEnvVar; + + string pattern = @"(?:\b\w+\b=\w+)(?:,(?:\b\w+\b=\w+))*"; + MatchCollection matches = Regex.Matches(input, pattern); + + foreach (Match match in matches) + { + string[] keyValue = match.Value.Split('='); + + if (keyValue[0] == "service.version") + { + return keyValue[1]; + } + } + return string.Empty; + } private static IOpenTelemetryProvider GetOpenTelemetryProvider() { IOpenTelemetryProvider otelProvider = null; @@ -34,7 +80,7 @@ private static IOpenTelemetryProvider GetOpenTelemetryProvider() } catch (Exception e) { - GXLogging.Error(log, "Couldn´t create OpenTelemetry provider.", e.Message, e); + GXLogging.Error(log, "Couldn't create OpenTelemetry provider.", e.Message, e); throw e; } } @@ -49,7 +95,7 @@ internal static void Setup(Microsoft.Extensions.DependencyInjection.IServiceColl bool started = provider.InstrumentAspNetCoreApplication(services); if (started) { - log.Info("OpenTelemetry instrumentation started"); + GXLogging.Info(log, "OpenTelemetry instrumentation started"); } } } diff --git a/dotnet/src/dotnetcore/GxClasses/Services/ServiceSettings.cs b/dotnet/src/dotnetcore/GxClasses/Services/ServiceSettings.cs index 2249548f0..a9ab19878 100644 --- a/dotnet/src/dotnetcore/GxClasses/Services/ServiceSettings.cs +++ b/dotnet/src/dotnetcore/GxClasses/Services/ServiceSettings.cs @@ -1,12 +1,11 @@ using System; using GeneXus.Encryption; -using log4net; namespace GeneXus.Services.Common { public class ServiceSettingsReader { - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(ServiceSettingsReader)); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); internal GXService service; public string serviceNameResolver { get; } @@ -25,7 +24,7 @@ public string GetEncryptedPropertyValue(string propertyName) if (value == null) { String errorMessage = String.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); - logger.Fatal(errorMessage); + GXLogging.Critical(logger, errorMessage); throw new Exception(errorMessage); } return value; @@ -45,7 +44,7 @@ public string GetEncryptedPropertyValue(string propertyName, string defaultValue } catch (Exception) { - logger.Warn($"Could not decrypt property name: {ResolvePropertyName(propertyName)}"); + GXLogging.Warn(logger, $"Could not decrypt property name: {ResolvePropertyName(propertyName)}"); } } return value; @@ -57,7 +56,7 @@ public string GetPropertyValue(string propertyName) if (value == null) { String errorMessage = String.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); - logger.Fatal(errorMessage); + GXLogging.Critical(logger, errorMessage); throw new Exception(errorMessage); } return value; diff --git a/dotnet/src/dotnetcore/GxClasses/Services/Session/GXSessionFactory.cs b/dotnet/src/dotnetcore/GxClasses/Services/Session/GXSessionFactory.cs index 51e578ce0..79a8b5827 100644 --- a/dotnet/src/dotnetcore/GxClasses/Services/Session/GXSessionFactory.cs +++ b/dotnet/src/dotnetcore/GxClasses/Services/Session/GXSessionFactory.cs @@ -5,13 +5,13 @@ using GeneXus.Data.ADO; using GeneXus.Encryption; using GxClasses.Helpers; -using log4net; namespace GeneXus.Services { public class GXSessionServiceFactory { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXSessionServiceFactory)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + static string REDIS = "REDIS"; static string DATABASE = "DATABASE"; public static ISessionService GetProvider() @@ -58,7 +58,7 @@ public static ISessionService GetProvider() } public class GxRedisSession : ISessionService { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxRedisSession)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); internal static string SESSION_ADDRESS = "SESSION_PROVIDER_ADDRESS"; internal static string SESSION_INSTANCE = "SESSION_PROVIDER_INSTANCE_NAME"; internal static string SESSION_PASSWORD = "SESSION_PROVIDER_PASSWORD"; @@ -108,7 +108,7 @@ public GxRedisSession(string host, string password, string instanceName, int ses } public class GxDatabaseSession : ISessionService { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxDatabaseSession)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); internal static string SESSION_ADDRESS = "SESSION_PROVIDER_ADDRESS"; internal static string SESSION_PASSWORD = "SESSION_PROVIDER_PASSWORD"; internal static string SESSION_SCHEMA = "SESSION_PROVIDER_SCHEMA"; diff --git a/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj b/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj index 7c41acaec..31547472d 100644 --- a/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj +++ b/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 Exe DataInitialization GeneXus.DataInitialization.Core @@ -12,6 +12,10 @@ + + + + @@ -19,11 +23,11 @@ - contentFiles/any/any + contentFiles/any/$(TargetFramework) true - contentFiles/any/any + contentFiles/any/$(TargetFramework) true diff --git a/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj b/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj index 15df73743..916d3c26d 100644 --- a/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj +++ b/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 NETCORE GxExcelI EPPlus Lite @@ -16,9 +16,9 @@ + - - + diff --git a/dotnet/src/dotnetcore/GxMail/GxMail.csproj b/dotnet/src/dotnetcore/GxMail/GxMail.csproj index 0b7d837ce..f8899a0f6 100644 --- a/dotnet/src/dotnetcore/GxMail/GxMail.csproj +++ b/dotnet/src/dotnetcore/GxMail/GxMail.csproj @@ -1,7 +1,7 @@ - net6.0 - 1701;1702;NU1701 + net6.0;net8.0 + 1701;1702;NU1701;CS0618 NETCORE SMTP Exchange POP3 GeneXus.Mail.Core @@ -66,11 +66,11 @@ - + - + diff --git a/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj b/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj index 128f906e0..dc95fa2c3 100644 --- a/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj +++ b/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 1701;1702;NU1701 NETCORE Maps @@ -13,10 +13,9 @@ - - False - ..\libs\Jayrock.dll - + + + \ No newline at end of file diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs b/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs new file mode 100644 index 000000000..2020e7371 --- /dev/null +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs @@ -0,0 +1,69 @@ + + +using System; +using System.Threading.Tasks; +using GeneXus.Configuration; +using GeneXus.Http; +using Microsoft.AspNetCore.Antiforgery; +using Microsoft.AspNetCore.Http; +namespace GeneXus.Application +{ + public class ValidateAntiForgeryTokenMiddleware + { + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private readonly RequestDelegate _next; + private readonly IAntiforgery _antiforgery; + private string _restBasePath; + private string _basePath; + + public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery, string basePath) + { + _next = next; + _antiforgery = antiforgery; + _restBasePath = $"{basePath}{Startup.REST_BASE_URL}"; + _basePath = $"/{basePath}"; + } + + public async Task Invoke(HttpContext context) + { + if (context.Request.Path.HasValue && context.Request.Path.Value.StartsWith(_basePath)) + { + if (HttpMethods.IsPost(context.Request.Method) || + HttpMethods.IsDelete(context.Request.Method) || + HttpMethods.IsPut(context.Request.Method)) + { + string cookieToken = context.Request.Cookies[HttpHeader.X_CSRF_TOKEN_COOKIE]; + string headerToken = context.Request.Headers[HttpHeader.X_CSRF_TOKEN_HEADER]; + GXLogging.Debug(log, $"Antiforgery validation, cookieToken:{cookieToken}, headerToken:{headerToken}"); + + await _antiforgery.ValidateRequestAsync(context); + GXLogging.Debug(log, $"Antiforgery validation OK"); + } + else if (HttpMethods.IsGet(context.Request.Method)) + { + SetAntiForgeryTokens(_antiforgery, context); + } + } + if (!IsVerificationTokenServiceRequest(context)) + await _next(context); + } + private bool IsVerificationTokenServiceRequest(HttpContext context) + { + return context.Request.Path.Value.EndsWith(_restBasePath); + } + internal static void SetAntiForgeryTokens(IAntiforgery _antiforgery, HttpContext context) + { + AntiforgeryTokenSet tokenSet = _antiforgery.GetAndStoreTokens(context); + string sameSite; + CookieOptions cookieOptions = new CookieOptions { HttpOnly = false, Secure = GxContext.GetHttpSecure(context) == 1 }; + SameSiteMode sameSiteMode = SameSiteMode.Unspecified; + if (Config.GetValueOf("SAMESITE_COOKIE", out sameSite) && Enum.TryParse(sameSite, out sameSiteMode)) + { + cookieOptions.SameSite = sameSiteMode; + } + context.Response.Cookies.Append(HttpHeader.X_CSRF_TOKEN_COOKIE, tokenSet.RequestToken, cookieOptions); + GXLogging.Debug(log, $"Setting cookie ", HttpHeader.X_CSRF_TOKEN_COOKIE, "=", tokenSet.RequestToken, " samesite:" + sameSiteMode); + } + + } +} diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj b/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj index 2118c21f1..522adb9f8 100644 --- a/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 GxNetCoreStartup GeneXus.NetCoreStartup true @@ -10,33 +10,38 @@ - - - + + - + + - + + + + + + + + + + + + - - - False - ..\libs\Jayrock.dll - - - contentFiles/any/any + contentFiles/any/$(TargetFramework) true - contentFiles/any/any + contentFiles/any/$(TargetFramework) true diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/Properties/AssemblyInfo.cs b/dotnet/src/dotnetcore/GxNetCoreStartup/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..e3c540157 --- /dev/null +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; + + +[assembly: InternalsVisibleTo("DotNetCoreOpenTelemetryTest")] +[assembly: InternalsVisibleTo("DotNetCoreWebUnitTest")] + + diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs b/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs index a5d8ceed3..b90fd4050 100644 --- a/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Net; +using System.Reflection; using System.Threading.Tasks; using GeneXus.Configuration; using GeneXus.Http; @@ -10,14 +11,15 @@ using GeneXus.Services.OpenTelemetry; using GeneXus.Utils; using GxClasses.Web.Middleware; -using log4net; using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Antiforgery; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Rewrite; using Microsoft.AspNetCore.Routing; @@ -29,13 +31,13 @@ using Microsoft.Extensions.Logging; using StackExchange.Redis; - namespace GeneXus.Application { public class Program { const string DEFAULT_PORT = "80"; static string DEFAULT_SCHEMA = Uri.UriSchemeHttp; + public static void Main(string[] args) { try @@ -74,10 +76,9 @@ public static void Main(string[] args) public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) - .ConfigureLogging(logging => logging.AddConsole()) - .UseStartup() - .UseContentRoot(Startup.LocalPath) - .Build(); + .UseStartup() + .UseContentRoot(Startup.LocalPath) + .Build(); public static IWebHost BuildWebHostPort(string[] args, string port) { @@ -86,12 +87,13 @@ public static IWebHost BuildWebHostPort(string[] args, string port) static IWebHost BuildWebHostPort(string[] args, string port, string schema) { return WebHost.CreateDefaultBuilder(args) - .ConfigureLogging(logging => logging.AddConsole()) .UseUrls($"{schema}://*:{port}") .UseStartup() + .UseWebRoot(Startup.LocalPath) .UseContentRoot(Startup.LocalPath) .Build(); } + private static void LocatePhysicalLocalPath() { string startup = FileUtil.GetStartupDirectory(); @@ -99,11 +101,14 @@ private static void LocatePhysicalLocalPath() if (startup == Startup.LocalPath && !File.Exists(Path.Combine(startup, Startup.APP_SETTINGS)) && File.Exists(Path.Combine(startupParent, Startup.APP_SETTINGS))) Startup.LocalPath = startupParent; } - } public static class GXHandlerExtensions { + public static IApplicationBuilder UseAntiforgeryTokens(this IApplicationBuilder app, string basePath) + { + return app.UseMiddleware(basePath); + } public static IApplicationBuilder UseGXHandlerFactory(this IApplicationBuilder builder, string basePath) { return builder.UseMiddleware(basePath); @@ -117,9 +122,10 @@ public static IApplicationBuilder MapWebSocketManager(this IApplicationBuilder a } public class Startup - { + { + static IGXLogger log; + internal static string APPLICATIONINSIGHTS_CONNECTION_STRING = "APPLICATIONINSIGHTS_CONNECTION_STRING"; - static readonly ILog log = log4net.LogManager.GetLogger(typeof(Startup)); const long DEFAULT_MAX_FILE_UPLOAD_SIZE_BYTES = 528000000; public static string VirtualPath = string.Empty; public static string LocalPath = Directory.GetCurrentDirectory(); @@ -129,7 +135,7 @@ public class Startup const string RESOURCES_FOLDER = "Resources"; const string TRACE_FOLDER = "logs"; const string TRACE_PATTERN = "trace.axd"; - const string REST_BASE_URL = "rest/"; + internal const string REST_BASE_URL = "rest/"; const string DATA_PROTECTION_KEYS = "DataProtection-Keys"; const string REWRITE_FILE = "rewrite.config"; const string SWAGGER_DEFAULT_YAML = "default.yaml"; @@ -138,35 +144,59 @@ public class Startup const string CORS_POLICY_NAME = "AllowSpecificOriginsPolicy"; const string CORS_ANY_ORIGIN = "*"; const double CORS_MAX_AGE_SECONDS = 86400; + internal const string GX_CONTROLLERS = "gxcontrollers"; public List servicesBase = new List(); private GXRouting gxRouting; - public Startup(IConfiguration configuration, IHostingEnvironment env) { Config.ConfigRoot = configuration; + GxContext.IsHttpContext = true; + Config.LoadConfiguration(); GXRouting.ContentRootPath = env.ContentRootPath; GXRouting.UrlTemplateControllerWithParms = "controllerWithParms"; - GxContext.IsHttpContext = true; gxRouting = new GXRouting(REST_BASE_URL); + log = GXLoggerFactory.GetLogger(); } public void ConfigureServices(IServiceCollection services) { OpenTelemetryService.Setup(services); - services.AddMvc(option => option.EnableEndpointRouting = false); + services.AddControllers(); + string controllers = Path.Combine(Startup.LocalPath, "bin", GX_CONTROLLERS); + IMvcBuilder mvcBuilder = services.AddMvc(option => option.EnableEndpointRouting = false); + try + { + if (Directory.Exists(controllers)) + { + foreach (string controller in Directory.GetFiles(controllers)) + { + Console.WriteLine($"Loading controller {controller}"); + mvcBuilder.AddApplicationPart(Assembly.LoadFrom(controller)).AddControllersAsServices(); + } + } + } + catch (Exception ex) + { + Console.Error.WriteLine("Error loading gxcontrollers " + ex.Message); + } services.Configure(options => { options.AllowSynchronousIO = true; options.Limits.MaxRequestBodySize = null; + if (Config.GetValueOrEnvironmentVarOf("MinRequestBodyDataRate", out string MinRequestBodyDataRateStr) && double.TryParse(MinRequestBodyDataRateStr, out double MinRequestBodyDataRate)) + { + GXLogging.Info(log, $"MinRequestBodyDataRate:{MinRequestBodyDataRate}"); + options.Limits.MinRequestBodyDataRate = new MinDataRate(bytesPerSecond: MinRequestBodyDataRate, gracePeriod: TimeSpan.FromSeconds(10)); + } }); services.Configure(options => { options.AllowSynchronousIO = true; }); services.AddDistributedMemoryCache(); - + services.AddLogging(builder => builder.AddConsole()); services.Configure(options => { if (Config.GetValueOf("MaxFileUploadSize", out string MaxFileUploadSizeStr) && long.TryParse(MaxFileUploadSizeStr, out long MaxFileUploadSize)) @@ -191,6 +221,12 @@ public void ConfigureServices(IServiceCollection services) options.Cookie.HttpOnly = true; options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; options.Cookie.IsEssential = true; + string sessionCookieName = GxWebSession.GetSessionCookieName(VirtualPath); + if (!string.IsNullOrEmpty(sessionCookieName)) + { + options.Cookie.Name=sessionCookieName; + GxWebSession.SessionCookieName = sessionCookieName; + } string sameSite; SameSiteMode sameSiteMode = SameSiteMode.Unspecified; if (Config.GetValueOf("SAMESITE_COOKIE", out sameSite) && Enum.TryParse(sameSite, out sameSiteMode)) @@ -199,7 +235,14 @@ public void ConfigureServices(IServiceCollection services) } }); - + if (RestAPIHelpers.ValidateCsrfToken()) + { + services.AddAntiforgery(options => + { + options.HeaderName = HttpHeader.X_CSRF_TOKEN_HEADER; + options.SuppressXFrameOptionsHeader = true; + }); + } services.AddDirectoryBrowser(); if (GXUtil.CompressResponse()) { @@ -224,7 +267,6 @@ public void ConfigureServices(IServiceCollection services) }); } DefineCorsPolicy(services); - services.AddMvc(); } private void DefineCorsPolicy(IServiceCollection services) @@ -285,7 +327,8 @@ private void ConfigureSessionService(IServiceCollection services, ISessionServic public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env, ILoggerFactory loggerFactory) { string baseVirtualPath = string.IsNullOrEmpty(VirtualPath) ? VirtualPath : $"/{VirtualPath}"; - LogConfiguration.SetupLog4Net(); + LogConfiguration.SetupLog4Net(); + var provider = new FileExtensionContentTypeProvider(); //mappings provider.Mappings[".json"] = "application/json"; @@ -304,10 +347,12 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos provider.Mappings[".usdz"] = "model/vnd.pixar.usd"; provider.Mappings[".sfb"] = "model/sfb"; provider.Mappings[".gltf"] = "model/gltf+json"; + provider.Mappings[".ini"] = "text/plain"; if (GXUtil.CompressResponse()) { app.UseResponseCompression(); } + app.UseRouting(); app.UseCookiePolicy(); app.UseSession(); app.UseStaticFiles(); @@ -330,6 +375,10 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos app.UseHttpsRedirection(); app.UseHsts(); } + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); if (log.IsDebugEnabled) { try @@ -357,14 +406,22 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos if (File.Exists(rewriteFile)) AddRewrite(app, rewriteFile, baseVirtualPath); + string tempMediaDir = string.Empty; + if (Config.GetValueOf("TMPMEDIA_DIR", out string mediaPath) && !PathUtil.IsAbsoluteUrlOrAnyScheme(mediaPath)) + { + tempMediaDir = mediaPath; + } app.UseStaticFiles(new StaticFileOptions() { FileProvider = new PhysicalFileProvider(LocalPath), RequestPath = new PathString($"{baseVirtualPath}"), OnPrepareResponse = s => { - var path = s.Context.Request.Path; - if (path.HasValue && path.Value.IndexOf($"/{APP_SETTINGS}", StringComparison.OrdinalIgnoreCase)>=0) + PathString path = s.Context.Request.Path; + bool appSettingsPath = path.HasValue && path.Value.IndexOf($"/{APP_SETTINGS}", StringComparison.OrdinalIgnoreCase) >= 0; + bool tempMediaPath = path.StartsWithSegments($"{baseVirtualPath}/{tempMediaDir}", StringComparison.OrdinalIgnoreCase); + bool privatePath = path.StartsWithSegments($"{baseVirtualPath}/{GXRouting.PRIVATE_DIR}", StringComparison.OrdinalIgnoreCase); + if (appSettingsPath || tempMediaPath || privatePath) { s.Context.Response.StatusCode = 401; s.Context.Response.Body = Stream.Null; @@ -386,6 +443,12 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos string restBasePath = string.IsNullOrEmpty(VirtualPath) ? REST_BASE_URL : $"{VirtualPath}/{REST_BASE_URL}"; string apiBasePath = string.IsNullOrEmpty(VirtualPath) ? string.Empty : $"{VirtualPath}/"; + IAntiforgery antiforgery = null; + if (RestAPIHelpers.ValidateCsrfToken()) + { + antiforgery = app.ApplicationServices.GetRequiredService(); + app.UseAntiforgeryTokens(apiBasePath); + } app.UseMvc(routes => { foreach (string serviceBasePath in servicesBase) @@ -400,10 +463,10 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos routes.MapRoute($"{restBasePath}{{*{UrlTemplateControllerWithParms}}}", new RequestDelegate(gxRouting.ProcessRestRequest)); routes.MapRoute("Default", VirtualPath, new { controller = "Home", action = "Index" }); }); - + app.UseWebSockets(); string basePath = string.IsNullOrEmpty(VirtualPath) ? string.Empty : $"/{VirtualPath}"; - Config.ScriptPath = basePath; + Config.ScriptPath = string.IsNullOrEmpty(basePath) ? "/" : basePath; app.MapWebSocketManager(basePath); app.MapWhen( @@ -474,9 +537,10 @@ bool IsAspx(HttpContext context, string basePath) } public class CustomExceptionHandlerMiddleware { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(CustomExceptionHandlerMiddleware)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public async Task Invoke(HttpContext httpContext) { + string httpReasonPhrase=string.Empty; Exception ex = httpContext.Features.Get()?.Error; HttpStatusCode httpStatusCode = (HttpStatusCode)httpContext.Response.StatusCode; if (ex!=null) @@ -485,6 +549,12 @@ public async Task Invoke(HttpContext httpContext) { httpStatusCode = HttpStatusCode.NotFound; } + else if (ex is AntiforgeryValidationException) + { + httpStatusCode = HttpStatusCode.BadRequest; + httpReasonPhrase = HttpHelper.InvalidCSRFToken; + GXLogging.Error(log, $"Validation of antiforgery failed", ex); + } else { httpStatusCode = HttpStatusCode.InternalServerError; @@ -495,13 +565,19 @@ public async Task Invoke(HttpContext httpContext) { string redirectPage = Config.MapCustomError(httpStatusCode.ToString(HttpHelper.INT_FORMAT)); if (!string.IsNullOrEmpty(redirectPage)) -{ + { httpContext.Response.Redirect($"{httpContext.Request.GetApplicationPath()}/{redirectPage}"); } else { httpContext.Response.StatusCode = (int)httpStatusCode; } + if (!string.IsNullOrEmpty(httpReasonPhrase)) + { + IHttpResponseFeature responseReason = httpContext.Response.HttpContext.Features.Get(); + if (responseReason!=null) + responseReason.ReasonPhrase = httpReasonPhrase; + } } await Task.CompletedTask; } diff --git a/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj b/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj index 4a31a7b68..094c0b13e 100644 --- a/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj +++ b/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 NETCORE Genexus.Office GxOffice @@ -35,8 +35,8 @@ - + diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs index 37a5076e3..74e1befce 100644 --- a/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs @@ -16,3 +16,5 @@ [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.NativeSharpFunctionsMS.getRegistrySubValues(System.String,System.String)~System.Collections.ArrayList")] [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.NativeSharpFunctionsMS.ReadRegKey(System.String)~System.String")] [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.MSPDFFontDescriptor.getTrueTypeFontLocation(System.String)~System.String")] +[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportBase.getAcrobatLocation~System.String")] +[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportBase.loadSubstituteTable")] diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj b/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj index 6f996b82c..95fa355b4 100644 --- a/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 1701;1702;NU1701 NETCORE Itext PDF Report @@ -9,12 +9,23 @@ - + + + + + + + + - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs new file mode 100644 index 000000000..c927b8281 --- /dev/null +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs @@ -0,0 +1,1330 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using GeneXus; +using iText.Barcodes; +using iText.Html2pdf; +using iText.Html2pdf.Resolver.Font; +using iText.IO.Font; +using iText.IO.Font.Otf; +using iText.IO.Image; +using iText.Kernel.Colors; +using iText.Kernel.Exceptions; +using iText.Kernel.Font; +using iText.Kernel.Geom; +using iText.Kernel.Pdf; +using iText.Kernel.Pdf.Action; +using iText.Kernel.Pdf.Canvas; +using iText.Layout; +using iText.Layout.Borders; +using iText.Layout.Element; +using iText.Layout.Font; +using iText.Layout.Layout; +using iText.Layout.Properties; +using iText.Layout.Splitting; +using Path = System.IO.Path; + +namespace GeneXus.Printer +{ + public class GxReportBuilderPdf8 : GxReportBuilderPdf + { + static IGXLogger log = GXLoggerFactory.GetLogger(); + public GxReportBuilderPdf8() { } + public GxReportBuilderPdf8(string appPath, Stream outputStream) + { + _appPath = appPath; + _pdfReport = new com.genexus.reports.PDFReportItext8(appPath); + if (outputStream != null) + { + _pdfReport.setOutputStream(outputStream); + GXLogging.Debug(log, "GxReportBuilderPdf outputStream: binaryWriter"); + } + } + } + +} +namespace com.genexus.reports +{ + + public class PDFReportItext8 : PDFReportBase + { + static IGXLogger log = GXLoggerFactory.GetLogger(); + + //const int ASCENT_NORMALIZED_UNITS = 1000; + + private PageSize pageSize; + private PdfFont baseFont; + private Style fontStyle; + //Color for, BaseColor for => Itext5 + private Color backColor, foreColor, templateColorFill; + private Document document; + PdfDocument pdfDocument; + private PdfPage pdfPage; + private PdfWriter writer; + private Rectangle templateRectangle; + private float templatex, templatey; + private PdfFont templateFont; + private PdfFont defaultFont; + internal Dictionary documentImages; + Barcode128 barcode; + private Boolean fontBold; + private Boolean fontItalic; + + public PDFReportItext8(String appPath) : base(appPath) + { + documentImages = new Dictionary(); + } + + protected override void init(ref int gxYPage, ref int gxXPage, int pageWidth, int pageLength) + { + this.pageSize = ComputePageSize(leftMargin, topMargin, pageWidth, pageLength, props.getBooleanGeneralProperty(Const.MARGINS_INSIDE_BORDER, Const.DEFAULT_MARGINS_INSIDE_BORDER)); + gxXPage = (int)this.pageSize.GetRight(); + if (props.getBooleanGeneralProperty(Const.FIX_SAC24437, true)) + gxYPage = (int)(pageLength / GX_PAGE_SCALE_Y); + else + gxYPage = (int)(pageLength / GX_PAGE_SCALE_Y_OLD); + + writer = new PdfWriter(outputStream); + writer.SetCompressionLevel(CompressionConstants.BEST_COMPRESSION); + try + { + string level = props.getGeneralProperty(Const.COMPLIANCE_LEVEL); + if (Enum.TryParse(level, true, out complianceLevel)) + { + //if (SetComplainceLevel(complianceLevel)) + //writer.SetTagged(); + } + pdfDocument = new PdfDocument(writer); + pdfDocument.SetDefaultPageSize(this.pageSize); + document = new Document(pdfDocument); + document.SetFontProvider(new DefaultFontProvider(false, false, false)); + } + catch (PdfException de) + { + GXLogging.Debug(log, "GxDrawRect error", de); + } + } + + internal override bool SetComplainceLevel(PdfConformanceLevel level) + { + /*switch (level) + { + case PdfConformanceLevel.Pdf_A1A: + writer.PDFXConformance = PdfWriter.PDFA1A; + return true; + case PdfConformanceLevel.Pdf_A1B: + writer.PDFXConformance = PdfWriter.PDFA1B; + return true; + default: + return false; + }*/ + return false; + } + + /** + * @param hideCorners indicates whether corner triangles should be hidden when the side that joins them is hidden. + */ + + private void drawRectangle(PdfCanvas cb, float x, float y, float w, float h, int styleTop, int styleBottom, int styleRight, int styleLeft, + float radioTL, float radioTR, float radioBL, float radioBR, float penAux, bool hideCorners) + { + + float[] dashPatternTop = getDashedPattern(styleTop); + float[] dashPatternBottom = getDashedPattern(styleBottom); + float[] dashPatternLeft = getDashedPattern(styleLeft); + float[] dashPatternRight = getDashedPattern(styleRight); + + //-------------------bottom line--------------------- + if (styleBottom != STYLE_NONE_CONST) + { + cb.SetLineDash(dashPatternBottom, 0); + } + + float b = 0.4477f; + if (radioBL > 0) + { + cb.MoveTo(x + radioBL, y); + } + else + { + if (hideCorners && styleLeft == STYLE_NONE_CONST && radioBL == 0) + { + cb.MoveTo(x + penAux, y); + } + else + { + cb.MoveTo(x, y); + } + } + + //-------------------bottom right corner--------------------- + + if (styleBottom != STYLE_NONE_CONST) + { + if (hideCorners && styleRight == STYLE_NONE_CONST && radioBR == 0) + { + cb.LineTo(x + w - penAux, y); + } + else + { + cb.LineTo(x + w - radioBR, y); + } + if (radioBR > 0 && styleRight != STYLE_NONE_CONST) + { + cb.CurveTo(x + w - radioBR * b, y, x + w, y + radioBR * b, x + w, y + radioBR); + } + } + + //-------------------right line--------------------- + + if (styleRight != STYLE_NONE_CONST && dashPatternRight != dashPatternBottom) + { + cb.Stroke(); + cb.SetLineDash(dashPatternRight, 0); + if (hideCorners && styleBottom == STYLE_NONE_CONST && radioBR == 0) + { + cb.MoveTo(x + w, y + penAux); + } + else + { + cb.MoveTo(x + w, y + radioBR); + } + } + + //-------------------top right corner--------------------- + if (styleRight != STYLE_NONE_CONST) + { + if (hideCorners && styleTop == STYLE_NONE_CONST && radioTR == 0) + { + cb.LineTo(x + w, y + h - penAux); + } + else + { + cb.LineTo(x + w, y + h - radioTR); + } + if (radioTR > 0 && styleTop != STYLE_NONE_CONST) + { + cb.CurveTo(x + w, y + h - radioTR * b, x + w - radioTR * b, y + h, x + w - radioTR, y + h); + } + } + + //-------------------top line--------------------- + + if (styleTop != STYLE_NONE_CONST && dashPatternTop != dashPatternRight) + { + cb.Stroke(); + cb.SetLineDash(dashPatternTop, 0); + if (hideCorners && styleRight == STYLE_NONE_CONST && radioTR == 0) + { + cb.MoveTo(x + w - penAux, y + h); + } + else + { + cb.MoveTo(x + w - radioTR, y + h); + } + } + + //-------------------top left corner--------------------- + if (styleTop != STYLE_NONE_CONST) + { + if (hideCorners && styleLeft == STYLE_NONE_CONST && radioTL == 0) + { + cb.LineTo(x + penAux, y + h); + } + else + { + cb.LineTo(x + radioTL, y + h); + } + if (radioTL > 0 && styleLeft != STYLE_NONE_CONST) + { + cb.CurveTo(x + radioTL * b, y + h, x, y + h - radioTL * b, x, y + h - radioTL); + } + } + + //-------------------left line--------------------- + + if (styleLeft != STYLE_NONE_CONST && dashPatternLeft != dashPatternTop) + { + cb.Stroke(); + cb.SetLineDash(dashPatternLeft, 0); + if (hideCorners && styleTop == STYLE_NONE_CONST && radioTL == 0) + { + cb.MoveTo(x, y + h - penAux); + } + else + { + cb.MoveTo(x, y + h - radioTL); + } + } + + //-------------------bottom left corner--------------------- + if (styleLeft != STYLE_NONE_CONST) + { + if (hideCorners && styleBottom == STYLE_NONE_CONST && radioBL == 0) + { + cb.LineTo(x, y + penAux); + } + else + { + cb.LineTo(x, y + radioBL); + } + if (radioBL > 0 && styleBottom != STYLE_NONE_CONST) + { + cb.CurveTo(x, y + radioBL * b, x + radioBL * b, y, x + radioBL, y); + } + } + cb.Stroke(); + + } + + private void roundRectangle(PdfCanvas cb, float x, float y, float w, float h, float radioTL, float radioTR, float radioBL, float radioBR) + { + //-------------------bottom line--------------------- + + + float b = 0.4477f; + if (radioBL > 0) + { + cb.MoveTo(x + radioBL, y); + } + else + { + cb.MoveTo(x, y); + } + + //-------------------bottom right corner--------------------- + + cb.LineTo(x + w - radioBR, y); + if (radioBR > 0) + { + cb.CurveTo(x + w - radioBR * b, y, x + w, y + radioBR * b, x + w, y + radioBR); + } + + + cb.LineTo(x + w, y + h - radioTR); + if (radioTR > 0) + { + cb.CurveTo(x + w, y + h - radioTR * b, x + w - radioTR * b, y + h, x + w - radioTR, y + h); + } + + cb.LineTo(x + radioTL, y + h); + if (radioTL > 0) + { + cb.CurveTo(x + radioTL * b, y + h, x, y + h - radioTL * b, x, y + h - radioTL); + } + cb.LineTo(x, y + radioBL); + if (radioBL > 0) + { + cb.CurveTo(x, y + radioBL * b, x + radioBL * b, y, x + radioBL, y); + } + } + + public override void GxDrawRect(int left, int top, int right, int bottom, int pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue, + int styleTop, int styleBottom, int styleRight, int styleLeft, int cornerRadioTL, int cornerRadioTR, int cornerRadioBL, int cornerRadioBR) + { + Color rectBackColor = new DeviceRgb(backRed, backGreen, backBlue); + Color rectForeColor = new DeviceRgb(foreRed, foreGreen, foreBlue); + GxDrawRect(left, top, right, bottom, pen, rectForeColor, backMode, rectBackColor, styleTop, styleBottom, styleRight, styleLeft, cornerRadioTL, cornerRadioTR, cornerRadioBL, cornerRadioBR); + } + + void GxDrawRect(int left, int top, int right, int bottom, int pen, Color foreColor, int backMode, Color backColor, + int styleTop, int styleBottom, int styleRight, int styleLeft, int cornerRadioTL, int cornerRadioTR, int cornerRadioBL, int cornerRadioBR) + { + PdfCanvas cb = new PdfCanvas(pdfPage); + + float penAux = (float)convertScale(pen); + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + + GXLogging.Debug(log, "GxDrawRect -> (" + left + "," + top + ") - (" + right + "," + bottom + ") BackMode: " + backMode + " Pen:" + pen + ",leftMargin:" + leftMargin); + cb.SaveState(); + + float x1, y1, x2, y2; + x1 = leftAux + leftMargin; + y1 = pageSize.GetTop() - bottomAux - topMargin - bottomMargin; + x2 = rightAux + leftMargin; + y2 = pageSize.GetTop() - topAux - topMargin - bottomMargin; + + cb.SetLineWidth(penAux); + cb.SetLineCapStyle(PdfCanvasConstants.LineCapStyle.PROJECTING_SQUARE); + + if (cornerRadioBL == 0 && cornerRadioBR == 0 && cornerRadioTL == 0 && cornerRadioTR == 0 && styleBottom == 0 && styleLeft == 0 && styleRight == 0 && styleTop == 0) + { + //border color must be the same as the fill if border=0 since setLineWidth does not work. + if (pen > 0) + cb.SetStrokeColor(foreColor); + else + cb.SetStrokeColor(backColor); + cb.Rectangle(x1, y1, x2 - x1, y2 - y1); + + if (backMode != 0) + { + cb.SetFillColor(backColor); + cb.FillStroke(); + } + + cb.ClosePathStroke(); + } + else + { + float w = x2 - x1; + float h = y2 - y1; + if (w < 0) + { + x1 += w; + w = -w; + } + if (h < 0) + { + y1 += h; + h = -h; + } + + float cRadioTL = (float)convertScale(cornerRadioTL); + float cRadioTR = (float)convertScale(cornerRadioTR); + float cRadioBL = (float)convertScale(cornerRadioBL); + float cRadioBR = (float)convertScale(cornerRadioBR); + + // Scale the radius if it's too large or to small to fit. + int max = (int)Math.Min(w, h); + cRadioTL = Math.Max(0, Math.Min(cRadioTL, max / 2)); + cRadioTR = Math.Max(0, Math.Min(cRadioTR, max / 2)); + cRadioBL = Math.Max(0, Math.Min(cRadioBL, max / 2)); + cRadioBR = Math.Max(0, Math.Min(cRadioBR, max / 2)); + + if (backMode != 0) + { + cb.SetStrokeColor(backColor); + cb.SetLineWidth(0); + roundRectangle(cb, x1, y1, w, h, cRadioTL, cRadioTR, cRadioBL, cRadioBR); + cb.SetFillColor(backColor); + cb.FillStroke(); + cb.SetLineWidth(penAux); + } + if (pen > 0) + { + //Rectangle edge + cb.SetFillColor(backColor); + cb.SetStrokeColor(foreColor); + drawRectangle(cb, x1, y1, w, h, + styleTop, styleBottom, styleRight, styleLeft, + cRadioTL, cRadioTR, + cRadioBL, cRadioBR, penAux, false); + } + } + cb.RestoreState(); + } + public override void GxDrawLine(int left, int top, int right, int bottom, int width, int foreRed, int foreGreen, int foreBlue, int style) + { + PdfCanvas cb = new PdfCanvas(pdfPage); + + Color foreColor = new DeviceRgb(foreRed, foreGreen, foreBlue); + + float widthAux = (float)convertScale(width); + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + + GXLogging.Debug(log, "GxDrawLine leftAux: " + leftAux + ",leftMargin:" + leftMargin + ",pageSize.Top:" + pageSize.GetTop() + ",bottomAux:" + bottomAux + ",topMargin:" + topMargin + ",bottomMargin:" + bottomMargin); + + GXLogging.Debug(log, "GxDrawLine -> (" + left + "," + top + ") - (" + right + "," + bottom + ") Width: " + width); + float x1, y1, x2, y2; + x1 = leftAux + leftMargin; + y1 = pageSize.GetTop() - bottomAux - topMargin - bottomMargin; + x2 = rightAux + leftMargin; + y2 = pageSize.GetTop() - topAux - topMargin - bottomMargin; + + GXLogging.Debug(log, "Line-> (" + (x1) + "," + y1 + ") - (" + x2 + "," + y2 + ") "); + cb.SaveState(); + cb.SetStrokeColor(foreColor); + cb.SetLineWidth(widthAux); + + if (lineCapProjectingSquare) + { + cb.SetLineCapStyle(PdfCanvasConstants.LineCapStyle.PROJECTING_SQUARE); + } + if (style != 0) + { + float[] dashPattern = getDashedPattern(style); + cb.SetLineDash(dashPattern, 0); + } + + cb.MoveTo(x1, y1); + cb.LineTo(x2, y2); + cb.Stroke(); + + cb.RestoreState(); + } + + public override void GxDrawBitMap(String bitmap, int left, int top, int right, int bottom, int aspectRatio) + { + try + { + Image image; + Image imageRef; + if (documentImages != null && documentImages.TryGetValue(bitmap, out imageRef)) + { + image = imageRef; + } + else + { + try + { + if (!Path.IsPathRooted(bitmap)) + { + + image = new Image(ImageDataFactory.Create(defaultRelativePrepend + bitmap)); + if (image == null) + { + bitmap = webAppDir + bitmap; + image = new Image(ImageDataFactory.Create(bitmap)); + } + else + { + bitmap = defaultRelativePrepend + bitmap; + } + } + else + { + image = new Image(ImageDataFactory.Create(bitmap)); + } + } + catch (Exception)//absolute url + { + Uri uri = new Uri(bitmap); + image = new Image(ImageDataFactory.Create(uri)); + } + if (documentImages == null) + { + documentImages = new Dictionary(); + } + documentImages[bitmap] = image; + } + GXLogging.Debug(log, "GxDrawBitMap -> '" + bitmap + "' [" + left + "," + top + "] - Size: (" + (right - left) + "," + (bottom - top) + ")"); + + if (image != null) + { + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + image.SetFixedPosition(this.getPage(),leftAux + leftMargin, this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin); + if (aspectRatio == 0) + image.ScaleAbsolute(rightAux - leftAux, bottomAux - topAux); + else + image.ScaleToFit(rightAux - leftAux, bottomAux - topAux); + image.GetAccessibilityProperties().SetAlternateDescription(Path.GetFileName(bitmap)); + document.Add(image); + } + } + catch (PdfException de) + { + GXLogging.Error(log, "GxDrawBitMap document error", de); + } + catch (IOException ioe) + { + GXLogging.Error(log, "GxDrawBitMap io error", ioe); + } + catch (Exception e) + { + GXLogging.Error(log, "GxDrawBitMap error", e); + } + } + + public override void GxAttris(String fontName, int fontSize, bool fontBold, bool fontItalic, bool fontUnderline, bool fontStrikethru, int Pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue) + { + fontStyle = null; + bool _isCJK = false; + bool embeedFont = IsEmbeddedFont(fontName); + if (!embeedFont) + { + fontName = getSubstitute(fontName); + } + + GXLogging.Debug(log, "GxAttris: "); + GXLogging.Debug(log, "\\-> Font: " + fontName + " (" + fontSize + ")" + (fontBold ? " BOLD" : "") + (fontItalic ? " ITALIC" : "") + (fontStrikethru ? " Strike" : "")); + GXLogging.Debug(log, "\\-> Fore (" + foreRed + ", " + foreGreen + ", " + foreBlue + ")"); + GXLogging.Debug(log, "\\-> Back (" + backRed + ", " + backGreen + ", " + backBlue + ")"); + + if (barcode128AsImage && fontName.ToLower().IndexOf("barcode 128") >= 0 || fontName.ToLower().IndexOf("barcode128") >= 0) + { + barcode = new Barcode128(pdfDocument); + barcode.SetCodeType(Barcode128.CODE128); + } + else + { + barcode = null; + } + this.fontUnderline = fontUnderline; + this.fontStrikethru = fontStrikethru; + this.fontSize = fontSize; + this.fontBold = fontBold; + this.fontItalic = fontItalic; + foreColor = new DeviceRgb(foreRed, foreGreen, foreBlue); + backColor = new DeviceRgb(backRed, backGreen, backBlue); + + backFill = (backMode != 0); + try + { + //LoadAsianFontsDll(); + string f = fontName.ToLower(); + if (PDFFont.isType1(fontName)) + { + //Asian font + for (int i = 0; i < Type1FontMetrics.CJKNames.Length; i++) + { + if (Type1FontMetrics.CJKNames[i][0].ToLower().Equals(f) || + Type1FontMetrics.CJKNames[i][1].ToLower().Equals(f)) + { + fontStyle = new Style(); + if (fontItalic) fontStyle.SetItalic(); + if (fontBold) fontStyle.SetBold(); + + setAsianFont(fontName, string.Empty); + fontStyle.SetFont(baseFont); + + _isCJK = true; + break; + } + } + if (!_isCJK) + { + int style = 0; + if (fontBold && fontItalic) + style = style + 3; + else + { + if (fontItalic) + style = style + 2; + if (fontBold) + style = style + 1; + } + for (int i = 0; i < PDFFont.base14.Length; i++) + { + if (PDFFont.base14[i][0].ToLower().Equals(f)) + { + fontName = PDFFont.base14[i][1 + style].Substring(1); + break; + } + } + baseFont = PdfFontFactory.CreateFont(fontName, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + } + } + else + {//True type font + + if (IsEmbeddedFont(fontName)) + { + fontStyle = new Style(); + if (fontItalic) fontStyle.SetItalic(); + if (fontBold) fontStyle.SetBold(); + } + string fontPath = GetFontLocation(fontName); + bool foundFont = true; + if (string.IsNullOrEmpty(fontPath)) + { + MSPDFFontDescriptor fontDescriptor = new MSPDFFontDescriptor(); + fontPath = fontDescriptor.getTrueTypeFontLocation(fontName); + if (string.IsNullOrEmpty(fontPath)) + { + baseFont = CreateDefaultFont(); + foundFont = false; + } + else + { + props.setProperty(Const.MS_FONT_LOCATION, fontName, fontPath); + } + } + if (foundFont) + { + if (IsEmbeddedFont(fontName)) + { + try + { + baseFont = PdfFontFactory.CreateFont(fontPath, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); + GXLogging.Debug(log, "EMBEED_SECTION Font"); + } + catch (IOException ioEx) + { + Exception exDetailed = new Exception($"Error creating {fontPath}. Check font is installed for the current user", ioEx); + throw exDetailed; + } + } + else + { + + fontStyle = new Style(); + if (fontItalic) fontStyle.SetItalic(); + if (fontBold) fontStyle.SetBold(); + + GXLogging.Debug(log, "NOT EMBEED_SECTION Font"); + + baseFont = PdfFontFactory.CreateFont(fontPath, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + fontStyle.SetFont(baseFont); + } + } + else + { + GXLogging.Debug(log, "NOT foundFont fontName:" + fontName); + } + } + } + catch (PdfException de) + { + GXLogging.Debug(log, "GxAttris DocumentException", de); + throw de; + } + catch (Exception e) + { + GXLogging.Debug(log, "GxAttris error", e); + baseFont = CreateDefaultFont(); + } + } + + private PdfFont CreateDefaultFont() + { + if (defaultFont == null) + { + if (IsPdfA()) + defaultFont = PdfFontFactory.CreateFont("Helvetica", PdfEncodings.CP1252, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + else + defaultFont = PdfFontFactory.CreateFont("Helvetica", PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + } + return PdfFontFactory.CreateFont("Helvetica", PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + } + + public override void setAsianFont(String fontName, String style) + { + //LoadAsianFontsDll(); + try + { + if (fontName.Equals("Japanese")) + baseFont = PdfFontFactory.CreateFont("HeiseiMin-W3", "UniJIS-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + if (fontName.Equals("Japanese2")) + baseFont = PdfFontFactory.CreateFont("HeiseiKakuGo-W5", "UniJIS-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + if (fontName.Equals("SimplifiedChinese")) + baseFont = PdfFontFactory.CreateFont("STSong-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + if (fontName.Equals("TraditionalChinese")) + baseFont = PdfFontFactory.CreateFont("MHei-Medium", "UniCNS-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + if (fontName.Equals("Korean")) + baseFont = PdfFontFactory.CreateFont("HYSMyeongJo-Medium", "UniKS-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + } + catch (PdfException de) + { + GXLogging.Debug(log, "setAsianFont error", de); + } + catch (IOException ioe) + { + GXLogging.Debug(log, "setAsianFont io error", ioe); + } + } + + public override void GxDrawText(String sTxt, int left, int top, int right, int bottom, int align, int htmlformat, int border, int valign) + { + GXLogging.Debug(log, "GxDrawText, text:" + sTxt); + + bool printRectangle = false; + if (props.getBooleanGeneralProperty(Const.BACK_FILL_IN_CONTROLS, true)) + printRectangle = true; + + if (printRectangle && (border == 1 || backFill)) + GxDrawRect(left, top, right, bottom, border, foreColor, backFill ? 1 : 0, backColor, 0, 0, 0, 0, 0, 0, 0, 0); + + PdfCanvas canvas = new PdfCanvas(pdfPage); + sTxt = sTxt.TrimEnd(TRIM_CHARS); + + PdfFont font = baseFont; + canvas.SetFontAndSize(this.baseFont, fontSize); + canvas.SetFillColor(foreColor); + float captionHeight = baseFont.GetAscent(sTxt, fontSize) / 1000; + float rectangleWidth = baseFont.GetWidth(sTxt, fontSize); + float lineHeight = (1 / 1000) * (baseFont.GetAscent(sTxt, fontSize) - baseFont.GetDescent(sTxt, fontSize)) + (fontSize * 1.2f); + float textBlockHeight = (float)convertScale(bottom - top); + int linesCount = (int)(textBlockHeight / lineHeight); + int bottomOri = bottom; + int topOri = top; + + if (linesCount >= 2 && !((align & 16) == 16) && htmlformat != 1) + if (valign == (int)VerticalAlign.TOP) + bottom = top + (int)reconvertScale(lineHeight); + else if (valign == (int)VerticalAlign.BOTTOM) + top = bottom - (int)reconvertScale(lineHeight); + + float bottomAux = (float)convertScale(bottom) - ((float)convertScale(bottom - top) - captionHeight) / 2; + float topAux = (float)convertScale(top) + ((float)convertScale(bottom - top) - captionHeight) / 2; ; + + float leftAux = (float)convertScale(left); + float rightAux = (float)convertScale(right); + int alignment = align & 3; + bool autoResize = (align & 256) == 256; + + GXLogging.Debug(log, "GxDrawText left: " + left + ",top:" + top + ",right:" + right + ",bottom:" + bottom + ",captionHeight:" + captionHeight + ",fontSize:" + fontSize); + GXLogging.Debug(log, "GxDrawText leftAux: " + leftAux + ",leftMargin:" + leftMargin + ",pageSize.Top:" + pageSize.GetTop() + ",bottomAux:" + bottomAux + ",topMargin:" + topMargin + ",bottomMargin:" + bottomMargin); + if (htmlformat == 1) + { + try + { + ConverterProperties converterProperties = new ConverterProperties(); + FontProvider fontProvider = document.GetFontProvider(); + if (IsTrueType(baseFont)) + { + Hashtable locations = GetFontLocations(); + foreach (string fontName in locations.Keys) + { + string fontPath = (string)locations[fontName]; + if (string.IsNullOrEmpty(fontPath)) + { + MSPDFFontDescriptor fontDescriptor = new MSPDFFontDescriptor(); + fontPath = fontDescriptor.getTrueTypeFontLocation(fontName); + } + if (!string.IsNullOrEmpty(fontPath)) + { + + fontProvider.AddFont(fontPath); + } + } + } + converterProperties.SetFontProvider(fontProvider); + bottomAux = (float)convertScale(bottom); + topAux = (float)convertScale(top); + float drawingPageHeight = this.pageSize.GetTop() - topMargin - bottomMargin; + + float llx = leftAux + leftMargin; + float lly = drawingPageHeight - bottomAux; + float urx = rightAux + leftMargin; + float ury = drawingPageHeight - topAux; + + Rectangle htmlRectangle = new Rectangle(llx, lly, urx - llx, ury - lly); + YPosition yPosition = new YPosition(htmlRectangle.GetTop()); + + Canvas htmlCanvas = new Canvas(canvas, htmlRectangle); + htmlCanvas.SetFontProvider(fontProvider); + + //Iterate over the elements (a.k.a the parsed HTML string) and handle each case accordingly + IList elements = HtmlConverter.ConvertToElements(sTxt, converterProperties); + foreach (IElement element in elements) + { + float blockElementHeight = GetBlockElementHeight((IBlockElement)element, htmlRectangle); + + if (PageHeightExceeded(bottomMargin, yPosition.CurrentYPosition)) + { + llx = leftAux + leftMargin; + lly = drawingPageHeight - bottomAux; + urx = rightAux + leftMargin; + ury = drawingPageHeight - topAux; + htmlRectangle = new Rectangle(llx, lly, urx - llx, ury - lly); + yPosition = new YPosition(htmlRectangle.GetTop()); + bottomAux -= drawingPageHeight; + GxEndPage(); + GxStartPage(); + + canvas = new PdfCanvas(pdfPage); + sTxt = sTxt.TrimEnd(TRIM_CHARS); + canvas.SetFontAndSize(this.baseFont, fontSize); + canvas.SetFillColor(foreColor); + + htmlCanvas = new Canvas(canvas, htmlRectangle); + htmlCanvas.SetFontProvider(fontProvider); + } + ProcessHTMLElement((IBlockElement)element, alignment, htmlCanvas); + yPosition.CurrentYPosition -= blockElementHeight; + } + + } + catch (Exception ex1) + { + GXLogging.Debug(log, "Error adding html: ", ex1); + } + + } + else if (barcode != null) + { + GXLogging.Debug(log, "Barcode" + barcode.GetType().ToString()); + try + { + barcode.SetCode(sTxt); + barcode.SetTextAlignment(alignment); + Rectangle rectangle = new Rectangle(0, 0); + + switch (alignment) + { + case 1: // Center Alignment + rectangle = rectangle.SetBbox((leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2, + (float)this.pageSize.GetTop() - (float)convertScale(bottom) - topMargin - bottomMargin, + (leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2, + (float)this.pageSize.GetTop() - (float)convertScale(top) - topMargin - bottomMargin); + break; + case 2: // Right Alignment + rectangle = rectangle.SetBbox(rightAux + leftMargin - rectangleWidth, + (float)this.pageSize.GetTop() - (float)convertScale(bottom) - topMargin - bottomMargin, + rightAux + leftMargin, + (float)this.pageSize.GetTop() - (float)convertScale(top) - topMargin - bottomMargin); + break; + case 0: // Left Alignment + rectangle = rectangle.SetBbox(leftAux + leftMargin, + (float)this.pageSize.GetTop() - (float)convertScale(bottom) - topMargin - bottomMargin, + leftAux + leftMargin + rectangleWidth, + (float)this.pageSize.GetTop() - (float)convertScale(top) - topMargin - bottomMargin); + break; + } + barcode.SetAltText(string.Empty); + barcode.SetBaseline(0); + + if (fontSize < Const.LARGE_FONT_SIZE) + barcode.SetX(Const.OPTIMAL_MINIMU_BAR_WIDTH_SMALL_FONT); + else + barcode.SetX(Const.OPTIMAL_MINIMU_BAR_WIDTH_LARGE_FONT); + + + Image imageCode = new Image(barcode.CreateFormXObject(backFill ? backColor : null, foreColor, pdfDocument)); + imageCode.SetFixedPosition(leftAux + leftMargin, rectangle.GetBottom()); + barcode.SetBarHeight(rectangle.GetHeight()); + imageCode.ScaleToFit(rectangle.GetWidth(), rectangle.GetHeight()); + document.Add(imageCode); + } + catch (Exception ex) + { + GXLogging.Error(log, "Error generating Barcode " + barcode.GetType().ToString(), ex); + } + } + else + { + + if (sTxt.Trim().ToLower().Equals("{{pages}}")) + { + GXLogging.Debug(log, "GxDrawText addTemplate-> (" + (leftAux + leftMargin) + "," + (this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin) + ") "); + templateRectangle = new Rectangle(leftAux + leftMargin, (float)this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin, rightAux + leftMargin, (float)this.pageSize.GetTop() - topAux - topMargin - bottomMargin); + + templatex = leftAux + leftMargin; + templatey = this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin; + templateFont = this.baseFont; + templateFontSize = fontSize; + templateColorFill = foreColor; + templateAlignment = alignment; + templateCreated = true; + } + + float textBlockWidth = rightAux - leftAux; + float TxtWidth = baseFont.GetWidth(sTxt, fontSize); + Boolean justified = (alignment == 3) && textBlockWidth < TxtWidth; + Boolean wrap = ((align & 16) == 16); + + float leading = (float)Convert.ToDouble(props.getGeneralProperty(Const.LEADING), CultureInfo.InvariantCulture.NumberFormat); + Style style = new Style(); + if (fontBold) style.SetBold(); + if (fontItalic) style.SetItalic(); + if (fontStrikethru) style.SetUnderline(fontSize / 6, fontSize / 2); + if (fontUnderline) style.SetUnderline(fontSize / 6, 0); + style.SetFont(font); + style.SetFontSize(fontSize); + style.SetFontColor(foreColor); + + if (wrap || justified) + { + bottomAux = (float)convertScale(bottomOri); + topAux = (float)convertScale(topOri); + + float llx = leftAux + leftMargin; + float lly = this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin; + float urx = rightAux + leftMargin; + float ury = this.pageSize.GetTop() - topAux - topMargin - bottomMargin; + + DrawTextColumn(llx, lly, urx, ury, leading, sTxt, valign, alignment, style, wrap); + } + else + { + try + { + if (!autoResize) + { + String newsTxt = sTxt; + while (TxtWidth > textBlockWidth && (newsTxt.Length - 1 >= 0)) + { + sTxt = newsTxt; + newsTxt = newsTxt.Substring(0, newsTxt.Length - 1); + TxtWidth = this.baseFont.GetWidth(newsTxt, fontSize); + } + } + + Paragraph p = new Paragraph(sTxt); + p.AddStyle(style); + + switch (alignment) + { + case 1: // Center Alignment + document.ShowTextAligned(p, ((leftAux + rightAux) / 2) + leftMargin, this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin, this.getPage(), TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0); + break; + case 2: // Right Alignment + document.ShowTextAligned(p, rightAux + leftMargin, this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin, this.getPage(), TextAlignment.RIGHT, VerticalAlignment.MIDDLE, 0); + break; + case 0: // Left Alignment + case 3: // Justified, only one text line + document.ShowTextAligned(p, leftAux + leftMargin, this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin, this.getPage(), TextAlignment.LEFT, VerticalAlignment.MIDDLE, 0); + break; + } + } + catch (Exception e) + { + GXLogging.Error(log, "GxDrawText failed to draw simple text: ", e); + } + } + } + } + + private void ProcessHTMLElement(IBlockElement blockElement, int alignment, Canvas htmlCanvas) + { + if (blockElement is Paragraph p) + { + if (alignment != 0) + p.SetTextAlignment(GetTextAlignment(alignment)); + } + htmlCanvas.Add(blockElement); + } + bool PageHeightExceeded(float bottomAux, float drawingPageHeight) + { + return bottomAux > drawingPageHeight; + } + private float GetBlockElementHeight(IBlockElement blockElement, Rectangle htmlRectangle) + { + return blockElement.CreateRendererSubTree().SetParent(document.GetRenderer()).Layout(new LayoutContext(new LayoutArea(this.getPage(), htmlRectangle))).GetOccupiedArea().GetBBox().GetHeight(); + } + + //Utility class used to know where the cursor is left after each block element (HTML tag) is rendered + private class YPosition + { + public YPosition(float initialYPosition) + { + CurrentYPosition = initialYPosition; + } + + public float CurrentYPosition { get; set; } + } + + private BaseDirection? GetBaseDirection(int runDirection) + { + switch (runDirection) + { + case 2: return BaseDirection.LEFT_TO_RIGHT; + default: return null; + } + } + + private VerticalAlignment GetVerticalAlignment(float valign) + { + if (valign == (int)VerticalAlign.TOP) + return VerticalAlignment.TOP; + else if (valign == (int)VerticalAlign.BOTTOM) + return VerticalAlignment.BOTTOM; + else + return VerticalAlignment.MIDDLE; + } + + private TextAlignment? GetTextAlignment(int alignment) + { + switch (alignment) + { + case 1: return TextAlignment.CENTER; + case 2: return TextAlignment.RIGHT; + case 0: return TextAlignment.LEFT; + case 3: return TextAlignment.JUSTIFIED; + } + return null; + } + + void DrawTextColumn(float llx, float lly, float urx, float ury, float leading, String text, int valign, int alignment, Style style, Boolean wrap) + { + Paragraph p = new Paragraph(text); + + if (valign == (int)VerticalAlign.MIDDLE) + { + ury = ury + leading; + p.SetVerticalAlignment(VerticalAlignment.MIDDLE); + } + else if (valign == (int)VerticalAlign.BOTTOM) + { + ury = ury + leading; + p.SetVerticalAlignment(VerticalAlignment.BOTTOM); + } + else if (valign == (int)VerticalAlign.TOP) + { + ury = ury + leading / 2; + p.SetVerticalAlignment(VerticalAlignment.TOP); + } + Rectangle rect = new Rectangle(llx, lly, urx - llx, ury - lly); + p.SetTextAlignment(GetTextAlignment(alignment)); + p.AddStyle(style); + + if (wrap) + { + p.SetProperty(Property.SPLIT_CHARACTERS, new CustomSplitCharacters()); + Table table = new Table(1); + table.SetFixedPosition(this.getPage(), rect.GetX(), rect.GetY(), rect.GetWidth()); + Cell cell = new Cell(); + cell.SetWidth(rect.GetWidth()); + cell.SetHeight(rect.GetHeight()); + cell.SetBorder(Border.NO_BORDER); + cell.SetVerticalAlignment(VerticalAlignment.MIDDLE); + cell.Add(p); + table.AddCell(cell); + document.Add(table); + } + else + { + try + { + PdfCanvas pdfCanvas = new PdfCanvas(pdfPage); + Canvas canvas = new Canvas(pdfCanvas, rect); + canvas.Add(p); + canvas.Close(); + } + catch (Exception e) { GXLogging.Error(log, "GxDrawText failed to justify text column: ", e); } + } + } + + public class CustomSplitCharacters : DefaultSplitCharacters + { + public override bool IsSplitCharacter(GlyphLine text, int glyphPos) + { + if (!text.Get(glyphPos).HasValidUnicode()) + { + return false; + } + + bool baseResult = base.IsSplitCharacter(text, glyphPos); + bool myResult = false; + Glyph glyph = text.Get(glyphPos); + + if (glyph.GetUnicode() == '_') + { + myResult = true; + } + return myResult || baseResult; + } + } + +#pragma warning restore CS0612 // Type or member is obsolete + + private PageSize ComputePageSize(float leftMargin, float topMargin, int width, int length, bool marginsInsideBorder) + { + if ((leftMargin == 0 && topMargin == 0) || marginsInsideBorder) + { + if (length == 23818 && width == 16834) + return PageSize.A3; + else if (length == 16834 && width == 11909) + return PageSize.A4; + else if (length == 11909 && width == 8395) + return PageSize.A5; + else if (length == 20016 && width == 5731) + return PageSize.B4; + else if (length == 14170 && width == 9979) + return PageSize.B5; + else if (length == 15120 && width == 10440) + return PageSize.EXECUTIVE; + else if (length == 20160 && width == 12240) + return PageSize.LEGAL; + else if (length == 15840 && width == 12240) + return PageSize.LETTER; + else + return new PageSize(new Rectangle((int)(width / PAGE_SCALE_X), (int)(length / PAGE_SCALE_Y))); + } + return new PageSize(new Rectangle((int)(width / PAGE_SCALE_X) + leftMargin, (int)(length / PAGE_SCALE_Y) + topMargin)); + } + public override void GxEndDocument() + { + //{{Pages}} + if (templateCreated) + { + int totalPages = pdfDocument.GetNumberOfPages(); + for (int i = 0; i < totalPages; i++) + { + PdfPage page = pdfDocument.GetPage(i); + Canvas canvas = new Canvas(page, templateRectangle); + canvas.ShowTextAligned(i.ToString(CultureInfo.InvariantCulture), templatex, templatey, TextAlignment.CENTER).SetBackgroundColor(templateColorFill).SetFont(templateFont).SetFontSize(templateFontSize); + } + } + + int copies = 1; + try + { + copies = Convert.ToInt32(printerSettings.getProperty(form, Const.COPIES)); + GXLogging.Debug(log, "Setting number of copies to " + copies); + PdfViewerPreferences preferences = new PdfViewerPreferences(); + preferences.SetNumCopies(copies); + + int duplex = Convert.ToInt32(printerSettings.getProperty(form, Const.DUPLEX)); + PdfViewerPreferences.PdfViewerPreferencesConstants duplexValue; + if (duplex == 1) + duplexValue = PdfViewerPreferences.PdfViewerPreferencesConstants.SIMPLEX; + else if (duplex == 2) + duplexValue = PdfViewerPreferences.PdfViewerPreferencesConstants.DUPLEX_FLIP_LONG_EDGE; + else if (duplex == 3) + duplexValue = PdfViewerPreferences.PdfViewerPreferencesConstants.DUPLEX_FLIP_SHORT_EDGE; + else if (duplex == 4) + duplexValue = PdfViewerPreferences.PdfViewerPreferencesConstants.DUPLEX_FLIP_LONG_EDGE; + else + duplexValue = PdfViewerPreferences.PdfViewerPreferencesConstants.NONE; + GXLogging.Debug(log, "Setting duplex to " + duplexValue.ToString()); + preferences.SetDuplex(duplexValue); + } + catch (Exception ex) + { + GXLogging.Error(log, "Setting viewer preference error", ex); + } + + bool printingScript = false; + String serverPrinting = props.getGeneralProperty(Const.SERVER_PRINTING); + bool fit = props.getGeneralProperty(Const.ADJUST_TO_PAPER).Equals("true"); + if ((outputType == Const.OUTPUT_PRINTER || outputType == Const.OUTPUT_STREAM_PRINTER) && serverPrinting.Equals("false")) + { + + printingScript = true; + StringBuilder javascript = new StringBuilder(); ; + + javascript.Append("var pp = this.getPrintParams();"); + String printer = printerSettings.getProperty(form, Const.PRINTER).Replace("\\", "\\\\"); + if (!string.IsNullOrEmpty(printer)) + javascript.Append("pp.printerName = \"" + printer + "\";\n"); + + if (fit) + { + javascript.Append("pp.pageHandling = pp.constants.handling.fit;\n"); + } + else + { + javascript.Append("pp.pageHandling = pp.constants.handling.none;\n"); + } + + GXLogging.Debug(log, "MODE:" + printerSettings.getProperty(form, Const.MODE) + ",form:" + form); + + if (printerSettings.getProperty(form, Const.MODE, "3").StartsWith("0"))//Show printer dialog Never + { + javascript.Append("pp.interactive = pp.constants.interactionLevel.automatic;\n"); + + for (int i = 0; i < copies; i++) + { + javascript.Append("this.print(pp);\n"); + } + + //writer.addJavaScript("this.print({bUI: false, bSilent: true, bShrinkToFit: true});"); + //No print dialog is displayed. During printing a progress monitor and cancel + //dialog is displayed and removed automatically when printing is complete. + } + else //Show printer dialog is sent directly to printer | always + { + javascript.Append("pp.interactive = pp.constants.interactionLevel.full;\n"); + //Displays the print dialog allowing the user to change print settings and requiring + //the user to press OK to continue. During printing a progress monitor and cancel + //dialog is displayed and removed automatically when printing is complete. + + javascript.Append("this.print(pp);\n"); + + } + pdfDocument.GetCatalog().SetOpenAction(PdfAction.CreateJavaScript(javascript.ToString())); + } + + if (IsPdfA()) + { + /*using (Stream iccProfile = ReadResource("sRGB Color Space Profile.icm")) + { + ICC_Profile icc = ICC_Profile.GetInstance(iccProfile); + writer.SetOutputIntents("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", icc); + } + + writer.ExtraCatalog.Put(PdfName.LANG, new PdfString(Config.GetCultureForLang(language).Name)); + PdfDictionary markInfo = new PdfDictionary(PdfName.MARKINFO); + markInfo.Put(PdfName.MARKED, new PdfBoolean(PdfBoolean.TRUE)); + writer.ExtraCatalog.Put(PdfName.MARKINFO, markInfo); + + writer.CreateXmpMetadata();*/ + + } + document.Close(); + GXLogging.Debug(log, "GxEndDocument!"); + try + { + props.save(); + GXLogging.Debug(log, "props.save()"); + } + catch (IOException e) + { + GXLogging.Error(log, "props.save() error", e); + + } + GXLogging.Debug(log, "outputType: " + outputType + ",docName:" + docName); + + switch (outputType) + { + case Const.OUTPUT_SCREEN: + try + { + outputStream.Close(); + GXLogging.Debug(log, "GxEndDocument OUTPUT_SCREEN outputstream length" + outputStream.ToString().Length); + } + catch (IOException e) + { + GXLogging.Error(log, "GxEndDocument OUTPUT_SCREEN error", e); + } + try { showReport(docName, modal); } + catch (Exception){} + break; + + case Const.OUTPUT_PRINTER: + try { outputStream.Close(); } + catch (IOException) {; } // Cierro el archivo + try + { + if (!serverPrinting.Equals("false") && !printingScript) + { + printReport(docName, this.printerOutputMode == 0, printerSettings.getProperty(form, Const.PRINTER)); + } + } + catch (Exception){} + break; + + case Const.OUTPUT_FILE: + try + { + outputStream.Close(); + GXLogging.Debug(log, "GxEndDocument OUTPUT_FILE outputstream length" + outputStream.ToString().Length); + } + catch (IOException e) + { + GXLogging.Error(log, "GxEndDocument OUTPUT_FILE error", e); + } + break; + + case Const.OUTPUT_STREAM: + case Const.OUTPUT_STREAM_PRINTER: + default: break; + } + outputStream = null; + + GXLogging.Debug(log, "GxEndDocument End"); + } + public override void GxStartPage() + { + try + { + pdfPage = pdfDocument.AddNewPage(); + GXLogging.Debug(log, "GxStartPage pages:" + pages + ",new page:" + pages + 1); + pages = pages + 1; + } + catch (PdfException de) + { + GXLogging.Error(log, "GxStartPage error", de); + } + } + + private bool IsTrueType(PdfFont font) + { + return font.GetFontProgram() is TrueTypeFont; + } + } +} + diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs new file mode 100644 index 000000000..0f1a30120 --- /dev/null +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs @@ -0,0 +1,1044 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Text; +using System.IO; +using System.Net.Http; +using System.Text; +using GeneXus; +using UglyToad.PdfPig.Core; +using UglyToad.PdfPig.Fonts.Standard14Fonts; +using UglyToad.PdfPig.Writer; +using static GeneXus.Utils.StringUtil; +using static UglyToad.PdfPig.Writer.PdfDocumentBuilder; +using static UglyToad.PdfPig.Writer.PdfPageBuilder; +using Color = System.Drawing.Color; +using Font = System.Drawing.Font; +using PageSize = UglyToad.PdfPig.Content.PageSize; +using PdfRectangle = UglyToad.PdfPig.Core.PdfRectangle; + +namespace GeneXus.Printer +{ + public class GxReportBuilderPDFPig : GxReportBuilderPdf + { + static IGXLogger log = GXLoggerFactory.GetLogger(); + public GxReportBuilderPDFPig() { } + public GxReportBuilderPDFPig(string appPath, Stream outputStream) + { + _appPath = appPath; + _pdfReport = new com.genexus.reports.PDFReportPDFPig(appPath); + if (outputStream != null) + { + _pdfReport.setOutputStream(outputStream); + GXLogging.Debug(log, "GxReportBuilderPdf outputStream: binaryWriter"); + } + } + } + +} + +namespace com.genexus.reports +{ + public class PDFReportPDFPig : PDFReportBase + { + static IGXLogger log = GXLoggerFactory.GetLogger(); + + private PdfDocumentBuilder documentBuilder; + private PdfPageBuilder pageBuilder; + private ExtendedPageSize pageSize; + + private AddedFont baseFont; + private string baseFontPath; + private string baseFontName; + private Color backColor, foreColor; + + private string barcodeType = null; + + private Dictionary documentImages; + + protected override void init(ref int gxYPage, ref int gxXPage, int pageWidth, int pageLength) + { + try + { + pageSize = ComputePageSize(leftMargin, topMargin, pageWidth, pageLength, props.getBooleanGeneralProperty(Const.MARGINS_INSIDE_BORDER, Const.DEFAULT_MARGINS_INSIDE_BORDER)); + documentBuilder = new PdfDocumentBuilder(); + + pageBuilder = pageSize.IsCustomPageSize() ? documentBuilder.AddPage(pageSize.Width, pageSize.Height) : documentBuilder.AddPage(pageSize.PageSize); + + gxXPage = (int) pageBuilder.PageSize.TopRight.X; + if (props.getBooleanGeneralProperty(Const.FIX_SAC24437, true)) + gxYPage = (int)(pageLength / GX_PAGE_SCALE_Y); + else + gxYPage = (int)(pageLength / GX_PAGE_SCALE_Y_OLD); + + } + catch (Exception e) + { + GXLogging.Error(log, "GxPrintInit failed", e); + } + } + + public PDFReportPDFPig(string appPath) : base(appPath) + { + documentBuilder = null; + documentImages = new Dictionary(); + } + + public override void GxStartPage() + { + try + { + if (pages > 0) + { + pageBuilder = pageSize.IsCustomPageSize() ? documentBuilder.AddPage(pageSize.Width, pageSize.Height) : documentBuilder.AddPage(pageSize.PageSize); + } + pages = pages + 1; + } + catch (Exception de) + { + GXLogging.Error(log, "GxStartPage error", de); + } + } + + internal override bool SetComplainceLevel(PdfConformanceLevel level) + { + return false; + } + + public override void GxDrawRect(int left, int top, int right, int bottom, int pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue, + int styleTop, int styleBottom, int styleRight, int styleLeft, int cornerRadioTL, int cornerRadioTR, int cornerRadioBL, int cornerRadioBR) + { + float penAux = (float)convertScale(pen); + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + + float x1, y1, x2, y2; + x1 = leftAux + leftMargin; + y1 = (float)pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin; + x2 = rightAux + leftMargin; + y2 = (float)pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin; + + GXLogging.Info(log, "Corner styling and radio are not taken into consideration because the PDFPig " + + "API provides no way to render rounded rectangles or style them"); + + if (pen > 0) + pageBuilder.SetStrokeColor( (byte)foreRed, (byte)foreGreen, (byte)foreBlue); + else + pageBuilder.SetStrokeColor( (byte)backRed, (byte)backGreen, (byte)backBlue); + + GXLogging.Info(log, "The PDFPig API provides no way of setting the line cap style"); + + if (backMode != 0) + { + pageBuilder.SetTextAndFillColor((byte)backRed, (byte)backGreen, (byte)backBlue); + pageBuilder.DrawRectangle(new PdfPoint(x1, y1), (decimal)(x2 - x1), (decimal)(y2 - y1), (decimal)penAux, true); + } + else + { + pageBuilder.DrawRectangle(new PdfPoint(x1, y1), (decimal)(x2 - x1), (decimal)(y2 - y1), (decimal) penAux, false); + } + pageBuilder.ResetColor(); + } + + public override void GxDrawLine(int left, int top, int right, int bottom, int width, int foreRed, int foreGreen, int foreBlue, int style) + { + try + { + float widthAux = (float)convertScale(width); + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + + GXLogging.Debug(log, "GxDrawLine -> (" + left + "," + top + ") - (" + right + "," + bottom + ") Width: " + width); + + float x1, y1, x2, y2; + + x1 = leftAux + leftMargin; + y1 = (float)pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin; + x2 = rightAux + leftMargin; + y2 = (float)pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin; + + pageBuilder.SetStrokeColor((byte)foreRed, (byte)foreGreen, (byte)foreBlue); + + if (style != 0) + { + GXLogging.Info(log, "The PDFPig API provides no way of creating a dashed line"); + float[] dashPattern = getDashedPattern(style); + } + + pageBuilder.DrawLine(new PdfPoint(x1, y1), new PdfPoint(x2, y2), (decimal)widthAux); + pageBuilder.ResetColor(); + } + catch (Exception e) + { + GXLogging.Error(log, "GxDrawLine error", e); + } + } + + public override void GxDrawBitMap(string bitmap, int left, int top, int right, int bottom, int aspectRatio) + { + try + { + string imageType = Path.GetExtension(bitmap).Substring(1); + + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + + float llx = leftAux + leftMargin; + float lly = (float) pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin; + float width; + float height; + if (aspectRatio == 0) + { + width = rightAux - leftAux; + height = bottomAux - topAux; + } + else + { + width = (rightAux - leftAux) * aspectRatio; + height = (bottomAux - topAux) * aspectRatio; + } + + PdfRectangle position = new PdfRectangle(llx, lly, llx + width, lly + height); + + AddedImage image = null; + AddedImage imageRef; + if (documentImages != null && documentImages.TryGetValue(bitmap, out imageRef)) + { + image = imageRef; + } + else + { + try + { + if (!Path.IsPathRooted(bitmap)) + { + + image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(defaultRelativePrepend + bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(defaultRelativePrepend + bitmap), position); + if (image == null) + { + bitmap = webAppDir + bitmap; + image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(bitmap), position); + } + else + { + bitmap = defaultRelativePrepend + bitmap; + } + } + else + { + image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(bitmap), position); + } + } + catch (Exception) + { + image = AddImageFromURL(bitmap, position); + } + if (image == null) + { + image = AddImageFromURL(bitmap, position); + } + + if (documentImages == null) + { + documentImages = new Dictionary(); + } + documentImages[bitmap] = image; + } + GXLogging.Debug(log, "GxDrawBitMap -> '" + bitmap + "' [" + left + "," + top + "] - Size: (" + (right - left) + "," + (bottom - top) + ")"); + } + catch (Exception e) + { + GXLogging.Error(log, "GxDrawBitMap error", e); + } + } + + private AddedImage AddImageFromURL(string url, PdfRectangle position) + { + AddedImage image = null; + using (HttpClient httpClient = new HttpClient()) + { + byte[] imageBytes = httpClient.GetByteArrayAsync(url).Result; + try + { + image = pageBuilder.AddJpeg(imageBytes, position); + } + catch (Exception) + { + pageBuilder.AddPng(imageBytes, position); + } + } + if (image == null) + { + GXLogging.Error(log, "GxDrawBitMap : PDFPig only supports adding jpeg or png images to documents"); + } + return image; + } + + public override void GxAttris(string fontName, int fontSize, bool fontBold, bool fontItalic, bool fontUnderline, bool fontStrikethru, int pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue) + { + bool isCJK = false; + bool embedFont = IsEmbeddedFont(fontName); + string originalFontName = fontName; + if (!embedFont) + { + fontName = getSubstitute(fontName); + } + + string fontSubstitute = originalFontName != fontName ? $"Original Font: {originalFontName} Substitute" : ""; + + GXLogging.Debug(log, $"GxAttris: "); + GXLogging.Debug(log, $"\\-> Font: {fontName} ({fontSize})" + + (fontBold ? " BOLD" : "") + + (fontItalic ? " ITALIC" : "") + + (fontStrikethru ? " Strike" : "")); + GXLogging.Debug(log, $"\\-> Fore ({foreRed}, {foreGreen}, {foreBlue})"); + GXLogging.Debug(log, $"\\-> Back ({backRed}, {backGreen}, {backBlue})"); + + this.fontUnderline = fontUnderline; + this.fontStrikethru = fontStrikethru; + this.fontSize = fontSize; + foreColor = Color.FromArgb(foreRed, foreGreen, foreBlue); + backColor = Color.FromArgb(backRed, backGreen, backBlue); + + backFill = backMode != 0; + + try + { + string fontNameLower = fontName.ToLower(); + if (PDFFont.isType1(fontName)) + { + foreach (string[] cjkName in Type1FontMetrics.CJKNames) + { + if (cjkName[0].ToLower().Equals(fontNameLower) || cjkName[1].ToLower().Equals(fontNameLower)) + { + string style = fontBold && fontItalic ? "BoldItalic" : fontItalic ? "Italic" : fontBold ? "Bold" : ""; + setAsianFont(fontName, style); + isCJK = true; + break; + } + } + if (!isCJK) + { + int style = (fontBold && fontItalic ? 3 : 0) + (fontItalic && !fontBold ? 2 : 0) + (fontBold && !fontItalic ? 1 : 0); + foreach (string[] base14Font in PDFFont.base14) + { + if (base14Font[0].ToLower().Equals(fontNameLower)) + { + fontName = base14Font[1 + style].Substring(1); + break; + } + } + ProcessBaseFont(fontName); + } + } + else + { + string style = (fontBold && fontItalic ? ",BoldItalic" : "") + (fontItalic && !fontBold ? ",Italic" : "") + (fontBold && !fontItalic ? ",Bold" : ""); + fontName += style; + ProcessBaseFont(fontName); + } + + if (barcode128AsImage && ( + fontName.ToLower().Contains("barcode 128") || fontName.ToLower().Contains("barcode128") + || + (!string.IsNullOrEmpty(baseFontPath) && (baseFontPath.ToLower().Contains("3of9") || baseFontPath.ToLower().Contains("3 of 9"))) + ) + ) + { + barcodeType = "barcode128"; + } + } + catch (Exception e) + { + GXLogging.Debug(log, "GxAttris DocumentException", e); + throw; + } + } + + private void ProcessBaseFont(string fontName) + { + string fontLocation = GetFontLocation(fontName); + if (string.IsNullOrEmpty(fontLocation)) + { + fontLocation = new MSPDFFontDescriptor().getTrueTypeFontLocation(fontName); + } + + if (!string.IsNullOrEmpty(fontLocation)) + { + byte[] fontBytes = File.ReadAllBytes(fontLocation); + baseFont = documentBuilder.AddTrueTypeFont(fontBytes); + } + else + { + baseFont = createType1FontFromName(fontName); + if (baseFont == null) + baseFont = documentBuilder.AddStandard14Font(Standard14Font.TimesRoman); + } + + baseFontName = fontName; + baseFontPath = fontLocation; + } + + private AddedFont createType1FontFromName(string fontName) + { + switch (fontName.ToLower()) + { + case "times-roman": + return documentBuilder.AddStandard14Font(Standard14Font.TimesRoman); + case "times-bold": + return documentBuilder.AddStandard14Font(Standard14Font.TimesBold); + case "times-italic": + return documentBuilder.AddStandard14Font(Standard14Font.TimesItalic); + case "times-bolditalic": + return documentBuilder.AddStandard14Font(Standard14Font.TimesBoldItalic); + case "helvetica": + return documentBuilder.AddStandard14Font(Standard14Font.Helvetica); + case "helvetica-bold": + return documentBuilder.AddStandard14Font(Standard14Font.HelveticaBold); + case "helvetica-oblique": + return documentBuilder.AddStandard14Font(Standard14Font.HelveticaOblique); + case "helvetica-boldoblique": + return documentBuilder.AddStandard14Font(Standard14Font.HelveticaBoldOblique); + case "courier": + return documentBuilder.AddStandard14Font(Standard14Font.Courier); + case "courier-bold": + return documentBuilder.AddStandard14Font(Standard14Font.CourierBold); + case "courier-oblique": + return documentBuilder.AddStandard14Font(Standard14Font.CourierOblique); + case "courier-boldoblique": + return documentBuilder.AddStandard14Font(Standard14Font.CourierBoldOblique); + case "symbol": + return documentBuilder.AddStandard14Font(Standard14Font.Symbol); + case "zapfdingbats": + return documentBuilder.AddStandard14Font(Standard14Font.ZapfDingbats); + default: + return null; + } + } + + public override void setAsianFont(string fontName, string style) + { + try + { + string fontPath = GetFontLocation(fontName); + if (string.IsNullOrEmpty(fontPath)) + { + MSPDFFontDescriptor fontDescriptor = new MSPDFFontDescriptor(); + fontPath = fontDescriptor.getTrueTypeFontLocation(fontName); + } + byte[] fontBytes = File.ReadAllBytes(fontPath); + baseFont = documentBuilder.AddTrueTypeFont(fontBytes); + } + catch (IOException de) + { + GXLogging.Debug(log, "setAsianFont error", de); + } + } + + public override void GxDrawText(string sTxt, int left, int top, int right, int bottom, int align, int htmlformat, int border, int valign) + { + bool printRectangle = false; + if (props.getBooleanGeneralProperty(Const.BACK_FILL_IN_CONTROLS, true)) + printRectangle = true; + + if (printRectangle && (border == 1 || backFill)) + { + GxDrawRect(left, top, right, bottom, border, foreColor.R, foreColor.G, foreColor.B, backFill ? 1 : 0, backColor.R, backColor.G, backColor.B, 0, 0); + } + + sTxt = RTrim(sTxt); + + AddedFont font = createType1FontFromName(baseFontName); + if (font == null) + { + try + { + byte[] fontBytes = File.ReadAllBytes(baseFontPath); + font = documentBuilder.AddTrueTypeFont(fontBytes); + } + catch + { + font = baseFont; + } + } + + float captionHeight = CalculateFontCaptionHeight(baseFontPath, fontSize); + float rectangleWidth = MeasureTextWidth(sTxt, baseFontPath, fontSize); + float lineHeight = MeasureTextHeight(sTxt, baseFontPath, fontSize); + float textBlockHeight = (float)convertScale(bottom - top); + int linesCount = (int)(textBlockHeight / lineHeight); + int bottomOri = bottom; + int topOri = top; + + if (linesCount >= 2 && !((align & 16) == 16) && htmlformat != 1) + { + if (valign == (int)VerticalAlign.TOP) + bottom = top + (int)reconvertScale(lineHeight); + else if (valign == (int)VerticalAlign.BOTTOM) + top = bottom - (int)reconvertScale(lineHeight); + } + + float bottomAux = (float)convertScale(bottom) - ((float)convertScale(bottom - top)) / 2; + float topAux = (float)convertScale(top) + ((float)convertScale(bottom - top)) / 2; + + float startHeight = bottomAux - topAux - captionHeight; + + float leftAux = (float)convertScale(left); + float rightAux = (float)convertScale(right); + int alignment = align & 3; + bool autoResize = (align & 256) == 256; + + if (htmlformat == 1) + { + GXLogging.Error(log, "GxDrawText: PDFPig report implementation does not support rendering HTML content into PDF reports"); + } + + if (barcodeType != null) + { + PdfRectangle barcodeRectangle; + switch (alignment) + { + case 1: // Center Alignment + barcodeRectangle = new PdfRectangle( + (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(bottom) - topMargin - bottomMargin, + (leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(top) - topMargin - bottomMargin + ); + break; + case 2: // Right Alignment + barcodeRectangle = new PdfRectangle( + rightAux + leftMargin - rectangleWidth, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(bottom) - topMargin - bottomMargin, + rightAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(top) - topMargin - bottomMargin + ); + break; + default: // Left Alignment (Corresponds to alignment = 0 but used as default) + barcodeRectangle = new PdfRectangle( + leftAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(bottom) - topMargin - bottomMargin, + leftAux + leftMargin + rectangleWidth, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(top) - topMargin - bottomMargin + ); + break; + + } + Image barcodeImage = CreateBarcodeImage((float)barcodeRectangle.Width, (float)barcodeRectangle.Height, baseFontPath, sTxt); + using (MemoryStream ms = new MemoryStream()) + { + barcodeImage.Save(ms, ImageFormat.Png); + pageBuilder.AddPng(ms.ToArray(), barcodeRectangle); + } + return; + } + + if (backFill) + { + PdfPoint rectangleStartingPoint; + switch (alignment) + { + case 1: // Center Alignment + rectangleStartingPoint = new PdfPoint( + (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + ); + break; + case 2: // Right Alignment + rectangleStartingPoint = new PdfPoint( + rightAux + leftMargin - rectangleWidth, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + ); + break; + default: // Left Alignment (Corresponds to alignment = 0 but used as default) + rectangleStartingPoint = new PdfPoint( + leftAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + ); + break; + + } + + decimal width = (decimal)(((leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2) - ((leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2)); + decimal height = (decimal)((pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin) - (pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin)); + + pageBuilder.SetTextAndFillColor(backColor.R, backColor.G, backColor.B); + pageBuilder.DrawRectangle(rectangleStartingPoint, width, height, 1, true); + } + + pageBuilder.SetTextAndFillColor(foreColor.R, foreColor.G, foreColor.B); + + if (fontUnderline) + { + float underlineSeparation = lineHeight / 5; + int underlineHeight = (int)underlineSeparation + (int)(underlineSeparation / 4); + + PdfPoint underlineStartingPoint; + PdfPoint underlineEndingPoint; + switch (alignment) + { + case 1: // Center Alignment + underlineStartingPoint = new PdfPoint( + (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + underlineEndingPoint = new PdfPoint( + (leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + break; + case 2: // Right Alignment + underlineStartingPoint = new PdfPoint( + rightAux + leftMargin - rectangleWidth, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + underlineEndingPoint = new PdfPoint( + rightAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + break; + default: // Left Alignment (Corresponds to alignment = 0 but used as default) + underlineStartingPoint = new PdfPoint( + leftAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + underlineEndingPoint = new PdfPoint( + leftAux + leftMargin + rectangleWidth, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + break; + + } + + pageBuilder.DrawLine(underlineStartingPoint, underlineEndingPoint, underlineHeight); + } + + if (fontStrikethru) + { + float strikethruSeparation = (float)(lineHeight / 1.5); + PdfPoint strikethruStartingPoint; + PdfPoint strikethruEndingPoint; + switch (alignment) + { + case 1: // Center Alignment + strikethruStartingPoint = new PdfPoint( + (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + strikethruEndingPoint = new PdfPoint( + (leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + break; + case 2: // Right Alignment + strikethruStartingPoint = new PdfPoint( + rightAux + leftMargin - rectangleWidth, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + strikethruEndingPoint = new PdfPoint( + rightAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + break; + default: // Left Alignment (Corresponds to alignment = 0 but used as default) + strikethruStartingPoint = new PdfPoint( + leftAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + strikethruEndingPoint = new PdfPoint( + leftAux + leftMargin + rectangleWidth, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + break; + + } + pageBuilder.DrawLine(strikethruStartingPoint, strikethruEndingPoint, (decimal)convertScale(fontSize) / 10); + } + + float textBlockWidth = rightAux - leftAux; + float TxtWidth = MeasureTextWidth(sTxt, baseFontPath, fontSize); + bool justified = (alignment == 3) && textBlockWidth < TxtWidth; + bool wrap = ((align & 16) == 16); + + if (wrap || justified) + { + bottomAux = (float)convertScale(bottomOri); + topAux = (float)convertScale(topOri); + + float llx = leftAux + leftMargin; + float lly = (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin); + float urx = rightAux + leftMargin; + float ury = (float)(pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin); + + ShowWrappedTextAligned(font, alignment, sTxt, llx, lly, urx, ury); + } + else + { + if (!autoResize) + { + string newsTxt = sTxt; + while (TxtWidth > textBlockWidth && (newsTxt.Length - 1 >= 0)) + { + sTxt = newsTxt; + newsTxt = newsTxt.Substring(0, newsTxt.Length - 1); + TxtWidth = MeasureTextWidth(sTxt, baseFontPath, fontSize); + } + } + switch (alignment) + { + case 1: // Center Alignment + ShowTextAligned(font, alignment, sTxt, ((leftAux + rightAux) / 2) + leftMargin, (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin)); + break; + case 2: // Right Alignment + ShowTextAligned(font, alignment, sTxt, rightAux + leftMargin, (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin)); + break; + case 0: // Left Alignment + case 3: // Justified, only one text line + ShowTextAligned(font, alignment, sTxt, leftAux + leftMargin, (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin)); + break; + } + } + } + + public Image CreateBarcodeImage(float width, float height, string fontPath, string text) + { + PrivateFontCollection fontCollection = new PrivateFontCollection(); + fontCollection.AddFontFile(fontPath); + FontFamily fontFamily = fontCollection.Families[0]; + + int bitmapWidth = (int)Math.Ceiling(width); + int bitmapHeight = (int)Math.Ceiling(height); + + float fontSize = Math.Min(width, height); + Font font; + SizeF textSize; + + using (Bitmap tempBitmap = new Bitmap(1, 1)) + { + using (Graphics tempGraphics = Graphics.FromImage(tempBitmap)) + { + do + { + font = new Font(fontFamily, fontSize, GraphicsUnit.Pixel); + textSize = tempGraphics.MeasureString(text, font); + fontSize--; + } while (textSize.Width > width || textSize.Height > height); + } + } + + Bitmap bitmap = new Bitmap(bitmapWidth, bitmapHeight); + bitmap.SetResolution(600, 600); + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + graphics.Clear(Color.White); + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit; + StringFormat format = new StringFormat + { + Alignment = StringAlignment.Center, + LineAlignment = StringAlignment.Center + }; + graphics.DrawString(text, font, Brushes.Black, new RectangleF(0, 0, width, height), format); + } + font.Dispose(); + fontCollection.Dispose(); + + return bitmap; + } + + private float MeasureTextWidth(string text, string fontPath, float fontSize) + { + Font font; + if (string.IsNullOrEmpty(fontPath)) + { + font = new Font("Times New Roman", fontSize, GraphicsUnit.Point); + } + else + { + PrivateFontCollection pfc = new PrivateFontCollection(); + pfc.AddFontFile(fontPath); + font = new Font(pfc.Families[0], fontSize, GraphicsUnit.Point); + } + + using (font) + { + using (var fakeImage = new Bitmap(1, 1)) + { + using (var graphics = Graphics.FromImage(fakeImage)) + { + graphics.PageUnit = GraphicsUnit.Point; + graphics.TextRenderingHint = TextRenderingHint.AntiAlias; + var sizeF = graphics.MeasureString(text, font); + return sizeF.Width; + } + } + } + } + + private float MeasureTextHeight(string text, string fontPath, float fontSize) + { + Font font; + if (string.IsNullOrEmpty(fontPath)) + { + font = new Font("Times New Roman", fontSize, GraphicsUnit.Point); + } + else + { + PrivateFontCollection pfc = new PrivateFontCollection(); + pfc.AddFontFile(fontPath); + font = new Font(pfc.Families[0], fontSize, GraphicsUnit.Point); + } + + using (font) + { + using (var fakeImage = new Bitmap(1, 1)) + { + using (var graphics = Graphics.FromImage(fakeImage)) + { + graphics.PageUnit = GraphicsUnit.Point; + graphics.TextRenderingHint = TextRenderingHint.AntiAlias; + var sizeF = graphics.MeasureString(text, font); + return sizeF.Height; + } + } + } + } + + private float CalculateFontCaptionHeight(string fontPath, float fontSize, FontStyle fontStyle = FontStyle.Regular) + { + Font font; + if (string.IsNullOrEmpty(fontPath)) + { + font = new Font("Times New Roman", fontSize, GraphicsUnit.Point); + } + else + { + PrivateFontCollection pfc = new PrivateFontCollection(); + pfc.AddFontFile(fontPath); + font = new Font(pfc.Families[0], fontSize, GraphicsUnit.Point); + } + using (font) + { + FontFamily ff = font.FontFamily; + + float ascent = ff.GetCellAscent(fontStyle); + float descent = ff.GetCellDescent(fontStyle); + float lineSpacing = ff.GetLineSpacing(fontStyle); + + float height = fontSize * (ascent + descent) / ff.GetEmHeight(fontStyle); + + return height; + } + } + + private void ShowTextAligned(AddedFont font, int alignment, string text, float x, float y) + { + try + { + float textWidth = MeasureTextWidth(text, baseFontPath, fontSize); + switch (alignment) + { + case 0: // Left-aligned + case 3: // Justified, only one text line + break; + case 1: // Center-aligned + x = x - textWidth / 2; + break; + case 2: // Right-aligned + x = x - textWidth; + break; + } + y = (float)(y - fontSize * 0.5); + pageBuilder.AddText(text, fontSize, new PdfPoint(x, y), font); + } + catch (IOException ioe) + { + GXLogging.Error(log, "failed to draw aligned text: ", ioe); + } + } + + private void ShowWrappedTextAligned(AddedFont font, int alignment, string text, float llx, float lly, float urx, float ury) + { + try + { + List lines = new List(); + string[] words = text.Split(' '); + StringBuilder currentLine = new StringBuilder(); + foreach (string word in words) + { + float currentLineWidth = MeasureTextWidth(currentLine + " " + word, baseFontPath, fontSize); + if (currentLineWidth < urx - llx) + { + if (currentLine.Length > 0) + { + currentLine.Append(" "); + } + currentLine.Append(word); + } + else + { + lines.Add(currentLine.ToString()); + currentLine.Clear(); + currentLine.Append(word); + } + } + lines.Add(currentLine.ToString()); + + float leading = (float)(lines.Count == 1 ? fontSize : 1.2 * fontSize); + float totalTextHeight = fontSize * lines.Count + leading * (lines.Count - 1); + float startY = lines.Count == 1 ? lly + (ury - lly - totalTextHeight) / 2 : lly + (ury - lly - totalTextHeight) / 2 + (lines.Count - 1) * (fontSize + leading); + + foreach (string line in lines) + { + float lineWidth = MeasureTextWidth(line, baseFontPath, fontSize); + float startX; + + switch (alignment) + { + case 1: // Center-aligned + startX = llx + (urx - llx - lineWidth) / 2; + break; + case 2: // Right-aligned + startX = urx - lineWidth; + break; + default: // Left-aligned & Justified, only one text line + startX = llx; + break; + } + + pageBuilder.AddText(line, fontSize, new PdfPoint(startX, startY), font); + startY -= leading; + } + } + catch (IOException ioe) + { + GXLogging.Error(log, "Failed to draw wrapped text", ioe); + } + } + + private ExtendedPageSize ComputePageSize(float leftMargin, float topMargin, int width, int length, bool marginsInsideBorder) + { + if ((leftMargin == 0 && topMargin == 0) || marginsInsideBorder) + { + if (length == 23818 && width == 16834) + return new ExtendedPageSize(PageSize.A3); + else if (length == 16834 && width == 11909) + return new ExtendedPageSize(PageSize.A4); + else if (length == 11909 && width == 8395) + return new ExtendedPageSize(PageSize.A5); + else if (length == 15120 && width == 10440) + return new ExtendedPageSize(PageSize.Executive); + else if (length == 20160 && width == 12240) + return new ExtendedPageSize(PageSize.Legal); + else if (length == 15840 && width == 12240) + return new ExtendedPageSize(PageSize.Letter); + else + return new ExtendedPageSize((width / PAGE_SCALE_X), (length / PAGE_SCALE_Y)); + } + return new ExtendedPageSize((width / PAGE_SCALE_X) + leftMargin, (length / PAGE_SCALE_Y) + topMargin); + + } + + public override void GxEndDocument() + { + if (pages == 0) + { + GxStartPage(); + } + + GXLogging.Info(log, "The PDFPig API provides no way of setting the number of copies and the duplex value for the viewer preferences"); + + GXLogging.Info(log, "The PDFPig API provides no way of embedding javascript into the document"); + + try + { + byte[] documentBytes = documentBuilder.Build(); + outputStream.Write(documentBytes, 0, documentBytes.Length); + } + catch (IOException e) + { + GXLogging.Debug(log,"GxEndDocument: failed to write document to the output stream", e); + } + + string serverPrinting = props.getGeneralProperty(Const.SERVER_PRINTING); + bool printingScript = (outputType == Const.OUTPUT_PRINTER || outputType == Const.OUTPUT_STREAM_PRINTER) && serverPrinting.Equals("false"); + switch (outputType) + { + case Const.OUTPUT_SCREEN: + try + { + outputStream.Close(); + GXLogging.Debug(log, "GxEndDocument OUTPUT_SCREEN outputstream length" + outputStream.ToString().Length); + } + catch (IOException e) + { + GXLogging.Error(log, "GxEndDocument OUTPUT_SCREEN error", e); + } + try { showReport(docName, modal); } + catch (Exception) { } + break; + + case Const.OUTPUT_PRINTER: + try { outputStream.Close(); } + catch (IOException) { } + try + { + if (!serverPrinting.Equals("false") && !printingScript) + { + printReport(docName, this.printerOutputMode == 0, printerSettings.getProperty(form, Const.PRINTER)); + } + } + catch (Exception) { } + break; + + case Const.OUTPUT_FILE: + try + { + outputStream.Close(); + GXLogging.Debug(log, "GxEndDocument OUTPUT_FILE outputstream length" + outputStream.ToString().Length); + } + catch (IOException e) + { + GXLogging.Error(log, "GxEndDocument OUTPUT_FILE error", e); + } + break; + + case Const.OUTPUT_STREAM: + case Const.OUTPUT_STREAM_PRINTER: + default: break; + } + outputStream = null; + } + + } + + internal class ExtendedPageSize + { + internal double Width; + internal double Height; + internal PageSize PageSize; + internal ExtendedPageSize(double w, double h) + { + Width = w; + Height = h; + PageSize = PageSize.Custom; + } + internal ExtendedPageSize(PageSize pageSize) + { + PageSize = pageSize; + } + internal bool IsCustomPageSize() + { + return PageSize == PageSize.Custom; + } + } +} diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/Properties/AssemblyInfo.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d916f63fa --- /dev/null +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("DotNetCoreWebUnitTest")] diff --git a/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj b/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj index 826d61a8b..8588e6c64 100644 --- a/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj +++ b/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 1701;1702;NU1701;NU1603;CS0618 NETCORE Lucene Search @@ -20,22 +20,13 @@ - + - + - - - - False - ..\libs\Jayrock.dll - - - - \ No newline at end of file diff --git a/dotnet/src/dotnetcore/GxXsl/GxXsl.csproj b/dotnet/src/dotnetcore/GxXsl/GxXsl.csproj index ece24ed17..d3f5fde98 100644 --- a/dotnet/src/dotnetcore/GxXsl/GxXsl.csproj +++ b/dotnet/src/dotnetcore/GxXsl/GxXsl.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 GxXsl GxXsl XML xsl apply diff --git a/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj b/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj index fba3e8425..b4815e353 100644 --- a/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj +++ b/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj @@ -1,12 +1,14 @@ - + - net6.0 + net6.0;net8.0 GeneXus.SD.Store.StoreManager GeneXus.SD.Store StoreManager GeneXus.StoreManager.Core - + NETCORE + + @@ -25,8 +27,9 @@ - + + @@ -36,10 +39,4 @@ - - - False - ..\..\libs\Jayrock.dll - - diff --git a/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj b/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj index 46337bb8a..ecc9fba40 100644 --- a/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj +++ b/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 Memcached GeneXus.Memcached.Core NETCORE @@ -15,8 +15,8 @@ - - + + diff --git a/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj b/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj index d83ab1b7f..f616027d3 100644 --- a/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj +++ b/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 Redis GeneXus.Redis.Core @@ -18,11 +18,14 @@ - - + + + + + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/AWSMessageQueueProvider.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/AWSMessageQueueProvider.cs index ebf20e338..66b5394f0 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/AWSMessageQueueProvider.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/AWSMessageQueueProvider.cs @@ -21,8 +21,10 @@ public SimpleMessageQueue Connect(string queueURL, out GXBaseCollection errorMessagesConnect, out bool successConnect); errorMessages = errorMessagesConnect; success = successConnect; @@ -31,10 +33,12 @@ public SimpleMessageQueue Connect(string queueURL, out GXBaseCollection("Accesskey")); - properties.Add("QUEUE_AWSSQS_SECRET_KEY", awsCredentials.GetPropertyValue("Secretkey")); - properties.Add("QUEUE_AWSSQS_REGION", awsCredentials.GetPropertyValue("Region")); + GXProperties properties = new GXProperties + { + { "QUEUE_AWSSQS_ACCESS_KEY", awsCredentials.GetPropertyValue("Accesskey") }, + { "QUEUE_AWSSQS_SECRET_KEY", awsCredentials.GetPropertyValue("Secretkey") }, + { "QUEUE_AWSSQS_REGION", awsCredentials.GetPropertyValue("Region") } + }; return properties; } } diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/AWSQueue.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/AWSQueue.cs index f0ed5882d..47f31e184 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/AWSQueue.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/AWSQueue.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; using Amazon; using Amazon.Runtime; using Amazon.SQS; @@ -7,16 +9,13 @@ using GeneXus.Messaging.Common; using GeneXus.Services; using GeneXus.Utils; -using log4net; -using System.Threading.Tasks; -using System.Reflection; namespace GeneXus.Messaging.Queue { public class AWSQueue : QueueBase, IQueue { - public static String Name = "AWSSQS"; + public static string Name = "AWSSQS"; const string ACCESS_KEY = "ACCESS_KEY"; const string SECRET_ACCESS_KEY = "SECRET_KEY"; const string REGION = "REGION"; @@ -31,7 +30,7 @@ public class AWSQueue : QueueBase, IQueue public const string MESSSAGE_GROUP_ID = "MessageGroupId"; public const string MESSSAGE_DEDUPLICATION_ID = "MessageDeduplicationId"; - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(AWSQueue)); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); public AWSQueue() : this(null) { @@ -313,13 +312,14 @@ private MessageQueueResult SetupMessageQueueResult(SendMessageResponse response) messageQueueResult.MessageId = response.MessageId; messageQueueResult.MessageStatus = MessageQueueResultStatus.Sent; - messageQueueResult.MessageAttributes = new GXProperties(); - - messageQueueResult.MessageAttributes.Add("MD5OfMessageSystemAttributes", response.MD5OfMessageSystemAttributes); - messageQueueResult.MessageAttributes.Add("MD5OfMessageAttributes", response.MD5OfMessageAttributes); - messageQueueResult.MessageAttributes.Add("ContentLength", response.ContentLength.ToString()); - messageQueueResult.MessageAttributes.Add("MD5OfMessageBody", response.MD5OfMessageBody); - messageQueueResult.MessageAttributes.Add("SequenceNumber", response.SequenceNumber); + messageQueueResult.MessageAttributes = new GXProperties + { + { "MD5OfMessageSystemAttributes", response.MD5OfMessageSystemAttributes }, + { "MD5OfMessageAttributes", response.MD5OfMessageAttributes }, + { "ContentLength", response.ContentLength.ToString() }, + { "MD5OfMessageBody", response.MD5OfMessageBody }, + { "SequenceNumber", response.SequenceNumber } + }; Type t = response.ResponseMetadata.GetType(); PropertyInfo[] props = t.GetProperties(); @@ -342,12 +342,13 @@ private MessageQueueResult SetupMessageQueueResult(SendMessageBatchResultEntry r messageQueueResult.MessageId = response.Id; messageQueueResult.MessageStatus = MessageQueueResultStatus.Sent; - messageQueueResult.MessageAttributes = new GXProperties(); - - messageQueueResult.MessageAttributes.Add("MD5OfMessageSystemAttributes", response.MD5OfMessageSystemAttributes); - messageQueueResult.MessageAttributes.Add("MD5OfMessageAttributes", response.MD5OfMessageAttributes); - messageQueueResult.MessageAttributes.Add("MD5OfMessageBody", response.MD5OfMessageBody); - messageQueueResult.MessageAttributes.Add("SequenceNumber", response.SequenceNumber); + messageQueueResult.MessageAttributes = new GXProperties + { + { "MD5OfMessageSystemAttributes", response.MD5OfMessageSystemAttributes }, + { "MD5OfMessageAttributes", response.MD5OfMessageAttributes }, + { "MD5OfMessageBody", response.MD5OfMessageBody }, + { "SequenceNumber", response.SequenceNumber } + }; return messageQueueResult; } @@ -378,11 +379,12 @@ private SimpleQueueMessage SetupSimpleQueueMessage(Message response) simpleQueueMessage.MessageBody = response.Body; simpleQueueMessage.MessageHandleId = response.ReceiptHandle; - simpleQueueMessage.MessageAttributes = new GXProperties(); - - simpleQueueMessage.MessageAttributes.Add("MD5OfMessageAttributes", response.MD5OfMessageAttributes); - simpleQueueMessage.MessageAttributes.Add("MD5OfBody", response.MD5OfBody); - + simpleQueueMessage.MessageAttributes = new GXProperties + { + { "MD5OfMessageAttributes", response.MD5OfMessageAttributes }, + { "MD5OfBody", response.MD5OfBody } + }; + foreach (var messageAttribute in response.MessageAttributes) { MessageAttributeValue messageAttributeValue = messageAttribute.Value; diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/GXAmazonSQS.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/GXAmazonSQS.csproj index 53ad7604c..525f3c3c1 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/GXAmazonSQS.csproj +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAmazonSQS/GXAmazonSQS.csproj @@ -1,7 +1,7 @@ - net6.0 + net6.0;net8.0 GeneXus.AWS.Queue AWS SQS Queue Messaging diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/AzureEventGrid.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/AzureEventGrid.cs new file mode 100644 index 000000000..821a7242a --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/AzureEventGrid.cs @@ -0,0 +1,301 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text.Json; +using System.Threading.Tasks; +using Azure; +using Azure.Identity; +using Azure.Messaging; +using Azure.Messaging.EventGrid; +using GeneXus.Messaging.Common; +using GeneXus.Services; +using GeneXus.Utils; + +namespace GeneXus.Messaging.GXAzureEventGrid +{ + public class AzureEventGrid : EventRouterBase, IEventRouter + { + public static string Name = "AZUREEVENTGRID"; + private EventGridPublisherClient _client; + private string _endpoint; + private string _accessKey; + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); + public AzureEventGrid() : this(null) + { + } + public AzureEventGrid(GXService providerService) : base(providerService) + { + Initialize(providerService); + } + private void Initialize(GXService providerService) + { + ServiceSettings serviceSettings = new(PropertyConstants.EVENT_ROUTER, Name, providerService); + _endpoint = serviceSettings.GetEncryptedPropertyValue(PropertyConstants.URI_ENDPOINT); + _accessKey = serviceSettings.GetEncryptedPropertyValue(PropertyConstants.ACCESS_KEY); + + if (!string.IsNullOrEmpty(_endpoint)) { + + if (!string.IsNullOrEmpty(_accessKey)) + _client = new EventGridPublisherClient( + new Uri(_endpoint), + new AzureKeyCredential(_accessKey)); + else + //Try authenticating using AD + { + ChainedTokenCredential credential = new ChainedTokenCredential(new ManagedIdentityCredential(), new ManagedIdentityCredential(Environment.GetEnvironmentVariable("AZURE_CLIENT_ID")), new EnvironmentCredential(), new AzureCliCredential()); + GXLogging.Debug(logger,"Authentication using Oauth 2.0."); + _client = new EventGridPublisherClient( + new Uri(_endpoint), + credential); + } + } + else + throw new Exception("Endpoint URI must be set."); + } + public override string GetName() + { + return Name; + } + + public bool SendEvent(GXCloudEvent gxCloudEvent, bool binaryData) + { + CloudEvent evt = ToCloudEvent(gxCloudEvent, binaryData); + bool success = false; + Task task; + if (_client != null) + { + task = Task.Run(async () => await sendEvtAsync(evt).ConfigureAwait(false)); + success = task.Result; + } + else + { + throw new Exception("There was an error at the Event Grid initialization."); + } + return success; + } + public bool SendEvents(IList gxCloudEvents, bool binaryData) + { + List evts = new List(); + foreach (GXCloudEvent e in gxCloudEvents) + evts.Add(ToCloudEvent(e, binaryData)); + + bool success = false; + Task task; + if (_client != null) + { + task = Task.Run(async () => await sendEvtsAsync(evts).ConfigureAwait(false)); + success = task.Result; + } + else + { + throw new Exception("There was an error at the Event Grid initialization."); + } + return success; + } + public bool SendCustomEvents(string jsonString, bool isBinary) + { + if (string.IsNullOrEmpty(jsonString)) + { + throw new Exception("Events cannot be empty."); + } + try + { + List evts = JsonSerializer.Deserialize>(jsonString); + + IList eventGridEvents = new List(); + foreach (GXEventGridSchema e in evts) + eventGridEvents.Add(ToEventGridSchema(e,isBinary)); + + bool success = false; + try + { + Task task; + if (_client != null) + { + task = Task.Run(async () => await sendEventGridSchemaEventsAsync(eventGridEvents).ConfigureAwait(false)); + success = task.Result; + } + else + { + throw new Exception("There was an error at the Event Grid initialization."); + } + } + catch (AggregateException ae) + { + throw ae; + } + return success; + } + catch (JsonException) + { + try + { + GXEventGridSchema evt = JsonSerializer.Deserialize(jsonString); + bool success = false; + try + { + Task task; + if (_client != null) + { + task = Task.Run(async () => await sendEventGridSchemaEventAsync(ToEventGridSchema(evt, isBinary)).ConfigureAwait(false)); + success = task.Result; + } + else + { + throw new Exception("There was an error at the Event Grid initialization."); + } + } + catch (AggregateException ae) + { + throw ae; + } + return success; + } + catch (JsonException) + { + throw new Exception("jsonEvents parameter format is no correct. Valid format is AzureEventGrid.EventGridSchema SDT."); + } + } + } + + #region Async methods + /// + /// Send asynchronously an event formatted as Azure EventGrid Schema. + /// + /// + /// bool + private async Task sendEventGridSchemaEventAsync(EventGridEvent evt) + { + Response response = await _client.SendEventAsync(evt).ConfigureAwait(false); + GXLogging.Debug(logger,string.Format("Send Event GridSchema: {0} {1}", response.Status, response.ReasonPhrase)); + return !response.IsError; + + } + /// + /// Send asynchronously a list of events formatted as Azure EventGrid Schema. + /// + /// + /// bool + private async Task sendEventGridSchemaEventsAsync(IEnumerable evts) + { + Response response = await _client.SendEventsAsync(evts).ConfigureAwait(false); + GXLogging.Debug(logger,string.Format("Send Events GridSchema: {0} {1}", response.Status, response.ReasonPhrase)); + return !response.IsError; + } + /// + /// Send asynchronously an event formatted as CloudEvent Schema. + /// + /// + /// bool + private async Task sendEvtAsync(CloudEvent cloudEvent) + { + Response response = await _client.SendEventAsync(cloudEvent).ConfigureAwait(false); + GXLogging.Debug(logger,string.Format("Send Event: {0} {1}", response.Status,response.ReasonPhrase)); + return !response.IsError; + } + /// + /// Send asynchronously a list of CloudEvent Schema formatted events. + /// + /// + /// bool + private async Task sendEvtsAsync(IEnumerable cloudEvents) + { + Response response = await _client.SendEventsAsync(cloudEvents).ConfigureAwait(false); + GXLogging.Debug(logger,string.Format("Send Events: {0} {1}", response.Status, response.ReasonPhrase)); + return !response.IsError; + } + + #endregion + + #region TransformMethods + public bool GetMessageFromException(Exception ex, SdtMessages_Message msg) + { + try + { + RequestFailedException az_ex = (RequestFailedException)ex; + msg.gxTpr_Id = az_ex.ErrorCode.ToString(); + msg.gxTpr_Description = az_ex.Message; + return true; + } + catch (Exception) + { + return false; + } + } + private EventGridEvent ToEventGridSchema(GXEventGridSchema gxEventGridSchema, bool isBinary) + { + EventGridEvent evt; + if (isBinary && !string.IsNullOrEmpty(gxEventGridSchema.data)) + { + BinaryData binaryData = new BinaryData(gxEventGridSchema.data); + evt = new EventGridEvent(gxEventGridSchema.subject, gxEventGridSchema.eventtype, gxEventGridSchema.dataversion, binaryData);} + else + evt = new EventGridEvent(gxEventGridSchema.subject, gxEventGridSchema.eventtype, gxEventGridSchema.dataversion, gxEventGridSchema.data, null); + if (!string.IsNullOrEmpty(gxEventGridSchema.id)) + evt.Id = gxEventGridSchema.id; + if (!string.IsNullOrEmpty(gxEventGridSchema.topic)) + evt.Topic = gxEventGridSchema.topic; + if (gxEventGridSchema.eventtime != DateTime.MinValue) + evt.EventTime = gxEventGridSchema.eventtime; + + return evt; + } + private CloudEvent ToCloudEvent(GXCloudEvent gxCloudEvent, bool isBinaryData) + { + CloudEvent evt; + Dictionary emptyData = new Dictionary(); + if (string.IsNullOrEmpty(gxCloudEvent.data)) + evt = new CloudEvent(source:gxCloudEvent.source, type:gxCloudEvent.type, emptyData); + else + { + if (!isBinaryData) + { + if (string.IsNullOrEmpty(gxCloudEvent.datacontenttype)) + gxCloudEvent.datacontenttype = "application/json"; + evt = new CloudEvent(gxCloudEvent.source, gxCloudEvent.type, BinaryData.FromString(gxCloudEvent.data),gxCloudEvent.datacontenttype,CloudEventDataFormat.Json); + } + else + { + if (string.IsNullOrEmpty(gxCloudEvent.datacontenttype)) + gxCloudEvent.datacontenttype = "application/octet-stream"; + evt = new CloudEvent(gxCloudEvent.source, gxCloudEvent.type, BinaryData.FromString(gxCloudEvent.data), gxCloudEvent.datacontenttype,CloudEventDataFormat.Binary); + } + } + if (!string.IsNullOrEmpty(gxCloudEvent.id)) + evt.Id = gxCloudEvent.id; + if (!string.IsNullOrEmpty(gxCloudEvent.dataschema)) + evt.DataSchema = gxCloudEvent.dataschema; + if (!string.IsNullOrEmpty(gxCloudEvent.subject)) + evt.Subject = gxCloudEvent.subject; + if (gxCloudEvent.time != DateTime.MinValue) + evt.Time = gxCloudEvent.time; + return evt; + } + #endregion + } + + [DataContract] + public class GXEventGridSchema + { + [DataMember] + public string topic { get; set; } + [DataMember] + public string eventtype { get; set; } + [DataMember] + public string id { get; set; } + [DataMember] + public string subject { get; set; } + [DataMember] + public string data { get; set; } + [DataMember] + public string dataversion { get; set; } + + [DataMember] + public DateTime eventtime { get; set; } + + [DataMember] + public string metadataversion { get; set; } + + + } +} \ No newline at end of file diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/EventGridRouterProvider.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/EventGridRouterProvider.cs new file mode 100644 index 000000000..f8c9e20ae --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/EventGridRouterProvider.cs @@ -0,0 +1,26 @@ +using GeneXus.Messaging.Common; +using GeneXus.Utils; + +namespace GeneXus.Messaging.GXAzureEventGrid +{ + /// + /// Implementation of EventGridRouterProvider External Object. + /// + public class EventGridRouterProvider + { + public EventRouterProviderBase Connect(string endpoint, string accesskey, out GXBaseCollection errorMessages, out bool success) + { + EventRouterProvider eventRouterProvider = new EventRouterProvider(); + GXProperties properties = new GXProperties + { + { PropertyConstants.EVENTROUTER_AZUREEG_ENDPOINT, endpoint }, + { PropertyConstants.EVENTROUTER_AZUREEG_ACCESS_KEY, accesskey } + }; + + EventRouterProviderBase evtRouterProvider = eventRouterProvider.Connect(PropertyConstants.AZUREEVENTGRID, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); + errorMessages = errorMessagesConnect; + success = successConnect; + return evtRouterProvider; + } + } +} diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/GXAzureEventGrid.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/GXAzureEventGrid.csproj new file mode 100644 index 000000000..5aebdae39 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/GXAzureEventGrid.csproj @@ -0,0 +1,20 @@ + + + + net6.0;net8.0 + GeneXus.Azure.EventGrid + Azure EventGrid Messaging + + + + + + + + + + + + + + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/AzureMessageQueueProvider.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/AzureMessageQueueProvider.cs index 698d7c0cc..93a9f49df 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/AzureMessageQueueProvider.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/AzureMessageQueueProvider.cs @@ -3,16 +3,35 @@ namespace GeneXus.Messaging.Queue { + /// + /// Implementation of AzureQueue.MessageQueueProvider external object. + /// + /// public class AzureMessageQueueProvider { - private const string AZUREQUEUE = "AZUREQUEUE"; - public SimpleMessageQueue Connect(string queueName, string queueURL, out GXBaseCollection errorMessages, out bool success) + public SimpleMessageQueue Connect(string queueName, string connectionString, out GXBaseCollection errorMessages, out bool success) { MessageQueueProvider messageQueueProvider = new MessageQueueProvider(); - GXProperties properties = new GXProperties(); - properties.Add("QUEUE_AZUREQUEUE_QUEUENAME", queueName); - properties.Add("QUEUE_AZUREQUEUE_CONNECTIONSTRING", queueURL); - SimpleMessageQueue simpleMessageQueue = messageQueueProvider.Connect(AZUREQUEUE, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); + GXProperties properties = new GXProperties + { + { PropertyConstants.QUEUE_AZUREQUEUE_QUEUENAME, queueName }, + { PropertyConstants.QUEUE_AZUREQUEUE_CONNECTIONSTRING, connectionString }, + { PropertyConstants.AUTHENTICATION_METHOD, AuthenticationMethod.Password.ToString()} + }; + SimpleMessageQueue simpleMessageQueue = messageQueueProvider.Connect(PropertyConstants.AZURE_QUEUE_PROVIDERTYPENAME, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); + errorMessages = errorMessagesConnect; + success = successConnect; + return simpleMessageQueue; + } + public SimpleMessageQueue Authenticate(string queueURI, out GXBaseCollection errorMessages, out bool success) + { + MessageQueueProvider messageQueueProvider = new MessageQueueProvider(); + GXProperties properties = new GXProperties + { + { PropertyConstants.QUEUE_AZUREQUEUE_QUEUEURI, queueURI }, + { PropertyConstants.AUTHENTICATION_METHOD, AuthenticationMethod.ActiveDirectory.ToString()} + }; + SimpleMessageQueue simpleMessageQueue = messageQueueProvider.Connect(PropertyConstants.AZURE_QUEUE_PROVIDERTYPENAME, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); errorMessages = errorMessagesConnect; success = successConnect; return simpleMessageQueue; diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/AzureQueue.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/AzureQueue.cs index 6953c8080..ac8fb4c69 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/AzureQueue.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/AzureQueue.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using Azure.Identity; using Azure.Storage.Queues; using Azure.Storage.Queues.Models; using GeneXus.Messaging.Common; @@ -11,18 +12,17 @@ namespace GeneXus.Messaging.Queue { public class AzureQueue : QueueBase, IQueue { + public static string Name = "AZUREQUEUE"; - public static String Name = "AZUREQUEUE"; - const string QUEUE_NAME = "QUEUENAME"; - const string QUEUE_CONNECTION_STRING = "CONNECTIONSTRING"; + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); QueueClient _queueClient { get; set; } private string _queueName { get; set; } private string _connectionString { get; set; } + private string _queueURI { get; set; } public AzureQueue() : this(null) - { - } + {} public AzureQueue(GXService providerService) : base(providerService) { @@ -31,16 +31,28 @@ public AzureQueue(GXService providerService) : base(providerService) private void Initialize(GXService providerService) { - ServiceSettings serviceSettings = new("QUEUE", Name, providerService); - _queueName = serviceSettings.GetEncryptedPropertyValue(QUEUE_NAME); - _connectionString = serviceSettings.GetEncryptedPropertyValue(QUEUE_CONNECTION_STRING); + ServiceSettings serviceSettings = new(PropertyConstants.QUEUE_SERVICE_NAME, Name, providerService); + _queueName = serviceSettings.GetEncryptedPropertyValue(PropertyConstants.QUEUENAME); + _connectionString = serviceSettings.GetEncryptedPropertyValue(PropertyConstants.CONNECTIONSTRING); + _queueURI = serviceSettings.GetEncryptedPropertyValue(PropertyConstants.QUEUEURI); + string authenticationMethod = serviceSettings.GetPropertiesValue(PropertyConstants.AUTHENTICATION_METHOD); QueueClientOptions queueClientOptions = new QueueClientOptions() { MessageEncoding = QueueMessageEncoding.Base64 }; - _queueClient = new QueueClient(_connectionString, _queueName, queueClientOptions); + if (authenticationMethod.Equals(AuthenticationMethod.ActiveDirectory.ToString())) + { + ChainedTokenCredential credential = new ChainedTokenCredential(new ManagedIdentityCredential(), new ManagedIdentityCredential(Environment.GetEnvironmentVariable("AZURE_CLIENT_ID")), new EnvironmentCredential(), new AzureCliCredential()); + _queueClient = new QueueClient(new Uri(_queueURI), credential,queueClientOptions); + GXLogging.Debug(logger, "Authenticate to Azure Storage Queue using Active Directory authentication."); + } + else + { + _queueClient = new QueueClient(_connectionString, _queueName, queueClientOptions); + GXLogging.Debug(logger, "Authenticate to Azure Storage Queue using Access Keys."); + } } QueueClient QueueClient @@ -59,11 +71,6 @@ public AzureQueue(string connectionString, string queueName) _connectionString = connectionString; } - //public AzureQueue(Uri uri, TokenCredential tokenCredential) - //{ - //_queueClient = new QueueClient(uri, tokenCredential); - //} - public bool GetMessageFromException(Exception ex, SdtMessages_Message msg) { try diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj index a9fc132d3..7ae7307f7 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj @@ -1,13 +1,16 @@ - net6.0 + net6.0;net8.0 GeneXus.Azure.Queue Azure Queue Messaging - + + + + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/AzureServiceBus.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/AzureServiceBus.cs index e00d3c3c4..7981f96db 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/AzureServiceBus.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/AzureServiceBus.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Runtime.Serialization; using System.Threading.Tasks; +using Azure.Identity; using Azure.Messaging.ServiceBus; using GeneXus.Messaging.Common; using GeneXus.Services; @@ -15,12 +16,14 @@ public class AzureServiceBus : MessageBrokerBase, IMessageBroker { private const int MAX_MESSAGES_DEFAULT = 10; private const short LOCK_DURATION = 5; - public static String Name = "AZURESB"; + public static string Name = "AZURESB"; + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); private ConcurrentDictionary> m_messages = new ConcurrentDictionary>(); ServiceBusClient _serviceBusClient { get; set; } private string _queueOrTopicName { get; set; } private string _connectionString { get; set; } + private string _fullyqualifiedNamespace { get; set; } private string _subscriptionName { get; set; } private ServiceBusSender _sender { get; set; } private ServiceBusReceiver _receiver { get; set; } @@ -41,15 +44,17 @@ private void Initialize(GXService providerService) _queueOrTopicName = serviceSettings.GetEncryptedPropertyValue(PropertyConstants.QUEUE_NAME); _connectionString = serviceSettings.GetEncryptedPropertyValue(PropertyConstants.QUEUE_CONNECTION_STRING); _subscriptionName = serviceSettings.GetEncryptedPropertyValue(PropertyConstants.TOPIC_SUBSCRIPTION); + _fullyqualifiedNamespace = serviceSettings.GetEncryptedPropertyValue(PropertyConstants.FULLYQUALIFIEDNAMESPACE); + string authenticationMethod = serviceSettings.GetPropertiesValue(PropertyConstants.AUTHENTICATION_METHOD); - string sessionEnabled = serviceSettings.GetEncryptedOptPropertyValue(PropertyConstants.SESSION_ENABLED); + string sessionEnabled = serviceSettings.GetPropertiesValue(PropertyConstants.SESSION_ENABLED); if (!string.IsNullOrEmpty(sessionEnabled)) _sessionEnabled = Convert.ToBoolean(sessionEnabled); else _sessionEnabled = false; - string senderIdentifier = serviceSettings.GetEncryptedOptPropertyValue(PropertyConstants.SENDER_IDENTIFIER); + string senderIdentifier = serviceSettings.GetPropertiesValue(PropertyConstants.SENDER_IDENTIFIER); ServiceBusSenderOptions serviceBusSenderOptions = new ServiceBusSenderOptions(); if (!string.IsNullOrEmpty(senderIdentifier)) @@ -58,7 +63,18 @@ private void Initialize(GXService providerService) //TO DO Consider connection options here //https://docs.microsoft.com/en-us/javascript/api/@azure/service-bus/servicebusclientoptions?view=azure-node-latest#@azure-service-bus-servicebusclientoptions-websocketoptions - _serviceBusClient = new ServiceBusClient(_connectionString); + if (authenticationMethod.Equals(AuthenticationMethod.ActiveDirectory.ToString())) + { + ChainedTokenCredential credential = new ChainedTokenCredential(new ManagedIdentityCredential(), new ManagedIdentityCredential(Environment.GetEnvironmentVariable("AZURE_CLIENT_ID")), new EnvironmentCredential(), new AzureCliCredential()); + _serviceBusClient = new ServiceBusClient(_fullyqualifiedNamespace, credential); + GXLogging.Debug(logger, "Authenticate to Azure Service Bus using Active Directory authentication."); + } + else + { + _serviceBusClient = new ServiceBusClient(_connectionString); + GXLogging.Debug(logger, "Authenticate to Azure Service Bus using SAS authentication."); + } + if (_serviceBusClient != null) { _sender = _serviceBusClient.CreateSender(_queueOrTopicName, serviceBusSenderOptions); @@ -66,9 +82,9 @@ private void Initialize(GXService providerService) { _serviceBusReceiverOptions = new ServiceBusReceiverOptions(); - string receiveMode = serviceSettings.GetEncryptedOptPropertyValue(PropertyConstants.RECEIVE_MODE); - string prefetchCount = serviceSettings.GetEncryptedOptPropertyValue(PropertyConstants.PREFETCH_COUNT); - string receiverIdentifier = serviceSettings.GetEncryptedOptPropertyValue(PropertyConstants.RECEIVER_IDENTIFIER); + string receiveMode = serviceSettings.GetPropertiesValue(PropertyConstants.RECEIVE_MODE); + string prefetchCount = serviceSettings.GetPropertiesValue(PropertyConstants.PREFETCH_COUNT); + string receiverIdentifier = serviceSettings.GetPropertiesValue(PropertyConstants.RECEIVER_IDENTIFIER); if (!string.IsNullOrEmpty(receiveMode)) _serviceBusReceiverOptions.ReceiveMode = (ServiceBusReceiveMode)Convert.ToInt16(receiveMode); diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj index ac8137a4a..049e25bef 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj @@ -1,13 +1,16 @@ - net6.0 + net6.0;net8.0 GeneXus.Azure.ServiceBus Azure ServiceBus Messaging - + + + + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/ServiceBusMessageBrokerProvider.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/ServiceBusMessageBrokerProvider.cs index 036c89652..e1f0ab226 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/ServiceBusMessageBrokerProvider.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/ServiceBusMessageBrokerProvider.cs @@ -5,14 +5,31 @@ namespace GeneXus.Messaging.GXAzureServiceBus { + /// + /// Implementation of AzureServiceBus.MessageBrokerProvider external object. + /// public class ServiceBusMessageBrokerProvider { - public MessageQueue Connect(string queueName, string connectionString, out GXBaseCollection errorMessages, out bool success) + + #region Azure Active Directory Authentication + + public MessageQueue Authenticate(string queueName, string fullyQualifiedNamespace, bool sessionEnabled, GxUserType receiverOptions, string senderIdentifier, out GXBaseCollection errorMessages, out bool success) { MessageBrokerProvider messageBrokerProvider = new MessageBrokerProvider(); - GXProperties properties = new GXProperties(); - properties.Add(PropertyConstants.MESSAGEBROKER_AZURESB_QUEUENAME, queueName); - properties.Add(PropertyConstants.MESSAGEBROKER_AZURESB_CONNECTIONSTRING, connectionString); + ReceiverOptions options = TransformGXUserTypeToReceiverOptions(receiverOptions); + + GXProperties properties = new GXProperties + { + { PropertyConstants.MESSAGEBROKER_AZURESB_QUEUENAME, queueName }, + { PropertyConstants.MESSAGEBROKER_AZURESB_FULLYQUALIFIEDNAMESPACE, fullyQualifiedNamespace }, + { PropertyConstants.SESSION_ENABLED, sessionEnabled.ToString() }, + { PropertyConstants.RECEIVE_MODE, options.ReceiveMode.ToString() }, + { PropertyConstants.PREFETCH_COUNT, options.PrefetchCount.ToString() }, + { PropertyConstants.RECEIVER_IDENTIFIER, options.Identifier }, + { PropertyConstants.RECEIVER_SESSIONID, options.SessionId }, + { PropertyConstants.SENDER_IDENTIFIER, senderIdentifier }, + { PropertyConstants.AUTHENTICATION_METHOD, AuthenticationMethod.ActiveDirectory.ToString()} + }; MessageQueue messageQueue = messageBrokerProvider.Connect(PropertyConstants.AZURESERVICEBUS, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); errorMessages = errorMessagesConnect; @@ -20,13 +37,90 @@ public MessageQueue Connect(string queueName, string connectionString, out GXBas return messageQueue; } - public MessageQueue Connect(string topicName, string subcriptionName, string connectionString, out GXBaseCollection errorMessages, out bool success) + public MessageQueue Authenticate(string topicName, string subcriptionName, string fullyQualifiedNamespace, bool sessionEnabled, GxUserType receiverOptions, string senderIdentifier, out GXBaseCollection errorMessages, out bool success) { MessageBrokerProvider messageBrokerProvider = new MessageBrokerProvider(); GXProperties properties = new GXProperties(); + ReceiverOptions options = TransformGXUserTypeToReceiverOptions(receiverOptions); + properties.Add(PropertyConstants.MESSAGEBROKER_AZURESB_QUEUENAME, topicName); properties.Add(PropertyConstants.MESSAGEBROKER_AZURESB_SUBSCRIPTION_NAME, subcriptionName); - properties.Add(PropertyConstants.MESSAGEBROKER_AZURESB_CONNECTIONSTRING, connectionString); + properties.Add(PropertyConstants.MESSAGEBROKER_AZURESB_FULLYQUALIFIEDNAMESPACE, fullyQualifiedNamespace); + properties.Add(PropertyConstants.SESSION_ENABLED, sessionEnabled.ToString()); + properties.Add(PropertyConstants.RECEIVE_MODE, options.ReceiveMode.ToString()); + properties.Add(PropertyConstants.PREFETCH_COUNT, options.PrefetchCount.ToString()); + properties.Add(PropertyConstants.RECEIVER_IDENTIFIER, options.Identifier); + properties.Add(PropertyConstants.RECEIVER_SESSIONID, options.SessionId); + properties.Add(PropertyConstants.SENDER_IDENTIFIER, senderIdentifier); + properties.Add(PropertyConstants.AUTHENTICATION_METHOD, AuthenticationMethod.ActiveDirectory.ToString()); + + MessageQueue messageQueue = messageBrokerProvider.Connect(PropertyConstants.AZURESERVICEBUS, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); + errorMessages = errorMessagesConnect; + success = successConnect; + return messageQueue; + } + + public MessageQueue Authenticate(string queueName, string fullyQualifiedNamespace, out GXBaseCollection errorMessages, out bool success) + { + MessageBrokerProvider messageBrokerProvider = new MessageBrokerProvider(); + GXProperties properties = new GXProperties + { + { PropertyConstants.MESSAGEBROKER_AZURESB_QUEUENAME, queueName }, + { PropertyConstants.MESSAGEBROKER_AZURESB_FULLYQUALIFIEDNAMESPACE, fullyQualifiedNamespace }, + { PropertyConstants.AUTHENTICATION_METHOD, AuthenticationMethod.ActiveDirectory.ToString()} + }; + + MessageQueue messageQueue = messageBrokerProvider.Connect(PropertyConstants.AZURESERVICEBUS, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); + errorMessages = errorMessagesConnect; + success = successConnect; + return messageQueue; + } + public MessageQueue Authenticate(string topicName, string subcriptionName, string fullyQualifiedNamespace, out GXBaseCollection errorMessages, out bool success) + { + MessageBrokerProvider messageBrokerProvider = new MessageBrokerProvider(); + GXProperties properties = new GXProperties + { + { PropertyConstants.MESSAGEBROKER_AZURESB_QUEUENAME, topicName }, + { PropertyConstants.MESSAGEBROKER_AZURESB_SUBSCRIPTION_NAME, subcriptionName }, + { PropertyConstants.MESSAGEBROKER_AZURESB_FULLYQUALIFIEDNAMESPACE, fullyQualifiedNamespace }, + { PropertyConstants.AUTHENTICATION_METHOD, AuthenticationMethod.ActiveDirectory.ToString()} + }; + + MessageQueue messageQueue = messageBrokerProvider.Connect(PropertyConstants.AZURESERVICEBUS, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); + errorMessages = errorMessagesConnect; + success = successConnect; + return messageQueue; + } + #endregion + + #region Connect using SAS (Shared Access Signatures) + public MessageQueue Connect(string queueName, string connectionString, out GXBaseCollection errorMessages, out bool success) + { + MessageBrokerProvider messageBrokerProvider = new MessageBrokerProvider(); + GXProperties properties = new GXProperties + { + { PropertyConstants.MESSAGEBROKER_AZURESB_QUEUENAME, queueName }, + { PropertyConstants.MESSAGEBROKER_AZURESB_CONNECTIONSTRING, connectionString }, + { PropertyConstants.AUTHENTICATION_METHOD, AuthenticationMethod.Password.ToString()} + + }; + + MessageQueue messageQueue = messageBrokerProvider.Connect(PropertyConstants.AZURESERVICEBUS, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); + errorMessages = errorMessagesConnect; + success = successConnect; + return messageQueue; + } + + public MessageQueue Connect(string topicName, string subcriptionName, string connectionString, out GXBaseCollection errorMessages, out bool success) + { + MessageBrokerProvider messageBrokerProvider = new MessageBrokerProvider(); + GXProperties properties = new GXProperties + { + { PropertyConstants.MESSAGEBROKER_AZURESB_QUEUENAME, topicName }, + { PropertyConstants.MESSAGEBROKER_AZURESB_SUBSCRIPTION_NAME, subcriptionName }, + { PropertyConstants.MESSAGEBROKER_AZURESB_CONNECTIONSTRING, connectionString }, + { PropertyConstants.AUTHENTICATION_METHOD, AuthenticationMethod.Password.ToString()} + }; MessageQueue messageQueue = messageBrokerProvider.Connect(PropertyConstants.AZURESERVICEBUS, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); errorMessages = errorMessagesConnect; @@ -39,15 +133,18 @@ public MessageQueue Connect(string queueName, string connectionString, bool sess MessageBrokerProvider messageBrokerProvider = new MessageBrokerProvider(); ReceiverOptions options = TransformGXUserTypeToReceiverOptions(receiverOptions); - GXProperties properties = new GXProperties(); - properties.Add(PropertyConstants.MESSAGEBROKER_AZURESB_QUEUENAME, queueName); - properties.Add(PropertyConstants.MESSAGEBROKER_AZURESB_CONNECTIONSTRING, connectionString); - properties.Add(PropertyConstants.SESSION_ENABLED, sessionEnabled.ToString()); - properties.Add(PropertyConstants.RECEIVE_MODE, options.ReceiveMode.ToString()); - properties.Add(PropertyConstants.PREFETCH_COUNT, options.PrefetchCount.ToString()); - properties.Add(PropertyConstants.RECEIVER_IDENTIFIER, options.Identifier); - properties.Add(PropertyConstants.RECEIVER_SESSIONID, options.SessionId); - properties.Add(PropertyConstants.SENDER_IDENTIFIER, senderIdentifier); + GXProperties properties = new GXProperties + { + { PropertyConstants.MESSAGEBROKER_AZURESB_QUEUENAME, queueName }, + { PropertyConstants.MESSAGEBROKER_AZURESB_CONNECTIONSTRING, connectionString }, + { PropertyConstants.SESSION_ENABLED, sessionEnabled.ToString() }, + { PropertyConstants.RECEIVE_MODE, options.ReceiveMode.ToString() }, + { PropertyConstants.PREFETCH_COUNT, options.PrefetchCount.ToString() }, + { PropertyConstants.RECEIVER_IDENTIFIER, options.Identifier }, + { PropertyConstants.RECEIVER_SESSIONID, options.SessionId }, + { PropertyConstants.SENDER_IDENTIFIER, senderIdentifier }, + { PropertyConstants.AUTHENTICATION_METHOD, AuthenticationMethod.Password.ToString()} + }; MessageQueue messageQueue = messageBrokerProvider.Connect(PropertyConstants.AZURESERVICEBUS, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); errorMessages = errorMessagesConnect; @@ -70,6 +167,7 @@ public MessageQueue Connect(string topicName, string subcriptionName, string con properties.Add(PropertyConstants.RECEIVER_IDENTIFIER, options.Identifier); properties.Add(PropertyConstants.RECEIVER_SESSIONID, options.SessionId); properties.Add(PropertyConstants.SENDER_IDENTIFIER, senderIdentifier); + properties.Add(PropertyConstants.AUTHENTICATION_METHOD, AuthenticationMethod.Password.ToString()); MessageQueue messageQueue = messageBrokerProvider.Connect(PropertyConstants.AZURESERVICEBUS, properties, out GXBaseCollection errorMessagesConnect, out bool successConnect); errorMessages = errorMessagesConnect; @@ -77,6 +175,8 @@ public MessageQueue Connect(string topicName, string subcriptionName, string con return messageQueue; } + #endregion + #region Transformation methods private ReceiverOptions TransformGXUserTypeToReceiverOptions(GxUserType options) { diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouter.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouter.cs new file mode 100644 index 000000000..56fa53a2a --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouter.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using GeneXus.Utils; + +namespace GeneXus.Messaging.Common +{ + public interface IEventRouter + { + bool SendEvent(GXCloudEvent gxCloudEvent, bool binaryData); + bool SendEvents(IList gxCloudEvents, bool binaryData); + bool SendCustomEvents(string jsonString, bool isBinary); + bool GetMessageFromException(Exception ex, SdtMessages_Message msg); + } + public class GXCloudEvent : GxUserType + { + public string type { get; set; } + public string source { get; set; } + public string data { get; set; } + public string datacontenttype { get; set; } + public string id { get; set; } + public string dataschema { get; set; } + public string subject { get; set; } + public string data_base64 { get; set; } + public DateTime time { get; set; } + } + +} \ No newline at end of file diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouterBase.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouterBase.cs new file mode 100644 index 000000000..fb4d42cf5 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouterBase.cs @@ -0,0 +1,32 @@ +using System; +using GeneXus.Services; + +namespace GeneXus.Messaging.Common +{ + public abstract class EventRouterBase + { + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); + internal GXService service; + public EventRouterBase() + { + } + + public EventRouterBase(GXService s) + { + if (s == null) + { + try + { + s = ServiceFactory.GetGXServices()?.Get(GXServices.EVENTROUTER_SERVICE); + } + catch (Exception) + { + GXLogging.Warn(logger, "EVENTROUTER_SERVICE is not activated"); + } + } + + service = s; + } + public abstract string GetName(); + } +} diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouterProvider.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouterProvider.cs new file mode 100644 index 000000000..a9cf3b0e7 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouterProvider.cs @@ -0,0 +1,112 @@ +using System; +using GeneXus.Attributes; +using GeneXus.Encryption; +using GeneXus.Services; +using GeneXus.Utils; +using GxClasses.Helpers; + +namespace GeneXus.Messaging.Common +{ + [GXApi] + public class EventRouterProvider : EventRouterProviderBase + { + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); + private static GXService providerService; + public EventRouterProvider() { + } + public EventRouterProviderBase Connect(string providerTypeName, GXProperties properties, out GXBaseCollection errorMessages, out bool success) + { + errorMessages = new GXBaseCollection(); + EventRouterProviderBase eventRouter = new EventRouterProviderBase(); + if (string.IsNullOrEmpty(providerTypeName)) + { + GXUtil.ErrorToMessages("GXEventRouter", "Event Router provider cannot be empty", errorMessages); + GXLogging.Error(logger, "(GXEventRouter)Failed to Connect to a Event Router : Provider cannot be empty."); + success = false; + return eventRouter; + } + try + { + if (providerService == null || !string.Equals(providerService.Name, providerTypeName, StringComparison.OrdinalIgnoreCase)) + { + providerService = new GXService(); + providerService.Type = GXServices.EVENTROUTER_SERVICE; + providerService.Name = providerTypeName; + providerService.AllowMultiple = false; + providerService.Properties = new GXProperties(); + } + Preprocess(providerTypeName, properties); + + GxKeyValuePair prop = properties.GetFirst(); + while (!properties.Eof()) + { + providerService.Properties.Set(prop.Key, prop.Value); + prop = properties.GetNext(); + } + + string typeFullName = providerService.ClassName; + GXLogging.Debug(logger, "Loading Event Router provider: " + typeFullName); + Type type = AssemblyLoader.GetType(typeFullName); + eventRouter.eventRouter = (IEventRouter)Activator.CreateInstance(type, new object[] { providerService }); + + } + catch (Exception ex) + { + GXLogging.Error(logger, "(GXEventRouter)Couldn't connect to Event Router provider: " + ExceptionExtensions.GetInnermostException(ex)); + GXUtil.ErrorToMessages("GXEventRouter", ex, errorMessages); + success = false; + return eventRouter; + } + success = true; + return (eventRouter); + } + private static void Preprocess(string name, GXProperties properties) + { + string className; + + switch (name) + { + case Providers.AzureEventGrid: + className = PropertyConstants.AZURE_EG_CLASSNAME; + SetEncryptedProperty(properties, PropertyConstants.EVENTROUTER_AZUREEG_ENDPOINT); + SetEncryptedProperty(properties, PropertyConstants.EVENTROUTER_AZUREEG_ACCESS_KEY); + if (string.IsNullOrEmpty(providerService.ClassName) || !providerService.ClassName.Contains(className)) + { + providerService.ClassName = PropertyConstants.AZURE_EG_PROVIDER_CLASSNAME; + } + break; + default: + throw new SystemException(string.Format("Provider {0} is not supported.", name)); + } + } + private static void SetEncryptedProperty(GXProperties properties, string prop) + { + string value = properties.Get(prop); + if (string.IsNullOrEmpty(value)) + value = string.Empty; + value = CryptoImpl.Encrypt(value); + properties.Set(prop, value); + } + + } + public static class ExceptionExtensions + { + public static string GetInnermostException(Exception e) + { + Exception ex = e; + if (ex != null) + { + while (ex.InnerException != null) + { + ex = ex.InnerException; + } + + } + return ex.Message; + } + } + static class Providers + { + public const string AzureEventGrid = "AZUREEVENTGRID"; + } +} diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouterProviderBase.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouterProviderBase.cs new file mode 100644 index 000000000..25acbc4cf --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/EventRouterProviderBase.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using GeneXus.Services; +using GeneXus.Utils; +using GxClasses.Helpers; + +namespace GeneXus.Messaging.Common +{ + public class EventRouterProviderBase + { + internal IEventRouter eventRouter = null; + public static Assembly assembly; + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); + private const string MODULE_DLL = @"GeneXusEventMessaging"; + + public EventRouterProviderBase() + { + } + public EventRouterProviderBase(EventRouterProviderBase other) + { + eventRouter = other.eventRouter; + } + void ValidQueue() + { + if (eventRouter == null) + { + GXLogging.Error(logger, "Event Router was not instantiated."); + throw new Exception("Event Router was not instantiated."); + } + } + private static void LoadAssemblyIfRequired() + { + if (assembly == null) + { + assembly = AssemblyLoader.LoadAssembly(new AssemblyName(MODULE_DLL)); + } + } + + public bool SendEvent(GxUserType evt, bool binaryData, out GXBaseCollection errorMessages) + { + bool success = false; + errorMessages = new GXBaseCollection(); + try + { + GXCloudEvent gxCloudEvent = ToGXCloudEvent(evt); + LoadAssemblyIfRequired(); + try + { + ValidQueue(); + if (eventRouter != null) + return(eventRouter.SendEvent(gxCloudEvent, binaryData)); + } + catch (Exception ex) + { + EventRouterErrorMessagesSetup(ex, out errorMessages); + success = false; + GXLogging.Error(logger, ex); + } + } + catch (Exception ex) + { + success = false; + GXLogging.Error(logger,ex); + throw ex; + } + return success; + } + + public bool SendCustomEvents(string evts, bool isBinary, out GXBaseCollection errorMessages) + { + errorMessages = new GXBaseCollection(); + bool success; + try + { + ValidQueue(); + success = eventRouter.SendCustomEvents(evts, isBinary); + } + catch (Exception ex) + { + EventRouterErrorMessagesSetup(ex, out errorMessages); + success = false; + GXLogging.Error(logger, ex); + } + return success; + } + + public bool SendEvents(IList evts, bool binaryData, out GXBaseCollection errorMessages) + { + errorMessages = new GXBaseCollection(); + bool success = false; + LoadAssemblyIfRequired(); + try + { + IList gxCloudEvents = new List(); + foreach (GxUserType e in evts) + { + if (ToGXCloudEvent(e) is GXCloudEvent gxCloudEvent) + gxCloudEvents.Add(gxCloudEvent); + } + try + { + ValidQueue(); + success = eventRouter.SendEvents(gxCloudEvents, binaryData); + } + catch (Exception ex) + { + EventRouterErrorMessagesSetup(ex, out errorMessages); + success = false; + GXLogging.Error(logger, ex); + } + } + catch (Exception ex) + { + GXLogging.Error(logger, ex); + throw ex; + } + return success; + } + + #region Transform operations + protected void EventRouterErrorMessagesSetup(Exception ex, out GXBaseCollection errorMessages) + { + errorMessages = new GXBaseCollection(); + bool foundGeneralException = false; + if (ex != null) + { + SdtMessages_Message msg = new SdtMessages_Message(); + if (eventRouter != null && ex.InnerException != null) + { + do + { + if (eventRouter.GetMessageFromException(ex.InnerException, msg)) + { + msg.gxTpr_Type = 1; + errorMessages.Add(msg); + } + else + { + foundGeneralException = true; + break; + } + ex = ex.InnerException; + } + while (ex.InnerException != null); + if (foundGeneralException) + GXUtil.ErrorToMessages("GXEventRouter", ex, errorMessages); + } + else + { + GXUtil.ErrorToMessages("GXEventRouter", ex, errorMessages); + } + } + } + private GXCloudEvent ToGXCloudEvent(GxUserType evt) + { + if (evt != null) + { + GXCloudEvent gxCloudEvent = new GXCloudEvent(); + gxCloudEvent.type = evt.GetPropertyValue("Type"); + gxCloudEvent.source = evt.GetPropertyValue("Source"); + gxCloudEvent.data = evt.GetPropertyValue("Data"); + gxCloudEvent.datacontenttype = evt.GetPropertyValue("Datacontenttype"); + gxCloudEvent.id = evt.GetPropertyValue("Id"); + gxCloudEvent.subject = evt.GetPropertyValue("Subject"); + gxCloudEvent.dataschema = evt.GetPropertyValue("Dataschema"); + gxCloudEvent.data_base64 = evt.GetPropertyValue("Data_base64"); + gxCloudEvent.time = evt.GetPropertyValue("Time"); + return gxCloudEvent; + } + return null; + } + #endregion + + } + internal class ServiceFactory + { + private static IEventRouter eventRouter; + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + + public static GXServices GetGXServices() + { + return GXServices.Instance; + } + + public static IEventRouter GetEventRouter() + { + if (eventRouter == null) + { + eventRouter = GetRouterImpl(GXServices.EVENTROUTER_SERVICE); + } + return eventRouter; + } + + public static IEventRouter GetRouterImpl(string service) + { + IEventRouter eventRouterImpl = null; + if (GetGXServices() != null) + { + GXService providerService = GetGXServices()?.Get(service); + if (providerService != null) + { + try + { + string typeFullName = providerService.ClassName; + GXLogging.Debug(log, "Loading Event Router settings:", typeFullName); + Type type = AssemblyLoader.GetType(typeFullName); + eventRouterImpl = (IEventRouter)Activator.CreateInstance(type); + } + catch (Exception e) + { + GXLogging.Error(log, "Couldn't connect to the Event Router.", e.Message, e); + throw e; + } + } + } + return eventRouterImpl; + } + } +} + + + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/GXEventRouter.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/GXEventRouter.csproj new file mode 100644 index 000000000..cdf1f3ab0 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/GXEventRouter.csproj @@ -0,0 +1,21 @@ + + + + net6.0;net8.0 + GeneXus.Message.EventRouter + Event Bus Messaging Router + TRACE;DEBUG;NETCORE + + + + + + + + + + + + + + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/PropertyConstants.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/PropertyConstants.cs new file mode 100644 index 000000000..2aed7d804 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/PropertyConstants.cs @@ -0,0 +1,17 @@ +namespace GeneXus.Messaging.Common +{ + public static class PropertyConstants + { + //Azure Event Grid + + internal const string AZURE_EG_CLASSNAME = "GeneXus.Messaging.GXAzureEventGrid.AzureEventGrid"; + internal const string AZURE_EG_PROVIDER_CLASSNAME = "GeneXus.Messaging.GXAzureEventGrid.AzureEventGrid, GXAzureEventGrid, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; + public const string EVENT_ROUTER = "EVENTROUTER"; + public const string URI_ENDPOINT = "ENDPOINT"; + public const string ACCESS_KEY = "ACCESS_KEY"; + public const string AZUREEVENTGRID = "AZUREEVENTGRID"; + public const string EVENTROUTER_AZUREEG_ENDPOINT = "EVENTROUTER_AZUREEVENTGRID_ENDPOINT"; + public const string EVENTROUTER_AZUREEG_ACCESS_KEY = "EVENTROUTER_AZUREEVENTGRID_ACCESS_KEY"; + + } +} diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj index a1fc0121d..d40bc0234 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj @@ -1,12 +1,17 @@ - net6.0 + net6.0;net8.0 GeneXus.Message.MessageBroker Broker Messaging TRACE;DEBUG;NETCORE + + + + + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageBrokerBase.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageBrokerBase.cs index 031837384..de6c1bece 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageBrokerBase.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageBrokerBase.cs @@ -1,12 +1,11 @@ using System; using GeneXus.Services; -using log4net; namespace GeneXus.Messaging.Common { public abstract class MessageBrokerBase { - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(MessageBrokerBase)); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); internal GXService service; public MessageBrokerBase() { @@ -28,6 +27,6 @@ public MessageBrokerBase(GXService s) service = s; } - public abstract String GetName(); + public abstract string GetName(); } } diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageBrokerProvider.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageBrokerProvider.cs index c3acfb7d8..69179faa5 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageBrokerProvider.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageBrokerProvider.cs @@ -4,14 +4,13 @@ using GeneXus.Services; using GeneXus.Utils; using GxClasses.Helpers; -using log4net; namespace GeneXus.Messaging.Common { [GXApi] public class MessageBrokerProvider : MessageQueue { - static readonly ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); private static GXService providerService; public MessageBrokerProvider() { @@ -64,7 +63,7 @@ public MessageQueue Connect(string providerTypeName, GXProperties properties, ou success = true; return (messageQueue); } - private static void Preprocess(String name, GXProperties properties) + private static void Preprocess(string name, GXProperties properties) { string className; @@ -75,6 +74,7 @@ private static void Preprocess(String name, GXProperties properties) SetEncryptedProperty(properties, PropertyConstants.MESSAGEBROKER_AZURESB_QUEUENAME); SetEncryptedProperty(properties, PropertyConstants.MESSAGEBROKER_AZURESB_SUBSCRIPTION_NAME); SetEncryptedProperty(properties, PropertyConstants.MESSAGEBROKER_AZURESB_CONNECTIONSTRING); + SetEncryptedProperty(properties, PropertyConstants.MESSAGEBROKER_AZURESB_FULLYQUALIFIEDNAMESPACE); if (string.IsNullOrEmpty(providerService.ClassName) || !providerService.ClassName.Contains(className)) { providerService.ClassName = PropertyConstants.AZURE_SB_PROVIDER_CLASSNAME; @@ -84,11 +84,11 @@ private static void Preprocess(String name, GXProperties properties) throw new SystemException(string.Format("Provider {0} is not supported.", name)); } } - private static void SetEncryptedProperty(GXProperties properties, String prop) + private static void SetEncryptedProperty(GXProperties properties, string prop) { - String value = properties.Get(prop); + string value = properties.Get(prop); if (string.IsNullOrEmpty(value)) - value = String.Empty; + value = string.Empty; value = CryptoImpl.Encrypt(value); properties.Set(prop, value); } diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageQueue.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageQueue.cs index 344626aa0..13f6c7702 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageQueue.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/MessageQueue.cs @@ -3,11 +3,9 @@ using System.Collections.Generic; using System.IO; using System.Reflection; -using System.Text; using GeneXus.Services; using GeneXus.Utils; using GxClasses.Helpers; -using log4net; namespace GeneXus.Messaging.Common { @@ -15,7 +13,7 @@ public class MessageQueue { internal IMessageBroker messageBroker = null; public static Assembly assembly; - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(MessageQueue)); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); private const string SDT_MESSAGE_CLASS_NAME = @"SdtMessage"; private const string SDT_MESSAGEPROPERTY_CLASS_NAME = @"SdtMessageProperty"; private const string NAMESPACE = @"GeneXus.Programs.genexusmessagingmessagebroker"; @@ -257,12 +255,12 @@ protected void QueueErrorMessagesSetup(Exception ex, out GXBaseCollection(); bool foundGeneralException = false; - if (errorMessages != null && ex != null) + if (ex != null) { SdtMessages_Message msg = new SdtMessages_Message(); - if (messageBroker != null) - { - while (ex.InnerException != null) + if (messageBroker != null && ex.InnerException != null) + { + do { if (messageBroker.GetMessageFromException(ex.InnerException, msg)) { @@ -276,6 +274,7 @@ protected void QueueErrorMessagesSetup(Exception ex, out GXBaseCollection(); public static GXServices GetGXServices() { diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/PropertyConstants.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/PropertyConstants.cs index 9c5ed2930..7a8d6c09e 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/PropertyConstants.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/PropertyConstants.cs @@ -16,10 +16,19 @@ public static class PropertyConstants public const string MESSAGEBROKER_AZURESB_TOPICNAME = "MESSAGEBROKER_AZURESB_TOPICNAME"; public const string MESSAGEBROKER_AZURESB_SUBSCRIPTION_NAME = "MESSAGEBROKER_AZURESB_SUBSCRIPTION"; public const string MESSAGEBROKER_AZURESB_CONNECTIONSTRING = "MESSAGEBROKER_AZURESB_QUEUECONNECTION"; + public const string MESSAGEBROKER_AZURESB_FULLYQUALIFIEDNAMESPACE = "MESSAGEBROKER_AZURESB_FULLYQUALIFIEDNAMESPACE"; public const string QUEUE_NAME = "QUEUENAME"; public const string QUEUE_CONNECTION_STRING = "QUEUECONNECTION"; + public const string FULLYQUALIFIEDNAMESPACE = "FULLYQUALIFIEDNAMESPACE"; public const string TOPIC_SUBSCRIPTION = "SUBSCRIPTION"; public const string MESSAGE_BROKER = "MESSAGEBROKER"; public const string SESSION_ENABLED = "SESSION_ENABLED"; + + public const string AUTHENTICATION_METHOD = "AUTHENTICATION_METHOD"; } + public enum AuthenticationMethod + { + ActiveDirectory, + Password + } } diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/ServiceSettings.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/ServiceSettings.cs index 2d920710c..f718b6c4e 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/ServiceSettings.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/ServiceSettings.cs @@ -1,18 +1,12 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using GeneXus.Encryption; -using GeneXus.Messaging.Common; using GeneXus.Services; -using log4net; namespace GeneXus.Messaging.Common { public class ServiceSettings { - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(ServiceSettings)); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); internal GXService service; public string serviceNameResolver { get; } @@ -22,33 +16,32 @@ public ServiceSettings(string serviceNameResolver, string name, GXService gXServ { this.serviceNameResolver = serviceNameResolver; this.name = name; - this.service = gXService; + service = gXService; } - public string GetEncryptedOptPropertyValue(string propertyName, string alternativePropertyName = null) + public string GetPropertiesValue(string propertyName) { - String value = GetEncryptedPropertyValue(propertyName, alternativePropertyName, null); - return value; + return service.Properties.Get(propertyName); } public string GetEncryptedPropertyValue(string propertyName, string alternativePropertyName = null) { - String value = GetEncryptedPropertyValue(propertyName, alternativePropertyName, null); + string value = GetEncryptedPropertyValue(propertyName, alternativePropertyName, null); if (value == null) { - String errorMessage = String.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); - logger.Fatal(errorMessage); + string errorMessage = string.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); + GXLogging.Error(logger, errorMessage); throw new Exception(errorMessage); } return value; } public string GetEncryptedPropertyValue(string propertyName, string alternativePropertyName, string defaultValue) { - String value = GetPropertyValue(propertyName, alternativePropertyName, defaultValue); - if (!String.IsNullOrEmpty(value)) + string value = GetPropertyValue(propertyName, alternativePropertyName, defaultValue); + if (!string.IsNullOrEmpty(value)) { try { - string ret = String.Empty; + string ret = string.Empty; if (CryptoImpl.Decrypt(ref ret, value)) { value = ret; @@ -56,7 +49,7 @@ public string GetEncryptedPropertyValue(string propertyName, string alternativeP } catch (Exception) { - logger.Warn($"Could not decrypt property name: {ResolvePropertyName(propertyName)}"); + GXLogging.Warn(logger, $"Could not decrypt property name: {ResolvePropertyName(propertyName)}"); } } return value; @@ -64,11 +57,11 @@ public string GetEncryptedPropertyValue(string propertyName, string alternativeP internal string GetPropertyValue(string propertyName, string alternativePropertyName = null) { - String value = GetPropertyValue(propertyName, alternativePropertyName, null); + string value = GetPropertyValue(propertyName, alternativePropertyName, null); if (value == null) { - String errorMessage = String.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); - logger.Fatal(errorMessage); + string errorMessage = string.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); + GXLogging.Error(logger,errorMessage); throw new Exception(errorMessage); } return value; @@ -76,7 +69,7 @@ internal string GetPropertyValue(string propertyName, string alternativeProperty internal string GetPropertyValue(string propertyName, string alternativePropertyName, string defaultValue) { - String value = null; + string value = null; value = string.IsNullOrEmpty(value) ? GetPropertyValueImpl(ResolvePropertyName(propertyName)) : value; value = string.IsNullOrEmpty(value) ? GetPropertyValueImpl(propertyName) : value; value = string.IsNullOrEmpty(value) ? GetPropertyValueImpl(alternativePropertyName) : value; @@ -86,7 +79,7 @@ internal string GetPropertyValue(string propertyName, string alternativeProperty internal string GetPropertyValueImpl(string propertyName) { - String value = null; + string value = null; if (!string.IsNullOrEmpty(propertyName)) { value = Environment.GetEnvironmentVariable(propertyName); diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj index b41e066ac..bce3bd249 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj @@ -1,10 +1,18 @@ - net6.0 + net6.0;net8.0 TRACE;DEBUG;NETCORE GeneXus.Message.Queue + + + + + + + + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/MessageQueueProvider.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/MessageQueueProvider.cs index 90d951738..cf6077c65 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/MessageQueueProvider.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/MessageQueueProvider.cs @@ -4,18 +4,15 @@ using GeneXus.Services; using GeneXus.Utils; using GxClasses.Helpers; -using log4net; namespace GeneXus.Messaging.Common { [GXApi] public class MessageQueueProvider : SimpleMessageQueue { - static readonly ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); private static GXService providerService; - public MessageQueueProvider() - { - + public MessageQueueProvider(){ } public SimpleMessageQueue Connect(string providerTypeName, GXProperties properties, out GXBaseCollection errorMessages, out bool success) @@ -69,30 +66,30 @@ public SimpleMessageQueue Connect(string providerTypeName, GXProperties properti return (simpleMessageQueue); } - private static void Preprocess(String name, GXProperties properties) + private static void Preprocess(string name, GXProperties properties) { string className; - switch (name) { case "AZUREQUEUE": - className = "GeneXus.Messaging.Queue.AzureQueue"; - SetEncryptedProperty(properties, "QUEUE_AZUREQUEUE_QUEUENAME"); - SetEncryptedProperty(properties, "QUEUE_AZUREQUEUE_CONNECTIONSTRING"); + className = PropertyConstants.AZURE_QUEUE_CLASSNAME; + SetEncryptedProperty(properties, PropertyConstants.QUEUENAME); + SetEncryptedProperty(properties, PropertyConstants.CONNECTIONSTRING); + SetEncryptedProperty(properties, PropertyConstants.QUEUEURI); if (string.IsNullOrEmpty(providerService.ClassName) || !providerService.ClassName.Contains(className)) { - providerService.ClassName = "GeneXus.Messaging.Queue.AzureQueue, GXAzureQueue, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; + providerService.ClassName = PropertyConstants.AZURE_QUEUE_PROVIDER_CLASSNAME; } break; case "AWS_SQS": className = "GeneXus.Messaging.Queue.AWSQueue"; - SetEncryptedProperty(properties, "QUEUE_AWSSQS_QUEUE_URL"); - SetEncryptedProperty(properties, "QUEUE_AWSSQS_ACCESS_KEY"); - SetEncryptedProperty(properties, "QUEUE_AWSSQS_SECRET_KEY"); - SetEncryptedProperty(properties, "QUEUE_AWSSQS_REGION"); + SetEncryptedProperty(properties, PropertyConstants.QUEUE_AWSSQS_QUEUE_URL); + SetEncryptedProperty(properties, PropertyConstants.QUEUE_AWSSQS_ACCESS_KEY); + SetEncryptedProperty(properties, PropertyConstants.QUEUE_AWSSQS_SECRET_KEY); + SetEncryptedProperty(properties, PropertyConstants.QUEUE_AWSSQS_REGION); if (string.IsNullOrEmpty(providerService.ClassName) || !providerService.ClassName.Contains(className)) { - providerService.ClassName = "GeneXus.Messaging.Queue.AWSQueue, GXAmazonSQS, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; + providerService.ClassName = PropertyConstants.AWSSQS_QUEUE_PROVIDERTYPENAME; } break; @@ -100,11 +97,11 @@ private static void Preprocess(String name, GXProperties properties) throw new SystemException(string.Format("Provider {0} is not supported.", name)); } } - private static void SetEncryptedProperty(GXProperties properties, String prop) + private static void SetEncryptedProperty(GXProperties properties, string prop) { - String value = properties.Get(prop); + string value = properties.Get(prop); if (string.IsNullOrEmpty(value)) - value = String.Empty; + value = string.Empty; value = CryptoImpl.Encrypt(value); properties.Set(prop, value); } diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/PropertyConstants.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/PropertyConstants.cs new file mode 100644 index 000000000..32276b68c --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/PropertyConstants.cs @@ -0,0 +1,35 @@ +namespace GeneXus.Messaging.Common +{ + public static class PropertyConstants + { + //Azure Storage Queue + internal const string AZURE_QUEUE_CLASSNAME = "GeneXus.Messaging.Queue.AzureQueue"; + internal const string AZURE_QUEUE_PROVIDER_CLASSNAME = "GeneXus.Messaging.Queue.AzureQueue, GXAzureQueue, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; + public const string AZURE_QUEUE_PROVIDERTYPENAME = "AZUREQUEUE"; + + public const string QUEUE_SERVICE_NAME = "QUEUE"; + + public const string QUEUE_AZUREQUEUE_QUEUENAME = "QUEUE_AZUREQUEUE_QUEUENAME"; + public const string QUEUE_AZUREQUEUE_CONNECTIONSTRING = "QUEUE_AZUREQUEUE_CONNECTIONSTRING"; + public const string QUEUE_AZUREQUEUE_QUEUEURI = "QUEUE_AZUREQUEUE_QUEUEURI"; + + public const string QUEUENAME = "QUEUENAME"; + public const string CONNECTIONSTRING = "CONNECTIONSTRING"; + public const string QUEUEURI = "QUEUEURI"; + public const string AUTHENTICATION_METHOD = "AUTHENTICATION_METHOD"; + + //AWS SQS + + public const string AWSSQS_QUEUE_PROVIDERTYPENAME = "GeneXus.Messaging.Queue.AWSQueue, GXAmazonSQS, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; + public const string QUEUE_AWSSQS_QUEUE_URL = "QUEUE_AWSSQS_QUEUE_URL"; + public const string QUEUE_AWSSQS_ACCESS_KEY = "QUEUE_AWSSQS_ACCESS_KEY"; + public const string QUEUE_AWSSQS_SECRET_KEY = "QUEUE_AWSSQS_SECRET_KEY"; + public const string QUEUE_AWSSQS_REGION = "QUEUE_AWSSQS_REGION"; + + } + public enum AuthenticationMethod + { + ActiveDirectory, + Password + } +} diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/QueueBase.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/QueueBase.cs index d8b80fdea..6d0635494 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/QueueBase.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/QueueBase.cs @@ -1,16 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using GeneXus.Services; -using log4net; namespace GeneXus.Messaging.Common { public abstract class QueueBase { - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(QueueBase)); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); internal GXService service; public QueueBase() { diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/ServiceSettings.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/ServiceSettings.cs deleted file mode 100644 index 443f5223e..000000000 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/ServiceSettings.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using GeneXus.Encryption; -using GeneXus.Messaging.Common; -using GeneXus.Services; -using log4net; - -namespace GeneXus.Messaging.Queue -{ - public class ServiceSettings - { - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(ServiceSettings)); - - internal GXService service; - public string serviceNameResolver { get; } - public string name { get; } - - public ServiceSettings(string serviceNameResolver, string name, GXService gXService) - { - this.serviceNameResolver = serviceNameResolver; - this.name = name; - this.service = gXService; - } - - public string GetEncryptedPropertyValue(string propertyName, string alternativePropertyName = null) - { - String value = GetEncryptedPropertyValue(propertyName, alternativePropertyName, null); - if (value == null) - { - String errorMessage = String.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); - logger.Fatal(errorMessage); - throw new Exception(errorMessage); - } - return value; - } - public string GetEncryptedPropertyValue(string propertyName, string alternativePropertyName, string defaultValue) - { - String value = GetPropertyValue(propertyName, alternativePropertyName, defaultValue); - if (!String.IsNullOrEmpty(value)) - { - try - { - string ret = String.Empty; - if (CryptoImpl.Decrypt(ref ret, value)) - { - value = ret; - } - } - catch (Exception) - { - logger.Warn($"Could not decrypt property name: {ResolvePropertyName(propertyName)}"); - } - } - return value; - } - - internal string GetPropertyValue(string propertyName, string alternativePropertyName = null) - { - String value = GetPropertyValue(propertyName, alternativePropertyName, null); - if (value == null) - { - String errorMessage = String.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); - logger.Fatal(errorMessage); - throw new Exception(errorMessage); - } - return value; - } - - internal string GetPropertyValue(string propertyName, string alternativePropertyName, string defaultValue) - { - String value = null; - value = string.IsNullOrEmpty(value) ? GetPropertyValueImpl(ResolvePropertyName(propertyName)) : value; - value = string.IsNullOrEmpty(value) ? GetPropertyValueImpl(propertyName) : value; - value = string.IsNullOrEmpty(value) ? GetPropertyValueImpl(alternativePropertyName) : value; - value = string.IsNullOrEmpty(value) ? defaultValue : value; - return value; - } - - internal string GetPropertyValueImpl(string propertyName) - { - String value = null; - if (!string.IsNullOrEmpty(propertyName)) - { - value = Environment.GetEnvironmentVariable(propertyName); - if (service != null && value == null) - { - value = service.Properties.Get(propertyName); - } - } - return value; - } - - internal string ResolvePropertyName(string propertyName) - { - return $"{serviceNameResolver}_{name}_{propertyName}"; - } - } -} diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/SimpleMessageQueue.cs b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/SimpleMessageQueue.cs index e243ca161..18739ffda 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/SimpleMessageQueue.cs +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/SimpleMessageQueue.cs @@ -1,14 +1,11 @@ using System; using System.Collections; using System.Collections.Generic; -using System.IO; using System.Reflection; using System.Text; -using GeneXus.Application; using GeneXus.Services; using GeneXus.Utils; using GxClasses.Helpers; -using log4net; namespace GeneXus.Messaging.Common { @@ -16,7 +13,7 @@ public class SimpleMessageQueue { internal IQueue queue = null; public static Assembly assembly; - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(SimpleMessageQueue)); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); private const string SDT_MESSAGE_CLASS_NAME = @"SdtMessage"; private const string SDT_MESSAGEPROPERTY_CLASS_NAME = @"SdtMessageProperty"; private const string SDT_MESSAGERESULT_CLASS_NAME = @"SdtMessageResult"; @@ -434,7 +431,7 @@ protected void QueueErrorMessagesSetup(Exception ex, out GXBaseCollection(); public static GXServices GetGXServices() { diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj new file mode 100644 index 000000000..ef080b861 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + NETCORE; + Properties + false + GeneXus.OpenTelemetry.Diagnostics + OpenTelemetry Diagnostics + GeneXus.OpenTelemetry.Diagnostics + NU1605 + + + + + + + + + + + + diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs new file mode 100644 index 000000000..d05c93aa8 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs @@ -0,0 +1,183 @@ +using System; +using System.Diagnostics; +using GeneXus.Attributes; +using OpenTelemetry; +using OpenTelemetry.Trace; + +namespace GeneXus.OpenTelemetry.Diagnostics +{ + [GXApi] + public class OtelSpan + { + private Activity _activity; + public enum SpanStatusCode + { + Unset, + Ok, + Error + } + + internal OtelSpan(Activity activity) + { + _activity = activity; + } + + public OtelSpan() + {} + + public Activity Activity => _activity; + + #region EO Properties + public GXSpanContext SpanContext => _activity == null ? null : new GXSpanContext(_activity.Context); + + public GXTraceContext GetContext => _activity == null ? null : new GXTraceContext(_activity.Context); + public string SpanId => _activity?.Id; + + public string TraceId => _activity?.TraceId.ToHexString(); + + #endregion + + #region Methods + public void Stop() + { + _activity?.Stop(); + } + public void RecordException(string message) + { + _activity?.RecordException(new Exception(message)); + } + + public void SetStringAttribute(string property, string value) + { + _activity?.SetTag(property, value); + + } + public void SetLongAttribute(string property, long value) + { + _activity?.SetTag(property, value); + + } + public void SetDoubleAttribute(string property, double value) + { + _activity?.SetTag(property, value); + + } + public void SetBooleanAttribute(string property, bool value) + { + _activity?.SetTag(property, value); + + } + public GXTraceContext AddBaggage(string property, string value) + { + Baggage.SetBaggage(property, value); + if (_activity != null) + return new GXTraceContext(_activity.Context); + else return null; + } + + public string GetBaggageItem(string property,GXTraceContext gXTraceContext) + { + return Baggage.GetBaggage(property); + } + public void SetStatus(SpanStatusCode spanStatusCode, string message) + { + _activity?.SetStatus((ActivityStatusCode)spanStatusCode, message); + } + public void SetStatus(SpanStatusCode spanStatusCode) + { + _activity?.SetStatus((ActivityStatusCode)spanStatusCode); + } + + #endregion + + #region Private Methods + public bool IsRecording => IsSpanRecording(); + + private bool IsSpanRecording() + { + if (_activity != null) + return _activity.IsAllDataRequested; + else + return false; + } + #endregion + + } + + public class GXTraceContext : GXSpanContext + { + //Dummy class to be compatible with Java. + //.NET does not requiere propagating the context explicitly in most of the cases. + //https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Api/README.md#baggage-api + + public GXTraceContext(ActivityContext activityContext):base(activityContext) { } + public GXTraceContext():base() { } + } + public class GXSpanContext + { + private ActivityContext _activityContext; + public GXSpanContext() + { + _activityContext = Activity.Current.Context; + } + + public GXSpanContext(ActivityContext activityContext) + { + _activityContext = activityContext; + } + public ActivityContext ActivityContext { get { return _activityContext; } } + + public string TraceId => GetTraceId(); + + public string SpanId => GetSpanId(); + + private string GetTraceId() + { + if (_activityContext != null) + { + ActivityTraceId activityTraceId = _activityContext.TraceId; + return activityTraceId.ToHexString(); + } else return null; + } + private string GetSpanId() + { + if (_activityContext != null) + { + ActivitySpanId activitySpanId = _activityContext.SpanId; + return activitySpanId.ToHexString(); + } else { return null; } + } + private GXActivityTraceFlags TraceFlags() + { + if (_activityContext != null) { + ActivityTraceFlags activityTraceFlags = _activityContext.TraceFlags; + return (GXActivityTraceFlags)activityTraceFlags; + } + else { return GXActivityTraceFlags.None;} + } + + private string TraceState() + { + if (_activityContext != null) + return _activityContext.TraceState; + else return null; + } + + + // + // Summary: + // Specifies flags defined by the W3C standard that are associated with an activity. + + internal enum GXActivityTraceFlags + { + // + // Summary: + // The activity has not been marked. + None = 0, + // + // Summary: + // The activity (or more likely its parents) has been marked as useful to record. + Recorded = 1 + } + } +} diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs new file mode 100644 index 000000000..812b1ae28 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using GeneXus.Attributes; +using GeneXus.Services.OpenTelemetry; + +namespace GeneXus.OpenTelemetry.Diagnostics +{ + [GXApi] + public class OtelTracer + { + internal static ActivitySource activitySource; + internal static ActivitySource ActivitySource + { + get + { + if (activitySource == null) + { + activitySource = string.IsNullOrEmpty(OpenTelemetryService.GX_ACTIVITY_SOURCE_VERSION) ? new(OpenTelemetryService.GX_ACTIVITY_SOURCE_NAME) : new(OpenTelemetryService.GX_ACTIVITY_SOURCE_NAME, OpenTelemetryService.GX_ACTIVITY_SOURCE_VERSION); + } + return activitySource; + } + } + public enum SpanType + { + Internal, + Server, + Client, + Producer, + Consumer + } + + public OtelSpan CreateSpan(string name) + { + //Returns null when the are no listeners. + Activity activity = ActivitySource.StartActivity(name); + if (activity != null) + return new OtelSpan(activity); + return null; + } + + public OtelSpan CreateSpan(string name, SpanType kind) + { + Activity activity = ActivitySource.StartActivity(name, (ActivityKind)kind); + if (activity != null) + return new OtelSpan(activity); + else + return null; + } + + public OtelSpan CreateSpan(string name, GXTraceContext gxTraceContext, SpanType spanType) + { + Activity activity = ActivitySource.StartActivity(name, + kind: (ActivityKind)spanType, + parentContext: gxTraceContext.ActivityContext); + return new OtelSpan(activity); + + } + + public OtelSpan CreateSpan(string name, GXTraceContext gxTraceContext, SpanType spanType, IList gxSpanContexts) + { + //https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing-instrumentation-walkthroughs#optional-links + List contexts = new List(); + + foreach (GXSpanContext gxSpanContext in gxSpanContexts) + { + ActivityContext context = gxSpanContext.ActivityContext; + contexts.Add(context); + } + + Activity activity = ActivitySource.StartActivity(name, + kind: (ActivityKind)spanType, + parentContext: gxTraceContext.ActivityContext, + links: contexts.Select(ctx => new ActivityLink(ctx))); + return new OtelSpan(activity); + } + + public OtelSpan CreateSpan(string name, GXTraceContext gxTraceContext, SpanType spanType, IList gxSpanContexts, DateTime dateTime) + { + //https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing-instrumentation-walkthroughs#optional-links + List contexts = new List(); + + foreach (GXSpanContext gxSpanContext in gxSpanContexts) + { + ActivityContext context = gxSpanContext.ActivityContext; + contexts.Add(context); + } + + Activity activity = ActivitySource.StartActivity(name, + kind: (ActivityKind)spanType, + parentContext: gxTraceContext.ActivityContext, + links: contexts.Select(ctx => new ActivityLink(ctx)), + startTime:dateTime); + return new OtelSpan(activity); + } + + public static OtelSpan GetCurrent() + { + return new OtelSpan(Activity.Current); + } + } +} diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GXMeterProviderBuilder.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GXMeterProviderBuilder.cs new file mode 100644 index 000000000..0e5630c85 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GXMeterProviderBuilder.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace GeneXus.OpenTelemetry +{ + public static class GXMeterProviderBuilder + { + public static MeterProviderBuilder AddGxMeterAspNetInstrumentation(this MeterProviderBuilder meter) + { + string envvar = Environment.GetEnvironmentVariable("OTEL_METRICS_EXPORTER"); + meter + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation() + .AddOtlpExporter(); + if (envvar != null && envvar.Contains("console")) + meter.AddConsoleExporter(); + return meter; + } + + } +} diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj index 1564bc6e3..64f9fc9ee 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj @@ -1,21 +1,20 @@ - - net6.0 - enable - enable - GeneXus.OpenTelemetry - OpenTelemetry GeneXus DotNet - GeneXus.OpenTelemetry.OpenTelemetry - NU1605 - + + net6.0;net8.0 + GeneXus.OpenTelemetry + OpenTelemetry GeneXus DotNet + GeneXus.OpenTelemetry.OpenTelemetry + NU1605 + - - - - - + + + + + + diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs index 58da3b8d2..124a86da6 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs @@ -1,3 +1,4 @@ +using System; using GeneXus.Services.OpenTelemetry; using OpenTelemetry.Trace; @@ -19,17 +20,15 @@ public static TracerProviderBuilder AddGxAspNetInstrumentation(this TracerProvid { opt.RecordException = true; }) - .AddAspNetCoreInstrumentation(opt => - { - opt.RecordException = true; - }) .AddSqlClientInstrumentation(opt => { opt.RecordException = true; opt.EnableConnectionLevelAttributes = true; opt.SetDbStatementForText = true; - }) - .AddConsoleExporter(); + }); + string envvar = Environment.GetEnvironmentVariable("OTEL_TRACES_EXPORTER"); + if (envvar != null && envvar.Contains("console")) + tracer.AddConsoleExporter(); return tracer; } } diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs index 78eaf649e..c7c237dcf 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs @@ -2,23 +2,41 @@ using GeneXus.Services.OpenTelemetry; using Microsoft.Extensions.DependencyInjection; using OpenTelemetry.Trace; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using System; +using Microsoft.IdentityModel.Tokens; namespace GeneXus.OpenTelemetry.OpenTelemetry { public class OpenTelemetryProvider : IOpenTelemetryProvider { + const string OTEL_METRICS_EXPORTER = "OTEL_METRICS_EXPORTER"; + const string OTEL_TRACES_EXPORTER = "OTEL_TRACES_EXPORTER"; public OpenTelemetryProvider(GXService s) { } public bool InstrumentAspNetCoreApplication(IServiceCollection services) { - services.AddOpenTelemetry().WithTracing(tracerProviderBuilder => - { - tracerProviderBuilder - .AddOtlpExporter() - .AddGxAspNetInstrumentation(); - }); + string envvar = Environment.GetEnvironmentVariable(OTEL_TRACES_EXPORTER); + if (envvar.IsNullOrEmpty() || !envvar.ToLower().Equals("none")) { + services.AddOpenTelemetry().WithTracing(tracerProviderBuilder => + { + tracerProviderBuilder + .AddOtlpExporter() + .AddGxAspNetInstrumentation(); + }); + } + + envvar = Environment.GetEnvironmentVariable(OTEL_METRICS_EXPORTER); + if (envvar.IsNullOrEmpty() || !envvar.ToLower().Equals("none")) { + services.AddOpenTelemetry().WithMetrics(metricsProviderBuilder => + { + metricsProviderBuilder + .AddGxMeterAspNetInstrumentation(); + }); + } return true; } } diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs index 2aae5321f..7de8b29f1 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs @@ -1,12 +1,11 @@ using System; +using GeneXus.Services; using GeneXus.Services.OpenTelemetry; using Microsoft.Extensions.DependencyInjection; using OpenTelemetry; -using OpenTelemetry.Trace; using OpenTelemetry.Contrib.Extensions.AWSXRay.Trace; -using GeneXus.Services; -using GeneXus.Services.Common; -using GeneXus.Diagnostics; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; namespace GeneXus.OpenTelemetry.AWS { @@ -33,6 +32,10 @@ public bool InstrumentAspNetCoreApplication(IServiceCollection _) .AddGxAspNetInstrumentation() .Build(); + Sdk.CreateMeterProviderBuilder() + .AddGxMeterAspNetInstrumentation() + .Build(); + Sdk.SetDefaultTextMapPropagator(new AWSXRayPropagator()); return true; diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/GeneXus.OpenTelemetry.AWS.AspNet.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/GeneXus.OpenTelemetry.AWS.AspNet.csproj index 2fe0c462b..1fb368fc7 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/GeneXus.OpenTelemetry.AWS.AspNet.csproj +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/GeneXus.OpenTelemetry.AWS.AspNet.csproj @@ -1,7 +1,7 @@ - net6.0 + net6.0;net8.0 NETCORE; Properties false diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs index 68e6152ae..96c804a64 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs @@ -1,39 +1,68 @@ using System; -using Azure.Monitor.OpenTelemetry.AspNetCore; +using Azure.Identity; +using Azure.Monitor.OpenTelemetry.Exporter; using GeneXus.Services; using GeneXus.Services.OpenTelemetry; -using log4net; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; namespace GeneXus.OpenTelemetry.Azure { public class AzureAppInsights : IOpenTelemetryProvider { - private static readonly ILog log = LogManager.GetLogger(typeof(AzureAppInsights)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private const string APPLICATIONINSIGHTS_CONNECTION_STRING = "APPLICATIONINSIGHTS_CONNECTION_STRING"; public AzureAppInsights(GXService s) { } - public bool InstrumentAspNetCoreApplication(IServiceCollection services) + public bool InstrumentAspNetCoreApplication(IServiceCollection _) { string oltpEndpoint = Environment.GetEnvironmentVariable(APPLICATIONINSIGHTS_CONNECTION_STRING); - - if (!string.IsNullOrEmpty(oltpEndpoint)) + try { - services.AddOpenTelemetry() - .UseAzureMonitor( o => - { + var resourceBuilder = ResourceBuilder.CreateDefault() + .AddTelemetrySdk(); + + Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(resourceBuilder) + .AddAzureMonitorTraceExporter(o => + { + if (!string.IsNullOrEmpty(oltpEndpoint)) o.ConnectionString = oltpEndpoint; - }); + else + { + o.Credential = new DefaultAzureCredential(); + GXLogging.Debug(log, "Connect to Azure monitor Opentelemetry Trace exporter using Default Azure credential"); + } + }) + .AddGxAspNetInstrumentation() + .Build(); + Sdk.CreateMeterProviderBuilder() + .SetResourceBuilder(resourceBuilder) + .AddAzureMonitorMetricExporter(o => + { + if (!string.IsNullOrEmpty(oltpEndpoint)) + o.ConnectionString = oltpEndpoint; + else + { + o.Credential = new DefaultAzureCredential(); + GXLogging.Debug(log, "Connect to Azure monitor Opentelemetry Metrics exporter using Default Azure credential"); + } + }) + .AddGxMeterAspNetInstrumentation() + .Build(); return true; } - else - { - log.Warn("OpenTelemetry Azure Monitor was not initialized due to missing 'APPLICATIONINSIGHTS_CONNECTION_STRING' Environment Variable"); + catch (Exception ex) + { + GXLogging.Warn(log, "Azure Monitor Opentelemetry could not be initialized. " + ex.Message); return false; } } diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj index f8d09b91f..b627b6bd5 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 NETCORE; Properties false @@ -10,7 +10,15 @@ NU1605 - + + + + + + + + + diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/GeneXus.OpenTelemetry.Lightstep.AspNet.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/GeneXus.OpenTelemetry.Lightstep.AspNet.csproj index 5702ecd91..6114b9112 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/GeneXus.OpenTelemetry.Lightstep.AspNet.csproj +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/GeneXus.OpenTelemetry.Lightstep.AspNet.csproj @@ -1,7 +1,7 @@ - net6.0 + net6.0;net8.0 NETCORE; Properties false diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/LightstepProvider.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/LightstepProvider.cs index 228b579ff..8d251d1c8 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/LightstepProvider.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/LightstepProvider.cs @@ -1,19 +1,16 @@ using System; +using GeneXus.Services; using GeneXus.Services.OpenTelemetry; using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry; using OpenTelemetry.Trace; -using OpenTelemetry.Resources; -using log4net; -using GeneXus.Services; namespace GeneXus.OpenTelemetry.Lightstep { public class LightStepOpenTelemetry : IOpenTelemetryProvider { - private static readonly ILog log = LogManager.GetLogger(typeof(LightStepOpenTelemetry)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private const string LIGHTSTEP_INGREST_URL = "ingest.lightstep.com:443"; private const string LIGHTSTEP_ACCESS_TOKEN = "LS_ACCESS_TOKEN"; @@ -27,7 +24,7 @@ public bool InstrumentAspNetCoreApplication(IServiceCollection services) if (string.IsNullOrEmpty(lightstepToken)) { - log.Warn("OpenTelemetry Lightstep was not initialized due to missing 'LS_ACCESS_TOKEN' Environment Variable"); + GXLogging.Warn(log, "OpenTelemetry Lightstep was not initialized due to missing 'LS_ACCESS_TOKEN' Environment Variable"); return false; } diff --git a/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj b/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj index 2f83482a0..ca162324b 100644 --- a/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj +++ b/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 TRACE;DEBUG;NETCORE Properties false @@ -13,6 +13,7 @@ + diff --git a/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj b/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj index 2e414d14e..62f614c32 100644 --- a/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj +++ b/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj @@ -1,7 +1,7 @@ - net6.0 + net6.0;net8.0 TRACE;DEBUG;NETCORE Properties false @@ -15,6 +15,7 @@ + diff --git a/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj b/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj index f036cd022..1af8b36e6 100644 --- a/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj +++ b/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 TRACE;DEBUG;NETCORE Properties false @@ -14,6 +14,7 @@ + diff --git a/dotnet/src/dotnetcore/Reor/Reor.csproj b/dotnet/src/dotnetcore/Reor/Reor.csproj index f02fc7005..7743e4090 100644 --- a/dotnet/src/dotnetcore/Reor/Reor.csproj +++ b/dotnet/src/dotnetcore/Reor/Reor.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net8.0 Exe Reorg GeneXus.Reorganization.Core @@ -16,14 +16,18 @@ + + + + - contentFiles/any/any + contentFiles/any/$(TargetFramework) true - contentFiles/any/any + contentFiles/any/$(TargetFramework) true diff --git a/dotnet/src/dotnetcore/libs/net8.0/Jayrock.dll b/dotnet/src/dotnetcore/libs/net8.0/Jayrock.dll new file mode 100644 index 000000000..fd1736afd Binary files /dev/null and b/dotnet/src/dotnetcore/libs/net8.0/Jayrock.dll differ diff --git a/dotnet/src/dotnetframework/DynServiceOData/DynServiceOData.csproj b/dotnet/src/dotnetframework/DynServiceOData/DynServiceOData.csproj index 7ef6d238e..2cf30c8fd 100644 --- a/dotnet/src/dotnetframework/DynServiceOData/DynServiceOData.csproj +++ b/dotnet/src/dotnetframework/DynServiceOData/DynServiceOData.csproj @@ -11,7 +11,6 @@ - diff --git a/dotnet/src/dotnetframework/GxClasses.Win/GxClasses.Win.csproj b/dotnet/src/dotnetframework/GxClasses.Win/GxClasses.Win.csproj index 1d113bc5d..bdff5e121 100644 --- a/dotnet/src/dotnetframework/GxClasses.Win/GxClasses.Win.csproj +++ b/dotnet/src/dotnetframework/GxClasses.Win/GxClasses.Win.csproj @@ -8,11 +8,10 @@ GeneXus.Classes.Win - - + \ No newline at end of file diff --git a/dotnet/src/dotnetframework/GxClasses.Win/Process/CommandLine.cs b/dotnet/src/dotnetframework/GxClasses.Win/Process/CommandLine.cs index ad02258b6..b43620759 100644 --- a/dotnet/src/dotnetframework/GxClasses.Win/Process/CommandLine.cs +++ b/dotnet/src/dotnetframework/GxClasses.Win/Process/CommandLine.cs @@ -1,8 +1,7 @@ -using GeneXus.Application; -using log4net; using System; using System.Diagnostics; using System.IO; +using GeneXus.Application; namespace GeneXus.Utils { @@ -19,8 +18,7 @@ public IProcessHelper GetProcessHelper() } public class GxProcess : IProcessHelper { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.GxProcess)); - + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public short OpenPrintDocument(string commandString) { Process p = new Process(); diff --git a/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs b/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs index f7433d04c..6357186e2 100644 --- a/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs +++ b/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs @@ -3,7 +3,6 @@ using GeneXus.Attributes; using GeneXus.Utils; using GeneXus.Encryption; -using log4net; #if NETCORE using GxClasses.Helpers; #endif @@ -16,8 +15,7 @@ public class ExternalStorage : GxStorageProvider private GXService providerService; - static readonly ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); public ExternalStorage() { @@ -59,7 +57,7 @@ public bool Create(string name, GXProperties properties, ref GxStorageProvider s } string typeFullName = providerService.ClassName; - logger.Debug("Loading storage provider: "+ typeFullName); + GXLogging.Debug(logger, "Loading storage provider: " + typeFullName); #if !NETCORE Type type = Type.GetType(typeFullName, true, true); #else @@ -70,7 +68,7 @@ public bool Create(string name, GXProperties properties, ref GxStorageProvider s } catch (Exception ex) { - logger.Error("Couldn't connect to external storage provider. ", ex); + GXLogging.Error(logger, "Couldn't connect to external storage provider. ", ex); StorageMessages(ex, messages); return false; } diff --git a/dotnet/src/dotnetframework/GxClasses/Configuration/LogConfiguration.cs b/dotnet/src/dotnetframework/GxClasses/Configuration/LogConfiguration.cs index 43495ecf7..69428b857 100644 --- a/dotnet/src/dotnetframework/GxClasses/Configuration/LogConfiguration.cs +++ b/dotnet/src/dotnetframework/GxClasses/Configuration/LogConfiguration.cs @@ -1,17 +1,16 @@ using System; +using System.Linq; using log4net; using log4net.Appender; using log4net.Core; using log4net.Repository; using log4net.Repository.Hierarchy; -using System.Linq; -using log4net.Layout; namespace GeneXus.Configuration { internal class LogConfiguration { - private static readonly ILog logger = log4net.LogManager.GetLogger(typeof(LogConfiguration)); + //private static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); public const string USER_LOG_TOPIC = "GeneXusUserLog"; private const string LOG_LEVEL_ENVVAR = "GX_LOG_LEVEL"; @@ -20,7 +19,11 @@ internal class LogConfiguration public static void SetupLog4Net() { - SetupLog4NetFromEnvironmentVariables(); + Config.GetValueOf("LOG_OUTPUT", out string logProvider); + if (logProvider == "ASPNetTraceAppender" || logProvider == "ConsoleAppender" || logProvider == "EventLogAppender" || logProvider == "RollingFile") + { + SetupLog4NetFromEnvironmentVariables(); + } } private static void SetupLog4NetFromEnvironmentVariables() @@ -53,10 +56,10 @@ private static void SetupLog4NetFromEnvironmentVariables() if (!String.IsNullOrEmpty(appenderName)) { Hierarchy h = (Hierarchy) LogManager.GetRepository(); - IAppender appenderToAdd = h.GetAppenders().First(a => a.Name == appenderName); + IAppender appenderToAdd = h.GetAppenders().FirstOrDefault(a => a.Name == appenderName); if (appenderToAdd == null) { - LogConfiguration.logger.Error($"Appender '{appenderName}' was not found on Log4Net Config file"); + //LogConfiguration.logger.Warn($"Appender '{appenderName}' was not found on Log4Net Config file"); return; } diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs index 27eed1ebf..82b3a5cce 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs @@ -13,6 +13,7 @@ namespace GeneXus.Application using System.Messaging; using System.ServiceModel.Web; using GeneXus.UserControls; + using System.Net.Http.Headers; #else using Microsoft.AspNetCore.Http; using GxClasses.Helpers; @@ -20,8 +21,9 @@ namespace GeneXus.Application #endif using GeneXus.Configuration; using GeneXus.Metadata; - using log4net; +#if !NETCORE using Jayrock.Json; +#endif using GeneXus.Http; using System.Collections.Specialized; using System.Collections.Generic; @@ -43,10 +45,12 @@ namespace GeneXus.Application using Microsoft.AspNetCore.Http.Features; #endif using NodaTime; - using NodaTime.TimeZones; using System.Threading; using System.Security.Claims; using System.Security; + using Microsoft.Net.Http.Headers; + using System.Threading.Tasks; + using GeneXus.Data.ADO; public interface IGxContext { @@ -291,18 +295,18 @@ public GxHttpContextAccesor(IHttpContextAccessor ctxAccessor) public override IFeatureCollection Features => ctxAccessor.HttpContext.Features; - public override IDictionary Items { get => ctxAccessor.HttpContext.Items; set => ctxAccessor.HttpContext.Items=value; } + public override IDictionary Items { get => ctxAccessor.HttpContext.Items; set => ctxAccessor.HttpContext.Items = value; } public override HttpRequest Request => ctxAccessor.HttpContext.Request; - public override CancellationToken RequestAborted { get => ctxAccessor.HttpContext.RequestAborted; set => ctxAccessor.HttpContext.RequestAborted=value; } - public override IServiceProvider RequestServices { get => ctxAccessor.HttpContext.RequestServices; set => ctxAccessor.HttpContext.RequestServices=value; } + public override CancellationToken RequestAborted { get => ctxAccessor.HttpContext.RequestAborted; set => ctxAccessor.HttpContext.RequestAborted = value; } + public override IServiceProvider RequestServices { get => ctxAccessor.HttpContext.RequestServices; set => ctxAccessor.HttpContext.RequestServices = value; } public override HttpResponse Response => ctxAccessor.HttpContext.Response; - public override ISession Session { get => ctxAccessor.HttpContext.Session; set => ctxAccessor.HttpContext.Session=value; } - public override string TraceIdentifier { get => ctxAccessor.HttpContext.TraceIdentifier; set => ctxAccessor.HttpContext.TraceIdentifier=value; } - public override ClaimsPrincipal User { get => ctxAccessor.HttpContext.User; set => ctxAccessor.HttpContext.User=value; } + public override ISession Session { get => ctxAccessor.HttpContext.Session; set => ctxAccessor.HttpContext.Session = value; } + public override string TraceIdentifier { get => ctxAccessor.HttpContext.TraceIdentifier; set => ctxAccessor.HttpContext.TraceIdentifier = value; } + public override ClaimsPrincipal User { get => ctxAccessor.HttpContext.User; set => ctxAccessor.HttpContext.User = value; } public override WebSocketManager WebSockets => ctxAccessor.HttpContext.WebSockets; @@ -319,7 +323,7 @@ internal class GxApplication [Serializable] public class GxContext : IGxContext { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Application.GxContext)); + private static IGXLogger log = null; internal static string GX_SPA_REQUEST_HEADER = "X-SPA-REQUEST"; internal static string GX_SPA_REDIRECT_URL = "X-SPA-REDIRECT-URL"; internal const string GXLanguage = "GXLanguage"; @@ -452,6 +456,26 @@ public static GxContext CreateDefaultInstance() context.SetDefaultTheme(theme); return context; } + + static IGXLogger Logger + { + get + { + if (Config.configLoaded) + { + if (log == null) + { + log = GXLoggerFactory.GetLogger(); + return log; + } + return log; + } + else + return null; + } + + } + public GxContext() { _DataStores = new ArrayList(2); @@ -459,7 +483,9 @@ public GxContext() _errorHandlerInfo = new GxErrorHandlerInfo(); setContext(this); httpContextVars = new GxHttpContextVars(); - GXLogging.Debug(log, "GxContext.Ctr Default handle:", () => _handle.ToString()); + + GXLogging.Debug(Logger, "GxContext.Ctr Default handle:", () => _handle.ToString()); + if (GxApplication.MainContext == null && !(IsHttpContext || GxContext.IsRestService)) GxApplication.MainContext = this; } @@ -474,12 +500,12 @@ public GxContext(int handle, string location) } public GxContext(String location) { - GXLogging.Debug(log, "GxContext.Ctr, parameters location=", location); + GXLogging.Debug(Logger, "GxContext.Ctr, parameters location=", location); _DataStores = new ArrayList(2); _errorHandlerInfo = new GxErrorHandlerInfo(); setContext(this); httpContextVars = new GxHttpContextVars(); - GXLogging.Debug(log, "Return GxContext.Ctr"); + GXLogging.Debug(Logger, "Return GxContext.Ctr"); } public GxContext(int handle, ArrayList dataStores, HttpContext httpContext) @@ -570,7 +596,7 @@ public CookieContainer GetCookieContainer(string url, bool includeCookies = true } catch (Exception ex) { - GXLogging.Debug(log, ex, "GetCookieContainer error url:", url); + GXLogging.Debug(Logger, ex, "GetCookieContainer error url:", url); } return new CookieContainer(); @@ -707,7 +733,7 @@ public static bool GetHttpRequestPostedFile(IGxContext gxContext, string varName if (ext != null) ext = ext.TrimStart('.'); string filePath = FileUtil.getTempFileName(tempDir); - GXLogging.Debug(log, "cgiGet(" + varName + "), fileName:" + filePath); + GXLogging.Debug(Logger, "cgiGet(" + varName + "), fileName:" + filePath); GxFile file = new GxFile(tempDir, filePath, GxFileType.PrivateAttribute); filePath = file.Create(pf.InputStream); string fileGuid = GxUploadHelper.GetUploadFileGuid(); @@ -978,12 +1004,12 @@ private bool CheckFileExists(string fileName) { string path = Path.Combine(this.GetPhysicalPath(), fileName); fileExists = File.Exists(path); - GXLogging.Debug(log, $"Searching if file exists ({fileName}). Found: {fileExists}"); + GXLogging.Debug(Logger, $"Searching if file exists ({fileName}). Found: {fileExists}"); } catch (Exception e) { fileExists = false; - GXLogging.Error(log, e, $"Failed searching for a file ({fileName})"); + GXLogging.Error(Logger, e, $"Failed searching for a file ({fileName})"); } return fileExists; } @@ -991,11 +1017,7 @@ private bool CheckFileExists(string fileName) public void StatusMessage(string message) { StackFrame frame = new StackFrame(1); -#if NETCORE - ILog statusLog = log4net.LogManager.GetLogger(frame.GetMethod().DeclaringType); -#else - ILog statusLog = log4net.LogManager.GetLogger(frame.GetMethod().DeclaringType.FullName); -#endif + IGXLogger statusLog = GXLoggerFactory.GetLogger(frame.GetMethod().DeclaringType.FullName); GXLogging.Info(statusLog, message); Console.WriteLine(message); } @@ -1387,12 +1409,27 @@ public IGxDataStore GetDataStore(string id) return ds; return null; } +#if NETCORE + internal async Task CloseConnectionsAsync() + { + GxUserInfo.RemoveHandle(this.handle); + foreach (GxDataStore ds in _DataStores) + await ds.CloseConnectionsAsync(); + + CloseConnectionsResources(); + } +#endif public void CloseConnections() { GxUserInfo.RemoveHandle(this.handle); foreach (IGxDataStore ds in _DataStores) ds.CloseConnections(); + CloseConnectionsResources(); + } + + private void CloseConnectionsResources() + { if (_reportHandlerToClose != null) { for (int i = 0; i < _reportHandlerToClose.Count; i++) @@ -1403,7 +1440,7 @@ public void CloseConnections() } catch (Exception ex) { - GXLogging.Error(log, "Error closing report", ex); + GXLogging.Error(Logger, "Error closing report", ex); } } _reportHandlerToClose.Clear(); @@ -1468,7 +1505,7 @@ public void Disconnect() { foreach (IGxDataStore ds in _DataStores) ds.Disconnect(); - GXLogging.Debug(log, "Local Disconnect"); + GXLogging.Debug(Logger, "Local Disconnect"); } public void RollbackDataStores() @@ -1799,7 +1836,7 @@ internal string AbsoluteUri { absoluteUri = String.Format("{0}://{1}{2}", GetServerSchema(), _HttpContext.Request.Headers["Host"], _HttpContext.Request.GetRawUrl()); } - GXLogging.DebugSanitized(log, "AbsoluteUri dynamicport:", absoluteUri); + GXLogging.DebugSanitized(Logger, "AbsoluteUri dynamicport:", absoluteUri); return absoluteUri; } else @@ -2131,18 +2168,22 @@ public string GetBrowserVersion() } } public virtual short GetHttpSecure() + { + return GetHttpSecure(_HttpContext); + } + static internal short GetHttpSecure(HttpContext httpContext) { try { - if (HttpContext == null) + if (httpContext == null) return 0; - if (_HttpContext.Request.GetIsSecureFrontEnd()) + if (httpContext.Request.GetIsSecureFrontEnd()) { - GXLogging.Debug(log, "Front-End-Https header activated"); + GXLogging.Debug(Logger, "Front-End-Https header activated"); return 1; } else - return _HttpContext.Request.GetIsSecureConnection(); + return httpContext.Request.GetIsSecureConnection(); } catch { @@ -2317,6 +2358,20 @@ public string GetCookie(string name) } return cookieVal; } + internal string GetUndecodedCookie(string name) + { + string cookieVal = string.Empty; + HttpCookie cookie = TryGetCookie(localCookies, name); + if (cookie == null && _HttpContext != null) + { + cookie = TryGetCookie(_HttpContext.Request.GetCookies(), name); + } + if (cookie != null && cookie.Value != null) + { + cookieVal = cookie.Value; + } + return cookieVal; + } private HttpCookie TryGetCookie(HttpCookieCollection cookieColl, string name) { @@ -2359,8 +2414,8 @@ public short SetCookie(string name, string cookieValue, string path, DateTime ex Secure = cookie.Secure }; string sameSite; - SameSiteMode sameSiteMode = SameSiteMode.Unspecified; - if (Config.GetValueOf("SAMESITE_COOKIE", out sameSite) && Enum.TryParse(sameSite, out sameSiteMode)) + Microsoft.AspNetCore.Http.SameSiteMode sameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified; + if (Config.GetValueOf("SAMESITE_COOKIE", out sameSite) && Enum.TryParse(sameSite, out sameSiteMode)) { cookieOptions.SameSite = sameSiteMode; } @@ -2423,18 +2478,17 @@ public byte SetHeader(string name, string value) _httpHeaders = new NameValueCollection(); } _httpHeaders[name] = value; - SetCustomHttpHeader(name, value); - return 0; + return SetCustomHttpHeader(name, value); } - private void SetCustomHttpHeader(string name, string value) + private byte SetCustomHttpHeader(string name, string value) { - _HttpContext.Response.AppendHeader(name, value); - -#if !NETCORE - switch (name.ToUpper()) + try { - case "CACHE-CONTROL": +#if !NETCORE + + if (name.Equals(HeaderNames.CacheControl, StringComparison.OrdinalIgnoreCase)) + { var Cache = _HttpContext.Response.Cache; string[] values = value.Split(','); foreach (string v in values) @@ -2454,30 +2508,65 @@ private void SetCustomHttpHeader(string name, string value) Cache.AppendCacheExtension("no-store, must-revalidate"); break; default: + GXLogging.Warn(Logger, String.Format("Could not set Cache Control Http Header Value '{0}' to HttpResponse", value)); break; } } - break; - } + } + else if (name.Equals(HeaderNames.ContentType, StringComparison.OrdinalIgnoreCase)) + { + _HttpContext.Response.ContentType = value; + } + else if (name.Equals(HeaderNames.Location, StringComparison.OrdinalIgnoreCase)) + { + _HttpContext.Response.RedirectLocation = value; + } + else + { + try + { + _HttpContext.Response.Headers[name] = value; + + }catch (PlatformNotSupportedException ex) + { + _HttpContext.Response.AppendHeader(name, value); + GXLogging.Warn(Logger, ex, "SetHeader ", name, value); + } + } #else - switch (name.ToUpper()) - { - case "CACHE-CONTROL": - switch (value.ToUpper()) + if (name.Equals(HeaderNames.CacheControl, StringComparison.OrdinalIgnoreCase)) + { + if (CacheControlHeaderValue.TryParse(value, out CacheControlHeaderValue parsedValue)) { - case "PUBLIC": - _HttpContext.Response.AddHeader("Cache-Control", "public"); - break; - case "PRIVATE": - _HttpContext.Response.AddHeader("Cache-Control", "private"); - break; - default: - GXLogging.Warn(log, String.Format("Could not set Cache Control Http Header Value '{0}' to HttpResponse", value)); - break; + _HttpContext.Response.GetTypedHeaders().CacheControl = parsedValue; } - break; - } + else + { + switch (value.ToUpper()) + { + case "PUBLIC": + _HttpContext.Response.AddHeader(HeaderNames.CacheControl, "public"); + break; + case "PRIVATE": + _HttpContext.Response.AddHeader(HeaderNames.CacheControl, "private"); + break; + default: + GXLogging.Warn(Logger, String.Format("Could not set Cache Control Http Header Value '{0}' to HttpResponse", value)); + break; + } + } + } + else { + _HttpContext.Response.AddHeader(name, value); + } #endif + return 0; + } + catch (Exception ex) + { + GXLogging.Error(Logger, ex, "Error adding header ", name, value); + return 1; + } } public string GetHeader(string name) @@ -2829,7 +2918,7 @@ internal static int GetServerPort(HttpRequest request, bool isSecure) } catch (Exception ex) { - GXLogging.Error(log, "GetServerPort error", ex); + GXLogging.Error(Logger, "GetServerPort error", ex); return 0; } @@ -2871,7 +2960,7 @@ private bool FrontEndHttps() { if (CheckHeaderValue("Front-End-Https", "on") || CheckHeaderValue("X-Forwarded-Proto", "https")) { - GXLogging.Debug(log, "Front-End-Https header activated"); + GXLogging.Debug(Logger, "Front-End-Https header activated"); return true; } else @@ -2926,7 +3015,7 @@ public string GetPhysicalPath() } catch (Exception ex) { - GXLogging.Debug(log, "GetPhysicalPath error", ex); + GXLogging.Debug(Logger, "GetPhysicalPath error", ex); _physicalPath = String.Empty; } } @@ -3215,7 +3304,7 @@ public bool ExecuteBeforeConnect(IGxDataStore datastore) if (!configuredEventHandling) configEventHandling(); if (beforeConnectObj != null) { - GXLogging.Debug(log, "ExecuteBeforeConnect"); + GXLogging.Debug(Logger, "ExecuteBeforeConnect"); ClassLoader.ExecuteVoidRef(beforeConnectObj, "execute", new Object[] { datastore }, "Before Connect"); return true; } @@ -3229,7 +3318,7 @@ public bool ExecuteAfterConnect(String datastoreName) if (!configuredEventHandling) configEventHandling(); if (afterConnectObj != null) { - GXLogging.Debug(log, "ExecuteAfterConnect"); + GXLogging.Debug(Logger, "ExecuteAfterConnect"); ClassLoader.ExecuteVoidRef(afterConnectObj, "execute", new Object[] { datastoreName }, "After Connect"); return true; } @@ -3247,7 +3336,7 @@ public void SetProperty(string key, string value) { if (HttpContext != null && HttpContext.Session != null) { - GXLogging.DebugSanitized(log, "HttpContext.Session.setProperty(", key, ")=", value); + GXLogging.DebugSanitized(Logger, "HttpContext.Session.setProperty(", key, ")=", value); WriteSessionKey(key, value); } else @@ -3256,7 +3345,7 @@ public void SetProperty(string key, string value) { _properties = new Hashtable(); } - GXLogging.DebugSanitized(log, "GxContext.Properties.getProperty(", key, ")=", value); + GXLogging.DebugSanitized(Logger, "GxContext.Properties.getProperty(", key, ")=", value); _properties[key] = value; } } @@ -3296,7 +3385,7 @@ public object GetContextProperty(string key) public string PathToUrl(string path) { - GXLogging.Debug(log, "PathToUrl:", () => GetContextPath() + " relativePath:" + PathToRelativeUrl(path)); + GXLogging.Debug(Logger, "PathToUrl:", () => GetContextPath() + " relativePath:" + PathToRelativeUrl(path)); #pragma warning disable SYSLIB0013 // EscapeUriString return Uri.EscapeUriString(GetContextPath()) + PathToRelativeUrl(path, false); #pragma warning disable SYSLIB0013 // EscapeUriString @@ -3516,11 +3605,11 @@ public OlsonTimeZone ClientTimeZone if (_currentTimeZone != null) return _currentTimeZone; string sTZ = _HttpContext == null ? "" : (string)_HttpContext.Request.Headers[GX_REQUEST_TIMEZONE]; - GXLogging.DebugSanitized(log, "ClientTimeZone GX_REQUEST_TIMEZONE header:", sTZ); + GXLogging.DebugSanitized(Logger, "ClientTimeZone GX_REQUEST_TIMEZONE header:", sTZ); if (String.IsNullOrEmpty(sTZ)) { sTZ = (string)GetCookie(GX_REQUEST_TIMEZONE); - GXLogging.Debug(log, "ClientTimeZone GX_REQUEST_TIMEZONE cookie:", sTZ); + GXLogging.Debug(Logger, "ClientTimeZone GX_REQUEST_TIMEZONE cookie:", sTZ); } try { @@ -3528,14 +3617,14 @@ public OlsonTimeZone ClientTimeZone } catch (Exception e1) { - GXLogging.Warn(log, "ClientTimeZone _currentTimeZone error", e1); + GXLogging.Warn(Logger, "ClientTimeZone _currentTimeZone error", e1); try { _currentTimeZone = TimeZoneUtil.GetInstanceFromWin32Id(TimeZoneInfo.Local.Id); } catch (Exception e2) { - GXLogging.Warn(log, "ClientTimeZone GetInstanceFromWin32Id error", e2); + GXLogging.Warn(Logger, "ClientTimeZone GetInstanceFromWin32Id error", e2); Preferences.StorageTimeZonePty storagePty = Preferences.getStorageTimezonePty(); if (storagePty == Preferences.StorageTimeZonePty.Undefined) _currentTimeZone = null; @@ -3553,19 +3642,39 @@ internal string ClientTimeZoneId if (_currentTimeZoneId != null) return _currentTimeZoneId; string sTZ = _HttpContext == null ? "" : (string)_HttpContext.Request.Headers[GX_REQUEST_TIMEZONE]; - GXLogging.DebugSanitized(log, "ClientTimeZone GX_REQUEST_TIMEZONE header:", sTZ); + GXLogging.DebugSanitized(Logger, "ClientTimeZone GX_REQUEST_TIMEZONE header:", sTZ); if (String.IsNullOrEmpty(sTZ)) { sTZ = (string)GetCookie(GX_REQUEST_TIMEZONE); - GXLogging.Debug(log, "ClientTimeZone GX_REQUEST_TIMEZONE cookie:", sTZ); + GXLogging.Debug(Logger, "ClientTimeZone GX_REQUEST_TIMEZONE cookie:", sTZ); + } + if (!DateTimeUtil.ValidTimeZone(sTZ)) + { + sTZ = (string)GetUndecodedCookie(GX_REQUEST_TIMEZONE); + GXLogging.Debug(Logger, "Try reading undecoded ClientTimeZone GX_REQUEST_TIMEZONE cookie:", sTZ); } try { - _currentTimeZoneId = String.IsNullOrEmpty(sTZ) ? DateTimeZoneProviders.Tzdb.GetSystemDefault().Id : sTZ; + if (!DateTimeUtil.ValidTimeZone(sTZ)) + { + try + { + string invalidTimezone = DateTimeZoneProviders.Tzdb[sTZ].Id; + }catch(Exception ex)//DateTimeZoneNotFound + { + GXLogging.Warn(Logger, $"Client timezone not found: {sTZ}", ex); + } + _currentTimeZoneId = DateTimeZoneProviders.Tzdb.GetSystemDefault().Id; + GXLogging.Warn(Logger, $"Setting Client timezone to System default: {_currentTimeZoneId}"); + } + else + { + _currentTimeZoneId = sTZ; + } } catch (Exception e1) { - GXLogging.Warn(log, "ClientTimeZone GetInstanceFromWin32Id error", e1); + GXLogging.Warn(Logger, "ClientTimeZone GetInstanceFromWin32Id error", e1); Preferences.StorageTimeZonePty storagePty = Preferences.getStorageTimezonePty(); if (storagePty == Preferences.StorageTimeZonePty.Undefined) _currentTimeZoneId = null; @@ -3606,14 +3715,15 @@ public String GetTimeZone() public Boolean SetTimeZone(String sTZ) { sTZ = StringUtil.RTrim(sTZ); - Boolean ret = false; + bool ret = false; #if NODATIME string tzId; try { - if (DateTimeZoneProviders.Tzdb[sTZ] != null) + DateTimeZone zone = DateTimeZoneProviders.Tzdb.GetZoneOrNull(sTZ); + if (zone != null) { - tzId = DateTimeZoneProviders.Tzdb[sTZ].Id; + tzId = zone.Id; } else { @@ -3669,9 +3779,9 @@ Hashtable Images string imgDir = ""; if (String.IsNullOrEmpty(dir) && _HttpContext == null) { - GXLogging.Debug(log, "Searching for txt files .."); + GXLogging.Debug(Logger, "Searching for txt files .."); int srchIx = 0; - string[] paths = new string[] { ".\\", "..\\" }; + string[] paths = { Directory.GetCurrentDirectory(), Directory.GetParent(Directory.GetCurrentDirectory()).FullName}; bool found = false; while (!found && srchIx < paths.Length) { @@ -3685,12 +3795,12 @@ Hashtable Images } } imgDir = dir; - GXLogging.Debug(log, $"{imgDir} txt file found"); + GXLogging.Debug(Logger, $"{imgDir} txt file found"); } else { imageFiles = Directory.GetFiles(dir, "*.txt"); - GXLogging.Debug(log, "imageFiles found"); + GXLogging.Debug(Logger, "imageFiles found"); } string KBPrefix = String.Empty; @@ -3754,7 +3864,7 @@ Hashtable Images } catch (FileNotFoundException) { - GXLogging.Debug(log, $"{filename} file not found"); + GXLogging.Debug(Logger, $"{filename} file not found"); } } } @@ -3773,7 +3883,7 @@ public string GetImagePath(string id, string KBId, string theme) return ret; else { - GXLogging.Debug(log, "Image not found at Images.txt. Image id:", () => KBId + id + " language:" + lang + " theme:" + theme); + GXLogging.Debug(Logger, "Image not found at Images.txt. Image id:", () => KBId + id + " language:" + lang + " theme:" + theme); return id; } } @@ -3897,7 +4007,7 @@ static public DateTime StartupDate public void SetSubmitInitialConfig(IGxContext context) { - GXLogging.Debug(log, "SetSubmitInitialConfig:", () => _handle.ToString() + " clientid:" + context.ClientID); + GXLogging.Debug(Logger, "SetSubmitInitialConfig:", () => _handle.ToString() + " clientid:" + context.ClientID); this._isSumbited = true; this.SetDefaultTheme(context.GetTheme(), context.GetThemeisDSO()); this.SetPhysicalPath(context.GetPhysicalPath()); diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs index 1f9e63a42..7b029ad64 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs @@ -11,7 +11,6 @@ namespace GeneXus.Utils using Cache; using GeneXus.Application; using GeneXus.Services; - using log4net; using TZ4Net; public class GxMail @@ -267,7 +266,7 @@ static string NormalizeNewLine(string value) public class FileIO { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.FileIO)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); const short GX_ASCDEL_BADFMTSTR = -10; const short GX_ASCDEL_WRITEERROR = -9; const short GX_ASCDEL_INVALIDDATE = -7; @@ -772,6 +771,7 @@ public short dfwnext() return GX_ASCDEL_WRITEERROR; } } + const int MAX_DECIMAL_PRECISION = 29; public short dfwpnum(decimal num, int dec) { if (_writeStatus == FileIOStatus.Closed) @@ -779,7 +779,7 @@ public short dfwpnum(decimal num, int dec) GXLogging.Error(log, "Error ADF0004"); return GX_ASCDEL_INVALIDSEQUENCE; } - appendFld(StringUtil.Str(num, 18, dec).TrimStart(null)); + appendFld(StringUtil.Str(num, MAX_DECIMAL_PRECISION, dec).TrimStart(null)); return GX_ASCDEL_SUCCESS; } public short dfwptxt(string s, int len) diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs index 91394ffe7..3106475bf 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs @@ -1,6 +1,5 @@ using GeneXus.Application; using GeneXus.Configuration; -using log4net; using System; using System.Collections; using System.Diagnostics; @@ -20,7 +19,6 @@ using System.Net; #endif using NodaTime; -using NodaTime.TimeZones; using NUglify; using NUglify.Html; using GeneXus.Web.Security; @@ -45,9 +43,10 @@ using GeneXus.Services; using GeneXus.Http; using System.Security; -using System.Threading.Tasks; using System.Drawing.Imaging; using System.Net.Http.Headers; +using Image = System.Drawing.Image; +using System.Net.Http; namespace GeneXus.Utils { @@ -88,7 +87,7 @@ public static void NextBytes(byte[] result) public class NumberUtil { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.NumberUtil)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private NumberFormatInfo numberFormat; public NumberUtil(NumberFormatInfo numFmt) @@ -502,7 +501,7 @@ public string FormatValid(string oldText) public class StringUtil { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.StringUtil)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private NumberFormatInfo numFmtInfo; private const int STR_LEN_DEFAULT = 10; @@ -511,8 +510,8 @@ public class StringUtil const char QMARK = '_'; static char[] numbersAndSep = new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' }; static char[] numbers = new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9' }; - internal static Dictionary LogUserEntryWhiteList = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890+-_=/[]{}\":, ".ToDictionary(item => item, item => item); - internal static Dictionary HttpHeaderWhiteList = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890.;+-_=/[]{}\"':, @()?<>\\".ToDictionary(item => item, item => item); + internal static Dictionary LogUserEntryWhiteList = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890.;+-_=/[]{}\"':, @()?<>\\&".ToDictionary(item => item, item => item); + internal static Dictionary HttpHeaderWhiteList = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890.;+-_=/[]{}\"':, @()?<>\\&".ToDictionary(item => item, item => item); internal static string Sanitize(string input, Dictionary WhiteList) { @@ -666,18 +665,83 @@ private enum FORMAT_SECTION NEGATIVE_VALUES = -1, ZEROS = 0 } - private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsLiterals, FORMAT_SECTION section) + const char ESCAPE_CHARACTER = '\\'; + const char NUMBER_SIGN = '#'; + const char QUESTION_MARK = '?'; + const char BLANK = ' '; + static bool EscapedSymbol(string picture, int idx) + { + return (idx > 0 && picture[idx - 1] == ESCAPE_CHARACTER); + } + static int LeadingBlanks(string gxpicture) + { + int leadingBlanks = 0; + bool inBlanks = false; + if (gxpicture.Contains(QUESTION_MARK)) { + for (int i = 0; i < gxpicture.Length; i++) + { + if (gxpicture[i] == QUESTION_MARK && !EscapedSymbol(gxpicture, i)) + { + inBlanks = true; + } + else if ((gxpicture[i] == '.') && !EscapedSymbol(gxpicture, i)) + { + inBlanks = false; + break; + } + + if (inBlanks) + leadingBlanks++; + } + } + return leadingBlanks; + } + static int TrailingBlanks(string gxpicture, out bool decimalsAsBlank) + { + int trailingBlanks = 0; + int sep = gxpicture.IndexOf('.'); + decimalsAsBlank = false; + if (sep >= 0) + { + string rightPic = gxpicture.Substring(sep); + if (rightPic.Contains(QUESTION_MARK)) + { + for (int i = gxpicture.Length-1; i >= 0; i--) + { + if (gxpicture[i] == QUESTION_MARK && !EscapedSymbol(gxpicture, i)) + trailingBlanks++; + else if (gxpicture[i] == '.') + { + decimalsAsBlank = true; + break; + } + else if (gxpicture[i] == NUMBER_SIGN || gxpicture[i] == 'Z' || gxpicture[i] == '9') + { + decimalsAsBlank = false; + break; + } + } + } + } + return trailingBlanks; + } + + private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsLiterals, FORMAT_SECTION section, int digits, int decimals) { if (string.IsNullOrEmpty(gxpicture)) return string.Empty; bool inDecimals = false; StringBuilder strPicture = new StringBuilder("{0,"); - strPicture.Append(gxpicture.Length); + strPicture.Append(PictureLength(gxpicture)); strPicture.Append(':'); bool blankwhenzero = true; bool explicitSign = (gxpicture[0] == '+'); bool withoutMinusSign = (gxpicture[0] == '(' && gxpicture[gxpicture.Length - 1] == ')') || gxpicture.EndsWith("DB") || explicitSign; + int totalLeadingBlanks = LeadingBlanks(gxpicture); + int totalRighBlanks = TrailingBlanks(gxpicture, out bool decimalsAsBlank); + int lBlanks = 0; + int rDigits = 0; if (section == FORMAT_SECTION.NEGATIVE_VALUES && withoutMinusSign) //If it has a sign, then use the first section (which by default assigns only a negative sign). @@ -689,9 +753,34 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL strPicture.Append(';');//Section Separator. strPicture.Append(';');//Section Separator. } + if (gxpicture.IndexOf(ESCAPE_CHARACTER) >= 0) + { + StringBuilder gxEscapedPicture = new StringBuilder(); + for (int i = 0; i < gxpicture.Length; i++) + { + + if (gxpicture[i] == ESCAPE_CHARACTER && i + 1 < gxpicture.Length && gxpicture[i + 1] == ESCAPE_CHARACTER) + { + gxEscapedPicture.Append(ESCAPE_CHARACTER); + gxEscapedPicture.Append(ESCAPE_CHARACTER); + gxEscapedPicture.Append(ESCAPE_CHARACTER); + i++; + } + else + { + gxEscapedPicture.Append(gxpicture[i]); + } + } + gxpicture = gxEscapedPicture.ToString(); + } for (int i = 0; i < gxpicture.Length; i++) { - if (gxpicture[i] == 'Z') + bool inLiteral = EscapedSymbol(gxpicture, i); + if (inLiteral || gxpicture[i] == ESCAPE_CHARACTER) + { + strPicture.Append(gxpicture[i]); + } + else if (gxpicture[i] == 'Z') { if (inDecimals) { @@ -702,7 +791,39 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL strPicture.Append('0'); } else - strPicture.Append('#'); + strPicture.Append(NUMBER_SIGN); + } + else if (gxpicture[i] == NUMBER_SIGN) + { + strPicture.Append(NUMBER_SIGN); + } + else if (gxpicture[i] == QUESTION_MARK) + { + if (inDecimals) + { + if (rDigits >= decimals && rDigits < totalRighBlanks) + { + strPicture.Append(BLANK); + } + else + { + strPicture.Append(NUMBER_SIGN); + } + rDigits++; + } + else + { + if (lBlanks < (totalLeadingBlanks - digits)) + { + strPicture.Append(BLANK); + lBlanks++; + } + else + { + strPicture.Append(NUMBER_SIGN); + } + } + } else if (gxpicture[i] == '9') { @@ -712,11 +833,20 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL else if (gxpicture[i] == '.') { inDecimals = true; - if (i > 0 && strPicture[strPicture.Length - 1] == '#') strPicture[strPicture.Length - 1] = '0'; + if (i > 0 && strPicture[strPicture.Length - 1] == NUMBER_SIGN) strPicture[strPicture.Length - 1] = '0'; if (separatorsAsLiterals) strPicture.Append("\".\""); else - strPicture.Append(gxpicture[i]); + { + if (decimalsAsBlank && decimals == 0) + { + strPicture.Append(BLANK); //Replace decimal separator by blank + } + else + { + strPicture.Append(gxpicture[i]); + } + } } else if (gxpicture[i] == ',') { @@ -725,6 +855,10 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL else strPicture.Append(gxpicture[i]); } + else if (gxpicture[i] == BLANK) + { + strPicture.Append(BLANK); + } else { //0,#,.,,,%,E0, E+0, E-0,e0,e+0,e-0, are characters with special meaning in Custom Numeric Format Strings of .net @@ -741,7 +875,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL //Pictures (99.9) => 12.5 -12.5 if (section != FORMAT_SECTION.NEGATIVE_VALUES && withoutMinusSign && (i == 0 || i == gxpicture.Length - 1)) { - strPicture.Append(' '); + strPicture.Append(BLANK); } else { @@ -752,7 +886,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL //Pictures +99.9 => +12.5 -12.5 if (explicitSign && i == 0 && section == FORMAT_SECTION.ZEROS) { - strPicture.Append(' '); + strPicture.Append(BLANK); } else if (explicitSign && i == 0 && section == FORMAT_SECTION.NEGATIVE_VALUES) { @@ -770,7 +904,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL if (section == FORMAT_SECTION.POSITIVE_VALUES) strPicture.Append('C'); else - strPicture.Append(' '); + strPicture.Append(BLANK); } else { @@ -783,7 +917,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL if (section == FORMAT_SECTION.POSITIVE_VALUES) strPicture.Append('R'); else - strPicture.Append(' '); + strPicture.Append(BLANK); } else { @@ -800,14 +934,29 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL } if (blankwhenzero && section == FORMAT_SECTION.ZEROS)//Z,ZZZ,ZZZ.ZZ format 0.00 to "". sac.20145 { - return Replicate(' ', gxpicture.Length); + return Replicate(BLANK, gxpicture.Length); } else { return strPicture.Append('}').ToString(); } } - static bool useLiteralSeparators(string gxpicture) + + private static int PictureLength(string gxpicture) + { + int count = 0; + if (gxpicture.Contains(ESCAPE_CHARACTER)) + { + foreach (char ch in gxpicture) + { + if (ch == ESCAPE_CHARACTER) + count++; + } + } + return gxpicture.Length - count; + } + + static bool UseLiteralSeparators(string gxpicture) { // If it has non-numerical characters, then the separators are used as literals @@ -862,7 +1011,7 @@ public static string GetPictureFromLD(int digits, int decimals, int thousandSepL } else { - str.Append('#'); + str.Append(NUMBER_SIGN); } } } @@ -878,14 +1027,14 @@ public static string GetPictureFromLD(int digits, int decimals, int thousandSepL } public static string Concat(string init, string last) { - char[] trimChars = { ' ' }; + char[] trimChars = { BLANK }; StringBuilder fmtString = new StringBuilder(init.TrimEnd(trimChars)); fmtString.Append(last); return fmtString.ToString(); } public static string Concat(string init, string last, string separator) { - char[] trimChars = { ' ' }; + char[] trimChars = { BLANK }; StringBuilder fmtString = new StringBuilder(init.TrimEnd(trimChars)); fmtString.Append(separator); fmtString.Append(last); @@ -1012,7 +1161,7 @@ static public int Len(string s) static public string Trim(string s) { if (!string.IsNullOrEmpty(s)) - return s.Trim(' '); + return s.Trim(BLANK); else return s; } @@ -1021,8 +1170,8 @@ static public string RTrim(string s) if (!string.IsNullOrEmpty(s)) { int len = s.Length; - if (len > 0 && s[len - 1] == ' ') - return s.TrimEnd(' '); + if (len > 0 && s[len - 1] == BLANK) + return s.TrimEnd(BLANK); else return s; } @@ -1034,8 +1183,8 @@ static public string LTrim(string s) if (!string.IsNullOrEmpty(s)) { int len = s.Length; - if (len > 0 && s[0] == ' ') - return s.TrimStart(' '); + if (len > 0 && s[0] == BLANK) + return s.TrimStart(BLANK); else return s; } @@ -1167,9 +1316,14 @@ public string Format(decimal value, string gxpicture) { section = FORMAT_SECTION.ZEROS; } - bool separatorsAsLiterals = useLiteralSeparators(gxpicture); + bool separatorsAsLiterals = UseLiteralSeparators(gxpicture); + string invariantStrValue = Math.Abs(value).ToString(CultureInfo.InvariantCulture.NumberFormat); + int decSeparatorIdx = invariantStrValue.IndexOf(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator); + + int digits = WholeDigits(value, invariantStrValue, decSeparatorIdx); + int decimals = DecimalDigits(invariantStrValue, decSeparatorIdx); - string picture = GxPictureToNetPicture(gxpicture, separatorsAsLiterals, section); + string picture = GxPictureToNetPicture(gxpicture, separatorsAsLiterals, section, digits, decimals); //It must consider format because it can have other types of characters that are not Z or 9 or. neither ,. string res; if (!string.IsNullOrEmpty(picture)) @@ -1178,7 +1332,7 @@ public string Format(decimal value, string gxpicture) } else { - res = value.ToString(CultureInfo.InvariantCulture.NumberFormat); + res = invariantStrValue; } if (separatorsAsLiterals) { @@ -1192,7 +1346,35 @@ public string Format(decimal value, string gxpicture) { return ReplaceSeparators(res, numFmtInfo.NumberDecimalSeparator, numFmtInfo.NumberGroupSeparator); } + } + int DecimalDigits(string invariantStrValue, int decSeparatorIdx) + { + int decimals; + if (decSeparatorIdx < 0) + decimals = 0; + else + decimals = invariantStrValue.Length - decSeparatorIdx - 1; + return decimals; + } + int WholeDigits(decimal value, string invariantStrValue, int decSeparatorIdx) + { + int digits; + if (value == 0) + { + digits = 1; + } + else if (value < 1 && value >= 0) + digits = 0; + else if (decSeparatorIdx < 0) + { + digits = invariantStrValue.Length; + } + else + { + digits = decSeparatorIdx; + } + return digits; } string FormatNumber(string s, string p) { @@ -1219,7 +1401,7 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep) pStart = p.Length; else pDec = p.Length - pStart; // decimal count (including point) - StringBuilder result = new StringBuilder(new string(' ', Math.Max(p.Length, s.Length))); + StringBuilder result = new StringBuilder(new string(BLANK, Math.Max(p.Length, s.Length))); // Process the left of the decimal point j = sStart - 1; k = pStart - 1; @@ -1230,7 +1412,7 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep) case '9': if (j < 0) result[k--] = '0'; - else if (s[j] == ' ') + else if (s[j] == BLANK) result[k--] = '0'; else result[k--] = s[j]; @@ -1238,10 +1420,10 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep) break; case 'Z': if (j < 0) - result[k--] = ' '; + result[k--] = BLANK; else if (leftZ || leftZero(s, j)) { - result[k--] = ' '; + result[k--] = BLANK; leftZ = true; } else @@ -1289,7 +1471,7 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep) case 'Z': if (rightZ || rightZero(s, j)) { - result[i] = ' '; + result[i] = BLANK; rightZ = true; } else if (j < s.Length) @@ -1393,7 +1575,7 @@ static public bool ToBoolean(string value) } public static string Space(int spaces) { - return new string(' ', spaces); + return new string(BLANK, spaces); } public static string Right(string text, int size) { @@ -1419,7 +1601,7 @@ public static string Left(string text, int size) static public bool Like(string str, string ptrn) { - return Like(str, ptrn, ' '); + return Like(str, ptrn, BLANK); } static public bool Like(string str, string ptrn, char escape) { @@ -1444,10 +1626,10 @@ static public bool Like(string str, string ptrn, char escape) srchPtr = 0; scapeCount = 0; - wildChr = ' '; - srchChr = ' '; + wildChr = BLANK; + srchChr = BLANK; - bool useEscape = escape != ' '; + bool useEscape = escape != BLANK; bool isEscape = false; bool applyEscape = false; @@ -1464,7 +1646,7 @@ static public bool Like(string str, string ptrn, char escape) if (srchPtr <= srchLen) srchChr = str[srchPtr - scapeCount]; else - srchChr = ' '; + srchChr = BLANK; } if (isEscape) @@ -1625,7 +1807,7 @@ public static string JSONEncode(string s) sb.Append("\\r"); else { - if (ch < ' ') + if (ch < BLANK) { sb.Append("\\u"); sb.Append(((int)ch).ToString("x4", CultureInfo.InvariantCulture)); @@ -1993,7 +2175,8 @@ public static string ToCamelCase(this string str) } public class DateTimeUtil { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.DateTimeUtil)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private CultureInfo cultureInfo; public static bool useConfigForDates; public static bool useConfigForTimes; @@ -2430,9 +2613,9 @@ static public short CurrentOffset(IGxContext context) static private TimeSpan CurrentOffset(string clientTimeZone) { - DateTimeZone clientTimeZoneObj = DateTimeZoneProviders.Tzdb[clientTimeZone]; + DateTimeZone clientTimeZoneObj = DateTimeZoneProviders.Tzdb.GetZoneOrNull(clientTimeZone); if (clientTimeZoneObj == null) - clientTimeZoneObj = DateTimeZoneProviders.Tzdb[LocalTimeZoneId]; + clientTimeZoneObj = DateTimeZoneProviders.Tzdb.GetZoneOrNull(LocalTimeZoneId); Instant now = SystemClock.Instance.GetCurrentInstant(); try @@ -2532,7 +2715,7 @@ public DateTime YMDToD(int year, int month, int day) { try { - return new DateTime(fourDigitsYear, month, day, cultureInfo.Calendar); + return new DateTime(fourDigitsYear, month, day, cultureInfo.DateTimeFormat.Calendar); } catch { @@ -2656,18 +2839,18 @@ public static DateTime CToD2(string value) } return nullDate; } - internal static DateTime CToDT2(string jsonDate) { + internal static DateTime CToDT2(string jsonDate, IGxContext context) { if (!string.IsNullOrEmpty(jsonDate) && (jsonDate.Contains(ISO_8601_TIME_SEPARATOR) || jsonDate.Contains(ISO_8601_TIME_SEPARATOR_1))) { - return CToT2(jsonDate); + return CToT2(jsonDate, context); } else { return CToD2(jsonDate); } } - public static DateTime CToT2(string value) + public static DateTime CToT2(string value, IGxContext context) { if (isNullJsonDate(value)) return nullDate; @@ -2684,38 +2867,76 @@ public static DateTime CToT2(string value) } } if (Preferences.useTimezoneFix()) - ret = fromUniversalTime(ret); + { + if (context==null) + ret = fromUniversalTime(ret); + else + ret = fromUniversalTime(ret, context); + } return ret; } } + //[Obsolete("CToT2 is deprecated, use CToT2(string, IGxContext) instead", false)] + public static DateTime CToT2(string value) + { + return CToT2(value, null); + } + //[Obsolete("TToC2 is deprecated, use TToC2(DateTime, IGxContext) instead", false)] public static string TToC2(DateTime dt) { return TToC2(dt, true); } + public static string TToC2(DateTime dt, IGxContext context) + { + return TToC2(dt, true, context); + } + //[Obsolete("TToC2 is deprecated, use TToC2(DateTime, bool, IGxContext) instead", false)] public static string TToC2(DateTime dt, bool toUTC) { - return TToCRest(dt, "0000-00-00T00:00:00", JsonDateFormat, toUTC); + return TToCRest(dt, "0000-00-00T00:00:00", JsonDateFormat, null, toUTC); } + internal static string TToC2(DateTime dt, bool toUTC, IGxContext context) + { + return TToCRest(dt, "0000-00-00T00:00:00", JsonDateFormat, context, toUTC); + } + //[Obsolete("TToC3 is deprecated, use TToC3(DateTime, IGxContext) instead", false)] public static string TToC3(DateTime dt) { return TToC3(dt, true); } + public static string TToC3(DateTime dt, IGxContext context) + { + return TToC3(dt, true, context); + } internal const string JsonDateFormatMillis = "yyyy-MM-ddTHH:mm:ss.fff"; internal const string JsonDateFormat = "yyyy-MM-ddTHH:mm:ss"; - + + //[Obsolete("TToC3 is deprecated, use TToC3(DateTime, bool, IGxContext) instead", false)] public static string TToC3(DateTime dt, bool toUTC) { - return TToCRest(dt, "0000-00-00T00:00:00.000", JsonDateFormatMillis, toUTC); + return TToCRest(dt, "0000-00-00T00:00:00.000", JsonDateFormatMillis, null, toUTC); + } + internal static string TToC3(DateTime dt, bool toUTC, IGxContext context) + { + return TToCRest(dt, "0000-00-00T00:00:00.000", JsonDateFormatMillis, context, toUTC); } - static string TToCRest(DateTime dt, String nullString, String formatStr, bool toUTC=true) + static string TToCRest(DateTime dt, String nullString, String formatStr, IGxContext context, bool toUTC=true) { if (dt == nullDate) return FormatEmptyDate(nullString); else { - DateTime ret = Preferences.useTimezoneFix() ? (toUTC ? toUniversalTime(dt) : dt) : dt; + DateTime ret; + if (context != null) + { + ret = Preferences.useTimezoneFix() ? (toUTC ? toUniversalTime(dt, context) : dt) : dt; + } + else + { + ret = Preferences.useTimezoneFix() ? (toUTC ? toUniversalTime(dt) : dt) : dt; + } return ret.ToString(formatStr, CultureInfo.InvariantCulture); } } @@ -3246,7 +3467,7 @@ private static DateTime ConvertDateTime(DateTime dt, string fromTimezone, string DateTime ret = toUniversalTime(dt, fromTimezone); - if (string.IsNullOrEmpty(toTimezone) || DateTimeZoneProviders.Tzdb[toTimezone]==null) + if (!ValidTimeZone(toTimezone)) toTimezone = DateTimeZoneProviders.Tzdb.GetSystemDefault().Id; if (ret < OlsonMinTime) @@ -3260,6 +3481,11 @@ private static DateTime ConvertDateTime(DateTime dt, string fromTimezone, string } return dtconverted.AddMilliseconds(milliSeconds); } + internal static bool ValidTimeZone(string timeZone) + { + return !string.IsNullOrEmpty(timeZone) && DateTimeZoneProviders.Tzdb.GetZoneOrNull(timeZone) != null; + } + internal static DateTime Local2DBserver(DateTime dt, string clientTimezone) { try @@ -3531,7 +3757,7 @@ static private bool isNullDateCompatible(DateTime dt) else return isNullDate(dt); } - + //[Obsolete("fromUniversalTime is deprecated, use fromUniversalTime(DateTime, IGxContext) instead", false)] static public DateTime fromUniversalTime(DateTime dt) { #if NODATIME @@ -3540,7 +3766,15 @@ static public DateTime fromUniversalTime(DateTime dt) return isNullDateCompatible(dt) ? dt : fromUniversalTime(dt, GxContext.Current.GetOlsonTimeZone()); #endif } - + static public DateTime fromUniversalTime(DateTime dt, IGxContext context) + { +#if NODATIME + return isNullDateCompatible(dt) ? dt : fromUniversalTime(dt, context.GetTimeZone()); +#else + return isNullDateCompatible(dt) ? dt : fromUniversalTime(dt, context.GetOlsonTimeZone()); +#endif + } + //[Obsolete("toUniversalTime is deprecated, use toUniversalTime(DateTime, IGxContext) instead", false)] static public DateTime toUniversalTime(DateTime dt) { #if NODATIME @@ -3562,7 +3796,10 @@ static public DateTime toUniversalTime(DateTime dt, IGxContext context) static public DateTime FromTimeZone(DateTime dt, String sTZ, IGxContext context) { #if NODATIME - return ConvertDateTime(dt, sTZ, context.GetTimeZone()); + if (ValidTimeZone(sTZ)) + return ConvertDateTime(dt, sTZ, context.GetTimeZone()); + else + return dt; #else OlsonTimeZone fromTimeZone = TimeZoneUtil.GetInstanceFromOlsonName(sTZ); if (fromTimeZone != null) @@ -3575,7 +3812,7 @@ static public DateTime FromTimeZone(DateTime dt, String sTZ, IGxContext context) public class FileUtil { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.FileUtil)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public static byte DeleteFile(string fileName) { try @@ -3650,7 +3887,9 @@ public static string GetBasePath() #endif public static string GetStartupDirectory() { +#pragma warning disable SYSLIB0044 string dir = Assembly.GetCallingAssembly().GetName().CodeBase; +#pragma warning restore SYSLIB0012 Uri uri = new Uri(dir); return Path.GetDirectoryName(uri.LocalPath); } @@ -4005,7 +4244,7 @@ internal static string SafeCombine(string basePath, string fileName) public class GXUtil { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.GXUtil)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); #if !NETCORE static Hashtable domains; static string DOMAINS_FILE; @@ -5445,15 +5684,22 @@ public static void ErrorToMessages(string errorId, string errorDescription, GXBa } } public static void ErrorToMessages(string errorId, Exception ex, GXBaseCollection Messages) + { + ErrorToMessages(errorId, ex, Messages, true); + } + internal static void ErrorToMessages(string errorId, Exception ex, GXBaseCollection Messages, bool parseInnerExceptions) { if (Messages != null && ex != null) { StringBuilder str = new StringBuilder(); str.Append(ex.Message); - while (ex.InnerException != null) + if (parseInnerExceptions) { - str.Append(ex.InnerException.Message); - ex = ex.InnerException; + while (ex.InnerException != null) + { + str.Append(ex.InnerException.Message); + ex = ex.InnerException; + } } ErrorToMessages(errorId, str.ToString(), Messages); } @@ -5541,7 +5787,7 @@ public string IniReadValue(string Section, string Key) #endif public static class GXDbFile { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.GXDbFile)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GXDbFile).FullName); private static Regex schemeRegex = new Regex("^" + Scheme + ":", RegexOptions.Compiled); @@ -5762,24 +6008,68 @@ public static string PathToUrl(string path, bool absUrl, IGxContext context = nu public static class GxImageUtil { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxImageUtil)); - + static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GxImageUtil).FullName); private static Bitmap BitmapCreateFromStream(string filePathOrUrl) { - using (Stream s = ImageFile(filePathOrUrl).GetStream()) + Uri uri; + if (Uri.TryCreate(filePathOrUrl, UriKind.Absolute, out uri) && (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) { - if (s != null) - return new Bitmap(s); - return null; + using (HttpClient httpClient = new HttpClient()) + { + try + { + byte[] data = httpClient.GetByteArrayAsync(uri).Result; + using (MemoryStream mem = new MemoryStream(data)) + { + return new Bitmap(mem); + } + } + catch + { + return null; + } + } + } + else + { + using (Stream s = ImageFile(filePathOrUrl).GetStream()) + { + if (s != null) + return new Bitmap(s); + return null; + } } + } private static Image ImageCreateFromStream(string filePathOrUrl) { - using (Stream s = ImageFile(filePathOrUrl).GetStream()) + Uri uri; + if (Uri.TryCreate(filePathOrUrl, UriKind.Absolute, out uri) && (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) { - if (s != null) - return Image.FromStream(s); - return null; + using (HttpClient httpClient = new HttpClient()) + { + try + { + byte[] data = httpClient.GetByteArrayAsync(uri).Result; + using (MemoryStream mem = new MemoryStream(data)) + { + return Image.FromStream(mem); + } + } + catch + { + return null; + } + } + } + else + { + using (Stream s = ImageFile(filePathOrUrl).GetStream()) + { + if (s != null) + return Image.FromStream(s); + return null; + } } } @@ -5866,6 +6156,71 @@ public static string Crop(string imageFile, int X, int Y, int Width, int Height) } return modifiedImage; } + public static string RoundBorders(string imageFile, int topLeftRadius, int topRightRadius, int bottomLeftRadius, int bottomRightRadius) + { + string modifiedImage = string.Empty; + try + { + using (MemoryStream ms = new MemoryStream()) + { + using (Image OriginalImage = ImageCreateFromStream(imageFile)) + { + int w = OriginalImage.Width; + int h = OriginalImage.Height; + + using (Bitmap roundedImage = new Bitmap(w, h)) + { + roundedImage.SetResolution(OriginalImage.HorizontalResolution, OriginalImage.VerticalResolution); + using (Graphics g = Graphics.FromImage(roundedImage)) + { + g.SmoothingMode = SmoothingMode.AntiAlias; + g.Clear(Color.Transparent); + + using (GraphicsPath path = new GraphicsPath()) + { + path.StartFigure(); + if (topLeftRadius > 0) + path.AddArc(0, 0, 2 * topLeftRadius, 2 * topLeftRadius, (float) 180, (float) 90); + path.AddLine(topLeftRadius, 0, w - topRightRadius, 0); + if (topRightRadius > 0) + path.AddArc(w - 2 * topRightRadius, 0, 2 * topRightRadius, 2 * topRightRadius, (float) 270, (float) 90); + path.AddLine(w, topRightRadius, w, h - bottomRightRadius); + if (bottomRightRadius > 0) + path.AddArc(w - 2 * bottomRightRadius, h - 2 * bottomRightRadius, 2 * bottomRightRadius, 2 * bottomRightRadius, (float) 0, (float) 90); + path.AddLine(w - bottomLeftRadius, h, bottomLeftRadius, h); + if (bottomLeftRadius > 0) + path.AddArc(0, h - 2 * bottomLeftRadius, 2 * bottomLeftRadius, 2 * bottomLeftRadius, (float) 90, (float) 90); + path.CloseFigure(); + + g.FillPath(Brushes.White, path); + + using (Bitmap bitmap = new Bitmap(OriginalImage)) + { + using (TextureBrush br = new TextureBrush(bitmap, WrapMode.Clamp)) + { + g.FillPath(br, path); + } + } + } + } + // Rounded images are basically images with transparent rounded borders and jpg and jpeg formats do not + // support transparency, so we have to create a new identical image but in png format + if (imageFile.IndexOf(".jpg") > 0) + modifiedImage = Save(roundedImage, imageFile.Replace(".jpg", ".png"), ImageFormat.Png); + else if (imageFile.IndexOf(".jpeg") > 0) + modifiedImage = Save(roundedImage, imageFile.Replace(".jpeg", ".png"), ImageFormat.Png); + else + modifiedImage = Save(roundedImage, imageFile, OriginalImage.RawFormat); + } + } + } + } + catch (Exception ex) + { + GXLogging.Error(log, $"RoundBorders {imageFile} failed", ex); + } + return modifiedImage; + } public static string Rotate(string imageFile, int angle) { string modifiedImage = string.Empty; @@ -5953,12 +6308,20 @@ public static string Save(Image bitmap, string imageFile, ImageFormat format) ms.Position = 0; try { - GxFile sourceImage = new GxFile(GxContext.StaticPhysicalPath(), imageFile); - string destinationImageName = FileUtil.getTempFileName(sourceImage.GetDirectory().GetAbsoluteName(), Path.GetFileNameWithoutExtension(sourceImage.GetName()), sourceImage.GetExtension()); - GxFile destinationImage = new GxFile(GxContext.StaticPhysicalPath(), destinationImageName); + if ((imageFile.StartsWith("http://") || imageFile.StartsWith("https://")) && ServiceFactory.GetExternalProvider() == null) + { + Uri uri = new Uri(imageFile); + imageFile = Path.GetFileName(uri.AbsolutePath); + } + string destinationImageName = FileUtil.getTempFileName( + "", + Path.GetFileNameWithoutExtension(imageFile), + Path.GetExtension(imageFile).TrimStart('.') + ); + GxFile destinationImage = new GxFile(Preferences.getTMP_MEDIA_PATH(), destinationImageName, GxFileType.PrivateAttribute); destinationImage.Create(ms); destinationImage.Close(); - destinationImagePath = destinationImage.GetAbsoluteName(); + destinationImagePath = destinationImage.GetURI(); } catch (Exception ex) { @@ -6075,7 +6438,7 @@ public static string EncodeNonAsciiCharacters(string value) } internal class ThreadUtil { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(ThreadUtil)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static ConcurrentDictionary events = new ConcurrentDictionary(); const int MAX_WAIT_HANDLES = 64; internal static void Submit(WaitCallback callbak, object state) diff --git a/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs b/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs index dbb4e1092..f834c38b4 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs @@ -148,7 +148,16 @@ public void AppendHeader( string name, string value) { value = GXUtil.EncodeContentDispositionHeader(value, _context.GetBrowserType()); } - if (_context!=null) +#if !NETCORE + else if (string.Compare(name,"Content-Type", true) == 0) + { + if (string.Compare(value, "text/event-stream", true) == 0) + { + _context.HttpContext.Response.BufferOutput = false; + } + } +#endif + if (_context!=null) _context.SetHeader(name, value); } diff --git a/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs b/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs index 6f16bd50a..b78405faa 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs @@ -2,7 +2,9 @@ namespace GeneXus.Http { using System; using System.IO; +#if !NETCORE using Jayrock.Json; +#endif using System.Web; using GeneXus.Application; using GeneXus.Utils; @@ -17,9 +19,7 @@ namespace GeneXus.Http #else using Microsoft.AspNetCore.Http; #endif - using System.Diagnostics; using GeneXus.Configuration; - using log4net; public interface IHttpAjaxContext { @@ -44,12 +44,6 @@ public interface IHttpAjaxContext void AddStylesHidden(); void ajax_rsp_clear(); - JArray AttValues { get;} - JObject HiddenValues { get;} - JArray PropValues { get; } - JObject WebComponents { get;} - JObject Messages { get;} - JArray Grids { get; } void LoadFormVars(HttpContext localHttpContext); void disableJsOutput(); void enableJsOutput(); @@ -91,7 +85,7 @@ public void ClearParmsMetadata() public bool isInputParm(string key) { - return inParmsMetadataHash.Contains(key); + return (inParmsMetadataHash!=null && inParmsMetadataHash.Contains(key)); } public void Clear() @@ -111,7 +105,7 @@ public bool isParmModified(string fieldName, object value) IGxJSONSerializable jsonValue = value as IGxJSONSerializable; if (jsonValue!=null) { - if (!inParmsHashValue.ContainsKey(fieldName)) + if (inParmsHashValue!=null && !inParmsHashValue.ContainsKey(fieldName)) return true; return GXUtil.GetHash(jsonValue.ToJSonString()) != inParmsHashValue[fieldName]; } @@ -122,7 +116,7 @@ public bool isParmModified(string fieldName, object value) public class HttpAjaxContext : IHttpAjaxContext { private IGxContext _context; - static readonly ILog log = log4net.LogManager.GetLogger(typeof(HttpAjaxContext)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private Stack cmpContents = new Stack(); private GXAjaxCommandCollection commands = new GXAjaxCommandCollection(); private JArray _AttValues = new JArray(); @@ -149,24 +143,24 @@ public class HttpAjaxContext : IHttpAjaxContext public string FormCaption { get; set; } - public JObject HiddenValues + internal JObject HiddenValues { get { return _HiddenValues; } } - public JArray AttValues + internal JArray AttValues { get { return _AttValues; } } - public JArray PropValues + internal JArray PropValues { get { return _PropValues; } } - public JObject WebComponents + internal JObject WebComponents { get { return _WebComponents; } } - public Hashtable LoadCommands + internal Hashtable LoadCommands { get { return _LoadCommands; } } - public JObject Messages + internal JObject Messages { get { return _Messages; } } - public JArray Grids + internal JArray Grids { get { return _Grids; } } - public JObject ComponentObjects + internal JObject ComponentObjects { get { return _ComponentObjects; } } public GXAjaxCommandCollection Commands { get { return commands; } } @@ -213,7 +207,7 @@ public void appendAjaxCommand(String cmdType, Object cmdData) commands.AppendCommand(new GXAjaxCommand(cmdType, cmdData)); } - public void appendLoadData(int SId, JObject Data) + internal void appendLoadData(int SId, JObject Data) { LoadCommands[SId] = Data; } @@ -352,7 +346,7 @@ public void ajax_rsp_assign_sdt_attri(String CmpContext, bool IsMasterPage, Stri try { JObject obj = GetGxObject(AttValues, CmpContext, IsMasterPage); - if (obj != null && (DynAjaxEventContext.isParmModified(AttName, SdtObj) || !isUndefinedOutParam( AttName, SdtObj))) + if (obj != null && (!isUndefinedOutParam(AttName, SdtObj) || DynAjaxEventContext.isParmModified(AttName, SdtObj))) { IGxJSONAble SdtObjJson = SdtObj as IGxJSONAble; if (SdtObjJson != null) @@ -670,7 +664,7 @@ internal string getJSONResponse(string cmpContext) return jsonCmdWrapper.ToString(); } - public static JArray GetParmsJArray(Object[] parms) + internal static JArray GetParmsJArray(Object[] parms) { JArray inputs = new JArray(); for (int i = 0; i < parms.Length; i++) @@ -691,29 +685,36 @@ public static JArray GetParmsJArray(Object[] parms) public string GetAjaxEncryptionKey() { - if (context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY) == null) + string ajaxKey = context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY); + if (ajaxKey == null) { - if(!RecoverEncryptionKey()) - context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY,CryptoImpl.GetRijndaelKey()); + string sessionKey; + if (!RecoverEncryptionKey(out sessionKey)) { + ajaxKey = CryptoImpl.GetRijndaelKey(); + context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY, ajaxKey); + } + else + { + ajaxKey = sessionKey; + } } - return context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY); + return ajaxKey; } - private bool RecoverEncryptionKey() + private bool RecoverEncryptionKey(out string sessionKey) { - if ( (context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY) == null)) + sessionKey = null; + if (context.HttpContext != null) { - if (context.HttpContext != null) + String clientKey = context.HttpContext.Request.Headers[CryptoImpl.AJAX_SECURITY_TOKEN]; + if (!string.IsNullOrEmpty(clientKey)) { - String clientKey = context.HttpContext.Request.Headers[CryptoImpl.AJAX_SECURITY_TOKEN]; - if (!string.IsNullOrEmpty(clientKey)) + bool correctKey; + clientKey = CryptoImpl.DecryptRijndael(CryptoImpl.GX_AJAX_PRIVATE_IV + clientKey, CryptoImpl.GX_AJAX_PRIVATE_KEY, out correctKey); + if (correctKey) { - bool correctKey = false; - clientKey = CryptoImpl.DecryptRijndael(CryptoImpl.GX_AJAX_PRIVATE_IV + clientKey, CryptoImpl.GX_AJAX_PRIVATE_KEY, out correctKey); - if (correctKey) - { - context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY, clientKey); - return true; - } + sessionKey = clientKey; + context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY, clientKey); + return true; } } } @@ -747,7 +748,7 @@ public void LoadFormVars(HttpContext localHttpContext) } } - public void ParseGXState(JObject tokenValues) + internal void ParseGXState(JObject tokenValues) { if (tokenValues != null) { @@ -824,7 +825,7 @@ public string Content } } - public class GXJObject : JObject + internal class GXJObject : JObject { private bool base64Encoded; @@ -885,7 +886,7 @@ public object Data } } - public JObject JSONObject + internal JObject JSONObject { get { @@ -1030,7 +1031,7 @@ private GXAjaxCommand GetCommand(GXAjaxCommand cmd) return null; } - public JArray JSONArray + internal JArray JSONArray { get { diff --git a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs index e387eaefa..48dc0151d 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs @@ -29,7 +29,8 @@ namespace GeneXus.Configuration public class Config { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Configuration.Config)); + private static IGXLogger log = null; + public const string GX_LANG_SPA = "spa"; public const string GX_LANG_POR = "por"; public const string GX_LANG_ITA = "ita"; @@ -42,7 +43,7 @@ public class Config private static string configFileName; public static string loadedConfigFile; private static bool configLog = true; - private static bool configLoaded; + internal static bool configLoaded; static NameValueCollection _config; static ConcurrentDictionary s_confMapping; const string CONFMAPPING_FILE = "confmapping.json"; @@ -156,7 +157,25 @@ public static bool GetValueOf(string sId, out string sString) return false; } } - + internal static bool GetValueOrEnvironmentVarOf(string sId, out string sString) + { + try + { + sString = config.Get(sId); + if (String.IsNullOrEmpty(sString)) + { + sString = MappedValue(sId, sString); + if (string.IsNullOrEmpty(sString)) + return false; + } + return true; + } + catch + { + sString = string.Empty; + return false; + } + } public static bool GetValueOf(string sId) { string sString; @@ -429,7 +448,7 @@ public static CultureInfo GetCultureForLang(string lang) } #if NETCORE - public static IConfiguration ConfigRoot { get; set; } + public static IConfiguration ConfigRoot { get ; set; } const string Log4NetShortName = "log4net"; static Version Log4NetVersion = new Version(2, 0, 15); @@ -439,13 +458,13 @@ public static CultureInfo GetCultureForLang(string lang) const string ITextSharpFileName = "iTextSharp.dll"; const string ITextSharpAssemblyName = "itextsharp"; private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) - { + { var requestedAssembly = new AssemblyName(args.Name); if (requestedAssembly.Name == Log4NetShortName){ requestedAssembly.Version = Log4NetVersion; return Assembly.Load(requestedAssembly); } - + if (args.Name.StartsWith(ConfigurationManagerBak)) { string fileName = Path.Combine(FileUtil.GetStartupDirectory(), ConfigurationManagerFileName); @@ -465,11 +484,12 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven else { AssemblyName assName = new AssemblyName(args.Name); - bool strongNamedAssembly = assName.GetPublicKeyToken().Length > 0; string fileName = Path.Combine(FileUtil.GetStartupDirectory(), $"{assName.Name}.dll"); - if (!strongNamedAssembly && File.Exists(fileName)) + if (File.Exists(fileName)) { - return Assembly.LoadFrom(fileName); + Assembly assm = Assembly.LoadFrom(fileName); + if (assm!=null && assm.GetName().Version == assName.Version) + return assm; } } return null; @@ -485,6 +505,7 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven }; static string GeoTypesAssembly = "Microsoft.SqlServer.Types"; + static string DiagnosticSourceAssembly = "System.Diagnostics.DiagnosticSource"; [SecurityCritical] private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { @@ -498,7 +519,16 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven { return SQLGeographyWrapper.GeoAssembly; } - else return null; + else if (requestedAssembly.Name == DiagnosticSourceAssembly) + { + //MySQLConnector requirement + string fileName = Path.Combine(FileUtil.GetStartupDirectory(), $"{DiagnosticSourceAssembly}.dll"); + if (File.Exists(fileName)) + { + return Assembly.LoadFrom(fileName); + } + } + return null; } #endif static object syncRoot = new Object(); @@ -509,115 +539,144 @@ static NameValueCollection config { if (!configLoaded || _config == null) { + bool isNetTrustException = false; + Exception netTrustedException = null; + string configuredFilename = null; lock (syncRoot) { if (_config == null) { + try { AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; } catch (Exception ex) { - GXLogging.Info(log, ".NET trust level is lower than full", ex.Message); + isNetTrustException = true; + netTrustedException = ex; } string logConfigSource; - configLoaded = true; if (configFileName != null) { - if (log.IsDebugEnabled) loadedConfigFile = configFileName; + loadedConfigFile = configFileName; _config = loadConfig(configFileName, out logConfigSource); + configLoaded = true; if (!string.IsNullOrEmpty(logConfigSource)) - logConfig(logConfigSource); + logConfig(logConfigSource, out configuredFilename); else - logConfig(configFileName); - return _config; + logConfig(configFileName, out configuredFilename); } #if !NETCORE - if (GxContext.IsHttpContext && - File.Exists(Path.Combine(GxContext.StaticPhysicalPath(), "web.config"))) - { - logConfig(null); - if (log.IsDebugEnabled) loadedConfigFile = Path.Combine(GxContext.StaticPhysicalPath(), "web.config"); - _config = ConfigurationSettings.AppSettings; - foreach (string key in _config.Keys) - { - string value = MappedValue(key, _config[key]); - if (value!=_config[key]) - _config[key] = value; - } - languages = null; - return _config; - } - if (GxContext.IsHttpContext && - File.Exists(Path.Combine(GxContext.StaticPhysicalPath(), "bin", "client.exe.config"))) - - { - - logConfig("bin/log.config"); - if (log.IsDebugEnabled) - loadedConfigFile = Path.Combine(GxContext.StaticPhysicalPath(), "bin", "client.exe.config"); - _config = loadConfig("bin/client.exe.config"); - return _config; - } - if (File.Exists("client.exe.config")) - { - logConfig("log.console.config"); - if (log.IsDebugEnabled) loadedConfigFile = Path.GetFullPath("client.exe.config"); - _config = loadConfig("client.exe.config"); - } - else - { - string file = FileUtil.GetStartupDirectory() + "/client.exe.config"; - string logFile = FileUtil.GetStartupDirectory() + "/log.console.config"; - logConfig(logFile); - if (log.IsDebugEnabled) loadedConfigFile = Path.GetFullPath(file); - _config = loadConfig(file); - - } - -#else - string basePath = FileUtil.GetBasePath(); - string currentDir = Directory.GetCurrentDirectory(); - string startupDir = FileUtil.GetStartupDirectory(); - string appSettings = "appsettings.json"; - string clientConfig = "client.exe.config"; - string logConfigFile = GxContext.IsHttpContext ? "log.config" : "log.console.config"; - - if (File.Exists(Path.Combine(basePath, appSettings))) - { - _config = loadConfigJson(basePath, appSettings); - logConfig(Path.Combine(basePath, logConfigFile)); - } - else if (File.Exists(appSettings)) - { - _config = loadConfigJson(currentDir, appSettings); - logConfig(logConfigFile); - } - else if (File.Exists(Path.Combine(startupDir, appSettings))) - { - _config = loadConfigJson(startupDir, appSettings); - logConfig(Path.Combine(startupDir, logConfigFile)); - } - else if (File.Exists(clientConfig)) + else { - _config = loadConfig(clientConfig, out logConfigSource); - if (!string.IsNullOrEmpty(logConfigSource)) - logConfig(logConfigSource); + if (GxContext.IsHttpContext && + File.Exists(Path.Combine(GxContext.StaticPhysicalPath(), "web.config"))) + { + logConfig(null, out configuredFilename); + loadedConfigFile = Path.Combine(GxContext.StaticPhysicalPath(), "web.config"); + _config = ConfigurationSettings.AppSettings; + foreach (string key in _config.Keys) + { + string value = MappedValue(key, _config[key]); + if (value != _config[key]) + _config[key] = value; + } + languages = null; + } + else if (GxContext.IsHttpContext && + File.Exists(Path.Combine(GxContext.StaticPhysicalPath(), "bin", "client.exe.config"))) + { + + logConfig("bin/log.config", out configuredFilename); + loadedConfigFile = Path.Combine(GxContext.StaticPhysicalPath(), "bin", "client.exe.config"); + _config = loadConfig("bin/client.exe.config"); + } + else if (File.Exists("client.exe.config")) + { + logConfig("log.console.config", out configuredFilename); + loadedConfigFile = Path.GetFullPath("client.exe.config"); + _config = loadConfig("client.exe.config"); + } else - logConfig(logConfigFile); - } - try - { - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + { + string file = FileUtil.GetStartupDirectory() + "/client.exe.config"; + string logFile = FileUtil.GetStartupDirectory() + "/log.console.config"; + logConfig(logFile, out configuredFilename); + loadedConfigFile = Path.GetFullPath(file); + _config = loadConfig(file); + } + configLoaded = true; } - catch (Exception ex) +#else + else { - GXLogging.Info(log, "Could not register encoding provider", ex.Message); + + string basePath = FileUtil.GetBasePath(); + string currentDir = Directory.GetCurrentDirectory(); + string startupDir = FileUtil.GetStartupDirectory(); + string appSettings = "appsettings.json"; + string clientConfig = "client.exe.config"; + + string logConfigurationSource = string.Empty; + + if (File.Exists(Path.Combine(basePath, appSettings))) + { + _config = loadConfigJson(basePath, appSettings); + + } + else if (File.Exists(appSettings)) + { + _config = loadConfigJson(currentDir, appSettings); + + } + else if (File.Exists(Path.Combine(startupDir, appSettings))) + { + _config = loadConfigJson(startupDir, appSettings); + + } + else if (File.Exists(clientConfig)) + { + _config = loadConfig(clientConfig, out logConfigSource); + logConfigurationSource = logConfigSource; + + } + configLoaded = true; + + string logConfigFile = GxContext.IsHttpContext ? "log.config" : "log.console.config"; + if (File.Exists(Path.Combine(basePath, appSettings))) + logConfig(Path.Combine(basePath, logConfigFile), out configuredFilename); + + else if (File.Exists(appSettings)) + logConfig(logConfigFile, out configuredFilename); + + else if (File.Exists(Path.Combine(startupDir, appSettings))) + logConfig(Path.Combine(startupDir, logConfigFile), out configuredFilename); + + else if (File.Exists(clientConfig)) + if (!string.IsNullOrEmpty(logConfigurationSource)) + logConfig(logConfigurationSource, out configuredFilename); + else + logConfig(logConfigFile, out configuredFilename); + + try + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } + catch (Exception) + { + //"Could not register encoding provider"; + } } #endif } } + log = GXLoggerFactory.GetLogger(); + GXLogging.Debug(log, "GxClasses version:", GxContext.StdClassesVersion()); + if (configuredFilename != null) + GXLogging.Debug(log, "DOMConfigurator log4net configured with ", configuredFilename); + if (isNetTrustException) + GXLogging.Info(log, ".NET trust level is lower than full", netTrustedException.Message); } return _config; } @@ -661,9 +720,10 @@ static NameValueCollection loadConfigJson(string baseDir, string appSettings) return cfg; } #endif - private static void logConfig(string filename) + private static void logConfig(string filename, out string configuredFilename) { - if (configLog) + configuredFilename = null; + if (configLog) { try { @@ -672,21 +732,20 @@ private static void logConfig(string filename) { var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()); XmlConfigurator.ConfigureAndWatch(logRepository, new FileInfo(filename)); - GXLogging.Debug(log, "DOMConfigurator log4net configured with ", filename); + configuredFilename = filename; } #else if (filename != null) { XmlConfigurator.ConfigureAndWatch(new FileInfo(filename)); - GXLogging.Debug(log, "DOMConfigurator log4net configured with ", filename); + configuredFilename = filename; } else { XmlConfigurator.Configure(); - GXLogging.Debug(log, "DOMConfigurator log4net configured with web.config"); + configuredFilename = "web.config"; } #endif - GXLogging.Debug(log, "GxClasses version:", GxContext.StdClassesVersion()); } catch (Exception ex) { @@ -703,7 +762,6 @@ static NameValueCollection loadConfig(string filename) } static NameValueCollection loadConfig(string filename, out string logConfigSource) { - GXLogging.Debug(log, "Start loadConfig, filename '", filename, "'"); NameValueCollection cfg = new NameValueCollection(StringComparer.Ordinal); //Case sensitive logConfigSource = null; if (!File.Exists(filename)) @@ -752,7 +810,6 @@ static NameValueCollection loadConfig(string filename, out string logConfigSourc } } } - GXLogging.Debug(log, "Return loadConfig"); return cfg; } private static string MappedValue(string key, string value) @@ -767,7 +824,7 @@ private static string MappedValue(string key, string value) } public class Preferences { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Configuration.Preferences)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); static object syncRoot = new Object(); static Hashtable cachingTtl; static string _applicationPath = ""; @@ -778,6 +835,7 @@ public class Preferences static int oldSTR = -1; static int instrumented = -1; static string mediaPath; + static string pdfLib; static string blobPath; static string blobPathFolderName; static int blankEmptyDates = -1; @@ -794,12 +852,21 @@ public class Preferences public static string DefaultRewriteFile = "rewrite.config"; const string USE_NAMED_PARAMETERS = "UseNamedParameters"; const string REST_DATES_WITH_MILLIS = "REST_DATES_WITH_MILLIS"; - const string YES = "1"; - const string NO = "0"; + internal const string YES = "1"; + internal const string NO = "0"; static string defaultDatastore; const string DEFAULT_DS = "Default"; static int httpclient_max_per_route = -1; static int sessionTimeout = -1; + static int singletonHttpClient = -1; + internal static string AppMainNamespace + { + get + { + Config.GetValueOf("AppMainNamespace", out string nameSpace); + return nameSpace; + } + } internal static string DefaultDatastore { get @@ -867,9 +934,9 @@ public static bool RewriteEnabled if (rewriteEnabled == -1) { #if NETCORE - var basePath = FileUtil.GetBasePath(); + string basePath = FileUtil.GetBasePath(); #else - var basePath = Directory.GetParent(FileUtil.GetStartupDirectory()).FullName; + string basePath = Directory.GetParent(FileUtil.GetStartupDirectory()).FullName; #endif string rewriteFile = Path.Combine(basePath, DefaultRewriteFile); rewriteEnabled = File.Exists(rewriteFile)?1:0; @@ -1192,6 +1259,28 @@ public static string getPRINT_LAYOUT_METADATA_DIR() } return mediaPath; } + internal static string PdfReportLibrary() + { + if (pdfLib == null) + { + lock (syncRoot) + { + if (pdfLib == null) + { + if (Config.GetValueOf("PDF_RPT_LIBRARY", out pdfLib)) + { + pdfLib = pdfLib.Trim(); + } + else + { + pdfLib = string.Empty; + } + GXLogging.Debug(log, "PDF_RPT_LIBRARY:", pdfLib); + } + } + } + return pdfLib; + } public enum StorageTimeZonePty { Undefined = 0, Utc = 1, Local = 2 }; public static Boolean useTimezoneFix() @@ -1354,7 +1443,29 @@ public static int SessionTimeout { return sessionTimeout; } } + const bool DefaultWrapSingleApiOutput = false; + internal static bool WrapSingleApiOutput + { + get + { + if (Config.GetValueOf("WRAP_SINGLE_API_OUTPUT", out string flatten)) + return (int.TryParse(flatten, out int value) && value == 1); + else + return DefaultWrapSingleApiOutput; + } + } + internal static bool SingletonHttpClient() + { + if (singletonHttpClient == -1) + { + if (Config.GetValueOrEnvironmentVarOf("HTTPCLIENT_SINGLETON", out string sValue) && int.TryParse(sValue, out int value)) + singletonHttpClient = value; + else + singletonHttpClient = 1; + } + return singletonHttpClient==1; + } internal static string CorsAllowedOrigins() { if (Config.GetValueOf("CORS_ALLOW_ORIGIN", out string corsOrigin)) @@ -1396,27 +1507,27 @@ public static string GetDefaultTheme() else return ""; } - - public static int GetHttpClientMaxConnectionPerRoute() + internal const int DEFAULT_HTTPCLIENT_MAX_PER_ROUTE= 1000; + internal static int GetHttpClientMaxConnectionPerRoute() { if (httpclient_max_per_route == -1) { try { string strmax; - if (Config.GetValueOf("HTTPCLIENT_MAX_PER_ROUTE", out strmax)) + if (Config.GetValueOrEnvironmentVarOf("HTTPCLIENT_MAX_PER_ROUTE", out strmax)) { httpclient_max_per_route = Convert.ToInt32(strmax); } else { - httpclient_max_per_route = 1000; + httpclient_max_per_route = DEFAULT_HTTPCLIENT_MAX_PER_ROUTE; } } catch (Exception ex) { GXLogging.Error(log, "HttpClientMaxPerRoute error", ex); - httpclient_max_per_route = 1000; + httpclient_max_per_route = DEFAULT_HTTPCLIENT_MAX_PER_ROUTE; } } return httpclient_max_per_route; diff --git a/dotnet/src/dotnetframework/GxClasses/Cryptography/GXAsymmetricEncryption.cs b/dotnet/src/dotnetframework/GxClasses/Cryptography/GXAsymmetricEncryption.cs index f9c110a7b..6c4840057 100644 --- a/dotnet/src/dotnetframework/GxClasses/Cryptography/GXAsymmetricEncryption.cs +++ b/dotnet/src/dotnetframework/GxClasses/Cryptography/GXAsymmetricEncryption.cs @@ -1,18 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; -using log4net; using GeneXus.Cryptography.CryptoException; using GeneXus.Cryptography.Encryption; namespace GeneXus.Cryptography { - public class GXAsymmetricEncryption + public class GXAsymmetricEncryption { - private static readonly ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - private int _lastError; + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private int _lastError; private string _lastErrorDescription; private GXCertificate _cert; private IGXAsymmetricEncryption _asymAlg; @@ -62,7 +56,7 @@ public string Encrypt(string text) catch (EncryptionException e) { SetError(1, e.Message); - _log.Error("Encryption Error", e); + GXLogging.Error(log, "Encryption Error", e); } } return encrypted; @@ -89,7 +83,7 @@ public string Decrypt(string text) catch (EncryptionException e) { SetError(1, e.Message); - _log.Error("Encryption Error", e); + GXLogging.Error(log, "Encryption Error", e); } } return decrypted; diff --git a/dotnet/src/dotnetframework/GxClasses/Cryptography/GXCertificate.cs b/dotnet/src/dotnetframework/GxClasses/Cryptography/GXCertificate.cs index 7a195f954..3c19b6dc4 100644 --- a/dotnet/src/dotnetframework/GxClasses/Cryptography/GXCertificate.cs +++ b/dotnet/src/dotnetframework/GxClasses/Cryptography/GXCertificate.cs @@ -1,8 +1,7 @@ using System; -using System.Security.Cryptography.X509Certificates; using System.IO; using System.Security.Cryptography; -using log4net; +using System.Security.Cryptography.X509Certificates; using GeneXus.Utils; @@ -10,9 +9,9 @@ namespace GeneXus.Cryptography { public class GXCertificate { - static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); - private X509Certificate2 _cert; + private X509Certificate2 _cert; private int _lastError; private string _lastErrorDescription; private string _password; diff --git a/dotnet/src/dotnetframework/GxClasses/Cryptography/GXHashing.cs b/dotnet/src/dotnetframework/GxClasses/Cryptography/GXHashing.cs index 59ee779a7..f3194e0cf 100644 --- a/dotnet/src/dotnetframework/GxClasses/Cryptography/GXHashing.cs +++ b/dotnet/src/dotnetframework/GxClasses/Cryptography/GXHashing.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Security.Cryptography; -using log4net; -using GeneXus.Cryptography.Hashing; using GeneXus.Cryptography.CryptoException; +using GeneXus.Cryptography.Hashing; namespace GeneXus.Cryptography { diff --git a/dotnet/src/dotnetframework/GxClasses/Cryptography/GXSymmetricEncryption.cs b/dotnet/src/dotnetframework/GxClasses/Cryptography/GXSymmetricEncryption.cs index 8bf205372..816093187 100644 --- a/dotnet/src/dotnetframework/GxClasses/Cryptography/GXSymmetricEncryption.cs +++ b/dotnet/src/dotnetframework/GxClasses/Cryptography/GXSymmetricEncryption.cs @@ -1,19 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; -using System.IO; -using log4net; +using System; using GeneXus.Cryptography.CryptoException; using GeneXus.Cryptography.Encryption; namespace GeneXus.Cryptography { - public class GXSymmetricEncryption + public class GXSymmetricEncryption { - private static readonly ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - private const string DEFAULT_SYM_ALGORITHM = "DES"; + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private const string DEFAULT_SYM_ALGORITHM = "DES"; private int _lastError; private string _lastErrorDescription; private IGXSymmetricEncryption _symAlg; // Algorithm instance @@ -81,7 +75,7 @@ public string Encrypt(string text) catch (EncryptionException e) { SetError(2, e.Message); - _log.Error(e); + GXLogging.Error(log, e); } } return encrypted; @@ -100,7 +94,7 @@ public string Decrypt(string text) catch (EncryptionException e) { SetError(3, e.Message); - _log.Error(e); + GXLogging.Error(log, e); } } return decrypted; diff --git a/dotnet/src/dotnetframework/GxClasses/Cryptography/Hashing/HashAlgorithmProvider.cs b/dotnet/src/dotnetframework/GxClasses/Cryptography/Hashing/HashAlgorithmProvider.cs index b7b999077..5bb9a6247 100644 --- a/dotnet/src/dotnetframework/GxClasses/Cryptography/Hashing/HashAlgorithmProvider.cs +++ b/dotnet/src/dotnetframework/GxClasses/Cryptography/Hashing/HashAlgorithmProvider.cs @@ -42,7 +42,9 @@ public HashAlgorithmProvider(string algorithm) _hash = new SHA512CryptoServiceProvider(); break; default: +#pragma warning disable SYSLIB0045 // Type or member is obsolete _hash = HashAlgorithm.Create(algorithm); +#pragma warning restore SYSLIB0045 // Type or member is obsolete break; } diff --git a/dotnet/src/dotnetframework/GxClasses/Cryptography/Hashing/HashedKeyAlgorithmProvider.cs b/dotnet/src/dotnetframework/GxClasses/Cryptography/Hashing/HashedKeyAlgorithmProvider.cs index f9b8e916b..1449f4953 100644 --- a/dotnet/src/dotnetframework/GxClasses/Cryptography/Hashing/HashedKeyAlgorithmProvider.cs +++ b/dotnet/src/dotnetframework/GxClasses/Cryptography/Hashing/HashedKeyAlgorithmProvider.cs @@ -14,7 +14,10 @@ public HashedKeyAlgorithmProvider(string algorithm) { if (GXUtil.IsWindowsPlatform) { +#pragma warning disable SYSLIB0045 // Type or member is obsolete _keyedHash = KeyedHashAlgorithm.Create(algorithm); +#pragma warning restore SYSLIB0045 // Type or member is obsolete + } if (_keyedHash == null) { diff --git a/dotnet/src/dotnetframework/GxClasses/Cryptography/Signing/Standards/PKCS1Signature.cs b/dotnet/src/dotnetframework/GxClasses/Cryptography/Signing/Standards/PKCS1Signature.cs index 23b4c6629..e4bb4e65c 100644 --- a/dotnet/src/dotnetframework/GxClasses/Cryptography/Signing/Standards/PKCS1Signature.cs +++ b/dotnet/src/dotnetframework/GxClasses/Cryptography/Signing/Standards/PKCS1Signature.cs @@ -77,7 +77,9 @@ private string SignRSA(string signed, byte[] data) throw new AlgorithmNotSupportedException("Private Key does not support RSA Sign Algorithm"); } +#pragma warning disable SYSLIB0045 // Type or member is obsolete HashAlgorithm ha = HashAlgorithm.Create(_hashAlgorithm); +#pragma warning restore SYSLIB0045 // Type or member is obsolete byte[] signature = null; try { @@ -118,8 +120,9 @@ private string SignDSA(string signed, string oid, byte[] data) throw new AlgorithmNotSupportedException("Private Key does not support DSA Sign Algorithm"); } - +#pragma warning disable SYSLIB0045 // Type or member is obsolete HashAlgorithm ha = System.Security.Cryptography.HashAlgorithm.Create(_hashAlgorithm); +#pragma warning restore SYSLIB0045 // Type or member is obsolete byte[] hash = ha.ComputeHash(data); byte[] signature = dsa.SignHash(hash, oid); @@ -134,8 +137,10 @@ public bool Verify(string signatureB64, string text) { try { +#pragma warning disable SYSLIB0045 // Type or member is obsolete HashAlgorithm ha = GXUtil.IsWindowsPlatform ? HashAlgorithm.Create(_hashAlgorithm) : null; - string oid = CryptoConfig.MapNameToOID(_hashAlgorithm); +#pragma warning restore SYSLIB0045 // Type or member is obsolete + string oid = CryptoConfig.MapNameToOID(_hashAlgorithm); if (ha != null && !string.IsNullOrEmpty(oid)) { byte[] textData = Constants.DEFAULT_ENCODING.GetBytes(text); diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs index 3aeb551d8..7cfe94720 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs @@ -1,27 +1,25 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; using GeneXus.Application; using GeneXus.Cache; using GeneXus.Configuration; using GeneXus.Data.NTier.ADO; -using GeneXus.Diagnostics; using GeneXus.Management; using GeneXus.Reorg; using GeneXus.Services; using GeneXus.Utils; using GeneXus.XML; using GxClasses.Helpers; -using log4net; -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Data; -using System.Data.Common; -using System.IO; -using System.Reflection; -using System.Threading; using TZ4Net; - namespace GeneXus.Data.ADO { @@ -64,7 +62,7 @@ public override void DecOpenHandlesImpl(GxConnection con) public class GxConnectionManager :IGxConnectionManager { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.ADO.GxConnectionManager)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static volatile GxConnectionManager instance; private static object instanceSync = new Object(); @@ -215,7 +213,22 @@ public void RemoveAllConnections(int handle) throw e; } } +#if NETCORE + internal async Task RemoveConnectionAsync(int handle, string dataSource) + { + ServerUserInformation sui; + if (userConnections.TryGetValue(handle, out sui)) + { + GXLogging.Debug(log, "RemoveConnection handle " + handle + ",datasource:" + dataSource); + GxConnection con = sui[dataSource]; + if (sui.TryRemove(dataSource, out con)) + await con.DisposeAsync(); + ServerUserInformation suiDeleted; + if (sui.Count == 0) userConnections.TryRemove(handle, out suiDeleted); + } + } +#endif public void RemoveConnection(int handle, string dataSource) { @@ -345,7 +358,7 @@ public class GxConnection: MarshalByRefObject, IGxConnection public class GxConnection: IGxConnection #endif { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.ADO.GxConnection)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private IDbTransaction transaction; private short lastErrorCode; private string lastErrorMsg; @@ -414,7 +427,12 @@ public void Dispose() { Close(); } - +#if NETCORE + internal async Task DisposeAsync() + { + await CloseAsync(); + } +#endif public IGxDataStore DataStore { get{ return dataStore;} @@ -739,7 +757,61 @@ public void Close() wmiconnection.CleanUp(); } } +#if NETCORE + internal async Task CloseAsync() + { + if (connection != null) + { + GXLogging.Debug(log, "GxConnection.Close Id " + " connection State '" + connection.State + "'" + " handle:" + handle + " datastore:" + DataStore.Id); + } + if (connection != null && ((connection.State & ConnectionState.Closed) == 0)) + { + try + { + connectionCache.Clear(); + } + catch (Exception e) + { + GXLogging.Warn(log, "GxConnection.Close can't close all prepared cursors", e); + } + + GXLogging.Debug(log, "UncommitedChanges before Close:" + UncommitedChanges); + try + { + if (UncommitedChanges) + { + rollbackTransactionOnly(); + UncommitedChanges = false; + } + } + catch (Exception e) + { + GXLogging.Warn(log, "GxConnection.Close can't rollback transaction", e); + } + try + { + await connection.CloseAsync(); + if (transaction != null) + { + transaction.Dispose(); + transaction = null; + } + } + catch (Exception e) + { + GXLogging.Warn(log, "GxConnection.Close can't close connection", e); + } + spid = 0; + GXLogging.Debug(log, "GxConnection.Close connection is closed "); + } + m_opened = false; + if (Preferences.Instrumented && wmiconnection != null) + { + wmiconnection.CleanUp(); + } + } +#endif public int OpenHandles { get{return openHandles;} @@ -1155,7 +1227,13 @@ public string DSVersion } public string BlobPath { - get {return blobPath;} + get { + if (blobPath == null) + { + blobPath = Preferences.getBLOB_PATH(); + } + return blobPath; + } set {blobPath=value;} } public string MultimediaPath @@ -1319,7 +1397,7 @@ public ParDef(string name, GXType type, int size, int scale) public class GxCommand: IGxDbCommand { internal List ParmDefinition; - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.ADO.GxCommand)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); string stmt; String stmtId; GxParameterCollection parameters; @@ -1728,7 +1806,10 @@ public IDataReader ExecuteReader() catch (Exception e) { GXLogging.Error(log, "Return GxCommand.ExecuteReader Error ", e); - + if (e.InnerException != null) + { + GXLogging.Error(log, "Inner Error", e.InnerException); + } throw (new GxADODataException(e)); } @@ -2408,7 +2489,7 @@ internal void AfterCreateCommand() public class GxDataStore : IGxDataStore { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxDataStore)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); string id; IGxConnection connection; int handle; @@ -2631,7 +2712,6 @@ private void Initialize(IGxDataRecord db, string id, int hnd, IGxContext context if ( ! cfg) connection.ConnectionString = connectionString; - connection.BlobPath = Preferences.getBLOB_PATH(); string strCache; connection.Cache=((Config.GetValueOf("CACHING",out strCache) && strCache.Equals("1")) || CacheFactory.ForceHighestTimetoLive) && ! GxContext.isReorganization; connection.DataStore = this; @@ -2714,7 +2794,12 @@ GxDataRecord getDbmsDataRecord(string id, string dbms) switch (dbms) { case "sqlserver": - return new GxSqlServer(); + GxSqlServer gxSqlServer = new GxSqlServer(); + if (Config.GetValueOf("Connection-" + id + "-UseSmallDateTime", out cfgBuf) && cfgBuf == "1") + { + gxSqlServer.UseSmallDateTime(); + } + return gxSqlServer; case "mysql": #if NETCORE return new GxMySqlConnector(id); @@ -2832,6 +2917,12 @@ public void CloseConnections() { GxConnectionManager.Instance.RemoveConnection(handle, id); } +#if NETCORE + internal async Task CloseConnectionsAsync() + { + await ((GxConnectionManager)GxConnectionManager.Instance).RemoveConnectionAsync(handle, id); + } +#endif public void Release() { } diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs index 6a7818d49..1eb63d2f7 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs @@ -4,7 +4,6 @@ using System.IO; using System.Text; using System.Threading; -using log4net; using GeneXus.Application; using GeneXus.Cache; using GeneXus.Configuration; @@ -26,6 +25,7 @@ using GeneXus.Metadata; using System.Data.Common; using System.Linq; +using System.Threading.Tasks; namespace GeneXus.Data { @@ -297,7 +297,8 @@ public enum GxErrorMask public abstract class GxDataRecord : IGxDataRecord { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxDataRecord)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + public static int RETRY_SLEEP = 500; //500 milliseconds. protected string m_connectionString; protected string m_datasource; @@ -358,7 +359,13 @@ public string BuildConnectionStringImpl(string datasourceName, string userId, } protected abstract string BuildConnectionString(string datasourceName, string userId, - string userPassword,string databaseName, string port, string schema, string extra); + string userPassword,string databaseName, string port, string schema, string extra); + + protected virtual string BuildConnectionStringForLog(string datasourceName, string userId, + string userPassword, string databaseName, string port, string schema, string extra) + { + return BuildConnectionString(datasourceName, userId, userPassword, databaseName, port, schema, extra); + } public virtual object[] ExecuteStoredProcedure(IDbCommand cmd) { @@ -1010,6 +1017,33 @@ public virtual string ReplaceKeyword(string data, string keyword, string newKeyw res.Append(newKeyword); res.Append('='); res.Append(prop[1]); + res.Append(';'); + } + else + { + res.Append(s); + res.Append(';'); + } + } + } + return res.ToString(); + } + public virtual string ReplaceKeyValue(string data, string keyword, string newValue) + { + char[] sep = { ';' }; + StringBuilder res = new StringBuilder(""); + string[] props = data.Split(sep); + foreach (string s in props) + { + if (s != null && s.Length > 0) + { + string[] prop = s.Split('='); + if (prop != null && prop.Length == 2 && prop[0].Trim().Equals(keyword, StringComparison.OrdinalIgnoreCase)) + { + res.Append(keyword); + res.Append('='); + res.Append(newValue); + res.Append(';'); } else { @@ -1060,21 +1094,19 @@ public string GetParameterValue(string conStr, string searchParam) return string.Empty; else { - - int posBegin = 0; - int posEnd = 0; string paramVal = string.Empty; - posBegin = conStr.IndexOf(searchParam, StringComparison.OrdinalIgnoreCase); + int posBegin = conStr.IndexOf(searchParam, StringComparison.OrdinalIgnoreCase); if (posBegin > -1) { posBegin += searchParam.Length + 1; + int posEnd; if (conStr.LastIndexOf(';') > posBegin) - + posEnd = conStr.IndexOf(';', posBegin); else - + posEnd = conStr.Length; paramVal = conStr.Substring(posBegin, (posEnd - posBegin)); @@ -1157,8 +1189,8 @@ public void Clear() public class SqlUtil { public static Hashtable mapping; - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.SqlUtil)); - + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + public static string SqlTypeToDbType(SqlParameter parameter) { SqlDbType type = parameter.SqlDbType; @@ -1433,8 +1465,7 @@ public static String GetKeyStmtValues(GxParameterCollection parameters, public class GxSqlDataReader : GxDataReader { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxSqlDataReader)); - + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GxSqlDataReader(IGxConnectionManager connManager, GxDataRecord dr, IGxConnection connection, GxParameterCollection parameters, string stmt, ushort fetchSize,bool forFirst, int handle, bool withCached, SlidingTime expiration, bool dynStmt):base(connManager, dr, connection, parameters, stmt, fetchSize, forFirst, handle, withCached, expiration, dynStmt) @@ -1552,7 +1583,7 @@ public override decimal GetDecimal(int i) } public class GxSqlServer : GxDataRecord { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxSqlServer)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static int MAX_NET_DECIMAL_PRECISION = 28; private static int MAX_GX_DECIMAL_SCALE = 15; @@ -1561,18 +1592,27 @@ public class GxSqlServer : GxDataRecord private int m_FailedConnections; private int m_FailedCreate; private int MAX_TRIES; - private int MAX_CREATE_TRIES=3; - private const int MILLISECONDS_BETWEEN_RETRY_ATTEMPTS=500; + private int MAX_CREATE_TRIES = 3; + private const int MILLISECONDS_BETWEEN_RETRY_ATTEMPTS = 500; private const string MULTIPLE_DATAREADERS = "MultipleActiveResultSets"; +#if NETCORE + private const string INTEGRATED_SECURITY = "Integrated Security"; + private const string INTEGRATED_SECURITY_NO = "no"; +#endif private bool multipleDatareadersEnabled; + private DateTime SqlServer_NullDateTime= SQLSERVER_NULL_DATETIME; - public override int GetCommandTimeout() - { - return base.GetCommandTimeout(); - } + internal void UseSmallDateTime() + { + SqlServer_NullDateTime = SQLSERVER_NULL_SMALLDATETIME; + } + public override int GetCommandTimeout() + { + return base.GetCommandTimeout(); + } - public override GxAbstractConnectionWrapper GetConnection(bool showPrompt, string datasourceName, string userId, - string userPassword,string databaseName, string port, string schema, string extra, GxConnectionCache connectionCache) + public override GxAbstractConnectionWrapper GetConnection(bool showPrompt, string datasourceName, string userId, + string userPassword, string databaseName, string port, string schema, string extra, GxConnectionCache connectionCache) { #if !NETCORE if (showPrompt) @@ -1583,48 +1623,48 @@ public override GxAbstractConnectionWrapper GetConnection(bool showPrompt, strin #endif { if (m_connectionString == null) - m_connectionString=BuildConnectionString(datasourceName, userId, userPassword, databaseName, port, schema, extra); + m_connectionString = BuildConnectionString(datasourceName, userId, userPassword, databaseName, port, schema, extra); } - GXLogging.Debug(log, "Setting connectionString property ", ()=> BuildConnectionString(datasourceName, userId, NaV, databaseName, port, schema, extra)); - MssqlConnectionWrapper connection=new MssqlConnectionWrapper(m_connectionString,connectionCache, isolationLevel); + GXLogging.Debug(log, "Setting connectionString property ", () => BuildConnectionString(datasourceName, userId, NaV, databaseName, port, schema, extra)); + MssqlConnectionWrapper connection = new MssqlConnectionWrapper(m_connectionString, connectionCache, isolationLevel); m_FailedConnections = 0; - m_FailedCreate=0; + m_FailedCreate = 0; return connection; } - public override bool AllowsDuplicateParameters - { - get - { - return false; - } - } + public override bool AllowsDuplicateParameters + { + get + { + return false; + } + } public override IDbDataParameter CreateParameter() { return new SqlParameter(); } public override IDbDataParameter CreateParameter(string name, Object dbtype, int gxlength, int gxdec) { - SqlParameter parm =new SqlParameter(); - SqlDbType type= GXTypeToSqlDbType(dbtype); - parm.SqlDbType=type; - parm.IsNullable=true; + SqlParameter parm = new SqlParameter(); + SqlDbType type = GXTypeToSqlDbType(dbtype); + parm.SqlDbType = type; + parm.IsNullable = true; parm.Size = gxlength; - if(type==SqlDbType.Decimal) + if (type == SqlDbType.Decimal) { parm.Precision = (byte)gxlength; - parm.Scale= (byte)gxdec; + parm.Scale = (byte)gxdec; } - + #if !NETCORE else if (type == SqlDbType.Udt) { parm.UdtTypeName = "Geography"; } #endif - parm.ParameterName=name; + parm.ParameterName = name; return parm; } private SqlDbType GXTypeToSqlDbType(object type) @@ -1647,11 +1687,11 @@ private SqlDbType GXTypeToSqlDbType(object type) case GXType.Geopolygon: return SqlDbType.Udt; #endif - case GXType.Int16: return SqlDbType.SmallInt; + case GXType.Int16: return SqlDbType.SmallInt; case GXType.Int32: return SqlDbType.Int; case GXType.Int64: return SqlDbType.BigInt; case GXType.Number: return SqlDbType.Float; - case GXType.Decimal: return SqlDbType.Decimal; + case GXType.Decimal: return SqlDbType.Decimal; case GXType.DateTime: return SqlDbType.DateTime; case GXType.DateTime2: return SqlDbType.DateTime2; case GXType.NChar: return SqlDbType.NChar; @@ -1677,7 +1717,7 @@ public override IDbCommand GetCachedCommand(IGxConnection con, string stmt) { if (multipleDatareadersEnabled) { - + return con.ConnectionCache.GetAvailablePreparedCommand(stmt); } else @@ -1686,10 +1726,10 @@ public override IDbCommand GetCachedCommand(IGxConnection con, string stmt) } } public override DbDataAdapter CreateDataAdapeter() - { - return new SqlDataAdapter(); - } - public override bool MultiThreadSafe + { + return new SqlDataAdapter(); + } + public override bool MultiThreadSafe { get { @@ -1698,23 +1738,23 @@ public override bool MultiThreadSafe } public override IDataReader GetDataReader( IGxConnectionManager connManager, - IGxConnection con, - GxParameterCollection parameters , - string stmt, ushort fetchSize, - bool forFirst, int handle, + IGxConnection con, + GxParameterCollection parameters, + string stmt, ushort fetchSize, + bool forFirst, int handle, bool cached, SlidingTime expiration, bool hasNested, bool dynStmt) { - + IDataReader idatareader; if (!hasNested || multipleDatareadersEnabled)//Client Cursor { - idatareader= new GxSqlDataReader(connManager,this, con,parameters,stmt,fetchSize,forFirst,handle,cached,expiration,dynStmt); + idatareader = new GxSqlDataReader(connManager, this, con, parameters, stmt, fetchSize, forFirst, handle, cached, expiration, dynStmt); } else //Server Cursor { - idatareader= new GxSqlCursorDataReader(connManager,this, con,parameters,stmt,fetchSize,forFirst,handle,cached,expiration,dynStmt); + idatareader = new GxSqlCursorDataReader(connManager, this, con, parameters, stmt, fetchSize, forFirst, handle, cached, expiration, dynStmt); } return idatareader; @@ -1722,7 +1762,7 @@ public override IDataReader GetDataReader( public override bool IsBlobType(IDbDataParameter idbparameter) { - SqlDbType type = ((SqlParameter)idbparameter).SqlDbType; + SqlDbType type = ((SqlParameter)idbparameter).SqlDbType; return (type == SqlDbType.Image || type == SqlDbType.VarBinary); } @@ -1744,10 +1784,10 @@ public override string GetServerVersionStmt() } public override void SetTimeout(IGxConnectionManager connManager, IGxConnection connection, int handle) { - if (m_lockTimeout>0) + if (m_lockTimeout > 0) { - GXLogging.Debug(log, "Set Lock Timeout to " +m_lockTimeout/1000); - IDbCommand cmd = GetCommand(connection,SetTimeoutSentence(m_lockTimeout), new GxParameterCollection()); + GXLogging.Debug(log, "Set Lock Timeout to " + m_lockTimeout / 1000); + IDbCommand cmd = GetCommand(connection, SetTimeoutSentence(m_lockTimeout), new GxParameterCollection()); cmd.ExecuteNonQuery(); } } @@ -1756,40 +1796,40 @@ public override string SetTimeoutSentence(long milliseconds) { return "SET LOCK_TIMEOUT " + milliseconds; } - public override bool ProcessError( int dbmsErrorCode, string emsg, GxErrorMask errMask, IGxConnection con, ref int status, ref bool retry, int retryCount) - + public override bool ProcessError(int dbmsErrorCode, string emsg, GxErrorMask errMask, IGxConnection con, ref int status, ref bool retry, int retryCount) + { - GXLogging.Debug(log, "ProcessError: dbmsErrorCode=" + dbmsErrorCode +", emsg '"+ emsg + "'"); + GXLogging.Debug(log, "ProcessError: dbmsErrorCode=" + dbmsErrorCode + ", emsg '" + emsg + "'"); switch (dbmsErrorCode) { case 1801: //Database '%.*ls' already exists. case 15032: //The database '%s' already exists. break; - case 20: /*The instance of SQL Server you attempted to connect to does not support encryption. (PMcE: amazingly, this is transient)*/ - case 64: /*A connection was successfully established with the server, but then an error occurred during the login process.*/ - case 233: /*The client was unable to establish a connection because of an error during connection initialization process before login*/ - case 10053: /*A transport-level error has occurred when receiving results from the server.*/ - case 10060: /*A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible.*/ - case 40143: /*The service has encountered an error processing your request. Please try again.*/ - case 40197: /*The service has encountered an error processing your request. Please try again.*/ - case 40501: /*The service is currently busy. Retry the request after 10 seconds.*/ - case 40613: /*Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later.*/ + case 20: /*The instance of SQL Server you attempted to connect to does not support encryption. (PMcE: amazingly, this is transient)*/ + case 64: /*A connection was successfully established with the server, but then an error occurred during the login process.*/ + case 233: /*The client was unable to establish a connection because of an error during connection initialization process before login*/ + case 10053: /*A transport-level error has occurred when receiving results from the server.*/ + case 10060: /*A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible.*/ + case 40143: /*The service has encountered an error processing your request. Please try again.*/ + case 40197: /*The service has encountered an error processing your request. Please try again.*/ + case 40501: /*The service is currently busy. Retry the request after 10 seconds.*/ + case 40613: /*Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later.*/ case 53:/*A network-related or instance-specific error occurred while establishing a connection to SQL Server*/ - case 11: + case 11: case 121: /*A transport-level error has occurred when receiving results from the server. (provider: TCP Provider, error: 0 - The semaphore timeout period has expired.*/ case 10054://A transport-level error has occurred when sending the request to the server. (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.) case 6404: //The connection is broken and recovery is not possible. The connection is marked by the server as unrecoverable. No attempt was made to restore the connection. if (!GxContext.isReorganization && con != null && m_FailedConnections < MAX_TRIES)//Si es una operacion Open se reintenta. { - try - { - con.Close(); - } - catch { } - status=104; // General network error. Check your network documentation - Retry [Max Pool Size] times. + try + { + con.Close(); + } + catch { } + status = 104; // General network error. Check your network documentation - Retry [Max Pool Size] times. m_FailedConnections++; Thread.Sleep(MILLISECONDS_BETWEEN_RETRY_ATTEMPTS); - retry=true; + retry = true; GXLogging.Debug(log, "ProcessError: General network error, FailedConnections:" + m_FailedConnections); } else @@ -1805,7 +1845,7 @@ public override bool ProcessError( int dbmsErrorCode, string emsg, GxErrorMask e con.Close(); } catch { } - status = 104; + status = 104; m_FailedCreate++; Thread.Sleep(MILLISECONDS_BETWEEN_RETRY_ATTEMPTS); retry = true; @@ -1816,32 +1856,32 @@ public override bool ProcessError( int dbmsErrorCode, string emsg, GxErrorMask e return false; } break; - case 903: // Locked + case 903: // Locked case 1222: retry = Retry(errMask, retryCount); if (retry) - status=110;// Locked - Retry - else - status=103;//Locked + status = 110;// Locked - Retry + else + status = 103;//Locked return retry; - case 2601: // Duplicated record - case 2627: // Duplicated record - status = 1; + case 2601: // Duplicated record + case 2627: // Duplicated record + status = 1; break; - case 3701: // File not found - case 3703: // File not found - case 3704: // File not found - case 3731: // File not found - case 4902: // File not found - case 3727: // File not found - case 3728: // File not found - status = 105; + case 3701: // File not found + case 3703: // File not found + case 3704: // File not found + case 3731: // File not found + case 4902: // File not found + case 3727: // File not found + case 3728: // File not found + status = 105; break; - case 503: // Parent key not found - case 547: //conflicted with COLUMN FOREIGN KEY constraint + case 503: // Parent key not found + case 547: //conflicted with COLUMN FOREIGN KEY constraint if ((errMask & GxErrorMask.GX_MASKFOREIGNKEY) == 0) { - status = 500; // ForeignKeyError + status = 500; // ForeignKeyError return false; } break; @@ -1853,9 +1893,9 @@ public override bool ProcessError( int dbmsErrorCode, string emsg, GxErrorMask e } public override string ToDbmsConstant(DateTime Value) { - if (Value == System.DateTime.MinValue) + if (Value == System.DateTime.MinValue) Value = System.Data.SqlTypes.SqlDateTime.MinValue.Value; - return "'" + Value.ToString("yyyy-MM-dd HH\\:mm\\:ss").Replace("'","''") + "'"; + return "'" + Value.ToString("yyyy-MM-dd HH\\:mm\\:ss").Replace("'", "''") + "'"; } public override IGeographicNative GetGeospatial(IGxDbCommand cmd, IDataRecord DR, int i) @@ -1926,7 +1966,7 @@ public override void GetValues(IDataReader reader, ref object[] values) GXLogging.Error(log, "GetValues error", ex); } } - static internal decimal ReadSQLDecimal(SqlDataReader sqlReader, int idx){ + static internal decimal ReadSQLDecimal(SqlDataReader sqlReader, int idx) { //Reduce the precision //The SQlServer data type NUMBER can hold up to 38 precision, and the .NET Decimal type can hold up to 28 precision SqlDecimal sqldecimal = sqlReader.GetSqlDecimal(idx); @@ -1967,11 +2007,38 @@ static internal Int16 ReadSQLDecimalToShort(SqlDataReader sqlReader, int idx) return Convert.ToInt16(sqldecimal.Value); } } +#if NETCORE + private bool UserPasswordAllowed(SqlConnectionStringBuilder sqlConnectionString) + { + if (sqlConnectionString != null && (sqlConnectionString.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated || + sqlConnectionString.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive || + sqlConnectionString.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || + sqlConnectionString.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow)) + return false; + else + return true; + } + private bool UserIdAllowed(SqlConnectionStringBuilder sqlConnectionString) + { + if (sqlConnectionString != null && sqlConnectionString.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow) + return false; + else + return true; + } + private string ResolveConnectionStringAuthentication(string extra, SqlConnectionStringBuilder sqlConnectionString) + { + if (sqlConnectionString!=null && sqlConnectionString.Authentication != SqlAuthenticationMethod.NotSpecified && sqlConnectionString.IntegratedSecurity) + { + return ReplaceKeyValue(extra, INTEGRATED_SECURITY, INTEGRATED_SECURITY_NO); + } + return extra; + } +#endif protected override string BuildConnectionString(string datasourceName, string userId, string userPassword,string databaseName, string port, string schema, string extra) { StringBuilder connectionString = new StringBuilder(); - string port1 = port!=null ? port.Trim() : ""; + string port1 = port!=null ? port.Trim() : string.Empty; if (!string.IsNullOrEmpty(datasourceName) && port1.Length > 0 && !HasKey(extra, "Data Source")) { connectionString.AppendFormat("Data Source={0},{1};",datasourceName, port1); @@ -1980,10 +2047,27 @@ protected override string BuildConnectionString(string datasourceName, string us { connectionString.AppendFormat("Data Source={0};",datasourceName); } +#if NETCORE + SqlConnectionStringBuilder additionalConnectionString=null; + if (!string.IsNullOrEmpty(extra)) + { + additionalConnectionString = new SqlConnectionStringBuilder(extra); + } + if (userId != null && UserIdAllowed(additionalConnectionString)) + { + connectionString.AppendFormat(";User ID={0}", userId); + } + if (!string.IsNullOrEmpty(userPassword) && UserPasswordAllowed(additionalConnectionString)) + { + connectionString.AppendFormat(";Password={0}", userPassword); + } + extra = ResolveConnectionStringAuthentication(extra, additionalConnectionString); +#else if (userId!=null) { connectionString.AppendFormat(";User ID={0};Password={1}",userId,userPassword); } +#endif if (databaseName != null && databaseName.Trim().Length > 0 && !HasKey(extra, "Database")) { connectionString.AppendFormat(";Database={0}",databaseName); @@ -2009,6 +2093,7 @@ protected override string BuildConnectionString(string datasourceName, string us return connstr; } + #if !NETCORE public override Object Net2DbmsGeo(GXType type, IGeographicNative geo) { @@ -2035,14 +2120,14 @@ public override DateTime Dbms2NetDate(IGxDbCommand cmd, IDataRecord DR, int i) public override DateTime Dbms2NetDateTime( DateTime dt, Boolean precision) { //DBMS MinDate => Genexus null Date - if (dt.Equals(SQLSERVER_NULL_DATE)) + if (dt.Equals(SqlServer_NullDateTime)) { return DateTimeUtil.NullDate(); } - if (dt.Year==SQLSERVER_NULL_DATE.Year && - dt.Month==SQLSERVER_NULL_DATE.Month && - dt.Day==SQLSERVER_NULL_DATE.Day) + if (dt.Year== SqlServer_NullDateTime.Year && + dt.Month== SqlServer_NullDateTime.Month && + dt.Day== SqlServer_NullDateTime.Day) { return new DateTime( @@ -2058,16 +2143,16 @@ public override Object Net2DbmsDateTime(IDbDataParameter parm, DateTime dt) //Genexus null => save DBMS MinDate if(dt.Equals(DateTimeUtil.NullDate())) { - return SQLSERVER_NULL_DATE; + return SqlServer_NullDateTime; } //Date < DBMS MinDate => save DBMS MinDate keeping the Time component - if (dt.CompareTo(SQLSERVER_NULL_DATE)<0) + if (dt.CompareTo(SqlServer_NullDateTime) <0) { DateTime aux = new DateTime( - SQLSERVER_NULL_DATE.Year,SQLSERVER_NULL_DATE.Month, - SQLSERVER_NULL_DATE.Day,dt.Hour,dt.Minute,dt.Second,dt.Millisecond); + SqlServer_NullDateTime.Year, SqlServer_NullDateTime.Month, + SqlServer_NullDateTime.Day,dt.Hour,dt.Minute,dt.Second,dt.Millisecond); return aux; } @@ -2082,20 +2167,20 @@ public override string ConcatOp(int pos) { return ConcatOpValues[pos]; } - - static DateTime SQLSERVER_NULL_DATE = new DateTime(1753,1,1) ; + static DateTime SQLSERVER_NULL_DATETIME = new DateTime(1753,1,1) ; + static DateTime SQLSERVER_NULL_SMALLDATETIME= new DateTime(1900, 1, 1); } public class GxDataReader: IDataReader { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxDataReader)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); protected IDataReader reader; protected GxParameterCollection parameters; protected int fetchSize; protected string stmt; protected GxConnectionCache cache; protected IGxConnection con; - protected GxArrayList block; + protected List block; protected string key; protected int pos; protected bool cached; @@ -2129,7 +2214,7 @@ public GxDataReader( IGxConnectionManager connManager, GxDataRecord dr, IGxConne reader=dr.GetCommand(con,stmt,parameters).ExecuteReader(); cache.SetAvailableCommand(stmt, false, dynStmt); open=true; - block=new GxArrayList(fetchSize); + block= new List(fetchSize); pos=-1; if (cached) { @@ -2461,7 +2546,8 @@ public int GetOrdinal(string name) public class GxConnectionCache { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxConnectionCache)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private static int MAX_SIZE=Preferences.GetMaximumOpenCursors(); private SqlCommand fetchCommand; @@ -2539,7 +2625,6 @@ public void PrepareClose() public GxConnectionCache(IGxConnection gxconn,int maxSize) { preparedCommandsCache = new Dictionary(MAX_SIZE); - InitCommands(); preparedStmtCache=new GxPreparedStatementCache(gxconn,maxSize); conn=gxconn; dataAdapterCacheQueue = new List(); @@ -2549,7 +2634,6 @@ public GxConnectionCache(IGxConnection gxconn) { preparedCommandsCache = new Dictionary(MAX_SIZE); preparedStmtCache = new GxPreparedStatementCache(gxconn, MAX_SIZE); - InitCommands(); conn = gxconn; dataAdapterCacheQueue = new List(); } @@ -2566,7 +2650,7 @@ public void AddPreparedStmt(string stmt, int cursorId) GxItemStmt s;; if (!preparedStmtCache.TryGetValue(stmt, out s)) { - GXLogging.Info(log, "AddPreparedStmt, totalCachedStmtCursors:" + totalCachedCursors + ", cursorId: " + cursorId + ", stmt:" + stmt); + GXLogging.Info(log, msg: "AddPreparedStmt, totalCachedStmtCursors: {totalCachedCursors}, {cursorId}: , stmt: {stmt}", totalCachedCursors.ToString(), cursorId.ToString(), stmt); totalCachedCursors++; CheckCacheSize(); @@ -2932,125 +3016,128 @@ public void Clear() if (commandsToRemove!=null) commandsToRemove.Clear(); } - private void InitCommands() + internal void InitCommands() { - int commandTimeout = 0; + if (fetchCommand == null) + { + int commandTimeout = 0; - //----------------Fetch command - fetchCommand=new SqlCommand("sp_cursorfetch"); - fetchCommand.CommandType=CommandType.StoredProcedure; - fetchCommand.CommandTimeout = commandTimeout; - SqlParameter p1 = fetchCommand.Parameters.Add("cursor", SqlDbType.Int); - p1.Direction = ParameterDirection.Input; + //----------------Fetch command + fetchCommand = new SqlCommand("sp_cursorfetch"); + fetchCommand.CommandType = CommandType.StoredProcedure; + fetchCommand.CommandTimeout = commandTimeout; + SqlParameter p1 = fetchCommand.Parameters.Add("cursor", SqlDbType.Int); + p1.Direction = ParameterDirection.Input; - SqlParameter p2 = fetchCommand.Parameters.Add("fetchtype", SqlDbType.Int); - p2.Direction = ParameterDirection.Input; - p2.Value = 2;//0x0002 Next row. + SqlParameter p2 = fetchCommand.Parameters.Add("fetchtype", SqlDbType.Int); + p2.Direction = ParameterDirection.Input; + p2.Value = 2;//0x0002 Next row. - SqlParameter p3 = fetchCommand.Parameters.Add("rownumber", SqlDbType.Int); - p3.Direction = ParameterDirection.Input; - p3.Value = 1; - - SqlParameter p5 = fetchCommand.Parameters.Add("nrows", SqlDbType.Int); - p5.Direction = ParameterDirection.Input; + SqlParameter p3 = fetchCommand.Parameters.Add("rownumber", SqlDbType.Int); + p3.Direction = ParameterDirection.Input; + p3.Value = 1; - //-----------------PrepExec command - prepExecCommand=new SqlCommand("sp_cursorprepexec"); - prepExecCommand.CommandType=CommandType.StoredProcedure; - prepExecCommand.CommandTimeout = commandTimeout; + SqlParameter p5 = fetchCommand.Parameters.Add("nrows", SqlDbType.Int); + p5.Direction = ParameterDirection.Input; - prepExecParms=new SqlParameter[7]; - prepExecParms[0]=new SqlParameter("cursor",SqlDbType.Int); - prepExecParms[0].Direction=ParameterDirection.Output; - prepExecParms[0].Value=DBNull.Value; + //-----------------PrepExec command + prepExecCommand = new SqlCommand("sp_cursorprepexec"); + prepExecCommand.CommandType = CommandType.StoredProcedure; + prepExecCommand.CommandTimeout = commandTimeout; - prepExecParms[1]=new SqlParameter("handle",SqlDbType.Int); - prepExecParms[1].Direction=ParameterDirection.Output; - prepExecParms[1].Value=0; + prepExecParms = new SqlParameter[7]; + prepExecParms[0] = new SqlParameter("cursor", SqlDbType.Int); + prepExecParms[0].Direction = ParameterDirection.Output; + prepExecParms[0].Value = DBNull.Value; - prepExecParms[2]=new SqlParameter("parameters",SqlDbType.NVarChar); - prepExecParms[2].Direction=ParameterDirection.Input; - - prepExecParms[3]=new SqlParameter("stmt",SqlDbType.NVarChar); - prepExecParms[3].Direction=ParameterDirection.Input; - - prepExecParms[4]=new SqlParameter("scrollopt",SqlDbType.Int); - prepExecParms[4].Direction=ParameterDirection.InputOutput; - - prepExecParms[5]=new SqlParameter("ccopt",SqlDbType.Int); - prepExecParms[5].Direction=ParameterDirection.InputOutput; - prepExecParms[5].Value=1;//Readonly - - prepExecParms[6]=new SqlParameter("rowcount",SqlDbType.Int); - prepExecParms[6].Direction=ParameterDirection.InputOutput; - - //-----------------Exec command - execCommand = new SqlCommand("sp_cursorexecute"); - execCommand.CommandType=CommandType.StoredProcedure; - execCommand.CommandTimeout = commandTimeout; - - execParms=new SqlParameter[5]; - execParms[0]=new SqlParameter("cursorId",SqlDbType.Int); - execParms[0].Direction = ParameterDirection.Input; - execParms[1]=new SqlParameter("handle",SqlDbType.Int); - execParms[1].Direction = ParameterDirection.Output; - execParms[2]=new SqlParameter("scrollopt",SqlDbType.Int); - execParms[2].Direction = ParameterDirection.InputOutput; - execParms[3]=new SqlParameter("ccopt",SqlDbType.Int); - execParms[3].Direction = ParameterDirection.InputOutput; - execParms[3].Value=1;//Readonly - execParms[4]=new SqlParameter("rowcount",SqlDbType.Int); - execParms[4].Direction = ParameterDirection.InputOutput; - - //------------------prepareCursor - prepareCommand = new SqlCommand("sp_cursorprepare"); - prepareCommand.CommandType=CommandType.StoredProcedure; - prepareCommand.CommandTimeout = commandTimeout; - prepareParms=new SqlParameter[6]; - - prepareParms[0]=new SqlParameter("cursor",SqlDbType.Int); - prepareParms[0].Direction = ParameterDirection.Output; - prepareParms[0].Value=DBNull.Value; - - prepareParms[1]=new SqlParameter("parameters",SqlDbType.NVarChar); - prepareParms[1].Direction = ParameterDirection.Input; + prepExecParms[1] = new SqlParameter("handle", SqlDbType.Int); + prepExecParms[1].Direction = ParameterDirection.Output; + prepExecParms[1].Value = 0; + + prepExecParms[2] = new SqlParameter("parameters", SqlDbType.NVarChar); + prepExecParms[2].Direction = ParameterDirection.Input; - prepareParms[2]=new SqlParameter("stmt",SqlDbType.NVarChar); - prepareParms[2].Direction = ParameterDirection.Input; + prepExecParms[3] = new SqlParameter("stmt", SqlDbType.NVarChar); + prepExecParms[3].Direction = ParameterDirection.Input; - prepareParms[3]=new SqlParameter("options",SqlDbType.Int); - prepareParms[3].Direction = ParameterDirection.Input; - prepareParms[3].Value=1; + prepExecParms[4] = new SqlParameter("scrollopt", SqlDbType.Int); + prepExecParms[4].Direction = ParameterDirection.InputOutput; - prepareParms[4]=new SqlParameter("scrollopt",SqlDbType.Int); - prepareParms[4].Direction = ParameterDirection.InputOutput; + prepExecParms[5] = new SqlParameter("ccopt", SqlDbType.Int); + prepExecParms[5].Direction = ParameterDirection.InputOutput; + prepExecParms[5].Value = 1;//Readonly - prepareParms[5]=new SqlParameter("ccopt",SqlDbType.Int); - prepareParms[5].Direction = ParameterDirection.InputOutput; - prepareParms[5].Value=4; + prepExecParms[6] = new SqlParameter("rowcount", SqlDbType.Int); + prepExecParms[6].Direction = ParameterDirection.InputOutput; - //----------------Close command - closeCommand = new SqlCommand("sp_cursorclose"); - closeCommand.CommandType=CommandType.StoredProcedure; - closeCommand.CommandTimeout = commandTimeout; - SqlParameter handle4 = closeCommand.Parameters.Add("handle", SqlDbType.Int); - handle4.Direction = ParameterDirection.Input; + //-----------------Exec command + execCommand = new SqlCommand("sp_cursorexecute"); + execCommand.CommandType = CommandType.StoredProcedure; + execCommand.CommandTimeout = commandTimeout; - //------------------unprepareCursor - unprepareCommand =new SqlCommand("sp_cursorunprepare"); - unprepareCommand.CommandType = CommandType.StoredProcedure; - unprepareCommand.CommandTimeout = commandTimeout; + execParms = new SqlParameter[5]; + execParms[0] = new SqlParameter("cursorId", SqlDbType.Int); + execParms[0].Direction = ParameterDirection.Input; + execParms[1] = new SqlParameter("handle", SqlDbType.Int); + execParms[1].Direction = ParameterDirection.Output; + execParms[2] = new SqlParameter("scrollopt", SqlDbType.Int); + execParms[2].Direction = ParameterDirection.InputOutput; + execParms[3] = new SqlParameter("ccopt", SqlDbType.Int); + execParms[3].Direction = ParameterDirection.InputOutput; + execParms[3].Value = 1;//Readonly + execParms[4] = new SqlParameter("rowcount", SqlDbType.Int); + execParms[4].Direction = ParameterDirection.InputOutput; - SqlParameter cursor1 = unprepareCommand.Parameters.Add("cursor", SqlDbType.Int); - cursor1.Direction = ParameterDirection.Input; + //------------------prepareCursor + prepareCommand = new SqlCommand("sp_cursorprepare"); + prepareCommand.CommandType = CommandType.StoredProcedure; + prepareCommand.CommandTimeout = commandTimeout; + prepareParms = new SqlParameter[6]; + prepareParms[0] = new SqlParameter("cursor", SqlDbType.Int); + prepareParms[0].Direction = ParameterDirection.Output; + prepareParms[0].Value = DBNull.Value; + + prepareParms[1] = new SqlParameter("parameters", SqlDbType.NVarChar); + prepareParms[1].Direction = ParameterDirection.Input; + + prepareParms[2] = new SqlParameter("stmt", SqlDbType.NVarChar); + prepareParms[2].Direction = ParameterDirection.Input; + + prepareParms[3] = new SqlParameter("options", SqlDbType.Int); + prepareParms[3].Direction = ParameterDirection.Input; + prepareParms[3].Value = 1; + + prepareParms[4] = new SqlParameter("scrollopt", SqlDbType.Int); + prepareParms[4].Direction = ParameterDirection.InputOutput; + + prepareParms[5] = new SqlParameter("ccopt", SqlDbType.Int); + prepareParms[5].Direction = ParameterDirection.InputOutput; + prepareParms[5].Value = 4; + + //----------------Close command + closeCommand = new SqlCommand("sp_cursorclose"); + closeCommand.CommandType = CommandType.StoredProcedure; + closeCommand.CommandTimeout = commandTimeout; + SqlParameter handle4 = closeCommand.Parameters.Add("handle", SqlDbType.Int); + handle4.Direction = ParameterDirection.Input; + + //------------------unprepareCursor + unprepareCommand = new SqlCommand("sp_cursorunprepare"); + unprepareCommand.CommandType = CommandType.StoredProcedure; + unprepareCommand.CommandTimeout = commandTimeout; + + SqlParameter cursor1 = unprepareCommand.Parameters.Add("cursor", SqlDbType.Int); + cursor1.Direction = ParameterDirection.Input; + } } } public class GxPreparedStatementCache : Dictionary { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxPreparedStatementCache)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private IGxConnection conn; public GxPreparedStatementCache(IGxConnection connection, int maxSize):base(maxSize) @@ -3103,7 +3190,7 @@ public void dump() public class GxSqlCursorDataReader: IDataReader { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxSqlCursorDataReader)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static int STARTPOS = -1; private int pos= STARTPOS; private ushort fetchSize; @@ -4044,7 +4131,7 @@ public string ValueList(IList Values) sealed internal class MssqlConnectionWrapper : GxAbstractConnectionWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.MssqlConnectionWrapper)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); int sqlserver7 = -1; int sqlserver9 = -1; @@ -4067,6 +4154,7 @@ public MssqlConnectionWrapper(String connectionString, GxConnectionCache connCac } override public void Open() { + m_connectionCache.InitCommands(); InternalConnection.Open(); if (!m_autoCommit) { @@ -4161,7 +4249,7 @@ public override bool IsSQLServer9() public abstract class GxAbstractConnectionWrapper : IDbConnection { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxAbstractConnectionWrapper)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); protected short m_spid; protected IDbConnection _connection; protected bool m_autoCommit; @@ -4256,7 +4344,28 @@ public virtual void Close() { InternalConnection.Close(); } +#if NETCORE + internal virtual async Task CloseAsync() + { + try + { + DbConnection dbConnection = InternalConnection as DbConnection; + if (dbConnection != null) + { + await dbConnection.CloseAsync(); + } + else + { + InternalConnection.Close(); + } + } + catch (Exception ex) + { + throw new DataException(ex.Message, ex); + } + } +#endif public void ChangeDatabase(String database) { throw new NotSupportedException("NoChangeMsg00" + database); @@ -4300,7 +4409,7 @@ public virtual short RollbackSavePoint(IDbTransaction transaction, string savepo public class GxCacheDataReader: IDataReader { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxCacheDataReader)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); protected GxArrayList block; protected int pos; int blockSize; diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2.cs index a3849f245..39d6c41f1 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2.cs @@ -4,12 +4,12 @@ using System.Reflection; using System.Text; using System.Threading; +using System.Threading.Tasks; using GeneXus.Application; using GeneXus.Cache; using GeneXus.Metadata; using GeneXus.Utils; using GxClasses.Helpers; -using log4net; namespace GeneXus.Data { @@ -116,7 +116,8 @@ protected override string BuildConnectionString(string datasourceName, string us public class GxDb2 : GxDataRecord { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + public static string SQL_NULL_DATE_10="0000-00-00"; public static string SQL_NULL_DATE_8="00000000"; static Assembly _db2Assembly; @@ -523,7 +524,7 @@ sealed internal class Db2ConnectionWrapper : GxAbstractConnectionWrapper private static int changeConnState=-1; private static int changeConnStateExecuting = -1; private int openDataReaders; - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public Db2ConnectionWrapper() { try @@ -655,7 +656,20 @@ override public void Close() throw new DataException(ex.Message, ex); } } - +#if NETCORE + internal override async Task CloseAsync() + { + try + { + CheckState(false); + await base.CloseAsync(); + } + catch (Exception ex) + { + throw new DataException(ex.Message, ex); + } + } +#endif override public IDbCommand CreateCommand() { return InternalConnection.CreateCommand(); diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2400.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2400.cs index 263f83260..38ea9a538 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2400.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2400.cs @@ -6,7 +6,6 @@ #if !NETCORE using Microsoft.HostIntegration.MsDb2Client; #endif -using log4net; using GeneXus.Application; using GeneXus.Cache; using GeneXus.Configuration; @@ -45,7 +44,7 @@ public override object GetValue(int i) } sealed internal class Db2ISeriesConnectionWrapper : GxAbstractConnectionWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public Db2ISeriesConnectionWrapper() { try @@ -121,7 +120,7 @@ public override DbDataAdapter CreateDataAdapter() public class GxDb2ISeries : GxDataRecord { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private bool m_UseCharInDate; public static string SQL_NULL_DATE="00000000"; private string m_InitialCatalog; @@ -653,7 +652,7 @@ public override void DisposeCommand(IDbCommand command) } catch (Exception ex) { - GXLogging.Error(log, "DisposeCommand error", ex); + GXLogging.Warn(log, "DisposeCommand error", ex); } } @@ -768,7 +767,7 @@ public override string ToDbmsConstant(DateTime Value) } sealed internal class Db2ISeriesHISConnectionWrapper : GxAbstractConnectionWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public Db2ISeriesHISConnectionWrapper() : base(new MsDb2Connection()) { } @@ -827,7 +826,7 @@ public override DbDataAdapter CreateDataAdapter() public class GxISeriesHIS : GxDataRecord { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private bool m_UseCharInDate; private string m_InitialCatalog; private string m_SqlPackage; diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataHana.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataHana.cs index 484449b3c..14954b0ca 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataHana.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataHana.cs @@ -4,17 +4,17 @@ using System.IO; using System.Reflection; using System.Text; +using System.Threading.Tasks; using GeneXus.Cache; using GeneXus.Metadata; using GeneXus.Utils; using GxClasses.Helpers; -using log4net; namespace GeneXus.Data { public class GxHana : GxDataRecord { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); static Assembly _hanaAssembly; const string HanaDbTypeEnum = "Sap.Data.Hana.HanaDbType"; #if NETCORE @@ -321,8 +321,8 @@ sealed internal class HanaConnectionWrapper : GxAbstractConnectionWrapper { const string HanaIsolationEnum = "Sap.Data.Hana.HanaIsolationLevel"; - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - public HanaConnectionWrapper() + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + public HanaConnectionWrapper() { try { @@ -416,7 +416,6 @@ override public void Close() { try { - CheckState(false); InternalConnection.Close(); } catch (Exception ex) @@ -424,7 +423,20 @@ override public void Close() throw new DataException(ex.Message, ex); } } - +#if NETCORE + internal override async Task CloseAsync() + { + try + { + CheckState(false); + await base.CloseAsync(); + } + catch (Exception ex) + { + throw new DataException(ex.Message, ex); + } + } +#endif override public IDbCommand CreateCommand() { return InternalConnection.CreateCommand(); diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataInformix.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataInformix.cs index 3c92389b9..8a731512f 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataInformix.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataInformix.cs @@ -2,25 +2,23 @@ using System.Collections; using System.Data; using System.Data.Common; +using System.Globalization; +using System.IO; using System.Reflection; using System.Text; using System.Threading; - -using log4net; using GeneXus.Application; using GeneXus.Cache; using GeneXus.Configuration; -using GeneXus.Utils; -using System.Globalization; using GeneXus.Metadata; +using GeneXus.Utils; using GxClasses.Helpers; -using System.IO; namespace GeneXus.Data { public class GxInformix : GxDataRecord { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); static Assembly _ifxAssembly; #if NETCORE internal static string InformixAssemblyName = "Informix.Net.Core"; @@ -459,7 +457,7 @@ public override IDbCommand GetCachedCommand(IGxConnection con, string stmt) public class GxInformixDataReader : GxDataReader { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxInformixDataReader)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GxInformixDataReader(IGxConnectionManager connManager, GxDataRecord dr, IGxConnection connection, GxParameterCollection parameters, string stmt, int fetchSize, bool forFirst, int handle, bool cached, SlidingTime expiration, bool dynStmt) : base(connManager, dr, connection, parameters, stmt, fetchSize, forFirst, handle, cached, expiration, dynStmt) @@ -586,7 +584,7 @@ sealed internal class InformixConnectionWrapper : GxAbstractConnectionWrapper { private static int changeConnState = -1; private int openDataReaders; - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public InformixConnectionWrapper() { try diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs index 14ed3a8be..56d6684c2 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs @@ -1,29 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Reflection; +using System.Security; +using System.Text; using GeneXus.Application; using GeneXus.Cache; using GeneXus.Utils; -using log4net; +using GxClasses.Helpers; using MySQLCommand = MySqlConnector.MySqlCommand; -using MySQLParameter = MySqlConnector.MySqlParameter; using MySQLConnection = MySqlConnector.MySqlConnection; -using MySQLException = MySqlConnector.MySqlException; -using MySQLDbType = MySqlConnector.MySqlDbType; using MySQLDataAdapter = MySqlConnector.MySqlDataAdapter; -using System.IO; -using GxClasses.Helpers; -using System.Reflection; -using System; -using System.Data; -using System.Data.Common; -using System.Text; -using System.Collections.Generic; -using System.Security; +using MySQLDbType = MySqlConnector.MySqlDbType; +using MySQLException = MySqlConnector.MySqlException; +using MySQLParameter = MySqlConnector.MySqlParameter; namespace GeneXus.Data { - + public class GxMySqlConnector : GxDataRecord { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxMySqlConnector)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private int MAX_TRIES; private int m_FailedConnections; #if NETCORE @@ -49,27 +47,9 @@ public override GxAbstractConnectionWrapper GetConnection(bool showPrompt, strin return new MySqlConnectorConnectionWrapper(m_connectionString, connectionCache, isolationLevel); } - string convertToMySqlCall(string stmt, GxParameterCollection parameters) - { - if (parameters == null) - return ""; - string pname; - StringBuilder sBld = new StringBuilder(); - for (int i = 0; i < parameters.Count; i++) - { - if (i > 0) - sBld.Append(", "); - pname = "@" + parameters[i].ParameterName; - sBld.Append(pname); - parameters[i].ParameterName = pname; - } - return "CALL " + stmt + "(" + sBld.ToString() + ")"; - } [SecuritySafeCritical] public override IDbCommand GetCommand(IGxConnection con, string stmt, GxParameterCollection parameters, bool isCursor, bool forFirst, bool isRpc) { - if (isRpc) - stmt = convertToMySqlCall(stmt, parameters); MySQLCommand mysqlcmd = (MySQLCommand)base.GetCommand(con, stmt, parameters.Distinct()); if (isCursor && !isRpc) { @@ -498,7 +478,9 @@ public override void SetParameter(IDbDataParameter parameter, object value) [SecuritySafeCritical] sealed internal class MySqlConnectorConnectionWrapper : GxAbstractConnectionWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.MySqlConnectorConnectionWrapper)); + + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + [SecuritySafeCritical] public MySqlConnectorConnectionWrapper() : base(new MySQLConnection()) { } @@ -566,7 +548,7 @@ public override short RollbackSavePoint(IDbTransaction transaction, string savep [SecuritySafeCritical] public class GxMySQLConnectorDataReader : GxDataReader { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxMySQLConnectorDataReader)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GxMySQLConnectorDataReader(IGxConnectionManager connManager, GxDataRecord dr, IGxConnection connection, GxParameterCollection parameters, string stmt, int fetchSize, bool forFirst, int handle, bool cached, SlidingTime expiration, bool dynStmt) { @@ -589,7 +571,7 @@ public GxMySQLConnectorDataReader(IGxConnectionManager connManager, GxDataRecord reader = cmd.ExecuteReader(); cache.SetAvailableCommand(stmt, false, dynStmt); open = true; - block = new GxArrayList(fetchSize); + block = new List(fetchSize); pos = -1; if (cached) { @@ -618,8 +600,7 @@ public override string GetString(int i) [SecuritySafeCritical] public class GxMySQLConnectorCursorDataReader : GxDataReader { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxDataReader)); - + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GxMySQLConnectorCursorDataReader(IGxConnectionManager connManager, GxDataRecord dr, IGxConnection connection, GxParameterCollection parameters, string stmt, int fetchSize, bool forFirst, int handle, bool cached, SlidingTime expiration, bool hasNested, bool dynStmt) { @@ -642,7 +623,7 @@ public GxMySQLConnectorCursorDataReader(IGxConnectionManager connManager, GxData reader = cmd.ExecuteReader(); cache.SetAvailableCommand(stmt, false, dynStmt); open = true; - block = new GxArrayList(fetchSize); + block = new List(fetchSize); pos = -1; if (cached) { diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlDriverCS.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlDriverCS.cs index 54fb262e4..956b7c011 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlDriverCS.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlDriverCS.cs @@ -1,18 +1,19 @@ -using GeneXus.Application; -using GeneXus.Cache; -using GeneXus.Utils; -using log4net; -using MySQLDriverCS; using System; +using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Text; +using GeneXus.Application; +using GeneXus.Cache; +using GeneXus.Utils; +using MySQLDriverCS; namespace GeneXus.Data { public class GxMySql : GxDataRecord { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxMySql)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private int MAX_TRIES; private int m_FailedConnections; private bool preparedStmts; @@ -403,7 +404,7 @@ public override void SetParameter(IDbDataParameter parameter, object value) sealed internal class MySqlDriverCSConnectionWrapper : GxAbstractConnectionWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.MySqlDriverCSConnectionWrapper)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public MySqlDriverCSConnectionWrapper() : base(new MySQLConnection()) { } @@ -497,7 +498,7 @@ public GxMySQLDriverCSDataReader(IGxConnectionManager connManager, GxDataRecord reader = cmd.ExecuteReader(); cache.SetAvailableCommand(stmt, false, dynStmt); open = true; - block = new GxArrayList(fetchSize); + block = new List(fetchSize); pos = -1; if (cached) { @@ -516,7 +517,7 @@ public override string GetString(int i) } public class GxMySQLDriverCSCursorDataReader : GxDataReader { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxDataReader)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GxMySQLDriverCSCursorDataReader(IGxConnectionManager connManager, GxDataRecord dr, IGxConnection connection, GxParameterCollection parameters, string stmt, int fetchSize, bool forFirst, int handle, bool cached, SlidingTime expiration, bool hasNested, bool dynStmt) @@ -542,7 +543,7 @@ public GxMySQLDriverCSCursorDataReader(IGxConnectionManager connManager, GxDataR reader = cmd.ExecuteReader(); cache.SetAvailableCommand(stmt, false, dynStmt); open = true; - block = new GxArrayList(fetchSize); + block = new List(fetchSize); pos = -1; if (cached) { diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTier.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTier.cs index 467cb2b4f..fca522d83 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTier.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTier.cs @@ -1,15 +1,14 @@ using System; -using GeneXus.Application; +using System.Collections.Generic; using System.Data; +using System.Text; +using GeneXus.Application; using GeneXus.Configuration; +using GeneXus.Data.ADO; using GeneXus.Data.NTier.ADO; -using GeneXus.Reorg; using GeneXus.Performance; -using log4net; -using System.Collections.Generic; -using GeneXus.Data.ADO; +using GeneXus.Reorg; using GeneXus.Utils; -using System.Text; namespace GeneXus.Data.NTier { @@ -142,7 +141,7 @@ public interface IDataStoreHelper public class DataStoreHelperBase { /*DO NOT ADD INSTANCE VARIABLES IN THIS CLASS, THIS IS REFERENCED BY THE STATIC CURSORDEF ARRAY IN THE XX___DEFAULT, ALL THE VARIABLES HERE LIVE FOREVER*/ - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(DataStoreHelperBase)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private const string AND = " and "; private const string WHERE = " WHERE "; @@ -342,7 +341,8 @@ public interface IDataStoreProviderBase public class DataStoreProvider : IDataStoreProviderBase,IDataStoreProvider { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.NTier.DataStoreProvider)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + ICursor[] _cursor; Object[][] results; [System.Diagnostics.CodeAnalysis.SuppressMessage("GxFxCopRules", "CR1000:EnforceThreadSafeType")] diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierADO.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierADO.cs index 5d2795af9..bb448e76e 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierADO.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierADO.cs @@ -1,20 +1,19 @@ using System; -using System.Data; using System.Collections; -using GeneXus.Data.ADO; -using GeneXus.Configuration; -using GeneXus.Utils; +using System.Collections.Generic; +using System.Data; using System.IO; -using log4net; +using System.Net; using GeneXus.Application; -using System.Collections.Generic; +using GeneXus.Configuration; +using GeneXus.Data.ADO; using GeneXus.Services; -using System.Net; using GeneXus.Storage; +using GeneXus.Utils; namespace GeneXus.Data.NTier.ADO { - public class GXFatErrorFieldGetter : IFieldGetter + public class GXFatErrorFieldGetter : IFieldGetter { GxCommand _gxDbCommand; public GXFatErrorFieldGetter(GxCommand gxDbCommand) @@ -204,18 +203,18 @@ public IGeographicNative getGeospatial(int id) } public class GXFatFieldGetter : IFieldGetter { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.NTier.ADO.GXFatFieldGetter)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); IGxDbCommand _gxDbCommand; IDataReader _DR; public GXFatFieldGetter(GxCommand gxDbCommand) { _gxDbCommand = gxDbCommand; } - void TraceRow(params string[] list) + void TraceRow(Func buildMsg) { - if (_gxDbCommand.HasMoreRows) + if (GXLogging.TraceEnabled(log) && _gxDbCommand.HasMoreRows) { - GXLogging.Trace(log, list); + GXLogging.Trace(log, buildMsg); } } public IDataReader DataReader @@ -226,57 +225,57 @@ public IDataReader DataReader public short getShort(int id) { short value = _gxDbCommand.Db.GetShort(_gxDbCommand, _DR, id - 1); - TraceRow("getShort - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(()=> $"getShort - index : {id} value:{value}"); return value; } public int getInt(int id) { int value = _gxDbCommand.Db.GetInt(_gxDbCommand, _DR, id - 1); - TraceRow("getInt - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getInt - index : {id} value:{value}"); return value; } public bool getBool(int id) { bool value = _gxDbCommand.Db.GetBoolean(_gxDbCommand, _DR, id - 1); - TraceRow("getBool - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getBool - index : {id} value:{value}"); return value; } public Guid getGuid(int id) { Guid value = _gxDbCommand.Db.GetGuid(_gxDbCommand, _DR, id - 1); - TraceRow("getGuid - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getGuid - index : {id} value:{value}"); return value; } public long getLong(int id) { long value = _gxDbCommand.Db.GetLong(_gxDbCommand, _DR, id - 1); - TraceRow("getLong - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getLong - index : {id} value:{value}"); return value; } public double getDouble(int id) { double value= _gxDbCommand.Db.GetDouble(_gxDbCommand, _DR, id - 1); - TraceRow("getDouble - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDouble - index : {id} value:{value}"); return value; } public Decimal getDecimal(int id) { Decimal value= _gxDbCommand.Db.GetDecimal(_gxDbCommand, _DR, id - 1); - TraceRow("getDecimal - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDecimal - index : {id} value:{value}"); return value; } public string getString(int id, int size) { String value = _gxDbCommand.Db.GetString(_gxDbCommand, _DR, id - 1, size); - TraceRow("getString - index : ", id.ToString(), " value:", (value!=null ? value.ToString(): string.Empty)); + TraceRow(() => $"getString - index : {id} value:{(value!=null ? value.ToString(): string.Empty)}"); return value; } public DateTime getDateTime(int id) { DateTime value = _gxDbCommand.Db.GetDateTime(_gxDbCommand, _DR, id - 1); - TraceRow("getDateTime - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDateTime - index : {id} value:{value}"); return value; } public DateTime getDateTime(int id, Boolean precision) @@ -288,19 +287,19 @@ public DateTime getDateTime(int id, Boolean precision) else { value = _gxDbCommand.Db.GetDateTime(_gxDbCommand, _DR, id - 1); } - TraceRow("getDateTime - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDateTime - index : {id} value:{value}"); return value; } public DateTime getDate(int id) { DateTime value = _gxDbCommand.Db.GetDate(_gxDbCommand, _DR, id - 1); - TraceRow("getDate - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDate - index : {id} value:{value}"); return value; } public string getLongVarchar(int id) { string value = _gxDbCommand.Db.GetString(_gxDbCommand, _DR, id - 1); - TraceRow("getLongVarchar - index : ", id.ToString(), " value:", (value!=null ? value.ToString(): string.Empty)); + TraceRow(() => $"getLongVarchar - index : {id} value:{(value != null ? value.ToString() : string.Empty)}"); return value; } public DateTime getGXDateTime(int id, Boolean precision) @@ -310,7 +309,7 @@ public DateTime getGXDateTime(int id, Boolean precision) #else DateTime value = DateTimeUtil.DBserver2local(getDateTime(id, precision), _gxDbCommand.Conn.ClientTimeZone); #endif - TraceRow("getDateTime - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDateTime - index : {id} value:{value}"); return value; } public DateTime getGXDateTime(int id) @@ -320,19 +319,19 @@ public DateTime getGXDateTime(int id) #else DateTime value = DateTimeUtil.DBserver2local(getDateTime(id, false), _gxDbCommand.Conn.ClientTimeZone); #endif - TraceRow("getGXDateTime - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getGXDateTime - index : {id} value:{value.ToString()}"); return value; } public DateTime getGXDate(int id) { DateTime value = getDate(id); - TraceRow("getGXDate - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getGXDate - index : {id} value:{value.ToString()}"); return value; } public string getBLOBFile(int id) { string value= getBLOBFile(id, "tmp", ""); - TraceRow("getBLOBFile - index : ", id.ToString(), " value:", (value!=null ? value.ToString() : string.Empty)); + TraceRow(() => $"getBLOBFile - index : {id} value:{(value != null ? value.ToString() : string.Empty)}"); return value; } @@ -340,7 +339,7 @@ public string getBLOBFile(int id, string extension, string name) { string fileName = FileUtil.getTempFileName(_gxDbCommand.Conn.BlobPath, name, extension, GxFileType.Private); String value = getBLOBFile(id, extension, name, fileName, true); - TraceRow("getBLOBFile - index : ", id.ToString(), " value:", (value!=null ? value.ToString() : string.Empty)); + TraceRow(() => $"getBLOBFile - index : {id} value:{(value != null ? value.ToString() : string.Empty)}"); return value; } @@ -390,7 +389,7 @@ private string getBLOBFile(int id, string extension, string name, string fileNam } streamClosed = true; - TraceRow("GetBlobFile fileName:" + fileName + ", retval bytes:" + retval); + TraceRow(() => $"GetBlobFile fileName:{fileName}, retval bytes:{retval}"); if (temporary) GXFileWatcher.Instance.AddTemporaryFile(file, _gxDbCommand.Conn.DataStore.Context); @@ -474,20 +473,20 @@ public string getMultimediaUri(int id, bool absUrl) public string getVarchar(int id) { string value = _gxDbCommand.Db.GetString(_gxDbCommand, _DR, id - 1); - TraceRow("getVarchar - index : ", id.ToString(), " value:", (value != null ? value.ToString() : string.Empty)); + TraceRow(() => $"getVarchar - index : {id} value:{(value != null ? value.ToString() : string.Empty)}"); return value; } public decimal getBigDecimal(int id, int dec) { decimal value =_gxDbCommand.Db.GetDecimal(_gxDbCommand, _DR, id - 1); - TraceRow("getBigDecimal - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getBigDecimal - index : {id} value:{value.ToString()}"); return value; } public IGeographicNative getGeospatial(int id) { IGeographicNative value = _gxDbCommand.Db.GetGeospatial(_gxDbCommand, _DR, id - 1); - TraceRow("getGeospatial - index : ", id.ToString(), " value:", (value != null ? value.ToString() : string.Empty)); + TraceRow(() => $"getGeospatial - index : {id} value:{(value != null ? value.ToString() : string.Empty)}"); return value; } @@ -499,7 +498,7 @@ public bool wasNull(int id) } public class GXFatFieldSetter : IFieldSetter { - protected static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXFatFieldSetter)); + protected static readonly IGXLogger log = GXLoggerFactory.GetLogger(); GxCommand _gxDbCommand; public GXFatFieldSetter(GxCommand gxDbCommand) { @@ -792,7 +791,7 @@ public void RestoreParametersRT() } public class Cursor : ICursor { - protected static readonly ILog log = log4net.LogManager.GetLogger(typeof(Cursor)); + protected static readonly IGXLogger log = GXLoggerFactory.GetLogger(); protected int _state = 0; protected string _name; protected string _stmt; diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierService.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierService.cs index 98b89d3af..57c25823c 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierService.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierService.cs @@ -1,21 +1,12 @@ -using System.Text; - using System; using System.Data; -using System.Collections; -using GeneXus.Data.ADO; -using GeneXus.Configuration; +using System.Data.Common; +using System.Text; using GeneXus.Cache; -using GeneXus.Utils; -using System.IO; -using log4net; -using GeneXus.Application; -using System.Collections.Generic; using GeneXus.Data.NTier.ADO; -using System.Reflection; -using GeneXus.Metadata; -using System.Data.Common; using GeneXus.Helpers; +using GeneXus.Metadata; +using GeneXus.Utils; namespace GeneXus.Data.NTier { @@ -65,7 +56,7 @@ public ServiceCursorDef(string name, object query, GxErrorMask nmask, Object[] p public class GxServiceFactory { - protected static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + protected static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public static GxService Create(string id, string providerId, string serviceClass) { @@ -75,7 +66,7 @@ public static GxService Create(string id, string providerId, string serviceClass public class GxService : GxDataRecord { - protected static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + protected static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private Type m_ServiceType; private CursorDef m_CursorDef; @@ -116,7 +107,7 @@ public override GxAbstractConnectionWrapper GetConnection(bool showPrompt, strin { if (string.IsNullOrEmpty(m_connectionString)) m_connectionString = BuildConnectionString(datasourceName, userId, userPassword, databaseName, port, schema, extra); - GXLogging.Debug(log, "Setting connectionString property ", () => BuildConnectionString(datasourceName, userId, NaV, databaseName, port, schema, extra)); + GXLogging.Debug(log, "Setting connectionString property ", () => BuildConnectionStringForLog(datasourceName, userId, NaV, databaseName, port, schema, extra)); return new ServiceConnectionWrapper(m_ServiceType, m_connectionString, connectionCache, isolationLevel, DataSource); } @@ -316,8 +307,7 @@ public interface IServiceCommand sealed public class ServiceConnectionWrapper : GxAbstractConnectionWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public ServiceConnectionWrapper(Type runtimeClassType, String connectionString, GxConnectionCache connCache, IsolationLevel isolationLevel, String dataSource) { try @@ -373,7 +363,6 @@ override public void Close() { try { - CheckState(false); InternalConnection.Close(); } catch (Exception ex) diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataOracle.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataOracle.cs index 0e93a3efe..a7fb9acdd 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataOracle.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataOracle.cs @@ -9,7 +9,6 @@ using GeneXus.Configuration; using GeneXus.Metadata; using GeneXus.Utils; -using log4net; using System.IO; #if NETCORE using GxClasses.Helpers; @@ -19,7 +18,7 @@ namespace GeneXus.Data { public class GxODPCacheDataReader : GxOracleCacheDataReader { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxODPCacheDataReader)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GxODPCacheDataReader(CacheItem cacheItem, bool computeSize, string keyCache) : base(cacheItem, computeSize, keyCache) { } @@ -116,7 +115,7 @@ public override bool IsDBNull(int i) } public class GxODPManagedCacheDataReader : GxOracleCacheDataReader { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxODPManagedCacheDataReader)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GxODPManagedCacheDataReader(CacheItem cacheItem, bool computeSize, string keyCache) : base(cacheItem, computeSize, keyCache) { } @@ -235,7 +234,7 @@ public override long GetBytes(int i, long fieldOffset, byte[] buffer, int buffer public class GxODPOracle : GxOracle { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxODPOracle)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); static Assembly _odpAssembly; const string OracleDbTypeEnum = "Oracle.DataAccess.Client.OracleDbType"; @@ -547,7 +546,8 @@ internal static bool IsClobType(object value) } public class GxODPManagedOracle : GxOracle { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxODPManagedOracle)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + static Assembly _odpAssembly; const string OracleDbTypeEnum = "Oracle.ManagedDataAccess.Client.OracleDbType"; const string OracleAssemblyName = "Oracle.ManagedDataAccess"; @@ -859,7 +859,7 @@ internal static bool IsClobType(object value) public class GxOracle : GxDataRecord { const string BLANK_STRING = " "; - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxOracle)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); #if !NETCORE public override GxAbstractConnectionWrapper GetConnection(bool showPrompt, string datasourceName, string userId, string userPassword, string databaseName, string port, string schema, string extra, GxConnectionCache connectionCache) @@ -1304,7 +1304,7 @@ public override string ConcatOp(int pos) } public class GxODPOracleDataReader : GxDataReader { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxODPOracleDataReader)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GxODPOracleDataReader(IGxConnectionManager connManager, GxDataRecord dr, IGxConnection connection, GxParameterCollection parameters, string stmt, int fetchSize, bool forFirst, int handle, bool cached, SlidingTime expiration, bool dynStmt) : base(connManager, dr, connection, parameters, stmt, fetchSize, forFirst, handle, cached, expiration, dynStmt) @@ -1370,7 +1370,7 @@ public override decimal GetDecimal(int i) public class GxODPManagedOracleDataReader : GxDataReader { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxODPManagedOracleDataReader)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GxODPManagedOracleDataReader(IGxConnectionManager connManager, GxDataRecord dr, IGxConnection connection, GxParameterCollection parameters, string stmt, int fetchSize, bool forFirst, int handle, bool cached, SlidingTime expiration, bool dynStmt) : base(connManager, dr, connection, parameters, stmt, fetchSize, forFirst, handle, cached, expiration, dynStmt) @@ -1436,7 +1436,7 @@ public override decimal GetDecimal(int i) #if !NETCORE sealed internal class MSOracleConnectionWrapper : GxAbstractConnectionWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); int oracle8 = -1; public MSOracleConnectionWrapper() : base(new MSOracleProvider.OracleConnection()) { @@ -1531,7 +1531,7 @@ public override short RollbackSavePoint(IDbTransaction transaction, string savep #endif sealed internal class OracleConnectionWrapper : GxAbstractConnectionWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(OracleConnectionWrapper)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public OracleConnectionWrapper() { try @@ -1625,7 +1625,7 @@ public override short RollbackSavePoint(IDbTransaction transaction, string savep sealed internal class OracleManagedConnectionWrapper : GxAbstractConnectionWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(OracleManagedConnectionWrapper)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public OracleManagedConnectionWrapper() { try diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataPostgreSQL.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataPostgreSQL.cs index b91c9e4cd..f89acc4dd 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataPostgreSQL.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataPostgreSQL.cs @@ -1,6 +1,5 @@ using System; using System.Text; -using log4net; using System.Data; using System.Data.Common; using GeneXus.Utils; @@ -20,7 +19,7 @@ namespace GeneXus.Data { public class GxPostgreSql : GxDataRecord { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); const string ConnectionStringEncoding = "encoding"; private byte[] _buffer; static Assembly _npgsqlAssembly; @@ -587,7 +586,10 @@ public override string ConcatOp(int pos) } sealed internal class PostgresqlConnectionWrapper : GxAbstractConnectionWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); +#if NETCORE + const string INFINITY_CONVERSIONS = "Npgsql.DisableDateTimeInfinityConversions"; +#endif public PostgresqlConnectionWrapper() { _connection = (IDbConnection)ClassLoader.CreateInstance(GxPostgreSql.NpgsqlAssembly, "Npgsql.NpgsqlConnection"); @@ -598,6 +600,9 @@ public PostgresqlConnectionWrapper(String connectionString, GxConnectionCache co try { _connection = (IDbConnection)ClassLoader.CreateInstance(GxPostgreSql.NpgsqlAssembly, "Npgsql.NpgsqlConnection", new object[] { connectionString }); +#if NETCORE + AppContext.SetSwitch(INFINITY_CONVERSIONS, true); +#endif m_isolationLevel = isolationLevel; m_connectionCache = connCache; } diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataSqlite.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataSqlite.cs index 5d70d808f..966cfe46d 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataSqlite.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataSqlite.cs @@ -1,6 +1,5 @@ using GeneXus.Cache; using GeneXus.Utils; -using log4net; using System; using System.Text; using System.Data; @@ -19,7 +18,7 @@ namespace GeneXus.Data { public class GxSqlite : GxDataRecord { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxSqlite)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private int MAX_TRIES = 10; private int m_FailedConnections; @@ -310,8 +309,7 @@ public override void SetParameterBlob(IDbDataParameter parameter, string value, sealed internal class SQLiteConnectionWrapper : GxAbstractConnectionWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.SQLiteConnectionWrapper)); - + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public SQLiteConnectionWrapper() : base(new SQLiteConnection()) { } diff --git a/dotnet/src/dotnetframework/GxClasses/Diagnostics/GXDebugManager.cs b/dotnet/src/dotnetframework/GxClasses/Diagnostics/GXDebugManager.cs index 252c3caf3..dd51e8e90 100644 --- a/dotnet/src/dotnetframework/GxClasses/Diagnostics/GXDebugManager.cs +++ b/dotnet/src/dotnetframework/GxClasses/Diagnostics/GXDebugManager.cs @@ -5,11 +5,10 @@ using System.IO; using System.Threading; using GeneXus.Application; -using log4net; namespace GeneXus.Diagnostics { - + public class GXDebugManager { public const short GXDEBUG_VERSION = 2; @@ -20,7 +19,7 @@ public class GXDebugManager private static bool initialized; private static GXDebugManager m_Instance; - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Application.GxContext)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public static GXDebugManager Instance { get diff --git a/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs b/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs index 17ede87d5..078672fcc 100644 --- a/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs +++ b/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs @@ -1,7 +1,13 @@ -using System; using GeneXus.Attributes; using GeneXus.Configuration; using log4net; +using System.Collections.Concurrent; + + +#if NETCORE +using GeneXus.Services.Log; +using Microsoft.Extensions.Logging; +#endif namespace GeneXus.Diagnostics { @@ -18,91 +24,100 @@ private enum LogLevel Error = 20, Fatal = 30 } -#if NETCORE - static readonly string defaultRepository = LogManager.GetRepository(System.Reflection.Assembly.GetEntryAssembly()).Name; -#else - static readonly string defaultRepository = LogManager.GetRepository().Name; -#endif - public static string defaultUserLogNamespace = Configuration.Config.GetValueOf("USER_LOG_NAMESPACE", LogConfiguration.USER_LOG_TOPIC); - static readonly ILog globalLog = LogManager.GetLogger(defaultRepository, defaultUserLogNamespace); - - private static ILog GetLogger(string topic) + private const string LoggerPrefix = "$"; + private static readonly string DefaultUserLogNamespace = Config.GetValueOf("USER_LOG_NAMESPACE", LogConfiguration.USER_LOG_TOPIC); + private static ConcurrentDictionary LoggerDictionary = new ConcurrentDictionary() {}; + internal static IGXLogger GetLogger(string topic) { - if (!String.IsNullOrEmpty(topic)) + if (LoggerDictionary.TryGetValue(topic, out IGXLogger logger)) + { + return logger; + } + else { - string loggerName = topic.StartsWith("$") ? topic.Substring(1) : string.Format("{0}.{1}", defaultUserLogNamespace, topic.Trim()); - return LogManager.GetLogger(defaultRepository, loggerName); + string loggerName; + if (!string.IsNullOrEmpty(topic)) + loggerName = topic.StartsWith(LoggerPrefix) ? topic.Substring(1) : $"{DefaultUserLogNamespace}.{topic.Trim()}"; + else + loggerName = DefaultUserLogNamespace; + + logger = GXLoggerFactory.GetLogger(loggerName); + LoggerDictionary.TryAdd(topic, logger); + return logger; } - return globalLog; } public static void Write(int logLevel, string message, string topic) { Write(message, topic, logLevel); } - public static void Write(string message, string topic, int logLevel) { - ILog log = GetLogger(topic); LogLevel logLvl = (LogLevel)logLevel; + WriteImp(message, topic, logLvl); + } - switch (logLvl) + private static void WriteImp(string message, string topic, LogLevel logLvl) + { + if (logLvl != LogLevel.Off) { - case LogLevel.Off: - break; - case LogLevel.Trace: - log.Debug(message); - break; - case LogLevel.Debug: - log.Debug(message); - break; - case LogLevel.Info: - log.Info(message); - break; - case LogLevel.Warn: - log.Warn(message); - break; - case LogLevel.Error: - log.Error(message); - break; - case LogLevel.Fatal: - log.Fatal(message); - break; - default: - log.Debug(message); - break; - } + IGXLogger log = GetLogger(topic); + switch (logLvl) + { + case LogLevel.Trace: + GXLogging.Trace(log, message); + break; + case LogLevel.Debug: + GXLogging.Debug(log, message); + break; + case LogLevel.Info: + GXLogging.Info(log, message); + break; + case LogLevel.Warn: + GXLogging.Warn(log, message); + break; + case LogLevel.Error: + GXLogging.Error(log, message); + break; + case LogLevel.Fatal: + GXLogging.Critical(log, message); + break; + default: + GXLogging.Debug(log, message); + break; + } + } } - + public static void Write(string message, string topic = "") { - GetLogger(topic).Debug(message); + WriteImp(message, topic, LogLevel.Debug); } - + public static void Fatal(string message, string topic = "") { - GetLogger(topic).Fatal(message); + WriteImp(message, topic, LogLevel.Fatal); } public static void Error(string message, string topic = "") { - GetLogger(topic).Error(message); + WriteImp(message, topic, LogLevel.Error); } public static void Warning(string message, string topic = "") { - GetLogger(topic).Warn(message); + WriteImp(message, topic, LogLevel.Warn); } public static void Info(string message, string topic = "") { - GetLogger(topic).Info(message); + WriteImp(message, topic, LogLevel.Info); } public static void Debug(string message, string topic = "") { - GetLogger(topic).Debug(message); + WriteImp(message, topic, LogLevel.Debug); } } } diff --git a/dotnet/src/dotnetframework/GxClasses/Dialog/IDialog.cs b/dotnet/src/dotnetframework/GxClasses/Dialog/IDialog.cs index dedc44ff7..9d2362692 100644 --- a/dotnet/src/dotnetframework/GxClasses/Dialog/IDialog.cs +++ b/dotnet/src/dotnetframework/GxClasses/Dialog/IDialog.cs @@ -1,6 +1,5 @@ -using ConnectionBuilder; -using log4net; using System; +using ConnectionBuilder; namespace GeneXus.Utils { @@ -36,7 +35,7 @@ public static IConnectionDialogFactory DialogFactory } catch (Exception ex) { - var strErrorMsg = $"Error loading Dialogs provider. Check if {OsProviderAssembly}.dll exists "; + string strErrorMsg = $"Error loading Dialogs provider. Check if {OsProviderAssembly}.dll exists "; GXLogging.Debug(log, strErrorMsg, ex); throw new Exception(strErrorMsg, ex); } diff --git a/dotnet/src/dotnetframework/GxClasses/Dialog/IProcess.cs b/dotnet/src/dotnetframework/GxClasses/Dialog/IProcess.cs index 74b48f2f1..bc03e868b 100644 --- a/dotnet/src/dotnetframework/GxClasses/Dialog/IProcess.cs +++ b/dotnet/src/dotnetframework/GxClasses/Dialog/IProcess.cs @@ -1,4 +1,3 @@ -using log4net; using System; using System.Diagnostics; @@ -18,7 +17,8 @@ public interface IProcessHelper } public class OSHelper { - protected static readonly ILog log = log4net.LogManager.GetLogger(typeof(OSHelper)); + protected static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + protected static string OsProviderAssembly = "GxClasses.Win"; protected static object syncRoot = new Object(); diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/ExpressionEvaluator.cs b/dotnet/src/dotnetframework/GxClasses/Domain/ExpressionEvaluator.cs index b9dd259b3..faf841598 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/ExpressionEvaluator.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/ExpressionEvaluator.cs @@ -1,10 +1,8 @@ using System; -using GeneXus.Metadata; +using System.Reflection; using GeneXus.Application; using GeneXus.Configuration; -using System.Reflection; -using log4net; -using System.Collections; +using GeneXus.Metadata; namespace GeneXus.Utils { @@ -17,7 +15,7 @@ namespace GeneXus.Utils /// public class ExpressionEvaluator { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.ExpressionEvaluator)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); internal IGxContext context; internal bool throwExceptions; diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GXFileIO.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GXFileIO.cs index 5788c1d3e..809321a32 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GXFileIO.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GXFileIO.cs @@ -3,7 +3,6 @@ using System.Collections; using GeneXus.Utils; using GeneXus.Application; -using log4net; using System.Runtime.CompilerServices; using GeneXus.Services; using System.Collections.Generic; @@ -223,8 +222,9 @@ public void MoveTo(string desDirName) } public class GxFileInfo : IGxFileInfo { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxFileInfo)); - private FileInfo _file; + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + + private FileInfo _file; private string _baseDirectory; public GxFileInfo(FileInfo file) @@ -584,7 +584,12 @@ public string AbsolutePath private string URL { get { - return _provider == null ? String.Empty : _provider.GetUrl(_name, _fileTypeAtt, 0); + if (_provider == null) + return string.Empty; + else if (!string.IsNullOrEmpty(_url)) + return _url; + else + return _provider.GetUrl(_name, _fileTypeAtt, 0); } } @@ -654,7 +659,7 @@ public enum GxFileType public class GxFile { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxFile)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); internal IGxFileInfo _file; string _baseDirectory; @@ -1574,7 +1579,7 @@ public void Close() public class GxDirectory { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxDirectory)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); IGxDirectoryInfo _directory; string _baseDirectory; int _lastError; diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GXGeolocation.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GXGeolocation.cs index 6dff7a7de..3de8bcdec 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GXGeolocation.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GXGeolocation.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using System.Net; using System.IO; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; -using log4net; +#endif using GeneXus.Utils; using System.Globalization; using GeneXus; @@ -16,10 +18,10 @@ namespace GX { - public class GXGeolocation + public class GXGeolocation { private const String MAPS_URI = "https://maps.google.com/maps/api/"; - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GX.GXGeolocation)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static double GetComponent(String geolocation, int item) { diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GXLDAP.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GXLDAP.cs index da79ac358..c1238f28d 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GXLDAP.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GXLDAP.cs @@ -2,7 +2,8 @@ using System.Collections; using System.DirectoryServices; using System.Text; -using log4net; +using GeneXus.Data; + #if NETCORE using Novell.Directory.Ldap; #endif @@ -11,7 +12,7 @@ namespace GeneXus.Utils { public class GXLDAPClient { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); string _server; int _port; diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GXWindow.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GXWindow.cs index bc9443380..f33c73abf 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GXWindow.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GXWindow.cs @@ -1,6 +1,8 @@ using GeneXus.Utils; using System; +#if !NETCORE using Jayrock.Json; +#endif namespace GeneXus.Application { public class GXWindow : IGxJSONAble diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GXXmlReadWrite.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GXXmlReadWrite.cs index 3e5981eb1..a194195ae 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GXXmlReadWrite.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GXXmlReadWrite.cs @@ -1,19 +1,15 @@ using System; -using System.Xml; using System.Collections; using System.Collections.Specialized; -using System.Text; -using System.Net; using System.IO; +using System.Net; +using System.Text; +using System.Xml; using System.Xml.Schema; using GeneXus.Application; using GeneXus.Http.Client; using GeneXus.Http.Server; using GeneXus.Utils; -using log4net; - -using System.Xml.Xsl; -using System.Xml.XPath; namespace GeneXus.XML { @@ -299,14 +295,7 @@ public void OpenFromString(string s) Resolver.Myself = baseUri; treader = null; mreader = null; - try - { - if (File.Exists(s)) - treader = new XmlTextReader(s); - } - catch { } - if (treader==null) - treader = new XmlTextReader(new StringReader(s)); + treader = new XmlTextReader(new StringReader(s)); SetDtdProcessing(treader, Resolver, validationType); vreader = new XmlValidatingReader( treader ); vreader.XmlResolver = Resolver; @@ -1905,7 +1894,7 @@ public void Dispose() public class GXXMLWriter: IDisposable { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXXMLWriter)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private XmlTextWriter writer; private short errorCode; @@ -2148,6 +2137,10 @@ string removeUnallowedChars( string s) public short WriteElement (string Name, string Value) { WriteStartElement(Name); + if (Value==null) + { + Value = string.Empty; + } valueBuffer = Value; return 0; } @@ -2161,7 +2154,7 @@ public short WriteElement (string Name, object Value) public short WriteElement (string Name) { - WriteElement (Name, ""); + WriteElement (Name, string.Empty); return 0; } diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs index 6da308291..6a6a11ab7 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs @@ -1,26 +1,29 @@ namespace GeneXus.Utils { using System; - using System.Collections.Specialized; using System.Collections; + using System.Collections.Concurrent; using System.Collections.Generic; - using GeneXus.XML; - using System.Reflection; - using System.Xml.Serialization; + using System.Collections.Specialized; + using System.ComponentModel; using System.Data; - using GeneXus.Application; - using System.Text; using System.Globalization; +#if !NETCORE using Jayrock.Json; - using log4net; - using System.ComponentModel; - using System.Xml; - using System.Runtime.Serialization; +#endif using System.Linq; - using GeneXus.Http; + using System.Reflection; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Json; + using System.Text; + using System.Xml; + using System.Xml.Serialization; + using GeneXus.Application; using GeneXus.Configuration; + using GeneXus.Data; + using GeneXus.Http; using GeneXus.Metadata; - using System.Collections.Concurrent; + using GeneXus.XML; public class GxParameterCollection : IDataParameterCollection { @@ -60,7 +63,10 @@ public int Add(IDataParameter value) { return parameters.Add(value); } - + internal void Reverse() + { + parameters.Reverse(); + } public void Clear() { parameters.Clear(); @@ -158,6 +164,7 @@ public GxParameterCollection Distinct() parms.Add(this[j].ParameterName); } } + uniqueParms.Reverse(); return uniqueParms; } else @@ -232,7 +239,7 @@ public bool FromJSonString(string s, GXBaseCollection Messa } catch (Exception ex) { - GXUtil.ErrorToMessages("FromJson Error", ex, Messages); + GXUtil.ErrorToMessages("FromJson Error", ex, Messages, false); return false; } } @@ -703,7 +710,7 @@ public bool FromJSonString(string s, GXBaseCollection Messa } catch (Exception ex) { - GXUtil.ErrorToMessages("FromJson Error", ex, Messages); + GXUtil.ErrorToMessages("FromJson Error", ex, Messages, false); return false; } } @@ -798,8 +805,8 @@ public void FromStringCollection(GxSimpleCollection value) public class GxObjectCollectionBase : GxSimpleCollection { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.GxObjectCollectionBase)); - + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + public string _containedName; public string _containedXmlNamespace; public string _containedType; @@ -1038,9 +1045,9 @@ public string Name [XmlType(IncludeInSchema = false)] public class GxUserType : IGxXMLSerializable, ICloneable, IGxJSONAble, IGxJSONSerializable, IGXAssigned { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.GxUserType)); - protected GXProperties dirties = new GXProperties(); - + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + protected ConcurrentDictionary dirties = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private const string PROPERTY_PREFIX = "gxtpr_"; static object setupChannelObject = null; static bool setupChannelInitialized; [XmlIgnore] @@ -1098,11 +1105,11 @@ public GxUserType() public virtual void SetDirty(string fieldName) { - dirties[fieldName.ToLower()] = "true"; + dirties[fieldName] = 1; } public virtual bool IsDirty(string fieldName) { - if (dirties.ContainsKey(fieldName.ToLower())) + if (dirties.ContainsKey(fieldName)) return true; return false; } @@ -1264,7 +1271,7 @@ public bool FromJSonString(string s, GXBaseCollection Messa } catch (Exception ex) { - GXUtil.ErrorToMessages("FromJson Error", ex, Messages); + GXUtil.ErrorToMessages("FromJson Error", ex, Messages, false); return false; } } @@ -1557,13 +1564,25 @@ public void AddObjectProperty(string name, object prop, bool includeState) public void AddObjectProperty(string name, object prop, bool includeState, bool includeNonInitialized) { IGxJSONAble ijsonProp = prop as IGxJSONAble; + IGxExternalObject extObj = prop as IGxExternalObject; if (ijsonProp != null) { GxSilentTrnSdt silentTrn = prop as GxSilentTrnSdt; if (silentTrn != null) silentTrn.GetJSONObject(includeState, includeNonInitialized); - else + else if (extObj != null) + { + object extInstance = extObj.ExternalInstance; + IGxJSONAble gxJSONAble = extInstance as IGxJSONAble; + if (gxJSONAble != null) + { + JsonObj.Put(name, gxJSONAble.GetJSONObject(includeState)); + } + } + else + { JsonObj.Put(name, ijsonProp.GetJSONObject(includeState)); + } } else { @@ -1600,24 +1619,27 @@ public Object GetJSONObject() private ICollection getFromJSONObjectOrderIterator(ICollection it) { - List v = new List(); + if (GxUploadAttrs.IsEmpty && !typeof(GxSilentTrnSdt).IsAssignableFrom(this.GetType())) + { + return it; + } + List _JsonObjectOrderIterator = new List(); + List vAtEnd = new List(); foreach (string name in it) { - string map = JsonMap(name); - PropertyInfo objProperty = GetTypeProperty("gxtpr_" + (!string.IsNullOrEmpty(map) ? map : name).ToLower()); - if (name.EndsWith("_N") || objProperty != null && IsGxUploadAttribute(objProperty)) + if (name.EndsWith("_N") || IsGxUploadAttribute(name)) { vAtEnd.Add(name); } else { - v.Add(name);//keep the order of attributes that do not end with _N. + _JsonObjectOrderIterator.Add(name);//keep the order of attributes that do not end with _N. } } if (vAtEnd.Count > 0) - v.AddRange(vAtEnd); - return v; + _JsonObjectOrderIterator.AddRange(vAtEnd); + return _JsonObjectOrderIterator; } public void FromJSONObject(dynamic obj) @@ -1630,9 +1652,7 @@ public void FromJSONObject(dynamic obj) foreach (string name in jsonIterator) { object currObj = jobj[name]; - string map = JsonMap(name); - PropertyInfo objProperty = GetTypeProperty("gxtpr_" + (map != null ? map : name).ToLower()); - + PropertyInfo objProperty = GetTypeProperty(JsonNameToInternalName(name)); if (objProperty != null) { if (!JSONHelper.IsJsonNull(currObj)) @@ -1696,7 +1716,14 @@ public void FromJSONObject(dynamic obj) IGXBCCollection bcColl; GxSimpleCollection currSimpleColl; IGxJSONAble currJsonProp; + IGxExternalObject currExtProp; CollectionBase currObjColl = currObj as CollectionBase; + + if ((currExtProp = currProp as IGxExternalObject) != null) + { + currProp = currExtProp.ExternalInstance; + } + #if !NETCORE GxObjectCollectionBase currColl; if ((currColl = currProp as GxObjectCollectionBase) != null) @@ -1892,32 +1919,51 @@ private bool TryConvertValueToProperty(object Value, PropertyInfo property, out return success; } - [System.Diagnostics.CodeAnalysis.SuppressMessage("GxFxCopRules", "CR1000:EnforceThreadSafeType")] - private Dictionary gxuploadAttrs = new Dictionary(); - private bool IsGxUploadAttribute(PropertyInfo property) + private GXTypeInfo _compatibilityGxuploadAttrs = null; + private bool IsGxUploadAttribute(string jsonPropertyName) { - string key = property.Name; - if (!gxuploadAttrs.ContainsKey(key)) - { - bool hasAtt = property.IsDefined(typeof(GxUpload), false); - gxuploadAttrs.Add(key, hasAtt); - } - return gxuploadAttrs[key]; + return GxUploadAttrs.ContainsKey(JsonNameToInternalName(jsonPropertyName)); } - - private Hashtable props; - - private PropertyInfo GetTypeProperty(string propName) + private bool IsGxUploadAttribute(PropertyInfo propertyInfo) + { + return GxUploadAttrs.ContainsKey(propertyInfo.Name); + } + private string JsonNameToInternalName(string jsonPropertyName) { - if (props == null) + string map = JsonMap(jsonPropertyName); + if (!string.IsNullOrEmpty(map)) + return $"{PROPERTY_PREFIX}{map}"; + else + return $"{PROPERTY_PREFIX}{jsonPropertyName}"; + } + protected virtual GXTypeInfo TypeInfo { get { return _compatibilityGxuploadAttrs; } set { _compatibilityGxuploadAttrs = value; } } + private ConcurrentDictionary GxUploadAttrs + { + get { - props = new Hashtable(); - foreach (PropertyInfo prop in this.GetType().GetProperties()) + if (TypeInfo == null) { - props.Add(prop.Name.ToLower(), prop); + TypeInfo = new GXTypeInfo(); + + TypeInfo.UploadAttrs = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + foreach (PropertyInfo property in this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + if (property.Name.StartsWith(PROPERTY_PREFIX, StringComparison.OrdinalIgnoreCase)) + { + bool hasAtt = property.IsDefined(typeof(GxUpload), false); + if (hasAtt) + { + TypeInfo.UploadAttrs.TryAdd(property.Name, 1); + } + } + } } + return TypeInfo.UploadAttrs; } - return (PropertyInfo)props[propName]; + } + private PropertyInfo GetTypeProperty(string propName) + { + return this.GetType().GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); } private Hashtable methods; @@ -1999,7 +2045,10 @@ public void SetPropertyValue(string propertyName, object propertyValue) GetType().GetProperty($"gxTpr_{propertyName}").SetValue(this, propertyValue); } } - + public class GXTypeInfo + { + public ConcurrentDictionary UploadAttrs { get; set; } + } public interface IGxJSONAble { void AddObjectProperty(string name, object prop); @@ -2041,6 +2090,10 @@ public GxArrayList(int capacity) { innerArray = new List(capacity); } + internal GxArrayList(List list) + { + innerArray = list; + } public GxArrayList() { innerArray = new List(); @@ -2073,7 +2126,6 @@ public object Item(int index, int i) return innerArray[index][i]; } } - [CollectionDataContract(Name = "GxUnknownObjectCollection")] [KnownType(typeof(GxSimpleCollection))] [KnownType(typeof(GxStringCollection))] @@ -2428,7 +2480,6 @@ public interface IGxCollection : IGxCollection { T GetNumeric(int idx); } - public interface IGxExternalObject { object ExternalInstance { get; set; } @@ -2827,7 +2878,173 @@ public override string ToString() } } + public class GxGenericDictionary : Dictionary, IGxJSONSerializable, IGxJSONAble + { + private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(GxGenericDictionary)); + private readonly DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings() { UseSimpleDictionaryFormat = true }; + public void SetDictionary(GxGenericDictionary dictionary) + { + foreach (var entry in dictionary) + { + this[entry.Key] = entry.Value; + } + } + + public bool Get(TKey key, out TValue value) + { + if (TryGetValue(key, out value)) + { + return true; + } + else + { + return false; + } + } + public TValue Get(TKey key) + { + if (TryGetValue(key, out TValue value)) + { + return value; + + } + else + { + return default; + } + } + public List ValueList + { + get{ + return base.Values.ToList(); + } + } + public List KeyList + { + get + { + return base.Keys.ToList(); + } + } + public void Set(TKey key, TValue value) + { + base[key] = value; + } + + public bool RemoveKey(TKey key) + { + return Remove(key); + } + + public void RemoveKeys(List keys) + { + foreach (var key in keys.ToList()) + { + RemoveKey(key); + } + } + + public void RemoveAll(GxGenericDictionary dictionary) + { + foreach (var key in dictionary.Keys.ToList()) + { + RemoveKey(key); + } + } + public string ToJson() + { + try + { + return JSONHelper.Serialize(this, settings); + } + catch (Exception e) + { + log.Error("Could not obtain JSON from Dictionary", e); + return ""; + } + } + + public void FromJson(string json) + { + try + { + Clear(); + Dictionary deserializedDictionary = JSONHelper.Deserialize>(json, Encoding.Unicode, null, null, settings); + foreach (var entry in deserializedDictionary) + { + this[entry.Key] = entry.Value; + } + } + catch (Exception e) + { + log.Error("Could not set Dictionary from JSON", e); + } + } + + public string ToJSonString() + { + return ToJson(); + } + + public bool FromJSonString(string s) + { + throw new NotImplementedException(); + } + + public bool FromJSonString(string s, GXBaseCollection Messages) + { + throw new NotImplementedException(); + } + + public bool FromJSonFile(GxFile file) + { + throw new NotImplementedException(); + } + + public bool FromJSonFile(GxFile file, GXBaseCollection Messages) + { + throw new NotImplementedException(); + } + + public void AddObjectProperty(string name, object prop) + { + throw new NotImplementedException(); + } + + public object GetJSONObject() + { + JObject jObj = new JObject(); + foreach (TKey item in Keys) + { + jObj.Accumulate(item.ToString(), this[item]); + } + return jObj; + } + + public object GetJSONObject(bool includeState) + { + return GetJSONObject(); + } + + public void FromJSONObject(dynamic obj) + { + this.Clear(); + JObject jObj = obj as JObject; + if (jObj != null) + { + foreach (DictionaryEntry item in jObj) + { + base[(TKey)item.Key]= (TValue)item.Value; + } + } + } + + public string ToJavascriptSource() + { + throw new NotImplementedException(); + } + } public class GxDictionary : Dictionary { public bool HasKey(string key) diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxFtp.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxFtp.cs index bf774c728..9fc057b38 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxFtp.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxFtp.cs @@ -8,12 +8,14 @@ using System.Linq; using GeneXus.Encryption; using GeneXus.Configuration; +using log4net; namespace GeneXus.Utils { [SecuritySafeCritical] public class FtpService { + private static readonly ILog log = log4net.LogManager.GetLogger(typeof(FtpService)); private Socket _DataSocket; private Socket _ControlSocket; @@ -511,8 +513,7 @@ private void OpenDataConnection() } _DataSocket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp ); - IPHostEntry localHostEntry = Dns.GetHostEntry(Dns.GetHostName()); - IPAddress hostAddress = localHostEntry.AddressList.First(a => (a.AddressFamily == _DataSocket.AddressFamily)); ; + IPAddress hostAddress = GetIpAddress(Dns.GetHostName()); IPEndPoint epListener = new IPEndPoint(hostAddress, 0); _DataSocket.Bind(epListener); @@ -565,8 +566,7 @@ private void OpenPassiveDataConnection() { ProcessProtocolViolationError("Error in creating Data Socket"); } - IPHostEntry serverHostEntry = Dns.GetHostEntry(_RequestUri.Host); - IPAddress hostAddress = serverHostEntry.AddressList.First(a => (a.AddressFamily == _DataSocket.AddressFamily)); ; ; + IPAddress hostAddress = GetIpAddress(_RequestUri.Host); IPEndPoint serverEndPoint = new IPEndPoint(hostAddress, Port); try @@ -631,10 +631,8 @@ private void OpenControlConnection(Uri uriToConnect) clientEndPoint = _ControlSocket.LocalEndPoint; try { - IPHostEntry serverHostEntry = Dns.GetHostEntry(Host); - IPAddress ipAddresses = serverHostEntry.AddressList.First(a => (a.AddressFamily == _ControlSocket.AddressFamily)); + IPAddress ipAddresses = GetIpAddress(Host); IPEndPoint serverEndPoint = new IPEndPoint(ipAddresses, Port); - try { if (GXUtil.IsWindowsPlatform) @@ -679,6 +677,21 @@ private void OpenControlConnection(Uri uriToConnect) } } + private IPAddress GetIpAddress(string host) + { + IPHostEntry serverHostEntry = Dns.GetHostEntry(host); + IPAddress ipAddresses = serverHostEntry.AddressList.FirstOrDefault(); + GXLogging.Debug(log, $"GetHostEntry({host}) AddressList length: ", serverHostEntry.AddressList.Length.ToString()); + if (ipAddresses == null) + { + serverHostEntry = Dns.GetHostByName(host); + ipAddresses = serverHostEntry.AddressList.FirstOrDefault(); + GXLogging.Debug(log, $"GetHostByName({host}) AddressList length: ", serverHostEntry.AddressList.Length.ToString()); + } + GXLogging.Debug(log, "HostAddress ", ipAddresses.ToString()); + return ipAddresses; + } + void CloseDataConnection() { if(_DataSocket != null) @@ -976,19 +989,19 @@ internal void ProcessApplicationError( string s) { this._status = 1; this._statusDescription = s; - + GXLogging.Error(log, "ProcessApplicationError " + s); } internal void ProcessApplicationError( string s, Exception e) { this._status = 1; this._statusDescription = s + e.Message; - + GXLogging.Error(log, "ProcessApplicationError " + s, e); } internal void ProcessProtocolViolationError( string s) { this._status = 1; this._statusDescription = new ProtocolViolationException(s).ToString(); - + GXLogging.Warn(log, "ProcessProtocolViolationError ", this._statusDescription); } internal string ComposeExceptionMessage(ResponseDescription resp_desc, string log) { diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs index 9acc4c5b0..fd7f8234e 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs @@ -1,13 +1,14 @@ -using GeneXus.Application; -using GeneXus.XML; +#if !NETCORE using Jayrock.Json; -using log4net; +#endif using System; using System.Collections; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using System.Xml.Serialization; +using GeneXus.Application; +using GeneXus.XML; + namespace GeneXus.Utils { @@ -44,13 +45,61 @@ public GXBaseList() base.Insert(idx, TObject); IsAssigned = true; } + public bool AddRange(GXBaseList value, int? index) + { + if (!index.HasValue) + { + base.AddRange(value); + return true; + } + else if (index == 0) + { + base.InsertRange(index.Value, value); + return true; + } + else if (index > 0 && index <= Count+1) + { + base.InsertRange(index.Value - 1, value); + return true; + } + return false; + } + public bool RemoveRange(int index, int? countItemsToRemove) + { + if (index > 0 && index <= Count) + { + if (countItemsToRemove == null) + { + int countToRemove = Count - (index-1); + base.RemoveRange(index - 1, countToRemove); + return true; + } + else if (countItemsToRemove.Value < Count - index) + { + base.RemoveRange(index - 1, countItemsToRemove.Value); + return true; + } + } + return false; + } + + public bool Set(int index, T value) + { + if (index > 0 && index < Count) + { + this[index - 1] = value; + return true; + } + return false; + } + } [Serializable] public class GXBaseCollection : GXBaseList, IGxXMLSerializable, IGxJSONAble, IGxCollection, IGxJSONSerializable where T : GxUserType, IGxXMLSerializable, IGxJSONAble, new() { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.GXBaseCollection)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger>(); public string _containedName; public string _containedXmlNamespace; public IGxContext context; @@ -470,7 +519,7 @@ public bool FromJSonString(string s, GXBaseCollection Messa } catch (Exception ex) { - GXUtil.ErrorToMessages("FromJson Error", ex, Messages); + GXUtil.ErrorToMessages("FromJson Error", ex, Messages, false); return false; } } @@ -619,7 +668,7 @@ public override void RemoveItem(int idx) [Serializable] public class GXBCCollection : GXBaseCollection, IGXBCCollection where T : GxSilentTrnSdt, new() { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.GXBCCollection)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger>(); public GXBCCollection() : base() { } public GXBCCollection(IGxContext context, diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs index 67d28923e..35b3136dc 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs @@ -2,6 +2,7 @@ namespace GeneXus.Http.Client { using System; using System.Collections; + using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; @@ -9,6 +10,7 @@ namespace GeneXus.Http.Client using System.Net; using System.Net.Http; using System.Net.Http.Headers; + using System.Net.Security; using System.Security; using System.Security.Cryptography.X509Certificates; using System.Text; @@ -19,7 +21,6 @@ namespace GeneXus.Http.Client using GeneXus.Application; using GeneXus.Configuration; using GeneXus.Utils; - using log4net; #if NETCORE using Microsoft.AspNetCore.WebUtilities; #endif @@ -41,6 +42,7 @@ public class MultiPartTemplate { public string Boundary; public string FormdataTemplate; + public string FormdataSeparator; public byte[] Boundarybytes; public byte[] EndBoundaryBytes; public string HeaderTemplate; @@ -50,7 +52,8 @@ internal MultiPartTemplate() { Boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x"); ContentType = $"multipart/form-data; boundary={Boundary}"; - FormdataTemplate = "\r\n--" + Boundary + "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}"; + FormdataSeparator = "\r\n"; + FormdataTemplate = "--" + Boundary + "\r\nContent-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; Boundarybytes = Encoding.ASCII.GetBytes($"\r\n--{Boundary}\r\n"); EndBoundaryBytes = Encoding.ASCII.GetBytes($"\r\n--{Boundary}--"); HeaderTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" + "Content-Type: {2}\r\n\r\n"; @@ -58,7 +61,8 @@ internal MultiPartTemplate() } public class GxHttpClient : IGxHttpClient, IDisposable { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Http.Client.GxHttpClient)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private const int DEFAULT_TIMEOUT = 30000; public const int _Basic = 0; public const int _Digest = 1; public const int _NTLM = 2; @@ -66,8 +70,7 @@ public class GxHttpClient : IGxHttpClient, IDisposable const int StreamWriterDefaultBufferSize = 1024; Stream _sendStream; byte[] _receiveData; - StreamReader _receiveStream; - int _timeout = 30000; + int _timeout = DEFAULT_TIMEOUT; short _statusCode = 0; string _proxyHost; int _proxyPort; @@ -76,6 +79,11 @@ public class GxHttpClient : IGxHttpClient, IDisposable NameValueCollection _headers; NameValueCollection _formVars; MultiPartTemplate _multipartTemplate; + bool _isChunkedResponse; + HttpResponseMessage _response; + bool _eof; + bool _encodingFound; + string _charset; string _scheme = "http://"; @@ -88,18 +96,19 @@ public class GxHttpClient : IGxHttpClient, IDisposable IGxContext _context; #if NETCORE - IWebProxy _proxyObject; + static IWebProxy _proxyObject; + static object syncRootHttpInstance = new Object(); #else - WebProxy _proxyObject; + static WebProxy _proxyObject; #endif ArrayList _authCollection; ArrayList _authProxyCollection; X509Certificate2Collection _certificateCollection; + List _fileCertificateCollection; Encoding _encoding; Encoding _contentEncoding; - - + static object syncRoot = new Object(); public MultiPartTemplate MultiPart { get @@ -134,29 +143,178 @@ internal byte[] ReceiveData { get { - if (_chunkedResponse && _receiveData == null && _receiveStream!=null) + ReadResponseData(); + return _receiveData; + } + } + + +#if NETCORE + private async Task ReceiveDataAsync() + { + await ReadResponseDataAsync(); + return _receiveData; + } + + private const int POOLED_CONNECTION_LIFETIME_MINUTES = 2; + internal static ConcurrentDictionary _httpClientInstances = new ConcurrentDictionary(); + private static HttpClient GetHttpClientInstance(Uri URI, int timeout, ArrayList authCollection, ArrayList authProxyCollection, X509Certificate2Collection certificateCollection, List fileCertificateCollection, string proxyHost, int proxyPort, out bool disposableInstance) + { + if (CacheableInstance(authCollection, authProxyCollection)) + { + HttpClient value; + disposableInstance = false; + string key = HttpClientInstanceIdentifier(proxyHost, proxyPort, fileCertificateCollection, timeout); + if (_httpClientInstances.TryGetValue(key, out value)) + { + GXLogging.Debug(log, $"Getting httpClient cached instance"); + return value; + } + else { - using (MemoryStream memstream = new MemoryStream()) + lock (syncRootHttpInstance) { - _receiveStream.BaseStream.CopyTo(memstream); - _receiveData = memstream.ToArray(); + if (_httpClientInstances.TryGetValue(key, out value)) + { + GXLogging.Debug(log, $"Getting httpClient cached instance"); + return value; + } + value = new HttpClient(GetHandler(URI, authCollection, authProxyCollection, certificateCollection, proxyHost, proxyPort)); + value.Timeout = TimeSpan.FromMilliseconds(timeout); + _httpClientInstances.TryAdd(key, value); + return value; } } - return _receiveData; + } + else + { + disposableInstance = true; + return new HttpClient(GetHandler(URI, authCollection, authProxyCollection, certificateCollection, proxyHost, proxyPort)); } } -#if NETCORE - [SecuritySafeCritical] - private HttpClientHandler GetHandler() + private static string HttpClientInstanceIdentifier(string proxyHost, int proxyPort, List fileCertificateCollection, int timeout) + { + bool defaultSslOptions = ServicePointManager.ServerCertificateValidationCallback == null; + if (string.IsNullOrEmpty(proxyHost) && fileCertificateCollection.Count==0 && timeout== DEFAULT_TIMEOUT && defaultSslOptions) + { + return string.Empty; + } + else if (fileCertificateCollection.Count==0) + { + return $"{proxyHost}:{proxyPort}::{timeout}:{defaultSslOptions}"; + } + else + { + return $"{proxyHost}:{proxyPort}:{string.Join(';', fileCertificateCollection)}:{timeout}:{defaultSslOptions}"; + } + } + + private static bool CacheableInstance(ArrayList authCollection, ArrayList authProxyCollection) + { + return authCollection.Count == 0 && authProxyCollection.Count == 0 && Preferences.SingletonHttpClient(); + } + private static SocketsHttpHandler GetHandler(Uri URI, ArrayList authCollection, ArrayList authProxyCollection, X509Certificate2Collection certificateCollection, string proxyHost, int proxyPort) + { + SocketsHttpHandler handler = new SocketsHttpHandler() + { + PooledConnectionLifetime = TimeSpan.FromMinutes(POOLED_CONNECTION_LIFETIME_MINUTES), + }; + int maxConnections = Preferences.GetHttpClientMaxConnectionPerRoute(); + if (maxConnections != Preferences.DEFAULT_HTTPCLIENT_MAX_PER_ROUTE) + { + handler.MaxConnectionsPerServer = maxConnections; + } + GXLogging.Debug(log, $"Creating SocketsHttpHandler MaxConnectionsPerServer:{handler.MaxConnectionsPerServer}"); + ICredentials cc = getCredentialCache(URI, authCollection); + if (cc != null) + { + handler.Credentials = getCredentialCache(URI, authCollection); + } + + SetSslOptions(handler); + + if (GXUtil.CompressResponse()) + { + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + } + if (certificateCollection.Count > 0) + { + if (handler.SslOptions.ClientCertificates == null) + { + handler.SslOptions.ClientCertificates = new X509CertificateCollection(certificateCollection); + } + else + { + foreach (X509Certificate2 cert in certificateCollection) + { + handler.SslOptions.ClientCertificates.Add(cert); + } + } + } + + WebProxy proxy = getProxy(proxyHost, proxyPort, authProxyCollection); + if (proxy != null) + { + handler.Proxy = proxy; + } + handler.UseCookies = false; + return handler; + + } + + private static void SetSslOptions(SocketsHttpHandler handler) { - return new HttpClientHandler(); + if (ServicePointManager.ServerCertificateValidationCallback != null) + { + handler.SslOptions = new SslClientAuthenticationOptions + { + RemoteCertificateValidationCallback = ServicePointManager.ServerCertificateValidationCallback + }; + } } #else [SecuritySafeCritical] - private WinHttpHandler GetHandler() + + private static HttpClient GetHttpClientInstance(Uri URI, int timeout, ArrayList authCollection, ArrayList authProxyCollection, X509Certificate2Collection certificateCollection, string proxyHost, int proxyPort, CookieContainer cookies) + { + TimeSpan milliseconds = TimeSpan.FromMilliseconds(timeout); + HttpClient value = new HttpClient(GetHandler(URI, milliseconds, authCollection, authProxyCollection, certificateCollection, proxyHost, proxyPort, cookies)); + value.Timeout = milliseconds; + return value; + } + [SecuritySafeCritical] + private static WinHttpHandler GetHandler(Uri URI, TimeSpan milliseconds, ArrayList authCollection, ArrayList authProxyCollection, X509Certificate2Collection certificateCollection, string proxyHost, int proxyPort, CookieContainer cookies) { - return new WinHttpHandler(); + WinHttpHandler handler = new WinHttpHandler(); + ICredentials cc = getCredentialCache(URI, authCollection); + if (cc != null) + { + handler.ServerCredentials = cc; + } + if (ServicePointManager.ServerCertificateValidationCallback != null) + { + handler.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => ServicePointManager.ServerCertificateValidationCallback(sender, certificate, chain, sslPolicyErrors)); + } + handler.CookieUsePolicy = CookieUsePolicy.UseSpecifiedCookieContainer; + handler.CookieContainer = cookies; + if (GXUtil.CompressResponse()) + { + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + } + foreach (X509Certificate2 cert in certificateCollection) + handler.ClientCertificates.Add(cert); + WebProxy proxy = getProxy(proxyHost, proxyPort, authProxyCollection); + if (proxy != null) + { + handler.Proxy = proxy; + handler.WindowsProxyUsePolicy = WindowsProxyUsePolicy.UseCustomProxy; + } + + handler.ReceiveDataTimeout = milliseconds; + handler.ReceiveHeadersTimeout = milliseconds; + + return handler; } #endif public GxHttpClient(IGxContext context) : this() @@ -174,21 +332,18 @@ public GxHttpClient() : base() _authCollection = new ArrayList(); _authProxyCollection = new ArrayList(); _certificateCollection = new X509Certificate2Collection(); + _fileCertificateCollection = new List(); IncludeCookies = true; _proxyHost = string.Empty; try { -#if NETCORE - _proxyObject = WebRequest.GetSystemWebProxy(); - -#else - _proxyObject = WebProxy.GetDefaultProxy(); - if (_proxyObject != null && _proxyObject.Address != null) +#if !NETCORE + if (ProxyObject != null && ProxyObject.Address != null) { - _proxyHost = _proxyObject.Address.Host; - _proxyPort = _proxyObject.Address.Port; + _proxyHost = ProxyObject.Address.Host; + _proxyPort = ProxyObject.Address.Port; } #endif } @@ -198,7 +353,36 @@ public GxHttpClient() : base() } } +#if NETCORE + private IWebProxy ProxyObject +#else + + private static WebProxy ProxyObject +#endif + { + get { + if (_proxyObject == null) + { + lock (syncRoot) + { + try + { +#if NETCORE + _proxyObject = WebRequest.GetSystemWebProxy(); +#else + _proxyObject = WebProxy.GetDefaultProxy(); +#endif + } + catch (Exception e) + { + GXLogging.Warn(log, "Error getting ProxyObject", e); + } + } + } + return _proxyObject; + } + } public short Digest { get { return _Digest; } @@ -423,7 +607,6 @@ public void AddHeader(string name, string value) GXLogging.Error(log, String.Format("Error parsing charset ", value, ex)); } } - _headers.Set(name, value); } public void ClearVariables() @@ -441,7 +624,7 @@ void SendVariables(Stream reqStream) for (int i = 0; i < _formVars.Count; i++) { if (_formVars.Keys[i] != null) - vars.Add(buildVariableToSend(_formVars.Keys[i], _formVars[i])); + vars.Add(buildVariableToSend(_formVars.Keys[i], _formVars[i], vars.Count)); } if (vars.Count > 0) { @@ -467,11 +650,15 @@ string variableSeparator() else return "&"; } - string buildVariableToSend(string key, string value) + string buildVariableToSend(string key, string value, int idx) { + bool needsCLRF = idx > 0; if (IsMultipart) { - return string.Format(MultiPart.FormdataTemplate, key, value); + if (needsCLRF) + return MultiPart.FormdataSeparator + string.Format(MultiPart.FormdataTemplate, key, value); + else + return string.Format(MultiPart.FormdataTemplate, key, value); } else { @@ -531,12 +718,19 @@ private void EndMultipartBoundary(Stream reqStream) if (IsMultipart) reqStream.Write(MultiPart.EndBoundaryBytes, 0, MultiPart.EndBoundaryBytes.Length); } - - void setHeaders(HttpRequestMessage request, CookieContainer cookies) + void setContentHeaders(HttpRequestMessage request, string contentType) + { + if (contentType != null) + { + HttpContentHeaders contentHeaders = request.Content.Headers; + contentHeaders.ContentType = MediaTypeHeaderValue.Parse(contentType); + } + InferContentType(contentType, request); + } + void setHeaders(HttpRequestMessage request, CookieContainer cookies, out string contentType) { - HttpContentHeaders contentHeaders = request.Content.Headers; HttpRequestHeaders headers = request.Headers; - string contentType = null; + contentType = null; for (int i = 0; i < _headers.Count; i++) { string currHeader = _headers.Keys[i]; @@ -549,7 +743,6 @@ void setHeaders(HttpRequestMessage request, CookieContainer cookies) break; case "CONTENT-TYPE": contentType = _headers[i].ToString(); - contentHeaders.ContentType = MediaTypeHeaderValue.Parse(_headers[i].ToString()); break; case "ACCEPT": AddHeader(headers, "Accept", _headers[i]); @@ -583,7 +776,12 @@ void setHeaders(HttpRequestMessage request, CookieContainer cookies) { if (cookie.Contains("=")) { - cookies.Add(new Uri(request.RequestUri.Host), new Cookie(cookie.Split('=')[0], cookie.Split('=')[1]) { Domain = request.RequestUri.Host }); + UriBuilder uriBuilder = new UriBuilder(request.RequestUri.Scheme, request.RequestUri.Host); + Cookie pCookie = ParseCookie(cookie, request.RequestUri.Host); + if (pCookie != null) + { + cookies.Add(uriBuilder.Uri, pCookie); + } } } break; @@ -605,7 +803,16 @@ void setHeaders(HttpRequestMessage request, CookieContainer cookies) else headers.ConnectionClose = false; } - InferContentType(contentType, request); + } + Cookie ParseCookie(string cookie, string domain) + { + string[] values = cookie.TrimEnd(';').Split('='); + if (values.Length >= 2) { + string cookieName = values[0].Trim(); + string cookieValue = values[1]; + return new Cookie(cookieName, cookieValue) { Domain = domain }; + } + return null; } void AddHeader(HttpRequestHeaders headers, string headerName, string headerValue) { @@ -619,77 +826,55 @@ void InferContentType(string contentType, HttpRequestMessage req) } } - void setHttpVersion(HttpRequestMessage req) + void SetHttpVersion(HttpRequestMessage req) { string httpVersion; if (Config.GetValueOf("HttpClientHttpVersion", out httpVersion)) { if (httpVersion == "1.0") req.Version = HttpVersion.Version10; +#if NETCORE + else if (httpVersion == "2.0") + req.Version = HttpVersion.Version20; + else if (httpVersion == "3.0") + req.Version = HttpVersion.Version30; +#endif else req.Version = HttpVersion.Version11; + + GXLogging.Debug(log, "Setting HTTPClient request version to ", req.Version.ToString()); } else req.Version = HttpVersion.Version11; } [SecuritySafeCritical] - HttpResponseMessage ExecuteRequest(string method, string requestUrl, CookieContainer cookies) + HttpResponseMessage ExecuteRequest(string method, string requestUrl, bool contextCookies) { - GXLogging.Debug(log, String.Format("Start HTTPClient buildRequest: requestUrl:{0} method:{1}", requestUrl, method)); + CookieContainer cookies = contextCookies ? _context.GetCookieContainer(requestUrl, IncludeCookies) : new CookieContainer(); + + GXLogging.Debug(log, string.Format("Start HTTPClient buildRequest: requestUrl:{0} method:{1}", requestUrl, method)); HttpRequestMessage request; - HttpClient client; + HttpClient client = null; int BytesRead; - Byte[] Buffer = new Byte[1024]; + byte[] Buffer = new byte[1024]; + HttpResponseMessage response; - request = new HttpRequestMessage(); - request.RequestUri = new Uri(requestUrl); -#if NETCORE - HttpClientHandler handler = GetHandler(); - handler.Credentials = getCredentialCache(request.RequestUri, _authCollection); - if (ServicePointManager.ServerCertificateValidationCallback != null) - { - handler.ServerCertificateCustomValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => ServicePointManager.ServerCertificateValidationCallback(sender, certificate, chain, sslPolicyErrors)); - } -#else - WinHttpHandler handler = GetHandler(); - handler.ServerCredentials = getCredentialCache(request.RequestUri, _authCollection); - if (ServicePointManager.ServerCertificateValidationCallback != null) - { - handler.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => ServicePointManager.ServerCertificateValidationCallback(sender, certificate, chain, sslPolicyErrors)); - } - handler.CookieUsePolicy = CookieUsePolicy.UseSpecifiedCookieContainer; -#endif - if (GXUtil.CompressResponse()) + request = new HttpRequestMessage() { - handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - } - - handler.CookieContainer = cookies; - - foreach (X509Certificate2 cert in _certificateCollection) - handler.ClientCertificates.Add(cert); - - request.Method = new HttpMethod(method); - setHttpVersion(request); - WebProxy proxy = getProxy(_proxyHost, _proxyPort, _authProxyCollection); - if (proxy != null) + RequestUri = new Uri(requestUrl), + Method = new HttpMethod(method), + }; + setHeaders(request, cookies, out string contentType); + SetHttpVersion(request); + bool disposableInstance = true; + try { - handler.Proxy = proxy; -#if !NETCORE - handler.WindowsProxyUsePolicy = WindowsProxyUsePolicy.UseCustomProxy; -#endif - } - HttpResponseMessage response; - TimeSpan milliseconds = TimeSpan.FromMilliseconds(_timeout); -#if !NETCORE - handler.ReceiveDataTimeout = milliseconds; - handler.ReceiveHeadersTimeout = milliseconds; +#if NETCORE + request.PopulateCookies(cookies); + client = GetHttpClientInstance(request.RequestUri, _timeout, _authCollection, _authProxyCollection, _certificateCollection, _fileCertificateCollection, _proxyHost, _proxyPort, out disposableInstance); +#else + client = GetHttpClientInstance(request.RequestUri, _timeout, _authCollection, _authProxyCollection, _certificateCollection, _proxyHost, _proxyPort, cookies); #endif - using (client = new HttpClient(handler)) - { - client.Timeout = milliseconds; - client.BaseAddress = request.RequestUri; - using (MemoryStream reqStream = new MemoryStream()) { SendVariables(reqStream); @@ -706,72 +891,148 @@ HttpResponseMessage ExecuteRequest(string method, string requestUrl, CookieConta GXLogging.Debug(log, "End SendStream.Read: stream " + reqStream.ToString()); reqStream.Seek(0, SeekOrigin.Begin); request.Content = new ByteArrayContent(reqStream.ToArray()); - setHeaders(request, handler.CookieContainer); + setContentHeaders(request, contentType); +#if NETCORE + response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).GetAwaiter().GetResult(); + response.ExtractCookies(cookies); +#else response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).GetAwaiter().GetResult(); +#endif + } + } + finally + { + if (disposableInstance && client != null) + { + client.Dispose(); } } return response; } - void ReadReponseContent(HttpResponseMessage response) +#if NETCORE + async Task ExecuteRequestAsync(string method, string requestUrl, bool contextCookies) { - _receiveData = Array.Empty(); + CookieContainer cookies = contextCookies ? _context.GetCookieContainer(requestUrl, IncludeCookies) : new CookieContainer(); + + GXLogging.Debug(log, string.Format("Start HTTPClient buildRequest: requestUrl:{0} method:{1}", requestUrl, method)); + HttpRequestMessage request; + HttpClient client = null; + int BytesRead; + byte[] Buffer = new byte[1024]; + HttpResponseMessage response; + + request = new HttpRequestMessage() + { + RequestUri = new Uri(requestUrl), + Method = new HttpMethod(method), + }; + setHeaders(request, cookies, out string contentType); + SetHttpVersion(request); + bool disposableInstance = true; try { - Stream stream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult(); - _chunkedResponse = response.Headers.TransferEncodingChunked.HasValue && response.Headers.TransferEncodingChunked.Value; - string charset; - if (response.Content.Headers.ContentType == null) - charset = null; - else - charset = response.Content.Headers.ContentType.CharSet; - bool encodingFound = false; - if (!string.IsNullOrEmpty(charset)) + request.PopulateCookies(cookies); + client = GetHttpClientInstance(request.RequestUri, _timeout, _authCollection, _authProxyCollection, _certificateCollection, _fileCertificateCollection, _proxyHost, _proxyPort, out disposableInstance); + using (MemoryStream reqStream = new MemoryStream()) { - int idx = charset.IndexOf("charset="); - if (idx > 0) - { - idx += 8; - charset = charset.Substring(idx, charset.Length - idx); - _encoding = GetEncoding(charset); - if (_encoding != null) - encodingFound = true; - } - else + SendVariables(reqStream); + SendStream.Seek(0, SeekOrigin.Begin); + BytesRead = await SendStream.ReadAsync(Buffer, 0, 1024); + GXLogging.Debug(log, "Start SendStream.Read: BytesRead " + BytesRead); + while (BytesRead > 0) { - charset = String.Empty; + GXLogging.Debug(log, "reqStream.Write: Buffer.length " + Buffer.Length + ",'" + Encoding.UTF8.GetString(Buffer, 0, Buffer.Length) + "'"); + await reqStream.WriteAsync(Buffer, 0, BytesRead); + BytesRead = await SendStream.ReadAsync(Buffer, 0, 1024); } + EndMultipartBoundary(reqStream); + GXLogging.Debug(log, "End SendStream.Read: stream " + reqStream.ToString()); + reqStream.Seek(0, SeekOrigin.Begin); + request.Content = new ByteArrayContent(reqStream.ToArray()); + setContentHeaders(request, contentType); + response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + response.ExtractCookies(cookies); } - if (_chunkedResponse) + } + finally + { + if (disposableInstance && client != null) { - if (_encoding == null) - _encoding = Encoding.UTF8; - - _receiveStream = new StreamReader(stream, _encoding); - _receiveData = null; + client.Dispose(); } - else + } + return response; + } +#endif + + void ReadResponseData() + { + if (_receiveData == null && _response!=null) + { + _receiveData = Array.Empty(); + try { +#if NETCORE + Stream stream = _response.Content.ReadAsStream(); +#else + Stream stream = _response.Content.ReadAsStreamAsync().GetAwaiter().GetResult(); +#endif + using (MemoryStream ms = new MemoryStream()) { stream.CopyTo(ms); _receiveData = ms.ToArray(); } + _eof = true; int bytesRead = _receiveData.Length; GXLogging.Debug(log, "BytesRead " + _receiveData.Length); - if (bytesRead > 0 && !encodingFound) + if (bytesRead > 0 && !_encodingFound) { - _encoding = DetectEncoding(charset, out encodingFound, _receiveData, bytesRead); + _encoding = DetectEncoding(_charset, out _encodingFound, _receiveData, bytesRead); } } + catch (IOException ioEx) + { + if (_errCode == 1) + GXLogging.Warn(log, "Could not read response", ioEx); + else + throw ioEx; + } } - catch (IOException ioEx) + } +#if NETCORE + async Task ReadResponseDataAsync() + { + if (_receiveData == null && _response != null) { - if (_errCode == 1) - GXLogging.Warn(log, "Could not read response", ioEx); - else - throw ioEx; + _receiveData = Array.Empty(); + try + { + Stream stream = await _response.Content.ReadAsStreamAsync(); + + using (MemoryStream ms = new MemoryStream()) + { + await stream.CopyToAsync(ms); + _receiveData = ms.ToArray(); + } + _eof = true; + int bytesRead = _receiveData.Length; + GXLogging.Debug(log, "BytesRead " + _receiveData.Length); + if (bytesRead > 0 && !_encodingFound) + { + _encoding = DetectEncoding(_charset, out _encodingFound, _receiveData, bytesRead); + } + } + catch (IOException ioEx) + { + if (_errCode == 1) + GXLogging.Warn(log, "Could not read response", ioEx); + else + throw ioEx; + } } } + #endif bool UseOldHttpClient(string name) { if (Config.GetValueOf("useoldhttpclient", out string useOld) && useOld.StartsWith("y", StringComparison.OrdinalIgnoreCase)) @@ -805,28 +1066,39 @@ public void Execute(string method, string name) HttpClientExecute(method, name); } } - - public void HttpClientExecute(string method, string name) +#if NETCORE + public async Task ExecuteAsync(string method, string name) { - response = null; - Byte[] Buffer = new Byte[1024]; - _errCode = 0; - _errDescription = string.Empty; - GXLogging.Debug(log, "Start Execute: method '" + method + "', name '" + name + "'"); - try + if (UseOldHttpClient(name)) { - string requestUrl = GetRequestURL(name); - bool contextCookies = _context != null && !String.IsNullOrEmpty(requestUrl); - CookieContainer cookies = contextCookies ? _context.GetCookieContainer(requestUrl, IncludeCookies) : new CookieContainer(); - response = ExecuteRequest(method, requestUrl, cookies); - - if (contextCookies) - _context.UpdateSessionCookieContainer(); - - + GXLogging.Debug(log, "Using legacy GxHttpClient"); + await WebExecuteAsync(method, name); } + else + { + await HttpClientExecuteAsync(method, name); + } + } +#endif + internal void ProcessResponse(HttpResponseMessage httpResponse) + { + _response = httpResponse; + LoadResponseHeaders(_response); + _statusCode = ((short)_response.StatusCode); + _statusDescription = GetStatusCodeDescrption(_response); + if ((_statusCode >= 400 && _statusCode < 600) && _errCode != 1) + { + _errCode = 1; + _errDescription = "The remote server returned an error: (" + _statusCode + ") " + _statusDescription + "."; + } + } + private void ProcessHttpClientException(Exception ex) + { + HttpRequestException httpex; + TaskCanceledException tcex; #if NETCORE - catch (AggregateException aex) + AggregateException aex; + if ((aex = ex as AggregateException) != null) { GXLogging.Warn(log, "Error Execute", aex); _errCode = 1; @@ -834,77 +1106,146 @@ public void HttpClientExecute(string method, string name) _errDescription = aex.InnerException.Message; else _errDescription = aex.Message; - response = new HttpResponseMessage(); - response.Content = new StringContent(HttpHelper.StatusCodeToTitle(HttpStatusCode.InternalServerError)); - response.StatusCode = HttpStatusCode.InternalServerError; + _response = new HttpResponseMessage(); + _response.Content = new StringContent(HttpHelper.StatusCodeToTitle(HttpStatusCode.InternalServerError)); + _response.StatusCode = HttpStatusCode.InternalServerError; } + else #endif - catch (HttpRequestException e) + if ((httpex = ex as HttpRequestException) != null) { - GXLogging.Warn(log, "Error Execute", e); + GXLogging.Warn(log, "Error Execute", httpex); _errCode = 1; - if (e.InnerException != null) - _errDescription = e.Message + " " + e.InnerException.Message; + if (httpex.InnerException != null) + _errDescription = httpex.Message + " " + httpex.InnerException.Message; else - _errDescription = e.Message; - response = new HttpResponseMessage(); - response.Content = new StringContent(HttpHelper.StatusCodeToTitle(HttpStatusCode.InternalServerError)); + _errDescription = httpex.Message; + _response = new HttpResponseMessage(); + _response.Content = new StringContent(HttpHelper.StatusCodeToTitle(HttpStatusCode.InternalServerError)); #if NETCORE - response.StatusCode = (HttpStatusCode)(e.StatusCode != null ? e.StatusCode : HttpStatusCode.InternalServerError); + _response.StatusCode = (HttpStatusCode)(httpex.StatusCode != null ? httpex.StatusCode : HttpStatusCode.InternalServerError); #else - response.StatusCode = HttpStatusCode.InternalServerError; + _response.StatusCode = HttpStatusCode.InternalServerError; #endif + } - catch (TaskCanceledException e) + else if ((tcex = ex as TaskCanceledException) != null) { - GXLogging.Warn(log, "Error Execute", e); + GXLogging.Warn(log, "Error Execute", tcex); _errCode = 1; - _errDescription = "The request has timed out. " + e.Message; - response = new HttpResponseMessage(); - response.StatusCode = 0; - response.Content = new StringContent(String.Empty); + _errDescription = "The request has timed out. " + tcex.Message; + _response = new HttpResponseMessage(); + _response.StatusCode = 0; + _response.Content = new StringContent(String.Empty); } - catch (Exception e) + else { - GXLogging.Warn(log, "Error Execute", e); + GXLogging.Warn(log, "Error Execute", ex); _errCode = 1; - if (e.InnerException != null) - _errDescription = e.Message + " " + e.InnerException.Message; + if (ex.InnerException != null) + _errDescription = ex.Message + " " + ex.InnerException.Message; else - _errDescription = e.Message; - response = new HttpResponseMessage(); - response.Content = new StringContent(HttpHelper.StatusCodeToTitle(HttpStatusCode.InternalServerError)); - response.StatusCode = HttpStatusCode.InternalServerError; + _errDescription = ex.Message; + _response = new HttpResponseMessage(); + _response.Content = new StringContent(HttpHelper.StatusCodeToTitle(HttpStatusCode.InternalServerError)); + _response.StatusCode = HttpStatusCode.InternalServerError; } + } +#if NETCORE + internal async Task HttpClientExecuteAsync(string method, string name) + { + _receiveData = null; + _response = null; + _errCode = 0; + _errDescription = string.Empty; + GXLogging.Debug(log, "Start Execute: method '" + method + "', name '" + name + "'"); + try + { + string requestUrl = GetRequestURL(name); + bool contextCookies = _context != null && !String.IsNullOrEmpty(requestUrl); + _response = await ExecuteRequestAsync(method, requestUrl, contextCookies); + + if (contextCookies) + _context.UpdateSessionCookieContainer(); + + } + catch (Exception ex) { + ProcessHttpClientException(ex); + } + GXLogging.Debug(log, "Reading response..."); - if (response == null) + if (_response == null) return; - LoadResponseHeaders(response); - ReadReponseContent(response); - _statusCode = ((short)response.StatusCode); - _statusDescription = GetStatusCodeDescrption(response); - if ((_statusCode >= 400 && _statusCode < 600) && _errCode != 1) + ProcessResponse(_response); + ClearSendStream(); + } +#endif + public void HttpClientExecute(string method, string name) + { + _receiveData = null; + _response = null; + _errCode = 0; + _errDescription = string.Empty; + GXLogging.Debug(log, "Start Execute: method '" + method + "', name '" + name + "'"); + try { - _errCode = 1; - _errDescription = "The remote server returned an error: (" + _statusCode + ") " + _statusDescription + "."; + string requestUrl = GetRequestURL(name); + bool contextCookies = _context != null && !String.IsNullOrEmpty(requestUrl); + _response = ExecuteRequest(method, requestUrl, contextCookies); + + if (contextCookies) + _context.UpdateSessionCookieContainer(); + + } + catch (Exception ex) + { + ProcessHttpClientException(ex); } + GXLogging.Debug(log, "Reading response..."); + if (_response == null) + return; + ProcessResponse(_response); ClearSendStream(); } NameValueCollection _respHeaders; private bool disposedValue; - private bool _chunkedResponse; - HttpResponseMessage response; - void LoadResponseHeaders(HttpResponseMessage resp) + + internal void LoadResponseHeaders(HttpResponseMessage resp) { _respHeaders = new NameValueCollection(); - foreach (KeyValuePair> header in resp.Headers) + HttpResponseHeaders headers = resp.Headers; + foreach (KeyValuePair> header in headers) { _respHeaders.Add(header.Key, String.Join(",", header.Value)); } - foreach (KeyValuePair> header in resp.Content.Headers) + HttpContentHeaders contentHeaders = resp.Content.Headers; + foreach (KeyValuePair> header in contentHeaders) { _respHeaders.Add(header.Key, String.Join(",", header.Value)); } + _isChunkedResponse = resp.Headers.TransferEncodingChunked.HasValue && resp.Headers.TransferEncodingChunked.Value; + + if (_response.Content.Headers.ContentType == null) + _charset = null; + else + _charset = _response.Content.Headers.ContentType.CharSet; + _encodingFound = false; + if (!string.IsNullOrEmpty(_charset)) + { + int idx = _charset.IndexOf("charset="); + if (idx > 0) + { + idx += 8; + _charset = _charset.Substring(idx, _charset.Length - idx); + _encoding = GetEncoding(_charset); + if (_encoding != null) + _encodingFound = true; + } + else + { + _charset = String.Empty; + } + } } private string GetStatusCodeDescrption(HttpResponseMessage message) @@ -1022,7 +1363,7 @@ private void InferContentType(string contentType, HttpWebRequest req) } } - WebProxy getProxy(string proxyHost, int proxyPort, ArrayList authenticationCollection) + static WebProxy getProxy(string proxyHost, int proxyPort, ArrayList authenticationCollection) { if (proxyHost.Length > 0) { @@ -1040,7 +1381,7 @@ WebProxy getProxy(string proxyHost, int proxyPort, ArrayList authenticationColle return null; } - private void setHttpVersion(HttpWebRequest req) + private void SetHttpVersion(HttpWebRequest req) { string httpVersion; @@ -1048,6 +1389,12 @@ private void setHttpVersion(HttpWebRequest req) { if (httpVersion == "1.0") req.ProtocolVersion = HttpVersion.Version10; +#if NETCORE + else if (httpVersion == "2.0") + req.ProtocolVersion = HttpVersion.Version20; + else if (httpVersion == "3.0") + req.ProtocolVersion = HttpVersion.Version30; +#endif else req.ProtocolVersion = HttpVersion.Version11; } @@ -1088,7 +1435,8 @@ public void ConfigureHttpClientProtocol(string name, SoapHttpClientProtocol http httpC.Timeout = _timeout; } #endif - HttpWebRequest buildRequest(string method, string requestUrl, CookieContainer cookies) +#if NETCORE + async Task buildRequestAsync(string method, string requestUrl, CookieContainer cookies) { GXLogging.Debug(log, String.Format("Start HTTPClient buildRequest: requestUrl:{0} method:{1}", requestUrl, method)); int BytesRead; @@ -1108,7 +1456,7 @@ HttpWebRequest buildRequest(string method, string requestUrl, CookieContainer co req.ClientCertificates.Add(cert); req.Method = method.Trim(); req.Timeout = _timeout; - setHttpVersion(req); + SetHttpVersion(req); WebProxy proxy = getProxy(_proxyHost, _proxyPort, _authProxyCollection); if (proxy != null) req.Proxy = proxy; @@ -1120,8 +1468,56 @@ HttpWebRequest buildRequest(string method, string requestUrl, CookieContainer co #if !NETCORE using (Stream reqStream = req.GetRequestStream()) #else - using (Stream reqStream = req.GetRequestStreamAsync().GetAwaiter().GetResult()) + using (Stream reqStream = await req.GetRequestStreamAsync()) #endif + { + SendVariables(reqStream); + SendStream.Seek(0, SeekOrigin.Begin); + BytesRead = await SendStream.ReadAsync(Buffer, 0, 1024); + GXLogging.Debug(log, "Start SendStream.Read: BytesRead " + BytesRead); + while (BytesRead > 0) + { + GXLogging.Debug(log, "reqStream.Write: Buffer.length " + Buffer.Length + ",'" + Encoding.UTF8.GetString(Buffer, 0, Buffer.Length) + "'"); + await reqStream.WriteAsync(Buffer, 0, BytesRead); + BytesRead = await SendStream.ReadAsync(Buffer, 0, 1024); + } + EndMultipartBoundary(reqStream); + } + } + return req; + } +#endif + + HttpWebRequest buildRequest(string method, string requestUrl, CookieContainer cookies) + { + GXLogging.Debug(log, String.Format("Start HTTPClient buildRequest: requestUrl:{0} method:{1}", requestUrl, method)); + int BytesRead; + Byte[] Buffer = new Byte[1024]; +#pragma warning disable SYSLIB0014 // WebRequest + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestUrl); +#pragma warning disable SYSLIB0014 // WebRequest + + if (GXUtil.CompressResponse()) + { + req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + } + + req.Credentials = getCredentialCache(req.RequestUri, _authCollection); + req.CookieContainer = cookies; + foreach (X509Certificate2 cert in _certificateCollection) + req.ClientCertificates.Add(cert); + req.Method = method.Trim(); + req.Timeout = _timeout; + SetHttpVersion(req); + WebProxy proxy = getProxy(_proxyHost, _proxyPort, _authProxyCollection); + if (proxy != null) + req.Proxy = proxy; + + setHeaders(req); + + if (!method.Equals(HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase) && !method.Equals(HttpMethod.Head.Method, StringComparison.OrdinalIgnoreCase)) + { + using (Stream reqStream = req.GetRequestStream()) { SendVariables(reqStream); SendStream.Seek(0, SeekOrigin.Begin); @@ -1143,11 +1539,11 @@ public ICredentials GetCredentials(string url) { return getCredentialCache(new Uri(url), _authCollection); } - ICredentials getCredentialCache(Uri URI, ArrayList authenticationCollection) + static ICredentials getCredentialCache(Uri URI, ArrayList authenticationCollection) { string sScheme; GxAuthScheme auth; - CredentialCache cc = new CredentialCache(); + CredentialCache cc = null; for (int i = 0; i < authenticationCollection.Count; i++) { @@ -1177,10 +1573,18 @@ ICredentials getCredentialCache(Uri URI, ArrayList authenticationCollection) } else if (sScheme != "Basic") { + if (cc == null) + { + cc = new CredentialCache(); + } cc.Add(URI, sScheme, new NetworkCredential(auth.User, auth.Password, auth.Realm)); } else { + if (cc == null) + { + cc = new CredentialCache(); + } cc.Add(URI, sScheme, new NetworkCredential(auth.User, auth.Password)); } } @@ -1190,11 +1594,11 @@ ICredentials getCredentialCache(Uri URI, ArrayList authenticationCollection) } return cc; } - - private void WebExecute(string method, string name) +#if NETCORE + private async Task WebExecuteAsync(string method, string name) { HttpWebRequest req; - HttpWebResponse resp = null; + HttpWebResponse resp; _errCode = 0; _errDescription = string.Empty; @@ -1205,43 +1609,140 @@ private void WebExecute(string method, string name) string requestUrl = GetRequestURL(name); bool contextCookies = _context != null && !String.IsNullOrEmpty(requestUrl); CookieContainer cookies = contextCookies ? _context.GetCookieContainer(requestUrl, IncludeCookies) : new CookieContainer(); - req = buildRequest(method, requestUrl, cookies); + req = await buildRequestAsync(method, requestUrl, cookies); -#if NETCORE - resp = req.GetResponse() as HttpWebResponse; + resp = await req.GetResponseAsync() as HttpWebResponse; if (contextCookies) _context.UpdateSessionCookieContainer(); -#else - resp = (HttpWebResponse)req.GetResponse(); -#endif } - catch (WebException e) + catch (Exception e) { - GXLogging.Warn(log, "Error Execute", e); - _errCode = 1; - _errDescription = e.Message; - resp = (HttpWebResponse)(e.Response); + resp = ProcessWebExecuteException(e); } + + + _receiveData = Array.Empty(); + if (resp != null) + { + GXLogging.Debug(log, "Reading response..."); + loadResponseHeaders(resp); + + String charset = resp.ContentType; + using (Stream rStream = resp.GetResponseStream()) + { + try + { + bool encodingFound = false; + if (!string.IsNullOrEmpty(charset)) + { + int idx = charset.IndexOf("charset="); + if (idx > 0) + { + idx += 8; + charset = charset.Substring(idx, charset.Length - idx); + _encoding = GetEncoding(charset); + if (_encoding != null) + encodingFound = true; + } + else + { + charset = String.Empty; + } + } + using (MemoryStream ms = new MemoryStream()) + { + await rStream.CopyToAsync(ms); + _receiveData = ms.ToArray(); + } + int bytesRead = _receiveData.Length; + GXLogging.Debug(log, "BytesRead " + bytesRead); + + if (bytesRead > 0 && !encodingFound) + { + _encoding = DetectEncoding(charset, out encodingFound, _receiveData, bytesRead); + } + } + catch (IOException ioEx) + { + if (_errCode == 1) + GXLogging.Warn(log, "Could not read response", ioEx); + else + throw ioEx; + } + } + _statusCode = (short)resp.StatusCode; + _statusDescription = resp.StatusDescription; + resp.Close(); + + GXLogging.DebugSanitized(log, "_responseString " + ToString()); + } + ClearSendStream(); + } +#endif + private HttpWebResponse ProcessWebExecuteException(Exception ex) + { + WebException we; + HttpWebResponse resp=null; #if NETCORE - catch (AggregateException aex) + AggregateException agge; + if ((agge = ex as AggregateException) != null) { - GXLogging.Warn(log, "Error Execute", aex); + GXLogging.Warn(log, "Error Execute", agge); _errCode = 1; - _errDescription = aex.Message; + _errDescription = agge.Message; - var baseEx = aex.GetBaseException() as WebException; + var baseEx = agge.GetBaseException() as WebException; if (baseEx != null) { resp = baseEx.Response as HttpWebResponse; _errDescription = baseEx.Message; } } + else #endif - catch (Exception e) + if ((we = ex as WebException)!=null) { - GXLogging.Warn(log, "Error Execute", e); + GXLogging.Warn(log, "Error Execute", we); _errCode = 1; - _errDescription = e.Message; + _errDescription = we.Message; + resp = (HttpWebResponse)(we.Response); + } + else + { + GXLogging.Warn(log, "Error Execute", ex); + _errCode = 1; + _errDescription = ex.Message; + } + return resp; + + } + private void WebExecute(string method, string name) + { + HttpWebRequest req; + HttpWebResponse resp = null; + + _errCode = 0; + _errDescription = string.Empty; + + GXLogging.Debug(log, "Start Execute: method '" + method + "', name '" + name + "'"); + try + { + string requestUrl = GetRequestURL(name); + bool contextCookies = _context != null && !String.IsNullOrEmpty(requestUrl); + CookieContainer cookies = contextCookies ? _context.GetCookieContainer(requestUrl, IncludeCookies) : new CookieContainer(); + req = buildRequest(method, requestUrl, cookies); + +#if NETCORE + resp = req.GetResponse() as HttpWebResponse; + if (contextCookies) + _context.UpdateSessionCookieContainer(); +#else + resp = (HttpWebResponse)req.GetResponse(); +#endif + } + catch (Exception e) + { + resp = ProcessWebExecuteException(e); } @@ -1372,10 +1873,9 @@ private Encoding ExtractEncodingFromCharset(string responseText, string regExpP, Encoding enc = null; Match m = Regex.Match(responseText, regExpP); - string parsedEncoding = string.Empty; if (m != null && m.Success) { - parsedEncoding = m.Value; + string parsedEncoding = m.Value; parsedEncoding = parsedEncoding.Substring(startAt, parsedEncoding.Length - (startAt + 1)); enc = GetEncoding(parsedEncoding); } @@ -1385,62 +1885,69 @@ public bool Eof { get { - if (_chunkedResponse && _receiveStream != null) - { - return _receiveStream.EndOfStream; - } - return true; + return _eof; + } } - + StreamReader _receivedChunkedStream; public string ReadChunk() { - if (_chunkedResponse) + if (!_isChunkedResponse) + return ToString(); + + if (_response == null) + return string.Empty; + try { - if (_receiveStream != null) + if (_receivedChunkedStream == null) { - if (!_receiveStream.EndOfStream) - { - string line = _receiveStream.ReadLine(); - if (line != null) - return line; - } - else +#if NETCORE + _receivedChunkedStream = new StreamReader(_response.Content.ReadAsStream()); +#else + _receivedChunkedStream = new StreamReader(_response.Content.ReadAsStreamAsync().GetAwaiter().GetResult()); +#endif + } + _eof = _receivedChunkedStream.EndOfStream; + if (!_eof) + { + string line = _receivedChunkedStream.ReadLine(); + if (line != null) { - _receiveStream.Dispose(); - _receiveStream = null; - response.Dispose(); - response = null; + return line; } } - return string.Empty; } - else - return ToString(); - } - public override string ToString() - { - string responseString; - if (_chunkedResponse) + catch (Exception ex) { - StringBuilder sb = new StringBuilder(); - while (!Eof){ - sb.Append(ReadChunk()); - } - responseString = sb.ToString(); + GXLogging.Error(log, String.Format("Error reading chunk", ex)); } - else - { - if (_encoding == null) - _encoding = Encoding.UTF8; - if (_receiveData == null) - return string.Empty; + return string.Empty; - responseString = _encoding.GetString(_receiveData); - } + } + public override string ToString() + { + if (ReceiveData == null) + return string.Empty; + if (_encoding == null) + _encoding = Encoding.UTF8; + string responseString = _encoding.GetString(ReceiveData); GXLogging.DebugSanitized(log, "_responseString " + responseString); return responseString; } +#if NETCORE + public async Task ToStringAsync() + { + byte[] bytes = await ReceiveDataAsync(); + if (bytes == null) + return string.Empty; + if (_encoding == null) + _encoding = Encoding.UTF8; + string responseString = _encoding.GetString(bytes); + GXLogging.DebugSanitized(log, "_responseString " + responseString); + return responseString; + } +#endif + public void ToFile(string fileName) { string pathName = fileName; @@ -1451,9 +1958,9 @@ public void ToFile(string fileName) if (fileName.IndexOfAny(new char[] { '\\', ':' }) == -1) pathName = Path.Combine(GxContext.StaticPhysicalPath(), fileName); - if (_receiveData != null) + if (ReceiveData != null) { - File.WriteAllBytes(pathName, _receiveData); + File.WriteAllBytes(pathName, ReceiveData); } } @@ -1518,6 +2025,7 @@ public void AddCertificate(string file, string pass) { c = new X509Certificate2(file, pass); } + _fileCertificateCollection.Add(file); _certificateCollection.Add(c); } @@ -1529,7 +2037,7 @@ protected virtual void Dispose(bool disposing) { _receiveData = null; _sendStream?.Dispose(); - _receiveStream?.Dispose(); + _receivedChunkedStream?.Dispose(); } disposedValue = true; } diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxLocations.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxLocations.cs index e8b2bc1eb..8ef8be5b2 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxLocations.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxLocations.cs @@ -16,6 +16,7 @@ public class GxLocation string _resourceName = ""; short _authentication; short _authenticationMethod; + string _accessToken = ""; string _authenticationUser = ""; string _authenticationRealm = ""; string _authenticationPassword = ""; @@ -76,6 +77,11 @@ public short AuthenticationMethod get {return _authenticationMethod;} set {_authenticationMethod = value;} } + public string AccessToken + { + get { return _accessToken; } + set { _accessToken = value; } + } public string AuthenticationUser { get {return _authenticationUser;} diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxMessaging.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxMessaging.cs index 75594364c..f05a85b98 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxMessaging.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxMessaging.cs @@ -8,7 +8,6 @@ using System.Text.RegularExpressions; using GeneXus.Configuration; using GeneXus.Application; -using log4net; using System.Security; namespace GeneXus.Utils @@ -16,7 +15,8 @@ namespace GeneXus.Utils [SecuritySafeCritical] public class GxQueueMessage { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.GxQueueMessage)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + int priority; Message message; @@ -207,7 +207,7 @@ public bool canAddMSMQPropertiesToGet( MessagePropertyFilter m, string key, stri [SecuritySafeCritical] public class GxQueue { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxQueue)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); string provider; string user; diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxSession.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxSession.cs index d6c2ef005..13881d355 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxSession.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxSession.cs @@ -5,17 +5,16 @@ using System.Web.SessionState; #else using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Session; #endif -using System.Threading; using GeneXus.Utils; using GeneXus.Encryption; using GeneXus.Application; -using log4net; -using System.Collections.Generic; +using GeneXus.Configuration; namespace GeneXus.Http { - public interface IGxSession + public interface IGxSession { void Set(string key, string val); void Set(string key, T val) where T:class; @@ -32,8 +31,13 @@ string Id } public class GxWebSession : IGxSession - { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Http.GxWebSession)); + { +#if NETCORE + const string SESSION_COOKIE_NAME = "SESSION_COOKIE_NAME"; + const string ASPNETCORE_APPL_PATH = "ASPNETCORE_APPL_PATH"; +#endif + + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private HttpSessionState _httpSession; #region InternalKeys GXNavigationHelper InternalKeyNavigationHelper; @@ -218,19 +222,61 @@ public static bool IsSessionExpired(HttpContext httpContext) if (httpContext.IsNewSession()) { string CookieHeaders = httpContext.Request.Headers["Cookie"]; - - if ((null != CookieHeaders) && ((CookieHeaders.IndexOf("ASP.NET_SessionId") >= 0)|| CookieHeaders.IndexOf(".AspNetCore.Session") >= 0)) - { - // IsNewSession is true, but session cookie exists, - // so, ASP.NET session is expired - return true; + if ((null != CookieHeaders) && (CookieHeaders.IndexOf(SessionCookieName) >= 0)) + { + // IsNewSession is true, but session cookie exists, + // so, ASP.NET session is expired + return true; } } } return false; } +#if NETCORE + internal static string GetSessionCookieName(string virtualPath) + { + string cookieName; + if (Config.GetValueOrEnvironmentVarOf(SESSION_COOKIE_NAME, out cookieName)) + return cookieName; + else + { + if (!string.IsNullOrEmpty(virtualPath)) + { + return $"{SessionDefaults.CookieName}.{virtualPath.ToLower()}"; + } + else + { + string applPath = Config.ConfigRoot[ASPNETCORE_APPL_PATH]; - } + if (!string.IsNullOrEmpty(applPath)) + { + cookieName = ToCookieName(applPath).ToLower(); + return $"{SessionDefaults.CookieName}.{cookieName}"; + } + } + return SessionDefaults.CookieName; + } + } + static string ToCookieName(string name) + { + char[] cookieName = new char[name.Length]; + int index = 0; + + foreach (char character in name) + { + if (char.IsLetter(character) || char.IsNumber(character)) + { + cookieName[index] = character; + index++; + } + } + return new string(cookieName, 0, index); + } + internal static string SessionCookieName { get; set; } +#else + const string SessionCookieName="ASP.NET_SessionId"; +#endif + } public class GxSession : IGxSession { diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/SD/SDApis.cs b/dotnet/src/dotnetframework/GxClasses/Domain/SD/SDApis.cs index 363bd3eaf..9ce156ff8 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/SD/SDApis.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/SD/SDApis.cs @@ -1,19 +1,15 @@ +using System; using GeneXus; using GeneXus.Application; using GeneXus.Data; -using GeneXus.Utils; -using log4net; -using System; -using System.Collections; -using System.Collections.Generic; namespace GX { public class ClientInformation { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GX.ClientInformation)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); - public static string AppVersionCode + public static string AppVersionCode { get { diff --git a/dotnet/src/dotnetframework/GxClasses/ErrorHandling/GxADODataException.cs b/dotnet/src/dotnetframework/GxClasses/ErrorHandling/GxADODataException.cs index 117c4ae61..1e6b26800 100644 --- a/dotnet/src/dotnetframework/GxClasses/ErrorHandling/GxADODataException.cs +++ b/dotnet/src/dotnetframework/GxClasses/ErrorHandling/GxADODataException.cs @@ -21,10 +21,6 @@ namespace GeneXus.Data [Serializable()] public class GxADODataException : Exception { - public GxADODataException(SerializationInfo info,StreamingContext ctx):base(info,ctx) - { - - } public GxADODataException(string mes, Exception ex) : base(mes, ex) { if (ex != null) diff --git a/dotnet/src/dotnetframework/GxClasses/ErrorHandling/GxNotImplementedException.cs b/dotnet/src/dotnetframework/GxClasses/ErrorHandling/GxNotImplementedException.cs index 5a872a533..6d0c4c3f3 100644 --- a/dotnet/src/dotnetframework/GxClasses/ErrorHandling/GxNotImplementedException.cs +++ b/dotnet/src/dotnetframework/GxClasses/ErrorHandling/GxNotImplementedException.cs @@ -8,10 +8,6 @@ namespace GeneXus.Data [Serializable()] public class GxNotImplementedException : Exception { - public GxNotImplementedException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) - { - - } public GxNotImplementedException(string message) : base(message) { diff --git a/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj b/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj index 622226cdd..7be48a51b 100644 --- a/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj +++ b/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj @@ -1,4 +1,4 @@ - + net462 GxClasses @@ -12,11 +12,12 @@ - + + - - + + diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/CsrfHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/CsrfHelper.cs new file mode 100644 index 000000000..6da5f4545 --- /dev/null +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/CsrfHelper.cs @@ -0,0 +1,77 @@ +using System.Net; +using System; +using System.Net.Http; +using System.Security; +using System.Web; +using System.Web.Helpers; +using System.Web.Mvc; +using GeneXus.Application; +using GeneXus.Utils; + +namespace GeneXus.Http +{ + internal class CSRFHelper + { + [SecuritySafeCritical] + internal static bool HandleException(Exception e, HttpContext httpContext) + { + if (RestAPIHelpers.ValidateCsrfToken()) + { + return HandleExceptionImp(e, httpContext); + } + return false; + } + [SecuritySafeCritical] + private static bool HandleExceptionImp(Exception e, HttpContext httpContext) + { + if (e is HttpAntiForgeryException) + { + httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + httpContext.Response.StatusDescription = HttpHelper.InvalidCSRFToken; + return true; + } + return false; + } + + [SecuritySafeCritical] + internal static void ValidateAntiforgery(HttpContext context) + { + if (RestAPIHelpers.ValidateCsrfToken()) + { + ValidateAntiforgeryImp(context); + } + } + [SecurityCritical] + private static void ValidateAntiforgeryImp(HttpContext context) + { + string cookieToken, formToken; + string httpMethod = context.Request.HttpMethod; + string tokens = context.Request.Cookies[HttpHeader.X_CSRF_TOKEN_COOKIE]?.Value; + string internalCookieToken = context.Request.Cookies[HttpHeader.X_CSRF_TOKEN_COOKIE]?.Value; + if (httpMethod == HttpMethod.Get.Method && (string.IsNullOrEmpty(tokens) || string.IsNullOrEmpty(internalCookieToken))) + { + AntiForgery.GetTokens(null, out cookieToken, out formToken); +#pragma warning disable SCS0009 // The cookie is missing security flag HttpOnly + HttpCookie cookie = new HttpCookie(HttpHeader.X_CSRF_TOKEN_COOKIE, formToken) + { + HttpOnly = false, + Secure = GxContext.GetHttpSecure(context) == 1, + }; +#pragma warning restore SCS0009 // The cookie is missing security flag HttpOnly + HttpCookie internalCookie = new HttpCookie(AntiForgeryConfig.CookieName, cookieToken) + { + HttpOnly = true, + Secure = GxContext.GetHttpSecure(context) == 1, + }; + context.Response.SetCookie(cookie); + context.Response.SetCookie(internalCookie); + } + if (httpMethod == HttpMethod.Delete.Method || httpMethod == HttpMethod.Post.Method || httpMethod == HttpMethod.Put.Method) + { + cookieToken = context.Request.Cookies[AntiForgeryConfig.CookieName]?.Value; + string headerToken = context.Request.Headers[HttpHeader.X_CSRF_TOKEN_HEADER]; + AntiForgery.Validate(cookieToken, headerToken); + } + } + } +} diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXFileWatcher.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXFileWatcher.cs index 49cc89efe..df785527a 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXFileWatcher.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXFileWatcher.cs @@ -4,7 +4,6 @@ namespace GeneXus.Application using System.Collections; using System.IO; using GeneXus.Configuration; - using log4net; using System.Collections.Generic; using System.Web; using System.Threading; @@ -14,12 +13,11 @@ namespace GeneXus.Application using System.Web.Configuration; #endif using System.Xml; - using GeneXus.Http; using System.Collections.Concurrent; public class GXFileWatcher : IDisposable { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXFileWatcher)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static volatile GXFileWatcher m_Instance; private static object m_SyncRoot = new Object(); TimeSpan TIMEOUT; diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs index d4a68765e..c27480c41 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs @@ -3,27 +3,381 @@ using System.Text; using log4net; using log4net.Core; +using System.Threading; +using log4net.Util; +using System.Globalization; +using System.Security; +#if NETCORE +using GeneXus.Services.Log; +using Microsoft.Extensions.Logging; +#endif namespace GeneXus { + public class GXLoggerFactory + { +#if NETCORE + static ILoggerFactory _instance = GXLogService.GetLogFactory(); +#endif + public static IGXLogger GetLogger(string categoryName) + { +#if NETCORE + if (_instance != null) + { + return new GXLoggerMsExtensions(_instance.CreateLogger(categoryName)); + } +#endif + return new GXLoggerLog4Net(log4net.LogManager.GetLogger(categoryName)); + } + public static IGXLogger GetLogger() where T : class + { +#if NETCORE + if (_instance != null) + { + return new GXLoggerMsExtensions(_instance.CreateLogger()); + } +#endif + return new GXLoggerLog4Net(log4net.LogManager.GetLogger(typeof(T))); + } + + } + public interface IGXLogger + { + bool IsTraceEnabled { get; } + bool IsErrorEnabled { get; } + bool IsWarningEnabled { get; } + bool IsDebugEnabled { get; } + bool IsInfoEnabled { get; } + bool IsCriticalEnabled { get; } + + bool TraceEnabled(); + bool CriticalEnabled(); + bool ErrorEnabled(); + bool WarningEnabled(); + bool DebugEnabled(); + bool InfoEnabled(); + + + void LogTrace(string value); + void LogError(string msg, Exception ex); + + void LogError(string msg); + void LogError(string msg, params string[] list); + void LogWarning(Exception ex, string msg); + void LogWarning(string msg); + void LogWarning(string msg, params string[] list); + void LogDebug(string msg); + void LogDebug(Exception ex, string msg); + void LogDebug(string msg, params string[] list); + void LogInfo(string msg); + void LogInfo(string msg, params string[] list); + void LogCritical(string msg); + void LogCritical(Exception ex , string msg); + void LogCritical(string msg, params string[] list); + } +#if NETCORE + internal class GXLoggerMsExtensions : IGXLogger + { + internal Microsoft.Extensions.Logging.ILogger log { get; set; } + + internal GXLoggerMsExtensions(Microsoft.Extensions.Logging.ILogger logInstance) + { + log = logInstance; + } + public bool IsTraceEnabled { get => TraceEnabled(); } + public bool IsErrorEnabled { get => ErrorEnabled(); } + public bool IsWarningEnabled { get => WarningEnabled(); } + public bool IsDebugEnabled { get => DebugEnabled(); } + public bool IsInfoEnabled { get => InfoEnabled(); } + public bool IsCriticalEnabled { get => CriticalEnabled(); } + public bool TraceEnabled() + { + return log.IsEnabled(LogLevel.Trace); + } + public bool ErrorEnabled() + { + return log.IsEnabled(LogLevel.Error); + } + public bool WarningEnabled() + { + return log.IsEnabled(LogLevel.Warning); + } + public bool DebugEnabled() + { + return log.IsEnabled(LogLevel.Debug); + } + public bool InfoEnabled() + { + return log.IsEnabled(LogLevel.Information); + } + public bool CriticalEnabled() + { + return log.IsEnabled(LogLevel.Critical); + } + public void LogTrace(string msg) + { + log.LogTrace(msg); + } + public void LogError(string msg, Exception ex) + { + log.LogError(ex, msg); + } + public void LogError(string msg) + { + log.LogError(msg); + } + public void LogError(string msg, params string[] list) + { + log.LogError(msg, list); + } + public void LogWarning(Exception ex, string msg) + { + log.LogWarning(ex, msg); + } + public void LogWarning(string msg) + { + log.LogWarning(msg); + } + public void LogWarning(string msg, params string[] list) + { + log.LogWarning(msg, list); + } + public void LogDebug(string msg) + { + log.LogDebug(msg); + } + public void LogDebug(string msg, params string[] list) + { + log.LogDebug(msg, list); + } + public void LogDebug(Exception ex, string msg) + { + log.LogDebug(ex, msg); + } + public void LogInfo(string msg) + { + log.LogInformation(msg); + } + + public void LogInfo(string msg, params string[] list) + { + log.LogInformation(msg, list); + } + public void LogCritical(string msg) + { + log.LogCritical(msg); + } + public void LogCritical(Exception ex, string msg) + { + log.LogCritical(ex, msg); + } + public void LogCritical(string msg, params string[] list) + { + log.LogCritical(msg, list); + } + } +#endif + internal class GXLoggerLog4Net : IGXLogger + { +#if NETCORE + const string ThreadNameNet8 = ".NET TP Worker"; + const string ThreadNameNet6 = ".NET ThreadPool Worker"; + const string ThreadId = "threadid"; +#endif + private bool _traceEnabled = false; + private bool _debugEnabled = false; + internal ILog log { get; set; } + + internal GXLoggerLog4Net(ILog logInstance) + { + log = logInstance; + _traceEnabled = log.Logger.IsEnabledFor(Level.Trace); + _debugEnabled = log.IsDebugEnabled; + } + void SetThreadIdForLogging() + { +#if NETCORE + if (ThreadContext.Properties[ThreadId] == null) + { + string name = Thread.CurrentThread.Name; + if (!string.IsNullOrEmpty(name) && name != ThreadNameNet6 && !name.StartsWith(ThreadNameNet8)) + { + ThreadContext.Properties[ThreadId] = name; + } + else + { + try + { + ThreadContext.Properties[ThreadId] = SystemInfo.CurrentThreadId.ToString(NumberFormatInfo.InvariantInfo); + } + catch (SecurityException) + { + log.Debug("Security exception while trying to get current thread ID. Error Ignored. Empty thread name."); + ThreadContext.Properties[ThreadId] = Thread.CurrentThread.GetHashCode().ToString(CultureInfo.InvariantCulture); + } + } + } +#endif + } + public bool IsTraceEnabled { get => _traceEnabled; } + public bool IsErrorEnabled { get => ErrorEnabled(); } + public bool IsWarningEnabled { get => WarningEnabled(); } + public bool IsDebugEnabled { get => _debugEnabled; } + public bool IsInfoEnabled { get => InfoEnabled(); } + public bool IsCriticalEnabled { get => CriticalEnabled(); } + public bool TraceEnabled() + { + return _traceEnabled; + } + public bool ErrorEnabled() + { + return log.IsErrorEnabled; + } + public bool WarningEnabled() + { + return log.IsWarnEnabled; + } + public bool DebugEnabled() + { + return _debugEnabled; + } + public bool InfoEnabled() + { + return log.IsInfoEnabled; + } + public bool CriticalEnabled() + { + return log.IsFatalEnabled; + } + + public void LogTrace(string value) + { + SetThreadIdForLogging(); + log.Logger.Log(MethodBase.GetCurrentMethod().DeclaringType, Level.Trace, value, null); + } + public void LogError(string msg, Exception ex) + { + SetThreadIdForLogging(); + log.Error(msg, ex); + } + public void LogError(string msg) + { + SetThreadIdForLogging(); + log.Error(msg); + } + public void LogError(string msg, params string[] list) + { + StringBuilder message = new StringBuilder(); + message.Append(msg); + foreach (string parm in list) + { + message.Append(parm); + } + + LogError(message.ToString()); + } + public void LogWarning(Exception ex, string msg) + { + SetThreadIdForLogging(); + log.Warn(msg, ex); + } + public void LogWarning(string msg) + { + SetThreadIdForLogging(); + log.Warn(msg); + } + public void LogWarning(string msg, params string[] list) + { + StringBuilder message = new StringBuilder(); + message.Append(msg); + foreach (string parm in list) + { + message.Append(parm); + } + + LogWarning(message.ToString()); + } + public void LogDebug(string msg) + { + SetThreadIdForLogging(); + log.Debug(msg); + } + + public void LogDebug(Exception ex, string msg) + { + SetThreadIdForLogging(); + log.Debug(msg, ex); + } + public void LogDebug(string msg, params string[] list) + { + StringBuilder message = new StringBuilder(); + message.Append(msg); + foreach (string parm in list) + { + message.Append(parm); + } + + LogDebug(message.ToString()); + } + public void LogInfo(string msg) + { + SetThreadIdForLogging(); + log.Info(msg); + } + public void LogInfo(string msg, params string[] list) + { + StringBuilder message = new StringBuilder(); + message.Append(msg); + foreach (string parm in list) + { + message.Append(parm); + } + + LogInfo(message.ToString()); + } + + public void LogCritical(string msg) + { + SetThreadIdForLogging(); + log.Fatal(msg); + } + public void LogCritical(Exception ex, string msg) + { + SetThreadIdForLogging(); + log.Fatal(msg, ex); + } + + public void LogCritical(string msg, params string[] list) + { + StringBuilder message = new StringBuilder(); + message.Append(msg); + foreach (string parm in list) + { + message.Append(parm); + } + + LogCritical(message.ToString()); + } + } public static class GXLogging { + #region log4NET + // Legacy // public static void Trace(this ILog log, params string[] list) { if (log.Logger.IsEnabledFor(Level.Trace)) { - log.Logger.Log(MethodBase.GetCurrentMethod().DeclaringType, Level.Trace, String.Join(" ", list), null); + log.Logger.Log(MethodBase.GetCurrentMethod().DeclaringType, Level.Trace, string.Join(" ", list), null); } } - public static void Error(ILog log, string msg, Exception ex) + public static void Error(ILog log, string msg, Exception ex) { if (log.IsErrorEnabled) { log.Error(msg, ex); } } - public static void ErrorSanitized(ILog log, string msg, Exception ex) { if (log.IsErrorEnabled) @@ -31,25 +385,25 @@ public static void ErrorSanitized(ILog log, string msg, Exception ex) log.Error(Utils.StringUtil.Sanitize(msg, Utils.StringUtil.LogUserEntryWhiteList), ex); } } - public static void Error(ILog log, string msg1, string msg2, Exception ex) { Error(log, msg1 + msg2, ex); } - public static void Error(ILog log, Exception ex, params string[] list) + public static void Error(ILog log, Exception ex, params string[] list) { - if (log.IsErrorEnabled){ - foreach (string parm in list){ + if (log.IsErrorEnabled) + { + foreach (string parm in list) + { log.Error(parm); } } } - public static void Error(ILog log, params string[] list) + public static void Error(ILog log, params string[] list) { Error(log, null, list); } - - public static void Warn(ILog log, Exception ex, params string[] list) + public static void Warn(ILog log, Exception ex, params string[] list) { if (log.IsWarnEnabled) { @@ -64,25 +418,34 @@ public static void Warn(ILog log, Exception ex, params string[] list) log.Warn(msg); } } - public static void Warn(ILog log, params string[] list) + public static void Warn(ILog log, params string[] list) { Warn(log, null, list); } - public static void Warn(ILog log, string msg, Exception ex) + public static void Warn(ILog log, string msg, Exception ex) { if (log.IsWarnEnabled) { log.Warn(msg, ex); } } - public static void Debug(ILog log, Exception ex, params string[] list) + internal static void DebugSanitized(IGXLogger log, string startMsg, Func buildMsg) + { + if (log.IsDebugEnabled) + { + string msg = buildMsg(); + DebugSanitized(log, startMsg + msg); + } + } + + public static void DebugSanitized(ILog log, Exception ex, params string[] list) { if (log.IsDebugEnabled) { StringBuilder msg = new StringBuilder(); foreach (string parm in list) { - msg.Append(parm); + msg.Append(Utils.StringUtil.Sanitize(parm, Utils.StringUtil.LogUserEntryWhiteList)); } if (ex != null) log.Debug(msg, ex); @@ -90,15 +453,18 @@ public static void Debug(ILog log, Exception ex, params string[] list) log.Debug(msg); } } - - public static void DebugSanitized(ILog log, Exception ex, params string[] list) + public static void Debug(ILog log, params string[] list) + { + Debug(log, null, list); + } + public static void Debug(ILog log, Exception ex, params string[] list) { if (log.IsDebugEnabled) { StringBuilder msg = new StringBuilder(); foreach (string parm in list) { - msg.Append(Utils.StringUtil.Sanitize(parm, Utils.StringUtil.LogUserEntryWhiteList)); + msg.Append(parm); } if (ex != null) log.Debug(msg, ex); @@ -106,14 +472,6 @@ public static void DebugSanitized(ILog log, Exception ex, params string[] list) log.Debug(msg); } } - - - - public static void Debug(ILog log, params string[] list) - { - Debug(log, null, list); - } - public static void DebugSanitized(ILog log, params string[] list) { DebugSanitized(log, null, list); @@ -127,18 +485,18 @@ public static void Debug(ILog log, string startMsg, Func buildMsg) log.Debug(startMsg + msg); } } - public static void Debug(ILog log, string msg1, string msg2, Exception ex) + public static void Debug(ILog log, string msg1, string msg2, Exception ex) { Debug(log, msg1 + msg2, ex); } - public static void Debug(ILog log, string msg, Exception ex) + public static void Debug(ILog log, string msg, Exception ex) { if (log.IsDebugEnabled) { log.Debug(msg, ex); } } - public static void Info(ILog log, params string[] list) + public static void Info(ILog log, params string[] list) { if (log.IsInfoEnabled) { @@ -150,6 +508,301 @@ public static void Info(ILog log, params string[] list) log.Info(msg); } } + #endregion + + #region Microsoft.Extensions + internal static void Trace(IGXLogger logger, params string[] list) + { + if (logger != null) + { + if (logger.IsTraceEnabled) + logger.LogTrace(string.Join(" ", list)); + } + } + internal static void Trace(IGXLogger logger, Func buildMsg) + { + if (logger != null) + { + if (logger.IsTraceEnabled) + { + string msg = buildMsg(); + logger.LogTrace(msg); + } + } + } + internal static bool TraceEnabled(IGXLogger logger) + { + if (logger != null) + return logger.IsTraceEnabled; + else + return false; + } + + public static void Critical(IGXLogger logger, params string[] list) + { + if (logger != null) + { + if (logger.IsCriticalEnabled) + { + logger.LogCritical(string.Join(" ", list)); + } + } + } + public static void Critical(IGXLogger logger, string msg, Exception ex) + { + if (logger != null) + { + if (logger.IsCriticalEnabled) + { + logger.LogCritical(ex, msg); + } + } + } + public static void Critical(IGXLogger logger, string msg, params string[] list) + { + if (logger != null) + { + if (logger.IsCriticalEnabled) + { + logger.LogCritical(msg, list); + } + } + } + + public static void Error(IGXLogger logger, string msg, Exception ex) + { + if (logger != null) + { + if (logger.IsErrorEnabled) + { + logger.LogError(msg, ex); + } + } + } + public static void Error(IGXLogger logger, string msg, params string[] list ) + { + if (logger != null) + { + if (logger.IsErrorEnabled) + { + logger.LogError(msg, list); + } + } + } + + internal static void ErrorSanitized(IGXLogger logger, string msg, Exception ex) + { + if (logger != null) + { + if (logger.IsErrorEnabled) + { + logger.LogError(Utils.StringUtil.Sanitize(msg, Utils.StringUtil.LogUserEntryWhiteList), ex); + } + } + } + + internal static void Error(IGXLogger logger, string msg1, string msg2, Exception ex) + { + Error(logger, msg1 + msg2, ex); + } + + internal static void Error(IGXLogger logger, Exception ex, params string[] list) + { + if (logger != null) + { + if (logger.IsErrorEnabled) + { + logger.LogError(ex.Message); + foreach (string parm in list) + { + logger.LogError(parm); + } + } + } + } + internal static void Error(IGXLogger logger, params string[] list) + { + if (logger != null) + { + if (logger.IsErrorEnabled) + { + foreach (string parm in list) + { + logger.LogError(parm); + } + } + } + } + public static void Warn(IGXLogger logger, Exception ex, params string[] list) + { + if (logger != null) + { + if (logger.IsWarningEnabled) + { + StringBuilder msg = new StringBuilder(); + foreach (string parm in list) + { + msg.Append(parm); + } + if (ex != null) + logger.LogWarning(ex, msg.ToString()); + else + logger.LogWarning(msg.ToString()); + } + } + } + public static void Warn(IGXLogger logger, params string[] list) + { + if (logger != null) + { + if (logger.IsWarningEnabled) + { + StringBuilder msg = new StringBuilder(); + foreach (string parm in list) + { + msg.Append(parm); + } + logger.LogWarning(msg.ToString()); + } + } + } + public static void Warn(IGXLogger logger, string msg, params string[] list) + { + if (logger != null) + { + if (logger.IsWarningEnabled) + { + logger.LogWarning(msg, list); + } + } + } + public static void Warn(IGXLogger logger, string msg, Exception ex) + { + if (logger != null) + { + if (logger.IsWarningEnabled) + { + logger.LogWarning(ex, msg); + } + } + } + internal static void DebugSanitized(IGXLogger logger, Exception ex, params string[] list) + { + if (logger != null) + { + if (logger.IsDebugEnabled) + { + StringBuilder msg = new StringBuilder(); + foreach (string parm in list) + { + msg.Append(Utils.StringUtil.Sanitize(parm, Utils.StringUtil.LogUserEntryWhiteList)); + } + if (ex != null) + logger.LogDebug(ex, msg.ToString()); + else + logger.LogDebug(msg.ToString()); + } + } + } + internal static void DebugSanitized(IGXLogger logger, params string[] list) + { + DebugSanitized(logger, null, list); + } + public static void Debug(IGXLogger logger, Exception ex, params string[] list) + { + if (logger != null) + { + if (logger.IsDebugEnabled) + { + StringBuilder msg = new StringBuilder(); + foreach (string parm in list) + { + msg.Append(parm); + } + if (ex != null) + logger.LogDebug(ex, msg.ToString()); + else + logger.LogDebug(msg.ToString()); + } + } + } + public static void Debug(IGXLogger logger, params string[] list) + { + if (logger != null) + { + if (logger.IsDebugEnabled) + { + StringBuilder msg = new StringBuilder(); + foreach (string parm in list) + { + msg.Append(parm); + } + + logger.LogDebug(msg.ToString()); + } + } + } + + public static void Debug(IGXLogger logger, string startMsg, Func buildMsg) + { + if (logger != null) + { + if (logger.IsDebugEnabled) + { + string msg = buildMsg(); + logger.LogDebug(startMsg + msg); + } + } + } + public static void Debug(IGXLogger logger, string msg1, string msg2, Exception ex) + { + Debug(logger, msg1 + msg2, ex); + } + public static void Debug(IGXLogger logger, string msg, params string[] list) + { + if (logger != null) + { + if (logger.IsDebugEnabled) + { + logger.LogDebug(msg, list); + } + } + } + public static void Debug(IGXLogger logger, string msg, Exception ex) + { + if (logger != null) + { + if (logger.IsDebugEnabled) + { + logger.LogDebug(ex, msg); + } + } + } + public static void Info(IGXLogger logger, params string[] list) + { + if (logger != null) + { + if (logger.IsInfoEnabled) + { + StringBuilder msg = new StringBuilder(); + foreach (string parm in list) + { + msg.Append(parm); + } + logger.LogInfo(msg.ToString()); + } + } + } + public static void Info(IGXLogger logger, string msg, params string[] list) + { + if (logger != null) { + if (logger.IsInfoEnabled) + { + logger.LogInfo(msg, list); + } + } + } + #endregion } } diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXMetadata.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXMetadata.cs index 20a5c51bc..e50e50906 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXMetadata.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXMetadata.cs @@ -3,7 +3,6 @@ namespace GeneXus.Metadata using System; using System.Reflection; using System.Collections; - using log4net; using System.IO; using GeneXus.Utils; using GeneXus.Configuration; @@ -19,7 +18,7 @@ namespace GeneXus.Metadata public class ClassLoader { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Metadata.ClassLoader)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); #if NETCORE private const string GXWEBPROCEDURE_TYPE = "GeneXus.Procedure.GXWebProcedure"; @@ -84,18 +83,17 @@ static public Type FindType(string defaultAssemblyName, string ns, string clssWi string clss = string.IsNullOrEmpty(ns) ? clssWithoutNamespace : string.Format("{0}.{1}", ns, clssWithoutNamespace); Type objType = null; string appNS; - loadedAssemblies.TryGetValue(clss, out objType); - if (objType == null) - { + if (!loadedAssemblies.TryGetValue(clss, out objType)) + { if (defaultAssembly != null) { try { - objType = ignoreCase? defaultAssembly.GetType(clss, false, ignoreCase): defaultAssembly.GetType(clss); + objType = ignoreCase? defaultAssembly.GetType(clss, false, ignoreCase): defaultAssembly.GetType(clss, false); } catch { - GXLogging.Error(log, "Failed to load type: " + clss + ", assembly: " + defaultAssembly.FullName); + GXLogging.Warn(log, "Failed to load type: " + clss + ", assembly: " + defaultAssembly.FullName); } } @@ -106,36 +104,40 @@ static public Type FindType(string defaultAssemblyName, string ns, string clssWi { #if NETCORE Assembly assem = AssemblyLoader.LoadAssembly(defaultAssemblyNameObj); - objType = ignoreCase ? assem.GetType(clss): assem.GetType(clss, false, ignoreCase); + objType = ignoreCase ? assem.GetType(clss, false): assem.GetType(clss, false, ignoreCase); #else - objType = Assembly.Load(defaultAssemblyNameObj).GetType(clss); + objType = Assembly.Load(defaultAssemblyNameObj).GetType(clss, false); #endif } } + catch(FileNotFoundException) + { + GXLogging.Warn(log, "Assembly: ", defaultAssemblyName, "not found"); + } catch(Exception ex) { - GXLogging.Error(log, "Failed to load type: " + clss + ", assembly: " + defaultAssemblyName, ex); + GXLogging.Warn(log, "Failed to load type: " + clss + ", assembly: " + defaultAssemblyName, ex); } try { if (objType == null) if (Assembly.GetEntryAssembly() != null) - objType = ignoreCase ? Assembly.GetEntryAssembly().GetType(clss, false, ignoreCase) : Assembly.GetEntryAssembly().GetType(clss); + objType = ignoreCase ? Assembly.GetEntryAssembly().GetType(clss, false, ignoreCase) : Assembly.GetEntryAssembly().GetType(clss, false); } catch { - GXLogging.Error(log, "Failed to load type: " + clss + " from entryAssembly"); + GXLogging.Warn(log, "Failed to load type: " + clss + " from entryAssembly"); } try { if (objType == null) - objType = ignoreCase ? Assembly.GetCallingAssembly().GetType(clss, false, ignoreCase) : Assembly.GetCallingAssembly().GetType(clss); + objType = ignoreCase ? Assembly.GetCallingAssembly().GetType(clss, false, ignoreCase) : Assembly.GetCallingAssembly().GetType(clss, false); } catch { - GXLogging.Error(log, "Failed to load type: " + clss + " from callingAssembly"); + GXLogging.Warn(log, "Failed to load type: " + clss + " from callingAssembly"); } if (objType == null && !string.IsNullOrEmpty(ns) && Config.GetValueOf("AppMainNamespace", out appNS)) @@ -151,7 +153,7 @@ static public Type FindType(string defaultAssemblyName, string ns, string clssWi foreach (Assembly asby in AppDomain.CurrentDomain.GetAssemblies()) { - objType = ignoreCase ? asby.GetType(clss, false, ignoreCase) : asby.GetType(clss); + objType = ignoreCase ? asby.GetType(clss, false, ignoreCase) : asby.GetType(clss, false); if (objType != null) break; } @@ -186,10 +188,10 @@ static public Type FindType(string defaultAssemblyName, string ns, string clssWi try { #if !NETCORE - objType = Assembly.LoadFrom(file).GetType(clss); + objType = Assembly.LoadFrom(file).GetType(clss, false); #else Assembly assem = AssemblyLoader.LoadAssembly(new AssemblyName(Path.GetFileNameWithoutExtension(file))); - objType = ignoreCase ? assem.GetType(clss, false, ignoreCase) : assem.GetType(clss); + objType = ignoreCase ? assem.GetType(clss, false, ignoreCase) : assem.GetType(clss, false); #endif if (objType != null) break; @@ -200,15 +202,15 @@ static public Type FindType(string defaultAssemblyName, string ns, string clssWi } } - if (objType == null) - { - GXLogging.Error(log, "Failed to load type: " + clss + " from currentdomain"); - throw new GxClassLoaderException("Failed to load type: " + clss); - } } loadedAssemblies[clss] = objType; } + if (objType == null) + { + GXLogging.Error(log, "Failed to load type: " + clss + " from currentdomain"); + throw new GxClassLoaderException("Failed to load type: " + clss); + } return objType; } diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXNavigationHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXNavigationHelper.cs index 1b755911c..218180e38 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXNavigationHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXNavigationHelper.cs @@ -1,7 +1,9 @@ namespace GeneXus.Application { using System; +#if !NETCORE using Jayrock.Json; +#endif using System.Collections.Generic; [Serializable] public class GXNavigationHelper diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRes.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRes.cs index 41967b215..d857504fb 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRes.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRes.cs @@ -7,7 +7,6 @@ namespace GeneXus.Resources using GeneXus.Configuration; using GeneXus.Utils; using System.IO; - using log4net; using GeneXus.Application; using System.Collections; using System.Text; @@ -20,7 +19,7 @@ namespace GeneXus.Resources public class GXResourceManager { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Resources.GXResourceManager)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); static ConcurrentDictionary _rm = new ConcurrentDictionary(); static ConcurrentDictionary _localUtils = new ConcurrentDictionary(); static string _defaultLangName; diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs index 170a3c5bf..295e266a0 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs @@ -45,7 +45,7 @@ public GXRestAPIClient() public string ResponseMessage { get => responseMessage; set => responseMessage = value; } public string HttpMethod { get => httpMethod; set => httpMethod = value; } - public string protocol = "REST"; + public int protocol = 1; private string httpMethod = "GET"; @@ -109,7 +109,7 @@ public void AddQueryVar(String varName, Geospatial varValue) public void AddQueryVar(String varName, bool varValue) { - _queryVars[varName] = varValue.ToString(); + _queryVars[varName] = StringUtil.BoolToStr(varValue); } public void AddQueryVar(String varName, GxUserType varValue) @@ -164,7 +164,7 @@ public void AddBodyVar(String varName, long varValue) } public void AddBodyVar(String varName, bool varValue) { - _bodyVars[varName] = varValue.ToString(); + _bodyVars[varName] = StringUtil.BoolToStr(varValue); } public void AddBodyVar(String varName, Guid varValue) { @@ -280,6 +280,7 @@ public string GetJsonStr(string varName) } else if (_responseData.Count >= 1) // can contain the same key (recursive unwrapped) { + #if NETCORE string rData = JsonSerializer.Serialize(_responseData); if (sdt.FromJSonString(rData, null)) @@ -370,6 +371,10 @@ public void RestExecute() httpClient.AddHeader("Content-Type", _contentType); } } + if (this.Location.AuthenticationMethod == 4 && !String.IsNullOrEmpty(this.Location.AccessToken)) + { + httpClient.AddHeader("Authorization", this.Location.AccessToken); + } string serviceuri = ((this.Location.Secure > 0) ? "https" : "http") + "://" + this.Location.Host; serviceuri += (this.Location.Port != 80) ? ":" + this.Location.Port.ToString() : String.Empty; serviceuri += "/" + this.Location.BaseUrl.TrimEnd('/').TrimStart('/') + "/" + this.Location.ResourceName; diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestUtils.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestUtils.cs index d5ec2a42d..ec16827f6 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestUtils.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestUtils.cs @@ -8,11 +8,11 @@ using System.Text.Json; using System.Text.Json.Serialization; #endif -using log4net; using System.IO; +#if !NETCORE using Jayrock.Json; - - +#endif +using GeneXus.Configuration; namespace GeneXus.Utils { @@ -97,7 +97,7 @@ public class UploadCachedFile internal class RestAPIHelpers { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.RestAPIHelpers)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public static Dictionary ReadRestParameters(string restData) { @@ -140,5 +140,10 @@ public static Dictionary ReadRestBodyParameters(Stream stream) } return bodyParameters; } + + internal static bool ValidateCsrfToken() + { + return Config.GetValueOf("CSRF_PROTECTION", Preferences.NO) == Preferences.YES; + } } } diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXUtilsGeospatial.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXUtilsGeospatial.cs index 259e61060..74e030fa4 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXUtilsGeospatial.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXUtilsGeospatial.cs @@ -1,19 +1,21 @@ using System; -using System.Globalization; -using System.Runtime.Serialization; using System.Collections; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; -using log4net; +#endif +using System.Data.SqlTypes; +using System.Globalization; using System.Reflection; +using System.Runtime.Serialization; using GeneXus.Metadata; -using System.Data.SqlTypes; -using System.IO; #if NETCORE using GxClasses.Helpers; #endif namespace GeneXus.Utils { - public interface IGeographicNative + public interface IGeographicNative { Object InnerValue @@ -33,7 +35,7 @@ int Srid class SQLGeographyWrapper { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.SQLGeographyWrapper)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); static Assembly _geoAssembly; public const string SqlGeographyClass = "Microsoft.SqlServer.Types.SqlGeography"; public const string SqlGeometryClass = "Microsoft.SqlServer.Types.SqlGeometry"; @@ -152,7 +154,7 @@ internal static object STGeomFromText(string geoText, int sRID) [DataContract] public class Geospatial : IGeographicNative { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.Geospatial)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); internal const string EMPTY_GEOMETRY = "GEOMETRYCOLLECTION EMPTY"; const string EMPTY_GEOGRAPHY = "GEOGRAPHY EMPTY"; diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GxObjectProperties.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GxObjectProperties.cs index 01e9634cc..3ff58bd25 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GxObjectProperties.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GxObjectProperties.cs @@ -10,7 +10,7 @@ namespace GeneXus.Application public class GxObjectProperties { private GxLocation location = null; - private string protocol = "rest"; + private int protocol = 1; private string errorMessage = String.Empty; private int errorCode = 0; private int statusCode = 0; @@ -19,7 +19,7 @@ public class GxObjectProperties public string ErrorMessage { get => errorMessage; set => errorMessage = value; } public int ErrorCode { get => errorCode; set => errorCode = value; } public int StatusCode { get => statusCode; set => statusCode = value; } - public string Protocol { get => protocol; set => protocol = value; } + public int Protocol { get => protocol; set => protocol = value; } } public class GxObjectsConfiguration { diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs index 8cebcb0b6..65d9c4789 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs @@ -1,7 +1,6 @@ using GeneXus.Application; using GeneXus.Configuration; using GeneXus.Utils; -using log4net; #if NETCORE using GxClasses.Helpers; using Microsoft.AspNetCore.Http; @@ -28,6 +27,7 @@ using Microsoft.Net.Http.Headers; using System.Net.Http; using System.Globalization; +using System.Linq; namespace GeneXus.Http { @@ -50,6 +50,8 @@ public class HttpHeader public static string XGXFILENAME = "x-gx-filename"; internal static string ACCEPT = "Accept"; internal static string TRANSFER_ENCODING = "Transfer-Encoding"; + internal static string X_CSRF_TOKEN_HEADER = "X-XSRF-TOKEN"; + internal static string X_CSRF_TOKEN_COOKIE = "XSRF-TOKEN"; } internal class HttpHeaderValue { @@ -71,10 +73,74 @@ public class WrappedJsonError [DataMember(Name = "error")] public HttpJsonError Error; } +#if NETCORE + internal static class CookiesHelper + { + static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(CookiesHelper).FullName); + + internal static void PopulateCookies(this HttpRequestMessage request, CookieContainer cookieContainer) + { + if (cookieContainer != null) + { + IEnumerable cookies = cookieContainer.GetCookies(); + if (cookies.Any()) + { + request.Headers.Add("Cookie", cookies.ToHeaderFormat()); + } + } + } + private static string ToHeaderFormat(this IEnumerable cookies) + { + return string.Join(";", cookies); + } + internal static void ExtractCookies(this HttpResponseMessage response, CookieContainer cookieContainer) + { + if (response.Headers.TryGetValues("Set-Cookie", out var cookieValues)) + { + Uri uri = response.RequestMessage.RequestUri; + foreach (string cookieValue in cookieValues) + { + try + { + cookieContainer.SetCookies(uri, cookieValue); + } + catch (CookieException ex) + { + GXLogging.Warn(log, $"Ignored cookie for container: {cookieValue} url:{uri}", ex); + } + } + + } + } + } +#endif public class HttpHelper { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Http.HttpHelper)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + + internal static Dictionary GAMServices = new Dictionary(){ + {"oauth/access_token","gxoauthaccesstoken.aspx"}, + {"oauth/logout","gxoauthlogout.aspx"}, + {"oauth/userinfo","gxoauthuserinfo.aspx"}, + {"oauth/gam/signin","agamextauthinput.aspx"}, + {"oauth/gam/callback","agamextauthinput.aspx"}, + {"oauth/gam/access_token","agamoauth20getaccesstoken.aspx"}, + {"oauth/gam/userinfo","agamoauth20getuserinfo.aspx"}, + {"oauth/gam/signout","agamextauthinput.aspx"}, + {"saml/gam/signin","Saml2/SignIn"}, + {"saml/gam/callback","gamexternalauthenticationinputsaml20_ws.aspx"}, + {"saml/gam/signout","Saml2/Logout"}, + {"oauth/requesttokenservice","agamstsauthappgetaccesstoken.aspx"}, + {"oauth/queryaccesstoken","agamstsauthappvalidaccesstoken.aspx"}, + {"oauth/gam/v2.0/access_token","agamoauth20getaccesstoken_v20.aspx"}, + {"oauth/gam/v2.0/userinfo","agamoauth20getuserinfo_v20.aspx"}, + {"oauth/gam/v2.0/requesttokenanduserinfo","agamssorequesttokenanduserinfo_v20.aspx"}}; + internal static HashSet GamServicesInternalName = new HashSet(GAMServices.Values.Select(value => value.Replace(ASPX, string.Empty))); + internal const string QUERYVIEWER_NAMESPACE = "QueryViewer.Services"; + internal const string GXFLOW_NSPACE = "GXflow.Programs"; + internal const string GAM_NSPACE = "GeneXus.Security.API"; + /* * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control * Specifying no-cache or max-age=0 indicates that @@ -92,7 +158,7 @@ public class HttpHelper const string GAM_CODE_TFA_USER_MUST_VALIDATE = "410"; const string GAM_CODE_TOKEN_EXPIRED = "103"; static Regex CapitalsToTitle = new Regex(@"(?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); - + internal const string InvalidCSRFToken = "InvalidCSRFToken"; const string CORS_MAX_AGE_SECONDS = "86400"; internal static void CorsHeaders(HttpContext httpContext) { @@ -285,10 +351,14 @@ internal static void TraceUnexpectedError(Exception ex) } internal static void SetUnexpectedError(HttpContext httpContext, HttpStatusCode statusCode, Exception ex) + { + string statusCodeDesc = StatusCodeToTitle(statusCode); + SetUnexpectedError(httpContext, statusCode, statusCodeDesc, ex); + } + internal static void SetUnexpectedError(HttpContext httpContext, HttpStatusCode statusCode, string statusCodeDesc, Exception ex) { TraceUnexpectedError(ex); string statusCodeStr = statusCode.ToString(INT_FORMAT); - string statusCodeDesc = StatusCodeToTitle(statusCode); SetResponseStatus(httpContext, statusCode, statusCodeDesc); SetJsonError(httpContext, statusCodeStr, statusCodeDesc); } diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/JSONHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/JSONHelper.cs index 663ff5a08..5cce38a9a 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/JSONHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/JSONHelper.cs @@ -1,19 +1,22 @@ using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization.Json; using System.Text; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; +#endif using System.Runtime.Serialization; using GeneXus.Configuration; -using System.Linq; #if NETCORE using System.Buffers.Text; using System.Text.Json; using System.Text.Json.Serialization; +using System.Text.Encodings.Web; +using System.Globalization; #endif -using log4net; namespace GeneXus.Utils { @@ -36,37 +39,80 @@ public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonS return JsonSerializer.Deserialize(ref reader, options); case JsonTokenType.StartObject: return JsonSerializer.Deserialize(ref reader, options); + case JsonTokenType.Number: + if (reader.TryGetInt32(out int l)) + return l; + else + if (reader.TryGetDecimal(out decimal d)) + return d; + else + return reader.GetDouble(); + case JsonTokenType.String: + return reader.GetString(); default: using (JsonDocument document = JsonDocument.ParseValue(ref reader)) { - return document.RootElement.Clone().ToString(); + return document.RootElement.Clone(); } } } - public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { throw new NotImplementedException(); } } + internal class CustomGeospatialConverter : JsonConverter + { + public override Geospatial Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + throw new NotImplementedException("Deserialization is not supported."); + + public override void Write(Utf8JsonWriter writer, Geospatial value, JsonSerializerOptions options) + { + string stringValue = value?.ToString(); + JsonSerializer.Serialize(writer, stringValue, options); + } + } + internal class CustomDateTimeConverter : JsonConverter + { + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException("Deserialization is not supported."); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(Convert.ToString(value, CultureInfo.InvariantCulture)); //"dd/MM/yyyy HH:mm:ss" + } + } internal class TextJsonSerializer : GXJsonSerializer { internal override bool IsJsonNull(object jobject) { return jobject == null; } + static JsonSerializerOptions DeserializationOptions = new JsonSerializerOptions() { Converters = { new GxJsonConverter() }, AllowTrailingCommas=true }; internal override T ReadJSON(string json) { - JsonSerializerOptions opts = new JsonSerializerOptions(); - opts.Converters.Add(new GxJsonConverter()); - return JsonSerializer.Deserialize(json, opts); + return JsonSerializer.Deserialize(json, DeserializationOptions); } internal override string WriteJSON(T kbObject) { - return JsonSerializer.Serialize(kbObject); + if (kbObject != null) + { + return kbObject.ToString(); + } + return null; + } + static JsonSerializerOptions JayrockCompatibleOptions = new JsonSerializerOptions() { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Converters = { new CustomDateTimeConverter() }, + NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals }; + internal static string SerializeToJayrockCompatibleJson(T value) where T : IJayrockCompatible + { + return JsonSerializer.Serialize(value, JayrockCompatibleOptions); } } -#endif +#else internal class JayRockJsonSerializer : GXJsonSerializer { internal override bool IsJsonNull(object jobject) @@ -88,6 +134,7 @@ internal override string WriteJSON(T kbObject) return null; } } +#endif internal enum GXJsonSerializerType { Utf8, @@ -97,8 +144,7 @@ internal enum GXJsonSerializerType internal abstract class GXJsonSerializer { private static GXJsonSerializer s_instance = null; - private static object syncRoot = new Object(); - static GXJsonSerializerType DefaultJSonSerializer= GXJsonSerializerType.Jayrock; + private static object syncRoot = new object(); internal static GXJsonSerializer Instance { get @@ -109,17 +155,11 @@ internal static GXJsonSerializer Instance { if (s_instance == null) { - switch (DefaultJSonSerializer) - { #if NETCORE - case GXJsonSerializerType.TextJson: - s_instance = new TextJsonSerializer(); - break; + s_instance = new TextJsonSerializer(); +#else + s_instance = new JayRockJsonSerializer(); #endif - default: - s_instance = new JayRockJsonSerializer(); - break; - } } } } @@ -137,7 +177,7 @@ internal static GXJsonSerializer Instance public class JSONHelper { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.JSONHelper)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); static string WFCDateTimeFormat = Preferences.WFCDateTimeMillis ? DateTimeUtil.JsonDateFormatMillis : DateTimeUtil.JsonDateFormat; public static bool IsJsonNull(object jobject) { @@ -159,7 +199,7 @@ public static T ReadJSON(string json, GXBaseCollection M } catch (Exception ex) { - GXUtil.ErrorToMessages("FromJson Error", ex, Messages); + GXUtil.ErrorToMessages("FromJson Error", ex, Messages, false); GXLogging.Error(log, "FromJsonError ", ex); return default(T); } @@ -181,7 +221,7 @@ public static T ReadJavascriptJSON(string json, GXBaseCollection(T kbObject, Encoding encoding, IEnumerable(T kbObject, Encoding encoding, IEnumerable(T kbObject, DataContractJsonSerializerSettings settings) where T : class + { + try + { + Encoding encoding = Encoding.UTF8; + DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), settings); + using (MemoryStream stream = new MemoryStream()) + { + serializer.WriteObject(stream, kbObject); + return encoding.GetString(stream.ToArray()); + } + } + catch (Exception ex) + { + GXLogging.Error(log, "Serialize error ", ex); + } + return null; + } internal static string WCFSerialize(T kbObject, Encoding encoding, IEnumerable knownTypes, bool useSimpleDictionaryFormat) where T : class { try @@ -279,12 +337,16 @@ static DataContractJsonSerializerSettings WCFSerializationSettings(IEnumerable(kbObject, encoding, knownTypes, new T()); } public static T Deserialize(string kbObject, Encoding encoding, IEnumerable knownTypes, T defaultValue) where T : class + { + var settings = SerializationSettings(knownTypes); + return Deserialize(kbObject, encoding, knownTypes, defaultValue, settings); + } + internal static T Deserialize(string kbObject, Encoding encoding, IEnumerable knownTypes, T defaultValue, DataContractJsonSerializerSettings settings) where T : class { if (!string.IsNullOrEmpty(kbObject)) { try { - var settings = SerializationSettings(knownTypes); using (MemoryStream stream = new MemoryStream(encoding.GetBytes(kbObject))) { DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), settings); @@ -302,14 +364,13 @@ public static T Deserialize(string kbObject, Encoding encoding, IEnumerable(string kbObject, Encoding encoding) where T : class, new() { - return Deserialize(kbObject, Encoding.Unicode, null, new T()); + return Deserialize(kbObject, encoding, null, new T()); } public static T Deserialize(string kbObject) where T : class, new() { return Deserialize(kbObject, Encoding.Unicode); } - public static T DeserializeNullDefaultValue(string kbObject) where T : class { return Deserialize(kbObject, Encoding.Unicode, null, null); diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/ThemeHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/ThemeHelper.cs index c5111af43..d1d33d772 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/ThemeHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/ThemeHelper.cs @@ -1,16 +1,15 @@ -using GeneXus.Application; -using GeneXus.Utils; -using log4net; using System; using System.Collections.Concurrent; using System.IO; using System.Runtime.Serialization; +using GeneXus.Application; +using GeneXus.Utils; namespace GeneXus.Helpers { public sealed class ThemeHelper { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Helpers.ThemeHelper)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static ConcurrentDictionary m_themes = new ConcurrentDictionary(); private static ThemeData CreateDefaultThemeData(string themeName) { diff --git a/dotnet/src/dotnetframework/GxClasses/Management/GXManagement.cs b/dotnet/src/dotnetframework/GxClasses/Management/GXManagement.cs index ed9aa8d42..3b2b69162 100644 --- a/dotnet/src/dotnetframework/GxClasses/Management/GXManagement.cs +++ b/dotnet/src/dotnetframework/GxClasses/Management/GXManagement.cs @@ -3,16 +3,14 @@ using System.ComponentModel; using System.Management.Instrumentation; using System.Reflection; +using System.Security; using GeneXus.Cache; using GeneXus.Configuration; using GeneXus.Data.ADO; using GeneXus.Utils; using GeneXus.XML; -using log4net; -using System.Security; -using System.Security.Permissions; -[assembly:Instrumented(@"root\GeneXus")] +[assembly: Instrumented(@"root\GeneXus")] namespace GeneXus.Management { [RunInstaller(true)] @@ -345,7 +343,7 @@ string LastConnectionId [SecuritySafeCritical] public class WMIApplicationServer : IWMIApplicationServer { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(WMIApplicationServer)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static WMIApplicationServer instance; private static object syncObj = new object(); public static WMIApplicationServer Instance() @@ -434,7 +432,8 @@ public void CleanUp() [SecuritySafeCritical] public class WMICache : IWMICache { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(WMICache)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + InProcessCache cache; Hashtable wmicacheItems; long maxCacheStorageSize; @@ -543,7 +542,7 @@ public void Remove(string key) [SecuritySafeCritical] public class WMICacheItem :IWMICacheItem { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(WMICacheItem)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); string stmt; ICacheItemExpiration itemExpiration; @@ -635,7 +634,7 @@ public void CleanUp() [SecuritySafeCritical] public class WMIConnection : IWMIConnection { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(WMIConnection)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); GxConnection connection; public WMIConnection(GxConnection connection) @@ -786,7 +785,7 @@ public void CleanUp() [SecuritySafeCritical] public class WMIDataSource : IWMIDataSource { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(WMIDataSource)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); GxDataStore dataSource; public WMIDataSource(GxDataStore dataSource) @@ -861,7 +860,7 @@ public void CleanUp() [SecuritySafeCritical] public class WMIServerUserInformation :IWMIServerUserInformation { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(WMIServerUserInformation)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); ServerUserInformation userInfo; string lastConnectionId; diff --git a/dotnet/src/dotnetframework/GxClasses/Management/GXPerformance.cs b/dotnet/src/dotnetframework/GxClasses/Management/GXPerformance.cs index 36d9d07c4..8d2f67e99 100644 --- a/dotnet/src/dotnetframework/GxClasses/Management/GXPerformance.cs +++ b/dotnet/src/dotnetframework/GxClasses/Management/GXPerformance.cs @@ -1,15 +1,14 @@ -using System.Management.Instrumentation; -using System.Collections; using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Management.Instrumentation; +using System.Security; using GeneXus.Data; using GeneXus.Data.ADO; using GeneXus.Data.NTier; using GeneXus.Data.NTier.ADO; using GeneXus.XML; -using System.Diagnostics; -using log4net; -using System.Security; -using System.Collections.Concurrent; namespace GeneXus.Performance { @@ -661,7 +660,7 @@ public void IncSentencesCount(ICursor cursor) [SecuritySafeCritical] public class WMIDataStoreProviders:WMIDataStoreProvidersBase { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(WMIDataStoreProviders)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private WMIDataStoreProviders() { @@ -735,7 +734,7 @@ public WMIDataStoreProvider(string datastoreName) : base() [SecuritySafeCritical] public class WMIDataStoreProviderBase { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(WMIDataStoreProviderBase)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private long statementCount; private long statementSelectCount; diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs index 8877eb7e2..cfe9ed0d5 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs @@ -20,12 +20,11 @@ namespace GeneXus.Http using GeneXus.Utils; using GeneXus.XML; using GeneXus.WebControls; - - using log4net; +#if !NETCORE using Jayrock.Json; +#endif using Helpers; using System.Collections.Concurrent; - using Microsoft.Net.Http.Headers; using System.Net.Http; #if NETCORE using Microsoft.AspNetCore.Http; @@ -39,24 +38,25 @@ namespace GeneXus.Http using System.Web; using System.Web.UI; using System.Web.UI.WebControls; - using System.Web.Script.Serialization; using System.Net; using GeneXus.Notifications; using Web.Security; using System.Web.SessionState; - using GeneXus.Mock; using GeneXus.Data.NTier; -#endif + using System.Security; +#endif + using System.Threading.Tasks; #if NETCORE public abstract class GXHttpHandler : GXBaseObject, IHttpHandler #else public abstract class GXHttpHandler : WebControl, IHttpHandler #endif { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Http.GXHttpHandler)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + internal const string GX_AJAX_REQUEST_HEADER = "GxAjaxRequest"; internal const string GX_SPA_GXOBJECT_RESPONSE_HEADER = "X-GXOBJECT"; internal const string GX_SPA_MASTERPAGE_HEADER = "X-SPA-MP"; @@ -231,16 +231,36 @@ private static bool IsNumericType(Type t) int _currParameter; #if NETCORE private GXWebRow _currentGridRow; -#endif + private Dictionary EventsMetadata = new Dictionary(); +#else private Hashtable EventsMetadata = new Hashtable(); +#endif +#if NETCORE + protected void setEventMetadata(string EventName, string Metadata) + { + if (EventsMetadata.ContainsKey(EventName)) + EventsMetadata[EventName] += Metadata; + else + EventsMetadata[EventName] = Metadata; + } + internal async Task WebExecuteExAsync(HttpContext httpContext) + { + if (IsUploadRequest(httpContext)) + new GXObjectUploadServices(context).webExecute(); + else if (IsFullAjaxRequest(httpContext)) + await WebAjaxEventAsync(); + else + await WebExecuteAsync(); + } +#else protected void setEventMetadata(string EventName, string Metadata) { if (EventsMetadata[EventName] == null) EventsMetadata[EventName] = string.Empty; EventsMetadata[EventName] += Metadata; } - +#endif public void webExecuteEx(HttpContext httpContext) { if (IsUploadRequest(httpContext)) @@ -276,6 +296,13 @@ private bool IsFullAjaxRequest(HttpContext httpContext) public virtual void InitializeDynEvents() { throw new Exception("The method or operation is not implemented."); } public virtual void initialize_properties() { throw new Exception("The method or operation is not implemented."); } public virtual void webExecute() { throw new Exception("The method or operation is not implemented."); } + protected virtual Task WebExecuteAsync() + { + GXLogging.Warn(log, this.GetType().FullName + " not generated as async service"); + webExecute(); + return Task.CompletedTask; + } + #if !NETCORE public virtual void initialize() { throw new Exception("The method or operation is not implemented."); } public virtual void cleanup() { } @@ -444,7 +471,7 @@ private void ParseInputJSonMessage(JObject objMessage, GXHttpHandler targetObj) } if (objMessage.Contains("fullPost")) { - this.targetObj._Context.httpAjaxContext.ParseGXState((Jayrock.Json.JObject)objMessage["fullPost"]); + this.targetObj._Context.httpAjaxContext.ParseGXState((JObject)objMessage["fullPost"]); } } private void ParseGridsDataParms(JObject gxGrids) @@ -524,7 +551,12 @@ private void ParseMetadata() int eventCount = 0; foreach (string eventName in events) { +#if NETCORE + + JObject eventMetadata = JSONHelper.ReadJSON(targetObj.EventsMetadata[eventName.ToString()]); +#else JObject eventMetadata = JSONHelper.ReadJSON((string)targetObj.EventsMetadata[eventName.ToString()]); +#endif eventHandlers[eventCount] = (string)eventMetadata["handler"]; JArray eventInputParms = (JArray)eventMetadata["iparms"]; foreach (JObject inputParm in eventInputParms) @@ -533,9 +565,12 @@ private void ParseMetadata() eventUseInternalParms[eventCount] = eventUseInternalParms[eventCount] || IsInternalParm(inputParm); } JArray eventOutputParms = (JArray)eventMetadata["oparms"]; - foreach (JObject outputParm in eventOutputParms) + if (eventOutputParms != null) { - AddParmsMetadata(outputParm, DynAjaxEventContext.outParmsMetadata, DynAjaxEventContext.outParmsMetadataHash); + foreach (JObject outputParm in eventOutputParms) + { + AddParmsMetadata(outputParm, DynAjaxEventContext.outParmsMetadata, DynAjaxEventContext.outParmsMetadataHash); + } } eventCount++; } @@ -884,7 +919,7 @@ private object[] BeforeInvoke() { try { - JObject hashObj = (JObject)(hash_i < inHashValues.Length ? inHashValues[hash_i] : new Jayrock.Json.JObject()); + JObject hashObj = (JObject)(hash_i < inHashValues.Length ? inHashValues[hash_i] : new JObject()); string sRow = hashObj.Contains("row") ? (string)hashObj["row"] : string.Empty; string hash = hashObj.Contains("hsh") ? (string)hashObj["hsh"] : string.Empty; SetScalarOrCollectionValue((string)parm["av"], inParmsValues[parm_i], columnValues); @@ -1030,6 +1065,40 @@ internal string Invoke(string JsonMessage, GXHttpHandler targetObj) return response; } } +#if NETCORE + internal virtual async Task WebAjaxEventAsync() + { + bool isMultipartRequest = context.IsMultipartRequest; + if (isMultipartRequest) + { + localHttpContext.Response.ContentType = MediaTypesNames.TextHtml; + } + else + { + localHttpContext.Response.ContentType = MediaTypesNames.ApplicationJson; + } + setAjaxCallMode(); + context.setFullAjaxMode(); + DynAjaxEvent dynAjaxEvent = new DynAjaxEvent(context.httpAjaxContext.DynAjaxEventContext); + string jsonRequest; + if (context.IsMultipartRequest) + jsonRequest = cgiGet(GX_AJAX_MULTIPART_ID); + else + { + using (StreamReader reader = new StreamReader(localHttpContext.Request.GetInputStream())) + { + jsonRequest = await reader.ReadToEndAsync(); ; + } + } + string jsonResponse = dynAjaxEvent.Invoke(jsonRequest, this); + + + if (!redirect(context)) + { + ((GxContext)context).SendFinalJSONResponse(jsonResponse); + } + } +#endif public virtual void webAjaxEvent() { @@ -1837,7 +1906,7 @@ private bool ValidateObjectAccess(String cmpCtx) SendResponseStatus(HttpStatusCode.Unauthorized); if (context.GetBrowserType() != GxContext.BROWSER_INDEXBOT) { - if (log.IsWarnEnabled) + if (log.IsWarningEnabled) { GXLogging.Warn(log, $"Validation security token '{GetObjectAccessWebToken(cmpCtx)}' failed for program: '{cmpCtx + this.GetPgmname().ToUpper()}'"); } @@ -1907,8 +1976,73 @@ public bool IsMain get { return _isMain; } } #endif +#if NETCORE + internal async Task ProcessRequestAsync(HttpContext httpContext) + { + localHttpContext = httpContext; + + if (IsSpaRequest() && !IsSpaSupported()) + { + this.SendResponseStatus(SPA_NOT_SUPPORTED_STATUS_CODE, "SPA not supported by the object"); + context.CloseConnections(); + await Task.CompletedTask; + } + ControlOutputWriter = new HtmlTextWriter(localHttpContext); + LoadParameters(localHttpContext.Request.QueryString.Value); + context.httpAjaxContext.GetAjaxEncryptionKey(); //Save encryption key in session + InitPrivates(); + try + { + SetStreaming(); + SendHeaders(); + string clientid = context.ClientID; //Send clientid cookie (before response HasStarted) if necessary, since UseResponseBuffering is not in .netcore3.0 + bool validSession = ValidWebSession(); + if (validSession && IntegratedSecurityEnabled) + validSession = ValidSession(); + if (validSession) + { + if (UseBigStack()) + { + Thread ts = new Thread(new ParameterizedThreadStart(webExecuteWorker)); + ts.Start(httpContext); + ts.Join(); + if (workerException != null) + throw workerException; + } + else + { + await WebExecuteExAsync(httpContext); + } + } + else + { + context.CloseConnections(); + if (IsGxAjaxRequest() || context.isAjaxRequest()) + context.DispatchAjaxCommands(); + } + SetCompression(httpContext); + context.ResponseCommited = true; + } + catch (Exception e) + { + try + { + context.CloseConnections(); + } + catch { } + { + Exception exceptionToHandle = e.InnerException ?? e; + handleException(exceptionToHandle.GetType().FullName, exceptionToHandle.Message, exceptionToHandle.StackTrace); + throw new Exception("GXApplication exception", e); + } + } + } +#endif +#if !NETCORE + [SecuritySafeCritical] +#endif public void ProcessRequest(HttpContext httpContext) { localHttpContext = httpContext; @@ -1936,6 +2070,9 @@ public void ProcessRequest(HttpContext httpContext) SetStreaming(); SendHeaders(); string clientid = context.ClientID; //Send clientid cookie (before response HasStarted) if necessary, since UseResponseBuffering is not in .netcore3.0 +#if !NETCORE + CSRFHelper.ValidateAntiforgery(httpContext); +#endif bool validSession = ValidWebSession(); if (validSession && IntegratedSecurityEnabled) @@ -1980,9 +2117,18 @@ public void ProcessRequest(HttpContext httpContext) context.CloseConnections(); } catch { } - Exception exceptionToHandle = e.InnerException ?? e; - handleException(exceptionToHandle.GetType().FullName, exceptionToHandle.Message, exceptionToHandle.StackTrace); - throw new Exception("GXApplication exception", e); +#if !NETCORE + if (CSRFHelper.HandleException(e, httpContext)) + { + GXLogging.Error(log, $"Validation of antiforgery failed", e); + } + else +#endif + { + Exception exceptionToHandle = e.InnerException ?? e; + handleException(exceptionToHandle.GetType().FullName, exceptionToHandle.Message, exceptionToHandle.StackTrace); + throw new Exception("GXApplication exception", e); + } } } protected virtual bool ChunkedStreaming() { return false; } @@ -2193,7 +2339,7 @@ private string GetGAMNotAuthorizedWebObject() private void SendHeaders() { sendCacheHeaders(); - GXLogging.DebugSanitized(log, "HttpHeaders: ", DumpHeaders(localHttpContext)); + GXLogging.DebugSanitized(log, "HttpHeaders: ", () => DumpHeaders(localHttpContext)); sendAdditionalHeaders(); HttpHelper.CorsHeaders(localHttpContext); HttpHelper.AllowHeader(localHttpContext, new List() { $"{HttpMethod.Get.Method},{HttpMethod.Post.Method}" }); @@ -2250,6 +2396,9 @@ protected virtual void sendSpaHeaders() private void webExecuteWorker(object target) { HttpContext httpContext = (HttpContext)target; +#if !NETCORE + HttpContext.Current = httpContext; +#endif try { webExecuteEx(httpContext); @@ -2274,7 +2423,11 @@ public bool IsReusable protected virtual void SetCompression(HttpContext httpContext) { +#if NETCORE if (CompressHtmlResponse()) +#else + if (CompressHtmlResponse() && httpContext.Response.BufferOutput) +#endif { GXUtil.SetGZip(httpContext); } diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs index 98ffd93e3..c13b10323 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs @@ -1,20 +1,19 @@ using System; using System.Collections.Generic; -using GeneXus.Configuration; -using System.Web; using System.IO; -using GeneXus.Utils; +using System.Net.Http; +using System.Reflection; +using System.Security; +using System.Web; +using System.Web.Configuration; +using System.Web.Hosting; +using System.Web.SessionState; using GeneXus.Application; +using GeneXus.Configuration; +using GeneXus.Utils; using ManagedFusion.Rewriter; -using System.Web.Hosting; -using ManagedFusion.Rewriter.Rules; using ManagedFusion.Rewriter.Engines; -using System.Reflection; -using log4net; -using System.Web.SessionState; -using System.Web.Configuration; -using System.Security; -using System.Net.Http; +using ManagedFusion.Rewriter.Rules; namespace GeneXus.Http.HttpModules { @@ -54,7 +53,7 @@ public class MapGroup public class GXAPIModule : IHttpModule { - + static private readonly object syncObject = new object(); public static List servicesPathUrl; public static Dictionary servicesBase; public static Dictionary servicesClass; @@ -86,6 +85,11 @@ private void onPostResolveRequestCache(object sender, EventArgs eventArgs) if (apiHandler != null) HttpContext.Current.RemapHandler(apiHandler); } + else if (string.Equals(HttpContext.Current.Request.HttpMethod, HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase) && + HttpContext.Current.Request.Path.EndsWith("/" + REST_BASE_URL, StringComparison.OrdinalIgnoreCase)) + { + CSRFHelper.ValidateAntiforgery(HttpContext.Current); + } } void IHttpModule.Dispose() { @@ -131,69 +135,77 @@ public static Boolean serviceInPath(String path, out String actualPath) public void ServicesGroupSetting(string webPath) { if (!String.IsNullOrEmpty(webPath) && servicesMap == null) - { - servicesPathUrl = new List(); - servicesBase = new Dictionary(); - servicesMap = new Dictionary>(); - //servicesVerbs = new Dictionary>(); - servicesMapData = new Dictionary, string>>(); - servicesClass = new Dictionary(); - - if (Directory.Exists(Path.Combine(webPath, PRIVATE_DIR))) + { + lock (syncObject) { - String[] grpFiles = Directory.GetFiles(Path.Combine(webPath, PRIVATE_DIR), "*.grp.json"); - foreach (String grp in grpFiles) + if (servicesMap == null) { -#pragma warning disable SCS0018 // Path traversal: injection possible in {1} argument passed to '{0}' - object p = JSONHelper.Deserialize(File.ReadAllText(grp)); -#pragma warning restore SCS0018 - MapGroup m = p as MapGroup; - if (m != null && m.Name != null && m.Mappings != null ) - { + servicesPathUrl = new List(); + servicesBase = new Dictionary(); + servicesMap = new Dictionary>(); + //servicesVerbs = new Dictionary>(); + servicesMapData = new Dictionary, string>>(); + servicesClass = new Dictionary(); - if (String.IsNullOrEmpty(m.BasePath)) - { - m.BasePath = REST_BASE_URL; - } - String mapPath = (m.BasePath.EndsWith("/")) ? m.BasePath : m.BasePath + "/"; - String mapPathLower = mapPath.ToLower(); - servicesPathUrl.Add(mapPathLower); - servicesBase.Add(mapPathLower, m.Name.ToLower()); - servicesClass.Add(mapPathLower, m.Name.ToLower() + "_services"); - foreach (SingleMap sm in m.Mappings) + if (Directory.Exists(Path.Combine(webPath, PRIVATE_DIR))) + { + String[] grpFiles = Directory.GetFiles(Path.Combine(webPath, PRIVATE_DIR), "*.grp.json"); + foreach (String grp in grpFiles) { - if (String.IsNullOrEmpty(sm.Verb)) - sm.Verb = "GET"; - if (sm.VariableAlias == null) - sm.VariableAlias = new Dictionary(); - else + string content = File.ReadAllText(grp); + if (!string.IsNullOrEmpty(content)) { - Dictionary vMap = new Dictionary(); - foreach (KeyValuePair v in sm.VariableAlias) +#pragma warning disable SCS0018 // Path traversal: injection possible in {1} argument passed to '{0}' + object p = JSONHelper.Deserialize(content); +#pragma warning restore SCS0018 + MapGroup m = p as MapGroup; + if (m != null && m.Name != null && m.Mappings != null) { - vMap.Add(v.Key.ToLower(), v.Value.ToLower()); - } - sm.VariableAlias = vMap; - } - if (servicesMap.ContainsKey(mapPathLower)) - { - if (!servicesMap[mapPathLower].ContainsKey(sm.Name.ToLower())) - { - servicesMapData[mapPathLower].Add(Tuple.Create(sm.Path.ToLower(), sm.Verb), sm.Name.ToLower()); - servicesMap[mapPathLower].Add(sm.Name.ToLower(), sm); + + if (String.IsNullOrEmpty(m.BasePath)) + { + m.BasePath = REST_BASE_URL; + } + String mapPath = (m.BasePath.EndsWith("/")) ? m.BasePath : m.BasePath + "/"; + String mapPathLower = mapPath.ToLower(); + servicesPathUrl.Add(mapPathLower); + servicesBase[mapPathLower]= m.Name.ToLower(); + servicesClass[mapPathLower]= m.Name.ToLower() + "_services"; + foreach (SingleMap sm in m.Mappings) + { + if (String.IsNullOrEmpty(sm.Verb)) + sm.Verb = "GET"; + if (sm.VariableAlias == null) + sm.VariableAlias = new Dictionary(); + else + { + Dictionary vMap = new Dictionary(); + foreach (KeyValuePair v in sm.VariableAlias) + { + vMap.Add(v.Key.ToLower(), v.Value.ToLower()); + } + sm.VariableAlias = vMap; + } + if (servicesMap.ContainsKey(mapPathLower)) + { + if (!servicesMap[mapPathLower].ContainsKey(sm.Name.ToLower())) + { + servicesMapData[mapPathLower].Add(Tuple.Create(sm.Path.ToLower(), sm.Verb), sm.Name.ToLower()); + servicesMap[mapPathLower].Add(sm.Name.ToLower(), sm); + } + } + else + { + servicesMapData.Add(mapPathLower, new Dictionary, string>()); + servicesMapData[mapPathLower].Add(Tuple.Create(sm.Path.ToLower(), sm.Verb), sm.Name.ToLower()); + servicesMap.Add(mapPathLower, new Dictionary()); + servicesMap[mapPathLower].Add(sm.Name.ToLower(), sm); + } + } } } - else - { - servicesMapData.Add(mapPathLower, new Dictionary, string>()); - servicesMapData[mapPathLower].Add(Tuple.Create(sm.Path.ToLower(), sm.Verb), sm.Name.ToLower()); - servicesMap.Add(mapPathLower, new Dictionary()); - servicesMap[mapPathLower].Add(sm.Name.ToLower(), sm); - } - } } - } } } @@ -201,7 +213,7 @@ public void ServicesGroupSetting(string webPath) } public class GXSessionModule : IHttpModule { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXSessionModule)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); HttpApplication App; const string ASPNETSESSION_COOKIE = "ASP.NET_SessionId"; string cookieName= ASPNETSESSION_COOKIE; @@ -306,7 +318,7 @@ void IHttpModule.Dispose() } public class GXRewriter : IHttpModule { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXRewriter)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static RewriterModule rewriter; private static bool moduleStarted; private static bool enabled; diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpServices.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpServices.cs index 5d7ef1e35..b9b993bc1 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpServices.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpServices.cs @@ -1,29 +1,21 @@ namespace GeneXus.Http { using System; - using System.Collections; - using System.Collections.Specialized; using System.Collections.Generic; - using System.Globalization; using System.IO; using System.Reflection; using System.Text; - using System.Text.RegularExpressions; - using System.Threading; using GeneXus.Application; using GeneXus.Configuration; using GeneXus.Data.NTier; - using GeneXus.Encryption; using GeneXus.Metadata; using GeneXus.Mime; using GeneXus.Security; using GeneXus.Utils; - using GeneXus.XML; - using GeneXus.WebControls; - - using log4net; +#if !NETCORE using Jayrock.Json; +#endif using System.Web.SessionState; using System.Web; #if NETCORE @@ -34,6 +26,8 @@ namespace GeneXus.Http using System.Linq; using GeneXus.Procedure; using GxClasses.Web.Middleware; + using Microsoft.AspNetCore.Hosting; + #else using System.Web.UI; @@ -144,7 +138,8 @@ public override void webExecute() public class GXReorServices : GXHttpHandler { - static readonly ILog log = LogManager.GetLogger(typeof(GXReorServices)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + static Assembly _reorAssembly; static Assembly _gxDataInitializationAssembly; readonly string[] reorArgs = { "-force", "-ignoreresume", "-nogui", "-noverifydatabaseschema" }; @@ -570,7 +565,7 @@ protected override GAMSecurityLevel IntegratedSecurityLevel internal class GXOAuthAccessToken : GXHttpHandler, IRequiresSessionState { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Http.GXOAuthAccessToken)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GXOAuthAccessToken() { diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs index b945d0df5..76d1410bb 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs @@ -1,30 +1,32 @@ using System; -using System.Web; -using System.Reflection; -using GeneXus.Configuration; using System.Collections.Generic; -using log4net; using System.IO; -using GeneXus.Application; -using GeneXus.Utils; -using GeneXus.Http.HttpModules; -using GeneXus.Metadata; using System.Net; -using System.Text.RegularExpressions; using System.Net.Http; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Web; +using GeneXus.Application; +using GeneXus.Configuration; using GeneXus.Http; +using GeneXus.Http.HttpModules; +using GeneXus.Metadata; +using GeneXus.Utils; namespace GeneXus.HttpHandlerFactory { internal class OptionsApiObjectRequestHandler : IHttpHandler { string actualPath; + string regexpPath; string objectName; - internal OptionsApiObjectRequestHandler(string path, string name) + internal OptionsApiObjectRequestHandler(string path, string name, string regexp) { actualPath = path; objectName = name; + regexpPath = regexp; } + public void ProcessRequest(HttpContext context) { // OPTIONS VERB @@ -32,7 +34,7 @@ public void ProcessRequest(HttpContext context) bool found = false; foreach (Tuple t in GXAPIModule.servicesMapData[actualPath].Keys) { - if (t.Item1.Equals(objectName.ToLower())) + if (t.Item1.Equals(objectName.ToLower()) || (GxRegex.IsMatch( t.Item1,regexpPath))) { mthheaders.Add(t.Item2); found = true; @@ -56,93 +58,98 @@ public bool IsReusable } class HandlerFactory : IHttpHandlerFactory { - private static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static List GxNamespaces; public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) { - IHttpHandler handlerToReturn; + IHttpHandler handlerToReturn; string relativeURL = context.Request.AppRelativeCurrentExecutionFilePath; string fname = relativeURL.Substring(relativeURL.LastIndexOf('~') + 2); - String cname1 = (fname.Contains(".")) ? fname.Substring(0, fname.LastIndexOf('.')) : fname; + string cname1 = (fname.Contains(".")) ? fname.Substring(0, fname.LastIndexOf('.')) : fname; string cname0 = cname1.ToLower(); - string actualPath = ""; - + string mainNamespace; + string assemblyName, cname; + string actualPath; if (cname0 == "gxoauthlogout") { - return new GeneXus.Http.GXOAuthLogout(); + return new GXOAuthLogout(); } else if (cname0 == "gxoauthuserinfo") { - return new GeneXus.Http.GXOAuthUserInfo(); + return new GXOAuthUserInfo(); } else if (cname0 == "gxoauthaccesstoken") { - return new GeneXus.Http.GXOAuthAccessToken(); + return new GXOAuthAccessToken(); } else if (cname0 == "gxmulticall") { - return new GeneXus.Http.GXMultiCall(); + return new GXMultiCall(); } - string assemblyName, cname; - if (GXAPIModule.serviceInPath(pathTranslated, actualPath: out actualPath)) + else if (HttpHelper.GamServicesInternalName.Contains(cname0)) { - string nspace; - Config.GetValueOf("AppMainNamespace", out nspace); - String objClass = GXAPIModule.servicesBase[actualPath]; - // - String objectName = GetObjFromPath(cname0, actualPath); - String objectNameUp = GetObjFromPath(cname1, actualPath); - // - Dictionary routeParms; - if (GXAPIModule.servicesMapData.ContainsKey(actualPath) && GetSMap(actualPath, objectName, objectNameUp, requestType, out string mapName, out routeParms)) + mainNamespace = HttpHelper.GAM_NSPACE; + } + else + { + if (!Config.GetValueOf("AppMainNamespace", out mainNamespace)) + mainNamespace = "GeneXus.Programs."; + + if (GXAPIModule.serviceInPath(pathTranslated, actualPath: out actualPath)) { - if (!String.IsNullOrEmpty(mapName) && GXAPIModule.servicesMap[actualPath].TryGetValue(mapName, out SingleMap value)) + string nspace; + Config.GetValueOf("AppMainNamespace", out nspace); + string objClass = GXAPIModule.servicesBase[actualPath]; + // + string objectName = GetObjFromPath(cname0, actualPath); + string objectNameUp = GetObjFromPath(cname1, actualPath); + // + Dictionary routeParms; + if (GXAPIModule.servicesMapData.ContainsKey(actualPath)) { - String tmpController = objClass; - String asssemblycontroller = tmpController; - if (objClass.Contains("\\")) + bool IsServiceCall = GetSMap(actualPath, objectName, objectNameUp, requestType, out string mapName, out string mapRegExp, out routeParms); + if (IsServiceCall) { - tmpController = objClass.Substring(objClass.LastIndexOf("\\") + 1); - String addNspace = objClass.Substring(0, objClass.LastIndexOf("\\")).Replace("\\", "."); - asssemblycontroller = addNspace + "." + tmpController; - nspace += "." + addNspace; - } - GxContext gxContext = GxContext.CreateDefaultInstance(); - object handler = ClassLoader.FindInstance(asssemblycontroller, nspace, tmpController, new Object[] { gxContext }, null); + if (!string.IsNullOrEmpty(mapName) && GXAPIModule.servicesMap[actualPath].TryGetValue(mapName, out SingleMap value)) + { + string tmpController = objClass; + string asssemblycontroller = tmpController; + if (objClass.Contains("\\")) + { + tmpController = objClass.Substring(objClass.LastIndexOf("\\") + 1); + string addNspace = objClass.Substring(0, objClass.LastIndexOf("\\")).Replace("\\", "."); + asssemblycontroller = addNspace + "." + tmpController; + nspace += "." + addNspace; + } + GxContext gxContext = GxContext.CreateDefaultInstance(); + object handler = ClassLoader.FindInstance(asssemblycontroller, nspace, tmpController, new Object[] { gxContext }, null); - gxContext.HttpContext = context; - GxRestWrapper restWrapper = new Application.GxRestWrapper(handler as GXBaseObject, context, gxContext, value.ServiceMethod, value.VariableAlias, routeParms); - return restWrapper; - } - } - else - { - if ( requestType.Equals(HttpMethod.Options.Method) && !String.IsNullOrEmpty(actualPath) && GXAPIModule.servicesMapData.ContainsKey(actualPath)) - { - return new OptionsApiObjectRequestHandler(actualPath, objectName); + gxContext.HttpContext = context; + GxRestWrapper restWrapper = new Application.GxRestWrapper(handler as GXBaseObject, context, gxContext, value.ServiceMethod, value.VariableAlias, routeParms); + return restWrapper; + } + } + else + { + if (requestType.Equals(HttpMethod.Options.Method) && !string.IsNullOrEmpty(actualPath) && GXAPIModule.servicesMapData.ContainsKey(actualPath)) + { + return new OptionsApiObjectRequestHandler(actualPath, objectName, mapRegExp); + } + } } + return null; } - return null; } - else + assemblyName = cname0; + cname = cname0; + if (cname.EndsWith("_bc_ws")) { - { - assemblyName = cname0; - cname = cname0; - } - if (cname.EndsWith("_bc_ws")) - { - cname = cname.Substring(0, cname.Length - 3); - assemblyName = cname; - } + cname = cname.Substring(0, cname.Length - 3); + assemblyName = cname; } - string mainNamespace, className; - if (Config.GetValueOf("AppMainNamespace", out mainNamespace)) - className = mainNamespace + "." + cname; - else - className = "GeneXus.Programs." + cname; + string className = mainNamespace + "." + cname; Type objType = GetHandlerType(assemblyName, className); if (objType == null) @@ -184,7 +191,7 @@ public IHttpHandler GetHandler(HttpContext context, string requestType, string u } else { - handlerToReturn = (IHttpHandler)System.Web.UI.PageParser.GetCompiledPageInstance(url, pathTranslated, context); + handlerToReturn = System.Web.UI.PageParser.GetCompiledPageInstance(url, pathTranslated, context); } return handlerToReturn; } @@ -193,34 +200,40 @@ public string GetObjFromPath(string cname, string apath) { if (cname.LastIndexOf("/") == (cname.Length - 1)) cname = cname.Substring(0, cname.Length - 1); - String objectName = cname.Remove(0, apath.Length); + string objectName = cname.Remove(0, apath.Length); return objectName; } - public bool GetSMap(string actualPath, string objectName, string objectNameUp, string requestType, out string mapName, out Dictionary routeParms) + public bool GetSMap(string actualPath, string objectName, string objectNameUp, string requestType, out string mapName, out string mapRegexp, out Dictionary routeParms) { routeParms = null; if (GXAPIModule.servicesMapData[actualPath].TryGetValue(Tuple.Create(objectName, requestType), out mapName)) - { + { + mapRegexp = mapName; return true; } else { + mapRegexp = mapName; foreach (SingleMap m in GXAPIModule.servicesMap[actualPath].Values) { if (!m.Path.Equals(m.PathRegexp) && GxRegex.IsMatch(objectName, m.PathRegexp)) { - mapName = m.Name; - routeParms = new Dictionary(); - int i=0; - foreach (string smatch in ((GxRegexMatch)GxRegex.Matches(objectNameUp, m.PathRegexp, RegexOptions.Multiline | RegexOptions.IgnoreCase)[0]).Groups) - { - string var = ((GxRegexMatch)GxRegex.Matches(m.Path, m.PathRegexp)[0]).Groups[i]; - var = var.Substring(1, var.Length -2); - routeParms.Add(var, smatch); - i++; + mapName = m.Name; + mapRegexp = m.PathRegexp; + if (m.Verb.Equals(requestType)) + { + routeParms = new Dictionary(); + int i = 0; + foreach (string smatch in ((GxRegexMatch)GxRegex.Matches(objectNameUp, m.PathRegexp, RegexOptions.Multiline | RegexOptions.IgnoreCase)[0]).Groups) + { + string var = ((GxRegexMatch)GxRegex.Matches(m.Path, m.PathRegexp)[0]).Groups[i]; + var = var.Substring(1, var.Length - 2); + routeParms.Add(var, smatch); + i++; + } + return true; } - return true; } } } @@ -290,7 +303,7 @@ internal static Type GetHandlerType(string assemblyName, string className) try { - objType = GeneXus.Metadata.ClassLoader.FindType(assemblyName, className, null); + objType = ClassLoader.FindType(assemblyName, className, null); if (objType == null) objType = Assembly.Load(assemblyName).GetType(className); } diff --git a/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs b/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs index df67abdf5..a3d87da7d 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs @@ -1,17 +1,24 @@ +using GeneXus.Configuration; using GeneXus.Data.NTier; using GeneXus.Encryption; using GeneXus.Http; using GeneXus.Mock; +#if NETCORE +using GeneXus.Services.OpenTelemetry; +#endif using GeneXus.Utils; +#if !NETCORE using Jayrock.Json; -using log4net; +#endif #if NETCORE using Microsoft.AspNetCore.Http.Extensions; #endif using System; using System.Collections.Generic; +using System.Diagnostics; using System.Reflection; using System.Threading; +using System.Threading.Tasks; namespace GeneXus.Application { @@ -22,11 +29,27 @@ public class GxObjectParameter } public class GXBaseObject { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXBaseObject)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + +#if NETCORE + internal static ActivitySource activitySource; +#endif private Dictionary callTargetsByObject = new Dictionary(); protected IGxContext _Context; bool _isMain; protected bool _isApi; + protected bool _isDP; +#if NETCORE + internal static ActivitySource ActivitySource { + get { + if (activitySource == null) + activitySource = new(OpenTelemetryService.GX_ACTIVITY_SOURCE_NAME); + return activitySource; + } + } +#endif + protected virtual bool GenOtelSpanEnabled() { return false; } + protected virtual void ExecuteEx() { if (GxMockProvider.Provider != null) @@ -35,12 +58,86 @@ protected virtual void ExecuteEx() if (GxMockProvider.Provider.Handle(_Context, this, parmInfo)) return; } - ExecutePrivate(); + ExecuteImpl(); } protected virtual void ExecutePrivate() { + + } +#if NETCORE + protected virtual bool AsyncEnabled { get; } + + internal bool GetAsyncEnabledInternal() + { + return AsyncEnabled; + } + protected async Task CloseConnectionsAsync() + { + GxContext gxContext = context as GxContext; + if (gxContext != null) + { + await gxContext.CloseConnectionsAsync(); + } + } + + protected virtual Task ExecutePrivateAsync() + { + return Task.CompletedTask; + } +#endif + internal static string GetObjectNameWithoutNamespace(string gxObjFullName) + { + string mainNamespace = Preferences.AppMainNamespace; + if (gxObjFullName.StartsWith(mainNamespace)) + gxObjFullName = gxObjFullName.Remove(0, mainNamespace.Length + 1); + return gxObjFullName; + } +#if NETCORE + private void ExecuteUsingSpanCode() + { + string gxObjFullName = GetObjectNameWithoutNamespace(GetType().FullName); + using (Activity activity = ActivitySource.StartActivity($"{gxObjFullName}.execute")) + { + ExecutePrivate(); + } + } + private async Task ExecuteUsingSpanCodeAsync() + { + string gxObjFullName = GetObjectNameWithoutNamespace(GetType().FullName); + using (Activity activity = ActivitySource.StartActivity($"{gxObjFullName}.execute")) + { + await ExecutePrivateAsync(); + } + } +#endif + protected virtual void ExecuteImpl() + { +#if NETCORE + if (GetAsyncEnabledInternal()) + { + ExecuteImplAsync().GetAwaiter().GetResult(); + } + else + { + if (GenOtelSpanEnabled()) + ExecuteUsingSpanCode(); + else + ExecutePrivate(); + } +#else + ExecutePrivate(); +#endif + } +#if NETCORE + protected virtual async Task ExecuteImplAsync() + { + if (GenOtelSpanEnabled()) + await ExecuteUsingSpanCodeAsync(); + else + await ExecutePrivateAsync(); } +#endif protected virtual void ExecutePrivateCatch(object stateInfo) { try @@ -127,14 +224,21 @@ public bool IsApiObject set { _isApi = value; } get { return _isApi; } } +#if NETCORE + protected virtual Task CleanupAsync() { + return Task.CompletedTask;// throw new NotImplementedException(); + } +#endif public virtual void cleanup() { } virtual public bool UploadEnabled() { return false; } public bool IntegratedSecurityEnabled2 { get { return IntegratedSecurityEnabled; } } public GAMSecurityLevel IntegratedSecurityLevel2 { get { return IntegratedSecurityLevel; } } + public bool IsSynchronizer2 { get { return IsSynchronizer; } } public string ExecutePermissionPrefix2 { get { return ExecutePermissionPrefix; } } - + internal string ApiExecutePermissionPrefix2(string gxMethod) { return ApiExecutePermissionPrefix(gxMethod); } + internal GAMSecurityLevel ApiIntegratedSecurityLevel2(string gxMethod) { return ApiIntegratedSecurityLevel(gxMethod); } public virtual string ServiceExecutePermissionPrefix { get { return string.Empty; } } public virtual string ServiceDeletePermissionPrefix { get { return string.Empty; } } public virtual string ServiceInsertPermissionPrefix { get { return string.Empty; } } @@ -144,12 +248,15 @@ public virtual void cleanup() { } protected virtual GAMSecurityLevel IntegratedSecurityLevel { get { return 0; } } protected virtual bool IsSynchronizer { get { return false; } } protected virtual string ExecutePermissionPrefix { get { return String.Empty; } } + + protected virtual string ApiExecutePermissionPrefix(string gxMethod) { return ExecutePermissionPrefix2; } + protected virtual GAMSecurityLevel ApiIntegratedSecurityLevel(string gxMethod) { return IntegratedSecurityLevel2; } public virtual void handleException(String gxExceptionType, String gxExceptionDetails, String gxExceptionStack) { } public virtual void CallWebObject(string url) { string target = GetCallTargetFromUrl(url); - if (String.IsNullOrEmpty(target)) + if (String.IsNullOrEmpty(target)) { context.wjLoc = url; } diff --git a/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs b/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs index 371405266..c64c26d10 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs @@ -6,11 +6,12 @@ namespace GeneXus.Utils using GeneXus.Application; using System.Xml.Serialization; using System.Collections.Generic; +#if !NETCORE using Jayrock.Json; +#endif using System.Text; using System.Security.Cryptography; using System.Reflection; - using log4net; using System.Runtime.Serialization; #if !NETCORE using System.ServiceModel; @@ -18,6 +19,7 @@ namespace GeneXus.Utils using Configuration; using System.Globalization; using GeneXus.Http; + using System.Diagnostics; public interface IGxSilentTrn { @@ -138,7 +140,7 @@ public msglist GX_msglist [Serializable] public class GxSilentTrnSdt : GxUserType { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.GxSilentTrnSdt)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GxSilentTrnSdt(IGxContext context){ this.context = context; @@ -160,6 +162,7 @@ public IGxSilentTrn Transaction get {return trn;} set {trn = value;} } + public IGxSilentTrn getTransaction() { return trn; @@ -169,30 +172,106 @@ public void setTransaction(IGxSilentTrn t) dirties.Clear(); trn = t; } +#if NETCORE + protected virtual bool GenOtelSpanEnabled() { return false; } + private Activity StartSpan(string methodName) + { + if (GenOtelSpanEnabled()) + { + return InitializeSpan(methodName); + } + return null; + } + private void EndSpan(Activity activity) + { + if (GenOtelSpanEnabled()) + { + activity?.Stop(); + } + } + private Activity InitializeSpan(string methodName) + { + if (GenOtelSpanEnabled()) + { + string gxObjFullName = GXBaseObject.GetObjectNameWithoutNamespace(GetType().FullName); + return GXBaseObject.ActivitySource.StartActivity($"{gxObjFullName}.{methodName}"); + } + return null; + } +#endif public virtual void Save() { - if( Transaction != null) + if (Transaction != null) + { +#if NETCORE + Activity activity = StartSpan("Save"); +#endif Transaction.Save(); +#if NETCORE + EndSpan(activity); +#endif + } } public virtual bool Insert() { - if (Transaction != null) - return Transaction.Insert(); - return false; + if (Transaction != null) + { +#if NETCORE + Activity activity = StartSpan("Insert"); +#endif + try + { + return Transaction.Insert(); + } + finally + { +#if NETCORE + EndSpan(activity); + +#endif + } + } + return false; } public virtual bool Update() { if (Transaction != null) - return Transaction.Update(); - return false; + { +#if NETCORE + Activity activity = StartSpan("Update"); +#endif + try + { + return Transaction.Update(); + } + finally { +#if NETCORE + EndSpan(activity); +#endif + } + } + return false; } - public virtual bool InsertOrUpdate() - { - if (Transaction != null) - return Transaction.InsertOrUpdate(); + public virtual bool InsertOrUpdate() + { + if (Transaction != null) + { +#if NETCORE + Activity activity = StartSpan("InsertOrUpdate"); +#endif + try + { + return Transaction.InsertOrUpdate(); + } + finally { +#if NETCORE + EndSpan(activity); +#endif + } + } return false; } @@ -205,8 +284,14 @@ public virtual void Delete( ) { if( Transaction != null) { +#if NETCORE + Activity activity = StartSpan("Delete"); +#endif Transaction.SetMode("DLT") ; Transaction.Save(); +#if NETCORE + EndSpan(activity); +#endif } } public virtual bool Success() @@ -498,6 +583,7 @@ public GXBaseCollection GetMessages() public interface IGxGenericCollectionWrapped { bool GetIsWrapped(); + string GetWrappedStatus(); void SetIsWrapped(bool value); } @@ -523,6 +609,12 @@ public GxGenericCollection(IGxCollection x, bool wrapped):this(x) isWrapped = wrapped; } + public GxGenericCollection(IGxCollection x, bool wrapped, string wrappedstatus) : this(x) + { + isWrapped = wrapped; + wrappedStatus = wrappedstatus; + } + public void LoadCollection(IGxCollection x) { foreach (IGxGenericCollectionItem x1 in this) @@ -543,6 +635,8 @@ public override string ToString() public bool GetIsWrapped() { + if (wrappedStatus.Equals("unwrapped")) + isWrapped = false; return isWrapped; } @@ -550,6 +644,18 @@ public void SetIsWrapped(bool value) { isWrapped = value; } + + private string wrappedStatus = ""; + + public string GetWrappedStatus() + { + return wrappedStatus; + } + + internal void SetWrappedStatus(string value) + { + wrappedStatus = value; + } } public interface IGxGenericCollectionItem { @@ -564,6 +670,17 @@ public GxUnWrappedJson() } } + [AttributeUsage(AttributeTargets.Class)] + public sealed class GxJsonSerialization : Attribute + { + string unwrapped = default; + public GxJsonSerialization(string jsonunwrapped) + { + unwrapped = jsonunwrapped; + } + public string JsonUnwrapped { get => unwrapped; set => unwrapped = value; } + } + [AttributeUsage(AttributeTargets.Class)] public sealed class GxOmitEmptyCollection : Attribute { @@ -751,6 +868,7 @@ public GxUpload() [XmlRoot(ElementName = "Message")] [XmlType(TypeName = "Message", Namespace = "GeneXus")] [Serializable] + [GxJsonSerialization("wrapped")] public class SdtMessages_Message : GxUserType { public SdtMessages_Message() @@ -853,6 +971,7 @@ public void initialize() [DataContract(Name = @"Messages.Message", Namespace = "GeneXus")] [GxOmitEmptyCollection] + [GxJsonSerialization("wrapped")] public class SdtMessages_Message_RESTInterface : GxGenericCollectionItem, System.Web.SessionState.IRequiresSessionState { public SdtMessages_Message_RESTInterface() diff --git a/dotnet/src/dotnetframework/GxClasses/Model/GXWebProcedure.cs b/dotnet/src/dotnetframework/GxClasses/Model/GXWebProcedure.cs index c7f9978d7..67ff7b635 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/GXWebProcedure.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/GXWebProcedure.cs @@ -5,20 +5,18 @@ namespace GeneXus.Procedure using GeneXus.Printer; using System.Globalization; using GeneXus.Http; - using System.Threading; using GeneXus.Mime; - using GeneXus.Utils; + using System.Net.Mime; #if NETCORE using Microsoft.AspNetCore.Http; + using System.Threading.Tasks; #else using System.Web; #endif - using log4net; - using GeneXus.Application; - using GeneXus.Data.NTier; public class GXWebProcedure : GXHttpHandler { + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); protected int handle; protected GXReportMetadata reportMetadata; @@ -26,6 +24,7 @@ public class GXWebProcedure : GXHttpHandler protected IReportHandler oldReportHandler; string outputFileName; string outputType; + bool fileContentInline; protected int lineHeight; protected int Gx_line; @@ -39,7 +38,14 @@ public class GXWebProcedure : GXHttpHandler protected virtual void printHeaders() { } protected virtual void printFooters() { } +#if NETCORE + public override void webExecute() + { + WebExecuteAsync().GetAwaiter().GetResult(); + } +#else public override void webExecute() { } +#endif public override void initialize() { } protected override void createObjects() { } public override void skipLines(long nToSkip) { } @@ -59,7 +65,7 @@ protected override void SetCompression(HttpContext httpContext) } } public void setContextReportHandler() - { + { oldReportHandler = null; reportHandler = context.reportHandler; @@ -129,18 +135,32 @@ protected override void sendCacheHeaders() private void setOuputFileName() { - string fileName = GetType().Name; - string fileType = "pdf"; - if (!string.IsNullOrEmpty(outputFileName)) + if (fileContentInline) { - fileName = outputFileName; - } - if (!string.IsNullOrEmpty(outputType)) - { - fileType = outputType.ToLower(); + string fileName = GetType().Name; + string fileType = "pdf"; + if (!string.IsNullOrEmpty(outputFileName)) + { + fileName = outputFileName; + } + if (!string.IsNullOrEmpty(outputType)) + { + fileType = outputType.ToLower(); + } + try + { + ContentDisposition contentDisposition = new ContentDisposition + { + Inline = true, + FileName = $"{fileName}.{fileType}" + }; + context.HttpContext.Response.AddHeader(HttpHeader.CONTENT_DISPOSITION, contentDisposition.ToString()); + } + catch (Exception ex) + { + GXLogging.Warn(log, $"{HttpHeader.CONTENT_DISPOSITION} couldn't be set for {fileName}.{fileType}", ex); + } } - - context.HttpContext.Response.AddHeader(HttpHeader.CONTENT_DISPOSITION, $"inline; filename={fileName}.{fileType}"); } public virtual int getOutputType() @@ -152,9 +172,10 @@ protected bool initPrinter(String output, int gxXPage, int gxYPage, String iniFi string idiom; if (!Config.GetValueOf("LANGUAGE", out idiom)) idiom = "eng"; -#if NETCORE + fileContentInline = true; + setOuputFileName(); -#endif + getPrinter().GxRVSetLanguage(idiom); int xPage = gxXPage; int yPage = gxYPage; diff --git a/dotnet/src/dotnetframework/GxClasses/Model/GxMockProvider.cs b/dotnet/src/dotnetframework/GxClasses/Model/GxMockProvider.cs index 166a67064..0cd5b3c23 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/GxMockProvider.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/GxMockProvider.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using GeneXus.Application; -using log4net; namespace GeneXus.Mock { public interface IGxMock @@ -9,7 +8,7 @@ public interface IGxMock } public class GxMockProvider { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxMockProvider)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static volatile IGxMock provider; diff --git a/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs b/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs index 7ec76f78e..088c2f917 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs @@ -14,7 +14,6 @@ namespace GeneXus.Procedure using GeneXus.Printer; using GeneXus.Utils; using GeneXus.XML; - using log4net; public abstract class GXProcedure: GXBaseObject { @@ -349,14 +348,97 @@ protected void initialize(int objClass, int objId, int dbgLines, long hash) protected void trkrng(int lineNro, int lineNro2) => dbgInfo?.TrkRng(lineNro, 0, lineNro2, 0); protected void trkrng(int lineNro, int colNro, int lineNro2, int colNro2) => dbgInfo?.TrkRng(lineNro, colNro, lineNro2, colNro2); } + public class GXDataGridProcedure : GXProcedure + { + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + const string HAS_NEXT_PAGE = "HasNextPage"; + const string RECORD_COUNT = "RecordCount"; + const string RECORD_COUNT_SUPPORTED = "RecordCountSupported"; + long totalRecordCount = -1; + protected virtual long RecordCount() + { + return -1; + } + protected virtual bool RecordCountSupported() + { + return true; + } + protected void SetPaginationHeaders(bool hasNextPage) + { + try + { + SetHasNextPageHeader(hasNextPage); + SetRecordCountSupportedHeader(); + } + catch (Exception ex) + { + GXLogging.Warn(log, $"A processing error occurred while setting pagination headers", ex); + } + } + private void SetRecordCountSupportedHeader() + { + if (!RecordCountSupported()) + { + GXLogging.Debug(log, $"Adding '{RECORD_COUNT_SUPPORTED}' header"); + context.SetHeader(RECORD_COUNT_SUPPORTED, false.ToString()); + } + } + + private void SetHasNextPageHeader(bool hasNextPage) + { + context.SetHeader(HAS_NEXT_PAGE, StringUtil.BoolToStr(hasNextPage)); + } + private void SetRecordCountHeader() + { + bool recordCountHeaderRequired = false; + bool setHeader = false; + if (context.HttpContext != null) + { + recordCountHeaderRequired = !string.IsNullOrEmpty(context.HttpContext.Request.Headers[RECORD_COUNT]); + } + if (totalRecordCount != -1) + { + setHeader = true; + } + else if (recordCountHeaderRequired) + { + totalRecordCount = RecordCount(); + setHeader = true; + } + if (setHeader) + { + GXLogging.Debug(log, $"Adding '{RECORD_COUNT}' header:", totalRecordCount.ToString()); + context.SetHeader(RECORD_COUNT, totalRecordCount.ToString()); + } + } + protected long GetPaginationStart(long start, long count) + { + if (start < 0) //last page + { + totalRecordCount = RecordCount(); + long lastPageRecords = totalRecordCount % count; + if (lastPageRecords == 0) + start = totalRecordCount - count; + else + start = totalRecordCount - lastPageRecords; + } + SetRecordCountHeader(); + return start; + } + } public class GxReportUtils { public static int OUTPUT_RVIEWER_NATIVE = 1; public static int OUTPUT_RVIEWER_DLL = 2; public static int OUTPUT_PDF = 3; - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + +#if NETCORE + const string PDF_LIBRARY_ITEXT8 = "ITEXT8"; + const string PDF_LIBRARY_PDFPIG = "PDFPIG"; +#endif static public IReportHandler GetPrinter( int outputType, string path, Stream reportOutputStream) { IReportHandler reportHandler; @@ -389,7 +471,14 @@ static public IReportHandler GetPrinter( int outputType, string path, Stream rep Type classType = assem.GetType( "GeneXus.Printer.GxReportBuilderPdf", false, true); reportHandler = (IReportHandler) Activator.CreateInstance(classType,new Object[]{path, reportOutputStream}); #else - reportHandler = (IReportHandler)(ClassLoader.FindInstance("GxPdfReportsCS", "GeneXus.Printer", "GxReportBuilderPdf", new Object[] { path, reportOutputStream }, null)); + string reportBuidler; + if (Preferences.PdfReportLibrary().Equals(PDF_LIBRARY_ITEXT8, StringComparison.OrdinalIgnoreCase)) + reportBuidler = "GxReportBuilderPdf8"; + else if (Preferences.PdfReportLibrary().Equals(PDF_LIBRARY_PDFPIG, StringComparison.OrdinalIgnoreCase)) + reportBuidler = "GxReportBuilderPDFPig"; + else + reportBuidler = "GxReportBuilderPdf"; + reportHandler = (IReportHandler)(ClassLoader.FindInstance("GxPdfReportsCS", "GeneXus.Printer", reportBuidler, new Object[] { path, reportOutputStream }, null)); #endif } catch (TargetInvocationException ex) diff --git a/dotnet/src/dotnetframework/GxClasses/Notifications/WebSocket/GXWebSocketFactory.cs b/dotnet/src/dotnetframework/GxClasses/Notifications/WebSocket/GXWebSocketFactory.cs index b35f4dc50..16e075739 100644 --- a/dotnet/src/dotnetframework/GxClasses/Notifications/WebSocket/GXWebSocketFactory.cs +++ b/dotnet/src/dotnetframework/GxClasses/Notifications/WebSocket/GXWebSocketFactory.cs @@ -1,14 +1,12 @@ using System; using GeneXus.Http.WebSocket; -using log4net; namespace GeneXus.Notifications.WebSocket { public class GXWebSocketFactory { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXWebSocketFactory)); - - public static IGXWebSocketAsync GetWebSocketProvider() + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + public static IGXWebSocketAsync GetWebSocketProvider() { IGXWebSocketAsync ws = null; try diff --git a/dotnet/src/dotnetframework/GxClasses/Printer/GxPrinter.cs b/dotnet/src/dotnetframework/GxClasses/Printer/GxPrinter.cs index 45b9b3989..8c3044799 100644 --- a/dotnet/src/dotnetframework/GxClasses/Printer/GxPrinter.cs +++ b/dotnet/src/dotnetframework/GxClasses/Printer/GxPrinter.cs @@ -1,24 +1,23 @@ namespace GeneXus.Printer { using System; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; + using System.Collections; + using System.Collections.Generic; + using System.Collections.Specialized; using System.Drawing; - using System.Drawing.Printing; using System.Drawing.Imaging; + using System.Drawing.Printing; + using System.Globalization; using System.IO; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Text; using System.Text.RegularExpressions; - using System.Collections.Specialized; - using System.Collections.Generic; - using System.Collections; using System.Threading; - using System.Globalization; - using System.Text; - using GeneXus.Configuration; - using GeneXus.XML; - using log4net; - using System.Runtime.Serialization; using System.Threading.Tasks; + using GeneXus.Configuration; + using GeneXus.Utils; + using GeneXus.XML; public interface IPrintHandler { @@ -1116,7 +1115,7 @@ float convertY( float y) } public class GxTxtPrinter : IPrintHandler { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxTxtPrinter)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); StreamReader streamToRead; StreamWriter streamToWrite; List pageContents; @@ -2147,11 +2146,6 @@ public void setOutputStream(object o) { } [Serializable()] public class ProcessInterruptedException: Exception { - public ProcessInterruptedException(SerializationInfo info, StreamingContext ctx) - : base(info, ctx) - { - - } public ProcessInterruptedException() { @@ -2169,10 +2163,19 @@ public class ReportUtils { static public string AddPath(string name, string path) { - if (Path.IsPathRooted(name) || name.IndexOf(":") != -1 || + if (name.IndexOf(":") != -1 || (name.Length >=2 && (name.Substring( 0,2) == "//" || name.Substring( 0,2) == @"\\")) || (name.StartsWith("http:" ) || name.StartsWith("https:" ))) return name; +#if NETCORE + if (Path.IsPathRooted (name)) + return name; +#else + if (PathUtil.IsValidFilePath(name) && Path.IsPathRooted(name)) + { + return name; + } +#endif return Path.Combine(path, name); } } diff --git a/dotnet/src/dotnetframework/GxClasses/Reorg/GXReorg.cs b/dotnet/src/dotnetframework/GxClasses/Reorg/GXReorg.cs index e2c0bba8f..1fa54132c 100644 --- a/dotnet/src/dotnetframework/GxClasses/Reorg/GXReorg.cs +++ b/dotnet/src/dotnetframework/GxClasses/Reorg/GXReorg.cs @@ -1,21 +1,20 @@ -namespace GeneXus.Reorg +namespace GeneXus.Reorg { using System; - using System.IO; using System.Collections; - using GeneXus.Application; - using log4net; using System.Collections.Specialized; - using System.Runtime.CompilerServices; - using GeneXus.Metadata; + using System.IO; + using System.Reflection; + using System.Runtime.CompilerServices; using System.Threading; + using GeneXus.Application; using GeneXus.Configuration; - using GeneXus.Data.ADO; using GeneXus.Data; + using GeneXus.Data.ADO; + using GeneXus.Data.NTier; + using GeneXus.Metadata; using GeneXus.Resources; using GeneXus.Utils; - using GeneXus.Data.NTier; - using System.Reflection; public interface IReorgReader { @@ -31,7 +30,7 @@ public interface IReorgReader public class GXReorganization { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Reorg.GXReorganization)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public virtual void initialize() { } public virtual void cleanup() { } static ArrayList lstMsgs; @@ -58,7 +57,11 @@ protected virtual void ExecutePrivate() { } - public IGxContext context + protected virtual void ExecuteImpl() + { + ExecutePrivate(); + } + public IGxContext context { get { return _Context; } set { _Context = value; } diff --git a/dotnet/src/dotnetframework/GxClasses/Security/GxSecurityProvider.cs b/dotnet/src/dotnetframework/GxClasses/Security/GxSecurityProvider.cs index fc6d125bf..2f6b276a3 100644 --- a/dotnet/src/dotnetframework/GxClasses/Security/GxSecurityProvider.cs +++ b/dotnet/src/dotnetframework/GxClasses/Security/GxSecurityProvider.cs @@ -1,11 +1,8 @@ +using System; +using System.Collections.Generic; using GeneXus.Application; using GeneXus.Configuration; using GeneXus.Metadata; -using log4net; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; namespace GeneXus.Security { public class GxResult @@ -35,7 +32,7 @@ public interface ISecurityProvider } public class GxSecurityProvider { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Security.GxSecurityProvider)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static volatile ISecurityProvider provider; private static object syncRoot = new Object(); diff --git a/dotnet/src/dotnetframework/GxClasses/Security/WebSecurity.cs b/dotnet/src/dotnetframework/GxClasses/Security/WebSecurity.cs index 8199d0488..27eb82564 100644 --- a/dotnet/src/dotnetframework/GxClasses/Security/WebSecurity.cs +++ b/dotnet/src/dotnetframework/GxClasses/Security/WebSecurity.cs @@ -1,8 +1,3 @@ -using GeneXus.Application; -using GeneXus.Configuration; -using GeneXus.Utils; -using log4net; -using Microsoft.IdentityModel.Tokens; using System; using System.IdentityModel.Tokens.Jwt; using System.Linq; @@ -10,6 +5,10 @@ using System.Security; using System.Security.Claims; using System.Text; +using GeneXus.Application; +using GeneXus.Configuration; +using GeneXus.Utils; +using Microsoft.IdentityModel.Tokens; using static GeneXus.Web.Security.SecureTokenHelper; namespace GeneXus.Web.Security @@ -17,14 +16,15 @@ namespace GeneXus.Web.Security [SecuritySafeCritical] public static class WebSecurityHelper { - private static readonly ILog _log = LogManager.GetLogger(typeof(GeneXus.Web.Security.WebSecurityHelper)); - const int SecretKeyMinimumLength = 16; + static readonly IGXLogger _log = GXLoggerFactory.GetLogger(typeof(WebSecurityHelper).FullName); + + const int SecretKeyMinimumLength = 32; public static string StripInvalidChars(string input) { if (string.IsNullOrEmpty(input)) return input; - var output = new string(input.Where(c => !char.IsControl(c)).ToArray()); + string output = new string(input.Where(c => !char.IsControl(c)).ToArray()); return output.Trim(); } @@ -111,9 +111,10 @@ internal static bool VerifySecureSignedSDTToken(string cmpCtx, GxUserType value, [SecuritySafeCritical] public static class SecureTokenHelper { - private static readonly ILog _log = LogManager.GetLogger(typeof(GeneXus.Web.Security.SecureTokenHelper)); + + static readonly IGXLogger _log = GXLoggerFactory.GetLogger(typeof(SecureTokenHelper).FullName); - public enum SecurityMode + public enum SecurityMode { Sign, SignEncrypt, @@ -128,6 +129,10 @@ internal static WebSecureToken getWebSecureToken(string signedToken, string secr using (var hmac = new System.Security.Cryptography.HMACSHA256(bSecretKey)) { var handler = new JwtSecurityTokenHandler(); + if (signedToken.Length >= handler.MaximumTokenSizeInBytes) + { + handler.MaximumTokenSizeInBytes = signedToken.Length + 1; + } var validationParameters = new TokenValidationParameters { ClockSkew = TimeSpan.FromMinutes(1), diff --git a/dotnet/src/dotnetframework/GxClasses/Services/Caching/GxCache.cs b/dotnet/src/dotnetframework/GxClasses/Services/Caching/GxCache.cs index d186d0d91..63f293597 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/Caching/GxCache.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/Caching/GxCache.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Specialized; using System.Xml; -using log4net; using System.Threading; using System.Globalization; using GeneXus.Management; @@ -21,7 +20,7 @@ #endif namespace GeneXus.Cache { - internal delegate void AddDataHandler(string key, + internal delegate void AddDataHandler(string key, ICacheItemExpiration expiration); [Obsolete("Not for public use. Replaced by ICacheService2", false)] @@ -95,8 +94,9 @@ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOp public class CacheFactory { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Cache.CacheFactory)); - public static string CACHE_SD = "SD"; + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + + public static string CACHE_SD = "SD"; public static string CACHE_DB = "DB"; public static string CACHE_FILES = "FL"; public static string FORCE_HIGHEST_TIME_TO_LIVE = "FORCE_HIGHEST_TIME_TO_LIVE"; @@ -175,7 +175,7 @@ public static bool ForceHighestTimetoLive } public sealed class InProcessCache : ICacheService2 { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Cache.InProcessCache)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); ICacheStorage cacheStorage; IScavengingAlgorithm storageScavengingImplementation; @@ -587,7 +587,13 @@ public class CacheItem public CacheItem() { } - + internal CacheItem(List data, bool hasnext, int blockSize, long sizeInBytes) + { + Data = new GxArrayList(data); + HasNext = hasnext; + BlockSize = blockSize; + SizeInBytes = sizeInBytes; + } public CacheItem(GxArrayList data, bool hasnext, int blockSize, long sizeInBytes) { Data = data; @@ -644,7 +650,7 @@ public interface ICacheStorage public class SingletonCacheStorage : ICacheStorage { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Cache.SingletonCacheStorage)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private HybridDictionary cacheStorage = new HybridDictionary(); private long size; @@ -715,8 +721,9 @@ void ICacheStorage.Update(string key, object keyData) public class LruScavenging : IScavengingAlgorithm { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Cache.LruScavenging)); - private HybridDictionary itemsLastUsed; + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + + private HybridDictionary itemsLastUsed; private ICacheService cachingService; private ICacheStorage cacheStorage; private ICacheMetadata cacheMetadata; diff --git a/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs b/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs index df1ac4b32..7f63cc340 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs @@ -5,8 +5,8 @@ using System.IO; using System.Net; using System.Net.Http; -using System.Runtime.Serialization; using System.Runtime.Serialization.Json; +using System.Security; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Configuration; @@ -15,13 +15,13 @@ using System.ServiceModel.Web; using System.Text; using System.Web; +using System.Web.Mvc; using GeneXus.Application; using GeneXus.Configuration; using GeneXus.Data; using GeneXus.Http; using GeneXus.Metadata; using GeneXus.Security; -using log4net; using Microsoft.Net.Http.Headers; namespace GeneXus.Utils @@ -103,7 +103,7 @@ internal class CorrelationState } class CustomOperationSelector : WebHttpDispatchOperationSelector { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(CustomOperationSelector)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public CustomOperationSelector(ServiceEndpoint endpoint) : base(endpoint) { } protected override string SelectOperation(ref Message message, out bool uriMatched) { @@ -227,12 +227,14 @@ public override object ConvertStringToValue(string parameter, Type parameterType } public class GxRestService : System.Web.SessionState.IRequiresSessionState { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GxRestService)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + internal const string WARNING_HEADER = "Warning"; protected IGxContext context; private HttpContext httpContext; private WebOperationContext wcfContext; protected string permissionPrefix; + protected string permissionMethod; bool runAsMain = true; public GxRestService() @@ -391,11 +393,21 @@ public void WebException(Exception ex) { HttpHelper.SetUnexpectedError(httpContext, HttpStatusCode.BadRequest, ex); } + else if (RestAPIHelpers.ValidateCsrfToken() && AntiForgeryException(ex)) + { + HttpHelper.SetUnexpectedError(httpContext, HttpStatusCode.BadRequest, HttpHelper.InvalidCSRFToken, ex); + } else { HttpHelper.SetUnexpectedError(httpContext, HttpStatusCode.InternalServerError, ex); } } + [SecuritySafeCritical] + private bool AntiForgeryException(Exception ex) + { + return ex is HttpAntiForgeryException; + } + public bool IsAuthenticated(string synchronizer) { GXLogging.Debug(log, "IsMainAuthenticated synchronizer:" + synchronizer); @@ -448,7 +460,6 @@ private bool IsAuthenticated(GAMSecurityLevel objIntegratedSecurityLevel, bool o } else { - token = token.Replace("OAuth ", ""); if (objIntegratedSecurityLevel == GAMSecurityLevel.SecurityLow) { @@ -545,9 +556,11 @@ void AddHeader(string header, string value) httpContext.Response.Headers[header]= value; } } + [SecuritySafeCritical] public bool ProcessHeaders(string queryId) { - + CSRFHelper.ValidateAntiforgery(context.HttpContext); + NameValueCollection headers = GetHeaders(); String language = null, theme = null, etag = null; if (headers != null) @@ -586,6 +599,8 @@ public bool ProcessHeaders(string queryId) return true; } + + private void SendCacheHeaders() { if (string.IsNullOrEmpty(context.GetHeader(HttpHeader.CACHE_CONTROL))) diff --git a/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs b/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs index fdaee8dbd..2baba5a70 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Text; -using log4net; #if NETCORE using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; @@ -25,11 +24,14 @@ using System.Collections.Specialized; using GeneXus.Security; using System.Collections; +#if !NETCORE using Jayrock.Json; -using Microsoft.Net.Http.Headers; +#endif using System.Net.Http; using System.Diagnostics; using GeneXus.Diagnostics; +using System.Xml.Linq; + namespace GeneXus.Application @@ -51,7 +53,7 @@ public class GxRestWrapper public class GxRestWrapper : IHttpHandler, IRequiresSessionState #endif { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Application.GxRestWrapper)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); protected HttpContext _httpContext; protected IGxContext _gxContext; private GXBaseObject _procWorker; @@ -110,7 +112,7 @@ public virtual Task MethodBodyExecute(object key) if (!IsAuthenticated(synchronizer)) return Task.CompletedTask; } - else if (!IsAuthenticated()) + else if (!IsAuthenticatedMethod(this._serviceMethod, _procWorker.IsApiObject)) { return Task.CompletedTask; } @@ -120,7 +122,7 @@ public virtual Task MethodBodyExecute(object key) gxobject.webExecute(); return Task.CompletedTask; } - if (!ProcessHeaders(_procWorker.GetType().Name)) + if (!ProcessHeaders(GXBaseObject.GetObjectNameWithoutNamespace(_procWorker.GetType().FullName))) return Task.CompletedTask; _procWorker.IsMain = true; if (bodyParameters == null) @@ -135,15 +137,16 @@ public virtual Task MethodBodyExecute(object key) if (!String.IsNullOrEmpty(this._serviceMethod)) { innerMethod = this._serviceMethod; - bodyParameters = PreProcessApiSdtParameter(_procWorker, innerMethod, bodyParameters, this._variableAlias); - } + bodyParameters = PreProcessApiSdtParameter( _procWorker, innerMethod, bodyParameters, this._variableAlias); + } + ServiceHeaders(); Dictionary outputParameters = ReflectionHelper.CallMethod(_procWorker, innerMethod, bodyParameters, _gxContext); Dictionary formatParameters = ReflectionHelper.ParametersFormat(_procWorker, innerMethod); setWorkerStatus(_procWorker); _procWorker.cleanup(); + int originalParameterCount = outputParameters.Count; RestProcess(_procWorker, outputParameters); - wrapped = GetWrappedStatus(_procWorker, wrapped, outputParameters, outputParameters.Count); - ServiceHeaders(); + wrapped = GetWrappedStatus(_procWorker, wrapped, outputParameters, outputParameters.Count, originalParameterCount); return Serialize(outputParameters, formatParameters, wrapped); } catch (Exception e) @@ -300,11 +303,11 @@ public virtual Task MethodUrlExecute(object key) { try { - if (!IsAuthenticated()) + if (!IsAuthenticatedMethod(this._serviceMethod, _procWorker.IsApiObject)) { return Task.CompletedTask; } - if (!ProcessHeaders(_procWorker.GetType().Name)) + if (!ProcessHeaders(GXBaseObject.GetObjectNameWithoutNamespace(_procWorker.GetType().FullName))) return Task.CompletedTask; _procWorker.IsMain = true; IDictionary queryParameters = ReadQueryParameters(this._variableAlias); @@ -312,6 +315,7 @@ public virtual Task MethodUrlExecute(object key) string innerMethod = EXECUTE_METHOD; Dictionary outputParameters; Dictionary formatParameters = new Dictionary(); + ServiceHeaders(); if (!string.IsNullOrEmpty(_serviceMethodPattern)) { innerMethod = _serviceMethodPattern; @@ -329,10 +333,10 @@ public virtual Task MethodUrlExecute(object key) int parCount = outputParameters.Count; setWorkerStatus(_procWorker); _procWorker.cleanup(); + int originalParameterCount = outputParameters.Count; RestProcess(_procWorker, outputParameters); bool wrapped = false; - wrapped = GetWrappedStatus(_procWorker, wrapped, outputParameters, parCount); - ServiceHeaders(); + wrapped = GetWrappedStatus(_procWorker, wrapped, outputParameters, parCount, originalParameterCount); return Serialize(outputParameters, formatParameters, wrapped); } catch (Exception e) @@ -344,29 +348,61 @@ public virtual Task MethodUrlExecute(object key) Cleanup(); } } - bool GetWrappedStatus(GXBaseObject worker, bool wrapped, Dictionary outputParameters, int parCount) + private bool GetWrappedStatus(GXBaseObject worker, bool defaultWrapped, Dictionary outputParameters, int parCount, int originalParCount) { + bool wrapped = defaultWrapped; + if (worker.IsApiObject) { if (outputParameters.Count == 1) { - wrapped = false; - Object v = outputParameters.First().Value; + if ((originalParCount == 1) || (originalParCount > 1 && !Preferences.WrapSingleApiOutput)) + { + wrapped = GetCollectionWrappedStatus(outputParameters, parCount, false, true); + } + if (originalParCount > 1 && Preferences.WrapSingleApiOutput) + { + wrapped = true; //Ignore defaultWrapped parameter. + } + } + } + else + { + if (originalParCount == 1) + wrapped = GetCollectionWrappedStatus(outputParameters, parCount, wrapped, false); + } + return wrapped; + } + - if (v.GetType().GetInterfaces().Contains(typeof(IGxGenericCollectionWrapped))) + private bool GetCollectionWrappedStatus(Dictionary outputParameters , int parCount, bool defaultWrapped, bool isAPI) + { + bool wrapped = defaultWrapped; + if (outputParameters.Count > 0) + { + Object v = outputParameters.First().Value; + if (v.GetType().GetInterfaces().Contains(typeof(IGxGenericCollectionWrapped))) + { + IGxGenericCollectionWrapped icollwrapped = v as IGxGenericCollectionWrapped; + if (icollwrapped != null) { - IGxGenericCollectionWrapped icollwrapped = v as IGxGenericCollectionWrapped; - if (icollwrapped != null) + if (icollwrapped.GetWrappedStatus().Equals("default")) + wrapped = defaultWrapped; + else wrapped = icollwrapped.GetIsWrapped(); } + } + + if (isAPI) + { if (v is IGxGenericCollectionItem item) { if (item.Sdt is GxSilentTrnSdt) { - wrapped = (parCount>1)?true:false; + wrapped = (parCount > 1) ? true : false; } } - } + } } return wrapped; } @@ -537,9 +573,19 @@ public bool IsAuthenticated(string synchronizer) SetError("0", "Invalid Synchronizer " + synchronizer); } } + protected bool IsAuthenticatedMethod(string serviceMethod, bool isApi) + { + if (!String.IsNullOrEmpty(serviceMethod) && isApi) + { + bool integratedSecurityEnabled = ( Worker.IntegratedSecurityEnabled2 && Worker.ApiIntegratedSecurityLevel2(serviceMethod) != GAMSecurityLevel.SecurityNone); + return IsAuthenticated(Worker.ApiIntegratedSecurityLevel2(serviceMethod), integratedSecurityEnabled, Worker.ApiExecutePermissionPrefix2(serviceMethod)); + } + else + return IsAuthenticated(); + } public bool IsAuthenticated() { - return IsAuthenticated(Worker.IntegratedSecurityLevel2, Worker.IntegratedSecurityEnabled2, Worker.ExecutePermissionPrefix2); + return IsAuthenticated( Worker.IntegratedSecurityLevel2, Worker.IntegratedSecurityEnabled2, Worker.ExecutePermissionPrefix2); } protected bool IsAuthenticated(GAMSecurityLevel objIntegratedSecurityLevel, bool objIntegratedSecurityEnabled, string objPermissionPrefix) { @@ -557,7 +603,6 @@ protected bool IsAuthenticated(GAMSecurityLevel objIntegratedSecurityLevel, bool } else { - token = token.Replace("OAuth ", ""); if (objIntegratedSecurityLevel == GAMSecurityLevel.SecurityLow) { @@ -700,7 +745,7 @@ string dateTimeToHTMLDate(DateTime dt) } public Task WebException(Exception ex) { -#if NETCORE +#if NETCORE GxHttpActivitySourceHelper.SetException(Activity.Current, ex); #endif GXLogging.Error(log, "WebException", ex); @@ -787,8 +832,10 @@ protected Task Serialize(object value) #else var responseStream = _httpContext.Response.OutputStream; #endif - var knownTypes = new List(); - knownTypes.Add(value.GetType()); + var knownTypes = new List + { + value.GetType() + }; JSONHelper.WCFSerialize(value, Encoding.UTF8, knownTypes, responseStream); return Task.CompletedTask; @@ -826,40 +873,70 @@ private static void RestProcess(GXBaseObject worker, Dictionary } } - protected static object MakeRestType( object v, bool isApiObject) + protected static object MakeRestType( object collectionValue, bool isApiObject) { - Type vType = v.GetType(); + Type vType = collectionValue.GetType(); Type itemType; if (vType.IsConstructedGenericType && typeof(IGxCollection).IsAssignableFrom(vType)) - { + { + bool isWrapped = (isApiObject)?false:true; + bool isEmpty = false; + object collectionObject = null; + string wrappedStatus = ""; Type restItemType=null; - itemType = v.GetType().GetGenericArguments()[0]; - if ((typeof(IGXBCCollection).IsAssignableFrom(vType)) && !isApiObject)//Collection convert to GxGenericCollection + itemType = collectionValue.GetType().GetGenericArguments()[0]; + if (vType.GetGenericTypeDefinition() == typeof(GxSimpleCollection<>) && isApiObject) { - restItemType = ClassLoader.FindType(Config.CommonAssemblyName, itemType.FullName + "_RESTLInterface", null); + restItemType = itemType; + isEmpty = true; + isWrapped = false; + collectionObject = collectionValue; } - if (restItemType == null)//Collection convert to GxGenericCollection + else { - restItemType = ClassLoader.FindType(Config.CommonAssemblyName, itemType.FullName + "_RESTInterface", null); + if ((typeof(IGXBCCollection).IsAssignableFrom(vType)) && !isApiObject)//Collection convert to GxGenericCollection + { + restItemType = ClassLoader.FindType(Config.CommonAssemblyName, itemType.FullName + "_RESTLInterface", null); + } + else //Collection convert to GxGenericCollection + { + if (typeof(GxUserType).IsAssignableFrom(itemType)){ + restItemType = ClassLoader.FindType(Config.CommonAssemblyName, itemType.FullName + "_RESTInterface", null); + } + else + { + collectionObject = collectionValue; + } + } + if (restItemType != null) + { + object[] attributes = restItemType.GetCustomAttributes(typeof(GxJsonSerialization), false); + IEnumerable serializationAttributes = attributes.Where(a => a.GetType() == typeof(GxJsonSerialization)); + if (serializationAttributes != null && serializationAttributes.Any()) + { + GxJsonSerialization attFmt = (GxJsonSerialization)serializationAttributes.FirstOrDefault(); + wrappedStatus = attFmt.JsonUnwrapped; + isWrapped = (isApiObject) ? ((wrappedStatus == "wrapped") ? true : false) : ((wrappedStatus == "unwrapped") ? false : true); + } + isEmpty = !restItemType.IsDefined(typeof(GxOmitEmptyCollection), false); + Type genericListItemType = typeof(GxGenericCollection<>).MakeGenericType(restItemType); + collectionObject = Activator.CreateInstance(genericListItemType, new object[] { collectionValue, isWrapped, wrappedStatus }); + } } - bool isWrapped = !restItemType.IsDefined(typeof(GxUnWrappedJson), false); - bool isEmpty = !restItemType.IsDefined(typeof(GxOmitEmptyCollection), false); - Type genericListItemType = typeof(GxGenericCollection<>).MakeGenericType(restItemType); - object c = Activator.CreateInstance(genericListItemType, new object[] { v, isWrapped}); // Empty collection serialized w/ noproperty - if (c is IList restList) + if (collectionObject is IList restList) { if (restList.Count == 0 && !isEmpty) return null; } - return c; + return collectionObject; } else if (typeof(GxUserType).IsAssignableFrom(vType)) //SDTType convert to SDTType_RESTInterface { Type restItemType = ClassLoader.FindType(Config.CommonAssemblyName, vType.FullName + "_RESTInterface", null); - return Activator.CreateInstance(restItemType, new object[] { v }); + return Activator.CreateInstance(restItemType, new object[] { collectionValue }); } - return v; + return collectionValue; } #if !NETCORE diff --git a/dotnet/src/dotnetframework/GxClasses/Services/ReflectionHelper.cs b/dotnet/src/dotnetframework/GxClasses/Services/ReflectionHelper.cs index a7a9a1b34..8d4c98aef 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/ReflectionHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/ReflectionHelper.cs @@ -7,7 +7,9 @@ using System.Text.RegularExpressions; using GeneXus.Utils; using System.Linq; +#if !NETCORE using Jayrock.Json; +#endif using Type = System.Type; @@ -150,7 +152,7 @@ private static object ConvertSingleJsonItem(object value, Type newType, IGxConte else if (newType == typeof(DateTime)) { string jsonDate = value as string; - return DateTimeUtil.CToDT2(jsonDate); + return DateTimeUtil.CToDT2(jsonDate, context); } else if (newType == typeof(Geospatial)) { @@ -179,9 +181,9 @@ private static object ConvertStringToNewNonNullableType(object value, Type newTy Type singleItemType = newType.GetElementType(); var elements = new ArrayList(); - foreach (var element in value.ToString().Split(',')) + foreach (string element in value.ToString().Split(',')) { - var convertedSingleItem = ConvertSingleJsonItem(element, singleItemType, context); + object convertedSingleItem = ConvertSingleJsonItem(element, singleItemType, context); elements.Add(convertedSingleItem); } return elements.ToArray(singleItemType); @@ -211,11 +213,11 @@ public static Dictionary ParametersFormat(object instance, strin var methodParameters = methodInfo.GetParameters(); foreach (var methodParameter in methodParameters) { - var gxParameterName = GxParameterName(methodParameter.Name); + string gxParameterName = GxParameterName(methodParameter.Name); if (IsByRefParameter(methodParameter)) { string fmt = ""; - var attributes = methodParameter.GetCustomAttributes(true); + object[] attributes = methodParameter.GetCustomAttributes(true); GxJsonFormatAttribute attFmt = (GxJsonFormatAttribute)attributes.Where(a => a.GetType() == typeof(GxJsonFormatAttribute)).FirstOrDefault(); if (attFmt != null) fmt = attFmt.JsonFormat; @@ -233,7 +235,7 @@ private static Dictionary ProcessParametersAfterInvoke(MethodInf int idx = 0; foreach (var methodParameter in methodParameters) { - var gxParameterName = GxParameterName(methodParameter.Name); + string gxParameterName = GxParameterName(methodParameter); if (IsByRefParameter(methodParameter)) { outputParameters.Add(gxParameterName, parametersForInvocation[idx]); @@ -250,18 +252,25 @@ internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IDict { var methodParameters = methodInfo.GetParameters(); object[] parametersForInvocation = new object[methodParameters.Length]; - var idx = 0; + int idx = 0; foreach (var methodParameter in methodParameters) { object value; - var gxParameterName = GxParameterName(methodParameter.Name).ToLower(); + string gxParameterName = GxParameterName(methodParameter.Name).ToLower(); Type parmType = methodParameter.ParameterType; + string jsontypename = ""; + object[] attributes = parmType.GetCustomAttributes(true); + GxJsonName jsonName = (GxJsonName)attributes.Where(a => a.GetType() == typeof(GxJsonName)).FirstOrDefault(); + if (jsonName != null) + jsontypename = jsonName.Name.ToLower(); + else + jsontypename = gxParameterName; if (IsByRefParameter(methodParameter)) { parmType = parmType.GetElementType(); } - if (parameters != null && parameters.TryGetValue(gxParameterName, out value)) + if (parameters != null && parameters.TryGetValue(jsontypename, out value)) { if (value == null || JSONHelper.IsJsonNull(value)) { @@ -269,13 +278,13 @@ internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IDict } else { - var convertedValue = ConvertStringToNewType(value, parmType, context); + object convertedValue = ConvertStringToNewType(value, parmType, context); parametersForInvocation[idx] = convertedValue; } } else { - var defaultValue = CreateInstance(parmType); + object defaultValue = CreateInstance(parmType); parametersForInvocation[idx] = defaultValue; } idx++; @@ -287,7 +296,7 @@ internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IList { var methodParameters = methodInfo.GetParameters(); object[] parametersForInvocation = new object[methodParameters.Length]; - var idx = 0; + int idx = 0; foreach (var methodParameter in methodParameters) { Type parmType = methodParameter.ParameterType; @@ -301,7 +310,7 @@ internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IList //To avoid convertion from string type if (value.GetType() != typeof(string)) { - var convertedValue = ConvertStringToNewType(value, parmType, context); + object convertedValue = ConvertStringToNewType(value, parmType, context); parametersForInvocation[idx] = convertedValue; } else @@ -318,12 +327,34 @@ internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IList return parametersForInvocation; } private static Regex attVar = new Regex(@"^AV?\d{1,}", RegexOptions.Compiled); + + private static string GxParameterName(ParameterInfo methodParameter) + { + int idx = methodParameter.Name.IndexOf('_'); + if (idx >= 0) + { + string mparm = methodParameter.Name.Substring(idx + 1); + string PName = methodParameter.ParameterType.FullName; + // The root element name should be in the metadata of the SDT + if (mparm.StartsWith("Gx") && mparm.EndsWith("rootcol") && PName.Contains("_")) + { + int Pos = PName.IndexOf("Sdt") + 3; + mparm = PName.Substring(Pos, PName.IndexOf("_") - Pos); + } + return mparm; + } + else + { + return attVar.Replace(methodParameter.Name, string.Empty); + } + } + private static string GxParameterName(string methodParameterName) { int idx = methodParameterName.IndexOf('_'); if (idx >= 0) { - return methodParameterName.Substring(idx + 1); + return methodParameterName.Substring(idx + 1); } else { @@ -335,7 +366,7 @@ private static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IList< { var methodParameters = methodInfo.GetParameters(); object[] parametersForInvocation = new object[methodParameters.Length]; - var idx = 0; + int idx = 0; string pattern = @"(AV\d+)(.*)"; string replacement = "$2"; Regex rgx = new Regex(pattern); @@ -344,7 +375,7 @@ private static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IList< { rgx.Replace(methodParameter.Name, replacement); Type parmType = methodParameter.ParameterType; - var convertedValue = ConvertStringToNewType(parametersValues[idx], parmType); + object convertedValue = ConvertStringToNewType(parametersValues[idx], parmType); parametersForInvocation[idx] = convertedValue; idx++; } diff --git a/dotnet/src/dotnetframework/GxClasses/Services/Search/GXSearch.cs b/dotnet/src/dotnetframework/GxClasses/Services/Search/GXSearch.cs index ed83471b8..70c2a49a9 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/Search/GXSearch.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/Search/GXSearch.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Text; using GeneXus.Utils; +#if !NETCORE using Jayrock.Json; +#endif using GeneXus.Application; using System.Reflection; using GeneXus.Metadata; @@ -169,12 +171,16 @@ public class SearchResultItem : ISearchResultItem, IGxJSONAble { #region Internal Data - protected JObject _Properties; + JObject _Properties; protected float m_score; #endregion + public SearchResultItem() + { + _Properties = new JObject(); + } -#region ISearchResultItem Members + #region ISearchResultItem Members public virtual string Id { get { return ""; } } public virtual string Viewer { get { return ""; } } diff --git a/dotnet/src/dotnetframework/GxClasses/Services/Storage/ExternalProviderBase.cs b/dotnet/src/dotnetframework/GxClasses/Services/Storage/ExternalProviderBase.cs index 0a2a6dc20..e45905029 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/Storage/ExternalProviderBase.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/Storage/ExternalProviderBase.cs @@ -1,7 +1,6 @@ using System; using System.IO; using GeneXus.Encryption; -using log4net; #if NETCORE using GeneXus.Mime; #else @@ -12,14 +11,14 @@ namespace GeneXus.Services { public abstract class ExternalProviderBase { - static readonly ILog logger = log4net.LogManager.GetLogger(typeof(ExternalProviderBase)); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); private GXService service; protected static String DEFAULT_ACL = "DEFAULT_ACL"; protected static String DEFAULT_EXPIRATION = "DEFAULT_EXPIRATION"; protected static String FOLDER = "FOLDER_NAME"; - protected static String DEFAULT_ACL_DEPRECATED = "STORAGE_PROVIDER_DEFAULT_ACL"; + protected static String DEFAULT_STORAGE_PRIVACY = "STORAGE_PROVIDER_DEFAULT_ACL"; protected static String DEFAULT_EXPIRATION_DEPRECATED = "STORAGE_PROVIDER_DEFAULT_EXPIRATION"; protected TimeSpan defaultExpiration = new TimeSpan(24, 0, 0); protected static string DEFAULT_TMP_CONTENT_TYPE = "image/jpeg"; @@ -41,7 +40,7 @@ public ExternalProviderBase(GXService s) s = ServiceFactory.GetGXServices()?.Get(GXServices.STORAGE_SERVICE); } catch (Exception) { - logger.Warn("STORAGE_SERVICE is not activated in CloudServices.config"); + GXLogging.Warn(logger, "STORAGE_SERVICE is not activated in CloudServices.config"); } } @@ -53,7 +52,7 @@ public ExternalProviderBase(GXService s) private void Initialize() { - String aclS = GetPropertyValue(DEFAULT_ACL, DEFAULT_ACL_DEPRECATED, ""); + String aclS = GetPropertyValue(DEFAULT_ACL, DEFAULT_STORAGE_PRIVACY, ""); if (!String.IsNullOrEmpty(aclS)) { this.defaultAcl = aclS.Equals("Private") ? GxFileType.Private : GxFileType.PublicRead; @@ -82,7 +81,7 @@ protected String GetEncryptedPropertyValue(String propertyName, String alternati if (value == null) { String errorMessage = String.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); - logger.Fatal(errorMessage); + GXLogging.Critical(logger, errorMessage); throw new Exception(errorMessage); } return value; @@ -102,7 +101,7 @@ protected String GetEncryptedPropertyValue(String propertyName, String alternati } catch (Exception) { - logger.Warn($"Could not decrypt property name: {ResolvePropertyName(propertyName)}"); + GXLogging.Warn(logger, $"Could not decrypt property name: {ResolvePropertyName(propertyName)}"); } } return value; @@ -114,7 +113,7 @@ protected String GetPropertyValue(String propertyName, String alternativePropert if (value == null) { String errorMessage = String.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); - logger.Fatal(errorMessage); + GXLogging.Critical(logger, errorMessage); throw new Exception(errorMessage); } return value; diff --git a/dotnet/src/dotnetframework/GxClasses/Services/Storage/GXServices.cs b/dotnet/src/dotnetframework/GxClasses/Services/Storage/GXServices.cs index 5d0879298..fc59e7b5c 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/Storage/GXServices.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/Storage/GXServices.cs @@ -1,21 +1,17 @@ -using GeneXus.Application; -using GeneXus.Utils; -using GeneXus.XML; -using log4net; using System; -using System.Linq; using System.Collections.Generic; using System.IO; -using System.Reflection; +using GeneXus.Application; +using GeneXus.Utils; +using GeneXus.XML; using GxClasses.Helpers; -using System.Reflection.Emit; - namespace GeneXus.Services { public class GXServices { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Services.GXServices)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + public static string STORAGE_SERVICE = "Storage"; public static string STORAGE_APISERVICE = "StorageAPI"; public static string CACHE_SERVICE = "Cache"; @@ -24,12 +20,12 @@ public class GXServices public static string WEBNOTIFICATIONS_SERVICE = "WebNotifications"; public static string QUEUE_SERVICE = "QueueService"; public static string MESSAGEBROKER_SERVICE = "MessageBrokerService"; + public static string EVENTROUTER_SERVICE = "EventRouterService"; private static string[] SERVICES_FILE = new string[] { "CloudServices.dev.config", "CloudServices.config" }; [System.Diagnostics.CodeAnalysis.SuppressMessage("GxFxCopRules", "CR1000:EnforceThreadSafeType")] private Dictionary services = new Dictionary(); private static GXServices s_instance = null; private static object syncRoot = new Object(); - public static GXServices Instance { get @@ -51,7 +47,6 @@ public static GXServices Instance } set { } } - public void AddService(string name, GXService service) { services[name] = service; @@ -66,7 +61,7 @@ public static void LoadFromFile(string fileName, ref GXServices services) { if (services == null) services = new GXServices(); - GXLogging.Debug(log, "Loading service:", filePath); + GXXMLReader reader = new GXXMLReader(); reader.Open(filePath); reader.ReadType(1, "Services"); @@ -177,8 +172,7 @@ public class GXService public class ServiceFactory { private static ExternalProvider externalProvider = null; - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Services.ServiceFactory)); - + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public static GXServices GetGXServices() { return GXServices.Instance; diff --git a/dotnet/src/dotnetframework/GxClasses/View/GXWebControls.cs b/dotnet/src/dotnetframework/GxClasses/View/GXWebControls.cs index 434bb2bf6..2ca0bd23d 100644 --- a/dotnet/src/dotnetframework/GxClasses/View/GXWebControls.cs +++ b/dotnet/src/dotnetframework/GxClasses/View/GXWebControls.cs @@ -1,5 +1,4 @@ using System; -using System.Web; using System.Drawing; #if !NETCORE using System.Web.UI; @@ -14,15 +13,15 @@ using System.Collections.Specialized; using GeneXus.Utils; using System.Text.RegularExpressions; +#if !NETCORE using Jayrock.Json; -using System.IO; +#endif using GeneXus.Configuration; using GeneXus.Application; -using log4net; namespace GeneXus.WebControls { - public class GXWebStdMethods + public class GXWebStdMethods { private static Type webStdType; @@ -326,7 +325,7 @@ public int GetWrapped() return 0; } - public List GetColsPropsCommon() + internal List GetColsPropsCommon() { return this._ColsPropsCommon; } @@ -711,7 +710,7 @@ public void FromJSONObject(dynamic obj) public abstract class GXWebControl : IGxJSONAble, IGxJSONSerializable { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXWebControl)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); bool ForeColorFlag; bool BackColorFlag; Color _ForeColor; @@ -736,7 +735,7 @@ public abstract class GXWebControl : IGxJSONAble, IGxJSONSerializable Unit _Height = Unit.Empty; ListDictionary _attributes = new ListDictionary(); ListDictionary _styleAttributes = new ListDictionary(); - protected JObject jsonObj = new JObject(); + JObject jsonObj = new JObject(); string _WebTags; static Regex rAttributes = new Regex("\\s*(?\\S*)\\s*=\\s*\"(?[^\"]*)\"", RegexOptions.IgnoreCase | RegexOptions.Compiled); @@ -755,7 +754,16 @@ public GXWebControl() _ID = ""; _title = new GxWebControlTitle(); } - public string CssClass + protected void PutJsonValue(string key, object value) + { + jsonObj.Put(key, value); + } + protected void ClearJsonValues() + { + jsonObj = new JObject(); + } + + public string CssClass { get { @@ -1417,7 +1425,7 @@ public void Render(HtmlTextWriter ControlOutputWriter) public override void ToJSON() { - jsonObj.Put(Value, Value); + PutJsonValue(Value, Value); } } @@ -1528,10 +1536,11 @@ public void removeAllItems() { _IsSet = true; Items.Clear(); - jsonObj = new JObject(); + ClearJsonValues(); _SelectedIndex = -1; } - public void removeItem(string itemValue) + + public void removeItem(string itemValue) { _IsSet = true; foreach (ListItem item in Items) @@ -1628,8 +1637,8 @@ protected virtual void ApplyProperties(ListControl control) public override void ToJSON() { - jsonObj.Put("isset", _IsSet); - jsonObj.Put("s", SelectedItemValue.Trim()); + PutJsonValue("isset", _IsSet); + PutJsonValue("s", SelectedItemValue.Trim()); JArray jsonArrValues = new JArray(); Dictionary itemsHash = new Dictionary(); foreach (ListItem Item in Items) @@ -1648,7 +1657,7 @@ public override void ToJSON() itemsHash[itemValue][1] = Item.Text; } } - jsonObj.Put("v", jsonArrValues); + PutJsonValue("v", jsonArrValues); } public override void FromJSONObject(dynamic Obj) @@ -1732,7 +1741,7 @@ public void Render(HtmlTextWriter ControlOutputWriter) public override void ToJSON() { - jsonObj.Put(CheckedValue, Checked); + PutJsonValue(CheckedValue, Checked); } } diff --git a/dotnet/src/dotnetframework/GxDataInitialization/GXDataInitialization.cs b/dotnet/src/dotnetframework/GxDataInitialization/GXDataInitialization.cs index a6a5291ed..19296078f 100644 --- a/dotnet/src/dotnetframework/GxDataInitialization/GXDataInitialization.cs +++ b/dotnet/src/dotnetframework/GxDataInitialization/GXDataInitialization.cs @@ -1,18 +1,14 @@ -using GeneXus.Application; -using GeneXus.Configuration; -using GeneXus.Data.NTier; -using GeneXus.Metadata; -using GeneXus.Procedure; -using GeneXus.Reorg; -using GeneXus.Resources; -using log4net; using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Reflection; using System.Text; using System.Xml.Serialization; +using GeneXus.Application; +using GeneXus.Configuration; +using GeneXus.Metadata; +using GeneXus.Procedure; +using GeneXus.Resources; namespace GeneXus.Utils { @@ -26,7 +22,7 @@ public DynTrnInitializer() { } public class GXDataInitialization : GXProcedure { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXDataInitialization)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public GXDataInitialization() { context = GxContext.CreateDefaultInstance(); diff --git a/dotnet/src/dotnetframework/GxDataInitialization/GXDataInitialization.csproj b/dotnet/src/dotnetframework/GxDataInitialization/GXDataInitialization.csproj index a54201c6f..6cb86a0b4 100644 --- a/dotnet/src/dotnetframework/GxDataInitialization/GXDataInitialization.csproj +++ b/dotnet/src/dotnetframework/GxDataInitialization/GXDataInitialization.csproj @@ -8,11 +8,6 @@ DataInitialization GeneXus.DataInitialization - - - runtime - - diff --git a/dotnet/src/dotnetframework/GxExcel/GxExcel.csproj b/dotnet/src/dotnetframework/GxExcel/GxExcel.csproj index 8a28b9670..1166d138b 100644 --- a/dotnet/src/dotnetframework/GxExcel/GxExcel.csproj +++ b/dotnet/src/dotnetframework/GxExcel/GxExcel.csproj @@ -9,7 +9,6 @@ - diff --git a/dotnet/src/dotnetframework/GxExcel/GxExcelEPPlus.cs b/dotnet/src/dotnetframework/GxExcel/GxExcelEPPlus.cs index 134c531d2..30363b39e 100644 --- a/dotnet/src/dotnetframework/GxExcel/GxExcelEPPlus.cs +++ b/dotnet/src/dotnetframework/GxExcel/GxExcelEPPlus.cs @@ -1,18 +1,17 @@ using System; using System.IO; -using log4net; +using GeneXus.Office.Excel; using OfficeOpenXml; using OfficeOpenXml.Style; -using GeneXus.Office.Excel; namespace GeneXus.Office.ExcelGXEPPlus { public class ExcelDocument : IGxError, IExcelDocument { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); - private ExcelPackage p; + private ExcelPackage p; public string dateFormat = "m/d/yy h:mm"; public bool OpenFromTemplate { get; set;} @@ -357,9 +356,9 @@ public short Init(string previousMsgError) public class ExcelCells : IExcelCells { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); - private IGxError m_errAccess; + private IGxError m_errAccess; private int pWidth, pHeight; private int pColPos; private ExcelWorksheet pSelectedSheet; diff --git a/dotnet/src/dotnetframework/GxExcel/GxExcelI.cs b/dotnet/src/dotnetframework/GxExcel/GxExcelI.cs index e5401195e..0309e828b 100644 --- a/dotnet/src/dotnetframework/GxExcel/GxExcelI.cs +++ b/dotnet/src/dotnetframework/GxExcel/GxExcelI.cs @@ -1,20 +1,19 @@ using System; -using System.Reflection; -using log4net; using System.Diagnostics; using System.IO; +using System.Reflection; using GeneXus.Application; -using GeneXus.Utils; -using GeneXus.Services; using GeneXus.Office.Excel; +using GeneXus.Services; namespace GeneXus.Office { - public class ExcelDocumentI + public class ExcelDocumentI { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - public short Index = -1; + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + + public short Index = -1; private string fileName; [Obsolete("It is here for backward compatibility", false)] @@ -52,7 +51,6 @@ public bool checkExcelDocument() { if (Document == null) { -#if !NETCORE if (this.fileName.EndsWith(Constants.EXCEL2003Extension) || this.template.EndsWith(Constants.EXCEL2003ExtensionTemplate)) { if (document == null || document.ErrCode == 99) @@ -63,6 +61,7 @@ public bool checkExcelDocument() document.ReadOnly = ReadOnly; document.Init(initErrDesc); } +#if !NETCORE if (document == null || document.ErrCode != 0) //Automation { GXLogging.Debug(log,"Interop.GXOFFICE2Lib.ExcelDocumentClass"); @@ -70,11 +69,11 @@ public bool checkExcelDocument() document.ReadOnly = ReadOnly; document.Init(""); } - } - else #endif - { - document = new GeneXus.Office.ExcelGXEPPlus.ExcelDocument(); + } + else + { + document = new GeneXus.Office.ExcelGXEPPlus.ExcelDocument(); document.ReadOnly = ReadOnly; } @@ -437,9 +436,14 @@ public static object GetEnumValue(Assembly ass, string enumSuperClass, string en return fi.GetValue(null); } + internal static object GetEnumValue(Assembly ass, string enumNameClass, string enumField) + { + Type prn1 = ass.GetType(enumNameClass); + return prn1.GetProperty(enumField).GetValue(null); + } - } - public interface IGxError + } + public interface IGxError { void setErrCod(short errCod); void setErrDes(String errDes); @@ -585,7 +589,7 @@ public IExcelCellWrapper(Interop.GXOFFICE2Lib.ExcelCells cell) this.cell = cell; } - #region IExcelCells Members + #region IExcelCells Members public DateTime Date { @@ -728,13 +732,13 @@ public short Underline } } - #endregion + #endregion } public class IExcelDocumentWrapper : IExcelDocument { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); Interop.GXOFFICE2Lib.IExcelDocument doc; @@ -742,7 +746,7 @@ public IExcelDocumentWrapper(Interop.GXOFFICE2Lib.IExcelDocument document) { doc = document; } - #region IExcelDocument Members + #region IExcelDocument Members public short Init(string previousMsgError) { @@ -924,15 +928,14 @@ public void CalculateFormulas() throw new NotImplementedException(); } - #endregion + #endregion } #endif public class ExcelUtils { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - public static void Show(string xlsFileName) + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + public static void Show(string xlsFileName) { Process p = new Process(); p.StartInfo.FileName = "excel"; diff --git a/dotnet/src/dotnetframework/GxExcel/GxExcelLite.cs b/dotnet/src/dotnetframework/GxExcel/GxExcelLite.cs index a17605cae..61a9ff9a9 100644 --- a/dotnet/src/dotnetframework/GxExcel/GxExcelLite.cs +++ b/dotnet/src/dotnetframework/GxExcel/GxExcelLite.cs @@ -1,17 +1,17 @@ using System; -using GeneXus.Office; using System.Collections; -using System.Reflection; using System.IO; -using log4net; +using System.Reflection; using GeneXus.Application; using GeneXus.Office.Excel; +using GxClasses.Helpers; + namespace GeneXus.Office.ExcelLite { public class ExcelCells : IExcelCells { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public ExcelCells(IGxError errAccess, object ef, int row, int col, int height, int width) { @@ -366,9 +366,18 @@ public int Color public class ExcelDocument : IGxError, IExcelDocument { - static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public static string nmspace; +#if NETCORE + public static string license = "FREE-LIMITED-KEY"; + const string LoadMethod = "Load"; + const string SaveMethod = "Save"; +#else public static string license; + const string LoadMethod = "LoadXls"; + const string SaveMethod = "SaveXls"; +#endif + public static Assembly ass; public short Init(string previousMsgError) @@ -381,17 +390,25 @@ public short Init(string previousMsgError) { if (nmspace==null) { +#if NETCORE + ass = AssemblyLoader.LoadAssembly(new AssemblyName("GemBox.Spreadsheet")); +#else + ass = loadAssembly(Path.Combine(GxContext.StaticPhysicalPath(), "GemBox.Spreadsheet.dll")); if (ass==null) { ass = loadAssembly(Path.Combine(GxContext.StaticPhysicalPath(), "bin", "GemBox.Spreadsheet.dll")); } +#endif if (ass!=null) { nmspace="GemBox.Spreadsheet"; } else { +#if NETCORE + ass = AssemblyLoader.LoadAssembly(new AssemblyName("GemBox.ExcelLite")); +#else if (ass==null) { ass = loadAssembly(Path.Combine(GxContext.StaticPhysicalPath(), @"GemBox.ExcelLite.dll")); @@ -400,6 +417,7 @@ public short Init(string previousMsgError) { ass = loadAssembly(Path.Combine(GxContext.StaticPhysicalPath(), "bin", "GemBox.ExcelLite.dll")); } +#endif if (ass!=null) { nmspace="GemBox.ExcelLite"; @@ -409,11 +427,15 @@ public short Init(string previousMsgError) } else { +#if NETCORE + ass = AssemblyLoader.LoadAssembly(new AssemblyName(nmspace)); +#else ass = loadAssembly(Path.Combine(GxContext.StaticPhysicalPath(), nmspace + ".dll")); if (ass==null) { ass = loadAssembly(Path.Combine(GxContext.StaticPhysicalPath(), "bin", nmspace + ".dll")); } +#endif } GXLogging.Debug(log, "nmspace:" + nmspace); @@ -434,6 +456,7 @@ public short Init(string previousMsgError) GXLogging.Error(log, @"Error setting license.", e); } } + } return 0; } @@ -458,7 +481,6 @@ private Assembly loadAssembly(string fileName) { return null; } - } public short Open(String fileName) { @@ -479,7 +501,11 @@ public short Open(String fileName) if (stream != null) { stream.Position = 0; - GxExcelUtils.Invoke(ef, "LoadXls", new object[] { stream }); +#if NETCORE + GxExcelUtils.InvokeStatic(ass, classType.FullName, LoadMethod, new object[] { stream }); +#else + GxExcelUtils.Invoke(ef, LoadMethod, new object[] { stream }); +#endif } } else @@ -495,7 +521,12 @@ public short Open(String fileName) if (stream != null) { stream.Position = 0; - GxExcelUtils.Invoke(ef, "LoadXls", new object[] { stream }); + +#if NETCORE + ef = GxExcelUtils.InvokeStatic(ass, classType.FullName, LoadMethod, new object[] { stream }); +#else + GxExcelUtils.Invoke(ef, LoadMethod, new object[] { stream }); +#endif } } else @@ -523,8 +554,14 @@ public short Save() { GxFile file = new GxFile(Path.GetDirectoryName(xlsFileName), xlsFileName, GxFileType.Private); MemoryStream content = new MemoryStream(); - GxExcelUtils.Invoke(ef, "SaveXls", new object[]{content}); - content.Position = 0; + +#if NETCORE + object saveOption = GxExcelUtils.GetEnumValue(ExcelDocument.ass, ExcelDocument.nmspace + ".SaveOptions", "XlsDefault"); + GxExcelUtils.Invoke(ef, SaveMethod, new object[] { content, saveOption }); +#else + GxExcelUtils.Invoke(ef, SaveMethod, new object[]{content}); +#endif + content.Position = 0; file.Create(content); } catch (Exception e) diff --git a/dotnet/src/dotnetframework/GxMail/Exchange/ExchangeSession.cs b/dotnet/src/dotnetframework/GxMail/Exchange/ExchangeSession.cs index e74e2b95d..2127f7b3b 100644 --- a/dotnet/src/dotnetframework/GxMail/Exchange/ExchangeSession.cs +++ b/dotnet/src/dotnetframework/GxMail/Exchange/ExchangeSession.cs @@ -1,16 +1,16 @@ -using log4net; -using Microsoft.Exchange.WebServices.Data; -using Microsoft.Identity.Client; using System; using System.IO; using System.Linq; using System.Net; +using Microsoft.Exchange.WebServices.Data; +using Microsoft.Identity.Client; namespace GeneXus.Mail.Exchange { public class ExchangeSession : IMailService { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(ExchangeSession)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private string _attachDir = string.Empty; private string _userName = string.Empty; private string _password = string.Empty; @@ -139,7 +139,7 @@ public void Login(GXMailServiceSession sessionInfo) } catch (Exception e) { - log.Error("Exchange Login Error", e); + GXLogging.Error(log, "Exchange Login Error", e); HandleError(e, 3); } } diff --git a/dotnet/src/dotnetframework/GxMail/Exchange/Service.cs b/dotnet/src/dotnetframework/GxMail/Exchange/Service.cs index e1f617b16..a63a18601 100644 --- a/dotnet/src/dotnetframework/GxMail/Exchange/Service.cs +++ b/dotnet/src/dotnetframework/GxMail/Exchange/Service.cs @@ -2,15 +2,14 @@ using System.Net; using Microsoft.Exchange.WebServices.Data; -using log4net; namespace GeneXus.Mail.Exchange { - public static class Service + public static class Service { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(Service)); - - static Service() + + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(Service).FullName); + static Service() { CertificateCallback.Initialize(); } diff --git a/dotnet/src/dotnetframework/GxMail/Exchange/TraceListener.cs b/dotnet/src/dotnetframework/GxMail/Exchange/TraceListener.cs index 2b2106118..5cbb39a09 100644 --- a/dotnet/src/dotnetframework/GxMail/Exchange/TraceListener.cs +++ b/dotnet/src/dotnetframework/GxMail/Exchange/TraceListener.cs @@ -1,22 +1,13 @@ -using log4net; using Microsoft.Exchange.WebServices.Data; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace GeneXus.Mail.Exchange { - public class TraceListener : ITraceListener + public class TraceListener : ITraceListener { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(TraceListener)); - - public void Trace(string traceType, string traceMessage) + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + public void Trace(string traceType, string traceMessage) { - if (log.IsDebugEnabled) - { - log.Debug(String.Format("Trace Type: {0} - Message: {1}", traceType, traceMessage)); - } + GXLogging.Debug(log, string.Format("Trace Type: {0} - Message: {1}", traceType, traceMessage)); } } } diff --git a/dotnet/src/dotnetframework/GxMail/GXMailSession.cs b/dotnet/src/dotnetframework/GxMail/GXMailSession.cs index 1f775cf3f..bdc7e236d 100644 --- a/dotnet/src/dotnetframework/GxMail/GXMailSession.cs +++ b/dotnet/src/dotnetframework/GxMail/GXMailSession.cs @@ -1,12 +1,11 @@ -using log4net; using System; namespace GeneXus.Mail { - + public abstract class GXMailSession { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXMailSession)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); protected short errorCode; protected string errorDescription; diff --git a/dotnet/src/dotnetframework/GxMail/GXPOP3Session.cs b/dotnet/src/dotnetframework/GxMail/GXPOP3Session.cs index 65ae678ed..aac24161e 100644 --- a/dotnet/src/dotnetframework/GxMail/GXPOP3Session.cs +++ b/dotnet/src/dotnetframework/GxMail/GXPOP3Session.cs @@ -1,14 +1,14 @@ using System; -using GeneXus.Mail.Internals; using GeneXus.Configuration; -using log4net; +using GeneXus.Mail.Internals; namespace GeneXus.Mail { - + public class GXPOP3Session : GXMailSession { - private static readonly ILog log = LogManager.GetLogger(typeof(GXSMTPSession)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private IPOP3Session session; private short secure; private short newMessages; diff --git a/dotnet/src/dotnetframework/GxMail/GXSMTPSession.cs b/dotnet/src/dotnetframework/GxMail/GXSMTPSession.cs index efaff579a..42ec2cf4d 100644 --- a/dotnet/src/dotnetframework/GxMail/GXSMTPSession.cs +++ b/dotnet/src/dotnetframework/GxMail/GXSMTPSession.cs @@ -1,17 +1,14 @@ using System; -using GeneXus.Mail.Internals; -using GeneXus.Utils; using GeneXus.Configuration; -using log4net; +using GeneXus.Mail.Internals; namespace GeneXus.Mail { - - public class GXSMTPSession : GXMailSession - { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXSMTPSession)); - private ISMTPSession session; + public class GXSMTPSession : GXMailSession + { + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private ISMTPSession session; private string attachDir; private short authentication; private short secure; diff --git a/dotnet/src/dotnetframework/GxMail/GxMail.csproj b/dotnet/src/dotnetframework/GxMail/GxMail.csproj index 0f121131f..fa8a83e9a 100644 --- a/dotnet/src/dotnetframework/GxMail/GxMail.csproj +++ b/dotnet/src/dotnetframework/GxMail/GxMail.csproj @@ -9,10 +9,9 @@ - - + diff --git a/dotnet/src/dotnetframework/GxMail/MAPISession.cs b/dotnet/src/dotnetframework/GxMail/MAPISession.cs index 7e61fc0a0..1211d92e7 100644 --- a/dotnet/src/dotnetframework/GxMail/MAPISession.cs +++ b/dotnet/src/dotnetframework/GxMail/MAPISession.cs @@ -1,15 +1,13 @@ using System; -using log4net; using GeneXus.Utils; using MAPI; namespace GeneXus.Mail.Internals { - + internal class MAPISession { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(MAPISession)); - + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private enum RecipientType { TO = 1, diff --git a/dotnet/src/dotnetframework/GxMail/MailMessage.cs b/dotnet/src/dotnetframework/GxMail/MailMessage.cs index 8ed31895f..c09eb4f1f 100644 --- a/dotnet/src/dotnetframework/GxMail/MailMessage.cs +++ b/dotnet/src/dotnetframework/GxMail/MailMessage.cs @@ -1,20 +1,19 @@ using System; using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; -using log4net; using System.Threading; using GeneXus.Utils; -using System.Collections.Generic; -using System.Collections.Concurrent; namespace GeneXus.Mail.Internals.Pop3 { - + internal class MailMessage { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(MailMessage)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private const string CRLF = "\r\n"; private static Hashtable monthList = new Hashtable(); diff --git a/dotnet/src/dotnetframework/GxMail/Mentalis/RFC822EndReader.cs b/dotnet/src/dotnetframework/GxMail/Mentalis/RFC822EndReader.cs index ebed69898..c15ee788f 100644 --- a/dotnet/src/dotnetframework/GxMail/Mentalis/RFC822EndReader.cs +++ b/dotnet/src/dotnetframework/GxMail/Mentalis/RFC822EndReader.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Text; -using log4net; namespace GeneXus.Mail.Internals.Pop3 { @@ -10,7 +9,7 @@ namespace GeneXus.Mail.Internals.Pop3 /// internal class RFC822EndReader : StreamReader { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(RFC822EndReader)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private const int BUFFER_SIZE = 1024; private bool textPlain = false; diff --git a/dotnet/src/dotnetframework/GxMail/OutlookSession.cs b/dotnet/src/dotnetframework/GxMail/OutlookSession.cs index 52ee82071..061ca6a41 100644 --- a/dotnet/src/dotnetframework/GxMail/OutlookSession.cs +++ b/dotnet/src/dotnetframework/GxMail/OutlookSession.cs @@ -1,16 +1,14 @@ using System; using System.IO; -using System.Collections; -using log4net; -using Outlook; using GeneXus.Utils; +using Outlook; namespace GeneXus.Mail.Internals { - + internal class OutlookSession { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(OutlookSession)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private enum RecipientType { diff --git a/dotnet/src/dotnetframework/GxMail/POP3Session.cs b/dotnet/src/dotnetframework/GxMail/POP3Session.cs index 75be7b0a3..cfc767353 100644 --- a/dotnet/src/dotnetframework/GxMail/POP3Session.cs +++ b/dotnet/src/dotnetframework/GxMail/POP3Session.cs @@ -1,22 +1,19 @@ using System; using System.IO; -using System.Text; +using System.Linq; using System.Net; using System.Net.Sockets; -using System.Threading; -using log4net; +using System.Text; using GeneXus.Mail.Internals.Pop3; -using Org.Mentalis.Security.Ssl; using Org.Mentalis.Security.Certificates; -using System.Linq; +using Org.Mentalis.Security.Ssl; namespace GeneXus.Mail.Internals { - - internal class POP3Session : Pop3SessionBase + + internal class POP3Session : Pop3SessionBase { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(POP3Session)); - + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private const string CRLF = "\r\n"; private string userName; diff --git a/dotnet/src/dotnetframework/GxMail/POP3SessionOpenPop.cs b/dotnet/src/dotnetframework/GxMail/POP3SessionOpenPop.cs index 45cb1e583..55894e69d 100644 --- a/dotnet/src/dotnetframework/GxMail/POP3SessionOpenPop.cs +++ b/dotnet/src/dotnetframework/GxMail/POP3SessionOpenPop.cs @@ -1,21 +1,20 @@ using System; using System.Collections.Generic; -using OpenPop.Pop3; -using OpenPop.Pop3.Exceptions; -using log4net; -using OpenPop.Mime; -using System.Net.Mail; using System.IO; -using GeneXus.Utils; +using System.Net.Mail; using System.Reflection; +using GeneXus.Utils; +using OpenPop.Mime; +using OpenPop.Pop3; +using OpenPop.Pop3.Exceptions; namespace GeneXus.Mail { - internal class POP3SessionOpenPop : Pop3SessionBase + internal class POP3SessionOpenPop : Pop3SessionBase { - private static readonly ILog log = LogManager.GetLogger(typeof(POP3SessionOpenPop)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); - private Pop3Client client; + private Pop3Client client; public override int GetMessageCount() { diff --git a/dotnet/src/dotnetframework/GxMail/Pop3MailKit.cs b/dotnet/src/dotnetframework/GxMail/Pop3MailKit.cs index 0cb0ea71a..54ce23d7c 100644 --- a/dotnet/src/dotnetframework/GxMail/Pop3MailKit.cs +++ b/dotnet/src/dotnetframework/GxMail/Pop3MailKit.cs @@ -1,22 +1,21 @@ using System; using System.Collections.Generic; +using System.IO; using System.Security.Authentication; -using log4net; +using GeneXus.Utils; using MailKit; -using MailKit.Security; using MailKit.Net.Pop3; +using MailKit.Security; using MimeKit; -using GeneXus.Utils; -using System.IO; namespace GeneXus.Mail { internal class Pop3MailKit : Pop3SessionBase { - - private static readonly ILog log = LogManager.GetLogger(typeof(Pop3MailKit)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private Pop3Client client; + const string INLINE_IMAGE_PREFIX = "cid:"; public override int GetMessageCount() { @@ -163,7 +162,7 @@ public override void Receive(GXPOP3Session sessionInfo, GXMailMessage gxmessage) } gxmessage.DateReceived = Internals.Pop3.MailMessage.GetMessageDate(GetHeaderFromMimeMessage(msg,"Delivery-Date")); AddHeader(gxmessage, "DispositionNotificationTo", GetHeaderFromMimeMessage(msg, "Disposition-Notification-To")); - ProcessMailAttachments(gxmessage, msg.Attachments); + ProcessMailAttachments(gxmessage, msg); } } } @@ -194,8 +193,9 @@ private string GetHeaderFromMimeMessage(MimeMessage msg, string headerValue) return msg.Headers.Contains(headerValue) ? msg.Headers[msg.Headers.IndexOf(headerValue)].ToString() : null; } - private void ProcessMailAttachments(GXMailMessage gxmessage, IEnumerable attachs) + private void ProcessMailAttachments(GXMailMessage gxmessage, MimeMessage msg) { + IEnumerable attachs = msg.Attachments; if (attachs == null) return; @@ -204,28 +204,49 @@ private void ProcessMailAttachments(GXMailMessage gxmessage, IEnumerable -1)); + } + + private void ProcessMailAttachment(GXMailMessage gxmessage, MimeEntity attach, string attachName) + { + if (!string.IsNullOrEmpty(attach.ContentId) && attach.ContentDisposition != null && !attach.ContentDisposition.IsAttachment) + { + string cid = INLINE_IMAGE_PREFIX + attach.ContentId; + attachName = String.Format("{0}_{1}", attach.ContentId, attachName); + gxmessage.HTMLText = gxmessage.HTMLText.Replace(cid, attachName); + } + try + { + SaveAttachedFile(attach, attachName); + gxmessage.Attachments.Add(attachName); + } + catch (Exception e) + { + LogError("Could not add Attachment", "Failed to save attachment", MailConstants.MAIL_InvalidAttachment, e, log); + } + + } + private void SaveAttachedFile(MimeEntity attach, string attachName) { using (var stream = File.Create(Path.Combine(AttachDir, attachName))) diff --git a/dotnet/src/dotnetframework/GxMail/Pop3SessionBase.cs b/dotnet/src/dotnetframework/GxMail/Pop3SessionBase.cs index f59a38dc0..50cb7ea6a 100644 --- a/dotnet/src/dotnetframework/GxMail/Pop3SessionBase.cs +++ b/dotnet/src/dotnetframework/GxMail/Pop3SessionBase.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; -using log4net; namespace GeneXus.Mail { @@ -13,7 +12,7 @@ internal abstract class Pop3SessionBase : IPOP3Session protected int lastReadMessage; protected int count; protected List uIds; - private static readonly ILog log = LogManager.GetLogger(typeof(Pop3SessionBase)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public bool DownloadAttachments { get; set; } @@ -63,11 +62,11 @@ public virtual string GetNextUID(GXPOP3Session session) public abstract void Receive(GXPOP3Session sessionInfo, GXMailMessage gxmessage); - protected void LogError(string title, string message, int code, Exception e, ILog log) + protected void LogError(string title, string message, int code, Exception e, IGXLogger log) { #if DEBUG - if (e != null && log.IsErrorEnabled) log.Error(message, e); + if (e != null && log.IsErrorEnabled) GXLogging.Error(log, message, e); #endif if (_sessionInfo != null) { @@ -75,20 +74,20 @@ protected void LogError(string title, string message, int code, Exception e, ILo } } - protected void LogError(string title, string message, int code, ILog log) + protected void LogError(string title, string message, int code, IGXLogger log) { LogError(title, message, code, null,log); } - protected void LogDebug(string title, string message, int code, ILog log) + protected void LogDebug(string title, string message, int code, IGXLogger log) { LogDebug(title, message, code, null, log); } - protected void LogDebug(string title, string message, int code, Exception e, ILog log) + protected void LogDebug(string title, string message, int code, Exception e, IGXLogger log) { #if DEBUG - if (e != null && log.IsDebugEnabled) log.Debug(message, e); + if (e != null && log.IsDebugEnabled) GXLogging.Debug(log, message, e); #endif if (_sessionInfo != null) { diff --git a/dotnet/src/dotnetframework/GxMail/SMTP/SmtpHelper.cs b/dotnet/src/dotnetframework/GxMail/SMTP/SmtpHelper.cs index 5d32bd771..93dacac98 100644 --- a/dotnet/src/dotnetframework/GxMail/SMTP/SmtpHelper.cs +++ b/dotnet/src/dotnetframework/GxMail/SMTP/SmtpHelper.cs @@ -1,5 +1,3 @@ -using GeneXus.Metadata; -using log4net; using System; using System.Net; using System.Net.Mail; @@ -11,7 +9,7 @@ namespace GeneXus.Mail.Smtp public class SmtpHelper { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(SMTPMailClient)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); #if NETCORE const string SMTP_TRANSPORT = "_transport"; const string SMTP_CONNECTION = "_connection"; @@ -34,7 +32,7 @@ public static bool ValidateConnection(SmtpClient smtp, string senderAddress, boo { senderAddress = (string.IsNullOrEmpty(senderAddress)) ? "test@gmail.com" : senderAddress; SmtpGetConnection(smtpTransportType, SmtpTransport, smtp); - var connection = SmtpConnectionField(SmtpTransport); + object connection = SmtpConnectionField(SmtpTransport); if (checkUnicodeSupport) { Type mailCommandType = ass.GetType(SMTP_MAIL_COMMAND_TYPE); diff --git a/dotnet/src/dotnetframework/GxMail/SMTPMailClient.cs b/dotnet/src/dotnetframework/GxMail/SMTPMailClient.cs index f598c05a6..8c1086aac 100644 --- a/dotnet/src/dotnetframework/GxMail/SMTPMailClient.cs +++ b/dotnet/src/dotnetframework/GxMail/SMTPMailClient.cs @@ -1,15 +1,14 @@ using System; -using System.Text; -using System.Net.Mail; using System.IO; -using log4net; +using System.Net.Mail; +using System.Text; using GeneXus.Mail.Smtp; namespace GeneXus.Mail { - internal class SMTPMailClient : ISMTPSession + internal class SMTPMailClient : ISMTPSession { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(SMTPMailClient)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); SmtpClient client; private string attachDir; diff --git a/dotnet/src/dotnetframework/GxMail/SMTPMailKit.cs b/dotnet/src/dotnetframework/GxMail/SMTPMailKit.cs index 24f11fdbc..651601626 100644 --- a/dotnet/src/dotnetframework/GxMail/SMTPMailKit.cs +++ b/dotnet/src/dotnetframework/GxMail/SMTPMailKit.cs @@ -1,18 +1,17 @@ using System; using System.IO; -using log4net; -using MimeKit; +using System.Net.Sockets; using MailKit; using MailKit.Net.Smtp; using MailKit.Security; -using System.Net.Sockets; +using MimeKit; namespace GeneXus.Mail { internal class SMTPMailKit : ISMTPSession { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(SMTPMailKit)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); SmtpClient client; private string attachDir; diff --git a/dotnet/src/dotnetframework/GxMail/SMTPSession.cs b/dotnet/src/dotnetframework/GxMail/SMTPSession.cs index f8f26847c..5201a4ec3 100644 --- a/dotnet/src/dotnetframework/GxMail/SMTPSession.cs +++ b/dotnet/src/dotnetframework/GxMail/SMTPSession.cs @@ -1,22 +1,21 @@ using System; +using System.Collections.Generic; +using System.Globalization; using System.IO; -using System.Text; using System.Net; using System.Net.Sockets; -using System.Globalization; -using log4net; -using GeneXus.Utils; +using System.Text; using GeneXus.Mail.Internals.Smtp; -using Org.Mentalis.Security.Ssl; +using GeneXus.Utils; using Org.Mentalis.Security.Certificates; -using System.Collections.Generic; +using Org.Mentalis.Security.Ssl; namespace GeneXus.Mail.Internals { - - internal class SMTPSession: ISMTPSession + + internal class SMTPSession: ISMTPSession { - private static readonly ILog log = log4net.LogManager.GetLogger(typeof(SMTPSession)); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); #region Mail Constants private const int CR = 13; private const int LF = 10; diff --git a/dotnet/src/dotnetframework/GxOffice/ExcelSpreadsheetGXWrapper.cs b/dotnet/src/dotnetframework/GxOffice/ExcelSpreadsheetGXWrapper.cs index a1ac17645..dd65d8849 100644 --- a/dotnet/src/dotnetframework/GxOffice/ExcelSpreadsheetGXWrapper.cs +++ b/dotnet/src/dotnetframework/GxOffice/ExcelSpreadsheetGXWrapper.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; using GeneXus.MSOffice.Excel.Poi.Xssf; -using log4net; namespace GeneXus.MSOffice.Excel { public class ExcelSpreadsheetGXWrapper : IGXError { - private static readonly ILog logger = LogManager.GetLogger(typeof(ExcelSpreadsheetGXWrapper)); + private static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); private int _errCode; private string _errDescription = string.Empty; private IExcelWorksheet _currentWorksheet; @@ -58,7 +57,7 @@ public bool Open(string filePath, string template) { try { - logger.Debug("Opening Excel file: " + filePath); + GXLogging.Debug(logger, "Opening Excel file: " + filePath); _document = ExcelFactory.Create(this, filePath, template); if (_autofit) { @@ -75,7 +74,7 @@ public bool Open(string filePath, string template) } catch (Exception e)//InvalidOpertaionException { - logger.Error("Excel File could not be loaded", e); + GXLogging.Error(logger, "Excel File could not be loaded", e); SetError(ErrorCodes.FILE_EXCEPTION, "Could not open file"); } return _document != null; @@ -150,13 +149,13 @@ public ExcelCells GetCell(int rowIdx, int colIdx) public void SetError(ExcelException e) { SetError(e.ErrorCode, e.ErrorDescription); - logger.Error(e.ErrorDescription, e); + GXLogging.Error(logger, e.ErrorDescription, e); } public void SetError(string errorMsg, ExcelException e) { SetError(e.ErrorCode, e.ErrorDescription); - logger.Error(errorMsg); + GXLogging.Error(logger, errorMsg); } public ExcelCells GetCells(int rowIdx, int colIdx, int rowCount, int colCount) diff --git a/dotnet/src/dotnetframework/GxOffice/GxOffice.csproj b/dotnet/src/dotnetframework/GxOffice/GxOffice.csproj index ba4b31da3..2ae847298 100644 --- a/dotnet/src/dotnetframework/GxOffice/GxOffice.csproj +++ b/dotnet/src/dotnetframework/GxOffice/GxOffice.csproj @@ -8,8 +8,8 @@ - + diff --git a/dotnet/src/dotnetframework/GxOffice/poi/xssf/ExcelCells.cs b/dotnet/src/dotnetframework/GxOffice/poi/xssf/ExcelCells.cs index b7b95ef1a..019fc95e7 100644 --- a/dotnet/src/dotnetframework/GxOffice/poi/xssf/ExcelCells.cs +++ b/dotnet/src/dotnetframework/GxOffice/poi/xssf/ExcelCells.cs @@ -2,7 +2,6 @@ using System; using GeneXus.MSOffice.Excel.Style; using GeneXus.Utils; -using log4net; using NPOI.OpenXmlFormats.Spreadsheet; using NPOI.SS.UserModel; using NPOI.SS.Util; @@ -14,7 +13,8 @@ namespace GeneXus.MSOffice.Excel.Poi.Xssf { public class ExcelCells : IExcelCellRange { - private static readonly ILog logger = LogManager.GetLogger(typeof(ExcelCells)); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + protected IGXError _errorHandler; protected ExcelSpreadsheet doc; protected int cellCount; @@ -428,7 +428,7 @@ public void SetSize(double value) } catch (Exception ex) { - GXLogging.Error(logger, "SetSize error", ex); + GXLogging.Error(log, "SetSize error", ex); } } diff --git a/dotnet/src/dotnetframework/GxOffice/poi/xssf/ExcelSpreadsheet.cs b/dotnet/src/dotnetframework/GxOffice/poi/xssf/ExcelSpreadsheet.cs index c74e4046e..8200cede2 100644 --- a/dotnet/src/dotnetframework/GxOffice/poi/xssf/ExcelSpreadsheet.cs +++ b/dotnet/src/dotnetframework/GxOffice/poi/xssf/ExcelSpreadsheet.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using GeneXus.Application; -using log4net; using NPOI.SS.UserModel; using NPOI.Util; using NPOI.XSSF.UserModel; @@ -11,7 +10,9 @@ namespace GeneXus.MSOffice.Excel.Poi.Xssf { public class ExcelSpreadsheet : IExcelSpreadsheet { - private static readonly ILog logger = LogManager.GetLogger(typeof(ExcelSpreadsheet)); + + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); + private XSSFWorkbook _workbook; private string _documentFileName; private bool _autoFitColumnsOnSave = false; @@ -247,7 +248,7 @@ private void RecalculateFormulas() } catch (Exception e) { - logger.Error("recalculateFormulas", e); + GXLogging.Error(logger, "recalculateFormulas", e); } } diff --git a/dotnet/src/dotnetframework/GxPdfReportsCS/GxPdfReportsCS.csproj b/dotnet/src/dotnetframework/GxPdfReportsCS/GxPdfReportsCS.csproj index 491c8720e..12fd568d7 100644 --- a/dotnet/src/dotnetframework/GxPdfReportsCS/GxPdfReportsCS.csproj +++ b/dotnet/src/dotnetframework/GxPdfReportsCS/GxPdfReportsCS.csproj @@ -6,10 +6,11 @@ Itext PDF Report GeneXus.PdfReportsCS - - - + + + + diff --git a/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext.cs b/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportCommon.cs similarity index 51% rename from dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext.cs rename to dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportCommon.cs index 3a8eb6d38..3374cf1f1 100644 --- a/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext.cs +++ b/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportCommon.cs @@ -1,27 +1,20 @@ -using System; -using System.IO; +using System; using System.Collections; -using System.Threading; -using System.Text; -using Microsoft.Win32; -using System.Runtime.InteropServices; -using System.Globalization; - -using System.util; -using System.Diagnostics; -using log4net; -using iTextSharp.text.pdf; -using iTextSharp.text; - -using GeneXus.Printer; -using iTextSharp.text.html.simpleparser; using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; using System.Security; +using System.Text; +using System.Threading; +using System.util; using GeneXus; +using GeneXus.Printer; using GeneXus.Utils; -using System.Reflection; -using GeneXus.Metadata; +using Microsoft.Win32; namespace com.genexus.reports { @@ -33,73 +26,63 @@ internal enum VerticalAlign BOTTOM = 2, } - public class PDFReportItextSharp : IReportHandler + public abstract class PDFReportBase : IReportHandler { - private int lineHeight, pageLines; - - static ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - private iTextSharp.text.Rectangle pageSize; - private BaseFont baseFont; - Barcode barcode = null; - private bool fontUnderline; - private bool fontStrikethru; - private int fontSize; - - //Color for, BaseColor for => Itext5 - private object backColor, foreColor, templateColorFill; - private Stream outputStream = null; - - private static object syncRoot = new Object(); - private static ParseINI props; - private ParseINI printerSettings; - private String form; - private ArrayList stringTotalPages; - private int outputType = -1; - private int printerOutputMode = -1; - private bool modal = false; - private String docName = "PDFReport.pdf"; - private static NativeSharpFunctionsMS nativeCode = new NativeSharpFunctionsMS(); - private Hashtable fontSubstitutes = new Hashtable(); - private static String configurationFile = null; - private static String configurationTemplateFile = null; - private static String defaultRelativePrepend = null; - private static String webAppDir = null; + protected int lineHeight, pageLines; + + static IGXLogger log = GXLoggerFactory.GetLogger(); + + protected bool fontUnderline; + protected bool fontStrikethru; + protected int fontSize; + protected string language; + + protected Stream outputStream = null; + + protected static object syncRoot = new Object(); + protected static ParseINI props; + protected ParseINI printerSettings; + protected String form; + protected ArrayList stringTotalPages; + protected int outputType = -1; + protected int printerOutputMode = -1; + protected bool modal = false; + protected String docName = "PDFReport.pdf"; + protected static NativeSharpFunctionsMS nativeCode = new NativeSharpFunctionsMS(); + protected static Hashtable fontSubstitutes = new Hashtable(); + protected static String configurationFile = null; + protected static String configurationTemplateFile = null; + protected static String defaultRelativePrepend = null; + protected static String webAppDir = null; public static bool DEBUG = false; - private Document document; - private PdfWriter writer; - private static String predefinedSearchPath = ""; - private float leftMargin; - private float topMargin; - private float bottomMargin; - private PdfTemplate template; - private BaseFont templateFont; - private int templateFontSize; - private bool backFill = true; - private int templateAlignment; - private int pages = 0; - private bool templateCreated = false; - private bool asianFontsDllLoaded = false; - private static char[] TRIM_CHARS = { ' ' }; - private int M_top, M_bot; + protected static String predefinedSearchPath = ""; + protected float leftMargin; + protected float topMargin; + protected float bottomMargin; + protected int templateFontSize; + protected bool backFill = true; + protected int templateAlignment; + protected int pages = 0; + protected bool templateCreated = false; + protected static char[] TRIM_CHARS = { ' ' }; + protected int M_top, M_bot; public static float DASHES_UNITS_ON = 10; public static float DASHES_UNITS_OFF = 10; public static float DOTS_UNITS_OFF = 3; public static float DOTS_UNITS_ON = 1; public bool lineCapProjectingSquare = true; public bool barcode128AsImage = true; - internal Dictionary documentImages; - float[] STYLE_SOLID = new float[] { 1, 0 };//0 - float[] STYLE_NONE = null;//1 - float[] STYLE_DOTTED, //2 + protected PdfConformanceLevel complianceLevel = PdfConformanceLevel.None; + protected float[] STYLE_SOLID = new float[] { 1, 0 };//0 + protected float[] STYLE_NONE = null;//1 + protected float[] STYLE_DOTTED, //2 STYLE_DASHED, //3 STYLE_LONG_DASHED, //4 STYLE_LONG_DOT_DASHED; //5 - int STYLE_NONE_CONST = 1; - int runDirection; - int justifiedType; - static Assembly iTextAssembly = typeof(Document).Assembly; + protected int STYLE_NONE_CONST = 1; + protected int runDirection; + protected int MULTIPLIED_LEADING = 1; public bool GxOpenDoc(string fileName) { @@ -244,14 +227,11 @@ public static void showReport(String filename1, bool modal) } private static char alternateSeparator = Path.DirectorySeparatorChar == '/' ? '\\' : '/'; - public PDFReportItextSharp(String appPath) + public PDFReportBase(String appPath) { try { - document = null; - pageSize = null; stringTotalPages = new ArrayList(); - documentImages = new Dictionary(); defaultRelativePrepend = appPath; webAppDir = defaultRelativePrepend; if (appPath.Length > 0) @@ -312,7 +292,11 @@ private void loadPrinterSettingsProps(String iniFile, String form, String printe printerSettings.setupProperty(form, Const.COLOR, color + ""); printerSettings.setupProperty(form, Const.DUPLEX, duplex + ""); } - + internal static void SetDefaultComplianceLevel(PdfConformanceLevel level) + { + if (props!=null) + props.setGeneralProperty(Const.COMPLIANCE_LEVEL, level.ToString()); + } private void loadProps() { if (props == null) @@ -350,6 +334,7 @@ private void loadProps() props.setupGeneralProperty(Const.MARGINS_INSIDE_BORDER, Const.DEFAULT_MARGINS_INSIDE_BORDER.ToString().ToLower()); props.setupGeneralProperty(Const.OUTPUT_FILE_DIRECTORY, "."); props.setupGeneralProperty(Const.LEADING, "2"); + props.setupGeneralProperty(Const.COMPLIANCE_LEVEL, PdfConformanceLevel.None.ToString()); props.setupGeneralProperty(Const.RUN_DIRECTION, Const.RUN_DIRECTION_LTR); props.setupGeneralProperty(Const.JUSTIFIED_TYPE_ALL, "false"); @@ -409,29 +394,41 @@ public static String getPredefinedSearchPaths() return predefinedSearchPath; } - private void init() + protected virtual void init(ref int gxYPage, ref int gxXPage, int pageWidth, int pageLength) { - Document.Compress = true; - try - { - writer = PdfWriter.GetInstance(document, outputStream); - } - catch (DocumentException de) + + } + private float[] parsePattern(String patternStr) + { + if (patternStr!=null) { - GXLogging.Debug(log,"GxDrawRect error", de); + patternStr = patternStr.Trim(); + String[] values = patternStr.Split(new char[]{';'}); + if (values.Length>0) + { + float[] pattern = new float[values.Length]; + for (int i=0; i